show in html reporter

This commit is contained in:
Simon Knott 2024-10-16 10:39:26 +02:00
parent 13d305b959
commit 9a9d789e9b
No known key found for this signature in database
GPG key ID: 8CEDC00028084AEC
6 changed files with 45 additions and 17 deletions

View file

@ -113,3 +113,11 @@ export const copy = () => {
<path d='M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z'></path>
</svg>;
};
export const paperclip = () => {
return (
<svg className='octicon' viewBox='0 0 16 16' width='16' height='16' aria-hidden='true'>
<path d='M12.212 3.02a1.753 1.753 0 0 0-2.478.003l-5.83 5.83a3.007 3.007 0 0 0-.88 2.127c0 .795.315 1.551.88 2.116.567.567 1.333.89 2.126.89.79 0 1.548-.321 2.116-.89l5.48-5.48a.75.75 0 0 1 1.061 1.06l-5.48 5.48a4.492 4.492 0 0 1-3.177 1.33c-1.2 0-2.345-.487-3.187-1.33a4.483 4.483 0 0 1-1.32-3.177c0-1.195.475-2.341 1.32-3.186l5.83-5.83a3.25 3.25 0 0 1 5.553 2.297c0 .863-.343 1.691-.953 2.301L7.439 12.39c-.375.377-.884.59-1.416.593a1.998 1.998 0 0 1-1.412-.593 1.992 1.992 0 0 1 0-2.828l5.48-5.48a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-5.48 5.48a.492.492 0 0 0 0 .707.499.499 0 0 0 .352.154.51.51 0 0 0 .356-.154l5.833-5.827a1.755 1.755 0 0 0 0-2.481Z'></path>
</svg>
);
};

View file

@ -40,7 +40,9 @@ const result: TestResult = {
location: { file: 'test.spec.ts', line: 82, column: 0 },
steps: [],
count: 1,
attachments: [],
}],
attachments: [],
}],
attachments: [],
status: 'passed',
@ -142,6 +144,7 @@ const resultWithAttachment: TestResult = {
location: { file: 'test.spec.ts', line: 62, column: 0 },
count: 1,
steps: [],
attachments: [1],
}],
attachments: [{
name: 'first attachment',
@ -183,3 +186,11 @@ test('should correctly render links in attachment name', async ({ mount }) => {
await expect(link).toHaveAttribute('href', 'https://github.com/microsoft/playwright/issues/31284');
await expect(link).toHaveText('https://github.com/microsoft/playwright/issues/31284');
});
test('should render attachments in step view', async ({ mount }) => {
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={attachmentLinkRenderingTestCase} run={0} anchor=''></TestCaseView>);
const steps = component.getByTestId('test-steps-chip');
await expect(steps).not.toContainText('attachment with inline link');
await steps.getByTitle('1 attachment').click();
await expect(steps).toContainText('attachment with inline link');
});

View file

@ -62,3 +62,8 @@
padding: 0 !important;
}
}
.attachments-icon {
color: var(--color-fg-muted);
margin-left: 4px;
}

View file

@ -19,6 +19,7 @@ import * as React from 'react';
import { TreeItem } from './treeItem';
import { msToString } from './utils';
import { AutoChip } from './chip';
import * as icons from './icons';
import { traceImage } from './images';
import { AttachmentLink, generateTraceUrl } from './links';
import { statusIcon } from './statusIcon';
@ -188,23 +189,19 @@ const StepTreeItem: React.FC<{
depth: number,
attachments: TestAttachment[],
}> = ({ step, depth, attachments }) => {
if (step.category === 'attach') {
const attachmentName = step.title.match(/^attach "(.*)"$/)?.[1];
const matchingAttachments = attachments.filter(a => a.name === attachmentName);
if (matchingAttachments.length === 1) {
const [attachment] = matchingAttachments;
return <AttachmentLink attachment={attachment} depth={depth} openInNewTab={getAttachmentCategory(attachment) === 'html'} />;
}
}
return <TreeItem title={<span>
<span style={{ float: 'right' }}>{msToString(step.duration)}</span>
{statusIcon(step.error || step.duration === -1 ? 'failed' : 'passed')}
<span>{step.title}</span>
{step.count > 1 && <> <span className='test-result-counter'>{step.count}</span></>}
{step.location && <span className='test-result-path'> {step.location.file}:{step.location.line}</span>}
</span>} loadChildren={step.steps.length + (step.snippet ? 1 : 0) ? () => {
{step.attachments.length > 0 && <span className='attachments-icon' title={`${step.attachments} attachment${step.attachments.length > 1 ? 's' : ''}`}>{icons.paperclip()}</span>}
</span>} loadChildren={step.steps.length + step.attachments.length + (step.snippet ? 1 : 0) ? () => {
const children = step.steps.map((s, i) => <StepTreeItem key={i} step={s} depth={depth + 1} attachments={attachments}></StepTreeItem>);
children.unshift(...step.attachments.map(a => {
const attachment = attachments[a];
return <AttachmentLink key={`attachment-${a}`} attachment={attachment} depth={depth + 1} openInNewTab={getAttachmentCategory(attachment) === 'html'}/>;
}));
if (step.snippet)
children.unshift(<TestErrorView key='line' error={step.snippet}></TestErrorView>);
return children;

View file

@ -109,5 +109,6 @@ export type TestStep = {
snippet?: string;
error?: string;
steps: TestStep[];
attachments: number[];
count: number;
};

View file

@ -484,7 +484,7 @@ class HtmlBuilder {
duration: result.duration,
startTime: result.startTime.toISOString(),
retry: result.retry,
steps: dedupeSteps(result.steps).map(s => this._createTestStep(s)),
steps: dedupeSteps(result.steps).map(s => this._createTestStep(s, result)),
errors: formatResultFailure(test, result, '', true).map(error => error.message),
status: result.status,
attachments: this._serializeAttachments([
@ -494,21 +494,27 @@ class HtmlBuilder {
};
}
private _createTestStep(dedupedStep: DedupedStep): TestStep {
private _createTestStep(dedupedStep: DedupedStep, result: TestResultPublic): TestStep {
const { step, duration, count } = dedupedStep;
const result: TestStep = {
const testStep: TestStep = {
title: step.title,
category: step.category,
startTime: step.startTime.toISOString(),
duration,
steps: dedupeSteps(step.steps).map(s => this._createTestStep(s)),
steps: dedupeSteps(step.steps).map(s => this._createTestStep(s, result)),
attachments: step.attachments.map(s => {
const index = result.attachments.indexOf(s);
if (index === -1)
throw new Error('Unexpected, attachment not found');
return index;
}),
location: this._relativeLocation(step.location),
error: step.error?.message,
count
};
if (result.location)
this._stepsInFile.set(result.location.file, result);
return result;
if (testStep.location)
this._stepsInFile.set(testStep.location.file, testStep);
return testStep;
}
private _relativeLocation(location: Location | undefined): Location | undefined {