Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,11 @@ describe.todo('unimplemented suite')

- **Alias:** `suite.each`

::: tip
While `describe.each` is provided for Jest compatibility,
Vitest also has [`describe.for`](#describe-for) which simplifies argument types and aligns with [`test.for`](#test-for).
:::

Use `describe.each` if you have more than one test that depends on the same data.

```ts
Expand Down Expand Up @@ -998,6 +1003,37 @@ describe.each`
You cannot use this syntax when using Vitest as [type checker](/guide/testing-types).
:::

### describe.for

- **Alias:** `suite.for`

The difference from `describe.each` is how array case is provided in the arguments.
Other non array case (including template string usage) works exactly same.

```ts
// `each` spreads array case
describe.each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
])('add(%i, %i) -> %i', (a, b, expected) => { // [!code --]
test('test', () => {
expect(a + b).toBe(expected)
})
})

// `for` doesn't spread array case
describe.for([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
])('add(%i, %i) -> %i', ([a, b, expected]) => { // [!code ++]
test('test', () => {
expect(a + b).toBe(expected)
})
})
```

## Setup and Teardown

These functions allow you to hook into the life cycle of tests to avoid repeating setup and teardown code. They apply to the current context: the file if they are used at the top-level or the current suite if they are inside a `describe` block. These hooks are not called, when you are running Vitest as a type checker.
Expand Down
25 changes: 25 additions & 0 deletions packages/runner/src/suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,31 @@ function createSuite() {
}
}

suiteFn.for = function <T>(
this: {
withContext: () => SuiteAPI
setContext: (key: string, value: boolean | undefined) => SuiteAPI
},
cases: ReadonlyArray<T>,
...args: any[]
) {
if (Array.isArray(cases) && args.length) {
cases = formatTemplateString(cases, args)
}

return (
name: string | Function,
optionsOrFn: ((...args: T[]) => void) | TestOptions,
fnOrOptions?: ((...args: T[]) => void) | number | TestOptions,
) => {
const name_ = formatName(name)
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions)
cases.forEach((item, idx) => {
suite(formatTitle(name_, toArray(item), idx), options, () => handler(item))
})
}
}

suiteFn.skipIf = (condition: any) =>
(condition ? suite.skip : suite) as SuiteAPI
suiteFn.runIf = (condition: any) =>
Expand Down
6 changes: 6 additions & 0 deletions packages/runner/src/types/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,11 @@ interface TestForFunction<ExtraContext> {
>
}

interface SuiteForFunction {
<T>(cases: ReadonlyArray<T>): EachFunctionReturn<[T]>
(...args: [TemplateStringsArray, ...any]): EachFunctionReturn<any[]>
}

interface TestCollectorCallable<C = object> {
/**
* @deprecated Use options as the second argument instead
Expand Down Expand Up @@ -525,6 +530,7 @@ type ChainableSuiteAPI<ExtraContext = object> = ChainableFunction<
SuiteCollectorCallable<ExtraContext>,
{
each: TestEachFunction
for: SuiteForFunction
}
>

Expand Down
45 changes: 45 additions & 0 deletions test/core/test/__snapshots__/test-for-suite.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`add(1, 1) > test 1`] = `2`;

exports[`add(1, 2) > test 1`] = `3`;

exports[`add(2, 1) > test 1`] = `3`;

exports[`basic case1 > test 1`] = `
{
"args": [
"case1",
],
}
`;

exports[`basic case2 > test 1`] = `
{
"args": [
"case2",
],
}
`;

exports[`template 'x' true > test 1`] = `
{
"args": [
{
"a": "x",
"b": true,
},
],
}
`;

exports[`template 'y' false > test 1`] = `
{
"args": [
{
"a": "y",
"b": false,
},
],
}
`;
35 changes: 35 additions & 0 deletions test/core/test/test-for-suite.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, expectTypeOf, test } from 'vitest'

describe.for(['case1', 'case2'])(
'basic %s',
(...args) => {
test('test', () => {
expectTypeOf(args).toEqualTypeOf<[string]>()
expect({ args }).matchSnapshot()
})
},
)

describe.for`
a | b
${'x'} | ${true}
${'y'} | ${false}
`(
'template $a $b',
(...args) => {
test('test', () => {
expectTypeOf(args).toEqualTypeOf<any[]>()
expect({ args }).toMatchSnapshot()
})
},
)

describe.for([
[1, 1],
[1, 2],
[2, 1],
])('add(%i, %i)', ([a, b]) => {
test('test', () => {
expect(a + b).matchSnapshot()
})
})
Loading