feat(html-report): add url fields for rendered.

This commit is contained in:
osohyun0224 2024-06-09 16:56:48 +09:00
parent e8fc8af22e
commit 68c6defbc3
13 changed files with 44 additions and 20 deletions

View file

@ -182,7 +182,7 @@ You can also annotate all tests in a group or provide multiple annotations:
import { test, expect } from '@playwright/test';
test.describe('report tests', {
annotation: { type: 'category', description: 'report' },
annotation: { type: 'category', description: 'report', url: 'https://github.com/microsoft/playwright/issues/23180'},
}, () => {
test('test report header', async ({ page }) => {
// ...
@ -192,6 +192,7 @@ test.describe('report tests', {
annotation: [
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/23180' },
{ type: 'performance', description: 'very slow test!' },
{ type: 'performance', description: 'very slow test!', url: 'https://github.com/microsoft/playwright/issues/23180'},
],
}, async ({ page }) => {
// ...

View file

@ -176,7 +176,10 @@ function cacheSearchValues(test: TestCaseSummary): SearchValues {
line: String(test.location.line),
column: String(test.location.column),
labels: test.tags.map(tag => tag.toLowerCase()),
annotations: test.annotations.map(a => a.type.toLowerCase() + '=' + a.description?.toLocaleLowerCase())
annotations: test.annotations.map(a => {
const value = a.description?.toLocaleLowerCase() || a.url?.toLocaleLowerCase() || '';
return a.type.toLowerCase() + '=' + value;
}),
};
(test as any)[searchValuesSymbol] = searchValues;
return searchValues;

View file

@ -80,7 +80,7 @@ export type TestEndPayload = {
errors: TestInfoError[];
hasNonRetriableError: boolean;
expectedStatus: TestStatus;
annotations: { type: string, description?: string }[];
annotations: { type: string, description?: string, url?: string }[];
timeout: number;
};

View file

@ -221,12 +221,13 @@ export class TestTypeImpl {
}
if (typeof modifierArgs[0] === 'function') {
suite._modifiers.push({ type, fn: modifierArgs[0], location, description: modifierArgs[1] });
suite._modifiers.push({ type, fn: modifierArgs[0], location, description: modifierArgs[1], url: modifierArgs[1] });
} else {
if (modifierArgs.length >= 1 && !modifierArgs[0])
return;
const description = modifierArgs[1];
suite._staticAnnotations.push({ type, description });
const url = modifierArgs[1];
suite._staticAnnotations.push({ type, description, url });
}
return;
}

View file

@ -69,14 +69,14 @@ export type JsonTestCase = {
retries: number;
tags?: string[];
repeatEachIndex: number;
annotations?: { type: string, description?: string }[];
annotations?: { type: string, description?: string, url?: string }[];
};
export type JsonTestEnd = {
testId: string;
expectedStatus: reporterTypes.TestStatus;
timeout: number;
annotations: { type: string, description?: string }[];
annotations: { type: string, description?: string, url?: string }[];
};
export type JsonTestResultStart = {

View file

@ -380,7 +380,11 @@ class HtmlBuilder {
location,
duration,
// Annotations can be pushed directly, with a wrong type.
annotations: test.annotations.map(a => ({ type: a.type, description: a.description ? String(a.description) : a.description })),
annotations: test.annotations.map(a => ({
type: a.type,
description: a.description ? String(a.description) : a.description,
url: a.url ? String(a.url) : a.url
})),
tags: test.tags,
outcome: test.outcome(),
path,
@ -394,7 +398,11 @@ class HtmlBuilder {
location,
duration,
// Annotations can be pushed directly, with a wrong type.
annotations: test.annotations.map(a => ({ type: a.type, description: a.description ? String(a.description) : a.description })),
annotations: test.annotations.map(a => ({
type: a.type,
description: a.description ? String(a.description) : a.description,
url: a.url ? String(a.url) : a.url
})),
tags: test.tags,
outcome: test.outcome(),
path,

View file

@ -71,7 +71,7 @@ export type JsonTestEnd = {
testId: string;
expectedStatus: reporterTypes.TestStatus;
timeout: number;
annotations: { type: string, description?: string }[];
annotations: { type: string, description?: string, url?: string }[];
};
export type JsonTestResultStart = {

View file

@ -198,7 +198,7 @@ export class TestInfoImpl implements TestInfo {
this._tracing = new TestTracing(this, workerParams.artifactsDir);
}
private _modifier(type: 'skip' | 'fail' | 'fixme' | 'slow', modifierArgs: [arg?: any, description?: string]) {
private _modifier(type: 'skip' | 'fail' | 'fixme' | 'slow', modifierArgs: [arg?: any, description?: string, url?: string]) {
if (typeof modifierArgs[1] === 'function') {
throw new Error([
'It looks like you are calling test.skip() inside the test and pass a callback.',
@ -473,19 +473,19 @@ export class TestInfoImpl implements TestInfo {
return path.normalize(path.resolve(this._configInternal.configDir, snapshotPath));
}
skip(...args: [arg?: any, description?: string]) {
skip(...args: [arg?: any, description?: string, url?: string]) {
this._modifier('skip', args);
}
fixme(...args: [arg?: any, description?: string]) {
fixme(...args: [arg?: any, description?: string, url?: string]) {
this._modifier('fixme', args);
}
fail(...args: [arg?: any, description?: string]) {
fail(...args: [arg?: any, description?: string, url?: string]) {
this._modifier('fail', args);
}
slow(...args: [arg?: any, description?: string]) {
slow(...args: [arg?: any, description?: string, url?: string]) {
this._modifier('slow', args);
}

View file

@ -1845,6 +1845,7 @@ export type TestStatus = 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interru
type TestDetailsAnnotation = {
type: string;
description?: string;
url?: string;
};
export type TestDetails = {
@ -8175,6 +8176,11 @@ export interface TestInfo {
* Optional description.
*/
description?: string;
/**
* Optional url.
*/
url?: string;
}>;
/**

View file

@ -273,7 +273,7 @@ export interface JSONReportSpec {
export interface JSONReportTest {
timeout: number;
annotations: { type: string, description?: string }[],
annotations: { type: string, description?: string, url?: string }[],
expectedStatus: TestStatus;
projectName: string;
projectId: string;
@ -481,6 +481,11 @@ export interface TestCase {
* Optional description.
*/
description?: string;
/**
* Optional url.
*/
url?: string;
}>;
/**

View file

@ -744,16 +744,15 @@ test.info().annotations.push({ type: 'issue', description: 'I am not interested
'a.test.js': `
import { test, expect } from '@playwright/test';
test('annotated test', async ({ page }) => {
test.info().annotations.push({ type: 'issue', description: 'I add URL field to annotations for hyperlink display', url: 'https://github.com/microsoft/playwright/pull/31014' });
test.info().annotations.push({ type: 'url', url: 'https://github.com/microsoft/playwright/pull/31014' });
});
`,
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
await showReport();
await page.click('text=annotated test');
await expect(page.locator('.test-case-annotation')).toHaveText('issue: I am not interested in this test');
await expect(page.locator('.test-case-annotation')).toHaveText(`url: https://github.com/microsoft/playwright/pull/31014`);
});
test('should render annotations as link if needed', async ({ runInlineTest, page, showReport, server }) => {

View file

@ -68,6 +68,7 @@ export type TestStatus = 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interru
type TestDetailsAnnotation = {
type: string;
description?: string;
url?: string;
};
export type TestDetails = {

View file

@ -94,7 +94,7 @@ export interface JSONReportSpec {
export interface JSONReportTest {
timeout: number;
annotations: { type: string, description?: string }[],
annotations: { type: string, description?: string, url?: string }[],
expectedStatus: TestStatus;
projectName: string;
projectId: string;