Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add WPT tests for dom/events #43151

Merged
merged 3 commits into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions test/fixtures/wpt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Last update:
- common: https://github.com/web-platform-tests/wpt/tree/03c5072aff/common
- console: https://github.com/web-platform-tests/wpt/tree/3b1f72e99a/console
- dom/abort: https://github.com/web-platform-tests/wpt/tree/c49cafb491/dom/abort
- dom/events: https://github.com/web-platform-tests/wpt/tree/f8821adb28/dom/events
- encoding: https://github.com/web-platform-tests/wpt/tree/35f70910d3/encoding
- FileAPI: https://github.com/web-platform-tests/wpt/tree/3b279420d4/FileAPI
- hr-time: https://github.com/web-platform-tests/wpt/tree/9910784394/hr-time
Expand Down
96 changes: 96 additions & 0 deletions test/fixtures/wpt/dom/events/AddEventListenerOptions-once.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// META: title=AddEventListenerOptions.once

"use strict";

test(function() {
var invoked_once = false;
var invoked_normal = false;
function handler_once() {
invoked_once = true;
}
function handler_normal() {
invoked_normal = true;
}

const et = new EventTarget();
et.addEventListener('test', handler_once, {once: true});
et.addEventListener('test', handler_normal);
et.dispatchEvent(new Event('test'));
assert_equals(invoked_once, true, "Once handler should be invoked");
assert_equals(invoked_normal, true, "Normal handler should be invoked");

invoked_once = false;
invoked_normal = false;
et.dispatchEvent(new Event('test'));
assert_equals(invoked_once, false, "Once handler shouldn't be invoked again");
assert_equals(invoked_normal, true, "Normal handler should be invoked again");
et.removeEventListener('test', handler_normal);
}, "Once listener should be invoked only once");

test(function() {
const et = new EventTarget();
var invoked_count = 0;
function handler() {
invoked_count++;
if (invoked_count == 1)
et.dispatchEvent(new Event('test'));
}
et.addEventListener('test', handler, {once: true});
et.dispatchEvent(new Event('test'));
assert_equals(invoked_count, 1, "Once handler should only be invoked once");

invoked_count = 0;
function handler2() {
invoked_count++;
if (invoked_count == 1)
et.addEventListener('test', handler2, {once: true});
if (invoked_count <= 2)
et.dispatchEvent(new Event('test'));
}
et.addEventListener('test', handler2, {once: true});
et.dispatchEvent(new Event('test'));
assert_equals(invoked_count, 2, "Once handler should only be invoked once after each adding");
}, "Once listener should be invoked only once even if the event is nested");

test(function() {
var invoked_count = 0;
function handler() {
invoked_count++;
}

const et = new EventTarget();

et.addEventListener('test', handler, {once: true});
et.addEventListener('test', handler);
et.dispatchEvent(new Event('test'));
assert_equals(invoked_count, 1, "The handler should only be added once");

invoked_count = 0;
et.dispatchEvent(new Event('test'));
assert_equals(invoked_count, 0, "The handler was added as a once listener");

invoked_count = 0;
et.addEventListener('test', handler, {once: true});
et.removeEventListener('test', handler);
et.dispatchEvent(new Event('test'));
assert_equals(invoked_count, 0, "The handler should have been removed");
}, "Once listener should be added / removed like normal listeners");

test(function() {
const et = new EventTarget();

var invoked_count = 0;

for (let n = 4; n > 0; n--) {
et.addEventListener('test', (e) => {
invoked_count++;
e.stopImmediatePropagation();
}, {once: true});
}

for (let n = 4; n > 0; n--) {
et.dispatchEvent(new Event('test'));
}

assert_equals(invoked_count, 4, "The listeners should be invoked");
}, "Multiple once listeners should be invoked even if the stopImmediatePropagation is set");
134 changes: 134 additions & 0 deletions test/fixtures/wpt/dom/events/AddEventListenerOptions-passive.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// META: title=AddEventListenerOptions.passive

test(function() {
var supportsPassive = false;
var query_options = {
get passive() {
supportsPassive = true;
return false;
},
get dummy() {
assert_unreached("dummy value getter invoked");
return false;
}
};

const et = new EventTarget();
et.addEventListener('test_event', null, query_options);
assert_true(supportsPassive, "addEventListener doesn't support the passive option");

supportsPassive = false;
et.removeEventListener('test_event', null, query_options);
assert_false(supportsPassive, "removeEventListener supports the passive option when it should not");
}, "Supports passive option on addEventListener only");

