Skip to content

Commit

Permalink
feat!: don't wrap the original Promise, introduce "resolves" property (
Browse files Browse the repository at this point in the history
…#45)

* fix: Don't wrap the original Promise

* Update test/index.test.ts
  • Loading branch information
dubzzz authored May 17, 2024
1 parent 9244f27 commit 4610fe8
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 16 deletions.
29 changes: 16 additions & 13 deletions src/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ let reset = (state: SpyInternalState) => {
state.callCount = 0
state.calls = []
state.results = []
state.resolves = []
state.next = []
}
let defineState = (spy: SpyInternal) => {
Expand Down Expand Up @@ -42,6 +43,7 @@ interface SpyInternalState<A extends any[] = any[], R = any> {
callCount: number
calls: A[]
results: ResultFn<R>[]
resolves: R extends PromiseLike<infer V> ? ResultFn<V>[] : never
reset(): void
impl: ((...args: A) => R) | undefined
next: ResultFn<R>[]
Expand Down Expand Up @@ -95,6 +97,7 @@ export function createInternalSpy<A extends any[], R>(
// it can be undefined, if there is no mocking function
let result: any
let type: 'ok' | 'error' = 'ok'
const resultIndex = state.results.length
if (state.impl) {
try {
if (new.target) {
Expand All @@ -112,18 +115,10 @@ export function createInternalSpy<A extends any[], R>(
}
let resultTuple: ResultFn<R> = [type, result]
if (isPromise(result)) {
const newPromise = result
.then((r: any) => (resultTuple[1] = r))
.catch((e: any) => {
// @ts-expect-error TS for some reasons narrows down the type
resultTuple[0] = 'error'
resultTuple[1] = e
throw e
})
// we need to reassign it because if it fails, the suite will fail
// see `async error` test in `test/index.test.ts`
Object.assign(newPromise, result)
result = newPromise
result.then(
(r: any) => (state.resolves[resultIndex] = ['ok', r]),
(e: any) => (state.resolves[resultIndex] = ['error', e])
)
}
state.results.push(resultTuple)
return result
Expand All @@ -147,7 +142,15 @@ export function populateSpy<A extends any[], R>(spy: SpyInternal<A, R>) {
get: () => I.results.map(([, r]) => r),
})
;(
['called', 'callCount', 'results', 'calls', 'reset', 'impl'] as const
[
'called',
'callCount',
'results',
'resolves',
'calls',
'reset',
'impl',
] as const
).forEach((n) =>
define(spy, n, { get: () => I[n], set: (v) => (I[n] = v as never) })
)
Expand Down
44 changes: 41 additions & 3 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,10 +434,12 @@ test('async', async () => {

expect(spy.called).toBe(true)
expect(spy.results[0][1]).toBeInstanceOf(Promise)
expect(spy.results[0][1]).toBe(promise)

const count = await promise

expect(spy.results).toEqual([['ok', 1]])
expect(spy.results).toEqual([['ok', promise]])
expect(spy.resolves).toEqual([['ok', 1]])
expect(count).toBe(1)
})

Expand All @@ -453,6 +455,7 @@ test('async error', async () => {

expect(spy.called).toBe(true)
expect(spy.results[0][1]).toBeInstanceOf(Promise)
expect(spy.results[0][1]).toBe(promise)

let caughtError: null | Error = null
try {
Expand All @@ -461,12 +464,47 @@ test('async error', async () => {
caughtError = e
}

expect(spy.results[0][0]).toEqual('error')
expect(spy.results[0][1].message).toEqual('async error')
expect(spy.results).toEqual([['ok', promise]])
expect(spy.resolves[0][0]).toEqual('error')
expect(spy.resolves[0][1].message).toEqual('async error')
expect(caughtError).toBeInstanceOf(Error)
expect(caughtError?.message).toEqual('async error')
})

test('async order preserved', async () => {
let resolvePromise1: () => void = null!
const promise1 = new Promise<void>((resolve) => (resolvePromise1 = resolve))
let resolvePromise2: () => void = null!
const promise2 = new Promise<void>((resolve) => (resolvePromise2 = resolve))
let calls = 0
const obj = {
async method(value: number) {
if (calls++ === 0) {
await promise1
} else {
await promise2
}
return value
},
}

const spy = spyOn(obj, 'method')
const promiseCall1 = obj.method(1)
const promiseCall2 = obj.method(2)

expect(spy.called).toBe(true)

resolvePromise2()
await promiseCall2
resolvePromise1()
await promiseCall1

expect(spy.resolves).toEqual([
['ok', 1],
['ok', 2],
])
})

test('proto null', () => {
const obj = Object.create(null)

Expand Down

0 comments on commit 4610fe8

Please sign in to comment.