feat(config): failOnFlakyTests option (#35109)

This commit is contained in:
Jean-François Greffier 2025-03-11 18:06:20 +01:00 committed by GitHub
parent 8f5b8c10c6
commit 85a66912c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 74 additions and 3 deletions

View File

@ -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.52
- type: <[boolean]>
See [`property: TestConfig.failOnFlakyTests`].
## property: FullConfig.forbidOnly
* since: v1.10
- type: <[boolean]>

View File

@ -108,6 +108,24 @@ export default defineConfig({
});
```
## property: TestConfig.failOnFlakyTests
* since: v1.52
- 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,
});
```
## property: TestConfig.forbidOnly
* since: v1.10
- type: ?<[boolean]>

View File

@ -56,7 +56,6 @@ export class FullConfigInternal {
cliProjectFilter?: string[];
cliListOnly = false;
cliPassWithNoTests?: boolean;
cliFailOnFlakyTests?: boolean;
cliLastFailed?: boolean;
testIdMatcher?: Matcher;
lastFailedTestIdMatcher?: Matcher;
@ -90,6 +89,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,

View File

@ -25,6 +25,7 @@ import type { SerializedCompilationCache } from '../transform/compilationCache'
export type ConfigCLIOverrides = {
debug?: boolean;
failOnFlakyTests?: boolean;
forbidOnly?: boolean;
fullyParallel?: boolean;
globalTimeout?: number;

View File

@ -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,

View File

@ -172,7 +172,6 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
config.cliListOnly = !!opts.list;
config.cliProjectFilter = opts.project || undefined;
config.cliPassWithNoTests = !!opts.passWithNoTests;
config.cliFailOnFlakyTests = !!opts.failOnFlakyTests;
config.cliLastFailed = !!opts.lastFailed;
// Evaluate project filters against config before starting execution. This enables a consistent error message across run modes
@ -294,6 +293,7 @@ function overridesFromOptions(options: { [key: string]: any }): ConfigCLIOverrid
updateSnapshots = 'updateSnapshots' in options ? 'changed' : undefined;
const overrides: ConfigCLIOverrides = {
failOnFlakyTests: options.failOnFlakyTests ? true : undefined,
forbidOnly: options.forbidOnly ? true : undefined,
fullyParallel: options.fullyParallel ? true : undefined,
globalTimeout: options.globalTimeout ? parseInt(options.globalTimeout, 10) : undefined,

View File

@ -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;
}
}

View File

@ -1177,6 +1177,25 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
};
};
/**
* 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,
* });
* ```
*
*/
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
@ -1911,6 +1930,12 @@ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
*/
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).
*/

View File

@ -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': `