Revert "Bug 1958081 - [devtools] Cover JS Tracer stringification with the new JS Objects test framework. r=devtools-reviewers,nchevobbe" for xpcshell failures at test_javascript_logging.js
This reverts commit6056567c3e. Revert "Bug 1958081 - [devtools] Migrate object inspector to the new JS objects test framework. r=devtools-reviewers,nchevobbe" This reverts commit8cbc65e7b5. Revert "Bug 1958081 - [devtools] Introduce a test framework to ease testing all possible JavaScript types against various codebases. r=frontend-codestyle-reviewers,devtools-reviewers,nchevobbe,Standard8" for causing xpc failures at test_javascript_logging.js This reverts commitddcbf73cd8.
This commit is contained in:
committed by
chorotan@mozilla.com
parent
d378045189
commit
539746f658
@@ -102,7 +102,6 @@ module.exports = [
|
|||||||
"devtools/client/preferences/",
|
"devtools/client/preferences/",
|
||||||
|
|
||||||
// Ignore devtools generated code
|
// Ignore devtools generated code
|
||||||
"devtools/**/*.snapshot.mjs",
|
|
||||||
"devtools/client/webconsole/test/node/fixtures/stubs/*.js",
|
"devtools/client/webconsole/test/node/fixtures/stubs/*.js",
|
||||||
"!devtools/client/webconsole/test/node/fixtures/stubs/index.js",
|
"!devtools/client/webconsole/test/node/fixtures/stubs/index.js",
|
||||||
"devtools/client/shared/source-map-loader/test/browser/fixtures/*.js",
|
"devtools/client/shared/source-map-loader/test/browser/fixtures/*.js",
|
||||||
|
|||||||
@@ -1054,7 +1054,6 @@ devtools/client/debugger/webpack.config.js
|
|||||||
devtools/client/preferences/
|
devtools/client/preferences/
|
||||||
|
|
||||||
# Ignore devtools generated code
|
# Ignore devtools generated code
|
||||||
devtools/**/*.snapshot.mjs
|
|
||||||
devtools/client/webconsole/test/node/fixtures/stubs/*.js
|
devtools/client/webconsole/test/node/fixtures/stubs/*.js
|
||||||
!devtools/client/webconsole/test/node/fixtures/stubs/index.js
|
!devtools/client/webconsole/test/node/fixtures/stubs/index.js
|
||||||
devtools/client/shared/components/test/node/stubs/reps/*.js
|
devtools/client/shared/components/test/node/stubs/reps/*.js
|
||||||
|
|||||||
@@ -527,9 +527,6 @@ fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and
|
|||||||
|
|
||||||
["browser_webconsole_object_inspector_entries.js"]
|
["browser_webconsole_object_inspector_entries.js"]
|
||||||
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
|
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
|
||||||
support-files = ["browser_webconsole_object_inspector_entries.snapshot.mjs"]
|
|
||||||
https_first_disabled = true # JS HttpServer doesn't support https
|
|
||||||
skip-if = ["http3"] # JS HttpServer doesn't support http3
|
|
||||||
|
|
||||||
["browser_webconsole_object_inspector_getters.js"]
|
["browser_webconsole_object_inspector_getters.js"]
|
||||||
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
|
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
|
||||||
@@ -589,9 +586,6 @@ fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and
|
|||||||
skip-if = ["os == 'linux' && os_version == '18.04' && processor == 'x86_64' && debug && http3"] # Bug 1829298
|
skip-if = ["os == 'linux' && os_version == '18.04' && processor == 'x86_64' && debug && http3"] # Bug 1829298
|
||||||
|
|
||||||
["browser_webconsole_previewers.js"]
|
["browser_webconsole_previewers.js"]
|
||||||
support-files = ["browser_webconsole_previewers.snapshot.mjs"]
|
|
||||||
https_first_disabled = true # JS HttpServer doesn't support https
|
|
||||||
skip-if = ["http3"] # JS HttpServer doesn't support http3
|
|
||||||
|
|
||||||
["browser_webconsole_promise_rejected_object.js"]
|
["browser_webconsole_promise_rejected_object.js"]
|
||||||
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
|
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
|
||||||
|
|||||||
@@ -3,101 +3,678 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { JSObjectsTestUtils, CONTEXTS } = ChromeUtils.importESModule(
|
// Check expanding/collapsing object with entries (Maps, Sets, URLSearchParams, …) in the console.
|
||||||
"resource://testing-common/JSObjectsTestUtils.sys.mjs"
|
const TEST_URI = `https://example.com/document-builder.sjs?html=${encodeURIComponent(
|
||||||
);
|
`<!DOCTYPE html><h1>Object Inspector on Object with entries</h1>`
|
||||||
add_setup(function () {
|
)}`;
|
||||||
JSObjectsTestUtils.init(this);
|
|
||||||
});
|
|
||||||
|
|
||||||
const EXPECTED_VALUES_FILE =
|
const { ELLIPSIS } = require("resource://devtools/shared/l10n.js");
|
||||||
"browser_webconsole_object_inspector_entries.snapshot.mjs";
|
|
||||||
|
|
||||||
add_task(async function () {
|
add_task(async function () {
|
||||||
// nsHttpServer does not support https
|
// This will make it so we'll have stable MIDI devices reported
|
||||||
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
await pushPref("midi.testing", true);
|
||||||
const hud = await openNewTabAndConsole("http://example.com");
|
await pushPref("dom.webmidi.enabled", true);
|
||||||
|
await pushPref("midi.prompt.testing", true);
|
||||||
|
await pushPref("media.navigator.permission.disabled", true);
|
||||||
|
// enable custom highlight API
|
||||||
|
await pushPref("dom.customHighlightAPI.enabled", true);
|
||||||
|
// enable custom state
|
||||||
|
await pushPref("dom.element.customstateset.enabled", true);
|
||||||
|
|
||||||
let count = 0;
|
const hud = await openNewTabAndConsole(TEST_URI);
|
||||||
await JSObjectsTestUtils.runTest(
|
|
||||||
EXPECTED_VALUES_FILE,
|
|
||||||
async function ({ context, expression }) {
|
|
||||||
if (context == CONTEXTS.CHROME) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await SpecialPowers.spawn(
|
logAllStoreChanges(hud);
|
||||||
gBrowser.selectedBrowser,
|
|
||||||
[expression, count],
|
const taskResult = await SpecialPowers.spawn(
|
||||||
async function (exp, i) {
|
gBrowser.selectedBrowser,
|
||||||
let value;
|
[],
|
||||||
try {
|
async function () {
|
||||||
value = content.eval(exp);
|
const formData = new content.FormData();
|
||||||
} catch (e) {
|
formData.append("a", 1);
|
||||||
value = e;
|
formData.append("a", 2);
|
||||||
}
|
formData.append("b", 3);
|
||||||
content.console.log("test message " + i, value);
|
|
||||||
}
|
const midiAccess = Cu.waiveXrays(
|
||||||
|
await content.wrappedJSObject.navigator.requestMIDIAccess()
|
||||||
);
|
);
|
||||||
|
|
||||||
const messageNode = await waitFor(() =>
|
content.CSS.highlights.set("search", new content.Highlight());
|
||||||
findConsoleAPIMessage(hud, "test message " + count)
|
content.CSS.highlights.set("glow", new content.Highlight());
|
||||||
|
content.CSS.highlights.set("anchor", new content.Highlight());
|
||||||
|
|
||||||
|
content.customElements.define(
|
||||||
|
"fx-test",
|
||||||
|
class extends content.HTMLElement {}
|
||||||
);
|
);
|
||||||
count++;
|
const { states } = content.document
|
||||||
const oi = messageNode.querySelector(".tree");
|
.createElement("fx-test")
|
||||||
|
.attachInternals();
|
||||||
|
states.add("custom-state");
|
||||||
|
states.add("another-custom-state");
|
||||||
|
|
||||||
if (oi) {
|
content.wrappedJSObject.console.log(
|
||||||
const preview = [];
|
"oi-entries-test",
|
||||||
|
new Map(
|
||||||
|
Array.from({ length: 2 }).map((el, i) => [
|
||||||
|
{ key: i },
|
||||||
|
content.document,
|
||||||
|
])
|
||||||
|
),
|
||||||
|
new Map(Array.from({ length: 20 }).map((el, i) => [Symbol(i), i])),
|
||||||
|
new Map(Array.from({ length: 331 }).map((el, i) => [Symbol(i), i])),
|
||||||
|
new Set(Array.from({ length: 2 }).map((el, i) => ({ value: i }))),
|
||||||
|
new Set(Array.from({ length: 20 }).map((el, i) => i)),
|
||||||
|
new Set(Array.from({ length: 222 }).map((el, i) => i)),
|
||||||
|
new content.URLSearchParams([
|
||||||
|
["a", 1],
|
||||||
|
["a", 2],
|
||||||
|
["b", 3],
|
||||||
|
["b", 3],
|
||||||
|
["b", 5],
|
||||||
|
["c", "this is 6"],
|
||||||
|
["d", 7],
|
||||||
|
["e", 8],
|
||||||
|
["f", 9],
|
||||||
|
["g", 10],
|
||||||
|
["h", 11],
|
||||||
|
]),
|
||||||
|
new content.Headers({ a: 1, b: 2, c: 3 }),
|
||||||
|
formData,
|
||||||
|
midiAccess.inputs,
|
||||||
|
midiAccess.outputs,
|
||||||
|
content.CSS.highlights,
|
||||||
|
states
|
||||||
|
);
|
||||||
|
|
||||||
// Expand the root node, otherwise the object is collapsed by default.
|
return {
|
||||||
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
midi: {
|
||||||
|
inputs: [...midiAccess.inputs.values()].map(input => ({
|
||||||
// Do a first lookup to expand all the "<entries>" nodes,
|
id: input.id,
|
||||||
// as well as their immediate first entries.
|
name: input.name,
|
||||||
// This will generate new ".tree-node"'s.
|
type: input.type,
|
||||||
for (const node of oi.querySelectorAll(".tree-node")) {
|
manufacturer: input.manufacturer,
|
||||||
const label = node.textContent.replace(/\u200B/g, "");
|
})),
|
||||||
|
outputs: [...midiAccess.outputs.values()].map(output => ({
|
||||||
if (label == "<entries>") {
|
id: output.id,
|
||||||
await expandObjectInspectorNode(node);
|
name: output.name,
|
||||||
const firstEntry = node.nextSibling;
|
type: output.type,
|
||||||
if (isObjectInspectorNodeExpandable(firstEntry)) {
|
manufacturer: output.manufacturer,
|
||||||
await expandObjectInspectorNode(firstEntry);
|
})),
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a human-friendly representation of the state of the object inspector
|
|
||||||
for (const node of oi.querySelectorAll(".tree-node")) {
|
|
||||||
const label = node.textContent.replace(/\u200B/g, "");
|
|
||||||
|
|
||||||
let icon = "\u251C "; // "|-" character
|
|
||||||
if (isObjectInspectorNodeExpandable(node)) {
|
|
||||||
icon = node
|
|
||||||
.querySelector(".theme-twisty")
|
|
||||||
.classList.contains("open")
|
|
||||||
? "▼ "
|
|
||||||
: "▶︎ ";
|
|
||||||
}
|
|
||||||
const level = node.getAttribute("aria-level");
|
|
||||||
const indent = " ".repeat(parseInt(level, 10) - 1);
|
|
||||||
|
|
||||||
preview.push(indent + icon + label);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Help debug the test by scrolling to the very last node,
|
|
||||||
// so that the object inspector is fully visible.
|
|
||||||
const nodes = oi.querySelectorAll(".node");
|
|
||||||
const lastNode = nodes[nodes.length - 1];
|
|
||||||
lastNode.scrollIntoView();
|
|
||||||
|
|
||||||
return preview;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a primitive data type, there won't be an object inspector,
|
|
||||||
// but only a simple Rep that we can only stringify.
|
|
||||||
const object = messageNode.querySelectorAll(".message-body > *")[1];
|
|
||||||
return object.textContent;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const node = await waitFor(() =>
|
||||||
|
findConsoleAPIMessage(hud, "oi-entries-test")
|
||||||
|
);
|
||||||
|
const objectInspectors = [...node.querySelectorAll(".tree")];
|
||||||
|
is(
|
||||||
|
objectInspectors.length,
|
||||||
|
13,
|
||||||
|
"There is the expected number of object inspectors"
|
||||||
|
);
|
||||||
|
|
||||||
|
const [
|
||||||
|
smallMapOi,
|
||||||
|
mapOi,
|
||||||
|
largeMapOi,
|
||||||
|
smallSetOi,
|
||||||
|
setOi,
|
||||||
|
largeSetOi,
|
||||||
|
urlSearchParamsOi,
|
||||||
|
headersOi,
|
||||||
|
formDataOi,
|
||||||
|
midiInputsOi,
|
||||||
|
midiOutputsOi,
|
||||||
|
highlightsRegistryOi,
|
||||||
|
customStateSetOi,
|
||||||
|
] = objectInspectors;
|
||||||
|
|
||||||
|
await testSmallMap(smallMapOi);
|
||||||
|
await testMap(mapOi);
|
||||||
|
await testLargeMap(largeMapOi);
|
||||||
|
await testSmallSet(smallSetOi);
|
||||||
|
await testSet(setOi);
|
||||||
|
await testLargeSet(largeSetOi);
|
||||||
|
await testUrlSearchParams(urlSearchParamsOi);
|
||||||
|
await testHeaders(headersOi);
|
||||||
|
await testFormData(formDataOi);
|
||||||
|
await testMidiInputs(midiInputsOi, taskResult.midi.inputs);
|
||||||
|
await testMidiOutputs(midiOutputsOi, taskResult.midi.outputs);
|
||||||
|
await testHighlightsRegistry(highlightsRegistryOi);
|
||||||
|
await testCustomStateSet(customStateSetOi);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function testSmallMap(oi) {
|
||||||
|
info("Expanding the Map");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 4 nodes: the root, size, entries and the proto.
|
||||||
|
is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the map");
|
||||||
|
const entriesNode = oiNodes[2];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are now 6 nodes, the 4 original ones, and the 2 entries.
|
||||||
|
is(oiNodes.length, 6, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
info("Expand first entry");
|
||||||
|
await expandObjectInspectorNode(oiNodes[3]);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
/*
|
||||||
|
* ▼ Map (2)
|
||||||
|
* | size: 2
|
||||||
|
* | ▼ <entries>
|
||||||
|
* | | ▼ 0: {…} -> HTMLDocument
|
||||||
|
* | | | ▶︎ <key>: Object {…}
|
||||||
|
* | | | ▶︎ <value>: HTMLDocument
|
||||||
|
* | | ▶︎ 1: {…} -> HTMLDocument
|
||||||
|
* | ▶︎ <prototype>
|
||||||
|
*/
|
||||||
|
is(oiNodes.length, 8, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
info("Expand <key> for first entry");
|
||||||
|
await expandObjectInspectorNode(oiNodes[4]);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
/*
|
||||||
|
* ▼ Map (2)
|
||||||
|
* | size: 2
|
||||||
|
* | ▼ <entries>
|
||||||
|
* | | ▼ 0: {…} -> HTMLDocument
|
||||||
|
* | | | ▼ <key>: Object {…}
|
||||||
|
* | | | | key: 0
|
||||||
|
* | | | | ▶︎ <prototype>
|
||||||
|
* | | | ▶︎ <value>: HTMLDocument
|
||||||
|
* | | ▶︎ 1: {…} -> HTMLDocument
|
||||||
|
* | ▶︎ <prototype>
|
||||||
|
*/
|
||||||
|
is(oiNodes.length, 10, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
info("Expand <value> for first entry");
|
||||||
|
await expandObjectInspectorNode(oiNodes[7]);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
Assert.greater(oiNodes.length, 10, "The document node was expanded");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testMap(oi) {
|
||||||
|
info("Expanding the Map");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 4 nodes: the root, size, entries and the proto.
|
||||||
|
is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the map");
|
||||||
|
const entriesNode = oiNodes[2];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are now 24 nodes, the 4 original ones, and the 20 entries.
|
||||||
|
is(oiNodes.length, 24, "There is the expected number of nodes in the tree");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testLargeMap(oi) {
|
||||||
|
info("Expanding the large map");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 4 nodes: the root, size, entries and the proto.
|
||||||
|
is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the map");
|
||||||
|
const entriesNode = oiNodes[2];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".node");
|
||||||
|
// There are now 8 nodes, the 4 original ones, and the 4 buckets.
|
||||||
|
is(oiNodes.length, 8, "There is the expected number of nodes in the tree");
|
||||||
|
is(oiNodes[3].textContent, `[0${ELLIPSIS}99]`);
|
||||||
|
is(oiNodes[4].textContent, `[100${ELLIPSIS}199]`);
|
||||||
|
is(oiNodes[5].textContent, `[200${ELLIPSIS}299]`);
|
||||||
|
is(oiNodes[6].textContent, `[300${ELLIPSIS}330]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testSmallSet(oi) {
|
||||||
|
info("Expanding the Set");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 4 nodes: the root, size, entries and the proto.
|
||||||
|
is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the map");
|
||||||
|
const entriesNode = oiNodes[2];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are now 6 nodes, the 4 original ones, and the 2 entries.
|
||||||
|
is(oiNodes.length, 6, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
info("Expand first entry");
|
||||||
|
await expandObjectInspectorNode(oiNodes[3]);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".node");
|
||||||
|
/*
|
||||||
|
* ▼ Set (2)
|
||||||
|
* | size: 2
|
||||||
|
* | ▼ <entries>
|
||||||
|
* | | ▼ 0: {…}
|
||||||
|
* | | | | value: 0
|
||||||
|
* | | | ▶︎ <prototype>
|
||||||
|
* | | ▶︎ 1: {…}
|
||||||
|
* | ▶︎ <prototype>
|
||||||
|
*/
|
||||||
|
is(oiNodes.length, 8, "There is the expected number of nodes in the tree");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testSet(oi) {
|
||||||
|
info("Expanding the Set");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 4 nodes: the root, size, entries and the proto.
|
||||||
|
is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the Set");
|
||||||
|
const entriesNode = oiNodes[2];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".node");
|
||||||
|
// There are now 24 nodes, the 4 original ones, and the 20 entries.
|
||||||
|
is(oiNodes.length, 24, "There is the expected number of nodes in the tree");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testLargeSet(oi) {
|
||||||
|
info("Expanding the large Set");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 4 nodes: the root, size, entries and the proto.
|
||||||
|
is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the Set");
|
||||||
|
const entriesNode = oiNodes[2];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".node");
|
||||||
|
// There are now 7 nodes, the 4 original ones, and the 3 buckets.
|
||||||
|
is(oiNodes.length, 7, "There is the expected number of nodes in the tree");
|
||||||
|
is(oiNodes[3].textContent, `[0${ELLIPSIS}99]`);
|
||||||
|
is(oiNodes[4].textContent, `[100${ELLIPSIS}199]`);
|
||||||
|
is(oiNodes[5].textContent, `[200${ELLIPSIS}221]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testUrlSearchParams(oi) {
|
||||||
|
is(
|
||||||
|
oi.textContent,
|
||||||
|
`URLSearchParams(11) { a → "1", a → "2", b → "3", b → "3", b → "5", c → "this is 6", d → "7", e → "8", f → "9", g → "10", ${ELLIPSIS} }`,
|
||||||
|
"URLSearchParams has expected content"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the URLSearchParams");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 4 nodes: the root, size, entries and the proto.
|
||||||
|
is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
const entriesNode = oiNodes[2];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the URLSearchParams");
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".node");
|
||||||
|
// There are now 14 nodes, the 4 original ones, and the 11 entries.
|
||||||
|
is(oiNodes.length, 15, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
is(
|
||||||
|
oiNodes[3].textContent,
|
||||||
|
`0: a → "1"`,
|
||||||
|
"First entry is displayed as expected"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[4].textContent,
|
||||||
|
`1: a → "2"`,
|
||||||
|
`Second "a" entry is also display although it has the same name as the first entry`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[5].textContent,
|
||||||
|
`2: b → "3"`,
|
||||||
|
`Third entry is the expected one...`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[6].textContent,
|
||||||
|
`3: b → "3"`,
|
||||||
|
`As well as fourth, even though both name and value are similar`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[7].textContent,
|
||||||
|
`4: b → "5"`,
|
||||||
|
`Fifth entry is displayed as expected`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[8].textContent,
|
||||||
|
`5: c → "this is 6"`,
|
||||||
|
`Sixth entry is displayed as expected`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testHeaders(oi) {
|
||||||
|
is(
|
||||||
|
oi.textContent,
|
||||||
|
`Headers(3) { a → "1", b → "2", c → "3" }`,
|
||||||
|
"Headers has expected content"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the Headers");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 3 nodes: the root, entries and the proto.
|
||||||
|
is(oiNodes.length, 3, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
const entriesNode = oiNodes[1];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the Headers");
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".node");
|
||||||
|
// There are now 6 nodes, the 3 original ones, and the 3 entries.
|
||||||
|
is(oiNodes.length, 6, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
is(oiNodes[2].textContent, `a: "1"`, "First entry is displayed as expected");
|
||||||
|
is(
|
||||||
|
oiNodes[3].textContent,
|
||||||
|
`b: "2"`,
|
||||||
|
`Second "a" entry is also display although it has the same name as the first entry`
|
||||||
|
);
|
||||||
|
is(oiNodes[4].textContent, `c: "3"`, `Third entry is the expected one...`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testFormData(oi) {
|
||||||
|
is(
|
||||||
|
oi.textContent,
|
||||||
|
`FormData(3) { a → "1", a → "2", b → "3" }`,
|
||||||
|
"FormData has expected content"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the FormData");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 3 nodes: the root, entries and the proto.
|
||||||
|
is(oiNodes.length, 3, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
const entriesNode = oiNodes[1];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the FormData");
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".node");
|
||||||
|
// There are now 6 nodes, the 3 original ones, and the 3 entries.
|
||||||
|
is(oiNodes.length, 6, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
is(
|
||||||
|
oiNodes[2].textContent,
|
||||||
|
`0: a → "1"`,
|
||||||
|
"First entry is displayed as expected"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[3].textContent,
|
||||||
|
`1: a → "2"`,
|
||||||
|
`Second "a" entry is also display although it has the same name as the first entry`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[4].textContent,
|
||||||
|
`2: b → "3"`,
|
||||||
|
`Third entry entry is displayed as expected`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testMidiInputs(oi, midiInputs) {
|
||||||
|
const [input] = midiInputs;
|
||||||
|
is(
|
||||||
|
oi.textContent,
|
||||||
|
`MIDIInputMap { "${input.id}" → MIDIInput }`,
|
||||||
|
"MIDIInputMap has expected content"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the MIDIInputMap");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 4 nodes: the root, size, entries and the proto.
|
||||||
|
is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
const entriesNode = oiNodes[2];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the MIDIInputMap");
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".node");
|
||||||
|
// There are now 5 nodes, the 4 original ones, and the entry.
|
||||||
|
is(oiNodes.length, 5, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
is(
|
||||||
|
oiNodes[3].textContent,
|
||||||
|
`"${input.id}": MIDIInput { id: "${input.id}", manufacturer: "${input.manufacturer}", name: "${input.name}", ${ELLIPSIS} }`,
|
||||||
|
"First entry is displayed as expected"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testMidiOutputs(oi, midiOutputs) {
|
||||||
|
is(
|
||||||
|
oi.textContent,
|
||||||
|
`MIDIOutputMap(3) { "${midiOutputs[0].id}" → MIDIOutput, "${midiOutputs[1].id}" → MIDIOutput, "${midiOutputs[2].id}" → MIDIOutput }`,
|
||||||
|
"MIDIOutputMap has expected content"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the MIDIOutputMap");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 4 nodes: the root, size, entries and the proto.
|
||||||
|
is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
const entriesNode = oiNodes[2];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the MIDIOutputMap");
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".node");
|
||||||
|
// There are now 7 nodes, the 4 original ones, and the 3 entries.
|
||||||
|
is(oiNodes.length, 7, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
is(
|
||||||
|
oiNodes[3].textContent,
|
||||||
|
`"${midiOutputs[0].id}": MIDIOutput { id: "${midiOutputs[0].id}", manufacturer: "${midiOutputs[0].manufacturer}", name: "${midiOutputs[0].name}", ${ELLIPSIS} }`,
|
||||||
|
"First entry is displayed as expected"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[4].textContent,
|
||||||
|
`"${midiOutputs[1].id}": MIDIOutput { id: "${midiOutputs[1].id}", manufacturer: "${midiOutputs[1].manufacturer}", name: "${midiOutputs[1].name}", ${ELLIPSIS} }`,
|
||||||
|
"Second entry is displayed as expected"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[5].textContent,
|
||||||
|
`"${midiOutputs[2].id}": MIDIOutput { id: "${midiOutputs[2].id}", manufacturer: "${midiOutputs[2].manufacturer}", name: "${midiOutputs[2].name}", ${ELLIPSIS} }`,
|
||||||
|
"Third entry is displayed as expected"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testHighlightsRegistry(oi) {
|
||||||
|
is(
|
||||||
|
oi.textContent,
|
||||||
|
`HighlightRegistry(3) { search → Highlight, glow → Highlight, anchor → Highlight }`,
|
||||||
|
"HighlightRegistry has expected content"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the HighlightRegistry");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 4 nodes: the root, size, entries and the proto.
|
||||||
|
is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
const entriesNode = oiNodes[2];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the HighlightRegistry");
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are now 7 nodes, the 4 original ones, and the 3 entries.
|
||||||
|
is(oiNodes.length, 7, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
is(
|
||||||
|
oiNodes[3].querySelector(".node").textContent,
|
||||||
|
`0: search → Highlight { priority: 0, type: "highlight", size: 0 }`,
|
||||||
|
"First entry is displayed as expected"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[4].querySelector(".node").textContent,
|
||||||
|
`1: glow → Highlight { priority: 0, type: "highlight", size: 0 }`,
|
||||||
|
`Second entry is displayed as expected`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[5].querySelector(".node").textContent,
|
||||||
|
`2: anchor → Highlight { priority: 0, type: "highlight", size: 0 }`,
|
||||||
|
`Third entry entry is displayed as expected`
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expand last entry");
|
||||||
|
await expandObjectInspectorNode(oiNodes[5]);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are now 9 nodes, the 7 original ones, <key> and <value>
|
||||||
|
is(oiNodes.length, 9, "There is the expected number of nodes in the tree");
|
||||||
|
is(
|
||||||
|
oiNodes[6].querySelector(".node").textContent,
|
||||||
|
`<key>: "anchor"`,
|
||||||
|
`Got expected key node`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[7].querySelector(".node").textContent,
|
||||||
|
`<value>: Highlight { priority: 0, type: "highlight", size: 0 }`,
|
||||||
|
`Got expected value node`
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Expand Highlight object");
|
||||||
|
await expandObjectInspectorNode(oiNodes[7]);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".node");
|
||||||
|
// There are now 13 nodes, the 9 previous ones, and all the properties of the Highlight object
|
||||||
|
is(oiNodes.length, 13, "There is the expected number of nodes in the tree");
|
||||||
|
is(oiNodes[8].textContent, `priority: 0`, `Got expected priority property`);
|
||||||
|
is(oiNodes[9].textContent, `size: 0`, `Got expected size property`);
|
||||||
|
is(
|
||||||
|
oiNodes[10].textContent,
|
||||||
|
`type: "highlight"`,
|
||||||
|
`Got expected type property`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[11].textContent,
|
||||||
|
`<prototype>: HighlightPrototype { add: add(), clear: clear(), delete: delete(), … }`,
|
||||||
|
`Got expected prototype property`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testCustomStateSet(oi) {
|
||||||
|
info("Expanding the CustomStateSet");
|
||||||
|
await expandObjectInspectorNode(oi.querySelector(".tree-node"));
|
||||||
|
|
||||||
|
let oiNodes = oi.querySelectorAll(".tree-node");
|
||||||
|
// There are 4 nodes: the root, size, entries and the proto.
|
||||||
|
is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
info("Expanding the <entries> leaf of the map");
|
||||||
|
|
||||||
|
const entriesNode = oiNodes[2];
|
||||||
|
is(
|
||||||
|
entriesNode.querySelector(".node").textContent,
|
||||||
|
"<entries>",
|
||||||
|
"There is the expected <entries> node"
|
||||||
|
);
|
||||||
|
await expandObjectInspectorNode(entriesNode);
|
||||||
|
|
||||||
|
oiNodes = oi.querySelectorAll(".node");
|
||||||
|
// There are now 6 nodes, the 4 original ones, and the 2 entries.
|
||||||
|
is(oiNodes.length, 6, "There is the expected number of nodes in the tree");
|
||||||
|
|
||||||
|
is(
|
||||||
|
oiNodes[3].textContent,
|
||||||
|
`0: "custom-state"`,
|
||||||
|
`Got expected first entry item`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
oiNodes[4].textContent,
|
||||||
|
`1: "another-custom-state"`,
|
||||||
|
`Got expected second entry item`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,761 +0,0 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND.
|
|
||||||
*
|
|
||||||
* More info in https://firefox-source-docs.mozilla.org/devtools/tests/js-object-tests.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default [
|
|
||||||
// undefined
|
|
||||||
"undefined",
|
|
||||||
|
|
||||||
// null
|
|
||||||
"null",
|
|
||||||
|
|
||||||
// true
|
|
||||||
"true",
|
|
||||||
|
|
||||||
// false
|
|
||||||
"false",
|
|
||||||
|
|
||||||
// NaN
|
|
||||||
"NaN",
|
|
||||||
|
|
||||||
// "abc"
|
|
||||||
"abc",
|
|
||||||
|
|
||||||
// "鼬ú"
|
|
||||||
"鼬ú",
|
|
||||||
|
|
||||||
// 42
|
|
||||||
"42",
|
|
||||||
|
|
||||||
// -42
|
|
||||||
"-42",
|
|
||||||
|
|
||||||
// -0
|
|
||||||
"-0",
|
|
||||||
|
|
||||||
// Infinity
|
|
||||||
"Infinity",
|
|
||||||
|
|
||||||
// BigInt(1000000000000000000)
|
|
||||||
"1000000000000000000n",
|
|
||||||
|
|
||||||
// 1n
|
|
||||||
"1n",
|
|
||||||
|
|
||||||
// -2n
|
|
||||||
"-2n",
|
|
||||||
|
|
||||||
// 0n
|
|
||||||
"0n",
|
|
||||||
|
|
||||||
// ({})
|
|
||||||
[
|
|
||||||
"▼ Object { }",
|
|
||||||
" ▶︎ <prototype>: Object { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// ({ foo: "bar"})
|
|
||||||
[
|
|
||||||
"▼ Object { foo: \"bar\" }",
|
|
||||||
" ├ foo: \"bar\"",
|
|
||||||
" ▶︎ <prototype>: Object { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// []
|
|
||||||
[
|
|
||||||
"▼ Array []",
|
|
||||||
" ├ length: 0",
|
|
||||||
" ▶︎ <prototype>: Array []"
|
|
||||||
],
|
|
||||||
|
|
||||||
// [1]
|
|
||||||
[
|
|
||||||
"▼ Array [ 1 ]",
|
|
||||||
" ├ 0: 1",
|
|
||||||
" ├ length: 1",
|
|
||||||
" ▶︎ <prototype>: Array []"
|
|
||||||
],
|
|
||||||
|
|
||||||
// ["foo"]
|
|
||||||
[
|
|
||||||
"▼ Array [ \"foo\" ]",
|
|
||||||
" ├ 0: \"foo\"",
|
|
||||||
" ├ length: 1",
|
|
||||||
" ▶︎ <prototype>: Array []"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new BigInt64Array()
|
|
||||||
[
|
|
||||||
"▼ BigInt64Array []",
|
|
||||||
" ▶︎ buffer: ArrayBuffer { byteLength: 0 }",
|
|
||||||
" ├ byteLength: 0",
|
|
||||||
" ├ byteOffset: 0",
|
|
||||||
" ├ length: 0",
|
|
||||||
" ▶︎ <prototype>: BigInt64Array.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// const a = new BigInt64Array(1);
|
|
||||||
// a[0] = BigInt(42);
|
|
||||||
// a;
|
|
||||||
//
|
|
||||||
[
|
|
||||||
"▼ BigInt64Array [ 42n ]",
|
|
||||||
" ├ 0: 42n",
|
|
||||||
" ▶︎ buffer: ArrayBuffer { byteLength: 8 }",
|
|
||||||
" ├ byteLength: 8",
|
|
||||||
" ├ byteOffset: 0",
|
|
||||||
" ├ length: 1",
|
|
||||||
" ▶︎ <prototype>: BigInt64Array.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Map(
|
|
||||||
// Array.from({ length: 2 }).map((el, i) => [
|
|
||||||
// { key: i },
|
|
||||||
// { object: 42 },
|
|
||||||
// ])
|
|
||||||
// )
|
|
||||||
[
|
|
||||||
"▼ Map { {…} → {…}, {…} → {…} }",
|
|
||||||
" ├ size: 2",
|
|
||||||
" ▼ <entries>",
|
|
||||||
" ▼ 0: Object { key: 0 } → Object { object: 42 }",
|
|
||||||
" ▶︎ <key>: Object { key: 0 }",
|
|
||||||
" ▶︎ <value>: Object { object: 42 }",
|
|
||||||
" ▶︎ 1: Object { key: 1 } → Object { object: 42 }",
|
|
||||||
" ▶︎ <prototype>: Map.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Map(Array.from({ length: 20 }).map((el, i) => [Symbol(i), i]))
|
|
||||||
[
|
|
||||||
"▼ Map(20) { Symbol(\"0\") → 0, Symbol(\"1\") → 1, Symbol(\"2\") → 2, Symbol(\"3\") → 3, Symbol(\"4\") → 4, Symbol(\"5\") → 5, Symbol(\"6\") → 6, Symbol(\"7\") → 7, Symbol(\"8\") → 8, Symbol(\"9\") → 9, … }",
|
|
||||||
" ├ size: 20",
|
|
||||||
" ▼ <entries>",
|
|
||||||
" ▼ 0: Symbol(\"0\") → 0",
|
|
||||||
" ├ <key>: Symbol(\"0\")",
|
|
||||||
" ├ <value>: 0",
|
|
||||||
" ▶︎ 1: Symbol(\"1\") → 1",
|
|
||||||
" ▶︎ 2: Symbol(\"2\") → 2",
|
|
||||||
" ▶︎ 3: Symbol(\"3\") → 3",
|
|
||||||
" ▶︎ 4: Symbol(\"4\") → 4",
|
|
||||||
" ▶︎ 5: Symbol(\"5\") → 5",
|
|
||||||
" ▶︎ 6: Symbol(\"6\") → 6",
|
|
||||||
" ▶︎ 7: Symbol(\"7\") → 7",
|
|
||||||
" ▶︎ 8: Symbol(\"8\") → 8",
|
|
||||||
" ▶︎ 9: Symbol(\"9\") → 9",
|
|
||||||
" ▶︎ 10: Symbol(\"10\") → 10",
|
|
||||||
" ▶︎ 11: Symbol(\"11\") → 11",
|
|
||||||
" ▶︎ 12: Symbol(\"12\") → 12",
|
|
||||||
" ▶︎ 13: Symbol(\"13\") → 13",
|
|
||||||
" ▶︎ 14: Symbol(\"14\") → 14",
|
|
||||||
" ▶︎ 15: Symbol(\"15\") → 15",
|
|
||||||
" ▶︎ 16: Symbol(\"16\") → 16",
|
|
||||||
" ▶︎ 17: Symbol(\"17\") → 17",
|
|
||||||
" ▶︎ 18: Symbol(\"18\") → 18",
|
|
||||||
" ▶︎ 19: Symbol(\"19\") → 19",
|
|
||||||
" ▶︎ <prototype>: Map.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Map(Array.from({ length: 331 }).map((el, i) => [Symbol(i), i]))
|
|
||||||
[
|
|
||||||
"▼ Map(331) { Symbol(\"0\") → 0, Symbol(\"1\") → 1, Symbol(\"2\") → 2, Symbol(\"3\") → 3, Symbol(\"4\") → 4, Symbol(\"5\") → 5, Symbol(\"6\") → 6, Symbol(\"7\") → 7, Symbol(\"8\") → 8, Symbol(\"9\") → 9, … }",
|
|
||||||
" ├ size: 331",
|
|
||||||
" ▼ <entries>",
|
|
||||||
" ▼ [0…99]",
|
|
||||||
" ▶︎ 0: Symbol(\"0\") → 0",
|
|
||||||
" ▶︎ 1: Symbol(\"1\") → 1",
|
|
||||||
" ▶︎ 2: Symbol(\"2\") → 2",
|
|
||||||
" ▶︎ 3: Symbol(\"3\") → 3",
|
|
||||||
" ▶︎ 4: Symbol(\"4\") → 4",
|
|
||||||
" ▶︎ 5: Symbol(\"5\") → 5",
|
|
||||||
" ▶︎ 6: Symbol(\"6\") → 6",
|
|
||||||
" ▶︎ 7: Symbol(\"7\") → 7",
|
|
||||||
" ▶︎ 8: Symbol(\"8\") → 8",
|
|
||||||
" ▶︎ 9: Symbol(\"9\") → 9",
|
|
||||||
" ▶︎ 10: Symbol(\"10\") → 10",
|
|
||||||
" ▶︎ 11: Symbol(\"11\") → 11",
|
|
||||||
" ▶︎ 12: Symbol(\"12\") → 12",
|
|
||||||
" ▶︎ 13: Symbol(\"13\") → 13",
|
|
||||||
" ▶︎ 14: Symbol(\"14\") → 14",
|
|
||||||
" ▶︎ 15: Symbol(\"15\") → 15",
|
|
||||||
" ▶︎ 16: Symbol(\"16\") → 16",
|
|
||||||
" ▶︎ 17: Symbol(\"17\") → 17",
|
|
||||||
" ▶︎ 18: Symbol(\"18\") → 18",
|
|
||||||
" ▶︎ 19: Symbol(\"19\") → 19",
|
|
||||||
" ▶︎ 20: Symbol(\"20\") → 20",
|
|
||||||
" ▶︎ 21: Symbol(\"21\") → 21",
|
|
||||||
" ▶︎ 22: Symbol(\"22\") → 22",
|
|
||||||
" ▶︎ 23: Symbol(\"23\") → 23",
|
|
||||||
" ▶︎ 24: Symbol(\"24\") → 24",
|
|
||||||
" ▶︎ 25: Symbol(\"25\") → 25",
|
|
||||||
" ▶︎ 26: Symbol(\"26\") → 26",
|
|
||||||
" ▶︎ 27: Symbol(\"27\") → 27",
|
|
||||||
" ▶︎ 28: Symbol(\"28\") → 28",
|
|
||||||
" ▶︎ 29: Symbol(\"29\") → 29",
|
|
||||||
" ▶︎ 30: Symbol(\"30\") → 30",
|
|
||||||
" ▶︎ 31: Symbol(\"31\") → 31",
|
|
||||||
" ▶︎ 32: Symbol(\"32\") → 32",
|
|
||||||
" ▶︎ 33: Symbol(\"33\") → 33",
|
|
||||||
" ▶︎ 34: Symbol(\"34\") → 34",
|
|
||||||
" ▶︎ 35: Symbol(\"35\") → 35",
|
|
||||||
" ▶︎ 36: Symbol(\"36\") → 36",
|
|
||||||
" ▶︎ 37: Symbol(\"37\") → 37",
|
|
||||||
" ▶︎ 38: Symbol(\"38\") → 38",
|
|
||||||
" ▶︎ 39: Symbol(\"39\") → 39",
|
|
||||||
" ▶︎ 40: Symbol(\"40\") → 40",
|
|
||||||
" ▶︎ 41: Symbol(\"41\") → 41",
|
|
||||||
" ▶︎ 42: Symbol(\"42\") → 42",
|
|
||||||
" ▶︎ 43: Symbol(\"43\") → 43",
|
|
||||||
" ▶︎ 44: Symbol(\"44\") → 44",
|
|
||||||
" ▶︎ 45: Symbol(\"45\") → 45",
|
|
||||||
" ▶︎ 46: Symbol(\"46\") → 46",
|
|
||||||
" ▶︎ 47: Symbol(\"47\") → 47",
|
|
||||||
" ▶︎ 48: Symbol(\"48\") → 48",
|
|
||||||
" ▶︎ 49: Symbol(\"49\") → 49",
|
|
||||||
" ▶︎ 50: Symbol(\"50\") → 50",
|
|
||||||
" ▶︎ 51: Symbol(\"51\") → 51",
|
|
||||||
" ▶︎ 52: Symbol(\"52\") → 52",
|
|
||||||
" ▶︎ 53: Symbol(\"53\") → 53",
|
|
||||||
" ▶︎ 54: Symbol(\"54\") → 54",
|
|
||||||
" ▶︎ 55: Symbol(\"55\") → 55",
|
|
||||||
" ▶︎ 56: Symbol(\"56\") → 56",
|
|
||||||
" ▶︎ 57: Symbol(\"57\") → 57",
|
|
||||||
" ▶︎ 58: Symbol(\"58\") → 58",
|
|
||||||
" ▶︎ 59: Symbol(\"59\") → 59",
|
|
||||||
" ▶︎ 60: Symbol(\"60\") → 60",
|
|
||||||
" ▶︎ 61: Symbol(\"61\") → 61",
|
|
||||||
" ▶︎ 62: Symbol(\"62\") → 62",
|
|
||||||
" ▶︎ 63: Symbol(\"63\") → 63",
|
|
||||||
" ▶︎ 64: Symbol(\"64\") → 64",
|
|
||||||
" ▶︎ 65: Symbol(\"65\") → 65",
|
|
||||||
" ▶︎ 66: Symbol(\"66\") → 66",
|
|
||||||
" ▶︎ 67: Symbol(\"67\") → 67",
|
|
||||||
" ▶︎ 68: Symbol(\"68\") → 68",
|
|
||||||
" ▶︎ 69: Symbol(\"69\") → 69",
|
|
||||||
" ▶︎ 70: Symbol(\"70\") → 70",
|
|
||||||
" ▶︎ 71: Symbol(\"71\") → 71",
|
|
||||||
" ▶︎ 72: Symbol(\"72\") → 72",
|
|
||||||
" ▶︎ 73: Symbol(\"73\") → 73",
|
|
||||||
" ▶︎ 74: Symbol(\"74\") → 74",
|
|
||||||
" ▶︎ 75: Symbol(\"75\") → 75",
|
|
||||||
" ▶︎ 76: Symbol(\"76\") → 76",
|
|
||||||
" ▶︎ 77: Symbol(\"77\") → 77",
|
|
||||||
" ▶︎ 78: Symbol(\"78\") → 78",
|
|
||||||
" ▶︎ 79: Symbol(\"79\") → 79",
|
|
||||||
" ▶︎ 80: Symbol(\"80\") → 80",
|
|
||||||
" ▶︎ 81: Symbol(\"81\") → 81",
|
|
||||||
" ▶︎ 82: Symbol(\"82\") → 82",
|
|
||||||
" ▶︎ 83: Symbol(\"83\") → 83",
|
|
||||||
" ▶︎ 84: Symbol(\"84\") → 84",
|
|
||||||
" ▶︎ 85: Symbol(\"85\") → 85",
|
|
||||||
" ▶︎ 86: Symbol(\"86\") → 86",
|
|
||||||
" ▶︎ 87: Symbol(\"87\") → 87",
|
|
||||||
" ▶︎ 88: Symbol(\"88\") → 88",
|
|
||||||
" ▶︎ 89: Symbol(\"89\") → 89",
|
|
||||||
" ▶︎ 90: Symbol(\"90\") → 90",
|
|
||||||
" ▶︎ 91: Symbol(\"91\") → 91",
|
|
||||||
" ▶︎ 92: Symbol(\"92\") → 92",
|
|
||||||
" ▶︎ 93: Symbol(\"93\") → 93",
|
|
||||||
" ▶︎ 94: Symbol(\"94\") → 94",
|
|
||||||
" ▶︎ 95: Symbol(\"95\") → 95",
|
|
||||||
" ▶︎ 96: Symbol(\"96\") → 96",
|
|
||||||
" ▶︎ 97: Symbol(\"97\") → 97",
|
|
||||||
" ▶︎ 98: Symbol(\"98\") → 98",
|
|
||||||
" ▶︎ 99: Symbol(\"99\") → 99",
|
|
||||||
" ▶︎ [100…199]",
|
|
||||||
" ▶︎ [200…299]",
|
|
||||||
" ▶︎ [300…330]",
|
|
||||||
" ▶︎ <prototype>: Map.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Set(Array.from({ length: 2 }).map((el, i) => ({ value: i })))
|
|
||||||
[
|
|
||||||
"▼ Set [ {…}, {…} ]",
|
|
||||||
" ├ size: 2",
|
|
||||||
" ▼ <entries>",
|
|
||||||
" ▼ 0: Object { value: 0 }",
|
|
||||||
" ├ value: 0",
|
|
||||||
" ▶︎ <prototype>: Object { … }",
|
|
||||||
" ▶︎ 1: Object { value: 1 }",
|
|
||||||
" ▶︎ <prototype>: Set.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Set(Array.from({ length: 20 }).map((el, i) => i))
|
|
||||||
[
|
|
||||||
"▼ Set(20) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, … ]",
|
|
||||||
" ├ size: 20",
|
|
||||||
" ▼ <entries>",
|
|
||||||
" ├ 0: 0",
|
|
||||||
" ├ 1: 1",
|
|
||||||
" ├ 2: 2",
|
|
||||||
" ├ 3: 3",
|
|
||||||
" ├ 4: 4",
|
|
||||||
" ├ 5: 5",
|
|
||||||
" ├ 6: 6",
|
|
||||||
" ├ 7: 7",
|
|
||||||
" ├ 8: 8",
|
|
||||||
" ├ 9: 9",
|
|
||||||
" ├ 10: 10",
|
|
||||||
" ├ 11: 11",
|
|
||||||
" ├ 12: 12",
|
|
||||||
" ├ 13: 13",
|
|
||||||
" ├ 14: 14",
|
|
||||||
" ├ 15: 15",
|
|
||||||
" ├ 16: 16",
|
|
||||||
" ├ 17: 17",
|
|
||||||
" ├ 18: 18",
|
|
||||||
" ├ 19: 19",
|
|
||||||
" ▶︎ <prototype>: Set.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Set(Array.from({ length: 222 }).map((el, i) => i))
|
|
||||||
[
|
|
||||||
"▼ Set(222) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, … ]",
|
|
||||||
" ├ size: 222",
|
|
||||||
" ▼ <entries>",
|
|
||||||
" ▼ [0…99]",
|
|
||||||
" ├ 0: 0",
|
|
||||||
" ├ 1: 1",
|
|
||||||
" ├ 2: 2",
|
|
||||||
" ├ 3: 3",
|
|
||||||
" ├ 4: 4",
|
|
||||||
" ├ 5: 5",
|
|
||||||
" ├ 6: 6",
|
|
||||||
" ├ 7: 7",
|
|
||||||
" ├ 8: 8",
|
|
||||||
" ├ 9: 9",
|
|
||||||
" ├ 10: 10",
|
|
||||||
" ├ 11: 11",
|
|
||||||
" ├ 12: 12",
|
|
||||||
" ├ 13: 13",
|
|
||||||
" ├ 14: 14",
|
|
||||||
" ├ 15: 15",
|
|
||||||
" ├ 16: 16",
|
|
||||||
" ├ 17: 17",
|
|
||||||
" ├ 18: 18",
|
|
||||||
" ├ 19: 19",
|
|
||||||
" ├ 20: 20",
|
|
||||||
" ├ 21: 21",
|
|
||||||
" ├ 22: 22",
|
|
||||||
" ├ 23: 23",
|
|
||||||
" ├ 24: 24",
|
|
||||||
" ├ 25: 25",
|
|
||||||
" ├ 26: 26",
|
|
||||||
" ├ 27: 27",
|
|
||||||
" ├ 28: 28",
|
|
||||||
" ├ 29: 29",
|
|
||||||
" ├ 30: 30",
|
|
||||||
" ├ 31: 31",
|
|
||||||
" ├ 32: 32",
|
|
||||||
" ├ 33: 33",
|
|
||||||
" ├ 34: 34",
|
|
||||||
" ├ 35: 35",
|
|
||||||
" ├ 36: 36",
|
|
||||||
" ├ 37: 37",
|
|
||||||
" ├ 38: 38",
|
|
||||||
" ├ 39: 39",
|
|
||||||
" ├ 40: 40",
|
|
||||||
" ├ 41: 41",
|
|
||||||
" ├ 42: 42",
|
|
||||||
" ├ 43: 43",
|
|
||||||
" ├ 44: 44",
|
|
||||||
" ├ 45: 45",
|
|
||||||
" ├ 46: 46",
|
|
||||||
" ├ 47: 47",
|
|
||||||
" ├ 48: 48",
|
|
||||||
" ├ 49: 49",
|
|
||||||
" ├ 50: 50",
|
|
||||||
" ├ 51: 51",
|
|
||||||
" ├ 52: 52",
|
|
||||||
" ├ 53: 53",
|
|
||||||
" ├ 54: 54",
|
|
||||||
" ├ 55: 55",
|
|
||||||
" ├ 56: 56",
|
|
||||||
" ├ 57: 57",
|
|
||||||
" ├ 58: 58",
|
|
||||||
" ├ 59: 59",
|
|
||||||
" ├ 60: 60",
|
|
||||||
" ├ 61: 61",
|
|
||||||
" ├ 62: 62",
|
|
||||||
" ├ 63: 63",
|
|
||||||
" ├ 64: 64",
|
|
||||||
" ├ 65: 65",
|
|
||||||
" ├ 66: 66",
|
|
||||||
" ├ 67: 67",
|
|
||||||
" ├ 68: 68",
|
|
||||||
" ├ 69: 69",
|
|
||||||
" ├ 70: 70",
|
|
||||||
" ├ 71: 71",
|
|
||||||
" ├ 72: 72",
|
|
||||||
" ├ 73: 73",
|
|
||||||
" ├ 74: 74",
|
|
||||||
" ├ 75: 75",
|
|
||||||
" ├ 76: 76",
|
|
||||||
" ├ 77: 77",
|
|
||||||
" ├ 78: 78",
|
|
||||||
" ├ 79: 79",
|
|
||||||
" ├ 80: 80",
|
|
||||||
" ├ 81: 81",
|
|
||||||
" ├ 82: 82",
|
|
||||||
" ├ 83: 83",
|
|
||||||
" ├ 84: 84",
|
|
||||||
" ├ 85: 85",
|
|
||||||
" ├ 86: 86",
|
|
||||||
" ├ 87: 87",
|
|
||||||
" ├ 88: 88",
|
|
||||||
" ├ 89: 89",
|
|
||||||
" ├ 90: 90",
|
|
||||||
" ├ 91: 91",
|
|
||||||
" ├ 92: 92",
|
|
||||||
" ├ 93: 93",
|
|
||||||
" ├ 94: 94",
|
|
||||||
" ├ 95: 95",
|
|
||||||
" ├ 96: 96",
|
|
||||||
" ├ 97: 97",
|
|
||||||
" ├ 98: 98",
|
|
||||||
" ├ 99: 99",
|
|
||||||
" ▶︎ [100…199]",
|
|
||||||
" ▶︎ [200…221]",
|
|
||||||
" ▶︎ <prototype>: Set.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Temporal.Instant(355924804000000000n)
|
|
||||||
[
|
|
||||||
"▼ Temporal.Instant 1981-04-12T12:00:04Z",
|
|
||||||
" ▶︎ <prototype>: Object { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Temporal.PlainDate(2021, 7, 1, "coptic")
|
|
||||||
[
|
|
||||||
"▼ Temporal.PlainDate 2021-07-01[u-ca=coptic]",
|
|
||||||
" ▶︎ <prototype>: Object { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Temporal.PlainDateTime(2021, 7, 1, 0, 0, 0, 0, 0, 0, "gregory")
|
|
||||||
[
|
|
||||||
"▼ Temporal.PlainDateTime 2021-07-01T00:00:00[u-ca=gregory]",
|
|
||||||
" ▶︎ <prototype>: Object { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Temporal.PlainMonthDay(7, 1, "chinese")
|
|
||||||
[
|
|
||||||
"▼ Temporal.PlainMonthDay 1972-07-01[u-ca=chinese]",
|
|
||||||
" ▶︎ <prototype>: Object { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Temporal.PlainTime(4, 20)
|
|
||||||
[
|
|
||||||
"▼ Temporal.PlainTime 04:20:00",
|
|
||||||
" ▶︎ <prototype>: Object { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Temporal.PlainYearMonth(2021, 7, "indian")
|
|
||||||
[
|
|
||||||
"▼ Temporal.PlainYearMonth 2021-07-01[u-ca=indian]",
|
|
||||||
" ▶︎ <prototype>: Object { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Temporal.ZonedDateTime(0n, "America/New_York")
|
|
||||||
[
|
|
||||||
"▼ Temporal.ZonedDateTime 1969-12-31T19:00:00-05:00[America/New_York]",
|
|
||||||
" ▶︎ <prototype>: Object { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// Temporal.Duration.from({ years: 1 })
|
|
||||||
[
|
|
||||||
"▼ Temporal.Duration P1Y",
|
|
||||||
" ▶︎ <prototype>: Object { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// myPolicy.createHTML("hello")
|
|
||||||
[
|
|
||||||
"▼ TrustedHTML \"<my-policy>hello</my-policy>\"",
|
|
||||||
" ▶︎ <prototype>: TrustedHTMLPrototype { toJSON: toJSON(), toString: toString(), … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// myPolicy.createScript("const hello = 'world'")
|
|
||||||
[
|
|
||||||
"▼ TrustedScript \"/* myPolicy */ const hello = 'world'\"",
|
|
||||||
" ▶︎ <prototype>: TrustedScriptPrototype { toJSON: toJSON(), toString: toString(), … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// myPolicy.createScriptURL("https://example.com/trusted")
|
|
||||||
[
|
|
||||||
"▼ TrustedScriptURL https://example.com/trusted?myPolicy",
|
|
||||||
" ▶︎ <prototype>: TrustedScriptURLPrototype { toJSON: toJSON(), toString: toString(), … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// const formData = new FormData();
|
|
||||||
// formData.append("a", 1);
|
|
||||||
// formData.append("a", 2);
|
|
||||||
// formData.append("b", 3);
|
|
||||||
// formData;
|
|
||||||
//
|
|
||||||
[
|
|
||||||
"▼ FormData(3) { a → \"1\", a → \"2\", b → \"3\" }",
|
|
||||||
" ▼ <entries>",
|
|
||||||
" ▼ 0: a → \"1\"",
|
|
||||||
" ├ <key>: \"a\"",
|
|
||||||
" ├ <value>: \"1\"",
|
|
||||||
" ▶︎ 1: a → \"2\"",
|
|
||||||
" ▶︎ 2: b → \"3\"",
|
|
||||||
" ▶︎ <prototype>: FormDataPrototype { append: append(), delete: delete(), get: get(), … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// customElements.define("fx-test", class extends HTMLElement {});
|
|
||||||
// const { states } = document.createElement("fx-test").attachInternals();
|
|
||||||
// states.add("custom-state");
|
|
||||||
// states.add("another-custom-state");
|
|
||||||
// states;
|
|
||||||
//
|
|
||||||
[
|
|
||||||
"▼ CustomStateSet [ \"custom-state\", \"another-custom-state\" ]",
|
|
||||||
" ├ size: 2",
|
|
||||||
" ▼ <entries>",
|
|
||||||
" ├ 0: \"custom-state\"",
|
|
||||||
" ├ 1: \"another-custom-state\"",
|
|
||||||
" ▶︎ <prototype>: CustomStateSetPrototype { add: add(), delete: delete(), clear: clear(), … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// CSS.highlights.set("search", new Highlight());
|
|
||||||
// CSS.highlights.set("glow", new Highlight());
|
|
||||||
// CSS.highlights.set("anchor", new Highlight());
|
|
||||||
// CSS.highlights;
|
|
||||||
//
|
|
||||||
[
|
|
||||||
"▼ HighlightRegistry(3) { search → Highlight, glow → Highlight, anchor → Highlight }",
|
|
||||||
" ├ size: 3",
|
|
||||||
" ▼ <entries>",
|
|
||||||
" ▼ 0: search → Highlight { priority: 0, type: \"highlight\", size: 0 }",
|
|
||||||
" ├ <key>: \"search\"",
|
|
||||||
" ▶︎ <value>: Highlight { priority: 0, type: \"highlight\", size: 0 }",
|
|
||||||
" ▶︎ 1: glow → Highlight { priority: 0, type: \"highlight\", size: 0 }",
|
|
||||||
" ▶︎ 2: anchor → Highlight { priority: 0, type: \"highlight\", size: 0 }",
|
|
||||||
" ▶︎ <prototype>: HighlightRegistryPrototype { set: set(), clear: clear(), delete: delete(), … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new URLSearchParams([
|
|
||||||
// ["a", 1],
|
|
||||||
// ["a", 2],
|
|
||||||
// ["b", 3],
|
|
||||||
// ["b", 3],
|
|
||||||
// ["b", 5],
|
|
||||||
// ["c", "this is 6"],
|
|
||||||
// ["d", 7],
|
|
||||||
// ["e", 8],
|
|
||||||
// ["f", 9],
|
|
||||||
// ["g", 10],
|
|
||||||
// ["h", 11],
|
|
||||||
// ])
|
|
||||||
[
|
|
||||||
"▼ URLSearchParams(11) { a → \"1\", a → \"2\", b → \"3\", b → \"3\", b → \"5\", c → \"this is 6\", d → \"7\", e → \"8\", f → \"9\", g → \"10\", … }",
|
|
||||||
" ├ size: 11",
|
|
||||||
" ▼ <entries>",
|
|
||||||
" ▼ 0: a → \"1\"",
|
|
||||||
" ├ <key>: \"a\"",
|
|
||||||
" ├ <value>: \"1\"",
|
|
||||||
" ▶︎ 1: a → \"2\"",
|
|
||||||
" ▶︎ 2: b → \"3\"",
|
|
||||||
" ▶︎ 3: b → \"3\"",
|
|
||||||
" ▶︎ 4: b → \"5\"",
|
|
||||||
" ▶︎ 5: c → \"this is 6\"",
|
|
||||||
" ▶︎ 6: d → \"7\"",
|
|
||||||
" ▶︎ 7: e → \"8\"",
|
|
||||||
" ▶︎ 8: f → \"9\"",
|
|
||||||
" ▶︎ 9: g → \"10\"",
|
|
||||||
" ▶︎ 10: h → \"11\"",
|
|
||||||
" ▶︎ <prototype>: URLSearchParamsPrototype { append: append(), delete: delete(), get: get(), … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// new Error("foo")
|
|
||||||
"Error: foo",
|
|
||||||
|
|
||||||
// throw new Error("Long error ".repeat(10000));
|
|
||||||
"Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error…",
|
|
||||||
|
|
||||||
// throw `“https://evil.com/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa“ is evil and “https://not-so-evil.com/?bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb“ is not good either`;
|
|
||||||
//
|
|
||||||
"“https://evil.com/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa“ is evil and “https://not-so-evil.com/?bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb“ is not good either",
|
|
||||||
|
|
||||||
// Error("bar")
|
|
||||||
"Error: bar",
|
|
||||||
|
|
||||||
// function bar() {
|
|
||||||
// asdf();
|
|
||||||
// }
|
|
||||||
// function foo() {
|
|
||||||
// bar();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// foo();
|
|
||||||
//
|
|
||||||
"ReferenceError: asdf is not defined",
|
|
||||||
|
|
||||||
// eval("let a, a")
|
|
||||||
"SyntaxError: redeclaration of let a",
|
|
||||||
|
|
||||||
// throw "";
|
|
||||||
"<empty string>",
|
|
||||||
|
|
||||||
// throw false;
|
|
||||||
"false",
|
|
||||||
|
|
||||||
// throw undefined;
|
|
||||||
"undefined",
|
|
||||||
|
|
||||||
// throw 0;
|
|
||||||
"0",
|
|
||||||
|
|
||||||
// throw { vegetable: "cucumber" };
|
|
||||||
[
|
|
||||||
"▼ Object { vegetable: \"cucumber\" }",
|
|
||||||
" ├ vegetable: \"cucumber\"",
|
|
||||||
" ▶︎ <prototype>: Object { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// throw Symbol("potato");
|
|
||||||
"Symbol(\"potato\")",
|
|
||||||
|
|
||||||
// var err = new Error("pineapple");
|
|
||||||
// err.name = "JuicyError";
|
|
||||||
// err.flavor = "delicious";
|
|
||||||
// throw err;
|
|
||||||
//
|
|
||||||
"JuicyError: pineapple",
|
|
||||||
|
|
||||||
// var originalError = new SyntaxError("original error");
|
|
||||||
// var err = new Error("something went wrong", {
|
|
||||||
// cause: originalError,
|
|
||||||
// });
|
|
||||||
// throw err;
|
|
||||||
//
|
|
||||||
"Error: something went wrongCaused by: SyntaxError: original error",
|
|
||||||
|
|
||||||
// var a = new Error("err-a");
|
|
||||||
// var b = new Error("err-b", { cause: a });
|
|
||||||
// var c = new Error("err-c", { cause: b });
|
|
||||||
// var d = new Error("err-d", { cause: c });
|
|
||||||
// throw d;
|
|
||||||
//
|
|
||||||
"Error: err-dCaused by: Error: err-cCaused by: Error: err-bCaused by: Error: err-a",
|
|
||||||
|
|
||||||
// var a = new Error("err-a", { cause: b });
|
|
||||||
// var b = new Error("err-b", { cause: a });
|
|
||||||
// throw b;
|
|
||||||
//
|
|
||||||
"Error: err-bCaused by: Error: err-aCaused by: Error: err-bCaused by: Error: err-a",
|
|
||||||
|
|
||||||
// throw new Error("null cause", { cause: null });
|
|
||||||
"Error: null causeCaused by: null",
|
|
||||||
|
|
||||||
// throw new Error("number cause", { cause: 0 });
|
|
||||||
"Error: number causeCaused by: 0",
|
|
||||||
|
|
||||||
// throw new Error("string cause", { cause: "cause message" });
|
|
||||||
"Error: string causeCaused by: \"cause message\"",
|
|
||||||
|
|
||||||
// throw new Error("object cause", {
|
|
||||||
// cause: { code: 234, message: "ERR_234" },
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
"Error: object causeCaused by: Object { … }",
|
|
||||||
|
|
||||||
// Promise.reject("")
|
|
||||||
[
|
|
||||||
"▼ Promise { <state>: \"rejected\", <reason>: \"\" }",
|
|
||||||
" ├ <state>: \"rejected\"",
|
|
||||||
" ▶︎ <prototype>: Promise.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// Promise.reject("tomato")
|
|
||||||
[
|
|
||||||
"▼ Promise { <state>: \"rejected\", <reason>: \"tomato\" }",
|
|
||||||
" ├ <state>: \"rejected\"",
|
|
||||||
" ├ <reason>: \"tomato\"",
|
|
||||||
" ▶︎ <prototype>: Promise.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// Promise.reject(false)
|
|
||||||
[
|
|
||||||
"▼ Promise { <state>: \"rejected\", <reason>: false }",
|
|
||||||
" ├ <state>: \"rejected\"",
|
|
||||||
" ▶︎ <prototype>: Promise.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// Promise.reject(0)
|
|
||||||
[
|
|
||||||
"▼ Promise { <state>: \"rejected\", <reason>: 0 }",
|
|
||||||
" ├ <state>: \"rejected\"",
|
|
||||||
" ▶︎ <prototype>: Promise.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// Promise.reject(null)
|
|
||||||
[
|
|
||||||
"▼ Promise { <state>: \"rejected\", <reason>: null }",
|
|
||||||
" ├ <state>: \"rejected\"",
|
|
||||||
" ├ <reason>: null",
|
|
||||||
" ▶︎ <prototype>: Promise.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// Promise.reject(undefined)
|
|
||||||
[
|
|
||||||
"▼ Promise { <state>: \"rejected\", <reason>: undefined }",
|
|
||||||
" ├ <state>: \"rejected\"",
|
|
||||||
" ├ <reason>: undefined",
|
|
||||||
" ▶︎ <prototype>: Promise.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// Promise.reject(Symbol("potato"))
|
|
||||||
[
|
|
||||||
"▼ Promise { <state>: \"rejected\", <reason>: Symbol(\"potato\") }",
|
|
||||||
" ├ <state>: \"rejected\"",
|
|
||||||
" ├ <reason>: Symbol(\"potato\")",
|
|
||||||
" ▶︎ <prototype>: Promise.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// Promise.reject({vegetable: "cucumber"})
|
|
||||||
[
|
|
||||||
"▼ Promise { <state>: \"rejected\", <reason>: {…} }",
|
|
||||||
" ├ <state>: \"rejected\"",
|
|
||||||
" ▶︎ <reason>: Object { vegetable: \"cucumber\" }",
|
|
||||||
" ▶︎ <prototype>: Promise.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// Promise.reject(new Error("pumpkin"))
|
|
||||||
[
|
|
||||||
"▼ Promise { <state>: \"rejected\", <reason>: Error }",
|
|
||||||
" ├ <state>: \"rejected\"",
|
|
||||||
" ▶︎ <reason>: Error: pumpkin",
|
|
||||||
" ▶︎ <prototype>: Promise.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// var err = new Error("pineapple");
|
|
||||||
// err.name = "JuicyError";
|
|
||||||
// err.flavor = "delicious";
|
|
||||||
// Promise.reject(err);
|
|
||||||
//
|
|
||||||
[
|
|
||||||
"▼ Promise { <state>: \"rejected\", <reason>: JuicyError }",
|
|
||||||
" ├ <state>: \"rejected\"",
|
|
||||||
" ▶︎ <reason>: JuicyError: pineapple",
|
|
||||||
" ▶︎ <prototype>: Promise.prototype { … }"
|
|
||||||
],
|
|
||||||
|
|
||||||
// Promise.resolve().then(() => {
|
|
||||||
// try {
|
|
||||||
// unknownFunc();
|
|
||||||
// } catch(e) {
|
|
||||||
// throw new Error("something went wrong", { cause: e })
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
[
|
|
||||||
"▼ Promise { <state>: \"pending\" }",
|
|
||||||
" ├ <state>: \"rejected\"",
|
|
||||||
" ▶︎ <reason>: Error: something went wrong",
|
|
||||||
" ▶︎ <prototype>: Promise.prototype { … }"
|
|
||||||
],
|
|
||||||
];
|
|
||||||
@@ -3,27 +3,44 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { JSObjectsTestUtils, CONTEXTS } = ChromeUtils.importESModule(
|
const TEST_URI = `data:text/html,<!DOCTYPE html>Test for object previews in console
|
||||||
"resource://testing-common/JSObjectsTestUtils.sys.mjs"
|
<script>
|
||||||
);
|
globalThis.myPolicy = trustedTypes.createPolicy("myPolicy", {
|
||||||
JSObjectsTestUtils.init(this);
|
createHTML: s => "<my-policy>" + s + "</my-policy>",
|
||||||
|
createScript: s => "/* myPolicy */ " + s,
|
||||||
const EXPECTED_VALUES_FILE = "browser_webconsole_previewers.snapshot.mjs";
|
createScriptURL: s => s + "?myPolicy",
|
||||||
|
});
|
||||||
|
</script>`;
|
||||||
|
|
||||||
add_task(async function () {
|
add_task(async function () {
|
||||||
// nsHttpServer does not support https
|
await pushPref("dom.security.trusted_types.enabled", true);
|
||||||
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
const hud = await openNewTabAndConsole(TEST_URI);
|
||||||
const hud = await openNewTabAndConsole("http://example.com");
|
|
||||||
|
|
||||||
await JSObjectsTestUtils.runTest(
|
const TESTS = [
|
||||||
EXPECTED_VALUES_FILE,
|
{
|
||||||
async function ({ context, expression }) {
|
input: `myPolicy.createHTML("hello")`,
|
||||||
if (context == CONTEXTS.CHROME) {
|
preview: `TrustedHTML "<my-policy>hello</my-policy>"`,
|
||||||
return undefined;
|
},
|
||||||
}
|
{
|
||||||
|
input: `myPolicy.createScript("const hello = 'world'")`,
|
||||||
|
preview: `TrustedScript "/* myPolicy */ const hello = 'world'"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: `myPolicy.createScriptURL("https://example.com/trusted")`,
|
||||||
|
preview: `TrustedScriptURL https://example.com/trusted?myPolicy`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: `new BigInt64Array(Array.from({length: 20}, (_, i) => BigInt(i)))`,
|
||||||
|
preview: `BigInt64Array(20) [ 0n, 1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, … ]`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const message = await executeAndWaitForResultMessage(hud, expression, "");
|
for (const { input, preview } of TESTS) {
|
||||||
return message.node.innerText.trim();
|
const message = await executeAndWaitForResultMessage(hud, input, "");
|
||||||
}
|
is(
|
||||||
);
|
message.node.innerText.trim(),
|
||||||
|
preview,
|
||||||
|
`Got expected preview for \`${input}\``
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,305 +0,0 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND.
|
|
||||||
*
|
|
||||||
* More info in https://firefox-source-docs.mozilla.org/devtools/tests/js-object-tests.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default [
|
|
||||||
// undefined
|
|
||||||
"undefined",
|
|
||||||
|
|
||||||
// null
|
|
||||||
"null",
|
|
||||||
|
|
||||||
// true
|
|
||||||
"true",
|
|
||||||
|
|
||||||
// false
|
|
||||||
"false",
|
|
||||||
|
|
||||||
// NaN
|
|
||||||
"NaN",
|
|
||||||
|
|
||||||
// "abc"
|
|
||||||
"\"abc\"",
|
|
||||||
|
|
||||||
// "鼬ú"
|
|
||||||
"\"鼬ú\"",
|
|
||||||
|
|
||||||
// 42
|
|
||||||
"42",
|
|
||||||
|
|
||||||
// -42
|
|
||||||
"-42",
|
|
||||||
|
|
||||||
// -0
|
|
||||||
"-0",
|
|
||||||
|
|
||||||
// Infinity
|
|
||||||
"Infinity",
|
|
||||||
|
|
||||||
// BigInt(1000000000000000000)
|
|
||||||
"1000000000000000000n",
|
|
||||||
|
|
||||||
// 1n
|
|
||||||
"1n",
|
|
||||||
|
|
||||||
// -2n
|
|
||||||
"-2n",
|
|
||||||
|
|
||||||
// 0n
|
|
||||||
"0n",
|
|
||||||
|
|
||||||
// ({})
|
|
||||||
"Object { }",
|
|
||||||
|
|
||||||
// ({ foo: "bar"})
|
|
||||||
"Object { foo: \"bar\" }",
|
|
||||||
|
|
||||||
// []
|
|
||||||
"Array []",
|
|
||||||
|
|
||||||
// [1]
|
|
||||||
"Array [ 1 ]",
|
|
||||||
|
|
||||||
// ["foo"]
|
|
||||||
"Array [ \"foo\" ]",
|
|
||||||
|
|
||||||
// new BigInt64Array()
|
|
||||||
"BigInt64Array []",
|
|
||||||
|
|
||||||
// const a = new BigInt64Array(1);
|
|
||||||
// a[0] = BigInt(42);
|
|
||||||
// a;
|
|
||||||
//
|
|
||||||
"BigInt64Array [ 42n ]",
|
|
||||||
|
|
||||||
// new Map(
|
|
||||||
// Array.from({ length: 2 }).map((el, i) => [
|
|
||||||
// { key: i },
|
|
||||||
// { object: 42 },
|
|
||||||
// ])
|
|
||||||
// )
|
|
||||||
"Map { {…} → {…}, {…} → {…} }",
|
|
||||||
|
|
||||||
// new Map(Array.from({ length: 20 }).map((el, i) => [Symbol(i), i]))
|
|
||||||
"Map(20) { Symbol(\"0\") → 0, Symbol(\"1\") → 1, Symbol(\"2\") → 2, Symbol(\"3\") → 3, Symbol(\"4\") → 4, Symbol(\"5\") → 5, Symbol(\"6\") → 6, Symbol(\"7\") → 7, Symbol(\"8\") → 8, Symbol(\"9\") → 9, … }",
|
|
||||||
|
|
||||||
// new Map(Array.from({ length: 331 }).map((el, i) => [Symbol(i), i]))
|
|
||||||
"Map(331) { Symbol(\"0\") → 0, Symbol(\"1\") → 1, Symbol(\"2\") → 2, Symbol(\"3\") → 3, Symbol(\"4\") → 4, Symbol(\"5\") → 5, Symbol(\"6\") → 6, Symbol(\"7\") → 7, Symbol(\"8\") → 8, Symbol(\"9\") → 9, … }",
|
|
||||||
|
|
||||||
// new Set(Array.from({ length: 2 }).map((el, i) => ({ value: i })))
|
|
||||||
"Set [ {…}, {…} ]",
|
|
||||||
|
|
||||||
// new Set(Array.from({ length: 20 }).map((el, i) => i))
|
|
||||||
"Set(20) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, … ]",
|
|
||||||
|
|
||||||
// new Set(Array.from({ length: 222 }).map((el, i) => i))
|
|
||||||
"Set(222) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, … ]",
|
|
||||||
|
|
||||||
// new Temporal.Instant(355924804000000000n)
|
|
||||||
"Temporal.Instant 1981-04-12T12:00:04Z",
|
|
||||||
|
|
||||||
// new Temporal.PlainDate(2021, 7, 1, "coptic")
|
|
||||||
"Temporal.PlainDate 2021-07-01[u-ca=coptic]",
|
|
||||||
|
|
||||||
// new Temporal.PlainDateTime(2021, 7, 1, 0, 0, 0, 0, 0, 0, "gregory")
|
|
||||||
"Temporal.PlainDateTime 2021-07-01T00:00:00[u-ca=gregory]",
|
|
||||||
|
|
||||||
// new Temporal.PlainMonthDay(7, 1, "chinese")
|
|
||||||
"Temporal.PlainMonthDay 1972-07-01[u-ca=chinese]",
|
|
||||||
|
|
||||||
// new Temporal.PlainTime(4, 20)
|
|
||||||
"Temporal.PlainTime 04:20:00",
|
|
||||||
|
|
||||||
// new Temporal.PlainYearMonth(2021, 7, "indian")
|
|
||||||
"Temporal.PlainYearMonth 2021-07-01[u-ca=indian]",
|
|
||||||
|
|
||||||
// new Temporal.ZonedDateTime(0n, "America/New_York")
|
|
||||||
"Temporal.ZonedDateTime 1969-12-31T19:00:00-05:00[America/New_York]",
|
|
||||||
|
|
||||||
// Temporal.Duration.from({ years: 1 })
|
|
||||||
"Temporal.Duration P1Y",
|
|
||||||
|
|
||||||
// myPolicy.createHTML("hello")
|
|
||||||
"TrustedHTML \"<my-policy>hello</my-policy>\"",
|
|
||||||
|
|
||||||
// myPolicy.createScript("const hello = 'world'")
|
|
||||||
"TrustedScript \"/* myPolicy */ const hello = 'world'\"",
|
|
||||||
|
|
||||||
// myPolicy.createScriptURL("https://example.com/trusted")
|
|
||||||
"TrustedScriptURL https://example.com/trusted?myPolicy",
|
|
||||||
|
|
||||||
// const formData = new FormData();
|
|
||||||
// formData.append("a", 1);
|
|
||||||
// formData.append("a", 2);
|
|
||||||
// formData.append("b", 3);
|
|
||||||
// formData;
|
|
||||||
//
|
|
||||||
"FormData(3) { a → \"1\", a → \"2\", b → \"3\" }",
|
|
||||||
|
|
||||||
// customElements.define("fx-test", class extends HTMLElement {});
|
|
||||||
// const { states } = document.createElement("fx-test").attachInternals();
|
|
||||||
// states.add("custom-state");
|
|
||||||
// states.add("another-custom-state");
|
|
||||||
// states;
|
|
||||||
//
|
|
||||||
"CustomStateSet [ \"custom-state\", \"another-custom-state\" ]",
|
|
||||||
|
|
||||||
// CSS.highlights.set("search", new Highlight());
|
|
||||||
// CSS.highlights.set("glow", new Highlight());
|
|
||||||
// CSS.highlights.set("anchor", new Highlight());
|
|
||||||
// CSS.highlights;
|
|
||||||
//
|
|
||||||
"HighlightRegistry(3) { search → Highlight, glow → Highlight, anchor → Highlight }",
|
|
||||||
|
|
||||||
// new URLSearchParams([
|
|
||||||
// ["a", 1],
|
|
||||||
// ["a", 2],
|
|
||||||
// ["b", 3],
|
|
||||||
// ["b", 3],
|
|
||||||
// ["b", 5],
|
|
||||||
// ["c", "this is 6"],
|
|
||||||
// ["d", 7],
|
|
||||||
// ["e", 8],
|
|
||||||
// ["f", 9],
|
|
||||||
// ["g", 10],
|
|
||||||
// ["h", 11],
|
|
||||||
// ])
|
|
||||||
"URLSearchParams(11) { a → \"1\", a → \"2\", b → \"3\", b → \"3\", b → \"5\", c → \"this is 6\", d → \"7\", e → \"8\", f → \"9\", g → \"10\", … }",
|
|
||||||
|
|
||||||
// new Error("foo")
|
|
||||||
"Error: foo",
|
|
||||||
|
|
||||||
// throw new Error("Long error ".repeat(10000));
|
|
||||||
"Uncaught Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error…\ndebugger eval code:1:7",
|
|
||||||
|
|
||||||
// throw `“https://evil.com/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa“ is evil and “https://not-so-evil.com/?bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb“ is not good either`;
|
|
||||||
//
|
|
||||||
"Uncaught “https://evil.com/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa“ is evil and “https://not-so-evil.com/?bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb“ is not good either\ndebugger eval code:2:7",
|
|
||||||
|
|
||||||
// Error("bar")
|
|
||||||
"Error: bar",
|
|
||||||
|
|
||||||
// function bar() {
|
|
||||||
// asdf();
|
|
||||||
// }
|
|
||||||
// function foo() {
|
|
||||||
// bar();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// foo();
|
|
||||||
//
|
|
||||||
"Uncaught ReferenceError: asdf is not defined[Learn More]\ndebugger eval code:3:9",
|
|
||||||
|
|
||||||
// eval("let a, a")
|
|
||||||
"Uncaught SyntaxError: redeclaration of let a[Learn More]\ndebugger eval code:1:1\nnote: Previously declared at line 1, column 5\ndebugger eval code:1:5",
|
|
||||||
|
|
||||||
// throw "";
|
|
||||||
"Uncaught <empty string>\ndebugger eval code:1:1",
|
|
||||||
|
|
||||||
// throw false;
|
|
||||||
"Uncaught false\ndebugger eval code:1:1",
|
|
||||||
|
|
||||||
// throw undefined;
|
|
||||||
"Uncaught undefined\ndebugger eval code:1:1",
|
|
||||||
|
|
||||||
// throw 0;
|
|
||||||
"Uncaught 0\ndebugger eval code:1:1",
|
|
||||||
|
|
||||||
// throw { vegetable: "cucumber" };
|
|
||||||
"Uncaught \nObject { vegetable: \"cucumber\" }\ndebugger eval code:1:1",
|
|
||||||
|
|
||||||
// throw Symbol("potato");
|
|
||||||
"Uncaught Symbol(\"potato\")\ndebugger eval code:1:7",
|
|
||||||
|
|
||||||
// var err = new Error("pineapple");
|
|
||||||
// err.name = "JuicyError";
|
|
||||||
// err.flavor = "delicious";
|
|
||||||
// throw err;
|
|
||||||
//
|
|
||||||
"Uncaught JuicyError: pineapple\ndebugger eval code:5:7",
|
|
||||||
|
|
||||||
// var originalError = new SyntaxError("original error");
|
|
||||||
// var err = new Error("something went wrong", {
|
|
||||||
// cause: originalError,
|
|
||||||
// });
|
|
||||||
// throw err;
|
|
||||||
//
|
|
||||||
"Uncaught Error: something went wrong\nCaused by: SyntaxError: original error\ndebugger eval code:6:7",
|
|
||||||
|
|
||||||
// var a = new Error("err-a");
|
|
||||||
// var b = new Error("err-b", { cause: a });
|
|
||||||
// var c = new Error("err-c", { cause: b });
|
|
||||||
// var d = new Error("err-d", { cause: c });
|
|
||||||
// throw d;
|
|
||||||
//
|
|
||||||
"Uncaught SyntaxError: redeclaration of const a[Learn More]\ndebugger eval code:1:1",
|
|
||||||
|
|
||||||
// var a = new Error("err-a", { cause: b });
|
|
||||||
// var b = new Error("err-b", { cause: a });
|
|
||||||
// throw b;
|
|
||||||
//
|
|
||||||
"Uncaught SyntaxError: redeclaration of const a[Learn More]\ndebugger eval code:1:1",
|
|
||||||
|
|
||||||
// throw new Error("null cause", { cause: null });
|
|
||||||
"Uncaught Error: null cause\nCaused by: null\ndebugger eval code:1:7",
|
|
||||||
|
|
||||||
// throw new Error("number cause", { cause: 0 });
|
|
||||||
"Uncaught Error: number cause\nCaused by: 0\ndebugger eval code:1:7",
|
|
||||||
|
|
||||||
// throw new Error("string cause", { cause: "cause message" });
|
|
||||||
"Uncaught Error: string cause\nCaused by: \"cause message\"\ndebugger eval code:1:7",
|
|
||||||
|
|
||||||
// throw new Error("object cause", {
|
|
||||||
// cause: { code: 234, message: "ERR_234" },
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
"Uncaught Error: object cause\nCaused by: Object { … }\ndebugger eval code:2:13",
|
|
||||||
|
|
||||||
// Promise.reject("")
|
|
||||||
"Promise { <state>: \"rejected\", <reason>: \"\" }",
|
|
||||||
|
|
||||||
// Promise.reject("tomato")
|
|
||||||
"Promise { <state>: \"rejected\", <reason>: \"tomato\" }",
|
|
||||||
|
|
||||||
// Promise.reject(false)
|
|
||||||
"Promise { <state>: \"rejected\", <reason>: false }",
|
|
||||||
|
|
||||||
// Promise.reject(0)
|
|
||||||
"Promise { <state>: \"rejected\", <reason>: 0 }",
|
|
||||||
|
|
||||||
// Promise.reject(null)
|
|
||||||
"Promise { <state>: \"rejected\", <reason>: null }",
|
|
||||||
|
|
||||||
// Promise.reject(undefined)
|
|
||||||
"Promise { <state>: \"rejected\", <reason>: undefined }",
|
|
||||||
|
|
||||||
// Promise.reject(Symbol("potato"))
|
|
||||||
"Promise { <state>: \"rejected\", <reason>: Symbol(\"potato\") }",
|
|
||||||
|
|
||||||
// Promise.reject({vegetable: "cucumber"})
|
|
||||||
"Promise { <state>: \"rejected\", <reason>: {…} }",
|
|
||||||
|
|
||||||
// Promise.reject(new Error("pumpkin"))
|
|
||||||
"Promise { <state>: \"rejected\", <reason>: Error }",
|
|
||||||
|
|
||||||
// var err = new Error("pineapple");
|
|
||||||
// err.name = "JuicyError";
|
|
||||||
// err.flavor = "delicious";
|
|
||||||
// Promise.reject(err);
|
|
||||||
//
|
|
||||||
"Promise { <state>: \"rejected\", <reason>: JuicyError }",
|
|
||||||
|
|
||||||
// Promise.resolve().then(() => {
|
|
||||||
// try {
|
|
||||||
// unknownFunc();
|
|
||||||
// } catch(e) {
|
|
||||||
// throw new Error("something went wrong", { cause: e })
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
"Promise { <state>: \"rejected\", <reason>: Error }",
|
|
||||||
];
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
const { JSObjectsTestUtils } = ChromeUtils.importESModule(
|
|
||||||
"resource://testing-common/JSObjectsTestUtils.sys.mjs"
|
|
||||||
);
|
|
||||||
JSObjectsTestUtils.init(this);
|
|
||||||
|
|
||||||
// Note that XPCShellContentUtils will already be initialized by JSObjectsTestUtils
|
|
||||||
const { XPCShellContentUtils } = ChromeUtils.importESModule(
|
|
||||||
"resource://testing-common/XPCShellContentUtils.sys.mjs"
|
|
||||||
);
|
|
||||||
|
|
||||||
const EXPECTED_VALUES_FILE = "test_javascript_logging.snapshot.mjs";
|
|
||||||
|
|
||||||
add_task(async function () {
|
|
||||||
// Create a content page in order to better simulate a real world page
|
|
||||||
const contentPage = await XPCShellContentUtils.loadContentPage(
|
|
||||||
"http://example.com/"
|
|
||||||
);
|
|
||||||
const buildPath = do_get_cwd().path;
|
|
||||||
|
|
||||||
await JSObjectsTestUtils.runTest(EXPECTED_VALUES_FILE, async function (arg) {
|
|
||||||
// Because the test page runs in a content process, we have to execute most of the test logic via `spawn`
|
|
||||||
return contentPage.spawn(
|
|
||||||
[arg, buildPath],
|
|
||||||
async ({ context, expression }, buildPathStr) => {
|
|
||||||
const { CONTEXTS } = ChromeUtils.importESModule(
|
|
||||||
"resource://testing-common/AllJavascriptTypes.mjs"
|
|
||||||
);
|
|
||||||
const { JSTracer } = ChromeUtils.importESModule(
|
|
||||||
"resource://devtools/server/tracer/tracer.sys.mjs"
|
|
||||||
);
|
|
||||||
const systemPrincipal =
|
|
||||||
Services.scriptSecurityManager.getSystemPrincipal();
|
|
||||||
const chromeSandbox = Cu.Sandbox(systemPrincipal);
|
|
||||||
|
|
||||||
let ref;
|
|
||||||
try {
|
|
||||||
if (context == CONTEXTS.CHROME) {
|
|
||||||
ref = Cu.evalInSandbox(expression, chromeSandbox);
|
|
||||||
} else {
|
|
||||||
ref = this.content.eval(expression);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
ref = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
let str = JSTracer.objectToString(ref);
|
|
||||||
|
|
||||||
// Silence any async rejection
|
|
||||||
if (ref instanceof this.content.Promise) {
|
|
||||||
// eslint-disable-next-line max-nested-callbacks
|
|
||||||
ref.catch(function () {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// The stringification may contain an exception stack trace which refers to local
|
|
||||||
// mozilla-central checkout. These absolute file path may easily change between
|
|
||||||
// local and try environment, so normalize them.
|
|
||||||
str = str.replaceAll(buildPathStr, "/mozilla-central");
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
info("Close content page");
|
|
||||||
await contentPage.close();
|
|
||||||
});
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -3,8 +3,4 @@ tags = "devtools"
|
|||||||
firefox-appdir = "browser"
|
firefox-appdir = "browser"
|
||||||
run-if = ["os != 'android'"]
|
run-if = ["os != 'android'"]
|
||||||
|
|
||||||
["test_javascript_logging.js"]
|
|
||||||
support-files = ["test_javascript_logging.snapshot.mjs"]
|
|
||||||
prefs = ["dom.security.https_first=false"] # JS HttpServer doesn't support https
|
|
||||||
|
|
||||||
["test_webconsole_l10n.js"]
|
["test_webconsole_l10n.js"]
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ Automated tests
|
|||||||
DevTools mochitests <tests/mochitest-devtools.md>
|
DevTools mochitests <tests/mochitest-devtools.md>
|
||||||
Node tests <tests/node-tests.md>
|
Node tests <tests/node-tests.md>
|
||||||
Memory Allocation tests </devtools/tests/memory/index.md>
|
Memory Allocation tests </devtools/tests/memory/index.md>
|
||||||
JavaScript Objects tests<tests/js-object-tests.md>
|
|
||||||
Writing tests <tests/writing-tests.md>
|
Writing tests <tests/writing-tests.md>
|
||||||
Debugging intermittent failures <tests/debugging-intermittents.md>
|
Debugging intermittent failures <tests/debugging-intermittents.md>
|
||||||
Performance tests overview<tests/performance-tests-overview.md>
|
Performance tests overview<tests/performance-tests-overview.md>
|
||||||
|
|||||||
@@ -1,133 +0,0 @@
|
|||||||
# JavaScript Objects test framework
|
|
||||||
|
|
||||||
`JSObjectsTestUtils.sys.mjs` exposes xpcshell and mochitest test helpers to easily test all the
|
|
||||||
arbitrary JavaScript object types that Gecko can spawn in JavaScript.
|
|
||||||
|
|
||||||
This includes:
|
|
||||||
* any JavaScript type that Spidermonkey supports,
|
|
||||||
* any JavaScript type exposed to Web Page from Gecko (All the DOM APIs),
|
|
||||||
* any JavaScript type only exposed to Worker threads,
|
|
||||||
* any privileged JavaScript type used in parent or content processes,
|
|
||||||
* ...
|
|
||||||
|
|
||||||
This test framework consists in:
|
|
||||||
* a manifest file, [AllJavaScriptTypes.mjs](https://searchfox.org/mozilla-central/source/devtools/shared/tests/objects/AllJavaScriptTypes.mjs) which defines all the JS objects that gecko can spawn
|
|
||||||
* one xpcshell or one mochitest file, using [JSObjectTestUtils](https://searchfox.org/mozilla-central/source/devtools/shared/tests/objects/JSObjectTestUtils.sys.mjs) helper to evaluate all the JS Objects and generate a value for each of them.
|
|
||||||
* a snapshot file, read and written by JSObjectTestUtils, specific to each xpcshell/mochitest and storing all its the generated values.
|
|
||||||
|
|
||||||
You can run your test to execute the assertions:
|
|
||||||
```bash
|
|
||||||
$ ./mach test my/browser_test.js
|
|
||||||
```
|
|
||||||
|
|
||||||
And you can update the snapshot by running:
|
|
||||||
```bash
|
|
||||||
$ ./mach test my/browser_test.js --setenv UPDATE_SNAPSHOT=true
|
|
||||||
```
|
|
||||||
|
|
||||||
## JSObjectsTestUtils APIs
|
|
||||||
|
|
||||||
This test helper is available to all xpcshell and mochitest tests from `resource://testing-common/JSObjectsTestUtils.sys.mjs`.
|
|
||||||
It exposes only two methods:
|
|
||||||
* `JSOBjectsTestUtils.init(testScope)`
|
|
||||||
|
|
||||||
Which is meant to be called early in the test run, before the test page is loaded.
|
|
||||||
This will update all preferences which helps enable all the experimental types
|
|
||||||
or ease instantiating them.
|
|
||||||
|
|
||||||
The global scope for the current test should be passed as argument.
|
|
||||||
This will be used to retrieve the current test location in order to
|
|
||||||
load the expected values from an ES Module located in the same folder as the current test.
|
|
||||||
|
|
||||||
* `JSOBjectsTestUtils.runTest(expectedValuesFileName, testFunction)`
|
|
||||||
|
|
||||||
This is the main method, which will call the `testFunction` for each JavaScript object example.
|
|
||||||
|
|
||||||
This method will receive a single argument which is an object with two attributes:
|
|
||||||
* `context`
|
|
||||||
A string whose value can be one of [AllJavaScriptTypes.mjs](https://searchfox.org/mozilla-central/source/devtools/shared/tests/objects/AllJavaScriptTypes.mjs) `CONTEXTS` dictionary:
|
|
||||||
* "js": Basic JS value available from any possible JavaScript context (worker, page, chrome scopes)
|
|
||||||
* "page": Values only available from a Web page global
|
|
||||||
* "chrome": Privileged values, only available from a chrome, privileged scope
|
|
||||||
|
|
||||||
* `expression`
|
|
||||||
A string which should be evaled in order to instantiate the object example to cover.
|
|
||||||
|
|
||||||
For example, it may receive as argument:
|
|
||||||
`{ type: "js", expression: "42" }`
|
|
||||||
`{ type: "js", expression: "[42]" }`
|
|
||||||
`{ type: "js", expression: "let a = new BigInt64Array(1); a[0] = BigInt(42); a;" }`
|
|
||||||
`{ type: "page", expression: "document.body" }`
|
|
||||||
`{ type: "chrome", expression: "ChromeUtils.domProcessChild" }`
|
|
||||||
|
|
||||||
This method should return the value which represents the given object example.
|
|
||||||
|
|
||||||
### Mochitest Example
|
|
||||||
|
|
||||||
```js
|
|
||||||
const { JSObjectsTestUtils, CONTEXTS } = ChromeUtils.importESModule(
|
|
||||||
"resource://testing-common/JSObjectsTestUtils.sys.mjs"
|
|
||||||
);
|
|
||||||
// We have to manually initialize the test helper module from the parent process
|
|
||||||
JSObjectsTestUtils.init(this);
|
|
||||||
|
|
||||||
// Name of your snapshot file, next to this test, to be registered in the .toml file in a support-files rule
|
|
||||||
const EXPECTED_VALUES_FILE = "browser_mytest.snapshot.mjs";
|
|
||||||
|
|
||||||
add_task(async function () {
|
|
||||||
// Open the test page suitable to spawn all the expected JS Objects in a new tab
|
|
||||||
//
|
|
||||||
// nsHttpServer does not support https
|
|
||||||
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
|
||||||
const tab = await addTab("http://example.com");
|
|
||||||
|
|
||||||
await JSObjectsTestUtils.runTest(
|
|
||||||
EXPECTED_VALUES_FILE,
|
|
||||||
async function ({ context, expression }) {
|
|
||||||
// In this test, we ignore the privileged objects
|
|
||||||
if (context == CONTEXTS.CHROME) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then, execute the key runTest method from the tab's content process
|
|
||||||
await SpecialPowsers.spawn(tab.linkedBrowser, [expression], async function (expressionString) {
|
|
||||||
|
|
||||||
// This is the most important part, where we evaluate the `expression` provided by the test framework
|
|
||||||
// in the test page and return the value we would like to assert over time.
|
|
||||||
// We have to ensure handling exception, which we expect to interpret as a returned value.
|
|
||||||
let value;
|
|
||||||
try {
|
|
||||||
value = content.eval(expressionString);
|
|
||||||
} catch(e) {
|
|
||||||
value = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we cover the native stringification of all the JS Objects.
|
|
||||||
return String(value);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## AllJavaScriptTypes manifest
|
|
||||||
|
|
||||||
All the JavaScript object examples are stored in a manifest file located in the current folder: AllJavaScriptTypes.mjs.
|
|
||||||
|
|
||||||
This module exports an array of objects descriptions, which are objects with the two following attributes:
|
|
||||||
* `context`:
|
|
||||||
A String to designate the context into which this expression could be evaluated.
|
|
||||||
See the first paragraph for the list of all contexts.
|
|
||||||
* `expression`:
|
|
||||||
The JavaScript expression to evaluate, which can either be:
|
|
||||||
* a string representing a piece of JavaScript value.
|
|
||||||
* a function, which would be stringified and evaluated in many scopes.
|
|
||||||
This is to be used when you need intermediate value before spawning another specific JS Value.
|
|
||||||
If the expression throws, the thrown exception will be considered as the value to assert.
|
|
||||||
|
|
||||||
The object descriptions can also have a couple of optional attributes:
|
|
||||||
* `prefs`: An array of arrays. The nested array are made of two elements: a string and a value.
|
|
||||||
The string represents a preference name and the value, the preference value.
|
|
||||||
This is used to set preference before starting the test.
|
|
||||||
This helps enable as well as ease instantiating the related JS value.
|
|
||||||
* `disabled`: A boolean to be set to true if the value can't be instantiated on the current runtime.
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
|
|
||||||
registerCleanupFunction(() => {
|
|
||||||
Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
|
|
||||||
});
|
|
||||||
|
|
||||||
const { JSObjectsTestUtils, CONTEXTS } = ChromeUtils.importESModule(
|
|
||||||
"resource://testing-common/JSObjectsTestUtils.sys.mjs"
|
|
||||||
);
|
|
||||||
JSObjectsTestUtils.init(this);
|
|
||||||
|
|
||||||
const EXPECTED_VALUES_FILE = "test_javascript_object_actors.snapshot.mjs";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This test will run `test` function twice.
|
|
||||||
* Once replicating a page debugging, and a second time replicating a worker debugging environment
|
|
||||||
*/
|
|
||||||
add_task(
|
|
||||||
threadFrontTest(
|
|
||||||
async function test({ threadFront, debuggee, _isWorkerServer }) {
|
|
||||||
await JSObjectsTestUtils.runTest(
|
|
||||||
EXPECTED_VALUES_FILE,
|
|
||||||
async function ({ context, expression }) {
|
|
||||||
// Only support basic JS Values
|
|
||||||
if (context != CONTEXTS.JS) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the function that the privileged code will call to pause
|
|
||||||
// from executeOnNextTickAndWaitForPause callback
|
|
||||||
debuggee.eval(`function stopMe(arg) { debugger; }`);
|
|
||||||
|
|
||||||
const packet = await executeOnNextTickAndWaitForPause(async () => {
|
|
||||||
let value;
|
|
||||||
try {
|
|
||||||
value = debuggee.eval(expression);
|
|
||||||
} catch (e) {
|
|
||||||
value = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Catch all async rejection to avoid unecessary error reports
|
|
||||||
if (value instanceof debuggee.Promise) {
|
|
||||||
// eslint-disable-next-line max-nested-callbacks
|
|
||||||
value.catch(function () {});
|
|
||||||
}
|
|
||||||
|
|
||||||
debuggee.stopMe(value);
|
|
||||||
}, threadFront);
|
|
||||||
|
|
||||||
const firstArg = packet.frame.arguments[0];
|
|
||||||
|
|
||||||
await threadFront.resume();
|
|
||||||
|
|
||||||
// Avoid storing any actor ID as it may not be super stable
|
|
||||||
stripActorIDs(firstArg);
|
|
||||||
|
|
||||||
return firstArg;
|
|
||||||
}
|
|
||||||
); // End of runTest
|
|
||||||
},
|
|
||||||
|
|
||||||
// Use a content principal to better reflect evaluating into a web page,
|
|
||||||
// but also to ensure seeing the stack trace of exception only within the sandbox
|
|
||||||
// and especially not see the test harness ones, which are privileged.
|
|
||||||
{ principal: "https://example.org" }
|
|
||||||
) // End of threadFrontTest
|
|
||||||
);
|
|
||||||
|
|
||||||
function stripActorIDs(obj) {
|
|
||||||
for (const name in obj) {
|
|
||||||
if (name == "actor") {
|
|
||||||
obj[name] = "<actor-id>";
|
|
||||||
}
|
|
||||||
if (typeof obj[name] == "object") {
|
|
||||||
stripActorIDs(obj[name]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -219,9 +219,6 @@ skip-if = [
|
|||||||
|
|
||||||
["test_interrupt.js"]
|
["test_interrupt.js"]
|
||||||
|
|
||||||
["test_javascript_object_actors.js"]
|
|
||||||
support-files = ["test_javascript_object_actors.snapshot.mjs"]
|
|
||||||
|
|
||||||
["test_layout-reflows-observer.js"]
|
["test_layout-reflows-observer.js"]
|
||||||
|
|
||||||
["test_listsources-01.js"]
|
["test_listsources-01.js"]
|
||||||
|
|||||||
@@ -933,7 +933,7 @@ function objectToString(obj) {
|
|||||||
} else if (typeof obj === "function") {
|
} else if (typeof obj === "function") {
|
||||||
return `function ${obj.name || "anonymous"}()`;
|
return `function ${obj.name || "anonymous"}()`;
|
||||||
}
|
}
|
||||||
return primitiveToString(obj);
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
function primitiveToString(value) {
|
function primitiveToString(value) {
|
||||||
@@ -952,7 +952,7 @@ function primitiveToString(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For all other types/cases, rely on native convertion to string
|
// For all other types/cases, rely on native convertion to string
|
||||||
return String(value);
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1112,5 +1112,4 @@ export const JSTracer = {
|
|||||||
removeTracingListener,
|
removeTracingListener,
|
||||||
NEXT_INTERACTION_MESSAGE,
|
NEXT_INTERACTION_MESSAGE,
|
||||||
DOM_MUTATIONS,
|
DOM_MUTATIONS,
|
||||||
objectToString,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -314,10 +314,6 @@ function WorkerDebuggerLoader(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var loader = {
|
var loader = {
|
||||||
// There is only one loader in the worker thread.
|
|
||||||
// This will be used by DevToolsServer to build server prefix and actor IDs.
|
|
||||||
id: 0,
|
|
||||||
|
|
||||||
lazyGetter(object, name, lambda) {
|
lazyGetter(object, name, lambda) {
|
||||||
Object.defineProperty(object, name, {
|
Object.defineProperty(object, name, {
|
||||||
get() {
|
get() {
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ DIRS += [
|
|||||||
"specs",
|
"specs",
|
||||||
"storage",
|
"storage",
|
||||||
"test-helpers",
|
"test-helpers",
|
||||||
"tests/objects",
|
|
||||||
"transport",
|
"transport",
|
||||||
"webconsole",
|
"webconsole",
|
||||||
"worker",
|
"worker",
|
||||||
|
|||||||
@@ -1,521 +0,0 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
/* eslint-disable object-shorthand */
|
|
||||||
|
|
||||||
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
|
||||||
|
|
||||||
// Try replicating real world environment, by using
|
|
||||||
// * a true HTML document
|
|
||||||
// * served from http (https isn't yet supported by nsHttpServer)
|
|
||||||
// * with a regular domain name (example.com)
|
|
||||||
export const TEST_PAGE_HTML = String.raw`<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<span id="span">Hello there.</span>
|
|
||||||
<script>
|
|
||||||
globalThis.myPolicy = trustedTypes.createPolicy("myPolicy", {
|
|
||||||
createHTML: s => "<my-policy>" + s + "</my-policy>",
|
|
||||||
createScript: s => "/* myPolicy */ " + s,
|
|
||||||
createScriptURL: s => s + "?myPolicy",
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
|
|
||||||
export const CONTEXTS = {
|
|
||||||
JS: "js",
|
|
||||||
PAGE: "page",
|
|
||||||
CHROME: "chrome"
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manifest covering all the possible JavaScript value types that gecko may create.
|
|
||||||
* This consist in JavaScript code instantiating one of more example values for all of these types.
|
|
||||||
*
|
|
||||||
* See README.md
|
|
||||||
*/
|
|
||||||
const BasicPrimitives = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "undefined",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "null",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "true",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "false",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "NaN",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Strings = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `"abc"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `"\u9f2c\xFA"`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Numbers = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "42",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "-42",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "-0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "Infinity",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "BigInt(1000000000000000000)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "1n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "-2n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "0n",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Primitives = [...BasicPrimitives, ...Strings, ...Numbers];
|
|
||||||
|
|
||||||
const PlainObjects = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "({})"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `({ foo: "bar"})`
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const Arrays = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "[]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "[1]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: '["foo"]'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const TypedArrays = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: "new BigInt64Array()"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `
|
|
||||||
const a = new BigInt64Array(1);
|
|
||||||
a[0] = BigInt(42);
|
|
||||||
a;
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Maps = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Map(
|
|
||||||
Array.from({ length: 2 }).map((el, i) => [
|
|
||||||
{ key: i },
|
|
||||||
{ object: 42 },
|
|
||||||
])
|
|
||||||
)`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Map(Array.from({ length: 20 }).map((el, i) => [Symbol(i), i]))`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Map(Array.from({ length: 331 }).map((el, i) => [Symbol(i), i]))`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Sets = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Set(Array.from({ length: 2 }).map((el, i) => ({ value: i })))`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Set(Array.from({ length: 20 }).map((el, i) => i))`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Set(Array.from({ length: 222 }).map((el, i) => i))`
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Temporals = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Temporal.Instant(355924804000000000n)`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Temporal.PlainDate(2021, 7, 1, "coptic")`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Temporal.PlainDateTime(2021, 7, 1, 0, 0, 0, 0, 0, 0, "gregory")`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Temporal.PlainMonthDay(7, 1, "chinese")`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Temporal.PlainTime(4, 20)`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Temporal.PlainYearMonth(2021, 7, "indian")`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Temporal.ZonedDateTime(0n, "America/New_York")`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Temporal.Duration.from({ years: 1 })`
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const DOMAPIs = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.PAGE,
|
|
||||||
expression: `myPolicy.createHTML("hello")`,
|
|
||||||
prefs: [["dom.security.trusted_types.enabled", true]],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.PAGE,
|
|
||||||
expression: `myPolicy.createScript("const hello = 'world'")`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.PAGE,
|
|
||||||
expression: `myPolicy.createScriptURL("https://example.com/trusted")`
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
context: CONTEXTS.PAGE,
|
|
||||||
expression: `
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("a", 1);
|
|
||||||
formData.append("a", 2);
|
|
||||||
formData.append("b", 3);
|
|
||||||
formData;
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
/* midi API requires https
|
|
||||||
{
|
|
||||||
context: CONTEXTS.PAGE,
|
|
||||||
expression: `
|
|
||||||
const midiAccess = await navigator.requestMIDIAccess();
|
|
||||||
midiAccess.inputs;
|
|
||||||
`,
|
|
||||||
prefs: [
|
|
||||||
// This will make it so we'll have stable MIDI devices reported
|
|
||||||
["midi.testing", true],
|
|
||||||
["dom.webmidi.enabled", true],
|
|
||||||
["midi.prompt.testing", true],
|
|
||||||
["media.navigator.permission.disabled", true],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
context: CONTEXTS.PAGE,
|
|
||||||
expression: `
|
|
||||||
customElements.define("fx-test", class extends HTMLElement {});
|
|
||||||
const { states } = document.createElement("fx-test").attachInternals();
|
|
||||||
states.add("custom-state");
|
|
||||||
states.add("another-custom-state");
|
|
||||||
states;
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
context: CONTEXTS.PAGE,
|
|
||||||
expression: `
|
|
||||||
CSS.highlights.set("search", new Highlight());
|
|
||||||
CSS.highlights.set("glow", new Highlight());
|
|
||||||
CSS.highlights.set("anchor", new Highlight());
|
|
||||||
CSS.highlights;
|
|
||||||
`,
|
|
||||||
prefs: [["dom.customHighlightAPI.enabled", true]],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.PAGE,
|
|
||||||
expression: `new URLSearchParams([
|
|
||||||
["a", 1],
|
|
||||||
["a", 2],
|
|
||||||
["b", 3],
|
|
||||||
["b", 3],
|
|
||||||
["b", 5],
|
|
||||||
["c", "this is 6"],
|
|
||||||
["d", 7],
|
|
||||||
["e", 8],
|
|
||||||
["f", 9],
|
|
||||||
["g", 10],
|
|
||||||
["h", 11],
|
|
||||||
])`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Errors = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `new Error("foo")`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `throw new Error("Long error ".repeat(10000));`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `
|
|
||||||
throw \`“https://evil.com/?${"a".repeat(
|
|
||||||
200
|
|
||||||
)}“ is evil and “https://not-so-evil.com/?${"b".repeat(
|
|
||||||
200
|
|
||||||
)}“ is not good either\`;
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Error("bar")`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `
|
|
||||||
function bar() {
|
|
||||||
asdf();
|
|
||||||
}
|
|
||||||
function foo() {
|
|
||||||
bar();
|
|
||||||
}
|
|
||||||
|
|
||||||
foo();
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
// Use nested `eval()` as syntax error would make the test framework throw on its own eval call
|
|
||||||
expression: `eval("let a, a")`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `throw "";`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `throw false;`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `throw undefined;`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `throw 0;`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `throw { vegetable: "cucumber" };`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `throw Symbol("potato");`
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `
|
|
||||||
var err = new Error("pineapple");
|
|
||||||
err.name = "JuicyError";
|
|
||||||
err.flavor = "delicious";
|
|
||||||
throw err;
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `
|
|
||||||
var originalError = new SyntaxError("original error");
|
|
||||||
var err = new Error("something went wrong", {
|
|
||||||
cause: originalError,
|
|
||||||
});
|
|
||||||
throw err;
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `
|
|
||||||
var a = new Error("err-a");
|
|
||||||
var b = new Error("err-b", { cause: a });
|
|
||||||
var c = new Error("err-c", { cause: b });
|
|
||||||
var d = new Error("err-d", { cause: c });
|
|
||||||
throw d;
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `
|
|
||||||
var a = new Error("err-a", { cause: b });
|
|
||||||
var b = new Error("err-b", { cause: a });
|
|
||||||
throw b;
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `throw new Error("null cause", { cause: null });`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `throw new Error("number cause", { cause: 0 });`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `throw new Error("string cause", { cause: "cause message" });`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `
|
|
||||||
throw new Error("object cause", {
|
|
||||||
cause: { code: 234, message: "ERR_234" },
|
|
||||||
});
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Promise.reject("")`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Promise.reject("tomato")`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Promise.reject(false)`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Promise.reject(0)`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Promise.reject(null)`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Promise.reject(undefined)`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Promise.reject(Symbol("potato"))`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Promise.reject({vegetable: "cucumber"})`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Promise.reject(new Error("pumpkin"))`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `
|
|
||||||
var err = new Error("pineapple");
|
|
||||||
err.name = "JuicyError";
|
|
||||||
err.flavor = "delicious";
|
|
||||||
Promise.reject(err);
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `Promise.resolve().then(() => {
|
|
||||||
try {
|
|
||||||
unknownFunc();
|
|
||||||
} catch(e) {
|
|
||||||
throw new Error("something went wrong", { cause: e })
|
|
||||||
}
|
|
||||||
})`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: CONTEXTS.JS,
|
|
||||||
expression: `
|
|
||||||
throw new SuppressedError(
|
|
||||||
new Error("foo"),
|
|
||||||
new Error("bar"),
|
|
||||||
"the suppressed error message"
|
|
||||||
);
|
|
||||||
`,
|
|
||||||
prefs: [
|
|
||||||
["javascript.options.experimental.explicit_resource_management", true],
|
|
||||||
],
|
|
||||||
disabled: true || !AppConstants.ENABLE_EXPLICIT_RESOURCE_MANAGEMENT,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Privileged = [
|
|
||||||
{
|
|
||||||
context: CONTEXTS.CHROME,
|
|
||||||
expression: `Components.Exception("foo")`
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
export const AllObjects = [
|
|
||||||
...Primitives,
|
|
||||||
...PlainObjects,
|
|
||||||
...Arrays,
|
|
||||||
...TypedArrays,
|
|
||||||
...Maps,
|
|
||||||
...Sets,
|
|
||||||
...Temporals,
|
|
||||||
...DOMAPIs,
|
|
||||||
...Errors,
|
|
||||||
...Privileged,
|
|
||||||
];
|
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { TEST_PAGE_HTML, CONTEXTS, AllObjects } from "resource://testing-common/AllJavascriptTypes.mjs";
|
|
||||||
export { CONTEXTS } from "resource://testing-common/AllJavascriptTypes.mjs";
|
|
||||||
import { ObjectUtils } from "resource://gre/modules/ObjectUtils.sys.mjs";
|
|
||||||
|
|
||||||
// Name of the environment variable to set while running the test to update the expected values
|
|
||||||
const UPDATE_SNAPSHOT_ENV = "UPDATE_SNAPSHOT";
|
|
||||||
|
|
||||||
const { AddonTestUtils } = ChromeUtils.importESModule(
|
|
||||||
"resource://testing-common/AddonTestUtils.sys.mjs"
|
|
||||||
);
|
|
||||||
|
|
||||||
// To avoid totally unrelated exceptions about missing appinfo when running from xpcshell tests
|
|
||||||
AddonTestUtils.createAppInfo(
|
|
||||||
"xpcshell@tests.mozilla.org",
|
|
||||||
"XPCShell",
|
|
||||||
"42",
|
|
||||||
"42"
|
|
||||||
);
|
|
||||||
|
|
||||||
let gTestScope;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the test helper.
|
|
||||||
*
|
|
||||||
* @param {Object} testScope
|
|
||||||
* XPCShell or mochitest test scope (i.e. the global object of the test currently executed)
|
|
||||||
*/
|
|
||||||
function init(testScope) {
|
|
||||||
if (!testScope?.gTestPath && !testScope?.Assert) {
|
|
||||||
throw new Error("`JSObjectsTestUtils.init()` should be called with the (xpcshell or mochitest) test global object");
|
|
||||||
}
|
|
||||||
gTestScope = testScope;
|
|
||||||
|
|
||||||
if ("gTestPath" in testScope) {
|
|
||||||
AddonTestUtils.initMochitest(testScope);
|
|
||||||
} else {
|
|
||||||
AddonTestUtils.init(testScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
const server = AddonTestUtils.createHttpServer({
|
|
||||||
hosts: ["example.com"],
|
|
||||||
});
|
|
||||||
|
|
||||||
server.registerPathHandler("/", (request, response) => {
|
|
||||||
response.setHeader("Content-Type", "text/html");
|
|
||||||
response.write(TEST_PAGE_HTML);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Lookup for all preferences to toggle in order to have all the expected objects type functional
|
|
||||||
let prefValues = new Map();
|
|
||||||
for (const { prefs } of AllObjects) {
|
|
||||||
if (!prefs) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (const elt of prefs) {
|
|
||||||
if (elt.length != 2) {
|
|
||||||
throw new Error("Each pref should be an array of two element [prefName, prefValue]. Got: "+elt);
|
|
||||||
}
|
|
||||||
const [ name, value ] = elt;
|
|
||||||
const otherValue = prefValues.get(name);
|
|
||||||
if (otherValue && otherValue != value) {
|
|
||||||
throw new Error(`Two javascript values in AllJavascriptTypes.mjs are expecting different values for '${name}' preference. (${otherValue} vs ${value})`);
|
|
||||||
}
|
|
||||||
prefValues.set(name, value);
|
|
||||||
if (typeof(value) == "boolean") {
|
|
||||||
Services.prefs.setBoolPref(name, value);
|
|
||||||
gTestScope.registerCleanupFunction(() => {
|
|
||||||
Services.prefs.clearUserPref(name);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error("Unsupported pref type: "+name+" = "+value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let gExpectedValuesFilePath;
|
|
||||||
let gCurrentTestFolderUrl;
|
|
||||||
|
|
||||||
const chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(
|
|
||||||
Ci.nsIChromeRegistry
|
|
||||||
);
|
|
||||||
function loadExpectedValues(expectedValuesFileName) {
|
|
||||||
const isUpdate = Services.env.get(UPDATE_SNAPSHOT_ENV) == "true";
|
|
||||||
dump(`JS Objects test: ${isUpdate ? "Update" : "Check"} ${expectedValuesFileName}\n`);
|
|
||||||
|
|
||||||
// Depending on the test suite, mochitest will expose `gTextPath` which is a chrome://
|
|
||||||
// for the current test file.
|
|
||||||
// Otherwise xpcshell will expose `resource://test/` for the current test folder.
|
|
||||||
gCurrentTestFolderUrl = "gTestPath" in gTestScope
|
|
||||||
? gTestScope.gTestPath.substr(0, gTestScope.gTestPath.lastIndexOf("/")) + "/"
|
|
||||||
: "resource://test/";
|
|
||||||
|
|
||||||
// Build the URL for the test data file
|
|
||||||
const url = gCurrentTestFolderUrl + expectedValuesFileName;
|
|
||||||
|
|
||||||
// Resolve the test data file URL into a file absolute path
|
|
||||||
if (url.startsWith("chrome")) {
|
|
||||||
const chromeURL = Services.io.newURI(url);
|
|
||||||
gExpectedValuesFilePath = chromeRegistry
|
|
||||||
.convertChromeURL(chromeURL)
|
|
||||||
.QueryInterface(Ci.nsIFileURL).file.path;
|
|
||||||
} else if (url.startsWith("resource")) {
|
|
||||||
const resURL = Services.io.newURI(url);
|
|
||||||
const resHandler = Services.io.getProtocolHandler("resource")
|
|
||||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
|
||||||
gExpectedValuesFilePath = Services.io.newURI(resHandler.resolveURI(resURL)).QueryInterface(Ci.nsIFileURL).file.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
let expectedValues;
|
|
||||||
if (!isUpdate) {
|
|
||||||
dump(`Loading test data file: ${url}\n`);
|
|
||||||
return ChromeUtils.importESModule(url).default;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function mayBeSaveExpectedValues(evaledStrings, newExpectedValues) {
|
|
||||||
if (!newExpectedValues?.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (evaledStrings.length != newExpectedValues.length) {
|
|
||||||
throw new Error("Unexpected discrepencies between the reported evaled strings and expected values");
|
|
||||||
}
|
|
||||||
|
|
||||||
const filePath = gExpectedValuesFilePath;
|
|
||||||
const assertionValues = [];
|
|
||||||
let i = 0;
|
|
||||||
for (const value of newExpectedValues) {
|
|
||||||
let evaled = evaledStrings[i];
|
|
||||||
// Remove any first empty line
|
|
||||||
evaled = evaled.replace(/^\s*\n/, "");
|
|
||||||
// remove the unnecessary indentation
|
|
||||||
const m = evaled.match(/^( +)/);
|
|
||||||
if (m && m[1]) {
|
|
||||||
const regexp = new RegExp("^"+m[1], "gm");
|
|
||||||
evaled = evaled.replace(regexp, "");
|
|
||||||
}
|
|
||||||
// Ensure prefixing all new lines in the evaled string with " //"
|
|
||||||
// to keep it being in a code comment.
|
|
||||||
evaled = evaled.replace(/\r?\n/g, "\n // ");
|
|
||||||
|
|
||||||
assertionValues.push(
|
|
||||||
" // " + evaled +
|
|
||||||
"\n" +
|
|
||||||
" " +
|
|
||||||
JSON.stringify(value, null, 2) +
|
|
||||||
","
|
|
||||||
);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
const fileContent = `/* Any copyright is dedicated to the Public Domain.
|
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND.
|
|
||||||
*
|
|
||||||
* More info in https://firefox-source-docs.mozilla.org/devtools/tests/js-object-tests.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default [
|
|
||||||
${assertionValues.join("\n\n")}
|
|
||||||
];`;
|
|
||||||
dump("Writing: " + fileContent + " in " + filePath + "\n");
|
|
||||||
await IOUtils.write(filePath, new TextEncoder().encode(fileContent));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runTest(expectedValuesFileName, testFunction) {
|
|
||||||
if (!gTestScope) {
|
|
||||||
throw new Error("`JSObjectsTestUtils.init()` should be called before `runTest()`");
|
|
||||||
}
|
|
||||||
if (typeof (expectedValuesFileName) != "string") {
|
|
||||||
throw new Error("`JSObjectsTestUtils.runTest()` first argument should be a data file name");
|
|
||||||
}
|
|
||||||
if (typeof (testFunction) != "function") {
|
|
||||||
throw new Error("`JSObjectsTestUtils.runTest()` second argument should be a test function");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let expectedValues = loadExpectedValues(expectedValuesFileName);
|
|
||||||
if (expectedValues) {
|
|
||||||
// Clone the Array as we are going to mutate it via Array.shift().
|
|
||||||
expectedValues = [...expectedValues];
|
|
||||||
}
|
|
||||||
|
|
||||||
const evaledStrings = [];
|
|
||||||
const newExpectedValues = [];
|
|
||||||
|
|
||||||
let failed = false;
|
|
||||||
|
|
||||||
for (const objectDescription of AllObjects) {
|
|
||||||
if (objectDescription.disabled) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { context, expression } = objectDescription;
|
|
||||||
if (!Object.values(CONTEXTS).includes(context)) {
|
|
||||||
throw new Error("Missing, or invalid context in: " + JSON.stringify(objectDescription));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!expression) {
|
|
||||||
throw new Error("Missing a value in: " + JSON.stringify(objectDescription));
|
|
||||||
}
|
|
||||||
|
|
||||||
const actual = await testFunction({ context, expression });
|
|
||||||
|
|
||||||
// Ignore this JS object as the test function did not return any actual value.
|
|
||||||
// We assume none of the tests would store "undefined" as a target value.
|
|
||||||
if (actual == undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const testPath = "gtestPath" in gTestScope ? gTestScope.gTestPath.replace("chrome://mochitest/content/browser/", "") : "path/to/your/xpcshell/test";
|
|
||||||
const failureMessage = `This is a JavaScript value processing test, which includes an automatically generated snapshot file (${expectedValuesFileName}).\n` +
|
|
||||||
"You may update this file by running:`\n" +
|
|
||||||
` $ mach test ${testPath} --headless --setenv ${UPDATE_SNAPSHOT_ENV}=true\n` +
|
|
||||||
"And then carefuly review if the result is valid regarding your ongoing changes.\n" +
|
|
||||||
"`More info in https://firefox-source-docs.mozilla.org/devtools/tests/js-object-tests.html\n";
|
|
||||||
|
|
||||||
const isMochitest = "gTestPath" in gTestScope;
|
|
||||||
const isXpcshell = !isMochitest;
|
|
||||||
|
|
||||||
// If we aren't in "update" mode, we are reading assertion values from $EXPECTED_VALUES_FILE
|
|
||||||
// and will assert the current returned values against these values
|
|
||||||
if (expectedValues) {
|
|
||||||
const expected = expectedValues.shift();
|
|
||||||
try {
|
|
||||||
gTestScope.Assert.deepEqual(actual, expected, `Got expected output for "${expression}"`);
|
|
||||||
} catch(e) {
|
|
||||||
// deepEqual only throws in case of differences when running in XPCShell tests. Mochitest won't throw and keep running.
|
|
||||||
// XPCShell will stop at the first failing assertion, so ensure showing our failure message and ok() will throw and stop the test.
|
|
||||||
if (isXpcshell) {
|
|
||||||
gTestScope.Assert.ok(false, failureMessage);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
// As mochitest won't throw when calling deepEqual with differences in the objects,
|
|
||||||
// we have to recompute the difference in order to know if any of the tests failed.
|
|
||||||
if (isMochitest && !failed && !ObjectUtils.deepEqual(actual, expected)) {
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Otherwise, if we are in update mode, we will collected all current values
|
|
||||||
// in order to store them in $EXPECTED_VALUES_FILE
|
|
||||||
//
|
|
||||||
// Force casting to string, in case this is a function.
|
|
||||||
evaledStrings.push(String(expression));
|
|
||||||
newExpectedValues.push(actual);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (failed) {
|
|
||||||
const failureMessage = "This is a JavaScript value processing test, which includes an automatically generated snapshot file.\n" +
|
|
||||||
"If the change made to that snapshot file makes sense, you may simply update them by running:`\n" +
|
|
||||||
" $ mach test ${testPath} --headless --setenv UPDATE_EXPECTED_VALUES=true\n" +
|
|
||||||
"`More info in devtools/shared/tests/objects/README.md\n";
|
|
||||||
gTestScope.Assert.ok(false, failureMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
mayBeSaveExpectedValues(evaledStrings, newExpectedValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const JSObjectsTestUtils = { init, runTest };
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# vim: set filetype=python:
|
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
TESTING_JS_MODULES += [
|
|
||||||
"AllJavascriptTypes.mjs",
|
|
||||||
"JSObjectsTestUtils.sys.mjs",
|
|
||||||
]
|
|
||||||
Reference in New Issue
Block a user