Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(types): add partial flag to mocked TypeScript helper (#1739) #1742

Merged
merged 1 commit into from Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/api/index.md
Expand Up @@ -1830,9 +1830,11 @@ Vitest provides utility functions to help you out through it's **vi** helper. Yo
### vi.mocked

- **Type**: `<T>(obj: T, deep?: boolean) => MaybeMockedDeep<T>`
- **Type**: `<T>(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep<T>`

Type helper for TypeScript. In reality just returns the object that was passed.

When `partial` is `true` it will expect a `Partial<T>` as a return value.
```ts
import example from './example'
vi.mock('./example')
Expand Down
20 changes: 20 additions & 0 deletions packages/vitest/src/integrations/spy.ts
Expand Up @@ -61,6 +61,10 @@ export interface Mock<TArgs extends any[] = any, TReturns = any> extends SpyInst
new (...args: TArgs): TReturns
(...args: TArgs): TReturns
}
export interface PartialMock<TArgs extends any[] = any, TReturns = any> extends SpyInstance<TArgs, Partial<TReturns>> {
new (...args: TArgs): TReturns
(...args: TArgs): TReturns
}

export type MaybeMockedConstructor<T> = T extends new (
...args: Array<any>
Expand All @@ -70,7 +74,11 @@ export type MaybeMockedConstructor<T> = T extends new (
export type MockedFunction<T extends Procedure> = Mock<Parameters<T>, ReturnType<T>> & {
[K in keyof T]: T[K];
}
export type PartiallyMockedFunction<T extends Procedure> = PartialMock<Parameters<T>, ReturnType<T>> & {
[K in keyof T]: T[K];
}
export type MockedFunctionDeep<T extends Procedure> = Mock<Parameters<T>, ReturnType<T>> & MockedObjectDeep<T>
export type PartiallyMockedFunctionDeep<T extends Procedure> = PartialMock<Parameters<T>, ReturnType<T>> & MockedObjectDeep<T>
export type MockedObject<T> = MaybeMockedConstructor<T> & {
[K in Methods<T>]: T[K] extends Procedure
? MockedFunction<T[K]>
Expand All @@ -88,12 +96,24 @@ export type MaybeMockedDeep<T> = T extends Procedure
? MockedObjectDeep<T>
: T

export type MaybePartiallyMockedDeep<T> = T extends Procedure
? PartiallyMockedFunctionDeep<T>
: T extends object
? MockedObjectDeep<T>
: T

export type MaybeMocked<T> = T extends Procedure
? MockedFunction<T>
: T extends object
? MockedObject<T>
: T

export type MaybePartiallyMocked<T> = T extends Procedure
? PartiallyMockedFunction<T>
: T extends object
? MockedObject<T>
: T

interface Constructable {
new (...args: any[]): any
}
Expand Down
11 changes: 9 additions & 2 deletions packages/vitest/src/integrations/vi.ts
Expand Up @@ -3,7 +3,7 @@ import { parseStacktrace } from '../utils/source-map'
import type { VitestMocker } from '../runtime/mocker'
import { getWorkerState, resetModules, setTimeout } from '../utils'
import { FakeTimers } from './mock/timers'
import type { EnhancedSpy, MaybeMocked, MaybeMockedDeep } from './spy'
import type { EnhancedSpy, MaybeMocked, MaybeMockedDeep, MaybePartiallyMocked, MaybePartiallyMockedDeep } from './spy'
import { fn, isMockFunction, spies, spyOn } from './spy'

class VitestUtils {
Expand Down Expand Up @@ -173,6 +173,8 @@ class VitestUtils {

/**
* Type helpers for TypeScript. In reality just returns the object that was passed.
*
* When `partial` is `true` it will expect a `Partial<T>` as a return value.
* @example
* import example from './example'
* vi.mock('./example')
Expand All @@ -186,10 +188,15 @@ class VitestUtils {
* })
* @param item Anything that can be mocked
* @param deep If the object is deeply mocked
* @param options If the object is partially or deeply mocked
*/
public mocked<T>(item: T, deep?: false): MaybeMocked<T>
public mocked<T>(item: T, deep: true): MaybeMockedDeep<T>
public mocked<T>(item: T, _deep = false): MaybeMocked<T> | MaybeMockedDeep<T> {
public mocked<T>(item: T, options: { partial?: false; deep?: false }): MaybeMocked<T>
public mocked<T>(item: T, options: { partial?: false; deep: true }): MaybeMockedDeep<T>
public mocked<T>(item: T, options: { partial: true; deep?: false }): MaybePartiallyMocked<T>
public mocked<T>(item: T, options: { partial: true; deep: true }): MaybePartiallyMockedDeep<T>
public mocked<T>(item: T, _options = {}): MaybeMocked<T> {
return item as any
}

Expand Down
24 changes: 24 additions & 0 deletions test/core/test/vi.spec.ts
Expand Up @@ -40,6 +40,30 @@ describe('testing vi utils', () => {
expectType<MockedFunction<() => boolean>>(vi.fn())
})

test('vi partial mocked', () => {
interface FooBar {
foo: () => void
bar: () => boolean
baz: string
}

type FooBarFactory = () => FooBar

const mockFactory: FooBarFactory = vi.fn()

vi.mocked(mockFactory, { partial: true }).mockReturnValue({
foo: vi.fn(),
})

vi.mocked(mockFactory, { partial: true, deep: false }).mockReturnValue({
bar: vi.fn(),
})

vi.mocked(mockFactory, { partial: true, deep: true }).mockReturnValue({
baz: 'baz',
})
})

// TODO: it's unstable in CI, skip until resolved
test.skip('loads unloaded module', async () => {
let mod: any
Expand Down