Skip to content

Commit 7a75bd4

Browse files
authoredJul 22, 2024··
fix(spy): fix mockImplementation for function overload and unions (#6181)
1 parent f68453f commit 7a75bd4

File tree

2 files changed

+22
-4
lines changed

2 files changed

+22
-4
lines changed
 

‎packages/spy/src/index.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ export interface MockContext<T extends Procedure> {
141141
}
142142

143143
type Procedure = (...args: any[]) => any
144+
// pick a single function type from function overloads, unions, etc...
145+
type NormalizedPrecedure<T extends Procedure> = (...args: Parameters<T>) => ReturnType<T>
144146

145147
type Methods<T> = keyof {
146148
[K in keyof T as T[K] extends Procedure ? K : never]: T[K];
@@ -204,22 +206,22 @@ export interface MockInstance<T extends Procedure = Procedure> {
204206
*
205207
* If mock was created with `vi.spyOn`, it will return `undefined` unless a custom implementation was provided.
206208
*/
207-
getMockImplementation(): T | undefined
209+
getMockImplementation(): NormalizedPrecedure<T> | undefined
208210
/**
209211
* Accepts a function that will be used as an implementation of the mock.
210212
* @example
211213
* const increment = vi.fn().mockImplementation(count => count + 1);
212214
* expect(increment(3)).toBe(4);
213215
*/
214-
mockImplementation(fn: T): this
216+
mockImplementation(fn: NormalizedPrecedure<T>): this
215217
/**
216218
* Accepts a function that will be used as a mock implementation during the next call. Can be chained so that multiple function calls produce different results.
217219
* @example
218220
* const fn = vi.fn(count => count).mockImplementationOnce(count => count + 1);
219221
* expect(fn(3)).toBe(4);
220222
* expect(fn(3)).toBe(3);
221223
*/
222-
mockImplementationOnce(fn: T): this
224+
mockImplementationOnce(fn: NormalizedPrecedure<T>): this
223225
/**
224226
* Overrides the original mock implementation temporarily while the callback is being executed.
225227
* @example
@@ -231,7 +233,7 @@ export interface MockInstance<T extends Procedure = Procedure> {
231233
*
232234
* myMockFn() // 'original'
233235
*/
234-
withImplementation<T2>(fn: T, cb: () => T2): T2 extends Promise<unknown> ? Promise<this> : this
236+
withImplementation<T2>(fn: NormalizedPrecedure<T>, cb: () => T2): T2 extends Promise<unknown> ? Promise<this> : this
235237

236238
/**
237239
* Use this if you need to return `this` context from the method without invoking actual implementation.

‎test/core/test/vi.spec.ts

+16
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,22 @@ describe('testing vi utils', () => {
152152
expectTypeOf(gSpy.mock.contexts).toEqualTypeOf<unknown[]>()
153153
})
154154

155+
test('mockImplementation types', async () => {
156+
// overload
157+
const fs = { readFileSync() {} } as any as typeof import('node:fs')
158+
vi.spyOn(fs, 'readFileSync').mockImplementation(() => 'str')
159+
vi.spyOn(fs, 'readFileSync').mockImplementation(() => Buffer.from('buf'))
160+
vi.fn(fs.readFileSync).mockImplementation(() => 'str')
161+
vi.fn(fs.readFileSync).mockImplementation(() => Buffer.from('buf'))
162+
163+
// union
164+
interface Handler {
165+
(v: number): number
166+
other: (v: number) => number
167+
}
168+
vi.fn<Handler>().mockImplementation(v => v + 1)
169+
})
170+
155171
test('can change config', () => {
156172
const state = getWorkerState()
157173
expect(state.config.hookTimeout).toBe(10000)

0 commit comments

Comments
 (0)
Please sign in to comment.