feat: allow a custom note when calling ctx.skip() dynamically (#6805)

This commit is contained in:
Vladimir 2024-11-13 17:03:12 +01:00 committed by GitHub
parent 8d179afcc7
commit 697c35c52d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 71 additions and 11 deletions

View File

@ -67,9 +67,9 @@ export function createTestContext<T extends Test | Custom>(
context.task = test
context.skip = () => {
context.skip = (note?: string) => {
test.pending = true
throw new PendingError('test is skipped; abort execution', test)
throw new PendingError('test is skipped; abort execution', test, note)
}
context.onTestFailed = (fn) => {

View File

@ -4,7 +4,7 @@ export class PendingError extends Error {
public code = 'VITEST_PENDING'
public taskId: string
constructor(public message: string, task: TaskBase) {
constructor(public message: string, task: TaskBase, public note: string | undefined) {
super(message)
this.taskId = task.id
}

View File

@ -257,7 +257,7 @@ export async function runTest(test: Test | Custom, runner: VitestRunner): Promis
// skipped with new PendingError
if (test.pending || test.result?.state === 'skip') {
test.mode = 'skip'
test.result = { state: 'skip' }
test.result = { state: 'skip', note: test.result?.note }
updateTask(test, runner)
setCurrentTest(undefined)
return
@ -336,6 +336,7 @@ export async function runTest(test: Test | Custom, runner: VitestRunner): Promis
function failTask(result: TaskResult, err: unknown, diffOptions: DiffOptions | undefined) {
if (err instanceof PendingError) {
result.state = 'skip'
result.note = err.note
return
}

View File

@ -147,6 +147,8 @@ export interface TaskResult {
* `repeats` option is set. This number also contains `retryCount`.
*/
repeatCount?: number
/** @private */
note?: string
}
/**
@ -611,7 +613,7 @@ export interface TaskContext<Task extends Custom | Test = Custom | Test> {
* Mark tests as skipped. All execution after this call will be skipped.
* This function throws an error, so make sure you are not catching it accidentally.
*/
skip: () => void
skip: (note?: string) => void
}
export type ExtendedContext<T extends Custom | Test> = TaskContext<T> &

View File

@ -144,7 +144,8 @@ function renderTree(
}
if (task.mode === 'skip' || task.mode === 'todo') {
suffix += ` ${c.dim(c.gray('[skipped]'))}`
const note = task.result?.note || 'skipped'
suffix += ` ${c.dim(c.gray(`[${note}]`))}`
}
if (

View File

@ -119,14 +119,27 @@ export class TestCase extends ReportedTaskImplementation {
return undefined
}
const state = result.state === 'fail'
? 'failed'
? 'failed' as const
: result.state === 'pass'
? 'passed'
: 'skipped'
? 'passed' as const
: 'skipped' as const
if (state === 'skipped') {
return {
state,
note: result.note,
errors: undefined,
} satisfies TestResultSkipped
}
if (state === 'passed') {
return {
state,
errors: result.errors as TestError[] | undefined,
} satisfies TestResultPassed
}
return {
state,
errors: result.errors as TestError[] | undefined,
} as TestResult
errors: (result.errors || []) as TestError[],
} satisfies TestResultFailed
}
/**
@ -441,6 +454,10 @@ export interface TestResultSkipped {
* Skipped tests have no errors.
*/
errors: undefined
/**
* A custom note.
*/
note: string | undefined
}
export interface TestDiagnostic {

View File

@ -43,6 +43,9 @@ export class VerboseReporter extends DefaultReporter {
` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`,
)
}
if (task.result?.note) {
title += c.dim(c.gray(` [${task.result.note}]`))
}
this.ctx.logger.log(title)
if (task.result.state === 'fail') {
task.result.errors?.forEach((error) => {

View File

@ -40,6 +40,7 @@ export class StateManager {
task.mode = 'skip'
task.result ??= { state: 'skip' }
task.result.state = 'skip'
task.result.note = _err.note
}
return
}

View File

@ -0,0 +1,5 @@
import { test } from 'vitest';
test('my skipped test', ctx => {
ctx.skip('custom message')
})

View File

@ -0,0 +1,30 @@
import type { TestCase } from 'vitest/node'
import { resolve } from 'node:path'
import { expect, test } from 'vitest'
import { runVitest } from '../../test-utils'
const root = resolve(import.meta.dirname, '../fixtures/skip-note')
test.for([
{ reporter: 'default', isTTY: true },
{ reporter: 'verbose', isTTY: false },
])('can leave a note when skipping in the $reporter reporter', async ({ reporter, isTTY }) => {
const { ctx, stdout, stderr } = await runVitest({
root,
reporters: [
[reporter, { isTTY }],
],
})
expect(stderr).toBe('')
expect(stdout).toContain('my skipped test [custom message]')
expect(ctx).toBeDefined()
const testTask = ctx!.state.getFiles()[0].tasks[0]
const test = ctx!.state.getReportedEntity(testTask) as TestCase
const result = test.result()
expect(result).toEqual({
state: 'skipped',
note: 'custom message',
})
})