cookie-test.js (7473B)
1 // getAndExpireCookiesForDefaultPathTest is a helper method to get and delete 2 // cookies using echo-cookie.html. 3 async function getAndExpireCookiesForDefaultPathTest() { 4 return new Promise((resolve, reject) => { 5 try { 6 const iframe = document.createElement('iframe'); 7 iframe.style = 'display: none'; 8 iframe.addEventListener('load', (e) => { 9 const win = e.target.contentWindow; 10 const iframeCookies = win.getCookies(); 11 win.expireCookies().then(() => { 12 document.documentElement.removeChild(iframe); 13 resolve(iframeCookies); 14 }); 15 }, {once: true}); 16 iframe.src = '/cookies/resources/echo-cookie.html'; 17 document.documentElement.appendChild(iframe); 18 } catch (e) { 19 reject(e); 20 } 21 }); 22 } 23 24 // getAndExpireCookiesForRedirectTest is a helper method to get and delete 25 // cookies that were set from a Location header redirect. 26 async function getAndExpireCookiesForRedirectTest(location) { 27 return new Promise((resolve, reject) => { 28 try { 29 const iframe = document.createElement('iframe'); 30 iframe.style = 'display: none'; 31 const listener = (e) => { 32 if (typeof e.data == 'object' && 'cookies' in e.data) { 33 window.removeEventListener('message', listener); 34 document.documentElement.removeChild(iframe); 35 resolve(e.data.cookies); 36 } 37 }; 38 window.addEventListener('message', listener); 39 iframe.addEventListener('load', (e) => { 40 e.target.contentWindow.postMessage('getAndExpireCookiesForRedirectTest', '*'); 41 }, {once: true}); 42 iframe.src = location; 43 document.documentElement.appendChild(iframe); 44 } catch (e) { 45 reject(e); 46 } 47 }); 48 } 49 50 // httpCookieTest sets a `cookie` (via HTTP), then asserts it was or was not set 51 // via `expectedValue` (via the DOM). Then cleans it up (via test driver). Most 52 // tests do not set a Path attribute, so `defaultPath` defaults to true. If the 53 // cookie values are expected to cause the HTTP request or response to fail, the 54 // test can be made to pass when this happens via `allowFetchFailure`, which 55 // defaults to false. 56 // 57 // `cookie` may be a single cookie string, or an array of cookie strings, where 58 // the order of the array items represents the order of the Set-Cookie headers 59 // sent by the server. 60 // 61 // Note: this function has a dependency on testdriver.js. Any test files calling 62 // it should include testdriver.js and testdriver-vendor.js 63 function httpCookieTest(cookie, expectedValue, name, defaultPath = true, 64 allowFetchFailure = false) { 65 return promise_test((t) => { 66 var skipAssertions = false; 67 return new Promise(async (resolve, reject) => { 68 // The result is ignored as we're expiring cookies for cleaning here. 69 await getAndExpireCookiesForDefaultPathTest(); 70 await test_driver.delete_all_cookies(); 71 t.add_cleanup(test_driver.delete_all_cookies); 72 73 let encodedCookie = encodeURIComponent(JSON.stringify(cookie)); 74 try { 75 await fetch(`/cookies/resources/cookie.py?set=${encodedCookie}`); 76 } catch { 77 if (allowFetchFailure) { 78 skipAssertions = true; 79 } else { 80 reject('Failed to fetch /cookies/resources/cookie.py'); 81 } 82 } 83 let cookies = document.cookie; 84 if (defaultPath) { 85 // for the tests where a Path is set from the request-uri 86 // path, we need to go look for cookies in an iframe at that 87 // default path. 88 cookies = await getAndExpireCookiesForDefaultPathTest(); 89 } 90 resolve(cookies); 91 }).then((cookies) => { 92 if (skipAssertions) { 93 return; 94 } 95 if (Boolean(expectedValue)) { 96 assert_equals(cookies, expectedValue, 'The cookie was set as expected.'); 97 } else { 98 assert_equals(cookies, expectedValue, 'The cookie was rejected.'); 99 } 100 }); 101 }, name); 102 } 103 104 // This is a variation on httpCookieTest, where a redirect happens via 105 // the Location header and we check to see if cookies are sent via 106 // getRedirectedCookies 107 // 108 // Note: the locations targeted by this function have a dependency on 109 // path-redirect-shared.js and should be sure to include it. 110 function httpRedirectCookieTest(cookie, expectedValue, name, location) { 111 return promise_test(async (t) => { 112 // The result is ignored as we're expiring cookies for cleaning here. 113 await getAndExpireCookiesForRedirectTest(location); 114 115 const encodedCookie = encodeURIComponent(JSON.stringify(cookie)); 116 const encodedLocation = encodeURIComponent(location); 117 const setParams = `?set=${encodedCookie}&location=${encodedLocation}`; 118 await fetch(`/cookies/resources/cookie.py${setParams}`); 119 // for the tests where a redirect happens, we need to head 120 // to that URI to get the cookies (and then delete them there) 121 const cookies = await getAndExpireCookiesForRedirectTest(location); 122 if (Boolean(expectedValue)) { 123 assert_equals(cookies, expectedValue, 'The cookie was set as expected.'); 124 } else { 125 assert_equals(cookies, expectedValue, 'The cookie was rejected.'); 126 } 127 }, name); 128 } 129 130 // Sets a `cookie` via the DOM, checks it against `expectedValue` via the DOM, 131 // then cleans it up via the DOM. This is needed in cases where going through 132 // HTTP headers may modify the cookie line (e.g. by stripping control 133 // characters). 134 // 135 // Note: this function has a dependency on testdriver.js. Any test files calling 136 // it should include testdriver.js and testdriver-vendor.js 137 function domCookieTest(cookie, expectedValue, name) { 138 return promise_test(async (t) => { 139 await test_driver.delete_all_cookies(); 140 t.add_cleanup(test_driver.delete_all_cookies); 141 142 if (typeof cookie === "string") { 143 document.cookie = cookie; 144 } else if (Array.isArray(cookie)) { 145 for (const singlecookie of cookie) { 146 document.cookie = singlecookie; 147 } 148 } else { 149 throw new Error('Unexpected type passed into domCookieTest as cookie: ' + typeof cookie); 150 } 151 let cookies = document.cookie; 152 assert_equals(cookies, expectedValue, Boolean(expectedValue) ? 153 'The cookie was set as expected.' : 154 'The cookie was rejected.'); 155 }, name); 156 } 157 158 // Returns an array of control characters along with their ASCII codes. Control 159 // characters are defined by RFC 5234 to be %x00-1F / %x7F. 160 function getCtlCharacters() { 161 const ctlCodes = [...Array(0x20).keys()] 162 .concat([0x7F]); 163 return ctlCodes.map(i => ({ code: i, chr: String.fromCharCode(i) })) 164 } 165 166 // Returns a cookie string with name set to "t" * nameLength and value 167 // set to "1" * valueLength. Passing in 0 for either allows for creating 168 // a name- or value-less cookie. 169 // 170 // Note: Cookie length checking should ignore the "=". 171 function cookieStringWithNameAndValueLengths(nameLength, valueLength) { 172 return `${"t".repeat(nameLength)}=${"1".repeat(valueLength)}`; 173 } 174 175 // Finds the root window.top.opener and directs test_driver commands to it. 176 // 177 // If you see a message like: "Error: Tried to run in a non-testharness window 178 // without a call to set_test_context." then you probably need to call this. 179 function setTestContextUsingRootWindow() { 180 let test_window = window.top; 181 while (test_window.opener && !test_window.opener.closed) { 182 test_window = test_window.opener.top; 183 } 184 test_driver.set_test_context(test_window); 185 }