254 lines
10 KiB
JavaScript
254 lines
10 KiB
JavaScript
let log = [];
|
|
|
|
function expect_log(test, expected_log) {
|
|
test.step_func_done(() => {
|
|
const actual_log = log;
|
|
log = [];
|
|
assert_array_equals(actual_log, expected_log, 'fallback log');
|
|
})();
|
|
}
|
|
|
|
// Results of resolving a specifier using import maps.
|
|
const Result = {
|
|
// A failure considered as a fetch error in a module script tree.
|
|
// <script>'s error event is fired.
|
|
FETCH_ERROR: "fetch_error",
|
|
|
|
// A failure considered as a parse error in a module script tree.
|
|
// Window's error event is fired.
|
|
PARSE_ERROR: "parse_error",
|
|
|
|
// The specifier is considered as a relative or absolute URL.
|
|
// Specifier Expected log
|
|
// ------------------------- ----------------------
|
|
// ...?name=foo log:foo
|
|
// data:...log('foo') foo
|
|
// Others, e.g. bare/bare relative:bare/bare
|
|
// ------------------------- ----------------------
|
|
// (The last case assumes a file `bare/bare` that logs `relative:bare/bare`
|
|
// exists)
|
|
URL: "URL",
|
|
};
|
|
|
|
const Handler = {
|
|
// Handlers for <script> element cases.
|
|
// Note that on a parse error both WindowErrorEvent and ScriptLoadEvent are
|
|
// called.
|
|
ScriptLoadEvent: "<script> element's load event handler",
|
|
ScriptErrorEvent: "<script> element's error event handler",
|
|
WindowErrorEvent: "window's error event handler",
|
|
|
|
// Handlers for dynamic imports.
|
|
DynamicImportResolve: "dynamic import resolve",
|
|
DynamicImportReject: "dynamic import reject",
|
|
};
|
|
|
|
// Returns a map with Handler.* as the keys.
|
|
function getHandlers(t, specifier, expected) {
|
|
let handlers = {};
|
|
handlers[Handler.ScriptLoadEvent] = t.unreached_func("Shouldn't load");
|
|
handlers[Handler.ScriptErrorEvent] =
|
|
t.unreached_func("script's error event shouldn't be fired");
|
|
handlers[Handler.WindowErrorEvent] =
|
|
t.unreached_func("window's error event shouldn't be fired");
|
|
handlers[Handler.DynamicImportResolve] =
|
|
t.unreached_func("dynamic import promise shouldn't be resolved");
|
|
handlers[Handler.DynamicImportReject] =
|
|
t.unreached_func("dynamic import promise shouldn't be rejected");
|
|
|
|
if (expected === Result.FETCH_ERROR) {
|
|
handlers[Handler.ScriptErrorEvent] = () => expect_log(t, []);
|
|
handlers[Handler.DynamicImportReject] = () => expect_log(t, []);
|
|
} else if (expected === Result.PARSE_ERROR) {
|
|
let error_occurred = false;
|
|
handlers[Handler.WindowErrorEvent] = () => { error_occurred = true; };
|
|
handlers[Handler.ScriptLoadEvent] = t.step_func(() => {
|
|
// Even if a parse error occurs, load event is fired (after
|
|
// window.onerror is called), so trigger the load handler only if
|
|
// there was no previous window.onerror call.
|
|
assert_true(error_occurred, "window.onerror should be fired");
|
|
expect_log(t, []);
|
|
});
|
|
handlers[Handler.DynamicImportReject] = t.step_func(() => {
|
|
assert_false(error_occurred,
|
|
"window.onerror shouldn't be fired for dynamic imports");
|
|
expect_log(t, []);
|
|
});
|
|
} else {
|
|
let expected_log;
|
|
if (expected === Result.URL) {
|
|
const match_data_url = specifier.match(/data:.*log\.push\('(.*)'\)/);
|
|
const match_log_js = specifier.match(/name=(.*)/);
|
|
if (match_data_url) {
|
|
expected_log = [match_data_url[1]];
|
|
} else if (match_log_js) {
|
|
expected_log = ["log:" + match_log_js[1]];
|
|
} else {
|
|
expected_log = ["relative:" + specifier];
|
|
}
|
|
} else {
|
|
expected_log = [expected];
|
|
}
|
|
handlers[Handler.ScriptLoadEvent] = () => expect_log(t, expected_log);
|
|
handlers[Handler.DynamicImportResolve] = () => expect_log(t, expected_log);
|
|
}
|
|
return handlers;
|
|
}
|
|
|
|
// Creates an <iframe> and run a test inside the <iframe>
|
|
// to separate the module maps and import maps in each test.
|
|
function testInIframe(importMapString, importMapBaseURL, testScript) {
|
|
const iframe = document.createElement('iframe');
|
|
document.body.appendChild(iframe);
|
|
if (!importMapBaseURL) {
|
|
importMapBaseURL = document.baseURI;
|
|
}
|
|
let content = `
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/import-maps/resources/test-helper.js"></script>
|
|
<base href="${importMapBaseURL}">
|
|
<script type="importmap">${importMapString}</script>
|
|
<body>
|
|
<script>
|
|
setup({ allow_uncaught_exception: true });
|
|
${testScript}
|
|
</sc` + `ript>
|
|
`;
|
|
iframe.contentDocument.write(content);
|
|
iframe.contentDocument.close();
|
|
return fetch_tests_from_window(iframe.contentWindow);
|
|
}
|
|
|
|
function testScriptElement(importMapString, importMapBaseURL, specifier, expected, type) {
|
|
return testInIframe(importMapString, importMapBaseURL, `
|
|
const t = async_test("${specifier}: <script src type=${type}>");
|
|
const handlers = getHandlers(t, "${specifier}", "${expected}");
|
|
const script = document.createElement("script");
|
|
script.setAttribute("type", "${type}");
|
|
script.setAttribute("src", "${specifier}");
|
|
script.addEventListener("load", handlers[Handler.ScriptLoadEvent]);
|
|
script.addEventListener("error", handlers[Handler.ScriptErrorEvent]);
|
|
window.addEventListener("error", handlers[Handler.WindowErrorEvent]);
|
|
document.body.appendChild(script);
|
|
`);
|
|
}
|
|
|
|
function testStaticImport(importMapString, importMapBaseURL, specifier, expected) {
|
|
return testInIframe(importMapString, importMapBaseURL, `
|
|
const t = async_test("${specifier}: static import");
|
|
const handlers = getHandlers(t, "${specifier}", "${expected}");
|
|
const script = document.createElement("script");
|
|
script.setAttribute("type", "module");
|
|
script.setAttribute("src",
|
|
"/import-maps/static-import.py?url=" +
|
|
encodeURIComponent("${specifier}"));
|
|
script.addEventListener("load", handlers[Handler.ScriptLoadEvent]);
|
|
script.addEventListener("error", handlers[Handler.ScriptErrorEvent]);
|
|
window.addEventListener("error", handlers[Handler.WindowErrorEvent]);
|
|
document.body.appendChild(script);
|
|
`);
|
|
}
|
|
|
|
function testDynamicImport(importMapString, importMapBaseURL, specifier, expected, type) {
|
|
return testInIframe(importMapString, importMapBaseURL, `
|
|
const t = async_test("${specifier}: dynamic import (from ${type})");
|
|
const handlers = getHandlers(t, "${specifier}", "${expected}");
|
|
const script = document.createElement("script");
|
|
script.setAttribute("type", "${type}");
|
|
script.innerText =
|
|
"import(\\"${specifier}\\")" +
|
|
".then(handlers[Handler.DynamicImportResolve], " +
|
|
"handlers[Handler.DynamicImportReject]);";
|
|
script.addEventListener("error",
|
|
t.unreached_func("top-level inline script shouldn't error"));
|
|
document.body.appendChild(script);
|
|
`);
|
|
}
|
|
|
|
function testInIframeInjectBase(importMapString, importMapBaseURL, testScript) {
|
|
const iframe = document.createElement('iframe');
|
|
document.body.appendChild(iframe);
|
|
let content = `
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/import-maps/resources/test-helper.js"></script>
|
|
<script src="/import-maps/resources/inject-base.js?pipe=sub&baseurl=${importMapBaseURL}"></script>
|
|
<script type="importmap">
|
|
${importMapString}
|
|
</script>
|
|
<body>
|
|
<script>
|
|
setup({ allow_uncaught_exception: true });
|
|
${testScript}
|
|
</sc` + `ript>
|
|
`;
|
|
iframe.contentDocument.write(content);
|
|
iframe.contentDocument.close();
|
|
return fetch_tests_from_window(iframe.contentWindow);
|
|
}
|
|
|
|
function testStaticImportInjectBase(importMapString, importMapBaseURL, specifier, expected) {
|
|
return testInIframeInjectBase(importMapString, importMapBaseURL, `
|
|
const t = async_test("${specifier}: static import with inject <base>");
|
|
const handlers = getHandlers(t, "${specifier}", "${expected}");
|
|
const script = document.createElement("script");
|
|
script.setAttribute("type", "module");
|
|
script.setAttribute("src",
|
|
"/import-maps/static-import.py?url=" +
|
|
encodeURIComponent("${specifier}"));
|
|
script.addEventListener("load", handlers[Handler.ScriptLoadEvent]);
|
|
script.addEventListener("error", handlers[Handler.ScriptErrorEvent]);
|
|
window.addEventListener("error", handlers[Handler.WindowErrorEvent]);
|
|
document.body.appendChild(script);
|
|
`);
|
|
}
|
|
|
|
function testDynamicImportInjectBase(importMapString, importMapBaseURL, specifier, expected, type) {
|
|
return testInIframeInjectBase(importMapString, importMapBaseURL, `
|
|
const t = async_test("${specifier}: dynamic import (from ${type}) with inject <base>");
|
|
const handlers = getHandlers(t, "${specifier}", "${expected}");
|
|
const script = document.createElement("script");
|
|
script.setAttribute("type", "${type}");
|
|
script.innerText =
|
|
"import(\\"${specifier}\\")" +
|
|
".then(handlers[Handler.DynamicImportResolve], " +
|
|
"handlers[Handler.DynamicImportReject]);";
|
|
script.addEventListener("error",
|
|
t.unreached_func("top-level inline script shouldn't error"));
|
|
document.body.appendChild(script);
|
|
`);
|
|
}
|
|
|
|
function doTests(importMapString, importMapBaseURL, tests) {
|
|
promise_setup(function () {
|
|
return new Promise((resolve) => {
|
|
window.addEventListener("load", async () => {
|
|
for (const specifier in tests) {
|
|
// <script src> (module scripts)
|
|
await testScriptElement(importMapString, importMapBaseURL, specifier, tests[specifier][0], "module");
|
|
|
|
// <script src> (classic scripts)
|
|
await testScriptElement(importMapString, importMapBaseURL, specifier, tests[specifier][1], "text/javascript");
|
|
|
|
// static imports.
|
|
await testStaticImport(importMapString, importMapBaseURL, specifier, tests[specifier][2]);
|
|
|
|
// dynamic imports from a module script.
|
|
await testDynamicImport(importMapString, importMapBaseURL, specifier, tests[specifier][3], "module");
|
|
|
|
// dynamic imports from a classic script.
|
|
await testDynamicImport(importMapString, importMapBaseURL, specifier, tests[specifier][3], "text/javascript");
|
|
}
|
|
done();
|
|
resolve();
|
|
});
|
|
});
|
|
}, { explicit_done: true });
|
|
}
|
|
|
|
function test_loaded(specifier, expected_log, description) {
|
|
promise_test(async t => {
|
|
log = [];
|
|
await import(specifier);
|
|
assert_array_equals(log, expected_log);
|
|
}, description);
|
|
};
|