diff options
| author | Marin Ivanov <[email protected]> | 2019-04-26 02:40:41 +0300 |
|---|---|---|
| committer | Marin Ivanov <[email protected]> | 2019-04-26 03:08:02 +0300 |
| commit | c5183ad3d99fd7b0e4208a257da51445c53f69a6 (patch) | |
| tree | b0c7afc923d9316676345bba098f63839c798a91 /src | |
| parent | bcbe1632ca93553d12d793eb45726fda125c8a14 (diff) | |
Rename {mitt => nitt}
Diffstat (limited to 'src')
| -rw-r--r-- | src/index.js | 61 | ||||
| -rw-r--r-- | src/index.test.ts | 186 | ||||
| -rw-r--r-- | src/index.ts | 85 |
3 files changed, 271 insertions, 61 deletions
diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 116b7d8..0000000 --- a/src/index.js +++ /dev/null @@ -1,61 +0,0 @@ -// @flow -// An event handler can take an optional event argument -// and should not return a value -type EventHandler = (event?: any) => void; -type WildCardEventHandler = (type: string, event?: any) => void - -// An array of all currently registered event handlers for a type -type EventHandlerList = Array<EventHandler>; -type WildCardEventHandlerList = Array<WildCardEventHandler>; -// A map of event types and their corresponding event handlers. -type EventHandlerMap = { - '*'?: WildCardEventHandlerList, - [type: string]: EventHandlerList, -}; - -/** Mitt: Tiny (~200b) functional event emitter / pubsub. - * @name mitt - * @returns {Mitt} - */ -export default function mitt(all: EventHandlerMap) { - all = all || Object.create(null); - - return { - /** - * Register an event handler for the given type. - * - * @param {String} type Type of event to listen for, or `"*"` for all events - * @param {Function} handler Function to call in response to given event - * @memberOf mitt - */ - on(type: string, handler: EventHandler) { - (all[type] || (all[type] = [])).push(handler); - }, - - /** - * Remove an event handler for the given type. - * - * @param {String} type Type of event to unregister `handler` from, or `"*"` - * @param {Function} handler Handler function to remove - * @memberOf mitt - */ - off(type: string, handler: EventHandler) { - if (all[type]) { - all[type].splice(all[type].indexOf(handler) >>> 0, 1); - } - }, - - /** - * Invoke all handlers for the given type. - * If present, `"*"` handlers are invoked after type-matched handlers. - * - * @param {String} type The event type to invoke - * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler - * @memberOf mitt - */ - emit(type: string, evt: any) { - (all[type] || []).slice().map((handler) => { handler(evt); }); - (all['*'] || []).slice().map((handler) => { handler(type, evt); }); - } - }; -} diff --git a/src/index.test.ts b/src/index.test.ts new file mode 100644 index 0000000..af07805 --- /dev/null +++ b/src/index.test.ts @@ -0,0 +1,186 @@ +import nitt from '.'; + +it('should default export be a function', () => { + expect(nitt).toBeInstanceOf(Function); +}); + +describe('nitt#', () => { + let events, inst; + + beforeEach(() => { + events = Object.create(null); + inst = nitt(events); + }); + + describe('on()', () => { + it('should be a function', () => { + expect(inst).toHaveProperty('on'); + expect(inst.on).toBeInstanceOf(Function); + }); + + it('should register handler for new type', () => { + let foo = () => {}; + inst.on('foo', foo); + + expect(events).toHaveProperty('foo'); + expect(events.foo).toEqual([foo]); + }); + + it('should register handlers for any type strings', () => { + let foo = () => {}; + inst.on('constructor', foo); + + expect(events).toHaveProperty('constructor'); + expect(events.constructor).toEqual([foo]); + }); + + it('should append handler for existing type', () => { + let foo = () => {}; + let bar = () => {}; + inst.on('foo', foo); + inst.on('foo', bar); + + expect(events).toHaveProperty('foo'); + expect(events.foo).toEqual([foo, bar]); + }); + + it('should NOT normalize case', () => { + let foo = () => {}; + inst.on('FOO', foo); + inst.on('Bar', foo); + inst.on('baz:baT!', foo); + + expect(events).toHaveProperty('FOO'); + expect(events.FOO).toEqual([foo]); + expect(events).not.toHaveProperty('foo'); + expect(events).toHaveProperty('Bar'); + expect(events.Bar).toEqual([foo]); + expect(events).not.toHaveProperty('bar'); + expect(events).toHaveProperty('baz:baT!'); + expect(events['baz:baT!']).toEqual([foo]); + }); + }); + + describe('once()', () => { + it('should execute handler just once', () => { + let foo = jest.fn(); + inst.once('foo', foo); + + inst.emit('foo', 1); + inst.emit('foo', 2); + + expect(foo).toBeCalledTimes(1); + expect(foo).toBeCalledWith(1); + }); + + it('should remove the handler once is executed', () => { + let foo = jest.fn(); + inst.once('foo', foo); + + expect(events.foo).toHaveLength(1); + inst.emit('foo', 1); + expect(events.foo).toHaveLength(0); + }); + }); + + describe('when()', () => { + it('should return a promise', () => { + let foo = jest.fn(); + const result = inst.when('foo'); + expect(result).toBeInstanceOf(Promise); + }); + + it('should resolve with the event', async () => { + const promise = inst.when('foo'); + + inst.emit('foo', 'event data'); + expect(await promise).toEqual('event data'); + }); + }); + + describe('off()', () => { + it('should be a function', () => { + expect(inst).toHaveProperty('off'); + expect(inst.off).toBeInstanceOf(Function); + }); + + it('should remove handler for type', () => { + let foo = () => {}; + inst.on('foo', foo); + inst.off('foo', foo); + + expect(events).toHaveProperty('foo'); + expect(events.foo).toHaveLength(0); + }); + + it('should NOT normalize case', () => { + let 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).toHaveProperty('FOO'); + expect(events.FOO).toHaveLength(0); + expect(events).not.toHaveProperty('foo'); + expect(events).toHaveProperty('Bar'); + expect(events.Bar).toHaveLength(0); + expect(events).not.toHaveProperty('bar'); + expect(events).toHaveProperty('baz:bat!'); + expect(events['baz:bat!']).toHaveLength(1); + }); + }); + + describe('emit()', () => { + it('should be a function', () => { + expect(inst).toHaveProperty('emit'); + expect(inst.emit).toBeInstanceOf(Function); + }); + + it('should invoke handler for type', () => { + let event = { a: 'b' }; + + inst.on('foo', (one, two) => { + expect(one).toEqual(event); + expect(two).toBe(undefined); + }); + + inst.emit('foo', event); + }); + + it('should NOT ignore case', () => { + let onFoo = jest.fn(), + onFOO = jest.fn(); + events.Foo = [onFoo]; + events.FOO = [onFOO]; + + inst.emit('Foo', 'Foo arg'); + inst.emit('FOO', 'FOO arg'); + + expect(onFoo).toHaveBeenCalledTimes(1); + expect(onFoo).toHaveBeenCalledWith('Foo arg'); + expect(onFOO).toHaveBeenCalledTimes(1); + expect(onFOO).toHaveBeenCalledWith('FOO arg'); + }); + + it('should invoke * handlers', () => { + let star = jest.fn(), + ea = { a: 'a' }, + eb = { b: 'b' }; + + events['*'] = [star]; + + inst.emit('foo', ea); + expect(star).toHaveBeenCalledTimes(1); + expect(star).toHaveBeenCalledWith('foo', ea); + star.mockReset(); + + inst.emit('bar', eb); + expect(star).toHaveBeenCalledTimes(1); + expect(star).toHaveBeenCalledWith('bar', eb); + }); + }); +}); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..aa07055 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,85 @@ +// An event handler can take an optional event argument +// and should not return a value +type EventHandler = (event?: any) => void; +type WildCardEventHandler = (type: string, event?: any) => void; + +// An array of all currently registered event handlers for a type +type EventHandlerList = Array<EventHandler>; +type WildCardEventHandlerList = Array<WildCardEventHandler>; +// A map of event types and their corresponding event handlers. +type EventHandlerMap = { + '*'?: WildCardEventHandlerList; + [type: string]: EventHandlerList; +}; + +/** Nitt: functional event emitter / pubsub. + * @name nitt + * @returns {Nitt} + */ +export default function nitt(all: EventHandlerMap) { + all = all || Object.create(null); + + return { + /** + * Register an event handler for the given type. + * + * @param {String} type Type of event to listen for, or `"*"` for all events + * @param {Function} handler Function to call in response to given event + */ + on(type: string, handler: EventHandler) { + (all[type] || (all[type] = [])).push(handler); + }, + + /** + * Register an event handler that is executed just once. + * + * @param {String} type Type of event to listen for, or `"*"` for any event + * @param {Function} handler Function to call in response to given event + */ + once(type: string, handler: EventHandler) { + const onceHandler = evt => { + handler(evt); + this.off(type, onceHandler); + }; + this.on(type, onceHandler); + }, + + /** + * Returns a promise for a single event + * + * @param {String} type Type of event to listen for, or `"*"` for any event + * @returns {Promise<any>} + */ + when(type: string): Promise<any> { + return new Promise<any>(r => this.once(type, r)); + }, + + /** + * Remove an event handler for the given type. + * + * @param {String} type Type of event to unregister `handler` from, or `"*"` + * @param {Function} handler Handler function to remove + */ + off(type: string, handler: EventHandler) { + if (all[type]) { + all[type].splice(all[type].indexOf(handler) >>> 0, 1); + } + }, + + /** + * Invoke all handlers for the given type. + * If present, `"*"` handlers are invoked after type-matched handlers. + * + * @param {String} type The event type to invoke + * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler + */ + emit(type: string, evt: any) { + (all[type] || []).forEach(handler => { + handler(evt); + }); + (all['*'] || []).forEach(handler => { + handler(type, evt); + }); + }, + }; +} |
