1// Flags: --expose-internals --no-warnings 2'use strict'; 3 4const common = require('../common'); 5const assert = require('assert'); 6const { on, EventEmitter } = require('events'); 7const { 8 NodeEventTarget, 9} = require('internal/event_target'); 10 11async function basic() { 12 const ee = new EventEmitter(); 13 process.nextTick(() => { 14 ee.emit('foo', 'bar'); 15 // 'bar' is a spurious event, we are testing 16 // that it does not show up in the iterable 17 ee.emit('bar', 24); 18 ee.emit('foo', 42); 19 }); 20 21 const iterable = on(ee, 'foo'); 22 23 const expected = [['bar'], [42]]; 24 25 for await (const event of iterable) { 26 const current = expected.shift(); 27 28 assert.deepStrictEqual(current, event); 29 30 if (expected.length === 0) { 31 break; 32 } 33 } 34 assert.strictEqual(ee.listenerCount('foo'), 0); 35 assert.strictEqual(ee.listenerCount('error'), 0); 36} 37 38async function invalidArgType() { 39 assert.throws(() => on({}, 'foo'), common.expectsError({ 40 code: 'ERR_INVALID_ARG_TYPE', 41 name: 'TypeError', 42 })); 43} 44 45async function error() { 46 const ee = new EventEmitter(); 47 const _err = new Error('kaboom'); 48 process.nextTick(() => { 49 ee.emit('error', _err); 50 }); 51 52 const iterable = on(ee, 'foo'); 53 let looped = false; 54 let thrown = false; 55 56 try { 57 // eslint-disable-next-line no-unused-vars 58 for await (const event of iterable) { 59 looped = true; 60 } 61 } catch (err) { 62 thrown = true; 63 assert.strictEqual(err, _err); 64 } 65 assert.strictEqual(thrown, true); 66 assert.strictEqual(looped, false); 67} 68 69async function errorDelayed() { 70 const ee = new EventEmitter(); 71 const _err = new Error('kaboom'); 72 process.nextTick(() => { 73 ee.emit('foo', 42); 74 ee.emit('error', _err); 75 }); 76 77 const iterable = on(ee, 'foo'); 78 const expected = [[42]]; 79 let thrown = false; 80 81 try { 82 for await (const event of iterable) { 83 const current = expected.shift(); 84 assert.deepStrictEqual(current, event); 85 } 86 } catch (err) { 87 thrown = true; 88 assert.strictEqual(err, _err); 89 } 90 assert.strictEqual(thrown, true); 91 assert.strictEqual(ee.listenerCount('foo'), 0); 92 assert.strictEqual(ee.listenerCount('error'), 0); 93} 94 95async function throwInLoop() { 96 const ee = new EventEmitter(); 97 const _err = new Error('kaboom'); 98 99 process.nextTick(() => { 100 ee.emit('foo', 42); 101 }); 102 103 try { 104 for await (const event of on(ee, 'foo')) { 105 assert.deepStrictEqual(event, [42]); 106 throw _err; 107 } 108 } catch (err) { 109 assert.strictEqual(err, _err); 110 } 111 112 assert.strictEqual(ee.listenerCount('foo'), 0); 113 assert.strictEqual(ee.listenerCount('error'), 0); 114} 115 116async function next() { 117 const ee = new EventEmitter(); 118 const iterable = on(ee, 'foo'); 119 120 process.nextTick(function() { 121 ee.emit('foo', 'bar'); 122 ee.emit('foo', 42); 123 iterable.return(); 124 }); 125 126 const results = await Promise.all([ 127 iterable.next(), 128 iterable.next(), 129 iterable.next(), 130 ]); 131 132 assert.deepStrictEqual(results, [{ 133 value: ['bar'], 134 done: false, 135 }, { 136 value: [42], 137 done: false, 138 }, { 139 value: undefined, 140 done: true, 141 }]); 142 143 assert.deepStrictEqual(await iterable.next(), { 144 value: undefined, 145 done: true, 146 }); 147} 148 149async function nextError() { 150 const ee = new EventEmitter(); 151 const iterable = on(ee, 'foo'); 152 const _err = new Error('kaboom'); 153 process.nextTick(function() { 154 ee.emit('error', _err); 155 }); 156 const results = await Promise.allSettled([ 157 iterable.next(), 158 iterable.next(), 159 iterable.next(), 160 ]); 161 assert.deepStrictEqual(results, [{ 162 status: 'rejected', 163 reason: _err, 164 }, { 165 status: 'fulfilled', 166 value: { 167 value: undefined, 168 done: true, 169 }, 170 }, { 171 status: 'fulfilled', 172 value: { 173 value: undefined, 174 done: true, 175 }, 176 }]); 177 assert.strictEqual(ee.listeners('error').length, 0); 178} 179 180async function iterableThrow() { 181 const ee = new EventEmitter(); 182 const iterable = on(ee, 'foo'); 183 184 process.nextTick(() => { 185 ee.emit('foo', 'bar'); 186 ee.emit('foo', 42); // lost in the queue 187 iterable.throw(_err); 188 }); 189 190 const _err = new Error('kaboom'); 191 let thrown = false; 192 193 assert.throws(() => { 194 // No argument 195 iterable.throw(); 196 }, { 197 message: 'The "EventEmitter.AsyncIterator" property must be' + 198 ' an instance of Error. Received undefined', 199 name: 'TypeError', 200 }); 201 202 const expected = [['bar'], [42]]; 203 204 try { 205 for await (const event of iterable) { 206 assert.deepStrictEqual(event, expected.shift()); 207 } 208 } catch (err) { 209 thrown = true; 210 assert.strictEqual(err, _err); 211 } 212 assert.strictEqual(thrown, true); 213 assert.strictEqual(expected.length, 0); 214 assert.strictEqual(ee.listenerCount('foo'), 0); 215 assert.strictEqual(ee.listenerCount('error'), 0); 216} 217 218async function eventTarget() { 219 const et = new EventTarget(); 220 const tick = () => et.dispatchEvent(new Event('tick')); 221 const interval = setInterval(tick, 0); 222 let count = 0; 223 for await (const [ event ] of on(et, 'tick')) { 224 count++; 225 assert.strictEqual(event.type, 'tick'); 226 if (count >= 5) { 227 break; 228 } 229 } 230 assert.strictEqual(count, 5); 231 clearInterval(interval); 232} 233 234async function errorListenerCount() { 235 const et = new EventEmitter(); 236 on(et, 'foo'); 237 assert.strictEqual(et.listenerCount('error'), 1); 238} 239 240async function nodeEventTarget() { 241 const et = new NodeEventTarget(); 242 const tick = () => et.dispatchEvent(new Event('tick')); 243 const interval = setInterval(tick, 0); 244 let count = 0; 245 for await (const [ event] of on(et, 'tick')) { 246 count++; 247 assert.strictEqual(event.type, 'tick'); 248 if (count >= 5) { 249 break; 250 } 251 } 252 assert.strictEqual(count, 5); 253 clearInterval(interval); 254} 255 256async function abortableOnBefore() { 257 const ee = new EventEmitter(); 258 const abortedSignal = AbortSignal.abort(); 259 [1, {}, null, false, 'hi'].forEach((signal) => { 260 assert.throws(() => on(ee, 'foo', { signal }), { 261 code: 'ERR_INVALID_ARG_TYPE', 262 }); 263 }); 264 assert.throws(() => on(ee, 'foo', { signal: abortedSignal }), { 265 name: 'AbortError', 266 }); 267} 268 269async function eventTargetAbortableOnBefore() { 270 const et = new EventTarget(); 271 const abortedSignal = AbortSignal.abort(); 272 [1, {}, null, false, 'hi'].forEach((signal) => { 273 assert.throws(() => on(et, 'foo', { signal }), { 274 code: 'ERR_INVALID_ARG_TYPE', 275 }); 276 }); 277 assert.throws(() => on(et, 'foo', { signal: abortedSignal }), { 278 name: 'AbortError', 279 }); 280} 281 282async function abortableOnAfter() { 283 const ee = new EventEmitter(); 284 const ac = new AbortController(); 285 286 const i = setInterval(() => ee.emit('foo', 'foo'), 10); 287 288 async function foo() { 289 for await (const f of on(ee, 'foo', { signal: ac.signal })) { 290 assert.strictEqual(f, 'foo'); 291 } 292 } 293 294 foo().catch(common.mustCall((error) => { 295 assert.strictEqual(error.name, 'AbortError'); 296 })).finally(() => { 297 clearInterval(i); 298 }); 299 300 process.nextTick(() => ac.abort()); 301} 302 303async function eventTargetAbortableOnAfter() { 304 const et = new EventTarget(); 305 const ac = new AbortController(); 306 307 const i = setInterval(() => et.dispatchEvent(new Event('foo')), 10); 308 309 async function foo() { 310 for await (const f of on(et, 'foo', { signal: ac.signal })) { 311 assert(f); 312 } 313 } 314 315 foo().catch(common.mustCall((error) => { 316 assert.strictEqual(error.name, 'AbortError'); 317 })).finally(() => { 318 clearInterval(i); 319 }); 320 321 process.nextTick(() => ac.abort()); 322} 323 324async function eventTargetAbortableOnAfter2() { 325 const et = new EventTarget(); 326 const ac = new AbortController(); 327 328 const i = setInterval(() => et.dispatchEvent(new Event('foo')), 10); 329 330 async function foo() { 331 for await (const f of on(et, 'foo', { signal: ac.signal })) { 332 assert(f); 333 // Cancel after a single event has been triggered. 334 ac.abort(); 335 } 336 } 337 338 foo().catch(common.mustCall((error) => { 339 assert.strictEqual(error.name, 'AbortError'); 340 })).finally(() => { 341 clearInterval(i); 342 }); 343} 344 345async function abortableOnAfterDone() { 346 const ee = new EventEmitter(); 347 const ac = new AbortController(); 348 349 const i = setInterval(() => ee.emit('foo', 'foo'), 1); 350 let count = 0; 351 352 async function foo() { 353 for await (const f of on(ee, 'foo', { signal: ac.signal })) { 354 assert.strictEqual(f[0], 'foo'); 355 if (++count === 5) 356 break; 357 } 358 ac.abort(); // No error will occur 359 } 360 361 foo().finally(() => { 362 clearInterval(i); 363 }); 364} 365 366async function run() { 367 const funcs = [ 368 basic, 369 invalidArgType, 370 error, 371 errorDelayed, 372 throwInLoop, 373 next, 374 nextError, 375 iterableThrow, 376 eventTarget, 377 errorListenerCount, 378 nodeEventTarget, 379 abortableOnBefore, 380 abortableOnAfter, 381 eventTargetAbortableOnBefore, 382 eventTargetAbortableOnAfter, 383 eventTargetAbortableOnAfter2, 384 abortableOnAfterDone, 385 ]; 386 387 for (const fn of funcs) { 388 await fn(); 389 } 390} 391 392run().then(common.mustCall()); 393