diff options
Diffstat (limited to 'testing/web-platform/tests/uievents/mouse/synthetic-mouse-enter-leave-over-out-button-state-after-target-removed.tentative.html')
-rw-r--r-- | testing/web-platform/tests/uievents/mouse/synthetic-mouse-enter-leave-over-out-button-state-after-target-removed.tentative.html | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/testing/web-platform/tests/uievents/mouse/synthetic-mouse-enter-leave-over-out-button-state-after-target-removed.tentative.html b/testing/web-platform/tests/uievents/mouse/synthetic-mouse-enter-leave-over-out-button-state-after-target-removed.tentative.html new file mode 100644 index 0000000000..fad82f850d --- /dev/null +++ b/testing/web-platform/tests/uievents/mouse/synthetic-mouse-enter-leave-over-out-button-state-after-target-removed.tentative.html @@ -0,0 +1,240 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<meta name="variant" content="?buttonType=LEFT&button=0&buttons=1"> +<meta name="variant" content="?buttonType=MIDDLE&button=1&buttons=4"> +<title>Testing button state of synthesized mouse(out|over|leave|enter) events</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<style> +#parent { + background-color: lightseagreen; + padding: 0; + height: 40px; + width: 40px; +} +#child { + background-color: red; + margin: 0; + height: 30px; + width: 30px; +} +</style> +</head> +<body> +<div id="parent"><div id="child">abc</div></div> +<script> +const searchParams = new URLSearchParams(document.location.search); +const buttonType = searchParams.get("buttonType"); +const button = parseInt(searchParams.get("button")); +const buttons = parseInt(searchParams.get("buttons")); + +let events = []; +function eventToString(data) { + if (!data) { + return "{}"; + } + return `{ '${data.type}' on '${data.target}': button=${data.button}, buttons=${data.buttons} }`; +} + +function eventsToString(events) { + if (!events.length) { + return "[]"; + } + let ret = "["; + for (const data of events) { + if (ret != "[") { + ret += ", "; + } + ret += eventToString(data); + } + return ret + "]"; +} + +function removeEventsBefore(eventType) { + while (events[0]?.type != eventType) { + events.shift(); + } +} + +const parentElement = document.getElementById("parent"); +const childElement = document.getElementById("child"); + +function promiseLayout() { + return new Promise(resolve => { + (childElement.isConnected ? childElement : parentElement).getBoundingClientRect(); + requestAnimationFrame(() => requestAnimationFrame(resolve)); + }); +} + +promise_test(async () => { + await new Promise(resolve => { + addEventListener("load", resolve, { once: true }); + }); + + ["mouseout", "mouseover", "mouseleave", "mouseenter", "mousemove", "mousedown"].forEach(eventType => { + parentElement.addEventListener(eventType, event => { + if (event.target != parentElement) { + return; + } + events.push({ + type: event.type, + target: "parent", + button: event.button, + buttons: event.buttons, + }); + }); + childElement.addEventListener(eventType, event => { + if (event.target != childElement) { + return; + } + events.push({ + type: event.type, + target: "child", + button: event.button, + buttons: event.buttons, + }); + }); + }); +}, "Setup event listeners and wait for load"); + +promise_test(async t => { + events = []; + await promiseLayout(); + childElement.addEventListener("mousedown", () => childElement.remove(), {once: true}); + const {x, y} = (function () { + const rect = childElement.getBoundingClientRect(); + return { x: rect.left, y: rect.top }; + })(); + const actions = new test_driver.Actions(); + await actions.pointerMove(10, 10, {origin: childElement}) + .pointerDown({button: actions.ButtonType[buttonType]}) + .pause(100) // Allow browsers to synthesize mouseout, etc + .pointerUp({button: actions.ButtonType[buttonType]}) + .send(); + await promiseLayout(); + removeEventsBefore("mousedown"); + test(() => { + const maybeMouseDownEvent = + events.length && events[0].type == "mousedown" ? events.shift() : undefined; + assert_equals( + eventToString(maybeMouseDownEvent), + eventToString({ type: "mousedown", target: "child", button, buttons }) + ); + }, `${t.name}: mousedown should've been fired`); + assert_true(events.length > 0, `${t.name}: Some events should've been fired after mousedown`); + test(() => { + // Before `mousedown` is fired, both parent and child must have received + // `mouseenter`, only the child must have received `mouseover`. Then, the + // child is now moved away by the `mousedown` listener. Therefore, + // `mouseout` and `mouseleave` should be fired on the child as the spec of + // UI Events defines. Then, they are not a button press events. Therefore, + // the `button` should be 0, but buttons should be set to 4 because of + // pressing the middle button. + let mouseOutOrLeave = []; + while (events[0]?.type == "mouseout" || events[0]?.type == "mouseleave") { + mouseOutOrLeave.push(events.shift()); + } + assert_equals( + eventsToString(mouseOutOrLeave), + eventsToString([ + { type: "mouseout", target: "child", button: 0, buttons }, + { type: "mouseleave", target: "child", button: 0, buttons }, + ]) + ); + }, `${t.name}: mouseout and mouseleave should've been fired on the removed child`); + test(() => { + // And `mouseover` should be fired on the parent as the spec of UI Events + // defines. + let mouseOver = []; + while (events[0]?.type == "mouseover") { + mouseOver.push(events.shift()); + } + assert_equals( + eventsToString(mouseOver), + eventsToString([{ type: "mouseover", target: "parent", button: 0, buttons }]) + ); + }, `${t.name}: mouseover should've been fired on the parent`); + test(() => { + // On the other hand, it's unclear about `mouseenter`. The mouse cursor has + // never been moved out from the parent. Therefore, it shouldn't be fired + // on the parent ideally, but all browsers do not pass this test and there + // is no clear definition about this case. + let mouseEnter = []; + while (events.length && events[0].type == "mouseenter") { + mouseEnter.push(events.shift()); + } + assert_equals(eventsToString(mouseEnter), eventsToString([])); + }, `${t.name}: mouseenter should not have been fired on the parent`); + assert_equals(eventsToString(events), eventsToString([]), "All events should've been checked"); + parentElement.appendChild(childElement); +}, "Removing an element at mousedown"); + +promise_test(async t => { + events = []; + await promiseLayout(); + childElement.addEventListener("mouseup", () => childElement.remove(), {once: true}); + const {x, y} = (function () { + const rect = childElement.getBoundingClientRect(); + return { x: rect.left, y: rect.top }; + })(); + const actions = new test_driver.Actions(); + await actions.pointerMove(10, 10, {origin: childElement}) + .pointerDown({button: actions.ButtonType[buttonType]}) + .pointerUp({button: actions.ButtonType[buttonType]}) + .send(); + await promiseLayout(); + removeEventsBefore("mousedown"); + test(() => { + const maybeMouseDownEvent = + events.length && events[0].type == "mousedown" ? events.shift() : undefined; + assert_equals( + eventToString(maybeMouseDownEvent), + eventToString({ type: "mousedown", target: "child", button, buttons }) + ); + }, `${t.name}: mousedown should've been fired`); + assert_true(events.length > 0, `${t.name}: Some events should've been fired after mousedown`); + // Same as the `mousedown` case except `buttons` value because `mouseout`, + // `mouseleave`, `mouseover` and `mouseenter` should (or may) be fired + // after the `mouseup`. Therefore, `.buttons` should not have the button + // flag. + test(() => { + let mouseOutOrLeave = []; + while (events[0]?.type == "mouseout" || events[0]?.type == "mouseleave") { + mouseOutOrLeave.push(events.shift()); + } + assert_equals( + eventsToString(mouseOutOrLeave), + eventsToString([ + { type: "mouseout", target: "child", button: 0, buttons: 0 }, + { type: "mouseleave", target: "child", button: 0, buttons: 0 }, + ]) + ); + }, `${t.name}: mouseout and mouseleave should've been fired on the removed child`); + test(() => { + let mouseOver = []; + while (events[0]?.type == "mouseover") { + mouseOver.push(events.shift()); + } + assert_equals( + eventsToString(mouseOver), + eventsToString([{ type: "mouseover", target: "parent", button: 0, buttons: 0 }]) + ); + }, `${t.name}: mouseover should've been fired on the parent`); + test(() => { + let mouseEnter = []; + while (events[0]?.type == "mouseenter") { + mouseEnter.push(events.shift()); + } + assert_equals(eventsToString(mouseEnter), eventsToString([])); + }, `${t.name}: mouseenter should not have been fired on the parent`); + assert_equals(eventsToString(events), eventsToString([]), "All events should've been checked"); + parentElement.appendChild(childElement); +}, "Removing an element at mouseup"); +</script> +</body> +</html> |