Skip to content

Commit

Permalink
feat: add spyAsync for Promise calls (#20)
Browse files Browse the repository at this point in the history
* feat: add spyAsync for Promise calls

* chore: remove is-promise

* chore: misc clean up

* chore: fix lint

Disable the lint for these lines as I want to explicity test the CallRecords
  • Loading branch information
unional authored Jan 6, 2018
1 parent 7a8f182 commit f703a59
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 35 deletions.
50 changes: 25 additions & 25 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"rimraf": "^2.6.2",
"semantic-release": "^11.0.2",
"tslint": "^5.8.0",
"tslint-config-unional": "^0.8.0",
"tslint-config-unional": "^0.9.0",
"typescript": "^2.6.1"
},
"dependencies": {
Expand Down
37 changes: 36 additions & 1 deletion src/spy.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test } from 'ava'

import { spy } from './index'
import { spy, spyAsync } from './index'

function increment(x: number) { return ++x }

Expand Down Expand Up @@ -52,3 +52,38 @@ test('callback are spied', t => {
t.is(cr.arguments[0], 1)
t.is(cr.arguments[1].calls[0].arguments[0], 1)
})

const resolve = x => Promise.resolve(x)

test('then() will receive result from promise', t => {
const spied = spyAsync(resolve)
// tslint:disable-next-line
spied(1)
return spied.calls[0].then(x => t.is(x, 1))
})

test('result from promise can be retrieved from await on the call', async t => {
const spied = spyAsync(resolve)
// tslint:disable-next-line
spied(1)
t.is(await spied.calls[0], 1)
})

const reject = x => Promise.reject(x)

test('throws() will receive error thrown by promise', t => {
const spied = spyAsync(reject)
// tslint:disable-next-line
spied(1)
return spied.calls[0].throws(x => t.is(x, 1))
})

test('error result is received on catch block', async t => {
const spied = spyAsync(reject)
try {
await spied(1)
}
catch (x) {
t.is(x, 1)
}
})
48 changes: 40 additions & 8 deletions src/spy.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
export interface Spy {
calls: ReadonlyArray<CallRecord>
}

export interface CallRecord {

arguments: any[],
result?: any,
error?: Error
result: any,
error: Error
}

export interface Spy {
calls: ReadonlyArray<CallRecord>
}

/**
* Spy on function that uses callback.
*/
export function spy<T extends Function>(fn: T): T & Spy {
const calls: CallRecord[] = []
const spiedFn: T = function (...args) {
const spiedArgs = args.map(a => {
return typeof a === 'function' ? spy(a) : a
})
const call: CallRecord = { arguments: spiedArgs }
const call = { arguments: spiedArgs } as CallRecord
calls.push(call)
try {
const result = fn(...spiedArgs)
Expand All @@ -32,3 +34,33 @@ export function spy<T extends Function>(fn: T): T & Spy {
calls
})
}


export interface AsyncCallRecord extends Promise<any> {
arguments: any[],
throws(errback: any): Promise<any>
}

export interface AsyncSpy {
calls: ReadonlyArray<AsyncCallRecord>
}

/**
* Spy on function that returns a promise.
*/
export function spyAsync<T extends Function>(fn: T): T & AsyncSpy {
const calls: AsyncCallRecord[] = []
const spiedFn: T = function (...args) {
const call = { arguments: args } as AsyncCallRecord
calls.push(call)
const result = fn(...args)
call.then = (cb, eb) => result.then(cb, eb)
call.catch = cb => (result.catch(cb))
call.throws = call.catch
return result
} as any

return Object.assign(spiedFn, {
calls
})
}
5 changes: 5 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
},
"buildOnSave": false,
"compileOnSave": false,
"compilerOptions": {
"plugins": [
{ "name": "tslint-language-service"}
]
},
"include": [
"src"
]
Expand Down

0 comments on commit f703a59

Please sign in to comment.