diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
commit | 9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /devtools/client/shared/components/test/chrome/head.js | |
parent | Initial commit. (diff) | |
download | thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.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/shared/components/test/chrome/head.js')
-rw-r--r-- | devtools/client/shared/components/test/chrome/head.js | 379 |
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 + ); +} |