Skip to content

Commit

Permalink
[WPT] Migrate most of import-maps resolution tests out of blink inter…
Browse files Browse the repository at this point in the history
…nals

This CL migrates most of import maps resolution tests by
observing the resolution results by intercepting
module script requests by a service worker.

This CL also introduces `useInternalMethods` flag to clarify
the tests still requiring internal methods.

Bug: 1026809
Change-Id: I16c2a87bb67b530dc97b1631f6968b2d3bafdac6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2457526
Commit-Queue: Hiroshige Hayashizaki <hiroshige@chromium.org>
Reviewed-by: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#818366}
  • Loading branch information
hiroshige-g authored and chromium-wpt-export-bot committed Oct 19, 2020
1 parent ebdb2fc commit e85837b
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 79 deletions.
28 changes: 28 additions & 0 deletions import-maps/common/common-test-service-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
let serveImporterScript = false;

self.addEventListener('message', event => {
serveImporterScript = true;
event.source.postMessage('Done');
});

self.addEventListener('fetch', event => {
if (event.request.url.indexOf('common-test-helper-iframe.js') >= 0) {
return;
}
if (serveImporterScript) {
serveImporterScript = false;
event.respondWith(
new Response(
'window.importHelper = (specifier) => import(specifier);',
{headers: {'Content-Type': 'text/javascript'}}
));
} else {
event.respondWith(
new Response(
'export const response = ' +
JSON.stringify({url: event.request.url}) + ';',
{headers: {'Access-Control-Allow-Origin': '*',
'Content-Type': 'text/javascript'}}
));
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
<script>
// All parsing tests requires Chromium's internal methods.
globalThis.useInternalMethods = true;
</script>
<body>
<script type="module">
import { runTestsFromJSON } from "./resources/common-test-helper.js";
import { runTestsFromJSON, setupGlobalCleanup } from "./resources/common-test-helper.js";

const promises = [];

for (const json of [
'resources/parsing-addresses-absolute.json',
Expand All @@ -19,8 +26,13 @@
'resources/parsing-specifier-keys.json',
'resources/parsing-trailing-slashes.json',
]) {
promise_test(() =>
runTestsFromJSON(json),
promise_test(() => {
const promise = runTestsFromJSON(json);
promises.push(promise);
return promise;
},
"Test helper: fetching and sanity checking test JSON: " + json);
}

Promise.all(promises).then(setupGlobalCleanup);
</script>
31 changes: 31 additions & 0 deletions import-maps/common/resolving-internal.tentative.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
<script>
// This test file is for resolution tests that require Chromium's internal
// methods.
// For tests that don't use Chromium's internal methods, see
// resolving.tentative.https.html.
globalThis.useInternalMethods = true;
</script>
<body>
<script type="module">
import { runTestsFromJSON, setupGlobalCleanup } from "./resources/common-test-helper.js";

const promises = [];

for (const json of [
'resources/empty-import-map-internal.json',
]) {
promise_test(() => {
const promise = runTestsFromJSON(json);
promises.push(promise);
return promise;
},
"Test helper: fetching and sanity checking test JSON: " + json);
}

Promise.all(promises).then(setupGlobalCleanup);
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
<body>
<script type="module">
import { runTestsFromJSON } from "./resources/common-test-helper.js";
import { runTestsFromJSON, setupGlobalCleanup } from "./resources/common-test-helper.js";

const promises = [];

for (const json of [
'resources/scopes.json',
Expand All @@ -17,8 +20,13 @@
'resources/overlapping-entries.json',
'resources/resolving-null.json',
]) {
promise_test(() =>
runTestsFromJSON(json),
promise_test(() => {
const promise = runTestsFromJSON(json);
promises.push(promise);
return promise;
},
"Test helper: fetching and sanity checking test JSON: " + json);
}

Promise.all(promises).then(setupGlobalCleanup);
</script>
103 changes: 103 additions & 0 deletions import-maps/common/resources/common-test-helper-iframe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Handle errors around fetching, parsing and registering import maps.
const onScriptError = event => {
window.registrationResult = {type: 'FetchError', error: event.error};
return false;
};
window.windowErrorHandler = event => {
window.registrationResult = {type: 'ParseError', error: event.error};
return false;
};
window.addEventListener('error', window.windowErrorHandler);

// Handle specifier resolution requests from the parent frame.
// For failures, we post error names and messages instead of error
// 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.

// 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.

// 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'},
'*');
}
try {
parent.postMessage({
type: 'GetParsedImportMapSuccess',
result: internals.getParsedImportMap(document)}, '*');
} 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}, '*');
}
});
Loading

0 comments on commit e85837b

Please sign in to comment.