function testPassiveValue(optionsValue, expectedDefaultPrevented, existingEventTarget) {
var defaultPrevented = undefined;
var handler = function handler(e) {
assert_false(e.defaultPrevented, "Event prematurely marked defaultPrevented");
e.preventDefault();
defaultPrevented = e.defaultPrevented;
}
const et = existingEventTarget || new EventTarget();
et.addEventListener('test', handler, optionsValue);
var uncanceled = et.dispatchEvent(new Event('test', {bubbles: true, cancelable: true}));

assert_equals(defaultPrevented, expectedDefaultPrevented, "Incorrect defaultPrevented for options: " + JSON.stringify(optionsValue));
assert_equals(uncanceled, !expectedDefaultPrevented, "Incorrect return value from dispatchEvent");

et.removeEventListener('test', handler, optionsValue);
}

test(function() {
testPassiveValue(undefined, true);
testPassiveValue({}, true);
testPassiveValue({passive: false}, true);
testPassiveValue({passive: true}, false);
testPassiveValue({passive: 0}, true);
testPassiveValue({passive: 1}, false);
}, "preventDefault should be ignored if-and-only-if the passive option is true");

function testPassiveValueOnReturnValue(test, optionsValue, expectedDefaultPrevented) {
var defaultPrevented = undefined;
var handler = test.step_func(e => {
assert_false(e.defaultPrevented, "Event prematurely marked defaultPrevented");
e.returnValue = false;
defaultPrevented = e.defaultPrevented;
});
const et = new EventTarget();
et.addEventListener('test', handler, optionsValue);
var uncanceled = et.dispatchEvent(new Event('test', {bubbles: true, cancelable: true}));

assert_equals(defaultPrevented, expectedDefaultPrevented, "Incorrect defaultPrevented for options: " + JSON.stringify(optionsValue));
assert_equals(uncanceled, !expectedDefaultPrevented, "Incorrect return value from dispatchEvent");

et.removeEventListener('test', handler, optionsValue);
}

async_test(t => {
testPassiveValueOnReturnValue(t, undefined, true);
testPassiveValueOnReturnValue(t, {}, true);
testPassiveValueOnReturnValue(t, {passive: false}, true);
testPassiveValueOnReturnValue(t, {passive: true}, false);
testPassiveValueOnReturnValue(t, {passive: 0}, true);
testPassiveValueOnReturnValue(t, {passive: 1}, false);
t.done();
}, "returnValue should be ignored if-and-only-if the passive option is true");

function testPassiveWithOtherHandlers(optionsValue, expectedDefaultPrevented) {
var handlerInvoked1 = false;
var dummyHandler1 = function() {
handlerInvoked1 = true;
};
var handlerInvoked2 = false;
var dummyHandler2 = function() {
handlerInvoked2 = true;
};

const et = new EventTarget();
et.addEventListener('test', dummyHandler1, {passive:true});
et.addEventListener('test', dummyHandler2);

testPassiveValue(optionsValue, expectedDefaultPrevented, et);

assert_true(handlerInvoked1, "Extra passive handler not invoked");
assert_true(handlerInvoked2, "Extra non-passive handler not invoked");

et.removeEventListener('test', dummyHandler1);
et.removeEventListener('test', dummyHandler2);
}

test(function() {
testPassiveWithOtherHandlers({}, true);
testPassiveWithOtherHandlers({passive: false}, true);
testPassiveWithOtherHandlers({passive: true}, false);
}, "passive behavior of one listener should be unaffected by the presence of other listeners");

function testOptionEquivalence(optionValue1, optionValue2, expectedEquality) {
var invocationCount = 0;
var handler = function handler(e) {
invocationCount++;
}
const et = new EventTarget();
et.addEventListener('test', handler, optionValue1);
et.addEventListener('test', handler, optionValue2);
et.dispatchEvent(new Event('test', {bubbles: true}));
assert_equals(invocationCount, expectedEquality ? 1 : 2, "equivalence of options " +
JSON.stringify(optionValue1) + " and " + JSON.stringify(optionValue2));
et.removeEventListener('test', handler, optionValue1);
et.removeEventListener('test', handler, optionValue2);
}

test(function() {
// Sanity check options that should be treated as distinct handlers
testOptionEquivalence({capture:true}, {capture:false, passive:false}, false);
testOptionEquivalence({capture:true}, {passive:true}, false);

// Option values that should be treated as equivalent
testOptionEquivalence({}, {passive:false}, true);
testOptionEquivalence({passive:true}, {passive:false}, true);
testOptionEquivalence(undefined, {passive:true}, true);
testOptionEquivalence({capture: true, passive: false}, {capture: true, passive: true}, true);

}, "Equivalence of option values");

