···149149150150If there are libraries that are needed and don't comply with our size
151151requirements, a fork can be tried to reduce its size while we work with them to
152152-upstream our changes (see [tinypool](https://github.com/tinylibs/tinypool) for example)
152152+upstream our changes.
153153154154### Think before adding yet another option
155155
-1
README.md
···4141- [JSDOM](https://github.com/jsdom/jsdom) and [happy-dom](https://github.com/capricorn86/happy-dom) for DOM and browser API mocking
4242- [Browser Mode](https://vitest.dev/guide/browser/) for running component tests in the browser
4343- Components testing ([Vue](https://github.com/vitest-tests/browser-examples/tree/main/examples/vue), [React](https://github.com/vitest-tests/browser-examples/tree/main/examples/react), [Svelte](https://github.com/vitest-tests/browser-examples/tree/main/examples/svelte), [Lit](./examples/lit), [Marko](https://github.com/marko-js/examples/tree/master/examples/library-ts))
4444-- Workers multi-threading via [Tinypool](https://github.com/tinylibs/tinypool) (a lightweight fork of [Piscina](https://github.com/piscinajs/piscina))
4544- Benchmarking support with [Tinybench](https://github.com/tinylibs/tinybench)
4645- [Projects](https://vitest.dev/guide/projects) support
4746- [expect-type](https://github.com/mmkal/expect-type) for type-level testing
···11# Custom Pool
2233::: warning
44-This is an advanced and very low-level API. If you just want to [run tests](/guide/), you probably don't need this. It is primarily used by library authors.
44+This is an advanced, experimental and very low-level API. If you just want to [run tests](/guide/), you probably don't need this. It is primarily used by library authors.
55:::
6677-Vitest runs tests in pools. By default, there are several pools:
77+Vitest runs tests in a pool. By default, there are several pool runners:
8899- `threads` to run tests using `node:worker_threads` (isolation is provided with a new worker context)
1010- `forks` to run tests using `node:child_process` (isolation is provided with a new `child_process.fork` process)
···1212- `browser` to run tests using browser providers
1313- `typescript` to run typechecking on tests
14141515-You can provide your own pool by specifying a file path:
1515+::: tip
1616+See [`vitest-pool-example`](https://www.npmjs.com/package/vitest-pool-example) for example of a custom pool runner implementation.
1717+:::
1818+1919+## Usage
2020+2121+You can provide your own pool runner by a function that returns `PoolRunnerInitializer`.
16221723```ts [vitest.config.ts]
1824import { defineConfig } from 'vitest/config'
2525+import customPool from './my-custom-pool.ts'
19262027export default defineConfig({
2128 test: {
2229 // will run every file with a custom pool by default
2323- pool: './my-custom-pool.ts',
2424- // you can provide options using `poolOptions` object
2525- poolOptions: {
2626- myCustomPool: {
2727- customProperty: true,
2828- },
2929- },
3030+ pool: customPool({
3131+ customProperty: true,
3232+ })
3033 },
3134})
3235```
···3437If you need to run tests in different pools, use the [`projects`](/guide/projects) feature:
35383639```ts [vitest.config.ts]
4040+import customPool from './my-custom-pool.ts'
4141+3742export default defineConfig({
3843 test: {
3944 projects: [
···4348 pool: 'threads',
4449 },
4550 },
5151+ {
5252+ extends: true,
5353+ test: {
5454+ pool: customPool({
5555+ customProperty: true,
5656+ })
5757+ }
5858+ }
4659 ],
4760 },
4861})
···50635164## API
52655353-The file specified in `pool` option should export a function (can be async) that accepts `Vitest` interface as its first option. This function needs to return an object matching `ProcessPool` interface:
6666+The `pool` option accepts a `PoolRunnerInitializer` that can be used for custom pool runners. The `name` property should indicate name of the custom pool runner. It should be identical with your worker's `name` property.
54675555-```ts
5656-import type { ProcessPool, TestSpecification } from 'vitest/node'
6868+```ts [my-custom-pool.ts]
6969+import type { PoolRunnerInitializer } from 'vitest/node'
57705858-export interface ProcessPool {
5959- name: string
6060- runTests: (files: TestSpecification[], invalidates?: string[]) => Promise<void>
6161- collectTests: (files: TestSpecification[], invalidates?: string[]) => Promise<void>
6262- close?: () => Promise<void>
7171+export function customPool(customOptions: CustomOptions): PoolRunnerInitializer {
7272+ return {
7373+ name: 'custom-pool',
7474+ createPoolWorker: options => new CustomPoolWorker(options, customOptions),
7575+ }
6376}
6477```
65786666-The function is called only once (unless the server config was updated), and it's generally a good idea to initialize everything you need for tests inside that function and reuse it when `runTests` is called.
7979+In your `CustomPoolWorker` you need to define all required methods:
67806868-Vitest calls `runTest` when new tests are scheduled to run. It will not call it if `files` is empty. The first argument is an array of [TestSpecifications](/advanced/api/test-specification). Files are sorted using [`sequencer`](/config/#sequence-sequencer) before `runTests` is called. It's possible (but unlikely) to have the same file twice, but it will always have a different project - this is implemented via [`projects`](/guide/projects) configuration.
8181+```ts [my-custom-pool.ts]
8282+import type { PoolOptions, PoolWorker, WorkerRequest } from 'vitest/node'
69837070-Vitest will wait until `runTests` is executed before finishing a run (i.e., it will emit [`onTestRunEnd`](/advanced/reporters) only after `runTests` is resolved).
8484+class CustomPoolWorker implements PoolWorker {
8585+ name = 'custom-pool'
8686+ private customOptions: CustomOptions
71877272-If you are using a custom pool, you will have to provide test files and their results yourself - you can reference [`vitest.state`](https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/node/state.ts) for that (most important are `collectFiles` and `updateTasks`). Vitest uses `startTests` function from `@vitest/runner` package to do that.
8888+ constructor(options: PoolOptions, customOptions: CustomOptions) {
8989+ this.customOptions = customOptions
9090+ }
73917474-Vitest will call `collectTests` if `vitest.collect` is called or `vitest list` is invoked via a CLI command. It works the same way as `runTests`, but you don't have to run test callbacks, only report their tasks by calling `vitest.state.collectFiles(files)`.
9292+ send(message: WorkerRequest): void {
9393+ // Provide way to send your worker a message
9494+ }
75957676-To communicate between different processes, you can create methods object using `createMethodsRPC` from `vitest/node`, and use any form of communication that you prefer. For example, to use WebSockets with `birpc` you can write something like this:
9696+ on(event: string, callback: (arg: any) => void): void {
9797+ // Provide way to listen to your workers events, e.g. message, error, exit
9898+ }
77997878-```ts
7979-import { createBirpc } from 'birpc'
8080-import { parse, stringify } from 'flatted'
8181-import { createMethodsRPC, TestProject } from 'vitest/node'
100100+ off(event: string, callback: (arg: any) => void): void {
101101+ // Provide way to unsubscribe `on` listeners
102102+ }
821038383-function createRpc(project: TestProject, wss: WebSocketServer) {
8484- return createBirpc(
8585- createMethodsRPC(project),
8686- {
8787- post: msg => wss.send(msg),
8888- on: fn => wss.on('message', fn),
8989- serialize: stringify,
9090- deserialize: parse,
9191- },
9292- )
104104+ async start() {
105105+ // do something when the worker is started
106106+ }
107107+108108+ async stop() {
109109+ // cleanup the state
110110+ }
111111+112112+ deserialize(data) {
113113+ return data
114114+ }
93115}
94116```
951179696-You can see a simple example of a pool made from scratch that doesn't run tests but marks them as collected in [pool/custom-pool.ts](https://github.com/vitest-dev/vitest/blob/main/test/cli/fixtures/custom-pool/pool/custom-pool.ts).
118118+Your `CustomPoolRunner` will be controlling how your custom test runner worker life cycles and communication channel works. For example, your `CustomPoolRunner` could launch a `node:worker_threads` `Worker`, and provide communication via `Worker.postMessage` and `parentPort`.
119119+120120+In your worker file, you can import helper utilities from `vitest/worker`:
121121+122122+```ts [my-worker.ts]
123123+import { init, runBaseTests } from 'vitest/worker'
124124+125125+init({
126126+ post: (response) => {
127127+ // Provide way to send this message to CustomPoolRunner's onWorker as message event
128128+ },
129129+ on: (callback) => {
130130+ // Provide a way to listen CustomPoolRunner's "postMessage" calls
131131+ },
132132+ off: (callback) => {
133133+ // Optional, provide a way to remove listeners added by "on" calls
134134+ },
135135+ teardown: () => {
136136+ // Optional, provide a way to teardown worker, e.g. unsubscribe all the `on` listeners
137137+ },
138138+ serialize: (value) => {
139139+ // Optional, provide custom serializer for `post` calls
140140+ },
141141+ deserialize: (value) => {
142142+ // Optional, provide custom deserializer for `on` callbacks
143143+ },
144144+ runTests: state => runBaseTests('run', state),
145145+ collectTests: state => runBaseTests('collect', state),
146146+})
147147+```
+18-219
docs/config/index.md
···674674Write test results to a file when the `--reporter=json`, `--reporter=html` or `--reporter=junit` option is also specified.
675675By providing an object instead of a string you can define individual outputs when using multiple reporters.
676676677677-### pool<NonProjectOption /> {#pool}
677677+### pool
678678679679- **Type:** `'threads' | 'forks' | 'vmThreads' | 'vmForks'`
680680- **Default:** `'forks'`
···682682683683Pool used to run tests in.
684684685685-#### threads<NonProjectOption />
685685+#### threads
686686687687-Enable multi-threading using [tinypool](https://github.com/tinylibs/tinypool) (a lightweight fork of [Piscina](https://github.com/piscinajs/piscina)). When using threads you are unable to use process related APIs such as `process.chdir()`. Some libraries written in native languages, such as Prisma, `bcrypt` and `canvas`, have problems when running in multiple threads and run into segfaults. In these cases it is advised to use `forks` pool instead.
687687+Enable multi-threading. When using threads you are unable to use process related APIs such as `process.chdir()`. Some libraries written in native languages, such as Prisma, `bcrypt` and `canvas`, have problems when running in multiple threads and run into segfaults. In these cases it is advised to use `forks` pool instead.
688688689689-#### forks<NonProjectOption />
689689+#### forks
690690691691-Similar as `threads` pool but uses `child_process` instead of `worker_threads` via [tinypool](https://github.com/tinylibs/tinypool). Communication between tests and main process is not as fast as with `threads` pool. Process related APIs such as `process.chdir()` are available in `forks` pool.
691691+Similar as `threads` pool but uses `child_process` instead of `worker_threads`. Communication between tests and main process is not as fast as with `threads` pool. Process related APIs such as `process.chdir()` are available in `forks` pool.
692692693693-#### vmThreads<NonProjectOption />
693693+#### vmThreads
694694695695Run tests using [VM context](https://nodejs.org/api/vm.html) (inside a sandboxed environment) in a `threads` pool.
696696697697-This makes tests run faster, but the VM module is unstable when running [ESM code](https://github.com/nodejs/node/issues/37648). Your tests will [leak memory](https://github.com/nodejs/node/issues/33439) - to battle that, consider manually editing [`poolOptions.vmThreads.memoryLimit`](#pooloptions-vmthreads-memorylimit) value.
697697+This makes tests run faster, but the VM module is unstable when running [ESM code](https://github.com/nodejs/node/issues/37648). Your tests will [leak memory](https://github.com/nodejs/node/issues/33439) - to battle that, consider manually editing [`vmMemoryLimit`](#vmMemorylimit) value.
698698699699::: warning
700700Running code in a sandbox has some advantages (faster tests), but also comes with a number of disadvantages.
···716716Please, be aware of these issues when using this option. Vitest team cannot fix any of the issues on our side.
717717:::
718718719719-#### vmForks<NonProjectOption />
719719+#### vmForks
720720721721-Similar as `vmThreads` pool but uses `child_process` instead of `worker_threads` via [tinypool](https://github.com/tinylibs/tinypool). Communication between tests and the main process is not as fast as with `vmThreads` pool. Process related APIs such as `process.chdir()` are available in `vmForks` pool. Please be aware that this pool has the same pitfalls listed in `vmThreads`.
721721+Similar as `vmThreads` pool but uses `child_process` instead of `worker_threads`. Communication between tests and the main process is not as fast as with `vmThreads` pool. Process related APIs such as `process.chdir()` are available in `vmForks` pool. Please be aware that this pool has the same pitfalls listed in `vmThreads`.
722722723723-### poolOptions<NonProjectOption /> {#pooloptions}
724724-725725-- **Type:** `Record<'threads' | 'forks' | 'vmThreads' | 'vmForks', {}>`
726726-- **Default:** `{}`
727727-728728-#### poolOptions.threads
729729-730730-Options for `threads` pool.
731731-732732-```ts
733733-import { defineConfig } from 'vitest/config'
734734-735735-export default defineConfig({
736736- test: {
737737- poolOptions: {
738738- threads: {
739739- // Threads related options here
740740- }
741741- }
742742- }
743743-})
744744-```
745745-746746-##### poolOptions.threads.maxThreads<NonProjectOption />
747747-748748-- **Type:** `number | string`
749749-- **Default:** _available CPUs_
750750-751751-Maximum number or percentage of threads. You can also use `VITEST_MAX_THREADS` environment variable.
752752-753753-##### poolOptions.threads.singleThread
754754-755755-- **Type:** `boolean`
756756-- **Default:** `false`
757757-758758-Run all tests with the same environment inside a single worker thread. This will disable built-in module isolation (your source code or [inlined](#server-deps-inline) code will still be reevaluated for each test), but can improve test performance.
759759-760760-:::warning
761761-Even though this option will force tests to run one after another, this option is different from Jest's `--runInBand`. Vitest uses workers not only for running tests in parallel, but also to provide isolation. By disabling this option, your tests will run sequentially, but in the same global context, so you must provide isolation yourself.
762762-763763-This might cause all sorts of issues, if you are relying on global state (frontend frameworks usually do) or your code relies on environment to be defined separately for each test. But can be a speed boost for your tests (up to 3 times faster), that don't necessarily rely on global state or can easily bypass that.
764764-:::
765765-766766-##### poolOptions.threads.useAtomics<NonProjectOption />
767767-768768-- **Type:** `boolean`
769769-- **Default:** `false`
770770-771771-Use Atomics to synchronize threads.
772772-773773-This can improve performance in some cases, but might cause segfault in older Node versions.
774774-775775-##### poolOptions.threads.isolate
776776-777777-- **Type:** `boolean`
778778-- **Default:** `true`
779779-780780-Isolate environment for each test file.
781781-782782-##### poolOptions.threads.execArgv<NonProjectOption />
723723+### execArgv
783724784725- **Type:** `string[]`
785726- **Default:** `[]`
786727787787-Pass additional arguments to `node` in the threads. See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information.
728728+Pass additional arguments to `node` in the runner worker. See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information.
788729789730:::warning
790731Be careful when using, it as some options may crash worker, e.g. --prof, --title. See https://github.com/nodejs/node/issues/41103.
791732:::
792733793793-#### poolOptions.forks
794794-795795-Options for `forks` pool.
796796-797797-```ts
798798-import { defineConfig } from 'vitest/config'
799799-800800-export default defineConfig({
801801- test: {
802802- poolOptions: {
803803- forks: {
804804- // Forks related options here
805805- }
806806- }
807807- }
808808-})
809809-```
810810-811811-##### poolOptions.forks.maxForks<NonProjectOption />
812812-813813-- **Type:** `number | string`
814814-- **Default:** _available CPUs_
815815-816816-Maximum number or percentage of forks. You can also use `VITEST_MAX_FORKS` environment variable.
817817-818818-##### poolOptions.forks.isolate
819819-820820-- **Type:** `boolean`
821821-- **Default:** `true`
822822-823823-Isolate environment for each test file.
824824-825825-##### poolOptions.forks.singleFork
826826-827827-- **Type:** `boolean`
828828-- **Default:** `false`
829829-830830-Run all tests with the same environment inside a single child process. This will disable built-in module isolation (your source code or [inlined](#server-deps-inline) code will still be reevaluated for each test), but can improve test performance.
831831-832832-:::warning
833833-Even though this option will force tests to run one after another, this option is different from Jest's `--runInBand`. Vitest uses child processes not only for running tests in parallel, but also to provide isolation. By disabling this option, your tests will run sequentially, but in the same global context, so you must provide isolation yourself.
834834-835835-This might cause all sorts of issues, if you are relying on global state (frontend frameworks usually do) or your code relies on environment to be defined separately for each test. But can be a speed boost for your tests (up to 3 times faster), that don't necessarily rely on global state or can easily bypass that.
836836-:::
837837-838838-##### poolOptions.forks.execArgv<NonProjectOption />
839839-840840-- **Type:** `string[]`
841841-- **Default:** `[]`
842842-843843-Pass additional arguments to `node` process in the child processes. See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information.
844844-845845-:::warning
846846-Be careful when using, it as some options may crash worker, e.g. --prof, --title. See https://github.com/nodejs/node/issues/41103.
847847-:::
848848-849849-#### poolOptions.vmThreads
850850-851851-Options for `vmThreads` pool.
852852-853853-```ts
854854-import { defineConfig } from 'vitest/config'
855855-856856-export default defineConfig({
857857- test: {
858858- poolOptions: {
859859- vmThreads: {
860860- // VM threads related options here
861861- }
862862- }
863863- }
864864-})
865865-```
866866-867867-##### poolOptions.vmThreads.maxThreads<NonProjectOption />
868868-869869-- **Type:** `number | string`
870870-- **Default:** _available CPUs_
871871-872872-Maximum number or percentage of threads. You can also use `VITEST_MAX_THREADS` environment variable.
873873-874874-##### poolOptions.vmThreads.memoryLimit<NonProjectOption />
734734+### vmMemoryLimit
875735876736- **Type:** `string | number`
877737- **Default:** `1 / CPU Cores`
738738+739739+This option affects only `vmForks` and `vmThreads` pools.
878740879741Specifies the memory limit for workers before they are recycled. This value heavily depends on your environment, so it's better to specify it manually instead of relying on the default.
880742···900762Percentage based memory limit [does not work on Linux CircleCI](https://github.com/jestjs/jest/issues/11956#issuecomment-1212925677) workers due to incorrect system memory being reported.
901763:::
902764903903-##### poolOptions.vmThreads.useAtomics<NonProjectOption />
904904-905905-- **Type:** `boolean`
906906-- **Default:** `false`
907907-908908-Use Atomics to synchronize threads.
909909-910910-This can improve performance in some cases, but might cause segfault in older Node versions.
911911-912912-##### poolOptions.vmThreads.execArgv<NonProjectOption />
913913-914914-- **Type:** `string[]`
915915-- **Default:** `[]`
916916-917917-Pass additional arguments to `node` process in the VM context. See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information.
918918-919919-:::warning
920920-Be careful when using, it as some options may crash worker, e.g. --prof, --title. See https://github.com/nodejs/node/issues/41103.
921921-:::
922922-923923-#### poolOptions.vmForks<NonProjectOption />
924924-925925-Options for `vmForks` pool.
926926-927927-```ts
928928-import { defineConfig } from 'vitest/config'
929929-930930-export default defineConfig({
931931- test: {
932932- poolOptions: {
933933- vmForks: {
934934- // VM forks related options here
935935- }
936936- }
937937- }
938938-})
939939-```
940940-941941-##### poolOptions.vmForks.maxForks<NonProjectOption />
942942-943943-- **Type:** `number | string`
944944-- **Default:** _available CPUs_
945945-946946-Maximum number or percentage of forks. You can also use `VITEST_MAX_FORKS` environment variable.
947947-948948-##### poolOptions.vmForks.memoryLimit<NonProjectOption />
949949-950950-- **Type:** `string | number`
951951-- **Default:** `1 / CPU Cores`
952952-953953-Specifies the memory limit for workers before they are recycled. This value heavily depends on your environment, so it's better to specify it manually instead of relying on the default. How the value is calculated is described in [`poolOptions.vmThreads.memoryLimit`](#pooloptions-vmthreads-memorylimit)
954954-955955-##### poolOptions.vmForks.execArgv<NonProjectOption />
956956-957957-- **Type:** `string[]`
958958-- **Default:** `[]`
959959-960960-Pass additional arguments to `node` process in the VM context. See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information.
961961-962962-:::warning
963963-Be careful when using, it as some options may crash worker, e.g. --prof, --title. See https://github.com/nodejs/node/issues/41103.
964964-:::
965965-966966-### fileParallelism<NonProjectOption /> {#fileparallelism}
765765+### fileParallelism
967766968767- **Type:** `boolean`
969768- **Default:** `true`
···975774This option doesn't affect tests running in the same file. If you want to run those in parallel, use `concurrent` option on [describe](/api/#describe-concurrent) or via [a config](#sequence-concurrent).
976775:::
977776978978-### maxWorkers<NonProjectOption /> {#maxworkers}
777777+### maxWorkers
979778980779- **Type:** `number | string`
981780982982-Maximum number or percentage of workers to run tests in. `poolOptions.{threads,vmThreads}.maxThreads`/`poolOptions.forks.maxForks` has higher priority.
781781+Maximum number or percentage of workers to run tests in.
983782984783### testTimeout
985784···23422141Disabling this option might [improve performance](/guide/improving-performance) if your code doesn't rely on side effects (which is usually true for projects with `node` environment).
2343214223442143::: tip
23452345-You can disable isolation for specific pools by using [`poolOptions`](#pooloptions) property.
21442144+You can disable isolation for specific test files by using Vitest workspaces and disabling isolation per project.
23462145:::
2347214623482147### includeTaskLocation {#includeTaskLocation}
+8-106
docs/guide/cli-generated.md
···390390391391Specify pool, if not running in the browser (default: `forks`)
392392393393-### poolOptions.threads.isolate
393393+### execArgv
394394395395-- **CLI:** `--poolOptions.threads.isolate`
396396-- **Config:** [poolOptions.threads.isolate](/config/#pooloptions-threads-isolate)
395395+- **CLI:** `--execArgv <option>`
396396+- **Config:** [execArgv](/config/#execargv)
397397398398-Isolate tests in threads pool (default: `true`)
398398+Pass additional arguments to `node` process when spawning `worker_threads` or `child_process`.
399399400400-### poolOptions.threads.singleThread
400400+### vmMemoryLimit
401401402402-- **CLI:** `--poolOptions.threads.singleThread`
403403-- **Config:** [poolOptions.threads.singleThread](/config/#pooloptions-threads-singlethread)
402402+- **CLI:** `--vmMemoryLimit <limit>`
403403+- **Config:** [vmMemoryLimit](/config/#vmmemorylimit)
404404405405-Run tests inside a single thread (default: `false`)
406406-407407-### poolOptions.threads.maxThreads
408408-409409-- **CLI:** `--poolOptions.threads.maxThreads <workers>`
410410-- **Config:** [poolOptions.threads.maxThreads](/config/#pooloptions-threads-maxthreads)
411411-412412-Maximum number or percentage of threads to run tests in
413413-414414-### poolOptions.threads.useAtomics
415415-416416-- **CLI:** `--poolOptions.threads.useAtomics`
417417-- **Config:** [poolOptions.threads.useAtomics](/config/#pooloptions-threads-useatomics)
418418-419419-Use Atomics to synchronize threads. This can improve performance in some cases, but might cause segfault in older Node versions (default: `false`)
420420-421421-### poolOptions.vmThreads.isolate
422422-423423-- **CLI:** `--poolOptions.vmThreads.isolate`
424424-- **Config:** [poolOptions.vmThreads.isolate](/config/#pooloptions-vmthreads-isolate)
425425-426426-Isolate tests in threads pool (default: `true`)
427427-428428-### poolOptions.vmThreads.singleThread
429429-430430-- **CLI:** `--poolOptions.vmThreads.singleThread`
431431-- **Config:** [poolOptions.vmThreads.singleThread](/config/#pooloptions-vmthreads-singlethread)
432432-433433-Run tests inside a single thread (default: `false`)
434434-435435-### poolOptions.vmThreads.maxThreads
436436-437437-- **CLI:** `--poolOptions.vmThreads.maxThreads <workers>`
438438-- **Config:** [poolOptions.vmThreads.maxThreads](/config/#pooloptions-vmthreads-maxthreads)
439439-440440-Maximum number or percentage of threads to run tests in
441441-442442-### poolOptions.vmThreads.useAtomics
443443-444444-- **CLI:** `--poolOptions.vmThreads.useAtomics`
445445-- **Config:** [poolOptions.vmThreads.useAtomics](/config/#pooloptions-vmthreads-useatomics)
446446-447447-Use Atomics to synchronize threads. This can improve performance in some cases, but might cause segfault in older Node versions (default: `false`)
448448-449449-### poolOptions.vmThreads.memoryLimit
450450-451451-- **CLI:** `--poolOptions.vmThreads.memoryLimit <limit>`
452452-- **Config:** [poolOptions.vmThreads.memoryLimit](/config/#pooloptions-vmthreads-memorylimit)
453453-454454-Memory limit for VM threads pool. If you see memory leaks, try to tinker this value.
455455-456456-### poolOptions.forks.isolate
457457-458458-- **CLI:** `--poolOptions.forks.isolate`
459459-- **Config:** [poolOptions.forks.isolate](/config/#pooloptions-forks-isolate)
460460-461461-Isolate tests in forks pool (default: `true`)
462462-463463-### poolOptions.forks.singleFork
464464-465465-- **CLI:** `--poolOptions.forks.singleFork`
466466-- **Config:** [poolOptions.forks.singleFork](/config/#pooloptions-forks-singlefork)
467467-468468-Run tests inside a single child_process (default: `false`)
469469-470470-### poolOptions.forks.maxForks
471471-472472-- **CLI:** `--poolOptions.forks.maxForks <workers>`
473473-- **Config:** [poolOptions.forks.maxForks](/config/#pooloptions-forks-maxforks)
474474-475475-Maximum number or percentage of processes to run tests in
476476-477477-### poolOptions.vmForks.isolate
478478-479479-- **CLI:** `--poolOptions.vmForks.isolate`
480480-- **Config:** [poolOptions.vmForks.isolate](/config/#pooloptions-vmforks-isolate)
481481-482482-Isolate tests in forks pool (default: `true`)
483483-484484-### poolOptions.vmForks.singleFork
485485-486486-- **CLI:** `--poolOptions.vmForks.singleFork`
487487-- **Config:** [poolOptions.vmForks.singleFork](/config/#pooloptions-vmforks-singlefork)
488488-489489-Run tests inside a single child_process (default: `false`)
490490-491491-### poolOptions.vmForks.maxForks
492492-493493-- **CLI:** `--poolOptions.vmForks.maxForks <workers>`
494494-- **Config:** [poolOptions.vmForks.maxForks](/config/#pooloptions-vmforks-maxforks)
495495-496496-Maximum number or percentage of processes to run tests in
497497-498498-### poolOptions.vmForks.memoryLimit
499499-500500-- **CLI:** `--poolOptions.vmForks.memoryLimit <limit>`
501501-- **Config:** [poolOptions.vmForks.memoryLimit](/config/#pooloptions-vmforks-memorylimit)
502502-503503-Memory limit for VM forks pool. If you see memory leaks, try to tinker this value.
405405+Memory limit for VM pools. If you see memory leaks, try to tinker this value.
504406505407### fileParallelism
506408
+2-12
docs/guide/debugging.md
···120120121121```sh
122122# To run in a single worker
123123-vitest --inspect-brk --pool threads --poolOptions.threads.singleThread
124124-125125-# To run in a single child process
126126-vitest --inspect-brk --pool forks --poolOptions.forks.singleFork
123123+vitest --inspect-brk --no-file-parallelism
127124128125# To run in browser mode
129126vitest --inspect-brk --browser --no-file-parallelism
130127```
131128132132-If you are using Vitest 1.1 or higher, you can also just provide `--no-file-parallelism` flag:
133133-134134-```sh
135135-# If pool is unknown
136136-vitest --inspect-brk --no-file-parallelism
137137-```
138138-139129Once Vitest starts it will stop execution and wait for you to open developer tools that can connect to [Node.js inspector](https://nodejs.org/en/docs/guides/debugging-getting-started/). You can use Chrome DevTools for this by opening `chrome://inspect` on browser.
140130141141-In watch mode you can keep the debugger open during test re-runs by using the `--poolOptions.threads.isolate false` options.
131131+In watch mode you can keep the debugger open during test re-runs by using the `--isolate false` options.
+2-3
docs/guide/features.md
···34343535## Threads
36363737-By default Vitest runs test files in [multiple processes](/guide/parallelism) using [`node:child_process`](https://nodejs.org/api/child_process.html) via [Tinypool](https://github.com/tinylibs/tinypool) (a lightweight fork of [Piscina](https://github.com/piscinajs/piscina)), allowing tests to run simultaneously. If you want to speed up your test suite even further, consider enabling `--pool=threads` to run tests using [`node:worker_threads`](https://nodejs.org/api/worker_threads.html) (beware that some packages might not work with this setup).
3838-3939-To run tests in a single thread or process, see [`poolOptions`](/config/#pooloptions).
3737+By default Vitest runs test files in [multiple processes](/guide/parallelism) using [`node:child_process`](https://nodejs.org/api/child_process.html), allowing tests to run simultaneously. If you want to speed up your test suite even further, consider enabling `--pool=threads` to run tests using [`node:worker_threads`](https://nodejs.org/api/worker_threads.html) (beware that some packages might not work with this setup).
3838+To run tests in a single thread or process, see [`fileParallelism`](/config/#fileParallelism).
40394140Vitest also isolates each file's environment so env mutations in one file don't affect others. Isolation can be disabled by passing `--no-isolate` to the CLI (trading correctness for run performance).
4241
+28-11
docs/guide/improving-performance.md
···2020export default defineConfig({
2121 test: {
2222 isolate: false,
2323- // you can also disable isolation only for specific pools
2424- poolOptions: {
2525- forks: {
2626- isolate: false,
2727- },
2828- },
2923 },
3024})
3125```
3226:::
2727+2828+You can also disable isolation for specific files only by using `projects`:
2929+3030+```ts [vitest.config.js]
3131+import { defineConfig } from 'vitest/config'
3232+3333+export default defineConfig({
3434+ test: {
3535+ projects: [
3636+ {
3737+ name: 'Isolated',
3838+ isolate: true, // (default value)
3939+ exclude: ['**.non-isolated.test.ts'],
4040+ },
4141+ {
4242+ name: 'Non-isolated',
4343+ isolate: false,
4444+ include: ['**.non-isolated.test.ts'],
4545+ }
4646+ ]
4747+ },
4848+})
4949+```
33503451:::tip
3552If you are using `vmThreads` pool, you cannot disable isolation. Use `threads` pool instead to improve your tests performance.
···179196```sh
180197# Example for splitting tests on 32 CPU to 4 shards.
181198# As each process needs 1 main thread, there's 7 threads for test runners (1+7)*4 = 32
182182-# Use VITEST_MAX_THREADS or VITEST_MAX_FORKS depending on the pool:
183183-VITEST_MAX_THREADS=7 vitest run --reporter=blob --shard=1/4 & \
184184-VITEST_MAX_THREADS=7 vitest run --reporter=blob --shard=2/4 & \
185185-VITEST_MAX_THREADS=7 vitest run --reporter=blob --shard=3/4 & \
186186-VITEST_MAX_THREADS=7 vitest run --reporter=blob --shard=4/4 & \
199199+# Use VITEST_MAX_WORKERS:
200200+VITEST_MAX_WORKERS=7 vitest run --reporter=blob --shard=1/4 & \
201201+VITEST_MAX_WORKERS=7 vitest run --reporter=blob --shard=2/4 & \
202202+VITEST_MAX_WORKERS=7 vitest run --reporter=blob --shard=3/4 & \
203203+VITEST_MAX_WORKERS=7 vitest run --reporter=blob --shard=4/4 & \
187204wait # https://man7.org/linux/man-pages/man2/waitpid.2.html
188205189206vitest run --merge-reports
+101-1
docs/guide/migration.md
···271271Both `@vitest/browser/context` and `@vitest/browser/utils` work at runtime during the transition period, but they will be removed in a future release.
272272:::
273273274274+### Pool Rework
275275+276276+Vitest has used [`tinypool`](https://github.com/tinylibs/tinypool) for orchestrating how test files are run in the test runner workers. Tinypool has controlled how complex tasks like parallelism, isolation and IPC communication works internally. However we've found that Tinypool has some flaws that are slowing down development of Vitest. In Vitest v4 we've completely removed Tinypool and rewritten how pools work without new dependencies. Read more about reasoning from [feat!: rewrite pools without tinypool #8705
277277+](https://github.com/vitest-dev/vitest/pull/8705).
278278+279279+New pool architecture allows Vitest to simplify many previously complex configuration options:
280280+281281+- `maxThreads` and `maxForks` are now `maxWorkers`.
282282+- Environment variables `VITEST_MAX_THREADS` and `VITEST_MAX_FORKS` are now `VITEST_MAX_WORKERS`.
283283+- `singleThread` and `singleFork` are now `maxWorkers: 1`.
284284+- `poolOptions` is removed. All previous `poolOptions` are now top-level options. The `memoryLimit` of VM pools is renamed to `vmMemoryLimit`.
285285+- `threads.useAtomics` is removed. If you have a use case for this, feel free to open a new feature request.
286286+- Custom pool interface has been rewritten, see [Custom Pool](/advanced/pool.html#custom-pool)
287287+288288+```ts
289289+export default defineConfig({
290290+ test: {
291291+ poolOptions: { // [!code --]
292292+ forks: { // [!code --]
293293+ execArgv: ['--expose-gc'], // [!code --]
294294+ isolate: false, // [!code --]
295295+ singleFork: true, // [!code --]
296296+ }, // [!code --]
297297+ vmThreads: { // [!code --]
298298+ memoryLimit: '300Mb' // [!code --]
299299+ }, // [!code --]
300300+ }, // [!code --]
301301+ execArgv: ['--expose-gc'], // [!code ++]
302302+ isolate: false, // [!code ++]
303303+ maxWorkers: 1, // [!code ++]
304304+ memoryLimit: '300Mb', // [!code ++]
305305+ }
306306+})
307307+```
308308+309309+Previously it was not possible to specify some pool related options per project when using [Vitest Projects](/guide/projects). With the new architecture this is no longer a blocker.
310310+311311+::: code-group
312312+```ts [Isolation per project]
313313+import { defineConfig } from 'vitest/config'
314314+315315+export default defineConfig({
316316+ test: {
317317+ projects: [
318318+ {
319319+ // Non-isolated unit tests
320320+ name: 'Unit tests',
321321+ isolate: false,
322322+ exclude: ['**.integration.test.ts'],
323323+ },
324324+ {
325325+ // Isolated integration tests
326326+ name: 'Integration tests',
327327+ include: ['**.integration.test.ts'],
328328+ },
329329+ ],
330330+ },
331331+})
332332+```
333333+```ts [Parallel & Sequential projects]
334334+import { defineConfig } from 'vitest/config'
335335+336336+export default defineConfig({
337337+ test: {
338338+ projects: [
339339+ {
340340+ name: 'Parallel',
341341+ exclude: ['**.sequantial.test.ts'],
342342+ },
343343+ {
344344+ name: 'Sequential',
345345+ include: ['**.sequantial.test.ts'],
346346+ fileParallelism: false,
347347+ },
348348+ ],
349349+ },
350350+})
351351+```
352352+```ts [Node CLI options per project]
353353+import { defineConfig } from 'vitest/config'
354354+355355+export default defineConfig({
356356+ test: {
357357+ projects: [
358358+ {
359359+ name: 'Production env',
360360+ execArgv: ['--env-file=.env.prod']
361361+ },
362362+ {
363363+ name: 'Staging env',
364364+ execArgv: ['--env-file=.env.staging']
365365+ },
366366+ ],
367367+ },
368368+})
369369+```
370370+:::
371371+372372+See [Recipes](/guide/recipes) for more examples.
373373+274374### Reporter Updates
275375276376Reporter APIs `onCollected`, `onSpecsCollected`, `onPathsCollected`, `onTaskUpdate` and `onFinished` were removed. See [`Reporters API`](/advanced/api/reporters) for new alternatives. The new APIs were introduced in Vitest `v3.0.0`.
···424524425525### Envs
426526427427-Just like Jest, Vitest sets `NODE_ENV` to `test`, if it wasn't set before. Vitest also has a counterpart for `JEST_WORKER_ID` called `VITEST_POOL_ID` (always less than or equal to `maxThreads`), so if you rely on it, don't forget to rename it. Vitest also exposes `VITEST_WORKER_ID` which is a unique ID of a running worker - this number is not affected by `maxThreads`, and will increase with each created worker.
527527+Just like Jest, Vitest sets `NODE_ENV` to `test`, if it wasn't set before. Vitest also has a counterpart for `JEST_WORKER_ID` called `VITEST_POOL_ID` (always less than or equal to `maxWorkers`), so if you rely on it, don't forget to rename it. Vitest also exposes `VITEST_WORKER_ID` which is a unique ID of a running worker - this number is not affected by `maxWorkers`, and will increase with each created worker.
428528429529### Replace property
430530
+1-1
docs/guide/parallelism.md
···1212- `forks` (the default) and `vmForks` run tests in different [child processes](https://nodejs.org/api/child_process.html)
1313- `threads` and `vmThreads` run tests in different [worker threads](https://nodejs.org/api/worker_threads.html)
14141515-Both "child processes" and "worker threads" are refered to as "workers". You can configure the number of running workers with [`maxWorkers`](/config/#maxworkers) option. Or more granually with [`poolOptions`](/config/#pooloptions) configuration.
1515+Both "child processes" and "worker threads" are refered to as "workers". You can configure the number of running workers with [`maxWorkers`](/config/#maxworkers) option.
16161717If you have a lot of tests, it is usually faster to run them in parallel, but it also depends on the project, the environment and [isolation](/config/#isolate) state. To disable file parallelisation, you can set [`fileParallelism`](/config/#fileparallelism) to `false`. To learn more about possible performance improvements, read the [Performance Guide](/guide/improving-performance).
1818
+9-40
docs/guide/profiling-test-performance.md
···3434The `--prof` option does not work with `pool: 'threads'` due to `node:worker_threads` limitations.
3535:::
36363737-To pass these options to Vitest's test runner, define `poolOptions.<pool>.execArgv` in your Vitest configuration:
3737+To pass these options to Vitest's test runner, define `execArgv` in your Vitest configuration:
38383939-::: code-group
4040-```ts [Forks]
3939+```ts
4140import { defineConfig } from 'vitest/config'
42414342export default defineConfig({
4443 test: {
4545- pool: 'forks',
4646- poolOptions: {
4747- forks: {
4848- execArgv: [
4949- '--cpu-prof',
5050- '--cpu-prof-dir=test-runner-profile',
5151- '--heap-prof',
5252- '--heap-prof-dir=test-runner-profile'
5353- ],
5454-5555- // To generate a single profile
5656- singleFork: true,
5757- },
5858- },
4444+ fileParallelism: false,
4545+ execArgv: [
4646+ '--cpu-prof',
4747+ '--cpu-prof-dir=test-runner-profile',
4848+ '--heap-prof',
4949+ '--heap-prof-dir=test-runner-profile'
5050+ ],
5951 },
6052})
6153```
6262-```ts [Threads]
6363-import { defineConfig } from 'vitest/config'
6464-6565-export default defineConfig({
6666- test: {
6767- pool: 'threads',
6868- poolOptions: {
6969- threads: {
7070- execArgv: [
7171- '--cpu-prof',
7272- '--cpu-prof-dir=test-runner-profile',
7373- '--heap-prof',
7474- '--heap-prof-dir=test-runner-profile'
7575- ],
7676-7777- // To generate a single profile
7878- singleThread: true,
7979- },
8080- },
8181- },
8282-})
8383-```
8484-:::
85548655After the tests have run there should be a `test-runner-profile/*.cpuprofile` and `test-runner-profile/*.heapprofile` files generated. See [Inspecting profiling records](#inspecting-profiling-records) for instructions how to analyze these files.
8756
+55
docs/guide/recipes.md
···11+---
22+title: Recipes | Guide
33+---
44+55+# Recipes
66+77+## Disabling isolation for specific test files only
88+99+You can speed up your test run by disabling isolation for specific set of files by specifying `isolate` per `projects` entries:
1010+1111+```ts [vitest.config.ts]
1212+import { defineConfig } from 'vitest/config'
1313+1414+export default defineConfig({
1515+ test: {
1616+ projects: [
1717+ {
1818+ // Non-isolated unit tests
1919+ name: 'Unit tests',
2020+ isolate: false,
2121+ exclude: ['**.integration.test.ts'],
2222+ },
2323+ {
2424+ // Isolated integration tests
2525+ name: 'Integration tests',
2626+ include: ['**.integration.test.ts'],
2727+ },
2828+ ],
2929+ },
3030+})
3131+```
3232+3333+## Parallel and Sequential test files
3434+3535+You can split test files into parallel and sequential groups by using `projects` option:
3636+3737+```ts [vitest.config.ts]
3838+import { defineConfig } from 'vitest/config'
3939+4040+export default defineConfig({
4141+ test: {
4242+ projects: [
4343+ {
4444+ name: 'Parallel',
4545+ exclude: ['**.sequantial.test.ts'],
4646+ },
4747+ {
4848+ name: 'Sequential',
4949+ include: ['**.sequantial.test.ts'],
5050+ fileParallelism: false,
5151+ },
5252+ ],
5353+ },
5454+})
5555+```
+1-1
docs/guide/test-context.md
···409409410410The `worker` scope will run the fixture once per worker. The number of running workers depends on various factors. By default, every file runs in a separate worker, so `file` and `worker` scopes work the same way.
411411412412-However, if you disable [isolation](/config/#isolate), then the number of workers is limited by the [`maxWorkers`](/config/#maxworkers) or [`poolOptions`](/config/#pooloptions) configuration.
412412+However, if you disable [isolation](/config/#isolate), then the number of workers is limited by the [`maxWorkers`](/config/#maxworkers) configuration.
413413414414Note that specifying `scope: 'worker'` when running tests in `vmThreads` or `vmForks` will work the same way as `scope: 'file'`. This limitation exists because every test file has its own VM context, so if Vitest were to initiate it once, one context could leak to another and create many reference inconsistencies (instances of the same class would reference different constructors, for example).
415415
···55 test: {
66 include: ['specs/**/*.{spec,test}.ts'],
77 pool: 'threads',
88- poolOptions: {
99- threads: {
1010- singleThread: true,
1111- },
1212- },
88+ fileParallelism: false,
139 reporters: 'verbose',
1410 setupFiles: ['./setup.unit.ts'],
1511 // 3 is the maximum of browser instances - in a perfect world they will run in parallel
···77 pool,
88 })
991010- expect(stderr).toContain('⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯')
1010+ expect(stderr).toContain('⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯')
1111 expect(stderr).toContain('Error: [vitest-pool]: Unexpected call to process.send(). Make sure your test cases are not interfering with process\'s channel.')
1212})
+5-13
test/cli/test/network-imports.test.ts
···22import { runVitest } from '../../test-utils'
3344const config = {
55- poolOptions: {
66- threads: {
77- execArgv: ['--experimental-network-imports'],
88- },
99- forks: {
1010- execArgv: ['--experimental-network-imports'],
1111- },
1212- vmThreads: {
1313- execArgv: ['--experimental-network-imports'],
1414- },
1515- },
55+ execArgv: ['--experimental-network-imports'],
166 // let vite serve public/slash@3.0.0.js
177 api: 9602,
188}
1992010const [major] = process.version.slice(1).split('.')
21111212+// TODO: remove when we drop support for Node 20
2213// --experimental-network-imports was removed in Node 22 in favor of module loaders
2314it.runIf(Number(major) <= 20).each([
2415 'threads',
2516 'forks',
2617 'vmThreads',
2718])('importing from network in %s', async (pool) => {
2828- const { stderr, exitCode } = await runVitest({
1919+ const { ctx, exitCode } = await runVitest({
2920 ...config,
3021 root: './fixtures/network-imports',
3122 pool,
3223 })
3333- expect(stderr).toBe('')
2424+ expect(ctx!.state.getTestModules()).toHaveLength(1)
2525+ expect(ctx!.state.getTestModules()[0].state()).toBe('passed')
3426 expect(exitCode).toBe(0)
3527})
···11import type { Awaitable } from '@vitest/utils'
22import type { Vitest } from './core'
33+import type { PoolTask } from './pools/types'
34import type { TestProject } from './project'
45import type { TestSpecification } from './spec'
55-import type { BuiltinPool, Pool } from './types/pool-options'
66+import type { BuiltinPool, ResolvedConfig } from './types/config'
77+import * as nodeos from 'node:os'
68import { isatty } from 'node:tty'
79import { resolve } from 'pathe'
810import { version as viteVersion } from 'vite'
911import { rootDir } from '../paths'
1012import { isWindows } from '../utils/env'
1313+import { getWorkerMemoryLimit, stringToBytes } from '../utils/memory-limit'
1414+import { groupFilesByEnv } from '../utils/test-helpers'
1115import { createBrowserPool } from './pools/browser'
1212-import { createForksPool } from './pools/forks'
1313-import { createThreadsPool } from './pools/threads'
1414-import { createTypecheckPool } from './pools/typecheck'
1515-import { createVmForksPool } from './pools/vmForks'
1616-import { createVmThreadsPool } from './pools/vmThreads'
1616+import { Pool } from './pools/pool'
17171818const suppressWarningsPath = resolve(rootDir, './suppress-warnings.cjs')
19192020-export type RunWithFiles = (
2020+type RunWithFiles = (
2121 files: TestSpecification[],
2222 invalidates?: string[]
2323-) => Awaitable<void>
2424-2525-type LocalPool = Exclude<Pool, 'browser'>
2323+) => Promise<void>
26242725export interface ProcessPool {
2826 name: string
···4543 'typescript',
4644]
47454848-function getDefaultPoolName(project: TestProject): Pool {
4646+export function getFilePoolName(project: TestProject): ResolvedConfig['pool'] {
4947 if (project.config.browser.enabled) {
5048 return 'browser'
5149 }
5250 return project.config.pool
5351}
54525555-export function getFilePoolName(project: TestProject): Pool {
5656- return getDefaultPoolName(project)
5757-}
5858-5953export function createPool(ctx: Vitest): ProcessPool {
6060- const pools: Record<Pool, ProcessPool | null> = {
6161- forks: null,
6262- threads: null,
6363- browser: null,
6464- vmThreads: null,
6565- vmForks: null,
6666- typescript: null,
5454+ const pool = new Pool({
5555+ distPath: ctx.distPath,
5656+ teardownTimeout: ctx.config.teardownTimeout,
5757+ state: ctx.state,
5858+ }, ctx.logger)
5959+6060+ const options = resolveOptions(ctx)
6161+6262+ const Sequencer = ctx.config.sequence.sequencer
6363+ const sequencer = new Sequencer(ctx)
6464+6565+ let browserPool: ProcessPool | undefined
6666+6767+ async function executeTests(method: 'run' | 'collect', specs: TestSpecification[], invalidates?: string[]): Promise<void> {
6868+ ctx.onCancel(() => pool.cancel())
6969+7070+ if (ctx.config.shard) {
7171+ if (!ctx.config.passWithNoTests && ctx.config.shard.count > specs.length) {
7272+ throw new Error(
7373+ '--shard <count> must be a smaller than count of test files. '
7474+ + `Resolved ${specs.length} test files for --shard=${ctx.config.shard.index}/${ctx.config.shard.count}.`,
7575+ )
7676+ }
7777+ specs = await sequencer.shard(Array.from(specs))
7878+ }
7979+8080+ const taskGroups: {
8181+ tasks: PoolTask[]
8282+ maxWorkers: number
8383+ // browser pool has a more complex logic, so we keep it separately for now
8484+ browserSpecs: TestSpecification[]
8585+ }[] = []
8686+ let workerId = 0
8787+8888+ const sorted = await sequencer.sort(specs)
8989+ const groups = groupSpecs(sorted)
9090+9191+ for (const group of groups) {
9292+ if (!group) {
9393+ continue
9494+ }
9595+9696+ const taskGroup: PoolTask[] = []
9797+ const browserSpecs: TestSpecification[] = []
9898+ taskGroups.push({
9999+ tasks: taskGroup,
100100+ maxWorkers: group.maxWorkers,
101101+ browserSpecs,
102102+ })
103103+104104+ for (const specs of group.specs) {
105105+ const { project, pool } = specs[0]
106106+ if (pool === 'browser') {
107107+ browserSpecs.push(...specs)
108108+ continue
109109+ }
110110+111111+ const byEnv = await groupFilesByEnv(specs)
112112+ const env = Object.values(byEnv)[0][0]
113113+114114+ taskGroup.push({
115115+ context: {
116116+ pool,
117117+ config: project.serializedConfig,
118118+ files: specs.map(spec => ({ filepath: spec.moduleId, testLocations: spec.testLines })),
119119+ invalidates,
120120+ environment: env.environment,
121121+ projectName: project.name,
122122+ providedContext: project.getProvidedContext(),
123123+ workerId: workerId++,
124124+ },
125125+ project,
126126+ env: options.env,
127127+ execArgv: [...options.execArgv, ...project.config.execArgv],
128128+ worker: pool,
129129+ isolate: project.config.isolate,
130130+ memoryLimit: getMemoryLimit(ctx.config, pool) ?? null,
131131+ })
132132+ }
133133+ }
134134+135135+ const results: PromiseSettledResult<void>[] = []
136136+137137+ for (const { tasks, browserSpecs, maxWorkers } of taskGroups) {
138138+ pool.setMaxWorkers(maxWorkers)
139139+140140+ const promises = tasks.map(async (task) => {
141141+ if (ctx.isCancelling) {
142142+ return ctx.state.cancelFiles(task.context.files, task.project)
143143+ }
144144+145145+ try {
146146+ await pool.run(task, method)
147147+ }
148148+ catch (error) {
149149+ // Intentionally cancelled
150150+ if (ctx.isCancelling && error instanceof Error && error.message === 'Cancelled') {
151151+ ctx.state.cancelFiles(task.context.files, task.project)
152152+ }
153153+ else {
154154+ throw error
155155+ }
156156+ }
157157+ })
158158+159159+ if (browserSpecs.length) {
160160+ browserPool ??= createBrowserPool(ctx)
161161+ if (method === 'collect') {
162162+ promises.push(browserPool.collectTests(browserSpecs))
163163+ }
164164+ else {
165165+ promises.push(browserPool.runTests(browserSpecs))
166166+ }
167167+ }
168168+169169+ const groupResults = await Promise.allSettled(promises)
170170+171171+ results.push(...groupResults)
172172+ }
173173+174174+ const errors = results
175175+ .filter(result => result.status === 'rejected')
176176+ .map(result => result.reason)
177177+178178+ if (errors.length > 0) {
179179+ throw new AggregateError(
180180+ errors,
181181+ 'Errors occurred while running tests. For more information, see serialized error.',
182182+ )
183183+ }
67184 }
68185186186+ return {
187187+ name: 'default',
188188+ runTests: (files, invalidates) => executeTests('run', files, invalidates),
189189+ collectTests: (files, invalidates) => executeTests('collect', files, invalidates),
190190+ async close() {
191191+ await Promise.all([pool.close(), browserPool?.close?.()])
192192+ },
193193+ }
194194+}
195195+196196+function resolveOptions(ctx: Vitest) {
69197 // in addition to resolve.conditions Vite also adds production/development,
70198 // see: https://github.com/vitejs/vite/blob/af2aa09575229462635b7cbb6d248ca853057ba2/packages/vite/src/node/plugins/resolve.ts#L1056-L1080
71199 const viteMajor = Number(viteVersion.split('.')[0])
200200+72201 const potentialConditions = new Set(viteMajor >= 6
73202 ? (ctx.vite.config.ssr.resolve?.conditions ?? [])
74203 : [
···76205 'development',
77206 ...ctx.vite.config.resolve.conditions,
78207 ])
208208+79209 const conditions = [...potentialConditions]
80210 .filter((condition) => {
81211 if (condition === 'production') {
···103233 || execArg.startsWith('--diagnostic-dir'),
104234 )
105235106106- async function executeTests(method: 'runTests' | 'collectTests', files: TestSpecification[], invalidate?: string[]) {
107107- const options: PoolProcessOptions = {
108108- execArgv: [
109109- ...execArgv,
110110- ...conditions,
111111- '--experimental-import-meta-resolve',
112112- '--require',
113113- suppressWarningsPath,
114114- ],
115115- env: {
116116- TEST: 'true',
117117- VITEST: 'true',
118118- NODE_ENV: process.env.NODE_ENV || 'test',
119119- VITEST_MODE: ctx.config.watch ? 'WATCH' : 'RUN',
120120- FORCE_TTY: isatty(1) ? 'true' : '',
121121- ...process.env,
122122- ...ctx.config.env,
123123- },
124124- }
125125-126126- // env are case-insensitive on Windows, but spawned processes don't support it
127127- if (isWindows) {
128128- for (const name in options.env) {
129129- options.env[name.toUpperCase()] = options.env[name]
130130- }
131131- }
132132-133133- const poolConcurrentPromises = new Map<string, Promise<ProcessPool>>()
134134- const customPools = new Map<string, ProcessPool>()
135135- async function resolveCustomPool(filepath: string) {
136136- if (customPools.has(filepath)) {
137137- return customPools.get(filepath)!
138138- }
139139-140140- const pool = await ctx.runner.import(filepath)
141141- if (typeof pool.default !== 'function') {
142142- throw new TypeError(
143143- `Custom pool "${filepath}" must export a function as default export`,
144144- )
145145- }
146146-147147- const poolInstance = await pool.default(ctx, options)
148148-149149- if (typeof poolInstance?.name !== 'string') {
150150- throw new TypeError(
151151- `Custom pool "${filepath}" should return an object with "name" property`,
152152- )
153153- }
154154- if (typeof poolInstance?.[method] !== 'function') {
155155- throw new TypeError(
156156- `Custom pool "${filepath}" should return an object with "${method}" method`,
157157- )
158158- }
159159-160160- customPools.set(filepath, poolInstance)
161161- return poolInstance as ProcessPool
162162- }
163163-164164- function getConcurrentPool(pool: string, fn: () => Promise<ProcessPool>) {
165165- if (poolConcurrentPromises.has(pool)) {
166166- return poolConcurrentPromises.get(pool)!
167167- }
168168- const promise = fn().finally(() => {
169169- poolConcurrentPromises.delete(pool)
170170- })
171171- poolConcurrentPromises.set(pool, promise)
172172- return promise
173173- }
174174-175175- function getCustomPool(pool: string) {
176176- return getConcurrentPool(pool, () => resolveCustomPool(pool))
177177- }
178178-179179- const groupedSpecifications: Record<string, TestSpecification[]> = {}
180180- const groups = new Set<number>()
181181-182182- const factories: Record<LocalPool, (specs: TestSpecification[]) => ProcessPool> = {
183183- vmThreads: specs => createVmThreadsPool(ctx, options, specs),
184184- vmForks: specs => createVmForksPool(ctx, options, specs),
185185- threads: specs => createThreadsPool(ctx, options, specs),
186186- forks: specs => createForksPool(ctx, options, specs),
187187- typescript: () => createTypecheckPool(ctx),
188188- browser: () => createBrowserPool(ctx),
189189- }
190190-191191- for (const spec of files) {
192192- const group = spec.project.config.sequence.groupOrder ?? 0
193193- groups.add(group)
194194- groupedSpecifications[group] ??= []
195195- groupedSpecifications[group].push(spec)
196196- }
197197-198198- const Sequencer = ctx.config.sequence.sequencer
199199- const sequencer = new Sequencer(ctx)
200200-201201- async function sortSpecs(specs: TestSpecification[]) {
202202- if (ctx.config.shard) {
203203- if (!ctx.config.passWithNoTests && ctx.config.shard.count > specs.length) {
204204- throw new Error(
205205- '--shard <count> must be a smaller than count of test files. '
206206- + `Resolved ${specs.length} test files for --shard=${ctx.config.shard.index}/${ctx.config.shard.count}.`,
207207- )
208208- }
209209-210210- specs = await sequencer.shard(specs)
211211- }
212212- return sequencer.sort(specs)
213213- }
214214-215215- const sortedGroups = Array.from(groups).sort()
216216- for (const group of sortedGroups) {
217217- const specifications = groupedSpecifications[group]
218218-219219- if (!specifications?.length) {
220220- continue
221221- }
222222-223223- const filesByPool: Record<LocalPool, TestSpecification[]> = {
224224- forks: [],
225225- threads: [],
226226- vmThreads: [],
227227- vmForks: [],
228228- typescript: [],
229229- }
230230-231231- specifications.forEach((specification) => {
232232- const pool = specification.pool
233233- filesByPool[pool] ??= []
234234- filesByPool[pool].push(specification)
235235- })
236236-237237- await Promise.all(
238238- Object.entries(filesByPool).map(async (entry) => {
239239- const [pool, files] = entry as [Pool, TestSpecification[]]
240240-241241- if (!files.length) {
242242- return null
243243- }
244244-245245- const specs = await sortSpecs(files)
246246-247247- if (pool in factories) {
248248- const factory = factories[pool]
249249- pools[pool] ??= factory(specs)
250250- return pools[pool]
251251- }
252252-253253- const poolHandler = await getCustomPool(pool)
254254- pools[poolHandler.name] ??= poolHandler
255255- return poolHandler[method](specs, invalidate)
256256- }),
257257- )
258258- }
259259- }
260260-261261- return {
262262- name: 'default',
263263- runTests: (files, invalidates) => executeTests('runTests', files, invalidates),
264264- collectTests: (files, invalidates) => executeTests('collectTests', files, invalidates),
265265- async close() {
266266- await Promise.all(Object.values(pools).map(p => p?.close?.()))
236236+ const options: PoolProcessOptions = {
237237+ execArgv: [
238238+ ...execArgv,
239239+ ...conditions,
240240+ '--experimental-import-meta-resolve',
241241+ '--require',
242242+ suppressWarningsPath,
243243+ ],
244244+ env: {
245245+ TEST: 'true',
246246+ VITEST: 'true',
247247+ NODE_ENV: process.env.NODE_ENV || 'test',
248248+ VITEST_MODE: ctx.config.watch ? 'WATCH' : 'RUN',
249249+ FORCE_TTY: isatty(1) ? 'true' : '',
250250+ ...process.env,
251251+ ...ctx.config.env,
267252 },
268253 }
254254+255255+ // env are case-insensitive on Windows, but spawned processes don't support it
256256+ if (isWindows) {
257257+ for (const name in options.env) {
258258+ options.env[name.toUpperCase()] = options.env[name]
259259+ }
260260+ }
261261+262262+ return options
263263+}
264264+265265+function resolveMaxWorkers(project: TestProject) {
266266+ if (project.config.maxWorkers) {
267267+ return project.config.maxWorkers
268268+ }
269269+270270+ if (project.vitest.config.maxWorkers) {
271271+ return project.vitest.config.maxWorkers
272272+ }
273273+274274+ const numCpus = typeof nodeos.availableParallelism === 'function'
275275+ ? nodeos.availableParallelism()
276276+ : nodeos.cpus().length
277277+278278+ if (project.vitest.config.watch) {
279279+ return Math.max(Math.floor(numCpus / 2), 1)
280280+ }
281281+282282+ return Math.max(numCpus - 1, 1)
283283+}
284284+285285+function getMemoryLimit(config: ResolvedConfig, pool: string) {
286286+ if (pool !== 'vmForks' && pool !== 'vmThreads') {
287287+ return null
288288+ }
289289+290290+ const memory = nodeos.totalmem()
291291+ const limit = getWorkerMemoryLimit(config)
292292+293293+ if (typeof memory === 'number') {
294294+ return stringToBytes(limit, config.watch ? memory / 2 : memory)
295295+ }
296296+297297+ // If totalmem is not supported we cannot resolve percentage based values like 0.5, "50%"
298298+ if (
299299+ (typeof limit === 'number' && limit > 1)
300300+ || (typeof limit === 'string' && limit.at(-1) !== '%')
301301+ ) {
302302+ return stringToBytes(limit)
303303+ }
304304+305305+ // just ignore "memoryLimit" value because we cannot detect memory limit
306306+ return null
307307+}
308308+309309+function groupSpecs(specs: TestSpecification[]) {
310310+ // Test files are passed to test runner one at a time, except Typechecker.
311311+ // TODO: Should non-isolated test files be passed to test runner all at once?
312312+ type SpecsForRunner = TestSpecification[]
313313+314314+ // Tests in a single group are executed with `maxWorkers` parallelism.
315315+ // Next group starts running after previous finishes - allows real sequential tests.
316316+ interface Groups { specs: SpecsForRunner[]; maxWorkers: number }
317317+ const groups: Groups[] = []
318318+319319+ // Files without file parallelism but without explicit sequence.groupOrder
320320+ const sequential: Groups = { specs: [], maxWorkers: 1 }
321321+322322+ // Type tests are run in a single group, per project
323323+ const typechecks: Record<string, TestSpecification[]> = {}
324324+325325+ specs.forEach((spec) => {
326326+ if (spec.pool === 'typescript') {
327327+ typechecks[spec.project.name] ||= []
328328+ typechecks[spec.project.name].push(spec)
329329+ return
330330+ }
331331+332332+ const order = spec.project.config.sequence.groupOrder
333333+334334+ // Files that have disabled parallelism and default groupId are set into their own group
335335+ if (order === 0 && spec.project.config.fileParallelism === false) {
336336+ return sequential.specs.push([spec])
337337+ }
338338+339339+ const maxWorkers = resolveMaxWorkers(spec.project)
340340+ groups[order] ||= { specs: [], maxWorkers }
341341+342342+ // Multiple projects with different maxWorkers but same groupId
343343+ if (groups[order].maxWorkers !== maxWorkers) {
344344+ const last = groups[order].specs.at(-1)?.at(-1)?.project.name
345345+346346+ throw new Error(`Projects "${last}" and "${spec.project.name}" have different 'maxWorkers' but same 'sequence.groupId'.\nProvide unique 'sequence.groupId' for them.`)
347347+ }
348348+349349+ groups[order].specs.push([spec])
350350+ })
351351+352352+ for (const project in typechecks) {
353353+ const order = Math.max(0, ...groups.keys()) + 1
354354+355355+ groups[order] ||= { specs: [], maxWorkers: resolveMaxWorkers(typechecks[project][0].project) }
356356+ groups[order].specs.push(typechecks[project])
357357+ }
358358+359359+ if (sequential.specs.length) {
360360+ groups.push(sequential)
361361+ }
362362+363363+ return groups
269364}
+1-1
packages/vitest/src/node/spec.ts
···11import type { SerializedTestSpecification } from '../runtime/types/utils'
22import type { TestProject } from './project'
33import type { TestModule } from './reporters/reported-tasks'
44-import type { Pool } from './types/pool-options'
44+import type { Pool } from './types/config'
55import { generateFileHash } from '@vitest/runner/utils'
66import { relative } from 'pathe'
77
+13-14
packages/vitest/src/node/state.ts
···11-import type { File, Task, TaskResultPack } from '@vitest/runner'
11+import type { File, FileSpecification, Task, TaskResultPack } from '@vitest/runner'
22import type { UserConsoleLog } from '../types/general'
33import type { TestProject } from './project'
44import type { MergedBlobs } from './reporters/blob'
55import type { OnUnhandledErrorCallback } from './types/config'
66-import { createFileTask } from '@vitest/runner/utils'
66+import { createFileTask, generateFileHash } from '@vitest/runner/utils'
77+import { relative } from 'pathe'
78import { defaultBrowserPort } from '../constants'
89import { TestCase, TestModule, TestSuite } from './reporters/reported-tasks'
910···2122 idMap: Map<string, Task> = new Map()
2223 taskFileMap: WeakMap<Task, File> = new WeakMap()
2324 errorsSet: Set<unknown> = new Set()
2424- processTimeoutCauses: Set<string> = new Set()
2525 reportedTasksMap: WeakMap<Task, TestModule | TestCase | TestSuite> = new WeakMap()
2626 blobs?: MergedBlobs
2727 transformTime = 0
···88888989 getUnhandledErrors(): unknown[] {
9090 return Array.from(this.errorsSet.values())
9191- }
9292-9393- addProcessTimeoutCause(cause: string): void {
9494- this.processTimeoutCauses.add(cause)
9595- }
9696-9797- getProcessTimeoutCauses(): string[] {
9898- return Array.from(this.processTimeoutCauses.values())
9991 }
1009210193 getPaths(): string[] {
···257249 ).length
258250 }
259251260260- cancelFiles(files: string[], project: TestProject): void {
252252+ cancelFiles(files: FileSpecification[], project: TestProject): void {
253253+ // if we don't filter existing modules, they will be overriden by `collectFiles`
254254+ const nonRegisteredFiles = files.filter(({ filepath }) => {
255255+ const relativePath = relative(project.config.root, filepath)
256256+ const id = generateFileHash(relativePath, project.name)
257257+ return !this.idMap.has(id)
258258+ })
259259+261260 this.collectFiles(
262261 project,
263263- files.map(filepath =>
264264- createFileTask(filepath, project.config.root, project.config.name),
262262+ nonRegisteredFiles.map(file =>
263263+ createFileTask(file.filepath, project.config.root, project.config.name),
265264 ),
266265 )
267266 }
+1
packages/vitest/src/node/test-run.ts
···135135 return modules.some(m => !m.ok())
136136 }
137137138138+ // make sure the error always has a "stacks" property
138139 private syncUpdateStacks(update: TaskResultPack[]): void {
139140 update.forEach(([taskId, result]) => {
140141 const task = this.vitest.state.idMap.get(taskId)
+13-1
packages/vitest/src/public/node.ts
···2424export type { ProcessPool } from '../node/pool'
2525export { getFilePoolName } from '../node/pool'
2626export { createMethodsRPC } from '../node/pools/rpc'
2727+export type {
2828+ PoolOptions,
2929+ PoolRunnerInitializer,
3030+ PoolTask,
3131+ PoolWorker,
3232+ WorkerRequest,
3333+ WorkerResponse,
3434+} from '../node/pools/types'
3535+export { ForksPoolWorker } from '../node/pools/workers/forksWorker'
3636+export { ThreadsPoolWorker } from '../node/pools/workers/threadsWorker'
3737+export { TypecheckPoolWorker } from '../node/pools/workers/typecheckWorker'
3838+export { VmForksPoolWorker } from '../node/pools/workers/vmForksWorker'
3939+export { VmThreadsPoolWorker } from '../node/pools/workers/vmThreadsWorker'
2740export type { SerializedTestProject, TestProject } from '../node/project'
2841export type { HTMLOptions } from '../node/reporters/html'
2942export type { JsonOptions } from '../node/reporters/json'
···90103 EnvironmentOptions,
91104 InlineConfig,
92105 Pool,
9393- PoolOptions,
94106 ProjectConfig,
95107 ResolvedConfig,
96108 ResolvedProjectConfig,
+2
packages/vitest/src/public/worker.ts
···11+export { runBaseTests } from '../runtime/workers/base'
22+export { init } from '../runtime/workers/init'
···102102exports[`should fail > typechecks empty "include" but with tests 1`] = `
103103"Testing types with tsc and vue-tsc is an experimental feature.
104104Breaking changes might not follow SemVer, please pin Vitest's version when using it.
105105-⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯
105105+⎯⎯ Unhandled Errors ⎯⎯
106106107107Vitest caught 1 unhandled error during the test run.
108108This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.
109109110110-⎯⎯⎯⎯⎯⎯ Typecheck Error ⎯⎯⎯⎯⎯⎯⎯
110110+⎯⎯ Typecheck Error ⎯⎯
111111Error: error TS18003: No inputs were found in config file '<root>/tsconfig.empty.json'. Specified 'include' paths were '["src"]' and 'exclude' paths were '["**/dist/**"]'.
112112113113-⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
113113+⎯⎯
114114115115"
116116`;
+6-5
packages/browser/src/client/tester/tester.ts
···11import type { BrowserRPC, IframeChannelEvent } from '@vitest/browser/client'
22+import type { FileSpecification } from '@vitest/runner'
23import { channel, client, onCancel } from '@vitest/browser/client'
34import { parse } from 'flatted'
45import { page, server, userEvent } from 'vitest/browser'
···155156 | Awaited<ReturnType<typeof prepareTestEnvironment>>
156157 | undefined
157158158158-async function executeTests(method: 'run' | 'collect', files: string[]) {
159159+async function executeTests(method: 'run' | 'collect', files: FileSpecification[]) {
159160 if (!preparedData) {
160161 throw new Error(`Data was not properly initialized. This is a bug in Vitest. Please, open a new issue with reproduction.`)
161162 }
···168169 runner.setMethod(method)
169170170171 const version = url.searchParams.get('browserv') || ''
171171- files.forEach((filename) => {
172172- const currentVersion = browserHashMap.get(filename)
172172+ files.forEach(({ filepath }) => {
173173+ const currentVersion = browserHashMap.get(filepath)
173174 if (!currentVersion || currentVersion[1] !== version) {
174174- browserHashMap.set(filename, version)
175175+ browserHashMap.set(filepath, version)
175176 }
176177 })
177178178179 debug?.('prepare time', state.durations.prepare, 'ms')
179180180181 for (const file of files) {
181181- state.filepath = file
182182+ state.filepath = file.filepath
182183183184 if (method === 'run') {
184185 await startTests([file], runner)
+8-79
packages/vitest/src/node/cli/cli-config.ts
···11import type { ApiConfig } from '../types/config'
22-import type {
33- ForksOptions,
44- ThreadsOptions,
55- VmOptions,
66- WorkerContextOptions,
77-} from '../types/pool-options'
82import type { CliOptions } from './cli-api'
93import { defaultBrowserPort, defaultPort } from '../../constants'
104import { ReportersMap } from '../reporters'
···5448 },
5549 middlewareMode: null,
5650})
5757-5858-const poolThreadsCommands: CLIOptions<ThreadsOptions & WorkerContextOptions> = {
5959- isolate: {
6060- description: 'Isolate tests in threads pool (default: `true`)',
6161- },
6262- singleThread: {
6363- description: 'Run tests inside a single thread (default: `false`)',
6464- },
6565- maxThreads: {
6666- description: 'Maximum number or percentage of threads to run tests in',
6767- argument: '<workers>',
6868- },
6969- useAtomics: {
7070- description:
7171- 'Use Atomics to synchronize threads. This can improve performance in some cases, but might cause segfault in older Node versions (default: `false`)',
7272- },
7373- execArgv: null,
7474-}
7575-7676-const poolForksCommands: CLIOptions<ForksOptions & WorkerContextOptions> = {
7777- isolate: {
7878- description: 'Isolate tests in forks pool (default: `true`)',
7979- },
8080- singleFork: {
8181- description: 'Run tests inside a single child_process (default: `false`)',
8282- },
8383- maxForks: {
8484- description: 'Maximum number or percentage of processes to run tests in',
8585- argument: '<workers>',
8686- },
8787- execArgv: null,
8888-}
89519052function watermarkTransform(value: unknown) {
9153 if (typeof value === 'string') {
···443405 argument: '<pool>',
444406 subcommands: null, // don't support custom objects
445407 },
446446- poolOptions: {
447447- description: 'Specify pool options',
448448- argument: '<options>',
449449- // we use casting here because TypeScript (for some reason) makes this into CLIOption<unknown>
450450- // even when using casting, these types fail if the new option is added which is good
451451- subcommands: {
452452- threads: {
453453- description: 'Specify threads pool options',
454454- argument: '<options>',
455455- subcommands: poolThreadsCommands,
456456- } as CLIOption<ThreadsOptions & WorkerContextOptions>,
457457- vmThreads: {
458458- description: 'Specify VM threads pool options',
459459- argument: '<options>',
460460- subcommands: {
461461- ...poolThreadsCommands,
462462- memoryLimit: {
463463- description:
464464- 'Memory limit for VM threads pool. If you see memory leaks, try to tinker this value.',
465465- argument: '<limit>',
466466- },
467467- },
468468- } as CLIOption<ThreadsOptions & VmOptions>,
469469- forks: {
470470- description: 'Specify forks pool options',
471471- argument: '<options>',
472472- subcommands: poolForksCommands,
473473- } as CLIOption<ForksOptions & WorkerContextOptions>,
474474- vmForks: {
475475- description: 'Specify VM forks pool options',
476476- argument: '<options>',
477477- subcommands: {
478478- ...poolForksCommands,
479479- memoryLimit: {
480480- description:
481481- 'Memory limit for VM forks pool. If you see memory leaks, try to tinker this value.',
482482- argument: '<limit>',
483483- },
484484- },
485485- } as CLIOption<ForksOptions & VmOptions>,
486486- },
408408+ execArgv: {
409409+ description: 'Pass additional arguments to `node` process when spawning `worker_threads` or `child_process`.',
410410+ argument: '<option>',
411411+ },
412412+ vmMemoryLimit: {
413413+ description:
414414+ 'Memory limit for VM pools. If you see memory leaks, try to tinker this value.',
415415+ argument: '<limit>',
487416 },
488417 fileParallelism: {
489418 description:
···1010import type { LabelColor, ParsedStack, ProvidedContext, TestError } from '../../types/general'
1111import type { HappyDOMOptions } from '../../types/happy-dom-options'
1212import type { JSDOMOptions } from '../../types/jsdom-options'
1313+import type { PoolRunnerInitializer } from '../pools/types'
1314import type {
1415 BuiltinReporterOptions,
1516 BuiltinReporters,
···2021import type { BenchmarkUserOptions } from './benchmark'
2122import type { BrowserConfigOptions, ResolvedBrowserOptions } from './browser'
2223import type { CoverageOptions, ResolvedCoverageOptions } from './coverage'
2323-import type { Pool, PoolOptions, ResolvedPoolOptions } from './pool-options'
2424import type { Reporter } from './reporter'
25252626export type { CoverageOptions, ResolvedCoverageOptions }
···3939export type VitestEnvironment
4040 = | BuiltinEnvironment
4141 | (string & Record<never, never>)
4242-export type { Pool, PoolOptions }
4342export type CSSModuleScopeStrategy = 'stable' | 'scoped' | 'non-scoped'
44434544export type ApiConfig = Pick<
···205204 context: ResolveSnapshotPathHandlerContext
206205) => string
207206207207+export type BuiltinPool
208208+ = | 'browser'
209209+ | 'threads'
210210+ | 'forks'
211211+ | 'vmThreads'
212212+ | 'vmForks'
213213+ | 'typescript'
214214+215215+export type Pool = BuiltinPool | (string & {})
216216+208217export interface InlineConfig {
209218 /**
210219 * Name of the project. Will be used to display in the reporter.
···298307 /**
299308 * Run tests in an isolated environment. This option has no effect on vmThreads pool.
300309 *
301301- * Disabling this option might improve performance if your code doesn't rely on side effects.
310310+ * Disabling this option improves performance if your code doesn't rely on side effects.
302311 *
303312 * @default true
304313 */
305314 isolate?: boolean
306315307316 /**
317317+ * Pass additional arguments to `node` process when spawning the worker.
318318+ *
319319+ * See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information.
320320+ *
321321+ * Set to `process.execArgv` to pass all arguments of the current process.
322322+ *
323323+ * Be careful when using, it as some options may crash worker, e.g. --prof, --title. See https://github.com/nodejs/node/issues/41103
324324+ *
325325+ * @default [] // no execution arguments are passed
326326+ */
327327+ execArgv?: string[]
328328+329329+ /**
330330+ * Specifies the memory limit for `worker_thread` or `child_process` before they are recycled.
331331+ * If you see memory leaks, try to tinker this value.
332332+ */
333333+ vmMemoryLimit?: string | number
334334+335335+ /**
308336 * Pool used to run tests in.
309337 *
310310- * Supports 'threads', 'forks', 'vmThreads'
338338+ * Supports 'threads', 'forks', 'vmThreads', 'vmForks'
311339 *
312340 * @default 'forks'
313341 */
314314- pool?: Exclude<Pool, 'browser'>
342342+ pool?: Exclude<Pool, 'browser'> | PoolRunnerInitializer
315343316344 /**
317317- * Pool options
318318- */
319319- poolOptions?: PoolOptions
320320-321321- /**
322322- * Maximum number or percentage of workers to run tests in. `poolOptions.{threads,vmThreads}.maxThreads`/`poolOptions.forks.maxForks` has higher priority.
345345+ * Maximum number or percentage of workers to run tests in.
323346 */
324347 maxWorkers?: number | string
325348···667690 * Debug tests by opening `node:inspector` in worker / child process.
668691 * Provides similar experience as `--inspect` Node CLI argument.
669692 *
670670- * Requires `poolOptions.threads.singleThread: true` OR `poolOptions.forks.singleFork: true`.
693693+ * Requires `fileParallelism: false`.
671694 */
672695 inspect?: boolean | string
673696···675698 * Debug tests by opening `node:inspector` in worker / child process and wait for debugger to connect.
676699 * Provides similar experience as `--inspect-brk` Node CLI argument.
677700 *
678678- * Requires `poolOptions.threads.singleThread: true` OR `poolOptions.forks.singleFork: true`.
701701+ * Requires `fileParallelism: false`.
679702 */
680703 inspectBrk?: boolean | string
681704···953976 | 'sequence'
954977 | 'typecheck'
955978 | 'runner'
956956- | 'poolOptions'
957979 | 'pool'
958980 | 'cliExclude'
959981 | 'diff'
···961983 | 'snapshotEnvironment'
962984 | 'bail'
963985 | 'name'
986986+ | 'vmMemoryLimit'
964987 > {
965988 mode: VitestRunMode
966989···98310069841007 browser: ResolvedBrowserOptions
9851008 pool: Pool
986986- poolOptions?: ResolvedPoolOptions
10091009+ poolRunner?: PoolRunnerInitializer
98710109881011 reporters: (InlineReporter | ReporterWithOptions)[]
9891012···1028105110291052 maxWorkers: number
1030105310541054+ vmMemoryLimit?: UserConfig['vmMemoryLimit']
10311055 dumpDir?: string
10321056}
10331057···10461070 | 'ui'
10471071 | 'open'
10481072 | 'uiBase'
10491049- // TODO: allow snapshot options
10731073+ // TODO: allow snapshot options
10501074 | 'snapshotFormat'
10511075 | 'resolveSnapshotPath'
10521076 | 'passWithNoTests'
···10571081 | 'inspect'
10581082 | 'inspectBrk'
10591083 | 'coverage'
10601060- | 'maxWorkers'
10611061- | 'fileParallelism'
10621084 | 'watchTriggerPatterns'
1063108510641086export interface ServerDepsOptions {
···10911113 NonProjectOptions
10921114 | 'sequencer'
10931115 | 'deps'
10941094- | 'poolOptions'
10951116> & {
10961117 mode?: string
10971118 sequencer?: Omit<SequenceOptions, 'sequencer' | 'seed'>
10981119 deps?: Omit<DepsOptions, 'moduleDirectories'>
10991099- poolOptions?: {
11001100- threads?: Pick<
11011101- NonNullable<PoolOptions['threads']>,
11021102- 'singleThread' | 'isolate'
11031103- >
11041104- vmThreads?: Pick<NonNullable<PoolOptions['vmThreads']>, 'singleThread'>
11051105- forks?: Pick<NonNullable<PoolOptions['forks']>, 'singleFork' | 'isolate'>
11061106- }
11201120+ fileParallelism?: boolean
11071121}
1108112211091123export type ResolvedProjectConfig = Omit<
-137
packages/vitest/src/node/types/pool-options.ts
···11-export type BuiltinPool
22- = | 'browser'
33- | 'threads'
44- | 'forks'
55- | 'vmThreads'
66- | 'vmForks'
77- | 'typescript'
88-export type Pool = BuiltinPool | (string & {})
99-1010-export interface PoolOptions extends Record<string, unknown> {
1111- /**
1212- * Run tests in `node:worker_threads`.
1313- *
1414- * Test isolation (when enabled) is done by spawning a new thread for each test file.
1515- *
1616- * This pool is used by default.
1717- */
1818- threads?: ThreadsOptions & WorkerContextOptions
1919-2020- /**
2121- * Run tests in `node:child_process` using [`fork()`](https://nodejs.org/api/child_process.html#child_processforkmodulepath-args-options)
2222- *
2323- * Test isolation (when enabled) is done by spawning a new child process for each test file.
2424- */
2525- forks?: ForksOptions & WorkerContextOptions
2626-2727- /**
2828- * Run tests in isolated `node:vm`.
2929- * Test files are run parallel using `node:worker_threads`.
3030- *
3131- * This makes tests run faster, but VM module is unstable. Your tests might leak memory.
3232- */
3333- vmThreads?: ThreadsOptions & VmOptions
3434-3535- /**
3636- * Run tests in isolated `node:vm`.
3737- *
3838- * Test files are run parallel using `node:child_process` [`fork()`](https://nodejs.org/api/child_process.html#child_processforkmodulepath-args-options)
3939- *
4040- * This makes tests run faster, but VM module is unstable. Your tests might leak memory.
4141- */
4242- vmForks?: ForksOptions & VmOptions
4343-}
4444-4545-export interface ResolvedPoolOptions extends PoolOptions {
4646- threads?: ResolvedThreadsOptions & WorkerContextOptions
4747- forks?: ResolvedForksOptions & WorkerContextOptions
4848- vmThreads?: ResolvedThreadsOptions & VmOptions
4949- vmForks?: ResolvedForksOptions & VmOptions
5050-}
5151-5252-export interface ThreadsOptions {
5353- /** Maximum amount of threads to use */
5454- maxThreads?: number | string
5555-5656- /**
5757- * Run tests inside a single thread.
5858- *
5959- * @default false
6060- */
6161- singleThread?: boolean
6262-6363- /**
6464- * Use Atomics to synchronize threads
6565- *
6666- * This can improve performance in some cases, but might cause segfault in older Node versions.
6767- *
6868- * @default false
6969- */
7070- useAtomics?: boolean
7171-}
7272-7373-export interface ResolvedThreadsOptions extends ThreadsOptions {
7474- maxThreads?: number
7575-}
7676-7777-export interface ForksOptions {
7878- /** Maximum amount of child processes to use */
7979- maxForks?: number | string
8080-8181- /**
8282- * Run tests inside a single fork.
8383- *
8484- * @default false
8585- */
8686- singleFork?: boolean
8787-}
8888-8989-export interface ResolvedForksOptions extends ForksOptions {
9090- maxForks?: number
9191-}
9292-9393-export interface WorkerContextOptions {
9494- /**
9595- * Isolate test environment by recycling `worker_threads` or `child_process` after each test
9696- *
9797- * @default true
9898- */
9999- isolate?: boolean
100100-101101- /**
102102- * Pass additional arguments to `node` process when spawning `worker_threads` or `child_process`.
103103- *
104104- * See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information.
105105- *
106106- * Set to `process.execArgv` to pass all arguments of the current process.
107107- *
108108- * Be careful when using, it as some options may crash worker, e.g. --prof, --title. See https://github.com/nodejs/node/issues/41103
109109- *
110110- * @default [] // no execution arguments are passed
111111- */
112112- execArgv?: string[]
113113-}
114114-115115-export interface VmOptions {
116116- /**
117117- * Specifies the memory limit for `worker_thread` or `child_process` before they are recycled.
118118- * If you see memory leaks, try to tinker this value.
119119- */
120120- memoryLimit?: string | number
121121-122122- /** Isolation is always enabled */
123123- isolate?: true
124124-125125- /**
126126- * Pass additional arguments to `node` process when spawning `worker_threads` or `child_process`.
127127- *
128128- * See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information.
129129- *
130130- * Set to `process.execArgv` to pass all arguments of the current process.
131131- *
132132- * Be careful when using, it as some options may crash worker, e.g. --prof, --title. See https://github.com/nodejs/node/issues/41103
133133- *
134134- * @default [] // no execution arguments are passed
135135- */
136136- execArgv?: string[]
137137-}
+1-4
packages/vitest/src/node/types/worker.ts
···11-import type { MessagePort } from 'node:worker_threads'
21import type { ContextRPC } from '../../types/worker'
3244-export interface WorkerContext extends ContextRPC {
55- port: MessagePort
66-}
33+export interface WorkerContext extends ContextRPC {}
+1
packages/vitest/src/runtime/workers/base.ts
···2323 return _moduleRunner
2424}
25252626+/** @experimental */
2627export async function runBaseTests(method: 'run' | 'collect', state: WorkerGlobalState): Promise<void> {
2728 const { ctx } = state
2829 // state has new context, but we want to reuse existing ones