[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

fix: fix per-project `sequence` config (#10659)

Co-authored-by: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com>
Co-authored-by: OpenCode (claude-opus-4-8) <noreply@opencode.ai>

authored by

Hiroshi Ogawa
Hiroshi Ogawa
OpenCode (claude-opus-4-8)
and committed by
GitHub
(Jun 25, 2026, 9:40 AM +0200) 40cdc7fd 1a20dd24

+100 -17
+3 -1
docs/config/sequence.md
··· 99 99 100 100 Vitest usually uses cache to sort tests, so long-running tests start earlier, which makes tests run faster. If your files and tests run in random order, you will lose this performance improvement, but it may be useful to track tests that accidentally depend on another test run previously. 101 101 102 - ### sequence.shuffle.files {#sequence-shuffle-files} 102 + ### sequence.shuffle.files <CRoot /> {#sequence-shuffle-files} 103 103 104 104 - **Type:** `boolean` 105 105 - **Default:** `false` 106 106 - **CLI:** `--sequence.shuffle.files`, `--sequence.shuffle.files=false` 107 107 108 108 Whether to randomize files, be aware that long running tests will not start earlier if you enable this option. 109 + 110 + Because file ordering is shared across [projects](/guide/projects), this option is resolved from the root config only. A project can still randomize its own tests with [`sequence.shuffle.tests`](#sequence-shuffle-tests). 109 111 110 112 ### sequence.shuffle.tests {#sequence-shuffle-tests} 111 113
+1 -4
packages/vitest/src/node/config/resolveConfig.ts
··· 790 790 } 791 791 resolved.sequence.groupOrder ??= 0 792 792 resolved.sequence.hooks ??= 'stack' 793 - // Set seed if either files or tests are shuffled 794 - if (resolved.sequence.sequencer === RandomSequencer || resolved.sequence.shuffle) { 795 - resolved.sequence.seed ??= Date.now() 796 - } 793 + resolved.sequence.seed ??= Date.now() 797 794 798 795 resolved.typecheck = { 799 796 ...configDefaults.typecheck,
+6 -4
packages/vitest/src/node/config/serializeConfig.ts
··· 87 87 ?? globalConfig.snapshotOptions.expand, 88 88 }, 89 89 sequence: { 90 - shuffle: globalConfig.sequence.shuffle, 91 - concurrent: globalConfig.sequence.concurrent, 90 + shuffle: config.sequence.shuffle, 91 + concurrent: config.sequence.concurrent, 92 + // `seed` and `sequencer` drive cross-project file ordering, so they are 93 + // resolved from the root config and shared across all projects. 92 94 seed: globalConfig.sequence.seed, 93 - hooks: globalConfig.sequence.hooks, 94 - setupFiles: globalConfig.sequence.setupFiles, 95 + hooks: config.sequence.hooks, 96 + setupFiles: config.sequence.setupFiles, 95 97 }, 96 98 inspect: globalConfig.inspect, 97 99 inspectBrk: globalConfig.inspectBrk,
+6 -1
packages/vitest/src/node/core.ts
··· 51 51 import { createReport } from './reporters/report' 52 52 import { createReporters } from './reporters/utils' 53 53 import { VitestResolver } from './resolver' 54 + import { RandomSequencer } from './sequencers/RandomSequencer' 54 55 import { VitestSpecifications } from './specifications' 55 56 import { StateManager } from './state' 56 57 import { populateProjectsTags } from './tags' ··· 680 681 * Returns the seed, if tests are running in a random order. 681 682 */ 682 683 public getSeed(): number | null { 683 - return this.config.sequence.seed ?? null 684 + // Tests can be shuffled per project, so check projects as well. 685 + const randomized = this.config.sequence.sequencer === RandomSequencer 686 + || !!this.config.sequence.shuffle 687 + || this.projects.some(p => !!p.config.sequence.shuffle) 688 + return randomized ? this.config.sequence.seed : null 684 689 } 685 690 686 691 /** @internal */
+3 -4
packages/vitest/src/node/logger.ts
··· 9 9 import { highlightCode } from '../utils/colors' 10 10 import { capturePrintError, printError } from './printError' 11 11 import { divider, errorBanner, formatProjectName, withLabel } from './reporters/renderers/utils' 12 - import { RandomSequencer } from './sequencers/RandomSequencer' 13 12 14 13 export interface ErrorOptions { 15 14 type?: string ··· 235 234 236 235 this.log(withLabel(color, mode, `v${this.ctx.version} `) + c.gray(this.ctx.config.root)) 237 236 238 - // Log seed if either files (RandomSequencer) or tests are shuffled 239 - if (this.ctx.config.sequence.sequencer === RandomSequencer || this.ctx.config.sequence.shuffle) { 240 - this.log(PAD + c.gray(`Running tests with seed "${this.ctx.config.sequence.seed}"`)) 237 + const seed = this.ctx.getSeed() 238 + if (seed != null) { 239 + this.log(PAD + c.gray(`Running tests with seed "${seed}"`)) 241 240 } 242 241 243 242 if (this.ctx.config.ui) {
+6 -2
packages/vitest/src/node/types/config.ts
··· 1296 1296 export type ProjectConfig = Omit< 1297 1297 InlineConfig, 1298 1298 NonProjectOptions 1299 - | 'sequencer' 1299 + | 'sequence' 1300 1300 | 'deps' 1301 1301 > & { 1302 1302 mode?: string 1303 - sequencer?: Omit<SequenceOptions, 'sequencer' | 'seed'> 1303 + sequence?: Omit<SequenceOptions, 'sequencer' | 'seed' | 'shuffle'> & { 1304 + // `shuffle.files` controls cross-project file ordering, which is resolved 1305 + // from the root config only, so projects can only shuffle their own tests. 1306 + shuffle?: boolean | { tests?: boolean } 1307 + } 1304 1308 deps?: Omit<DepsOptions, 'moduleDirectories'> 1305 1309 } 1306 1310
+19 -1
test/e2e/test/config/sequence-shuffle.test.ts
··· 148 148 expect(stdout).toContain('Running tests with seed "67890"') 149 149 }) 150 150 151 + test('should log seed when a project shuffles tests', async () => { 152 + const { stdout, ctx } = await runInlineTests({ 153 + 'a.test.js': /* js */ ` 154 + import { test } from 'vitest' 155 + test('example', () => {}) 156 + `, 157 + }, { 158 + projects: [ 159 + { test: { sequence: { shuffle: { tests: true } } } }, 160 + ], 161 + }) 162 + 163 + const seed = ctx?.getSeed() 164 + expect(seed).toEqual(expect.any(Number)) 165 + expect(stdout).toContain(`Running tests with seed "${seed}"`) 166 + }) 167 + 151 168 test('should not log seed when shuffle is disabled', async () => { 152 - const { stdout } = await runInlineTests({ 169 + const { stdout, ctx } = await runInlineTests({ 153 170 'basic.test.js': /* js */ ` 154 171 import { test } from 'vitest' 155 172 test('example', () => {}) ··· 160 177 }, 161 178 }) 162 179 180 + expect(ctx?.getSeed()).toBe(null) 163 181 expect(stdout).not.toContain('Running tests with seed') 164 182 })
+56
test/e2e/test/sequence-concurrent.test.ts
··· 65 65 } 66 66 `) 67 67 }) 68 + 69 + test('applies sequence.concurrent per project (not just the root config)', async () => { 70 + // The root config leaves `sequence.concurrent` at its default (false). Each 71 + // project sets it independently, so the fixtures only pass if the project's 72 + // value reaches the runtime. The fixtures self-assert `task.concurrent` and 73 + // their completion order via staggered delays, so this is deterministic. 74 + const { stderr, errorTree } = await runVitest({ 75 + root: './fixtures/sequence-concurrent', 76 + projects: [ 77 + { 78 + test: { 79 + name: 'concurrent', 80 + include: ['sequence-concurrent-true-concurrent.test.ts'], 81 + sequence: { 82 + concurrent: true, 83 + }, 84 + }, 85 + }, 86 + { 87 + test: { 88 + name: 'sequential', 89 + include: ['sequence-concurrent-false-sequential.test.ts'], 90 + sequence: { 91 + concurrent: false, 92 + }, 93 + }, 94 + }, 95 + ], 96 + }) 97 + 98 + expect(stderr).toBe('') 99 + expect(errorTree({ project: true })).toMatchInlineSnapshot(` 100 + { 101 + "concurrent": { 102 + "sequence-concurrent-true-concurrent.test.ts": { 103 + "concurrent suite": { 104 + "first test completes last": "passed", 105 + "second test completes third": "passed", 106 + }, 107 + "last test completes first": "passed", 108 + "third test completes second": "passed", 109 + }, 110 + }, 111 + "sequential": { 112 + "sequence-concurrent-false-sequential.test.ts": { 113 + "last test completes last": "passed", 114 + "sequential suite": { 115 + "first test completes first": "passed", 116 + "second test completes second": "passed", 117 + }, 118 + "third test completes third": "passed", 119 + }, 120 + }, 121 + } 122 + `) 123 + })