diff --git a/packages/trace-viewer/src/ui/workbench.tsx b/packages/trace-viewer/src/ui/workbench.tsx index 01ae6142bd..2d6b0bd8bd 100644 --- a/packages/trace-viewer/src/ui/workbench.tsx +++ b/packages/trace-viewer/src/ui/workbench.tsx @@ -83,7 +83,7 @@ export const Workbench: React.FunctionComponent<{ setRevealedStack(action?.stack); }, [setSelectedActionImpl, setRevealedStack]); - const sources = React.useMemo(() => model?.sources || new Map(), [model]); + const sources = React.useMemo(() => model?.sources || new Map(), [model]); React.useEffect(() => { setSelectedTime(undefined); @@ -179,9 +179,17 @@ export const Workbench: React.FunctionComponent<{ selectPropertiesTab('source'); }} /> }; + + // Fallback location w/o action stands for file / test. + // Render error count on Source tab for that case. + let fallbackSourceErrorCount: number | undefined = undefined; + if (!selectedAction && fallbackLocation) + fallbackSourceErrorCount = fallbackLocation.source?.errors.length; + const sourceTab: TabbedPaneTabModel = { id: 'source', title: 'Source', + errorCount: fallbackSourceErrorCount, render: () => { /Missing semicolon./ ]); }); + +test('should load error (dupe tests) indicator on sources', async ({ runUITest }) => { + const { page } = await runUITest({ + 'a.test.ts': ` + import { test } from '@playwright/test'; + test('first', () => {}); + test('first', () => {}); + `, + }); + await expect.poll(dumpTestTree(page)).toBe(` + ▼ ◯ a.test.ts + ◯ first + `); + + await page.getByTestId('test-tree').getByText('a.test.ts').click(); + await expect(page.getByText('Source1')).toBeVisible(); + + await expect( + page.locator('.CodeMirror .source-line-running'), + ).toHaveText(`4 test('first', () => {});`); + + await expect( + page.locator('.CodeMirror-linewidget') + ).toHaveText([ + '                              ', + /Error: duplicate test title "first", first declared in a.test.ts:3/ + ]); +});