From b9114f9cbc73d911f23372e2357193495c99b338 Mon Sep 17 00:00:00 2001 From: Ross Wollman Date: Fri, 17 Jun 2022 08:09:49 -0700 Subject: [PATCH] fix(html-reporter): file-browser friendly extensions (#14943) Fixes #14904. This is done to make looking at the raw contents of the report friendlier when using a file browser. However, it should be noted, the public API of the HTML Reporter makes no guarantees of its contents structure/layout/naming-conventions. --- .../playwright-test/src/reporters/html.ts | 6 ++-- tests/playwright-test/reporter-html.spec.ts | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/playwright-test/src/reporters/html.ts b/packages/playwright-test/src/reporters/html.ts index 07de403e2e..4b95c7e793 100644 --- a/packages/playwright-test/src/reporters/html.ts +++ b/packages/playwright-test/src/reporters/html.ts @@ -27,10 +27,11 @@ import { removeFolders } from 'playwright-core/lib/utils/fileUtils'; import type { JsonAttachment, JsonReport, JsonSuite, JsonTestCase, JsonTestResult, JsonTestStep } from './raw'; import RawReporter from './raw'; import { stripAnsiEscapes } from './base'; -import { getPackageJsonPath } from '../util'; +import { getPackageJsonPath, sanitizeForFilePath } from '../util'; import type { FullConfigInternal, Metadata } from '../types'; import type { ZipFile } from 'playwright-core/lib/zipBundle'; import { yazl } from 'playwright-core/lib/zipBundle'; +import { mime } from 'playwright-core/lib/utilsBundle'; export type Stats = { total: number; @@ -463,7 +464,8 @@ class HtmlBuilder { } fs.mkdirSync(path.join(this._reportFolder, 'data'), { recursive: true }); - const sha1 = calculateSha1(a.body) + '.dat'; + const extension = sanitizeForFilePath(path.extname(a.name).replace(/^\./, '')) || mime.getExtension(a.contentType) || 'dat'; + const sha1 = calculateSha1(a.body) + '.' + extension; fs.writeFileSync(path.join(this._reportFolder, 'data', sha1), a.body); return { name: a.name, diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index b5ade9b39c..3e6057353d 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -15,6 +15,7 @@ */ import fs from 'fs'; +import path from 'path'; import { test as baseTest, expect, createImage } from './playwright-test-fixtures'; import type { HttpServer } from '../../packages/playwright-core/lib/utils/httpServer'; import { startHtmlReportServer } from '../../packages/playwright-test/lib/reporters/html'; @@ -591,6 +592,33 @@ test('should render text attachments as text', async ({ runInlineTest, page, sho 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) => { + const result = await runInlineTest({ + 'a.test.js': ` + const { test } = pwt; + test('passing', async ({ page }, testInfo) => { + await testInfo.attach('screenshot', { body: await page.screenshot(), contentType: 'image/png' }); + await testInfo.attach('some-pdf', { body: Buffer.from('foo'), contentType: 'application/pdf' }); + await testInfo.attach('madeup-contentType', { body: Buffer.from('bar'), contentType: 'madeup' }); + + await testInfo.attach('screenshot-that-already-has-an-extension-with-madeup.png', { body: Buffer.from('a'), contentType: 'madeup' }); + await testInfo.attach('screenshot-that-already-has-an-extension-with-correct-contentType.png', { body: Buffer.from('c'), contentType: 'image/png' }); + await testInfo.attach('example.ext with spaces', { body: Buffer.from('b'), contentType: 'madeup' }); + }); + `, + }, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' }); + expect(result.exitCode).toBe(0); + const files = await fs.promises.readdir(path.join(testInfo.outputPath('playwright-report'), 'data')); + expect(new Set(files)).toEqual(new Set([ + 'f6aa9785bc9c7b8fd40c3f6ede6f59112a939527.png', // screenshot + '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33.pdf', // some-pdf + '62cdb7020ff920e5aa642c3d4066950dd1f01f4d.dat', // madeup-contentType + '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8.png', // screenshot-that-already-has-an-extension-with-madeup.png + '84a516841ba77a5b4648de2cd0dfcb30ea46dbb4.png', // screenshot-that-already-has-an-extension-with-correct-contentType.png + 'e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98.ext-with-spaces', // example.ext with spaces + ])); +}); + test('should strikethough textual diff', async ({ runInlineTest, showReport, page }) => { const result = await runInlineTest({ 'helper.ts': `