Skip to content

Commit

Permalink
Import maps: simplify web platform tests
Browse files Browse the repository at this point in the history
* Use import.meta.resolve() and <base> trickery, instead of a service worker + sometimes Chromium internals.

* Remove the parsing tests that used Chromium internals. We should convert these into resolution tests eventually, but for now they just make wpt.fyi look sad and are confusing for other engines. We leave the JSON files here for now in anticipation of that upcoming conversion.

  These tests are partially restored, in wpt_internal, in https://chromium-review.googlesource.com/c/chromium/src/+/3815111/.

* Use <meta name="variant"> instead of a loop over the JSON files. This avoids the need for <meta timeout="long"> and possibly allows some parallelism.

* Renamed data-base-url.json to be data-url-prefix.json and update the test description to reflect the intent. This confusion originates from the original JS to JSON conversion at WICG/import-maps@6cb173d.

Fixes #32697.

Change-Id: I4943f4249eaec89f718c7a7fef271dbc61ec3f77
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3816001
Reviewed-by: Hiroshige Hayashizaki <[email protected]>
Commit-Queue: Domenic Denicola <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1032864}
  • Loading branch information
domenic authored and chromium-wpt-export-bot committed Aug 9, 2022
1 parent efa2e0b commit e4148ca
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 379 deletions.
20 changes: 11 additions & 9 deletions import-maps/data-driven/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
# Data-driven import maps tests

