diff --git a/docs/src/test-api/class-fullconfig.md b/docs/src/test-api/class-fullconfig.md index 923c9fa858..848e42a926 100644 --- a/docs/src/test-api/class-fullconfig.md +++ b/docs/src/test-api/class-fullconfig.md @@ -10,6 +10,12 @@ Resolved configuration which is accessible via [`property: TestInfo.config`] and Path to the configuration file used to run the tests. The value is an empty string if no config file was used. +## property: FullConfig.failOnFlakyTests +* since: v1.51 +- type: <[boolean]> + +See [`property: TestConfig.failOnFlakyTests`]. + ## property: FullConfig.forbidOnly * since: v1.10 - type: <[boolean]> diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index 213928e2e7..536eb0f55e 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -76,6 +76,24 @@ export default defineConfig({ }); ``` +## property: TestConfig.failOnFlakyTests +* since: v1.51 +- type: ?<[boolean]> + +Whether to exit with an error if any tests are marked as flaky. Useful on CI. + +Also available in the [command line](../test-cli.md) with the `--fail-on-flaky-tests` option. + +**Usage** + +```js title="playwright.config.ts" +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + failOnFlakyTests: process.env.CI ? true : false, +}); +``` + ## property: TestConfig.forbidOnly * since: v1.10 - type: ?<[boolean]> diff --git a/packages/playwright/src/common/config.ts b/packages/playwright/src/common/config.ts index 4ebc900872..b4cf59ebe9 100644 --- a/packages/playwright/src/common/config.ts +++ b/packages/playwright/src/common/config.ts @@ -55,7 +55,6 @@ export class FullConfigInternal { cliProjectFilter?: string[]; cliListOnly = false; cliPassWithNoTests?: boolean; - cliFailOnFlakyTests?: boolean; cliLastFailed?: boolean; testIdMatcher?: Matcher; lastFailedTestIdMatcher?: Matcher; @@ -88,6 +87,7 @@ export class FullConfigInternal { this.config = { configFile: resolvedConfigFile, rootDir: pathResolve(configDir, userConfig.testDir) || configDir, + failOnFlakyTests: takeFirst(configCLIOverrides.failOnFlakyTests, userConfig.failOnFlakyTests, false), forbidOnly: takeFirst(configCLIOverrides.forbidOnly, userConfig.forbidOnly, false), fullyParallel: takeFirst(configCLIOverrides.fullyParallel, userConfig.fullyParallel, false), globalSetup: this.globalSetups[0] ?? null, diff --git a/packages/playwright/src/common/ipc.ts b/packages/playwright/src/common/ipc.ts index d1cebd5cb4..10821f931e 100644 --- a/packages/playwright/src/common/ipc.ts +++ b/packages/playwright/src/common/ipc.ts @@ -25,6 +25,7 @@ import type { SerializedCompilationCache } from '../transform/compilationCache' export type ConfigCLIOverrides = { debug?: boolean; + failOnFlakyTests?: boolean; forbidOnly?: boolean; fullyParallel?: boolean; globalTimeout?: number; diff --git a/packages/playwright/src/isomorphic/teleReceiver.ts b/packages/playwright/src/isomorphic/teleReceiver.ts index b9aa5edba9..6ac930644f 100644 --- a/packages/playwright/src/isomorphic/teleReceiver.ts +++ b/packages/playwright/src/isomorphic/teleReceiver.ts @@ -593,6 +593,7 @@ export class TeleTestResult implements reporterTypes.TestResult { export type TeleFullProject = reporterTypes.FullProject; export const baseFullConfig: reporterTypes.FullConfig = { + failOnFlakyTests: false, forbidOnly: false, fullyParallel: false, globalSetup: null, diff --git a/packages/playwright/src/runner/failureTracker.ts b/packages/playwright/src/runner/failureTracker.ts index ac62677571..afc4548a66 100644 --- a/packages/playwright/src/runner/failureTracker.ts +++ b/packages/playwright/src/runner/failureTracker.ts @@ -49,7 +49,7 @@ export class FailureTracker { } result(): 'failed' | 'passed' { - return this._hasWorkerErrors || this.hasReachedMaxFailures() || this.hasFailedTests() || (this._config.cliFailOnFlakyTests && this.hasFlakyTests()) ? 'failed' : 'passed'; + return this._hasWorkerErrors || this.hasReachedMaxFailures() || this.hasFailedTests() || (this._config.config.failOnFlakyTests && this.hasFlakyTests()) ? 'failed' : 'passed'; } hasFailedTests() { @@ -63,4 +63,5 @@ export class FailureTracker { maxFailures() { return this._config.config.maxFailures; } + } diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 1e9fef6b6c..4b03c3bf37 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -1096,6 +1096,25 @@ interface TestConfig { }; }; + /** + * Whether to exit with an error if any tests are marked as flaky. Useful on CI. + * + * Also available in the [command line](https://playwright.dev/docs/test-cli) with the `--fail-on-flaky-tests` option. + * + * **Usage** + * + * ```js + * // playwright.config.ts + * import { defineConfig } from '@playwright/test'; + * + * export default defineConfig({ + * failOnFlakyTests: process.env.CI ? true : false, + * }); + * ``` + * + */ + failOnFlakyTests?: boolean; + /** * Whether to exit with an error if any tests or groups are marked as * [test.only(title[, details, body])](https://playwright.dev/docs/api/class-test#test-only) or @@ -1835,6 +1854,12 @@ export interface FullConfig { */ configFile?: string; + /** + * See + * [testConfig.failOnFlakyTests](https://playwright.dev/docs/api/class-testconfig#test-config-fail-on-flaky-tests). + */ + failOnFlakyTests: boolean; + /** * See [testConfig.forbidOnly](https://playwright.dev/docs/api/class-testconfig#test-config-forbid-only). */ diff --git a/tests/playwright-test/config.spec.ts b/tests/playwright-test/config.spec.ts index 77e5fb23f6..30945fdc1e 100644 --- a/tests/playwright-test/config.spec.ts +++ b/tests/playwright-test/config.spec.ts @@ -72,6 +72,25 @@ test('should prioritize command line timeout over project timeout', async ({ run expect(result.output).toContain('Test timeout of 500ms exceeded.'); }); +test('should support failOnFlakyTests config option', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { + failOnFlakyTests: true, + retries: 1 + }; + `, + 'a.test.js': ` + import { test, expect } from '@playwright/test'; + test('flake', async ({}, testInfo) => { + expect(testInfo.retry).toBe(1); + }); + `, + }, { 'retries': 1 }); + expect(result.exitCode).not.toBe(0); + expect(result.flaky).toBe(1); +}); + test('should read config from --config, resolve relative testDir', async ({ runInlineTest }) => { const result = await runInlineTest({ 'my.config.ts': `