diff --git a/packages/playwright/src/isomorphic/teleReceiver.ts b/packages/playwright/src/isomorphic/teleReceiver.ts index e675ce336b..8f364329a3 100644 --- a/packages/playwright/src/isomorphic/teleReceiver.ts +++ b/packages/playwright/src/isomorphic/teleReceiver.ts @@ -130,6 +130,7 @@ type TeleReporterReceiverOptions = { }; export class TeleReporterReceiver { + public isListing = false; private _rootSuite: TeleSuite; private _options: TeleReporterReceiverOptions; private _reporter: Partial; @@ -143,12 +144,6 @@ export class TeleReporterReceiver { this._reporter = reporter; } - reset() { - this._rootSuite.suites = []; - this._rootSuite.tests = []; - this._tests.clear(); - } - dispatch(message: JsonEvent): Promise | void { const { method, params } = message; if (method === 'onConfigure') { @@ -209,6 +204,28 @@ export class TeleReporterReceiver { // Always update project in watch mode. projectSuite._project = this._parseProject(project); this._mergeSuitesInto(project.suites, 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.tests.map(t => t.testId).forEach(testId => testIds.add(testId)); + suite.suites.forEach(collectIds); + }; + project.suites.forEach(collectIds); + + const filterTests = (suite: TeleSuite) => { + suite.tests = suite.tests.filter(t => { + if (testIds.has(t.id)) + return true; + this._tests.delete(t.id); + return false; + }); + suite.suites.forEach(filterTests); + }; + filterTests(projectSuite); + } } private _onBegin() { diff --git a/packages/trace-viewer/src/ui/teleSuiteUpdater.ts b/packages/trace-viewer/src/ui/teleSuiteUpdater.ts index 253ad61a13..600722db73 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 4996c959e6..7797803143 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(`