185 lines
7.3 KiB
JavaScript
185 lines
7.3 KiB
JavaScript
// 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.addEventListener('load', (e) => {
|
|
const win = e.target.contentWindow;
|
|
const iframeCookies = win.getCookies();
|
|
win.expireCookies().then(() => {
|
|
document.documentElement.removeChild(iframe);
|
|
resolve(iframeCookies);
|
|
});
|
|
}, {once: true});
|
|
iframe.src = '/cookies/resources/echo-cookie.html';
|
|
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';
|
|
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});
|
|
iframe.src = location;
|
|
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;
|
|
} 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);
|
|
}
|