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