2024-06-05 14:19:28 +00:00
|
|
|
/**
|
|
|
|
* A **flaky** test is defined as a test which passed after auto-retrying.
|
|
|
|
* - By default, all tests run once if they pass.
|
|
|
|
* - If a test fails, it will automatically re-run at most 2 times.
|
|
|
|
* - If it pass after retrying (below 2 times), then it's marked as **flaky**
|
|
|
|
* but displayed as **passed** in the original test suite.
|
|
|
|
* - If it fail all 3 times, then it's a **failed** test.
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
|
|
|
import fs from 'fs';
|
|
|
|
import type { Reporter, TestCase, TestResult } from '@playwright/test/reporter';
|
|
|
|
|
2024-06-11 16:57:23 +00:00
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import pwConfig from './playwright.config';
|
|
|
|
|
2024-06-05 14:19:28 +00:00
|
|
|
type FormattedTestResult = Omit< TestResult, 'steps' >;
|
2024-06-11 16:57:23 +00:00
|
|
|
const flakyTestsDir = `${ pwConfig.outputDir }/flaky-tests`;
|
2024-06-05 14:19:28 +00:00
|
|
|
|
|
|
|
// Remove "steps" to prevent stringify circular structure.
|
|
|
|
function formatTestResult( testResult: TestResult ): FormattedTestResult {
|
|
|
|
const result = { ...testResult, steps: undefined };
|
|
|
|
delete result.steps;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
class FlakyTestsReporter implements Reporter {
|
|
|
|
failingTestCaseResults = new Map< string, FormattedTestResult[] >();
|
|
|
|
|
|
|
|
onBegin() {
|
|
|
|
try {
|
2024-06-11 16:57:23 +00:00
|
|
|
fs.mkdirSync( flakyTestsDir, {
|
|
|
|
recursive: true,
|
|
|
|
} );
|
2024-06-05 14:19:28 +00:00
|
|
|
} catch ( err ) {
|
|
|
|
if (
|
|
|
|
err instanceof Error &&
|
|
|
|
( err as NodeJS.ErrnoException ).code === 'EEXIST'
|
|
|
|
) {
|
|
|
|
// Ignore the error if the directory already exists.
|
|
|
|
} else {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onTestEnd( test: TestCase, testCaseResult: TestResult ) {
|
|
|
|
const testPath = test.location.file;
|
|
|
|
const testTitle = test.title;
|
|
|
|
|
|
|
|
switch ( test.outcome() ) {
|
|
|
|
case 'unexpected': {
|
|
|
|
if ( ! this.failingTestCaseResults.has( testTitle ) ) {
|
|
|
|
this.failingTestCaseResults.set( testTitle, [] );
|
|
|
|
}
|
|
|
|
this.failingTestCaseResults
|
|
|
|
.get( testTitle )!
|
|
|
|
.push( formatTestResult( testCaseResult ) );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'flaky': {
|
|
|
|
const safeFileName = testTitle.replace( /[^a-z0-9]/gi, '_' );
|
|
|
|
fs.writeFileSync(
|
2024-06-11 16:57:23 +00:00
|
|
|
`${ flakyTestsDir }/${ safeFileName }.json`,
|
2024-06-05 14:19:28 +00:00
|
|
|
JSON.stringify( {
|
|
|
|
version: 1,
|
|
|
|
runner: '@playwright/test',
|
|
|
|
title: testTitle,
|
|
|
|
path: testPath,
|
|
|
|
results: this.failingTestCaseResults.get( testTitle ),
|
|
|
|
} ),
|
|
|
|
'utf-8'
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onEnd() {
|
|
|
|
this.failingTestCaseResults.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
printsToStdio() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = FlakyTestsReporter;
|