diff --git a/packages/trace-viewer/src/ui/uiModeView.css b/packages/trace-viewer/src/ui/uiModeView.css index 3674e16e20c68..113ac87abc123 100644 --- a/packages/trace-viewer/src/ui/uiModeView.css +++ b/packages/trace-viewer/src/ui/uiModeView.css @@ -98,6 +98,10 @@ text-overflow: ellipsis; } +.status-line .status-line-failed { + color: var(--vscode-testing-iconFailed); +} + .ui-mode-sidebar input[type=search] { flex: auto; padding: 0 5px; diff --git a/packages/trace-viewer/src/ui/uiModeView.tsx b/packages/trace-viewer/src/ui/uiModeView.tsx index f1c595027c21f..ed0c67e196c6a 100644 --- a/packages/trace-viewer/src/ui/uiModeView.tsx +++ b/packages/trace-viewer/src/ui/uiModeView.tsx @@ -74,6 +74,26 @@ function escapeRegex(text: string) { return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } +function renderStatusLine(progress: TeleSuiteUpdaterProgress, total: number, isRunning: boolean) { + const finished = progress.passed + progress.failed + progress.skipped; + const pct = total ? (finished / total) * 100 | 0 : 0; + const counts: React.ReactNode[] = []; + if (progress.passed > 0) + counts.push(`${progress.passed} passed`); + if (progress.failed > 0) + counts.push({progress.failed} failed); + if (progress.skipped > 0) + counts.push(`${progress.skipped} skipped`); + return
+
+ {isRunning && 'Running '} + {finished}/{total} ({pct}%) + {counts.length > 0 && ' — '} + {counts.map((count, i) => {i > 0 ? ', ' : ''}{count})} +
+
; +} + export const UIModeView: React.FC<{}> = ({ }) => { const [filterText, setFilterText] = React.useState(''); @@ -486,12 +506,7 @@ export const UIModeView: React.FC<{}> = ({ runTests={runVisibleTests} /> {!isRunningTest && !progress &&
Tests
} - {!isRunningTest && progress &&
-
{progress.passed}/{progress.total} passed ({(progress.passed / progress.total) * 100 | 0}%)
-
} - {isRunningTest && progress &&
-
Running {progress.passed}/{runningState.testIds.size} passed ({(progress.passed / runningState.testIds.size) * 100 | 0}%)
-
} + {progress && renderStatusLine(progress, isRunningTest ? runningState.testIds.size : progress.total, !!isRunningTest)} testServerConnection?.stopTests({})} disabled={!isRunningTest || isLoading} testId={'stop-button'}> { diff --git a/tests/playwright-test/ui-mode-metadata.spec.ts b/tests/playwright-test/ui-mode-metadata.spec.ts index bfbbba08a54f2..eda38bff3c622 100644 --- a/tests/playwright-test/ui-mode-metadata.spec.ts +++ b/tests/playwright-test/ui-mode-metadata.spec.ts @@ -41,7 +41,7 @@ test('should render html report git info metadata', async ({ runUITest }) => { }); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByTitle('Toggle output').click(); await expect(page.getByTestId('output')).toContainText('ci.link: https://playwright.dev'); diff --git a/tests/playwright-test/ui-mode-test-annotations.spec.ts b/tests/playwright-test/ui-mode-test-annotations.spec.ts index cc1f0b5f04db8..2ff20556292bb 100644 --- a/tests/playwright-test/ui-mode-test-annotations.spec.ts +++ b/tests/playwright-test/ui-mode-test-annotations.spec.ts @@ -32,7 +32,7 @@ test('should display annotations', async ({ runUITest }) => { `, }); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByRole('treeitem', { name: 'suite' }).locator('.codicon-chevron-right').click(); await page.getByText('annotation test').click(); await page.getByText('Annotations', { exact: true }).click(); diff --git a/tests/playwright-test/ui-mode-test-attachments.spec.ts b/tests/playwright-test/ui-mode-test-attachments.spec.ts index deabe4f6c9196..29f62006ae566 100644 --- a/tests/playwright-test/ui-mode-test-attachments.spec.ts +++ b/tests/playwright-test/ui-mode-test-attachments.spec.ts @@ -33,7 +33,7 @@ test('should contain text attachment', async ({ runUITest }) => { }); await page.getByText('attach test').click(); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByText('Attachments').click(); await page.locator('.tab-attachments').getByText('text attachment').click(); @@ -69,7 +69,7 @@ test('should contain binary attachment', async ({ runUITest }) => { }); await page.getByText('attach test').click(); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByText('Attachments').click(); const downloadPromise = page.waitForEvent('download'); await page.getByRole('link', { name: 'download' }).click(); @@ -89,7 +89,7 @@ test('should contain string attachment', async ({ runUITest }) => { }); await page.getByText('attach test').click(); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByText('Attachments').click(); await page.getByText('note', { exact: true }).click(); const downloadPromise = page.waitForEvent('download'); @@ -116,7 +116,7 @@ test('should linkify string attachments', async ({ runUITest, server }) => { }); await page.getByText('attach test').click(); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByText('Attachments').click(); const attachmentsPane = page.locator('.attachments-tab'); @@ -162,7 +162,7 @@ test('should link from attachment step to attachments view', async ({ runUITest await page.getByText('attach test').click(); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByRole('tab', { name: 'Attachments' }).click(); const panel = page.getByRole('tabpanel', { name: 'Attachments' }); @@ -193,7 +193,7 @@ test('attachments from inside boxed fixture should be visible', { annotation: { }, { reporter: 'line' }, {}); await page.getByText('my test').click(); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByRole('treeitem', { name: 'attach "my attachment"' }).getByLabel('Open Attachment').click(); await expect(page.getByRole('tabpanel', { name: 'Attachments' })).toMatchAriaSnapshot(` diff --git a/tests/playwright-test/ui-mode-test-output.spec.ts b/tests/playwright-test/ui-mode-test-output.spec.ts index f13c515743911..e6e548b7e2792 100644 --- a/tests/playwright-test/ui-mode-test-output.spec.ts +++ b/tests/playwright-test/ui-mode-test-output.spec.ts @@ -254,7 +254,7 @@ test('should print beforeAll console messages once', async ({ runUITest }, testI await page.getByTitle('Run all').click(); await page.getByText('Console').click(); await page.getByText('print').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await expect(page.locator('.console-tab .console-line-message')).toHaveText([ 'before all log', 'test log', diff --git a/tests/playwright-test/ui-mode-test-run.spec.ts b/tests/playwright-test/ui-mode-test-run.spec.ts index 724a8eb3517e2..0f3f36a31c04f 100644 --- a/tests/playwright-test/ui-mode-test-run.spec.ts +++ b/tests/playwright-test/ui-mode-test-run.spec.ts @@ -81,7 +81,7 @@ test('should run visible', async ({ runUITest }) => { - treeitem "[icon-circle-slash] skipped" `); - await expect(page.getByTestId('status-line')).toHaveText('4/8 passed (50%)'); + await expect(page.getByTestId('status-line')).toHaveText('8/8 (100%) — 4 passed, 3 failed, 1 skipped'); }); test('should show running progress', async ({ runUITest }) => { @@ -96,9 +96,9 @@ test('should show running progress', async ({ runUITest }) => { }); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('Running 1/4 passed (25%)'); + await expect(page.getByTestId('status-line')).toHaveText('Running 1/4 (25%) — 1 passed'); await page.getByTestId('stop-button').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/4 passed (25%)'); + await expect(page.getByTestId('status-line')).toHaveText('2/4 (50%) — 1 passed, 1 skipped'); await page.getByTitle('Reload').click(); await expect(page.getByTestId('status-line')).toBeHidden(); }); @@ -491,7 +491,7 @@ test('should show time', async ({ runUITest }) => { - treeitem "[icon-circle-slash] skipped" `); - await expect(page.getByTestId('status-line')).toHaveText('4/8 passed (50%)'); + await expect(page.getByTestId('status-line')).toHaveText('8/8 (100%) — 4 passed, 3 failed, 1 skipped'); }); test('should show test.fail as passing', async ({ runUITest }) => { @@ -522,7 +522,7 @@ test('should show test.fail as passing', async ({ runUITest }) => { - treeitem ${/\[icon-check\] should fail \d+m?s/} `); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); }); test('should ignore repeatEach', async ({ runUITest }) => { @@ -558,7 +558,7 @@ test('should ignore repeatEach', async ({ runUITest }) => { - treeitem ${/\[icon-check\] should pass/} `); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); }); test('should remove output folder before test run', async ({ runUITest }) => { @@ -593,7 +593,7 @@ test('should remove output folder before test run', async ({ runUITest }) => { - treeitem ${/\[icon-check\] should pass/} `); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByTitle('Run all').click(); await expect.poll(dumpTestTree(page)).toBe(` @@ -608,7 +608,7 @@ test('should remove output folder before test run', async ({ runUITest }) => { - treeitem ${/\[icon-check\] should pass/} `); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); }); test('should show proper total when using deps', async ({ runUITest }) => { @@ -660,7 +660,7 @@ test('should show proper total when using deps', async ({ runUITest }) => { - treeitem "[icon-circle-outline] run @chromium chromium" `); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByTitle('run @chromium').dblclick(); await expect.poll(dumpTestTree(page)).toBe(` @@ -680,7 +680,7 @@ test('should show proper total when using deps', async ({ runUITest }) => { - button "Watch" `); - await expect(page.getByTestId('status-line')).toHaveText('2/2 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('2/2 (100%) — 2 passed'); }); test('should respect --tsconfig option', { @@ -746,7 +746,7 @@ test('should respect --tsconfig option', { - treeitem ${/\[icon-check\] test/} `); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); }); test('should respect --ignore-snapshots option', { diff --git a/tests/playwright-test/ui-mode-test-screencast.spec.ts b/tests/playwright-test/ui-mode-test-screencast.spec.ts index 4affc726bc313..7856e4843ecd6 100644 --- a/tests/playwright-test/ui-mode-test-screencast.spec.ts +++ b/tests/playwright-test/ui-mode-test-screencast.spec.ts @@ -35,7 +35,7 @@ test('should show screenshots', async ({ runUITest }) => { `, }); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('2/2 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('2/2 (100%) — 2 passed'); await page.getByText('test 1', { exact: true }).click(); await expect(page.getByTestId('actions-tree')).toContainText('Expect'); diff --git a/tests/playwright-test/ui-mode-test-setup.spec.ts b/tests/playwright-test/ui-mode-test-setup.spec.ts index 0fcac731b0934..5738a3ec22af5 100644 --- a/tests/playwright-test/ui-mode-test-setup.spec.ts +++ b/tests/playwright-test/ui-mode-test-setup.spec.ts @@ -47,7 +47,7 @@ test('should run global setup and teardown', async ({ runUITest }, testInfo) => ` }, undefined, { additionalArgs: ['--output=foo'] }); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByTitle('Toggle output').click(); const output = page.getByTestId('output'); @@ -85,7 +85,7 @@ test('should teardown on sigint', async ({ runUITest, nodeVersion }) => { ` }); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByTitle('Toggle output').click(); await expect(page.getByTestId('output')).toContainText('from-global-setup'); @@ -335,7 +335,7 @@ for (const useWeb of [true, false]) { ` }, null, { useWeb }); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await testProcess.kill('SIGINT'); await expect.poll(() => testProcess.outputLines()).toEqual([ 'from-global-teardown0000', @@ -368,7 +368,7 @@ test('should restart webserver on reload', async ({ runUITest }) => { ` }, { DEBUG: 'pw:webserver' }); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByTitle('Toggle output').click(); await expect(page.getByTestId('output')).toContainText('[WebServer] listening'); @@ -381,5 +381,5 @@ test('should restart webserver on reload', async ({ runUITest }) => { await expect(page.getByTestId('output')).not.toContainText('set reuseExistingServer:true'); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); }); diff --git a/tests/playwright-test/ui-mode-test-shortcut.spec.ts b/tests/playwright-test/ui-mode-test-shortcut.spec.ts index a7b6852c19303..bc8e85933cccd 100644 --- a/tests/playwright-test/ui-mode-test-shortcut.spec.ts +++ b/tests/playwright-test/ui-mode-test-shortcut.spec.ts @@ -37,7 +37,7 @@ test('should run tests', async ({ runUITest }) => { await page.getByPlaceholder('Filter (e.g. text, @tag)').fill('test 3'); await page.keyboard.press('F5'); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByPlaceholder('Filter (e.g. text, @tag)').fill(''); // Only the filtered test was run. diff --git a/tests/playwright-test/ui-mode-test-source.spec.ts b/tests/playwright-test/ui-mode-test-source.spec.ts index e9985904fa185..a22a142587863 100644 --- a/tests/playwright-test/ui-mode-test-source.spec.ts +++ b/tests/playwright-test/ui-mode-test-source.spec.ts @@ -207,7 +207,7 @@ test('should keep showing source when test is pending', async ({ runUITest }, te await page.getByTitle('Run all').click(); await page.getByTestId('test-tree').getByText('second').click(); - await expect(page.getByTestId('status-line')).toHaveText('Running 1/3 passed (33%)'); + await expect(page.getByTestId('status-line')).toHaveText('Running 1/3 (33%) — 1 passed'); await expect(page.getByTestId('source-code').locator('.source-tab-file-name')).toHaveText('a.test.ts'); await expect(page.locator('.CodeMirror .source-line-running')).toHaveText(`7 test('second', () => {});`); }); diff --git a/tests/playwright-test/ui-mode-test-watch.spec.ts b/tests/playwright-test/ui-mode-test-watch.spec.ts index 851eefd804a93..951919991cba2 100644 --- a/tests/playwright-test/ui-mode-test-watch.spec.ts +++ b/tests/playwright-test/ui-mode-test-watch.spec.ts @@ -128,7 +128,7 @@ test('should batch watch updates', async ({ runUITest, writeFiles }) => { 'd.test.ts': `import { test } from '@playwright/test'; test('test', () => {});`, }); - await expect(page.getByTestId('status-line')).toHaveText('4/4 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('4/4 (100%) — 4 passed'); await expect.poll(dumpTestTree(page)).toBe(` ▼ ✅ a.test.ts 👁 @@ -168,7 +168,7 @@ test('should watch all', async ({ runUITest, writeFiles }) => { 'd.test.ts': `import { test } from '@playwright/test'; test('test', () => {});`, }); - await expect(page.getByTestId('status-line')).toHaveText('2/2 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('2/2 (100%) — 2 passed'); await expect.poll(dumpTestTree(page)).toBe(` ▼ ✅ a.test.ts @@ -211,7 +211,7 @@ test('should watch new file', async ({ runUITest, writeFiles }) => { 'b.test.ts': ` import { test } from '@playwright/test'; test('test', () => {});`, }); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await expect.poll(dumpTestTree(page)).toBe(` ▼ ◯ a.test.ts @@ -277,7 +277,7 @@ test('should queue watches', async ({ runUITest, writeFiles, createLatch }) => { await page.getByTitle('Watch all').click(); await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('Running 1/4 passed (25%)'); + await expect(page.getByTestId('status-line')).toHaveText('Running 1/4 (25%) — 1 passed'); await writeFiles({ 'a.test.ts': `import { test } from '@playwright/test'; test('test', () => {});`, @@ -287,12 +287,12 @@ test('should queue watches', async ({ runUITest, writeFiles, createLatch }) => { // Now watches should not kick in. await new Promise(f => setTimeout(f, 1000)); - await expect(page.getByTestId('status-line')).toHaveText('Running 1/4 passed (25%)'); + await expect(page.getByTestId('status-line')).toHaveText('Running 1/4 (25%) — 1 passed'); // Allow test to finish and new watch to kick in. latch.open(); - await expect(page.getByTestId('status-line')).toHaveText('3/3 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('3/3 (100%) — 3 passed'); }); test('should not watch output', async ({ runUITest }) => { @@ -317,7 +317,7 @@ test('should not watch output', async ({ runUITest }) => { await page.getByTitle('Run all').click(); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); expect(commands).toContain('runTests'); expect(commands).not.toContain('listTests'); }); diff --git a/tests/playwright-test/ui-mode-trace.spec.ts b/tests/playwright-test/ui-mode-trace.spec.ts index 13f69d8964cfb..e890f35e83e8a 100644 --- a/tests/playwright-test/ui-mode-trace.spec.ts +++ b/tests/playwright-test/ui-mode-trace.spec.ts @@ -744,7 +744,7 @@ test('should be able to create and dispose APIRequestContext inside Promise.all' await page.getByText('create api request contexts').dblclick(); await expect(page.getByTestId('workbench-run-status')).toContainText('Passed'); - await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + await expect(page.getByTestId('status-line')).toHaveText('1/1 (100%) — 1 passed'); await page.getByText('Errors', { exact: true }).click(); await expect(page.locator('.tab-errors')).toHaveText('No errors');