summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/markup/test/helper_events_test_runner.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--devtools/client/inspector/markup/test/helper_events_test_runner.js229
1 files changed, 229 insertions, 0 deletions
diff --git a/devtools/client/inspector/markup/test/helper_events_test_runner.js b/devtools/client/inspector/markup/test/helper_events_test_runner.js
new file mode 100644
index 0000000000..d6f96cdfb2
--- /dev/null
+++ b/devtools/client/inspector/markup/test/helper_events_test_runner.js
@@ -0,0 +1,229 @@
+/* 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"}] */
+/* import-globals-from head.js */
+/* import-globals-from helper_diff.js */
+"use strict";
+
+const beautify = require("resource://devtools/shared/jsbeautify/beautify.js");
+
+loadHelperScript("helper_diff.js");
+
+/**
+ * Generator function that runs checkEventsForNode() for each object in the
+ * TEST_DATA array.
+ */
+async function runEventPopupTests(url, tests) {
+ const { inspector } = await openInspectorForURL(url);
+
+ await inspector.markup.expandAll();
+
+ for (const test of tests) {
+ await checkEventsForNode(test, inspector);
+ }
+
+ // Wait for promises to avoid leaks when running this as a single test.
+ // We need to do this because we have opened a bunch of popups and don't them
+ // to affect other test runs when they are GCd.
+ await promiseNextTick();
+}
+
+/**
+ * Generator function that takes a selector and expected results and returns
+ * the event info.
+ *
+ * @param {Object} test
+ * A test object should contain the following properties:
+ * - selector {String} a css selector targeting the node to edit
+ * - expected {Array} array of expected event objects
+ * - type {String} event type
+ * - filename {String} filename:line where the evt handler is defined
+ * - attributes {Array} array of event attributes ({String})
+ * - handler {String} string representation of the handler
+ * - beforeTest {Function} (optional) a function to execute on the page
+ * before running the test
+ * - isSourceMapped {Boolean} (optional) true if the location
+ * is source-mapped, requiring some extra delay before the checks
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * opened
+ */
+async function checkEventsForNode(test, inspector) {
+ const { selector, expected, beforeTest, isSourceMapped } = test;
+ const container = await getContainerForSelector(selector, inspector);
+
+ if (typeof beforeTest === "function") {
+ await beforeTest(inspector);
+ }
+
+ const evHolder = container.elt.querySelector(
+ ".inspector-badge.interactive[data-event]"
+ );
+
+ if (expected.length === 0) {
+ // If no event is expected, check that event bubble is hidden.
+ ok(!evHolder, "event bubble should be hidden");
+ return;
+ }
+
+ const tooltip = inspector.markup.eventDetailsTooltip;
+
+ await selectNode(selector, inspector);
+
+ let sourceMapPromise = null;
+ if (isSourceMapped) {
+ sourceMapPromise = tooltip.once("event-tooltip-source-map-ready");
+ }
+
+ // Click button to show tooltip
+ info("Clicking evHolder");
+ evHolder.scrollIntoView();
+ EventUtils.synthesizeMouseAtCenter(
+ evHolder,
+ {},
+ inspector.markup.doc.defaultView
+ );
+ await tooltip.once("shown");
+ info("tooltip shown");
+
+ if (isSourceMapped) {
+ info("Waiting for source map to be applied");
+ await sourceMapPromise;
+ }
+
+ // Check values
+ const headers = tooltip.panel.querySelectorAll(".event-header");
+ const nodeFront = container.node;
+ const cssSelector = nodeFront.nodeName + "#" + nodeFront.id;
+
+ for (let i = 0; i < headers.length; i++) {
+ const label = `${cssSelector}.${expected[i].type} (index ${i})`;
+ info(`${label} START`);
+
+ const header = headers[i];
+ const type = header.querySelector(".event-tooltip-event-type");
+ const filename = header.querySelector(".event-tooltip-filename");
+ const attributes = header.querySelectorAll(".event-tooltip-attributes");
+ const contentBox = header.nextElementSibling;
+
+ info("Looking for " + type.textContent);
+
+ is(type.textContent, expected[i].type, "type matches for " + cssSelector);
+ is(
+ filename.textContent,
+ expected[i].filename,
+ "filename matches for " + cssSelector
+ );
+
+ is(
+ attributes.length,
+ expected[i].attributes.length,
+ "we have the correct number of attributes"
+ );
+
+ for (let j = 0; j < expected[i].attributes.length; j++) {
+ is(
+ attributes[j].textContent,
+ expected[i].attributes[j],
+ "attribute[" + j + "] matches for " + cssSelector
+ );
+ }
+
+ is(
+ header.classList.contains("content-expanded"),
+ false,
+ "We are not in expanded state"
+ );
+
+ // Make sure the header is not hidden by scrollbars before clicking.
+ header.scrollIntoView();
+
+ // Avoid clicking the header's center (could hit the debugger button)
+ EventUtils.synthesizeMouse(header, 2, 2, {}, type.ownerGlobal);
+ await tooltip.once("event-tooltip-ready");
+
+ is(
+ header.classList.contains("content-expanded") &&
+ contentBox.hasAttribute("open"),
+ true,
+ "We are in expanded state and icon changed"
+ );
+
+ is(
+ tooltip.panel.querySelectorAll(".event-header.content-expanded")
+ .length === 1 &&
+ tooltip.panel.querySelectorAll(".event-tooltip-content-box[open]")
+ .length === 1,
+ true,
+ "Only one event box is expanded at a time"
+ );
+
+ const editor = tooltip.eventTooltip._eventEditors.get(contentBox).editor;
+ const tidiedHandler = beautify.js(expected[i].handler, {
+ indent_size: 2,
+ });
+ testDiff(
+ editor.getText(),
+ tidiedHandler,
+ "handler matches for " + cssSelector,
+ ok
+ );
+
+ const checkbox = header.querySelector("input[type=checkbox]");
+ ok(checkbox, "The event toggling checkbox is displayed");
+ const disabled = checkbox.hasAttribute("disabled");
+ // We can't disable React/jQuery events at the moment, so ensure that for those,
+ // the checkbox is disabled.
+ const shouldBeDisabled =
+ expected[i].attributes?.includes("React") ||
+ expected[i].attributes?.includes("jQuery");
+ ok(
+ disabled === shouldBeDisabled,
+ `The checkbox is ${shouldBeDisabled ? "disabled" : "enabled"}\n`
+ );
+
+ info(`${label} END`);
+ }
+
+ const tooltipHidden = tooltip.once("hidden");
+ tooltip.hide();
+ await tooltipHidden;
+}
+
+/**
+ * Create diff of two strings.
+ *
+ * @param {String} text1
+ * String to compare with text2.
+ * @param {String} text2 [description]
+ * String to compare with text1.
+ * @param {String} msg
+ * Message to display on failure. A diff will be displayed after this
+ * message.
+ */
+function testDiff(text1, text2, msg) {
+ let out = "";
+
+ if (text1 === text2) {
+ ok(true, msg);
+ return;
+ }
+
+ const result = textDiff(text1, text2);
+
+ for (const { atom, operation } of result) {
+ switch (operation) {
+ case "add":
+ out += "+ " + atom + "\n";
+ break;
+ case "delete":
+ out += "- " + atom + "\n";
+ break;
+ case "none":
+ out += " " + atom + "\n";
+ break;
+ }
+ }
+
+ ok(false, msg + "\nDIFF:\n==========\n" + out + "==========\n");
+}