diff --git a/docs/api/index.md b/docs/api/index.md
index 7df39b39058e..6626041b4892 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -32,15 +32,42 @@ interface TestOptions {
}
```
+Vitest 1.3.0 deprecates the use of options as the last parameter. You will see a deprecation message until 2.0.0 when this syntax will be removed. If you need to pass down options, use `test` function's second argument:
+
+```ts
+import { test } from 'vitest'
+
+test('flaky test', () => {}, { retry: 3 }) // [!code --]
+test('flaky test', { retry: 3 }, () => {}) // [!code ++]
+```
+
When a test function returns a promise, the runner will wait until it is resolved to collect async expectations. If the promise is rejected, the test will fail.
::: tip
In Jest, `TestFunction` can also be of type `(done: DoneCallback) => void`. If this form is used, the test will not be concluded until `done` is called. You can achieve the same using an `async` function, see the [Migration guide Done Callback section](/guide/migration#done-callback).
:::
+Since Vitest 1.3.0 most options support both dot-syntax and object-syntax allowing you to use whatever style you prefer.
+
+:::code-group
+```ts [dot-syntax]
+import { test } from 'vitest'
+
+test.skip('skipped test', () => {
+ // some logic that fails right now
+})
+```
+```ts [object-syntax 1.3.0+]
+import { test } from 'vitest'
+
+test('skipped test', { skip: true }, () => {
+ // some logic that fails right now
+})
+```
+:::
+
## test
-- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number | TestOptions) => void`
- **Alias:** `it`
`test` defines a set of related expectations. It receives the test name and a function that holds the expectations to test.
@@ -57,7 +84,6 @@ test('should work as expected', () => {
### test.extend 0.32.3+
-- **Type:** `>(fixtures: Fixtures): TestAPI`
- **Alias:** `it.extend`
Use `test.extend` to extend the test context with custom fixtures. This will return a new `test` and it's also extendable, so you can compose more fixtures or override existing ones by extending it as you need. See [Extend Test Context](/guide/test-context.html#test-extend) for more information.
@@ -87,7 +113,6 @@ myTest('add item', ({ todos }) => {
### test.skip
-- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number | TestOptions) => void`
- **Alias:** `it.skip`
If you want to skip running certain tests, but you don't want to delete the code due to any reason, you can use `test.skip` to avoid running them.
@@ -115,7 +140,6 @@ test('skipped test', (context) => {
### test.skipIf
-- **Type:** `(condition: any) => Test`
- **Alias:** `it.skipIf`
In some cases you might run tests multiple times with different environments, and some of the tests might be environment-specific. Instead of wrapping the test code with `if`, you can use `test.skipIf` to skip the test whenever the condition is truthy.
@@ -136,7 +160,6 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
### test.runIf
-- **Type:** `(condition: any) => Test`
- **Alias:** `it.runIf`
Opposite of [test.skipIf](#test-skipif).
@@ -157,7 +180,6 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
### test.only
-- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number) => void`
- **Alias:** `it.only`
Use `test.only` to only run certain tests in a given suite. This is useful when debugging.
@@ -182,7 +204,6 @@ In order to do that run `vitest` with specific file containing the tests in ques
### test.concurrent
-- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number) => void`
- **Alias:** `it.concurrent`
`test.concurrent` marks consecutive tests to be run in parallel. It receives the test name, an async function with the tests to collect, and an optional timeout (in milliseconds).
@@ -224,7 +245,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
### test.sequential
-- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number) => void`
+- **Alias:** `it.sequential`
`test.sequential` marks a test as sequential. This is useful if you want to run tests in sequence within `describe.concurrent` or with the `--sequence.concurrent` command option.
@@ -248,7 +269,6 @@ describe.concurrent('suite', () => {
### test.todo
-- **Type:** `(name: string | Function) => void`
- **Alias:** `it.todo`
Use `test.todo` to stub tests to be implemented later. An entry will be shown in the report for the tests so you know how many tests you still need to implement.
@@ -260,7 +280,6 @@ test.todo('unimplemented test')
### test.fails
-- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number) => void`
- **Alias:** `it.fails`
Use `test.fails` to indicate that an assertion will fail explicitly.
@@ -282,7 +301,6 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
### test.each
-- **Type:** `(cases: ReadonlyArray, ...args: any[]) => void`
- **Alias:** `it.each`
Use `test.each` when you need to run the same test with different variables.
@@ -570,7 +588,7 @@ describe('numberToCurrency', () => {
### describe.skip
-- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void`
+- **Alias:** `suite.skip`
Use `describe.skip` in a suite to avoid running a particular describe block.
@@ -587,7 +605,7 @@ describe.skip('skipped suite', () => {
### describe.skipIf
-- **Type:** `(condition: any) => void`
+- **Alias:** `suite.skipIf`
In some cases, you might run suites multiple times with different environments, and some of the suites might be environment-specific. Instead of wrapping the suite with `if`, you can use `describe.skipIf` to skip the suite whenever the condition is truthy.
@@ -607,7 +625,7 @@ You cannot use this syntax when using Vitest as [type checker](/guide/testing-ty
### describe.runIf
-- **Type:** `(condition: any) => void`
+- **Alias:** `suite.runIf`
Opposite of [describe.skipIf](#describe-skipif).
@@ -653,7 +671,7 @@ In order to do that run `vitest` with specific file containing the tests in ques
### describe.concurrent
-- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void`
+- **Alias:** `suite.concurrent`
`describe.concurrent` in a suite marks every tests as concurrent
@@ -694,7 +712,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
### describe.sequential
-- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void`
+- **Alias:** `suite.sequential`
`describe.sequential` in a suite marks every test as sequential. This is useful if you want to run tests in sequence within `describe.concurrent` or with the `--sequence.concurrent` command option.
@@ -712,7 +730,7 @@ describe.concurrent('suite', () => {
### describe.shuffle
-- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void`
+- **Alias:** `suite.shuffle`
Vitest provides a way to run all tests in random order via CLI flag [`--sequence.shuffle`](/guide/cli) or config option [`sequence.shuffle`](/config/#sequence-shuffle), but if you want to have only part of your test suite to run tests in random order, you can mark it with this flag.
@@ -733,7 +751,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
### describe.todo
-- **Type:** `(name: string | Function) => void`
+- **Alias:** `suite.todo`
Use `describe.todo` to stub suites to be implemented later. An entry will be shown in the report for the tests so you know how many tests you still need to implement.
@@ -744,7 +762,7 @@ describe.todo('unimplemented suite')
### describe.each
-- **Type:** `(cases: ReadonlyArray, ...args: any[]): (name: string | Function, fn: (...args: T[]) => void, options?: number | TestOptions) => void`
+- **Alias:** `suite.each`
Use `describe.each` if you have more than one test that depends on the same data.
diff --git a/packages/runner/src/suite.ts b/packages/runner/src/suite.ts
index f4d197ddb121..e3ae628cbfce 100644
--- a/packages/runner/src/suite.ts
+++ b/packages/runner/src/suite.ts
@@ -1,4 +1,4 @@
-import { format, isObject, noop, objDisplay, objectAttr } from '@vitest/utils'
+import { format, isObject, objDisplay, objectAttr } from '@vitest/utils'
import type { Custom, CustomAPI, File, Fixtures, RunMode, Suite, SuiteAPI, SuiteCollector, SuiteFactory, SuiteHooks, Task, TaskCustomOptions, Test, TestAPI, TestFunction, TestOptions } from './types'
import type { VitestRunner } from './types/runner'
import { createChainable } from './utils/chain'
@@ -11,11 +11,11 @@ import { getCurrentTest } from './test-state'
// apis
export const suite = createSuite()
export const test = createTest(
- function (name: string | Function, fn?: TestFunction, options?: number | TestOptions) {
+ function (name: string | Function, optionsOrFn?: TestOptions | TestFunction, optionsOrTest?: number | TestOptions | TestFunction) {
if (getCurrentTest())
throw new Error('Calling the test function inside another test function is not allowed. Please put it inside "describe" or "suite" so it can be properly collected.')
- getCurrentSuite().test.fn.call(this, formatName(name), fn, options)
+ getCurrentSuite().test.fn.call(this, formatName(name), optionsOrFn as TestOptions, optionsOrTest as TestFunction)
},
)
@@ -56,8 +56,48 @@ export function createSuiteHooks() {
}
}
+function parseArguments any>(
+ optionsOrFn: T | object | undefined,
+ optionsOrTest: object | T | number | undefined,
+) {
+ let options: TestOptions = {}
+ let fn: T = (() => {}) as T
+
+ // it('', () => {}, { retry: 2 })
+ if (typeof optionsOrTest === 'object') {
+ // it('', { retry: 2 }, { retry: 3 })
+ if (typeof optionsOrFn === 'object')
+ throw new TypeError('Cannot use two objects as arguments. Please provide options and a function callback in that order.')
+ // TODO: more info, add a name
+ // console.warn('The third argument is deprecated. Please use the second argument for options.')
+ options = optionsOrTest
+ }
+ // it('', () => {}, 1000)
+ else if (typeof optionsOrTest === 'number') {
+ options = { timeout: optionsOrTest }
+ }
+ // it('', { retry: 2 }, () => {})
+ else if (typeof optionsOrFn === 'object') {
+ options = optionsOrFn
+ }
+
+ if (typeof optionsOrFn === 'function') {
+ if (typeof optionsOrTest === 'function')
+ throw new TypeError('Cannot use two functions as arguments. Please use the second argument for options.')
+ fn = optionsOrFn as T
+ }
+ else if (typeof optionsOrTest === 'function') {
+ fn = optionsOrTest as T
+ }
+
+ return {
+ options,
+ handler: fn,
+ }
+}
+
// implementations
-function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, mode: RunMode, concurrent?: boolean, sequential?: boolean, shuffle?: boolean, each?: boolean, suiteOptions?: TestOptions) {
+function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, mode: RunMode, shuffle?: boolean, each?: boolean, suiteOptions?: TestOptions) {
const tasks: (Test | Custom | Suite | SuiteCollector)[] = []
const factoryQueue: (Test | Suite | SuiteCollector)[] = []
@@ -104,9 +144,11 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m
return task
}
- const test = createTest(function (name: string | Function, fn = noop, options = {}) {
- if (typeof options === 'number')
- options = { timeout: options }
+ const test = createTest(function (name: string | Function, optionsOrFn?: TestOptions | TestFunction, optionsOrTest?: number | TestOptions | TestFunction) {
+ let { options, handler } = parseArguments(
+ optionsOrFn,
+ optionsOrTest,
+ )
// inherit repeats, retry, timeout from suite
if (typeof suiteOptions === 'object')
@@ -118,7 +160,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m
const test = task(
formatName(name),
- { ...this, ...options, handler: fn as any },
+ { ...this, ...options, handler },
) as unknown as Test
test.type = 'test'
@@ -193,12 +235,14 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m
}
function createSuite() {
- function suiteFn(this: Record, name: string | Function, factory?: SuiteFactory, options: number | TestOptions = {}) {
+ function suiteFn(this: Record, name: string | Function, factoryOrOptions?: SuiteFactory | TestOptions, optionsOrFactory: number | TestOptions | SuiteFactory = {}) {
const mode: RunMode = this.only ? 'only' : this.skip ? 'skip' : this.todo ? 'todo' : 'run'
const currentSuite = getCurrentSuite()
- if (typeof options === 'number')
- options = { timeout: options }
+ let { options, handler: factory } = parseArguments(
+ factoryOrOptions,
+ optionsOrFactory,
+ )
// inherit options from current suite
if (currentSuite?.options)
@@ -208,7 +252,7 @@ function createSuite() {
options.concurrent = this.concurrent || (!this.sequential && options?.concurrent)
options.sequential = this.sequential || (!this.concurrent && options?.sequential)
- return createSuiteCollector(formatName(name), factory, mode, this.concurrent, this.sequential, this.shuffle, this.each, options)
+ return createSuiteCollector(formatName(name), factory, mode, this.shuffle, this.each, options)
}
suiteFn.each = function(this: { withContext: () => SuiteAPI; setContext: (key: string, value: boolean | undefined) => SuiteAPI }, cases: ReadonlyArray, ...args: any[]) {
@@ -218,14 +262,20 @@ function createSuite() {
if (Array.isArray(cases) && args.length)
cases = formatTemplateString(cases, args)
- return (name: string | Function, fn: (...args: T[]) => void, options?: number | TestOptions) => {
+ return (name: string | Function, optionsOrFn: ((...args: T[]) => void) | TestOptions, fnOrOptions?: ((...args: T[]) => void) | number | TestOptions) => {
const _name = formatName(name)
const arrayOnlyCases = cases.every(Array.isArray)
+
+ const { options, handler } = parseArguments(
+ optionsOrFn,
+ fnOrOptions,
+ )
+
cases.forEach((i, idx) => {
const items = Array.isArray(i) ? i : [i]
arrayOnlyCases
- ? suite(formatTitle(_name, items, idx), () => fn(...items), options)
- : suite(formatTitle(_name, items, idx), () => fn(i), options)
+ ? suite(formatTitle(_name, items, idx), options, () => handler(...items))
+ : suite(formatTitle(_name, items, idx), options, () => handler(i))
})
this.setContext('each', undefined)
@@ -254,15 +304,21 @@ export function createTaskCollector(
if (Array.isArray(cases) && args.length)
cases = formatTemplateString(cases, args)
- return (name: string | Function, fn: (...args: T[]) => void, options?: number | TestOptions) => {
+ return (name: string | Function, optionsOrFn: ((...args: T[]) => void) | TestOptions, fnOrOptions?: ((...args: T[]) => void) | number | TestOptions) => {
const _name = formatName(name)
const arrayOnlyCases = cases.every(Array.isArray)
+
+ const { options, handler } = parseArguments(
+ optionsOrFn,
+ fnOrOptions,
+ )
+
cases.forEach((i, idx) => {
const items = Array.isArray(i) ? i : [i]
arrayOnlyCases
- ? test(formatTitle(_name, items, idx), () => fn(...items), options)
- : test(formatTitle(_name, items, idx), () => fn(i), options)
+ ? test(formatTitle(_name, items, idx), options, () => handler(...items))
+ : test(formatTitle(_name, items, idx), options, () => handler(i))
})
this.setContext('each', undefined)
@@ -279,8 +335,8 @@ export function createTaskCollector(
taskFn.extend = function (fixtures: Fixtures>) {
const _context = mergeContextFixtures(fixtures, context)
- return createTest(function fn(name: string | Function, fn?: TestFunction, options?: number | TestOptions) {
- getCurrentSuite().test.fn.call(this, formatName(name), fn, options)
+ return createTest(function fn(name: string | Function, optionsOrFn?: TestOptions | TestFunction, optionsOrTest?: number | TestOptions | TestFunction) {
+ getCurrentSuite().test.fn.call(this, formatName(name), optionsOrFn as TestOptions, optionsOrTest as TestFunction)
}, _context)
}
@@ -299,8 +355,8 @@ function createTest(fn: (
(
this: Record<'concurrent' | 'sequential' | 'skip' | 'only' | 'todo' | 'fails' | 'each', boolean | undefined> & { fixtures?: FixtureItem[] },
title: string,
- fn?: TestFunction,
- options?: number | TestOptions
+ optionsOrFn?: TestOptions | TestFunction,
+ optionsOrTest?: number | TestOptions | TestFunction,
) => void
), context?: Record) {
return createTaskCollector(fn, context) as TestAPI
diff --git a/packages/runner/src/types/tasks.ts b/packages/runner/src/types/tasks.ts
index 57895ee11055..bc6be12fbdd2 100644
--- a/packages/runner/src/types/tasks.ts
+++ b/packages/runner/src/types/tasks.ts
@@ -112,51 +112,48 @@ type ExtractEachCallbackArgs> = {
? 10
: 'fallback']
-interface SuiteEachFunction {
- (cases: ReadonlyArray): (
+interface EachFunctionReturn {
+ /**
+ * @deprecated Use options as the second argument instead
+ */
+ (
name: string | Function,
fn: (...args: T) => Awaitable,
- ) => void
- >(cases: ReadonlyArray): (
+ options: TestOptions,
+ ): void
+ (
name: string | Function,
- fn: (...args: ExtractEachCallbackArgs) => Awaitable,
- ) => void
- (cases: ReadonlyArray): (
+ fn: (...args: T) => Awaitable,
+ options?: number | TestOptions,
+ ): void
+ (
name: string | Function,
- fn: (...args: T[]) => Awaitable,
- ) => void
+ options: TestOptions,
+ fn: (...args: T) => Awaitable,
+ ): void
}
interface TestEachFunction {
- (cases: ReadonlyArray): (
- name: string | Function,
- fn: (...args: T) => Awaitable,
- options?: number | TestOptions,
- ) => void
- >(cases: ReadonlyArray): (
- name: string | Function,
- fn: (...args: ExtractEachCallbackArgs) => Awaitable,
- options?: number | TestOptions,
- ) => void
- (cases: ReadonlyArray): (
- name: string | Function,
- fn: (...args: T[]) => Awaitable,
- options?: number | TestOptions,
- ) => void
- (...args: [TemplateStringsArray, ...any]): (
- name: string | Function,
- fn: (...args: any[]) => Awaitable,
- options?: number | TestOptions,
- ) => void
+ (cases: ReadonlyArray): EachFunctionReturn
+ >(cases: ReadonlyArray): EachFunctionReturn>
+ (cases: ReadonlyArray): EachFunctionReturn
+ (...args: [TemplateStringsArray, ...any]): EachFunctionReturn
+}
+
+interface TestCollectorCallable {
+ /**
+ * @deprecated Use options as the second argument instead
+ */
+ (name: string | Function, fn: TestFunction, options: TestOptions): void
+ (name: string | Function, fn?: TestFunction, options?: number | TestOptions): void
+ (name: string | Function, options?: TestOptions, fn?: TestFunction): void
}
type ChainableTestAPI = ChainableFunction<
'concurrent' | 'sequential' | 'only' | 'skip' | 'todo' | 'fails',
- [name: string | Function, fn?: TestFunction, options?: number | TestOptions],
- void,
+ TestCollectorCallable,
{
each: TestEachFunction
- (name: string | Function, fn?: TestFunction, options?: number | TestOptions): void
}
>
@@ -189,10 +186,25 @@ export interface TestOptions {
* Tests inherit `sequential` from `describe()` and nested `describe()` will inherit from parent's `sequential`.
*/
sequential?: boolean
+ /**
+ * Whether the test should be skipped.
+ */
+ skip?: boolean
+ /**
+ * Should this test be the only one running in a suite.
+ */
+ only?: boolean
+ /**
+ * Whether the test should be skipped and marked as a todo.
+ */
+ todo?: boolean
+ /**
+ * Whether the test is expected to fail. If it does, the test will pass, otherwise it will fail.
+ */
+ fails?: boolean
}
interface ExtendedAPI {
- each: TestEachFunction
skipIf: (condition: any) => ChainableTestAPI
runIf: (condition: any) => ChainableTestAPI
}
@@ -224,18 +236,24 @@ export type Fixtures, ExtraContext = {}> = {
export type InferFixturesTypes = T extends TestAPI ? C : T
+interface SuiteCollectorCallable {
+ /**
+ * @deprecated Use options as the second argument instead
+ */
+ (name: string | Function, fn: SuiteFactory, options: TestOptions): SuiteCollector
+ (name: string | Function, fn?: SuiteFactory, options?: number | TestOptions): SuiteCollector
+ (name: string | Function, options: TestOptions, fn?: SuiteFactory): SuiteCollector
+}
+
type ChainableSuiteAPI = ChainableFunction<
'concurrent' | 'sequential' | 'only' | 'skip' | 'todo' | 'shuffle',
- [name: string | Function, factory?: SuiteFactory, options?: number | TestOptions],
- SuiteCollector,
+ SuiteCollectorCallable,
{
each: TestEachFunction
- (name: string | Function, factory?: SuiteFactory): SuiteCollector
}
>
export type SuiteAPI = ChainableSuiteAPI & {
- each: SuiteEachFunction
skipIf: (condition: any) => ChainableSuiteAPI
runIf: (condition: any) => ChainableSuiteAPI
}
diff --git a/packages/runner/src/utils/chain.ts b/packages/runner/src/utils/chain.ts
index ef1c3c6cc029..79da93447ec7 100644
--- a/packages/runner/src/utils/chain.ts
+++ b/packages/runner/src/utils/chain.ts
@@ -1,15 +1,13 @@
-export type ChainableFunction = {
- (...args: Args): R
+export type ChainableFunction any, C = {}> = F & {
+ [x in T]: ChainableFunction
} & {
- [x in T]: ChainableFunction
-} & {
- fn: (this: Record, ...args: Args) => R
-} & E
+ fn: (this: Record, ...args: Parameters) => ReturnType
+} & C
-export function createChainable(
+export function createChainable(
keys: T[],
fn: (this: Record, ...args: Args) => R,
-): ChainableFunction {
+): ChainableFunction R> {
function create(context: Record) {
const chain = function (this: any, ...args: Args) {
return fn.apply(context, args)
diff --git a/packages/vitest/src/types/benchmark.ts b/packages/vitest/src/types/benchmark.ts
index ad9c6f2c8a1c..df4fc1d79876 100644
--- a/packages/vitest/src/types/benchmark.ts
+++ b/packages/vitest/src/types/benchmark.ts
@@ -54,8 +54,7 @@ export interface BenchmarkResult extends TinybenchResult {
export type BenchFunction = (this: BenchFactory) => Promise | void
type ChainableBenchmarkAPI = ChainableFunction<
'skip' | 'only' | 'todo',
- [name: string | Function, fn?: BenchFunction, options?: BenchOptions],
- void
+ (name: string | Function, fn?: BenchFunction, options?: BenchOptions) => void
>
export type BenchmarkAPI = ChainableBenchmarkAPI & {
skipIf: (condition: any) => ChainableBenchmarkAPI
diff --git a/test/core/test/propagate-options-nested-suite.test.ts b/test/core/test/propagate-options-nested-suite.test.ts
index d7b11fd44403..9225b5bae367 100644
--- a/test/core/test/propagate-options-nested-suite.test.ts
+++ b/test/core/test/propagate-options-nested-suite.test.ts
@@ -1,23 +1,19 @@
import { describe, expect, it } from 'vitest'
-describe(
- 'suite name',
- () => {
- let outerCount = 0
+describe('suite name', { retry: 9 }, () => {
+ let outerCount = 0
- it('should retry until success', () => {
- outerCount++
- expect(outerCount).toBe(5)
- })
+ it('should retry until success', () => {
+ outerCount++
+ expect(outerCount).toBe(5)
+ })
- describe('nested', () => {
- let innerCount = 0
+ describe('nested', () => {
+ let innerCount = 0
- it('should retry until success (nested)', () => {
- innerCount++
- expect(innerCount).toBe(5)
- })
+ it('should retry until success (nested)', () => {
+ innerCount++
+ expect(innerCount).toBe(5)
})
- },
- { retry: 9 },
-)
+ })
+})
diff --git a/test/core/test/repeats.test.ts b/test/core/test/repeats.test.ts
index ad12bfc0914b..52ca1612a905 100644
--- a/test/core/test/repeats.test.ts
+++ b/test/core/test/repeats.test.ts
@@ -6,18 +6,18 @@ const testNumbers: number[] = []
describe('testing it/test', () => {
const result = [1, 1, 1, 1, 1, 2, 2, 2]
- test('test 1', () => {
+ test('test 1', { repeats: 4 }, () => {
testNumbers.push(1)
- }, { repeats: 4 })
+ })
- test('test 2', () => {
+ test('test 2', { repeats: 2 }, () => {
testNumbers.push(2)
- }, { repeats: 2 })
+ })
- test.fails('test 3', () => {
+ test.fails('test 3', { repeats: 0 }, () => {
testNumbers.push(3)
expect(testNumbers).toStrictEqual(result)
- }, { repeats: 0 })
+ })
afterAll(() => {
result.push(3)
@@ -27,11 +27,11 @@ describe('testing it/test', () => {
const describeNumbers: number[] = []
-describe('testing describe', () => {
+describe('testing describe', { repeats: 2 }, () => {
test('test 1', () => {
describeNumbers.push(1)
})
-}, { repeats: 2 })
+})
afterAll(() => {
expect(describeNumbers).toStrictEqual([1, 1, 1])
@@ -42,24 +42,24 @@ const retryNumbers: number[] = []
describe('testing repeats with retry', () => {
describe('normal test', () => {
const result = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
- test.fails('test 1', () => {
+ test.fails('test 1', { repeats: 4, retry: 1 }, () => {
retryNumbers.push(1)
expect(1).toBe(2)
- }, { repeats: 4, retry: 1 })
+ })
afterAll(() => {
expect(retryNumbers).toStrictEqual(result)
})
})
- test('should not reset retry count', () => {
+ test('should not reset retry count', { repeats: 2, retry: 1 }, () => {
expect(getCurrentTest()!.result?.retryCount).toBe(3)
- }, { repeats: 2, retry: 1 })
+ })
})
const nestedDescribeNumbers: number[] = []
-describe('testing nested describe', () => {
+describe('testing nested describe', { repeats: 1 }, () => {
test ('test 1', () => {
nestedDescribeNumbers.push(1)
})
@@ -69,7 +69,7 @@ describe('testing nested describe', () => {
nestedDescribeNumbers.push(2)
})
- describe('nested 2', () => {
+ describe('nested 2', { repeats: 2 }, () => {
test('test 3', () => {
nestedDescribeNumbers.push(3)
})
@@ -79,10 +79,10 @@ describe('testing nested describe', () => {
nestedDescribeNumbers.push(4)
})
}, 100)
- }, { repeats: 2 })
+ })
})
afterAll(() => {
expect(nestedDescribeNumbers).toStrictEqual([1, 1, 2, 2, 3, 3, 3, 4, 4, 4])
})
-}, { repeats: 1 })
+})
diff --git a/test/core/test/retry-only.test.ts b/test/core/test/retry-only.test.ts
index 8a8a2b0df54c..823b4b7fb907 100644
--- a/test/core/test/retry-only.test.ts
+++ b/test/core/test/retry-only.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest'
-describe.only('description.only retry', () => {
+describe.only('description.only retry', { retry: 1 }, () => {
let count4 = 0
let count5 = 0
it('test should inherit options from the description block if missing', () => {
@@ -8,8 +8,8 @@ describe.only('description.only retry', () => {
expect(count4).toBe(2)
})
- it('test should not inherit options from the description block if exists', () => {
+ it('test should not inherit options from the description block if exists', { retry: 4 }, () => {
count5 += 1
expect(count5).toBe(5)
- }, { retry: 4 })
-}, { retry: 1 })
+ })
+})
diff --git a/test/core/test/retry.test.ts b/test/core/test/retry.test.ts
index 9a40879c4d24..df7af6045565 100644
--- a/test/core/test/retry.test.ts
+++ b/test/core/test/retry.test.ts
@@ -1,22 +1,22 @@
import { describe, expect, it } from 'vitest'
let count1 = 0
-it('retry test', () => {
+it('retry test', { retry: 2 }, () => {
count1 += 1
expect(count1).toBe(3)
-}, { retry: 2 })
+})
let count2 = 0
-it.fails('retry test fails', () => {
+it.fails('retry test fails', { retry: 1 }, () => {
count2 += 1
expect(count2).toBe(3)
-}, { retry: 1 })
+})
let count3 = 0
-it('retry test fails', () => {
+it('retry test fails', { retry: 10 }, () => {
count3 += 1
expect(count3).toBe(3)
-}, { retry: 10 })
+})
it('result', () => {
expect(count1).toEqual(3)
@@ -24,7 +24,7 @@ it('result', () => {
expect(count3).toEqual(3)
})
-describe('description retry', () => {
+describe('description retry', { retry: 2 }, () => {
let count4 = 0
let count5 = 0
it('test should inherit options from the description block if missing', () => {
@@ -32,17 +32,17 @@ describe('description retry', () => {
expect(count4).toBe(2)
})
- it('test should not inherit options from the description block if exists', () => {
+ it('test should not inherit options from the description block if exists', { retry: 5 }, () => {
count5 += 1
expect(count5).toBe(5)
- }, { retry: 5 })
-}, { retry: 2 })
+ })
+})
describe.each([
{ a: 1, b: 1, expected: 2 },
{ a: 1, b: 2, expected: 3 },
{ a: 2, b: 1, expected: 3 },
-])('describe object add($a, $b)', ({ a, b, expected }) => {
+])('describe object add($a, $b)', { retry: 2 }, ({ a, b, expected }) => {
let flag1 = false
let flag2 = false
let flag3 = false
@@ -63,4 +63,4 @@ describe.each([
expect(a + b).not.toBeLessThan(expected)
expect(flag3).toBe(false)
})
-}, { retry: 2 })
+})
diff --git a/test/core/test/test-options.test.ts b/test/core/test/test-options.test.ts
new file mode 100644
index 000000000000..f2557b33f08a
--- /dev/null
+++ b/test/core/test/test-options.test.ts
@@ -0,0 +1,37 @@
+import { describe, expect, test } from 'vitest'
+
+const fail = () => expect.fail('expected to be skipped')
+
+describe('all test variations are allowed', () => {
+ test('skipped by default')
+
+ test.skip('skipped explicitly', fail)
+ test.skip('skipped explicitly', fail, 1000)
+ test('skipped explicitly via options', { skip: true }, fail)
+ test('skipped explicitly via options as the last argument', fail, { skip: true })
+
+ test.todo('todo explicitly', fail)
+ test.todo('todo explicitly', fail, 1000)
+ test('todo explicitly via options', { todo: true }, fail)
+ test('todo explicitly via options as the last argument', fail, { todo: true })
+
+ test.fails('fails by default', fail)
+ test.fails('fails by default', fail, 1000)
+ test('fails explicitly via options', { fails: true }, fail)
+ test('fails explicitly via options as the last argument', fail, { fails: true })
+})
+
+describe('only is allowed explicitly', () => {
+ test('not only by default', fail)
+ test.only('only explicitly', () => {})
+})
+
+describe('only is allowed via options', () => {
+ test('not only by default', fail)
+ test('only via options', { only: true }, () => {})
+})
+
+describe('only is allowed via option as the last argument', () => {
+ test('not only by default', fail)
+ test('only via options as the last argument', () => {}, { only: true })
+})