From 4974653ea7c74daefc1c5fea8e92233023a1064f Mon Sep 17 00:00:00 2001 From: yoho Date: Wed, 16 Nov 2022 10:49:49 +0800 Subject: [PATCH 1/8] feat: support string template --- packages/vitest/src/runtime/suite.ts | 15 ++++++++++++++- packages/vitest/src/types/tasks.ts | 5 +++++ test/core/test/each.test.ts | 9 +++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/vitest/src/runtime/suite.ts b/packages/vitest/src/runtime/suite.ts index 3eca5afe3a8f..aab3544245ef 100644 --- a/packages/vitest/src/runtime/suite.ts +++ b/packages/vitest/src/runtime/suite.ts @@ -212,9 +212,22 @@ function createTest(fn: ( )) { const testFn = fn as any - testFn.each = function(this: { withContext: () => TestAPI }, cases: ReadonlyArray) { + testFn.each = function(this: { withContext: () => TestAPI }, cases: ReadonlyArray, ...args: any[]) { const test = this.withContext() + // for template string + if (Array.isArray(cases) && !Array.isArray(args?.[0]) && typeof args?.[0] !== 'undefined') { + const header = cases.join('').trim().replace(/ /g, '').split('\n').map(i => i.split('|'))[0] + const res = [] + for (let i = 0; i < Math.floor((args.length) / header.length); i++) { + const oneCase: Record = {} + for (let j = 0; j < header.length; j++) + oneCase[header[j]] = args[i * header.length + j] as any + res.push(oneCase) + } + cases = res as any + } + return (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => { const arrayOnlyCases = cases.every(Array.isArray) cases.forEach((i, idx) => { diff --git a/packages/vitest/src/types/tasks.ts b/packages/vitest/src/types/tasks.ts index ba2497b42bc3..5791217dcad1 100644 --- a/packages/vitest/src/types/tasks.ts +++ b/packages/vitest/src/types/tasks.ts @@ -130,6 +130,11 @@ interface TestEachFunction { fn: (...args: T[]) => Awaitable, options?: number | TestOptions, ) => void + (...cases: any[]): ( + name: string, + fn: (...args: T[]) => Awaitable, + options?: number | TestOptions, + ) => void } type ChainableTestAPI = ChainableFunction< diff --git a/test/core/test/each.test.ts b/test/core/test/each.test.ts index fe156aa024d0..2593c3e9bc06 100644 --- a/test/core/test/each.test.ts +++ b/test/core/test/each.test.ts @@ -169,3 +169,12 @@ test.each([ expect(value1).toBeNull() expect(value2).toBeNull() }) + +test.each` +a | b | expected +${1} | ${1} | ${2} +${1} | ${2} | ${3} +${2} | ${1} | ${3} +`('returns $expected when $a is added $b', ({ a, b, expected }) => { + expect(a + b).toBe(expected) +}) From 256d8bccdeb37f743102418fe10e4d049ba6d8ec Mon Sep 17 00:00:00 2001 From: yoho Date: Wed, 16 Nov 2022 10:50:53 +0800 Subject: [PATCH 2/8] fix: judge --- packages/vitest/src/runtime/suite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vitest/src/runtime/suite.ts b/packages/vitest/src/runtime/suite.ts index aab3544245ef..47470c464936 100644 --- a/packages/vitest/src/runtime/suite.ts +++ b/packages/vitest/src/runtime/suite.ts @@ -216,7 +216,7 @@ function createTest(fn: ( const test = this.withContext() // for template string - if (Array.isArray(cases) && !Array.isArray(args?.[0]) && typeof args?.[0] !== 'undefined') { + if (Array.isArray(cases) && args.length) { const header = cases.join('').trim().replace(/ /g, '').split('\n').map(i => i.split('|'))[0] const res = [] for (let i = 0; i < Math.floor((args.length) / header.length); i++) { From 0cc60d91a1fe509cee97f0146b9e14717b719f8b Mon Sep 17 00:00:00 2001 From: yoho Date: Wed, 16 Nov 2022 10:51:51 +0800 Subject: [PATCH 3/8] chore: update --- packages/vitest/src/runtime/suite.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/vitest/src/runtime/suite.ts b/packages/vitest/src/runtime/suite.ts index 47470c464936..65f5d7599693 100644 --- a/packages/vitest/src/runtime/suite.ts +++ b/packages/vitest/src/runtime/suite.ts @@ -218,14 +218,13 @@ function createTest(fn: ( // for template string if (Array.isArray(cases) && args.length) { const header = cases.join('').trim().replace(/ /g, '').split('\n').map(i => i.split('|'))[0] - const res = [] + cases = [] for (let i = 0; i < Math.floor((args.length) / header.length); i++) { const oneCase: Record = {} for (let j = 0; j < header.length; j++) oneCase[header[j]] = args[i * header.length + j] as any - res.push(oneCase) + ;(cases as any).push(oneCase) } - cases = res as any } return (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => { From e905d87adb9c1e03df35211740d185e8f97d518a Mon Sep 17 00:00:00 2001 From: yoho Date: Wed, 16 Nov 2022 10:57:37 +0800 Subject: [PATCH 4/8] fix: types --- packages/vitest/src/types/tasks.ts | 4 ++-- test/core/test/each.test.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/vitest/src/types/tasks.ts b/packages/vitest/src/types/tasks.ts index 5791217dcad1..21480be3b34c 100644 --- a/packages/vitest/src/types/tasks.ts +++ b/packages/vitest/src/types/tasks.ts @@ -130,9 +130,9 @@ interface TestEachFunction { fn: (...args: T[]) => Awaitable, options?: number | TestOptions, ) => void - (...cases: any[]): ( + (...args: [TemplateStringsArray, ...any]): ( name: string, - fn: (...args: T[]) => Awaitable, + fn: (...args: any[]) => Awaitable, options?: number | TestOptions, ) => void } diff --git a/test/core/test/each.test.ts b/test/core/test/each.test.ts index 2593c3e9bc06..8d907f193ab1 100644 --- a/test/core/test/each.test.ts +++ b/test/core/test/each.test.ts @@ -178,3 +178,4 @@ ${2} | ${1} | ${3} `('returns $expected when $a is added $b', ({ a, b, expected }) => { expect(a + b).toBe(expected) }) + From f3ca976e03bf113c65f61fdd88344987a4ddc15b Mon Sep 17 00:00:00 2001 From: yoho Date: Wed, 16 Nov 2022 11:02:45 +0800 Subject: [PATCH 5/8] test: more --- test/core/test/each.test.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/test/core/test/each.test.ts b/test/core/test/each.test.ts index 8d907f193ab1..5a755cde42e6 100644 --- a/test/core/test/each.test.ts +++ b/test/core/test/each.test.ts @@ -173,9 +173,25 @@ test.each([ test.each` a | b | expected ${1} | ${1} | ${2} -${1} | ${2} | ${3} -${2} | ${1} | ${3} +${'a'} | ${'b'} | ${'ab'} +${[]} | ${'b'} | ${'b'} +${{}} | ${'b'} | ${'[object Object]b'} +${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} `('returns $expected when $a is added $b', ({ a, b, expected }) => { expect(a + b).toBe(expected) }) +test.each` +a | b | expected +${true} | ${true} | ${true} +`('($a && $b) -> $expected', ({ a, b, expected }) => { + expect(a && b).toBe(expected) +}) + +test.each` +a | b | expected +${{ val: 1 }} | ${{ val: 2 }}} | ${3} +`('($a && $b) -> $expected', ({ a, b, expected }) => { + expect(a.val + b.val).toBe(expected) +}) + From a72df357063435965af2e5945e1dbeaa3a49ed8d Mon Sep 17 00:00:00 2001 From: yoho Date: Thu, 17 Nov 2022 11:31:57 +0800 Subject: [PATCH 6/8] feat: suite each --- packages/vitest/src/runtime/suite.ts | 31 +++++++++++++++++----------- test/core/test/each.test.ts | 13 ++++++++++++ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/vitest/src/runtime/suite.ts b/packages/vitest/src/runtime/suite.ts index 65f5d7599693..db07a6902559 100644 --- a/packages/vitest/src/runtime/suite.ts +++ b/packages/vitest/src/runtime/suite.ts @@ -180,8 +180,12 @@ function createSuite() { return createSuiteCollector(name, factory, mode, this.concurrent, this.shuffle, options) } - suiteFn.each = function(this: { withContext: () => SuiteAPI }, cases: ReadonlyArray) { + suiteFn.each = function(this: { withContext: () => SuiteAPI }, cases: ReadonlyArray, ...args: any[]) { const suite = this.withContext() + + if (Array.isArray(cases) && args.length) + cases = formatTemplateString(cases, args) + return (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => { const arrayOnlyCases = cases.every(Array.isArray) cases.forEach((i, idx) => { @@ -215,17 +219,8 @@ function createTest(fn: ( testFn.each = function(this: { withContext: () => TestAPI }, cases: ReadonlyArray, ...args: any[]) { const test = this.withContext() - // for template string - if (Array.isArray(cases) && args.length) { - const header = cases.join('').trim().replace(/ /g, '').split('\n').map(i => i.split('|'))[0] - cases = [] - for (let i = 0; i < Math.floor((args.length) / header.length); i++) { - const oneCase: Record = {} - for (let j = 0; j < header.length; j++) - oneCase[header[j]] = args[i * header.length + j] as any - ;(cases as any).push(oneCase) - } - } + if (Array.isArray(cases) && args.length) + cases = formatTemplateString(cases, args) return (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => { const arrayOnlyCases = cases.every(Array.isArray) @@ -284,3 +279,15 @@ function formatTitle(template: string, items: any[], idx: number) { } return formatted } + +function formatTemplateString(cases: any[], args: any[]): any[] { + const header = cases.join('').trim().replace(/ /g, '').split('\n').map(i => i.split('|'))[0] + const res: any[] = [] + for (let i = 0; i < Math.floor((args.length) / header.length); i++) { + const oneCase: Record = {} + for (let j = 0; j < header.length; j++) + oneCase[header[j]] = args[i * header.length + j] as any + res.push(oneCase) + } + return res +} diff --git a/test/core/test/each.test.ts b/test/core/test/each.test.ts index 5a755cde42e6..bcf1c26324d3 100644 --- a/test/core/test/each.test.ts +++ b/test/core/test/each.test.ts @@ -170,6 +170,19 @@ test.each([ expect(value2).toBeNull() }) +describe.each` +a | b | expected +${1} | ${1} | ${2} +${'a'} | ${'b'} | ${'ab'} +${[]} | ${'b'} | ${'b'} +${{}} | ${'b'} | ${'[object Object]b'} +${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} +`('describe template string add($a, $b)', ({ a, b, expected }) => { + test(`returns ${expected}`, () => { + expect(a + b).toBe(expected) + }) +}) + test.each` a | b | expected ${1} | ${1} | ${2} From 686a10c6bbd00f908f3aaa8d04e240a4d36dd6b0 Mon Sep 17 00:00:00 2001 From: yoho Date: Thu, 17 Nov 2022 14:42:15 +0800 Subject: [PATCH 7/8] docs: more --- docs/api/index.md | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/api/index.md b/docs/api/index.md index a744cc5c4efe..ffc0b195ddc3 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -196,7 +196,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t ::: ### test.each -- **Type:** `(cases: ReadonlyArray) => void` +- **Type:** `(cases: ReadonlyArray, ...args: any[]) => void` - **Alias:** `it.each` Use `test.each` when you need to run the same test with different variables. @@ -243,6 +243,23 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t // ✓ add(2, 1) -> 3 ``` + You can also access template string table. + * First row of variable name column headings separated with `|` + * One or more subsequent rows of data supplied as template literal expressions using ${value} syntax. + + ```ts + test.each` + a | b | expected + ${1} | ${1} | ${2} + ${'a'} | ${'b'} | ${'ab'} + ${[]} | ${'b'} | ${'b'} + ${{}} | ${'b'} | ${'[object Object]b'} + ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} + `('returns $expected when $a is added $b', ({ a, b, expected }) => { + expect(a + b).toBe(expected) + }) + ``` + If you want to have access to `TestContext`, use `describe.each` with a single test. ::: warning @@ -547,7 +564,7 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t ### describe.each -- **Type:** `(cases: ReadonlyArray): (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => void` +- **Type:** `(cases: ReadonlyArray, ...args: any[]): (name: string, fn: (...args: T[]) => void, options?: number | TestOptions) => void` Use `describe.each` if you have more than one test that depends on the same data. @@ -571,6 +588,25 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t }) ``` + You can also access template string table. + * First row of variable name column headings separated with `|` + * One or more subsequent rows of data supplied as template literal expressions using ${value} syntax. + + ```ts + describe.each` + a | b | expected + ${1} | ${1} | ${2} + ${'a'} | ${'b'} | ${'ab'} + ${[]} | ${'b'} | ${'b'} + ${{}} | ${'b'} | ${'[object Object]b'} + ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} + `('describe template string add($a, $b)', ({ a, b, expected }) => { + test(`returns ${expected}`, () => { + expect(a + b).toBe(expected) + }) + }) + ``` + ::: warning You cannot use this syntax, when using Vitest as [type checker](/guide/testing-types). ::: From 0c725607ae835e54dbd2e0ef4dc229bc2d0e0ffa Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 22 Nov 2022 08:59:41 +0800 Subject: [PATCH 8/8] fix: format --- docs/api/index.md | 24 ++++++++++++------------ test/core/test/each.test.ts | 26 +++++++++++++------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/api/index.md b/docs/api/index.md index ffc0b195ddc3..4669c0748227 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -249,12 +249,12 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t ```ts test.each` - a | b | expected - ${1} | ${1} | ${2} - ${'a'} | ${'b'} | ${'ab'} - ${[]} | ${'b'} | ${'b'} - ${{}} | ${'b'} | ${'[object Object]b'} - ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} + a | b | expected + ${1} | ${1} | ${2} + ${'a'} | ${'b'} | ${'ab'} + ${[]} | ${'b'} | ${'b'} + ${{}} | ${'b'} | ${'[object Object]b'} + ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} `('returns $expected when $a is added $b', ({ a, b, expected }) => { expect(a + b).toBe(expected) }) @@ -594,12 +594,12 @@ You cannot use this syntax, when using Vitest as [type checker](/guide/testing-t ```ts describe.each` - a | b | expected - ${1} | ${1} | ${2} - ${'a'} | ${'b'} | ${'ab'} - ${[]} | ${'b'} | ${'b'} - ${{}} | ${'b'} | ${'[object Object]b'} - ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} + a | b | expected + ${1} | ${1} | ${2} + ${'a'} | ${'b'} | ${'ab'} + ${[]} | ${'b'} | ${'b'} + ${{}} | ${'b'} | ${'[object Object]b'} + ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} `('describe template string add($a, $b)', ({ a, b, expected }) => { test(`returns ${expected}`, () => { expect(a + b).toBe(expected) diff --git a/test/core/test/each.test.ts b/test/core/test/each.test.ts index bcf1c26324d3..a54ba8ac4ded 100644 --- a/test/core/test/each.test.ts +++ b/test/core/test/each.test.ts @@ -171,11 +171,11 @@ test.each([ }) describe.each` -a | b | expected -${1} | ${1} | ${2} -${'a'} | ${'b'} | ${'ab'} -${[]} | ${'b'} | ${'b'} -${{}} | ${'b'} | ${'[object Object]b'} +a | b | expected +${1} | ${1} | ${2} +${'a'} | ${'b'} | ${'ab'} +${[]} | ${'b'} | ${'b'} +${{}} | ${'b'} | ${'[object Object]b'} ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} `('describe template string add($a, $b)', ({ a, b, expected }) => { test(`returns ${expected}`, () => { @@ -184,25 +184,25 @@ ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} }) test.each` -a | b | expected -${1} | ${1} | ${2} -${'a'} | ${'b'} | ${'ab'} -${[]} | ${'b'} | ${'b'} -${{}} | ${'b'} | ${'[object Object]b'} -${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} +a | b | expected +${1} | ${1} | ${2} +${'a'} | ${'b'} | ${'ab'} +${[]} | ${'b'} | ${'b'} +${{}} | ${'b'} | ${'[object Object]b'} +${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} `('returns $expected when $a is added $b', ({ a, b, expected }) => { expect(a + b).toBe(expected) }) test.each` -a | b | expected +a | b | expected ${true} | ${true} | ${true} `('($a && $b) -> $expected', ({ a, b, expected }) => { expect(a && b).toBe(expected) }) test.each` -a | b | expected +a | b | expected ${{ val: 1 }} | ${{ val: 2 }}} | ${3} `('($a && $b) -> $expected', ({ a, b, expected }) => { expect(a.val + b.val).toBe(expected)