From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../browser_inspector_breadcrumbs_mutations.js | 282 +++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 devtools/client/inspector/test/browser_inspector_breadcrumbs_mutations.js (limited to 'devtools/client/inspector/test/browser_inspector_breadcrumbs_mutations.js') diff --git a/devtools/client/inspector/test/browser_inspector_breadcrumbs_mutations.js b/devtools/client/inspector/test/browser_inspector_breadcrumbs_mutations.js new file mode 100644 index 0000000000..91e3976907 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_mutations.js @@ -0,0 +1,282 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that the breadcrumbs widget refreshes correctly when there are markup +// mutations (and that it doesn't refresh when those mutations don't change its +// output). + +const TEST_URI = URL_ROOT + "doc_inspector_breadcrumbs.html"; + +// Each item in the TEST_DATA array is a test case that should contain the +// following properties: +// - desc {String} A description of this test case (will be logged). +// - setup {Function*} A generator function (can yield promises) that sets up +// the test case. Useful for selecting a node before starting the test. +// - run {Function*} A generator function (can yield promises) that runs the +// actual test case, i.e, mutates the content DOM to cause the breadcrumbs +// to refresh, or not. +// - shouldRefresh {Boolean} Once the `run` function has completed, and the test +// has detected that the page has changed, this boolean instructs the test to +// verify if the breadcrumbs has refreshed or not. +// - output {Array} A list of strings for the text that should be found in each +// button after the test has run. +const TEST_DATA = [ + { + desc: "Adding a child at the end of the chain shouldn't change anything", + async setup(inspector) { + await selectNode("#i1111", inspector); + }, + async run({ walker, selection }) { + await walker.setInnerHTML(selection.nodeFront, "test"); + }, + shouldRefresh: false, + output: ["html", "body", "article#i1", "div#i11", "div#i111", "div#i1111"], + }, + { + desc: "Updating an ID to an displayed element should refresh", + setup() {}, + async run({ walker }) { + const node = await walker.querySelector(walker.rootNode, "#i1"); + await node.modifyAttributes([ + { + attributeName: "id", + newValue: "i1-changed", + }, + ]); + }, + shouldRefresh: true, + output: [ + "html", + "body", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: "Updating an class to a displayed element should refresh", + setup() {}, + async run({ walker }) { + const node = await walker.querySelector(walker.rootNode, "body"); + await node.modifyAttributes([ + { + attributeName: "class", + newValue: "test-class", + }, + ]); + }, + shouldRefresh: true, + output: [ + "html", + "body.test-class", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: + "Updating a non id/class attribute to a displayed element should not " + + "refresh", + setup() {}, + async run({ walker }) { + const node = await walker.querySelector(walker.rootNode, "#i11"); + await node.modifyAttributes([ + { + attributeName: "name", + newValue: "value", + }, + ]); + }, + shouldRefresh: false, + output: [ + "html", + "body.test-class", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: "Moving a child in an element that's not displayed should not refresh", + setup() {}, + async run({ walker }) { + // Re-append #i1211 as a last child of #i2. + const parent = await walker.querySelector(walker.rootNode, "#i2"); + const child = await walker.querySelector(walker.rootNode, "#i211"); + await walker.insertBefore(child, parent); + }, + shouldRefresh: false, + output: [ + "html", + "body.test-class", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: "Moving an undisplayed child in a displayed element should not refresh", + setup() {}, + async run({ walker }) { + // Re-append #i2 in body (move it to the end). + const parent = await walker.querySelector(walker.rootNode, "body"); + const child = await walker.querySelector(walker.rootNode, "#i2"); + await walker.insertBefore(child, parent); + }, + shouldRefresh: false, + output: [ + "html", + "body.test-class", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: + "Updating attributes on an element that's not displayed should not " + + "refresh", + setup() {}, + async run({ walker }) { + const node = await walker.querySelector(walker.rootNode, "#i2"); + await node.modifyAttributes([ + { + attributeName: "id", + newValue: "i2-changed", + }, + { + attributeName: "class", + newValue: "test-class", + }, + ]); + }, + shouldRefresh: false, + output: [ + "html", + "body.test-class", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: "Removing the currently selected node should refresh", + async setup(inspector) { + await selectNode("#i2-changed", inspector); + }, + async run({ walker, selection }) { + await walker.removeNode(selection.nodeFront); + }, + shouldRefresh: true, + output: ["html", "body.test-class"], + }, + { + desc: "Changing the class of the currently selected node should refresh", + setup() {}, + async run({ selection }) { + await selection.nodeFront.modifyAttributes([ + { + attributeName: "class", + newValue: "test-class-changed", + }, + ]); + }, + shouldRefresh: true, + output: ["html", "body.test-class-changed"], + }, + { + desc: "Changing the id of the currently selected node should refresh", + setup() {}, + async run({ selection }) { + await selection.nodeFront.modifyAttributes([ + { + attributeName: "id", + newValue: "new-id", + }, + ]); + }, + shouldRefresh: true, + output: ["html", "body#new-id.test-class-changed"], + }, +]; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URI); + const breadcrumbs = inspector.panelDoc.getElementById( + "inspector-breadcrumbs" + ); + const container = breadcrumbs.querySelector(".html-arrowscrollbox-inner"); + const win = container.ownerDocument.defaultView; + + for (const { desc, setup, run, shouldRefresh, output } of TEST_DATA) { + info("Running test case: " + desc); + + info( + "Listen to markupmutation events from the inspector to know when a " + + "test case has completed" + ); + const onContentMutation = inspector.once("markupmutation"); + + info("Running setup"); + await setup(inspector); + + info("Listen to mutations on the breadcrumbs container"); + let hasBreadcrumbsMutated = false; + const observer = new win.MutationObserver(mutations => { + // Only consider childList changes or tooltiptext/checked attributes + // changes. The rest may be mutations caused by the overflowing arrowbox. + for (const { type, attributeName } of mutations) { + const isChildList = type === "childList"; + const isAttributes = + type === "attributes" && + (attributeName === "checked" || attributeName === "tooltiptext"); + if (isChildList || isAttributes) { + hasBreadcrumbsMutated = true; + break; + } + } + }); + observer.observe(container, { + attributes: true, + childList: true, + subtree: true, + }); + + info("Running the test case"); + await run(inspector); + + info("Wait until the page has mutated"); + await onContentMutation; + + if (shouldRefresh) { + info("The breadcrumbs is expected to refresh, so wait for it"); + await inspector.once("inspector-updated"); + } else { + ok( + !inspector._updateProgress, + "The breadcrumbs widget is not currently updating" + ); + } + + is(shouldRefresh, hasBreadcrumbsMutated, "Has the breadcrumbs refreshed?"); + observer.disconnect(); + + info("Check the output of the breadcrumbs widget"); + is(container.childNodes.length, output.length, "Correct number of buttons"); + for (let i = 0; i < container.childNodes.length; i++) { + is( + output[i], + container.childNodes[i].textContent, + "Text content for button " + i + " is correct" + ); + } + } +}); -- cgit v1.2.3