From cd32d1b08c2d3f26abfaa1ae4222a0d5221e4f80 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Mon, 30 Dec 2024 18:45:49 +0000 Subject: [PATCH] fix(test runner): apply `--last-failed` after sharding (#34166) --- packages/playwright/src/common/config.ts | 1 + packages/playwright/src/runner/lastRun.ts | 2 +- packages/playwright/src/runner/loadUtils.ts | 4 +++ tests/playwright-test/runner.spec.ts | 32 +++++++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/playwright/src/common/config.ts b/packages/playwright/src/common/config.ts index a86fbb5157..443cb58319 100644 --- a/packages/playwright/src/common/config.ts +++ b/packages/playwright/src/common/config.ts @@ -56,6 +56,7 @@ export class FullConfigInternal { cliFailOnFlakyTests?: boolean; cliLastFailed?: boolean; testIdMatcher?: Matcher; + lastFailedTestIdMatcher?: Matcher; defineConfigWasUsed = false; globalSetups: string[] = []; diff --git a/packages/playwright/src/runner/lastRun.ts b/packages/playwright/src/runner/lastRun.ts index 407543041e..2152f977cf 100644 --- a/packages/playwright/src/runner/lastRun.ts +++ b/packages/playwright/src/runner/lastRun.ts @@ -43,7 +43,7 @@ export class LastRunReporter implements ReporterV2 { return; try { const lastRunInfo = JSON.parse(await fs.promises.readFile(this._lastRunFile, 'utf8')) as LastRunInfo; - this._config.testIdMatcher = id => lastRunInfo.failedTests.includes(id); + this._config.lastFailedTestIdMatcher = id => lastRunInfo.failedTests.includes(id); } catch { } } diff --git a/packages/playwright/src/runner/loadUtils.ts b/packages/playwright/src/runner/loadUtils.ts index 1315eea6e4..62799747b6 100644 --- a/packages/playwright/src/runner/loadUtils.ts +++ b/packages/playwright/src/runner/loadUtils.ts @@ -194,6 +194,10 @@ export async function createRootSuite(testRun: TestRun, errors: TestError[], sho filterTestsRemoveEmptySuites(rootSuite, test => testsInThisShard.has(test)); } + // Explicitly apply --last-failed filter after sharding. + if (config.lastFailedTestIdMatcher) + filterByTestIds(rootSuite, config.lastFailedTestIdMatcher); + // Now prepend dependency projects without filtration. { // Filtering 'only' and sharding might have reduced the number of top-level projects. diff --git a/tests/playwright-test/runner.spec.ts b/tests/playwright-test/runner.spec.ts index dc88187229..dece006a06 100644 --- a/tests/playwright-test/runner.spec.ts +++ b/tests/playwright-test/runner.spec.ts @@ -841,3 +841,35 @@ test('should run last failed tests', async ({ runInlineTest }) => { expect(result2.passed).toBe(0); expect(result2.failed).toBe(1); }); + +test('should run last failed tests in a shard', async ({ runInlineTest }) => { + const workspace = { + 'a.spec.js': ` + import { test, expect } from '@playwright/test'; + test('pass-a', async () => {}); + test('fail-a', async () => { + expect(1).toBe(2); + }); + `, + 'b.spec.js': ` + import { test, expect } from '@playwright/test'; + test('pass-b', async () => {}); + test('fail-b', async () => { + expect(1).toBe(2); + }); + `, + }; + const result1 = await runInlineTest(workspace, { shard: '2/2' }); + expect(result1.exitCode).toBe(1); + expect(result1.passed).toBe(1); + expect(result1.failed).toBe(1); + expect(result1.output).toContain('b.spec.js:3:11 › pass-b'); + expect(result1.output).toContain('b.spec.js:4:11 › fail-b'); + + const result2 = await runInlineTest(workspace, { shard: '2/2' }, {}, { additionalArgs: ['--last-failed'] }); + expect(result2.exitCode).toBe(1); + expect(result2.passed).toBe(0); + expect(result2.failed).toBe(1); + expect(result2.output).not.toContain('b.spec.js:3:11 › pass-b'); + expect(result2.output).toContain('b.spec.js:4:11 › fail-b'); +});