diff --git a/packages/spy/src/types.ts b/packages/spy/src/types.ts index e035fe3d446d..e065d3bdd530 100644 --- a/packages/spy/src/types.ts +++ b/packages/spy/src/types.ts @@ -370,28 +370,36 @@ type PartialMaybePromise = T extends Promise> ? Promise>> : Partial -export interface PartialMock - extends MockInstance< - (...args: Parameters) => PartialMaybePromise> - > { - new (...args: Parameters): ReturnType - (...args: Parameters): ReturnType -} +type PartialResultFunction = T extends Constructable + ? ({ + new (...args: ConstructorParameters): InstanceType + }) + | ({ + (this: InstanceType, ...args: ConstructorParameters): void + }) + : T extends Procedure + ? (...args: Parameters) => PartialMaybePromise> + : T + +export interface PartialMock + extends Mock< + PartialResultFunction> + : T> + > {} -export type MaybeMockedConstructor = T extends new ( - ...args: Array -) => infer R - ? Mock<(...args: ConstructorParameters) => R> +export type MaybeMockedConstructor = T extends Constructable + ? Mock : T -export type MockedFunction = Mock & { +export type MockedFunction = Mock & { [K in keyof T]: T[K]; } -export type PartiallyMockedFunction = PartialMock & { +export type PartiallyMockedFunction = PartialMock & { [K in keyof T]: T[K]; } -export type MockedFunctionDeep = Mock +export type MockedFunctionDeep = Mock & MockedObjectDeep -export type PartiallyMockedFunctionDeep = PartialMock +export type PartiallyMockedFunctionDeep = PartialMock & MockedObjectDeep export type MockedObject = MaybeMockedConstructor & { [K in Methods]: T[K] extends Procedure ? MockedFunction : T[K]; @@ -400,25 +408,25 @@ export type MockedObjectDeep = MaybeMockedConstructor & { [K in Methods]: T[K] extends Procedure ? MockedFunctionDeep : T[K]; } & { [K in Properties]: MaybeMockedDeep } -export type MaybeMockedDeep = T extends Procedure +export type MaybeMockedDeep = T extends Procedure | Constructable ? MockedFunctionDeep : T extends object ? MockedObjectDeep : T -export type MaybePartiallyMockedDeep = T extends Procedure +export type MaybePartiallyMockedDeep = T extends Procedure | Constructable ? PartiallyMockedFunctionDeep : T extends object ? MockedObjectDeep : T -export type MaybeMocked = T extends Procedure +export type MaybeMocked = T extends Procedure | Constructable ? MockedFunction : T extends object ? MockedObject : T -export type MaybePartiallyMocked = T extends Procedure +export type MaybePartiallyMocked = T extends Procedure | Constructable ? PartiallyMockedFunction : T extends object ? MockedObject @@ -428,9 +436,7 @@ export interface Constructable { new (...args: any[]): any } -export type MockedClass = MockInstance< - (...args: ConstructorParameters) => InstanceType -> & { +export type MockedClass = MockInstance & { prototype: T extends { prototype: any } ? Mocked : never } & T diff --git a/test/core/test/vi.spec.ts b/test/core/test/vi.spec.ts index b516d2a771cd..37602f31ddea 100644 --- a/test/core/test/vi.spec.ts +++ b/test/core/test/vi.spec.ts @@ -2,7 +2,7 @@ * @vitest-environment jsdom */ -import type { Mock, MockedFunction, MockedObject, MockInstance } from 'vitest' +import type { Mock, Mocked, MockedFunction, MockedObject, MockInstance } from 'vitest' import { describe, expect, expectTypeOf, test, vi } from 'vitest' import { getWorkerState } from '../../../packages/vitest/src/runtime/utils' @@ -56,33 +56,49 @@ describe('testing vi utils', () => { baz: string } - const mockFactory = vi.fn<() => FooBar>() + const mockFnFactory = vi.fn<() => FooBar>() - vi.mocked(mockFactory, { partial: true }).mockReturnValue({ + vi.mocked(mockFnFactory, { partial: true }).mockReturnValue({ foo: vi.fn(), }) - vi.mocked(mockFactory, { partial: true, deep: false }).mockReturnValue({ + vi.mocked(mockFnFactory, { partial: true, deep: false }).mockReturnValue({ bar: vi.fn(), }) - vi.mocked(mockFactory, { partial: true, deep: true }).mockReturnValue({ + vi.mocked(mockFnFactory, { partial: true, deep: true }).mockReturnValue({ baz: 'baz', }) - const mockFactoryAsync = vi.fn<() => Promise>() + if (0) { + const mockFactory = (): FooBar => ({} as FooBar) - vi.mocked(mockFactoryAsync, { partial: true }).mockResolvedValue({ - foo: vi.fn(), - }) + vi.mocked(mockFactory, { partial: true }).mockReturnValue({ + foo: vi.fn(), + }) - vi.mocked(mockFactoryAsync, { partial: true, deep: false }).mockResolvedValue({ - bar: vi.fn(), - }) + vi.mocked(mockFactory, { partial: true, deep: false }).mockReturnValue({ + bar: vi.fn(), + }) - vi.mocked(mockFactoryAsync, { partial: true, deep: true }).mockResolvedValue({ - baz: 'baz', - }) + vi.mocked(mockFactory, { partial: true, deep: true }).mockReturnValue({ + baz: 'baz', + }) + + const mockFactoryAsync = async (): Promise => ({} as FooBar) + + vi.mocked(mockFactoryAsync, { partial: true }).mockResolvedValue({ + foo: vi.fn(), + }) + + vi.mocked(mockFactoryAsync, { partial: true, deep: false }).mockResolvedValue({ + bar: vi.fn(), + }) + + vi.mocked(mockFactoryAsync, { partial: true, deep: true }).mockResolvedValue({ + baz: 'baz', + }) + } function fetchSomething(): Promise { return fetch('https://vitest.dev/') @@ -94,6 +110,33 @@ describe('testing vi utils', () => { } }) + test('vi.mocked with classes', () => { + class Foo { + constructor(public readonly bar: string) {} + + public getBar(): string { + return this.bar + } + } + class FooMock implements Mocked { + readonly barMock: Mock<() => string> = vi.fn() + + public get bar(): string { + return this.barMock() + } + + public getBar: Mock<() => string> = vi + .fn() + .mockImplementation(() => this.barMock()) + } + + // type check only + if (0) { + vi.mocked(Foo).mockImplementation(FooMock) + vi.mocked(Foo).mockImplementation(Foo) + } + }) + test('vi.fn and Mock type', () => { // use case from https://github.com/vitest-dev/vitest/issues/4723#issuecomment-1851034249