1// Flags: --expose-internals --no-warnings 2'use strict'; 3 4const common = require('../common'); 5const { 6 Event, 7 EventTarget, 8 defineEventHandler 9} = require('internal/event_target'); 10 11const { 12 ok, 13 deepStrictEqual, 14 strictEqual, 15 throws, 16} = require('assert'); 17 18const { once } = require('events'); 19 20const { promisify } = require('util'); 21const delay = promisify(setTimeout); 22 23// The globals are defined. 24ok(Event); 25ok(EventTarget); 26 27// The warning event has special behavior regarding attaching listeners 28let lastWarning; 29process.on('warning', (e) => { 30 lastWarning = e; 31}); 32 33// Utility promise for parts of the test that need to wait for eachother - 34// Namely tests for warning events 35/* eslint-disable no-unused-vars */ 36let asyncTest = Promise.resolve(); 37 38// First, test Event 39{ 40 const ev = new Event('foo'); 41 strictEqual(ev.type, 'foo'); 42 strictEqual(ev.cancelable, false); 43 strictEqual(ev.defaultPrevented, false); 44 strictEqual(typeof ev.timeStamp, 'number'); 45 46 // Compatibility properties with the DOM 47 deepStrictEqual(ev.composedPath(), []); 48 strictEqual(ev.returnValue, true); 49 strictEqual(ev.bubbles, false); 50 strictEqual(ev.composed, false); 51 strictEqual(ev.isTrusted, false); 52 strictEqual(ev.eventPhase, 0); 53 strictEqual(ev.cancelBubble, false); 54 55 // Not cancelable 56 ev.preventDefault(); 57 strictEqual(ev.defaultPrevented, false); 58} 59{ 60 [ 61 'foo', 62 1, 63 false, 64 ].forEach((i) => ( 65 throws(() => new Event('foo', i), { 66 code: 'ERR_INVALID_ARG_TYPE', 67 name: 'TypeError', 68 message: 'The "options" argument must be of type object.' + 69 common.invalidArgTypeHelper(i) 70 }) 71 )); 72} 73{ 74 const ev = new Event('foo'); 75 strictEqual(ev.cancelBubble, false); 76 ev.cancelBubble = true; 77 strictEqual(ev.cancelBubble, true); 78} 79{ 80 const ev = new Event('foo'); 81 strictEqual(ev.cancelBubble, false); 82 ev.stopPropagation(); 83 strictEqual(ev.cancelBubble, true); 84} 85{ 86 const ev = new Event('foo'); 87 strictEqual(ev.cancelBubble, false); 88 ev.cancelBubble = 'some-truthy-value'; 89 strictEqual(ev.cancelBubble, true); 90} 91{ 92 // No argument behavior - throw TypeError 93 throws(() => { 94 new Event(); 95 }, TypeError); 96 // Too many arguments passed behavior - ignore additional arguments 97 const ev = new Event('foo', {}, {}); 98 strictEqual(ev.type, 'foo'); 99} 100{ 101 const ev = new Event('foo'); 102 strictEqual(ev.cancelBubble, false); 103 ev.cancelBubble = true; 104 strictEqual(ev.cancelBubble, true); 105} 106{ 107 const ev = new Event('foo'); 108 strictEqual(ev.cancelBubble, false); 109 ev.stopPropagation(); 110 strictEqual(ev.cancelBubble, true); 111} 112{ 113 const ev = new Event('foo'); 114 strictEqual(ev.cancelBubble, false); 115 ev.cancelBubble = 'some-truthy-value'; 116 strictEqual(ev.cancelBubble, true); 117} 118{ 119 const ev = new Event('foo', { cancelable: true }); 120 strictEqual(ev.type, 'foo'); 121 strictEqual(ev.cancelable, true); 122 strictEqual(ev.defaultPrevented, false); 123 124 ev.preventDefault(); 125 strictEqual(ev.defaultPrevented, true); 126 throws(() => new Event(Symbol()), TypeError); 127} 128{ 129 const ev = new Event('foo'); 130 deepStrictEqual(Object.keys(ev), ['isTrusted']); 131} 132{ 133 const eventTarget = new EventTarget(); 134 135 const ev1 = common.mustCall(function(event) { 136 strictEqual(event.type, 'foo'); 137 strictEqual(this, eventTarget); 138 strictEqual(event.eventPhase, 2); 139 }, 2); 140 141 const ev2 = { 142 handleEvent: common.mustCall(function(event) { 143 strictEqual(event.type, 'foo'); 144 strictEqual(this, ev2); 145 }) 146 }; 147 148 eventTarget.addEventListener('foo', ev1); 149 eventTarget.addEventListener('foo', ev2, { once: true }); 150 ok(eventTarget.dispatchEvent(new Event('foo'))); 151 eventTarget.dispatchEvent(new Event('foo')); 152 153 eventTarget.removeEventListener('foo', ev1); 154 eventTarget.dispatchEvent(new Event('foo')); 155} 156{ 157 // event subclassing 158 const SubEvent = class extends Event {}; 159 const ev = new SubEvent('foo'); 160 const eventTarget = new EventTarget(); 161 const fn = common.mustCall((event) => strictEqual(event, ev)); 162 eventTarget.addEventListener('foo', fn, { once: true }); 163 eventTarget.dispatchEvent(ev); 164} 165 166{ 167 const eventTarget = new EventTarget(); 168 const event = new Event('foo', { cancelable: true }); 169 eventTarget.addEventListener('foo', (event) => event.preventDefault()); 170 ok(!eventTarget.dispatchEvent(event)); 171} 172{ 173 // Adding event listeners with a boolean useCapture 174 const eventTarget = new EventTarget(); 175 const event = new Event('foo'); 176 const fn = common.mustCall((event) => strictEqual(event.type, 'foo')); 177 eventTarget.addEventListener('foo', fn, false); 178 eventTarget.dispatchEvent(event); 179} 180 181{ 182 // The `options` argument can be `null`. 183 const eventTarget = new EventTarget(); 184 const event = new Event('foo'); 185 const fn = common.mustCall((event) => strictEqual(event.type, 'foo')); 186 eventTarget.addEventListener('foo', fn, null); 187 eventTarget.dispatchEvent(event); 188} 189 190{ 191 const uncaughtException = common.mustCall((err, event) => { 192 strictEqual(err.message, 'boom'); 193 strictEqual(event.type, 'foo'); 194 }, 4); 195 196 // Whether or not the handler function is async or not, errors 197 // are routed to uncaughtException 198 process.on('error', uncaughtException); 199 200 const eventTarget = new EventTarget(); 201 202 const ev1 = async () => { throw new Error('boom'); }; 203 const ev2 = () => { throw new Error('boom'); }; 204 const ev3 = { handleEvent() { throw new Error('boom'); } }; 205 const ev4 = { async handleEvent() { throw new Error('boom'); } }; 206 207 // Errors in a handler won't stop calling the others. 208 eventTarget.addEventListener('foo', ev1, { once: true }); 209 eventTarget.addEventListener('foo', ev2, { once: true }); 210 eventTarget.addEventListener('foo', ev3, { once: true }); 211 eventTarget.addEventListener('foo', ev4, { once: true }); 212 213 eventTarget.dispatchEvent(new Event('foo')); 214} 215 216{ 217 const eventTarget = new EventTarget(); 218 219 // Once handler only invoked once 220 const ev = common.mustCall((event) => { 221 // Can invoke the same event name recursively 222 eventTarget.dispatchEvent(new Event('foo')); 223 }); 224 225 // Errors in a handler won't stop calling the others. 226 eventTarget.addEventListener('foo', ev, { once: true }); 227 228 eventTarget.dispatchEvent(new Event('foo')); 229} 230 231{ 232 // Coercion to string works 233 strictEqual((new Event(1)).type, '1'); 234 strictEqual((new Event(false)).type, 'false'); 235 strictEqual((new Event({})).type, String({})); 236 237 const target = new EventTarget(); 238 239 [ 240 'foo', 241 {}, // No type event 242 undefined, 243 1, 244 false, 245 ].forEach((i) => { 246 throws(() => target.dispatchEvent(i), { 247 code: 'ERR_INVALID_ARG_TYPE', 248 name: 'TypeError', 249 message: 'The "event" argument must be an instance of Event.' + 250 common.invalidArgTypeHelper(i) 251 }); 252 }); 253 254 const err = (arg) => ({ 255 code: 'ERR_INVALID_ARG_TYPE', 256 name: 'TypeError', 257 message: 'The "listener" argument must be an instance of EventListener.' + 258 common.invalidArgTypeHelper(arg) 259 }); 260 261 [ 262 'foo', 263 1, 264 {}, // No handleEvent function 265 false, 266 ].forEach((i) => throws(() => target.addEventListener('foo', i), err(i))); 267} 268 269{ 270 const target = new EventTarget(); 271 once(target, 'foo').then(common.mustCall()); 272 target.dispatchEvent(new Event('foo')); 273} 274 275{ 276 const target = new EventTarget(); 277 const event = new Event('foo'); 278 event.stopImmediatePropagation(); 279 target.addEventListener('foo', common.mustNotCall()); 280 target.dispatchEvent(event); 281} 282 283{ 284 const target = new EventTarget(); 285 const event = new Event('foo'); 286 target.addEventListener('foo', common.mustCall((event) => { 287 event.stopImmediatePropagation(); 288 })); 289 target.addEventListener('foo', common.mustNotCall()); 290 target.dispatchEvent(event); 291} 292 293{ 294 const target = new EventTarget(); 295 const event = new Event('foo'); 296 target.addEventListener('foo', common.mustCall((event) => { 297 event.stopImmediatePropagation(); 298 })); 299 target.addEventListener('foo', common.mustNotCall()); 300 target.dispatchEvent(event); 301} 302 303{ 304 const target = new EventTarget(); 305 const event = new Event('foo'); 306 strictEqual(event.target, null); 307 target.addEventListener('foo', common.mustCall((event) => { 308 strictEqual(event.target, target); 309 strictEqual(event.currentTarget, target); 310 strictEqual(event.srcElement, target); 311 })); 312 target.dispatchEvent(event); 313} 314 315{ 316 const target1 = new EventTarget(); 317 const target2 = new EventTarget(); 318 const event = new Event('foo'); 319 target1.addEventListener('foo', common.mustCall((event) => { 320 throws(() => target2.dispatchEvent(event), { 321 code: 'ERR_EVENT_RECURSION' 322 }); 323 })); 324 target1.dispatchEvent(event); 325} 326 327{ 328 const target = new EventTarget(); 329 const a = common.mustCall(() => target.removeEventListener('foo', a)); 330 const b = common.mustCall(2); 331 332 target.addEventListener('foo', a); 333 target.addEventListener('foo', b); 334 335 target.dispatchEvent(new Event('foo')); 336 target.dispatchEvent(new Event('foo')); 337} 338 339{ 340 const target = new EventTarget(); 341 const a = common.mustCall(3); 342 343 target.addEventListener('foo', a, { capture: true }); 344 target.addEventListener('foo', a, { capture: false }); 345 346 target.dispatchEvent(new Event('foo')); 347 target.removeEventListener('foo', a, { capture: true }); 348 target.dispatchEvent(new Event('foo')); 349 target.removeEventListener('foo', a, { capture: false }); 350 target.dispatchEvent(new Event('foo')); 351} 352{ 353 const target = new EventTarget(); 354 strictEqual(target.toString(), '[object EventTarget]'); 355 const event = new Event(''); 356 strictEqual(event.toString(), '[object Event]'); 357} 358{ 359 const target = new EventTarget(); 360 defineEventHandler(target, 'foo'); 361 target.onfoo = common.mustCall(); 362 target.dispatchEvent(new Event('foo')); 363} 364{ 365 const target = new EventTarget(); 366 defineEventHandler(target, 'foo'); 367 let count = 0; 368 target.onfoo = () => count++; 369 target.onfoo = common.mustCall(() => count++); 370 target.dispatchEvent(new Event('foo')); 371 strictEqual(count, 1); 372} 373{ 374 const target = new EventTarget(); 375 defineEventHandler(target, 'foo'); 376 let count = 0; 377 target.addEventListener('foo', () => count++); 378 target.onfoo = common.mustCall(() => count++); 379 target.dispatchEvent(new Event('foo')); 380 strictEqual(count, 2); 381} 382{ 383 const target = new EventTarget(); 384 defineEventHandler(target, 'foo'); 385 const fn = common.mustNotCall(); 386 target.onfoo = fn; 387 strictEqual(target.onfoo, fn); 388 target.onfoo = null; 389 target.dispatchEvent(new Event('foo')); 390} 391 392{ 393 // `this` value of dispatchEvent 394 const target = new EventTarget(); 395 const target2 = new EventTarget(); 396 const event = new Event('foo'); 397 398 ok(target.dispatchEvent.call(target2, event)); 399 400 [ 401 'foo', 402 {}, 403 [], 404 1, 405 null, 406 undefined, 407 false, 408 Symbol(), 409 /a/, 410 ].forEach((i) => { 411 throws(() => target.dispatchEvent.call(i, event), { 412 code: 'ERR_INVALID_THIS' 413 }); 414 }); 415} 416 417{ 418 // Event Statics 419 strictEqual(Event.NONE, 0); 420 strictEqual(Event.CAPTURING_PHASE, 1); 421 strictEqual(Event.AT_TARGET, 2); 422 strictEqual(Event.BUBBLING_PHASE, 3); 423 strictEqual(new Event('foo').eventPhase, Event.NONE); 424 const target = new EventTarget(); 425 target.addEventListener('foo', common.mustCall((e) => { 426 strictEqual(e.eventPhase, Event.AT_TARGET); 427 }), { once: true }); 428 target.dispatchEvent(new Event('foo')); 429 // Event is a function 430 strictEqual(Event.length, 1); 431} 432 433{ 434 const target = new EventTarget(); 435 const ev = new Event('toString'); 436 const fn = common.mustCall((event) => strictEqual(event.type, 'toString')); 437 target.addEventListener('toString', fn); 438 target.dispatchEvent(ev); 439} 440{ 441 const target = new EventTarget(); 442 const ev = new Event('__proto__'); 443 const fn = common.mustCall((event) => strictEqual(event.type, '__proto__')); 444 target.addEventListener('__proto__', fn); 445 target.dispatchEvent(ev); 446} 447 448{ 449 const eventTarget = new EventTarget(); 450 // Single argument throws 451 throws(() => eventTarget.addEventListener('foo'), TypeError); 452 // Null events - does not throw 453 eventTarget.addEventListener('foo', null); 454 eventTarget.removeEventListener('foo', null); 455 eventTarget.addEventListener('foo', undefined); 456 eventTarget.removeEventListener('foo', undefined); 457 // Strings, booleans 458 throws(() => eventTarget.addEventListener('foo', 'hello'), TypeError); 459 throws(() => eventTarget.addEventListener('foo', false), TypeError); 460 throws(() => eventTarget.addEventListener('foo', Symbol()), TypeError); 461 asyncTest = asyncTest.then(async () => { 462 const eventTarget = new EventTarget(); 463 // Single argument throws 464 throws(() => eventTarget.addEventListener('foo'), TypeError); 465 // Null events - does not throw 466 467 eventTarget.addEventListener('foo', null); 468 eventTarget.removeEventListener('foo', null); 469 470 // Warnings always happen after nextTick, so wait for a timer of 0 471 await delay(0); 472 strictEqual(lastWarning.name, 'AddEventListenerArgumentTypeWarning'); 473 strictEqual(lastWarning.target, eventTarget); 474 lastWarning = null; 475 eventTarget.addEventListener('foo', undefined); 476 await delay(0); 477 strictEqual(lastWarning.name, 'AddEventListenerArgumentTypeWarning'); 478 strictEqual(lastWarning.target, eventTarget); 479 eventTarget.removeEventListener('foo', undefined); 480 // Strings, booleans 481 throws(() => eventTarget.addEventListener('foo', 'hello'), TypeError); 482 throws(() => eventTarget.addEventListener('foo', false), TypeError); 483 throws(() => eventTarget.addEventListener('foo', Symbol()), TypeError); 484 }); 485} 486{ 487 const eventTarget = new EventTarget(); 488 const event = new Event('foo'); 489 eventTarget.dispatchEvent(event); 490 strictEqual(event.target, eventTarget); 491} 492{ 493 // Event target exported keys 494 const eventTarget = new EventTarget(); 495 deepStrictEqual(Object.keys(eventTarget), []); 496 deepStrictEqual(Object.getOwnPropertyNames(eventTarget), []); 497 const parentKeys = Object.keys(Object.getPrototypeOf(eventTarget)).sort(); 498 const keys = ['addEventListener', 'dispatchEvent', 'removeEventListener']; 499 deepStrictEqual(parentKeys, keys); 500} 501{ 502 // Subclassing 503 class SubTarget extends EventTarget {} 504 const target = new SubTarget(); 505 target.addEventListener('foo', common.mustCall()); 506 target.dispatchEvent(new Event('foo')); 507} 508{ 509 // Test event order 510 const target = new EventTarget(); 511 let state = 0; 512 target.addEventListener('foo', common.mustCall(() => { 513 strictEqual(state, 0); 514 state++; 515 })); 516 target.addEventListener('foo', common.mustCall(() => { 517 strictEqual(state, 1); 518 })); 519 target.dispatchEvent(new Event('foo')); 520} 521{ 522 const target = new EventTarget(); 523 defineEventHandler(target, 'foo'); 524 const descriptor = Object.getOwnPropertyDescriptor(target, 'onfoo'); 525 strictEqual(descriptor.configurable, true); 526 strictEqual(descriptor.enumerable, true); 527} 528{ 529 const target = new EventTarget(); 530 defineEventHandler(target, 'foo'); 531 const output = []; 532 target.addEventListener('foo', () => output.push(1)); 533 target.onfoo = common.mustNotCall(); 534 target.addEventListener('foo', () => output.push(3)); 535 target.onfoo = () => output.push(2); 536 target.addEventListener('foo', () => output.push(4)); 537 target.dispatchEvent(new Event('foo')); 538 deepStrictEqual(output, [1, 2, 3, 4]); 539} 540