1// Flags: --expose-internals --no-warnings --expose-gc 2'use strict'; 3 4const common = require('../common'); 5const { 6 defineEventHandler, 7 kWeakHandler, 8} = require('internal/event_target'); 9 10const { 11 ok, 12 deepStrictEqual, 13 strictEqual, 14 throws, 15} = require('assert'); 16 17const { once } = require('events'); 18 19const { inspect } = require('util'); 20const { setTimeout: delay } = require('timers/promises'); 21 22// The globals are defined. 23ok(Event); 24ok(EventTarget); 25 26// The warning event has special behavior regarding attaching listeners 27let lastWarning; 28process.on('warning', (e) => { 29 lastWarning = e; 30}); 31 32// Utility promise for parts of the test that need to wait for eachother - 33// Namely tests for warning events 34/* eslint-disable no-unused-vars */ 35let asyncTest = Promise.resolve(); 36 37// First, test Event 38{ 39 const ev = new Event('foo'); 40 strictEqual(ev.type, 'foo'); 41 strictEqual(ev.cancelable, false); 42 strictEqual(ev.defaultPrevented, false); 43 strictEqual(typeof ev.timeStamp, 'number'); 44 45 // Compatibility properties with the DOM 46 deepStrictEqual(ev.composedPath(), []); 47 strictEqual(ev.returnValue, true); 48 strictEqual(ev.bubbles, false); 49 strictEqual(ev.composed, false); 50 strictEqual(ev.isTrusted, false); 51 strictEqual(ev.eventPhase, 0); 52 strictEqual(ev.cancelBubble, false); 53 54 // Not cancelable 55 ev.preventDefault(); 56 strictEqual(ev.defaultPrevented, false); 57} 58{ 59 [ 60 'foo', 61 1, 62 false, 63 ].forEach((i) => ( 64 throws(() => new Event('foo', i), { 65 code: 'ERR_INVALID_ARG_TYPE', 66 name: 'TypeError', 67 message: 'The "options" argument must be of type object.' + 68 common.invalidArgTypeHelper(i), 69 }) 70 )); 71} 72{ 73 const ev = new Event('foo'); 74 strictEqual(ev.cancelBubble, false); 75 ev.cancelBubble = true; 76 strictEqual(ev.cancelBubble, true); 77} 78{ 79 const ev = new Event('foo'); 80 strictEqual(ev.cancelBubble, false); 81 ev.stopPropagation(); 82 strictEqual(ev.cancelBubble, true); 83} 84{ 85 const ev = new Event('foo'); 86 strictEqual(ev.cancelBubble, false); 87 ev.cancelBubble = 'some-truthy-value'; 88 strictEqual(ev.cancelBubble, true); 89} 90{ 91 // No argument behavior - throw TypeError 92 throws(() => { 93 new Event(); 94 }, TypeError); 95 // Too many arguments passed behavior - ignore additional arguments 96 const ev = new Event('foo', {}, {}); 97 strictEqual(ev.type, 'foo'); 98} 99{ 100 const ev = new Event('foo'); 101 strictEqual(ev.cancelBubble, false); 102 ev.cancelBubble = true; 103 strictEqual(ev.cancelBubble, true); 104} 105{ 106 const ev = new Event('foo'); 107 strictEqual(ev.cancelBubble, false); 108 ev.stopPropagation(); 109 strictEqual(ev.cancelBubble, true); 110} 111{ 112 const ev = new Event('foo'); 113 strictEqual(ev.cancelBubble, false); 114 ev.cancelBubble = 'some-truthy-value'; 115 strictEqual(ev.cancelBubble, true); 116} 117{ 118 const ev = new Event('foo', { cancelable: true }); 119 strictEqual(ev.type, 'foo'); 120 strictEqual(ev.cancelable, true); 121 strictEqual(ev.defaultPrevented, false); 122 123 ev.preventDefault(); 124 strictEqual(ev.defaultPrevented, true); 125 throws(() => new Event(Symbol()), TypeError); 126} 127{ 128 const ev = new Event('foo'); 129 strictEqual(ev.isTrusted, false); 130} 131{ 132 const eventTarget = new EventTarget(); 133 134 const ev1 = common.mustCall(function(event) { 135 strictEqual(event.type, 'foo'); 136 strictEqual(this, eventTarget); 137 strictEqual(event.eventPhase, 2); 138 }, 2); 139 140 const ev2 = { 141 handleEvent: common.mustCall(function(event) { 142 strictEqual(event.type, 'foo'); 143 strictEqual(this, ev2); 144 }), 145 }; 146 147 eventTarget.addEventListener('foo', ev1); 148 eventTarget.addEventListener('foo', ev2, { once: true }); 149 ok(eventTarget.dispatchEvent(new Event('foo'))); 150 eventTarget.dispatchEvent(new Event('foo')); 151 152 eventTarget.removeEventListener('foo', ev1); 153 eventTarget.dispatchEvent(new Event('foo')); 154} 155{ 156 // event subclassing 157 const SubEvent = class extends Event {}; 158 const ev = new SubEvent('foo'); 159 const eventTarget = new EventTarget(); 160 const fn = common.mustCall((event) => strictEqual(event, ev)); 161 eventTarget.addEventListener('foo', fn, { once: true }); 162 eventTarget.dispatchEvent(ev); 163} 164 165{ 166 // Same event dispatched multiple times. 167 const event = new Event('foo'); 168 const eventTarget1 = new EventTarget(); 169 const eventTarget2 = new EventTarget(); 170 171 eventTarget1.addEventListener('foo', common.mustCall((event) => { 172 strictEqual(event.eventPhase, Event.AT_TARGET); 173 strictEqual(event.target, eventTarget1); 174 deepStrictEqual(event.composedPath(), [eventTarget1]); 175 })); 176 177 eventTarget2.addEventListener('foo', common.mustCall((event) => { 178 strictEqual(event.eventPhase, Event.AT_TARGET); 179 strictEqual(event.target, eventTarget2); 180 deepStrictEqual(event.composedPath(), [eventTarget2]); 181 })); 182 183 eventTarget1.dispatchEvent(event); 184 strictEqual(event.eventPhase, Event.NONE); 185 strictEqual(event.target, eventTarget1); 186 deepStrictEqual(event.composedPath(), []); 187 188 189 eventTarget2.dispatchEvent(event); 190 strictEqual(event.eventPhase, Event.NONE); 191 strictEqual(event.target, eventTarget2); 192 deepStrictEqual(event.composedPath(), []); 193} 194{ 195 // Same event dispatched multiple times, without listeners added. 196 const event = new Event('foo'); 197 const eventTarget1 = new EventTarget(); 198 const eventTarget2 = new EventTarget(); 199 200 eventTarget1.dispatchEvent(event); 201 strictEqual(event.eventPhase, Event.NONE); 202 strictEqual(event.target, eventTarget1); 203 deepStrictEqual(event.composedPath(), []); 204 205 eventTarget2.dispatchEvent(event); 206 strictEqual(event.eventPhase, Event.NONE); 207 strictEqual(event.target, eventTarget2); 208 deepStrictEqual(event.composedPath(), []); 209} 210 211{ 212 const eventTarget = new EventTarget(); 213 const event = new Event('foo', { cancelable: true }); 214 eventTarget.addEventListener('foo', (event) => event.preventDefault()); 215 ok(!eventTarget.dispatchEvent(event)); 216} 217{ 218 // Adding event listeners with a boolean useCapture 219 const eventTarget = new EventTarget(); 220 const event = new Event('foo'); 221 const fn = common.mustCall((event) => strictEqual(event.type, 'foo')); 222 eventTarget.addEventListener('foo', fn, false); 223 eventTarget.dispatchEvent(event); 224} 225 226{ 227 // The `options` argument can be `null`. 228 const eventTarget = new EventTarget(); 229 const event = new Event('foo'); 230 const fn = common.mustCall((event) => strictEqual(event.type, 'foo')); 231 eventTarget.addEventListener('foo', fn, null); 232 eventTarget.dispatchEvent(event); 233} 234 235{ 236 const target = new EventTarget(); 237 const listener = {}; 238 // AddEventListener should not require handleEvent to be 239 // defined on an EventListener. 240 target.addEventListener('foo', listener); 241 listener.handleEvent = common.mustCall(function(event) { 242 strictEqual(event.type, 'foo'); 243 strictEqual(this, listener); 244 }); 245 target.dispatchEvent(new Event('foo')); 246} 247 248{ 249 const target = new EventTarget(); 250 const listener = {}; 251 // do not throw 252 target.removeEventListener('foo', listener); 253 target.addEventListener('foo', listener); 254 target.removeEventListener('foo', listener); 255 listener.handleEvent = common.mustNotCall(); 256 target.dispatchEvent(new Event('foo')); 257} 258 259{ 260 const uncaughtException = common.mustCall((err, origin) => { 261 strictEqual(err.message, 'boom'); 262 strictEqual(origin, 'uncaughtException'); 263 }, 4); 264 265 // Make sure that we no longer call 'error' on error. 266 process.on('error', common.mustNotCall()); 267 // Don't call rejection even for async handlers. 268 process.on('unhandledRejection', common.mustNotCall()); 269 process.on('uncaughtException', uncaughtException); 270 271 const eventTarget = new EventTarget(); 272 273 const ev1 = async () => { throw new Error('boom'); }; 274 const ev2 = () => { throw new Error('boom'); }; 275 const ev3 = { handleEvent() { throw new Error('boom'); } }; 276 const ev4 = { async handleEvent() { throw new Error('boom'); } }; 277 278 // Errors in a handler won't stop calling the others. 279 eventTarget.addEventListener('foo', ev1, { once: true }); 280 eventTarget.addEventListener('foo', ev2, { once: true }); 281 eventTarget.addEventListener('foo', ev3, { once: true }); 282 eventTarget.addEventListener('foo', ev4, { once: true }); 283 284 eventTarget.dispatchEvent(new Event('foo')); 285} 286 287{ 288 const eventTarget = new EventTarget(); 289 290 // Once handler only invoked once 291 const ev = common.mustCall((event) => { 292 // Can invoke the same event name recursively 293 eventTarget.dispatchEvent(new Event('foo')); 294 }); 295 296 // Errors in a handler won't stop calling the others. 297 eventTarget.addEventListener('foo', ev, { once: true }); 298 299 eventTarget.dispatchEvent(new Event('foo')); 300} 301 302{ 303 // Coercion to string works 304 strictEqual((new Event(1)).type, '1'); 305 strictEqual((new Event(false)).type, 'false'); 306 strictEqual((new Event({})).type, String({})); 307 308 const target = new EventTarget(); 309 310 [ 311 'foo', 312 {}, // No type event 313 undefined, 314 1, 315 false, 316 ].forEach((i) => { 317 throws(() => target.dispatchEvent(i), { 318 code: 'ERR_INVALID_ARG_TYPE', 319 name: 'TypeError', 320 message: 'The "event" argument must be an instance of Event.' + 321 common.invalidArgTypeHelper(i), 322 }); 323 }); 324 325 const err = (arg) => ({ 326 code: 'ERR_INVALID_ARG_TYPE', 327 name: 'TypeError', 328 message: 'The "listener" argument must be an instance of EventListener.' + 329 common.invalidArgTypeHelper(arg), 330 }); 331 332 [ 333 'foo', 334 1, 335 false, 336 ].forEach((i) => throws(() => target.addEventListener('foo', i), err(i))); 337} 338 339{ 340 const target = new EventTarget(); 341 once(target, 'foo').then(common.mustCall()); 342 target.dispatchEvent(new Event('foo')); 343} 344 345{ 346 const target = new EventTarget(); 347 const event = new Event('foo'); 348 event.stopImmediatePropagation(); 349 target.addEventListener('foo', common.mustNotCall()); 350 target.dispatchEvent(event); 351} 352 353{ 354 const target = new EventTarget(); 355 const event = new Event('foo'); 356 target.addEventListener('foo', common.mustCall((event) => { 357 event.stopImmediatePropagation(); 358 })); 359 target.addEventListener('foo', common.mustNotCall()); 360 target.dispatchEvent(event); 361} 362 363{ 364 const target = new EventTarget(); 365 const event = new Event('foo'); 366 target.addEventListener('foo', common.mustCall((event) => { 367 event.stopImmediatePropagation(); 368 })); 369 target.addEventListener('foo', common.mustNotCall()); 370 target.dispatchEvent(event); 371} 372 373{ 374 const target = new EventTarget(); 375 const event = new Event('foo'); 376 strictEqual(event.target, null); 377 target.addEventListener('foo', common.mustCall((event) => { 378 strictEqual(event.target, target); 379 strictEqual(event.currentTarget, target); 380 strictEqual(event.srcElement, target); 381 })); 382 target.dispatchEvent(event); 383} 384 385{ 386 const target1 = new EventTarget(); 387 const target2 = new EventTarget(); 388 const event = new Event('foo'); 389 target1.addEventListener('foo', common.mustCall((event) => { 390 throws(() => target2.dispatchEvent(event), { 391 code: 'ERR_EVENT_RECURSION', 392 }); 393 })); 394 target1.dispatchEvent(event); 395} 396 397{ 398 const target = new EventTarget(); 399 const a = common.mustCall(() => target.removeEventListener('foo', a)); 400 const b = common.mustCall(2); 401 402 target.addEventListener('foo', a); 403 target.addEventListener('foo', b); 404 405 target.dispatchEvent(new Event('foo')); 406 target.dispatchEvent(new Event('foo')); 407} 408 409{ 410 const target = new EventTarget(); 411 const a = common.mustCall(3); 412 413 target.addEventListener('foo', a, { capture: true }); 414 target.addEventListener('foo', a, { capture: false }); 415 416 target.dispatchEvent(new Event('foo')); 417 target.removeEventListener('foo', a, { capture: true }); 418 target.dispatchEvent(new Event('foo')); 419 target.removeEventListener('foo', a, { capture: false }); 420 target.dispatchEvent(new Event('foo')); 421} 422{ 423 const target = new EventTarget(); 424 strictEqual(target.toString(), '[object EventTarget]'); 425 const event = new Event(''); 426 strictEqual(event.toString(), '[object Event]'); 427} 428{ 429 const target = new EventTarget(); 430 defineEventHandler(target, 'foo'); 431 target.onfoo = common.mustCall(); 432 target.dispatchEvent(new Event('foo')); 433} 434 435{ 436 const target = new EventTarget(); 437 defineEventHandler(target, 'foo'); 438 strictEqual(target.onfoo, null); 439} 440 441{ 442 const target = new EventTarget(); 443 defineEventHandler(target, 'foo'); 444 let count = 0; 445 target.onfoo = () => count++; 446 target.onfoo = common.mustCall(() => count++); 447 target.dispatchEvent(new Event('foo')); 448 strictEqual(count, 1); 449} 450{ 451 const target = new EventTarget(); 452 defineEventHandler(target, 'foo'); 453 let count = 0; 454 target.addEventListener('foo', () => count++); 455 target.onfoo = common.mustCall(() => count++); 456 target.dispatchEvent(new Event('foo')); 457 strictEqual(count, 2); 458} 459{ 460 const target = new EventTarget(); 461 defineEventHandler(target, 'foo'); 462 const fn = common.mustNotCall(); 463 target.onfoo = fn; 464 strictEqual(target.onfoo, fn); 465 target.onfoo = null; 466 target.dispatchEvent(new Event('foo')); 467} 468 469{ 470 // `this` value of dispatchEvent 471 const target = new EventTarget(); 472 const target2 = new EventTarget(); 473 const event = new Event('foo'); 474 475 ok(target.dispatchEvent.call(target2, event)); 476 477 [ 478 'foo', 479 {}, 480 [], 481 1, 482 null, 483 undefined, 484 false, 485 Symbol(), 486 /a/, 487 ].forEach((i) => { 488 throws(() => target.dispatchEvent.call(i, event), { 489 code: 'ERR_INVALID_THIS', 490 }); 491 }); 492} 493 494{ 495 // Event Statics 496 strictEqual(Event.NONE, 0); 497 strictEqual(Event.CAPTURING_PHASE, 1); 498 strictEqual(Event.AT_TARGET, 2); 499 strictEqual(Event.BUBBLING_PHASE, 3); 500 strictEqual(new Event('foo').eventPhase, Event.NONE); 501 const target = new EventTarget(); 502 target.addEventListener('foo', common.mustCall((e) => { 503 strictEqual(e.eventPhase, Event.AT_TARGET); 504 }), { once: true }); 505 target.dispatchEvent(new Event('foo')); 506 // Event is a function 507 strictEqual(Event.length, 1); 508} 509 510{ 511 const target = new EventTarget(); 512 const ev = new Event('toString'); 513 const fn = common.mustCall((event) => strictEqual(event.type, 'toString')); 514 target.addEventListener('toString', fn); 515 target.dispatchEvent(ev); 516} 517{ 518 const target = new EventTarget(); 519 const ev = new Event('__proto__'); 520 const fn = common.mustCall((event) => strictEqual(event.type, '__proto__')); 521 target.addEventListener('__proto__', fn); 522 target.dispatchEvent(ev); 523} 524 525{ 526 const eventTarget = new EventTarget(); 527 // Single argument throws 528 throws(() => eventTarget.addEventListener('foo'), TypeError); 529 // Null events - does not throw 530 eventTarget.addEventListener('foo', null); 531 eventTarget.removeEventListener('foo', null); 532 eventTarget.addEventListener('foo', undefined); 533 eventTarget.removeEventListener('foo', undefined); 534 // Strings, booleans 535 throws(() => eventTarget.addEventListener('foo', 'hello'), TypeError); 536 throws(() => eventTarget.addEventListener('foo', false), TypeError); 537 throws(() => eventTarget.addEventListener('foo', Symbol()), TypeError); 538 asyncTest = asyncTest.then(async () => { 539 const eventTarget = new EventTarget(); 540 // Single argument throws 541 throws(() => eventTarget.addEventListener('foo'), TypeError); 542 // Null events - does not throw 543 544 eventTarget.addEventListener('foo', null); 545 eventTarget.removeEventListener('foo', null); 546 547 // Warnings always happen after nextTick, so wait for a timer of 0 548 await delay(0); 549 strictEqual(lastWarning.name, 'AddEventListenerArgumentTypeWarning'); 550 strictEqual(lastWarning.target, eventTarget); 551 lastWarning = null; 552 eventTarget.addEventListener('foo', undefined); 553 await delay(0); 554 strictEqual(lastWarning.name, 'AddEventListenerArgumentTypeWarning'); 555 strictEqual(lastWarning.target, eventTarget); 556 eventTarget.removeEventListener('foo', undefined); 557 // Strings, booleans 558 throws(() => eventTarget.addEventListener('foo', 'hello'), TypeError); 559 throws(() => eventTarget.addEventListener('foo', false), TypeError); 560 throws(() => eventTarget.addEventListener('foo', Symbol()), TypeError); 561 }); 562} 563{ 564 const eventTarget = new EventTarget(); 565 const event = new Event('foo'); 566 eventTarget.dispatchEvent(event); 567 strictEqual(event.target, eventTarget); 568} 569{ 570 // Event target exported keys 571 const eventTarget = new EventTarget(); 572 deepStrictEqual(Object.keys(eventTarget), []); 573 deepStrictEqual(Object.getOwnPropertyNames(eventTarget), []); 574 const parentKeys = Object.keys(Object.getPrototypeOf(eventTarget)).sort(); 575 const keys = ['addEventListener', 'dispatchEvent', 'removeEventListener']; 576 deepStrictEqual(parentKeys, keys); 577} 578{ 579 // Subclassing 580 class SubTarget extends EventTarget {} 581 const target = new SubTarget(); 582 target.addEventListener('foo', common.mustCall()); 583 target.dispatchEvent(new Event('foo')); 584} 585{ 586 // Test event order 587 const target = new EventTarget(); 588 let state = 0; 589 target.addEventListener('foo', common.mustCall(() => { 590 strictEqual(state, 0); 591 state++; 592 })); 593 target.addEventListener('foo', common.mustCall(() => { 594 strictEqual(state, 1); 595 })); 596 target.dispatchEvent(new Event('foo')); 597} 598{ 599 const target = new EventTarget(); 600 defineEventHandler(target, 'foo'); 601 const descriptor = Object.getOwnPropertyDescriptor(target, 'onfoo'); 602 strictEqual(descriptor.configurable, true); 603 strictEqual(descriptor.enumerable, true); 604} 605{ 606 const target = new EventTarget(); 607 defineEventHandler(target, 'foo'); 608 const output = []; 609 target.addEventListener('foo', () => output.push(1)); 610 target.onfoo = common.mustNotCall(); 611 target.addEventListener('foo', () => output.push(3)); 612 target.onfoo = () => output.push(2); 613 target.addEventListener('foo', () => output.push(4)); 614 target.dispatchEvent(new Event('foo')); 615 deepStrictEqual(output, [1, 2, 3, 4]); 616} 617{ 618 const et = new EventTarget(); 619 const listener = common.mustNotCall(); 620 et.addEventListener('foo', common.mustCall((e) => { 621 et.removeEventListener('foo', listener); 622 })); 623 et.addEventListener('foo', listener); 624 et.dispatchEvent(new Event('foo')); 625} 626 627{ 628 const ev = new Event('test'); 629 const evConstructorName = inspect(ev, { 630 depth: -1, 631 }); 632 strictEqual(evConstructorName, 'Event'); 633 634 const inspectResult = inspect(ev, { 635 depth: 1, 636 }); 637 ok(inspectResult.includes('Event')); 638} 639 640{ 641 const et = new EventTarget(); 642 const inspectResult = inspect(et, { 643 depth: 1, 644 }); 645 ok(inspectResult.includes('EventTarget')); 646} 647 648{ 649 const ev = new Event('test'); 650 strictEqual(ev.constructor.name, 'Event'); 651 652 const et = new EventTarget(); 653 strictEqual(et.constructor.name, 'EventTarget'); 654} 655{ 656 // Weak event listeners work 657 const et = new EventTarget(); 658 const listener = common.mustCall(); 659 et.addEventListener('foo', listener, { [kWeakHandler]: et }); 660 et.dispatchEvent(new Event('foo')); 661} 662{ 663 // Weak event listeners can be removed and weakness is not part of the key 664 const et = new EventTarget(); 665 const listener = common.mustNotCall(); 666 et.addEventListener('foo', listener, { [kWeakHandler]: et }); 667 et.removeEventListener('foo', listener); 668 et.dispatchEvent(new Event('foo')); 669} 670{ 671 // Test listeners are held weakly 672 const et = new EventTarget(); 673 et.addEventListener('foo', common.mustNotCall(), { [kWeakHandler]: {} }); 674 setImmediate(() => { 675 global.gc(); 676 et.dispatchEvent(new Event('foo')); 677 }); 678} 679 680{ 681 const et = new EventTarget(); 682 683 throws(() => et.addEventListener(), { 684 code: 'ERR_MISSING_ARGS', 685 name: 'TypeError', 686 }); 687 688 throws(() => et.addEventListener('foo'), { 689 code: 'ERR_MISSING_ARGS', 690 name: 'TypeError', 691 }); 692 693 throws(() => et.removeEventListener(), { 694 code: 'ERR_MISSING_ARGS', 695 name: 'TypeError', 696 }); 697 698 throws(() => et.removeEventListener('foo'), { 699 code: 'ERR_MISSING_ARGS', 700 name: 'TypeError', 701 }); 702 703 throws(() => et.dispatchEvent(), { 704 code: 'ERR_MISSING_ARGS', 705 name: 'TypeError', 706 }); 707} 708 709{ 710 const et = new EventTarget(); 711 712 throws(() => { 713 et.addEventListener(Symbol('symbol'), () => {}); 714 }, TypeError); 715 716 throws(() => { 717 et.removeEventListener(Symbol('symbol'), () => {}); 718 }, TypeError); 719} 720 721{ 722 // Test that event listeners are removed by signal even when 723 // signal's abort event propagation stopped 724 const controller = new AbortController(); 725 const { signal } = controller; 726 signal.addEventListener('abort', (e) => e.stopImmediatePropagation(), { once: true }); 727 const et = new EventTarget(); 728 et.addEventListener('foo', common.mustNotCall(), { signal }); 729 controller.abort(); 730 et.dispatchEvent(new Event('foo')); 731} 732