fix: browser.devtools.inspectedWindow.eval promise should always resolve to an array (#175)

* fix: browser.devtools.inspectedWindow.eval promise should always resolve to an array
* test(integration): Add test for browser.devtools.inspectedWindow.eval API
This commit is contained in:
Luca Greco 2019-02-01 08:38:35 +01:00 committed by GitHub
parent e6601af79c
commit 6f178c56da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 111 additions and 4 deletions

View File

@ -207,7 +207,8 @@
"inspectedWindow": { "inspectedWindow": {
"eval": { "eval": {
"minArgs": 1, "minArgs": 1,
"maxArgs": 2 "maxArgs": 2,
"singleCallbackArg": false
} }
}, },
"panels": { "panels": {

View File

@ -92,7 +92,8 @@ if (typeof browser === "undefined" || Object.getPrototypeOf(browser) !== Object.
return (...callbackArgs) => { return (...callbackArgs) => {
if (extensionAPIs.runtime.lastError) { if (extensionAPIs.runtime.lastError) {
promise.reject(extensionAPIs.runtime.lastError); promise.reject(extensionAPIs.runtime.lastError);
} else if (metadata.singleCallbackArg || callbackArgs.length <= 1) { } else if (metadata.singleCallbackArg ||
(callbackArgs.length <= 1 && metadata.singleCallbackArg !== false)) {
promise.resolve(callbackArgs[0]); promise.resolve(callbackArgs[0]);
} else { } else {
promise.resolve(callbackArgs); promise.resolve(callbackArgs);

View File

@ -0,0 +1,12 @@
let onDevToolsPageLoaded = new Promise(resolve => {
const listener = () => {
browser.runtime.onConnect.removeListener(listener);
resolve();
};
browser.runtime.onConnect.addListener(listener);
});
browser.runtime.onMessage.addListener(async msg => {
await onDevToolsPageLoaded;
return browser.runtime.sendMessage(msg);
});

View File

@ -0,0 +1,30 @@
test("devtools.inspectedWindow.eval resolved with an error result", async (t) => {
const {evalResult} = await browser.runtime.sendMessage({
apiMethod: "devtools.inspectedWindow.eval",
params: ["throw new Error('fake error');"],
});
t.ok(Array.isArray(evalResult), "devtools.inspectedWindow.eval should resolve to an array");
t.equal(evalResult[0], navigator.userAgent.includes("Firefox/") ? undefined : null,
"the first element should be null (on chrome) or undefined (on firefox)");
t.ok(evalResult[1].isException, "the second element should represent an exception");
t.ok(evalResult[1].value && evalResult[1].value.includes("fake error"),
"the second element value property should include the expected error message");
});
test("devtools.inspectedWindow.eval resolved without an error result", async (t) => {
const {evalResult} = await browser.runtime.sendMessage({
apiMethod: "devtools.inspectedWindow.eval",
params: ["[document.documentElement.localName]"],
});
t.ok(Array.isArray(evalResult), "devtools.inspectedWindow.eval should resolve to an array");
if (navigator.userAgent.includes("Firefox/")) {
t.deepEqual(evalResult, [["html"], undefined], "got the expected values in the array");
} else {
t.deepEqual(evalResult, [["html"]], "got the expected values in the array");
}
});

View File

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<script src="browser-polyfill.js"></script>
<script src="devtools_page.js"></script>
</head>
</html>

View File

@ -0,0 +1,14 @@
console.log("devtools page loaded");
browser.runtime.onMessage.addListener(async msg => {
switch (msg.apiMethod) {
case "devtools.inspectedWindow.eval": {
const evalResult = await browser.devtools.inspectedWindow.eval(...msg.params);
return {evalResult};
}
}
throw new Error(`devtools_page received an unxpected message: ${msg}`);
});
browser.runtime.connect({name: "devtools_page"});

View File

@ -0,0 +1,27 @@
{
"manifest_version": 2,
"name": "test-extension-devtools-api",
"version": "0.1",
"description": "test-extension-devtools-api",
"content_scripts": [
{
"matches": [
"http://localhost/*"
],
"js": [
"browser-polyfill.js",
"tape.js",
"content.js"
],
"run_at": "document_end"
}
],
"permissions": [],
"background": {
"scripts": [
"browser-polyfill.js",
"background.js"
]
},
"devtools_page": "devtools_page.html"
}

View File

@ -18,6 +18,7 @@ const TEST_TIMEOUT = 5000;
const launchBrowser = async (launchOptions) => { const launchBrowser = async (launchOptions) => {
const browser = launchOptions.browser; const browser = launchOptions.browser;
const extensionPath = launchOptions.extensionPath; const extensionPath = launchOptions.extensionPath;
const openDevTools = launchOptions.openDevTools;
let driver; let driver;
@ -37,6 +38,10 @@ const launchBrowser = async (launchOptions) => {
"--no-sandbox", "--no-sandbox",
]); ]);
if (openDevTools) {
options.addArguments(["-auto-open-devtools-for-tabs"]);
}
if (process.env.TEST_NATIVE_CRX_BINDINGS === "1") { if (process.env.TEST_NATIVE_CRX_BINDINGS === "1") {
console.warn("NOTE: Running tests on a Chrome instance with NativeCrxBindings enabled."); console.warn("NOTE: Running tests on a Chrome instance with NativeCrxBindings enabled.");
options.addArguments([ options.addArguments([
@ -60,6 +65,10 @@ const launchBrowser = async (launchOptions) => {
options.headless(); options.headless();
} }
if (openDevTools) {
options.addArguments("-devtools");
}
driver = await new Builder() driver = await new Builder()
.forBrowser("firefox") .forBrowser("firefox")
.setFirefoxOptions(options) .setFirefoxOptions(options)
@ -157,7 +166,7 @@ test.onFailure(() => {
* @param {string[]} parameters.extensions * @param {string[]} parameters.extensions
* @param {boolean|string|string[]} [parameters.skip] * @param {boolean|string|string[]} [parameters.skip]
*/ */
const defineExtensionTests = ({description, extensions, skip}) => { const defineExtensionTests = ({description, extensions, skip, openDevTools}) => {
for (const extensionDirName of extensions) { for (const extensionDirName of extensions) {
test(`${description} (test extension: ${extensionDirName})`, async (tt) => { test(`${description} (test extension: ${extensionDirName})`, async (tt) => {
let timeout; let timeout;
@ -192,7 +201,7 @@ const defineExtensionTests = ({description, extensions, skip}) => {
await bundleTapeStandalone(extensionPath); await bundleTapeStandalone(extensionPath);
server = await createHTTPServer(path.join(__dirname, "..", "fixtures")); server = await createHTTPServer(path.join(__dirname, "..", "fixtures"));
driver = await launchBrowser({extensionPath, browser}); driver = await launchBrowser({extensionPath, browser, openDevTools});
await Promise.race([ await Promise.race([
runExtensionTest(tt, server, driver, extensionDirName, browser), runExtensionTest(tt, server, driver, extensionDirName, browser),
new Promise((resolve, reject) => { new Promise((resolve, reject) => {

View File

@ -26,3 +26,9 @@ defineExtensionTests({
description: "Instance of BrowserSetting API", description: "Instance of BrowserSetting API",
extensions: ["privacy-setting-extension"], extensions: ["privacy-setting-extension"],
}); });
defineExtensionTests({
description: "browser.devtools",
extensions: ["devtools-extension"],
openDevTools: true,
});