diff --git a/packages/playwright-core/src/web/htmlReport/htmlReport.css b/packages/playwright-core/src/web/htmlReport/htmlReport.css
index b97640d065..e733bdb696 100644
--- a/packages/playwright-core/src/web/htmlReport/htmlReport.css
+++ b/packages/playwright-core/src/web/htmlReport/htmlReport.css
@@ -80,7 +80,7 @@ svg {
.test-case-column {
border-radius: 6px;
- margin: 20px;
+ margin: 20px 0;
}
.tree-item {
diff --git a/packages/playwright-core/src/web/htmlReport/htmlReport.tsx b/packages/playwright-core/src/web/htmlReport/htmlReport.tsx
index deeacb58b5..dcb6da6300 100644
--- a/packages/playwright-core/src/web/htmlReport/htmlReport.tsx
+++ b/packages/playwright-core/src/web/htmlReport/htmlReport.tsx
@@ -189,6 +189,9 @@ const TestCaseView: React.FC<{
const [selectedResultIndex, setSelectedResultIndex] = React.useState(0);
return
+
+
+
{test &&
{test.path.join(' › ')}
}
{test &&
{test?.title}
}
{test &&
{test.location.file}:{test.location.line}
}
@@ -279,7 +282,7 @@ const StepTreeItem: React.FC<{
}> = ({ step, depth }) => {
return
{msToString(step.duration)}
- {statusIcon(step.error ? 'failed' : 'passed')}
+ {statusIcon(step.error || step.duration === -1 ? 'failed' : 'passed')}
{step.title}
{step.location && — {step.location.file}:{step.location.line}}
} loadChildren={step.steps.length + (step.snippet ? 1 : 0) ? () => {
diff --git a/packages/playwright-test/src/dispatcher.ts b/packages/playwright-test/src/dispatcher.ts
index 7a8f024b22..de55dde214 100644
--- a/packages/playwright-test/src/dispatcher.ts
+++ b/packages/playwright-test/src/dispatcher.ts
@@ -195,7 +195,7 @@ export class Dispatcher {
parent: parentStep,
category: params.category,
startTime: new Date(params.wallTime),
- duration: 0,
+ duration: -1,
steps: [],
location: params.location,
data: {},
diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts
index c203d4ce50..57c6bb62ce 100644
--- a/tests/playwright-test/reporter-html.spec.ts
+++ b/tests/playwright-test/reporter-html.spec.ts
@@ -248,3 +248,29 @@ test('should show trace title', async ({ runInlineTest, page, showReport }) => {
await page.click('img');
await expect(page.locator('.workbench .title')).toHaveText('a.test.js:6 › passes');
});
+
+test('should show timed out steps', async ({ runInlineTest, page, showReport }) => {
+ const result = await runInlineTest({
+ 'playwright.config.js': `
+ module.exports = { timeout: 500 };
+ `,
+ 'a.test.js': `
+ const { test } = pwt;
+ test('fails', async ({ page }) => {
+ await test.step('outer step', async () => {
+ await test.step('inner step', async () => {
+ await new Promise(() => {});
+ });
+ });
+ });
+ `,
+ }, { reporter: 'dot,html' });
+ expect(result.exitCode).toBe(1);
+ expect(result.passed).toBe(0);
+
+ await showReport();
+ await page.click('text=fails');
+ await page.click('text=outer step');
+ await expect(page.locator('.tree-item:has-text("outer step") svg.color-text-danger')).toHaveCount(2);
+ await expect(page.locator('.tree-item:has-text("inner step") svg.color-text-danger')).toHaveCount(2);
+});