diff --git a/packages/playwright/src/worker/testInfo.ts b/packages/playwright/src/worker/testInfo.ts index fa71ac1b15..8d93429136 100644 --- a/packages/playwright/src/worker/testInfo.ts +++ b/packages/playwright/src/worker/testInfo.ts @@ -455,7 +455,10 @@ export class TestInfoImpl implements TestInfo { title: `Attach ${escapeWithQuotes(name, '"')}`, category: 'test.attach', }); - this._attach(await normalizeAndSaveAttachment(this.outputPath(), name, options), step.stepId); + this._attach( + await normalizeAndSaveAttachment(this.outputPath(), name, options), + step.group ? undefined : step.stepId + ); step.complete({}); } diff --git a/tests/playwright-test/reporter-attachment.spec.ts b/tests/playwright-test/reporter-attachment.spec.ts index 50d6d1ee0c..65a536e77f 100644 --- a/tests/playwright-test/reporter-attachment.spec.ts +++ b/tests/playwright-test/reporter-attachment.spec.ts @@ -322,3 +322,30 @@ test('render text attachment with multiple lines', async ({ runInlineTest }) => expect(text).toContain(' ────────────────────────────────────────────────────────────────────────────────────────────────'); expect(result.exitCode).toBe(1); }); + +test('attaching inside boxed fixture should not log error', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/37147' } }, async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.ts': ` + import { test as base } from '@playwright/test'; + + const test = base.extend<{ myFixture: void }>({ + myFixture: [async ({}, use, testInfo) => { + await testInfo.attach('my attachment', { + body: 'foo', + contentType: 'text/plain', + }); + await use(); + }, { box: true }], + }); + + test('my test', ({ myFixture }) => { + expect(1).toBe(0); + }); + `, + }, { reporter: 'line' }, {}); + const text = result.output; + expect(text).toContain(' attachment #1: my attachment (text/plain) ──────────────────────────────────────────────────────'); + expect(text).toContain(' foo'); + expect(text).toContain(' ────────────────────────────────────────────────────────────────────────────────────────────────'); + expect(result.exitCode).toBe(1); +}); diff --git a/tests/playwright-test/ui-mode-test-attachments.spec.ts b/tests/playwright-test/ui-mode-test-attachments.spec.ts index 6d466acec5..4984afd9fc 100644 --- a/tests/playwright-test/ui-mode-test-attachments.spec.ts +++ b/tests/playwright-test/ui-mode-test-attachments.spec.ts @@ -173,6 +173,35 @@ test('should link from attachment step to attachments view', async ({ runUITest await expect(attachment).toBeInViewport(); }); +test('attachments from inside boxed fixture should be visible', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/37147' } }, async ({ runUITest }) => { + const { page } = await runUITest({ + 'a.test.ts': ` + import { test as base } from '@playwright/test'; + + const test = base.extend<{ myFixture: void }>({ + myFixture: [async ({}, use, testInfo) => { + await testInfo.attach('my attachment', { + body: 'foo', + contentType: 'text/plain', + }); + await use(); + }, { box: true }], + }); + + test('my test', ({ myFixture }) => {}); + `, + }, { reporter: 'line' }, {}); + await page.getByText('my test').click(); + await page.getByTitle('Run all').click(); + await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + + await page.getByRole('treeitem', { name: 'attach "my attachment"' }).getByLabel('Open Attachment').click(); + await expect(page.getByRole('tabpanel', { name: 'Attachments' })).toMatchAriaSnapshot(` + - tabpanel: + - button /my attachment/ + `); +}); + function readAllFromStream(stream: NodeJS.ReadableStream): Promise { return new Promise(resolve => { const chunks: Buffer[] = [];