From 84350412eb77a28ef6a39ed1c08d5d9146aa94b8 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 13 Feb 2026 12:57:35 +0900 Subject: [PATCH 01/33] fix: `beforeAll/afterAll` should respect `maxConcurrency` --- packages/runner/src/run.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/runner/src/run.ts b/packages/runner/src/run.ts index 398008044aec..3be0a69b05c5 100644 --- a/packages/runner/src/run.ts +++ b/packages/runner/src/run.ts @@ -786,6 +786,8 @@ function markTasksAsSkipped(suite: Suite, runner: VitestRunner) { }) } +let limitMaxConcurrency: ReturnType + export async function runSuite(suite: Suite, runner: VitestRunner): Promise { await runner.onBeforeRunSuite?.(suite) @@ -829,13 +831,13 @@ export async function runSuite(suite: Suite, runner: VitestRunner): Promise callSuiteHook( + beforeAllCleanups = await $('suite.beforeAll', () => limitMaxConcurrency(() => callSuiteHook( suite, suite, 'beforeAll', runner, [suite], - )) + ))) } catch (e) { failTask(suite.result!, e, runner.config.diffOptions) @@ -875,9 +877,9 @@ export async function runSuite(suite: Suite, runner: VitestRunner): Promise callSuiteHook(suite, suite, 'afterAll', runner, [suite])) + await $('suite.afterAll', () => limitMaxConcurrency(() => callSuiteHook(suite, suite, 'afterAll', runner, [suite]))) if (beforeAllCleanups.length) { - await $('suite.cleanup', () => callCleanupHooks(runner, beforeAllCleanups)) + await $('suite.cleanup', () => limitMaxConcurrency(() => callCleanupHooks(runner, beforeAllCleanups))) } if (suite.file === suite) { const contexts = TestFixtures.getFileContexts(suite.file) @@ -924,8 +926,6 @@ export async function runSuite(suite: Suite, runner: VitestRunner): Promise - async function runSuiteChild(c: Task, runner: VitestRunner) { const $ = runner.trace! if (c.type === 'test') { From e202b06ee859fb10438879c91c33a3b88e1f2eed Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 13 Feb 2026 13:01:25 +0900 Subject: [PATCH 02/33] docs: update --- docs/config/maxconcurrency.md | 2 +- packages/vitest/src/node/cli/cli-config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/config/maxconcurrency.md b/docs/config/maxconcurrency.md index 6026efe77c05..6d59d633d0b0 100644 --- a/docs/config/maxconcurrency.md +++ b/docs/config/maxconcurrency.md @@ -9,6 +9,6 @@ outline: deep - **Default**: `5` - **CLI**: `--max-concurrency=10`, `--maxConcurrency=10` -A number of tests that are allowed to run at the same time marked with `test.concurrent`. +A number of tests and hooks that are allowed to run at the same time marked with `test.concurrent`. Test above this limit will be queued to run when available slot appears. diff --git a/packages/vitest/src/node/cli/cli-config.ts b/packages/vitest/src/node/cli/cli-config.ts index a1d982da003b..afe132644982 100644 --- a/packages/vitest/src/node/cli/cli-config.ts +++ b/packages/vitest/src/node/cli/cli-config.ts @@ -716,7 +716,7 @@ export const cliOptionsConfig: VitestCLIOptions = { }, }, maxConcurrency: { - description: 'Maximum number of concurrent tests in a suite (default: `5`)', + description: 'Maximum number of tests and hooks allowed to run concurrently (default: `5`)', argument: '', }, expect: { From 4a709e7f6195b5805c212cfd80ff91c405152478 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 13 Feb 2026 13:03:05 +0900 Subject: [PATCH 03/33] docs: more --- docs/config/maxconcurrency.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config/maxconcurrency.md b/docs/config/maxconcurrency.md index 6d59d633d0b0..11a467fde226 100644 --- a/docs/config/maxconcurrency.md +++ b/docs/config/maxconcurrency.md @@ -9,6 +9,6 @@ outline: deep - **Default**: `5` - **CLI**: `--max-concurrency=10`, `--maxConcurrency=10` -A number of tests and hooks that are allowed to run at the same time marked with `test.concurrent`. +A number of tests and hooks that are allowed to run at the same time marked with `test.concurrent` and `describe.concurrent`. Test above this limit will be queued to run when available slot appears. From bc91621a7f1728977c70a988aa480924385d832c Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 13 Feb 2026 13:07:21 +0900 Subject: [PATCH 04/33] docs: clarify maxConcurrency includes concurrent hooks --- docs/config/maxconcurrency.md | 4 ++-- docs/guide/cli-generated.md | 2 +- docs/guide/parallelism.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/config/maxconcurrency.md b/docs/config/maxconcurrency.md index 11a467fde226..08d584b6f052 100644 --- a/docs/config/maxconcurrency.md +++ b/docs/config/maxconcurrency.md @@ -9,6 +9,6 @@ outline: deep - **Default**: `5` - **CLI**: `--max-concurrency=10`, `--maxConcurrency=10` -A number of tests and hooks that are allowed to run at the same time marked with `test.concurrent` and `describe.concurrent`. +The maximum number of tests and hooks allowed to run at the same time when using `test.concurrent` and `describe.concurrent`. -Test above this limit will be queued to run when available slot appears. +Tasks above this limit will be queued until a slot becomes available. diff --git a/docs/guide/cli-generated.md b/docs/guide/cli-generated.md index b449a9ad481b..ff7c596d2685 100644 --- a/docs/guide/cli-generated.md +++ b/docs/guide/cli-generated.md @@ -767,7 +767,7 @@ Default timeout of a teardown function in milliseconds (default: `10000`) - **CLI:** `--maxConcurrency ` - **Config:** [maxConcurrency](/config/maxconcurrency) -Maximum number of concurrent tests in a suite (default: `5`) +Maximum number of tests and hooks allowed to run concurrently (default: `5`) ### expect.requireAssertions diff --git a/docs/guide/parallelism.md b/docs/guide/parallelism.md index c5772b20865d..b35032f67177 100644 --- a/docs/guide/parallelism.md +++ b/docs/guide/parallelism.md @@ -20,7 +20,7 @@ If you have a lot of tests, it is usually faster to run them in parallel, but it Unlike _test files_, Vitest runs _tests_ in sequence. This means that tests inside a single test file will run in the order they are defined. -Vitest supports the [`concurrent`](/api/test#test-concurrent) option to run tests together. If this option is set, Vitest will group concurrent tests in the same _file_ (the number of simultaneously running tests depends on the [`maxConcurrency`](/config/maxconcurrency) option) and run them with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). +Vitest supports the [`concurrent`](/api/test#test-concurrent) option to run tests together. If this option is set, Vitest will group concurrent tests in the same _file_ (the number of simultaneously running tasks, including hooks, depends on the [`maxConcurrency`](/config/maxconcurrency) option) and run them with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). Vitest doesn't perform any smart analysis and doesn't create additional workers to run these tests. This means that the performance of your tests will improve only if you rely heavily on asynchronous operations. For example, these tests will still run one after another even though the `concurrent` option is specified. This is because they are synchronous: From ab411616f2b27fe4b99ee233fafb1a0484da0a73 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 13 Feb 2026 13:30:51 +0900 Subject: [PATCH 05/33] docs: clarify maxConcurrency and sequence.hooks behavior --- docs/config/maxconcurrency.md | 6 ++++-- docs/guide/cli-generated.md | 2 +- docs/guide/parallelism.md | 2 +- packages/vitest/src/node/cli/cli-config.ts | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/config/maxconcurrency.md b/docs/config/maxconcurrency.md index 08d584b6f052..199a8fb3cead 100644 --- a/docs/config/maxconcurrency.md +++ b/docs/config/maxconcurrency.md @@ -9,6 +9,8 @@ outline: deep - **Default**: `5` - **CLI**: `--max-concurrency=10`, `--maxConcurrency=10` -The maximum number of tests and hooks allowed to run at the same time when using `test.concurrent` and `describe.concurrent`. +The maximum number of tests and suite lifecycle hooks allowed to run at the same time when using `test.concurrent` and `describe.concurrent`. -Tasks above this limit will be queued until a slot becomes available. +Entries above this limit are queued until a slot becomes available. + +Within each running test or suite, hooks at the same level can still run fully in parallel when [`sequence.hooks`](/config/sequence#sequence-hooks) is set to `parallel` (for example, `beforeEach` / `afterEach`, and `beforeAll` / `afterAll` registered in the same suite). diff --git a/docs/guide/cli-generated.md b/docs/guide/cli-generated.md index ff7c596d2685..055d28b506ef 100644 --- a/docs/guide/cli-generated.md +++ b/docs/guide/cli-generated.md @@ -767,7 +767,7 @@ Default timeout of a teardown function in milliseconds (default: `10000`) - **CLI:** `--maxConcurrency ` - **Config:** [maxConcurrency](/config/maxconcurrency) -Maximum number of tests and hooks allowed to run concurrently (default: `5`) +Maximum number of concurrent tests and suite lifecycle hooks in a file (default: `5`) ### expect.requireAssertions diff --git a/docs/guide/parallelism.md b/docs/guide/parallelism.md index b35032f67177..d3c9c7cc0e10 100644 --- a/docs/guide/parallelism.md +++ b/docs/guide/parallelism.md @@ -20,7 +20,7 @@ If you have a lot of tests, it is usually faster to run them in parallel, but it Unlike _test files_, Vitest runs _tests_ in sequence. This means that tests inside a single test file will run in the order they are defined. -Vitest supports the [`concurrent`](/api/test#test-concurrent) option to run tests together. If this option is set, Vitest will group concurrent tests in the same _file_ (the number of simultaneously running tasks, including hooks, depends on the [`maxConcurrency`](/config/maxconcurrency) option) and run them with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). +Vitest supports the [`concurrent`](/api/test#test-concurrent) option to run tests together. If this option is set, Vitest will group concurrent tests in the same _file_ (the number of simultaneously running tests and suite lifecycle hooks depends on the [`maxConcurrency`](/config/maxconcurrency) option) and run them with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). Vitest doesn't perform any smart analysis and doesn't create additional workers to run these tests. This means that the performance of your tests will improve only if you rely heavily on asynchronous operations. For example, these tests will still run one after another even though the `concurrent` option is specified. This is because they are synchronous: diff --git a/packages/vitest/src/node/cli/cli-config.ts b/packages/vitest/src/node/cli/cli-config.ts index afe132644982..081dd4a5bb68 100644 --- a/packages/vitest/src/node/cli/cli-config.ts +++ b/packages/vitest/src/node/cli/cli-config.ts @@ -716,7 +716,7 @@ export const cliOptionsConfig: VitestCLIOptions = { }, }, maxConcurrency: { - description: 'Maximum number of tests and hooks allowed to run concurrently (default: `5`)', + description: 'Maximum number of concurrent tests and suite lifecycle hooks in a file (default: `5`)', argument: '', }, expect: { From f60f0255cc5c448b203126c7bdd0b979d6d1e38a Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 13 Feb 2026 14:18:06 +0900 Subject: [PATCH 06/33] docs: simplify maxConcurrency wording and hook note --- docs/config/maxconcurrency.md | 6 +++--- docs/config/sequence.md | 2 +- docs/guide/parallelism.md | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/config/maxconcurrency.md b/docs/config/maxconcurrency.md index 199a8fb3cead..b397044eb7c7 100644 --- a/docs/config/maxconcurrency.md +++ b/docs/config/maxconcurrency.md @@ -9,8 +9,8 @@ outline: deep - **Default**: `5` - **CLI**: `--max-concurrency=10`, `--maxConcurrency=10` -The maximum number of tests and suite lifecycle hooks allowed to run at the same time when using `test.concurrent` and `describe.concurrent`. +The maximum number of tests and hooks that can run at the same time. -Entries above this limit are queued until a slot becomes available. +Anything above this limit is queued until a slot becomes available. -Within each running test or suite, hooks at the same level can still run fully in parallel when [`sequence.hooks`](/config/sequence#sequence-hooks) is set to `parallel` (for example, `beforeEach` / `afterEach`, and `beforeAll` / `afterAll` registered in the same suite). +[`sequence.hooks`](/config/sequence#sequence-hooks) controls hook ordering. Even when it is set to `parallel`, hook execution still respects this `maxConcurrency` limit. diff --git a/docs/config/sequence.md b/docs/config/sequence.md index f3b05eab398f..50b4e60d0aa6 100644 --- a/docs/config/sequence.md +++ b/docs/config/sequence.md @@ -145,7 +145,7 @@ Changes the order in which hooks are executed. - `stack` will order "after" hooks in reverse order, "before" hooks will run in the order they were defined - `list` will order all hooks in the order they are defined -- `parallel` will run hooks in a single group in parallel (hooks in parent suites will still run before the current suite's hooks) +- `parallel` runs hooks in a single group in parallel (hooks in parent suites still run before the current suite's hooks). The actual number of simultaneously running hooks still depends on [`maxConcurrency`](/config/maxconcurrency). ::: tip This option doesn't affect [`onTestFinished`](/api/hooks#ontestfinished). It is always called in reverse order. diff --git a/docs/guide/parallelism.md b/docs/guide/parallelism.md index d3c9c7cc0e10..4748b52c9625 100644 --- a/docs/guide/parallelism.md +++ b/docs/guide/parallelism.md @@ -22,6 +22,8 @@ Unlike _test files_, Vitest runs _tests_ in sequence. This means that tests insi Vitest supports the [`concurrent`](/api/test#test-concurrent) option to run tests together. If this option is set, Vitest will group concurrent tests in the same _file_ (the number of simultaneously running tests and suite lifecycle hooks depends on the [`maxConcurrency`](/config/maxconcurrency) option) and run them with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). +Hook order is controlled by [`sequence.hooks`](/config/sequence#sequence-hooks). Even with `sequence.hooks: 'parallel'`, hook execution still respects [`maxConcurrency`](/config/maxconcurrency). + Vitest doesn't perform any smart analysis and doesn't create additional workers to run these tests. This means that the performance of your tests will improve only if you rely heavily on asynchronous operations. For example, these tests will still run one after another even though the `concurrent` option is specified. This is because they are synchronous: ```ts From 87f6d32e6187f36e80907abdb11a41a06002ff3d Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 13 Feb 2026 14:23:46 +0900 Subject: [PATCH 07/33] wip --- docs/config/maxconcurrency.md | 4 +--- docs/config/sequence.md | 2 +- docs/guide/parallelism.md | 2 +- packages/runner/src/run.ts | 39 ++++++++++++++++++----------------- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/docs/config/maxconcurrency.md b/docs/config/maxconcurrency.md index b397044eb7c7..ba68446bbaac 100644 --- a/docs/config/maxconcurrency.md +++ b/docs/config/maxconcurrency.md @@ -11,6 +11,4 @@ outline: deep The maximum number of tests and hooks that can run at the same time. -Anything above this limit is queued until a slot becomes available. - -[`sequence.hooks`](/config/sequence#sequence-hooks) controls hook ordering. Even when it is set to `parallel`, hook execution still respects this `maxConcurrency` limit. +[`sequence.hooks`](/config/sequence#sequence-hooks) controls hook ordering. With `sequence.hooks: 'parallel'`, hook execution is bounded by this `maxConcurrency` limit. diff --git a/docs/config/sequence.md b/docs/config/sequence.md index 50b4e60d0aa6..9fc65fb77dbf 100644 --- a/docs/config/sequence.md +++ b/docs/config/sequence.md @@ -145,7 +145,7 @@ Changes the order in which hooks are executed. - `stack` will order "after" hooks in reverse order, "before" hooks will run in the order they were defined - `list` will order all hooks in the order they are defined -- `parallel` runs hooks in a single group in parallel (hooks in parent suites still run before the current suite's hooks). The actual number of simultaneously running hooks still depends on [`maxConcurrency`](/config/maxconcurrency). +- `parallel` runs hooks in a single group in parallel (hooks in parent suites still run before the current suite's hooks). The actual number of simultaneously running hooks is limited by [`maxConcurrency`](/config/maxconcurrency). ::: tip This option doesn't affect [`onTestFinished`](/api/hooks#ontestfinished). It is always called in reverse order. diff --git a/docs/guide/parallelism.md b/docs/guide/parallelism.md index 4748b52c9625..d5e6c01b517b 100644 --- a/docs/guide/parallelism.md +++ b/docs/guide/parallelism.md @@ -22,7 +22,7 @@ Unlike _test files_, Vitest runs _tests_ in sequence. This means that tests insi Vitest supports the [`concurrent`](/api/test#test-concurrent) option to run tests together. If this option is set, Vitest will group concurrent tests in the same _file_ (the number of simultaneously running tests and suite lifecycle hooks depends on the [`maxConcurrency`](/config/maxconcurrency) option) and run them with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). -Hook order is controlled by [`sequence.hooks`](/config/sequence#sequence-hooks). Even with `sequence.hooks: 'parallel'`, hook execution still respects [`maxConcurrency`](/config/maxconcurrency). +Hook order is controlled by [`sequence.hooks`](/config/sequence#sequence-hooks). With `sequence.hooks: 'parallel'`, hook execution is bounded by [`maxConcurrency`](/config/maxconcurrency). Vitest doesn't perform any smart analysis and doesn't create additional workers to run these tests. This means that the performance of your tests will improve only if you rely heavily on asynchronous operations. For example, these tests will still run one after another even though the `concurrent` option is specified. This is because they are synchronous: diff --git a/packages/runner/src/run.ts b/packages/runner/src/run.ts index 3be0a69b05c5..735537552f73 100644 --- a/packages/runner/src/run.ts +++ b/packages/runner/src/run.ts @@ -35,6 +35,7 @@ import { hasFailed, hasTests } from './utils/tasks' const now = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now const unixNow = Date.now const { clearTimeout, setTimeout } = getSafeTimers() +let limitMaxConcurrency: ReturnType /** * Normalizes retry configuration to extract individual values. @@ -141,7 +142,7 @@ async function callTestHooks( if (sequence === 'parallel') { try { - await Promise.all(hooks.map(fn => fn(test.context))) + await Promise.all(hooks.map(fn => limitMaxConcurrency(() => fn(test.context)))) } catch (e) { failTask(test.result!, e, runner.config.diffOptions) @@ -150,7 +151,7 @@ async function callTestHooks( else { for (const fn of hooks) { try { - await fn(test.context) + await limitMaxConcurrency(() => fn(test.context)) } catch (e) { failTask(test.result!, e, runner.config.diffOptions) @@ -188,11 +189,13 @@ export async function callSuiteHook( } async function runHook(hook: Function) { - return getBeforeHookCleanupCallback( - hook, - await hook(...args), - name === 'beforeEach' ? args[0] as TestContext : undefined, - ) + return limitMaxConcurrency(async () => { + return getBeforeHookCleanupCallback( + hook, + await hook(...args), + name === 'beforeEach' ? args[0] as TestContext : undefined, + ) + }) } if (sequence === 'parallel') { @@ -508,7 +511,7 @@ async function callCleanupHooks(runner: VitestRunner, cleanups: unknown[]) { if (typeof fn !== 'function') { return } - await fn() + await limitMaxConcurrency(() => fn()) }), ) } @@ -517,7 +520,7 @@ async function callCleanupHooks(runner: VitestRunner, cleanups: unknown[]) { if (typeof fn !== 'function') { continue } - await fn() + await limitMaxConcurrency(() => fn()) } } } @@ -607,7 +610,7 @@ export async function runTest(test: Test, runner: VitestRunner): Promise { )) if (runner.runTask) { - await $('test.callback', () => runner.runTask!(test)) + await $('test.callback', () => limitMaxConcurrency(() => runner.runTask!(test))) } else { const fn = getFn(test) @@ -616,7 +619,7 @@ export async function runTest(test: Test, runner: VitestRunner): Promise { 'Test function is not found. Did you add it using `setFn`?', ) } - await $('test.callback', () => fn()) + await $('test.callback', () => limitMaxConcurrency(() => fn())) } await runner.onAfterTryTask?.(test, { @@ -786,8 +789,6 @@ function markTasksAsSkipped(suite: Suite, runner: VitestRunner) { }) } -let limitMaxConcurrency: ReturnType - export async function runSuite(suite: Suite, runner: VitestRunner): Promise { await runner.onBeforeRunSuite?.(suite) @@ -831,13 +832,13 @@ export async function runSuite(suite: Suite, runner: VitestRunner): Promise limitMaxConcurrency(() => callSuiteHook( + beforeAllCleanups = await $('suite.beforeAll', () => callSuiteHook( suite, suite, 'beforeAll', runner, [suite], - ))) + )) } catch (e) { failTask(suite.result!, e, runner.config.diffOptions) @@ -877,9 +878,9 @@ export async function runSuite(suite: Suite, runner: VitestRunner): Promise limitMaxConcurrency(() => callSuiteHook(suite, suite, 'afterAll', runner, [suite]))) + await $('suite.afterAll', () => callSuiteHook(suite, suite, 'afterAll', runner, [suite])) if (beforeAllCleanups.length) { - await $('suite.cleanup', () => limitMaxConcurrency(() => callCleanupHooks(runner, beforeAllCleanups))) + await $('suite.cleanup', () => callCleanupHooks(runner, beforeAllCleanups)) } if (suite.file === suite) { const contexts = TestFixtures.getFileContexts(suite.file) @@ -929,7 +930,7 @@ export async function runSuite(suite: Suite, runner: VitestRunner): Promise $( + return $( 'run.test', { 'vitest.test.id': c.id, @@ -941,7 +942,7 @@ async function runSuiteChild(c: Task, runner: VitestRunner) { 'code.column.number': c.location?.column, }, () => runTest(c, runner), - )) + ) } else if (c.type === 'suite') { return $( From 5c329226b1f34fb64368af115577bcbb547b212c Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 13 Feb 2026 14:37:37 +0900 Subject: [PATCH 08/33] docs: tweak --- docs/config/maxconcurrency.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/config/maxconcurrency.md b/docs/config/maxconcurrency.md index ba68446bbaac..78b533f0d1ec 100644 --- a/docs/config/maxconcurrency.md +++ b/docs/config/maxconcurrency.md @@ -9,6 +9,6 @@ outline: deep - **Default**: `5` - **CLI**: `--max-concurrency=10`, `--maxConcurrency=10` -The maximum number of tests and hooks that can run at the same time. +The maximum number of tests and hooks that can run at the same time when using `test.concurrent` or `describe.concurrent`. -[`sequence.hooks`](/config/sequence#sequence-hooks) controls hook ordering. With `sequence.hooks: 'parallel'`, hook execution is bounded by this `maxConcurrency` limit. +The execution order of hooks within a single group is controlled by [`sequence.hooks`](/config/sequence#sequence-hooks). With `sequence.hooks: 'parallel'`, the parallelism is limited by `maxConcurrency`. From e5d6cdf17eedec03a83f2b5be6753b8b93b056c5 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 13 Feb 2026 14:42:37 +0900 Subject: [PATCH 09/33] docs: tweak --- docs/config/maxconcurrency.md | 2 +- docs/guide/cli-generated.md | 2 +- docs/guide/parallelism.md | 4 ++-- packages/vitest/src/node/cli/cli-config.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/config/maxconcurrency.md b/docs/config/maxconcurrency.md index 78b533f0d1ec..deb88476b2a0 100644 --- a/docs/config/maxconcurrency.md +++ b/docs/config/maxconcurrency.md @@ -11,4 +11,4 @@ outline: deep The maximum number of tests and hooks that can run at the same time when using `test.concurrent` or `describe.concurrent`. -The execution order of hooks within a single group is controlled by [`sequence.hooks`](/config/sequence#sequence-hooks). With `sequence.hooks: 'parallel'`, the parallelism is limited by `maxConcurrency`. +The hook execution order within a single group is also controlled by [`sequence.hooks`](/config/sequence#sequence-hooks). With `sequence.hooks: 'parallel'`, the execution is bounded by the same limit of [`maxConcurrency`](/config/maxconcurrency). diff --git a/docs/guide/cli-generated.md b/docs/guide/cli-generated.md index 055d28b506ef..cedaf6a0eb84 100644 --- a/docs/guide/cli-generated.md +++ b/docs/guide/cli-generated.md @@ -767,7 +767,7 @@ Default timeout of a teardown function in milliseconds (default: `10000`) - **CLI:** `--maxConcurrency ` - **Config:** [maxConcurrency](/config/maxconcurrency) -Maximum number of concurrent tests and suite lifecycle hooks in a file (default: `5`) +Maximum number of concurrent tests and suites during test file execution (default: `5`) ### expect.requireAssertions diff --git a/docs/guide/parallelism.md b/docs/guide/parallelism.md index d5e6c01b517b..c2033531e1ff 100644 --- a/docs/guide/parallelism.md +++ b/docs/guide/parallelism.md @@ -20,9 +20,9 @@ If you have a lot of tests, it is usually faster to run them in parallel, but it Unlike _test files_, Vitest runs _tests_ in sequence. This means that tests inside a single test file will run in the order they are defined. -Vitest supports the [`concurrent`](/api/test#test-concurrent) option to run tests together. If this option is set, Vitest will group concurrent tests in the same _file_ (the number of simultaneously running tests and suite lifecycle hooks depends on the [`maxConcurrency`](/config/maxconcurrency) option) and run them with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). +Vitest supports the [`concurrent`](/api/test#test-concurrent) option to run tests together. If this option is set, Vitest will group concurrent tests in the same _file_ (the number of simultaneously running tests depends on the [`maxConcurrency`](/config/maxconcurrency) option) and run them with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). -Hook order is controlled by [`sequence.hooks`](/config/sequence#sequence-hooks). With `sequence.hooks: 'parallel'`, hook execution is bounded by [`maxConcurrency`](/config/maxconcurrency). +The hook execution order within a single group is also controlled by [`sequence.hooks`](/config/sequence#sequence-hooks). With `sequence.hooks: 'parallel'`, the execution is bounded by the same limit of [`maxConcurrency`](/config/maxconcurrency). Vitest doesn't perform any smart analysis and doesn't create additional workers to run these tests. This means that the performance of your tests will improve only if you rely heavily on asynchronous operations. For example, these tests will still run one after another even though the `concurrent` option is specified. This is because they are synchronous: diff --git a/packages/vitest/src/node/cli/cli-config.ts b/packages/vitest/src/node/cli/cli-config.ts index 081dd4a5bb68..389fc5b6933b 100644 --- a/packages/vitest/src/node/cli/cli-config.ts +++ b/packages/vitest/src/node/cli/cli-config.ts @@ -716,7 +716,7 @@ export const cliOptionsConfig: VitestCLIOptions = { }, }, maxConcurrency: { - description: 'Maximum number of concurrent tests and suite lifecycle hooks in a file (default: `5`)', + description: 'Maximum number of concurrent tests and suites during test file execution (default: `5`)', argument: '', }, expect: { From 5b314fdfaae478031ab6056108fc49ad85cf02d6 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 13 Feb 2026 15:04:54 +0900 Subject: [PATCH 10/33] test: fix concurrent --- test/cli/test/expect-task.test.ts | 10 ++++++++-- test/test-utils/index.ts | 9 +++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/test/cli/test/expect-task.test.ts b/test/cli/test/expect-task.test.ts index d1dae4edb203..5fcb4851ab11 100644 --- a/test/cli/test/expect-task.test.ts +++ b/test/cli/test/expect-task.test.ts @@ -172,15 +172,18 @@ describe('serial', { concurrent: true }, () => { name: 'test-bound extend & local extend', test: testBoundLocalExtend, }, - ] as const)('works with $name', async ({ options, test }, { expect }) => { + ] as const)('works with $name', async ({ options, test }, context) => { const { stdout, stderr } = await runInlineTests( { 'basic.test.ts': test, 'to-match-test.ts': toMatchTest, }, { reporters: ['tap'], ...options }, + undefined, + context, ) + const { expect } = context expect(stderr).toBe('') expect(stdout.replace(/[\d.]+ms/g, '