feat(reporter): links in attachment names, attachments name only (#31714)
* Allow calling `test.info().attach('My text');` without options (no
path nor body).
* Highlight links in attachment names:
<img width="992" alt="image"
src="https://github.com/user-attachments/assets/770e7876-3e43-4434-8cf1-194ad6ae5819">
Fixes https://github.com/microsoft/playwright/issues/31284
This commit is contained in:
parent
f4399f7f06
commit
3f15fe8518
|
|
@ -78,7 +78,7 @@ export const AttachmentLink: React.FunctionComponent<{
|
|||
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.body && <span>{linkifyText(attachment.name)}</span>}
|
||||
{!attachment.path && <span>{linkifyText(attachment.name)}</span>}
|
||||
</span>} loadChildren={attachment.body ? () => {
|
||||
return [<div className='attachment-body'><CopyToClipboard value={attachment.body!}/>{linkifyText(attachment.body!)}</div>];
|
||||
} : undefined} depth={0} style={{ lineHeight: '32px' }}></TreeItem>;
|
||||
|
|
|
|||
|
|
@ -132,6 +132,9 @@ const resultWithAttachment: TestResult = {
|
|||
name: 'first attachment',
|
||||
body: 'The body with https://playwright.dev/docs/intro link and https://github.com/microsoft/playwright/issues/31284.',
|
||||
contentType: 'text/plain'
|
||||
}, {
|
||||
name: 'attachment with inline link https://github.com/microsoft/playwright/issues/31284',
|
||||
contentType: 'text/plain'
|
||||
}],
|
||||
status: 'passed',
|
||||
};
|
||||
|
|
@ -157,4 +160,11 @@ test('should correctly render links in attachments', async ({ mount }) => {
|
|||
await expect(body).toBeVisible();
|
||||
await expect(body.locator('a').filter({ hasText: 'playwright.dev' })).toHaveAttribute('href', 'https://playwright.dev/docs/intro');
|
||||
await expect(body.locator('a').filter({ hasText: 'github.com' })).toHaveAttribute('href', 'https://github.com/microsoft/playwright/issues/31284');
|
||||
});
|
||||
});
|
||||
|
||||
test('should correctly render links in attachment name', async ({ mount }) => {
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={attachmentLinkRenderingTestCase} run={0} anchor=''></TestCaseView>);
|
||||
const link = component.getByText('attachment with inline link').locator('a');
|
||||
await expect(link).toHaveAttribute('href', 'https://github.com/microsoft/playwright/issues/31284');
|
||||
await expect(link).toHaveText('https://github.com/microsoft/playwright/issues/31284');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ export function formatFailure(config: FullConfig, test: TestCase, options: {inde
|
|||
if (includeAttachments) {
|
||||
for (let i = 0; i < result.attachments.length; ++i) {
|
||||
const attachment = result.attachments[i];
|
||||
const hasPrintableContent = attachment.contentType.startsWith('text/') && attachment.body;
|
||||
const hasPrintableContent = attachment.contentType.startsWith('text/');
|
||||
if (!attachment.path && !hasPrintableContent)
|
||||
continue;
|
||||
resultLines.push('');
|
||||
|
|
|
|||
|
|
@ -256,6 +256,8 @@ export function resolveReporterOutputPath(defaultValue: string, configDir: strin
|
|||
}
|
||||
|
||||
export async function normalizeAndSaveAttachment(outputPath: string, name: string, options: { path?: string, body?: string | Buffer, contentType?: string } = {}): Promise<{ name: string; path?: string | undefined; body?: Buffer | undefined; contentType: string; }> {
|
||||
if (options.path === undefined && options.body === undefined)
|
||||
return { name, contentType: 'text/plain' };
|
||||
if ((options.path !== undefined ? 1 : 0) + (options.body !== undefined ? 1 : 0) !== 1)
|
||||
throw new Error(`Exactly one of "path" and "body" must be specified`);
|
||||
if (options.path !== undefined) {
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@ test(`testInfo.attach errors`, async ({ runInlineTest }) => {
|
|||
const text = result.output.replace(/\\/g, '/');
|
||||
expect(text).toMatch(/Error: ENOENT: no such file or directory, copyfile '.*foo.txt.*'/);
|
||||
expect(text).toContain(`Exactly one of "path" and "body" must be specified`);
|
||||
expect(result.passed).toBe(0);
|
||||
expect(result.failed).toBe(3);
|
||||
expect(result.passed).toBe(1);
|
||||
expect(result.failed).toBe(2);
|
||||
expect(result.exitCode).toBe(1);
|
||||
});
|
||||
|
||||
|
|
@ -175,6 +175,21 @@ test(`testInfo.attach allow empty string body`, async ({ runInlineTest }) => {
|
|||
expect(result.output).toMatch(/^.*attachment #1: name \(text\/plain\).*\n.*\n.*──────/gm);
|
||||
});
|
||||
|
||||
test(`testInfo.attach allow without options`, async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('success', async ({}, testInfo) => {
|
||||
await testInfo.attach('Full name');
|
||||
expect(0).toBe(1);
|
||||
});
|
||||
`,
|
||||
});
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(result.output).toMatch(/^.*attachment #1: Full name \(text\/plain\).*\n.*──────/gm);
|
||||
});
|
||||
|
||||
test(`testInfo.attach allow empty buffer body`, async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
|
|
|
|||
Loading…
Reference in a new issue