feat(html reporter): open html attachments in new tab (#32389)

Closes https://github.com/microsoft/playwright/issues/32281.

HTML attachments get a linkified name that opens the attachment in a new
tab.
This commit is contained in:
Simon Knott 2024-09-02 08:35:53 +02:00 committed by GitHub
parent a6b320e362
commit cf8c14f884
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 41 additions and 6 deletions

View file

@ -75,11 +75,16 @@ export const AttachmentLink: React.FunctionComponent<{
attachment: TestAttachment,
href?: string,
linkName?: string,
}> = ({ attachment, href, linkName }) => {
openInNewTab?: boolean,
}> = ({ attachment, href, linkName, openInNewTab }) => {
return <TreeItem title={<span>
{attachment.contentType === kMissingContentType ? icons.warning() : icons.attachment()}
{attachment.path && <a href={href || attachment.path} download={downloadFileNameForAttachment(attachment)}>{linkName || attachment.name}</a>}
{!attachment.path && <span>{linkifyText(attachment.name)}</span>}
{!attachment.path && (
openInNewTab
? <a href={URL.createObjectURL(new Blob([attachment.body!], { type: attachment.contentType }))} target='_blank' rel='noreferrer' onClick={e => e.stopPropagation()}>{attachment.name}</a>
: <span>{linkifyText(attachment.name)}</span>
)}
</span>} loadChildren={attachment.body ? () => {
return [<div key={1} className='attachment-body'><CopyToClipboard value={attachment.body!}/>{linkifyText(attachment.body!)}</div>];
} : undefined} depth={0} style={{ lineHeight: '32px' }}></TreeItem>;

View file

@ -67,15 +67,16 @@ export const TestResultView: React.FC<{
anchor: 'video' | 'diff' | '',
}> = ({ result, anchor }) => {
const { screenshots, videos, traces, otherAttachments, diffs } = React.useMemo(() => {
const { screenshots, videos, traces, otherAttachments, diffs, htmls } = React.useMemo(() => {
const attachments = result?.attachments || [];
const screenshots = new Set(attachments.filter(a => a.contentType.startsWith('image/')));
const videos = attachments.filter(a => a.name === 'video');
const traces = attachments.filter(a => a.name === 'trace');
const htmls = attachments.filter(a => a.contentType.startsWith('text/html'));
const otherAttachments = new Set<TestAttachment>(attachments);
[...screenshots, ...videos, ...traces].forEach(a => otherAttachments.delete(a));
[...screenshots, ...videos, ...traces, ...htmls].forEach(a => otherAttachments.delete(a));
const diffs = groupImageDiffs(screenshots);
return { screenshots: [...screenshots], videos, traces, otherAttachments, diffs };
return { screenshots: [...screenshots], videos, traces, otherAttachments, diffs, htmls };
}, [result]);
const videoRef = React.useRef<HTMLDivElement>(null);
@ -135,7 +136,10 @@ export const TestResultView: React.FC<{
</div>)}
</AutoChip>}
{!!otherAttachments.size && <AutoChip header='Attachments'>
{!!(otherAttachments.size + htmls.length) && <AutoChip header='Attachments'>
{[...htmls].map((a, i) => (
<AttachmentLink key={`html-link-${i}`} attachment={a} openInNewTab />)
)}
{[...otherAttachments].map((a, i) => <AttachmentLink key={`attachment-link-${i}`} attachment={a}></AttachmentLink>)}
</AutoChip>}
</div>;

View file

@ -802,6 +802,32 @@ for (const useIntermediateMergeReport of [false] as const) {
await expect(page.locator('.attachment-body')).toHaveText(['foo', '{"foo":1}', 'utf16 encoded']);
});
test('should have link for opening HTML attachments in new tab', async ({ runInlineTest, page, showReport }) => {
const result = await runInlineTest({
'a.test.js': `
import { test, expect } from '@playwright/test';
test('passing', async ({ page }, testInfo) => {
testInfo.attach('axe-report.html', {
contentType: 'text/html',
body: '<h1>Axe Report</h1>',
});
});
`,
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
expect(result.exitCode).toBe(0);
await showReport();
await page.getByText('passing', { exact: true }).click();
const [newTab] = await Promise.all([
page.waitForEvent('popup'),
page.getByText('axe-report.html', { exact: true }).click(),
]);
await expect(newTab).toHaveURL(/^blob:/);
await expect(newTab.getByText('Axe Report')).toBeVisible();
});
test('should use file-browser friendly extensions for buffer attachments based on contentType', async ({ runInlineTest, showReport, page }, testInfo) => {
const result = await runInlineTest({
'a.test.js': `