diff --git a/packages/runner/src/types/tasks.ts b/packages/runner/src/types/tasks.ts index 60b6101f7..4315baf8c 100644 --- a/packages/runner/src/types/tasks.ts +++ b/packages/runner/src/types/tasks.ts @@ -189,16 +189,32 @@ export type TestAPI = ChainableTestAPI & { K extends keyof ExtraContext ? ExtraContext[K] : never }> } +type FixtureType = T extends (context: any, use: (fixture: infer F) => any) => any ? F : T +export type Fixture< + T, + K extends keyof T, + OnlyFunction, + ExtraContext = {}, + V = FixtureType, + FN = ( + context: { + [P in keyof T | keyof ExtraContext as P extends K ? + P extends keyof ExtraContext ? P : never : P + ]: + K extends P ? K extends keyof ExtraContext ? ExtraContext[K] : V : + P extends keyof T ? T[P] : never + } & TestContext, + use: (fixture: V) => Promise + ) => Promise, +> = OnlyFunction extends true ? FN : (FN | V) export type Fixtures, ExtraContext = {}> = { - [K in keyof T]: T[K] | ((context: { - [P in keyof T | keyof ExtraContext as P extends K ? - P extends keyof ExtraContext ? P : never : P - ]: - K extends P ? K extends keyof ExtraContext ? ExtraContext[K] : never : - P extends keyof T ? T[P] : never - } & TestContext, use: (fixture: T[K]) => Promise) => Promise) + [K in keyof T]: Fixture +} | { + [K in keyof T]: Fixture } +export type InferFixturesTypes = T extends TestAPI ? C : T + type ChainableSuiteAPI = ChainableFunction< 'concurrent' | 'sequential' | 'only' | 'skip' | 'todo' | 'shuffle', [name: string | Function, factory?: SuiteFactory, options?: number | TestOptions], diff --git a/test/core/test/test-extend.test.ts b/test/core/test/test-extend.test.ts index 6da9ac6b2..106ccdd92 100644 --- a/test/core/test/test-extend.test.ts +++ b/test/core/test/test-extend.test.ts @@ -1,5 +1,7 @@ /* eslint-disable prefer-rest-params */ /* eslint-disable no-empty-pattern */ +import type { InferFixturesTypes } from '@vitest/runner' +import type { TestAPI } from 'vitest' import { afterAll, afterEach, beforeEach, describe, expect, expectTypeOf, test, vi } from 'vitest' interface Fixtures { @@ -38,6 +40,30 @@ const myTest = test }) describe('test.extend()', () => { + test('types', () => { + interface TypesContext { + number: number + array: number[] + string: string + any: any + boolean: boolean + } + + const typesTest = test.extend({ + number: 1, + array: [1, 2, 3], + async string({ }, use) { + await use('string') + }, + async any({}, use) { + await use({}) + }, + boolean: true, + }) + + expectTypeOf(typesTest).toEqualTypeOf>>() + }) + describe('basic', () => { myTest('todoList and doneList', ({ todoList, doneList, archiveList }) => { expect(todoFn).toBeCalledTimes(1) @@ -66,6 +92,7 @@ describe('test.extend()', () => { archiveList.pop() }) }) + describe('smartly init fixtures', () => { myTest('should not init any fixtures', function () { expect(todoFn).not.toBeCalled()