• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals
2
3'use strict';
4
5const common = require('../common');
6const { ok, strictEqual, deepStrictEqual, throws } = require('node:assert');
7const { inspect } = require('node:util');
8const { Event, EventTarget, CustomEvent } = require('internal/event_target');
9
10{
11  ok(CustomEvent);
12
13  // Default string
14  const tag = Object.prototype.toString.call(new CustomEvent('$'));
15  strictEqual(tag, '[object CustomEvent]');
16}
17
18{
19  // No argument behavior - throw TypeError
20  throws(() => {
21    new CustomEvent();
22  }, TypeError);
23
24  throws(() => new CustomEvent(Symbol()), TypeError);
25
26  // Too many arguments passed behavior - ignore additional arguments
27  const ev = new CustomEvent('foo', {}, {});
28  strictEqual(ev.type, 'foo');
29}
30
31{
32  const ev = new CustomEvent('$');
33  strictEqual(ev.type, '$');
34  strictEqual(ev.bubbles, false);
35  strictEqual(ev.cancelable, false);
36  strictEqual(ev.detail, null);
37}
38
39{
40  // Coercion to string works
41  strictEqual(new CustomEvent(1).type, '1');
42  strictEqual(new CustomEvent(false).type, 'false');
43  strictEqual(new CustomEvent({}).type, String({}));
44}
45
46{
47  const ev = new CustomEvent('$', {
48    detail: 56,
49    sweet: 'x',
50    cancelable: true,
51  });
52  strictEqual(ev.type, '$');
53  strictEqual(ev.bubbles, false);
54  strictEqual(ev.cancelable, true);
55  strictEqual(ev.sweet, undefined);
56  strictEqual(ev.detail, 56);
57}
58
59{
60  // Any types of value for `detail` are acceptable.
61  ['foo', 1, false, [], {}].forEach((i) => {
62    const ev = new CustomEvent('$', { detail: i });
63    strictEqual(ev.detail, i);
64  });
65}
66
67{
68  // Readonly `detail` behavior
69  const ev = new CustomEvent('$', {
70    detail: 56,
71  });
72  strictEqual(ev.detail, 56);
73  try {
74    ev.detail = 96;
75    // eslint-disable-next-line no-unused-vars
76  } catch (error) {
77    common.mustCall()();
78  }
79  strictEqual(ev.detail, 56);
80}
81
82{
83  const ev = new Event('$', {
84    detail: 96,
85  });
86  strictEqual(ev.detail, undefined);
87}
88
89// The following tests verify whether CustomEvent works the same as Event
90// except carrying custom data. They're based on `parallel/test-eventtarget.js`.
91
92{
93  const ev = new CustomEvent('$');
94  strictEqual(ev.type, '$');
95  strictEqual(ev.bubbles, false);
96  strictEqual(ev.cancelable, false);
97  strictEqual(ev.detail, null);
98
99  strictEqual(ev.defaultPrevented, false);
100  strictEqual(typeof ev.timeStamp, 'number');
101
102  // Compatibility properties with the DOM
103  deepStrictEqual(ev.composedPath(), []);
104  strictEqual(ev.returnValue, true);
105  strictEqual(ev.composed, false);
106  strictEqual(ev.isTrusted, false);
107  strictEqual(ev.eventPhase, 0);
108  strictEqual(ev.cancelBubble, false);
109
110  // Not cancelable
111  ev.preventDefault();
112  strictEqual(ev.defaultPrevented, false);
113}
114
115{
116  // Invalid options
117  ['foo', 1, false].forEach((i) =>
118    throws(() => new CustomEvent('foo', i), {
119      code: 'ERR_INVALID_ARG_TYPE',
120      name: 'TypeError',
121      message:
122        'The "options" argument must be of type object.' +
123        common.invalidArgTypeHelper(i),
124    }),
125  );
126}
127
128{
129  const ev = new CustomEvent('$');
130  strictEqual(ev.constructor.name, 'CustomEvent');
131
132  // CustomEvent Statics
133  strictEqual(CustomEvent.NONE, 0);
134  strictEqual(CustomEvent.CAPTURING_PHASE, 1);
135  strictEqual(CustomEvent.AT_TARGET, 2);
136  strictEqual(CustomEvent.BUBBLING_PHASE, 3);
137  strictEqual(new CustomEvent('foo').eventPhase, CustomEvent.NONE);
138
139  // CustomEvent is a function
140  strictEqual(CustomEvent.length, 1);
141}
142
143{
144  const ev = new CustomEvent('foo');
145  strictEqual(ev.cancelBubble, false);
146  ev.cancelBubble = true;
147  strictEqual(ev.cancelBubble, true);
148}
149{
150  const ev = new CustomEvent('foo');
151  strictEqual(ev.cancelBubble, false);
152  ev.stopPropagation();
153  strictEqual(ev.cancelBubble, true);
154}
155{
156  const ev = new CustomEvent('foo');
157  strictEqual(ev.cancelBubble, false);
158  ev.cancelBubble = 'some-truthy-value';
159  strictEqual(ev.cancelBubble, true);
160}
161{
162  const ev = new CustomEvent('foo');
163  strictEqual(ev.cancelBubble, false);
164  ev.cancelBubble = true;
165  strictEqual(ev.cancelBubble, true);
166}
167{
168  const ev = new CustomEvent('foo');
169  strictEqual(ev.cancelBubble, false);
170  ev.stopPropagation();
171  strictEqual(ev.cancelBubble, true);
172}
173{
174  const ev = new CustomEvent('foo');
175  strictEqual(ev.cancelBubble, false);
176  ev.cancelBubble = 'some-truthy-value';
177  strictEqual(ev.cancelBubble, true);
178}
179{
180  const ev = new CustomEvent('foo', { cancelable: true });
181  strictEqual(ev.type, 'foo');
182  strictEqual(ev.cancelable, true);
183  strictEqual(ev.defaultPrevented, false);
184
185  ev.preventDefault();
186  strictEqual(ev.defaultPrevented, true);
187}
188{
189  const ev = new CustomEvent('foo');
190  strictEqual(ev.isTrusted, false);
191}
192
193// Works with EventTarget
194
195{
196  const obj = { sweet: 'x', memory: { x: 56, y: 96 } };
197  const et = new EventTarget();
198  const ev = new CustomEvent('$', { detail: obj });
199  const fn = common.mustCall((event) => {
200    strictEqual(event, ev);
201    deepStrictEqual(event.detail, obj);
202  });
203  et.addEventListener('$', fn);
204  et.dispatchEvent(ev);
205}
206
207{
208  const eventTarget = new EventTarget();
209  const event = new CustomEvent('$');
210  eventTarget.dispatchEvent(event);
211  strictEqual(event.target, eventTarget);
212}
213
214{
215  const obj = { sweet: 'x' };
216  const eventTarget = new EventTarget();
217
218  const ev1 = common.mustCall(function(event) {
219    strictEqual(event.type, 'foo');
220    strictEqual(event.detail, obj);
221    strictEqual(this, eventTarget);
222    strictEqual(event.eventPhase, 2);
223  }, 2);
224
225  const ev2 = {
226    handleEvent: common.mustCall(function(event) {
227      strictEqual(event.type, 'foo');
228      strictEqual(event.detail, obj);
229      strictEqual(this, ev2);
230    }),
231  };
232
233  eventTarget.addEventListener('foo', ev1);
234  eventTarget.addEventListener('foo', ev2, { once: true });
235  ok(eventTarget.dispatchEvent(new CustomEvent('foo', { detail: obj })));
236  eventTarget.dispatchEvent(new CustomEvent('foo', { detail: obj }));
237
238  eventTarget.removeEventListener('foo', ev1);
239  eventTarget.dispatchEvent(new CustomEvent('foo'));
240}
241
242{
243  // Same event dispatched multiple times.
244  const obj = { sweet: 'x' };
245  const event = new CustomEvent('foo', { detail: obj });
246  const eventTarget1 = new EventTarget();
247  const eventTarget2 = new EventTarget();
248
249  eventTarget1.addEventListener(
250    'foo',
251    common.mustCall((event) => {
252      strictEqual(event.eventPhase, CustomEvent.AT_TARGET);
253      strictEqual(event.target, eventTarget1);
254      strictEqual(event.detail, obj);
255      deepStrictEqual(event.composedPath(), [eventTarget1]);
256    }),
257  );
258
259  eventTarget2.addEventListener(
260    'foo',
261    common.mustCall((event) => {
262      strictEqual(event.eventPhase, CustomEvent.AT_TARGET);
263      strictEqual(event.target, eventTarget2);
264      strictEqual(event.detail, obj);
265      deepStrictEqual(event.composedPath(), [eventTarget2]);
266    }),
267  );
268
269  eventTarget1.dispatchEvent(event);
270  strictEqual(event.eventPhase, CustomEvent.NONE);
271  strictEqual(event.target, eventTarget1);
272  deepStrictEqual(event.composedPath(), []);
273
274  eventTarget2.dispatchEvent(event);
275  strictEqual(event.eventPhase, CustomEvent.NONE);
276  strictEqual(event.target, eventTarget2);
277  deepStrictEqual(event.composedPath(), []);
278}
279
280{
281  const obj = { sweet: 'x' };
282  const target = new EventTarget();
283  const event = new CustomEvent('foo', { detail: obj });
284
285  strictEqual(event.target, null);
286
287  target.addEventListener(
288    'foo',
289    common.mustCall((event) => {
290      strictEqual(event.target, target);
291      strictEqual(event.currentTarget, target);
292      strictEqual(event.srcElement, target);
293      strictEqual(event.detail, obj);
294    }),
295  );
296  target.dispatchEvent(event);
297}
298
299{
300  // Event subclassing
301  const SubEvent = class extends CustomEvent {};
302  const ev = new SubEvent('foo', { detail: 56 });
303  const eventTarget = new EventTarget();
304  const fn = common.mustCall((event) => {
305    strictEqual(event, ev);
306    strictEqual(event.detail, 56);
307  });
308  eventTarget.addEventListener('foo', fn, { once: true });
309  eventTarget.dispatchEvent(ev);
310}
311
312// Works with inspect
313
314{
315  const ev = new CustomEvent('test');
316  const evConstructorName = inspect(ev, {
317    depth: -1,
318  });
319  strictEqual(evConstructorName, 'CustomEvent');
320
321  const inspectResult = inspect(ev, {
322    depth: 1,
323  });
324  ok(inspectResult.includes('CustomEvent'));
325}
326