@vitest/runner 4.1.0-beta.3 → 4.1.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- import { DiffOptions } from '@vitest/utils/diff';
2
1
  import { TestError, Awaitable } from '@vitest/utils';
2
+ import { DiffOptions } from '@vitest/utils/diff';
3
3
 
4
4
  /**
5
5
  * This is a subset of Vitest config that's required for the runner to work.
@@ -7,10 +7,10 @@ import { TestError, Awaitable } from '@vitest/utils';
7
7
  interface VitestRunnerConfig {
8
8
  root: string;
9
9
  setupFiles: string[];
10
- name?: string;
10
+ name: string | undefined;
11
11
  passWithNoTests: boolean;
12
- testNamePattern?: RegExp;
13
- allowOnly?: boolean;
12
+ testNamePattern: RegExp | undefined;
13
+ allowOnly: boolean;
14
14
  sequence: {
15
15
  shuffle?: boolean;
16
16
  concurrent?: boolean;
@@ -18,17 +18,17 @@ interface VitestRunnerConfig {
18
18
  hooks: SequenceHooks;
19
19
  setupFiles: SequenceSetupFiles;
20
20
  };
21
- chaiConfig?: {
21
+ chaiConfig: {
22
22
  truncateThreshold?: number;
23
- };
23
+ } | undefined;
24
24
  maxConcurrency: number;
25
25
  testTimeout: number;
26
26
  hookTimeout: number;
27
27
  retry: SerializableRetry;
28
- includeTaskLocation?: boolean;
28
+ includeTaskLocation: boolean | undefined;
29
29
  diffOptions?: DiffOptions;
30
30
  tags: TestTagDefinition[];
31
- tagsFilter?: string[];
31
+ tagsFilter: string[] | undefined;
32
32
  strictTags: boolean;
33
33
  }
34
34
  /**
@@ -187,10 +187,6 @@ interface VitestRunner {
187
187
  * The current Vite environment that processes the files on the server.
188
188
  */
189
189
  viteEnvironment?: string;
190
- /**
191
- * Return the worker context for fixtures specified with `scope: 'worker'`
192
- */
193
- getWorkerContext?: () => Record<string, unknown>;
194
190
  onCleanupWorkerContext?: (cleanup: () => unknown) => void;
195
191
  trace?<T>(name: string, cb: () => T): T;
196
192
  trace?<T>(name: string, attributes: Record<string, any>, cb: () => T): T;
@@ -200,18 +196,34 @@ interface VitestRunner {
200
196
  _currentTaskTimeout?: number;
201
197
  }
202
198
 
