| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2034,7 +2034,7 @@ export default defineConfig({ | |||
| 2034 | 2034 | ||
| 2035 | 2035 | ### sequence | |
| 2036 | 2036 | ||
| 2037 | - - **Type**: `{ sequencer?, shuffle?, seed?, hooks?, setupFiles? }` | ||
| 2037 | + - **Type**: `{ sequencer?, shuffle?, seed?, hooks?, setupFiles?, groupOrder }` | ||
| 2038 | 2038 | ||
| 2039 | 2039 | Options for how tests should be sorted. | |
| 2040 | 2040 | ||
@@ -2053,6 +2053,71 @@ A custom class that defines methods for sharding and sorting. You can extend `Ba | |||
| 2053 | 2053 | ||
| 2054 | 2054 | Sharding is happening before sorting, and only if `--shard` option is provided. | |
| 2055 | 2055 | ||
| 2056 | + If [`sequencer.groupOrder`](#groupOrder) is specified, the sequencer will be called once for each group and pool. | ||
| 2057 | + | ||
| 2058 | + #### groupOrder <Version>3.2.0</Version> {#groupOrder} | ||
| 2059 | + | ||
| 2060 | + - **Type:** `number` | ||
| 2061 | + - **Default:** `0` | ||
| 2062 | + | ||
| 2063 | + Controls the order in which this project runs its tests when using multiple [projects](/guide/projects). | ||
| 2064 | + | ||
| 2065 | + - Projects with the same group order number will run together, and groups are run from lowest to highest. | ||
| 2066 | + - If you don’t set this option, all projects run in parallel. | ||
| 2067 | + - If several projects use the same group order, they will run at the same time. | ||
| 2068 | + | ||
| 2069 | + This setting only affects the order in which projects run, not the order of tests within a project. | ||
| 2070 | + To control test isolation or the order of tests inside a project, use the [`isolate`](#isolate) and [`sequence.sequencer`](#sequence-sequencer) options. | ||
| 2071 | + | ||
| 2072 | + ::: details Example | ||
| 2073 | + Consider this example: | ||
| 2074 | + | ||
| 2075 | + ```ts | ||
| 2076 | + import { defineConfig } from 'vitest/config' | ||
| 2077 | + | ||
| 2078 | + export default defineConfig({ | ||
| 2079 | + test: { | ||
| 2080 | + projects: [ | ||
| 2081 | + { | ||
| 2082 | + test: { | ||
| 2083 | + name: 'slow', | ||
| 2084 | + sequence: { | ||
| 2085 | + groupOrder: 0, | ||
| 2086 | + }, | ||
| 2087 | + }, | ||
| 2088 | + }, | ||
| 2089 | + { | ||
| 2090 | + test: { | ||
| 2091 | + name: 'fast', | ||
| 2092 | + sequence: { | ||
| 2093 | + groupOrder: 0, | ||
| 2094 | + }, | ||
| 2095 | + }, | ||
| 2096 | + }, | ||
| 2097 | + { | ||
| 2098 | + test: { | ||
| 2099 | + name: 'flaky', | ||
| 2100 | + sequence: { | ||
| 2101 | + groupOrder: 1, | ||
| 2102 | + }, | ||
| 2103 | + }, | ||
| 2104 | + }, | ||
| 2105 | + ], | ||
| 2106 | + }, | ||
| 2107 | + }) | ||
| 2108 | + ``` | ||
| 2109 | + | ||
| 2110 | + Tests in these projects will run in this order: | ||
| 2111 | + | ||
| 2112 | + ``` | ||
| 2113 | + 0. slow | | ||
| 2114 | + |> running together | ||
| 2115 | + 0. fast | | ||
| 2116 | + | ||
| 2117 | + 1. flaky |> runs after slow and fast alone | ||
| 2118 | + ``` | ||
| 2119 | + ::: | ||
| 2120 | + | ||
| 2056 | 2121 | #### sequence.shuffle | |
| 2057 | 2122 | ||
| 2058 | 2123 | - **Type**: `boolean | { files?, tests? }` | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -89,9 +89,7 @@ Hide logs for skipped tests | |||
| 89 | 89 | - **CLI:** `--reporter <name>` | |
| 90 | 90 | - **Config:** [reporters](/config/#reporters) | |
| 91 | 91 | ||
| 92 | - Specify which reporter(s) to use. Available values: | ||
| 93 | - | ||
| 94 | - `default`, `basic`, `blob`, `verbose`, `dot`, `json`, `tap`, `tap-flat`, `junit`, `hanging-process`, `github-actions`. | ||
| 92 | + Specify reporters (default, basic, blob, verbose, dot, json, tap, tap-flat, junit, hanging-process, github-actions) | ||
| 95 | 93 | ||
| 96 | 94 | ### outputFile | |
| 97 | 95 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -549,6 +549,7 @@ export const cliOptionsConfig: VitestCLIOptions = { | |||
| 549 | 549 | 'Changes the order in which setup files are executed. Accepted values are: "list" and "parallel". If set to "list", will run setup files in the order they are defined. If set to "parallel", will run setup files in parallel (default: `"parallel"`)', | |
| 550 | 550 | argument: '<order>', | |
| 551 | 551 | }, | |
| 552 | + groupOrder: null, | ||
| 552 | 553 | }, | |
| 553 | 554 | }, | |
| 554 | 555 | inspect: { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -772,6 +772,7 @@ export function resolveConfig( | |||
| 772 | 772 | ? RandomSequencer | |
| 773 | 773 | : BaseSequencer | |
| 774 | 774 | } | |
| 775 | + resolved.sequence.groupOrder ??= 0 | ||
| 775 | 776 | resolved.sequence.hooks ??= 'stack' | |
| 776 | 777 | if (resolved.sequence.sequencer === RandomSequencer) { | |
| 777 | 778 | resolved.sequence.seed ??= Date.now() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -148,6 +148,7 @@ export function createPool(ctx: Vitest): ProcessPool { | |||
| 148 | 148 | } | |
| 149 | 149 | } | |
| 150 | 150 | ||
| 151 | + const poolConcurrentPromises = new Map<string, Promise<ProcessPool>>() | ||
| 151 | 152 | const customPools = new Map<string, ProcessPool>() | |
| 152 | 153 | async function resolveCustomPool(filepath: string) { | |
| 153 | 154 | if (customPools.has(filepath)) { | |
@@ -178,26 +179,44 @@ export function createPool(ctx: Vitest): ProcessPool { | |||
| 178 | 179 | return poolInstance as ProcessPool | |
| 179 | 180 | } | |
| 180 | 181 | ||
| 181 | - const filesByPool: Record<LocalPool, TestSpecification[]> = { | ||
| 182 | - forks: [], | ||
| 183 | - threads: [], | ||
| 184 | - vmThreads: [], | ||
| 185 | - vmForks: [], | ||
| 186 | - typescript: [], | ||
| 182 | + function getConcurrentPool(pool: string, fn: () => Promise<ProcessPool>) { | ||
| 183 | + if (poolConcurrentPromises.has(pool)) { | ||
| 184 | + return poolConcurrentPromises.get(pool)! | ||
| 185 | + } | ||
| 186 | + const promise = fn().finally(() => { | ||
| 187 | + poolConcurrentPromises.delete(pool) | ||
| 188 | + }) | ||
| 189 | + poolConcurrentPromises.set(pool, promise) | ||
| 190 | + return promise | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + function getCustomPool(pool: string) { | ||
| 194 | + return getConcurrentPool(pool, () => resolveCustomPool(pool)) | ||
| 187 | 195 | } | |
| 188 | 196 | ||
| 197 | + function getBrowserPool() { | ||
| 198 | + return getConcurrentPool('browser', async () => { | ||
| 199 | + const { createBrowserPool } = await import('@vitest/browser') | ||
| 200 | + return createBrowserPool(ctx) | ||
| 201 | + }) | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + const groupedSpecifications: Record<string, TestSpecification[]> = {} | ||
| 205 | + const groups = new Set<number>() | ||
| 206 | + | ||
| 189 | 207 | const factories: Record<LocalPool, () => ProcessPool> = { | |
| 190 | 208 | vmThreads: () => createVmThreadsPool(ctx, options), | |
| 209 | + vmForks: () => createVmForksPool(ctx, options), | ||
| 191 | 210 | threads: () => createThreadsPool(ctx, options), | |
| 192 | 211 | forks: () => createForksPool(ctx, options), | |
| 193 | - vmForks: () => createVmForksPool(ctx, options), | ||
| 194 | 212 | typescript: () => createTypecheckPool(ctx), | |
| 195 | 213 | } | |
| 196 | 214 | ||
| 197 | 215 | for (const spec of files) { | |
| 198 | - const { pool } = spec[2] | ||
| 199 | - filesByPool[pool] ??= [] | ||
| 200 | - filesByPool[pool].push(spec) | ||
| 216 | + const group = spec[0].config.sequence.groupOrder ?? 0 | ||
| 217 | + groups.add(group) | ||
| 218 | + groupedSpecifications[group] ??= [] | ||
| 219 | + groupedSpecifications[group].push(spec) | ||
| 201 | 220 | } | |
| 202 | 221 | ||
| 203 | 222 | const Sequencer = ctx.config.sequence.sequencer | |
@@ -210,35 +229,55 @@ export function createPool(ctx: Vitest): ProcessPool { | |||
| 210 | 229 | return sequencer.sort(specs) | |
| 211 | 230 | } | |
| 212 | 231 | ||
| 213 | - await Promise.all( | ||
| 214 | - Object.entries(filesByPool).map(async (entry) => { | ||
| 215 | - const [pool, files] = entry as [Pool, TestSpecification[]] | ||
| 216 | - | ||
| 217 | - if (!files.length) { | ||
| 218 | - return null | ||
| 219 | - } | ||
| 220 | - | ||
| 221 | - const specs = await sortSpecs(files) | ||
| 222 | - | ||
| 223 | - if (pool in factories) { | ||
| 224 | - const factory = factories[pool] | ||
| 225 | - pools[pool] ??= factory() | ||
| 226 | - return pools[pool] | ||
| 227 | - } | ||
| 228 | - | ||
| 229 | - if (pool === 'browser') { | ||
| 230 | - pools[pool] ??= await (async () => { | ||
| 231 | - const { createBrowserPool } = await import('@vitest/browser') | ||
| 232 | - return createBrowserPool(ctx) | ||
| 233 | - })() | ||
| 234 | - return pools[pool] | ||
| 235 | - } | ||
| 236 | - | ||
| 237 | - const poolHandler = await resolveCustomPool(pool) | ||
| 238 | - pools[poolHandler.name] ??= poolHandler | ||
| 239 | - return poolHandler[method](specs, invalidate) | ||
| 240 | - }), | ||
| 241 | - ) | ||
| 232 | + const sortedGroups = Array.from(groups).sort() | ||
| 233 | + for (const group of sortedGroups) { | ||
| 234 | + const specifications = groupedSpecifications[group] | ||
| 235 | + | ||
| 236 | + if (!specifications?.length) { | ||
| 237 | + continue | ||
| 238 | + } | ||
| 239 | + | ||
| 240 | + const filesByPool: Record<LocalPool, TestSpecification[]> = { | ||
| 241 | + forks: [], | ||
| 242 | + threads: [], | ||
| 243 | + vmThreads: [], | ||
| 244 | + vmForks: [], | ||
| 245 | + typescript: [], | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + specifications.forEach((specification) => { | ||
| 249 | + const pool = specification[2].pool | ||
| 250 | + filesByPool[pool] ??= [] | ||
| 251 | + filesByPool[pool].push(specification) | ||
| 252 | + }) | ||
| 253 | + | ||
| 254 | + await Promise.all( | ||
| 255 | + Object.entries(filesByPool).map(async (entry) => { | ||
| 256 | + const [pool, files] = entry as [Pool, TestSpecification[]] | ||
| 257 | + | ||
| 258 | + if (!files.length) { | ||
| 259 | + return null | ||
| 260 | + } | ||
| 261 | + | ||
| 262 | + const specs = await sortSpecs(files) | ||
| 263 | + | ||
| 264 | + if (pool in factories) { | ||
| 265 | + const factory = factories[pool] | ||
| 266 | + pools[pool] ??= factory() | ||
| 267 | + return pools[pool] | ||
| 268 | + } | ||
| 269 | + | ||
| 270 | + if (pool === 'browser') { | ||
| 271 | + pools.browser ??= await getBrowserPool() | ||
| 272 | + return pools.browser[method](specs, invalidate) | ||
| 273 | + } | ||
| 274 | + | ||
| 275 | + const poolHandler = await getCustomPool(pool) | ||
| 276 | + pools[poolHandler.name] ??= poolHandler | ||
| 277 | + return poolHandler[method](specs, invalidate) | ||
| 278 | + }), | ||
| 279 | + ) | ||
| 280 | + } | ||
| 242 | 281 | } | |
| 243 | 282 | ||
| 244 | 283 | return { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -64,6 +64,15 @@ interface SequenceOptions { | |||
| 64 | 64 | * @default BaseSequencer | |
| 65 | 65 | */ | |
| 66 | 66 | sequencer?: TestSequencerConstructor | |
| 67 | + /** | ||
| 68 | + * Controls the order in which this project runs its tests when using multiple [projects](/guide/projects). | ||
| 69 | + * | ||
| 70 | + * - Projects with the same group order number will run together, and groups are run from lowest to highest. | ||
| 71 | + * - If you don’t set this option, all projects run in parallel. | ||
| 72 | + * - If several projects use the same group order, they will run at the same time. | ||
| 73 | + * @default 0 | ||
| 74 | + */ | ||
| 75 | + groupOrder?: number | ||
| 67 | 76 | /** | |
| 68 | 77 | * Should files and tests run in random order. | |
| 69 | 78 | * @default false | |
@@ -1066,6 +1075,7 @@ export interface ResolvedConfig | |||
| 1066 | 1075 | shuffle?: boolean | |
| 1067 | 1076 | concurrent?: boolean | |
| 1068 | 1077 | seed: number | |
| 1078 | + groupOrder: number | ||
| 1069 | 1079 | } | |
| 1070 | 1080 | ||
| 1071 | 1081 | typecheck: Omit<TypecheckConfig, 'enabled'> & { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,53 @@ | |||
| 1 | + import { expect, test } from 'vitest' | ||
| 2 | + import { runInlineTests } from '../../test-utils' | ||
| 3 | + | ||
| 4 | + test('tests run according to the group order', async () => { | ||
| 5 | + const { stdout, stderr } = await runInlineTests({ | ||
| 6 | + 'example.1.test.ts': `test('1')`, | ||
| 7 | + 'example.2.test.ts': `test('2')`, | ||
| 8 | + 'example.2-2.test.ts': `test('2-2')`, | ||
| 9 | + 'example.3.test.ts': `test('3')`, | ||
| 10 | + }, { | ||
| 11 | + globals: true, | ||
| 12 | + // run projects in the opposite order! | ||
| 13 | + projects: [ | ||
| 14 | + { | ||
| 15 | + test: { | ||
| 16 | + name: '3', | ||
| 17 | + include: ['./example.3.test.ts'], | ||
| 18 | + sequence: { | ||
| 19 | + groupOrder: 1, | ||
| 20 | + }, | ||
| 21 | + }, | ||
| 22 | + }, | ||
| 23 | + { | ||
| 24 | + test: { | ||
| 25 | + include: ['./example.2.test.ts', './example.2-2.test.ts'], | ||
| 26 | + name: '2', | ||
| 27 | + sequence: { | ||
| 28 | + groupOrder: 2, | ||
| 29 | + }, | ||
| 30 | + }, | ||
| 31 | + }, | ||
| 32 | + { | ||
| 33 | + test: { | ||
| 34 | + name: '1', | ||
| 35 | + include: ['./example.1.test.ts'], | ||
| 36 | + sequence: { | ||
| 37 | + groupOrder: 3, | ||
| 38 | + }, | ||
| 39 | + }, | ||
| 40 | + }, | ||
| 41 | + ], | ||
| 42 | + }) | ||
| 43 | + expect(stderr).toBe('') | ||
| 44 | + | ||
| 45 | + const tests = stdout.split('\n').filter(c => c.startsWith(' ✓')).join('\n').replace(/\d+ms/g, '<time>') | ||
| 46 | + | ||
| 47 | + expect(tests).toMatchInlineSnapshot(` | ||
| 48 | + " ✓ |3| example.3.test.ts > 3 <time> | ||
| 49 | + ✓ |2| example.2-2.test.ts > 2-2 <time> | ||
| 50 | + ✓ |2| example.2.test.ts > 2 <time> | ||
| 51 | + ✓ |1| example.1.test.ts > 1 <time>" | ||
| 52 | + `) | ||
| 53 | + }) | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments