Skip to content

Commit 62f2147

Browse files
KhafraDevmarco-ippolito
authored andcommitted
test: update DOM events web platform tests
PR-URL: #54642 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Mattias Buelens <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Ethan Arrowood <[email protected]> Reviewed-By: Chemi Atlow <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 453df77 commit 62f2147

File tree

39 files changed

+2693
-565
lines changed

39 files changed

+2693
-565
lines changed

test/fixtures/wpt/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Last update:
1313
- common: https://github.com/web-platform-tests/wpt/tree/dbd648158d/common
1414
- console: https://github.com/web-platform-tests/wpt/tree/767ae35464/console
1515
- dom/abort: https://github.com/web-platform-tests/wpt/tree/d1f1ecbd52/dom/abort
16-
- dom/events: https://github.com/web-platform-tests/wpt/tree/ab8999891c/dom/events
16+
- dom/events: https://github.com/web-platform-tests/wpt/tree/0a811c5161/dom/events
1717
- encoding: https://github.com/web-platform-tests/wpt/tree/5aa50dd415/encoding
1818
- fetch/data-urls/resources: https://github.com/web-platform-tests/wpt/tree/7c79d998ff/fetch/data-urls/resources
1919
- FileAPI: https://github.com/web-platform-tests/wpt/tree/cceaf3628d/FileAPI

test/fixtures/wpt/dom/events/Event-dispatch-click.html

+56
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,22 @@
8787
child.dispatchEvent(new MouseEvent("click", {bubbles:true}))
8888
}, "pick the first with activation behavior <a href>")
8989

90+
async_test(function(t) {
91+
var input = document.createElement("input")
92+
input.type = "radio"
93+
dump.appendChild(input)
94+
input.onclick = t.step_func(function() {
95+
assert_false(input.checked, "input pre-click must not be triggered")
96+
})
97+
var child = input.appendChild(document.createElement("input"))
98+
child.type = "radio"
99+
child.onclick = t.step_func(function() {
100+
assert_true(child.checked, "child pre-click must be triggered")
101+
})
102+
child.dispatchEvent(new MouseEvent("click", {bubbles:true}))
103+
t.done()
104+
}, "pick the first with activation behavior <input type=radio>")
105+
90106
async_test(function(t) {
91107
var input = document.createElement("input")
92108
input.type = "checkbox"
@@ -173,6 +189,46 @@
173189
t.done()
174190
}, "disabled checkbox still has activation behavior, part 2")
175191

