mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1724026 - Support running existing Extension API mochitests with a background service worker. r=mixedpuppy
Provide a way to use service workers as the background script in existing tests, possibly by only requiring minimal changes to the existing test cases. This patch includes: - changes needed to detect when a test extension is being created for a test running in the "background service worker mode" and automatically turn the background script into a background service worker (instead of a background page) when not explicitly listed in the test extension manifest - a new mochitest-serviceworker.ini manifest where new or existing test files meant to be run on a background service worker can be added to run them automatically in the "background service worker mode" - a new test_verify_sw_mode.html smoke test that make sure the mochitest-serviceworker.ini manifest is running in the expected mode. - a new `sw-webextension` tag, which can be used locally to run a test file only in the "background service worker mode" - changes to test_ext_test.html to make it able to run in both background pages and background workers - small tweaks to `test` API (both the WebIDL binding and the current bindings injected from privileged js code, to better match each other behavior) Differential Revision: https://phabricator.services.mozilla.com/D122536
This commit is contained in:
parent
804f0d3fde
commit
63324905ef
@ -1,10 +1,22 @@
|
||||
var ExtensionTestUtils = {};
|
||||
|
||||
const { ExtensionTestCommon } = SpecialPowers.Cu.import(
|
||||
"resource://testing-common/ExtensionTestCommon.jsm",
|
||||
{}
|
||||
);
|
||||
|
||||
var ExtensionTestUtils = {
|
||||
// Shortcut to more easily access WebExtensionPolicy.backgroundServiceWorkerEnabled
|
||||
// from mochitest-plain tests.
|
||||
getBackgroundServiceWorkerEnabled() {
|
||||
return ExtensionTestCommon.getBackgroundServiceWorkerEnabled();
|
||||
},
|
||||
|
||||
// A test helper used to check if the pref "extension.backgroundServiceWorker.forceInTestExtension"
|
||||
// is set to true.
|
||||
isInBackgroundServiceWorkerTests() {
|
||||
return ExtensionTestCommon.isInBackgroundServiceWorkerTests();
|
||||
},
|
||||
};
|
||||
|
||||
ExtensionTestUtils.loadExtension = function(ext) {
|
||||
// Cleanup functions need to be registered differently depending on
|
||||
// whether we're in browser chrome or plain mochitests.
|
||||
|
@ -236,6 +236,37 @@ function provide(obj, keys, value, override = false) {
|
||||
}
|
||||
|
||||
ExtensionTestCommon = class ExtensionTestCommon {
|
||||
/**
|
||||
* Shortcut to more easily access WebExtensionPolicy.backgroundServiceWorkerEnabled
|
||||
* from mochitest-plain tests.
|
||||
*
|
||||
* @returns {boolean} true if the background service worker are enabled.
|
||||
*/
|
||||
static getBackgroundServiceWorkerEnabled() {
|
||||
return WebExtensionPolicy.backgroundServiceWorkerEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* A test helper mainly used to skip test tasks if running in "backgroundServiceWorker" test mode
|
||||
* (e.g. while running test files shared across multiple test modes: e.g. in-process-webextensions,
|
||||
* remote-webextensions, sw-webextensions etc.).
|
||||
*
|
||||
* The underlying pref "extension.backgroundServiceWorker.forceInTestExtension":
|
||||
* - is set to true in the xpcshell-serviceworker.ini and mochitest-serviceworker.ini manifests
|
||||
* (and so it is going to be set to true while running the test files listed in those manifests)
|
||||
* - when set to true, all test extension using a background script without explicitly listing it
|
||||
* in the test extension manifest will be automatically executed as background service workers
|
||||
* (instead of background scripts loaded in a background page)
|
||||
*
|
||||
* @returns {boolean} true if the test is running in "background service worker mode"
|
||||
*/
|
||||
static isInBackgroundServiceWorkerTests() {
|
||||
return Services.prefs.getBoolPref(
|
||||
"extensions.backgroundServiceWorker.forceInTestExtension",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This code is designed to make it easy to test a WebExtension
|
||||
* without creating a bunch of files. Everything is contained in a
|
||||
@ -284,10 +315,22 @@ ExtensionTestCommon = class ExtensionTestCommon {
|
||||
delete manifest.host_permissions;
|
||||
}
|
||||
|
||||
if (data.useServiceWorker === undefined) {
|
||||
// If we're force-testing service workers we will turn the background
|
||||
// script part of ExtensionTestUtils test extensions into a background
|
||||
// service worker.
|
||||
data.useServiceWorker = ExtensionTestCommon.isInBackgroundServiceWorkerTests();
|
||||
}
|
||||
|
||||
if (data.background) {
|
||||
let bgScript = Services.uuid.generateUUID().number + ".js";
|
||||
|
||||
provide(manifest, ["background", "scripts"], [bgScript], true);
|
||||
let scriptKey = data.useServiceWorker
|
||||
? ["background", "service_worker"]
|
||||
: ["background", "scripts"];
|
||||
let scriptVal = data.useServiceWorker ? bgScript : [bgScript];
|
||||
provide(manifest, scriptKey, scriptVal, true);
|
||||
|
||||
files[bgScript] = data.background;
|
||||
}
|
||||
|
||||
|
@ -231,16 +231,24 @@ this.test = class extends ExtensionAPI {
|
||||
|
||||
return promise.then(
|
||||
result => {
|
||||
assertTrue(false, `Promise resolved, expected rejection: ${msg}`);
|
||||
let message = `Promise resolved, expected rejection '${toSource(
|
||||
expectedError
|
||||
)}'`;
|
||||
if (msg) {
|
||||
message += `: ${msg}`;
|
||||
}
|
||||
assertTrue(false, message);
|
||||
},
|
||||
error => {
|
||||
let errorMessage = toSource(error && error.message);
|
||||
let expected = toSource(expectedError);
|
||||
let message = `got '${toSource(error)}'`;
|
||||
if (msg) {
|
||||
message += `: ${msg}`;
|
||||
}
|
||||
|
||||
assertTrue(
|
||||
errorMatches(error, expectedError, context),
|
||||
`Promise rejected, expecting rejection to match ${toSource(
|
||||
expectedError
|
||||
)}, got ${errorMessage}: ${msg}`
|
||||
`Promise rejected, expecting rejection to match '${expected}', ${message}`
|
||||
);
|
||||
}
|
||||
);
|
||||
@ -250,15 +258,23 @@ this.test = class extends ExtensionAPI {
|
||||
try {
|
||||
func();
|
||||
|
||||
assertTrue(false, `Function did not throw, expected error: ${msg}`);
|
||||
let message = `Function did not throw, expected error '${toSource(
|
||||
expectedError
|
||||
)}'`;
|
||||
if (msg) {
|
||||
message += `: ${msg}`;
|
||||
}
|
||||
assertTrue(false, message);
|
||||
} catch (error) {
|
||||
let errorMessage = toSource(error && error.message);
|
||||
let expected = toSource(expectedError);
|
||||
let message = `got '${toSource(error)}'`;
|
||||
if (msg) {
|
||||
message += `: ${msg}`;
|
||||
}
|
||||
|
||||
assertTrue(
|
||||
errorMatches(error, expectedError, context),
|
||||
`Function threw, expecting error to match ${toSource(
|
||||
expectedError
|
||||
)}, got ${errorMessage}: ${msg}`
|
||||
`Function threw, expecting error to match '${expected}', ${message}`
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -127,6 +127,8 @@ XPCSHELL_TESTS_MANIFESTS += [
|
||||
# in builds where they are enabled (currently only on Nightly builds).
|
||||
if CONFIG["MOZ_WEBEXT_WEBIDL_ENABLED"]:
|
||||
XPCSHELL_TESTS_MANIFESTS += ["test/xpcshell/webidl-api/xpcshell.ini"]
|
||||
MOCHITEST_MANIFESTS += ["test/mochitest/mochitest-serviceworker.ini"]
|
||||
|
||||
|
||||
SPHINX_TREES["webextensions"] = "docs"
|
||||
|
||||
|
@ -0,0 +1,15 @@
|
||||
[DEFAULT]
|
||||
tags = webextensions sw-webextensions
|
||||
skip-if =
|
||||
!e10s # Thunderbird does still run in non e10s mode (and so also with in-process-webextensions mode)
|
||||
(os == 'android') # Bug 1620091: disable on android until extension process is done
|
||||
|
||||
prefs =
|
||||
extensions.webextensions.remote=true
|
||||
extensions.backgroundServiceWorker.enabled=true
|
||||
extensions.backgroundServiceWorker.forceInTestExtension=true
|
||||
|
||||
dupe-manifest = true
|
||||
|
||||
[test_verify_sw_mode.html]
|
||||
[test_ext_test.html]
|
@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<title>Testing test</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
|
||||
<script type="text/javascript" src="head.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
@ -43,9 +44,15 @@ function loadExtensionAndInterceptTest(extensionData) {
|
||||
return extension;
|
||||
}
|
||||
|
||||
// NOTE: This test does not verify the behavior expected by calling the browser.test API methods.
|
||||
//
|
||||
// On the contrary it tests what messages ext-test.js sends to the parent process as a result of
|
||||
// processing different kind of parameters (e.g. how a dom element or a JS object with a custom
|
||||
// toString method are being serialized into strings).
|
||||
//
|
||||
// All browser.test calls results are intercepted by the test itself, see verifyTestResults for
|
||||
// the expectations of each browser.test call.
|
||||
function testScript() {
|
||||
// Note: The result of these browser.test calls are intercepted by the test.
|
||||
// See verifyTestResults for the expectations of each browser.test call.
|
||||
browser.test.notifyPass("dot notifyPass");
|
||||
browser.test.notifyFail("dot notifyFail");
|
||||
browser.test.log("dot log");
|
||||
@ -57,43 +64,36 @@ function testScript() {
|
||||
|
||||
let obj = {};
|
||||
let arr = [];
|
||||
let dom = document.createElement("body");
|
||||
browser.test.assertTrue(obj, "Object truthy");
|
||||
browser.test.assertTrue(arr, "Array truthy");
|
||||
browser.test.assertTrue(dom, "Element truthy");
|
||||
browser.test.assertTrue(true, "True truthy");
|
||||
browser.test.assertTrue(false, "False truthy");
|
||||
browser.test.assertTrue(null, "Null truthy");
|
||||
browser.test.assertTrue(undefined, "Void truthy");
|
||||
browser.test.assertTrue(false, document.createElement("html"));
|
||||
|
||||
browser.test.assertFalse(obj, "Object falsey");
|
||||
browser.test.assertFalse(arr, "Array falsey");
|
||||
browser.test.assertFalse(dom, "Element falsey");
|
||||
browser.test.assertFalse(true, "True falsey");
|
||||
browser.test.assertFalse(false, "False falsey");
|
||||
browser.test.assertFalse(null, "Null falsey");
|
||||
browser.test.assertFalse(undefined, "Void falsey");
|
||||
browser.test.assertFalse(true, document.createElement("head"));
|
||||
|
||||
browser.test.assertEq(obj, obj, "Object equality");
|
||||
browser.test.assertEq(arr, arr, "Array equality");
|
||||
browser.test.assertEq(dom, dom, "Element equality");
|
||||
browser.test.assertEq(null, null, "Null equality");
|
||||
browser.test.assertEq(undefined, undefined, "Void equality");
|
||||
|
||||
browser.test.assertEq({}, {}, "Object reference ineqality");
|
||||
browser.test.assertEq([], [], "Array reference ineqality");
|
||||
browser.test.assertEq(dom, document.createElement("body"), "Element ineqality");
|
||||
browser.test.assertEq(null, undefined, "Null and void ineqality");
|
||||
browser.test.assertEq(true, false, document.createElement("div"));
|
||||
|
||||
obj = {
|
||||
toString() {
|
||||
return "Dynamic toString forbidden";
|
||||
return "Dynamic toString";
|
||||
},
|
||||
};
|
||||
browser.test.assertEq(obj, obj, "obj with dynamic toString()");
|
||||
|
||||
browser.test.assertThrows(
|
||||
() => { throw new Error("dummy"); },
|
||||
/dummy2/,
|
||||
@ -107,11 +107,25 @@ function testScript() {
|
||||
() => {},
|
||||
/dummy/
|
||||
);
|
||||
|
||||
// Set of additional tests to only run on background page and content script
|
||||
// (but skip on background service worker).
|
||||
if (self === self.window) {
|
||||
let dom = document.createElement("body");
|
||||
browser.test.assertTrue(dom, "Element truthy");
|
||||
browser.test.assertTrue(false, document.createElement("html"));
|
||||
browser.test.assertFalse(dom, "Element falsey");
|
||||
browser.test.assertFalse(true, document.createElement("head"));
|
||||
browser.test.assertEq(dom, dom, "Element equality");
|
||||
browser.test.assertEq(dom, document.createElement("body"), "Element ineqality");
|
||||
browser.test.assertEq(true, false, document.createElement("div"));
|
||||
}
|
||||
|
||||
browser.test.sendMessage("Ran test at", location.protocol);
|
||||
browser.test.sendMessage("This is the last browser.test call");
|
||||
}
|
||||
|
||||
function verifyTestResults(results, shortName, expectedProtocol) {
|
||||
function verifyTestResults(results, shortName, expectedProtocol, useServiceWorker) {
|
||||
let expectations = [
|
||||
["test-done", true, "dot notifyPass"],
|
||||
["test-done", false, "dot notifyFail"],
|
||||
@ -124,42 +138,72 @@ function verifyTestResults(results, shortName, expectedProtocol) {
|
||||
|
||||
["test-result", true, "Object truthy"],
|
||||
["test-result", true, "Array truthy"],
|
||||
["test-result", true, "Element truthy"],
|
||||
["test-result", true, "True truthy"],
|
||||
["test-result", false, "False truthy"],
|
||||
["test-result", false, "Null truthy"],
|
||||
["test-result", false, "Void truthy"],
|
||||
["test-result", false, "[object HTMLHtmlElement]"],
|
||||
|
||||
["test-result", false, "Object falsey"],
|
||||
["test-result", false, "Array falsey"],
|
||||
["test-result", false, "Element falsey"],
|
||||
["test-result", false, "True falsey"],
|
||||
["test-result", true, "False falsey"],
|
||||
["test-result", true, "Null falsey"],
|
||||
["test-result", true, "Void falsey"],
|
||||
["test-result", false, "[object HTMLHeadElement]"],
|
||||
|
||||
["test-eq", true, "Object equality", "[object Object]", "[object Object]"],
|
||||
["test-eq", true, "Array equality", "", ""],
|
||||
["test-eq", true, "Element equality", "[object HTMLBodyElement]", "[object HTMLBodyElement]"],
|
||||
["test-eq", true, "Null equality", "null", "null"],
|
||||
["test-eq", true, "Void equality", "undefined", "undefined"],
|
||||
|
||||
["test-eq", false, "Object reference ineqality", "[object Object]", "[object Object] (different)"],
|
||||
["test-eq", false, "Array reference ineqality", "", " (different)"],
|
||||
["test-eq", false, "Element ineqality", "[object HTMLBodyElement]", "[object HTMLBodyElement] (different)"],
|
||||
["test-eq", false, "Null and void ineqality", "null", "undefined"],
|
||||
|
||||
[
|
||||
"test-eq",
|
||||
true,
|
||||
"obj with dynamic toString()",
|
||||
// - Privileged JS API Bindings: the ext-test.js module will get a XrayWrapper and so when
|
||||
// the object is being stringified the custom `toString()` method will not be called and
|
||||
// "[object Object]" is the value we expect.
|
||||
// - WebIDL API Bindngs: the parameter is being serialized into a string on the worker thread,
|
||||
// the object is stringified using the worker principal and so there is no XrayWrapper
|
||||
// involved and the value expected is the value returned by the custom toString method the.
|
||||
// object does provide.
|
||||
useServiceWorker ? "Dynamic toString" : "[object Object]",
|
||||
useServiceWorker ? "Dynamic toString" : "[object Object]",
|
||||
],
|
||||
|
||||
[
|
||||
"test-result", false,
|
||||
"Function threw, expecting error to match '/dummy2/', got \'Error: dummy\': intentional failure"
|
||||
],
|
||||
[
|
||||
"test-result", false,
|
||||
"Function threw, expecting error to match '/dummy3/', got \'Error: dummy2\'"
|
||||
],
|
||||
[
|
||||
"test-result", false,
|
||||
"Function did not throw, expected error '/dummy/'"
|
||||
],
|
||||
];
|
||||
|
||||
if (!useServiceWorker) {
|
||||
expectations.push(...[
|
||||
["test-result", true, "Element truthy"],
|
||||
["test-result", false, "[object HTMLHtmlElement]"],
|
||||
["test-result", false, "Element falsey"],
|
||||
["test-result", false, "[object HTMLHeadElement]"],
|
||||
["test-eq", true, "Element equality", "[object HTMLBodyElement]", "[object HTMLBodyElement]"],
|
||||
["test-eq", false, "Element ineqality", "[object HTMLBodyElement]", "[object HTMLBodyElement] (different)"],
|
||||
["test-eq", false, "[object HTMLDivElement]", "true", "false"],
|
||||
]);
|
||||
}
|
||||
|
||||
["test-eq", true, "obj with dynamic toString()", "[object Object]", "[object Object]"],
|
||||
["test-result", false, "Function threw, expecting error to match /dummy2/, got \"dummy\": intentional failure"],
|
||||
["test-result", false, "Function threw, expecting error to match /dummy3/, got \"dummy2\": null"],
|
||||
["test-result", false, "Function did not throw, expected error: null"],
|
||||
|
||||
expectations.push(...[
|
||||
["test-message", "Ran test at", expectedProtocol],
|
||||
["test-message", "This is the last browser.test call"],
|
||||
];
|
||||
]);
|
||||
|
||||
expectations.forEach((expectation, i) => {
|
||||
let msg = expectation.slice(2).join(" - ");
|
||||
@ -171,12 +215,41 @@ function verifyTestResults(results, shortName, expectedProtocol) {
|
||||
add_task(async function test_test_in_background() {
|
||||
let extensionData = {
|
||||
background: `(${testScript})()`,
|
||||
// This test case should never run the background script in a worker,
|
||||
// even if this test file is running when "extensions.backgroundServiceWorker.forceInTest"
|
||||
// pref is true
|
||||
useServiceWorker: false,
|
||||
};
|
||||
|
||||
let extension = loadExtensionAndInterceptTest(extensionData);
|
||||
await extension.startup();
|
||||
let results = await extension.awaitResults();
|
||||
verifyTestResults(results, "background page", "moz-extension:");
|
||||
verifyTestResults(results, "background page", "moz-extension:", false);
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function test_test_in_background_service_worker() {
|
||||
if (!ExtensionTestUtils.isInBackgroundServiceWorkerTests()) {
|
||||
is(
|
||||
ExtensionTestUtils.getBackgroundServiceWorkerEnabled(),
|
||||
false,
|
||||
"This test should only be skipped with background service worker disabled"
|
||||
)
|
||||
info("Test intentionally skipped on 'extensions.backgroundServiceWorker.enabled=false'");
|
||||
return;
|
||||
}
|
||||
|
||||
let extensionData = {
|
||||
background: `(${testScript})()`,
|
||||
// This test case should always run the background script in a worker,
|
||||
// or be skipped if the background service worker is disabled by prefs.
|
||||
useServiceWorker: true,
|
||||
};
|
||||
|
||||
let extension = loadExtensionAndInterceptTest(extensionData);
|
||||
await extension.startup();
|
||||
let results = await extension.awaitResults();
|
||||
verifyTestResults(results, "background service worker", "moz-extension:", true);
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
@ -198,7 +271,7 @@ add_task(async function test_test_in_content_script() {
|
||||
let win = window.open("file_sample.html");
|
||||
let results = await extension.awaitResults();
|
||||
win.close();
|
||||
verifyTestResults(results, "content script", "http:");
|
||||
verifyTestResults(results, "content script", "http:", false);
|
||||
await extension.unload();
|
||||
});
|
||||
</script>
|
||||
|
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Verify WebExtension background service worker mode</title>
|
||||
<meta charset="utf-8">
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
|
||||
<script type="text/javascript" src="head.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
// This test ensures we are running with the proper settings.
|
||||
const {WebExtensionPolicy} = SpecialPowers.Cu.getGlobalForObject(SpecialPowers.Services);
|
||||
SimpleTest.ok(WebExtensionPolicy.useRemoteWebExtensions, "extensions running remote");
|
||||
SimpleTest.ok(!WebExtensionPolicy.isExtensionProcess, "testing from remote process");
|
||||
SimpleTest.ok(WebExtensionPolicy.backgroundServiceWorkerEnabled, "extensions background service worker enabled");
|
||||
SimpleTest.ok(AppConstants.MOZ_WEBEXT_WEBIDL_ENABLED, "extensions API webidl bindings enabled");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -322,7 +322,7 @@ MOZ_CAN_RUN_SCRIPT void ExtensionTest::AssertThrows(
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!AssertMatchInternal(aCx, exn, aExpectedError,
|
||||
u"Function did throw, expected error"_ns,
|
||||
u"Function threw, expecting error"_ns,
|
||||
aMessage, nullptr, aRv))) {
|
||||
ThrowUnexpectedError(aCx, aRv);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user