diff --git a/packages/playwright-core/src/utils/traceUtils.ts b/packages/playwright-core/src/utils/traceUtils.ts index ca2a27ed59..f03b306b6a 100644 --- a/packages/playwright-core/src/utils/traceUtils.ts +++ b/packages/playwright-core/src/utils/traceUtils.ts @@ -117,15 +117,16 @@ export async function saveTraceFile(fileName: string, traceEvents: TraceEvent[], const sha1s = new Set(); for (const event of traceEvents.filter(e => e.type === 'after') as AfterActionTraceEvent[]) { for (const attachment of (event.attachments || [])) { - let contentPromise: Promise | undefined; + let contentPromise: Promise | undefined; if (attachment.path) - contentPromise = fs.promises.readFile(attachment.path); + contentPromise = fs.promises.readFile(attachment.path).catch(() => undefined); else if (attachment.base64) contentPromise = Promise.resolve(Buffer.from(attachment.base64, 'base64')); - if (!contentPromise) - continue; const content = await contentPromise; + if (content === undefined) + continue; + const sha1 = calculateSha1(content); attachment.sha1 = sha1; delete attachment.path; diff --git a/packages/trace-viewer/src/ui/attachmentsTab.tsx b/packages/trace-viewer/src/ui/attachmentsTab.tsx index 7601fb0252..597ee60c36 100644 --- a/packages/trace-viewer/src/ui/attachmentsTab.tsx +++ b/packages/trace-viewer/src/ui/attachmentsTab.tsx @@ -68,13 +68,7 @@ export const AttachmentsSection: React.FunctionComponent<{ ; }; -function attachmentURL(traceUrl: string, attachment: { - name: string; - contentType: string; - path?: string; - sha1?: string; - body?: string; -}) { +function attachmentURL(traceUrl: string, attachment: NonNullable[0]) { if (attachment.sha1) return 'sha1/' + attachment.sha1 + '?trace=' + encodeURIComponent(traceUrl); return 'file?path=' + encodeURIComponent(attachment.path!); diff --git a/tests/playwright-test/playwright.trace.spec.ts b/tests/playwright-test/playwright.trace.spec.ts index a6a4a5d363..74962f2cfe 100644 --- a/tests/playwright-test/playwright.trace.spec.ts +++ b/tests/playwright-test/playwright.trace.spec.ts @@ -686,3 +686,22 @@ test('should show non-expect error in trace', async ({ runInlineTest }, testInfo ' fixture: context', ]); }); + +test('should not throw when attachment is missing', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { use: { trace: 'on' } }; + `, + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('passes', async ({}) => { + test.info().attachments.push({ name: 'screenshot', path: 'nothing-to-see-here', contentType: 'image/png' }); + }); + `, + }, { workers: 1 }); + + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); + const trace = await parseTrace(testInfo.outputPath('test-results', 'a-passes', 'trace.zip')); + expect(trace.actionTree).toContain('attach "screenshot"'); +});