192+
async_test(function(t) {
193+
var state = "start"
194+
195+
var form = document.createElement("form")
196+
form.onsubmit = t.step_func(() => {
197+
if(state == "start" || state == "radio") {
198+
state = "failure"
199+
} else if(state == "form") {
200+
state = "done"
201+
}
202+
return false
203+
})
204+
dump.appendChild(form)
205+
var button = form.appendChild(document.createElement("button"))
206+
button.type = "submit"
207+
var radio = button.appendChild(document.createElement("input"))
208+
radio.type = "radio"
209+
radio.onclick = t.step_func(() => {
210+
if(state == "start") {
211+
assert_unreached()
212+
} else if(state == "radio") {
213+
assert_true(radio.checked)
214+
}
215+
})
216+
radio.disabled = true
217+
radio.click()
218+
assert_equals(state, "start")
219+
220+
state = "radio"
221+
radio.disabled = false
222+
radio.click()
223+
assert_equals(state, "radio")
224+
225+
state = "form"
226+
button.click()
227+
assert_equals(state, "done")
228+
229+
t.done()
230+
}, "disabled radio still has activation behavior")
231+
176232
async_test(function(t) {
177233
var input = document.createElement("input")
178234
input.type = "checkbox"

test/fixtures/wpt/dom/events/Event-dispatch-on-disabled-elements.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<body>
2020
<script>
2121
// HTML elements that can be disabled
22-
const formElements = ["button", "fieldset", "input", "select", "textarea"];
22+
const formElements = ["button", "input", "select", "textarea"];
2323

2424
test(() => {
2525
for (const localName of formElements) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<!DOCTYPE html>
2+
<meta charset=utf-8>
3+
<title> Only one activation behavior is executed during dispatch</title>
4+
<link rel="author" title="Vincent Hilla" href="mailto:[email protected]">
5+
<link rel="help" href="https://dom.spec.whatwg.org/#eventtarget-activation-behavior">
6+
<link rel="help" href="https://dom.spec.whatwg.org/#concept-event-dispatch">
7+
<script src="/resources/testharness.js"></script>
8+
<script src="/resources/testharnessreport.js"></script>
9+
<div id=log></div>
10+
11+
<div id=test_container></div>
12+
13+
<!--
14+
Three classes:
15+
click
16+
Element to be clicked to cause activation behavior
17+
activates
18+
Element that registers the activation behavior
19+
container
20+
Element in which other elements with activation behavior are placed.
21+
We test that those won't be activated too.
22+
-->
23+
<template>
24+
<!--input, change event bubble, so have to check if checked is true-->
25+
<input class="click activates container" type="checkbox" oninput="this.checked ? activated(this) : null">
26+
<input class="click activates container" type="radio" oninput="this.checked ? activated(this) : null">
27+
<form onsubmit="activated(this); return false" class="activates">
28+
<input class="click container" type="submit">
29+
</form>
30+
<form onsubmit="activated(this); return false" class="activates">
31+
<input class="click container" type="image">
32+
</form>
33+
<form onreset="activated(this)" class="activates">
34+
<input class="click container" type="reset">
35+
</form>
36+
<form onsubmit="activated(this); return false" class="activates">
37+
<button class="click container" type="submit"></button>
38+
</form>
39+
<form onreset="activated(this)" class="activates">
40+
<button class="click container" type="reset"></button>
41+
</form>
42+
<a href="#link" class="click container activates"></a>
43+
<area href="#link" class="click container activates">
44+
<details ontoggle="activated(this)" class="activates">
45+
<summary class="click container"></summary>
46+
</details>
47+
<label>
48+
<input type=checkbox onclick="this.checked ? activated(this) : null" class="activates">
49+
<span class="click container">label</span>
50+
</label>
51+
<!--activation behavior of label for event targeted at interactive content descendant is to do nothing-->
52+
<label class="container">
53+
<button class="click" type="button"></button>
54+
</label>
55+
</template>
56+
57+
<script>
58+
let activations = [];
59+
function activated(e) {
60+
activations.push(e);
61+
}
62+
63+
function getActivations(testidx) {
64+
return activations.filter(a =>
65+
(a.endsWith && a.endsWith("test"+testidx+"_link"))
66+
|| (a.classList && a.classList.contains("test"+testidx))
67+
);
68+
}
69+
70+
// for a and area elements
71+
window.onhashchange = function(e) {
72+
if (e.newURL.endsWith("link")) {
73+
activated(e.newURL);
74+
}
75+
window.location.hash = "";
76+
};
77+
78+
function getElementsByClassNameInclusive(e, clsname) {
79+
let ls = Array.from(e.getElementsByClassName(clsname));
80+
if (e.classList.contains(clsname)) ls.push(e);
81+
return ls;
82+
}
83+
84+
function getClickTarget(e) {
85+
return getElementsByClassNameInclusive(e, "click")[0];
86+
}
87+
88+
function getContainer(e) {
89+
return getElementsByClassNameInclusive(e, "container")[0];
90+
}
91+
92+
function getExpectedActivations(e) {
93+
let ls = getElementsByClassNameInclusive(e, "activates");
94+
95+
// special case, for a and area the window registers the activation
96+
// have to use string, as testrunner cannot stringify the window object
97+
ls = ls.map(e => e.tagName === "A" || e.tagName === "AREA" ? e.href : e);
98+
99+
return ls;
100+
}
101+
102+
function toString(e) {
103+
const children = Array.from(e.children);
104+
const childstr = (children.map(toString)).join("");
105+
const tag = e.tagName;
106+
const typestr = e.type ? " type="+e.type : "";
107+
return `<${tag}${typestr}>${childstr}</${tag}>`;
108+
}
109+
110+
// generate O(n^2) test combinations
111+
const template = document.querySelector("template");
112+
const elements = Array.from(template.content.children);
113+
const tests = []
114+
for (const target of elements) {
115+
for (const parent of elements) {
116+
if (target === parent) continue;
117+
tests.push([target.cloneNode(true), parent.cloneNode(true)])
118+
}
119+
}
120+
121+
const test_container = document.getElementById("test_container");
122+
123+
/**
124+
* Test that if two elements in an event target chain have activation behavior,
125+
* only one of them will be activated.
126+
*
127+
* Each child of <template> represents one case of activation behavior.
128+
* The behavior should be triggered by clicking the element of class click
129+
* and will manifest as a call to activated().
130+
*
131+
* For each [target, parent] in tests, we make target a descendant of parent
132+
* and test that only target gets activated when dispatching a click.
133+
*/
134+
for (let i = 0; i < tests.length; i++) {
135+
let [target, parent] = tests[i];
136+
async_test(function(t) {
137+
let test = document.createElement("div");
138+
test_container.appendChild(test);
139+
test.appendChild(parent);
140+
getContainer(parent).appendChild(target);
141+
142+
// for later filtering out the activations belonging to this test
143+
for (let e of test.getElementsByClassName("activates")) {
144+
e.classList.add("test"+i);
145+
}
146+
for (let e of test.querySelectorAll("a, area")) {
147+
e.href = "#test"+i+"_link";
148+
}
149+
150+
getClickTarget(target).click();
151+
152+
// Need to spin event loop twice, as some clicks might dispatch another task
153+
t.step_timeout(() => {
154+
t.step_timeout(t.step_func_done(() => {
155+
assert_array_equals(getActivations(i), getExpectedActivations(target));
156+
}), 0);
157+
}, 0);
158+
159+
t.add_cleanup(function() {
160+
test_container.removeChild(test);
161+
});
162+
}, `When clicking child ${toString(target)} of parent ${toString(parent)}, only child should be activated.`);
163+
}
164+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<!DOCTYPE html>
2+
<script src="/resources/testharness.js"></script>
3+
<script src="/resources/testharnessreport.js"></script>
4+
<body>
5+
<script>
6+
function createIframe(t, srcdoc = '') {
7+
let iframe = document.createElement('iframe');
8+
iframe.srcdoc = srcdoc;
9+
t.add_cleanup(() => iframe.remove());
10+
return new Promise((resolve, reject) => {
11+
iframe.addEventListener('load', () => resolve(iframe.contentWindow));
12+
document.body.appendChild(iframe);
13+
});
14+
}
15+
16+
// Returns a promise which will resolve with the next error event fired at any
17+
// of `windows`, after the invocation of this function. Once one does, this
18+
// function removes its listeners and produces that error event so that it can
19+
// be examined (most notably for which global proxy it was targeted at).
20+
async function nextErrorEvent(windows) {
21+
let listener;
22+
let p = new Promise((resolve, reject) => {
23+
listener = (event) => { resolve(event); event.preventDefault(); };
24+
});
25+
for (let w of windows) {
26+
w.addEventListener('error', listener);
27+
}
28+
try {
29+
return await p;
30+
} finally {
31+
for (let w of windows) {
32+
w.removeEventListener('error', listener);
33+
}
34+
}
35+
}
36+
37+
promise_test(async t => {
38+
let w = await createIframe(t, `<script>function listener() { throw new Error(); }<`+`/script>`);
39+
let w2 = await createIframe(t);
40+
41+
let target = new w2.EventTarget();
42+
target.addEventListener('party', w.listener);
43+
let nextErrorPromise = nextErrorEvent([self, w, w2]);
44+
target.dispatchEvent(new Event('party'));
45+
let errorEvent = await nextErrorPromise;
46+
if (errorEvent.error) {
47+
assert_true(errorEvent.error instanceof w.Error, 'error should be an instance created inside the listener function');
48+
}
49+
assert_equals(errorEvent.target, w, `error event should target listener's global but instead targets ${event.currentTarget === w2 ? 'target\'s global' : 'test harness global'}`);
50+
}, 'exception thrown in event listener function should result in error event on listener\'s global');
51+
52+
promise_test(async t => {
53+
let w = await createIframe(t, `<script>listener = {};<`+`/script>`);
54+
let w2 = await createIframe(t, `<script>handleEvent = () => { throw new Error; };<`+`/script>`);
55+
let w3 = await createIframe(t);
56+
w.listener.handleEvent = w2.handleEvent;
57+
58+
let target = new w3.EventTarget();
59+
target.addEventListener('party', w.listener);
60+
let nextErrorPromise = nextErrorEvent([self, w, w2, w3]);
61+
target.dispatchEvent(new Event('party'));
62+
let errorEvent = await nextErrorPromise;
63+
if (errorEvent.error) {
64+
assert_true(errorEvent.error instanceof w2.Error, 'error should be an instance created inside the listener function');
65+
}
66+
assert_equals(errorEvent.target, w, `error event should target listener's global but instead targets ${event.currentTarget === w2 ? 'target\'s global' : event.currentTarget === w3 ? 'function\'s global' : 'test harness global'}`);
67+
}, 'exception thrown in event listener interface object should result in error event on listener\'s global');
68+
</script>
69+
</body>

test/fixtures/wpt/dom/events/EventTarget-constructible.any.js

+17
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,23 @@ test(() => {
2323
assert_equals(callCount, 2);
2424
}, "A constructed EventTarget can be used as expected");
2525

26+
test(() => {
27+
const target = new EventTarget();
28+
const event = new Event("foo");
29+
30+
function listener(e) {
31+
assert_equals(e, event);
32+
assert_equals(e.target, target);
33+
assert_equals(e.currentTarget, target);
34+
assert_array_equals(e.composedPath(), [target]);
35+
}
36+
target.addEventListener("foo", listener, { once: true });
37+
target.dispatchEvent(event);
38+
assert_equals(event.target, target);
39+
assert_equals(event.currentTarget, null);
40+
assert_array_equals(event.composedPath(), []);
41+
}, "A constructed EventTarget implements dispatch correctly");
42+
2643
test(() => {
2744
class NicerEventTarget extends EventTarget {
2845
on(...args) {

test/fixtures/wpt/dom/events/event-global.html

+10
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,14 @@
114114

115115
target.dispatchEvent(new Event("click"));
116116
}, "window.event is set to the current event, which is the event passed to dispatch");
117+
118+
async_test(t => {
119+
let target = new XMLHttpRequest();
120+
121+
target.onload = t.step_func_done(e => {
122+
assert_equals(e, window.event);
123+
});
124+
125+
target.dispatchEvent(new Event("load"));
126+
}, "window.event is set to the current event, which is the event passed to dispatch (2)");
117127
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE html>
2+
<link rel="help" href="https://crbug.com/341104769">
3+
<script src="/resources/testharness.js"></script>
4+
<script src="/resources/testharnessreport.js"></script>
5+
<script src="/resources/testdriver.js"></script>
6+
<script src="/resources/testdriver-actions.js"></script>
7+
<script src="/resources/testdriver-vendor.js"></script>
8+
9+
<template>
10+
<p>TEST</p>
11+
</template>
12+
13+
<body>
14+
<script>
15+
const clone = document.querySelector("template").content.cloneNode(true);
16+
const p = clone.querySelector("p");
17+
18+
let gotEvent = false;
19+
p.addEventListener("pointerup", () => {
20+
gotEvent = true;
21+
});
22+
23+
document.body.append(clone);
24+
25+
promise_test(async () => {
26+
await test_driver.click(document.querySelector("p"));
27+
assert_true(gotEvent);
28+
}, "Moving a node to new document should move the registered event listeners together");
29+
</script>

0 commit comments

Comments
 (0)