diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /devtools/client/dom/test | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/dom/test')
-rw-r--r-- | devtools/client/dom/test/browser.ini | 18 | ||||
-rw-r--r-- | devtools/client/dom/test/browser_dom_array.js | 66 | ||||
-rw-r--r-- | devtools/client/dom/test/browser_dom_basic.js | 22 | ||||
-rw-r--r-- | devtools/client/dom/test/browser_dom_fission_target_switching.js | 41 | ||||
-rw-r--r-- | devtools/client/dom/test/browser_dom_iframe_picker.js | 80 | ||||
-rw-r--r-- | devtools/client/dom/test/browser_dom_nodes_highlight.js | 73 | ||||
-rw-r--r-- | devtools/client/dom/test/browser_dom_nodes_select.js | 43 | ||||
-rw-r--r-- | devtools/client/dom/test/browser_dom_refresh.js | 30 | ||||
-rw-r--r-- | devtools/client/dom/test/head.js | 201 | ||||
-rw-r--r-- | devtools/client/dom/test/page_array.html | 19 | ||||
-rw-r--r-- | devtools/client/dom/test/page_basic.html | 15 | ||||
-rw-r--r-- | devtools/client/dom/test/page_dom_nodes.html | 18 |
12 files changed, 626 insertions, 0 deletions
diff --git a/devtools/client/dom/test/browser.ini b/devtools/client/dom/test/browser.ini new file mode 100644 index 0000000000..379ca86471 --- /dev/null +++ b/devtools/client/dom/test/browser.ini @@ -0,0 +1,18 @@ +[DEFAULT] +tags = devtools +subsuite = devtools +support-files = + head.js + page_array.html + page_basic.html + page_dom_nodes.html + !/devtools/client/shared/test/shared-head.js + !/devtools/client/shared/test/telemetry-test-helpers.js + +[browser_dom_array.js] +[browser_dom_basic.js] +[browser_dom_fission_target_switching.js] +[browser_dom_iframe_picker.js] +[browser_dom_nodes_highlight.js] +[browser_dom_nodes_select.js] +[browser_dom_refresh.js] diff --git a/devtools/client/dom/test/browser_dom_array.js b/devtools/client/dom/test/browser_dom_array.js new file mode 100644 index 0000000000..9ef35b1717 --- /dev/null +++ b/devtools/client/dom/test/browser_dom_array.js @@ -0,0 +1,66 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PAGE_URL = URL_ROOT + "page_array.html"; +const TEST_ARRAY = [ + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", +]; + +/** + * Basic test that checks content of the DOM panel. + */ +add_task(async function () { + info("Test DOM Panel Array Expansion started"); + + const { panel } = await addTestTab(TEST_PAGE_URL); + + // Expand specified row and wait till children are displayed. + await expandRow(panel, "_a"); + + // Verify that children is displayed now. + const childRows = getAllRowsForLabel(panel, "_a"); + + const item = childRows.pop(); + is(item.name, "length", "length property is correct"); + is(item.value, 26, "length property value is 26"); + + let i = 0; + for (const name in childRows) { + const row = childRows[name]; + + is( + parseInt(name, 10), + i++, + `index ${name} is correct and sorted into the correct position` + ); + ok(typeof row.name === "number", "array index is displayed as a number"); + is(TEST_ARRAY[name], row.value, `value for array[${name}] is ${row.value}`); + } +}); diff --git a/devtools/client/dom/test/browser_dom_basic.js b/devtools/client/dom/test/browser_dom_basic.js new file mode 100644 index 0000000000..c6b962015f --- /dev/null +++ b/devtools/client/dom/test/browser_dom_basic.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PAGE_URL = URL_ROOT + "page_basic.html"; + +/** + * Basic test that checks content of the DOM panel. + */ +add_task(async function () { + info("Test DOM panel basic started"); + + const { panel } = await addTestTab(TEST_PAGE_URL); + + // Expand specified row and wait till children are displayed. + await expandRow(panel, "_a"); + + // Verify that child is displayed now. + const childRow = getRowByLabel(panel, "_data"); + ok(childRow, "Child row must exist"); +}); diff --git a/devtools/client/dom/test/browser_dom_fission_target_switching.js b/devtools/client/dom/test/browser_dom_fission_target_switching.js new file mode 100644 index 0000000000..4393a7d842 --- /dev/null +++ b/devtools/client/dom/test/browser_dom_fission_target_switching.js @@ -0,0 +1,41 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test top-level target switching in the DOM panel. + +const PARENT_PROCESS_URI = "about:robots"; +const CONTENT_PROCESS_URI = URL_ROOT_SSL + "page_basic.html"; + +add_task(async function () { + // We use about:robots as the starting page because it will run in the parent process. + // Navigating from that page to a regular content page will always trigger a target + // switch, with or without fission. + + info("Open a page that runs in the parent process"); + const { panel } = await addTestTab(PARENT_PROCESS_URI); + + const _aProperty = getRowByLabel(panel, "_a"); + let buttonProperty = getRowByLabel(panel, "button"); + + ok(!_aProperty, "There is no _a property on the about:robots page"); + ok(buttonProperty, "There is, however, a button property on this page"); + + info("Navigate to a page that runs in the content process"); + // Wait for the DOM panel to refresh. + const store = getReduxStoreFromPanel(panel); + const onPropertiesFetched = waitForDispatch(store, "FETCH_PROPERTIES"); + // Also wait for the toolbox to switch to the new target, to avoid hanging requests when + // the test ends. + await navigateTo(CONTENT_PROCESS_URI); + await onPropertiesFetched; + + await waitFor(() => getRowByLabel(panel, "_a")); + ok(true, "This time, the _a property exists on this content process page"); + + buttonProperty = getRowByLabel(panel, "button"); + ok( + !buttonProperty, + "There is, however, no more button property on this page" + ); +}); diff --git a/devtools/client/dom/test/browser_dom_iframe_picker.js b/devtools/client/dom/test/browser_dom_iframe_picker.js new file mode 100644 index 0000000000..60e7747386 --- /dev/null +++ b/devtools/client/dom/test/browser_dom_iframe_picker.js @@ -0,0 +1,80 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Check that the DOM panel works as expected when a specific frame is selected in the +// iframe picker. + +const TEST_URL = `https://example.com/document-builder.sjs?html= + <h1>top_level</h1> + <iframe src="https://example.org/document-builder.sjs?html=in_iframe"></iframe>`; + +add_task(async function () { + const { panel } = await addTestTab(TEST_URL); + const toolbox = panel._toolbox; + + info("Wait until the iframe picker button is visible"); + try { + await waitFor(() => toolbox.doc.getElementById("command-button-frames")); + } catch (e) { + if (isFissionEnabled() && !isEveryFrameTargetEnabled()) { + ok( + true, + "Remote frames are not displayed in iframe picker if Fission is enabled but EFT is not" + ); + return; + } + throw e; + } + + info("Check `document` property when no specific frame is focused"); + let documentPropertyValue = getDocumentPropertyValue(panel); + + ok( + documentPropertyValue.startsWith("HTMLDocument https://example.com"), + `Got expected "document" value (${documentPropertyValue})` + ); + + info( + "Select the frame in the iframe picker and check that the document property is updated" + ); + // Wait for the DOM panel to refresh. + const store = getReduxStoreFromPanel(panel); + let onPropertiesFetched = waitForDispatch(store, "FETCH_PROPERTIES"); + + const exampleOrgFrame = toolbox.doc.querySelector( + "#toolbox-frame-menu .menuitem:last-child .command" + ); + + exampleOrgFrame.click(); + await onPropertiesFetched; + + documentPropertyValue = getDocumentPropertyValue(panel); + ok( + documentPropertyValue.startsWith("HTMLDocument https://example.org"), + `Got expected "document" value (${documentPropertyValue})` + ); + + info( + "Select the top-level frame and check that the document property is updated" + ); + onPropertiesFetched = waitForDispatch(store, "FETCH_PROPERTIES"); + + const exampleComFrame = toolbox.doc.querySelector( + "#toolbox-frame-menu .menuitem:first-child .command" + ); + exampleComFrame.click(); + await onPropertiesFetched; + + documentPropertyValue = getDocumentPropertyValue(panel); + ok( + documentPropertyValue.startsWith("HTMLDocument https://example.com"), + `Got expected "document" value (${documentPropertyValue})` + ); +}); + +function getDocumentPropertyValue(panel) { + return getRowByLabel(panel, "document").querySelector("td.treeValueCell") + .textContent; +} diff --git a/devtools/client/dom/test/browser_dom_nodes_highlight.js b/devtools/client/dom/test/browser_dom_nodes_highlight.js new file mode 100644 index 0000000000..311a52b30c --- /dev/null +++ b/devtools/client/dom/test/browser_dom_nodes_highlight.js @@ -0,0 +1,73 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PAGE_URL = URL_ROOT + "page_dom_nodes.html"; + +/** + * Checks that hovering nodes highlights them in the content page + */ +add_task(async function () { + info("Test DOM panel node highlight started"); + + const { panel, tab } = await addTestTab(TEST_PAGE_URL); + const toolbox = await gDevTools.getToolboxForTab(tab); + const highlighter = toolbox.getHighlighter(); + + const tests = [ + { + expected: "h1", + getNode: async () => { + return getRowByIndex(panel, 0).querySelector(".objectBox-node"); + }, + }, + { + expected: "h2", + getNode: async () => { + info("Expand specified row and wait till children are displayed"); + await expandRow(panel, "B"); + return getRowByIndex(panel, 1).querySelector(".objectBox-node"); + }, + }, + ]; + + for (const test of tests) { + info(`Get the NodeFront for ${test.expected}`); + const node = await test.getNode(); + + info("Highlight the node by moving the cursor on it"); + const onHighlighterShown = highlighter.waitForHighlighterShown(); + EventUtils.synthesizeMouseAtCenter( + node, + { + type: "mouseover", + }, + node.ownerDocument.defaultView + ); + const { nodeFront } = await onHighlighterShown; + is( + nodeFront.displayName, + test.expected, + "The correct node was highlighted" + ); + + info("Unhighlight the node by moving the cursor away from it"); + const onHighlighterHidden = highlighter.waitForHighlighterHidden(); + const btn = toolbox.doc.querySelector("#toolbox-meatball-menu-button"); + EventUtils.synthesizeMouseAtCenter( + btn, + { + type: "mouseover", + }, + btn.ownerDocument.defaultView + ); + + const { nodeFront: unhighlightedNode } = await onHighlighterHidden; + is( + unhighlightedNode.displayName, + test.expected, + "The node was unhighlighted" + ); + } +}); diff --git a/devtools/client/dom/test/browser_dom_nodes_select.js b/devtools/client/dom/test/browser_dom_nodes_select.js new file mode 100644 index 0000000000..7c71f2fde1 --- /dev/null +++ b/devtools/client/dom/test/browser_dom_nodes_select.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PAGE_URL = URL_ROOT + "page_dom_nodes.html"; + +/** + * Checks whether hovering nodes highlight them in the content page + */ +add_task(async function () { + info("Test DOM panel node highlight started"); + + const { panel, tab } = await addTestTab(TEST_PAGE_URL); + const toolbox = await gDevTools.getToolboxForTab(tab); + const node = getRowByIndex(panel, 0); + + // Loading the inspector panel at first, to make it possible to listen for + // new node selections + + await toolbox.loadTool("inspector"); + const inspector = toolbox.getPanel("inspector"); + + const openInInspectorIcon = node.querySelector(".open-inspector"); + ok(node !== null, "Node was logged as expected"); + + info( + "Clicking on the inspector icon and waiting for the " + + "inspector to be selected" + ); + const onInspectorSelected = toolbox.once("inspector-selected"); + const onInspectorUpdated = inspector.once("inspector-updated"); + const onNewNode = toolbox.selection.once("new-node-front"); + + openInInspectorIcon.click(); + + await onInspectorSelected; + await onInspectorUpdated; + const nodeFront = await onNewNode; + + ok(true, "Inspector selected and new node got selected"); + is(nodeFront.displayName, "h1", "The expected node was selected"); +}); diff --git a/devtools/client/dom/test/browser_dom_refresh.js b/devtools/client/dom/test/browser_dom_refresh.js new file mode 100644 index 0000000000..a82eb0accc --- /dev/null +++ b/devtools/client/dom/test/browser_dom_refresh.js @@ -0,0 +1,30 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PAGE_URL = URL_ROOT + "page_basic.html"; + +/** + * Basic test that checks the Refresh action in DOM panel. + */ +add_task(async function () { + info("Test DOM panel basic started"); + + const { panel } = await addTestTab(TEST_PAGE_URL); + + // Create a new variable in the page scope and refresh the panel. + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + content.wrappedJSObject._b = 10; + }); + + await refreshPanel(panel); + + // Verify that the variable is displayed now. + const row = getRowByLabel(panel, "_b"); + ok(row, "New variable must be displayed"); + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + delete content.wrappedJSObject._b; + }); +}); diff --git a/devtools/client/dom/test/head.js b/devtools/client/dom/test/head.js new file mode 100644 index 0000000000..93afb2fe09 --- /dev/null +++ b/devtools/client/dom/test/head.js @@ -0,0 +1,201 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */ + +"use strict"; + +// shared-head.js handles imports, constants, and utility functions +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", + this +); + +// DOM panel actions. +const constants = require("resource://devtools/client/dom/content/constants.js"); + +// Uncomment this pref to dump all devtools emitted events to the console. +// Services.prefs.setBoolPref("devtools.dom.enabled", true); + +// Enable the DOM panel +Services.prefs.setBoolPref("devtools.dom.enabled", true); + +registerCleanupFunction(() => { + info("finish() was called, cleaning up..."); + Services.prefs.clearUserPref("devtools.dump.emit"); + Services.prefs.clearUserPref("devtools.dom.enabled"); +}); + +/** + * Add a new test tab in the browser and load the given url. + * @param {String} url + * The url to be loaded in the new tab + * @return a promise that resolves to the tab object when + * the url is loaded + */ +async function addTestTab(url) { + info("Adding a new test tab with URL: '" + url + "'"); + + const tab = await addTab(url); + + // Select the DOM panel and wait till it's initialized. + const panel = await initDOMPanel(tab); + + // FETCH_PROPERTIES should be fired during the call to initDOMPanel + // But note that this behavior changed during a change in webconsole + // initialization. So this might be racy. + const doc = panel.panelWin.document; + const nodes = [...doc.querySelectorAll(".treeLabel")]; + ok(!!nodes.length, "The DOM panel is already populated"); + + return { + tab, + browser: tab.linkedBrowser, + panel, + }; +} + +/** + * Open the DOM panel for the given tab. + * + * @param {Element} tab + * Optional tab element for which you want open the DOM panel. + * The default tab is taken from the global variable |tab|. + * @return a promise that is resolved once the web console is open. + */ +async function initDOMPanel(tab) { + tab = tab || gBrowser.selectedTab; + const toolbox = await gDevTools.showToolboxForTab(tab, { toolId: "dom" }); + const panel = toolbox.getCurrentPanel(); + return panel; +} + +/** + * Synthesize asynchronous click event (with clean stack trace). + */ +function synthesizeMouseClickSoon(panel, element) { + return new Promise(resolve => { + executeSoon(() => { + EventUtils.synthesizeMouse(element, 2, 2, {}, panel.panelWin); + resolve(); + }); + }); +} + +/** + * Returns tree row with specified label. + */ +function getRowByLabel(panel, text) { + const doc = panel.panelWin.document; + const labels = [...doc.querySelectorAll(".treeLabel")]; + const label = labels.find(node => node.textContent == text); + return label ? label.closest(".treeRow") : null; +} + +/** + * Returns tree row with specified index. + */ +function getRowByIndex(panel, id) { + const doc = panel.panelWin.document; + const labels = [...doc.querySelectorAll(".treeLabel")]; + const label = labels.find((node, i) => i == id); + return label ? label.closest(".treeRow") : null; +} + +/** + * Returns the children (tree row text) of the specified object name as an + * array. + */ +function getAllRowsForLabel(panel, text) { + let rootObjectLevel; + let node; + const result = []; + const doc = panel.panelWin.document; + const nodes = [...doc.querySelectorAll(".treeLabel")]; + + // Find the label (object name) for which we want the children. We remove + // nodes from the start of the array until we reach the property. The children + // are then at the start of the array. + while (true) { + node = nodes.shift(); + + if (!node || node.textContent === text) { + rootObjectLevel = node.getAttribute("data-level"); + break; + } + } + + // Return an empty array if the node is not found. + if (!node) { + return result; + } + + // Now get the children. + for (node of nodes) { + const level = node.getAttribute("data-level"); + + if (level > rootObjectLevel) { + result.push({ + name: normalizeTreeValue(node.textContent), + value: normalizeTreeValue( + node.parentNode.nextElementSibling.textContent + ), + }); + } else { + break; + } + } + + return result; +} + +/** + * Strings in the tree are in the form ""a"" and numbers in the form "1". We + * normalize these values by converting ""a"" to "a" and "1" to 1. + * + * @param {String} value + * The value to normalize. + * @return {String|Number} + * The normalized value. + */ +function normalizeTreeValue(value) { + if (value === `""`) { + return ""; + } + if (value.startsWith(`"`) && value.endsWith(`"`)) { + return value.substr(1, value.length - 2); + } + if (isFinite(value) && parseInt(value, 10) == value) { + return parseInt(value, 10); + } + + return value; +} + +/** + * Expands elements with given label and waits till + * children are received from the backend. + */ +function expandRow(panel, labelText) { + const row = getRowByLabel(panel, labelText); + return synthesizeMouseClickSoon(panel, row).then(() => { + // Wait till children (properties) are fetched + // from the backend. + const store = getReduxStoreFromPanel(panel); + return waitForDispatch(store, "FETCH_PROPERTIES"); + }); +} + +function refreshPanel(panel) { + const doc = panel.panelWin.document; + const button = doc.querySelector("#dom-refresh-button"); + return synthesizeMouseClickSoon(panel, button).then(() => { + // Wait till children (properties) are fetched + // from the backend. + const store = getReduxStoreFromPanel(panel); + return waitForDispatch(store, "FETCH_PROPERTIES"); + }); +} + +function getReduxStoreFromPanel(panel) { + return panel.panelWin.view.mainFrame.store; +} diff --git a/devtools/client/dom/test/page_array.html b/devtools/client/dom/test/page_array.html new file mode 100644 index 0000000000..848d6c4ce5 --- /dev/null +++ b/devtools/client/dom/test/page_array.html @@ -0,0 +1,19 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> +<html> + <head> + <meta charset="utf-8"/> + <title>DOM Panel Array Expansion Test Page</title> + </head> + <body> + <h2>DOM Panel Array Expansion Test Page</h2> + <script type="text/javascript"> + "use strict"; + window._a = [ + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", + "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + ]; + </script> + </body> +</html> diff --git a/devtools/client/dom/test/page_basic.html b/devtools/client/dom/test/page_basic.html new file mode 100644 index 0000000000..170b3112a6 --- /dev/null +++ b/devtools/client/dom/test/page_basic.html @@ -0,0 +1,15 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> +<html> + <head> + <meta charset="utf-8"/> + <title>DOM test page</title> + </head> + <body> + <script type="text/javascript"> + "use strict"; + window._a = {_data: "test"}; + </script> + </body> +</html> diff --git a/devtools/client/dom/test/page_dom_nodes.html b/devtools/client/dom/test/page_dom_nodes.html new file mode 100644 index 0000000000..64e78d08db --- /dev/null +++ b/devtools/client/dom/test/page_dom_nodes.html @@ -0,0 +1,18 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"/> + <title>DOM test hovering nodes page</title> + </head> + <body> + <h1 id="a">Node highlight test</h1> + <h2 id="b">Node highlight test inside object</h2> + <script> + "use strict"; + window.A = document.getElementById("a"); + window.B = {_data: document.getElementById("b")}; + </script> + </body> +</html> |