summaryrefslogtreecommitdiffstats
path: root/devtools/client/responsive/test/browser/browser_touch_simulation.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/responsive/test/browser/browser_touch_simulation.js')
-rw-r--r--devtools/client/responsive/test/browser/browser_touch_simulation.js341
1 files changed, 341 insertions, 0 deletions
diff --git a/devtools/client/responsive/test/browser/browser_touch_simulation.js b/devtools/client/responsive/test/browser/browser_touch_simulation.js
new file mode 100644
index 0000000000..b74c9f53c4
--- /dev/null
+++ b/devtools/client/responsive/test/browser/browser_touch_simulation.js
@@ -0,0 +1,341 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test global touch simulation button
+
+const TEST_URL = `${URL_ROOT_SSL}touch.html`;
+const PREF_DOM_META_VIEWPORT_ENABLED = "dom.meta-viewport.enabled";
+
+// A 300ms delay between a `touchend` and `click` event is added whenever double-tap zoom
+// is allowed.
+const DELAY_MIN = 250;
+
+addRDMTask(TEST_URL, async function ({ ui }) {
+ reloadOnTouchChange(true);
+
+ await waitBootstrap(ui);
+ await testWithNoTouch(ui);
+ await toggleTouchSimulation(ui);
+ await promiseContentReflow(ui);
+ await testWithTouch(ui);
+ await testWithMetaViewportEnabled(ui);
+ await testWithMetaViewportDisabled(ui);
+ testTouchButton(ui);
+
+ reloadOnTouchChange(false);
+});
+
+async function testWithNoTouch(ui) {
+ await SpecialPowers.spawn(ui.getViewportBrowser(), [], async function () {
+ const div = content.document.querySelector("div");
+ let x = 0,
+ y = 0;
+
+ info("testWithNoTouch: Initial test parameter and mouse mouse outside div");
+ x = -1;
+ y = -1;
+ await EventUtils.synthesizeMouse(
+ div,
+ x,
+ y,
+ { type: "mousemove", isSynthesized: false },
+ content
+ );
+ div.style.transform = "none";
+ div.style.backgroundColor = "";
+
+ info("testWithNoTouch: Move mouse into the div element");
+ await EventUtils.synthesizeMouseAtCenter(
+ div,
+ { type: "mousemove", isSynthesized: false },
+ content
+ );
+ is(div.style.backgroundColor, "red", "mouseenter or mouseover should work");
+
+ info("testWithNoTouch: Drag the div element");
+ await EventUtils.synthesizeMouseAtCenter(
+ div,
+ { type: "mousedown", isSynthesized: false },
+ content
+ );
+ x = 100;
+ y = 100;
+ await EventUtils.synthesizeMouse(
+ div,
+ x,
+ y,
+ { type: "mousemove", isSynthesized: false },
+ content
+ );
+ is(div.style.transform, "none", "touchmove shouldn't work");
+ await EventUtils.synthesizeMouse(
+ div,
+ x,
+ y,
+ { type: "mouseup", isSynthesized: false },
+ content
+ );
+
+ info("testWithNoTouch: Move mouse out of the div element");
+ x = -1;
+ y = -1;
+ await EventUtils.synthesizeMouse(
+ div,
+ x,
+ y,
+ { type: "mousemove", isSynthesized: false },
+ content
+ );
+ is(div.style.backgroundColor, "blue", "mouseout or mouseleave should work");
+
+ info("testWithNoTouch: Click the div element");
+ await EventUtils.synthesizeClick(div);
+ is(
+ div.dataset.isDelay,
+ "false",
+ "300ms delay between touch events and mouse events should not work"
+ );
+
+ // Assuming that this test runs on devices having no touch screen device.
+ ok(
+ !content.document.defaultView.matchMedia("(pointer: coarse)").matches,
+ "pointer: coarse shouldn't be matched"
+ );
+ ok(
+ !content.document.defaultView.matchMedia("(hover: none)").matches,
+ "hover: none shouldn't be matched"
+ );
+ ok(
+ !content.document.defaultView.matchMedia("(any-pointer: coarse)").matches,
+ "any-pointer: coarse shouldn't be matched"
+ );
+ ok(
+ !content.document.defaultView.matchMedia("(any-hover: none)").matches,
+ "any-hover: none shouldn't be matched"
+ );
+ });
+}
+
+async function testWithTouch(ui) {
+ await SpecialPowers.spawn(ui.getViewportBrowser(), [], async function () {
+ const div = content.document.querySelector("div");
+ let x = 0,
+ y = 0;
+
+ info("testWithTouch: Initial test parameter and mouse mouse outside div");
+ x = -1;
+ y = -1;
+ await EventUtils.synthesizeMouse(
+ div,
+ x,
+ y,
+ { type: "mousemove", isSynthesized: false },
+ content
+ );
+ div.style.transform = "none";
+ div.style.backgroundColor = "";
+
+ info("testWithTouch: Move mouse into the div element");
+ await EventUtils.synthesizeMouseAtCenter(
+ div,
+ { type: "mousemove", isSynthesized: false },
+ content
+ );
+ isnot(
+ div.style.backgroundColor,
+ "red",
+ "mouseenter or mouseover should not work"
+ );
+
+ info("testWithTouch: Drag the div element");
+ await EventUtils.synthesizeMouseAtCenter(
+ div,
+ { type: "mousedown", isSynthesized: false },
+ content
+ );
+ x = 100;
+ y = 100;
+ const touchMovePromise = ContentTaskUtils.waitForEvent(div, "touchmove");
+ await EventUtils.synthesizeMouse(
+ div,
+ x,
+ y,
+ { type: "mousemove", isSynthesized: false },
+ content
+ );
+ await touchMovePromise;
+ isnot(div.style.transform, "none", "touchmove should work");
+ await EventUtils.synthesizeMouse(
+ div,
+ x,
+ y,
+ { type: "mouseup", isSynthesized: false },
+ content
+ );
+
+ info("testWithTouch: Move mouse out of the div element");
+ x = -1;
+ y = -1;
+ await EventUtils.synthesizeMouse(
+ div,
+ x,
+ y,
+ { type: "mousemove", isSynthesized: false },
+ content
+ );
+ isnot(
+ div.style.backgroundColor,
+ "blue",
+ "mouseout or mouseleave should not work"
+ );
+
+ ok(
+ content.document.defaultView.matchMedia("(pointer: coarse)").matches,
+ "pointer: coarse should be matched"
+ );
+ ok(
+ content.document.defaultView.matchMedia("(hover: none)").matches,
+ "hover: none should be matched"
+ );
+ ok(
+ content.document.defaultView.matchMedia("(any-pointer: coarse)").matches,
+ "any-pointer: coarse should be matched"
+ );
+ ok(
+ content.document.defaultView.matchMedia("(any-hover: none)").matches,
+ "any-hover: none should be matched"
+ );
+ });
+
+ // Capturing touch events with the content window as a registered listener causes the
+ // "changedTouches" field to be undefined when using deprecated TouchEvent APIs.
+ // See Bug 1549220 and Bug 1588438 for more information on this issue.
+ info("Test that changed touches captured on the content window are defined.");
+ await SpecialPowers.spawn(ui.getViewportBrowser(), [], async function () {
+ const div = content.document.querySelector("div");
+
+ content.addEventListener(
+ "touchstart",
+ event => {
+ const changedTouch = event.changedTouches[0];
+ ok(changedTouch, "Changed touch is defined.");
+ },
+ { once: true }
+ );
+ await EventUtils.synthesizeClick(div);
+ });
+}
+
+async function testWithMetaViewportEnabled(ui) {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_DOM_META_VIEWPORT_ENABLED, true]],
+ });
+
+ await SpecialPowers.spawn(
+ ui.getViewportBrowser(),
+ [{ delay_min: DELAY_MIN }],
+ async function ({ delay_min }) {
+ // A helper for testing the delay between touchend and click events.
+ async function testDelay(mvc, el) {
+ const touchendPromise = ContentTaskUtils.waitForEvent(el, "touchend");
+ const clickPromise = ContentTaskUtils.waitForEvent(el, "click");
+ await EventUtils.synthesizeClick(el);
+ const { timeStamp: touchendTimestamp } = await touchendPromise;
+ const { timeStamp: clickTimeStamp } = await clickPromise;
+ const delay = clickTimeStamp - touchendTimestamp;
+
+ const expected = delay >= delay_min;
+
+ ok(
+ expected,
+ `${mvc}: There should be greater than a ${delay_min}ms delay between touch events and mouse events. Got delay of ${delay}ms`
+ );
+ }
+
+ // A helper function for waiting for reflow to complete.
+ const promiseReflow = () => {
+ return new Promise(resolve => {
+ content.window.requestAnimationFrame(() => {
+ content.window.requestAnimationFrame(resolve);
+ });
+ });
+ };
+
+ const meta = content.document.querySelector("meta[name=viewport]");
+ const div = content.document.querySelector("div");
+
+ info(
+ "testWithMetaViewportEnabled: " +
+ "click the div element with <meta name='viewport'>"
+ );
+ meta.content = "";
+ await promiseReflow();
+ await testDelay("(empty)", div);
+ }
+ );
+
+ await SpecialPowers.popPrefEnv();
+}
+
+async function testWithMetaViewportDisabled(ui) {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_DOM_META_VIEWPORT_ENABLED, false]],
+ });
+
+ await SpecialPowers.spawn(
+ ui.getViewportBrowser(),
+ [{ delay_min: DELAY_MIN }],
+ async function ({ delay_min }) {
+ const meta = content.document.querySelector("meta[name=viewport]");
+ const div = content.document.querySelector("div");
+
+ info(
+ "testWithMetaViewportDisabled: click the div with <meta name='viewport'>"
+ );
+ meta.content = "";
+ const touchendPromise = ContentTaskUtils.waitForEvent(div, "touchend");
+ const clickPromise = ContentTaskUtils.waitForEvent(div, "click");
+ await EventUtils.synthesizeClick(div);
+ const { timeStamp: touchendTimestamp } = await touchendPromise;
+ const { timeStamp: clickTimeStamp } = await clickPromise;
+ const delay = clickTimeStamp - touchendTimestamp;
+
+ const expected = delay >= delay_min;
+
+ ok(
+ expected,
+ `There should be greater than a ${delay_min}ms delay between touch events and mouse events. Got delay of ${delay}ms`
+ );
+ }
+ );
+}
+
+function testTouchButton(ui) {
+ const { document } = ui.toolWindow;
+ const touchButton = document.getElementById("touch-simulation-button");
+
+ ok(
+ touchButton.classList.contains("checked"),
+ "Touch simulation is active at end of test."
+ );
+
+ touchButton.click();
+
+ ok(
+ !touchButton.classList.contains("checked"),
+ "Touch simulation is stopped on click."
+ );
+
+ touchButton.click();
+
+ ok(
+ touchButton.classList.contains("checked"),
+ "Touch simulation is started on click."
+ );
+}
+
+async function waitBootstrap(ui) {
+ await waitForFrameLoad(ui, TEST_URL);
+}