Skip to content

Commit 2739334

Browse files
neumaennlCopilotneumann4softCopilot
authored
feat(junit-reporter): add jest-junit-compatible naming options (#10189)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: neumaennl <7926739+neumaennl@users.noreply.github.com> Co-authored-by: Martin Neumann <neumann@4soft.de> Co-authored-by: Copilot <copilot@github.com>
1 parent 95dc6e3 commit 2739334

4 files changed

Lines changed: 416 additions & 35 deletions

File tree

docs/guide/reporters.md

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -349,17 +349,70 @@ AssertionError: expected 5 to be 4 // Object.is equality
349349
</testsuites>
350350
```
351351

352-
The outputted XML contains nested `testsuites` and `testcase` tags. These can also be customized via reporter options `suiteName` and `classnameTemplate`. `classnameTemplate` can either be a template string or a function.
352+
The output XML contains nested `testsuites``testsuite``testcase` tags. You can customize the reporter's behaviour with the following options:
353+
354+
| Option | Description | Default |
355+
|---|---|---|
356+
| `suiteName` | `name` attribute of `<testsuites>` | `"vitest tests"` |
357+
| `suiteNameTemplate` | Template for the `name` attribute of `<testsuite>`. Accepts a string with placeholders or a function. | Relative file path |
358+
| `classnameTemplate` | Template for the `classname` attribute of `<testcase>`. Accepts a string with placeholders or a function. | Relative file path |
359+
| `titleTemplate` | Template for the `name` attribute of `<testcase>`. Accepts a string with placeholders or a function. | Full test title with ancestor hierarchy |
360+
| `ancestorSeparator` | Separator used when joining ancestor describe block names in the `{classname}` placeholder and in the default test title. | `" > "` |
361+
| `addFileAttribute` | Add a `file` attribute to each `<testcase>`. | `false` |
362+
| `includeConsoleOutput` | Include `<system-out>` / `<system-err>` console output. | `true` |
363+
| `stackTrace` | Include stack traces in `<failure>` elements. | `true` |
364+
365+
The following placeholders are available for `suiteNameTemplate`:
366+
- `{title}` – name of the first top-level `describe` block; falls back to the file basename when there is no top-level `describe`
367+
- `{filename}` – relative file path from the root (e.g. `src/foo.test.ts`)
368+
- `{filepath}` – absolute file path
369+
- `{basename}` – file name without directory (e.g. `foo.test.ts`)
370+
- `{displayName}` – Vitest project name
371+
372+
The following placeholders are available for `classnameTemplate` and `titleTemplate`:
373+
- `{classname}` – ancestor `describe` block names joined by `ancestorSeparator` (e.g. `outer > inner`)
374+
- `{title}` – leaf test title (the string passed to `it`/`test`)
375+
- `{suitename}` – top-level `describe` block name, empty string when the test has no enclosing `describe`
376+
- `{filename}` – relative file path from the root
377+
- `{filepath}` – absolute file path
378+
- `{basename}` – file name without directory
379+
- `{displayName}` – Vitest project name
353380

354-
The supported placeholders for the `classnameTemplate` option are:
355-
- filename
356-
- filepath
381+
::: tip
382+
`{filename}` follows Vitest's convention and resolves to the **relative path** from the project root (e.g. `src/foo.test.ts`). This differs from jest-junit where `{filename}` is the bare file name. Use `{basename}` to get only the file name.
383+
:::
384+
385+
```ts
386+
export default defineConfig({
387+
test: {
388+
reporters: [
389+
['junit', {
390+
suiteName: 'My Test Suite',
391+
// Use the first top-level describe block name as the testsuite name
392+
suiteNameTemplate: '{title}',
393+
// classname = ancestor describe chain
394+
classnameTemplate: '{classname}',
395+
// name = leaf test title only (jest-junit-compatible)
396+
titleTemplate: '{title}',
397+
ancestorSeparator: ' > ',
398+
}]
399+
]
400+
},
401+
})
402+
```
403+
404+
Function-based templates receive all available variables and can return any string:
357405

358406
```ts
359407
export default defineConfig({
360408
test: {
361409
reporters: [
362-
['junit', { suiteName: 'custom suite name', classnameTemplate: 'filename:{filename} - filepath:{filepath}' }]
410+
['junit', {
411+
classnameTemplate: ({ classname, filename }) =>
412+
classname ? `${filename}::${classname}` : filename,
413+
titleTemplate: ({ suitename, title }) =>
414+
suitename ? `[${suitename}] ${title}` : title,
415+
}]
363416
]
364417
},
365418
})

0 commit comments

Comments
 (0)