Source code

Revision control

Copy as Markdown

Other Tools

"use strict";
const { ExtensionTestCommon } = ChromeUtils.importESModule(
);
const WPT = "web-platform.test";
const server = createHttpServer({ hosts: [WPT, "example.com"] });
server.registerDirectory("/data/", do_get_file("data"));
function loadPage(domain, query = "") {
return ExtensionTestUtils.loadContentPage(
`http://${domain}/data/file_wpt_events.html?${query}`
);
}
async function requestAndCollect(unsubscribe = false) {
let done = Promise.withResolvers();
let events = [];
function onEvent({ detail }) {
Assert.ok(true, "Received event: " + JSON.stringify(detail));
events.push(detail);
if (detail.error || detail.done || detail.data.remainingTests === 0) {
done.resolve(events);
this.content.removeEventListener("testEvent", onEvent);
}
}
this.content.addEventListener("testEvent", onEvent);
if (!unsubscribe) {
this.content.wrappedJSObject.subscribe();
} else {
this.content.wrappedJSObject.unsubscribe();
}
return done.promise;
}
async function runTestExt() {
// Intentionally not using ExtensionWrapper here, we don't want
// browser.test.assertX() assertions to reach the test harness,
// and explicitly check for expected events in tests below.
let ext = ExtensionTestCommon.generate({
async background() {
await browser.test.runTests([
async () => {
browser.test.assertEq(0xff, 255, "Same value.");
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(r => setTimeout(r, 100));
browser.test.assertTrue(1, "Truthy after await.");
},
function subTest2() {
browser.test.assertThrows(() => {
throw new Error("Wrong!");
});
browser.test.assertTrue(false, "Actually false.");
// Thrown errors interrupt only the current task.
throw new Error("Interrupted!");
},
function still_runs_despite_previous_tast_failed() {
// Assert without a message.
browser.test.fail();
// Once failed assertions start throwing, this will be unreachable.
browser.test.assertTrue(true, "Assertions don't throw (yet).");
},
function subTest4() {
browser.test.assertEq(true, "true", "No type coercion.");
},
]);
browser.test.sendMessage("test-ext-done");
},
});
let done = Promise.withResolvers();
ext.on("test-message", function fn(_, msg) {
if (msg === "test-ext-done") {
ext.off("test-message", fn);
done.resolve(ext);
}
});
await Promise.all([ext.startup(), done.promise]);
await ExtensionTestCommon.unloadTestExtension(ext);
}
function checkEvents(events, info) {
Assert.deepEqual(
events,
[
{
event: "onTestStarted",
data: { testName: "unnamed_test_1" },
},
{
event: "onAssertEquality",
data: {
result: true,
message: "Same value.",
expectedValue: "255",
actualValue: "255",
},
},
{
event: "onAssert",
data: { result: true, message: "Truthy after await." },
},
{
event: "onTestFinished",
data: {
remainingTests: 3,
testName: "unnamed_test_1",
result: true,
assertionDescription: "unnamed_test_1 PASS",
},
},
{
event: "onTestStarted",
data: { testName: "subTest2" },
},
{
event: "onAssert",
data: {
result: true,
message:
"Function threw, expecting error to match '/.*/', got 'Error: Wrong!'",
},
},
{
event: "onAssert",
data: { result: false, message: "Actually false." },
},
{
event: "onTestFinished",
data: {
remainingTests: 2,
testName: "subTest2",
result: false,
assertionDescription: "Exception running subTest2: Interrupted!",
},
},
{
event: "onTestStarted",
data: { testName: "still_runs_despite_previous_tast_failed" },
},
{
event: "onAssert",
data: { result: false, message: "Assertion FAIL" },
},
{
event: "onAssert",
data: { result: true, message: "Assertions don't throw (yet)." },
},
{
event: "onTestFinished",
data: {
remainingTests: 1,
testName: "still_runs_despite_previous_tast_failed",
result: false,
assertionDescription: "Assertion FAIL",
},
},
{
event: "onTestStarted",
data: { testName: "subTest4" },
},
{
event: "onAssertEquality",
data: {
result: false,
message: "No type coercion.",
expectedValue: "true",
actualValue: "true (different)",
},
},
{
event: "onTestFinished",
data: {
remainingTests: 0,
testName: "subTest4",
result: false,
assertionDescription:
"No type coercion. - Expected: true, Actual: true (different)",
},
},
],
`Expected events - ${info}`
);
}
// Normally this is not an expected situation. Real Firefox will always trigger
// loading ExtensionParent.sys.mjs on startup to support builtin extensions.
add_task(async function test_not_available_before_actor_init() {
Assert.equal(
false,
Cu.isESModuleLoaded("resource://gre/modules/ExtensionParent.sys.mjs"),
"ExtensionParent.sys.mjs not loaded in xpcshell tests by itself."
);
let page = await loadPage(WPT);
let results = await page.spawn([], requestAndCollect);
await page.close();
Assert.deepEqual(
results,
[{ error: "Missing browser namespace." }],
"browser.test not defined before WPTEvents actor initializion."
);
});
add_task(async function test_events_queuing_scenarios() {
// Importing to trigger the WPTEvents actor initialization.
ChromeUtils.importESModule("resource://gre/modules/ExtensionParent.sys.mjs");
info("Run test ext before event listener subscribed.");
await runTestExt();
let page1 = await loadPage(WPT, 1);
let results1 = await page1.spawn([], requestAndCollect);
checkEvents(results1, "Correct events queued.");
info("Open second page with listener without closing the first one.");
let page2 = await loadPage(WPT, 2);
let results2 = page2.spawn([], requestAndCollect);
await runTestExt();
checkEvents(await results2, "Newest listener received messages.");
let unsub1 = await page1.spawn([true], requestAndCollect);
Assert.deepEqual(
unsub1,
[{ done: "unsubscribed" }],
"Unsubscribing first listener shouldn't affect second."
);
let again2 = page2.spawn([], requestAndCollect);
await runTestExt();
checkEvents(await again2, "Second listener still received messages.");
info("Closing both listeners.");
await page1.close();
await page2.close();
info("Running the test before third listener subscribed.");
await runTestExt();
let page3 = await loadPage(WPT, 3);
let results3 = await page3.spawn([], requestAndCollect);
checkEvents(results3, "Third listener got queued messages.");
await page3.close();
});
add_task(async function test_not_available_on_example_com() {
// Importing to trigger the WPTEvents actor initialization.
ChromeUtils.importESModule("resource://gre/modules/ExtensionParent.sys.mjs");
let page = await loadPage("example.com");
let results = await page.spawn([], requestAndCollect);
await page.close();
Assert.deepEqual(
results,
[{ error: "Missing browser namespace." }],
"browser.test not defined on example.com."
);
});