diff options
Diffstat (limited to 'devtools/client/inspector/compatibility/test/browser/head.js')
-rw-r--r-- | devtools/client/inspector/compatibility/test/browser/head.js | 283 |
1 files changed, 283 insertions, 0 deletions
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); +} |