diff options
Diffstat (limited to 'testing/web-platform/tests/cookies/resources/cookie-test.js')
-rw-r--r-- | testing/web-platform/tests/cookies/resources/cookie-test.js | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/testing/web-platform/tests/cookies/resources/cookie-test.js b/testing/web-platform/tests/cookies/resources/cookie-test.js new file mode 100644 index 0000000000..a909e4d72f --- /dev/null +++ b/testing/web-platform/tests/cookies/resources/cookie-test.js @@ -0,0 +1,186 @@ +// getAndExpireCookiesForDefaultPathTest is a helper method to get and delete +// cookies using echo-cookie.html. +async function getAndExpireCookiesForDefaultPathTest() { + return new Promise((resolve, reject) => { + try { + const iframe = document.createElement('iframe'); + iframe.style = 'display: none'; + iframe.src = '/cookies/resources/echo-cookie.html'; + iframe.addEventListener('load', (e) => { + const win = e.target.contentWindow; + const iframeCookies = win.getCookies(); + win.expireCookies().then(() => { + document.documentElement.removeChild(iframe); + resolve(iframeCookies); + }); + }, {once: true}); + document.documentElement.appendChild(iframe); + } catch (e) { + reject(e); + } + }); +} + +// getAndExpireCookiesForRedirectTest is a helper method to get and delete +// cookies that were set from a Location header redirect. +async function getAndExpireCookiesForRedirectTest(location) { + return new Promise((resolve, reject) => { + try { + const iframe = document.createElement('iframe'); + iframe.style = 'display: none'; + iframe.src = location; + const listener = (e) => { + if (typeof e.data == 'object' && 'cookies' in e.data) { + window.removeEventListener('message', listener); + document.documentElement.removeChild(iframe); + resolve(e.data.cookies); + } + }; + window.addEventListener('message', listener); + iframe.addEventListener('load', (e) => { + e.target.contentWindow.postMessage('getAndExpireCookiesForRedirectTest', '*'); + }, {once: true}); + document.documentElement.appendChild(iframe); + } catch (e) { + reject(e); + } + }); +} + +// httpCookieTest sets a `cookie` (via HTTP), then asserts it was or was not set +// via `expectedValue` (via the DOM). Then cleans it up (via test driver). Most +// tests do not set a Path attribute, so `defaultPath` defaults to true. If the +// cookie values are expected to cause the HTTP request or response to fail, the +// test can be made to pass when this happens via `allowFetchFailure`, which +// defaults to false. +// +// `cookie` may be a single cookie string, or an array of cookie strings, where +// the order of the array items represents the order of the Set-Cookie headers +// sent by the server. +// +// Note: this function has a dependency on testdriver.js. Any test files calling +// it should include testdriver.js and testdriver-vendor.js +function httpCookieTest(cookie, expectedValue, name, defaultPath = true, + allowFetchFailure = false) { + return promise_test((t) => { + var skipAssertions = false; + return new Promise(async (resolve, reject) => { + // The result is ignored as we're expiring cookies for cleaning here. + await getAndExpireCookiesForDefaultPathTest(); + await test_driver.delete_all_cookies(); + t.add_cleanup(test_driver.delete_all_cookies); + + let encodedCookie = encodeURIComponent(JSON.stringify(cookie)); + try { + await fetch(`/cookies/resources/cookie.py?set=${encodedCookie}`); + } catch { + if (allowFetchFailure) { + skipAssertions = true; + resolve(); + } else { + reject('Failed to fetch /cookies/resources/cookie.py'); + } + } + let cookies = document.cookie; + if (defaultPath) { + // for the tests where a Path is set from the request-uri + // path, we need to go look for cookies in an iframe at that + // default path. + cookies = await getAndExpireCookiesForDefaultPathTest(); + } + resolve(cookies); + }).then((cookies) => { + if (skipAssertions) { + return; + } + if (Boolean(expectedValue)) { + assert_equals(cookies, expectedValue, 'The cookie was set as expected.'); + } else { + assert_equals(cookies, expectedValue, 'The cookie was rejected.'); + } + }); + }, name); +} + +// This is a variation on httpCookieTest, where a redirect happens via +// the Location header and we check to see if cookies are sent via +// getRedirectedCookies +// +// Note: the locations targeted by this function have a dependency on +// path-redirect-shared.js and should be sure to include it. +function httpRedirectCookieTest(cookie, expectedValue, name, location) { + return promise_test(async (t) => { + // The result is ignored as we're expiring cookies for cleaning here. + await getAndExpireCookiesForRedirectTest(location); + + const encodedCookie = encodeURIComponent(JSON.stringify(cookie)); + const encodedLocation = encodeURIComponent(location); + const setParams = `?set=${encodedCookie}&location=${encodedLocation}`; + await fetch(`/cookies/resources/cookie.py${setParams}`); + // for the tests where a redirect happens, we need to head + // to that URI to get the cookies (and then delete them there) + const cookies = await getAndExpireCookiesForRedirectTest(location); + if (Boolean(expectedValue)) { + assert_equals(cookies, expectedValue, 'The cookie was set as expected.'); + } else { + assert_equals(cookies, expectedValue, 'The cookie was rejected.'); + } + }, name); +} + +// Sets a `cookie` via the DOM, checks it against `expectedValue` via the DOM, +// then cleans it up via the DOM. This is needed in cases where going through +// HTTP headers may modify the cookie line (e.g. by stripping control +// characters). +// +// Note: this function has a dependency on testdriver.js. Any test files calling +// it should include testdriver.js and testdriver-vendor.js +function domCookieTest(cookie, expectedValue, name) { + return promise_test(async (t) => { + await test_driver.delete_all_cookies(); + t.add_cleanup(test_driver.delete_all_cookies); + + if (typeof cookie === "string") { + document.cookie = cookie; + } else if (Array.isArray(cookie)) { + for (const singlecookie of cookie) { + document.cookie = singlecookie; + } + } else { + throw new Error('Unexpected type passed into domCookieTest as cookie: ' + typeof cookie); + } + let cookies = document.cookie; + assert_equals(cookies, expectedValue, Boolean(expectedValue) ? + 'The cookie was set as expected.' : + 'The cookie was rejected.'); + }, name); +} + +// Returns an array of control characters along with their ASCII codes. Control +// characters are defined by RFC 5234 to be %x00-1F / %x7F. +function getCtlCharacters() { + const ctlCodes = [...Array(0x20).keys()] + .concat([0x7F]); + return ctlCodes.map(i => ({ code: i, chr: String.fromCharCode(i) })) +} + +// Returns a cookie string with name set to "t" * nameLength and value +// set to "1" * valueLength. Passing in 0 for either allows for creating +// a name- or value-less cookie. +// +// Note: Cookie length checking should ignore the "=". +function cookieStringWithNameAndValueLengths(nameLength, valueLength) { + return `${"t".repeat(nameLength)}=${"1".repeat(valueLength)}`; +} + +// Finds the root window.top.opener and directs test_driver commands to it. +// +// If you see a message like: "Error: Tried to run in a non-testharness window +// without a call to set_test_context." then you probably need to call this. +function setTestContextUsingRootWindow() { + let test_window = window.top; + while (test_window.opener && !test_window.opener.closed) { + test_window = test_window.opener.top; + } + test_driver.set_test_context(test_window); +} |