feat(html): render annotations as links if needed (#21165)

Fixes https://github.com/microsoft/playwright/issues/20584
This commit is contained in:
Max Schmitt 2023-02-23 21:57:02 +01:00 committed by GitHub
parent 7626267ec5
commit f3a46f7405
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 6 deletions

View File

@ -14,7 +14,7 @@
limitations under the License.
*/
import type { TestCase } from './types';
import type { TestCase, TestCaseAnnotation } from './types';
import * as React from 'react';
import { TabbedPane } from './tabbedPane';
import { AutoChip } from './chip';
@ -38,10 +38,7 @@ export const TestCaseView: React.FC<{
{test && <div className='test-case-location'>{test.location.file}:{test.location.line}</div>}
{test && !!test.projectName && <ProjectLink projectNames={projectNames} projectName={test.projectName}></ProjectLink>}
{test && !!test.annotations.length && <AutoChip header='Annotations'>
{test.annotations.map(a => <div className='test-case-annotation'>
<span style={{ fontWeight: 'bold' }}>{a.type}</span>
{a.description && <span>: {a.description}</span>}
</div>)}
{test.annotations.map(annotation => <TestCaseAnnotationView annotation={annotation} />)}
</AutoChip>}
{test && <TabbedPane tabs={
test.results.map((result, index) => ({
@ -52,6 +49,23 @@ export const TestCaseView: React.FC<{
</div>;
};
function renderAnnotationDescription(description: string) {
try {
if (['http:', 'https:'].includes(new URL(description).protocol))
return <a href={description} target='_blank' rel='noopener noreferrer'>{description}</a>;
} catch {}
return description;
}
function TestCaseAnnotationView({ annotation: { type, description } }: { annotation: TestCaseAnnotation }) {
return (
<div className='test-case-annotation'>
<span style={{ fontWeight: 'bold' }}>{type}</span>
{description && <span>: {renderAnnotationDescription(description)}</span>}
</div>
);
}
function retryLabel(index: number) {
if (!index)
return 'Run';

View File

@ -52,13 +52,15 @@ export type TestFileSummary = {
stats: Stats;
};
export type TestCaseAnnotation = { type: string, description?: string };
export type TestCaseSummary = {
testId: string,
title: string;
path: string[];
projectName: string;
location: Location;
annotations: { type: string, description?: string }[];
annotations: TestCaseAnnotation[];
outcome: 'skipped' | 'expected' | 'unexpected' | 'flaky';
duration: number;
ok: boolean;

View File

@ -577,6 +577,30 @@ test('should render annotations', async ({ runInlineTest, page, showReport }) =>
await expect(page.locator('.test-case-annotation')).toHaveText('skip: I am not interested in this test');
});
test('should render annotations as link if needed', async ({ runInlineTest, page, showReport, server }) => {
const result = await runInlineTest({
'playwright.config.js': `
module.exports = { timeout: 1500 };
`,
'a.test.js': `
import { test, expect } from '@playwright/test';
test('pass test', async ({ page }) => {
test.info().annotations.push({ type: 'issue', description: '${server.EMPTY_PAGE}' });
});
`,
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
await showReport();
await page.getByText('pass test').click();
await expect(page.locator('.test-case-annotation')).toHaveText(`issue: ${server.EMPTY_PAGE}`);
const popupPromise = page.waitForEvent('popup');
await page.getByRole('link', { name: server.EMPTY_PAGE }).click();
const popup = await popupPromise;
expect(popup.url()).toBe(server.EMPTY_PAGE);
});
test('should render text attachments as text', async ({ runInlineTest, page, showReport }) => {
const result = await runInlineTest({
'a.test.js': `