203
- interface FixtureItem extends FixtureOptions {
204
- prop: string;
205
- value: any;
199
+ interface TestFixtureItem extends FixtureOptions {
200
+ name: string;
201
+ value: unknown;
206
202
  scope: "test" | "file" | "worker";
207
- /**
208
- * Indicates whether the fixture is a function
209
- */
210
- isFn: boolean;
211
- /**
212
- * The dependencies(fixtures) of current fixture function.
213
- */
214
- deps?: FixtureItem[];
203
+ deps: Set<string>;
204
+ parent?: TestFixtureItem;
205
+ }
206
+ type UserFixtures = Record<string, unknown>;
207
+ type FixtureRegistrations = Map<string, TestFixtureItem>;
208
+ declare class TestFixtures {
209
+ private _suiteContexts;
210
+ private _overrides;
211
+ private _registrations;
212
+ private static _definitions;
213
+ private static _builtinFixtures;
214
+ private static _fixtureOptionKeys;
215
+ private static _fixtureScopes;
216
+ private static _workerContextSymbol;
217
+ static clearDefinitions(): void;
218
+ static getWorkerContexts(): Record<string, any>[];
219
+ static getFileContexts(file: File): Record<string, any>[];
220
+ constructor(registrations?: FixtureRegistrations);
221
+ extend(runner: VitestRunner, userFixtures: UserFixtures): TestFixtures;
222
+ get(suite: Suite): FixtureRegistrations;
223
+ override(runner: VitestRunner, userFixtures: UserFixtures): void;
224
+ getFileContext(file: File): Record<string, any>;
225
+ getWorkerContext(): Record<string, any>;
226
+ private parseUserFixtures;
215
227
  }
216
228
 
217
229
  /**
@@ -231,7 +243,7 @@ interface FixtureItem extends FixtureOptions {
231
243
  * });
232
244
  * ```
233
245
  */
234
- declare function beforeAll(fn: BeforeAllListener, timeout?: number): void;
246
+ declare function beforeAll<ExtraContext = object>(this: unknown, fn: BeforeAllListener<ExtraContext>, timeout?: number): void;
235
247
  /**
236
248
  * Registers a callback function to be executed once after all tests within the current suite have completed.
237
249
  * This hook is useful for scenarios where you need to perform cleanup operations after all tests in a suite have run, such as closing database connections or cleaning up temporary files.
@@ -249,7 +261,7 @@ declare function beforeAll(fn: BeforeAllListener, timeout?: number): void;
249
261
  * });
250
262
  * ```
251
263
  */
252
- declare function afterAll(fn: AfterAllListener, timeout?: number): void;
264
+ declare function afterAll<ExtraContext = object>(this: unknown, fn: AfterAllListener<ExtraContext>, timeout?: number): void;
253
265
  /**
254
266
  * Registers a callback function to be executed before each test within the current suite.
255
267
  * This hook is useful for scenarios where you need to reset or reinitialize the test environment before each test runs, such as resetting database states, clearing caches, or reinitializing variables.
@@ -338,9 +350,6 @@ declare const onTestFinished: TaskHook<OnTestFinishedHandler>;
338
350
  * **Note:** When multiple `aroundAll` hooks are registered, they are nested inside each other.
339
351
  * The first registered hook is the outermost wrapper.
340
352
  *
341
- * **Note:** Unlike `aroundEach`, the `aroundAll` hook does not receive test context or support fixtures,
342
- * as it runs at the suite level before any individual test context is created.
343
- *
344
353
  * @param {Function} fn - The callback function that wraps the suite. Must call `runSuite()` to run the tests.
345
354
  * @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
346
355
  * @returns {void}
@@ -353,13 +362,13 @@ declare const onTestFinished: TaskHook<OnTestFinishedHandler>;
353
362
  * ```
354
363
  * @example
355
364
  * ```ts
356
- * // Example of using aroundAll with AsyncLocalStorage context
357
- * aroundAll(async (runSuite) => {
358
- * await asyncLocalStorage.run({ suiteId: 'my-suite' }, runSuite);
365
+ * // Example of using aroundAll with fixtures
366
+ * aroundAll(async (runSuite, { db }) => {
367
+ * await db.transaction(() => runSuite());
359
368
  * });
360
369
  * ```
361
370
  */
362
- declare function aroundAll(fn: AroundAllListener, timeout?: number): void;
371
+ declare function aroundAll<ExtraContext = object>(this: unknown, fn: AroundAllListener<ExtraContext>, timeout?: number): void;
363
372
  /**
364
373
  * Registers a callback function that wraps around each test within the current suite.
365
374
  * The callback receives a `runTest` function that must be called to run the test.
@@ -399,7 +408,7 @@ declare function createChainable<
399
408
  T extends string,
400
409
  Args extends any[],
401
410
  R = any
402
- >(keys: T[], fn: (this: Record<T, any>, ...args: Args) => R): ChainableFunction<T, (...args: Args) => R>;
411
+ >(keys: T[], fn: (this: Record<T, any>, ...args: Args) => R, context?: Record<string, any>): ChainableFunction<T, (...args: Args) => R>;
403
412
 
404
413
  type RunMode = "run" | "skip" | "only" | "todo" | "queued";
405
414
  type TaskState = RunMode | "pass" | "fail";
@@ -501,10 +510,7 @@ interface TaskBase {
501
510
  * `includeTaskLocation` option is set. It is generated by calling `new Error`
502
511
  * and parsing the stack trace, so the location might differ depending on the runtime.
503
512
  */
504
- location?: {
505
- line: number;
506
- column: number;
507
- };
513
+ location?: Location;
508
514
  /**
509
515
  * If the test was collected by parsing the file AST, and the name
510
516
  * is not a static string, this property will be set to `true`.
@@ -828,17 +834,143 @@ interface ExtendedAPI<ExtraContext> {
828
834
  runIf: (condition: any) => ChainableTestAPI<ExtraContext>;
829
835
  }
830
836
  interface Hooks<ExtraContext> {
831
- beforeAll: typeof beforeAll;
832
- afterAll: typeof afterAll;
837
+ /**
838
+ * Suite-level hooks only receive file/worker scoped fixtures.
839
+ * Test-scoped fixtures are NOT available in beforeAll/afterAll/aroundAll.
840
+ */
841
+ beforeAll: typeof beforeAll<ExtractSuiteContext<ExtraContext>>;
842
+ afterAll: typeof afterAll<ExtractSuiteContext<ExtraContext>>;
843
+ aroundAll: typeof aroundAll<ExtractSuiteContext<ExtraContext>>;
844
+ /**
845
+ * Test-level hooks receive all fixtures including test-scoped ones.
846
+ */
833
847
  beforeEach: typeof beforeEach<ExtraContext>;
834
848
  afterEach: typeof afterEach<ExtraContext>;
835
849
  aroundEach: typeof aroundEach<ExtraContext>;
836
- aroundAll: typeof aroundAll;
837
850
  }
