Skip to content

Commit

Permalink
feat: support readonly tuples as input to .each (#733)
Browse files Browse the repository at this point in the history
  • Loading branch information
IgnusG committed Feb 11, 2022
1 parent 597db8c commit 73d36dc
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 7 deletions.
10 changes: 5 additions & 5 deletions packages/vitest/src/runtime/suite.ts
@@ -1,5 +1,5 @@
import { format } from 'util'
import type { File, RunMode, Suite, SuiteAPI, SuiteCollector, SuiteFactory, SuiteHooks, Test, TestAPI, TestFunction } from '../types'
import type { File, MutableArray, RunMode, Suite, SuiteAPI, SuiteCollector, SuiteFactory, SuiteHooks, Test, TestAPI, TestFunction } from '../types'
import { isObject, noop, toArray } from '../utils'
import { createChainable } from './chain'
import { collectTask, context, normalizeTest, runWithSuite } from './context'
Expand Down Expand Up @@ -145,8 +145,8 @@ function createSuite() {
},
) as SuiteAPI

suite.each = <T>(cases: T[]) => {
return (name: string, fn: (...args: T extends any[] ? T : [T]) => void) => {
suite.each = <T>(cases: T[] | readonly T[]) => {
return (name: string, fn: (...args: T extends any[] | readonly any[] ? MutableArray<T> : [T]) => void) => {
cases.forEach((i) => {
const items = toArray(i) as any
suite(formatTitle(name, items), () => fn(...items))
Expand All @@ -163,8 +163,8 @@ function createTest(fn: ((this: Record<'concurrent'| 'skip'| 'only'| 'todo'| 'fa
fn,
) as TestAPI

test.each = <T>(cases: T[]) => {
return (name: string, fn: (...args: T extends any[] ? T : [T]) => void) => {
test.each = <T>(cases: T[] | readonly T[]) => {
return (name: string, fn: (...args: T extends any[] | readonly any[] ? MutableArray<T> : [T]) => void) => {
cases.forEach((i) => {
const items = toArray(i) as any
test(formatTitle(name, items), () => fn(...items))
Expand Down
2 changes: 2 additions & 0 deletions packages/vitest/src/types/general.ts
Expand Up @@ -18,6 +18,8 @@ export type DeepMerge<F, S> = MergeInsertions<{
: never;
}>

export type MutableArray<T extends readonly any[]> = { -readonly [k in keyof T]: T[k] }

export interface Constructable {
new (...args: any[]): any
}
Expand Down
4 changes: 2 additions & 2 deletions packages/vitest/src/types/tasks.ts
@@ -1,5 +1,5 @@
import type { ChainableFunction } from '../runtime/chain'
import type { Awaitable, ErrorWithDiff } from './general'
import type { Awaitable, ErrorWithDiff, MutableArray } from './general'
import type { UserConsoleLog } from '.'

export type RunMode = 'run' | 'skip' | 'only' | 'todo'
Expand Down Expand Up @@ -44,7 +44,7 @@ export type Task = Test | Suite | File

export type DoneCallback = (error?: any) => void
export type TestFunction = (done: DoneCallback) => Awaitable<void>
export type EachFunction = <T>(cases: T[]) => (name: string, fn: (...args: T extends any[] ? T : [T]) => void) => void
export type EachFunction = <T>(cases: T[] | readonly T[]) => (name: string, fn: (...args: T extends any[] | readonly any[] ? MutableArray<T> : [T]) => void) => void

export type TestAPI = ChainableFunction<
'concurrent' | 'only' | 'skip' | 'todo' | 'fails',
Expand Down
14 changes: 14 additions & 0 deletions test/core/test/each.test.ts
Expand Up @@ -26,6 +26,20 @@ describe.each([
})
})

describe.each([
[1, 'a', '1a'],
[1, 'b', '1b'],
[2, 'c', '2c'],
] as const)('describe concatenate(%i, %s)', (a, b, expected) => {
test(`returns ${expected}`, () => {
// This will fail typechecking if const is not used and/or types for a,b are merged into a union
const typedA: number = a
const typedB: string = b

expect(`${typedA}${typedB}`).toBe(expected)
})
})

describe.each([
{ a: 1, b: 1, expected: 2 },
{ a: 1, b: 2, expected: 3 },
Expand Down

0 comments on commit 73d36dc

Please sign in to comment.