Skip to content

Commit 1525389

Browse files
authoredJun 5, 2023
feat(runner): support using function/class as describe/test name (#3497)
1 parent 5b73cbf commit 1525389

File tree

5 files changed

+84
-36
lines changed

5 files changed

+84
-36
lines changed
 

‎docs/api/index.md

+12-12
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ In Jest, `TestFunction` can also be of type `(done: DoneCallback) => void`. If t
3636

3737
## test
3838

39-
- **Type:** `(name: string, fn: TestFunction, timeout?: number | TestOptions) => void`
39+
- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number | TestOptions) => void`
4040
- **Alias:** `it`
4141

4242
`test` defines a set of related expectations. It receives the test name and a function that holds the expectations to test.
@@ -53,7 +53,7 @@ In Jest, `TestFunction` can also be of type `(done: DoneCallback) => void`. If t
5353

5454
### test.skip
5555

56-
- **Type:** `(name: string, fn: TestFunction, timeout?: number | TestOptions) => void`
56+
- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number | TestOptions) => void`
5757
- **Alias:** `it.skip`
5858

5959
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.
@@ -111,7 +111,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
111111

112112
### test.only
113113

114-
- **Type:** `(name: string, fn: TestFunction, timeout?: number) => void`
114+
- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number) => void`
115115
- **Alias:** `it.only`
116116

117117
Use `test.only` to only run certain tests in a given suite. This is useful when debugging.
@@ -136,7 +136,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
136136

137137
### test.concurrent
138138

139-
- **Type:** `(name: string, fn: TestFunction, timeout?: number) => void`
139+
- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number) => void`
140140
- **Alias:** `it.concurrent`
141141

142142
`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).
@@ -179,7 +179,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
179179

180180
### test.todo
181181

182-
- **Type:** `(name: string) => void`
182+
- **Type:** `(name: string | Function) => void`
183183
- **Alias:** `it.todo`
184184

185185
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.
@@ -191,7 +191,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
191191

192192
### test.fails
193193

194-
- **Type:** `(name: string, fn: TestFunction, timeout?: number) => void`
194+
- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number) => void`
195195
- **Alias:** `it.fails`
196196

197197
Use `test.fails` to indicate that an assertion will fail explicitly.
@@ -502,7 +502,7 @@ When you use `test` or `bench` in the top level of file, they are collected as p
502502

503503
### describe.skip
504504

505-
- **Type:** `(name: string, fn: TestFunction, options?: number | TestOptions) => void`
505+
- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void`
506506

507507
Use `describe.skip` in a suite to avoid running a particular describe block.
508508

@@ -539,7 +539,7 @@ You cannot use this syntax when using Vitest as [type checker](/guide/testing-ty
539539

540540
### describe.only
541541

542-
- **Type:** `(name: string, fn: TestFunction, options?: number | TestOptions) => void`
542+
- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void`
543543

544544
Use `describe.only` to only run certain suites
545545

