From e2d6312ad6c8672f700607b029d827be22ffcc3f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 20 Dec 2023 09:37:40 +0900 Subject: [PATCH 01/13] fix!(spy): align vi.fn and vi.Mock typings to jest --- packages/spy/src/index.ts | 116 ++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index e4a3e94c8f4e..56bc0cfdbbb3 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -22,7 +22,7 @@ interface MockResultThrow { type MockResult = MockResultReturn | MockResultThrow | MockResultIncomplete -export interface MockContext { +export interface MockContext { /** * This is an array containing all arguments for each call. One item of the array is the arguments of that call. * @@ -37,11 +37,11 @@ export interface MockContext { * ['arg3'], // second call * ] */ - calls: TArgs[] + calls: Parameters[] /** * This is an array containing all instances that were instantiated when mock was called with a `new` keyword. Note that this is an actual context (`this`) of the function, not a return value. */ - instances: TReturns[] + instances: ReturnType[] /** * The order of mock's execution. This returns an array of numbers which are shared between all defined mocks. * @@ -85,14 +85,15 @@ export interface MockContext { * }, * ] */ - results: MockResult[] + results: MockResult>[] /** * This contains the arguments of the last call. If spy wasn't called, will return `undefined`. */ - lastCall: TArgs | undefined + lastCall: Parameters | undefined } type Procedure = (...args: any[]) => any +type UnknownProcedure = (...args: unknown[]) => unknown type Methods = keyof { [K in keyof T as T[K] extends Procedure ? K : never]: T[K]; @@ -107,9 +108,9 @@ type Classes = { /** * @deprecated Use MockInstance instead */ -export interface SpyInstance extends MockInstance {} +export interface SpyInstance extends MockInstance<(...args: TArgs) => TReturns> {} -export interface MockInstance { +export interface MockInstance { /** * Use it to return the name given to mock with method `.mockName(name)`. */ @@ -121,7 +122,7 @@ export interface MockInstance { /** * Current context of the mock. It stores information about all invocation calls, instances, and results. */ - mock: MockContext + mock: MockContext /** * Clears all information about every call. After calling it, all properties on `.mock` will return an empty state. This method does not reset implementations. * @@ -147,14 +148,14 @@ export interface MockInstance { * * If mock was created with `vi.spyOn`, it will return `undefined` unless a custom implementation was provided. */ - getMockImplementation(): ((...args: TArgs) => TReturns) | undefined + getMockImplementation(): (T) | undefined /** * Accepts a function that will be used as an implementation of the mock. * @example * const increment = vi.fn().mockImplementation(count => count + 1); * expect(increment(3)).toBe(4); */ - mockImplementation(fn: ((...args: TArgs) => TReturns) | (() => Promise)): this + mockImplementation(fn: T | (() => Promise>)): this /** * 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. * @example @@ -162,7 +163,7 @@ export interface MockInstance { * expect(fn(3)).toBe(4); * expect(fn(3)).toBe(3); */ - mockImplementationOnce(fn: ((...args: TArgs) => TReturns) | (() => Promise)): this + mockImplementationOnce(fn: T | (() => Promise>)): this /** * Overrides the original mock implementation temporarily while the callback is being executed. * @example @@ -174,7 +175,7 @@ export interface MockInstance { * * myMockFn() // 'original' */ - withImplementation(fn: ((...args: TArgs) => TReturns), cb: () => T): T extends Promise ? Promise : this + withImplementation(fn: T, cb: () => T2): T2 extends Promise ? Promise : this /** * Use this if you need to return `this` context from the method without invoking actual implementation. */ @@ -182,7 +183,7 @@ export interface MockInstance { /** * Accepts a value that will be returned whenever the mock function is called. */ - mockReturnValue(obj: TReturns): this + mockReturnValue(obj: ReturnType): this /** * Accepts a value that will be returned during the next function call. If chained, every consecutive call will return the specified value. * @@ -197,14 +198,14 @@ export interface MockInstance { * // 'first call', 'second call', 'default' * console.log(myMockFn(), myMockFn(), myMockFn()) */ - mockReturnValueOnce(obj: TReturns): this + mockReturnValueOnce(obj: ReturnType): this /** * Accepts a value that will be resolved when async function is called. * @example * const asyncMock = vi.fn().mockResolvedValue(42) * asyncMock() // Promise<42> */ - mockResolvedValue(obj: Awaited): this + mockResolvedValue(obj: Awaited>): this /** * Accepts a value that will be resolved during the next function call. If chained, every consecutive call will resolve specified value. * @example @@ -217,7 +218,7 @@ export interface MockInstance { * // Promise<'first call'>, Promise<'second call'>, Promise<'default'> * console.log(myMockFn(), myMockFn(), myMockFn()) */ - mockResolvedValueOnce(obj: Awaited): this + mockResolvedValueOnce(obj: Awaited>): this /** * Accepts an error that will be rejected when async function is called. * @example @@ -239,28 +240,31 @@ export interface MockInstance { mockRejectedValueOnce(obj: any): this } -export interface Mock extends MockInstance { - new (...args: TArgs): TReturns - (...args: TArgs): TReturns +export interface Mock extends MockInstance { + new (...args: Parameters): ReturnType + (...args: Parameters): ReturnType } -export interface PartialMock extends MockInstance> ? Promise>> : Partial> { - new (...args: TArgs): TReturns - (...args: TArgs): TReturns + +type PartialMaybePromise = T extends Promise> ? Promise>> : Partial + +export interface PartialMock extends MockInstance<(...args: Parameters) => PartialMaybePromise>> { + new (...args: Parameters): ReturnType + (...args: Parameters): ReturnType } export type MaybeMockedConstructor = T extends new ( ...args: Array ) => infer R - ? Mock, R> + ? Mock<(...args: ConstructorParameters) => R> : T -export type MockedFunction = Mock, ReturnType> & { +export type MockedFunction = Mock & { [K in keyof T]: T[K]; } -export type PartiallyMockedFunction = PartialMock, ReturnType> & { +export type PartiallyMockedFunction = PartialMock & { [K in keyof T]: T[K]; } -export type MockedFunctionDeep = Mock, ReturnType> & MockedObjectDeep -export type PartiallyMockedFunctionDeep = PartialMock, ReturnType> & MockedObjectDeep +export type MockedFunctionDeep = Mock & MockedObjectDeep +export type PartiallyMockedFunctionDeep = PartialMock & MockedObjectDeep export type MockedObject = MaybeMockedConstructor & { [K in Methods]: T[K] extends Procedure ? MockedFunction @@ -300,23 +304,20 @@ interface Constructable { new (...args: any[]): any } -export type MockedClass = MockInstance< - T extends new (...args: infer P) => any ? P : never, - InstanceType -> & { +export type MockedClass = MockInstance<(...args: ConstructorParameters) => InstanceType> & { prototype: T extends { prototype: any } ? Mocked : never } & T export type Mocked = { - [P in keyof T]: T[P] extends (...args: infer Args) => infer Returns - ? MockInstance + [P in keyof T]: T[P] extends Procedure + ? MockInstance : T[P] extends Constructable ? MockedClass : T[P] } & T -export const mocks = new Set() +export const mocks = new Set>() export function isMockFunction(fn: any): fn is MockInstance { return typeof fn === 'function' @@ -328,16 +329,16 @@ export function spyOn>>( obj: T, methodName: S, accessType: 'get', -): MockInstance<[], T[S]> +): MockInstance<() => T[S]> export function spyOn>>( obj: T, methodName: G, accessType: 'set', -): MockInstance<[T[G]], void> +): MockInstance<(arg: T[G]) => void> export function spyOn> | Methods>)>( obj: T, methodName: M, -): Required[M] extends ({ new (...args: infer A): infer R }) | ((...args: infer A) => infer R) ? MockInstance : never +): Required[M] extends ({ new (...args: infer A): infer R }) | ((...args: infer A) => infer R) ? MockInstance<(...args: A) => R> : never export function spyOn( obj: T, method: K, @@ -356,12 +357,15 @@ export function spyOn( let callOrder = 0 -function enhanceSpy( - spy: SpyInternalImpl, -): MockInstance { - const stub = spy as unknown as MockInstance +function enhanceSpy( + spy: SpyInternalImpl, ReturnType>, +): MockInstance { + type TArgs = Parameters + type TReturns = ReturnType + + const stub = spy as unknown as MockInstance - let implementation: ((...args: TArgs) => TReturns) | undefined + let implementation: T | undefined let instances: any[] = [] let invocations: number[] = [] @@ -416,7 +420,7 @@ function enhanceSpy( stub.mockReset = () => { stub.mockClear() - implementation = () => undefined as unknown as TReturns + implementation = (() => undefined) as T onceImplementations = [] return stub } @@ -429,20 +433,20 @@ function enhanceSpy( } stub.getMockImplementation = () => implementation - stub.mockImplementation = (fn: (...args: TArgs) => TReturns) => { + stub.mockImplementation = (fn: T) => { implementation = fn state.willCall(mockCall) return stub } - stub.mockImplementationOnce = (fn: (...args: TArgs) => TReturns) => { + stub.mockImplementationOnce = (fn: T) => { onceImplementations.push(fn) return stub } - function withImplementation(fn: (...args: TArgs) => TReturns, cb: () => void): MockInstance - function withImplementation(fn: (...args: TArgs) => TReturns, cb: () => Promise): Promise> - function withImplementation(fn: (...args: TArgs) => TReturns, cb: () => void | Promise): MockInstance | Promise> { + function withImplementation(fn: T, cb: () => void): MockInstance + function withImplementation(fn: T, cb: () => Promise): Promise> + function withImplementation(fn: T, cb: () => void | Promise): MockInstance | Promise> { const originalImplementation = implementation implementation = fn @@ -501,16 +505,16 @@ function enhanceSpy( return stub as any } -export function fn(): Mock -export function fn( - implementation: (...args: TArgs) => R -): Mock -export function fn( - implementation?: (...args: TArgs) => R, -): Mock { +export function fn(): Mock +export function fn( + implementation: T +): Mock +export function fn( + implementation?: T, +): Mock { const enhancedSpy = enhanceSpy(tinyspy.internalSpyOn({ spy: implementation || (() => {}) }, 'spy')) if (implementation) enhancedSpy.mockImplementation(implementation) - return enhancedSpy as Mock + return enhancedSpy as Mock } From c5781fe361271bff5f6caf675e24f875f8a362f6 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 20 Dec 2023 09:57:13 +0900 Subject: [PATCH 02/13] fix: put back `any` for starter --- packages/spy/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index 56bc0cfdbbb3..45d6070e5842 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -93,7 +93,10 @@ export interface MockContext { } type Procedure = (...args: any[]) => any -type UnknownProcedure = (...args: unknown[]) => unknown + +// TODO: jest uses stricter default based on `unknown` +// type UnknownProcedure = (...args: unknown[]) => unknown +type UnknownProcedure = (...args: any[]) => any type Methods = keyof { [K in keyof T as T[K] extends Procedure ? K : never]: T[K]; From 8a13a22fa052adaa756f9959e49a7f69e5f1bd96 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 20 Dec 2023 10:00:20 +0900 Subject: [PATCH 03/13] chore: lint --- packages/spy/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index 45d6070e5842..5e39a2f078a6 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -151,7 +151,7 @@ export interface MockInstance { * * If mock was created with `vi.spyOn`, it will return `undefined` unless a custom implementation was provided. */ - getMockImplementation(): (T) | undefined + getMockImplementation(): T | undefined /** * Accepts a function that will be used as an implementation of the mock. * @example From a87d320cb5c4017270943804927ce0c1eb1d3abf Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 20 Dec 2023 10:33:03 +0900 Subject: [PATCH 04/13] chore: back to "unknown" --- packages/spy/src/index.ts | 6 +++--- test/core/test/spy.test.ts | 2 +- test/core/test/vi.spec.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index 5e39a2f078a6..994ce767e7d2 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -94,9 +94,9 @@ export interface MockContext { type Procedure = (...args: any[]) => any -// TODO: jest uses stricter default based on `unknown` -// type UnknownProcedure = (...args: unknown[]) => unknown -type UnknownProcedure = (...args: any[]) => any +// TODO: jest uses stricter default based on `unknown`, but vitest has been using `any`. +type UnknownProcedure = (...args: unknown[]) => unknown +// type UnknownProcedure = (...args: any[]) => any type Methods = keyof { [K in keyof T as T[K] extends Procedure ? K : never]: T[K]; diff --git a/test/core/test/spy.test.ts b/test/core/test/spy.test.ts index 35cddb90374a..719160540b01 100644 --- a/test/core/test/spy.test.ts +++ b/test/core/test/spy.test.ts @@ -15,7 +15,7 @@ describe('spyOn', () => { test('infers a class correctly', () => { vi.spyOn(mock, 'HelloWorld').mockImplementationOnce(() => { - const Mock = vi.fn() + const Mock = vi.fn() Mock.prototype.hello = vi.fn(() => 'hello world') return new Mock() }) diff --git a/test/core/test/vi.spec.ts b/test/core/test/vi.spec.ts index 79ea9ad5b910..cf2d9d10074f 100644 --- a/test/core/test/vi.spec.ts +++ b/test/core/test/vi.spec.ts @@ -52,14 +52,14 @@ describe('testing vi utils', () => { type FooBarFactory = () => FooBar - const mockFactory: FooBarFactory = vi.fn() + const mockFactory = vi.fn() vi.mocked(mockFactory, { partial: true }).mockReturnValue({ foo: vi.fn(), }) vi.mocked(mockFactory, { partial: true, deep: false }).mockReturnValue({ - bar: vi.fn(), + bar: vi.fn(), }) vi.mocked(mockFactory, { partial: true, deep: true }).mockReturnValue({ @@ -68,14 +68,14 @@ describe('testing vi utils', () => { type FooBarAsyncFactory = () => Promise - const mockFactoryAsync: FooBarAsyncFactory = vi.fn() + const mockFactoryAsync = vi.fn() vi.mocked(mockFactoryAsync, { partial: true }).mockResolvedValue({ foo: vi.fn(), }) vi.mocked(mockFactoryAsync, { partial: true, deep: false }).mockResolvedValue({ - bar: vi.fn(), + bar: vi.fn(), }) vi.mocked(mockFactoryAsync, { partial: true, deep: true }).mockResolvedValue({ From f79f1c8e8758998f0aadbb83419ae3ddc41eaa87 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 4 Jan 2024 08:17:18 +0900 Subject: [PATCH 05/13] chore: revert lock --- pnpm-lock.yaml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index efdc9b450406..ae3d4523d6e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,7 +143,7 @@ importers: version: 4.7.1 unocss: specifier: ^0.57.4 - version: 0.57.4(postcss@8.4.32)(rollup@2.79.1)(vite@5.0.2) + version: 0.57.4(postcss@8.4.31)(rollup@2.79.1)(vite@5.0.2) unplugin-vue-components: specifier: ^0.25.2 version: 0.25.2(rollup@2.79.1)(vue@3.3.8) @@ -155,7 +155,7 @@ importers: version: 0.16.7(vite@5.0.2)(workbox-build@7.0.0)(workbox-window@7.0.0) vitepress: specifier: ^1.0.0-rc.34 - version: 1.0.0-rc.34(@types/node@18.18.9)(postcss@8.4.32)(search-insights@2.9.0)(typescript@5.2.2) + version: 1.0.0-rc.34(@types/node@18.18.9)(postcss@8.4.31)(search-insights@2.9.0)(typescript@5.2.2) workbox-window: specifier: ^7.0.0 version: 7.0.0 @@ -1188,7 +1188,7 @@ importers: version: 3.1.5 unocss: specifier: ^0.57.4 - version: 0.57.4(postcss@8.4.32)(rollup@4.4.0)(vite@5.0.2) + version: 0.57.4(postcss@8.4.31)(rollup@4.4.0)(vite@5.0.2) unplugin-auto-import: specifier: ^0.16.7 version: 0.16.7(@vueuse/core@10.6.1)(rollup@4.4.0) @@ -6250,7 +6250,7 @@ packages: '@emotion/cache': 11.11.0 '@emotion/react': 11.10.4(@babel/core@7.23.3)(@types/react@18.2.37)(react@18.2.0) '@emotion/styled': 11.10.4(@babel/core@7.23.3)(@emotion/react@11.10.4)(@types/react@18.2.37)(react@18.2.0) - csstype: 3.1.3 + csstype: 3.1.2 prop-types: 15.8.1 react: 18.2.0 dev: false @@ -6310,7 +6310,7 @@ packages: '@mui/utils': 5.14.14(@types/react@18.2.37)(react@18.2.0) '@types/react': 18.2.37 clsx: 2.0.0 - csstype: 3.1.3 + csstype: 3.1.2 prop-types: 15.8.1 react: 18.2.0 dev: false @@ -9815,7 +9815,7 @@ packages: sirv: 2.0.4 dev: true - /@unocss/postcss@0.57.4(postcss@8.4.32): + /@unocss/postcss@0.57.4(postcss@8.4.31): resolution: {integrity: sha512-ggq8JS4rvgvW2QXjLGwg+m8e4YcmvOtbUS6C7UCrP8pmUqBCpbnTmLi6inpBbBuCN5WokecNZS5f3C4EwNMOMA==} engines: {node: '>=14'} peerDependencies: @@ -9827,7 +9827,7 @@ packages: css-tree: 2.3.1 fast-glob: 3.3.2 magic-string: 0.30.5 - postcss: 8.4.32 + postcss: 8.4.31 dev: true /@unocss/preset-attributify@0.57.4: @@ -13522,6 +13522,7 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dev: true /currently-unhandled@0.4.1: resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==} @@ -25394,7 +25395,7 @@ packages: detect-node: 2.1.0 dev: false - /unocss@0.57.4(postcss@8.4.32)(rollup@2.79.1)(vite@5.0.2): + /unocss@0.57.4(postcss@8.4.31)(rollup@2.79.1)(vite@5.0.2): resolution: {integrity: sha512-rf5eiCVb8957rqzCyRxLzljeYguVMS70X322/Z1sYhosKhh8SBBMsC/TrZEf5o8LTn/MbFN9fVizEtbUKaFjUg==} engines: {node: '>=14'} peerDependencies: @@ -25410,7 +25411,7 @@ packages: '@unocss/cli': 0.57.4(rollup@2.79.1) '@unocss/core': 0.57.4 '@unocss/extractor-arbitrary-variants': 0.57.4 - '@unocss/postcss': 0.57.4(postcss@8.4.32) + '@unocss/postcss': 0.57.4(postcss@8.4.31) '@unocss/preset-attributify': 0.57.4 '@unocss/preset-icons': 0.57.4 '@unocss/preset-mini': 0.57.4 @@ -25433,7 +25434,7 @@ packages: - supports-color dev: true - /unocss@0.57.4(postcss@8.4.32)(rollup@4.4.0)(vite@5.0.2): + /unocss@0.57.4(postcss@8.4.31)(rollup@4.4.0)(vite@5.0.2): resolution: {integrity: sha512-rf5eiCVb8957rqzCyRxLzljeYguVMS70X322/Z1sYhosKhh8SBBMsC/TrZEf5o8LTn/MbFN9fVizEtbUKaFjUg==} engines: {node: '>=14'} peerDependencies: @@ -25449,7 +25450,7 @@ packages: '@unocss/cli': 0.57.4(rollup@4.4.0) '@unocss/core': 0.57.4 '@unocss/extractor-arbitrary-variants': 0.57.4 - '@unocss/postcss': 0.57.4(postcss@8.4.32) + '@unocss/postcss': 0.57.4(postcss@8.4.31) '@unocss/preset-attributify': 0.57.4 '@unocss/preset-icons': 0.57.4 '@unocss/preset-mini': 0.57.4 @@ -26030,7 +26031,7 @@ packages: vite: 5.0.2(@types/node@18.18.9)(less@4.1.3) dev: true - /vitepress@1.0.0-rc.34(@types/node@18.18.9)(postcss@8.4.32)(search-insights@2.9.0)(typescript@5.2.2): + /vitepress@1.0.0-rc.34(@types/node@18.18.9)(postcss@8.4.31)(search-insights@2.9.0)(typescript@5.2.2): resolution: {integrity: sha512-TUbTiSdAZFni2XlHlpx61KikgkQ5uG4Wtmw2R0SXhIOG6qGqzDJczAFjkMc4i45I9c3KyatwOYe8oEfCnzVYwQ==} hasBin: true peerDependencies: @@ -26053,7 +26054,7 @@ packages: mark.js: 8.11.1 minisearch: 6.3.0 mrmime: 2.0.0 - postcss: 8.4.32 + postcss: 8.4.31 shikiji: 0.9.17 shikiji-core: 0.9.17 shikiji-transformers: 0.9.17 From 5efee9510de7d6dd1ef9808e5a997c4826705f18 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 4 Jan 2024 08:21:37 +0900 Subject: [PATCH 06/13] chore: more any type --- packages/spy/src/index.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index 41bce785fe78..d9065f86401e 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -478,24 +478,24 @@ function enhanceSpy( stub.withImplementation = withImplementation stub.mockReturnThis = () => - stub.mockImplementation(function (this: TReturns) { + stub.mockImplementation((function (this: TReturns) { return this - }) + }) as any) - stub.mockReturnValue = (val: TReturns) => stub.mockImplementation(() => val) - stub.mockReturnValueOnce = (val: TReturns) => stub.mockImplementationOnce(() => val) + stub.mockReturnValue = (val: TReturns) => stub.mockImplementation((() => val) as any) + stub.mockReturnValueOnce = (val: TReturns) => stub.mockImplementationOnce((() => val) as any) stub.mockResolvedValue = (val: Awaited) => - stub.mockImplementation(() => Promise.resolve(val as TReturns) as any) + stub.mockImplementation((() => Promise.resolve(val as TReturns)) as any) stub.mockResolvedValueOnce = (val: Awaited) => - stub.mockImplementationOnce(() => Promise.resolve(val as TReturns) as any) + stub.mockImplementationOnce((() => Promise.resolve(val as TReturns)) as any) stub.mockRejectedValue = (val: unknown) => - stub.mockImplementation(() => Promise.reject(val) as any) + stub.mockImplementation((() => Promise.reject(val)) as any) stub.mockRejectedValueOnce = (val: unknown) => - stub.mockImplementationOnce(() => Promise.reject(val) as any) + stub.mockImplementationOnce((() => Promise.reject(val)) as any) Object.defineProperty(stub, 'mock', { get: () => mockContext, From a3bdb7a5059c4a7fa4afdf749a06531f2d3e5ac5 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 23 Apr 2024 10:38:16 +0900 Subject: [PATCH 07/13] fix: fix types --- packages/spy/src/index.ts | 27 ++++++++++----------------- packages/vitest/src/types/index.ts | 1 - 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index 7747f4dc4c08..136ad99e2cd5 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -92,11 +92,9 @@ export interface MockContext { lastCall: Parameters | undefined } -type Procedure = (...args: any[]) => any - // TODO: jest uses stricter default based on `unknown`, but vitest has been using `any`. -type UnknownProcedure = (...args: unknown[]) => unknown -// type UnknownProcedure = (...args: any[]) => any +// type Procedure = (...args: unknown[]) => unknown +type Procedure = (...args: any[]) => any type Methods = keyof { [K in keyof T as T[K] extends Procedure ? K : never]: T[K]; @@ -108,12 +106,7 @@ type Classes = { [K in keyof T]: T[K] extends new (...args: any[]) => any ? K : never }[keyof T] & (string | symbol) -/** - * @deprecated Use MockInstance instead - */ -export interface SpyInstance extends MockInstance<(...args: TArgs) => TReturns> {} - -export interface MockInstance { +export interface MockInstance { /** * Use it to return the name given to mock with method `.mockName(name)`. */ @@ -243,14 +236,14 @@ export interface MockInstance { mockRejectedValueOnce: (obj: any) => this } -export interface Mock extends MockInstance { +export interface Mock extends MockInstance { new (...args: Parameters): ReturnType (...args: Parameters): ReturnType } type PartialMaybePromise = T extends Promise> ? Promise>> : Partial -export interface PartialMock extends MockInstance<(...args: Parameters) => PartialMaybePromise>> { +export interface PartialMock extends MockInstance<(...args: Parameters) => PartialMaybePromise>> { new (...args: Parameters): ReturnType (...args: Parameters): ReturnType } @@ -503,21 +496,21 @@ function enhanceSpy( state.willCall(mockCall) - mocks.add(stub) + mocks.add(stub as MockInstance) return stub as any } -export function fn(): Mock -export function fn( +export function fn(): Mock +export function fn( implementation: T ): Mock -export function fn( +export function fn( implementation?: T, ): Mock { const enhancedSpy = enhanceSpy(tinyspy.internalSpyOn({ spy: implementation || (() => {}) }, 'spy')) if (implementation) enhancedSpy.mockImplementation(implementation) - return enhancedSpy as Mock + return enhancedSpy as any } diff --git a/packages/vitest/src/types/index.ts b/packages/vitest/src/types/index.ts index 3da526a29867..05eaef50742d 100644 --- a/packages/vitest/src/types/index.ts +++ b/packages/vitest/src/types/index.ts @@ -17,7 +17,6 @@ export type { DiffOptions } from '@vitest/utils/diff' export type { MockedFunction, MockedObject, - SpyInstance, MockInstance, Mock, MockContext, From e034a05709a19508481867fb84945c9b47891f4b Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 23 Apr 2024 11:03:25 +0900 Subject: [PATCH 08/13] test: fix and add --- test/config/test/resolution.test.ts | 2 +- test/core/test/mock-internals.test.ts | 4 +-- test/core/test/vi.spec.ts | 45 ++++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/test/config/test/resolution.test.ts b/test/config/test/resolution.test.ts index f047d7d44760..5b5c99fb2eaf 100644 --- a/test/config/test/resolution.test.ts +++ b/test/config/test/resolution.test.ts @@ -305,7 +305,7 @@ describe.each([ await expect(async () => { await config(rawConfig.options) }).rejects.toThrowError() - expect(error.mock.lastCall[0]).toEqual( + expect(error.mock.lastCall?.[0]).toEqual( expect.stringContaining(`Inspector host cannot be a URL. Use "host:port" instead of "${url}"`), ) }) diff --git a/test/core/test/mock-internals.test.ts b/test/core/test/mock-internals.test.ts index ea3ac225d36f..246c691093c1 100644 --- a/test/core/test/mock-internals.test.ts +++ b/test/core/test/mock-internals.test.ts @@ -1,6 +1,6 @@ import childProcess, { exec } from 'node:child_process' import timers from 'node:timers' -import { type SpyInstance, afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { type MockInstance, afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import { execDefault, execHelloWorld, execImportAll } from '../src/exec' import { dynamicImport } from '../src/dynamic-import' @@ -31,7 +31,7 @@ test('mocked dynamically imported packages', async () => { describe('Math.random', () => { describe('mock is restored', () => { - let spy: SpyInstance + let spy: MockInstance beforeEach(() => { spy = vi.spyOn(Math, 'random').mockReturnValue(0.1) diff --git a/test/core/test/vi.spec.ts b/test/core/test/vi.spec.ts index cf2d9d10074f..a8de4d38b488 100644 --- a/test/core/test/vi.spec.ts +++ b/test/core/test/vi.spec.ts @@ -2,8 +2,8 @@ * @vitest-environment jsdom */ -import type { MockedFunction, MockedObject } from 'vitest' -import { describe, expect, test, vi } from 'vitest' +import type { Mock, MockedFunction, MockedObject } from 'vitest' +import { describe, expect, expectTypeOf, test, vi } from 'vitest' import { getWorkerState } from '../../../packages/vitest/src/utils' function expectType(obj: T) { @@ -36,11 +36,14 @@ describe('testing vi utils', () => { }) test('vi mocked', () => { - expectType boolean }>>({ + // TODO: constant `true` isn't assignable `boolean`. + // check what Jest does. + expectType true }>>({ bar: vi.fn(() => true), }) - expectType boolean>>(vi.fn(() => true)) + expectType true>>(vi.fn(() => true)) expectType boolean>>(vi.fn()) + expectType true>>(vi.fn(() => true)) }) test('vi partial mocked', () => { @@ -83,6 +86,40 @@ describe('testing vi utils', () => { }) }) + test('vi.fn and Mock type', () => { + // use case from https://github.com/vitest-dev/vitest/issues/4723#issuecomment-1851034249 + + // library to be tested + type SomeFn = (v: string) => number + function acceptSomeFn(f: SomeFn) { + f('hi') + } + + // SETUP + + // no args are allowed even though it's not type safe + const someFn1: Mock = vi.fn() + + // argument type is infered + const someFn2: Mock = vi.fn((v) => { + expectTypeOf(v).toEqualTypeOf() + return 0 + }) + + // @ts-expect-error argument is required + const someFn3: Mock = vi.fn(() => 0) + + // @ts-expect-error wrong return type + const someFn4: Mock = vi.fn(_ => '0') + + // TEST + acceptSomeFn(someFn1) + expect(someFn1).toBeCalledWith('hi') + expect(someFn2).not.toBeCalled() + expect(someFn3).not.toBeCalled() + expect(someFn4).not.toBeCalled() + }) + test('can change config', () => { const state = getWorkerState() expect(state.config.hookTimeout).toBe(10000) From eb1c442793334c9acef945ec2e7aaf6f85c5d7f4 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 23 Apr 2024 14:05:22 +0900 Subject: [PATCH 09/13] fix: give up "ts/method-signature-style" --- packages/spy/src/index.ts | 41 ++++++++++++++++++++++++--------------- test/core/test/vi.spec.ts | 16 ++++++--------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index 136ad99e2cd5..3227b5aafb57 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -92,8 +92,7 @@ export interface MockContext { lastCall: Parameters | undefined } -// TODO: jest uses stricter default based on `unknown`, but vitest has been using `any`. -// type Procedure = (...args: unknown[]) => unknown +// TODO: used `(...args: unknown[]) => unknown` for stricter default like jest? type Procedure = (...args: any[]) => any type Methods = keyof { @@ -106,6 +105,18 @@ type Classes = { [K in keyof T]: T[K] extends new (...args: any[]) => any ? K : never }[keyof T] & (string | symbol) +/* +cf. https://typescript-eslint.io/rules/method-signature-style/ + +Typescript assignability is different between + { foo: (f: T) => void } (this is "method-signature-style") +and + { foo(f: T): void } + +Jest uses the latter for `MockInstance.mockImplementation` etc... and it allows assignment such as: + const boolFn: Jest.Mock<() => boolean> = jest.fn(() => true) +*/ +/* eslint-disable ts/method-signature-style */ export interface MockInstance { /** * Use it to return the name given to mock with method `.mockName(name)`. @@ -151,7 +162,7 @@ export interface MockInstance { * const increment = vi.fn().mockImplementation(count => count + 1); * expect(increment(3)).toBe(4); */ - mockImplementation: (fn: T) => this + mockImplementation(fn: T): this /** * 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. * @example @@ -159,7 +170,7 @@ export interface MockInstance { * expect(fn(3)).toBe(4); * expect(fn(3)).toBe(3); */ - mockImplementationOnce: (fn: T) => this + mockImplementationOnce(fn: T): this /** * Overrides the original mock implementation temporarily while the callback is being executed. * @example @@ -171,15 +182,16 @@ export interface MockInstance { * * myMockFn() // 'original' */ - withImplementation: (fn: T, cb: () => T2) => T2 extends Promise ? Promise : this + withImplementation(fn: T, cb: () => T2): T2 extends Promise ? Promise : this + /** * Use this if you need to return `this` context from the method without invoking actual implementation. */ - mockReturnThis: () => this + mockReturnThis(): this /** * Accepts a value that will be returned whenever the mock function is called. */ - mockReturnValue: (obj: ReturnType) => this + mockReturnValue(obj: ReturnType): this /** * Accepts a value that will be returned during the next function call. If chained, every consecutive call will return the specified value. * @@ -194,14 +206,14 @@ export interface MockInstance { * // 'first call', 'second call', 'default' * console.log(myMockFn(), myMockFn(), myMockFn()) */ - mockReturnValueOnce: (obj: ReturnType) => this + mockReturnValueOnce(obj: ReturnType): this /** * Accepts a value that will be resolved when async function is called. * @example * const asyncMock = vi.fn().mockResolvedValue(42) * asyncMock() // Promise<42> */ - mockResolvedValue: (obj: Awaited>) => this + mockResolvedValue(obj: Awaited>): this /** * Accepts a value that will be resolved during the next function call. If chained, every consecutive call will resolve specified value. * @example @@ -214,14 +226,14 @@ export interface MockInstance { * // Promise<'first call'>, Promise<'second call'>, Promise<'default'> * console.log(myMockFn(), myMockFn(), myMockFn()) */ - mockResolvedValueOnce: (obj: Awaited>) => this + mockResolvedValueOnce(obj: Awaited>): this /** * Accepts an error that will be rejected when async function is called. * @example * const asyncMock = vi.fn().mockRejectedValue(new Error('Async error')) * await asyncMock() // throws 'Async error' */ - mockRejectedValue: (obj: any) => this + mockRejectedValue(obj: any): this /** * Accepts a value that will be rejected during the next function call. If chained, every consecutive call will reject specified value. * @example @@ -233,8 +245,9 @@ export interface MockInstance { * await asyncMock() // first call * await asyncMock() // throws "Async error" */ - mockRejectedValueOnce: (obj: any) => this + mockRejectedValueOnce(obj: any): this } +/* eslint-enable ts/method-signature-style */ export interface Mock extends MockInstance { new (...args: Parameters): ReturnType @@ -501,10 +514,6 @@ function enhanceSpy( return stub as any } -export function fn(): Mock -export function fn( - implementation: T -): Mock export function fn( implementation?: T, ): Mock { diff --git a/test/core/test/vi.spec.ts b/test/core/test/vi.spec.ts index a8de4d38b488..193baaf39b8d 100644 --- a/test/core/test/vi.spec.ts +++ b/test/core/test/vi.spec.ts @@ -36,14 +36,11 @@ describe('testing vi utils', () => { }) test('vi mocked', () => { - // TODO: constant `true` isn't assignable `boolean`. - // check what Jest does. - expectType true }>>({ + expectType boolean }>>({ bar: vi.fn(() => true), }) - expectType true>>(vi.fn(() => true)) + expectType boolean>>(vi.fn(() => true)) expectType boolean>>(vi.fn()) - expectType true>>(vi.fn(() => true)) }) test('vi partial mocked', () => { @@ -89,28 +86,27 @@ describe('testing vi utils', () => { test('vi.fn and Mock type', () => { // use case from https://github.com/vitest-dev/vitest/issues/4723#issuecomment-1851034249 - // library to be tested + // hypotetical library to be tested type SomeFn = (v: string) => number function acceptSomeFn(f: SomeFn) { f('hi') } // SETUP - // no args are allowed even though it's not type safe const someFn1: Mock = vi.fn() - // argument type is infered + // argument types are infered const someFn2: Mock = vi.fn((v) => { expectTypeOf(v).toEqualTypeOf() return 0 }) - // @ts-expect-error argument is required + // arguments are not necessary const someFn3: Mock = vi.fn(() => 0) // @ts-expect-error wrong return type - const someFn4: Mock = vi.fn(_ => '0') + const someFn4: Mock = vi.fn(() => '0') // TEST acceptSomeFn(someFn1) From d0903b67844ca48394948d5086b006ef9c765616 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 23 Apr 2024 14:07:57 +0900 Subject: [PATCH 10/13] chore: comment --- packages/spy/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index 3227b5aafb57..a84b4b7cff8d 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -109,9 +109,9 @@ type Classes = { cf. https://typescript-eslint.io/rules/method-signature-style/ Typescript assignability is different between - { foo: (f: T) => void } (this is "method-signature-style") + { foo: (f: T) => U } (this is "method-signature-style") and - { foo(f: T): void } + { foo(f: T): U } Jest uses the latter for `MockInstance.mockImplementation` etc... and it allows assignment such as: const boolFn: Jest.Mock<() => boolean> = jest.fn(() => true) From fef7e5f076e73313a5535e9a60f26d13bedac699 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 23 Apr 2024 14:13:27 +0900 Subject: [PATCH 11/13] chore: cleanup --- packages/spy/src/index.ts | 18 +++++++++--------- test/core/test/spy.test.ts | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index a84b4b7cff8d..147a195c2ab5 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -92,7 +92,7 @@ export interface MockContext { lastCall: Parameters | undefined } -// TODO: used `(...args: unknown[]) => unknown` for stricter default like jest? +// TODO: use `(...args: unknown[]) => unknown` for stricter default like jest? type Procedure = (...args: any[]) => any type Methods = keyof { @@ -121,11 +121,11 @@ export interface MockInstance { /** * Use it to return the name given to mock with method `.mockName(name)`. */ - getMockName: () => string + getMockName(): string /** * Sets internal mock name. Useful to see the name of the mock if an assertion fails. */ - mockName: (n: string) => this + mockName(n: string): this /** * Current context of the mock. It stores information about all invocation calls, instances, and results. */ @@ -135,19 +135,19 @@ export interface MockInstance { * * It is useful if you need to clean up mock between different assertions. */ - mockClear: () => this + mockClear(): this /** * Does what `mockClear` does and makes inner implementation an empty function (returning `undefined` when invoked). This also resets all "once" implementations. * * This is useful when you want to completely reset a mock to the default state. */ - mockReset: () => this + mockReset(): this /** * Does what `mockReset` does and restores inner implementation to the original function. * * Note that restoring mock from `vi.fn()` will set implementation to an empty function that returns `undefined`. Restoring a `vi.fn(impl)` will restore implementation to `impl`. */ - mockRestore: () => void + mockRestore(): void /** * Returns current mock implementation if there is one. * @@ -155,7 +155,7 @@ export interface MockInstance { * * If mock was created with `vi.spyOn`, it will return `undefined` unless a custom implementation was provided. */ - getMockImplementation: () => T | undefined + getMockImplementation(): T | undefined /** * Accepts a function that will be used as an implementation of the mock. * @example @@ -326,7 +326,7 @@ export type Mocked = { } & T -export const mocks = new Set>() +export const mocks = new Set() export function isMockFunction(fn: any): fn is MockInstance { return typeof fn === 'function' @@ -509,7 +509,7 @@ function enhanceSpy( state.willCall(mockCall) - mocks.add(stub as MockInstance) + mocks.add(stub) return stub as any } diff --git a/test/core/test/spy.test.ts b/test/core/test/spy.test.ts index 719160540b01..35cddb90374a 100644 --- a/test/core/test/spy.test.ts +++ b/test/core/test/spy.test.ts @@ -15,7 +15,7 @@ describe('spyOn', () => { test('infers a class correctly', () => { vi.spyOn(mock, 'HelloWorld').mockImplementationOnce(() => { - const Mock = vi.fn() + const Mock = vi.fn() Mock.prototype.hello = vi.fn(() => 'hello world') return new Mock() }) From cb777f14f2bfaef0da98e20058a6c658e92fc4ad Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 23 Apr 2024 14:20:36 +0900 Subject: [PATCH 12/13] chore: cleanup --- packages/spy/src/index.ts | 2 +- test/core/test/vi.spec.ts | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index 147a195c2ab5..db22a60ccd3a 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -114,7 +114,7 @@ and { foo(f: T): U } Jest uses the latter for `MockInstance.mockImplementation` etc... and it allows assignment such as: - const boolFn: Jest.Mock<() => boolean> = jest.fn(() => true) + const boolFn: Jest.Mock<() => boolean> = jest.fn<() => true>(() => true) */ /* eslint-disable ts/method-signature-style */ export interface MockInstance { diff --git a/test/core/test/vi.spec.ts b/test/core/test/vi.spec.ts index 193baaf39b8d..86cacc412462 100644 --- a/test/core/test/vi.spec.ts +++ b/test/core/test/vi.spec.ts @@ -50,32 +50,28 @@ describe('testing vi utils', () => { baz: string } - type FooBarFactory = () => FooBar - - const mockFactory = vi.fn() + const mockFactory = vi.fn<() => FooBar>() vi.mocked(mockFactory, { partial: true }).mockReturnValue({ foo: vi.fn(), }) vi.mocked(mockFactory, { partial: true, deep: false }).mockReturnValue({ - bar: vi.fn(), + bar: vi.fn(), }) vi.mocked(mockFactory, { partial: true, deep: true }).mockReturnValue({ baz: 'baz', }) - type FooBarAsyncFactory = () => Promise - - const mockFactoryAsync = vi.fn() + const mockFactoryAsync = vi.fn<() => Promise>() vi.mocked(mockFactoryAsync, { partial: true }).mockResolvedValue({ foo: vi.fn(), }) vi.mocked(mockFactoryAsync, { partial: true, deep: false }).mockResolvedValue({ - bar: vi.fn(), + bar: vi.fn(), }) vi.mocked(mockFactoryAsync, { partial: true, deep: true }).mockResolvedValue({ @@ -105,7 +101,7 @@ describe('testing vi utils', () => { // arguments are not necessary const someFn3: Mock = vi.fn(() => 0) - // @ts-expect-error wrong return type + // @ts-expect-error wrong return type will be caught const someFn4: Mock = vi.fn(() => '0') // TEST From 7b59e10a8305acc4c31f31fa9ca063b94d860159 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 23 Apr 2024 16:13:32 +0900 Subject: [PATCH 13/13] feat: replace `any` with `unknown` for default types --- packages/spy/src/index.ts | 10 +++++----- test/core/test/spy.test.ts | 2 +- test/core/test/vi.spec.ts | 13 +++++++++++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index db22a60ccd3a..a9dfcc70fe04 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -92,8 +92,8 @@ export interface MockContext { lastCall: Parameters | undefined } -// TODO: use `(...args: unknown[]) => unknown` for stricter default like jest? type Procedure = (...args: any[]) => any +type UnknownProcedure = (...args: unknown[]) => unknown type Methods = keyof { [K in keyof T as T[K] extends Procedure ? K : never]: T[K]; @@ -117,7 +117,7 @@ Jest uses the latter for `MockInstance.mockImplementation` etc... and it allows const boolFn: Jest.Mock<() => boolean> = jest.fn<() => true>(() => true) */ /* eslint-disable ts/method-signature-style */ -export interface MockInstance { +export interface MockInstance { /** * Use it to return the name given to mock with method `.mockName(name)`. */ @@ -249,14 +249,14 @@ export interface MockInstance { } /* eslint-enable ts/method-signature-style */ -export interface Mock extends MockInstance { +export interface Mock extends MockInstance { new (...args: Parameters): ReturnType (...args: Parameters): ReturnType } type PartialMaybePromise = T extends Promise> ? Promise>> : Partial -export interface PartialMock extends MockInstance<(...args: Parameters) => PartialMaybePromise>> { +export interface PartialMock extends MockInstance<(...args: Parameters) => PartialMaybePromise>> { new (...args: Parameters): ReturnType (...args: Parameters): ReturnType } @@ -514,7 +514,7 @@ function enhanceSpy( return stub as any } -export function fn( +export function fn( implementation?: T, ): Mock { const enhancedSpy = enhanceSpy(tinyspy.internalSpyOn({ spy: implementation || (() => {}) }, 'spy')) diff --git a/test/core/test/spy.test.ts b/test/core/test/spy.test.ts index 35cddb90374a..719160540b01 100644 --- a/test/core/test/spy.test.ts +++ b/test/core/test/spy.test.ts @@ -15,7 +15,7 @@ describe('spyOn', () => { test('infers a class correctly', () => { vi.spyOn(mock, 'HelloWorld').mockImplementationOnce(() => { - const Mock = vi.fn() + const Mock = vi.fn() Mock.prototype.hello = vi.fn(() => 'hello world') return new Mock() }) diff --git a/test/core/test/vi.spec.ts b/test/core/test/vi.spec.ts index 86cacc412462..5de26f05f495 100644 --- a/test/core/test/vi.spec.ts +++ b/test/core/test/vi.spec.ts @@ -41,6 +41,15 @@ describe('testing vi utils', () => { }) expectType boolean>>(vi.fn(() => true)) expectType boolean>>(vi.fn()) + + expectType boolean>>(vi.fn<() => boolean>(() => true)) + expectType boolean>>(vi.fn<() => boolean>(() => true)) + expectType<() => boolean>(vi.fn(() => true)) + + // @ts-expect-error default unkonwn + expectType<(v: number) => boolean>(vi.fn()) + + expectType<(v: number) => boolean>(vi.fn()) }) test('vi partial mocked', () => { @@ -57,7 +66,7 @@ describe('testing vi utils', () => { }) vi.mocked(mockFactory, { partial: true, deep: false }).mockReturnValue({ - bar: vi.fn(), + bar: vi.fn(), }) vi.mocked(mockFactory, { partial: true, deep: true }).mockReturnValue({ @@ -71,7 +80,7 @@ describe('testing vi utils', () => { }) vi.mocked(mockFactoryAsync, { partial: true, deep: false }).mockResolvedValue({ - bar: vi.fn(), + bar: vi.fn(), }) vi.mocked(mockFactoryAsync, { partial: true, deep: true }).mockResolvedValue({