Skip to content

Commit

Permalink
Run Web Platform Tests against the polyfill. (#88)
Browse files Browse the repository at this point in the history
* Throw TypeError if get is called without arguments

See step 4 in "The get(options) method" section in the [CookieStore
Spec][cookiestore]:

>If options is empty, then return a promise rejected with a TypeError.

[cookiestore]: https://wicg.github.io/cookie-store/#ref-for-dom-cookiestore-get-options%E2%91%A4

* Refactor sanitizeOptions to use TypeScript generics

* Implement getting a cookie by URL

* Add cookieStore get arguments WPT tests

* Set `assert_equals` variable directly

* Actually parse URL and check its origin
  • Loading branch information
koddsson authored and markcellus committed Aug 18, 2022
1 parent dd52f45 commit 9f603bc
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 17 deletions.
38 changes: 22 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,14 +234,9 @@ function serialize(
return str;
}

function sanitizeOptions(
arg: string | CookieStoreGetOptions | undefined
): CookieStoreGetOptions | CookieStoreDeleteOptions {
if (!arg) {
return {};
}
function sanitizeOptions<T>(arg: string | T): T {
if (typeof arg === 'string') {
return { name: arg };
return ({ name: arg } as unknown) as T;
}
return arg;
}
Expand All @@ -256,7 +251,20 @@ const CookieStore = {
async get(
options?: CookieStoreGetOptions['name'] | CookieStoreGetOptions
): Promise<Cookie | undefined> {
const { name } = sanitizeOptions(options);
if (!options || !Object.keys(options).length) {
throw new TypeError('CookieStoreGetOptions must not be empty');
}
const { name, url } = sanitizeOptions<CookieStoreGetOptions>(options);
if (url) {
const parsedURL = new URL(url, window.location.origin);
if (
window.location.href !== parsedURL.href ||
window.location.origin !== parsedURL.origin
) {
throw new TypeError('URL must match the document URL');
}
return parse(document.cookie)[0];
}
return parse(document.cookie).find((cookie) => cookie.name === name);
},

Expand Down Expand Up @@ -285,12 +293,12 @@ const CookieStore = {
async getAll(
options?: CookieStoreGetOptions['name'] | CookieStoreGetOptions
): Promise<Cookie[]> {
const { name } = sanitizeOptions(options);
if (name) {
const cookie = await this.get(name);
return cookie ? [cookie] : [];
if (!options) {
return parse(document.cookie);
}
return parse(document.cookie);
const { name } = sanitizeOptions<CookieStoreGetOptions>(options);
const cookie = await this.get(name);
return cookie ? [cookie] : [];
},

/**
Expand All @@ -302,9 +310,7 @@ const CookieStore = {
async delete(
options: CookieStoreDeleteOptions['name'] | CookieStoreDeleteOptions
): Promise<void> {
const { name, domain } = sanitizeOptions(
options
) as CookieStoreDeleteOptions;
const { name, domain } = sanitizeOptions<CookieStoreDeleteOptions>(options);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const { value } = (await this.get(name))!;
const serializedValue = serialize(name, value, {
Expand Down
8 changes: 7 additions & 1 deletion test/karma.conf.cjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
module.exports = function (config) {
config.set({
files: [
// Include the compiled library
{ pattern: '../dist/index.js', type: 'module' },
{ pattern: './*.tests.js', type: 'module' }
// Set up test environment to be able to run WPT tests
{ pattern: './wpt-setup/*.js', type: 'module' },
// Our tests
{ pattern: './index.tests.js', type: 'module' },
// Web Platform Tests
{ pattern: './wpt/*.js', type: 'module' }
],
plugins: ['karma-*'],
reporters: ['progress'],
Expand Down
33 changes: 33 additions & 0 deletions test/wpt-setup/harness.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* global assert */

window.promise_test = async (fn, name) => {
const cleanups = [];
const testCase = {
name,
add_cleanup(fn) {
cleanups.push(fn);
},
};
it(name, async () => {
await fn(testCase);
for (const cleanup of cleanups) {
cleanup();
}
});
};

window.promise_rejects_js = async (testCase, expectedError, promise) => {
try {
await promise;
} catch (error) {
if (error.name !== expectedError.name) {
assert.fail(
`${testCase.name}: Promise rejected with ${error.name}, expected ${expectedError.name}`
);
}
return;
}
assert.fail(`${testCase.name}: Promise didn't reject when it should have.`);
};

window.assert_equals = assert.equal;
13 changes: 13 additions & 0 deletions test/wpt-setup/serviceworker_cookieStore_cross_origin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
self.GLOBAL = {
isWindow: () => false,
isWorker: () => false,
};

self.addEventListener('message', async event => {
if (event.data.op === 'get-cookies') {
const workerCookies = await cookieStore.getAll();
event.ports[0].postMessage({ workerCookies }, {
domain: event.origin,
});
}
});
107 changes: 107 additions & 0 deletions test/wpt/cookieStore_get_arguments.tentative.https.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// META: title=Cookie Store API: cookieStore.get() arguments
// META: global=window,serviceworker

'use strict';

promise_test(async (testCase) => {
await cookieStore.set('cookie-name', 'cookie-value');
testCase.add_cleanup(async () => {
await cookieStore.delete('cookie-name');
});

await promise_rejects_js(testCase, TypeError, cookieStore.get());
}, 'cookieStore.get with no arguments returns TypeError');

promise_test(async (testCase) => {
await cookieStore.set('cookie-name', 'cookie-value');
testCase.add_cleanup(async () => {
await cookieStore.delete('cookie-name');
});

await promise_rejects_js(testCase, TypeError, cookieStore.get({}));
}, 'cookieStore.get with empty options returns TypeError');

promise_test(async (testCase) => {
await cookieStore.set('cookie-name', 'cookie-value');
testCase.add_cleanup(async () => {
await cookieStore.delete('cookie-name');
});

const cookie = await cookieStore.get('cookie-name');
assert_equals(cookie.name, 'cookie-name');
assert_equals(cookie.value, 'cookie-value');
}, 'cookieStore.get with positional name');

promise_test(async (testCase) => {
await cookieStore.set('cookie-name', 'cookie-value');
testCase.add_cleanup(async () => {
await cookieStore.delete('cookie-name');
});

const cookie = await cookieStore.get({ name: 'cookie-name' });
assert_equals(cookie.name, 'cookie-name');
assert_equals(cookie.value, 'cookie-value');
}, 'cookieStore.get with name in options');

promise_test(async (testCase) => {
await cookieStore.set('cookie-name', 'cookie-value');
testCase.add_cleanup(async () => {
await cookieStore.delete('cookie-name');
});

const cookie = await cookieStore.get('cookie-name', {
name: 'wrong-cookie-name',
});
assert_equals(cookie.name, 'cookie-name');
assert_equals(cookie.value, 'cookie-value');
}, 'cookieStore.get with name in both positional arguments and options');

promise_test(async (testCase) => {
await cookieStore.set('cookie-name', 'cookie-value');
testCase.add_cleanup(async () => {
await cookieStore.delete('cookie-name');
});

let target_url = self.location.href;
if (self.GLOBAL.isWorker()) {
target_url = target_url + '/path/within/scope';
}

const cookie = await cookieStore.get({ url: target_url });
assert_equals(cookie.name, 'cookie-name');
assert_equals(cookie.value, 'cookie-value');
}, 'cookieStore.get with absolute url in options');

promise_test(async (testCase) => {
await cookieStore.set('cookie-name', 'cookie-value');
testCase.add_cleanup(async () => {
await cookieStore.delete('cookie-name');
});

let target_path = self.location.pathname;
if (self.GLOBAL.isWorker()) {
target_path = target_path + '/path/within/scope';
}

const cookie = await cookieStore.get({ url: target_path });
assert_equals(cookie.name, 'cookie-name');
assert_equals(cookie.value, 'cookie-value');
}, 'cookieStore.get with relative url in options');

promise_test(async (testCase) => {
const invalid_url = `${self.location.protocol}//${self.location.host}/different/path`;
await promise_rejects_js(
testCase,
TypeError,
cookieStore.get({ url: invalid_url })
);
}, 'cookieStore.get with invalid url path in options');

promise_test(async (testCase) => {
const invalid_url = `${self.location.protocol}//www.example.com${self.location.pathname}`;
await promise_rejects_js(
testCase,
TypeError,
cookieStore.get({ url: invalid_url })
);
}, 'cookieStore.get with invalid url host in options');

0 comments on commit 9f603bc

Please sign in to comment.