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 }) +})