diff --git a/docs/api/index.md b/docs/api/index.md index 8c964a9b6dbc..e5611ced246e 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -639,6 +639,24 @@ When running concurrent tests, Snapshots and Assertions must use `expect` from t You cannot use this syntax, when using Vitest as [type checker](/guide/testing-types). ::: +### describe.sequential + +- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void` + + `describe.sequential` in a suite marks every test as sequential. This is useful if you want to run tests in sequential within `describe.concurrent` or with the `--sequence.concurrent` command option. + + ```ts + describe.concurrent('suite', () => { + test('concurrent test 1', async () => { /* ... */ }) + test('concurrent test 2', async () => { /* ... */ }) + + describe.sequential('', () => { + test('sequential test 1', async () => { /* ... */ }) + test('sequential test 2', async () => { /* ... */ }) + }) + }) + ``` + ### describe.shuffle - **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void` diff --git a/packages/runner/src/suite.ts b/packages/runner/src/suite.ts index 55ad3df09603..bb3d2e80dbbe 100644 --- a/packages/runner/src/suite.ts +++ b/packages/runner/src/suite.ts @@ -53,7 +53,7 @@ export function createSuiteHooks() { } // implementations -function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, mode: RunMode, concurrent?: boolean, shuffle?: boolean, each?: boolean, suiteOptions?: TestOptions) { +function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, mode: RunMode, concurrent?: boolean, sequential?: boolean, shuffle?: boolean, each?: boolean, suiteOptions?: TestOptions) { const tasks: (Test | TaskCustom | Suite | SuiteCollector)[] = [] const factoryQueue: (Test | Suite | SuiteCollector)[] = [] @@ -84,7 +84,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m meta: Object.create(null), } as Omit as Test - if (this.concurrent || concurrent || runner.config.sequence.concurrent) + if (this.concurrent || (!sequential && (concurrent || runner.config.sequence.concurrent))) test.concurrent = true if (shuffle) test.shuffle = true @@ -198,7 +198,7 @@ function createSuite() { if (currentSuite?.options) options = { ...currentSuite.options, ...options } - return createSuiteCollector(formatName(name), factory, mode, this.concurrent, this.shuffle, this.each, options) + return createSuiteCollector(formatName(name), factory, mode, this.concurrent, this.sequence, this.shuffle, this.each, options) } suiteFn.each = function(this: { withContext: () => SuiteAPI; setContext: (key: string, value: boolean | undefined) => SuiteAPI }, cases: ReadonlyArray, ...args: any[]) { @@ -226,14 +226,14 @@ function createSuite() { suiteFn.runIf = (condition: any) => (condition ? suite : suite.skip) as SuiteAPI return createChainable( - ['concurrent', 'shuffle', 'skip', 'only', 'todo'], + ['concurrent', 'sequential', 'shuffle', 'skip', 'only', 'todo'], suiteFn, ) as unknown as SuiteAPI } function createTest(fn: ( ( - this: Record<'concurrent' | 'skip' | 'only' | 'todo' | 'fails' | 'each', boolean | undefined> & { fixtures?: FixtureItem[] }, + this: Record<'concurrent' | 'sequential' | 'skip' | 'only' | 'todo' | 'fails' | 'each', boolean | undefined> & { fixtures?: FixtureItem[] }, title: string, fn?: TestFunction, options?: number | TestOptions diff --git a/packages/runner/src/types/tasks.ts b/packages/runner/src/types/tasks.ts index 07c3c4e0da33..b18a1e997cbb 100644 --- a/packages/runner/src/types/tasks.ts +++ b/packages/runner/src/types/tasks.ts @@ -199,7 +199,7 @@ export type Fixtures, ExtraContext = {}> = { } type ChainableSuiteAPI = ChainableFunction< - 'concurrent' | 'only' | 'skip' | 'todo' | 'shuffle', + 'concurrent' | 'sequential' | 'only' | 'skip' | 'todo' | 'shuffle', [name: string | Function, factory?: SuiteFactory, options?: number | TestOptions], SuiteCollector, { diff --git a/test/core/test/sequential.test.ts b/test/core/test/sequential.test.ts new file mode 100644 index 000000000000..d134e9643680 --- /dev/null +++ b/test/core/test/sequential.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, test } from 'vitest' + +const delay = (timeout: number) => new Promise(resolve => setTimeout(resolve, timeout)) + +let count = 0 + +describe.concurrent('', () => { + describe.sequential('', () => { + test('should pass', async ({ task }) => { + await delay(50) + expect(task.concurrent).toBeFalsy() + expect(++count).toBe(1) + }) + + test('should pass', ({ task }) => { + expect(task.concurrent).toBeFalsy() + expect(++count).toBe(2) + }) + }) +})