diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/index_test.ts | 204 | ||||
| -rw-r--r-- | test/test-types-compilation.ts | 78 |
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'); +} |
