diff options
Diffstat (limited to 'devtools/client/responsive/test/browser/browser_touch_simulation.js')
-rw-r--r-- | devtools/client/responsive/test/browser/browser_touch_simulation.js | 341 |
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..42ab8e925c --- /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}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); +} |