• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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