tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

head.js (9013B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Import the rule view's head.js first (which itself imports inspector's head.js and shared-head.js).
      7 Services.scriptloader.loadSubScript(
      8  "chrome://mochitests/content/browser/devtools/client/inspector/rules/test/head.js",
      9  this
     10 );
     11 
     12 const {
     13  COMPATIBILITY_UPDATE_SELECTED_NODE_COMPLETE,
     14  COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_COMPLETE,
     15 } = require("resource://devtools/client/inspector/compatibility/actions/index.js");
     16 
     17 const {
     18  toCamelCase,
     19 } = require("resource://devtools/client/inspector/compatibility/utils/cases.js");
     20 
     21 async function openCompatibilityView() {
     22  info("Open the compatibility view");
     23  const { inspector } = await openInspectorSidebarTab("compatibilityview");
     24  await Promise.all([
     25    waitForUpdateSelectedNodeAction(inspector.store),
     26    waitForUpdateTopLevelTargetAction(inspector.store),
     27  ]);
     28  const panel = inspector.panelDoc.querySelector(
     29    "#compatibilityview-panel .inspector-tabpanel"
     30  );
     31 
     32  const selectedElementPane = panel.querySelector(
     33    "#compatibility-app--selected-element-pane"
     34  );
     35 
     36  const allElementsPane = panel.querySelector(
     37    "#compatibility-app--all-elements-pane"
     38  );
     39 
     40  return { allElementsPane, inspector, panel, selectedElementPane };
     41 }
     42 
     43 /**
     44 * Check whether the content of issue item element is matched with the expected values.
     45 *
     46 * @param {Element} panel
     47 * @param {Array} expectedIssues
     48 *        Array of the issue expected.
     49 *        For the structure of issue items, see types.js.
     50 */
     51 async function assertIssueList(panel, expectedIssues) {
     52  await waitFor(
     53    () =>
     54      panel.querySelectorAll("[data-qa-property]").length ===
     55      expectedIssues.length,
     56    "The number of issues is correct"
     57  );
     58 
     59  if (expectedIssues.length === 0) {
     60    // No issue.
     61    return;
     62  }
     63 
     64  const getFluentString = await getFluentStringHelper([
     65    "devtools/client/compatibility.ftl",
     66  ]);
     67 
     68  for (const expectedIssue of expectedIssues) {
     69    const property = expectedIssue.property;
     70    info(`Check an element for ${property}`);
     71    const issueEl = getIssueItem(property, panel);
     72    ok(issueEl, `Issue element for the ${property} is in the panel`);
     73 
     74    if (expectedIssue.unsupportedBrowsers) {
     75      // We only display a single icon per unsupported browser, so we need to
     76      // group the expected unsupported browsers (versions) by their browser id.
     77      const expectedUnsupportedBrowsersById = new Map();
     78      for (const unsupportedBrowser of expectedIssue.unsupportedBrowsers) {
     79        if (!expectedUnsupportedBrowsersById.has(unsupportedBrowser.id)) {
     80          expectedUnsupportedBrowsersById.set(unsupportedBrowser.id, []);
     81        }
     82        expectedUnsupportedBrowsersById
     83          .get(unsupportedBrowser.id)
     84          .push(unsupportedBrowser);
     85      }
     86 
     87      const unsupportedBrowserListEl = issueEl.querySelector(
     88        ".compatibility-unsupported-browser-list"
     89      );
     90      const unsupportedBrowsersEl =
     91        unsupportedBrowserListEl.querySelectorAll("li");
     92 
     93      is(
     94        unsupportedBrowsersEl.length,
     95        expectedUnsupportedBrowsersById.size,
     96        "The expected number of browser icons are displayed"
     97      );
     98 
     99      for (const unsupportedBrowserEl of unsupportedBrowsersEl) {
    100        const expectedUnsupportedBrowsers = expectedUnsupportedBrowsersById.get(
    101          unsupportedBrowserEl.getAttribute("data-browser-id")
    102        );
    103 
    104        ok(expectedUnsupportedBrowsers, "The expected browser is displayed");
    105        // debugger;
    106        is(
    107          unsupportedBrowserEl.querySelector(".compatibility-browser-version")
    108            .innerText,
    109          // If esr is not supported, but a newest version isn't as well, we don't display
    110          // the esr version number
    111          (
    112            expectedUnsupportedBrowsers.find(
    113              ({ status }) => status !== "esr"
    114            ) || expectedUnsupportedBrowsers[0]
    115          ).version,
    116          "The expected browser version is displayed"
    117        );
    118 
    119        is(
    120          unsupportedBrowserEl.getAttribute("title"),
    121          getFluentString("compatibility-issue-browsers-list", "title", {
    122            browsers: expectedUnsupportedBrowsers
    123              .map(
    124                ({ name, status, version }) =>
    125                  `${name} ${version}${status ? ` (${status})` : ""}`
    126              )
    127              .join("\n"),
    128          }),
    129          "The brower item has the expected title attribute"
    130        );
    131      }
    132    }
    133 
    134    for (const [key, value] of Object.entries(expectedIssue)) {
    135      const datasetKey = toCamelCase(`qa-${key}`);
    136      is(
    137        issueEl.dataset[datasetKey],
    138        JSON.stringify(value),
    139        `The value of ${datasetKey} is correct`
    140      );
    141    }
    142 
    143    const propertyEl = issueEl.querySelector(
    144      ".compatibility-issue-item__property"
    145    );
    146    const MDN_CLASSNAME = "compatibility-issue-item__mdn-link";
    147    const SPEC_CLASSNAME = "compatibility-issue-item__spec-link";
    148 
    149    is(
    150      propertyEl.textContent,
    151      property,
    152      "property name is displayed as expected"
    153    );
    154 
    155    is(
    156      propertyEl.classList.contains(MDN_CLASSNAME),
    157      !!expectedIssue.url,
    158      `${property} element ${
    159        expectedIssue.url ? "has" : "does not have"
    160      } mdn link class`
    161    );
    162    is(
    163      propertyEl.classList.contains(SPEC_CLASSNAME),
    164      !!expectedIssue.specUrl,
    165      `${property} element ${
    166        expectedIssue.specUrl ? "has" : "does not have"
    167      } spec link class`
    168    );
    169 
    170    if (expectedIssue.url || expectedIssue.specUrl) {
    171      is(
    172        propertyEl.nodeName.toLowerCase(),
    173        "a",
    174        `Link rendered for ${property}`
    175      );
    176 
    177      const expectedUrl = expectedIssue.url
    178        ? expectedIssue.url +
    179          "?utm_source=devtools&utm_medium=inspector-compatibility&utm_campaign=default"
    180        : expectedIssue.specUrl;
    181      const { link } = await simulateLinkClick(propertyEl);
    182      is(
    183        link,
    184        expectedUrl,
    185        `Click on ${property} link navigates user to expected url`
    186      );
    187    } else {
    188      is(
    189        propertyEl.nodeName.toLowerCase(),
    190        "span",
    191        `No link rendered for ${property}`
    192      );
    193 
    194      const { link } = await simulateLinkClick(propertyEl);
    195      is(link, null, `Click on ${property} does not navigate`);
    196    }
    197  }
    198 }
    199 
    200 /**
    201 * Check whether the content of node item element is matched with the expected values.
    202 *
    203 * @param {Element} panel
    204 * @param {Array} expectedNodes
    205 *        e.g.
    206 *        [{ property: "margin-inline-end", nodes: ["body", "div.classname"] },...]
    207 */
    208 async function assertNodeList(panel, expectedNodes) {
    209  for (const { property, nodes } of expectedNodes) {
    210    info(`Check nodes for ${property}`);
    211    const issueEl = getIssueItem(property, panel);
    212 
    213    await waitUntil(
    214      () =>
    215        issueEl.querySelectorAll(".compatibility-node-item").length ===
    216        nodes.length
    217    );
    218    ok(true, "The number of nodes is correct");
    219 
    220    const nodeEls = [...issueEl.querySelectorAll(".compatibility-node-item")];
    221    for (const node of nodes) {
    222      const nodeEl = nodeEls.find(el => el.textContent === node);
    223      ok(nodeEl, "The text content of the node element is correct");
    224    }
    225  }
    226 }
    227 
    228 /**
    229 * Get IssueItem of given property from given element.
    230 *
    231 * @param {string} property
    232 * @param {Element} element
    233 * @return {Element}
    234 */
    235 function getIssueItem(property, element) {
    236  return element.querySelector(`[data-qa-property=\"\\"${property}\\"\"]`);
    237 }
    238 
    239 /**
    240 * Toggle enable/disable checkbox of a specific property on rule view.
    241 *
    242 * @param {Inspector} inspector
    243 * @param {number} ruleIndex
    244 * @param {number} propIndex
    245 */
    246 async function togglePropStatusOnRuleView(inspector, ruleIndex, propIndex) {
    247  const ruleView = inspector.getPanel("ruleview").view;
    248  const rule = getRuleViewRuleEditor(ruleView, ruleIndex).rule;
    249  // In case of inline style changes, we track the mutations via the
    250  // inspector's markupmutation event to react to dynamic style changes
    251  // which Resource Watcher doesn't cover yet.
    252  // If an inline style is applied to the element, we need to wait on the
    253  // markupmutation event
    254  const onMutation =
    255    ruleIndex === 0 ? inspector.once("markupmutation") : Promise.resolve();
    256  const textProp = rule.textProps[propIndex];
    257  const onRuleviewChanged = ruleView.once("ruleview-changed");
    258  textProp.editor.enable.click();
    259  await Promise.all([onRuleviewChanged, onMutation]);
    260 }
    261 
    262 /**
    263 * Return a promise which waits for COMPATIBILITY_UPDATE_SELECTED_NODE_COMPLETE action.
    264 *
    265 * @param {object} store
    266 * @return {Promise}
    267 */
    268 function waitForUpdateSelectedNodeAction(store) {
    269  return waitForDispatch(store, COMPATIBILITY_UPDATE_SELECTED_NODE_COMPLETE);
    270 }
    271 
    272 /**
    273 * Return a promise which waits for COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_COMPLETE action.
    274 *
    275 * @param {object} store
    276 * @return {Promise}
    277 */
    278 function waitForUpdateTopLevelTargetAction(store) {
    279  return waitForDispatch(store, COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_COMPLETE);
    280 }