Skip to content

Commit aa1dac3

Browse files
authoredDec 5, 2024··
feat(runner)!: support describe(..., { shuffle: boolean }) and inherit from parent suite (#6670)
1 parent e3144fd commit aa1dac3

File tree

9 files changed

+119
-25
lines changed

9 files changed

+119
-25
lines changed
 

‎docs/api/index.md

+13
Original file line numberDiff line numberDiff line change
@@ -906,10 +906,23 @@ Vitest provides a way to run all tests in random order via CLI flag [`--sequence
906906
```ts
907907
import { describe, test } from 'vitest'
908908

909+
// or describe('suite', { shuffle: true }, ...)
909910
describe.shuffle('suite', () => {
910911
test('random test 1', async () => { /* ... */ })
911912
test('random test 2', async () => { /* ... */ })
912913
test('random test 3', async () => { /* ... */ })
914+
915+
// `shuffle` is inherited
916+
describe('still random', () => {
917+
test('random 4.1', async () => { /* ... */ })
918+
test('random 4.2', async () => { /* ... */ })
919+
})
920+
921+
// disable shuffle inside
922+
describe('not random', { shuffle: false }, () => {
923+
test('in order 5.1', async () => { /* ... */ })
924+
test('in order 5.2', async () => { /* ... */ })
925+
})
913926
})
914927
// order depends on sequence.seed option in config (Date.now() by default)
915928
```

‎packages/runner/src/collect.ts

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export async function collectTests(
3232
const testLocations = typeof spec === 'string' ? undefined : spec.testLocations
3333

3434
const file = createFileTask(filepath, config.root, config.name, runner.pool)
35+
file.shuffle = config.sequence.shuffle
3536

3637
runner.onCollectStart?.(file)
3738

‎packages/runner/src/run.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ export async function runSuite(suite: Suite, runner: VitestRunner): Promise<void
412412
}
413413
else {
414414
const { sequence } = runner.config
415-
if (sequence.shuffle || suite.shuffle) {
415+
if (suite.shuffle) {
416416
// run describe block independently from tests
417417
const suites = tasksGroup.filter(
418418
group => group.type === 'suite',

‎packages/runner/src/suite.ts

+7-10
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,7 @@ export function getRunner(): VitestRunner {
207207

208208
function createDefaultSuite(runner: VitestRunner) {
209209
const config = runner.config.sequence
210-
const api = config.shuffle ? suite.shuffle : suite
211-
return api('', { concurrent: config.concurrent }, () => {})
210+
return suite('', { concurrent: config.concurrent }, () => {})
212211
}
213212

214213
export function clearCollectorContext(
@@ -292,7 +291,6 @@ function createSuiteCollector(
292291
name: string,
293292
factory: SuiteFactory = () => {},
294293
mode: RunMode,
295-
shuffle?: boolean,
296294
each?: boolean,
297295
suiteOptions?: TestOptions,
298296
) {
@@ -331,9 +329,7 @@ function createSuiteCollector(
331329
) {
332330
task.concurrent = true
333331
}
334-
if (shuffle) {
335-
task.shuffle = true
336-
}
332+
task.shuffle = suiteOptions?.shuffle
337333

338334
const context = createTestContext(task, runner)
339335
// create test context
@@ -425,7 +421,7 @@ function createSuiteCollector(
425421
mode,
426422
each,
427423
file: undefined!,
428-
shuffle,
424+
shuffle: suiteOptions?.shuffle,
429425
tasks: [],
430426
meta: Object.create(null),
431427
concurrent: suiteOptions?.concurrent,
@@ -523,8 +519,10 @@ function createSuite() {
523519
const isSequentialSpecified = options.sequential || this.sequential || options.concurrent === false
524520

525521
// inherit options from current suite
526-
if (currentSuite?.options) {
527-
options = { ...currentSuite.options, ...options }
522+
options = {
523+
...currentSuite?.options,
524+
...options,
525+
shuffle: this.shuffle ?? options.shuffle ?? currentSuite?.options?.shuffle ?? runner?.config.sequence.shuffle,
528526
}
529527

530528
// inherit concurrent / sequential from suite
@@ -537,7 +535,6 @@ function createSuite() {
537535
formatName(name),
538536
factory,
539537
mode,
540-
this.shuffle,
541538
this.each,
542539
options,
543540
)

‎packages/runner/src/types/tasks.ts

+13-7
Original file line numberDiff line numberDiff line change
@@ -286,16 +286,16 @@ interface EachFunctionReturn<T extends any[]> {
286286
(
287287
name: string | Function,
288288
fn: (...args: T) => Awaitable<void>,
289-
options: TestOptions
289+
options: TestCollectorOptions
290290
): void
291291
(
292292
name: string | Function,
293293
fn: (...args: T) => Awaitable<void>,
294-
options?: number | TestOptions
294+
options?: number | TestCollectorOptions
295295
): void
296296
(
297297
name: string | Function,
298-
options: TestOptions,
298+
options: TestCollectorOptions,
299299
fn: (...args: T) => Awaitable<void>
300300
): void
301301
}
@@ -316,7 +316,7 @@ interface TestForFunctionReturn<Arg, Context> {
316316
): void
317317
(
318318
name: string | Function,
319-
options: TestOptions,
319+
options: TestCollectorOptions,
320320
fn: (args: Arg, context: Context) => Awaitable<void>
321321
): void
322322
}
@@ -347,16 +347,16 @@ interface TestCollectorCallable<C = object> {
347347
<ExtraContext extends C>(
348348
name: string | Function,
349349
fn: TestFunction<ExtraContext>,
350-
options: TestOptions
350+
options: TestCollectorOptions
351351
): void
352352
<ExtraContext extends C>(
353353
name: string | Function,
354354
fn?: TestFunction<ExtraContext>,
355-
options?: number | TestOptions
355+
options?: number | TestCollectorOptions
356356
): void
357357
<ExtraContext extends C>(
358358
name: string | Function,
359-
options?: TestOptions,
359+
options?: TestCollectorOptions,
360360
fn?: TestFunction<ExtraContext>
361361
): void
362362
}
@@ -370,6 +370,8 @@ type ChainableTestAPI<ExtraContext = object> = ChainableFunction<
370370
}
371371
>
372372

373+
type TestCollectorOptions = Omit<TestOptions, 'shuffle'>
374+
373375
export interface TestOptions {
374376
/**
375377
* Test timeout.
@@ -399,6 +401,10 @@ export interface TestOptions {
399401
* Tests inherit `sequential` from `describe()` and nested `describe()` will inherit from parent's `sequential`.
400402
*/
401403
sequential?: boolean
404+
/**
405+
* Whether the tasks of the suite run in a random order.
406+
*/
407+
shuffle?: boolean
402408
/**
403409
* Whether the test should be skipped.
404410
*/
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { afterAll, describe, expect, test } from 'vitest'
2+
3+
const numbers: number[] = []
4+
5+
test.for([1, 2, 3, 4, 5])('test %s', (v) => {
6+
numbers.push(10 + v)
7+
})
8+
9+
describe("inherit shuffle", () => {
10+
test.for([1, 2, 3, 4, 5])('test %s', (v) => {
11+
numbers.push(20 + v)
12+
})
13+
})
14+
15+
describe('unshuffle', { shuffle: false }, () => {
16+
test.for([1, 2, 3, 4, 5])('test %s', (v) => {
17+
numbers.push(30 + v)
18+
})
19+
})
20+
21+
afterAll(() => {
22+
expect(numbers).toEqual([
23+
11, 14, 13, 15, 12,
24+
31, 32, 33, 34, 35,
25+
21, 24, 23, 25, 22
26+
])
27+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defineConfig } from 'vitest/config'
2+
3+
export default defineConfig({
4+
test: {
5+
sequence: {
6+
seed: 101,
7+
shuffle: true,
8+
}
9+
}
10+
})

‎test/config/test/shuffle-options.test.ts

+15
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,18 @@ test.each([
4747
const { ctx } = await run({ shuffle, sequencer: CustomSequencer })
4848
expect(ctx?.config.sequence.sequencer.name).toBe('CustomSequencer')
4949
})
50+
51+
test('shuffle', async () => {
52+
const { stderr, ctx } = await runVitest({
53+
root: './fixtures/shuffle',
54+
})
55+
expect(stderr).toBe('')
56+
expect(ctx?.state.getFiles().map(f => [f.name, f.result?.state])).toMatchInlineSnapshot(`
57+
[
58+
[
59+
"basic.test.ts",
60+
"pass",
61+
],
62+
]
63+
`)
64+
})

‎test/core/test/random.test.ts

+32-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
1-
import { afterAll, describe, expect, test } from 'vitest'
1+
import { describe, expect, test } from 'vitest'
22

33
// tests use seed of 101, so they have deterministic random order
44
const numbers: number[] = []
55

66
describe.shuffle('random tests', () => {
7-
describe('inside', () => {
8-
// shuffle is not inherited from parent
9-
7+
describe('suite unshuffle', { shuffle: false }, () => {
108
test('inside 1', () => {
119
numbers.push(1)
1210
})
11+
test('inside 1.5', () => {
12+
numbers.push(1.5)
13+
})
1314
test('inside 2', () => {
1415
numbers.push(2)
1516
})
17+
18+
describe('suite shuffle', { shuffle: true }, () => {
19+
test('inside 2.1', () => {
20+
numbers.push(2.1)
21+
})
22+
test('inside 2.2', () => {
23+
numbers.push(2.2)
24+
})
25+
test('inside 2.3', () => {
26+
numbers.push(2.3)
27+
})
28+
})
1629
})
1730

1831
test('test 1', () => {
@@ -24,8 +37,20 @@ describe.shuffle('random tests', () => {
2437
test('test 3', () => {
2538
numbers.push(5)
2639
})
40+
})
2741

28-
afterAll(() => {
29-
expect(numbers).toStrictEqual([4, 5, 3, 1, 2])
30-
})
42+
test('assert', () => {
43+
expect(numbers).toMatchInlineSnapshot(`
44+
[
45+
4,
46+
5,
47+
3,
48+
1,
49+
1.5,
50+
2,
51+
2.2,
52+
2.3,
53+
2.1,
54+
]
55+
`)
3156
})

0 commit comments

Comments
 (0)
Please sign in to comment.