Skip to content

Commit 697c35c

Browse files
authored
feat: allow a custom note when calling ctx.skip() dynamically (#6805)
1 parent 8d179af commit 697c35c

File tree

10 files changed

+71
-11
lines changed

10 files changed

+71
-11
lines changed

packages/runner/src/context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ export function createTestContext<T extends Test | Custom>(
6767

6868
context.task = test
6969

70-
context.skip = () => {
70+
context.skip = (note?: string) => {
7171
test.pending = true
72-
throw new PendingError('test is skipped; abort execution', test)
72+
throw new PendingError('test is skipped; abort execution', test, note)
7373
}
7474

7575
context.onTestFailed = (fn) => {

packages/runner/src/errors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export class PendingError extends Error {
44
public code = 'VITEST_PENDING'
55
public taskId: string
66

7-
constructor(public message: string, task: TaskBase) {
7+
constructor(public message: string, task: TaskBase, public note: string | undefined) {
88
super(message)
99
this.taskId = task.id
1010
}

packages/runner/src/run.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ export async function runTest(test: Test | Custom, runner: VitestRunner): Promis
257257
// skipped with new PendingError
258258
if (test.pending || test.result?.state === 'skip') {
259259
test.mode = 'skip'
260-
test.result = { state: 'skip' }
260+
test.result = { state: 'skip', note: test.result?.note }
261261
updateTask(test, runner)
262262
setCurrentTest(undefined)
263263
return
@@ -336,6 +336,7 @@ export async function runTest(test: Test | Custom, runner: VitestRunner): Promis
336336
function failTask(result: TaskResult, err: unknown, diffOptions: DiffOptions | undefined) {
337337
if (err instanceof PendingError) {
338338
result.state = 'skip'
339+
result.note = err.note
339340
return
340341
}
341342

packages/runner/src/types/tasks.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ export interface TaskResult {
147147
* `repeats` option is set. This number also contains `retryCount`.
148148
*/
149149
repeatCount?: number
150+
/** @private */
151+
note?: string
150152
}
151153

152154
/**
@@ -611,7 +613,7 @@ export interface TaskContext<Task extends Custom | Test = Custom | Test> {
611613
* Mark tests as skipped. All execution after this call will be skipped.
612614
* This function throws an error, so make sure you are not catching it accidentally.
613615
*/
614-
skip: () => void
616+
skip: (note?: string) => void
615617
}
616618

617619
export type ExtendedContext<T extends Custom | Test> = TaskContext<T> &

packages/vitest/src/node/reporters/renderers/listRenderer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ function renderTree(
144144
}
145145

146146
if (task.mode === 'skip' || task.mode === 'todo') {
147-
suffix += ` ${c.dim(c.gray('[skipped]'))}`
147+
const note = task.result?.note || 'skipped'
148+
suffix += ` ${c.dim(c.gray(`[${note}]`))}`
148149
}
149150

150151
if (

packages/vitest/src/node/reporters/reported-tasks.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,27 @@ export class TestCase extends ReportedTaskImplementation {
119119
return undefined
120120
}
121121
const state = result.state === 'fail'
122-
? 'failed'
122+
? 'failed' as const
123123
: result.state === 'pass'
124-
? 'passed'
125-
: 'skipped'
124+
? 'passed' as const
125+
: 'skipped' as const
126+
if (state === 'skipped') {
127+
return {
128+
state,
129+
note: result.note,
130+
errors: undefined,
131+
} satisfies TestResultSkipped
132+
}
133+
if (state === 'passed') {
134+
return {
135+
state,
136+
errors: result.errors as TestError[] | undefined,
137+
} satisfies TestResultPassed
138+
}
126139
return {
127140
state,
128-
errors: result.errors as TestError[] | undefined,
129-
} as TestResult
141+
errors: (result.errors || []) as TestError[],
142+
} satisfies TestResultFailed
130143
}
131144

132145
/**
@@ -441,6 +454,10 @@ export interface TestResultSkipped {
441454
* Skipped tests have no errors.
442455
*/
443456
errors: undefined
457+
/**
458+
* A custom note.
459+
*/
460+
note: string | undefined
444461
}
445462

446463
export interface TestDiagnostic {

packages/vitest/src/node/reporters/verbose.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ export class VerboseReporter extends DefaultReporter {
4343
` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`,
4444
)
4545
}
46+
if (task.result?.note) {
47+
title += c.dim(c.gray(` [${task.result.note}]`))
48+
}
4649
this.ctx.logger.log(title)
4750
if (task.result.state === 'fail') {
4851
task.result.errors?.forEach((error) => {

packages/vitest/src/node/state.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export class StateManager {
4040
task.mode = 'skip'
4141
task.result ??= { state: 'skip' }
4242
task.result.state = 'skip'
43+
task.result.note = _err.note
4344
}
4445
return
4546
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { test } from 'vitest';
2+
3+
test('my skipped test', ctx => {
4+
ctx.skip('custom message')
5+
})

test/cli/test/skip-note.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { TestCase } from 'vitest/node'
2+
import { resolve } from 'node:path'
3+
import { expect, test } from 'vitest'
4+
import { runVitest } from '../../test-utils'
5+
6+
const root = resolve(import.meta.dirname, '../fixtures/skip-note')
7+
8+
test.for([
9+
{ reporter: 'default', isTTY: true },
10+
{ reporter: 'verbose', isTTY: false },
11+
])('can leave a note when skipping in the $reporter reporter', async ({ reporter, isTTY }) => {
12+
const { ctx, stdout, stderr } = await runVitest({
13+
root,
14+
reporters: [
15+
[reporter, { isTTY }],
16+
],
17+
})
18+
19+
expect(stderr).toBe('')
20+
expect(stdout).toContain('my skipped test [custom message]')
21+
22+
expect(ctx).toBeDefined()
23+
const testTask = ctx!.state.getFiles()[0].tasks[0]
24+
const test = ctx!.state.getReportedEntity(testTask) as TestCase
25+
const result = test.result()
26+
expect(result).toEqual({
27+
state: 'skipped',
28+
note: 'custom message',
29+
})
30+
})

0 commit comments

Comments
 (0)