Skip to content

Commit

Permalink
Add TestEmitter (#18)
Browse files Browse the repository at this point in the history
Revert setMaxListener()
  • Loading branch information
unional committed Nov 12, 2017
1 parent cb551a3 commit 70d5080
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 8 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ and provides provides IDE type support so they can be consumed easily.

## Usage

`Emitter` will capture any error thrown in listener and send it to `console.error()`.
This is because the listener are UI code and any error thrown in there should not affact logic.

### createEvent<Payload, Meta>(type: string): Event<Payload, Meta>

Creates an event.
Expand Down Expand Up @@ -77,6 +80,11 @@ const emitter = new Emitter()
add(emitter, { a: 1, b: 2 }, undefined)
```

### TestEmitter

Same as `Emitter` but it will not capture error thrown in listeners.
`TestEmitter` can be used during testing to make your test easier to write.

## Contribute

```sh
Expand Down
6 changes: 3 additions & 3 deletions src/Emitter.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import test from 'ava'

import { createEvent, Emitter, errorEvent } from './index'
import { createEvent, Emitter, errorEvent, createEventAction } from './index'

test('addListener(): listener(payload) is typed', t => {
const emitter = new Emitter()
Expand Down Expand Up @@ -88,11 +88,11 @@ test(`error thrown in listener should not affect emitting code. An error action

test('error thrown in errorAction handler should not cause call stack overflow', t => {
const emitter = new Emitter()
const count = createEvent<number>('count')
const count = createEventAction<number, number>('count', input => emit => emit(input + 1))

function noThrow() {
try {
emitter.emit(count(1, undefined))
count(emitter, 1, undefined)
}
catch {
t.fail('should not throw')
Expand Down
4 changes: 2 additions & 2 deletions src/Emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TypedEvent } from './createEvent'
import { errorEvent } from './errorEvent'

export class Emitter {
private emitter: EventEmitter
protected emitter: EventEmitter
constructor() {
this.emitter = new EventEmitter()
}
Expand Down Expand Up @@ -38,7 +38,7 @@ export class Emitter {
return this.emitter.once(type, listener)
}

private addErrorEventListener<Payload, Meta>(listener: (payload: Payload, meta: Meta, error: boolean) => void): EventSubscription {
protected addErrorEventListener<Payload, Meta>(listener: (payload: Payload, meta: Meta, error: boolean) => void): EventSubscription {
const wrappedListener = (payload, meta, error) => {
try {
listener(payload, meta, error)
Expand Down
54 changes: 54 additions & 0 deletions src/TestEmitter.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import test from 'ava'

import { createEvent, TestEmitter, createEventAction, errorEvent } from './index'

test(`error thrown in listener is thrown`, t => {
const emitter = new TestEmitter()
const count = createEvent<number>('count')

emitter.on(count, () => { throw new Error('thrown') })

const err = t.throws(() => emitter.emit(count(1, undefined)))
t.is(err.message, 'thrown')
})

test('error thrown in listener is thrown for event action', t => {
const emitter = new TestEmitter()
const count = createEventAction<number, number>('count', input => emit => emit(input + 1))

emitter.on(count, () => { throw new Error('thrown') })

const err = t.throws(() => count(emitter, 1, undefined))
t.is(err.message, 'thrown')
})

test(`error thrown in listener is thrown for string event`, t => {
const emitter = new TestEmitter()
const count = createEvent<number>('count')

emitter.on(count.type, () => { throw new Error('thrown') })

const err = t.throws(() => emitter.emit(count(1, undefined)))
t.is(err.message, 'thrown')
})

test('error thrown in listener is thrown for event action', t => {
const emitter = new TestEmitter()
const count = createEventAction<number, number>('count', input => emit => emit(input + 1))

emitter.on(count, () => { throw new Error('thrown') })

const err = t.throws(() => count(emitter, 1, undefined))
t.is(err.message, 'thrown')
})

test('error thrown in listener is thrown for error event', t => {
const emitter = new TestEmitter()

emitter.on(errorEvent, () => {
throw new Error('thrown')
})

const err = t.throws(() => emitter.emit(errorEvent(new Error('abc'), undefined)))
t.is(err.message, 'thrown')
})
25 changes: 25 additions & 0 deletions src/TestEmitter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Emitter } from './Emitter';
import { TypedEvent } from './createEvent';
import { EventSubscription } from 'fbemitter';
import { errorEvent } from './errorEvent';

/**
* Emitter used for testing.
* The real emitter will capture the error thrown in listener and send it to console.error.
* It is good for main code but not good for testing.
*/
export class TestEmitter extends Emitter {
addListener<Payload, Meta>(
event: TypedEvent<Payload, Meta> | string,
listener: (payload: Payload, meta: Meta, error: boolean) => void): EventSubscription {
const type = typeof event === 'string' ? event : event.type
if (type === errorEvent.type)
return this.addErrorEventListener(listener)

return this.emitter.addListener(type, listener)
}

protected addErrorEventListener<Payload, Meta>(listener: (payload: Payload, meta: Meta, error: boolean) => void): EventSubscription {
return this.emitter.addListener(errorEvent.type, listener)
}
}
8 changes: 5 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ export * from './createEvent'
export * from './createEventAction'
export * from './Emitter'
export * from './errorEvent'
export * from './TestEmitter'

import { isNode } from './environment'
// This is commented out because likely this occurs in testing only and may be an issue with fbemitter.
// import { isNode } from './environment'

// We are using Emitter as store, just like redux.
// Expect to have many listeners.
// istanbul ignore next
if (isNode)
process.setMaxListeners(0)
// if (isNode)
// process.setMaxListeners(0)

0 comments on commit 70d5080

Please sign in to comment.