Skip to content

Commit

Permalink
WPT coverage for Pointer Events for slotted elements.
Browse files Browse the repository at this point in the history
This CL adds tests for two untested aspects of PointerEvents in slots:
- PE event sequence around slots when the shadow DOM remains unchanged.
- Boundary events after modifications to slotted elements.

This covers the last remaining WPT for:
w3c/pointerevents#477

Bug: 40156858

Change-Id: I9823595a0b15672d0395d4e117ccc6462c3e42f2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5598510
Commit-Queue: Mustaq Ahmed <mustaq@chromium.org>
Reviewed-by: Robert Flack <flackr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1313570}
  • Loading branch information
mustaqahmed authored and chromium-wpt-export-bot committed Jun 11, 2024
1 parent 3856e17 commit 628348f
Show file tree
Hide file tree
Showing 2 changed files with 282 additions and 0 deletions.
174 changes: 174 additions & 0 deletions pointerevents/pointerevent_after_target_removed_from_slot.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<!DOCTYPE HTML>
<link rel="help" href="https://w3c.github.io/pointerevents/#firing-events-using-the-pointerevent-interface">
<title>Enter/leave events fired to parent after child is removed from slot</title>
<meta name="variant" content="?mouse">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="pointerevent_support.js"></script>

<template id="template">
<style>
div {
width: 100px;
height: 100px;
}
</style>
<div id="parent">
<slot>slot</slot>
</div>
</template>

<style>
div, my-elem {
width: 100px;
height: 100px;
display: block;
}
</style>

<my-elem id="host">
<div id="child">child</div>
</my-elem>
<div id="done">done</div>

<script>
"use strict";

customElements.define(
"my-elem",
class extends HTMLElement {
constructor() {
super();
let content = document.getElementById("template").content;
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.appendChild(content.cloneNode(true));
}
},
);

const pointer_type = location.search.substring(1);

const shadow_host = document.getElementById("host");
const parent = shadow_host.shadowRoot.getElementById("parent");
const slot = parent.firstElementChild;
const slotted_child = document.getElementById("child");
const done = document.getElementById("done");

let event_log = [];
let elem_to_remove;

function logEvent(e) {
if (e.eventPhase == e.AT_TARGET) {
event_log.push(e.type + "@" + e.target.id);
}
}

function removeChildFromSlot() {
elem_to_remove.remove();
event_log.push("(child-removed)");
}

function restoreChildInSlot() {
if (!slotted_child.parentElement) {
shadow_host.appendChild(slotted_child);
}
if (!slot.parentElement) {
parent.appendChild(slot);
}
}

function setup() {
const events = ["pointerover", "pointerout",
"pointerenter", "pointerleave", "pointerdown", "pointerup"];
let targets = [parent, slotted_child];
for (let i = 0; i < targets.length; i++) {
events.forEach(event => targets[i].addEventListener(event, logEvent));
}
}

function addPromiseTest(remover_event, tested_elem_to_remove,
expected_events) {
assert_true(["slot", "slotted-child"].includes(tested_elem_to_remove),
"Unexpcted tested_elem_to_remove param");

const test_name = `Pointer events from ${pointer_type} `+
`received before/after ${tested_elem_to_remove} removal `+
`at ${remover_event}`;

promise_test(async test => {
event_log = [];
elem_to_remove = (tested_elem_to_remove == "slot" ? slot : slotted_child);

restoreChildInSlot();
child.addEventListener(remover_event, removeChildFromSlot,
{ once: true });
// TODO(mustaq@chromium.org): It would be more robust if we could remove
// the event listener above through `test.add_cleanup()` but strangely the
// cleanup call fails after the test that removes the slotted-child! This
// happens even if we make the shadow DOM construction dynamic inside this
// `promise_test`!!!

let done_click_promise = getEvent("click", done);

let actions = new test_driver.Actions()
.addPointer("TestPointer", pointer_type)
.pointerMove(-30, -30, {origin: shadow_host})
.pointerDown()
.pointerUp()
.pointerMove(30, 30, {origin: shadow_host})
.pointerDown()
.pointerUp()
.pointerMove(0, 0, {origin: done})
.pointerDown()
.pointerUp();

await actions.send();
await done_click_promise;

assert_equals(event_log.toString(), expected_events.toString(),
"events received");
}, test_name);
}

