diff --git a/README.md b/README.md index 4487bc2..a4b97c3 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ export default defineConfig({ }) ``` -After running a browser test through the extension, right-click the test in the Testing view or its editor gutter icon and select **Open Trace View**. The extension opens the generated HTML report in a reusable editor webview beside your code. Opening another test updates the same tab. Changes to the report reload the view and reset its selection. +After running a browser test through the extension, right-click the test in the Testing view or its editor gutter icon and select **Open Trace View**. The extension opens the generated HTML report in a reusable editor webview beside your code. Opening another test updates the same tab. Changes to the report reload the current test at its first trace step if its trace is still available. Otherwise, the view switches to the latest run's only recorded test, or asks you to select a test if there is no unambiguous target. This webview prototype loads report resources through VS Code's local-resource API without a static server. Canvas replay and snapshots referencing external resources have not been verified. diff --git a/packages/extension/src/traceView.ts b/packages/extension/src/traceView.ts index 8c5654b..afd270d 100644 --- a/packages/extension/src/traceView.ts +++ b/packages/extension/src/traceView.ts @@ -12,6 +12,8 @@ interface TraceViewTarget { export class TraceViewManager { private targets = new Map() + private latestTargets = new Map() + private currentTarget?: TraceViewTarget private panel?: vscode.WebviewPanel private reportPath?: string private watcher?: vscode.FileSystemWatcher @@ -27,6 +29,7 @@ export class TraceViewManager { clear() { this.targets.clear() + this.latestTargets.clear() void this.updateContext() } @@ -37,7 +40,9 @@ export class TraceViewManager { } } - for (const target of findTraceViewTargets(apiId, reportPath, files)) { + const targets = findTraceViewTargets(apiId, reportPath, files) + this.latestTargets.set(reportPath, targets) + for (const target of targets) { const item = tree.getTestItemByTaskId(target.testId) if (item) { this.targets.set(item, target) @@ -70,8 +75,9 @@ export class TraceViewManager { return } - const sameReport = this.reportPath === target.reportPath + const sameReport = this.reportPath === target.reportPath && !!this.currentTarget this.reportPath = target.reportPath + this.currentTarget = target const selection = createTraceViewUrl(target).split('#')[1] if (!this.panel) { const panel = vscode.window.createWebviewPanel( @@ -107,12 +113,25 @@ export class TraceViewManager { await this.refresh(selection) } - private async refresh(selection = '') { + private async refresh(selection?: string) { const panel = this.panel const reportPath = this.reportPath if (!panel || !reportPath) return const revision = ++this.revision + if (selection === undefined) { + const targets = this.latestTargets.get(reportPath) ?? [] + this.currentTarget = targets.find(target => target.testId === this.currentTarget?.testId) + ?? (targets.length === 1 ? targets[0] : undefined) + if (!this.currentTarget) { + panel.webview.html = ` + +

The selected test has no trace in the latest run. + Open Trace View on a test to select one.

` + return + } + selection = createTraceViewUrl(this.currentTarget).split('#')[1] + } try { const reportUri = vscode.Uri.file(reportPath) const directory = vscode.Uri.joinPath(reportUri, '..') diff --git a/samples/browser-v5/basic.test.ts b/samples/browser-v5/basic.test.ts index e727257..38acd59 100644 --- a/samples/browser-v5/basic.test.ts +++ b/samples/browser-v5/basic.test.ts @@ -2,7 +2,13 @@ import { expect, test } from 'vitest' import { page } from 'vitest/browser' test('records a trace', async () => { - document.body.innerHTML = '' + document.body.innerHTML = '' await page.getByRole('button').mark('Render button') await expect.element(page.getByRole('button')).toBeVisible() }) + +test('records a second trace', async () => { + document.body.innerHTML = '' + await page.getByRole('button').mark('Render cancel button') + await expect.element(page.getByRole('button')).toHaveTextContent('Cancel BAR') +})