143 changes: 143 additions & 0 deletions test/fixtures/wpt/dom/events/AddEventListenerOptions-signal.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
'use strict';

test(function() {
let count = 0;
function handler() {
count++;
}
const et = new EventTarget();
const controller = new AbortController();
et.addEventListener('test', handler, { signal: controller.signal });
et.dispatchEvent(new Event('test'));
assert_equals(count, 1, "Adding a signal still adds a listener");
et.dispatchEvent(new Event('test'));
assert_equals(count, 2, "The listener was not added with the once flag");
controller.abort();
et.dispatchEvent(new Event('test'));
assert_equals(count, 2, "Aborting on the controller removes the listener");
et.addEventListener('test', handler, { signal: controller.signal });
et.dispatchEvent(new Event('test'));
assert_equals(count, 2, "Passing an aborted signal never adds the handler");
}, "Passing an AbortSignal to addEventListener options should allow removing a listener");

test(function() {
let count = 0;
function handler() {
count++;
}
const et = new EventTarget();
const controller = new AbortController();
et.addEventListener('test', handler, { signal: controller.signal });
et.removeEventListener('test', handler);
et.dispatchEvent(new Event('test'));
assert_equals(count, 0, "The listener was still removed");
}, "Passing an AbortSignal to addEventListener does not prevent removeEventListener");

test(function() {
let count = 0;
function handler() {
count++;
}
const et = new EventTarget();
const controller = new AbortController();
et.addEventListener('test', handler, { signal: controller.signal, once: true });
controller.abort();
et.dispatchEvent(new Event('test'));
assert_equals(count, 0, "The listener was still removed");
}, "Passing an AbortSignal to addEventListener works with the once flag");

test(function() {
let count = 0;
function handler() {
count++;
}
const et = new EventTarget();
const controller = new AbortController();
et.addEventListener('test', handler, { signal: controller.signal, once: true });
et.removeEventListener('test', handler);
et.dispatchEvent(new Event('test'));
assert_equals(count, 0, "The listener was still removed");
}, "Removing a once listener works with a passed signal");

test(function() {
let count = 0;
function handler() {
count++;
}
const et = new EventTarget();
const controller = new AbortController();
et.addEventListener('first', handler, { signal: controller.signal, once: true });
et.addEventListener('second', handler, { signal: controller.signal, once: true });
controller.abort();
et.dispatchEvent(new Event('first'));
et.dispatchEvent(new Event('second'));
assert_equals(count, 0, "The listener was still removed");
}, "Passing an AbortSignal to multiple listeners");

test(function() {
let count = 0;
function handler() {
count++;
}
const et = new EventTarget();
const controller = new AbortController();
et.addEventListener('test', handler, { signal: controller.signal, capture: true });
controller.abort();
et.dispatchEvent(new Event('test'));
assert_equals(count, 0, "The listener was still removed");
}, "Passing an AbortSignal to addEventListener works with the capture flag");

test(function() {
let count = 0;
function handler() {
count++;
}
const et = new EventTarget();
const controller = new AbortController();
et.addEventListener('test', () => {
controller.abort();
}, { signal: controller.signal });
et.addEventListener('test', handler, { signal: controller.signal });
et.dispatchEvent(new Event('test'));
assert_equals(count, 0, "The listener was still removed");
}, "Aborting from a listener does not call future listeners");

test(function() {
let count = 0;
function handler() {
count++;
}
const et = new EventTarget();
const controller = new AbortController();
et.addEventListener('test', () => {
et.addEventListener('test', handler, { signal: controller.signal });
controller.abort();
}, { signal: controller.signal });
et.dispatchEvent(new Event('test'));
assert_equals(count, 0, "The listener was still removed");
}, "Adding then aborting a listener in another listener does not call it");

test(function() {
const et = new EventTarget();
const ac = new AbortController();
let count = 0;
et.addEventListener('foo', () => {
et.addEventListener('foo', () => {
count++;
if (count > 5) ac.abort();
et.dispatchEvent(new Event('foo'));
}, { signal: ac.signal });
et.dispatchEvent(new Event('foo'));
}, { once: true });
et.dispatchEvent(new Event('foo'));
}, "Aborting from a nested listener should remove it");

test(function() {
const et = new EventTarget();
assert_throws_js(TypeError, () => { et.addEventListener("foo", () => {}, { signal: null }); });
}, "Passing null as the signal should throw");

test(function() {
const et = new EventTarget();
assert_throws_js(TypeError, () => { et.addEventListener("foo", null, { signal: null }); });
}, "Passing null as the signal should throw (listener is also null)");