Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow using todo/skip/concurrent with each #1802

Merged
merged 1 commit into from Aug 5, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/api/index.md
Expand Up @@ -198,6 +198,8 @@ In Jest, `TestFunction` can also be of type `(done: DoneCallback) => void`. If t
// ✓ add(2, 1) -> 3
```

If you want to have access to `TestContext`, use `describe.each` with a single test.

## describe

When you use `test` in the top level of file, they are collected as part of the implicit suite for it. Using `describe` you can define a new suite in the current context, as a set of related tests and other nested suites. A suite lets you organize your tests so reports are more clear.
Expand Down
18 changes: 10 additions & 8 deletions packages/vitest/src/runtime/chain.ts
@@ -1,21 +1,23 @@
export type ChainableFunction<T extends string, Args extends any[], R = any> = {
export type ChainableFunction<T extends string, Args extends any[], R = any, E = {}> = {
(...args: Args): R
} & {
[x in T]: ChainableFunction<T, Args, R>
}
[x in T]: ChainableFunction<T, Args, R, E>
} & E

export function createChainable<T extends string, Args extends any[], R = any>(
export function createChainable<T extends string, Args extends any[], R = any, E = {}>(
keys: T[],
fn: (this: Record<T, boolean | undefined>, ...args: Args) => R,
): ChainableFunction<T, Args, R> {
function create(obj: Record<T, boolean | undefined>) {
): ChainableFunction<T, Args, R, E> {
function create(context: Record<T, boolean | undefined>) {
const chain = function (this: any, ...args: Args) {
return fn.apply(obj, args)
return fn.apply(context, args)
}
Object.assign(chain, fn)
chain.withContext = () => chain.bind(context)
for (const key of keys) {
Object.defineProperty(chain, key, {
get() {
return create({ ...obj, [key]: true })
return create({ ...context, [key]: true })
},
})
}
Expand Down
41 changes: 22 additions & 19 deletions packages/vitest/src/runtime/suite.ts
Expand Up @@ -163,15 +163,13 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m
}

function createSuite() {
const suite = createChainable(
['concurrent', 'shuffle', 'skip', 'only', 'todo'],
function (name: string, factory?: SuiteFactory) {
const mode: RunMode = this.only ? 'only' : this.skip ? 'skip' : this.todo ? 'todo' : 'run'
return createSuiteCollector(name, factory, mode, this.concurrent, this.shuffle)
},
) as SuiteAPI
function suiteFn(this: Record<string, boolean | undefined>, name: string, factory?: SuiteFactory) {
const mode: RunMode = this.only ? 'only' : this.skip ? 'skip' : this.todo ? 'todo' : 'run'
return createSuiteCollector(name, factory, mode, this.concurrent, this.shuffle)
}

suite.each = <T>(cases: ReadonlyArray<T>) => {
suiteFn.each = function<T>(this: { withContext: () => SuiteAPI }, cases: ReadonlyArray<T>) {
const suite = this.withContext()
return (name: string, fn: (...args: T[]) => void) => {
cases.forEach((i, idx) => {
const items = Array.isArray(i) ? i : [i]
Expand All @@ -180,10 +178,13 @@ function createSuite() {
}
}

suite.skipIf = (condition: any) => (condition ? suite.skip : suite) as SuiteAPI
suite.runIf = (condition: any) => (condition ? suite : suite.skip) as SuiteAPI
suiteFn.skipIf = (condition: any) => (condition ? suite.skip : suite) as SuiteAPI
suiteFn.runIf = (condition: any) => (condition ? suite : suite.skip) as SuiteAPI

return suite
return createChainable(
['concurrent', 'shuffle', 'skip', 'only', 'todo'],
suiteFn,
) as unknown as SuiteAPI
}

function createTest(fn: (
Expand All @@ -194,12 +195,11 @@ function createTest(fn: (
timeout?: number
) => void
)) {
const test = createChainable(
['concurrent', 'skip', 'only', 'todo', 'fails'],
fn,
) as TestAPI
const testFn = fn as any

testFn.each = function<T>(this: { withContext: () => TestAPI }, cases: ReadonlyArray<T>) {
const test = this.withContext()

test.each = <T>(cases: ReadonlyArray<T>) => {
return (name: string, fn: (...args: T[]) => void, timeout?: number) => {
cases.forEach((i, idx) => {
const items = Array.isArray(i) ? i : [i]
Expand All @@ -208,8 +208,11 @@ function createTest(fn: (
}
}

test.skipIf = (condition: any) => (condition ? test.skip : test) as TestAPI
test.runIf = (condition: any) => (condition ? test : test.skip) as TestAPI
testFn.skipIf = (condition: any) => (condition ? test.skip : test) as TestAPI
testFn.runIf = (condition: any) => (condition ? test : test.skip) as TestAPI

return test as TestAPI
return createChainable(
['concurrent', 'skip', 'only', 'todo', 'fails'],
testFn,
) as TestAPI
}
6 changes: 4 additions & 2 deletions packages/vitest/src/types/tasks.ts
Expand Up @@ -125,7 +125,8 @@ interface TestEachFunction {
type ChainableTestAPI<ExtraContext = {}> = ChainableFunction<
'concurrent' | 'only' | 'skip' | 'todo' | 'fails',
[name: string, fn?: TestFunction<ExtraContext>, timeout?: number],
void
void,
{ each: TestEachFunction }
>

export type TestAPI<ExtraContext = {}> = ChainableTestAPI<ExtraContext> & {
Expand All @@ -137,7 +138,8 @@ export type TestAPI<ExtraContext = {}> = ChainableTestAPI<ExtraContext> & {
type ChainableSuiteAPI<ExtraContext = {}> = ChainableFunction<
'concurrent' | 'only' | 'skip' | 'todo' | 'shuffle',
[name: string, factory?: SuiteFactory],
SuiteCollector<ExtraContext>
SuiteCollector<ExtraContext>,
{ each: TestEachFunction }
>

export type SuiteAPI<ExtraContext = {}> = ChainableSuiteAPI & {
Expand Down
34 changes: 33 additions & 1 deletion test/core/test/each.test.ts
@@ -1,4 +1,4 @@
import { describe, expect, test } from 'vitest'
import { afterAll, describe, expect, test } from 'vitest'

test.each([
[1, 1, 2],
Expand Down Expand Up @@ -104,3 +104,35 @@ test.each([
const result = await promiseResolver(a, b)
expect(result).toBe(expected)
})

describe('context on test and describe - todo/skip', () => {
let count = 0

describe.todo.each([1])('todo describe', () => {
test('this is todo test', () => {
count++
})
})

describe.skip.each([1])('todo describe', () => {
test('this is todo test', () => {
count++
})
})

test.skip.each([1])('todo test', () => {
count++
})

afterAll(() => {
expect(count).toBe(0)
})
})

describe('context with each - concurrent', () => {
describe.concurrent.each([[1, 1, 2], [1, 2, 3], [1, 3, 4]])('block', (number1, number2, number3) => {
test('numbered test', ({ expect }) => {
expect(number1 + number2).toBe(number3)
})
})
})