diff --git a/packages/browser/src/client/public/esm-client-injector.js b/packages/browser/src/client/public/esm-client-injector.js index def79a213264..051d394d3a95 100644 --- a/packages/browser/src/client/public/esm-client-injector.js +++ b/packages/browser/src/client/public/esm-client-injector.js @@ -50,6 +50,9 @@ if (config.testNamePattern) config.testNamePattern = parseRegexp(config.testNamePattern); + if (config.retry?.condition) + config.retry.condition = parseRegexp(config.retry.condition); + function parseRegexp(input) { // Parse input const m = input.match(/(\/?)(.+)\1([a-z]*)/i); diff --git a/packages/browser/src/node/project.ts b/packages/browser/src/node/project.ts index bbc6f32860b0..77bd7878ad82 100644 --- a/packages/browser/src/node/project.ts +++ b/packages/browser/src/node/project.ts @@ -129,11 +129,15 @@ export class ProjectBrowser implements IProjectBrowser { } function wrapConfig(config: SerializedConfig): SerializedConfig { - return { - ...config, - // workaround RegExp serialization - testNamePattern: config.testNamePattern - ? (config.testNamePattern.toString() as any as RegExp) - : undefined, + config = { ...config } + + // workaround RegExp serialization + config.testNamePattern &&= config.testNamePattern.toString() as any as RegExp + + // workaround RegExp serialization + if (typeof config.retry === 'object') { + config.retry.condition &&= config.retry.condition.toString() as any as RegExp } + + return config } diff --git a/test/cli/test/retry-condition.test.ts b/test/cli/test/retry-condition.test.ts new file mode 100644 index 000000000000..54b1b93865ae --- /dev/null +++ b/test/cli/test/retry-condition.test.ts @@ -0,0 +1,105 @@ +import type { RunVitestConfig } from '#test-utils' +import { runInlineTests } from '#test-utils' +import { playwright } from '@vitest/browser-playwright' +import { expect, it } from 'vitest' + +function modeToConfig(mode: string): RunVitestConfig { + if (mode === 'playwright') { + return { + browser: { + enabled: true, + headless: true, + screenshotFailures: false, + provider: playwright(), + instances: [{ browser: 'chromium' }], + }, + } + } + return {} +} + +it.for(['node', 'playwright'])('test.retry.condition is corrrectly serialized %s', async (mode) => { + const { stderr, errorTree } = await runInlineTests({ + 'basic.test.js': /* js */` + import { expect, test } from 'vitest' + + test('task.retry.condition is corrrectly deserialized', ({ task }) => { + expect(task.retry.condition).toBeInstanceOf(RegExp) + expect(task.retry.condition).toStrictEqual(/retry_this/) + }) + + let trial = 0; + test('retry', () => { + trial++ + if (trial === 1) { + throw new Error('retry_this') + } + }) + + let trial2 = 0; + test('not retry', () => { + trial2++ + if (trial2 === 1) { + throw new Error('retry_that') + } + }) + `, + }, { + ...modeToConfig(mode), + retry: { + count: 1, + condition: /retry_this/, + }, + }) + if (mode === 'playwright') { + expect(stderr).toMatchInlineSnapshot(` + " + ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL |chromium| basic.test.js > not retry + Error: retry_that + ❯ basic.test.js:21:17 + 19| trial2++ + 20| if (trial2 === 1) { + 21| throw new Error('retry_that') + | ^ + 22| } + 23| }) + + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ + + " + `) + } + else { + expect(stderr).toMatchInlineSnapshot(` + " + ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL basic.test.js > not retry + Error: retry_that + ❯ basic.test.js:21:17 + 19| trial2++ + 20| if (trial2 === 1) { + 21| throw new Error('retry_that') + | ^ + 22| } + 23| }) + + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ + + " + `) + } + expect(errorTree()).toMatchInlineSnapshot(` + { + "basic.test.js": { + "not retry": [ + "retry_that", + ], + "retry": "passed", + "task.retry.condition is corrrectly deserialized": "passed", + }, + } + `) +})