|
1 | | -import type { Awaitable } from '@vitest/utils' |
| 1 | +import type { Awaitable, TestError } from '@vitest/utils' |
2 | 2 | import type { DiffOptions } from '@vitest/utils/diff' |
3 | 3 | import type { FileSpecification, VitestRunner } from './types/runner' |
4 | 4 | import type { |
@@ -34,6 +34,42 @@ const now = globalThis.performance ? globalThis.performance.now.bind(globalThis. |
34 | 34 | const unixNow = Date.now |
35 | 35 | const { clearTimeout, setTimeout } = getSafeTimers() |
36 | 36 |
|
| 37 | +/** |
| 38 | + * Normalizes retry configuration to extract individual values. |
| 39 | + * Handles both number and object forms. |
| 40 | + */ |
| 41 | +function getRetryCount(retry: number | { count?: number } | undefined): number { |
| 42 | + if (retry === undefined) { |
| 43 | + return 0 |
| 44 | + } |
| 45 | + if (typeof retry === 'number') { |
| 46 | + return retry |
| 47 | + } |
| 48 | + return retry.count ?? 0 |
| 49 | +} |
| 50 | + |
| 51 | +function getRetryDelay(retry: number | { delay?: number } | undefined): number { |
| 52 | + if (retry === undefined) { |
| 53 | + return 0 |
| 54 | + } |
| 55 | + if (typeof retry === 'number') { |
| 56 | + return 0 |
| 57 | + } |
| 58 | + return retry.delay ?? 0 |
| 59 | +} |
| 60 | + |
| 61 | +function getRetryCondition( |
| 62 | + retry: number | { condition?: RegExp | ((error: TestError) => boolean) } | undefined, |
| 63 | +): RegExp | ((error: TestError) => boolean) | undefined { |
| 64 | + if (retry === undefined) { |
| 65 | + return undefined |
| 66 | + } |
| 67 | + if (typeof retry === 'number') { |
| 68 | + return undefined |
| 69 | + } |
| 70 | + return retry.condition |
| 71 | +} |
| 72 | + |
37 | 73 | function updateSuiteHookState( |
38 | 74 | task: Task, |
39 | 75 | name: keyof SuiteHooks, |
@@ -266,6 +302,32 @@ async function callCleanupHooks(runner: VitestRunner, cleanups: unknown[]) { |
266 | 302 | } |
267 | 303 | } |
268 | 304 |
|
| 305 | +/** |
| 306 | + * Determines if a test should be retried based on its retryCondition configuration |
| 307 | + */ |
| 308 | +function passesRetryCondition(test: Test, errors: TestError[] | undefined): boolean { |
| 309 | + const condition = getRetryCondition(test.retry) |
| 310 | + |
| 311 | + if (!errors || errors.length === 0) { |
| 312 | + return false |
| 313 | + } |
| 314 | + |
| 315 | + if (!condition) { |
| 316 | + return true |
| 317 | + } |
| 318 | + |
| 319 | + const error = errors[errors.length - 1] |
| 320 | + |
| 321 | + if (condition instanceof RegExp) { |
| 322 | + return condition.test(error.message || '') |
| 323 | + } |
| 324 | + else if (typeof condition === 'function') { |
| 325 | + return condition(error) |
| 326 | + } |
| 327 | + |
| 328 | + return false |
| 329 | +} |
| 330 | + |
269 | 331 | export async function runTest(test: Test, runner: VitestRunner): Promise<void> { |
270 | 332 | await runner.onBeforeRunTask?.(test) |
271 | 333 |
|
@@ -300,7 +362,7 @@ export async function runTest(test: Test, runner: VitestRunner): Promise<void> { |
300 | 362 |
|
301 | 363 | const repeats = test.repeats ?? 0 |
302 | 364 | for (let repeatCount = 0; repeatCount <= repeats; repeatCount++) { |
303 | | - const retry = test.retry ?? 0 |
| 365 | + const retry = getRetryCount(test.retry) |
304 | 366 | for (let retryCount = 0; retryCount <= retry; retryCount++) { |
305 | 367 | let beforeEachCleanups: unknown[] = [] |
306 | 368 | try { |
@@ -412,9 +474,19 @@ export async function runTest(test: Test, runner: VitestRunner): Promise<void> { |
412 | 474 | } |
413 | 475 |
|
414 | 476 | if (retryCount < retry) { |
415 | | - // reset state when retry test |
| 477 | + const shouldRetry = passesRetryCondition(test, test.result.errors) |
| 478 | + |
| 479 | + if (!shouldRetry) { |
| 480 | + break |
| 481 | + } |
| 482 | + |
416 | 483 | test.result.state = 'run' |
417 | 484 | test.result.retryCount = (test.result.retryCount ?? 0) + 1 |
| 485 | + |
| 486 | + const delay = getRetryDelay(test.retry) |
| 487 | + if (delay > 0) { |
| 488 | + await new Promise(resolve => setTimeout(resolve, delay)) |
| 489 | + } |
418 | 490 | } |
419 | 491 |
|
420 | 492 | // update retry info |
|
0 commit comments