mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(html): render annotations as links if needed (#21165)
Fixes https://github.com/microsoft/playwright/issues/20584
This commit is contained in:
parent
7626267ec5
commit
f3a46f7405
@ -14,7 +14,7 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { TestCase } from './types';
|
import type { TestCase, TestCaseAnnotation } from './types';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { TabbedPane } from './tabbedPane';
|
import { TabbedPane } from './tabbedPane';
|
||||||
import { AutoChip } from './chip';
|
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 && <div className='test-case-location'>{test.location.file}:{test.location.line}</div>}
|
||||||
{test && !!test.projectName && <ProjectLink projectNames={projectNames} projectName={test.projectName}></ProjectLink>}
|
{test && !!test.projectName && <ProjectLink projectNames={projectNames} projectName={test.projectName}></ProjectLink>}
|
||||||
{test && !!test.annotations.length && <AutoChip header='Annotations'>
|
{test && !!test.annotations.length && <AutoChip header='Annotations'>
|
||||||
{test.annotations.map(a => <div className='test-case-annotation'>
|
{test.annotations.map(annotation => <TestCaseAnnotationView annotation={annotation} />)}
|
||||||
<span style={{ fontWeight: 'bold' }}>{a.type}</span>
|
|
||||||
{a.description && <span>: {a.description}</span>}
|
|
||||||
</div>)}
|
|
||||||
</AutoChip>}
|
</AutoChip>}
|
||||||
{test && <TabbedPane tabs={
|
{test && <TabbedPane tabs={
|
||||||
test.results.map((result, index) => ({
|
test.results.map((result, index) => ({
|
||||||
@ -52,6 +49,23 @@ export const TestCaseView: React.FC<{
|
|||||||
</div>;
|
</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) {
|
function retryLabel(index: number) {
|
||||||
if (!index)
|
if (!index)
|
||||||
return 'Run';
|
return 'Run';
|
||||||
|
|||||||
@ -52,13 +52,15 @@ export type TestFileSummary = {
|
|||||||
stats: Stats;
|
stats: Stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TestCaseAnnotation = { type: string, description?: string };
|
||||||
|
|
||||||
export type TestCaseSummary = {
|
export type TestCaseSummary = {
|
||||||
testId: string,
|
testId: string,
|
||||||
title: string;
|
title: string;
|
||||||
path: string[];
|
path: string[];
|
||||||
projectName: string;
|
projectName: string;
|
||||||
location: Location;
|
location: Location;
|
||||||
annotations: { type: string, description?: string }[];
|
annotations: TestCaseAnnotation[];
|
||||||
outcome: 'skipped' | 'expected' | 'unexpected' | 'flaky';
|
outcome: 'skipped' | 'expected' | 'unexpected' | 'flaky';
|
||||||
duration: number;
|
duration: number;
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
|
|||||||
@ -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');
|
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 }) => {
|
test('should render text attachments as text', async ({ runInlineTest, page, showReport }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'a.test.js': `
|
'a.test.js': `
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user