Commit 80b14936 authored by Hiroshige Hayashizaki's avatar Hiroshige Hayashizaki Committed by Commit Bot

[Import Maps] Update resolving tests from upstream

This CL imports more tests from upstream:

- Test files from
  https://github.com/WICG/import-maps/tree/master/reference-implementation/__tests__
  are imported to import-maps/imported/resources/.
- Generated test HTMLs are placed in import-maps/imported/.
- To support more tests, the glue code in resolving.tentative.html
  is moved into `jest-test-helper.js` and extended to support new tests.

The current test failures are added as -expected.txt, and will be
fixed in the future when import map functionalities are implemented or fixed.

Bug: 990561
Change-Id: I087df82355513584a0cf1891da648dd2155f53c8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1734024
Commit-Queue: Hiroshige Hayashizaki <hiroshige@chromium.org>
Reviewed-by: default avatarDomenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#686701}
parent 4b3acdaf
This is a testharness.js-based test.
PASS Relative URL-like addresses / should accept strings prefixed with ./, ../, or /
FAIL Relative URL-like addresses / should not accept strings prefixed with ./, ../, or / for data: base URLs assert_equals: expected "{\"dotDotSlash\":[],\"dotSlash\":[],\"slash\":[]}" but got "{\"dotDotSlash\":[\"http://web-platform.test:8001/import-maps/foo\"],\"dotSlash\":[\"http://web-platform.test:8001/import-maps/imported/foo\"],\"slash\":[\"http://web-platform.test:8001/foo\"]}"
PASS Relative URL-like addresses / should accept the literal strings ./, ../, or / with no suffix
PASS Relative URL-like addresses / should ignore percent-encoded variants of ./, ../, or /
PASS Built-in module addresses / should accept URLs using the built-in module scheme
PASS Built-in module addresses / should ignore percent-encoded variants of the built-in module scheme
FAIL Built-in module addresses / should allow built-in module URLs that contain "/" or "\" assert_equals: expected "{\"backslash\":[\"std:foo\\\baz\"],\"slashEnd\":[\"std:foo/\"],\"slashMiddle\":[\"std:foo/bar\"]}" but got "{\"backslash\":[\"std:foo\\baz\"],\"slashEnd\":[\"std:foo/\"],\"slashMiddle\":[\"std:foo/bar\"]}"
FAIL Absolute URL addresses / should only accept absolute URL addresses with fetch schemes assert_equals: expected "{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[\"filesystem:good\"],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[],\"javascript\":[],\"mailto\":[],\"wss\":[]}" but got "{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[\"import:bad\"],\"javascript\":[\"javascript:bad\"],\"mailto\":[\"mailto:bad\"],\"wss\":[\"wss://bad/\"]}"
FAIL Absolute URL addresses / should only accept absolute URL addresses with fetch schemes inside arrays assert_equals: expected "{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[\"filesystem:good\"],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[],\"javascript\":[],\"mailto\":[],\"wss\":[]}" but got "{\"about\":[\"about:good\"],\"blob\":[\"blob:good\"],\"data\":[\"data:good\"],\"file\":[\"file:///good\"],\"filesystem\":[],\"ftp\":[\"ftp://good/\"],\"http\":[\"http://good/\"],\"https\":[\"https://good/\"],\"import\":[\"import:bad\"],\"javascript\":[\"javascript:bad\"],\"mailto\":[\"mailto:bad\"],\"wss\":[\"wss://bad/\"]}"
FAIL Absolute URL addresses / should parse absolute URLs, ignoring unparseable ones assert_equals: expected "{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/%41\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[],\"unparseable2\":[],\"unparseable3\":[]}" but got "{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/A\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[\"https://ex%20ample.org/\"],\"unparseable2\":[],\"unparseable3\":[]}"
FAIL Absolute URL addresses / should parse absolute URLs, ignoring unparseable ones inside arrays assert_equals: expected "{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/%41\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[],\"unparseable2\":[],\"unparseable3\":[]}" but got "{\"invalidButParseable1\":[\"https://example.org/\"],\"invalidButParseable2\":[\"https://example.com///\"],\"noPercentDecoding\":[\"https://example.com/A\"],\"percentDecoding\":[\"https://example.com/\"],\"prettyNormal\":[\"https://example.net/\"],\"unparseable1\":[\"https://ex%20ample.org/\"],\"unparseable2\":[],\"unparseable3\":[]}"
FAIL Failing addresses: mismatched trailing slashes / should warn for the simple case assert_equals: expected "{\"std:trailer/\":[],\"trailer/\":[]}" but got "{\"trailer/\":[\"https://base.example/notrailer\"]}"
FAIL Failing addresses: mismatched trailing slashes / should warn for a mismatch alone in an array assert_equals: expected "{\"std:trailer/\":[],\"trailer/\":[]}" but got "{\"trailer/\":[\"https://base.example/notrailer\"]}"
FAIL Failing addresses: mismatched trailing slashes / should warn for a mismatch alongside non-mismatches in an array assert_equals: expected "{\"std:trailer/\":[\"https://base.example/bim-atrailer/\"],\"trailer/\":[\"https://base.example/atrailer/\"]}" but got "{\"trailer/\":[]}"
PASS Other invalid addresses / should ignore unprefixed strings that are not absolute URLs
Harness: the test ran to completion.
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/jest-test-helper.js"></script>
<script type="module" src="resources/helpers/parsing.js"></script>
<!--
Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/parsing-addresses.js
-->
<script type="module" src="resources/parsing-addresses.js"></script>
This is a testharness.js-based test.
FAIL Invalid JSON assert_throws: function "() => parseFromString('{ imports: {} }', 'https://base.example/')" did not throw
FAIL Mismatching the top-level schema / should throw for top-level non-objects assert_throws: function "() => parseFromString(input, baseURL)" did not throw
FAIL Mismatching the top-level schema / should throw if imports is a non-object assert_throws: function "() => parseFromString(input, baseURL)" did not throw
FAIL Mismatching the top-level schema / should throw if scopes is a non-object assert_throws: function "() => parseFromString(input, baseURL)" did not throw
FAIL Mismatching the top-level schema / should ignore unspecified top-level entries assert_object_equals: expected property "0" missing
FAIL Mismatching the specifier map schema / should ignore entries where the address is not a string, array, or null assert_equals: expected "{\"bar\":[\"https://example.com/\"]}" but got "{\"bar\":[\"https://example.com/\"],\"foo\":[]}"
PASS Mismatching the specifier map schema / should ignore entries where the specifier key is an empty string
PASS Mismatching the specifier map schema / should ignore members of an address array that are not strings
FAIL Mismatching the specifier map schema / should throw if a scope's value is not an object assert_throws: function "() => parseFromString(input, baseURL)" did not throw
PASS Normalization / should normalize empty import maps to have imports and scopes keys
PASS Normalization / should normalize an import map without imports to have imports
PASS Normalization / should normalize an import map without scopes to have scopes
PASS Normalization / should normalize addresses to arrays
Harness: the test ran to completion.
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/jest-test-helper.js"></script>
<script type="module" src="resources/helpers/parsing.js"></script>
<!--
Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/parsing-schema.js
-->
<script type="module" src="resources/parsing-schema.js"></script>
This is a testharness.js-based test.
PASS Relative URL scope keys / should work with no prefix
PASS Relative URL scope keys / should work with ./, ../, and / prefixes
PASS Relative URL scope keys / should work with /s, ?s, and #s
PASS Relative URL scope keys / should work with an empty string scope key
PASS Relative URL scope keys / should work with / suffixes
PASS Relative URL scope keys / should deduplicate based on URL parsing rules
FAIL Absolute URL scope keys / should only accept absolute URL scope keys with fetch schemes assert_object_equals: expected property "0" missing
FAIL Absolute URL scope keys / should parse absolute URL scope keys, ignoring unparseable ones assert_object_equals: expected property "0" missing
FAIL Absolute URL scope keys / should ignore relative URL scope keys when the base URL is a data: URL assert_object_equals: expected property "0" missing
Harness: the test ran to completion.
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/jest-test-helper.js"></script>
<script type="module" src="resources/helpers/parsing.js"></script>
<!--
Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/parsing-scope-keys.js
-->
<script type="module" src="resources/parsing-scope-keys.js"></script>
This is a testharness.js-based test.
PASS Relative URL-like specifier keys / should absolutize strings prefixed with ./, ../, or / into the corresponding URLs
FAIL Relative URL-like specifier keys / should not absolutize strings prefixed with ./, ../, or / with a data: URL base assert_equals: expected "{\"../foo\":[\"https://example.com/dotdotslash\"],\"./foo\":[\"https://example.com/dotslash\"],\"/foo\":[\"https://example.com/slash\"]}" but got "{\"http://web-platform.test:8001/foo\":[\"https://example.com/slash\"],\"http://web-platform.test:8001/import-maps/foo\":[\"https://example.com/dotdotslash\"],\"http://web-platform.test:8001/import-maps/imported/foo\":[\"https://example.com/dotslash\"]}"
PASS Relative URL-like specifier keys / should absolutize the literal strings ./, ../, or / with no suffix
PASS Relative URL-like specifier keys / should treat percent-encoded variants of ./, ../, or / as bare specifiers
FAIL Absolute URL specifier keys / should only accept absolute URL specifier keys with fetch schemes, treating others as bare specifiers assert_equals: expected "{\"about:good\":[\"https://base.example/about\"],\"blob:good\":[\"https://base.example/blob\"],\"data:good\":[\"https://base.example/data\"],\"file:///good\":[\"https://base.example/file\"],\"filesystem:good\":[\"https://base.example/filesystem\"],\"ftp://good/\":[\"https://base.example/ftp/\"],\"http://good/\":[\"https://base.example/http/\"],\"https://good/\":[\"https://base.example/https/\"],\"import:bad\":[\"https://base.example/import\"],\"javascript:bad\":[\"https://base.example/javascript\"],\"mailto:bad\":[\"https://base.example/mailto\"],\"wss:bad\":[\"https://base.example/wss\"]}" but got "{\"about:good\":[\"https://base.example/about\"],\"blob:good\":[\"https://base.example/blob\"],\"data:good\":[\"https://base.example/data\"],\"file:///good\":[\"https://base.example/file\"],\"filesystem:good\":[\"https://base.example/filesystem\"],\"ftp://good/\":[\"https://base.example/ftp/\"],\"http://good/\":[\"https://base.example/http/\"],\"https://good/\":[\"https://base.example/https/\"]}"
FAIL Absolute URL specifier keys / should parse absolute URLs, treating unparseable ones as bare specifiers assert_equals: expected "{\"http://[www.example.com]/\":[\"https://base.example/unparseable3/\"],\"https://ex ample.org/\":[\"https://base.example/unparseable1/\"],\"https://example.com/\":[\"https://base.example/percentDecoding/\"],\"https://example.com/%41\":[\"https://base.example/noPercentDecoding\"],\"https://example.com///\":[\"https://base.example/invalidButParseable2/\"],\"https://example.com:demo\":[\"https://base.example/unparseable2\"],\"https://example.net/\":[\"https://base.example/prettyNormal/\"],\"https://example.org/\":[\"https://base.example/invalidButParseable1/\"]}" but got "{\"http://[www.example.com]/\":[\"https://base.example/unparseable3/\"],\"https://ex%20ample.org/\":[\"https://base.example/unparseable1/\"],\"https://example.com/\":[\"https://base.example/percentDecoding/\"],\"https://example.com///\":[\"https://base.example/invalidButParseable2/\"],\"https://example.com/A\":[\"https://base.example/noPercentDecoding\"],\"https://example.com:demo\":[\"https://base.example/unparseable2\"],\"https://example.net/\":[\"https://base.example/prettyNormal/\"],\"https://example.org/\":[\"https://base.example/invalidButParseable1/\"]}"
FAIL Absolute URL specifier keys / should parse built-in module specifier keys, including with a "/" assert_equals: expected "{\"std:blank\":[\"https://base.example/blank\"],\"std:blank/\":[\"https://base.example/blank/\"],\"std:blank/foo\":[\"https://base.example/blank/foo\"],\"std:blank\\\foo\":[\"https://base.example/blank/backslashfoo\"]}" but got "{}"
Harness: the test ran to completion.
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/jest-test-helper.js"></script>
<script type="module" src="resources/helpers/parsing.js"></script>
<!--
Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/parsing-specifier-keys.js
-->
<script type="module" src="resources/parsing-specifier-keys.js"></script>
This is a testharness.js-based test.
PASS Unmapped built-in module specifiers / should resolve "std:blank" to "std:blank"
FAIL Unmapped built-in module specifiers / should error resolving "std:none" assert_throws: function "() => resolveUnderTest(NONE)" did not throw
FAIL Remapping built-in module specifiers / should remap built-in modules assert_equals: expected "https://example.com/app/blank.mjs" but got "std:blank"
FAIL Remapping built-in module specifiers / should remap built-in modules with slashes assert_equals: expected "https://example.com/app/blank-slash/" but got "std:blank/"
FAIL Remapping built-in module specifiers / should remap built-in modules with fallbacks assert_equals: expected "https://example.com/app/none.mjs" but got "std:none"
FAIL Remapping built-in module specifiers / should remap built-in modules with slashes and fallbacks assert_equals: expected "https://example.com/app/blank/" but got "std:blank/"
PASS Remapping to built-in modules / should remap to "std:blank"
PASS Remapping to built-in modules / should fail when remapping to "std:blank/"
FAIL Remapping to built-in modules / should remap to "std:blank/for-testing" assert_equals: expected "std:blank/for-testing" but got "https://example.com/blank/for-testing"
PASS Remapping to built-in modules / should remap to "std:blank" for URL-like specifiers
PASS Remapping to built-in modules / should fail when remapping to "std:none"
FAIL Fallbacks with built-in module addresses / should resolve to "std:blank" Failed to resolve module specifier blank: Import Map: "blank" matches with "blank" but fails to be mapped (no viable URLs)
FAIL Fallbacks with built-in module addresses / should fall back past "std:none" Failed to resolve module specifier none: Import Map: "none" matches with "none" but fails to be mapped (no viable URLs)
Harness: the test ran to completion.
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/jest-test-helper.js"></script>
<script type="module" src="resources/helpers/parsing.js"></script>
<!--
Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/resolving-builtins.js
-->
<script type="module" src="resources/resolving-builtins.js"></script>
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/jest-test-helper.js"></script>
<script type="module" src="resources/helpers/parsing.js"></script>
<!--
Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/resolving-not-yet-implemented.js
-->
<script type="module" src="resources/resolving-not-yet-implemented.js"></script>
This is a testharness.js-based test.
PASS Mapped using scope instead of "imports" / should fail when the mapping is to an empty array
FAIL Mapped using scope instead of "imports" / Exact vs. prefix based matching / should match correctly when both are in the map Failed to resolve module specifier moment: Relative references must start with either "/", "./", or "../".
FAIL Mapped using scope instead of "imports" / Exact vs. prefix based matching / should match correctly when only an exact match is in the map Failed to resolve module specifier moment: Relative references must start with either "/", "./", or "../".
FAIL Mapped using scope instead of "imports" / Exact vs. prefix based matching / should match correctly when only a prefix match is in the map Failed to resolve module specifier moment: Relative references must start with either "/", "./", or "../".
FAIL Mapped using scope instead of "imports" / Package-like scenarios / should resolve scoped assert_equals: expected "https://example.com/app/node_modules_2/lodash-es/lodash.js" but got "https://example.com/app/node_modules/lodash-es/lodash.js"
FAIL Mapped using scope instead of "imports" / Package-like scenarios / should apply best scope match assert_equals: expected "https://example.com/node_modules_3/moment/src/moment.js" but got "https://example.com/node_modules/moment/src/moment.js"
PASS Mapped using scope instead of "imports" / Package-like scenarios / should fallback to "imports"
PASS Mapped using scope instead of "imports" / Package-like scenarios / should still fail for package-like specifiers that are not declared
PASS Mapped using scope instead of "imports" / The scope inheritance example from the README / should fall back to "imports" when none match
FAIL Mapped using scope instead of "imports" / The scope inheritance example from the README / should use a direct scope override assert_equals: expected "https://example.com/a-2.mjs" but got "https://example.com/a-1.mjs"
FAIL Mapped using scope instead of "imports" / The scope inheritance example from the README / should use an indirect scope override assert_equals: expected "https://example.com/a-2.mjs" but got "https://example.com/a-1.mjs"
FAIL Mapped using scope instead of "imports" / Relative URL scope keys / should resolve an empty string scope using the import map URL assert_equals: expected "https://example.com/a-empty-string.mjs" but got "https://example.com/a-1.mjs"
FAIL Mapped using scope instead of "imports" / Relative URL scope keys / should resolve a ./ scope using the import map URL's directory assert_equals: expected "https://example.com/b-dot-slash.mjs" but got "https://example.com/b-1.mjs"
FAIL Mapped using scope instead of "imports" / Relative URL scope keys / should resolve a ../ scope using the import map URL's directory assert_equals: expected "https://example.com/c-dot-dot-slash.mjs" but got "https://example.com/c-1.mjs"
Harness: the test ran to completion.
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/jest-test-helper.js"></script>
<script type="module" src="resources/helpers/parsing.js"></script>
<!--
Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/resolving-scopes.js
-->
<script type="module" src="resources/resolving-scopes.js"></script>
...@@ -10,14 +10,17 @@ PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / ...@@ -10,14 +10,17 @@ PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios /
PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should work for package submodules PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should work for package submodules
PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should work for package names that end in a slash by just passing through PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should work for package names that end in a slash by just passing through
PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should still fail for package modules that are not declared PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should still fail for package modules that are not declared
PASS Mapped using the "imports" key only (no scopes) / Package-like scenarios / should fail for package submodules that map to nowhere
PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should work for explicitly-mapped specifiers that happen to have a slash PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should work for explicitly-mapped specifiers that happen to have a slash
PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should work when the specifier has punctuation PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should work when the specifier has punctuation
PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should fail for attempting to get a submodule of something not declared with a trailing slash PASS Mapped using the "imports" key only (no scopes) / Tricky specifiers / should fail for attempting to get a submodule of something not declared with a trailing slash
PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap to built-in modules
PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap to other URLs PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap to other URLs
PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should fail for URLs that remap to empty arrays PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should fail for URLs that remap to empty arrays
PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap URLs that are just composed from / and . PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap URLs that are just composed from / and .
FAIL Mapped using the "imports" key only (no scopes) / URL-like specifiers / should remap URLs that are prefix-matched by keys with trailing slashes assert_equals: expected "https://example.com/lib/url-trailing-slash/foo.mjs" but got "https://example.com/test/foo.mjs"
PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should use the last entry's address when URL-like specifiers parse to the same absolute URL PASS Mapped using the "imports" key only (no scopes) / URL-like specifiers / should use the last entry's address when URL-like specifiers parse to the same absolute URL
PASS Mapped using the "imports" key only (no scopes) / overlapping entries with trailing slashes / most-specific wins PASS Mapped using the "imports" key only (no scopes) / Overlapping entries with trailing slashes / should favor the most-specific key (no empty arrays)
PASS Mapped using the "imports" key only (no scopes) / Overlapping entries with trailing slashes / should favor the most-specific key when empty arrays are involved for less-specific keys
PASS Mapped using the "imports" key only (no scopes) / Overlapping entries with trailing slashes / should favor the most-specific key when empty arrays are involved for more-specific keys
Harness: the test ran to completion. Harness: the test ran to completion.
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/jest-test-helper.js"></script>
<script type="module" src="resources/helpers/parsing.js"></script>
<!--
Imported from https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/resolving.js
-->
<script type="module" src="resources/resolving.js"></script>
'use strict';
const { parseFromString } = require('../../lib/parser.js');
// Local modifications from upstream:
// Currently warnings and scopes are not checked in expectSpecifierMap().
exports.expectSpecifierMap = (input, baseURL, output, warnings = []) => {
expect(parseFromString(`{ "imports": ${input} }`, baseURL))
.toEqual({ imports: output, scopes: {} });
};
exports.expectScopes = (inputArray, baseURL, outputArray, warnings = []) => {
const checkWarnings = testWarningHandler(warnings);
const inputScopesAsStrings = inputArray.map(scopePrefix => `${JSON.stringify(scopePrefix)}: {}`);
const inputString = `{ "scopes": { ${inputScopesAsStrings.join(', ')} } }`;
const outputScopesObject = {};
for (const outputScopePrefix of outputArray) {
outputScopesObject[outputScopePrefix] = {};
}
expect(parseFromString(inputString, baseURL)).toEqual({ imports: {}, scopes: outputScopesObject });
checkWarnings();
};
exports.expectBad = (input, baseURL, warnings = []) => {
const checkWarnings = testWarningHandler(warnings);
expect(() => parseFromString(input, baseURL)).toThrow(TypeError);
checkWarnings();
};
exports.expectWarnings = (input, baseURL, output, warnings = []) => {
const checkWarnings = testWarningHandler(warnings);
expect(parseFromString(input, baseURL)).toEqual(output);
checkWarnings();
};
function testWarningHandler(expectedWarnings) {
const warnings = [];
const { warn } = console;
console.warn = warning => {
warnings.push(warning);
};
return () => {
console.warn = warn;
expect(warnings).toEqual(expectedWarnings);
};
}
'use strict';
const { parseFromString } = require('../lib/parser.js');
const { expectBad, expectWarnings, expectSpecifierMap } = require('./helpers/parsing.js');
const nonObjectStrings = ['null', 'true', '1', '"foo"', '[]'];
test('Invalid JSON', () => {
expect(() => parseFromString('{ imports: {} }', 'https://base.example/')).toThrow(SyntaxError);
});
describe('Mismatching the top-level schema', () => {
it('should throw for top-level non-objects', () => {
for (const nonObject of nonObjectStrings) {
expectBad(nonObject, 'https://base.example/');
}
});
it('should throw if imports is a non-object', () => {
for (const nonObject of nonObjectStrings) {
expectBad(`{ "imports": ${nonObject} }`, 'https://base.example/');
}
});
it('should throw if scopes is a non-object', () => {
for (const nonObject of nonObjectStrings) {
expectBad(`{ "scopes": ${nonObject} }`, 'https://base.example/');
}
});
it('should ignore unspecified top-level entries', () => {
expectWarnings(
`{
"imports": {},
"new-feature": {},
"scops": {}
}`,
'https://base.example/',
{ imports: {}, scopes: {} },
[
`Invalid top-level key "new-feature". Only "imports" and "scopes" can be present.`,
`Invalid top-level key "scops". Only "imports" and "scopes" can be present.`
]
);
});
});
describe('Mismatching the specifier map schema', () => {
const invalidAddressStrings = ['true', '1', '{}'];
const invalidInsideArrayStrings = ['null', 'true', '1', '{}', '[]'];
it('should ignore entries where the address is not a string, array, or null', () => {
for (const invalid of invalidAddressStrings) {
expectSpecifierMap(
`{
"foo": ${invalid},
"bar": ["https://example.com/"]
}`,
'https://base.example/',
{
bar: [expect.toMatchURL('https://example.com/')]
},
[
`Invalid address ${invalid} for the specifier key "foo". ` +
`Addresses must be strings, arrays, or null.`
]
);
}
});
it('should ignore entries where the specifier key is an empty string', () => {
expectSpecifierMap(
`{
"": ["https://example.com/"]
}`,
'https://base.example/',
{},
[`Invalid empty string specifier key.`]
);
});
it('should ignore members of an address array that are not strings', () => {
for (const invalid of invalidInsideArrayStrings) {
expectSpecifierMap(
`{
"foo": ["https://example.com/", ${invalid}],
"bar": ["https://example.com/"]
}`,
'https://base.example/',
{
foo: [expect.toMatchURL('https://example.com/')],
bar: [expect.toMatchURL('https://example.com/')]
},
[
`Invalid address ${invalid} inside the address array for the specifier key "foo". ` +
`Address arrays must only contain strings.`
]
);
}
});
it('should throw if a scope\'s value is not an object', () => {
for (const invalid of nonObjectStrings) {
expectBad(`{ "scopes": { "https://scope.example/": ${invalid} } }`, 'https://base.example/');
}
});
});
describe('Normalization', () => {
it('should normalize empty import maps to have imports and scopes keys', () => {
expect(parseFromString(`{}`, 'https://base.example/'))
.toEqual({ imports: {}, scopes: {} });
});
it('should normalize an import map without imports to have imports', () => {
expect(parseFromString(`{ "scopes": {} }`, 'https://base.example/'))
.toEqual({ imports: {}, scopes: {} });
});
it('should normalize an import map without scopes to have scopes', () => {
expect(parseFromString(`{ "imports": {} }`, 'https://base.example/'))
.toEqual({ imports: {}, scopes: {} });
});
it('should normalize addresses to arrays', () => {
expectSpecifierMap(
`{
"foo": "https://example.com/1",
"bar": ["https://example.com/2"],
"baz": null
}`,
'https://base.example/',
{
foo: [expect.toMatchURL('https://example.com/1')],
bar: [expect.toMatchURL('https://example.com/2')],
baz: []
}
);
});
});
'use strict';
const { expectScopes } = require('./helpers/parsing.js');
describe('Relative URL scope keys', () => {
it('should work with no prefix', () => {
expectScopes(
['foo'],
'https://base.example/path1/path2/path3',
['https://base.example/path1/path2/foo']
);
});
it('should work with ./, ../, and / prefixes', () => {
expectScopes(
['./foo', '../foo', '/foo'],
'https://base.example/path1/path2/path3',
[
'https://base.example/path1/path2/foo',
'https://base.example/path1/foo',
'https://base.example/foo'
]
);
});
it('should work with /s, ?s, and #s', () => {
expectScopes(
['foo/bar?baz#qux'],
'https://base.example/path1/path2/path3',
['https://base.example/path1/path2/foo/bar?baz#qux']
);
});
it('should work with an empty string scope key', () => {
expectScopes(
[''],
'https://base.example/path1/path2/path3',
['https://base.example/path1/path2/path3']
);
});
it('should work with / suffixes', () => {
expectScopes(
['foo/', './foo/', '../foo/', '/foo/', '/foo//'],
'https://base.example/path1/path2/path3',
[
'https://base.example/path1/path2/foo/',
'https://base.example/path1/path2/foo/',
'https://base.example/path1/foo/',
'https://base.example/foo/',
'https://base.example/foo//'
]
);
});
it('should deduplicate based on URL parsing rules', () => {
expectScopes(
['foo/\\', 'foo//', 'foo\\\\'],
'https://base.example/path1/path2/path3',
['https://base.example/path1/path2/foo//']
);
});
});
describe('Absolute URL scope keys', () => {
it('should only accept absolute URL scope keys with fetch schemes', () => {
expectScopes(
[
'about:good',
'blob:good',
'data:good',
'file:///good',
'filesystem:good',
'http://good/',
'https://good/',
'ftp://good/',
'import:bad',
'mailto:bad',
'javascript:bad',
'wss:ba'
],
'https://base.example/path1/path2/path3',
[
'about:good',
'blob:good',
'data:good',
'file:///good',
'filesystem:good',
'http://good/',
'https://good/',
'ftp://good/'
],
[
'Invalid scope "import:bad". Scope URLs must have a fetch scheme.',
'Invalid scope "mailto:bad". Scope URLs must have a fetch scheme.',
'Invalid scope "javascript:bad". Scope URLs must have a fetch scheme.',
'Invalid scope "wss://ba/". Scope URLs must have a fetch scheme.'
]
);
});
it('should parse absolute URL scope keys, ignoring unparseable ones', () => {
expectScopes(
[
'https://ex ample.org/',
'https://example.com:demo',
'http://[www.example.com]/',
'https:example.org',
'https://///example.com///',
'https://example.net',
'https://ex%41mple.com/foo/',
'https://example.com/%41'
],
'https://base.example/path1/path2/path3',
[
'https://base.example/path1/path2/example.org', // tricky case! remember we have a base URL
'https://example.com///',
'https://example.net/',
'https://example.com/foo/',
'https://example.com/%41'
],
[
'Invalid scope "https://ex ample.org/" (parsed against base URL "https://base.example/path1/path2/path3").',
'Invalid scope "https://example.com:demo" (parsed against base URL "https://base.example/path1/path2/path3").',
'Invalid scope "http://[www.example.com]/" (parsed against base URL "https://base.example/path1/path2/path3").'
]
);
});
it('should ignore relative URL scope keys when the base URL is a data: URL', () => {
expectScopes(
[
'./foo',
'../foo',
'/foo'
],
'data:text/html,test',
[],
[
'Invalid scope "./foo" (parsed against base URL "data:text/html,test").',
'Invalid scope "../foo" (parsed against base URL "data:text/html,test").',
'Invalid scope "/foo" (parsed against base URL "data:text/html,test").'
]
);
});
});
'use strict';
const { expectSpecifierMap } = require('./helpers/parsing.js');
const { BUILT_IN_MODULE_SCHEME } = require('../lib/utils.js');
const BLANK = `${BUILT_IN_MODULE_SCHEME}:blank`;
describe('Relative URL-like specifier keys', () => {
it('should absolutize strings prefixed with ./, ../, or / into the corresponding URLs', () => {
expectSpecifierMap(
`{
"./foo": "/dotslash",
"../foo": "/dotdotslash",
"/foo": "/slash"
}`,
'https://base.example/path1/path2/path3',
{
'https://base.example/path1/path2/foo': [expect.toMatchURL('https://base.example/dotslash')],
'https://base.example/path1/foo': [expect.toMatchURL('https://base.example/dotdotslash')],
'https://base.example/foo': [expect.toMatchURL('https://base.example/slash')]
}
);
});
it('should not absolutize strings prefixed with ./, ../, or / with a data: URL base', () => {
expectSpecifierMap(
`{
"./foo": "https://example.com/dotslash",
"../foo": "https://example.com/dotdotslash",
"/foo": "https://example.com/slash"
}`,
'data:text/html,test',
{
'./foo': [expect.toMatchURL('https://example.com/dotslash')],
'../foo': [expect.toMatchURL('https://example.com/dotdotslash')],
'/foo': [expect.toMatchURL('https://example.com/slash')]
}
);
});
it('should absolutize the literal strings ./, ../, or / with no suffix', () => {
expectSpecifierMap(
`{
"./": "/dotslash/",
"../": "/dotdotslash/",
"/": "/slash/"
}`,
'https://base.example/path1/path2/path3',
{
'https://base.example/path1/path2/': [expect.toMatchURL('https://base.example/dotslash/')],
'https://base.example/path1/': [expect.toMatchURL('https://base.example/dotdotslash/')],
'https://base.example/': [expect.toMatchURL('https://base.example/slash/')]
}
);
});
it('should treat percent-encoded variants of ./, ../, or / as bare specifiers', () => {
expectSpecifierMap(
`{
"%2E/": "/dotSlash1/",
"%2E%2E/": "/dotDotSlash1/",
".%2F": "/dotSlash2",
"..%2F": "/dotDotSlash2",
"%2F": "/slash2",
"%2E%2F": "/dotSlash3",
"%2E%2E%2F": "/dotDotSlash3"
}`,
'https://base.example/path1/path2/path3',
{
'%2E/': [expect.toMatchURL('https://base.example/dotSlash1/')],
'%2E%2E/': [expect.toMatchURL('https://base.example/dotDotSlash1/')],
'.%2F': [expect.toMatchURL('https://base.example/dotSlash2')],
'..%2F': [expect.toMatchURL('https://base.example/dotDotSlash2')],
'%2F': [expect.toMatchURL('https://base.example/slash2')],
'%2E%2F': [expect.toMatchURL('https://base.example/dotSlash3')],
'%2E%2E%2F': [expect.toMatchURL('https://base.example/dotDotSlash3')]
}
);
});
});
describe('Absolute URL specifier keys', () => {
it('should only accept absolute URL specifier keys with fetch schemes, treating others as bare specifiers', () => {
expectSpecifierMap(
`{
"about:good": "/about",
"blob:good": "/blob",
"data:good": "/data",
"file:///good": "/file",
"filesystem:good": "/filesystem",
"http://good/": "/http/",
"https://good/": "/https/",
"ftp://good/": "/ftp/",
"import:bad": "/import",
"mailto:bad": "/mailto",
"javascript:bad": "/javascript",
"wss:bad": "/wss"
}`,
'https://base.example/path1/path2/path3',
{
'about:good': [expect.toMatchURL('https://base.example/about')],
'blob:good': [expect.toMatchURL('https://base.example/blob')],
'data:good': [expect.toMatchURL('https://base.example/data')],
'file:///good': [expect.toMatchURL('https://base.example/file')],
'filesystem:good': [expect.toMatchURL('https://base.example/filesystem')],
'http://good/': [expect.toMatchURL('https://base.example/http/')],
'https://good/': [expect.toMatchURL('https://base.example/https/')],
'ftp://good/': [expect.toMatchURL('https://base.example/ftp/')],
'import:bad': [expect.toMatchURL('https://base.example/import')],
'mailto:bad': [expect.toMatchURL('https://base.example/mailto')],
'javascript:bad': [expect.toMatchURL('https://base.example/javascript')],
'wss:bad': [expect.toMatchURL('https://base.example/wss')]
}
);
});
it('should parse absolute URLs, treating unparseable ones as bare specifiers', () => {
expectSpecifierMap(
`{
"https://ex ample.org/": "/unparseable1/",
"https://example.com:demo": "/unparseable2",
"http://[www.example.com]/": "/unparseable3/",
"https:example.org": "/invalidButParseable1/",
"https://///example.com///": "/invalidButParseable2/",
"https://example.net": "/prettyNormal/",
"https://ex%41mple.com/": "/percentDecoding/",
"https://example.com/%41": "/noPercentDecoding"
}`,
'https://base.example/path1/path2/path3',
{
'https://ex ample.org/': [expect.toMatchURL('https://base.example/unparseable1/')],
'https://example.com:demo': [expect.toMatchURL('https://base.example/unparseable2')],
'http://[www.example.com]/': [expect.toMatchURL('https://base.example/unparseable3/')],
'https://example.org/': [expect.toMatchURL('https://base.example/invalidButParseable1/')],
'https://example.com///': [expect.toMatchURL('https://base.example/invalidButParseable2/')],
'https://example.net/': [expect.toMatchURL('https://base.example/prettyNormal/')],
'https://example.com/': [expect.toMatchURL('https://base.example/percentDecoding/')],
'https://example.com/%41': [expect.toMatchURL('https://base.example/noPercentDecoding')]
}
);
});
it('should parse built-in module specifier keys, including with a "/"', () => {
expectSpecifierMap(
`{
"${BLANK}": "/blank",
"${BLANK}/": "/blank/",
"${BLANK}/foo": "/blank/foo",
"${BLANK}\\\\foo": "/blank/backslashfoo"
}`,
'https://base.example/path1/path2/path3',
{
[BLANK]: [expect.toMatchURL('https://base.example/blank')],
[`${BLANK}/`]: [expect.toMatchURL('https://base.example/blank/')],
[`${BLANK}/foo`]: [expect.toMatchURL('https://base.example/blank/foo')],
[`${BLANK}\\foo`]: [expect.toMatchURL('https://base.example/blank/backslashfoo')]
}
);
});
});
'use strict';
const { URL } = require('url');
const { parseFromString } = require('../lib/parser.js');
const { resolve } = require('../lib/resolver.js');
const { BUILT_IN_MODULE_SCHEME } = require('../lib/utils.js');
const mapBaseURL = new URL('https://example.com/app/index.html');
const scriptURL = new URL('https://example.com/js/app.mjs');
const BLANK = `${BUILT_IN_MODULE_SCHEME}:blank`;
const NONE = `${BUILT_IN_MODULE_SCHEME}:none`;
function makeResolveUnderTest(mapString) {
const map = parseFromString(mapString, mapBaseURL);
return specifier => resolve(specifier, map, scriptURL);
}
describe('Unmapped built-in module specifiers', () => {
const resolveUnderTest = makeResolveUnderTest(`{}`);
it(`should resolve "${BLANK}" to "${BLANK}"`, () => {
expect(resolveUnderTest(BLANK)).toMatchURL(BLANK);
});
it(`should error resolving "${NONE}"`, () => {
expect(() => resolveUnderTest(NONE)).toThrow(TypeError);
});
});
describe('Remapping built-in module specifiers', () => {
it('should remap built-in modules', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"${BLANK}": "./blank.mjs",
"${NONE}": "./none.mjs"
}
}`);
expect(resolveUnderTest(BLANK)).toMatchURL('https://example.com/app/blank.mjs');
expect(resolveUnderTest(NONE)).toMatchURL('https://example.com/app/none.mjs');
});
it('should remap built-in modules with slashes', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"${BLANK}/": "./blank-slash/",
"${BLANK}/foo": "./blank-foo.mjs",
"${NONE}/": "./none-slash/",
"${NONE}/foo": "./none-foo.mjs"
}
}`);
expect(resolveUnderTest(`${BLANK}/`)).toMatchURL('https://example.com/app/blank-slash/');
expect(resolveUnderTest(`${BLANK}/foo`)).toMatchURL('https://example.com/app/blank-foo.mjs');
expect(resolveUnderTest(`${BLANK}/bar`)).toMatchURL('https://example.com/app/blank-slash/bar');
expect(resolveUnderTest(`${NONE}/`)).toMatchURL('https://example.com/app/none-slash/');
expect(resolveUnderTest(`${NONE}/foo`)).toMatchURL('https://example.com/app/none-foo.mjs');
expect(resolveUnderTest(`${NONE}/bar`)).toMatchURL('https://example.com/app/none-slash/bar');
});
it('should remap built-in modules with fallbacks', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"${BLANK}": ["${BLANK}", "./blank.mjs"],
"${NONE}": ["${NONE}", "./none.mjs"]
}
}`);
expect(resolveUnderTest(BLANK)).toMatchURL(BLANK);
expect(resolveUnderTest(NONE)).toMatchURL('https://example.com/app/none.mjs');
});
it('should remap built-in modules with slashes and fallbacks', () => {
// NOTE: `${BLANK}/for-testing` is not per spec, just for these tests.
// See resolver.js.
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"${BLANK}/": ["${BLANK}/", "./blank/"],
"${BLANK}/for-testing": ["${BLANK}/for-testing", "./blank-for-testing-special"],
"${NONE}/": ["${NONE}/", "./none/"],
"${NONE}/foo": ["${NONE}/foo", "./none-foo-special"]
}
}`);
// Built-in modules only resolve for exact matches, so this will trigger the fallback.
expect(resolveUnderTest(`${BLANK}/`)).toMatchURL('https://example.com/app/blank/');
expect(resolveUnderTest(`${BLANK}/foo`)).toMatchURL('https://example.com/app/blank/foo');
// This would fall back in a real implementation; it's only because we've gone against
// spec in the reference implementation (to make this testable) that this maps.
expect(resolveUnderTest(`${BLANK}/for-testing`)).toMatchURL(`${BLANK}/for-testing`);
expect(resolveUnderTest(`${NONE}/`)).toMatchURL('https://example.com/app/none/');
expect(resolveUnderTest(`${NONE}/bar`)).toMatchURL('https://example.com/app/none/bar');
expect(resolveUnderTest(`${NONE}/foo`)).toMatchURL('https://example.com/app/none-foo-special');
});
});
describe('Remapping to built-in modules', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"blank": "${BLANK}",
"/blank": "${BLANK}",
"/blank/": "${BLANK}/",
"/blank-for-testing": "${BLANK}/for-testing",
"none": "${NONE}",
"/none": "${NONE}"
}
}`);
it(`should remap to "${BLANK}"`, () => {
expect(resolveUnderTest('blank')).toMatchURL(BLANK);
expect(resolveUnderTest('/blank')).toMatchURL(BLANK);
});
it(`should fail when remapping to "${BLANK}/"`, () => {
expect(() => resolveUnderTest('/blank/')).toThrow(TypeError);
});
it(`should remap to "${BLANK}/for-testing"`, () => {
expect(resolveUnderTest('/blank/for-testing')).toMatchURL(`${BLANK}/for-testing`);
expect(resolveUnderTest('/blank-for-testing')).toMatchURL(`${BLANK}/for-testing`);
});
it(`should remap to "${BLANK}" for URL-like specifiers`, () => {
expect(resolveUnderTest('/blank')).toMatchURL(BLANK);
expect(resolveUnderTest('https://example.com/blank')).toMatchURL(BLANK);
expect(resolveUnderTest('https://///example.com/blank')).toMatchURL(BLANK);
});
it(`should fail when remapping to "${NONE}"`, () => {
expect(() => resolveUnderTest('none')).toThrow(TypeError);
expect(() => resolveUnderTest('/none')).toThrow(TypeError);
});
});
describe('Fallbacks with built-in module addresses', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"blank": [
"${BLANK}",
"./blank-fallback.mjs"
],
"none": [
"${NONE}",
"./none-fallback.mjs"
]
}
}`);
it(`should resolve to "${BLANK}"`, () => {
expect(resolveUnderTest('blank')).toMatchURL(BLANK);
});
it(`should fall back past "${NONE}"`, () => {
expect(resolveUnderTest('none')).toMatchURL('https://example.com/app/none-fallback.mjs');
});
});
'use strict';
const { URL } = require('url');
const { parseFromString } = require('../lib/parser.js');
const { resolve } = require('../lib/resolver.js');
const { BUILT_IN_MODULE_SCHEME } = require('../lib/utils.js');
const mapBaseURL = new URL('https://example.com/app/index.html');
const scriptURL = new URL('https://example.com/js/app.mjs');
const BLANK = `${BUILT_IN_MODULE_SCHEME}:blank`;
function makeResolveUnderTest(mapString) {
const map = parseFromString(mapString, mapBaseURL);
return specifier => resolve(specifier, map, scriptURL);
}
describe('Fallbacks that are not [built-in, fetch scheme]', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"bad1": [
"${BLANK}",
"${BLANK}"
],
"bad2": [
"${BLANK}",
"/bad2-1.mjs",
"/bad2-2.mjs"
],
"bad3": [
"/bad3-1.mjs",
"/bad3-2.mjs"
]
}
}`);
it('should fail for [built-in, built-in]', () => {
expect(() => resolveUnderTest('bad1')).toThrow(/not yet implemented/);
});
it('should fail for [built-in, fetch scheme, fetch scheme]', () => {
expect(() => resolveUnderTest('bad2')).toThrow(/not yet implemented/);
});
it('should fail for [fetch scheme, fetch scheme]', () => {
expect(() => resolveUnderTest('bad3')).toThrow(/not yet implemented/);
});
});
'use strict'; 'use strict';
// Imported from:
// https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/resolving.js
// TODO: Upstream local changes.
const { URL } = require('url'); const { URL } = require('url');
const { parseFromString } = require('../lib/parser.js'); const { parseFromString } = require('../lib/parser.js');
const { resolve } = require('../lib/resolver.js'); const { resolve } = require('../lib/resolver.js');
...@@ -89,7 +84,8 @@ describe('Mapped using the "imports" key only (no scopes)', () => { ...@@ -89,7 +84,8 @@ describe('Mapped using the "imports" key only (no scopes)', () => {
"lodash-dot": "./node_modules/lodash-es/lodash.js", "lodash-dot": "./node_modules/lodash-es/lodash.js",
"lodash-dot/": "./node_modules/lodash-es/", "lodash-dot/": "./node_modules/lodash-es/",
"lodash-dotdot": "../node_modules/lodash-es/lodash.js", "lodash-dotdot": "../node_modules/lodash-es/lodash.js",
"lodash-dotdot/": "../node_modules/lodash-es/" "lodash-dotdot/": "../node_modules/lodash-es/",
"nowhere/": []
} }
}`); }`);
...@@ -114,6 +110,10 @@ describe('Mapped using the "imports" key only (no scopes)', () => { ...@@ -114,6 +110,10 @@ describe('Mapped using the "imports" key only (no scopes)', () => {
expect(() => resolveUnderTest('underscore/')).toThrow(TypeError); expect(() => resolveUnderTest('underscore/')).toThrow(TypeError);
expect(() => resolveUnderTest('underscore/foo')).toThrow(TypeError); expect(() => resolveUnderTest('underscore/foo')).toThrow(TypeError);
}); });
it('should fail for package submodules that map to nowhere', () => {
expect(() => resolveUnderTest('nowhere/foo')).toThrow(TypeError);
});
}); });
describe('Tricky specifiers', () => { describe('Tricky specifiers', () => {
...@@ -149,7 +149,7 @@ describe('Mapped using the "imports" key only (no scopes)', () => { ...@@ -149,7 +149,7 @@ describe('Mapped using the "imports" key only (no scopes)', () => {
describe('URL-like specifiers', () => { describe('URL-like specifiers', () => {
const resolveUnderTest = makeResolveUnderTest(`{ const resolveUnderTest = makeResolveUnderTest(`{
"imports": { "imports": {
"/node_modules/als-polyfill/index.mjs": "@std/kv-storage", "/node_modules/als-polyfill/index.mjs": "std:kv-storage",
"/lib/foo.mjs": "./more/bar.mjs", "/lib/foo.mjs": "./more/bar.mjs",
"./dotrelative/foo.mjs": "/lib/dot.mjs", "./dotrelative/foo.mjs": "/lib/dot.mjs",
...@@ -158,20 +158,17 @@ describe('Mapped using the "imports" key only (no scopes)', () => { ...@@ -158,20 +158,17 @@ describe('Mapped using the "imports" key only (no scopes)', () => {
"/lib/no.mjs": null, "/lib/no.mjs": null,
"./dotrelative/no.mjs": [], "./dotrelative/no.mjs": [],
"/": "/lib/slash-only.mjs", "/": "/lib/slash-only/",
"./": "/lib/dotslash-only.mjs", "./": "/lib/dotslash-only/",
"/test/": "/lib/url-trailing-slash/",
"./test/": "/lib/url-trailing-slash-dot/",
"/test": "/lib/test1.mjs", "/test": "/lib/test1.mjs",
"../test": "/lib/test2.mjs" "../test": "/lib/test2.mjs"
} }
}`); }`);
it('should remap to built-in modules', () => {
expect(resolveUnderTest('/node_modules/als-polyfill/index.mjs')).toMatchURL('import:@std/kv-storage');
expect(resolveUnderTest('https://example.com/node_modules/als-polyfill/index.mjs')).toMatchURL('import:@std/kv-storage');
expect(resolveUnderTest('https://///example.com/node_modules/als-polyfill/index.mjs')).toMatchURL('import:@std/kv-storage');
});
it('should remap to other URLs', () => { it('should remap to other URLs', () => {
expect(resolveUnderTest('https://example.com/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs'); expect(resolveUnderTest('https://example.com/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs');
expect(resolveUnderTest('https://///example.com/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs'); expect(resolveUnderTest('https://///example.com/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs');
...@@ -195,13 +192,18 @@ describe('Mapped using the "imports" key only (no scopes)', () => { ...@@ -195,13 +192,18 @@ describe('Mapped using the "imports" key only (no scopes)', () => {
}); });
it('should remap URLs that are just composed from / and .', () => { it('should remap URLs that are just composed from / and .', () => {
expect(resolveUnderTest('https://example.com/')).toMatchURL('https://example.com/lib/slash-only.mjs'); expect(resolveUnderTest('https://example.com/')).toMatchURL('https://example.com/lib/slash-only/');
expect(resolveUnderTest('/')).toMatchURL('https://example.com/lib/slash-only.mjs'); expect(resolveUnderTest('/')).toMatchURL('https://example.com/lib/slash-only/');
expect(resolveUnderTest('../')).toMatchURL('https://example.com/lib/slash-only.mjs'); expect(resolveUnderTest('../')).toMatchURL('https://example.com/lib/slash-only/');
expect(resolveUnderTest('https://example.com/app/')).toMatchURL('https://example.com/lib/dotslash-only/');
expect(resolveUnderTest('/app/')).toMatchURL('https://example.com/lib/dotslash-only/');
expect(resolveUnderTest('../app/')).toMatchURL('https://example.com/lib/dotslash-only/');
});
expect(resolveUnderTest('https://example.com/app/')).toMatchURL('https://example.com/lib/dotslash-only.mjs'); it('should remap URLs that are prefix-matched by keys with trailing slashes', () => {
expect(resolveUnderTest('/app/')).toMatchURL('https://example.com/lib/dotslash-only.mjs'); expect(resolveUnderTest('/test/foo.mjs')).toMatchURL('https://example.com/lib/url-trailing-slash/foo.mjs');
expect(resolveUnderTest('../app/')).toMatchURL('https://example.com/lib/dotslash-only.mjs'); expect(resolveUnderTest('https://example.com/app/test/foo.mjs')).toMatchURL('https://example.com/lib/url-trailing-slash-dot/foo.mjs');
}); });
it('should use the last entry\'s address when URL-like specifiers parse to the same absolute URL', () => { it('should use the last entry\'s address when URL-like specifiers parse to the same absolute URL', () => {
...@@ -209,22 +211,60 @@ describe('Mapped using the "imports" key only (no scopes)', () => { ...@@ -209,22 +211,60 @@ describe('Mapped using the "imports" key only (no scopes)', () => {
}); });
}); });
describe('overlapping entries with trailing slashes', () => { describe('Overlapping entries with trailing slashes', () => {
const resolveUnderTest = makeResolveUnderTest(`{ it('should favor the most-specific key (no empty arrays)', () => {
"imports": { const resolveUnderTest = makeResolveUnderTest(`{
"a": "/1", "imports": {
"a/": "/2/", "a": "/1",
"a/b": "/3", "a/": "/2/",
"a/b/": "/4/" "a/b": "/3",
} "a/b/": "/4/"
}`); }
}`);
it('most-specific wins', () => {
expect(resolveUnderTest('a')).toMatchURL('https://example.com/1'); expect(resolveUnderTest('a')).toMatchURL('https://example.com/1');
expect(resolveUnderTest('a/')).toMatchURL('https://example.com/2/'); expect(resolveUnderTest('a/')).toMatchURL('https://example.com/2/');
expect(resolveUnderTest('a/b')).toMatchURL('https://example.com/3'); expect(resolveUnderTest('a/b')).toMatchURL('https://example.com/3');
expect(resolveUnderTest('a/b/')).toMatchURL('https://example.com/4/'); expect(resolveUnderTest('a/b/')).toMatchURL('https://example.com/4/');
expect(resolveUnderTest('a/b/c')).toMatchURL('https://example.com/4/c'); expect(resolveUnderTest('a/b/c')).toMatchURL('https://example.com/4/c');
}); });
it('should favor the most-specific key when empty arrays are involved for less-specific keys', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"a": [],
"a/": [],
"a/b": "/3",
"a/b/": "/4/"
}
}`);
expect(() => resolveUnderTest('a')).toThrow(TypeError);
expect(() => resolveUnderTest('a/')).toThrow(TypeError);
expect(() => resolveUnderTest('a/x')).toThrow(TypeError);
expect(resolveUnderTest('a/b')).toMatchURL('https://example.com/3');
expect(resolveUnderTest('a/b/')).toMatchURL('https://example.com/4/');
expect(resolveUnderTest('a/b/c')).toMatchURL('https://example.com/4/c');
expect(() => resolveUnderTest('a/x/c')).toThrow(TypeError);
});
it('should favor the most-specific key when empty arrays are involved for more-specific keys', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"a": "/1",
"a/": "/2/",
"a/b": [],
"a/b/": []
}
}`);
expect(resolveUnderTest('a')).toMatchURL('https://example.com/1');
expect(resolveUnderTest('a/')).toMatchURL('https://example.com/2/');
expect(resolveUnderTest('a/x')).toMatchURL('https://example.com/2/x');
expect(() => resolveUnderTest('a/b')).toThrow(TypeError);
expect(() => resolveUnderTest('a/b/')).toThrow(TypeError);
expect(() => resolveUnderTest('a/b/c')).toThrow(TypeError);
expect(resolveUnderTest('a/x/c')).toMatchURL('https://example.com/2/x/c');
});
}); });
}); });
<!DOCTYPE html> // Hacky glue code to run Jest-based tests as WPT tests.
<html> // TODO(https://github.com/WICG/import-maps/issues/170): Consider better ways
<script src="/resources/testharness.js"></script> // to write and run tests.
<script src="/resources/testharnessreport.js"></script>
<script>
setup({allow_uncaught_exception : true}); setup({allow_uncaught_exception : true});
// Hacky glue code to run Jest-based tests as WPT tests. const exports = {};
// Only supports resolving.js.
function require(name) { function require(name) {
return { return Object.assign({
'URL': URL, 'URL': URL,
'parseFromString': parseFromString, 'parseFromString': parseFromString,
'resolve': resolve 'resolve': resolve,
}; 'BUILT_IN_MODULE_SCHEME': 'std'
}, exports);
} }
function expect(v) { function expect(v) {
return { return {
toMatchURL: expected => assert_equals(v, expected), toMatchURL: expected => assert_equals(v, expected),
toThrow: expected => assert_throws(expected(), v) toThrow: expected => {
if (expected.test && expected.test('not yet implemented')) {
// We override /not yet implemented/ expectation.
assert_throws(TypeError(), v);
} else {
assert_throws(expected(), v);
}
},
toEqual: expected => {
if (v.localName === 'iframe') {
// `v` is the result of parseFromString(), and thus toEqual() is
// expected to compare parsed import maps.
// We sort keys when stringifying for normalization.
const actualParsedImportMap = JSON.parse(
internals.getParsedImportMap(v.contentDocument));
assert_equals(
JSON.stringify(actualParsedImportMap,
Object.keys(actualParsedImportMap).sort()),
JSON.stringify(expected.imports,
Object.keys(expected.imports).sort())
);
} else {
assert_object_equals(v, expected);
}
}
}; };
} }
expect.toMatchURL = expected => expected;
const test_harness_test = test;
test = it;
let current_message = ''; let current_message = '';
function describe(message, f) { function describe(message, f) {
const old = current_message; const old = current_message;
...@@ -38,7 +67,7 @@ function it(message, f) { ...@@ -38,7 +67,7 @@ function it(message, f) {
current_message += ' / '; current_message += ' / ';
} }
current_message += message; current_message += message;
test(t => t.step_func(f)(), current_message); test_harness_test(t => t.step_func(f)(), current_message);
current_message = old; current_message = old;
} }
...@@ -71,11 +100,3 @@ function resolve(specifier, map, baseURL) { ...@@ -71,11 +100,3 @@ function resolve(specifier, map, baseURL) {
baseURL, baseURL,
map.contentDocument); map.contentDocument);
} }
</script>
<!--
resolving.js is
https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/resolving.js
-->
<script type="module" src="resources/resolving.js"></script>
...@@ -808,7 +808,7 @@ MISSING-LINK: css/filter-effects/*.any.js ...@@ -808,7 +808,7 @@ MISSING-LINK: css/filter-effects/*.any.js
# Tests that use WebKit/Blink testing APIs # Tests that use WebKit/Blink testing APIs
LAYOUTTESTS APIS: css/css-regions/interactivity/* LAYOUTTESTS APIS: css/css-regions/interactivity/*
LAYOUTTESTS APIS: import-maps/resolving.tentative.html LAYOUTTESTS APIS: import-maps/resources/jest-test-helper.js
LAYOUTTESTS APIS: permissions/test-background-fetch-permission.html LAYOUTTESTS APIS: permissions/test-background-fetch-permission.html
LAYOUTTESTS APIS: resources/chromium/generic_sensor_mocks.js LAYOUTTESTS APIS: resources/chromium/generic_sensor_mocks.js
LAYOUTTESTS APIS: resources/chromium/webxr-test.js LAYOUTTESTS APIS: resources/chromium/webxr-test.js
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment