/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; loadTestSubscript("head_devtools.js"); const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/"; /** * this test file ensures that: * * - devtools.inspectedWindow.eval provides the expected $0 and inspect bindings */ add_task(async function test_devtools_inspectedWindow_eval_bindings() { const TEST_TARGET_URL = `${BASE_URL}/file_inspectedwindow_eval.html`; let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, TEST_TARGET_URL ); function devtools_page() { browser.test.onMessage.addListener(async (msg, ...args) => { if (msg !== "inspectedWindow-eval-request") { browser.test.fail(`Unexpected test message received: ${msg}`); return; } try { const [evalResult, errorResult] = await browser.devtools.inspectedWindow.eval(...args); browser.test.sendMessage("inspectedWindow-eval-result", { evalResult, errorResult, }); } catch (err) { browser.test.sendMessage("inspectedWindow-eval-result"); browser.test.fail(`Error: ${err} :: ${err.stack}`); } }); browser.test.sendMessage("devtools-page-loaded"); } let extension = ExtensionTestUtils.loadExtension({ manifest: { devtools_page: "devtools_page.html", }, files: { "devtools_page.html": ` `, "devtools_page.js": devtools_page, }, }); await extension.startup(); const toolbox = await openToolboxForTab(tab); await extension.awaitMessage("devtools-page-loaded"); // Test $0 binding with no selected node info("Test inspectedWindow.eval $0 binding with no selected node"); const evalNoSelectedNodePromise = extension.awaitMessage( `inspectedWindow-eval-result` ); extension.sendMessage(`inspectedWindow-eval-request`, "$0"); const evalNoSelectedNodeResult = await evalNoSelectedNodePromise; Assert.deepEqual( evalNoSelectedNodeResult, { evalResult: undefined, errorResult: undefined }, "Got the expected eval result" ); // Test $0 binding with a selected node in the inspector. await openToolboxForTab(tab, "inspector"); info( "Test inspectedWindow.eval $0 binding with a selected node in the inspector" ); const evalSelectedNodePromise = extension.awaitMessage( `inspectedWindow-eval-result` ); extension.sendMessage(`inspectedWindow-eval-request`, "$0 && $0.tagName"); const evalSelectedNodeResult = await evalSelectedNodePromise; Assert.deepEqual( evalSelectedNodeResult, { evalResult: "BODY", errorResult: undefined }, "Got the expected eval result" ); // Test that inspect($0) switch the developer toolbox to the inspector. await openToolboxForTab(tab, TOOLBOX_BLANK_PANEL_ID); const inspectorPanelSelectedPromise = (async () => { const toolId = await toolbox.once("select"); if (toolId === "inspector") { info("Toolbox has been switched to the inspector as expected"); const selectedNodeName = toolbox.selection.nodeFront && toolbox.selection.nodeFront._form.nodeName; is( selectedNodeName, "A", "The expected DOM node has been selected in the inspector" ); } else { throw new Error( `inspector panel expected, ${toolId} has been selected instead` ); } })(); info("Test inspectedWindow.eval inspect() binding called for a DOM element"); extension.sendMessage( `inspectedWindow-eval-request`, "inspect(document.querySelector('a#link-to-inspect'))" ); await extension.awaitMessage(`inspectedWindow-eval-result`); info( "Wait for the toolbox to switch to the inspector and the expected node has been selected" ); await inspectorPanelSelectedPromise; function expectedSourceSelected(sourceFilename, sourceLine) { return () => { const dbg = toolbox.getPanel("jsdebugger"); const selectedLocation = dbg._selectors.getSelectedLocation( dbg._getState() ); if (!selectedLocation) { return false; } return ( selectedLocation.sourceId.includes(sourceFilename) && selectedLocation.line == sourceLine ); }; } info("Test inspectedWindow.eval inspect() binding called for a function"); const debuggerPanelSelectedPromise = (async () => { const toolId = await toolbox.once("select"); if (toolId === "jsdebugger") { info("Toolbox has been switched to the jsdebugger as expected"); } else { throw new Error( `jsdebugger panel expected, ${toolId} has been selected instead` ); } })(); extension.sendMessage( `inspectedWindow-eval-request`, "inspect(test_inspect_function)" ); await extension.awaitMessage(`inspectedWindow-eval-result`); await debuggerPanelSelectedPromise; await BrowserTestUtils.waitForCondition( expectedSourceSelected("file_inspectedwindow_eval.html", 9), "Wait the expected function to be selected in the jsdebugger panel" ); info("Test inspectedWindow.eval inspect() bound function"); extension.sendMessage( `inspectedWindow-eval-request`, "inspect(test_bound_function)" ); await extension.awaitMessage(`inspectedWindow-eval-result`); await BrowserTestUtils.waitForCondition( expectedSourceSelected("file_inspectedwindow_eval.html", 15), "Wait the expected function to be selected in the jsdebugger panel" ); info("Test inspectedWindow.eval inspect() binding called for a JS object"); const splitPanelOpenedPromise = (async () => { await toolbox.once("split-console"); const { hud } = toolbox.getPanel("webconsole"); // Wait for the message to appear on the console. const messageNode = await new Promise(resolve => { hud.ui.on("new-messages", function onThisMessage(messages) { for (let m of messages) { resolve(m.node); hud.ui.off("new-messages", onThisMessage); return; } }); }); let objectInspectors = [...messageNode.querySelectorAll(".tree")]; is( objectInspectors.length, 1, "There is the expected number of object inspectors" ); // We need to wait for the object to be expanded so we don't call the server on a closed connection. const [oi] = objectInspectors; let nodes = oi.querySelectorAll(".node"); ok(nodes.length >= 1, "The object preview is rendered as expected"); // The tree can still be collapsed since the properties are fetched asynchronously. if (nodes.length === 1) { info("Waiting for the object properties to be displayed"); // If this is the case, we wait for the properties to be fetched and displayed. await new Promise(resolve => { const observer = new MutationObserver(mutations => { resolve(); observer.disconnect(); }); observer.observe(oi, { childList: true }); }); // Retrieve the new nodes. nodes = oi.querySelectorAll(".node"); } // We should have 3 nodes : // ▼ Object { testkey: "testvalue" } // | testkey: "testvalue" // | ▶︎ __proto__: Object { … } is(nodes.length, 3, "The object preview has the expected number of nodes"); })(); const inspectJSObjectPromise = extension.awaitMessage( `inspectedWindow-eval-result` ); extension.sendMessage( `inspectedWindow-eval-request`, "inspect({testkey: 'testvalue'})" ); await inspectJSObjectPromise; info("Wait for the split console to be opened and the JS object inspected"); await splitPanelOpenedPromise; info("Split console has been opened as expected"); await closeToolboxForTab(tab); await extension.unload(); BrowserTestUtils.removeTab(tab); });