diff --git a/packages/vitest/src/node/reporters/base.ts b/packages/vitest/src/node/reporters/base.ts index 96e1a4c97fea..1e41b5bd3206 100644 --- a/packages/vitest/src/node/reporters/base.ts +++ b/packages/vitest/src/node/reporters/base.ts @@ -134,6 +134,7 @@ export abstract class BaseReporter implements Reporter { let testsCount = 0 let failedCount = 0 let skippedCount = 0 + let todoCount = 0 // delaying logs to calculate the test stats first // which minimizes the amount of for loops @@ -147,7 +148,7 @@ export abstract class BaseReporter implements Reporter { const suiteState = child.state() // Skipped suites are hidden when --hideSkippedTests, print otherwise - if (!this.ctx.config.hideSkippedTests || suiteState !== 'skipped') { + if (!this.ctx.config.hideSkippedTests || suiteState !== 'skipped' || child.task.mode === 'todo') { this.printTestSuite(child) } @@ -161,10 +162,15 @@ export abstract class BaseReporter implements Reporter { failedCount++ } else if (testResult.state === 'skipped') { - skippedCount++ + if (child.options.mode === 'todo') { + todoCount++ + } + else { + skippedCount++ + } } - if (this.ctx.config.hideSkippedTests && suiteState === 'skipped') { + if (this.ctx.config.hideSkippedTests && suiteState === 'skipped' && child.options.mode !== 'todo') { // Skipped suites are hidden when --hideSkippedTests continue } @@ -185,6 +191,7 @@ export abstract class BaseReporter implements Reporter { tests: testsCount, failed: failedCount, skipped: skippedCount, + todo: todoCount, })) logs.forEach(log => this.log(log)) } @@ -205,7 +212,7 @@ export abstract class BaseReporter implements Reporter { this.log(` ${padding}${c.yellow(c.dim(F_CHECK))} ${this.getTestName(test.task, separator)} ${suffix}`) } - else if (this.ctx.config.hideSkippedTests && (testResult.state === 'skipped')) { + else if (this.ctx.config.hideSkippedTests && testResult.state === 'skipped' && test.options.mode !== 'todo') { // Skipped tests are hidden when --hideSkippedTests } @@ -218,6 +225,7 @@ export abstract class BaseReporter implements Reporter { tests: number failed: number skipped: number + todo: number }): string { let state = c.dim(`${counts.tests} test${counts.tests > 1 ? 's' : ''}`) @@ -229,6 +237,10 @@ export abstract class BaseReporter implements Reporter { state += c.dim(' | ') + c.yellow(`${counts.skipped} skipped`) } + if (counts.todo) { + state += c.dim(' | ') + c.gray(`${counts.todo} todo`) + } + let suffix = c.dim('(') + state + c.dim(')') + this.getDurationPrefix(testModule.task) const diagnostic = testModule.diagnostic() diff --git a/packages/vitest/src/node/reporters/renderers/figures.ts b/packages/vitest/src/node/reporters/renderers/figures.ts index 99543e078861..855a14eb42ad 100644 --- a/packages/vitest/src/node/reporters/renderers/figures.ts +++ b/packages/vitest/src/node/reporters/renderers/figures.ts @@ -8,5 +8,6 @@ export const F_CHECK = '✓' export const F_CROSS = '×' export const F_LONG_DASH = '⎯' export const F_RIGHT_TRI = '▶' +export const F_TODO = '□' export const F_TREE_NODE_MIDDLE = '├──' export const F_TREE_NODE_END = '└──' diff --git a/packages/vitest/src/node/reporters/renderers/utils.ts b/packages/vitest/src/node/reporters/renderers/utils.ts index 75fd91658b8b..f98edd3150e2 100644 --- a/packages/vitest/src/node/reporters/renderers/utils.ts +++ b/packages/vitest/src/node/reporters/renderers/utils.ts @@ -14,10 +14,12 @@ import { F_DOWN_RIGHT, F_LONG_DASH, F_POINTER, + F_TODO, } from './figures' export const pointer: string = c.yellow(F_POINTER) export const skipped: string = c.dim(c.gray(F_DOWN)) +export const todo: string = c.dim(c.gray(F_TODO)) export const benchmarkPass: string = c.green(F_DOT) export const testPass: string = c.green(F_CHECK) export const taskFail: string = c.red(F_CROSS) @@ -181,7 +183,11 @@ export function getStateString( } export function getStateSymbol(task: Task): string { - if (task.mode === 'skip' || task.mode === 'todo') { + if (task.mode === 'todo') { + return todo + } + + if (task.mode === 'skip') { return skipped } diff --git a/packages/vitest/src/node/reporters/summary.ts b/packages/vitest/src/node/reporters/summary.ts index 736d7812e6ae..26a236cd16ec 100644 --- a/packages/vitest/src/node/reporters/summary.ts +++ b/packages/vitest/src/node/reporters/summary.ts @@ -221,7 +221,12 @@ export class SummaryReporter implements Reporter { this.tests.failed++ } else if (!result?.state || result?.state === 'skipped') { - this.tests.skipped++ + if (test.options.mode === 'todo') { + this.tests.todo++ + } + else { + this.tests.skipped++ + } } this.renderer.schedule() diff --git a/packages/vitest/src/node/reporters/verbose.ts b/packages/vitest/src/node/reporters/verbose.ts index 7686a8d4a150..36a62f86271b 100644 --- a/packages/vitest/src/node/reporters/verbose.ts +++ b/packages/vitest/src/node/reporters/verbose.ts @@ -18,7 +18,7 @@ export class VerboseReporter extends DefaultReporter { const testResult = test.result() - if (this.ctx.config.hideSkippedTests && testResult.state === 'skipped') { + if (this.ctx.config.hideSkippedTests && testResult.state === 'skipped' && test.options.mode !== 'todo') { return } diff --git a/test/cli/fixtures/reporters/pass-and-skip-test-suites.test.ts b/test/cli/fixtures/reporters/pass-and-skip-test-suites.test.ts index 9ba94f2505a7..0bf9d7bab7a0 100644 --- a/test/cli/fixtures/reporters/pass-and-skip-test-suites.test.ts +++ b/test/cli/fixtures/reporters/pass-and-skip-test-suites.test.ts @@ -1,4 +1,4 @@ -import { expect, describe, test } from 'vitest' +import { describe, test } from 'vitest' test('passing test #1', () => {}) @@ -8,6 +8,8 @@ describe("passing suite", () => { test.skip('skipped test #1', () => {}) +test.todo('todo test #1') + describe.skip("skipped suite", () => { test('skipped test #2', () => {}) }) \ No newline at end of file diff --git a/test/cli/test/reporters/default.test.ts b/test/cli/test/reporters/default.test.ts index 7478a48e45b3..8fa8c1088a04 100644 --- a/test/cli/test/reporters/default.test.ts +++ b/test/cli/test/reporters/default.test.ts @@ -164,11 +164,12 @@ describe('default reporter', async () => { }) expect(trimReporterOutput(stdout)).toMatchInlineSnapshot(` - "✓ fixtures/reporters/pass-and-skip-test-suites.test.ts (4 tests | 2 skipped) [...]ms + "✓ fixtures/reporters/pass-and-skip-test-suites.test.ts (5 tests | 2 skipped | 1 todo) [...]ms ✓ passing test #1 [...]ms ✓ passing suite (1) ✓ passing test #2 [...]ms ↓ skipped test #1 + □ todo test #1 ↓ skipped suite (1) ↓ skipped test #2" `) @@ -183,10 +184,11 @@ describe('default reporter', async () => { }) expect(trimReporterOutput(stdout)).toMatchInlineSnapshot(` - "✓ fixtures/reporters/pass-and-skip-test-suites.test.ts (4 tests | 2 skipped) [...]ms + "✓ fixtures/reporters/pass-and-skip-test-suites.test.ts (5 tests | 2 skipped | 1 todo) [...]ms ✓ passing test #1 [...]ms ✓ passing suite (1) - ✓ passing test #2 [...]ms" + ✓ passing test #2 [...]ms + □ todo test #1" `) }) diff --git a/test/cli/test/reporters/verbose.test.ts b/test/cli/test/reporters/verbose.test.ts index 1ff31fa2df3e..0f15a9930702 100644 --- a/test/cli/test/reporters/verbose.test.ts +++ b/test/cli/test/reporters/verbose.test.ts @@ -36,6 +36,7 @@ test('prints skipped tests by default', async () => { "✓ fixtures/reporters/pass-and-skip-test-suites.test.ts > passing test #1 [...]ms ✓ fixtures/reporters/pass-and-skip-test-suites.test.ts > passing suite > passing test #2 [...]ms ↓ fixtures/reporters/pass-and-skip-test-suites.test.ts > skipped test #1 + □ fixtures/reporters/pass-and-skip-test-suites.test.ts > todo test #1 ↓ fixtures/reporters/pass-and-skip-test-suites.test.ts > skipped suite > skipped test #2" `) }) @@ -50,7 +51,8 @@ test('hides skipped tests when --hideSkippedTests', async () => { expect(trimReporterOutput(stdout)).toMatchInlineSnapshot(` "✓ fixtures/reporters/pass-and-skip-test-suites.test.ts > passing test #1 [...]ms - ✓ fixtures/reporters/pass-and-skip-test-suites.test.ts > passing suite > passing test #2 [...]ms" + ✓ fixtures/reporters/pass-and-skip-test-suites.test.ts > passing suite > passing test #2 [...]ms + □ fixtures/reporters/pass-and-skip-test-suites.test.ts > todo test #1" `) })