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/inspector/compatibility/test/browser | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.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/inspector/compatibility/test/browser')
18 files changed, 1909 insertions, 0 deletions
diff --git a/devtools/client/inspector/compatibility/test/browser/browser.ini b/devtools/client/inspector/compatibility/test/browser/browser.ini new file mode 100644 index 0000000000..4d09958d0b --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser.ini @@ -0,0 +1,27 @@ +[DEFAULT] +tags = devtools +subsuite = devtools +support-files = + head.js + !/devtools/client/inspector/test/head.js + !/devtools/client/inspector/test/shared-head.js + !/devtools/client/shared/test/shared-head.js + !/devtools/client/shared/test/telemetry-test-helpers.js + !/devtools/client/shared/test/highlighter-test-actor.js + +[browser_compatibility_css-property_issue.js] +[browser_compatibility_dynamic_js-attribute-change.js] +[browser_compatibility_dynamic_js-dom-change.js] +[browser_compatibility_dynamic_markup-dom-change.js] +[browser_compatibility_dynamic_ruleview-attribute-change.js] +[browser_compatibility_event_document-reload.js] +[browser_compatibility_event_panel-select.js] +[browser_compatibility_event_rule-change.js] +[browser_compatibility_event_selected-node-change.js] +[browser_compatibility_event_top-level-target-change.js] +[browser_compatibility_issue-node.js] +[browser_compatibility_preference.js] +[browser_compatibility_settings.js] +[browser_compatibility_throbber.js] +[browser_compatibility_unsupported-browsers_all.js] +[browser_compatibility_unsupported-browsers_some.js] diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_css-property_issue.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_css-property_issue.js new file mode 100644 index 0000000000..dc3df0d1f0 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_css-property_issue.js @@ -0,0 +1,91 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that unsupported CSS properties are correctly reported as issues. + +const { + COMPATIBILITY_ISSUE_TYPE, +} = require("resource://devtools/shared/constants.js"); + +const TEST_URI = ` + <style> + body { + color: blue; + scrollbar-width: thin; + user-modify: read-only; + hyphenate-limit-chars: auto; + overflow-clip-box: padding-box; + } + div { + ruby-align: center; + } + </style> + <body> + <div>test</div> + </body> +`; + +const TEST_DATA_SELECTED = [ + { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY, + property: "scrollbar-width", + url: "https://developer.mozilla.org/docs/Web/CSS/scrollbar-width", + deprecated: false, + experimental: false, + }, + { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY_ALIASES, + property: "user-modify", + url: "https://developer.mozilla.org/docs/Web/CSS/user-modify", + aliases: ["user-modify"], + deprecated: true, + experimental: false, + }, + { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY, + property: "hyphenate-limit-chars", + // No MDN url but a spec one + specUrl: + "https://drafts.csswg.org/css-text-4/#propdef-hyphenate-limit-chars", + deprecated: false, + experimental: false, + }, + { + // No MDN url nor spec url + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY, + property: "overflow-clip-box", + deprecated: false, + experimental: false, + }, +]; + +const TEST_DATA_ALL = [ + ...TEST_DATA_SELECTED, + { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY, + property: "ruby-align", + url: "https://developer.mozilla.org/docs/Web/CSS/ruby-align", + deprecated: false, + experimental: true, + }, +]; + +add_task(async function () { + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + + const { allElementsPane, selectedElementPane } = + await openCompatibilityView(); + + // If the test fail because the properties used are no longer in the dataset, or they + // now have mdn/spec url although we expected them not to, uncomment the next line + // to get all the properties in the dataset that don't have a MDN url. + // logCssCompatDataPropertiesWithoutMDNUrl() + + info("Check the content of the issue list on the selected element"); + await assertIssueList(selectedElementPane, TEST_DATA_SELECTED); + + info("Check the content of the issue list on all elements"); + await assertIssueList(allElementsPane, TEST_DATA_ALL); +}); diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_js-attribute-change.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_js-attribute-change.js new file mode 100644 index 0000000000..6280e73278 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_js-attribute-change.js @@ -0,0 +1,127 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { + COMPATIBILITY_ISSUE_TYPE, +} = require("resource://devtools/shared/constants.js"); + +const { + COMPATIBILITY_UPDATE_NODE_COMPLETE, +} = require("resource://devtools/client/inspector/compatibility/actions/index.js"); + +// Test the behavior rules are dynamically added + +const ISSUE_OUTLINE_RADIUS = { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY, + property: "-moz-user-input", + url: "https://developer.mozilla.org/docs/Web/CSS/-moz-user-input", + deprecated: true, + experimental: false, +}; + +const ISSUE_HYPHENS = { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY_ALIASES, + aliases: ["hyphens"], + property: "hyphens", + url: "https://developer.mozilla.org/docs/Web/CSS/hyphens", + deprecated: false, + experimental: false, +}; + +const TEST_URI = ` + <style> + .issue { + -moz-user-input: none; + } + </style> + <body> + <div class="test"></div> + </body> +`; + +add_task(async function () { + info("Testing dynamic style change using JavaScript"); + const tab = await addTab( + "data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI) + ); + + const { allElementsPane, inspector, selectedElementPane } = + await openCompatibilityView(); + + info("Testing inline style change due to JavaScript execution"); + const onPanelUpdate = waitForUpdateSelectedNodeAction(inspector.store); + info("Select the div to undergo mutation"); + await selectNode(".test", inspector); + await onPanelUpdate; + + info("Check initial issues"); + await assertIssueList(selectedElementPane, []); + await assertIssueList(allElementsPane, []); + + info("Adding inline style with compatibility issue"); + await testAttributeMutation( + tab, + inspector, + selectedElementPane, + allElementsPane, + [ISSUE_HYPHENS], + [ISSUE_HYPHENS], + async function () { + content.document.querySelector(".test").style.hyphens = "none"; + } + ); + + info("Adding a class with declarations having compatibility issue"); + await testAttributeMutation( + tab, + inspector, + selectedElementPane, + allElementsPane, + [ISSUE_HYPHENS, ISSUE_OUTLINE_RADIUS], + [ISSUE_HYPHENS, ISSUE_OUTLINE_RADIUS], + async function () { + content.document.querySelector(".test").classList.add("issue"); + } + ); + + info("Removing a class with declarations having compatibility issue"); + await testAttributeMutation( + tab, + inspector, + selectedElementPane, + allElementsPane, + [ISSUE_HYPHENS], + [ISSUE_HYPHENS], + async function () { + content.document.querySelector(".test").classList.remove("issue"); + } + ); + + await removeTab(tab); +}); + +async function testAttributeMutation( + tab, + inspector, + selectedElementPane, + allElementsPane, + expectedSelectedElementIssues, + expectedAllElementsIssues, + contentTaskFunction +) { + const onPanelUpdate = Promise.all([ + inspector.once("markupmutation"), + waitForDispatch(inspector.store, COMPATIBILITY_UPDATE_NODE_COMPLETE), + ]); + info("Run the task in webpage context"); + await ContentTask.spawn(tab.linkedBrowser, {}, contentTaskFunction); + info("Wait for changes to reflect"); + await onPanelUpdate; + + info("Check issues listed in selected element pane"); + await assertIssueList(selectedElementPane, expectedSelectedElementIssues); + info("Check issues listed in all issues pane"); + await assertIssueList(allElementsPane, expectedAllElementsIssues); +} diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_js-dom-change.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_js-dom-change.js new file mode 100644 index 0000000000..1d8488a468 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_js-dom-change.js @@ -0,0 +1,150 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { + COMPATIBILITY_ISSUE_TYPE, +} = require("resource://devtools/shared/constants.js"); + +const { + COMPATIBILITY_APPEND_NODE_COMPLETE, + COMPATIBILITY_CLEAR_DESTROYED_NODES, +} = require("resource://devtools/client/inspector/compatibility/actions/index.js"); + +// Test the behavior rules are dynamically added + +const ISSUE_OUTLINE_RADIUS = { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY, + property: "-moz-user-input", + url: "https://developer.mozilla.org/docs/Web/CSS/-moz-user-input", + deprecated: true, + experimental: false, +}; + +const ISSUE_HYPHENS = { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY_ALIASES, + aliases: ["hyphens"], + property: "hyphens", + url: "https://developer.mozilla.org/docs/Web/CSS/hyphens", + deprecated: false, + experimental: false, +}; + +const TEST_URI = ` + <style> + .child { + -moz-user-input: none; + } + </style> + <body></body> +`; + +add_task(async function () { + info("Testing dynamic DOM mutation using JavaScript"); + const tab = await addTab( + "data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI) + ); + + const { allElementsPane, inspector, selectedElementPane } = + await openCompatibilityView(); + + info("Check initial issues"); + await assertIssueList(selectedElementPane, []); + await assertIssueList(allElementsPane, []); + + info("Append nodes dynamically using JavaScript"); + await testNodeMutation( + ".child", + COMPATIBILITY_APPEND_NODE_COMPLETE, + tab, + inspector, + selectedElementPane, + allElementsPane, + [ISSUE_OUTLINE_RADIUS], + [ISSUE_HYPHENS, ISSUE_OUTLINE_RADIUS], + async function () { + const doc = content.document; + const parent = doc.querySelector("body"); + + const newElementWithIssue = doc.createElement("div"); + newElementWithIssue.style.hyphens = "none"; + + const parentOfIssueElement = doc.createElement("div"); + parentOfIssueElement.classList.add("parent"); + const child = doc.createElement("div"); + child.classList.add("child"); + parentOfIssueElement.appendChild(child); + + parent.appendChild(newElementWithIssue); + parent.appendChild(parentOfIssueElement); + } + ); + + info("Remove node whose child has compatibility issue"); + await testNodeMutation( + "div", + COMPATIBILITY_CLEAR_DESTROYED_NODES, + tab, + inspector, + selectedElementPane, + allElementsPane, + [ISSUE_HYPHENS], + [ISSUE_HYPHENS], + async function () { + const doc = content.document; + const parent = doc.querySelector(".parent"); + parent.remove(); + } + ); + + info("Remove node which has compatibility issue"); + await testNodeMutation( + "body", + COMPATIBILITY_CLEAR_DESTROYED_NODES, + tab, + inspector, + selectedElementPane, + allElementsPane, + [], + [], + async function () { + const doc = content.document; + const issueElement = doc.querySelector("div"); + issueElement.remove(); + } + ); + + await removeTab(tab); +}); + +async function testNodeMutation( + selector, + action, + tab, + inspector, + selectedElementPane, + allElementsPane, + expectedSelectedElementIssues, + expectedAllElementsIssues, + contentTaskFunction +) { + let onPanelUpdate = Promise.all([ + inspector.once("markupmutation"), + waitForDispatch(inspector.store, action), + ]); + info("Add a new node with issue and another node whose child has the issue"); + await ContentTask.spawn(tab.linkedBrowser, {}, contentTaskFunction); + info("Wait for changes"); + await onPanelUpdate; + + onPanelUpdate = waitForUpdateSelectedNodeAction(inspector.store); + await selectNode(selector, inspector); + await onPanelUpdate; + + info("Check element issues"); + await assertIssueList(selectedElementPane, expectedSelectedElementIssues); + + info("Check all issues"); + await assertIssueList(allElementsPane, expectedAllElementsIssues); +} diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_markup-dom-change.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_markup-dom-change.js new file mode 100644 index 0000000000..0a600ffddf --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_markup-dom-change.js @@ -0,0 +1,158 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { + COMPATIBILITY_ISSUE_TYPE, +} = require("resource://devtools/shared/constants.js"); + +const { + COMPATIBILITY_APPEND_NODE_COMPLETE, + COMPATIBILITY_REMOVE_NODE_COMPLETE, +} = require("resource://devtools/client/inspector/compatibility/actions/index.js"); + +// Test the behavior rules are dynamically added + +const ISSUE_OUTLINE_RADIUS = { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY, + property: "-moz-user-input", + url: "https://developer.mozilla.org/docs/Web/CSS/-moz-user-input", + deprecated: true, + experimental: false, +}; + +const ISSUE_HYPHENS = { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY_ALIASES, + aliases: ["hyphens"], + property: "hyphens", + url: "https://developer.mozilla.org/docs/Web/CSS/hyphens", + deprecated: false, + experimental: false, +}; + +const TEST_URI = ` + <style> + div { + -moz-user-input: none; + } + </style> + <body> + <div></div> + <div class="parent"> + <div style="hyphens: none"></div> + </div> + </body> +`; + +add_task(async function () { + info("Testing dynamic DOM mutation using JavaScript"); + const tab = await addTab( + "data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI) + ); + + const { allElementsPane, inspector } = await openCompatibilityView(); + + info("Check initial issues"); + await assertIssueList(allElementsPane, [ISSUE_OUTLINE_RADIUS, ISSUE_HYPHENS]); + + info("Delete node whose child node has CSS compatibility issue"); + await testNodeRemoval(".parent", inspector, allElementsPane, [ + ISSUE_OUTLINE_RADIUS, + ]); + + info("Delete node that has CSS compatibility issue"); + await testNodeRemoval("div", inspector, allElementsPane, []); + + info("Add node that has CSS compatibility issue"); + await testNodeAddition("div", inspector, allElementsPane, [ + ISSUE_OUTLINE_RADIUS, + ]); + + await removeTab(tab); +}); + +/** + * Simulate a click on the markup-container (a line in the markup-view) + * that corresponds to the selector passed. + * This overrides the definition in inspector/test/head.js which times + * out when the container to be clicked is already the selected node. + * @param {String|NodeFront} selector + * @param {InspectorPanel} inspector The instance of InspectorPanel currently + * loaded in the toolbox + * @return {Promise} Resolves when the node has been selected. + */ +var clickContainer = async function (selector, inspector) { + info("Clicking on the markup-container for node " + selector); + + const nodeFront = await getNodeFront(selector, inspector); + const container = getContainerForNodeFront(nodeFront, inspector); + + const updated = container.selected + ? Promise.resolve() + : inspector.once("inspector-updated"); + EventUtils.synthesizeMouseAtCenter( + container.tagLine, + { type: "mousedown" }, + inspector.markup.doc.defaultView + ); + EventUtils.synthesizeMouseAtCenter( + container.tagLine, + { type: "mouseup" }, + inspector.markup.doc.defaultView + ); + return updated; +}; + +async function deleteNode(inspector, selector) { + info("Select node " + selector + " and make sure it is focused"); + await selectNode(selector, inspector); + await clickContainer(selector, inspector); + + info("Delete the node"); + const mutated = inspector.once("markupmutation"); + const updated = inspector.once("inspector-updated"); + EventUtils.sendKey("delete", inspector.panelWin); + await mutated; + await updated; +} + +async function testNodeAddition( + selector, + inspector, + allElementsPane, + expectedAllElementsIssues +) { + let onPanelUpdate = Promise.all([ + inspector.once("markupmutation"), + waitForDispatch(inspector.store, COMPATIBILITY_APPEND_NODE_COMPLETE), + ]); + info("Add a new node"); + await inspector.addNode(); + await onPanelUpdate; + + onPanelUpdate = waitForUpdateSelectedNodeAction(inspector.store); + await selectNode(selector, inspector); + await onPanelUpdate; + + info("Check issues list for the webpage"); + await assertIssueList(allElementsPane, expectedAllElementsIssues); +} + +async function testNodeRemoval( + selector, + inspector, + allElementsPane, + expectedAllElementsIssues +) { + const onPanelUpdate = Promise.all([ + inspector.once("markupmutation"), + waitForDispatch(inspector.store, COMPATIBILITY_REMOVE_NODE_COMPLETE), + ]); + info(`Delete the node with selector ${selector}`); + await deleteNode(inspector, selector); + await onPanelUpdate; + + info("Check issues list for the webpage"); + await assertIssueList(allElementsPane, expectedAllElementsIssues); +} diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_ruleview-attribute-change.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_ruleview-attribute-change.js new file mode 100644 index 0000000000..920e360ef0 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_dynamic_ruleview-attribute-change.js @@ -0,0 +1,117 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { + COMPATIBILITY_ISSUE_TYPE, +} = require("resource://devtools/shared/constants.js"); + +const { + COMPATIBILITY_UPDATE_NODE_COMPLETE, +} = require("resource://devtools/client/inspector/compatibility/actions/index.js"); + +// Test the behavior rules are dynamically added + +const ISSUE_OUTLINE_RADIUS = { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY, + property: "-moz-user-input", + url: "https://developer.mozilla.org/docs/Web/CSS/-moz-user-input", + deprecated: true, + experimental: false, +}; + +const ISSUE_HYPHENS = { + type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY_ALIASES, + aliases: ["hyphens"], + property: "hyphens", + url: "https://developer.mozilla.org/docs/Web/CSS/hyphens", + deprecated: false, + experimental: false, +}; + +const TEST_URI = ` + <style> + .issue { + -moz-user-input: none; + } + </style> + <body> + <div class="test issue"></div> + </body> +`; + +add_task(async function () { + info("Testing dynamic style change via the devtools inspector's rule view"); + const tab = await addTab( + "data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI) + ); + + const { allElementsPane, inspector, selectedElementPane } = + await openCompatibilityView(); + + info("Select the div to undergo mutation"); + const waitForCompatibilityListUpdate = waitForUpdateSelectedNodeAction( + inspector.store + ); + await selectNode(".test.issue", inspector); + await waitForCompatibilityListUpdate; + + info("Check initial issues"); + await checkPanelIssues(selectedElementPane, allElementsPane, [ + ISSUE_OUTLINE_RADIUS, + ]); + + await addNewRule( + "hyphens", + "none", + inspector, + selectedElementPane, + allElementsPane, + [ISSUE_OUTLINE_RADIUS, ISSUE_HYPHENS] + ); + + info("Toggle the inline issue rendering it disable"); + await togglePropStatusOnRuleView(inspector, 0, 0); + info("Check the issues listed in panel"); + await checkPanelIssues(selectedElementPane, allElementsPane, [ + ISSUE_OUTLINE_RADIUS, + ]); + + info("Toggle the class rule rendering it disabled"); + await togglePropStatusOnRuleView(inspector, 1, 0); + info("Check the panel issues listed in panel"); + await checkPanelIssues(selectedElementPane, allElementsPane, []); + + await removeTab(tab); +}); + +async function addNewRule( + newDeclaration, + value, + inspector, + selectedElementPane, + allElementsPane, + issue +) { + const { view } = await openRuleView(); + const waitForCompatibilityListUpdate = waitForDispatch( + inspector.store, + COMPATIBILITY_UPDATE_NODE_COMPLETE + ); + + info("Add a new inline property"); + await addProperty(view, 0, newDeclaration, value); + info("Wait for changes"); + await waitForCompatibilityListUpdate; + + info("Check issues list for element and the webpage"); + await checkPanelIssues(selectedElementPane, allElementsPane, issue); +} + +async function checkPanelIssues(selectedElementPane, allElementsPane, issues) { + info("Check selected element issues"); + await assertIssueList(selectedElementPane, issues); + info("Check all panel issues"); + await assertIssueList(allElementsPane, issues); +} diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_document-reload.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_document-reload.js new file mode 100644 index 0000000000..a7f18e0811 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_document-reload.js @@ -0,0 +1,95 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test the issues after reloading the browsing document. + +const TEST_URI = ` + <style> + body { + color: blue; + ruby-align: center; + user-modify: read-only; + } + div { + scrollbar-width: thin; + } + </style> + <body> + <div>test</div> + </body> +`; + +const TEST_DATA_SELECTED = [ + { + property: "ruby-align", + url: "https://developer.mozilla.org/docs/Web/CSS/ruby-align", + }, + { + property: "user-modify", + url: "https://developer.mozilla.org/docs/Web/CSS/user-modify", + }, +]; + +const TEST_DATA_ALL = [ + ...TEST_DATA_SELECTED, + { + property: "scrollbar-width", + url: "https://developer.mozilla.org/docs/Web/CSS/scrollbar-width", + }, +]; + +const { + COMPATIBILITY_UPDATE_SELECTED_NODE_FAILURE, + COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_FAILURE, +} = require("resource://devtools/client/inspector/compatibility/actions/index.js"); + +add_task(async function () { + const tab = await addTab( + "data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI) + ); + + const { allElementsPane, inspector, selectedElementPane } = + await openCompatibilityView(); + + info("Check the issues on the selected element"); + await assertIssueList(selectedElementPane, TEST_DATA_SELECTED); + info("Check the issues on all elements"); + await assertIssueList(allElementsPane, TEST_DATA_ALL); + + let isUpdateSelectedNodeFailure = false; + let isUpdateTopLevelTargetFailure = false; + waitForDispatch( + inspector.store, + COMPATIBILITY_UPDATE_SELECTED_NODE_FAILURE + ).then(() => { + isUpdateSelectedNodeFailure = true; + }); + waitForDispatch( + inspector.store, + COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_FAILURE + ).then(() => { + isUpdateTopLevelTargetFailure = true; + }); + + info("Reload the browsing page"); + const onReloaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + const onUpdateSelectedNode = waitForUpdateSelectedNodeAction(inspector.store); + const onUpdateTopLevelTarget = waitForUpdateTopLevelTargetAction( + inspector.store + ); + gBrowser.reloadTab(tab); + await Promise.all([onReloaded, onUpdateSelectedNode, onUpdateTopLevelTarget]); + + info("Check whether the failure action will be fired or not"); + ok( + !isUpdateSelectedNodeFailure && !isUpdateTopLevelTargetFailure, + "No error occurred" + ); + + info("Check the issues on the selected element again"); + await assertIssueList(selectedElementPane, TEST_DATA_SELECTED); + info("Check the issues on all elements again"); + await assertIssueList(allElementsPane, TEST_DATA_ALL); +}); diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_panel-select.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_panel-select.js new file mode 100644 index 0000000000..9aa845db0d --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_panel-select.js @@ -0,0 +1,173 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test the behavior when the panel is selected. + +const TEST_URI = "<body style='background-color: lime;'><div>test</div></body>"; +const TEST_ANOTHER_URI = "<body></body>"; + +const { + COMPATIBILITY_UPDATE_SELECTED_NODE_START, + COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_START, +} = require("resource://devtools/client/inspector/compatibility/actions/index.js"); + +add_task(async function () { + info( + "Check that the panel does not update when no changes occur while hidden" + ); + + await pushPref("devtools.inspector.activeSidebar", ""); + + const tab = await addTab(_toDataURL(TEST_URI)); + const { inspector } = await openCompatibilityView(); + + info("Select another sidebar panel"); + await _selectSidebarPanel(inspector, "changesview"); + + info("Select the compatibility panel again"); + let isSelectedNodeUpdated = false; + let isTopLevelTargetUpdated = false; + waitForDispatch( + inspector.store, + COMPATIBILITY_UPDATE_SELECTED_NODE_START + ).then(() => { + isSelectedNodeUpdated = true; + }); + waitForDispatch( + inspector.store, + COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_START + ).then(() => { + isTopLevelTargetUpdated = true; + }); + + await _selectSidebarPanel(inspector, "compatibilityview"); + + // Check above both flags after taking enough time. + await wait(1000); + + ok(!isSelectedNodeUpdated, "Avoid updating the selected node pane"); + ok(!isTopLevelTargetUpdated, "Avoid updating the top level target pane"); + + await removeTab(tab); +}); + +add_task(async function () { + info( + "Check that the panel only updates for the selected node when the node is changed while the panel is hidden" + ); + + await pushPref("devtools.inspector.activeSidebar", ""); + + const tab = await addTab(_toDataURL(TEST_URI)); + const { inspector } = await openCompatibilityView(); + + info("Select another sidebar panel"); + await _selectSidebarPanel(inspector, "changesview"); + + info("Select another node"); + await selectNode("div", inspector); + + info("Select the compatibility panel again"); + const onSelectedNodePaneUpdated = waitForUpdateSelectedNodeAction( + inspector.store + ); + let isTopLevelTargetUpdated = false; + waitForDispatch( + inspector.store, + COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_START + ).then(() => { + isTopLevelTargetUpdated = true; + }); + + await _selectSidebarPanel(inspector, "compatibilityview"); + + await onSelectedNodePaneUpdated; + ok(true, "Update the selected node pane"); + ok(!isTopLevelTargetUpdated, "Avoid updating the top level target pane"); + + await removeTab(tab); +}); + +add_task(async function () { + info( + "Check that both panes update when the top-level target changed while the panel is hidden" + ); + + await pushPref("devtools.inspector.activeSidebar", ""); + + const tab = await addTab(_toDataURL(TEST_URI)); + const { inspector } = await openCompatibilityView(); + + info("Select another sidebar panel"); + await _selectSidebarPanel(inspector, "changesview"); + + info("Navigate to another page"); + BrowserTestUtils.loadURIString( + tab.linkedBrowser, + _toDataURL(TEST_ANOTHER_URI) + ); + + info("Select the compatibility panel again"); + const onSelectedNodePaneUpdated = waitForUpdateSelectedNodeAction( + inspector.store + ); + const onTopLevelTargetPaneUpdated = waitForUpdateTopLevelTargetAction( + inspector.store + ); + + await _selectSidebarPanel(inspector, "compatibilityview"); + + await onSelectedNodePaneUpdated; + await onTopLevelTargetPaneUpdated; + ok(true, "Update both panes"); + + await removeTab(tab); +}); + +add_task(async function () { + info( + "Check that both panes update when a rule is changed changed while the panel is hidden" + ); + + await pushPref("devtools.inspector.activeSidebar", ""); + + info("Disable 3 pane mode"); + await pushPref("devtools.inspector.three-pane-enabled", false); + + const tab = await addTab(_toDataURL(TEST_URI)); + const { inspector } = await openCompatibilityView(); + + info("Select rule view"); + await _selectSidebarPanel(inspector, "ruleview"); + + info("Change a rule"); + await togglePropStatusOnRuleView(inspector, 0, 0); + + info("Select the compatibility panel again"); + const onSelectedNodePaneUpdated = waitForUpdateSelectedNodeAction( + inspector.store + ); + const onTopLevelTargetPaneUpdated = waitForUpdateTopLevelTargetAction( + inspector.store + ); + + await _selectSidebarPanel(inspector, "compatibilityview"); + + await onSelectedNodePaneUpdated; + await onTopLevelTargetPaneUpdated; + ok(true, "Update both panes"); + + await removeTab(tab); +}); + +async function _selectSidebarPanel(inspector, toolId) { + const onSelected = inspector.sidebar.once(`${toolId}-selected`); + inspector.sidebar.select(toolId); + await onSelected; +} + +function _toDataURL(content) { + return "data:text/html;charset=utf-8," + encodeURIComponent(content); +} diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_rule-change.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_rule-change.js new file mode 100644 index 0000000000..91b29b488b --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_rule-change.js @@ -0,0 +1,179 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test whether the content of the issue list will be changed when the rules are changed +// on the rule view. + +const TEST_URI = ` + <style> + .test-class { + ruby-align: center; + } + div { + scrollbar-width: thin; + } + </style> + <div class="test-class">test class</div> + <div>test</div> +`; + +const TEST_DATA_SELECTED = { + fullRule: { + expectedProperties: [ + { + property: "ruby-align", + url: "https://developer.mozilla.org/docs/Web/CSS/ruby-align", + }, + { + property: "scrollbar-width", + url: "https://developer.mozilla.org/docs/Web/CSS/scrollbar-width", + }, + ], + expectedNodes: [ + { + property: "ruby-align", + nodes: [], + }, + { + property: "scrollbar-width", + nodes: [], + }, + ], + }, + classRule: { + expectedProperties: [ + { + property: "ruby-align", + url: "https://developer.mozilla.org/docs/Web/CSS/ruby-align", + }, + ], + expectedNodes: [ + { + property: "ruby-align", + nodes: [], + }, + ], + }, + elementRule: { + expectedProperties: [ + { + property: "scrollbar-width", + url: "https://developer.mozilla.org/docs/Web/CSS/scrollbar-width", + }, + ], + expectedNodes: [ + { + property: "scrollbar-width", + nodes: [], + }, + ], + }, +}; + +const TEST_DATA_ALL = { + fullRule: { + expectedProperties: [ + { + property: "ruby-align", + url: "https://developer.mozilla.org/docs/Web/CSS/ruby-align", + }, + { + property: "scrollbar-width", + url: "https://developer.mozilla.org/docs/Web/CSS/scrollbar-width", + }, + ], + expectedNodes: [ + { + property: "ruby-align", + nodes: ["div.test-class"], + }, + { + property: "scrollbar-width", + nodes: ["div.test-class", "div"], + }, + ], + }, + classRule: { + expectedProperties: [ + { + property: "ruby-align", + url: "https://developer.mozilla.org/docs/Web/CSS/ruby-align", + }, + ], + expectedNodes: [ + { + property: "ruby-align", + nodes: ["div.test-class"], + }, + ], + }, + elementRule: { + expectedProperties: [ + { + property: "scrollbar-width", + url: "https://developer.mozilla.org/docs/Web/CSS/scrollbar-width", + }, + ], + expectedNodes: [ + { + property: "scrollbar-width", + nodes: ["div.test-class", "div"], + }, + ], + }, +}; + +const { + COMPATIBILITY_UPDATE_NODES_COMPLETE, +} = require("resource://devtools/client/inspector/compatibility/actions/index.js"); + +add_task(async function () { + info("Enable 3 pane mode"); + await pushPref("devtools.inspector.three-pane-enabled", true); + + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + + const { allElementsPane, inspector, selectedElementPane } = + await openCompatibilityView(); + await selectNode(".test-class", inspector); + + info("Check the initial issue"); + await assertAll(selectedElementPane, TEST_DATA_SELECTED.fullRule); + await assertAll(allElementsPane, TEST_DATA_ALL.fullRule); + + info("Check the issue after unchecking class rule"); + await _togglePropStatus(inspector, 1, 0); + await assertAll(selectedElementPane, TEST_DATA_SELECTED.elementRule); + await assertAll(allElementsPane, TEST_DATA_ALL.elementRule); + + info("Check the issue after unchecking div rule"); + await _togglePropStatus(inspector, 2, 0); + await assertIssueList(selectedElementPane, []); + await assertIssueList(allElementsPane, []); + + info("Check the issue after reverting class rule"); + await _togglePropStatus(inspector, 1, 0); + await assertAll(selectedElementPane, TEST_DATA_SELECTED.classRule); + await assertAll(allElementsPane, TEST_DATA_ALL.classRule); + + info("Check the issue after reverting div rule"); + await _togglePropStatus(inspector, 2, 0); + await assertAll(selectedElementPane, TEST_DATA_SELECTED.fullRule); + await assertAll(allElementsPane, TEST_DATA_ALL.fullRule); +}); + +async function assertAll(pane, { expectedProperties, expectedNodes }) { + await assertIssueList(pane, expectedProperties); + await assertNodeList(pane, expectedNodes); +} + +async function _togglePropStatus(inspector, ruleIndex, propIndex) { + const onNodesUpdated = waitForDispatch( + inspector.store, + COMPATIBILITY_UPDATE_NODES_COMPLETE + ); + await togglePropStatusOnRuleView(inspector, ruleIndex, propIndex); + await onNodesUpdated; +} diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_selected-node-change.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_selected-node-change.js new file mode 100644 index 0000000000..d24f450968 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_selected-node-change.js @@ -0,0 +1,86 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test whether the content of the issue list will be changed when the new node is selected. + +const TEST_URI = ` + <style> + body { + ruby-align: center; + } + + .has-issue { + scrollbar-width: thin; + user-modify: read-only; + } + + .no-issue { + color: black; + } + </style> + <body> + <div class="has-issue">has issue</div> + <div class="no-issue">no issue</div> + </body> +`; + +const TEST_DATA_SELECTED = [ + { + selector: ".has-issue", + expectedIssues: [ + { + property: "scrollbar-width", + url: "https://developer.mozilla.org/docs/Web/CSS/scrollbar-width", + }, + { + property: "user-modify", + url: "https://developer.mozilla.org/docs/Web/CSS/user-modify", + }, + ], + }, + { + selector: ".no-issue", + expectedIssues: [], + }, + { + selector: "body", + expectedIssues: [ + { + property: "ruby-align", + url: "https://developer.mozilla.org/docs/Web/CSS/ruby-align", + }, + ], + }, +]; + +const TEST_DATA_ALL = [ + { + property: "ruby-align", + url: "https://developer.mozilla.org/docs/Web/CSS/ruby-align", + }, + { + property: "scrollbar-width", + url: "https://developer.mozilla.org/docs/Web/CSS/scrollbar-width", + }, + { + property: "user-modify", + url: "https://developer.mozilla.org/docs/Web/CSS/user-modify", + }, +]; + +add_task(async function () { + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + + const { allElementsPane, inspector, selectedElementPane } = + await openCompatibilityView(); + + for (const { selector, expectedIssues } of TEST_DATA_SELECTED) { + info(`Check the issue list for ${selector} node`); + await selectNode(selector, inspector); + await assertIssueList(selectedElementPane, expectedIssues); + info("Check whether the issues on all elements pane are not changed"); + await assertIssueList(allElementsPane, TEST_DATA_ALL); + } +}); diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_top-level-target-change.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_top-level-target-change.js new file mode 100644 index 0000000000..b6a0239d46 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_top-level-target-change.js @@ -0,0 +1,84 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test the issues after navigating to another page. + +const TEST_DATA_ISSUES = { + uri: ` + <style> + body { + ruby-align: center; + } + div { + scrollbar-width: thin; + } + </style> + <body> + <div>test</div> + </body> + `, + expectedIssuesOnSelected: [ + { + property: "ruby-align", + url: "https://developer.mozilla.org/docs/Web/CSS/ruby-align", + }, + ], + expectedIssuesOnAll: [ + { + property: "ruby-align", + url: "https://developer.mozilla.org/docs/Web/CSS/ruby-align", + }, + { + property: "scrollbar-width", + url: "https://developer.mozilla.org/docs/Web/CSS/scrollbar-width", + }, + ], +}; + +const TEST_DATA_NO_ISSUES = { + uri: "<body></body>", + expectedIssuesOnSelected: [], + expectedIssuesOnAll: [], +}; + +add_task(async function () { + const tab = await addTab( + "data:text/html;charset=utf-8," + encodeURIComponent(TEST_DATA_ISSUES.uri) + ); + + const { allElementsPane, inspector, selectedElementPane } = + await openCompatibilityView(); + + info("Check issues at initial"); + await assertIssues(selectedElementPane, allElementsPane, TEST_DATA_ISSUES); + + info("Navigate to next uri that has no issues"); + await navigateTo(TEST_DATA_NO_ISSUES.uri, tab, inspector); + info("Check issues after navigating"); + await assertIssues(selectedElementPane, allElementsPane, TEST_DATA_NO_ISSUES); + + info("Revert the uri"); + await navigateTo(TEST_DATA_ISSUES.uri, tab, inspector); + info("Check issues after reverting"); + await assertIssues(selectedElementPane, allElementsPane, TEST_DATA_ISSUES); +}); + +async function assertIssues( + selectedElementPane, + allElementsPane, + { expectedIssuesOnSelected, expectedIssuesOnAll } +) { + await assertIssueList(selectedElementPane, expectedIssuesOnSelected); + await assertIssueList(allElementsPane, expectedIssuesOnAll); +} + +async function navigateTo(uri, tab, { store }) { + uri = "data:text/html;charset=utf-8," + encodeURIComponent(uri); + const onSelectedNodeUpdated = waitForUpdateSelectedNodeAction(store); + const onTopLevelTargetUpdated = waitForUpdateTopLevelTargetAction(store); + const onLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + BrowserTestUtils.loadURIString(tab.linkedBrowser, uri); + await Promise.all([onLoaded, onSelectedNodeUpdated, onTopLevelTargetUpdated]); +} diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_issue-node.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_issue-node.js new file mode 100644 index 0000000000..b99dfd4062 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_issue-node.js @@ -0,0 +1,49 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test displaying the nodes that caused issues. + +const TEST_URI = ` + <style> + body { + user-modify: read-only; + } + div { + user-modify: read-only; + scrollbar-width: thin; + } + </style> + <body> + <div>div</div> + </body> +`; + +const TEST_DATA_ALL = [ + { + property: "user-modify", + nodes: ["body", "div"], + }, + { + property: "scrollbar-width", + nodes: ["div"], + }, +]; + +add_task(async function () { + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + + const { allElementsPane, selectedElementPane } = + await openCompatibilityView(); + + info("Check nodes that caused issues on the selected element"); + is( + selectedElementPane.querySelectorAll(".compatibility-node-item").length, + 0, + "Nodes are not displayed on the selected element" + ); + + info("Check nodes that caused issues on all elements"); + await assertNodeList(allElementsPane, TEST_DATA_ALL); +}); diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_preference.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_preference.js new file mode 100644 index 0000000000..5c54206c2d --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_preference.js @@ -0,0 +1,28 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test whether the compatibility tool is enabled or not according to the preference. + +add_task(async function () { + info("Check the compatibility tool is enabled if the pref is on"); + await addTab("data:text/html;charset=utf-8,test"); + await pushPref("devtools.inspector.compatibility.enabled", true); + const { inspector } = await openRuleView(); + const compatibilityTab = inspector.panelDoc.getElementById( + "compatibilityview-tab" + ); + ok(compatibilityTab, "The compatibility tool is enabled"); +}); + +add_task(async function () { + info("Check the compatibility tool is disabled if the pref is off"); + await addTab("data:text/html;charset=utf-8,test"); + await pushPref("devtools.inspector.compatibility.enabled", false); + const { inspector } = await openRuleView(); + const compatibilityTab = inspector.panelDoc.getElementById( + "compatibilityview-tab" + ); + ok(!compatibilityTab, "The compatibility tool is disabled"); +}); diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_settings.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_settings.js new file mode 100644 index 0000000000..36e4d57735 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_settings.js @@ -0,0 +1,113 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test whether settings page works. + +const TEST_URI = ` + <style> + body { + text-size-adjust: none; + } + div { + text-size-adjust: none; + } + </style> + <body><div></div></body> +`; + +const { + COMPATIBILITY_UPDATE_TARGET_BROWSERS_COMPLETE, +} = require("resource://devtools/client/inspector/compatibility/actions/index.js"); + +add_task(async function () { + registerCleanupFunction(() => { + Services.prefs.clearUserPref( + "devtools.inspector.compatibility.target-browsers" + ); + }); + + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + const { inspector, panel } = await openCompatibilityView(); + const { store } = inspector; + + info("Check initial state"); + ok( + panel.querySelector(`.compatibility-browser-icon__image[src*="firefox"]`), + "Firefox browsers are the target" + ); + + info("Make Firefox browsers out of target"); + await updateTargetBrowsers(panel, store, id => !id.includes("firefox")); + ok( + !panel.querySelector(`.compatibility-browser-icon__image[src*="firefox"]`), + "Firefox browsers are not the target" + ); + + info("Make all browsers out of target"); + await updateTargetBrowsers(panel, store, () => false); + ok( + !panel.querySelector(".compatibility-browser-icon__image"), + "No browsers are the target" + ); + + info("Make Firefox browsers target"); + await updateTargetBrowsers(panel, store, id => id.includes("firefox")); + ok( + panel.querySelector(`.compatibility-browser-icon__image[src*="firefox"]`), + "Firefox browsers are the target now" + ); +}); + +async function updateTargetBrowsers(panel, store, isTargetBrowserFunc) { + info("Open settings pane"); + const settingsButton = panel.querySelector(".compatibility-footer__button"); + settingsButton.click(); + await waitUntil(() => panel.querySelector(".compatibility-settings")); + + const browsers = [ + ...new Set( + Array.from(panel.querySelectorAll("[data-id]")).map(el => + el.getAttribute("data-id") + ) + ), + ]; + Assert.deepEqual( + // Filter out IE, to be removed in an upcoming browser compat data sync. + // TODO: Remove the filter once D150961 lands. see Bug 1778009 + browsers.filter(browser => browser != "ie"), + [ + "chrome", + "chrome_android", + "edge", + "firefox", + "firefox_android", + "safari", + "safari_ios", + ], + "The expected browsers are displayed" + ); + + info("Change target browsers"); + const settingsPane = panel.querySelector(".compatibility-settings"); + for (const checkbox of settingsPane.querySelectorAll( + ".compatibility-settings__target-browsers-item input" + )) { + if (checkbox.checked !== isTargetBrowserFunc(checkbox.id)) { + // Need to click to toggle since the input is designed as controlled component. + checkbox.click(); + } + } + + info("Close settings pane"); + const onUpdated = waitForDispatch( + store, + COMPATIBILITY_UPDATE_TARGET_BROWSERS_COMPLETE + ); + const closeButton = settingsPane.querySelector( + ".compatibility-settings__header-button" + ); + closeButton.click(); + await onUpdated; +} diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_throbber.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_throbber.js new file mode 100644 index 0000000000..eff0f9ac93 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_throbber.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test whether the throbber is displayed correctly or not. + +const TEST_URI = ` + <style> + body { + color: blue; + border-block-color: lime; + user-modify: read-only; + } + div { + font-variant-alternates: historical-forms; + } + </style> + <body> + <div>test</div> + </body> +`; + +const { + COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_START, +} = require("resource://devtools/client/inspector/compatibility/actions/index.js"); + +add_task(async function () { + const tab = await addTab( + "data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI) + ); + + const { allElementsPane, inspector, selectedElementPane } = + await openCompatibilityView(); + + info("Check the throbber visibility at the beginning"); + assertThrobber(allElementsPane, false); + assertThrobber(selectedElementPane, false); + + info("Reload the browsing page"); + const onStart = waitForDispatch( + inspector.store, + COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_START + ); + const onComplete = waitForDispatch( + inspector.store, + COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_COMPLETE + ); + gBrowser.reloadTab(tab); + + info("Check the throbber visibility of after starting updating action"); + await onStart; + assertThrobber(allElementsPane, true); + assertThrobber(selectedElementPane, false); + + info("Check the throbber visibility of after completing updating action"); + await onComplete; + assertThrobber(allElementsPane, false); + assertThrobber(selectedElementPane, false); +}); + +function assertThrobber(panel, expectedVisibility) { + const isThrobberVisible = !!panel.querySelector(".devtools-throbber"); + is( + isThrobberVisible, + expectedVisibility, + "Visibility of the throbber is correct" + ); +} diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_unsupported-browsers_all.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_unsupported-browsers_all.js new file mode 100644 index 0000000000..c57c805ce1 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_unsupported-browsers_all.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test whether the all of default browsers are unsupported. + +const TEST_URI = ` + <style> + body { + user-modify: read-only; + } + </style> + <body></body> +`; + +add_task(async function () { + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + const { inspector, selectedElementPane } = await openCompatibilityView(); + + info("Get the taget browsers we set as default"); + const { targetBrowsers } = inspector.store.getState().compatibility; + + info("Check the content of the issue item"); + const expectedIssues = [ + { + property: "user-modify", + unsupportedBrowsers: targetBrowsers, + url: "https://developer.mozilla.org/docs/Web/CSS/user-modify", + }, + ]; + await assertIssueList(selectedElementPane, expectedIssues); +}); diff --git a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_unsupported-browsers_some.js b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_unsupported-browsers_some.js new file mode 100644 index 0000000000..6359b34e31 --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_unsupported-browsers_some.js @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test whether some of browsers are unsupported. + +const { + updateTargetBrowsers, +} = require("resource://devtools/client/inspector/compatibility/actions/compatibility.js"); + +const TEST_URI = ` + <style> + body { + border-block-color: lime; + } + </style> + <body></body> +`; + +const TARGET_BROWSERS = [ + { id: "firefox", name: "Firefox", version: "1" }, + { id: "firefox", name: "Firefox", version: "70" }, + { id: "firefox_android", name: "Firefox Android", version: "1" }, + { id: "firefox_android", name: "Firefox Android", version: "70" }, +]; + +add_task(async function () { + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + const { inspector, selectedElementPane } = await openCompatibilityView(); + + info("Update the target browsers for this test"); + await inspector.store.dispatch(updateTargetBrowsers(TARGET_BROWSERS)); + + info("Check the content of the issue item"); + const expectedIssues = [ + { + property: "border-block-color", + unsupportedBrowsers: [ + { id: "firefox", name: "Firefox", version: "1" }, + { id: "firefox_android", name: "Firefox Android", version: "1" }, + ], + url: "https://developer.mozilla.org/docs/Web/CSS/border-block-color", + }, + ]; + await assertIssueList(selectedElementPane, expectedIssues); +}); diff --git a/devtools/client/inspector/compatibility/test/browser/head.js b/devtools/client/inspector/compatibility/test/browser/head.js new file mode 100644 index 0000000000..54e897093b --- /dev/null +++ b/devtools/client/inspector/compatibility/test/browser/head.js @@ -0,0 +1,283 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Import the rule view's head.js first (which itself imports inspector's head.js and shared-head.js). +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/inspector/rules/test/head.js", + this +); + +const { + COMPATIBILITY_UPDATE_SELECTED_NODE_COMPLETE, + COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_COMPLETE, +} = require("resource://devtools/client/inspector/compatibility/actions/index.js"); + +const { + toCamelCase, +} = require("resource://devtools/client/inspector/compatibility/utils/cases.js"); + +async function openCompatibilityView() { + info("Open the compatibility view"); + await pushPref("devtools.inspector.compatibility.enabled", true); + + const { inspector } = await openInspectorSidebarTab("compatibilityview"); + await Promise.all([ + waitForUpdateSelectedNodeAction(inspector.store), + waitForUpdateTopLevelTargetAction(inspector.store), + ]); + const panel = inspector.panelDoc.querySelector( + "#compatibilityview-panel .inspector-tabpanel" + ); + + const selectedElementPane = panel.querySelector( + "#compatibility-app--selected-element-pane" + ); + + const allElementsPane = panel.querySelector( + "#compatibility-app--all-elements-pane" + ); + + return { allElementsPane, inspector, panel, selectedElementPane }; +} + +/** + * Check whether the content of issue item element is matched with the expected values. + * + * @param {Element} panel + * @param {Array} expectedIssues + * Array of the issue expected. + * For the structure of issue items, see types.js. + */ +async function assertIssueList(panel, expectedIssues) { + info("Check the number of issues"); + await waitUntil( + () => + panel.querySelectorAll("[data-qa-property]").length === + expectedIssues.length + ); + ok(true, "The number of issues is correct"); + + if (expectedIssues.length === 0) { + // No issue. + return; + } + + const getFluentString = await getFluentStringHelper([ + "devtools/client/compatibility.ftl", + ]); + + for (const expectedIssue of expectedIssues) { + const property = expectedIssue.property; + info(`Check an element for ${property}`); + const issueEl = getIssueItem(property, panel); + ok(issueEl, `Issue element for the ${property} is in the panel`); + + if (expectedIssue.unsupportedBrowsers) { + // We only display a single icon per unsupported browser, so we need to + // group the expected unsupported browsers (versions) by their browser id. + const expectedUnsupportedBrowsersById = new Map(); + for (const unsupportedBrowser of expectedIssue.unsupportedBrowsers) { + if (!expectedUnsupportedBrowsersById.has(unsupportedBrowser.id)) { + expectedUnsupportedBrowsersById.set(unsupportedBrowser.id, []); + } + expectedUnsupportedBrowsersById + .get(unsupportedBrowser.id) + .push(unsupportedBrowser); + } + + const unsupportedBrowserListEl = issueEl.querySelector( + ".compatibility-unsupported-browser-list" + ); + const unsupportedBrowsersEl = + unsupportedBrowserListEl.querySelectorAll("li"); + + is( + unsupportedBrowsersEl.length, + expectedUnsupportedBrowsersById.size, + "The expected number of browser icons are displayed" + ); + + for (const unsupportedBrowserEl of unsupportedBrowsersEl) { + const expectedUnsupportedBrowsers = expectedUnsupportedBrowsersById.get( + unsupportedBrowserEl.getAttribute("data-browser-id") + ); + + ok(expectedUnsupportedBrowsers, "The expected browser is displayed"); + // debugger; + is( + unsupportedBrowserEl.querySelector(".compatibility-browser-version") + .innerText, + // If esr is not supported, but a newest version isn't as well, we don't display + // the esr version number + ( + expectedUnsupportedBrowsers.find( + ({ status }) => status !== "esr" + ) || expectedUnsupportedBrowsers[0] + ).version, + "The expected browser version is displayed" + ); + + is( + unsupportedBrowserEl.getAttribute("title"), + getFluentString("compatibility-issue-browsers-list", "title", { + browsers: expectedUnsupportedBrowsers + .map( + ({ name, status, version }) => + `${name} ${version}${status ? ` (${status})` : ""}` + ) + .join("\n"), + }), + "The brower item has the expected title attribute" + ); + } + } + + for (const [key, value] of Object.entries(expectedIssue)) { + const datasetKey = toCamelCase(`qa-${key}`); + is( + issueEl.dataset[datasetKey], + JSON.stringify(value), + `The value of ${datasetKey} is correct` + ); + } + + const propertyEl = issueEl.querySelector( + ".compatibility-issue-item__property" + ); + const MDN_CLASSNAME = "compatibility-issue-item__mdn-link"; + const SPEC_CLASSNAME = "compatibility-issue-item__spec-link"; + + is( + propertyEl.textContent, + property, + "property name is displayed as expected" + ); + + is( + propertyEl.classList.contains(MDN_CLASSNAME), + !!expectedIssue.url, + `${property} element ${ + expectedIssue.url ? "has" : "does not have" + } mdn link class` + ); + is( + propertyEl.classList.contains(SPEC_CLASSNAME), + !!expectedIssue.specUrl, + `${property} element ${ + expectedIssue.specUrl ? "has" : "does not have" + } spec link class` + ); + + if (expectedIssue.url || expectedIssue.specUrl) { + is( + propertyEl.nodeName.toLowerCase(), + "a", + `Link rendered for ${property}` + ); + + const expectedUrl = expectedIssue.url + ? expectedIssue.url + + "?utm_source=devtools&utm_medium=inspector-compatibility&utm_campaign=default" + : expectedIssue.specUrl; + const { link } = await simulateLinkClick(propertyEl); + is( + link, + expectedUrl, + `Click on ${property} link navigates user to expected url` + ); + } else { + is( + propertyEl.nodeName.toLowerCase(), + "span", + `No link rendered for ${property}` + ); + + const { link } = await simulateLinkClick(propertyEl); + is(link, null, `Click on ${property} does not navigate`); + } + } +} + +/** + * Check whether the content of node item element is matched with the expected values. + * + * @param {Element} panel + * @param {Array} expectedNodes + * e.g. + * [{ property: "margin-inline-end", nodes: ["body", "div.classname"] },...] + */ +async function assertNodeList(panel, expectedNodes) { + for (const { property, nodes } of expectedNodes) { + info(`Check nodes for ${property}`); + const issueEl = getIssueItem(property, panel); + + await waitUntil( + () => + issueEl.querySelectorAll(".compatibility-node-item").length === + nodes.length + ); + ok(true, "The number of nodes is correct"); + + const nodeEls = [...issueEl.querySelectorAll(".compatibility-node-item")]; + for (const node of nodes) { + const nodeEl = nodeEls.find(el => el.textContent === node); + ok(nodeEl, "The text content of the node element is correct"); + } + } +} + +/** + * Get IssueItem of given property from given element. + * + * @param {String} property + * @param {Element} element + * @return {Element} + */ +function getIssueItem(property, element) { + return element.querySelector(`[data-qa-property=\"\\"${property}\\"\"]`); +} + +/** + * Toggle enable/disable checkbox of a specific property on rule view. + * + * @param {Inspector} inspector + * @param {Number} ruleIndex + * @param {Number} propIndex + */ +async function togglePropStatusOnRuleView(inspector, ruleIndex, propIndex) { + const ruleView = inspector.getPanel("ruleview").view; + const rule = getRuleViewRuleEditor(ruleView, ruleIndex).rule; + // In case of inline style changes, we track the mutations via the + // inspector's markupmutation event to react to dynamic style changes + // which Resource Watcher doesn't cover yet. + // If an inline style is applied to the element, we need to wait on the + // markupmutation event + const onMutation = + ruleIndex === 0 ? inspector.once("markupmutation") : Promise.resolve(); + const textProp = rule.textProps[propIndex]; + const onRuleviewChanged = ruleView.once("ruleview-changed"); + textProp.editor.enable.click(); + await Promise.all([onRuleviewChanged, onMutation]); +} + +/** + * Return a promise which waits for COMPATIBILITY_UPDATE_SELECTED_NODE_COMPLETE action. + * + * @param {Object} store + * @return {Promise} + */ +function waitForUpdateSelectedNodeAction(store) { + return waitForDispatch(store, COMPATIBILITY_UPDATE_SELECTED_NODE_COMPLETE); +} + +/** + * Return a promise which waits for COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_COMPLETE action. + * + * @param {Object} store + * @return {Promise} + */ +function waitForUpdateTopLevelTargetAction(store) { + return waitForDispatch(store, COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_COMPLETE); +} |