Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
/* exported runTests */
// This file is imported into the same scope as head.js.
/* import-globals-from head.js */
{
// At the moment extension language negotiation is tied to Firefox language
// negotiation result. That means that to test an extension in `es-ES`, we need
// to mock `es-ES` being available in Firefox and then request it.
//
// In the future, we should provide some way for tests to decouple their
// language selection from that of Firefox.
const avLocales = Services.locale.availableLocales;
Services.locale.availableLocales = ["en-US", "es-ES"];
registerCleanupFunction(() => {
Services.locale.availableLocales = avLocales;
});
}
async function runTests(options) {
function background(getTests) {
let tests;
// Gets the current details of the page action, and returns a
// promise that resolves to an object containing them.
async function getDetails(tabId) {
return {
title: await browser.pageAction.getTitle({ tabId }),
popup: await browser.pageAction.getPopup({ tabId }),
isShown: await browser.pageAction.isShown({ tabId }),
};
}
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
let test = tests.shift();
test(async expecting => {
let [tab] = await browser.tabs.query({
active: true,
currentWindow: true,
});
let { id: tabId, windowId, url } = tab;
browser.test.log(`Get details: tab={id: ${tabId}, url: ${url}}`);
// Check that the API returns the expected values, and then
// run the next test.
let details = await getDetails(tabId);
if (expecting) {
browser.test.assertEq(
expecting.title,
details.title,
"expected value from getTitle"
);
browser.test.assertEq(
expecting.popup,
details.popup,
"expected value from getPopup"
);
}
browser.test.assertEq(
!!expecting,
details.isShown,
"expected value from isShown"
);
// Check that the actual icon has the expected values, then
// run the next test.
browser.test.sendMessage("nextTest", expecting, windowId, tests.length);
});
}
async function runTests() {
let tabs = [];
let windows = [];
tests = getTests(tabs, windows);
let resultTabs = await browser.tabs.query({
active: true,
currentWindow: true,
});
tabs[0] = resultTabs[0].id;
windows[0] = resultTabs[0].windowId;
nextTest();
}
browser.test.onMessage.addListener(msg => {
if (msg == "runTests") {
runTests();
} else if (msg == "runNextTest") {
nextTest();
} else {
browser.test.fail(`Unexpected message: ${msg}`);
}
});
runTests();
}
let extension = ExtensionTestUtils.loadExtension({
manifest: options.manifest,
files: options.files || {},
background: `(${background})(${options.getTests})`,
});
let pageActionId;
let currentWindow = window;
let windows = [];
async function waitForDetails(details, windowId) {
function check() {
let { document } = Services.wm.getOuterWindowWithId(windowId);
let image = document.getElementById(pageActionId);
if (details == null) {
return image == null || image.getAttribute("disabled") == "true";
}
let title = details.title || options.manifest.name;
return (
!!image &&
getListStyleImage(image) == details.icon &&
image.getAttribute("tooltiptext") == title &&
image.getAttribute("aria-label") == title
);
// TODO: Popup URL. If this is updated, modify also checkDetails.
}
// eslint-disable-next-line no-async-promise-executor
return new Promise(async resolve => {
let maxCounter = 10;
while (!check() && --maxCounter > 0) {
info("checks left: " + maxCounter);
await promiseAnimationFrame(currentWindow);
}
resolve();
});
}
function checkDetails(details, windowId) {
let { document } = Services.wm.getOuterWindowWithId(windowId);
let image = document.getElementById(pageActionId);
if (details == null) {
ok(
image == null || image.getAttribute("disabled") == "true",
"image is disabled"
);
} else {
ok(image, "image exists");
is(getListStyleImage(image), details.icon, "icon URL is correct");
let title = details.title || options.manifest.name;
is(image.getAttribute("tooltiptext"), title, "image title is correct");
is(
image.getAttribute("aria-label"),
title,
"image aria-label is correct"
);
// TODO: Popup URL. If this is updated, modify also waitForDetails.
}
}
let testNewWindows = 1;
let awaitFinish = new Promise(resolve => {
extension.onMessage(
"nextTest",
async (expecting, windowId, testsRemaining) => {
if (!pageActionId) {
pageActionId = BrowserPageActions.urlbarButtonNodeIDForActionID(
makeWidgetId(extension.id)
);
}
await waitForDetails(expecting, windowId);
checkDetails(expecting, windowId);
if (testsRemaining) {
extension.sendMessage("runNextTest");
} else if (testNewWindows) {
testNewWindows--;
BrowserTestUtils.openNewBrowserWindow()
.then(window => {
windows.push(window);
currentWindow = window;
return focusWindow(window);
})
.then(() => {
extension.sendMessage("runTests");
});
} else {
resolve();
}
}
);
});
let reqLoc = Services.locale.requestedLocales;
Services.locale.requestedLocales = ["es-ES"];
await extension.startup();
await awaitFinish;
await extension.unload();
Services.locale.requestedLocales = reqLoc;
let node = document.getElementById(pageActionId);
is(node, null, "pageAction image removed from document");
currentWindow = null;
for (let win of windows.splice(0)) {
node = win.document.getElementById(pageActionId);
is(node, null, "pageAction image removed from second document");
await BrowserTestUtils.closeWindow(win);
}
}