blob: 2447bfb94353e1ec193857cbcda79f46556216a1 [file] [log] [blame]
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 built-in module (std:blank) is loaded.
BUILTIN: "builtin",
// 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. @std/blank relative:@std/blank
// ------------------------- ----------------------
// (The last case assumes a file `@std/blank` that logs `relative:@std/blank`
// 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.BUILTIN) {
expected_log = [];
} else 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}">
`;
if (importMapString) {
content += `
<script type="importmap">
${importMapString}
</sc` + `ript>
`;
}
content += `
<body>
<script>
setup({ allow_uncaught_exception: true });
${testScript}
</sc` + `ript>
`;
iframe.contentDocument.write(content);
iframe.contentDocument.close();
fetch_tests_from_window(iframe.contentWindow);
}
function testScriptElement(importMapString, importMapBaseURL, specifier, expected, type) {
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) {
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.js?pipe=sub(none)&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) {
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 doTests(importMapString, importMapBaseURL, tests) {
window.addEventListener("load", () => {
for (const specifier in tests) {
// <script src> (module scripts)
testScriptElement(importMapString, importMapBaseURL, specifier,
tests[specifier][0], "module");
// <script src> (classic scripts)
testScriptElement(importMapString, importMapBaseURL, specifier,
tests[specifier][1], "text/javascript");
// static imports.
testStaticImport(importMapString, importMapBaseURL, specifier,
tests[specifier][2]);
// dynamic imports from a module script.
testDynamicImport(importMapString, importMapBaseURL, specifier,
tests[specifier][3], "module");
// dynamic imports from a classic script.
testDynamicImport(importMapString, importMapBaseURL, specifier,
tests[specifier][3], "text/javascript");
}
});
}