838
851
  type TestAPI<ExtraContext = object> = ChainableTestAPI<ExtraContext> & ExtendedAPI<ExtraContext> & Hooks<ExtraContext> & {
839
- extend: <T extends Record<string, any> = object>(fixtures: Fixtures<T, ExtraContext>) => TestAPI<{ [K in keyof T | keyof ExtraContext] : K extends keyof T ? T[K] : K extends keyof ExtraContext ? ExtraContext[K] : never }>;
840
- scoped: (fixtures: Partial<Fixtures<ExtraContext>>) => void;
852
+ /**
853
+ * Extend the test API with custom fixtures.
854
+ *
855
+ * @example
856
+ * ```ts
857
+ * // Simple test fixtures (backward compatible)
858
+ * const myTest = test.extend<{ foo: string }>({
859
+ * foo: 'value',
860
+ * })
861
+ *
862
+ * // With scoped fixtures - use $test/$file/$worker structure
863
+ * const myTest = test.extend<{
864
+ * $test: { testData: string }
865
+ * $file: { fileDb: Database }
866
+ * $worker: { workerConfig: Config }
867
+ * }>({
868
+ * testData: async ({ fileDb }, use) => {
869
+ * await use(await fileDb.getData())
870
+ * },
871
+ * fileDb: [async ({ workerConfig }, use) => {
872
+ * // File fixture can only access workerConfig, NOT testData
873
+ * const db = new Database(workerConfig)
874
+ * await use(db)
875
+ * await db.close()
876
+ * }, { scope: 'file' }],
877
+ * workerConfig: [async ({}, use) => {
878
+ * // Worker fixture can only access other worker fixtures
879
+ * await use(loadConfig())
880
+ * }, { scope: 'worker' }],
881
+ * })
882
+ *
883
+ * // Builder pattern with automatic type inference
884
+ * const myTest = test
885
+ * .extend('config', { scope: 'worker' }, async ({}) => {
886
+ * return { port: 3000 } // Type inferred as { port: number }
887
+ * })
888
+ * .extend('db', { scope: 'file' }, async ({ config }, { onCleanup }) => {
889
+ * // TypeScript knows config is { port: number }
890
+ * const db = new Database(config.port)
891
+ * onCleanup(() => db.close()) // Register cleanup
892
+ * return db // Type inferred as Database
893
+ * })
894
+ * .extend('data', async ({ db }) => {
895
+ * // TypeScript knows db is Database
896
+ * return await db.getData() // Type inferred from return
897
+ * })
898
+ * ```
899
+ */
900
+ extend: {
901
+ <
902
+ K extends string,
903
+ T extends (K extends keyof ExtraContext ? ExtraContext[K] : unknown)
904
+ >(name: K, options: WorkerScopeFixtureOptions, fn: BuilderFixtureFn<T, WorkerScopeContext<ExtraContext>>): TestAPI<AddBuilderWorker<ExtraContext, K, T>>;
905
+ <
906
+ K extends string,
907
+ T extends (K extends keyof ExtraContext ? ExtraContext[K] : unknown)
908
+ >(name: K, options: FileScopeFixtureOptions, fn: BuilderFixtureFn<T, FileScopeContext<ExtraContext>>): TestAPI<AddBuilderFile<ExtraContext, K, T>>;
909
+ <
910
+ K extends string,
911
+ T extends (K extends keyof ExtraContext ? ExtraContext[K] : unknown)
912
+ >(name: K, options: TestScopeFixtureOptions, fn: BuilderFixtureFn<T, TestScopeContext<ExtraContext>>): TestAPI<AddBuilderTest<ExtraContext, K, T>>;
913
+ <
914
+ K extends string,
915
+ T extends (K extends keyof ExtraContext ? ExtraContext[K] : unknown)
916
+ >(name: K, fn: BuilderFixtureFn<T, TestScopeContext<ExtraContext>>): TestAPI<AddBuilderTest<ExtraContext, K, T>>;
917
+ <
918
+ K extends string,
919
+ T extends (K extends keyof ExtraContext ? ExtraContext[K] : unknown)
920
+ >(name: K, options: WorkerScopeFixtureOptions, value: T extends (...args: any[]) => any ? never : T): TestAPI<AddBuilderWorker<ExtraContext, K, T>>;
921
+ <
922
+ K extends string,
923
+ T extends (K extends keyof ExtraContext ? ExtraContext[K] : unknown)
924
+ >(name: K, options: FileScopeFixtureOptions, value: T extends (...args: any[]) => any ? never : T): TestAPI<AddBuilderFile<ExtraContext, K, T>>;
925
+ <
926
+ K extends string,
927
+ T extends (K extends keyof ExtraContext ? ExtraContext[K] : unknown)
928
+ >(name: K, options: TestScopeFixtureOptions, value: T extends (...args: any[]) => any ? never : T): TestAPI<AddBuilderTest<ExtraContext, K, T>>;
929
+ <
930
+ K extends string,
931
+ T extends (K extends keyof ExtraContext ? ExtraContext[K] : unknown)
932
+ >(name: K, value: T extends (...args: any[]) => any ? never : T): TestAPI<AddBuilderTest<ExtraContext, K, T>>;
933
+ <T extends ScopedFixturesDef>(fixtures: ScopedFixturesObject<T, ExtraContext>): TestAPI<ExtractScopedFixtures<T> & ExtraContext>;
934
+ <T extends Record<string, any> = object>(fixtures: Fixtures<T, ExtraContext>): TestAPI<{ [K in keyof T | keyof ExtraContext] : K extends keyof T ? T[K] : K extends keyof ExtraContext ? ExtraContext[K] : never }>;
935
+ };
936
+ /**
937
+ * Overwrite fixture values for the current suite scope.
938
+ * Supports both object syntax and builder pattern.
939
+ *
940
+ * @example
941
+ * ```ts
942
+ * describe('with custom config', () => {
943
+ * // Object syntax
944
+ * test.override({ config: { port: 4000 } })
945
+ *
946
+ * // Builder pattern - value
947
+ * test.override('config', { port: 4000 })
948
+ *
949
+ * // Builder pattern - function
950
+ * test.override('config', () => ({ port: 4000 }))
951
+ *
952
+ * // Builder pattern - function with cleanup
953
+ * test.override('db', async ({ config }, { onCleanup }) => {
954
+ * const db = await createDb(config)
955
+ * onCleanup(() => db.close())
956
+ * return db
957
+ * })
958
+ * })
959
+ * ```
960
+ */
961
+ override: {
962
+ <K extends keyof ExtraContext>(name: K, options: FixtureOptions, fn: BuilderFixtureFn<ExtraContext[K], ExtraContext & TestContext>): TestAPI<ExtraContext>;
963
+ <K extends keyof ExtraContext>(name: K, fn: BuilderFixtureFn<ExtraContext[K], ExtraContext & TestContext>): TestAPI<ExtraContext>;
964
+ <K extends keyof ExtraContext>(name: K, options: FixtureOptions, value: ExtraContext[K] extends (...args: any[]) => any ? never : ExtraContext[K]): TestAPI<ExtraContext>;
965
+ <K extends keyof ExtraContext>(name: K, value: ExtraContext[K] extends (...args: any[]) => any ? never : ExtraContext[K]): TestAPI<ExtraContext>;
966
+ (fixtures: Partial<Fixtures<ExtraContext>>): TestAPI<ExtraContext>;
967
+ };
968
+ /**
969
+ * @deprecated Use `test.override()` instead
970
+ */
971
+ scoped: (fixtures: Partial<Fixtures<ExtraContext>>) => TestAPI<ExtraContext>;
841
972
  describe: SuiteAPI<ExtraContext>;
973
+ suite: SuiteAPI<ExtraContext>;
842
974
  };
