Skip to content

Commit

Permalink
test: add WPT tests for dom/events
Browse files Browse the repository at this point in the history
PR-URL: nodejs/node#43151
Refs: nodejs/node#40678
Reviewed-By: Masashi Hirano <shisama07@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
nissy-dev authored and guangwong committed Oct 10, 2022
1 parent af79bfa commit 15ed34c
Show file tree
Hide file tree
Showing 106 changed files with 6,906 additions and 1 deletion.
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)");
Loading

0 comments on commit 15ed34c

Please sign in to comment.