From 77d75fd344b6105b9965f068bb39ee307591bad5 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:56:03 +0100 Subject: [PATCH] feat: track and display expectedly failed tests (.fails) in UI and CLI (#9476) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sheremet-va <16173870+sheremet-va@users.noreply.github.com> Co-authored-by: Vladimir --- .../client/components/dashboard/TestsEntry.vue | 12 ++++++++++++ .../ui/client/composables/explorer/collector.ts | 17 ++++++++++++++--- packages/ui/client/composables/explorer/tree.ts | 1 + .../ui/client/composables/explorer/types.ts | 2 ++ .../src/node/reporters/renderers/utils.ts | 16 +++++++++++++++- packages/vitest/src/node/reporters/summary.ts | 12 ++++++++++-- 6 files changed, 54 insertions(+), 6 deletions(-) diff --git a/packages/ui/client/components/dashboard/TestsEntry.vue b/packages/ui/client/components/dashboard/TestsEntry.vue index 76825c216..7be0fe7bc 100644 --- a/packages/ui/client/components/dashboard/TestsEntry.vue +++ b/packages/ui/client/components/dashboard/TestsEntry.vue @@ -47,6 +47,18 @@ function toggleFilter(type: 'success' | 'failed' | 'skipped' | 'total') { {{ explorerTree.summary.testsFailed }} + + + + i.result?.state === 'pass' ? acc + 1 : acc, 0) + const passed = tasks.reduce((acc, i) => { + // Exclude expected failures from passed count + if (i.result?.state === 'pass' && i.type === 'test' && i.fails) { + return acc + } + return i.result?.state === 'pass' ? acc + 1 : acc + }, 0) const failed = tasks.reduce((acc, i) => i.result?.state === 'fail' ? acc + 1 : acc, 0) const skipped = tasks.reduce((acc, i) => i.mode === 'skip' ? acc + 1 : acc, 0) const todo = tasks.reduce((acc, i) => i.mode === 'todo' ? acc + 1 : acc, 0) + const expectedFail = tasks.reduce((acc, i) => { + // Count tests that are marked as .fails and passed (which means they failed as expected) + if (i.result?.state === 'pass' && i.type === 'test' && i.fails) { + return acc + 1 + } + return acc + }, 0) return ( [ failed ? c.bold(c.red(`${failed} failed`)) : null, passed ? c.bold(c.green(`${passed} passed`)) : null, + expectedFail ? c.cyan(`${expectedFail} expected fail`) : null, skipped ? c.yellow(`${skipped} skipped`) : null, todo ? c.gray(`${todo} todo`) : null, ] diff --git a/packages/vitest/src/node/reporters/summary.ts b/packages/vitest/src/node/reporters/summary.ts index bbbf7860e..736d7812e 100644 --- a/packages/vitest/src/node/reporters/summary.ts +++ b/packages/vitest/src/node/reporters/summary.ts @@ -21,6 +21,7 @@ interface Counter { failed: number skipped: number todo: number + expectedFail: number } interface SlowTask { @@ -208,7 +209,13 @@ export class SummaryReporter implements Reporter { const result = test.result() if (result?.state === 'passed') { - this.tests.passed++ + // Check if this is an expected failure (test.fails && passed) + if (test.options.fails) { + this.tests.expectedFail++ + } + else { + this.tests.passed++ + } } else if (result?.state === 'failed') { this.tests.failed++ @@ -349,7 +356,7 @@ export class SummaryReporter implements Reporter { } function emptyCounters(): Counter { - return { completed: 0, passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 } + return { completed: 0, passed: 0, failed: 0, skipped: 0, todo: 0, expectedFail: 0, total: 0 } } function getStateString(entry: Counter) { @@ -357,6 +364,7 @@ function getStateString(entry: Counter) { [ entry.failed ? c.bold(c.red(`${entry.failed} failed`)) : null, c.bold(c.green(`${entry.passed} passed`)), + entry.expectedFail ? c.cyan(`${entry.expectedFail} expected fail`) : null, entry.skipped ? c.yellow(`${entry.skipped} skipped`) : null, entry.todo ? c.gray(`${entry.todo} todo`) : null, ] -- 2.54.0