Skip to content

Commit

Permalink
Reference implementation: migrate to JSON-based tests
Browse files Browse the repository at this point in the history
In order to share common structure with Web Platform Tests, this PR migrates hard-coded Jest tests into JSON test metadata (__tests__/json/*.json, which are shared with
https://github.com/web-platform-tests/wpt/tree/master/import-maps/common/resources)
and test runners (__tests__/helpers/, which are Jest-specific).

In the future we will likely use a script to download the json/ directory and not check it in to this repository, in order to maintain a single source of truth in the web platform tests repository.
  • Loading branch information
hiroshige-g authored Jan 29, 2020
1 parent 71c80b5 commit 6cb173d
Show file tree
Hide file tree
Showing 28 changed files with 1,486 additions and 1,095 deletions.
129 changes: 129 additions & 0 deletions reference-implementation/__tests__/helpers/common-test-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
'use strict';
const assert = require('assert');
const { URL } = require('url');
const { parseFromString } = require('../../lib/parser.js');
const { resolve } = require('../../lib/resolver.js');

function assertNoExtraProperties(object, expectedProperties, description) {
for (const actualProperty in object) {
assert(
expectedProperties.indexOf(actualProperty) !== -1,
description + ': unexpected property ' + actualProperty
);
}
}

function assertOwnProperty(j, name) {
assert(name in j);
}

// Parsed import maps in the reference implementation uses `URL`s instead of
// strings as the values of specifier maps, while
// expected import maps (taken from JSONs) uses strings.
// This function converts `m` (expected import maps or its part)
// into URL-based, for comparison.
function replaceStringWithURL(m) {
if (typeof m === 'string') {
return new URL(m);
}
if (m === null || typeof m !== 'object') {
return m;
}

const result = {};
for (const key in m) {
result[key] = replaceStringWithURL(m[key]);
}
return result;
}

function runTests(j) {
const { tests } = j;
delete j.tests;

if ('importMap' in j) {
assertOwnProperty(j, 'importMap');
assertOwnProperty(j, 'importMapBaseURL');
try {
j.parsedImportMap = parseFromString(
JSON.stringify(j.importMap),
new URL(j.importMapBaseURL)
);
} catch (e) {
j.parsedImportMap = e;
}
delete j.importMap;
delete j.importMapBaseURL;
}

assertNoExtraProperties(
j,
[
'expectedResults', 'expectedParsedImportMap',
'baseURL', 'name', 'parsedImportMap',
'importMap', 'importMapBaseURL',
'link', 'details'
],
j.name
);

if (tests) {
// Nested node.
for (const testName in tests) {
let fullTestName = testName;
if (j.name) {
fullTestName = j.name + ': ' + testName;
}
tests[testName].name = fullTestName;
const k = Object.assign(Object.assign({}, j), tests[testName]);
runTests(k);
}
} else {
// Leaf node.
for (const key of ['parsedImportMap', 'name']) {
assertOwnProperty(j, key, j.name);
}
assert(
'expectedResults' in j ||
'expectedParsedImportMap' in j,
'expectedResults or expectedParsedImportMap should exist'
);

// Resolution tests.
if ('expectedResults' in j) {
it(j.name, () => {
assertOwnProperty(j, 'baseURL');
describe(
'Import map registration should be successful for resolution tests',
() => {
expect(j.parsedImportMap).not.toBeInstanceOf(Error);
}
);

for (const specifier in j.expectedResults) {
const expected = j.expectedResults[specifier];
if (expected === null) {
expect(() => resolve(specifier, j.parsedImportMap, new URL(j.baseURL))).toThrow(TypeError);
} else {
// Should be resolved to `expected`.
expect(resolve(specifier, j.parsedImportMap, new URL(j.baseURL))).toMatchURL(expected);
}
}
});
}

// Parsing tests.
if ('expectedParsedImportMap' in j) {
it(j.name, () => {
if (j.expectedParsedImportMap === null) {
expect(j.parsedImportMap).toBeInstanceOf(TypeError);
} else {
expect(j.parsedImportMap)
.toEqual(replaceStringWithURL(j.expectedParsedImportMap));
}
});
}
}
}

exports.runTests = runTests;
59 changes: 0 additions & 59 deletions reference-implementation/__tests__/helpers/parsing.js

This file was deleted.

17 changes: 17 additions & 0 deletions reference-implementation/__tests__/json/data-base-url.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"importMap": {
"imports": {
"foo/": "data:text/javascript,foo/"
}
},
"importMapBaseURL": "https://example.com/app/index.html",
"baseURL": "https://example.com/js/app.mjs",
"name": "data: base URL (?)",
"tests": {
"should favor the most-specific key": {
"expectedResults": {
"foo/bar": null
}
}
}
}
56 changes: 56 additions & 0 deletions reference-implementation/__tests__/json/empty-import-map.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"importMap": {},
"importMapBaseURL": "https://example.com/app/index.html",
"baseURL": "https://example.com/js/app.mjs",
"tests": {
"valid relative specifiers": {
"expectedResults": {
"./foo": "https://example.com/js/foo",
"./foo/bar": "https://example.com/js/foo/bar",
"./foo/../bar": "https://example.com/js/bar",
"./foo/../../bar": "https://example.com/bar",
"../foo": "https://example.com/foo",
"../foo/bar": "https://example.com/foo/bar",
"../../../foo/bar": "https://example.com/foo/bar",
"/foo": "https://example.com/foo",
"/foo/bar": "https://example.com/foo/bar",
"/../../foo/bar": "https://example.com/foo/bar",
"/../foo/../bar": "https://example.com/bar"
}
},
"fetch scheme absolute URLs": {
"expectedResults": {
"about:fetch-scheme": "about:fetch-scheme",
"https://fetch-scheme.net": "https://fetch-scheme.net/",
"https:fetch-scheme.org": "https://fetch-scheme.org/",
"https://fetch%2Dscheme.com/": "https://fetch-scheme.com/",
"https://///fetch-scheme.com///": "https://fetch-scheme.com///"
}
},
"non-fetch scheme absolute URLs": {
"expectedResults": {
"mailto:non-fetch-scheme": "mailto:non-fetch-scheme",
"import:non-fetch-scheme": "import:non-fetch-scheme",
"javascript:non-fetch-scheme": "javascript:non-fetch-scheme",
"wss:non-fetch-scheme": "wss://non-fetch-scheme/"
}
},
"valid relative URLs that are invalid as specifiers should fail": {
"expectedResults": {
"invalid-specifier": null,
"\\invalid-specifier": null,
":invalid-specifier": null,
"@invalid-specifier": null,
"%2E/invalid-specifier": null,
"%2E%2E/invalid-specifier": null,
".%2Finvalid-specifier": null
}
},
"invalid absolute URLs should fail": {
"expectedResults": {
"https://invalid-url.com:demo": null,
"http://[invalid-url.com]/": null
}
}
}
}
25 changes: 25 additions & 0 deletions reference-implementation/__tests__/json/overlapping-entries.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"importMapBaseURL": "https://example.com/app/index.html",
"baseURL": "https://example.com/js/app.mjs",
"name": "should favor the most-specific key",
"tests": {
"Overlapping entries with trailing slashes": {
"importMap": {
"imports": {
"a": "/1",
"a/": "/2/",
"a/b": "/3",
"a/b/": "/4/"
}
},
"expectedResults": {
"a": "https://example.com/1",
"a/": "https://example.com/2/",
"a/x": "https://example.com/2/x",
"a/b": "https://example.com/3",
"a/b/": "https://example.com/4/",
"a/b/c": "https://example.com/4/c"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"importMap": {
"imports": {
"moment": "/node_modules/moment/src/moment.js",
"moment/": "/node_modules/moment/src/",
"lodash-dot": "./node_modules/lodash-es/lodash.js",
"lodash-dot/": "./node_modules/lodash-es/",
"lodash-dotdot": "../node_modules/lodash-es/lodash.js",
"lodash-dotdot/": "../node_modules/lodash-es/"
}
},
"importMapBaseURL": "https://example.com/app/index.html",
"baseURL": "https://example.com/js/app.mjs",
"name": "Package-like scenarios",
"link": "https://github.com/WICG/import-maps#packages-via-trailing-slashes",
"tests": {
"package main modules": {
"expectedResults": {
"moment": "https://example.com/node_modules/moment/src/moment.js",
"lodash-dot": "https://example.com/app/node_modules/lodash-es/lodash.js",
"lodash-dotdot": "https://example.com/node_modules/lodash-es/lodash.js"
}
},
"package submodules": {
"expectedResults": {
"moment/foo": "https://example.com/node_modules/moment/src/foo",
"lodash-dot/foo": "https://example.com/app/node_modules/lodash-es/foo",
"lodash-dotdot/foo": "https://example.com/node_modules/lodash-es/foo"
}
},
"package names that end in a slash should just pass through": {
"expectedResults": {
"moment/": "https://example.com/node_modules/moment/src/"
}
},
"package modules that are not declared should fail": {
"expectedResults": {
"underscore/": null,
"underscore/foo": null
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"name": "Absolute URL addresses",
"tests": {
"should only accept absolute URL addresses with fetch schemes": {
"importMap": {
"imports": {
"about": "about:good",
"blob": "blob:good",
"data": "data:good",
"file": "file:///good",
"filesystem": "filesystem:http://example.com/good/",
"http": "http://good/",
"https": "https://good/",
"ftp": "ftp://good/",
"import": "import:bad",
"mailto": "mailto:bad",
"javascript": "javascript:bad",
"wss": "wss:bad"
}
},
"importMapBaseURL": "https://base.example/path1/path2/path3",
"expectedParsedImportMap": {
"imports": {
"about": "about:good",
"blob": "blob:good",
"data": "data:good",
"file": "file:///good",
"filesystem": "filesystem:http://example.com/good/",
"http": "http://good/",
"https": "https://good/",
"ftp": "ftp://good/",
"import": "import:bad",
"javascript": "javascript:bad",
"mailto": "mailto:bad",
"wss": "wss://bad/"
},
"scopes": {}
}
},
"should parse absolute URLs, ignoring unparseable ones": {
"importMap": {
"imports": {
"unparseable2": "https://example.com:demo",
"unparseable3": "http://[www.example.com]/",
"invalidButParseable1": "https:example.org",
"invalidButParseable2": "https://///example.com///",
"prettyNormal": "https://example.net",
"percentDecoding": "https://ex%41mple.com/"
}
},
"importMapBaseURL": "https://base.example/path1/path2/path3",
"expectedParsedImportMap": {
"imports": {
"invalidButParseable1": "https://example.org/",
"invalidButParseable2": "https://example.com///",
"prettyNormal": "https://example.net/",
"percentDecoding": "https://example.com/"
},
"scopes": {}
}
}
}
}
Loading

0 comments on commit 6cb173d

Please sign in to comment.