From 49be42dc94ca23b765aba435fb560a28048eddee Mon Sep 17 00:00:00 2001 From: Han Feng Date: Fri, 2 Jun 2023 18:30:48 +0800 Subject: [PATCH 1/6] feat: support using function as describe/test name --- packages/runner/src/suite.ts | 30 +++++++++++-------- packages/runner/src/types/tasks.ts | 24 +++++++-------- .../fixtures/test/example.test.ts | 28 +++++++++++++++++ test/describe-test-name/package.json | 10 +++++++ test/describe-test-name/test/runner.test.ts | 14 +++++++++ test/describe-test-name/vitest.config.ts | 7 +++++ 6 files changed, 89 insertions(+), 24 deletions(-) create mode 100644 test/describe-test-name/fixtures/test/example.test.ts create mode 100644 test/describe-test-name/package.json create mode 100644 test/describe-test-name/test/runner.test.ts create mode 100644 test/describe-test-name/vitest.config.ts diff --git a/packages/runner/src/suite.ts b/packages/runner/src/suite.ts index e223b92de4e4..acd191eda960 100644 --- a/packages/runner/src/suite.ts +++ b/packages/runner/src/suite.ts @@ -8,8 +8,8 @@ import { getHooks, setFn, setHooks } from './map' // apis export const suite = createSuite() export const test = createTest( - function (name: string, fn?: TestFunction, options?: number | TestOptions) { - getCurrentSuite().test.fn.call(this, name, fn, options) + function (name: string | Function, fn?: TestFunction, options?: number | TestOptions) { + getCurrentSuite().test.fn.call(this, formatName(name), fn, options) }, ) @@ -59,7 +59,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m initSuite() - const test = createTest(function (name: string, fn = noop, options) { + const test = createTest(function (name: string | Function, fn = noop, options) { const mode = this.only ? 'only' : this.skip ? 'skip' : this.todo ? 'todo' : 'run' if (typeof options === 'number') @@ -78,7 +78,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m const test: Test = { id: '', type: 'test', - name, + name: formatName(name), each: this.each, mode, suite: undefined!, @@ -189,7 +189,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m } function createSuite() { - function suiteFn(this: Record, name: string, factory?: SuiteFactory, options?: number | TestOptions) { + function suiteFn(this: Record, name: string | Function, factory?: SuiteFactory, options?: number | TestOptions) { const mode: RunMode = this.only ? 'only' : this.skip ? 'skip' : this.todo ? 'todo' : 'run' const currentSuite = getCurrentSuite() @@ -200,7 +200,7 @@ function createSuite() { if (currentSuite?.options) options = { ...currentSuite.options, ...options } - return createSuiteCollector(name, factory, mode, this.concurrent, this.shuffle, this.each, options) + return createSuiteCollector(formatName(name), factory, mode, this.concurrent, this.shuffle, this.each, options) } suiteFn.each = function(this: { withContext: () => SuiteAPI; setContext: (key: string, value: boolean | undefined) => SuiteAPI }, cases: ReadonlyArray, ...args: any[]) { @@ -210,13 +210,14 @@ function createSuite() { if (Array.isArray(cases) && args.length) cases = formatTemplateString(cases, args) - return (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => { + return (name: string | Function, fn: (...args: T[]) => void, options?: number | TestOptions) => { + const _name = formatName(name) const arrayOnlyCases = cases.every(Array.isArray) 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), () => fn(...items), options) + : suite(formatTitle(_name, items, idx), () => fn(i), options) }) this.setContext('each', undefined) @@ -249,14 +250,15 @@ function createTest(fn: ( if (Array.isArray(cases) && args.length) cases = formatTemplateString(cases, args) - return (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => { + return (name: string | Function, fn: (...args: T[]) => void, options?: number | TestOptions) => { + const _name = formatName(name) const arrayOnlyCases = cases.every(Array.isArray) 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), () => fn(...items), options) + : test(formatTitle(_name, items, idx), () => fn(i), options) }) this.setContext('each', undefined) @@ -272,6 +274,10 @@ function createTest(fn: ( ) as TestAPI } +function formatName(name: string | Function) { + return typeof name === 'string' ? name : name instanceof Function ? name.name : String(name) +} + function formatTitle(template: string, items: any[], idx: number) { if (template.includes('%#')) { // '%#' match index of the test case diff --git a/packages/runner/src/types/tasks.ts b/packages/runner/src/types/tasks.ts index 7ff9c4e4b7b1..eeca49956da7 100644 --- a/packages/runner/src/types/tasks.ts +++ b/packages/runner/src/types/tasks.ts @@ -112,37 +112,37 @@ type ExtractEachCallbackArgs> = { interface SuiteEachFunction { (cases: ReadonlyArray): ( - name: string, + name: string | Function, fn: (...args: T) => Awaitable, ) => void >(cases: ReadonlyArray): ( - name: string, + name: string | Function, fn: (...args: ExtractEachCallbackArgs) => Awaitable, ) => void (cases: ReadonlyArray): ( - name: string, + name: string | Function, fn: (...args: T[]) => Awaitable, ) => void } interface TestEachFunction { (cases: ReadonlyArray): ( - name: string, + name: string | Function, fn: (...args: T) => Awaitable, options?: number | TestOptions, ) => void >(cases: ReadonlyArray): ( - name: string, + name: string | Function, fn: (...args: ExtractEachCallbackArgs) => Awaitable, options?: number | TestOptions, ) => void (cases: ReadonlyArray): ( - name: string, + name: string | Function, fn: (...args: T[]) => Awaitable, options?: number | TestOptions, ) => void (...args: [TemplateStringsArray, ...any]): ( - name: string, + name: string | Function, fn: (...args: any[]) => Awaitable, options?: number | TestOptions, ) => void @@ -150,11 +150,11 @@ interface TestEachFunction { type ChainableTestAPI = ChainableFunction< 'concurrent' | 'only' | 'skip' | 'todo' | 'fails', - [name: string, fn?: TestFunction, options?: number | TestOptions], + [name: string | Function, fn?: TestFunction, options?: number | TestOptions], void, { each: TestEachFunction - (name: string, fn?: TestFunction, options?: number | TestOptions): void + (name: string | Function, fn?: TestFunction, options?: number | TestOptions): void } > @@ -188,11 +188,11 @@ export type TestAPI = ChainableTestAPI & { type ChainableSuiteAPI = ChainableFunction< 'concurrent' | 'only' | 'skip' | 'todo' | 'shuffle', - [name: string, factory?: SuiteFactory, options?: number | TestOptions], + [name: string | Function, factory?: SuiteFactory, options?: number | TestOptions], SuiteCollector, { each: TestEachFunction - (name: string, factory?: SuiteFactory): SuiteCollector + (name: string | Function, factory?: SuiteFactory): SuiteCollector } > @@ -226,7 +226,7 @@ export interface SuiteCollector { on: >(name: T, ...fn: SuiteHooks[T]) => void } -export type SuiteFactory = (test: (name: string, fn: TestFunction) => void) => Awaitable +export type SuiteFactory = (test: (name: string | Function, fn: TestFunction) => void) => Awaitable export interface RuntimeContext { tasks: (SuiteCollector | Test)[] diff --git a/test/describe-test-name/fixtures/test/example.test.ts b/test/describe-test-name/fixtures/test/example.test.ts new file mode 100644 index 000000000000..6047d72720c1 --- /dev/null +++ b/test/describe-test-name/fixtures/test/example.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, test } from 'vitest' + +function foo() {} +class Bar {} + +describe(foo, () => { + test(Bar, () => { + expect(0).toBe(1) + }) +}) + +describe(Bar, () => { + test(foo, () => { + expect(0).toBe(1) + }) +}) + +describe.each([1])(foo, () => { + test.each([1])(foo, () => { + expect(0).toBe(1) + }) +}) + +describe.each([1])(Bar, () => { + test.each([1])(Bar, () => { + expect(0).toBe(1) + }) +}) diff --git a/test/describe-test-name/package.json b/test/describe-test-name/package.json new file mode 100644 index 000000000000..f207b85300a0 --- /dev/null +++ b/test/describe-test-name/package.json @@ -0,0 +1,10 @@ +{ + "name": "@vitest/test-describe-test-name", + "private": true, + "scripts": { + "test": "vitest run" + }, + "devDependencies": { + "vitest": "workspace:*" + } +} diff --git a/test/describe-test-name/test/runner.test.ts b/test/describe-test-name/test/runner.test.ts new file mode 100644 index 000000000000..96b8617bc7b3 --- /dev/null +++ b/test/describe-test-name/test/runner.test.ts @@ -0,0 +1,14 @@ +import { expect, test } from 'vitest' +import { resolve } from 'pathe' +import { runVitest } from '../../test-utils' + +test('should print function name', async () => { + const filename = resolve('./fixtures/test/example.test.ts') + const { stderr } = await runVitest({ root: './fixtures' }, [filename]) + + expect(stderr).toBeTruthy() + expect(stderr).toContain('FAIL test/example.test.ts > foo > Bar') + expect(stderr).toContain('FAIL test/example.test.ts > Bar > foo') + expect(stderr).toContain('FAIL test/example.test.ts > foo > foo') + expect(stderr).toContain('FAIL test/example.test.ts > Bar > Bar') +}) diff --git a/test/describe-test-name/vitest.config.ts b/test/describe-test-name/vitest.config.ts new file mode 100644 index 000000000000..94e33acbde1b --- /dev/null +++ b/test/describe-test-name/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ['test/**/*.test.*'], + }, +}) From 3970374246c3fc40a1ab2f985503e16453b10854 Mon Sep 17 00:00:00 2001 From: Han Feng Date: Fri, 2 Jun 2023 18:56:57 +0800 Subject: [PATCH 2/6] fix: update lock file --- pnpm-lock.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a65e911d3eaa..26b8bda66a9c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1556,6 +1556,12 @@ importers: specifier: workspace:* version: link:../../packages/vitest + test/describe-test-name: + devDependencies: + vitest: + specifier: workspace:* + version: link:../../packages/vitest + test/edge-runtime: devDependencies: '@edge-runtime/vm': From 7fc5d58f2d4e29b1668c76cc80fd74693475cbfe Mon Sep 17 00:00:00 2001 From: Han Feng Date: Fri, 2 Jun 2023 19:14:51 +0800 Subject: [PATCH 3/6] fix: update lock file --- pnpm-lock.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 26b8bda66a9c..372628281be1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1557,10 +1557,10 @@ importers: version: link:../../packages/vitest test/describe-test-name: - devDependencies: - vitest: - specifier: workspace:* - version: link:../../packages/vitest + devDependencies: + vitest: + specifier: workspace:* + version: link:../../packages/vitest test/edge-runtime: devDependencies: From e64be7de863a4d80afbe4cf10bdef9eb487f6e34 Mon Sep 17 00:00:00 2001 From: Han Feng Date: Fri, 2 Jun 2023 19:36:28 +0800 Subject: [PATCH 4/6] docs: update api types --- docs/api/index.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/api/index.md b/docs/api/index.md index 5da908cc8b25..0ef06edccf81 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -36,7 +36,7 @@ In Jest, `TestFunction` can also be of type `(done: DoneCallback) => void`. If t ## test -- **Type:** `(name: string, fn: TestFunction, timeout?: number | TestOptions) => void` +- **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. @@ -53,7 +53,7 @@ In Jest, `TestFunction` can also be of type `(done: DoneCallback) => void`. If t ### test.skip -- **Type:** `(name: string, fn: TestFunction, timeout?: number | TestOptions) => void` +- **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. @@ -111,7 +111,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t ### test.only -- **Type:** `(name: string, fn: TestFunction, timeout?: number) => void` +- **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. @@ -136,7 +136,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t ### test.concurrent -- **Type:** `(name: string, fn: TestFunction, timeout?: number) => void` +- **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). @@ -179,7 +179,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t ### test.todo -- **Type:** `(name: string) => void` +- **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. @@ -191,7 +191,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t ### test.fails -- **Type:** `(name: string, fn: TestFunction, timeout?: number) => void` +- **Type:** `(name: string | Function, fn: TestFunction, timeout?: number) => void` - **Alias:** `it.fails` 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 ### describe.skip -- **Type:** `(name: string, fn: TestFunction, options?: number | TestOptions) => void` +- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void` Use `describe.skip` in a suite to avoid running a particular describe block. @@ -539,7 +539,7 @@ You cannot use this syntax when using Vitest as [type checker](/guide/testing-ty ### describe.only -- **Type:** `(name: string, fn: TestFunction, options?: number | TestOptions) => void` +- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void` Use `describe.only` to only run certain suites @@ -565,7 +565,7 @@ You cannot use this syntax when using Vitest as [type checker](/guide/testing-ty ### describe.concurrent -- **Type:** `(name: string, fn: TestFunction, options?: number | TestOptions) => void` +- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void` `describe.concurrent` in a suite marks every tests as concurrent @@ -606,7 +606,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t ### describe.shuffle -- **Type:** `(name: string, fn: TestFunction, options?: number | TestOptions) => void` +- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void` 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. @@ -627,7 +627,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t ### describe.todo -- **Type:** `(name: string) => void` +- **Type:** `(name: string | Function) => void` 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. @@ -638,7 +638,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t ### describe.each -- **Type:** `(cases: ReadonlyArray, ...args: any[]): (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => void` +- **Type:** `(cases: ReadonlyArray, ...args: any[]): (name: string | Function, fn: (...args: T[]) => void, options?: number | TestOptions) => void` Use `describe.each` if you have more than one test that depends on the same data. From 1252aea3b87335ba7d9ac4ff9f934f6ee7a9c8a0 Mon Sep 17 00:00:00 2001 From: Han Feng Date: Sat, 3 Jun 2023 11:01:52 +0800 Subject: [PATCH 5/6] test: move to reporters --- test/describe-test-name/package.json | 10 ---------- test/describe-test-name/test/runner.test.ts | 14 -------------- test/describe-test-name/vitest.config.ts | 7 ------- .../fixtures/function-as-name.test.ts} | 8 ++++---- test/reporters/tests/function-as-name.test.ts | 14 ++++++++++++++ 5 files changed, 18 insertions(+), 35 deletions(-) delete mode 100644 test/describe-test-name/package.json delete mode 100644 test/describe-test-name/test/runner.test.ts delete mode 100644 test/describe-test-name/vitest.config.ts rename test/{describe-test-name/fixtures/test/example.test.ts => reporters/fixtures/function-as-name.test.ts} (78%) create mode 100644 test/reporters/tests/function-as-name.test.ts diff --git a/test/describe-test-name/package.json b/test/describe-test-name/package.json deleted file mode 100644 index f207b85300a0..000000000000 --- a/test/describe-test-name/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "@vitest/test-describe-test-name", - "private": true, - "scripts": { - "test": "vitest run" - }, - "devDependencies": { - "vitest": "workspace:*" - } -} diff --git a/test/describe-test-name/test/runner.test.ts b/test/describe-test-name/test/runner.test.ts deleted file mode 100644 index 96b8617bc7b3..000000000000 --- a/test/describe-test-name/test/runner.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { expect, test } from 'vitest' -import { resolve } from 'pathe' -import { runVitest } from '../../test-utils' - -test('should print function name', async () => { - const filename = resolve('./fixtures/test/example.test.ts') - const { stderr } = await runVitest({ root: './fixtures' }, [filename]) - - expect(stderr).toBeTruthy() - expect(stderr).toContain('FAIL test/example.test.ts > foo > Bar') - expect(stderr).toContain('FAIL test/example.test.ts > Bar > foo') - expect(stderr).toContain('FAIL test/example.test.ts > foo > foo') - expect(stderr).toContain('FAIL test/example.test.ts > Bar > Bar') -}) diff --git a/test/describe-test-name/vitest.config.ts b/test/describe-test-name/vitest.config.ts deleted file mode 100644 index 94e33acbde1b..000000000000 --- a/test/describe-test-name/vitest.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - include: ['test/**/*.test.*'], - }, -}) diff --git a/test/describe-test-name/fixtures/test/example.test.ts b/test/reporters/fixtures/function-as-name.test.ts similarity index 78% rename from test/describe-test-name/fixtures/test/example.test.ts rename to test/reporters/fixtures/function-as-name.test.ts index 6047d72720c1..fb01790f0ec8 100644 --- a/test/describe-test-name/fixtures/test/example.test.ts +++ b/test/reporters/fixtures/function-as-name.test.ts @@ -5,24 +5,24 @@ class Bar {} describe(foo, () => { test(Bar, () => { - expect(0).toBe(1) + expect(0).toBe(0) }) }) describe(Bar, () => { test(foo, () => { - expect(0).toBe(1) + expect(0).toBe(0) }) }) describe.each([1])(foo, () => { test.each([1])(foo, () => { - expect(0).toBe(1) + expect(0).toBe(0) }) }) describe.each([1])(Bar, () => { test.each([1])(Bar, () => { - expect(0).toBe(1) + expect(0).toBe(0) }) }) diff --git a/test/reporters/tests/function-as-name.test.ts b/test/reporters/tests/function-as-name.test.ts new file mode 100644 index 000000000000..75714f30b053 --- /dev/null +++ b/test/reporters/tests/function-as-name.test.ts @@ -0,0 +1,14 @@ +import { expect, test } from 'vitest' +import { resolve } from 'pathe' +import { runVitest } from '../../test-utils' + +test('should print function name', async () => { + const filename = resolve('./fixtures/function-as-name.test.ts') + const { stdout } = await runVitest({ root: './fixtures' }, [filename]) + + expect(stdout).toBeTruthy() + expect(stdout).toContain('function-as-name.test.ts > foo > Bar') + expect(stdout).toContain('function-as-name.test.ts > Bar > foo') + expect(stdout).toContain('function-as-name.test.ts > foo > foo') + expect(stdout).toContain('function-as-name.test.ts > Bar > Bar') +}) From 91e19626208fe0ab2a6050c6b4fd1458f0b19adb Mon Sep 17 00:00:00 2001 From: Han Feng Date: Sat, 3 Jun 2023 11:02:26 +0800 Subject: [PATCH 6/6] chore: update lock file --- pnpm-lock.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 372628281be1..a65e911d3eaa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1556,12 +1556,6 @@ importers: specifier: workspace:* version: link:../../packages/vitest - test/describe-test-name: - devDependencies: - vitest: - specifier: workspace:* - version: link:../../packages/vitest - test/edge-runtime: devDependencies: '@edge-runtime/vm':