Skip to content

Commit ea3d67b

Browse files
authored
feat: allow array element for test.each/for title formatting (#7522)
1 parent eacab25 commit ea3d67b

File tree

4 files changed

+119
-19
lines changed

4 files changed

+119
-19
lines changed

docs/api/index.md

+22-9
Original file line numberDiff line numberDiff line change
@@ -331,16 +331,29 @@ test.each([
331331
// ✓ add(2, 1) -> 3
332332
```
333333

334-
You can also access object properties with `$` prefix, if you are using objects as arguments:
334+
You can also access object properties and array elements with `$` prefix:
335335

336-
```ts
337-
test.each([
338-
{ a: 1, b: 1, expected: 2 },
339-
{ a: 1, b: 2, expected: 3 },
340-
{ a: 2, b: 1, expected: 3 },
341-
])('add($a, $b) -> $expected', ({ a, b, expected }) => {
342-
expect(a + b).toBe(expected)
343-
})
336+
```ts
337+
test.each([
338+
{ a: 1, b: 1, expected: 2 },
339+
{ a: 1, b: 2, expected: 3 },
340+
{ a: 2, b: 1, expected: 3 },
341+
])('add($a, $b) -> $expected', ({ a, b, expected }) => {
342+
expect(a + b).toBe(expected)
343+
})
344+
345+
// this will return
346+
// ✓ add(1, 1) -> 2
347+
// ✓ add(1, 2) -> 3
348+
// ✓ add(2, 1) -> 3
349+
350+
test.each([
351+
[1, 1, 2],
352+
[1, 2, 3],
353+
[2, 1, 3],
354+
])('add($0, $1) -> $2', (a, b, expected) => {
355+
expect(a + b).toBe(expected)
356+
})
344357

345358
// this will return
346359
// ✓ add(1, 1) -> 2

packages/runner/src/suite.ts

+15-10
Original file line numberDiff line numberDiff line change
@@ -816,16 +816,21 @@ function formatTitle(template: string, items: any[], idx: number) {
816816
}
817817

818818
let formatted = format(template, ...items.slice(0, count))
819-
if (isObject(items[0])) {
820-
formatted = formatted.replace(
821-
/\$([$\w.]+)/g,
822-
// https://github.com/chaijs/chai/pull/1490
823-
(_, key) =>
824-
objDisplay(objectAttr(items[0], key), {
825-
truncate: runner?.config?.chaiConfig?.truncateThreshold,
826-
}) as unknown as string,
827-
)
828-
}
819+
const isObjectItem = isObject(items[0])
820+
formatted = formatted.replace(
821+
/\$([$\w.]+)/g,
822+
(_, key: string) => {
823+
const isArrayKey = /^\d+$/.test(key)
824+
if (!isObjectItem && !isArrayKey) {
825+
return `$${key}`
826+
}
827+
const arrayElement = isArrayKey ? objectAttr(items, key) : undefined
828+
const value = isObjectItem ? objectAttr(items[0], key, arrayElement) : arrayElement
829+
return objDisplay(value, {
830+
truncate: runner?.config?.chaiConfig?.truncateThreshold,
831+
}) as unknown as string
832+
},
833+
)
829834
return formatted
830835
}
831836

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { expect, test } from "vitest"
2+
3+
test.for([
4+
{ 0: 'a', 1: {}, 2: { te: "st" } },
5+
{ "0": 'b', "1": [], "2": ["test"] },
6+
])('test.for object : 0 = $0, 2 = $2', () => {});
7+
8+
test.each([
9+
{ 0: 'a', 1: {}, 2: { te: "st" } },
10+
{ "0": 'b', "1": [], "2": ["test"] },
11+
])('test.each object : 0 = $0, 2 = $2 ', () => {});
12+
13+
test.for([
14+
['a', {}, { te: "st" }],
15+
['b', [], [ "test" ]],
16+
])('test.for array : 0 = $0, 2 = $2', () => {});
17+
18+
test.each([
19+
['a', {}, { te: "st" }],
20+
['b', [], [ "test" ]],
21+
])('test.each array : 0 = $0, 2 = $2', () => {});
22+
23+
test.each([
24+
{ a: 1, b: 1, expected: 2 },
25+
{ a: 1, b: 2, expected: 3 },
26+
{ a: 2, b: 1, expected: 3 },
27+
])('object : add($a, $b) -> $expected', ({ a, b, expected }) => {
28+
expect(a + b).toBe(expected)
29+
})
30+
31+
test.each([
32+
[1, 1, 2],
33+
[1, 2, 3],
34+
[2, 1, 3],
35+
])('array : add($0, $1) -> $2', (a, b, expected) => {
36+
expect(a + b).toBe(expected)
37+
})
38+
39+
test.for([
40+
[{ k1: "v1" }, { k2: "v2" }],
41+
])('first array element is object: 0 = $0, 1 = $1, k1 = $k1, k2 = $k2', () => {})
42+
43+
test.for([
44+
["foo", "bar"],
45+
])('first array element is not object: 0 = $0, 1 = $1, k = $k', () => {})
46+
47+
test.for([
48+
{ k: "v1" },
49+
{ k: "v2" },
50+
])('not array: 0 = $0, 1 = $1, k = $k', () => {})

test/reporters/tests/default.test.ts

+32
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,38 @@ describe('default reporter', async () => {
189189
expect(stdout).toContain('✓ passed > 1-based index of the test case is 2')
190190
expect(stdout).toContain('✓ passed > 1-based index of the test case is 3')
191191
})
192+
193+
test('test.each/for title format', async () => {
194+
const { stdout } = await runVitest({
195+
include: ['fixtures/test-for-title.test.ts'],
196+
reporters: [['default', { isTTY: true, summary: false }]],
197+
config: false,
198+
})
199+
expect(
200+
[...stdout.matchAll(/( .*)$/gm)].map(v => v[0]).filter(v => !v.includes('ms')),
201+
).toMatchInlineSnapshot(`
202+
[
203+
"✓ test.for object : 0 = 'a', 2 = { te: 'st' }",
204+
"✓ test.for object : 0 = 'b', 2 = [ 'test' ]",
205+
"✓ test.each object : 0 = 'a', 2 = { te: 'st' } ",
206+
"✓ test.each object : 0 = 'b', 2 = [ 'test' ] ",
207+
"✓ test.for array : 0 = 'a', 2 = { te: 'st' }",
208+
"✓ test.for array : 0 = 'b', 2 = [ 'test' ]",
209+
"✓ test.each array : 0 = 'a', 2 = { te: 'st' }",
210+
"✓ test.each array : 0 = 'b', 2 = [ 'test' ]",
211+
"✓ object : add(1, 1) -> 2",
212+
"✓ object : add(1, 2) -> 3",
213+
"✓ object : add(2, 1) -> 3",
214+
"✓ array : add(1, 1) -> 2",
215+
"✓ array : add(1, 2) -> 3",
216+
"✓ array : add(2, 1) -> 3",
217+
"✓ first array element is object: 0 = { k1: 'v1' }, 1 = { k2: 'v2' }, k1 = 'v1', k2 = undefined",
218+
"✓ first array element is not object: 0 = 'foo', 1 = 'bar', k = $k",
219+
"✓ not array: 0 = { k: 'v1' }, 1 = undefined, k = 'v1'",
220+
"✓ not array: 0 = { k: 'v2' }, 1 = undefined, k = 'v2'",
221+
]
222+
`)
223+
})
192224
}, 120000)
193225

194226
function trimReporterOutput(report: string) {

0 commit comments

Comments
 (0)