843
975
  interface FixtureOptions {
844
976
  /**
@@ -861,7 +993,141 @@ interface FixtureOptions {
861
993
  */
862
994
  scope?: "test" | "worker" | "file";
863
995
  }
996
+ /**
997
+ * Options for test-scoped fixtures.
998
+ * Test fixtures are set up before each test and have access to all fixtures.
999
+ */
1000
+ interface TestScopeFixtureOptions extends Omit<FixtureOptions, "scope"> {
1001
+ /**
1002
+ * @default 'test'
1003
+ */
1004
+ scope?: "test";
1005
+ }
1006
+ /**
1007
+ * Options for file-scoped fixtures.
1008
+ * File fixtures are set up once per file and can only access other file fixtures and worker fixtures.
1009
+ */
1010
+ interface FileScopeFixtureOptions extends Omit<FixtureOptions, "scope"> {
1011
+ /**
1012
+ * Must be 'file' for file-scoped fixtures.
1013
+ */
1014
+ scope: "file";
1015
+ }
1016
+ /**
1017
+ * Options for worker-scoped fixtures.
1018
+ * Worker fixtures are set up once per worker and can only access other worker fixtures.
1019
+ */
1020
+ interface WorkerScopeFixtureOptions extends Omit<FixtureOptions, "scope"> {
1021
+ /**
1022
+ * Must be 'worker' for worker-scoped fixtures.
1023
+ */
1024
+ scope: "worker";
1025
+ }
864
1026
  type Use<T> = (value: T) => Promise<void>;
1027
+ /**
1028
+ * Cleanup registration function for builder pattern fixtures.
1029
+ * Call this to register a cleanup function that runs after the test/file/worker completes.
1030
+ *
1031
+ * **Note:** This function can only be called once per fixture. If you need multiple
1032
+ * cleanup operations, either combine them into a single cleanup function or split
1033
+ * your fixture into multiple smaller fixtures.
1034
+ */
1035
+ type OnCleanup = (cleanup: () => Awaitable<void>) => void;
1036
+ /**
1037
+ * Builder pattern fixture function with automatic type inference.
1038
+ * Returns the fixture value directly (type is inferred from return).
1039
+ * Use onCleanup to register teardown logic.
1040
+ *
1041
+ * Parameters can be omitted if not needed:
1042
+ * - `async () => value` - no dependencies, no cleanup
1043
+ * - `async ({ dep }) => value` - with dependencies, no cleanup
1044
+ * - `async ({ dep }, { onCleanup }) => value` - with dependencies and cleanup
1045
+ */
1046
+ type BuilderFixtureFn<
1047
+ T,
1048
+ Context
1049
+ > = (context: Context, fixture: {
1050
+ onCleanup: OnCleanup;
1051
+ }) => T | Promise<T>;
1052
+ type ExtractSuiteContext<C> = C extends {
1053
+ $__worker?: any;
1054
+ } | {
1055
+ $__file?: any;
1056
+ } | {
1057
+ $__test?: any;
1058
+ } ? ExtractBuilderWorker<C> & ExtractBuilderFile<C> : C;
1059
+ /**
1060
+ * Extracts worker-scoped fixtures from a context that includes scope info.
1061
+ */
1062
+ type ExtractBuilderWorker<C> = C extends {
1063
+ $__worker?: infer W;
1064
+ } ? W extends Record<string, any> ? W : object : object;
1065
+ /**
1066
+ * Extracts file-scoped fixtures from a context that includes scope info.
1067
+ */
1068
+ type ExtractBuilderFile<C> = C extends {
1069
+ $__file?: infer F;
1070
+ } ? F extends Record<string, any> ? F : object : object;
1071
+ /**
1072
+ * Extracts test-scoped fixtures from a context that includes scope info.
1073
+ */
1074
+ type ExtractBuilderTest<C> = C extends {
1075
+ $__test?: infer T;
1076
+ } ? T extends Record<string, any> ? T : object : object;
1077
+ /**
1078
+ * Adds a worker fixture to the context with proper scope tracking.
1079
+ */
1080
+ type AddBuilderWorker<
1081
+ C,
1082
+ K extends string,
1083
+ V
1084
+ > = Omit<C, "$__worker"> & Record<K, V> & {
1085
+ readonly $__worker?: ExtractBuilderWorker<C> & Record<K, V>;
1086
+ readonly $__file?: ExtractBuilderFile<C>;
1087
+ readonly $__test?: ExtractBuilderTest<C>;
1088
+ };
1089
+ /**
1090
+ * Adds a file fixture to the context with proper scope tracking.
1091
+ */
1092
+ type AddBuilderFile<
1093
+ C,
1094
+ K extends string,
1095
+ V
1096
+ > = Omit<C, "$__file"> & Record<K, V> & {
1097
+ readonly $__worker?: ExtractBuilderWorker<C>;
1098
+ readonly $__file?: ExtractBuilderFile<C> & Record<K, V>;
1099
+ readonly $__test?: ExtractBuilderTest<C>;
1100
+ };
1101
+ /**
1102
+ * Adds a test fixture to the context with proper scope tracking.
1103
+ */
1104
+ type AddBuilderTest<
1105
+ C,
1106
+ K extends string,
1107
+ V
1108
+ > = Omit<C, "$__test"> & Record<K, V> & {
1109
+ readonly $__worker?: ExtractBuilderWorker<C>;
1110
+ readonly $__file?: ExtractBuilderFile<C>;
1111
+ readonly $__test?: ExtractBuilderTest<C> & Record<K, V>;
1112
+ };
1113
+ /**
1114
+ * Context available to worker-scoped fixtures.
1115
+ * Worker fixtures can only access other worker fixtures.
1116
+ * They do NOT have access to test context (task, expect, onTestFailed, etc.)
1117
+ * since they run once per worker, outside of any specific test.
1118
+ */
1119
+ type WorkerScopeContext<C> = ExtractBuilderWorker<C>;
1120
+ /**
1121
+ * Context available to file-scoped fixtures.
1122
+ * File fixtures can access worker and other file fixtures.
1123
+ * They do NOT have access to test context (task, expect, onTestFailed, etc.)
1124
+ * since they run once per file, outside of any specific test.
1125
+ */
1126
+ type FileScopeContext<C> = ExtractBuilderWorker<C> & ExtractBuilderFile<C>;
1127
+ /**
1128
+ * Context available to test-scoped fixtures (all fixtures + test context).
1129
+ */
1130
+ type TestScopeContext<C> = C & TestContext;
865
1131
  type FixtureFn<
