| 'use strict'; |
| // Flags: --expose-internals --no-warnings |
| |
| const common = require('../common'); |
| const { once, EventEmitter } = require('events'); |
| const { |
| deepStrictEqual, |
| fail, |
| rejects, |
| strictEqual, |
| } = require('assert'); |
| const { kEvents } = require('internal/event_target'); |
| |
| async function onceAnEvent() { |
| const ee = new EventEmitter(); |
| |
| process.nextTick(() => { |
| ee.emit('myevent', 42); |
| }); |
| |
| const [value] = await once(ee, 'myevent'); |
| strictEqual(value, 42); |
| strictEqual(ee.listenerCount('error'), 0); |
| strictEqual(ee.listenerCount('myevent'), 0); |
| } |
| |
| async function onceAnEventWithInvalidOptions() { |
| const ee = new EventEmitter(); |
| |
| await Promise.all([1, 'hi', null, false, () => {}, Symbol(), 1n].map((options) => { |
| return rejects(once(ee, 'myevent', options), { |
| code: 'ERR_INVALID_ARG_TYPE', |
| }); |
| })); |
| } |
| |
| async function onceAnEventWithTwoArgs() { |
| const ee = new EventEmitter(); |
| |
| process.nextTick(() => { |
| ee.emit('myevent', 42, 24); |
| }); |
| |
| const value = await once(ee, 'myevent'); |
| deepStrictEqual(value, [42, 24]); |
| } |
| |
| async function catchesErrors() { |
| const ee = new EventEmitter(); |
| |
| const expected = new Error('kaboom'); |
| let err; |
| process.nextTick(() => { |
| ee.emit('error', expected); |
| }); |
| |
| try { |
| await once(ee, 'myevent'); |
| } catch (_e) { |
| err = _e; |
| } |
| strictEqual(err, expected); |
| strictEqual(ee.listenerCount('error'), 0); |
| strictEqual(ee.listenerCount('myevent'), 0); |
| } |
| |
| async function catchesErrorsWithAbortSignal() { |
| const ee = new EventEmitter(); |
| const ac = new AbortController(); |
| const signal = ac.signal; |
| |
| const expected = new Error('boom'); |
| let err; |
| process.nextTick(() => { |
| ee.emit('error', expected); |
| }); |
| |
| try { |
| const promise = once(ee, 'myevent', { signal }); |
| strictEqual(ee.listenerCount('error'), 1); |
| strictEqual(signal[kEvents].size, 1); |
| |
| await promise; |
| } catch (e) { |
| err = e; |
| } |
| strictEqual(err, expected); |
| strictEqual(ee.listenerCount('error'), 0); |
| strictEqual(ee.listenerCount('myevent'), 0); |
| strictEqual(signal[kEvents].size, 0); |
| } |
| |
| async function stopListeningAfterCatchingError() { |
| const ee = new EventEmitter(); |
| |
| const expected = new Error('kaboom'); |
| let err; |
| process.nextTick(() => { |
| ee.emit('error', expected); |
| ee.emit('myevent', 42, 24); |
| }); |
| |
| try { |
| await once(ee, 'myevent'); |
| } catch (_e) { |
| err = _e; |
| } |
| process.removeAllListeners('multipleResolves'); |
| strictEqual(err, expected); |
| strictEqual(ee.listenerCount('error'), 0); |
| strictEqual(ee.listenerCount('myevent'), 0); |
| } |
| |
| async function onceError() { |
| const ee = new EventEmitter(); |
| |
| const expected = new Error('kaboom'); |
| process.nextTick(() => { |
| ee.emit('error', expected); |
| }); |
| |
| const promise = once(ee, 'error'); |
| strictEqual(ee.listenerCount('error'), 1); |
| const [ err ] = await promise; |
| strictEqual(err, expected); |
| strictEqual(ee.listenerCount('error'), 0); |
| strictEqual(ee.listenerCount('myevent'), 0); |
| } |
| |
| async function onceWithEventTarget() { |
| const et = new EventTarget(); |
| const event = new Event('myevent'); |
| process.nextTick(() => { |
| et.dispatchEvent(event); |
| }); |
| const [ value ] = await once(et, 'myevent'); |
| strictEqual(value, event); |
| } |
| |
| async function onceWithEventTargetError() { |
| const et = new EventTarget(); |
| const error = new Event('error'); |
| process.nextTick(() => { |
| et.dispatchEvent(error); |
| }); |
| |
| const [ err ] = await once(et, 'error'); |
| strictEqual(err, error); |
| } |
| |
| async function onceWithInvalidEventEmmiter() { |
| const ac = new AbortController(); |
| return rejects(once(ac, 'myevent'), { |
| code: 'ERR_INVALID_ARG_TYPE', |
| }); |
| } |
| |
| async function prioritizesEventEmitter() { |
| const ee = new EventEmitter(); |
| ee.addEventListener = fail; |
| ee.removeAllListeners = fail; |
| process.nextTick(() => ee.emit('foo')); |
| await once(ee, 'foo'); |
| } |
| |
| async function abortSignalBefore() { |
| const ee = new EventEmitter(); |
| ee.on('error', common.mustNotCall()); |
| const abortedSignal = AbortSignal.abort(); |
| |
| await Promise.all([1, {}, 'hi', null, false].map((signal) => { |
| return rejects(once(ee, 'foo', { signal }), { |
| code: 'ERR_INVALID_ARG_TYPE', |
| }); |
| })); |
| |
| return rejects(once(ee, 'foo', { signal: abortedSignal }), { |
| name: 'AbortError', |
| }); |
| } |
| |
| async function abortSignalAfter() { |
| const ee = new EventEmitter(); |
| const ac = new AbortController(); |
| ee.on('error', common.mustNotCall()); |
| const r = rejects(once(ee, 'foo', { signal: ac.signal }), { |
| name: 'AbortError', |
| }); |
| process.nextTick(() => ac.abort()); |
| return r; |
| } |
| |
| async function abortSignalAfterEvent() { |
| const ee = new EventEmitter(); |
| const ac = new AbortController(); |
| process.nextTick(() => { |
| ee.emit('foo'); |
| ac.abort(); |
| }); |
| const promise = once(ee, 'foo', { signal: ac.signal }); |
| strictEqual(ac.signal[kEvents].size, 1); |
| await promise; |
| strictEqual(ac.signal[kEvents].size, 0); |
| } |
| |
| async function abortSignalRemoveListener() { |
| const ee = new EventEmitter(); |
| const ac = new AbortController(); |
| |
| try { |
| process.nextTick(() => ac.abort()); |
| await once(ee, 'test', { signal: ac.signal }); |
| } catch { |
| strictEqual(ee.listeners('test').length, 0); |
| strictEqual(ee.listeners('error').length, 0); |
| } |
| } |
| |
| async function eventTargetAbortSignalBefore() { |
| const et = new EventTarget(); |
| const abortedSignal = AbortSignal.abort(); |
| |
| await Promise.all([1, {}, 'hi', null, false].map((signal) => { |
| return rejects(once(et, 'foo', { signal }), { |
| code: 'ERR_INVALID_ARG_TYPE', |
| }); |
| })); |
| |
| return rejects(once(et, 'foo', { signal: abortedSignal }), { |
| name: 'AbortError', |
| }); |
| } |
| |
| async function eventTargetAbortSignalBeforeEvenWhenSignalPropagationStopped() { |
| const et = new EventTarget(); |
| const ac = new AbortController(); |
| const { signal } = ac; |
| signal.addEventListener('abort', (e) => e.stopImmediatePropagation(), { once: true }); |
| |
| process.nextTick(() => ac.abort()); |
| return rejects(once(et, 'foo', { signal }), { |
| name: 'AbortError', |
| }); |
| } |
| |
| async function eventTargetAbortSignalAfter() { |
| const et = new EventTarget(); |
| const ac = new AbortController(); |
| const r = rejects(once(et, 'foo', { signal: ac.signal }), { |
| name: 'AbortError', |
| }); |
| process.nextTick(() => ac.abort()); |
| return r; |
| } |
| |
| async function eventTargetAbortSignalAfterEvent() { |
| const et = new EventTarget(); |
| const ac = new AbortController(); |
| process.nextTick(() => { |
| et.dispatchEvent(new Event('foo')); |
| ac.abort(); |
| }); |
| await once(et, 'foo', { signal: ac.signal }); |
| } |
| |
| Promise.all([ |
| onceAnEvent(), |
| onceAnEventWithInvalidOptions(), |
| onceAnEventWithTwoArgs(), |
| catchesErrors(), |
| catchesErrorsWithAbortSignal(), |
| stopListeningAfterCatchingError(), |
| onceError(), |
| onceWithEventTarget(), |
| onceWithEventTargetError(), |
| onceWithInvalidEventEmmiter(), |
| prioritizesEventEmitter(), |
| abortSignalBefore(), |
| abortSignalAfter(), |
| abortSignalAfterEvent(), |
| abortSignalRemoveListener(), |
| eventTargetAbortSignalBefore(), |
| eventTargetAbortSignalBeforeEvenWhenSignalPropagationStopped(), |
| eventTargetAbortSignalAfter(), |
| eventTargetAbortSignalAfterEvent(), |
| ]).then(common.mustCall()); |