In this directory, test inputs and expectations are expressed as JSON files.
This is in order to share the same JSON files between WPT tests and Jest-based
tests for the reference JavaScript implementation at [WICG repository](https://github.com/WICG/import-maps/tree/master/reference-implementation).
This is in order to share the same JSON files between WPT tests and other
implementations that might not run the full WPT suite, e.g. server-side
JavaScript runtimes or the [JavaScript reference implementation](https://github.com/WICG/import-maps/tree/master/reference-implementation).

## Basics

A **test object** describes a set of parameters (import maps and base URLs) and test expectations.
Test expectations can be:

- Expected resulting URLs for specifiers (resolution tests), or
- Expected parsed import maps (parsing tests).
Test expectations consist of the expected resulting URLs for specifiers.

Each JSON file under [resources/](resources/) directory consists of a test object.
A minimum test object would be:
Expand Down Expand Up @@ -38,11 +36,10 @@ Required fields:
- In WPT tests, this is used for the test name of `promise_test()` together with specifier to be resolved, like `"Main test name: a"`.
- `importMap` (object or string): the import map to be attached.
- `importMapBaseURL` (string): the base URL used for [parsing the import map](https://wicg.github.io/import-maps/#parse-an-import-map-string).
- (resolution tests only) `expectedResults` (object; string to (string or null)): resolution test cases.
- `expectedResults` (object; string to (string or null)): resolution test cases.
- The keys are specifiers to be resolved.
- The values are expected resolved URLs. If `null`, resolution should fail.
- (resolution tests only) `baseURL` (string): the base URL used in [resolving a specifier](https://wicg.github.io/import-maps/#resolve-a-module-specifier) for each specifiers.
- (parsing tests only) `expectedParsedImportMap` (object): the expected parsed import map for parsing test cases.
- `baseURL` (string): the base URL used in [resolving a specifier](https://wicg.github.io/import-maps/#resolve-a-module-specifier) for each specifiers.

Optional fields:

Expand Down Expand Up @@ -83,3 +80,8 @@ The top-level test object contains two sub test objects, named as `Subtest1` and

Child test objects inherit fields from their parent test object.
In the example above, the child test objects specifies `baseURL` fields, while they inherits other fields (e.g. `importMapBaseURL`) from the top-level test object.

## TODO

The `parsing-*.json` files are not currently used by the WPT harness. We should
convert them to resolution tests.
38 changes: 0 additions & 38 deletions import-maps/data-driven/parsing-internal.https.html

This file was deleted.

32 changes: 0 additions & 32 deletions import-maps/data-driven/resolving-internal.https.html

This file was deleted.

23 changes: 23 additions & 0 deletions import-maps/data-driven/resolving.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<meta name="variant" content="?data-url-prefix.json">
<meta name="variant" content="?empty-import-map.json">
<meta name="variant" content="?overlapping-entries.json">
<meta name="variant" content="?packages-via-trailing-slashes.json">
<meta name="variant" content="?resolving-null.json">
<meta name="variant" content="?scopes-exact-vs-prefix.json">
<meta name="variant" content="?scopes.json">
<meta name="variant" content="?tricky-specifiers.json">
<meta name="variant" content="?url-specifiers-schemes.json">
<meta name="variant" content="?url-specifiers.json">

<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script type="module">
import { runTestsFromJSON } from "./resources/test-helper.js";

const filename = location.search.substring(1);
promise_test(
() => runTestsFromJSON('resources/' + filename),
"Test helper: fetching and sanity checking test JSON: " + filename);
</script>
32 changes: 0 additions & 32 deletions import-maps/data-driven/resolving.https.html

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
},
"importMapBaseURL": "https://example.com/app/index.html",
"baseURL": "https://example.com/js/app.mjs",
"name": "data: base URL (?)",
"name": "data: URL prefix",
"tests": {
"should favor the most-specific key": {
"should not resolve since you can't resolve relative to a data: URL": {
"expectedResults": {
"foo/bar": null
}
Expand Down
21 changes: 0 additions & 21 deletions import-maps/data-driven/resources/empty-import-map-internal.json

This file was deleted.

14 changes: 14 additions & 0 deletions import-maps/data-driven/resources/empty-import-map.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@
"https://invalid-url.com:demo": null,
"http://[invalid-url.com]/": null
}
},
"non-HTTPS fetch scheme absolute URLs": {
"expectedResults": {
"about:fetch-scheme": "about:fetch-scheme"
}
},
"non-fetch scheme absolute URLs": {
"expectedResults": {
"about:fetch-scheme": "about:fetch-scheme",
"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/"
}
}
}
}
103 changes: 23 additions & 80 deletions import-maps/data-driven/resources/test-helper-iframe.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Handle errors around fetching, parsing and registering import maps.
const onScriptError = event => {
window.onScriptError = event => {
window.registrationResult = {type: 'FetchError', error: event.error};
return false;
};
Expand All @@ -14,90 +14,33 @@ window.addEventListener('error', window.windowErrorHandler);
// objects themselves and re-create error objects later, to avoid
// issues around serializing error objects which is a quite new feature.
window.addEventListener('message', event => {
if (event.data.action === 'prepareResolve') {
// To get the result of #resolve-a-module-specifier given a script
// (with base URL = |baseURL|) and |specifier|, the service worker
// first serves an importer script with response URL = |baseURL|:
// window.importHelper = (specifier) => import(specifier);
// This is to use |baseURL| as the referringScript's base URL.
if (event.data.action !== 'resolve') {
parent.postMessage({
type: 'Failure',
result: 'Error',
message: 'Invalid Action: ' + event.data.action}, '*');
return;
}

// Step 1. Signal the service worker to serve
// the importer script for the next fetch request.
parent.worker.postMessage('serveImporterScript');
} else if (event.data.action === 'resolve') {
if (event.data.expectedURL === null ||
new URL(event.data.expectedURL).protocol === 'https:') {
// Testing without internal methods:
// If the resolution is expected to fail (null case here),
// we can test the failure just by catching the exception.
// If the expected URL is HTTPS, we can test the result by
// intercepting requests by service workers.
// To respond to a resolution request, we:
// 1. Save the specifier to resolve into a global.
// 2. Update the document's base URL to the requested base URL.
// 3. Create a new inline script, parsed with that base URL, which
// resolves the saved specifier using import.meta.resolve(), and
// sents the result to the parent window.
window.specifierToResolve = event.data.specifier;
document.querySelector('base').href = event.data.baseURL;

// Step 3. Evaluate the importer script as a classic script,
// in order to prevent |baseURL| from being mapped by import maps.
const script = document.createElement('script');
script.onload = () => {
// Step 4. Trigger dynamic import from |baseURL|.
importHelper(event.data.specifier)
.then(module => {
// Step 5. Service worker responds with a JSON containing
// the request URL for the dynamic import
// (= the result of #resolve-a-module-specifier).
parent.postMessage({type: 'ResolutionSuccess',
result: module.response.url},
'*');
})
.catch(e => {
parent.postMessage(
{type: 'Failure', result: e.name, message: e.message},
'*');
});
};
script.src = event.data.baseURL;
document.body.appendChild(script);
} else {
// Testing with internal methods.
// For example, the resolution results are data: URLs.
if (!event.data.useInternalMethods) {
parent.postMessage(
{type: 'Failure',
result: 'Error',
message: 'internals.resolveModuleSpecifier is not available'},
'*');
return;
}
try {
const result = internals.resolveModuleSpecifier(
event.data.specifier,
event.data.baseURL,
document);
parent.postMessage(
{type: 'ResolutionSuccess', result: result}, '*');
} catch (e) {
parent.postMessage(
{type: 'Failure', result: e.name, message: e.message}, '*');
}
}
} else if (event.data.action === 'getParsedImportMap') {
if (!event.data.useInternalMethods) {
parent.postMessage(
{type: 'Failure',
result: 'Error',
message: 'internals.getParsedImportMap is not available'},
'*');
}
const inlineScript = document.createElement('script');
inlineScript.type = 'module';
inlineScript.textContent = `
try {
parent.postMessage({
type: 'GetParsedImportMapSuccess',
result: internals.getParsedImportMap(document)}, '*');
const result = import.meta.resolve(window.specifierToResolve);
parent.postMessage({type: 'ResolutionSuccess', result}, '*');
} catch (e) {
parent.postMessage(
{type: 'Failure', result: e.name, message: e.message}, '*');
}
} else {
parent.postMessage({
type: 'Failure',
result: 'Error',
message: 'Invalid Action: ' + event.data.action}, '*');
}
`;
document.body.append(inlineScript);
});
Loading

0 comments on commit e4148ca

Please sign in to comment.