866
1132
  T,
867
1133
  K extends keyof T,
@@ -872,10 +1138,55 @@ type Fixture<
872
1138
  K extends keyof T,
873
1139
  ExtraContext = object
874
1140
  > = ((...args: any) => any) extends T[K] ? T[K] extends any ? FixtureFn<T, K, Omit<ExtraContext, Exclude<keyof T, K>>> : never : T[K] | (T[K] extends any ? FixtureFn<T, K, Omit<ExtraContext, Exclude<keyof T, K>>> : never);
1141
+ /**
1142
+ * Fixture function with explicit context type for scoped fixtures.
1143
+ */
1144
+ type ScopedFixtureFn<
1145
+ Value,
1146
+ Context
1147
+ > = (context: Context, use: Use<Value>) => Promise<void>;
1148
+ /**
1149
+ * Fixtures definition for backward compatibility.
1150
+ * All fixtures are in T and any scope is allowed.
1151
+ */
875
1152
  type Fixtures<
876
1153
  T,
877
1154
  ExtraContext = object
878
1155
  > = { [K in keyof T] : Fixture<T, K, ExtraContext & TestContext> | [Fixture<T, K, ExtraContext & TestContext>, FixtureOptions?] };
1156
+ /**
1157
+ * Scoped fixtures definition using a single generic with optional scope keys.
1158
+ * This provides better ergonomics than multiple generics.
1159
+ * Uses $ prefix to avoid conflicts with fixture names.
1160
+ *
1161
+ * @example
1162
+ * ```ts
1163
+ * test.extend<{
1164
+ * $worker?: { config: Config }
1165
+ * $file?: { db: Database }
1166
+ * $test?: { data: string }
1167
+ * }>({ ... })
1168
+ * ```
1169
+ */
1170
+ interface ScopedFixturesDef {
1171
+ $test?: Record<string, any>;
1172
+ $file?: Record<string, any>;
1173
+ $worker?: Record<string, any>;
1174
+ }
1175
+ /**
1176
+ * Extracts fixture types from a ScopedFixturesDef.
1177
+ * Handles optional properties by using Exclude to remove undefined.
1178
+ */
1179
+ type ExtractScopedFixtures<T extends ScopedFixturesDef> = ([Exclude<T["$test"], undefined>] extends [never] ? object : Exclude<T["$test"], undefined>) & ([Exclude<T["$file"], undefined>] extends [never] ? object : Exclude<T["$file"], undefined>) & ([Exclude<T["$worker"], undefined>] extends [never] ? object : Exclude<T["$worker"], undefined>);
1180
+ /**
1181
+ * Creates the fixtures object type for ScopedFixturesDef with proper scope validation.
1182
+ * - Test fixtures: can be defined as value, function, or tuple with optional scope
1183
+ * - File fixtures: MUST have { scope: 'file' }
1184
+ * - Worker fixtures: MUST have { scope: 'worker' }
1185
+ */
1186
+ type ScopedFixturesObject<
1187
+ T extends ScopedFixturesDef,
1188
+ ExtraContext = object
1189
+ > = { [K in keyof NonNullable<T["$test"]>] : NonNullable<T["$test"]>[K] | ScopedFixtureFn<NonNullable<T["$test"]>[K], ExtractScopedFixtures<T> & ExtraContext & TestContext> | [ScopedFixtureFn<NonNullable<T["$test"]>[K], ExtractScopedFixtures<T> & ExtraContext & TestContext>, TestScopeFixtureOptions?] } & { [K in keyof NonNullable<T["$file"]>] : [ScopedFixtureFn<NonNullable<T["$file"]>[K], (NonNullable<T["$file"]> & NonNullable<T["$worker"]>) & ExtraContext>, FileScopeFixtureOptions] } & { [K in keyof NonNullable<T["$worker"]>] : [ScopedFixtureFn<NonNullable<T["$worker"]>[K], NonNullable<T["$worker"]> & ExtraContext>, WorkerScopeFixtureOptions] };
879
1190
  type InferFixturesTypes<T> = T extends TestAPI<infer C> ? C : T;
