diff --git a/packages/playwright/src/isomorphic/teleReceiver.ts b/packages/playwright/src/isomorphic/teleReceiver.ts index dbeb2645a075a..f33041a97f3b1 100644 --- a/packages/playwright/src/isomorphic/teleReceiver.ts +++ b/packages/playwright/src/isomorphic/teleReceiver.ts @@ -131,6 +131,7 @@ type TeleReporterReceiverOptions = { }; export class TeleReporterReceiver { + public isListing = false; private _rootSuite: TeleSuite; private _options: TeleReporterReceiverOptions; private _reporter: Partial; @@ -144,11 +145,6 @@ export class TeleReporterReceiver { this._reporter = reporter; } - reset() { - this._rootSuite._entries = []; - this._tests.clear(); - } - dispatch(message: JsonEvent): Promise | void { const { method, params } = message; if (method === 'onConfigure') { @@ -209,6 +205,35 @@ export class TeleReporterReceiver { projectSuite._project = this._parseProject(project); for (const suite of project.suites) this._mergeSuiteInto(suite, projectSuite); + + // Remove deleted tests when listing. Empty suites will be auto-filtered + // in the UI layer. + if (this.isListing) { + const testIds = new Set(); + const collectIds = (suite: JsonSuite) => { + suite.entries.forEach(entry => { + if ('testId' in entry) + testIds.add(entry.testId); + else + collectIds(entry); + }); + }; + project.suites.forEach(collectIds); + + const filterTests = (suite: TeleSuite) => { + suite._entries = suite._entries.filter(entry => { + if (entry.type === 'test') { + if (testIds.has(entry.id)) + return true; + this._tests.delete(entry.id); + return false; + } + filterTests(entry); + return true; + }); + }; + filterTests(projectSuite); + } } private _onBegin() { diff --git a/packages/trace-viewer/src/ui/teleSuiteUpdater.ts b/packages/trace-viewer/src/ui/teleSuiteUpdater.ts index 253ad61a1300c..600722db73381 100644 --- a/packages/trace-viewer/src/ui/teleSuiteUpdater.ts +++ b/packages/trace-viewer/src/ui/teleSuiteUpdater.ts @@ -123,9 +123,10 @@ export class TeleSuiteUpdater { } processListReport(report: any[]) { - this._receiver.reset(); + this._receiver.isListing = true; for (const message of report) this._receiver.dispatch(message); + this._receiver.isListing = false; } processTestReportEvent(message: any) { diff --git a/tests/playwright-test/ui-mode-test-update.spec.ts b/tests/playwright-test/ui-mode-test-update.spec.ts index 4996c959e6197..7797803143c04 100644 --- a/tests/playwright-test/ui-mode-test-update.spec.ts +++ b/tests/playwright-test/ui-mode-test-update.spec.ts @@ -129,6 +129,36 @@ test('should pick new / deleted tests', async ({ runUITest, writeFiles, deleteFi `); }); +test('should not loose run information after execution if test wrote into testDir', async ({ runUITest, writeFiles, deleteFile }) => { + test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30300' }); + const { page } = await runUITest({ + 'a.test.ts': ` + import fs from 'fs'; + import path from 'path'; + import { test, expect } from '@playwright/test'; + test('passes', () => { + fs.writeFileSync(path.join(test.info().project.testDir, 'something.txt'), 'hi'); + }); + `, + }); + await expect.poll(dumpTestTree(page)).toBe(` + ▼ ◯ a.test.ts + ◯ passes + `); + await page.getByTitle('passes').click(); + await page.getByTitle('Run all').click(); + await page.waitForTimeout(5_000); + await expect(page.getByText('Did not run')).toBeHidden(); + const listItem = page.getByTestId('actions-tree').getByRole('listitem'); + await expect( + listItem, + 'action list' + ).toHaveText([ + /Before Hooks[\d.]+m?s/, + /After Hooks[\d.]+m?s/, + ]); +}); + test('should pick new / deleted nested tests', async ({ runUITest, writeFiles, deleteFile }) => { const { page } = await runUITest(basicTestTree); await expect.poll(dumpTestTree(page)).toContain(`