npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bupkis/events

v0.2.0

Published

EventEmitter and EventTarget assertions for Bupkis

Readme

@bupkis/events

EventEmitter and EventTarget assertions for bupkis.

Installation

npm install @bupkis/events

Usage

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',
);

Assertions

Synchronous Assertions (EventEmitter)

to have listener for {event}

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)

to have listeners for {events}

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

to have listener count {event} {count}

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

to have listeners

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)

to have max listeners {count}

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

Asynchronous Assertions (EventEmitter)

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

to emit from {emitter} {event}

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',
);

to emit from {emitter} {event} with args {args}

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) })],
);

to emit error from {emitter}

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,
);

to emit events from {emitter} {events}

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'],
);

Asynchronous Assertions (EventTarget)

to dispatch from {target} {eventType}

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',
);

to dispatch from {target} {eventType} with detail {detail}

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) }),
);

Timeout Options

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.

Duck-Typed EventEmitter Support

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!

Symbol Events

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);

License

BlueOak-1.0.0