summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/cookie-store/resources
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /testing/web-platform/tests/cookie-store/resources
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/cookie-store/resources')
-rw-r--r--testing/web-platform/tests/cookie-store/resources/always_changing_sw.sub.js6
-rw-r--r--testing/web-platform/tests/cookie-store/resources/cookie-test-helpers.js226
-rw-r--r--testing/web-platform/tests/cookie-store/resources/cookie_helper.py84
-rw-r--r--testing/web-platform/tests/cookie-store/resources/empty_sw.js1
-rw-r--r--testing/web-platform/tests/cookie-store/resources/helper_iframe.sub.html31
-rw-r--r--testing/web-platform/tests/cookie-store/resources/helpers.js72
6 files changed, 420 insertions, 0 deletions
diff --git a/testing/web-platform/tests/cookie-store/resources/always_changing_sw.sub.js b/testing/web-platform/tests/cookie-store/resources/always_changing_sw.sub.js
new file mode 100644
index 0000000000..9fdf99848f
--- /dev/null
+++ b/testing/web-platform/tests/cookie-store/resources/always_changing_sw.sub.js
@@ -0,0 +1,6 @@
+// This script changes every time it is fetched.
+
+// When used as a service worker script, this causes the Service Worker to be
+// updated on every ServiceWorkerRegistration.update() call.
+
+// The following bytes change on every fetch: {{uuid()}}
diff --git a/testing/web-platform/tests/cookie-store/resources/cookie-test-helpers.js b/testing/web-platform/tests/cookie-store/resources/cookie-test-helpers.js
new file mode 100644
index 0000000000..178947ad6e
--- /dev/null
+++ b/testing/web-platform/tests/cookie-store/resources/cookie-test-helpers.js
@@ -0,0 +1,226 @@
+'use strict';
+
+// TODO(jsbell): Once ServiceWorker is supported, add arbitrary path coverage.
+const kPath = location.pathname.replace(/[^/]+$/, '');
+
+// True when running in a document context as opposed to a worker context
+const kHasDocument = typeof document !== 'undefined';
+
+// True when running on unsecured 'http:' rather than secured 'https:'.
+const kIsUnsecured = location.protocol !== 'https:';
+
+const kCookieHelperCgi = 'resources/cookie_helper.py';
+
+// Approximate async equivalent to the document.cookie getter but with
+// important differences: optional additional getAll arguments are
+// forwarded, and an empty cookie jar returns undefined.
+//
+// This is intended primarily for verification against expected cookie
+// jar contents. It should produce more readable messages using
+// assert_equals in failing cases than assert_object_equals would
+// using parsed cookie jar contents and also allows expectations to be
+// written more compactly.
+async function getCookieString(...args) {
+ const cookies = await cookieStore.getAll(...args);
+ return cookies.length
+ ? cookies.map(({name, value}) =>
+ (name ? (name + '=') : '') + value).join('; ')
+ : undefined;
+}
+
+// Approximate async equivalent to the document.cookie getter but from
+// the server's point of view. Returns UTF-8 interpretation. Allows
+// sub-path to be specified.
+//
+// Unlike document.cookie, this returns undefined when no cookies are
+// present.
+async function getCookieStringHttp(extraPath = null) {
+ const url =
+ kCookieHelperCgi + ((extraPath == null) ? '' : ('/' + extraPath));
+ const response = await fetch(url, { credentials: 'include' });
+ const text = await response.text();
+ assert_equals(
+ response.ok,
+ true,
+ 'CGI should have succeeded in getCookieStringHttp\n' + text);
+ assert_equals(
+ response.headers.get('content-type'),
+ 'text/plain; charset=utf-8',
+ 'CGI did not return UTF-8 text in getCookieStringHttp');
+ if (text === '')
+ return undefined;
+ assert_equals(
+ text.indexOf('cookie='),
+ 0,
+ 'CGI response did not begin with "cookie=" and was not empty: ' + text);
+ return decodeURIComponent(text.replace(/^cookie=/, ''));
+}
+
+// Approximate async equivalent to the document.cookie getter but from
+// the server's point of view. Returns binary string
+// interpretation. Allows sub-path to be specified.
+//
+// Unlike document.cookie, this returns undefined when no cookies are
+// present.
+async function getCookieBinaryHttp(extraPath = null) {
+ const url =
+ kCookieHelperCgi +
+ ((extraPath == null) ?
+ '' :
+ ('/' + extraPath)) + '?charset=iso-8859-1';
+ const response = await fetch(url, { credentials: 'include' });
+ const text = await response.text();
+ assert_equals(
+ response.ok,
+ true,
+ 'CGI should have succeeded in getCookieBinaryHttp\n' + text);
+ assert_equals(
+ response.headers.get('content-type'),
+ 'text/plain; charset=iso-8859-1',
+ 'CGI did not return ISO 8859-1 text in getCookieBinaryHttp');
+ if (text === '')
+ return undefined;
+ assert_equals(
+ text.indexOf('cookie='),
+ 0,
+ 'CGI response did not begin with "cookie=" and was not empty: ' + text);
+ return unescape(text.replace(/^cookie=/, ''));
+}
+
+// Approximate async equivalent to the document.cookie setter but from
+// the server's point of view.
+async function setCookieStringHttp(setCookie) {
+ const encodedSetCookie = encodeURIComponent(setCookie);
+ const url = kCookieHelperCgi;
+ const headers = new Headers();
+ headers.set(
+ 'content-type',
+ 'application/x-www-form-urlencoded; charset=utf-8');
+ const response = await fetch(
+ url,
+ {
+ credentials: 'include',
+ method: 'POST',
+ headers: headers,
+ body: 'set-cookie=' + encodedSetCookie,
+ });
+ const text = await response.text();
+ assert_equals(
+ response.ok,
+ true,
+ 'CGI should have succeeded in setCookieStringHttp set-cookie: ' +
+ setCookie + '\n' + text);
+ assert_equals(
+ response.headers.get('content-type'),
+ 'text/plain; charset=utf-8',
+ 'CGI did not return UTF-8 text in setCookieStringHttp');
+ assert_equals(
+ text,
+ 'set-cookie=' + encodedSetCookie,
+ 'CGI did not faithfully echo the set-cookie value');
+}
+
+// Approximate async equivalent to the document.cookie setter but from
+// the server's point of view. This version sets a binary cookie rather
+// than a UTF-8 one.
+async function setCookieBinaryHttp(setCookie) {
+ const encodedSetCookie = escape(setCookie).split('/').join('%2F');
+ const url = kCookieHelperCgi + '?charset=iso-8859-1';
+ const headers = new Headers();
+ headers.set(
+ 'content-type',
+ 'application/x-www-form-urlencoded; charset=iso-8859-1');
+ const response = await fetch(url, {
+ credentials: 'include',
+ method: 'POST',
+ headers: headers,
+ body: 'set-cookie=' + encodedSetCookie
+ });
+ const text = await response.text();
+ assert_equals(
+ response.ok,
+ true,
+ 'CGI should have succeeded in setCookieBinaryHttp set-cookie: ' +
+ setCookie + '\n' + text);
+ assert_equals(
+ response.headers.get('content-type'),
+ 'text/plain; charset=iso-8859-1',
+ 'CGI did not return Latin-1 text in setCookieBinaryHttp');
+ assert_equals(
+ text,
+ 'set-cookie=' + encodedSetCookie,
+ 'CGI did not faithfully echo the set-cookie value');
+}
+
+// Async document.cookie getter; converts '' to undefined which loses
+// information in the edge case where a single ''-valued anonymous
+// cookie is visible.
+async function getCookieStringDocument() {
+ if (!kHasDocument)
+ throw 'document.cookie not available in this context';
+ return String(document.cookie || '') || undefined;
+}
+
+// Async document.cookie setter
+async function setCookieStringDocument(setCookie) {
+ if (!kHasDocument)
+ throw 'document.cookie not available in this context';
+ document.cookie = setCookie;
+}
+
+// Observe the next 'change' event on the cookieStore. Typical usage:
+//
+// const eventPromise = observeNextCookieChangeEvent();
+// await /* something that modifies cookies */
+// await verifyCookieChangeEvent(
+// eventPromise, {changed: [{name: 'name', value: 'value'}]});
+//
+function observeNextCookieChangeEvent() {
+ return new Promise(resolve => {
+ cookieStore.addEventListener('change', e => resolve(e), {once: true});
+ });
+}
+
+async function verifyCookieChangeEvent(eventPromise, expected, description) {
+ description = description ? description + ': ' : '';
+ expected = Object.assign({changed:[], deleted:[]}, expected);
+ const event = await eventPromise;
+ assert_equals(event.changed.length, expected.changed.length,
+ description + 'number of changed cookies');
+ for (let i = 0; i < event.changed.length; ++i) {
+ assert_equals(event.changed[i].name, expected.changed[i].name,
+ description + 'changed cookie name');
+ assert_equals(event.changed[i].value, expected.changed[i].value,
+ description + 'changed cookie value');
+ }
+ assert_equals(event.deleted.length, expected.deleted.length,
+ description + 'number of deleted cookies');
+ for (let i = 0; i < event.deleted.length; ++i) {
+ assert_equals(event.deleted[i].name, expected.deleted[i].name,
+ description + 'deleted cookie name');
+ assert_equals(event.deleted[i].value, expected.deleted[i].value,
+ description + 'deleted cookie value');
+ }
+}
+
+// Helper function for promise_test with cookies; cookies
+// named in these tests are cleared before/after the test
+// body function is executed.
+async function cookie_test(func, description) {
+
+ // Wipe cookies used by tests before and after the test.
+ async function deleteAllCookies() {
+ (await cookieStore.getAll()).forEach(({name, value}) => {
+ cookieStore.delete(name);
+ });
+ }
+
+ return promise_test(async t => {
+ await deleteAllCookies();
+ try {
+ return await func(t);
+ } finally {
+ await deleteAllCookies();
+ }
+ }, description);
+}
diff --git a/testing/web-platform/tests/cookie-store/resources/cookie_helper.py b/testing/web-platform/tests/cookie-store/resources/cookie_helper.py
new file mode 100644
index 0000000000..71dd8b82ee
--- /dev/null
+++ b/testing/web-platform/tests/cookie-store/resources/cookie_helper.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+# Active wptserve handler for cookie operations.
+#
+# This must support the following requests:
+#
+# - GET with the following query parameters:
+# - charset: (optional) character set for response (default: utf-8)
+# A cookie: request header (if present) is echoed in the body with a
+# cookie= prefix followed by the urlencoded bytes from the header.
+# Used to inspect the cookie jar from an HTTP request header context.
+# - POST with form-data in the body and the following query-or-form parameters:
+# - set-cookie: (optional; repeated) echoed in the set-cookie: response
+# header and also echoed in the body with a set-cookie= prefix
+# followed by the urlencoded bytes from the parameter; multiple occurrences
+# are CRLF-delimited.
+# Used to set cookies from an HTTP response header context.
+#
+# The response has 200 status and content-type: text/plain; charset=<charset>
+import encodings, re
+
+from urllib.parse import parse_qs, quote
+
+from wptserve.utils import isomorphic_decode, isomorphic_encode
+
+# NOTE: These are intentionally very lax to permit testing
+DISALLOWED_IN_COOKIE_NAME_RE = re.compile(br'[;\0-\x1f\x7f]')
+DISALLOWED_IN_HEADER_RE = re.compile(br'[\0-\x1f\x7f]')
+
+# Ensure common charset names do not end up with different
+# capitalization or punctuation
+CHARSET_OVERRIDES = {
+ encodings.codecs.lookup(charset).name: charset
+ for charset in (u'utf-8', u'iso-8859-1',)
+}
+
+def quote_str(cookie_str):
+ return isomorphic_encode(quote(isomorphic_decode(cookie_str), u'', encoding=u'iso-8859-1'))
+
+def parse_qs_str(query_str):
+ args = parse_qs(isomorphic_decode(query_str), keep_blank_values=True, encoding=u'iso-8859-1')
+ binary_args = {}
+ for key, val in args.items():
+ binary_args[isomorphic_encode(key)] = [isomorphic_encode(x) for x in val]
+ return binary_args
+
+def main(request, response):
+ assert request.method in (
+ u'GET',
+ u'POST',
+ ), u'request method was neither GET nor POST: %r' % request.method
+ qd = (isomorphic_encode(request.url).split(b'#')[0].split(b'?', 1) + [b''])[1]
+ if request.method == u'POST':
+ qd += b'&' + request.body
+ args = parse_qs_str(qd)
+
+ charset = encodings.codecs.lookup([isomorphic_decode(x) for x in args.get(b'charset', [u'utf-8'])][-1]).name
+ charset = CHARSET_OVERRIDES.get(charset, charset)
+ headers = [(b'content-type', b'text/plain; charset=' + isomorphic_encode(charset))]
+ body = []
+ if request.method == u'POST':
+ for set_cookie in args.get(b'set-cookie', []):
+ if b'=' in set_cookie.split(b';', 1)[0]:
+ name, rest = set_cookie.split(b'=', 1)
+ assert re.search(
+ DISALLOWED_IN_COOKIE_NAME_RE,
+ name
+ ) is None, b'name had disallowed characters: %r' % name
+ else:
+ rest = set_cookie
+ assert re.search(
+ DISALLOWED_IN_HEADER_RE,
+ rest
+ ) is None, b'rest had disallowed characters: %r' % rest
+ headers.append((b'set-cookie', set_cookie))
+ body.append(b'set-cookie=' + quote_str(set_cookie))
+
+ else:
+ cookie = request.headers.get(b'cookie')
+ if cookie is not None:
+ body.append(b'cookie=' + quote_str(cookie))
+ body = b'\r\n'.join(body)
+ headers.append((b'content-length', len(body)))
+ return 200, headers, body
diff --git a/testing/web-platform/tests/cookie-store/resources/empty_sw.js b/testing/web-platform/tests/cookie-store/resources/empty_sw.js
new file mode 100644
index 0000000000..2b0ae22612
--- /dev/null
+++ b/testing/web-platform/tests/cookie-store/resources/empty_sw.js
@@ -0,0 +1 @@
+// Empty service worker script.
diff --git a/testing/web-platform/tests/cookie-store/resources/helper_iframe.sub.html b/testing/web-platform/tests/cookie-store/resources/helper_iframe.sub.html
new file mode 100644
index 0000000000..9017eace44
--- /dev/null
+++ b/testing/web-platform/tests/cookie-store/resources/helper_iframe.sub.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset='utf-8'>
+<link rel='author' href='jarrydg@chromium.org' title='Jarryd Goodman'>
+<script>
+ 'use strict';
+
+ // Writing a cookie:
+ // Input: { cookieToSet: { name: 'cookie-name', value: 'cookie-value' } }
+ // Response: "Cookie has been set"
+ //
+ // Read a cookie.
+ // Command: { existingCookieName: 'cookie-name' }
+ // Response: Result of cookieStore.get('cookie-name'):
+ // { frameCookie: { name: 'cookie-name', value: 'cookie-value' } }
+ window.addEventListener('message', async function (event) {
+ const { opname } = event.data;
+ if (opname === 'set-cookie') {
+ const { name, value } = event.data
+ await cookieStore.set({
+ name,
+ value,
+ domain: '{{host}}',
+ });
+ event.source.postMessage('Cookie has been set', event.origin);
+ } else if (opname === 'get-cookie') {
+ const { name } = event.data
+ const frameCookie = await cookieStore.get(name);
+ event.source.postMessage({frameCookie}, event.origin);
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/cookie-store/resources/helpers.js b/testing/web-platform/tests/cookie-store/resources/helpers.js
new file mode 100644
index 0000000000..8d5dddef65
--- /dev/null
+++ b/testing/web-platform/tests/cookie-store/resources/helpers.js
@@ -0,0 +1,72 @@
+/**
+ * Promise based helper function who's return promise will resolve
+ * once the iframe src has been loaded
+ * @param {string} url the url to set the iframe src
+ * @param {test} t a test object to add a cleanup function to
+ * @return {Promise} when resolved, will return the iframe
+ */
+self.createIframe = (url, t) => new Promise(resolve => {
+ const iframe = document.createElement('iframe');
+ iframe.addEventListener('load', () => {resolve(iframe);}, {once: true});
+ iframe.src = url;
+ document.documentElement.appendChild(iframe);
+ t.add_cleanup(() => iframe.remove());
+});
+
+/**
+ * @description - Function unregisters any service workers in this scope
+ * and then creates a new registration. The function returns
+ * a promise that resolves when the registered service worker
+ * becomes activated. The resolved promise yields the
+ * service worker registration
+ * @param {testCase} t - test case to add cleanup functions to
+ */
+self.createServiceWorker = async (t, sw_registration_name, scope_url) => {
+ let registration = await navigator.serviceWorker.getRegistration(scope_url);
+ if (registration)
+ await registration.unregister();
+
+ registration = await navigator.serviceWorker.register(sw_registration_name,
+ {scope_url});
+ t.add_cleanup(() => registration.unregister());
+
+ return new Promise(resolve => {
+ const serviceWorker = registration.installing || registration.active ||
+ registration.waiting;
+ serviceWorker.addEventListener('statechange', event => {
+ if (event.target.state === 'activated') {
+ resolve(serviceWorker);
+ }
+ });
+ })
+}
+
+/**
+ * Function that will return a promise that resolves when a message event
+ * is fired. Returns a promise that resolves to the message that was received
+ */
+self.waitForMessage = () => new Promise(resolve => {
+ window.addEventListener('message', event => {
+ resolve(event.data);
+ }, {once: true});
+});
+
+/**
+ * Sends a message via MessageChannel and waits for the response
+ * @param {*} message
+ * @returns {Promise} resolves with the response payload
+ */
+self.sendMessageOverChannel = (message, target) => {
+ return new Promise(function(resolve, reject) {
+ const messageChannel = new MessageChannel();
+ messageChannel.port1.onmessage = event => {
+ if (event.data.error) {
+ reject(event.data.error);
+ } else {
+ resolve(event.data);
+ }
+ };
+
+ target.postMessage(message, [messageChannel.port2]);
+ })
+};