summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/compatibility/test/browser/head.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/inspector/compatibility/test/browser/head.js')
-rw-r--r--devtools/client/inspector/compatibility/test/browser/head.js283
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);
+}