diff --git a/packages/html-reporter/src/links.tsx b/packages/html-reporter/src/links.tsx
index 03eae930a6..2aa835d7d6 100644
--- a/packages/html-reporter/src/links.tsx
+++ b/packages/html-reporter/src/links.tsx
@@ -68,13 +68,22 @@ export const AttachmentLink: React.FunctionComponent<{
}> = ({ attachment, href, linkName }) => {
return
{attachment.contentType === kMissingContentType ? icons.warning() : icons.attachment()}
- {attachment.path && {linkName || attachment.name}}
+ {attachment.path && {linkName || attachment.name}}
{attachment.body && {attachment.name}}
} loadChildren={attachment.body ? () => {
return [{attachment.body}
];
} : undefined} depth={0} style={{ lineHeight: '32px' }}>;
};
+function downloadFileNameForAttachment(attachment: TestAttachment): string {
+ if (attachment.name.includes('.') || !attachment.path)
+ return attachment.name;
+ const firstDotIndex = attachment.path.indexOf('.');
+ if (firstDotIndex === -1)
+ return attachment.name;
+ return attachment.name + attachment.path.slice(firstDotIndex, attachment.path.length);
+}
+
export function generateTraceUrl(traces: TestAttachment[]) {
return `trace/index.html?${traces.map((a, i) => `trace=${new URL(a.path!, window.location.href)}`).join('&')}`;
}
diff --git a/tests/playwright-test/reporter-blob.spec.ts b/tests/playwright-test/reporter-blob.spec.ts
index 661eba7324..af138a6902 100644
--- a/tests/playwright-test/reporter-blob.spec.ts
+++ b/tests/playwright-test/reporter-blob.spec.ts
@@ -577,13 +577,13 @@ test('preserve attachments', async ({ runInlineTest, mergeReports, showReport, p
await showReport();
await page.getByText('first').click();
- const popupPromise = page.waitForEvent('popup');
+ const downloadPromise = page.waitForEvent('download');
// Check file attachment.
await page.getByRole('link', { name: 'file-attachment' }).click();
- const popup = await popupPromise;
+ const download = await downloadPromise;
// Check file attachment content.
- await expect(popup.locator('body')).toHaveText('hello!');
- await popup.close();
+ expect(await readAllFromStreamAsString(await download.createReadStream())).toEqual('hello!');
+
await page.goBack();
await page.getByText('failing 1').click();
@@ -651,13 +651,13 @@ test('generate html with attachment urls', async ({ runInlineTest, mergeReports,
await page.goto(`${server.PREFIX}/index.html`);
await page.getByText('first').click();
- const popupPromise = page.waitForEvent('popup');
+ const downloadPromise = page.waitForEvent('download');
// Check file attachment.
await page.getByRole('link', { name: 'file-attachment' }).click();
- const popup = await popupPromise;
+ const download = await downloadPromise;
// Check file attachment content.
- await expect(popup.locator('body')).toHaveText('hello!');
- await popup.close();
+ expect(await readAllFromStreamAsString(await download.createReadStream())).toEqual('hello!');
+
await page.goBack();
// Check inline attachment.
@@ -720,11 +720,10 @@ test('resource names should not clash between runs', async ({ runInlineTest, sho
await page.getByText('first').click();
await expect(fileAttachment).toBeVisible();
- const popupPromise = page.waitForEvent('popup');
+ const downloadPromise = page.waitForEvent('download');
await fileAttachment.click();
- const popup = await popupPromise;
- await expect(popup.locator('body')).toHaveText('hello!');
- await popup.close();
+ const download = await downloadPromise;
+ expect(await readAllFromStreamAsString(await download.createReadStream())).toEqual('hello!');
await page.goBack();
}
@@ -733,11 +732,10 @@ test('resource names should not clash between runs', async ({ runInlineTest, sho
await page.getByText('failing 2').click();
await expect(fileAttachment).toBeVisible();
- const popupPromise = page.waitForEvent('popup');
+ const downloadPromise = page.waitForEvent('download');
await fileAttachment.click();
- const popup = await popupPromise;
- await expect(popup.locator('body')).toHaveText('bye!');
- await popup.close();
+ const download = await downloadPromise;
+ expect(await readAllFromStreamAsString(await download.createReadStream())).toEqual('bye!');
await page.goBack();
}
});
@@ -1371,4 +1369,12 @@ test('should merge blob reports with same name', async ({ runInlineTest, mergeRe
await expect(page.locator('.subnav-item:has-text("Failed") .counter')).toHaveText('4');
await expect(page.locator('.subnav-item:has-text("Flaky") .counter')).toHaveText('2');
await expect(page.locator('.subnav-item:has-text("Skipped") .counter')).toHaveText('4');
-});
\ No newline at end of file
+});
+
+function readAllFromStreamAsString(stream: NodeJS.ReadableStream): Promise {
+ return new Promise(resolve => {
+ const chunks: Buffer[] = [];
+ stream.on('data', chunk => chunks.push(chunk));
+ stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
+ });
+}
diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts
index ac7cbf2fe0..23ba5bf395 100644
--- a/tests/playwright-test/reporter-html.spec.ts
+++ b/tests/playwright-test/reporter-html.spec.ts
@@ -684,7 +684,7 @@ for (const useIntermediateMergeReport of [false, true] as const) {
await expect(page.locator('.attachment-body')).toHaveText(['foo', '{"foo":1}', 'utf16 encoded']);
});
- test('should use file-browser friendly extensions for buffer attachments based on contentType', async ({ runInlineTest }, testInfo) => {
+ 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': `
import { test, expect } from '@playwright/test';
@@ -700,6 +700,28 @@ for (const useIntermediateMergeReport of [false, true] as const) {
`,
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
expect(result.exitCode).toBe(0);
+ await showReport();
+ await page.getByRole('link', { name: 'passing' }).click();
+
+ const expectedAttachments = [
+ ['screenshot', 'screenshot.png', 'f6aa9785bc9c7b8fd40c3f6ede6f59112a939527.png'],
+ ['some-pdf', 'some-pdf.pdf', '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33.pdf'],
+ ['madeup-contentType', 'madeup-contentType.dat', '62cdb7020ff920e5aa642c3d4066950dd1f01f4d.dat'],
+ ['screenshot-that-already-has-an-extension-with-madeup.png', 'screenshot-that-already-has-an-extension-with-madeup.png', '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8.png'],
+ ['screenshot-that-already-has-an-extension-with-correct-contentType.png', 'screenshot-that-already-has-an-extension-with-correct-contentType.png', '84a516841ba77a5b4648de2cd0dfcb30ea46dbb4.png'],
+ ['example.ext with spaces', 'example.ext with spaces', 'e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98.ext-with-spaces'],
+ ];
+
+ for (const [visibleAttachmentName, downloadFileName, sha1] of expectedAttachments) {
+ await test.step(`should download ${visibleAttachmentName}`, async () => {
+ const downloadPromise = page.waitForEvent('download');
+ await page.getByRole('link', { name: visibleAttachmentName, exact: true }).click();
+ const download = await downloadPromise;
+ expect(download.suggestedFilename()).toBe(downloadFileName);
+ expect(await readAllFromStream(await download.createReadStream())).toEqual(await fs.promises.readFile(path.join(testInfo.outputPath('playwright-report'), 'data', sha1)));
+ });
+ }
+
const files = await fs.promises.readdir(path.join(testInfo.outputPath('playwright-report'), 'data'));
expect(new Set(files)).toEqual(new Set([
'f6aa9785bc9c7b8fd40c3f6ede6f59112a939527.png', // screenshot
@@ -2025,4 +2047,12 @@ for (const useIntermediateMergeReport of [false, true] as const) {
await expect(page.getByText('passes title')).toBeVisible();
});
});
-}
\ No newline at end of file
+}
+
+function readAllFromStream(stream: NodeJS.ReadableStream): Promise {
+ return new Promise(resolve => {
+ const chunks: Buffer[] = [];
+ stream.on('data', chunk => chunks.push(chunk));
+ stream.on('end', () => resolve(Buffer.concat(chunks)));
+ });
+}