setup();

addPromiseTest(
"pointerdown",
"slot",
["pointerover@child", "pointerenter@parent", "pointerenter@child",
"pointerdown@child", "(child-removed)",
"pointerout@child", "pointerleave@child",
"pointerover@parent", "pointerup@parent",
"pointerdown@parent", "pointerup@parent",
"pointerout@parent", "pointerleave@parent"]
);
addPromiseTest(
"pointerdown",
"slotted-child",
["pointerover@child", "pointerenter@parent", "pointerenter@child",
"pointerdown@child", "(child-removed)",
"pointerover@parent", "pointerup@parent",
"pointerdown@parent", "pointerup@parent",
"pointerout@parent", "pointerleave@parent"]
);
addPromiseTest(
"pointerup",
"slot",
["pointerover@child", "pointerenter@parent", "pointerenter@child",
"pointerdown@child", "pointerup@child", "(child-removed)",
"pointerout@child", "pointerleave@child",
"pointerover@parent", "pointerdown@parent", "pointerup@parent",
"pointerout@parent", "pointerleave@parent"]
);
addPromiseTest(
"pointerup",
"slotted-child",
["pointerover@child", "pointerenter@parent", "pointerenter@child",
"pointerdown@child", "pointerup@child", "(child-removed)",
"pointerover@parent", "pointerdown@parent", "pointerup@parent",
"pointerout@parent", "pointerleave@parent"]
);
</script>
108 changes: 108 additions & 0 deletions pointerevents/pointerevent_to_slotted_target.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<!DOCTYPE HTML>
<link rel="help" href="https://w3c.github.io/pointerevents/#firing-events-using-the-pointerevent-interface">
<title>Enter/leave events fired to parent after child is removed from slot</title>
<meta name="variant" content="?mouse">
<meta name="variant" content="?touch">
<meta name="variant" content="?pen">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="pointerevent_support.js"></script>

<template id="template">
<style>
div {
width: 100px;
height: 100px;
}
</style>
<div id="parent">
<slot id="slot">slot</slot>
</div>
</template>

<style>
div, my-elem {
width: 100px;
height: 100px;
display: block;
}
</style>

<my-elem id="host">
<div id="child">child</div>
</my-elem>
<div id="done">done</div>

<script>
"use strict";

customElements.define(
"my-elem",
class extends HTMLElement {
constructor() {
super();
let content = document.getElementById("template").content;
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.appendChild(content.cloneNode(true));
}
},
);

const pointer_type = location.search.substring(1);

const shadow_host = document.getElementById("host");
const parent = shadow_host.shadowRoot.getElementById("parent");
const slot = parent.firstElementChild;
const slotted_child = document.getElementById("child");
const done = document.getElementById("done");

let event_log = [];

function logEvent(e) {
if (e.eventPhase == e.AT_TARGET) {
event_log.push(e.type + "@" + e.target.id);
}
}

function setup() {
const events = ["pointerover", "pointerout",
"pointerenter", "pointerleave", "pointerdown", "pointerup"];
let targets = [shadow_host, parent, slot, slotted_child];
for (let i = 0; i < targets.length; i++) {
events.forEach(event => targets[i].addEventListener(event, logEvent));
}
}

setup();

promise_test(async test => {
event_log = [];

let done_click_promise = getEvent("click", done);

let actions = new test_driver.Actions()
.addPointer("TestPointer", pointer_type)
.pointerMove(0, 0, {origin: shadow_host})
.pointerDown()
.pointerUp()
.pointerMove(0, 0, {origin: done})
.pointerDown()
.pointerUp();

await actions.send();
await done_click_promise;

const expected_events = [
"pointerover@child",
"pointerenter@host", "pointerenter@parent", "pointerenter@slot", "pointerenter@child",
"pointerdown@child", "pointerup@child",
"pointerout@child",
"pointerleave@child", "pointerleave@slot", "pointerleave@parent", "pointerleave@host"
];
assert_equals(event_log.toString(), expected_events.toString(),
"events received");
}, `Pointer events from ${pointer_type} to slotted element and shadow-host`);
</script>

0 comments on commit 628348f

Please sign in to comment.