mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(runner): allow running last failed tests (#30533)
Fixes: https://github.com/microsoft/playwright/issues/30506
This commit is contained in:
parent
8e6272b1e3
commit
a2eb43b335
@ -19,7 +19,7 @@
|
|||||||
import type { Command } from 'playwright-core/lib/utilsBundle';
|
import type { Command } from 'playwright-core/lib/utilsBundle';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { Runner } from './runner/runner';
|
import { Runner, readLastRunInfo } from './runner/runner';
|
||||||
import { stopProfiling, startProfiling, gracefullyProcessExitDoNotHang } from 'playwright-core/lib/utils';
|
import { stopProfiling, startProfiling, gracefullyProcessExitDoNotHang } from 'playwright-core/lib/utils';
|
||||||
import { serializeError } from './util';
|
import { serializeError } from './util';
|
||||||
import { showHTMLReport } from './reporters/html';
|
import { showHTMLReport } from './reporters/html';
|
||||||
@ -183,6 +183,11 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
|||||||
if (!config)
|
if (!config)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (opts.lastFailed) {
|
||||||
|
const lastRunInfo = await readLastRunInfo(config);
|
||||||
|
config.testIdMatcher = id => lastRunInfo.failedTests.includes(id);
|
||||||
|
}
|
||||||
|
|
||||||
config.cliArgs = args;
|
config.cliArgs = args;
|
||||||
config.cliGrep = opts.grep as string | undefined;
|
config.cliGrep = opts.grep as string | undefined;
|
||||||
config.cliGrepInvert = opts.grepInvert as string | undefined;
|
config.cliGrepInvert = opts.grepInvert as string | undefined;
|
||||||
@ -338,6 +343,7 @@ const testOptions: [string, string][] = [
|
|||||||
['-gv, --grep-invert <grep>', `Only run tests that do not match this regular expression`],
|
['-gv, --grep-invert <grep>', `Only run tests that do not match this regular expression`],
|
||||||
['--headed', `Run tests in headed browsers (default: headless)`],
|
['--headed', `Run tests in headed browsers (default: headless)`],
|
||||||
['--ignore-snapshots', `Ignore screenshot and snapshot expectations`],
|
['--ignore-snapshots', `Ignore screenshot and snapshot expectations`],
|
||||||
|
['--last-failed', `Run last failed tests`],
|
||||||
['--list', `Collect all the tests and report them, but do not run`],
|
['--list', `Collect all the tests and report them, but do not run`],
|
||||||
['--max-failures <N>', `Stop after the first N failures`],
|
['--max-failures <N>', `Stop after the first N failures`],
|
||||||
['--no-deps', 'Do not run project dependencies'],
|
['--no-deps', 'Do not run project dependencies'],
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { monotonicTime } from 'playwright-core/lib/utils';
|
import { monotonicTime } from 'playwright-core/lib/utils';
|
||||||
import type { FullResult, TestError } from '../../types/testReporter';
|
import type { FullResult, TestError } from '../../types/testReporter';
|
||||||
@ -93,6 +94,8 @@ export class Runner {
|
|||||||
if (modifiedResult && modifiedResult.status)
|
if (modifiedResult && modifiedResult.status)
|
||||||
status = modifiedResult.status;
|
status = modifiedResult.status;
|
||||||
|
|
||||||
|
await writeLastRunInfo(testRun, status);
|
||||||
|
|
||||||
await reporter.onExit();
|
await reporter.onExit();
|
||||||
|
|
||||||
// Calling process.exit() might truncate large stdout/stderr output.
|
// Calling process.exit() might truncate large stdout/stderr output.
|
||||||
@ -144,3 +147,25 @@ export class Runner {
|
|||||||
return { testFiles: affectedTestFiles(resolvedFiles) };
|
return { testFiles: affectedTestFiles(resolvedFiles) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LastRunInfo = {
|
||||||
|
status: FullResult['status'];
|
||||||
|
failedTests: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
async function writeLastRunInfo(testRun: TestRun, status: FullResult['status']) {
|
||||||
|
await fs.promises.mkdir(testRun.config.globalOutputDir, { recursive: true });
|
||||||
|
const lastRunReportFile = path.join(testRun.config.globalOutputDir, 'last-run.json');
|
||||||
|
const failedTests = testRun.rootSuite?.allTests().filter(t => !t.ok()).map(t => t.id);
|
||||||
|
const lastRunReport = JSON.stringify({ status, failedTests }, undefined, 2);
|
||||||
|
await fs.promises.writeFile(lastRunReportFile, lastRunReport);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readLastRunInfo(config: FullConfigInternal): Promise<LastRunInfo> {
|
||||||
|
const lastRunReportFile = path.join(config.globalOutputDir, 'last-run.json');
|
||||||
|
try {
|
||||||
|
return JSON.parse(await fs.promises.readFile(lastRunReportFile, 'utf8')) as LastRunInfo;
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
return { status: 'passed', failedTests: [] };
|
||||||
|
}
|
||||||
|
@ -818,3 +818,24 @@ test('wait for workers to finish before reporter.onEnd', async ({ runInlineTest
|
|||||||
expect(secondIndex).not.toBe(-1);
|
expect(secondIndex).not.toBe(-1);
|
||||||
expect(secondIndex).toBeLessThan(endIndex);
|
expect(secondIndex).toBeLessThan(endIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should run last failed tests', async ({ runInlineTest }) => {
|
||||||
|
const workspace = {
|
||||||
|
'a.spec.js': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('pass', async () => {});
|
||||||
|
test('fail', async () => {
|
||||||
|
expect(1).toBe(2);
|
||||||
|
});
|
||||||
|
`
|
||||||
|
};
|
||||||
|
const result1 = await runInlineTest(workspace);
|
||||||
|
expect(result1.exitCode).toBe(1);
|
||||||
|
expect(result1.passed).toBe(1);
|
||||||
|
expect(result1.failed).toBe(1);
|
||||||
|
|
||||||
|
const result2 = await runInlineTest(workspace, {}, {}, { additionalArgs: ['--last-failed'] });
|
||||||
|
expect(result2.exitCode).toBe(1);
|
||||||
|
expect(result2.passed).toBe(0);
|
||||||
|
expect(result2.failed).toBe(1);
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user