@@ -565,7 +565,7 @@ You cannot use this syntax when using Vitest as [type checker](/guide/testing-ty
565565

566566
### describe.concurrent
567567

568-
- **Type:** `(name: string, fn: TestFunction, options?: number | TestOptions) => void`
568+
- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void`
569569

570570
`describe.concurrent` in a suite marks every tests as concurrent
571571

@@ -606,7 +606,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
606606

607607
### describe.shuffle
608608

609-
- **Type:** `(name: string, fn: TestFunction, options?: number | TestOptions) => void`
609+
- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void`
610610

611611
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.
612612

@@ -627,7 +627,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
627627

628628
### describe.todo
629629

630-
- **Type:** `(name: string) => void`
630+
- **Type:** `(name: string | Function) => void`
631631

632632
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.
633633

@@ -638,7 +638,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t
638638

639639
### describe.each
640640

641-
- **Type:** `(cases: ReadonlyArray<T>, ...args: any[]): (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => void`
641+
- **Type:** `(cases: ReadonlyArray<T>, ...args: any[]): (name: string | Function, fn: (...args: T[]) => void, options?: number | TestOptions) => void`
642642

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

‎packages/runner/src/suite.ts

+18-12
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { getHooks, setFn, setHooks } from './map'
88
// apis
99
export const suite = createSuite()
1010
export const test = createTest(
11-
function (name: string, fn?: TestFunction, options?: number | TestOptions) {
12-
getCurrentSuite().test.fn.call(this, name, fn, options)
11+
function (name: string | Function, fn?: TestFunction, options?: number | TestOptions) {
12+
getCurrentSuite().test.fn.call(this, formatName(name), fn, options)
1313
},
1414
)
1515

@@ -59,7 +59,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m
5959

6060
initSuite()
6161

62-
const test = createTest(function (name: string, fn = noop, options) {
62+
const test = createTest(function (name: string | Function, fn = noop, options) {
6363
const mode = this.only ? 'only' : this.skip ? 'skip' : this.todo ? 'todo' : 'run'
6464

6565
if (typeof options === 'number')
@@ -78,7 +78,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m
7878
const test: Test = {
7979
id: '',
8080
type: 'test',
81-
name,
81+
name: formatName(name),
8282
each: this.each,
8383
mode,
8484
suite: undefined!,
@@ -189,7 +189,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m
189189
}
190190

191191
function createSuite() {
192-
function suiteFn(this: Record<string, boolean | undefined>, name: string, factory?: SuiteFactory, options?: number | TestOptions) {
192+
function suiteFn(this: Record<string, boolean | undefined>, name: string | Function, factory?: SuiteFactory, options?: number | TestOptions) {
193193
const mode: RunMode = this.only ? 'only' : this.skip ? 'skip' : this.todo ? 'todo' : 'run'
194194
const currentSuite = getCurrentSuite()
195195

@@ -200,7 +200,7 @@ function createSuite() {
200200
if (currentSuite?.options)
201201
options = { ...currentSuite.options, ...options }
202202

203-
return createSuiteCollector(name, factory, mode, this.concurrent, this.shuffle, this.each, options)
203+
return createSuiteCollector(formatName(name), factory, mode, this.concurrent, this.shuffle, this.each, options)
204204
}
205205

206206
suiteFn.each = function<T>(this: { withContext: () => SuiteAPI; setContext: (key: string, value: boolean | undefined) => SuiteAPI }, cases: ReadonlyArray<T>, ...args: any[]) {
@@ -210,13 +210,14 @@ function createSuite() {
210210
if (Array.isArray(cases) && args.length)
211211
cases = formatTemplateString(cases, args)
212212

213-
return (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => {
213+
return (name: string | Function, fn: (...args: T[]) => void, options?: number | TestOptions) => {
214+
const _name = formatName(name)
214215
const arrayOnlyCases = cases.every(Array.isArray)
215216
cases.forEach((i, idx) => {
216217
const items = Array.isArray(i) ? i : [i]
217218
arrayOnlyCases
218-
? suite(formatTitle(name, items, idx), () => fn(...items), options)
219-
: suite(formatTitle(name, items, idx), () => fn(i), options)
219+
? suite(formatTitle(_name, items, idx), () => fn(...items), options)
220+
: suite(formatTitle(_name, items, idx), () => fn(i), options)
220221
})
221222

222223
this.setContext('each', undefined)
@@ -249,14 +250,15 @@ function createTest(fn: (
249250
if (Array.isArray(cases) && args.length)
250251
cases = formatTemplateString(cases, args)
251252

252-
return (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => {
253+
return (name: string | Function, fn: (...args: T[]) => void, options?: number | TestOptions) => {
254+
const _name = formatName(name)
253255
const arrayOnlyCases = cases.every(Array.isArray)
254256
cases.forEach((i, idx) => {
255257
const items = Array.isArray(i) ? i : [i]
256258

257259
arrayOnlyCases
258-
? test(formatTitle(name, items, idx), () => fn(...items), options)
259-
: test(formatTitle(name, items, idx), () => fn(i), options)
260+
? test(formatTitle(_name, items, idx), () => fn(...items), options)
261+
: test(formatTitle(_name, items, idx), () => fn(i), options)
260262
})
261263

262264
this.setContext('each', undefined)
@@ -272,6 +274,10 @@ function createTest(fn: (
272274
) as TestAPI
273275
}
274276

277+
function formatName(name: string | Function) {
278+
return typeof name === 'string' ? name : name instanceof Function ? name.name : String(name)
279+
}
280+
275281
function formatTitle(template: string, items: any[], idx: number) {
276282
if (template.includes('%#')) {
277283
// '%#' match index of the test case

‎packages/runner/src/types/tasks.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -112,49 +112,49 @@ type ExtractEachCallbackArgs<T extends ReadonlyArray<any>> = {
112112

113113
interface SuiteEachFunction {
114114
<T extends any[] | [any]>(cases: ReadonlyArray<T>): (
115-
name: string,
115+
name: string | Function,
116116
fn: (...args: T) => Awaitable<void>,
117117
) => void
118118
<T extends ReadonlyArray<any>>(cases: ReadonlyArray<T>): (
119-
name: string,
119+
name: string | Function,
120120
fn: (...args: ExtractEachCallbackArgs<T>) => Awaitable<void>,
121121
) => void
122122
<T>(cases: ReadonlyArray<T>): (
123-
name: string,
123+
name: string | Function,
124124
fn: (...args: T[]) => Awaitable<void>,
125125
) => void
126126
}
127127

128128
interface TestEachFunction {
129129
<T extends any[] | [any]>(cases: ReadonlyArray<T>): (
130-
name: string,
130+
name: string | Function,
131131
fn: (...args: T) => Awaitable<void>,
132132
options?: number | TestOptions,
133133
) => void
134134
<T extends ReadonlyArray<any>>(cases: ReadonlyArray<T>): (
135-
name: string,
135+
name: string | Function,
136136
fn: (...args: ExtractEachCallbackArgs<T>) => Awaitable<void>,
137137
options?: number | TestOptions,
138138
) => void
139139
<T>(cases: ReadonlyArray<T>): (
140-
name: string,
140+
name: string | Function,
141141
fn: (...args: T[]) => Awaitable<void>,
142142
options?: number | TestOptions,
143143
) => void
144144
(...args: [TemplateStringsArray, ...any]): (
145-
name: string,
145+
name: string | Function,
146146
fn: (...args: any[]) => Awaitable<void>,
147147
options?: number | TestOptions,
148148
) => void
149149
}
150150

151151
type ChainableTestAPI<ExtraContext = {}> = ChainableFunction<
152152
'concurrent' | 'only' | 'skip' | 'todo' | 'fails',
153-
[name: string, fn?: TestFunction<ExtraContext>, options?: number | TestOptions],
153+
[name: string | Function, fn?: TestFunction<ExtraContext>, options?: number | TestOptions],
154154
void,
155155
{
156156
each: TestEachFunction
157-
<T extends ExtraContext>(name: string, fn?: TestFunction<T>, options?: number | TestOptions): void
157+
<T extends ExtraContext>(name: string | Function, fn?: TestFunction<T>, options?: number | TestOptions): void
158158
}
159159
>
160160

@@ -188,11 +188,11 @@ export type TestAPI<ExtraContext = {}> = ChainableTestAPI<ExtraContext> & {
188188

189189
type ChainableSuiteAPI<ExtraContext = {}> = ChainableFunction<
190190
'concurrent' | 'only' | 'skip' | 'todo' | 'shuffle',
191-
[name: string, factory?: SuiteFactory<ExtraContext>, options?: number | TestOptions],
191+
[name: string | Function, factory?: SuiteFactory<ExtraContext>, options?: number | TestOptions],
192192
SuiteCollector<ExtraContext>,
193193
{
194194
each: TestEachFunction
195-
<T extends ExtraContext>(name: string, factory?: SuiteFactory<T>): SuiteCollector<T>
195+
<T extends ExtraContext>(name: string | Function, factory?: SuiteFactory<T>): SuiteCollector<T>
196196
}
197197
>
198198

@@ -226,7 +226,7 @@ export interface SuiteCollector<ExtraContext = {}> {
226226
on: <T extends keyof SuiteHooks<ExtraContext>>(name: T, ...fn: SuiteHooks<ExtraContext>[T]) => void
227227
}
228228

229-
export type SuiteFactory<ExtraContext = {}> = (test: (name: string, fn: TestFunction<ExtraContext>) => void) => Awaitable<void>
229+
export type SuiteFactory<ExtraContext = {}> = (test: (name: string | Function, fn: TestFunction<ExtraContext>) => void) => Awaitable<void>
230230

231231
export interface RuntimeContext {
232232
tasks: (SuiteCollector | Test)[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { describe, expect, test } from 'vitest'
2+
3+
function foo() {}
4+
class Bar {}
5+
6+
describe(foo, () => {
7+
test(Bar, () => {
8+
expect(0).toBe(0)
9+
})
10+
})
11+
12+
describe(Bar, () => {
13+
test(foo, () => {
14+
expect(0).toBe(0)
15+
})
16+
})
17+
18+
describe.each([1])(foo, () => {
19+
test.each([1])(foo, () => {
20+
expect(0).toBe(0)
21+
})
22+
})
23+
24+
describe.each([1])(Bar, () => {
25+
test.each([1])(Bar, () => {
26+
expect(0).toBe(0)
27+
})
28+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { expect, test } from 'vitest'
2+
import { resolve } from 'pathe'
3+
import { runVitest } from '../../test-utils'
4+
5+
test('should print function name', async () => {
6+
const filename = resolve('./fixtures/function-as-name.test.ts')
7+
const { stdout } = await runVitest({ root: './fixtures' }, [filename])
8+
9+
expect(stdout).toBeTruthy()
10+
expect(stdout).toContain('function-as-name.test.ts > foo > Bar')
11+
expect(stdout).toContain('function-as-name.test.ts > Bar > foo')
12+
expect(stdout).toContain('function-as-name.test.ts > foo > foo')
13+
expect(stdout).toContain('function-as-name.test.ts > Bar > Bar')
14+
})

0 commit comments

Comments
 (0)
Please sign in to comment.