BUPKIS
    Preparing search index...

    @bupkis/events

    EventEmitter and EventTarget assertions for bupkis.

    npm install @bupkis/events
    
    import { use } from 'bupkis';
    import { eventAssertions } from '@bupkis/events';
    import { EventEmitter } from 'node:events';

    // Register the assertions
    const { expect, expectAsync } = use(eventAssertions);

    // Sync assertions for listener state
    const emitter = new EventEmitter();
    emitter.on('data', () => {});
    expect(emitter, 'to have listener for', 'data');

    // Async assertions for event emission
    await expectAsync(
    () => emitter.emit('ready'),
    'to emit from',
    emitter,
    'ready',
    );

    Asserts that an emitter has at least one listener for the specified event.

    const emitter = new EventEmitter();
    emitter.on('data', () => {});

    expect(emitter, 'to have listener for', 'data'); // passes
    expect(emitter, 'not to have listener for', 'other'); // passes (negation)

    Asserts that an emitter has listeners for all specified events.

    const emitter = new EventEmitter();
    emitter.on('data', () => {});
    emitter.on('end', () => {});

    expect(emitter, 'to have listeners for', ['data', 'end']); // passes

    Asserts that an emitter has exactly the specified number of listeners for an event.

    const emitter = new EventEmitter();
    emitter.on('data', () => {});
    emitter.on('data', () => {}); // second listener

    expect(emitter, 'to have listener count', 'data', 2); // passes

    Asserts that an emitter has at least one listener registered (for any event).

    const emitter = new EventEmitter();
    emitter.on('anything', () => {});

    expect(emitter, 'to have listeners'); // passes
    expect(new EventEmitter(), 'not to have listeners'); // passes (fresh emitter)

    Asserts that an emitter's maxListeners is set to the specified value.

    const emitter = new EventEmitter();
    emitter.setMaxListeners(20);

    expect(emitter, 'to have max listeners', 20); // passes

    Async assertions use a trigger-based API where the first argument is a function or Promise that causes the event to be emitted.

    Asserts that a trigger causes an emitter to emit the specified event.

    const emitter = new EventEmitter();

    // Function trigger
    await expectAsync(
    () => emitter.emit('ready'),
    'to emit from',
    emitter,
    'ready',
    );

    // Async trigger
    await expectAsync(
    () => setTimeout(() => emitter.emit('ready'), 10),
    'to emit from',
    emitter,
    'ready',
    );

    Asserts that a trigger causes an emitter to emit an event with specific arguments. Uses 'to satisfy' semantics, allowing partial matching for objects and expect.it() for custom assertions.

    const emitter = new EventEmitter();

    await expectAsync(
    () => emitter.emit('data', 'hello', 42),
    'to emit from',
    emitter,
    'data',
    'with args',
    ['hello', 42],
    );

    With custom assertions:

    await expectAsync(
    () => emitter.emit('data', { count: 42, extra: 'ignored' }),
    'to emit from',
    emitter,
    'data',
    'with args',
    [expect.it('to satisfy', { count: expect.it('to be greater than', 0) })],
    );

    Asserts that a trigger causes an emitter to emit the 'error' event.

    const emitter = new EventEmitter();
    emitter.on('error', () => {}); // Prevent unhandled error

    await expectAsync(
    () => emitter.emit('error', new Error('oops')),
    'to emit error from',
    emitter,
    );

    Asserts that a trigger causes an emitter to emit events in the specified order.

    const emitter = new EventEmitter();

    await expectAsync(
    () => {
    emitter.emit('start');
    emitter.emit('data');
    emitter.emit('end');
    },
    'to emit events from',
    emitter,
    ['start', 'data', 'end'],
    );

    Asserts that a trigger causes an EventTarget to dispatch the specified event.

    const target = new EventTarget();

    await expectAsync(
    () => target.dispatchEvent(new Event('click')),
    'to dispatch from',
    target,
    'click',
    );

    Asserts that a trigger causes an EventTarget to dispatch a CustomEvent with specific detail. Uses 'to satisfy' semantics for objects, allowing partial matching and expect.it() for custom assertions.

    const target = new EventTarget();

    await expectAsync(
    () =>
    target.dispatchEvent(new CustomEvent('custom', { detail: { foo: 'bar' } })),
    'to dispatch from',
    target,
    'custom',
    'with detail',
    { foo: 'bar' },
    );

    Partial matching (extra properties allowed):

    await expectAsync(
    () =>
    target.dispatchEvent(
    new CustomEvent('custom', { detail: { foo: 'bar', extra: 'ignored' } }),
    ),
    'to dispatch from',
    target,
    'custom',
    'with detail',
    { foo: 'bar' },
    );

    With custom assertions:

    await expectAsync(
    () =>
    target.dispatchEvent(new CustomEvent('data', { detail: { count: 42 } })),
    'to dispatch from',
    target,
    'data',
    'with detail',
    expect.it('to satisfy', { count: expect.it('to be greater than', 0) }),
    );

    All async assertions accept an optional timeout configuration:

    // Wait up to 100ms for the event
    await expectAsync(
    () => setTimeout(() => emitter.emit('slow'), 50),
    'to emit from',
    emitter,
    'slow',
    { within: 100 },
    );

    The default timeout is 2000ms.

    This package uses duck-typing to detect EventEmitter-like objects, making it compatible with:

    • Node.js EventEmitter
    • eventemitter3
    • Custom implementations that match the interface
    import EventEmitter3 from 'eventemitter3';

    const emitter = new EventEmitter3();
    emitter.on('data', () => {});

    expect(emitter, 'to have listener for', 'data'); // works!

    Both sync and async assertions support symbol event names:

    const sym = Symbol('myEvent');
    emitter.on(sym, () => {});

    expect(emitter, 'to have listener for', sym);

    await expectAsync(() => emitter.emit(sym), 'to emit from', emitter, sym);

    BlueOak-1.0.0