[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

feat(vitest): allow calling `skip` dynamically (#3966)

authored by

Vladimir and committed by
GitHub
(Aug 16, 2023, 3:41 PM +0200) 5c88d8e5 3073b9a2

+139 -2
+12
docs/api/index.md
··· 102 102 }) 103 103 ``` 104 104 105 + You can also skip test by calling `skip` on its [context](/guide/test-context) dynamically: 106 + 107 + ```ts 108 + import { assert, test } from 'vitest' 109 + 110 + test('skipped test', (context) => { 111 + context.skip() 112 + // Test skipped, no error 113 + assert.equal(Math.sqrt(4), 3) 114 + }) 115 + ``` 116 + 105 117 ### test.skipIf 106 118 107 119 - **Type:** `(condition: any) => Test`
+36 -1
docs/guide/test-context.md
··· 27 27 28 28 #### `context.expect` 29 29 30 - The `expect` API bound to the current test. 30 + The `expect` API bound to the current test: 31 + 32 + ```ts 33 + import { it } from 'vitest' 34 + 35 + it('math is easy', ({ expect }) => { 36 + expect(2 + 2).toBe(4) 37 + }) 38 + ``` 39 + 40 + This API is useful for running snapshot tests concurrently because global expect cannot track them: 41 + 42 + ```ts 43 + import { it } from 'vitest' 44 + 45 + it.concurrent('math is easy', ({ expect }) => { 46 + expect(2 + 2).toMatchInlineSnapshot() 47 + }) 48 + 49 + it.concurrent('math is hard', ({ expect }) => { 50 + expect(2 * 2).toMatchInlineSnapshot() 51 + }) 52 + ``` 53 + 54 + #### `context.skip` 55 + 56 + Skips subsequent test execution and marks test as skipped: 57 + 58 + ```ts 59 + import { expect, it } from 'vitest' 60 + 61 + it('math is hard', ({ skip }) => { 62 + skip() 63 + expect(2 + 2).toBe(5) 64 + }) 65 + ``` 31 66 32 67 ## Extend Test Context 33 68
+6
packages/runner/src/context.ts
··· 2 2 import { getSafeTimers } from '@vitest/utils' 3 3 import type { RuntimeContext, SuiteCollector, Test, TestContext } from './types' 4 4 import type { VitestRunner } from './types/runner' 5 + import { PendingError } from './errors' 5 6 6 7 export const collectorContext: RuntimeContext = { 7 8 tasks: [], ··· 48 49 49 50 context.meta = test 50 51 context.task = test 52 + 53 + context.skip = () => { 54 + test.pending = true 55 + throw new PendingError('test is skipped; abort execution', test) 56 + } 51 57 52 58 context.onTestFailed = (fn) => { 53 59 test.onFailed ||= []
+11
packages/runner/src/errors.ts
··· 1 + import type { TaskBase } from './types' 2 + 3 + export class PendingError extends Error { 4 + public code = 'VITEST_PENDING' 5 + public taskId: string 6 + 7 + constructor(public message: string, task: TaskBase) { 8 + super(message) 9 + this.taskId = task.id 10 + } 11 + }
+14
packages/runner/src/run.ts
··· 8 8 import { collectTests } from './collect' 9 9 import { setCurrentTest } from './test-state' 10 10 import { hasFailed, hasTests } from './utils/tasks' 11 + import { PendingError } from './errors' 11 12 12 13 const now = Date.now 13 14 ··· 175 176 failTask(test.result, e) 176 177 } 177 178 179 + // skipped with new PendingError 180 + if (test.pending || test.result?.state === 'skip') { 181 + test.mode = 'skip' 182 + test.result = { state: 'skip' } 183 + updateTask(test, runner) 184 + return 185 + } 186 + 178 187 try { 179 188 await callSuiteHook(test.suite, test, 'afterEach', runner, [test.context, test.suite]) 180 189 await callCleanupHooks(beforeEachCleanups) ··· 225 234 } 226 235 227 236 function failTask(result: TaskResult, err: unknown) { 237 + if (err instanceof PendingError) { 238 + result.state = 'skip' 239 + return 240 + } 241 + 228 242 result.state = 'fail' 229 243 const errors = Array.isArray(err) 230 244 ? err
+1 -1
packages/utils/src/helpers.ts
··· 147 147 return result 148 148 } 149 149 150 - type DeferPromise<T> = Promise<T> & { 150 + export type DeferPromise<T> = Promise<T> & { 151 151 resolve: (value: T | PromiseLike<T>) => void 152 152 reject: (reason?: any) => void 153 153 }
+39
test/core/test/skip.test.ts
··· 1 + import EventEmitter from 'node:events' 2 + import { expect, it } from 'vitest' 3 + 4 + const sleep = (ms?: number) => new Promise(resolve => setTimeout(resolve, ms)) 5 + 6 + it('correctly skips sync tests', ({ skip }) => { 7 + skip() 8 + expect(1).toBe(2) 9 + }) 10 + 11 + it('correctly skips async tests with skip before async', async ({ skip }) => { 12 + await sleep(100) 13 + skip() 14 + expect(1).toBe(2) 15 + }) 16 + 17 + it('correctly skips async tests with async after skip', async ({ skip }) => { 18 + skip() 19 + await sleep(100) 20 + expect(1).toBe(2) 21 + }) 22 + 23 + it('correctly skips tests with callback', ({ skip }) => { 24 + const emitter = new EventEmitter() 25 + emitter.on('test', () => { 26 + skip() 27 + }) 28 + emitter.emit('test') 29 + expect(1).toBe(2) 30 + }) 31 + 32 + it('correctly skips tests with async callback', ({ skip }) => { 33 + const emitter = new EventEmitter() 34 + emitter.on('test', async () => { 35 + skip() 36 + }) 37 + emitter.emit('test') 38 + expect(1).toBe(2) 39 + })
+6
packages/runner/src/types/tasks.ts
··· 59 59 export interface Test<ExtraContext = {}> extends TaskBase { 60 60 type: 'test' 61 61 suite: Suite 62 + pending?: boolean 62 63 result?: TaskResult 63 64 fails?: boolean 64 65 context: TestContext & ExtraContext ··· 262 263 * Extract hooks on test failed 263 264 */ 264 265 onTestFailed: (fn: OnTestFailedHandler) => void 266 + 267 + /** 268 + * Mark tests as skipped. All execution after this call will be skipped. 269 + */ 270 + skip: () => void 265 271 } 266 272 267 273 export type OnTestFailedHandler = (result: TaskResult) => Awaitable<void>
+14
packages/vitest/src/node/state.ts
··· 32 32 else 33 33 err = { type, message: err } 34 34 35 + const _err = err as Record<string, any> 36 + if (_err && typeof _err === 'object' && _err.code === 'VITEST_PENDING') { 37 + const task = this.idMap.get(_err.taskId) 38 + if (task) { 39 + task.mode = 'skip' 40 + task.result ??= { state: 'skip' } 41 + task.result.state = 'skip' 42 + } 43 + return 44 + } 45 + 35 46 this.errorsSet.add(err) 36 47 } 37 48 ··· 119 130 if (task) { 120 131 task.result = result 121 132 task.meta = meta 133 + // skipped with new PendingError 134 + if (result?.state === 'skip') 135 + task.mode = 'skip' 122 136 } 123 137 } 124 138 }