// Load the shared-head file first.
Services.scriptloader.loadSubScript( "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", this
);
Services.scriptloader.loadSubScript( "chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/shared-head.js", this
);
// Make sure the ADB addon is removed and ADB is stopped when the test ends.
registerCleanupFunction(async function () { // Reset the selected tool in case we opened about:devtools-toolbox to // avoid side effects between tests.
Services.prefs.clearUserPref("devtools.toolbox.selectedTool");
try { const {
adbAddon,
} = require("resource://devtools/client/shared/remote-debugging/adb/adb-addon.js");
await adbAddon.uninstall();
} catch (e) { // Will throw if the addon is already uninstalled, ignore exceptions here.
} const {
adbProcess,
} = require("resource://devtools/client/shared/remote-debugging/adb/adb-process.js");
await adbProcess.kill();
async function openAboutDebugging({
enableWorkerUpdates,
enableLocalTabs = true,
} = {}) { if (!enableWorkerUpdates) {
silenceWorkerUpdates();
}
// This preference changes value depending on the build type, tests need to use a // consistent value regarless of the build used.
await pushPref( "devtools.aboutdebugging.local-tab-debugging",
enableLocalTabs
);
// Local WebExtension toolboxes open in a dedicated window if (isLocalWebExtension) { const toolbox = await onToolboxReady; // For some reason the test helpers prevents the toolbox from being automatically focused on opening, // whereas it is IRL. const focusedWin = Services.focus.focusedWindow; if (focusedWin?.top != toolbox.win) {
info("Wait for the toolbox window to be focused");
await new Promise(r => { // focus event only fired on the chrome event handler and in capture phase
toolbox.win.docShell.chromeEventHandler.addEventListener("focus", r, {
once: true,
capture: true,
});
toolbox.win.focus();
});
info("The toolbox is focused");
} return {
devtoolsBrowser: null,
devtoolsDocument: toolbox.doc,
devtoolsTab: null,
devtoolsWindow: toolbox.win,
};
}
info("Wait until a new tab is opened");
await waitUntil(() => tab.nextElementSibling);
info("Wait for about:devtools-toolbox tab will be selected"); const devtoolsTab = tab.nextElementSibling;
await waitUntil(() => gBrowser.selectedTab === devtoolsTab); const devtoolsBrowser = gBrowser.selectedBrowser;
info("Wait for about:devtools-toolbox tab to have the expected URL");
await waitUntil(() =>
devtoolsBrowser.contentWindow.location.href.startsWith( "about:devtools-toolbox?"
)
);
if (!shouldWaitToolboxReady) { // Wait for show error page.
await waitUntil(() =>
devtoolsBrowser.contentDocument.querySelector(".qa-error-page")
);
}
async function closeAboutDevtoolsToolbox(
aboutDebuggingDocument,
devtoolsTab,
win
) { // Wait for all requests to settle on the opened about:devtools toolbox. const devtoolsBrowser = devtoolsTab.linkedBrowser; const devtoolsWindow = devtoolsBrowser.contentWindow; const toolbox = getToolbox(devtoolsWindow);
info("Wait for requests to settle");
await toolbox.commands.client.waitForRequestsToSettle({
ignoreOrphanedFronts: true,
});
info("Wait for removeTab");
await removeTab(devtoolsTab);
info("Wait for toolbox destroyed");
await onToolboxDestroyed;
// Changing the tab will also trigger a request to list tabs, so wait until the selected // tab has changed to wait for requests to settle.
info("Wait until aboutdebugging is selected");
await waitUntil(() => gBrowser.selectedTab !== devtoolsTab);
// Wait for removing about:devtools-toolbox tab info from about:debugging.
info("Wait until about:devtools-toolbox is removed from debug targets");
await waitUntil(
() => !findDebugTargetByText("Toolbox - ", aboutDebuggingDocument)
);
async function closeWebExtAboutDevtoolsToolbox(devtoolsWindow, win) { // Wait for all requests to settle on the opened about:devtools toolbox. const toolbox = getToolbox(devtoolsWindow);
await toolbox.commands.client.waitForRequestsToSettle();
info("Close the toolbox and wait for its destruction");
await toolbox.destroy();
async function reloadAboutDebugging(tab) {
info("reload about:debugging");
await reloadBrowser(tab.linkedBrowser); const browser = tab.linkedBrowser; const document = browser.contentDocument; const window = browser.contentWindow;
info("wait for the initial about:debugging requests to settle");
await waitForAboutDebuggingRequests(window.AboutDebugging.store);
return document;
}
// Wait for all about:debugging target request actions to succeed. // They will typically be triggered after watching a new runtime or loading // about:debugging. function waitForRequestsSuccess(store) { return Promise.all([
waitForDispatch(store, "REQUEST_EXTENSIONS_SUCCESS"),
waitForDispatch(store, "REQUEST_TABS_SUCCESS"),
waitForDispatch(store, "REQUEST_WORKERS_SUCCESS"),
]);
}
/** * Wait for all aboutdebugging REQUEST_*_SUCCESS actions to settle, meaning here * that no new request has been dispatched after the provided delay.
*/
async function waitForAboutDebuggingRequests(store, delay = 500) {
let hasSettled = false;
// After each iteration of this while loop, we check is the timerPromise had the time // to resolve or if we captured a REQUEST_*_SUCCESS action before. while (!hasSettled) {
let timer;
// This timer will be executed only if no REQUEST_*_SUCCESS action is dispatched // during the delay. We consider that when no request are received for some time, it // means there are no ongoing requests anymore. const timerPromise = new Promise(resolve => {
timer = setTimeout(() => {
hasSettled = true;
resolve();
}, delay);
});
// Wait either for a REQUEST_*_SUCCESS to be dispatched, or for the timer to resolve.
await Promise.race([
waitForDispatch(store, "REQUEST_EXTENSIONS_SUCCESS"),
waitForDispatch(store, "REQUEST_TABS_SUCCESS"),
waitForDispatch(store, "REQUEST_WORKERS_SUCCESS"),
timerPromise,
]);
// Clear the timer to avoid setting hasSettled to true accidently unless timerPromise // was the first to resolve.
clearTimeout(timer);
}
}
/** * Navigate to "This Firefox"
*/
async function selectThisFirefoxPage(doc, store) {
info("Select This Firefox page");
const onRequestSuccess = waitForRequestsSuccess(store);
doc.location.hash = "#/runtime/this-firefox";
info("Wait for requests to be complete");
await onRequestSuccess;
info("Wait for runtime page to be rendered");
await waitUntil(() => doc.querySelector(".qa-runtime-page"));
// Navigating to this-firefox will trigger a title change for the // about:debugging tab. This title change _might_ trigger a tablist update. // If it does, we should make sure to wait for pending tab requests.
await waitForAboutDebuggingRequests(store);
}
/** * Navigate to the Connect page. Resolves when the Connect page is rendered.
*/
async function selectConnectPage(doc) { const sidebarItems = doc.querySelectorAll(".qa-sidebar-item"); const connectSidebarItem = [...sidebarItems].find(element => { return element.textContent === "Setup";
});
ok(connectSidebarItem, "Sidebar contains a Connect item"); const connectLink = connectSidebarItem.querySelector(".qa-sidebar-link");
ok(connectLink, "Sidebar contains a Connect link");
info("Click on the Connect link in the sidebar");
connectLink.click();
info("Wait until Connect page is displayed");
await waitUntil(() => doc.querySelector(".qa-connect-page"));
}
function getDebugTargetPane(title, document) { // removes the suffix "(<NUMBER>)" in debug target pane's title, if needed const sanitizeTitle = x => { return x.replace(/\s+\(\d+\)$/, "");
};
const targetTitle = sanitizeTitle(title); for (const titleEl of document.querySelectorAll( ".qa-debug-target-pane-title"
)) { if (sanitizeTitle(titleEl.textContent) !== targetTitle) { continue;
}
async function connectToRuntime(deviceName, document) {
info(`Wait until the sidebar item for ${deviceName} appears`);
await waitUntil(() => findSidebarItemByText(deviceName, document)); const sidebarItem = findSidebarItemByText(deviceName, document); const connectButton = sidebarItem.querySelector(".qa-connect-button");
ok(
connectButton,
`Connect button is displayed for the runtime ${deviceName}`
);
info("Click on the connect button and wait until it disappears");
connectButton.click();
await waitUntil(() => !sidebarItem.querySelector(".qa-connect-button"));
}
async function selectRuntime(deviceName, name, document) { const sidebarItem = findSidebarItemByText(deviceName, document); const store = document.defaultView.AboutDebugging.store; const onSelectPageSuccess = waitForDispatch(store, "SELECT_PAGE_SUCCESS");
info("Wait for SELECT_PAGE_SUCCESS to be dispatched");
await onSelectPageSuccess;
}
function getToolbox(win) { return gDevTools.getToolboxes().find(toolbox => toolbox.win === win);
}
/** * Open the performance profiler dialog. Assumes the client is a mocked remote runtime * client.
*/
async function openProfilerDialog(client, doc) { const onProfilerLoaded = new Promise(r => {
client.loadPerformanceProfiler = r;
});
info("Click on the Profile Runtime button"); const profileButton = doc.querySelector(".qa-profile-runtime-button");
profileButton.click();
info( "Wait for the loadPerformanceProfiler callback to be executed on client-wrapper"
); return onProfilerLoaded;
}
/** * The "This Firefox" string depends on the brandShortName, which will be different * depending on the channel where tests are running.
*/ function getThisFirefoxString(aboutDebuggingWindow) { const loader = aboutDebuggingWindow.getBrowserLoaderForWindow(); const { l10n } = loader.require( "resource://devtools/client/aboutdebugging/src/modules/l10n.js"
); return l10n.getString("about-debugging-this-firefox-runtime-name");
}
function waitUntilUsbDeviceIsUnplugged(deviceName, aboutDebuggingDocument) {
info("Wait until the USB sidebar item appears as unplugged"); return waitUntil(() => { const sidebarItem = findSidebarItemByText(
deviceName,
aboutDebuggingDocument
); return !!sidebarItem.querySelector(".qa-runtime-item-unplugged");
});
}
/** * Changing the selected tab in the current browser will trigger a tablist * update. * If the currently selected page is "this-firefox", we should wait for the * the corresponding REQUEST_TABS_SUCCESS that will be triggered by the change. * * @param {Browser} browser * The browser instance to update. * @param {XULTab} tab * The tab to select. * @param {Object} store * The about:debugging redux store.
*/
async function updateSelectedTab(browser, tab, store) {
info("Update the selected tab");
// A tabs request will only be issued if we are on this-firefox. const onTabsSuccess = isOnThisFirefox
? waitForDispatch(store, "REQUEST_TABS_SUCCESS")
: null;
// Update the selected tab.
browser.selectedTab = tab;
if (onTabsSuccess) {
info("Wait for the tablist update after updating the selected tab");
await onTabsSuccess;
}
}
/** * Synthesizes key input inside the DebugTargetInfo's URL component. * * @param {DevToolsToolbox} toolbox * The DevToolsToolbox debugging the target. * @param {HTMLElement} inputEl * The <input> element to submit the URL with. * @param {String} url * The URL to navigate to.
*/
async function synthesizeUrlKeyInput(toolbox, inputEl, url) { const { devtoolsDocument, devtoolsWindow } = toolbox;
info("Wait for URL input to be focused."); const onInputFocused = waitUntil(
() => devtoolsDocument.activeElement === inputEl
);
inputEl.focus();
await onInputFocused;
info("Synthesize entering URL into text field"); const onInputChange = waitUntil(() => inputEl.value === url); for (const key of url.split("")) {
EventUtils.synthesizeKey(key, {}, devtoolsWindow);
}
await onInputChange;
info("Submit URL to navigate to");
EventUtils.synthesizeKey("KEY_Enter");
}
/** * Click on a given add-on widget button so that its browser actor is fired. * Typically a popup would open, or a listener would be called in the background page. * * @param {String} addonId * The ID of the add-on to click on.
*/ function clickOnAddonWidget(addonId) { // Devtools are in another window and may have the focus. // Ensure focusing the browser window when clicking on the widget. const focusedWin = Services.focus.focusedWindow; if (focusedWin != window) {
window.focus();
} // Find the browserAction button that will show the webextension popup. const widgetId = addonId.toLowerCase().replace(/[^a-z0-9_-]/g, "_"); const browserActionId = widgetId + "-browser-action"; const browserActionEl = window.document.getElementById(browserActionId);
ok(browserActionEl, "Got the browserAction button from the browser UI");
info("Show the web extension popup");
browserActionEl
.querySelector(".unified-extensions-item-action-button")
.click();
}
// Create basic addon data as the DevToolsClient would return it. function createAddonData({
id,
name,
isSystem = false,
hidden = false,
temporary = false,
}) { return {
actor: `actorid-${id}`,
hidden,
iconURL: `moz-extension://${id}/icon-url.png`,
id,
manifestURL: `moz-extension://${id}/manifest-url.json`,
name,
isSystem,
temporarilyInstalled: temporary,
debuggable: true,
};
}
async function connectToLocalFirefox({ runtimeId, runtimeName, deviceName }) { // This is a client to the current Firefox. const clientWrapper = await createLocalClientWrapper();
// enable USB devices mocks const mocks = new Mocks(); const usbClient = mocks.createUSBRuntime(runtimeId, {
deviceName,
name: runtimeName,
clientWrapper,
});
// Wrap a disconnect helper for convenience for the caller. const disconnect = doc =>
disconnectFromLocalFirefox({
doc,
runtimeId,
deviceName,
mocks,
});
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.