summaryrefslogtreecommitdiffstats
path: root/devtools/client/dom/test/head.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/dom/test/head.js')
-rw-r--r--devtools/client/dom/test/head.js201
1 files changed, 201 insertions, 0 deletions
diff --git a/devtools/client/dom/test/head.js b/devtools/client/dom/test/head.js
new file mode 100644
index 0000000000..93afb2fe09
--- /dev/null
+++ b/devtools/client/dom/test/head.js
@@ -0,0 +1,201 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
+
+"use strict";
+
+// shared-head.js handles imports, constants, and utility functions
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
+ this
+);
+
+// DOM panel actions.
+const constants = require("resource://devtools/client/dom/content/constants.js");
+
+// Uncomment this pref to dump all devtools emitted events to the console.
+// Services.prefs.setBoolPref("devtools.dom.enabled", true);
+
+// Enable the DOM panel
+Services.prefs.setBoolPref("devtools.dom.enabled", true);
+
+registerCleanupFunction(() => {
+ info("finish() was called, cleaning up...");
+ Services.prefs.clearUserPref("devtools.dump.emit");
+ Services.prefs.clearUserPref("devtools.dom.enabled");
+});
+
+/**
+ * Add a new test tab in the browser and load the given url.
+ * @param {String} url
+ * The url to be loaded in the new tab
+ * @return a promise that resolves to the tab object when
+ * the url is loaded
+ */
+async function addTestTab(url) {
+ info("Adding a new test tab with URL: '" + url + "'");
+
+ const tab = await addTab(url);
+
+ // Select the DOM panel and wait till it's initialized.
+ const panel = await initDOMPanel(tab);
+
+ // FETCH_PROPERTIES should be fired during the call to initDOMPanel
+ // But note that this behavior changed during a change in webconsole
+ // initialization. So this might be racy.
+ const doc = panel.panelWin.document;
+ const nodes = [...doc.querySelectorAll(".treeLabel")];
+ ok(!!nodes.length, "The DOM panel is already populated");
+
+ return {
+ tab,
+ browser: tab.linkedBrowser,
+ panel,
+ };
+}
+
+/**
+ * Open the DOM panel for the given tab.
+ *
+ * @param {Element} tab
+ * Optional tab element for which you want open the DOM panel.
+ * The default tab is taken from the global variable |tab|.
+ * @return a promise that is resolved once the web console is open.
+ */
+async function initDOMPanel(tab) {
+ tab = tab || gBrowser.selectedTab;
+ const toolbox = await gDevTools.showToolboxForTab(tab, { toolId: "dom" });
+ const panel = toolbox.getCurrentPanel();
+ return panel;
+}
+
+/**
+ * Synthesize asynchronous click event (with clean stack trace).
+ */
+function synthesizeMouseClickSoon(panel, element) {
+ return new Promise(resolve => {
+ executeSoon(() => {
+ EventUtils.synthesizeMouse(element, 2, 2, {}, panel.panelWin);
+ resolve();
+ });
+ });
+}
+
+/**
+ * Returns tree row with specified label.
+ */
+function getRowByLabel(panel, text) {
+ const doc = panel.panelWin.document;
+ const labels = [...doc.querySelectorAll(".treeLabel")];
+ const label = labels.find(node => node.textContent == text);
+ return label ? label.closest(".treeRow") : null;
+}
+
+/**
+ * Returns tree row with specified index.
+ */
+function getRowByIndex(panel, id) {
+ const doc = panel.panelWin.document;
+ const labels = [...doc.querySelectorAll(".treeLabel")];
+ const label = labels.find((node, i) => i == id);
+ return label ? label.closest(".treeRow") : null;
+}
+
+/**
+ * Returns the children (tree row text) of the specified object name as an
+ * array.
+ */
+function getAllRowsForLabel(panel, text) {
+ let rootObjectLevel;
+ let node;
+ const result = [];
+ const doc = panel.panelWin.document;
+ const nodes = [...doc.querySelectorAll(".treeLabel")];
+
+ // Find the label (object name) for which we want the children. We remove
+ // nodes from the start of the array until we reach the property. The children
+ // are then at the start of the array.
+ while (true) {
+ node = nodes.shift();
+
+ if (!node || node.textContent === text) {
+ rootObjectLevel = node.getAttribute("data-level");
+ break;
+ }
+ }
+
+ // Return an empty array if the node is not found.
+ if (!node) {
+ return result;
+ }
+
+ // Now get the children.
+ for (node of nodes) {
+ const level = node.getAttribute("data-level");
+
+ if (level > rootObjectLevel) {
+ result.push({
+ name: normalizeTreeValue(node.textContent),
+ value: normalizeTreeValue(
+ node.parentNode.nextElementSibling.textContent
+ ),
+ });
+ } else {
+ break;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Strings in the tree are in the form ""a"" and numbers in the form "1". We
+ * normalize these values by converting ""a"" to "a" and "1" to 1.
+ *
+ * @param {String} value
+ * The value to normalize.
+ * @return {String|Number}
+ * The normalized value.
+ */
+function normalizeTreeValue(value) {
+ if (value === `""`) {
+ return "";
+ }
+ if (value.startsWith(`"`) && value.endsWith(`"`)) {
+ return value.substr(1, value.length - 2);
+ }
+ if (isFinite(value) && parseInt(value, 10) == value) {
+ return parseInt(value, 10);
+ }
+
+ return value;
+}
+
+/**
+ * Expands elements with given label and waits till
+ * children are received from the backend.
+ */
+function expandRow(panel, labelText) {
+ const row = getRowByLabel(panel, labelText);
+ return synthesizeMouseClickSoon(panel, row).then(() => {
+ // Wait till children (properties) are fetched
+ // from the backend.
+ const store = getReduxStoreFromPanel(panel);
+ return waitForDispatch(store, "FETCH_PROPERTIES");
+ });
+}
+
+function refreshPanel(panel) {
+ const doc = panel.panelWin.document;
+ const button = doc.querySelector("#dom-refresh-button");
+ return synthesizeMouseClickSoon(panel, button).then(() => {
+ // Wait till children (properties) are fetched
+ // from the backend.
+ const store = getReduxStoreFromPanel(panel);
+ return waitForDispatch(store, "FETCH_PROPERTIES");
+ });
+}
+
+function getReduxStoreFromPanel(panel) {
+ return panel.panelWin.view.mainFrame.store;
+}