880
1191
  interface SuiteCollectorCallable<ExtraContext = object> {
881
1192
  <OverrideExtraContext extends ExtraContext = ExtraContext>(name: string | Function, fn?: SuiteFactory<OverrideExtraContext>, options?: number): SuiteCollector<OverrideExtraContext>;
@@ -889,11 +1200,11 @@ type SuiteAPI<ExtraContext = object> = ChainableSuiteAPI<ExtraContext> & {
889
1200
  skipIf: (condition: any) => ChainableSuiteAPI<ExtraContext>;
890
1201
  runIf: (condition: any) => ChainableSuiteAPI<ExtraContext>;
891
1202
  };
892
- interface BeforeAllListener {
893
- (suite: Readonly<Suite | File>): Awaitable<unknown>;
1203
+ interface BeforeAllListener<ExtraContext = object> {
1204
+ (context: ExtraContext, suite: Readonly<Suite | File>): Awaitable<unknown>;
894
1205
  }
895
- interface AfterAllListener {
896
- (suite: Readonly<Suite | File>): Awaitable<unknown>;
1206
+ interface AfterAllListener<ExtraContext = object> {
1207
+ (context: ExtraContext, suite: Readonly<Suite | File>): Awaitable<unknown>;
897
1208
  }
898
1209
  interface BeforeEachListener<ExtraContext = object> {
899
1210
  (context: TestContext & ExtraContext, suite: Readonly<Suite>): Awaitable<unknown>;
@@ -904,16 +1215,22 @@ interface AfterEachListener<ExtraContext = object> {
904
1215
  interface AroundEachListener<ExtraContext = object> {
905
1216
  (runTest: () => Promise<void>, context: TestContext & ExtraContext, suite: Readonly<Suite>): Awaitable<unknown>;
906
1217
  }
907
- interface AroundAllListener {
1218
+ interface AroundAllListener<ExtraContext = object> {
1219
+ (runSuite: () => Promise<void>, context: ExtraContext, suite: Readonly<Suite | File>): Awaitable<unknown>;
1220
+ }
1221
+ interface RegisteredAllListener {
1222
+ (suite: Readonly<Suite | File>): Awaitable<unknown>;
1223
+ }
1224
+ interface RegisteredAroundAllListener {
908
1225
  (runSuite: () => Promise<void>, suite: Readonly<Suite | File>): Awaitable<unknown>;
909
1226
  }
910
1227
  interface SuiteHooks<ExtraContext = object> {
911
- beforeAll: BeforeAllListener[];
912
- afterAll: AfterAllListener[];
1228
+ beforeAll: RegisteredAllListener[];
1229
+ afterAll: RegisteredAllListener[];
1230
+ aroundAll: RegisteredAroundAllListener[];
913
1231
  beforeEach: BeforeEachListener<ExtraContext>[];
914
1232
  afterEach: AfterEachListener<ExtraContext>[];
915
1233
  aroundEach: AroundEachListener<ExtraContext>[];
916
- aroundAll: AroundAllListener[];
917
1234
  }
918
1235
  interface TaskCustomOptions extends TestOptions {
919
1236
  /**
@@ -923,7 +1240,7 @@ interface TaskCustomOptions extends TestOptions {
923
1240
  /**
924
1241
  * Task fixtures.
925
1242
  */
926
- fixtures?: FixtureItem[];
1243
+ fixtures?: TestFixtures;
927
1244
  /**
928
1245
  * Function that will be called when the task is executed.
929
1246
  * If nothing is provided, the runner will try to get the function using `getFn(task)`.
@@ -938,9 +1255,7 @@ interface SuiteCollector<ExtraContext = object> {
938
1255
  type: "collector";
939
1256
  test: TestAPI<ExtraContext>;
940
1257
  tasks: (Suite | Test<ExtraContext> | SuiteCollector<ExtraContext>)[];
941
- scoped: (fixtures: Fixtures<any, ExtraContext>) => void;
942
- fixtures: () => FixtureItem[] | undefined;
943
- file?: File;
1258
+ file: File;
944
1259
  suite?: Suite;
945
1260
  task: (name: string, options?: TaskCustomOptions) => Test<ExtraContext>;
946
1261
  collect: (file: File) => Promise<Suite>;
@@ -1017,12 +1332,13 @@ interface TestAttachment {
1017
1332
  /** Inline attachment content as a string or raw binary data */
1018
1333
  body?: string | Uint8Array;
1019
1334
  }
1020
- /**
1021
- * Source code location information for a test artifact.
1022
- *
1023
- * Indicates where in the source code the artifact originated from.
1024
- */
1025
- interface TestArtifactLocation {
1335
+ interface Location {
1336
+ /** Line number in the source file (1-indexed) */
1337
+ line: number;
1338
+ /** Column number in the line (1-indexed) */
1339
+ column: number;
1340
+ }
1341
+ interface FileLocation extends Location {
1026
1342
  /** Line number in the source file (1-indexed) */
1027
1343
  line: number;
1028
1344
  /** Column number in the line (1-indexed) */
@@ -1031,11 +1347,19 @@ interface TestArtifactLocation {
1031
1347
  file: string;
1032
1348
  }
1033
1349
  /**
1350
+ * Source code location information for a test artifact.
1351
+ *
1352
+ * Indicates where in the source code the artifact originated from.
1353
+ */
1354
+ interface TestArtifactLocation extends FileLocation {}
1355
+ /**
1034
1356
  * @experimental
1035
1357
  *
1036
1358
  * Base interface for all test artifacts.
1037
1359
  *
1038
1360
  * Extend this interface when creating custom test artifacts. Vitest automatically manages the `attachments` array and injects the `location` property to indicate where the artifact was created in your test code.
1361
+ *
1362
+ * **Important**: when running with [`api.allowWrite`](https://vitest.dev/config/api#api-allowwrite) or [`browser.api.allowWrite`](https://vitest.dev/config/browser/api#api-allowwrite) disabled, Vitest empties the `attachments` array on every artifact before reporting it.
1039
1363
  */
1040
1364
  interface TestArtifactBase {
1041
1365
  /** File or data attachments associated with this artifact */
@@ -1080,6 +1404,21 @@ interface VisualRegressionArtifact extends TestArtifactBase {
1080
1404
  message: string;
1081
1405
  attachments: VisualRegressionArtifactAttachment[];
1082
1406
  }
1407
+ interface FailureScreenshotArtifactAttachment extends TestAttachment {
1408
+ path: string;
1409
+ /** Original file system path to the screenshot, before attachment resolution */
1410
+ originalPath: string;
1411
+ body?: undefined;
1412
+ }
1413
+ /**
1414
+ * @experimental
1415
+ *
1416
+ * Artifact type for failure screenshots.
1417
+ */
1418
+ interface FailureScreenshotArtifact extends TestArtifactBase {
1419
+ type: "internal:failureScreenshot";
1420
+ attachments: [FailureScreenshotArtifactAttachment] | [];
1421
+ }
1083
1422
  /**
1084
1423
  * @experimental
1085
1424
  * @advanced
@@ -1160,7 +1499,7 @@ interface TestArtifactRegistry {}
1160
1499
  *
1161
1500
  * This type automatically includes all artifacts registered via {@link TestArtifactRegistry}.
1162
1501
  */
1163
- type TestArtifact = TestAnnotationArtifact | VisualRegressionArtifact | TestArtifactRegistry[keyof TestArtifactRegistry];
1502
+ type TestArtifact = FailureScreenshotArtifact | TestAnnotationArtifact | VisualRegressionArtifact | TestArtifactRegistry[keyof TestArtifactRegistry];
1164
1503
 
1165
- export { afterAll as a7, afterEach as a8, aroundAll as a9, aroundEach as aa, beforeAll as ab, beforeEach as ac, onTestFailed as ad, onTestFinished as ae, createChainable as ag };
1166
- export type { TestOptions as $, AfterAllListener as A, BeforeAllListener as B, CancelReason as C, TaskCustomOptions as D, TaskEventPack as E, FileSpecification as F, TaskHook as G, TaskMeta as H, ImportDuration as I, TaskPopulated as J, TaskResult as K, TaskResultPack as L, TaskState as M, TestAnnotation as N, OnTestFailedHandler as O, TestAnnotationArtifact as P, TestAnnotationLocation as Q, Retry as R, Suite as S, TestArtifact as T, TestArtifactBase as U, VitestRunner as V, TestArtifactLocation as W, TestArtifactRegistry as X, TestAttachment as Y, TestContext as Z, TestFunction as _, Test as a, TestTagDefinition as a0, TestTags as a1, Use as a2, VisualRegressionArtifact as a3, VitestRunnerConfig as a4, VitestRunnerConstructor as a5, VitestRunnerImportSource as a6, ChainableFunction as af, SuiteHooks as b, File as c, TaskUpdateEvent as d, Task as e, TestAPI as f, SuiteAPI as g, SuiteCollector as h, AfterEachListener as i, AroundAllListener as j, AroundEachListener as k, BeforeEachListener as l, Fixture as m, FixtureFn as n, FixtureOptions as o, Fixtures as p, InferFixturesTypes as q, OnTestFinishedHandler as r, RunMode as s, RuntimeContext as t, SequenceHooks as u, SequenceSetupFiles as v, SerializableRetry as w, SuiteFactory as x, SuiteOptions as y, TaskBase as z };
1504
+ export { afterAll as a8, afterEach as a9, aroundAll as aa, aroundEach as ab, beforeAll as ac, beforeEach as ad, onTestFailed as ae, onTestFinished as af, createChainable as ah };
1505
+ export type { TestFunction as $, AfterAllListener as A, BeforeAllListener as B, CancelReason as C, TaskBase as D, TaskCustomOptions as E, FileSpecification as F, TaskEventPack as G, TaskHook as H, ImportDuration as I, TaskMeta as J, TaskPopulated as K, TaskResult as L, TaskResultPack as M, TaskState as N, OnTestFailedHandler as O, TestAnnotation as P, TestAnnotationArtifact as Q, Retry as R, Suite as S, TestArtifact as T, TestAnnotationLocation as U, VitestRunner as V, TestArtifactBase as W, TestArtifactLocation as X, TestArtifactRegistry as Y, TestAttachment as Z, TestContext as _, Test as a, TestOptions as a0, TestTagDefinition as a1, TestTags as a2, Use as a3, VisualRegressionArtifact as a4, VitestRunnerConfig as a5, VitestRunnerConstructor as a6, VitestRunnerImportSource as a7, ChainableFunction as ag, SuiteHooks as b, File as c, TaskUpdateEvent as d, Task as e, TestAPI as f, SuiteAPI as g, SuiteCollector as h, AfterEachListener as i, AroundAllListener as j, AroundEachListener as k, BeforeEachListener as l, FailureScreenshotArtifact as m, Fixture as n, FixtureFn as o, FixtureOptions as p, Fixtures as q, InferFixturesTypes as r, OnTestFinishedHandler as s, RunMode as t, RuntimeContext as u, SequenceHooks as v, SequenceSetupFiles as w, SerializableRetry as x, SuiteFactory as y, SuiteOptions as z };
package/dist/types.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { A as AfterAllListener, i as AfterEachListener, j as AroundAllListener, k as AroundEachListener, B as BeforeAllListener, l as BeforeEachListener, C as CancelReason, c as File, F as FileSpecification, m as Fixture, n as FixtureFn, o as FixtureOptions, p as Fixtures, I as ImportDuration, q as InferFixturesTypes, O as OnTestFailedHandler, r as OnTestFinishedHandler, R as Retry, s as RunMode, t as RuntimeContext, u as SequenceHooks, v as SequenceSetupFiles, w as SerializableRetry, S as Suite, g as SuiteAPI, h as SuiteCollector, x as SuiteFactory, b as SuiteHooks, y as SuiteOptions, e as Task, z as TaskBase, D as TaskCustomOptions, E as TaskEventPack, G as TaskHook, H as TaskMeta, J as TaskPopulated, K as TaskResult, L as TaskResultPack, M as TaskState, d as TaskUpdateEvent, a as Test, f as TestAPI, N as TestAnnotation, P as TestAnnotationArtifact, Q as TestAnnotationLocation, T as TestArtifact, U as TestArtifactBase, W as TestArtifactLocation, X as TestArtifactRegistry, Y as TestAttachment, Z as TestContext, _ as TestFunction, $ as TestOptions, a0 as TestTagDefinition, a1 as TestTags, a2 as Use, a3 as VisualRegressionArtifact, V as VitestRunner, a4 as VitestRunnerConfig, a5 as VitestRunnerConstructor, a6 as VitestRunnerImportSource } from './tasks.d-WWG4yDf6.js';
2
- import '@vitest/utils/diff';
1
+ export { A as AfterAllListener, i as AfterEachListener, j as AroundAllListener, k as AroundEachListener, B as BeforeAllListener, l as BeforeEachListener, C as CancelReason, m as FailureScreenshotArtifact, c as File, F as FileSpecification, n as Fixture, o as FixtureFn, p as FixtureOptions, q as Fixtures, I as ImportDuration, r as InferFixturesTypes, O as OnTestFailedHandler, s as OnTestFinishedHandler, R as Retry, t as RunMode, u as RuntimeContext, v as SequenceHooks, w as SequenceSetupFiles, x as SerializableRetry, S as Suite, g as SuiteAPI, h as SuiteCollector, y as SuiteFactory, b as SuiteHooks, z as SuiteOptions, e as Task, D as TaskBase, E as TaskCustomOptions, G as TaskEventPack, H as TaskHook, J as TaskMeta, K as TaskPopulated, L as TaskResult, M as TaskResultPack, N as TaskState, d as TaskUpdateEvent, a as Test, f as TestAPI, P as TestAnnotation, Q as TestAnnotationArtifact, U as TestAnnotationLocation, T as TestArtifact, W as TestArtifactBase, X as TestArtifactLocation, Y as TestArtifactRegistry, Z as TestAttachment, _ as TestContext, $ as TestFunction, a0 as TestOptions, a1 as TestTagDefinition, a2 as TestTags, a3 as Use, a4 as VisualRegressionArtifact, V as VitestRunner, a5 as VitestRunnerConfig, a6 as VitestRunnerConstructor, a7 as VitestRunnerImportSource } from './tasks.d-C-iOiT8j.js';
3
2
  import '@vitest/utils';
3
+ import '@vitest/utils/diff';
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Suite, c as File, e as Task, a0 as TestTagDefinition, a4 as VitestRunnerConfig, a as Test } from './tasks.d-WWG4yDf6.js';
2
- export { af as ChainableFunction, ag as createChainable } from './tasks.d-WWG4yDf6.js';
1
+ import { S as Suite, c as File, e as Task, a1 as TestTagDefinition, a5 as VitestRunnerConfig, a as Test } from './tasks.d-C-iOiT8j.js';
2
+ export { ag as ChainableFunction, ah as createChainable } from './tasks.d-C-iOiT8j.js';
3
3
  import { ParsedStack, Arrayable } from '@vitest/utils';
4
4
  import '@vitest/utils/diff';
5
5