summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/components/test/chrome/head.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/shared/components/test/chrome/head.js')
-rw-r--r--devtools/client/shared/components/test/chrome/head.js379
1 files changed, 379 insertions, 0 deletions
diff --git a/devtools/client/shared/components/test/chrome/head.js b/devtools/client/shared/components/test/chrome/head.js
new file mode 100644
index 0000000000..7abe54942f
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/head.js
@@ -0,0 +1,379 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+
+/* global _snapshots */
+
+"use strict";
+
+var { require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+);
+var { Assert } = ChromeUtils.importESModule(
+ "resource://testing-common/Assert.sys.mjs"
+);
+var { gDevTools } = require("resource://devtools/client/framework/devtools.js");
+var { BrowserLoader } = ChromeUtils.import(
+ "resource://devtools/shared/loader/browser-loader.js"
+);
+var {
+ DevToolsServer,
+} = require("resource://devtools/server/devtools-server.js");
+var {
+ DevToolsClient,
+} = require("resource://devtools/client/devtools-client.js");
+var DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js");
+var { Toolbox } = require("resource://devtools/client/framework/toolbox.js");
+
+var { require: browserRequire } = BrowserLoader({
+ baseURI: "resource://devtools/client/shared/",
+ window,
+});
+
+const React = browserRequire("devtools/client/shared/vendor/react");
+const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+const dom = browserRequire("devtools/client/shared/vendor/react-dom-factories");
+const TestUtils = browserRequire(
+ "devtools/client/shared/vendor/react-dom-test-utils"
+);
+
+const ShallowRenderer = browserRequire(
+ "devtools/client/shared/vendor/react-test-renderer-shallow"
+);
+const TestRenderer = browserRequire(
+ "devtools/client/shared/vendor/react-test-renderer"
+);
+
+var EXAMPLE_URL = "https://example.com/browser/browser/devtools/shared/test/";
+
+SimpleTest.registerCleanupFunction(() => {
+ window._snapshots = null;
+});
+
+function forceRender(comp) {
+ return setState(comp, {}).then(() => setState(comp, {}));
+}
+
+// All tests are asynchronous.
+SimpleTest.waitForExplicitFinish();
+
+function onNextAnimationFrame(fn) {
+ return () => requestAnimationFrame(() => requestAnimationFrame(fn));
+}
+
+function setState(component, newState) {
+ return new Promise(resolve => {
+ component.setState(newState, onNextAnimationFrame(resolve));
+ });
+}
+
+function dumpn(msg) {
+ dump(`SHARED-COMPONENTS-TEST: ${msg}\n`);
+}
+
+/**
+ * Tree View
+ */
+
+const TEST_TREE_VIEW = {
+ A: { label: "A", value: "A" },
+ B: { label: "B", value: "B" },
+ C: { label: "C", value: "C" },
+ D: { label: "D", value: "D" },
+ E: { label: "E", value: "E" },
+ F: { label: "F", value: "F" },
+ G: { label: "G", value: "G" },
+ H: { label: "H", value: "H" },
+ I: { label: "I", value: "I" },
+ J: { label: "J", value: "J" },
+ K: { label: "K", value: "K" },
+ L: { label: "L", value: "L" },
+};
+
+TEST_TREE_VIEW.children = {
+ A: [TEST_TREE_VIEW.B, TEST_TREE_VIEW.C, TEST_TREE_VIEW.D],
+ B: [TEST_TREE_VIEW.E, TEST_TREE_VIEW.F, TEST_TREE_VIEW.G],
+ C: [TEST_TREE_VIEW.H, TEST_TREE_VIEW.I],
+ D: [TEST_TREE_VIEW.J],
+ E: [TEST_TREE_VIEW.K, TEST_TREE_VIEW.L],
+ F: [],
+ G: [],
+ H: [],
+ I: [],
+ J: [],
+ K: [],
+ L: [],
+};
+
+const TEST_TREE_VIEW_INTERFACE = {
+ provider: {
+ getChildren: x => TEST_TREE_VIEW.children[x.label],
+ hasChildren: x => !!TEST_TREE_VIEW.children[x.label].length,
+ getLabel: x => x.label,
+ getValue: x => x.value,
+ getKey: x => x.label,
+ getType: () => "string",
+ },
+ object: TEST_TREE_VIEW.A,
+ columns: [{ id: "default" }, { id: "value" }],
+};
+
+/**
+ * Tree
+ */
+
+var TEST_TREE_INTERFACE = {
+ getParent: x => TEST_TREE.parent[x],
+ getChildren: x => TEST_TREE.children[x],
+ renderItem: (x, depth, focused) =>
+ "-".repeat(depth) + x + ":" + focused + "\n",
+ getRoots: () => ["A", "M"],
+ getKey: x => "key-" + x,
+ itemHeight: 1,
+ onExpand: x => TEST_TREE.expanded.add(x),
+ onCollapse: x => TEST_TREE.expanded.delete(x),
+ isExpanded: x => TEST_TREE.expanded.has(x),
+};
+
+function isRenderedTree(actual, expectedDescription, msg) {
+ const expected = expectedDescription.map(x => x + "\n").join("");
+ dumpn(`Expected tree:\n${expected}`);
+ dumpn(`Actual tree:\n${actual}`);
+ is(actual, expected, msg);
+}
+
+function isAccessibleTree(tree, options = {}) {
+ const treeNode = tree.refs.tree;
+ is(treeNode.getAttribute("tabindex"), "0", "Tab index is set");
+ is(treeNode.getAttribute("role"), "tree", "Tree semantics is present");
+ if (options.hasActiveDescendant) {
+ ok(
+ treeNode.hasAttribute("aria-activedescendant"),
+ "Tree has an active descendant set"
+ );
+ }
+
+ const treeNodes = [...treeNode.querySelectorAll(".tree-node")];
+ for (const node of treeNodes) {
+ ok(node.id, "TreeNode has an id");
+ is(node.getAttribute("role"), "treeitem", "Tree item semantics is present");
+ is(
+ parseInt(node.getAttribute("aria-level"), 10),
+ parseInt(node.getAttribute("data-depth"), 10) + 1,
+ "Aria level attribute is set correctly"
+ );
+ }
+}
+
+// Encoding of the following tree/forest:
+//
+// A
+// |-- B
+// | |-- E
+// | | |-- K
+// | | `-- L
+// | |-- F
+// | `-- G
+// |-- C
+// | |-- H
+// | `-- I
+// `-- D
+// `-- J
+// M
+// `-- N
+// `-- O
+var TEST_TREE = {
+ children: {
+ A: ["B", "C", "D"],
+ B: ["E", "F", "G"],
+ C: ["H", "I"],
+ D: ["J"],
+ E: ["K", "L"],
+ F: [],
+ G: [],
+ H: [],
+ I: [],
+ J: [],
+ K: [],
+ L: [],
+ M: ["N"],
+ N: ["O"],
+ O: [],
+ },
+ parent: {
+ A: null,
+ B: "A",
+ C: "A",
+ D: "A",
+ E: "B",
+ F: "B",
+ G: "B",
+ H: "C",
+ I: "C",
+ J: "D",
+ K: "E",
+ L: "E",
+ M: null,
+ N: "M",
+ O: "N",
+ },
+ expanded: new Set(),
+};
+
+/**
+ * Frame
+ */
+function checkFrameString({
+ el,
+ file,
+ line,
+ column,
+ source,
+ functionName,
+ shouldLink,
+ tooltip,
+ locationPrefix,
+}) {
+ const $ = selector => el.querySelector(selector);
+
+ const $func = $(".frame-link-function-display-name");
+ const $source = $(".frame-link-source");
+ const $locationPrefix = $(".frame-link-prefix");
+ const $filename = $(".frame-link-filename");
+ const $line = $(".frame-link-line");
+
+ is($filename.textContent, file, "Correct filename");
+ is(
+ el.getAttribute("data-line"),
+ line ? `${line}` : null,
+ "Expected `data-line` found"
+ );
+ is(
+ el.getAttribute("data-column"),
+ column ? `${column}` : null,
+ "Expected `data-column` found"
+ );
+ is($source.getAttribute("title"), tooltip, "Correct tooltip");
+ is($source.tagName, shouldLink ? "A" : "SPAN", "Correct linkable status");
+ if (shouldLink) {
+ is($source.getAttribute("href"), source, "Correct source");
+ }
+
+ if (line != null) {
+ let lineText = `:${line}`;
+ if (column != null) {
+ lineText += `:${column}`;
+ }
+
+ is($line.textContent, lineText, "Correct line number");
+ } else {
+ ok(!$line, "Should not have an element for `line`");
+ }
+
+ if (functionName != null) {
+ is($func.textContent, functionName, "Correct function name");
+ } else {
+ ok(!$func, "Should not have an element for `functionName`");
+ }
+
+ if (locationPrefix != null) {
+ is($locationPrefix.textContent, locationPrefix, "Correct prefix");
+ } else {
+ ok(!$locationPrefix, "Should not have an element for `locationPrefix`");
+ }
+}
+
+function checkSmartFrameString({ el, location, functionName, tooltip }) {
+ const $ = selector => el.querySelector(selector);
+
+ const $func = $(".title");
+ const $location = $(".location");
+
+ is($location.textContent, location, "Correct filename");
+ is(el.getAttribute("title"), tooltip, "Correct tooltip");
+ if (functionName != null) {
+ is($func.textContent, functionName, "Correct function name");
+ } else {
+ ok(!$func, "Should not have an element for `functionName`");
+ }
+}
+
+function renderComponent(component, props) {
+ const el = React.createElement(component, props, {});
+ // By default, renderIntoDocument() won't work for stateless components, but
+ // it will work if the stateless component is wrapped in a stateful one.
+ // See https://github.com/facebook/react/issues/4839
+ const wrappedEl = dom.span({}, [el]);
+ const renderedComponent = TestUtils.renderIntoDocument(wrappedEl);
+ return ReactDOM.findDOMNode(renderedComponent).children[0];
+}
+
+function shallowRenderComponent(component, props) {
+ const el = React.createElement(component, props);
+ const renderer = new ShallowRenderer();
+ renderer.render(el, {});
+ return renderer.getRenderOutput();
+}
+
+/**
+ * Creates a React Component for testing
+ *
+ * @param {string} factory - factory object of the component to be created
+ * @param {object} props - React props for the component
+ * @returns {object} - container Node, Object with React component
+ * and querySelector function with $ as name.
+ */
+async function createComponentTest(factory, props) {
+ const container = document.createElement("div");
+ document.body.appendChild(container);
+
+ const component = ReactDOM.render(factory(props), container);
+ await forceRender(component);
+
+ return {
+ container,
+ component,
+ $: s => container.querySelector(s),
+ };
+}
+
+async function waitFor(condition = () => true, delay = 50) {
+ do {
+ const res = condition();
+ if (res) {
+ return res;
+ }
+ await new Promise(resolve => setTimeout(resolve, delay));
+ } while (true);
+}
+
+/**
+ * Matches a component tree rendererd using TestRenderer to a given expected JSON
+ * snapshot.
+ * @param {String} name
+ * Name of the function derived from a test [step] name.
+ * @param {Object} el
+ * React element to be rendered using TestRenderer.
+ */
+function matchSnapshot(name, el) {
+ if (!_snapshots) {
+ is(false, "No snapshots were loaded into test.");
+ }
+
+ const snapshot = _snapshots[name];
+ if (snapshot === undefined) {
+ is(false, `Snapshot for "${name}" not found.`);
+ }
+
+ const renderer = TestRenderer.create(el, {});
+ const tree = renderer.toJSON();
+
+ is(
+ JSON.stringify(tree, (key, value) =>
+ typeof value === "function" ? value.toString() : value
+ ),
+ JSON.stringify(snapshot),
+ name
+ );
+}