aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorMarin Ivanov <[email protected]>2026-01-19 02:41:01 +0200
committerMarin Ivanov <[email protected]>2026-01-19 02:41:01 +0200
commita6bdee992a0e8850b922944a943cdbd907db5e66 (patch)
treedd114f4e6f3fa7b21bf11541e128feef893743a5 /test
parent1fc5ce993a1769a3d0dd5fe2fdb51726f06f804c (diff)
parentb240473b5707857ba2c6a8e6d707c28d1e39da49 (diff)
Merge tag '3.0.1' from mittHEADmaster
Diffstat (limited to 'test')
-rw-r--r--test/index_test.ts204
-rw-r--r--test/test-types-compilation.ts78
2 files changed, 282 insertions, 0 deletions
diff --git a/test/index_test.ts b/test/index_test.ts
new file mode 100644
index 0000000..4a67fe6
--- /dev/null
+++ b/test/index_test.ts
@@ -0,0 +1,204 @@
+import nitt, { Emitter, EventHandlerMap } from '..';
+import chai, { expect } from 'chai';
+import { spy } from 'sinon';
+import sinonChai from 'sinon-chai';
+chai.use(sinonChai);
+
+describe('nitt', () => {
+ it('should default export be a function', () => {
+ expect(nitt).to.be.a('function');
+ });
+
+ it('should accept an optional event handler map', () => {
+ expect(() => nitt(new Map())).not.to.throw;
+ const map = new Map();
+ const a = spy();
+ const b = spy();
+ map.set('foo', [a, b]);
+ const events = nitt<{ foo: undefined }>(map);
+ events.emit('foo');
+ expect(a).to.have.been.calledOnce;
+ expect(b).to.have.been.calledOnce;
+ });
+});
+
+describe('nitt#', () => {
+ const eventType = Symbol('eventType');
+ type Events = {
+ foo: unknown;
+ constructor: unknown;
+ FOO: unknown;
+ bar: unknown;
+ Bar: unknown;
+ 'baz:bat!': unknown;
+ 'baz:baT!': unknown;
+ Foo: unknown;
+ [eventType]: unknown;
+ };
+ let events: EventHandlerMap<Events>, inst: Emitter<Events>;
+
+ beforeEach(() => {
+ events = new Map();
+ inst = nitt(events);
+ });
+
+ describe('properties', () => {
+ it('should expose the event handler map', () => {
+ expect(inst).to.have.property('all').that.is.a('map');
+ });
+ });
+
+ describe('on()', () => {
+ it('should be a function', () => {
+ expect(inst).to.have.property('on').that.is.a('function');
+ });
+
+ it('should register handler for new type', () => {
+ const foo = () => {};
+ inst.on('foo', foo);
+
+ expect(events.get('foo')).to.deep.equal([foo]);
+ });
+
+ it('should register handlers for any type strings', () => {
+ const foo = () => {};
+ inst.on('constructor', foo);
+
+ expect(events.get('constructor')).to.deep.equal([foo]);
+ });
+
+ it('should append handler for existing type', () => {
+ const foo = () => {};
+ const bar = () => {};
+ inst.on('foo', foo);
+ inst.on('foo', bar);
+
+ expect(events.get('foo')).to.deep.equal([foo, bar]);
+ });
+
+ it('should NOT normalize case', () => {
+ const foo = () => {};
+ inst.on('FOO', foo);
+ inst.on('Bar', foo);
+ inst.on('baz:baT!', foo);
+
+ expect(events.get('FOO')).to.deep.equal([foo]);
+ expect(events.has('foo')).to.equal(false);
+ expect(events.get('Bar')).to.deep.equal([foo]);
+ expect(events.has('bar')).to.equal(false);
+ expect(events.get('baz:baT!')).to.deep.equal([foo]);
+ });
+
+ it('can take symbols for event types', () => {
+ const foo = () => {};
+ inst.on(eventType, foo);
+ expect(events.get(eventType)).to.deep.equal([foo]);
+ });
+
+ // Adding the same listener multiple times should register it multiple times.
+ // See https://nodejs.org/api/events.html#events_emitter_on_eventname_listener
+ it('should add duplicate listeners', () => {
+ const foo = () => {};
+ inst.on('foo', foo);
+ inst.on('foo', foo);
+ expect(events.get('foo')).to.deep.equal([foo, foo]);
+ });
+ });
+
+ describe('off()', () => {
+ it('should be a function', () => {
+ expect(inst).to.have.property('off').that.is.a('function');
+ });
+
+ it('should remove handler for type', () => {
+ const foo = () => {};
+ inst.on('foo', foo);
+ inst.off('foo', foo);
+
+ expect(events.get('foo')).to.be.empty;
+ });
+
+ it('should NOT normalize case', () => {
+ const foo = () => {};
+ inst.on('FOO', foo);
+ inst.on('Bar', foo);
+ inst.on('baz:bat!', foo);
+
+ inst.off('FOO', foo);
+ inst.off('Bar', foo);
+ inst.off('baz:baT!', foo);
+
+ expect(events.get('FOO')).to.be.empty;
+ expect(events.has('foo')).to.equal(false);
+ expect(events.get('Bar')).to.be.empty;
+ expect(events.has('bar')).to.equal(false);
+ expect(events.get('baz:bat!')).to.have.lengthOf(1);
+ });
+
+ it('should remove only the first matching listener', () => {
+ const foo = () => {};
+ inst.on('foo', foo);
+ inst.on('foo', foo);
+ inst.off('foo', foo);
+ expect(events.get('foo')).to.deep.equal([foo]);
+ inst.off('foo', foo);
+ expect(events.get('foo')).to.deep.equal([]);
+ });
+
+ it('off("type") should remove all handlers of the given type', () => {
+ inst.on('foo', () => {});
+ inst.on('foo', () => {});
+ inst.on('bar', () => {});
+ inst.off('foo');
+ expect(events.get('foo')).to.deep.equal([]);
+ expect(events.get('bar')).to.have.length(1);
+ inst.off('bar');
+ expect(events.get('bar')).to.deep.equal([]);
+ });
+ });
+
+ describe('emit()', () => {
+ it('should be a function', () => {
+ expect(inst).to.have.property('emit').that.is.a('function');
+ });
+
+ it('should invoke handler for type', () => {
+ const event = { a: 'b' };
+
+ inst.on('foo', (one, two?: unknown) => {
+ expect(one).to.deep.equal(event);
+ expect(two).to.be.an('undefined');
+ });
+
+ inst.emit('foo', event);
+ });
+
+ it('should NOT ignore case', () => {
+ const onFoo = spy(),
+ onFOO = spy();
+ events.set('Foo', [onFoo]);
+ events.set('FOO', [onFOO]);
+
+ inst.emit('Foo', 'Foo arg');
+ inst.emit('FOO', 'FOO arg');
+
+ expect(onFoo).to.have.been.calledOnce.and.calledWith('Foo arg');
+ expect(onFOO).to.have.been.calledOnce.and.calledWith('FOO arg');
+ });
+
+ it('should invoke * handlers', () => {
+ const star = spy(),
+ ea = { a: 'a' },
+ eb = { b: 'b' };
+
+ events.set('*', [star]);
+
+ inst.emit('foo', ea);
+ expect(star).to.have.been.calledOnce.and.calledWith('foo', ea);
+ star.resetHistory();
+
+ inst.emit('bar', eb);
+ expect(star).to.have.been.calledOnce.and.calledWith('bar', eb);
+ });
+ });
+});
diff --git a/test/test-types-compilation.ts b/test/test-types-compilation.ts
new file mode 100644
index 0000000..25c51cc
--- /dev/null
+++ b/test/test-types-compilation.ts
@@ -0,0 +1,78 @@
+/* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unused-vars */
+
+import nitt from '..';
+
+interface SomeEventData {
+ name: string;
+}
+
+const emitter = nitt<{
+ foo: string;
+ someEvent: SomeEventData;
+ bar?: number;
+}>();
+
+const barHandler = (x?: number) => {};
+const fooHandler = (x: string) => {};
+const wildcardHandler = (
+ _type: 'foo' | 'bar' | 'someEvent',
+ _event: string | SomeEventData | number | undefined
+) => {};
+
+/*
+ * Check that 'on' args are inferred correctly
+ */
+{
+ // @ts-expect-error
+ emitter.on('foo', barHandler);
+ emitter.on('foo', fooHandler);
+
+ emitter.on('bar', barHandler);
+ // @ts-expect-error
+ emitter.on('bar', fooHandler);
+
+ emitter.on('*', wildcardHandler);
+ // fooHandler is ok, because ('foo' | 'bar' | 'someEvent') extends string
+ emitter.on('*', fooHandler);
+ // @ts-expect-error
+ emitter.on('*', barHandler);
+}
+
+/*
+ * Check that 'off' args are inferred correctly
+ */
+{
+ // @ts-expect-error
+ emitter.off('foo', barHandler);
+ emitter.off('foo', fooHandler);
+
+ emitter.off('bar', barHandler);
+ // @ts-expect-error
+ emitter.off('bar', fooHandler);
+
+ emitter.off('*', wildcardHandler);
+ // fooHandler is ok, because ('foo' | 'bar' | 'someEvent') extends string
+ emitter.off('*', fooHandler);
+ // @ts-expect-error
+ emitter.off('*', barHandler);
+}
+
+/*
+ * Check that 'emit' args are inferred correctly
+ */
+{
+ // @ts-expect-error
+ emitter.emit('someEvent', 'NOT VALID');
+ emitter.emit('someEvent', { name: 'jack' });
+
+ // @ts-expect-error
+ emitter.emit('foo');
+ // @ts-expect-error
+ emitter.emit('foo', 1);
+ emitter.emit('foo', 'string');
+
+ emitter.emit('bar');
+ emitter.emit('bar', 1);
+ // @ts-expect-error
+ emitter.emit('bar', 'string');
+}