chore: clean up git commit metadata props and UI (#34867)

This commit is contained in:
Pavel Feldman 2025-02-25 09:21:17 -08:00 committed by GitHub
parent b148cbad76
commit 411f938296
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 129 additions and 162 deletions

View file

@ -239,7 +239,7 @@ export default defineConfig({
Metadata contains key-value pairs to be included in the report. For example, HTML report will display it as key-value pairs, and JSON report will include metadata serialized as json. Metadata contains key-value pairs to be included in the report. For example, HTML report will display it as key-value pairs, and JSON report will include metadata serialized as json.
See also [`property: TestConfig.populateGitInfo`] that populates metadata. Providing `'git.commit.info': {}` property will populate it with the git commit details. This is useful for CI/CD environments.
**Usage** **Usage**
@ -326,26 +326,6 @@ This path will serve as the base directory for each test file snapshot directory
## property: TestConfig.snapshotPathTemplate = %%-test-config-snapshot-path-template-%% ## property: TestConfig.snapshotPathTemplate = %%-test-config-snapshot-path-template-%%
* since: v1.28 * since: v1.28
## property: TestConfig.populateGitInfo
* since: v1.51
- type: ?<[boolean]>
Whether to populate `'git.commit.info'` field of the [`property: TestConfig.metadata`] with Git commit info and CI/CD information.
This information will appear in the HTML and JSON reports and is available in the Reporter API.
On Github Actions, this feature is enabled by default.
**Usage**
```js title="playwright.config.ts"
import { defineConfig } from '@playwright/test';
export default defineConfig({
populateGitInfo: !!process.env.CI,
});
```
## property: TestConfig.preserveOutput ## property: TestConfig.preserveOutput
* since: v1.10 * since: v1.10
- type: ?<[PreserveOutput]<"always"|"never"|"failures-only">> - type: ?<[PreserveOutput]<"always"|"never"|"failures-only">>

View file

@ -37,10 +37,6 @@
line-height: 24px; line-height: 24px;
} }
.metadata-section {
align-items: center;
}
.metadata-properties { .metadata-properties {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -57,9 +53,8 @@
border-bottom: 1px solid var(--color-border-default); border-bottom: 1px solid var(--color-border-default);
} }
.git-commit-info a { .metadata-view a {
color: var(--color-fg-default); color: var(--color-fg-default);
font-weight: 600;
} }
.copyable-property { .copyable-property {

View file

@ -87,12 +87,12 @@ const InnerMetadataView = () => {
<GitCommitInfoView info={gitCommitInfo}/> <GitCommitInfoView info={gitCommitInfo}/>
{entries.length > 0 && <div className='metadata-separator' />} {entries.length > 0 && <div className='metadata-separator' />}
</>} </>}
<div className='metadata-section metadata-properties'> <div className='metadata-section metadata-properties' role='list'>
{entries.map(([propertyName, value]) => { {entries.map(([propertyName, value]) => {
const valueString = typeof value !== 'object' || value === null || value === undefined ? String(value) : JSON.stringify(value); const valueString = typeof value !== 'object' || value === null || value === undefined ? String(value) : JSON.stringify(value);
const trimmedValue = valueString.length > 1000 ? valueString.slice(0, 1000) + '\u2026' : valueString; const trimmedValue = valueString.length > 1000 ? valueString.slice(0, 1000) + '\u2026' : valueString;
return ( return (
<div key={propertyName} className='copyable-property'> <div key={propertyName} className='copyable-property' role='listitem'>
<CopyToClipboardContainer value={valueString}> <CopyToClipboardContainer value={valueString}>
<span style={{ fontWeight: 'bold' }} title={propertyName}>{propertyName}</span> <span style={{ fontWeight: 'bold' }} title={propertyName}>{propertyName}</span>
: <span title={trimmedValue}>{linkifyText(trimmedValue)}</span> : <span title={trimmedValue}>{linkifyText(trimmedValue)}</span>
@ -105,24 +105,21 @@ const InnerMetadataView = () => {
}; };
const GitCommitInfoView: React.FC<{ info: GitCommitInfo }> = ({ info }) => { const GitCommitInfoView: React.FC<{ info: GitCommitInfo }> = ({ info }) => {
const email = info['revision.email'] ? ` <${info['revision.email']}>` : ''; const email = info.revision?.email ? ` <${info.revision?.email}>` : '';
const author = `${info['revision.author'] || ''}${email}`; const author = `${info.revision?.author || ''}${email}`;
let subject = info['revision.subject'] || ''; let subject = info.revision?.subject || '';
let link = info['revision.link']; let link = info.revision?.link;
let shortSubject = info['revision.id']?.slice(0, 7) || 'unknown';
if (info['pull.link'] && info['pull.title']) { if (info.pull_request?.link && info.pull_request?.title) {
subject = info['pull.title']; subject = info.pull_request?.title;
link = info['pull.link']; link = info.pull_request?.link;
shortSubject = link ? 'Pull Request' : '';
} }
const shortTimestamp = Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(info['revision.timestamp']); const shortTimestamp = Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(info.revision?.timestamp);
const longTimestamp = Intl.DateTimeFormat(undefined, { dateStyle: 'full', timeStyle: 'long' }).format(info['revision.timestamp']); const longTimestamp = Intl.DateTimeFormat(undefined, { dateStyle: 'full', timeStyle: 'long' }).format(info.revision?.timestamp);
return <div className='hbox git-commit-info metadata-section'> return <div className='metadata-section' role='list'>
<div className='vbox metadata-properties'> <div role='listitem'>
<div>
{link ? ( {link ? (
<a href={link} target='_blank' rel='noopener noreferrer' title={subject}> <a href={link} target='_blank' rel='noopener noreferrer' title={subject}>
{subject} {subject}
@ -131,21 +128,15 @@ const GitCommitInfoView: React.FC<{ info: GitCommitInfo }> = ({ info }) => {
{subject} {subject}
</span>} </span>}
</div> </div>
<div className='hbox'> <div role='listitem' className='hbox'>
<span className='mr-1'>{author}</span> <span className='mr-1'>{author}</span>
<span title={longTimestamp}> on {shortTimestamp}</span> <span title={longTimestamp}> on {shortTimestamp}</span>
{info['ci.link'] && ( {info.ci?.link && (
<> <>
<span className='mx-2'>·</span> <span className='mx-2'>·</span>
<a href={info['ci.link']} target='_blank' rel='noopener noreferrer' title='CI/CD logs'>Logs</a> <a href={info.ci?.link} target='_blank' rel='noopener noreferrer' title='CI/CD logs'>Logs</a>
</> </>
)} )}
</div> </div>
</div>
{link ? (
<a href={link} target='_blank' rel='noopener noreferrer' title='View commit details'>
{shortSubject}
</a>
) : !!shortSubject && <span>{shortSubject}</span>}
</div>; </div>;
}; };

View file

@ -50,7 +50,7 @@ const PromptButton: React.FC<{
const gitCommitInfo = useGitCommitInfo(); const gitCommitInfo = useGitCommitInfo();
const prompt = React.useMemo(() => fixTestPrompt( const prompt = React.useMemo(() => fixTestPrompt(
error, error,
gitCommitInfo?.['pull.diff'] ?? gitCommitInfo?.['revision.diff'], gitCommitInfo?.pull_request?.diff ?? gitCommitInfo?.revision?.diff,
result?.attachments.find(a => a.name === 'pageSnapshot')?.body result?.attachments.find(a => a.name === 'pageSnapshot')?.body
), [gitCommitInfo, result, error]); ), [gitCommitInfo, result, error]);

View file

@ -48,7 +48,6 @@ export class FullConfigInternal {
readonly plugins: TestRunnerPluginRegistration[]; readonly plugins: TestRunnerPluginRegistration[];
readonly projects: FullProjectInternal[] = []; readonly projects: FullProjectInternal[] = [];
readonly singleTSConfigPath?: string; readonly singleTSConfigPath?: string;
readonly populateGitInfo: boolean;
cliArgs: string[] = []; cliArgs: string[] = [];
cliGrep: string | undefined; cliGrep: string | undefined;
cliGrepInvert: string | undefined; cliGrepInvert: string | undefined;
@ -78,7 +77,6 @@ export class FullConfigInternal {
const privateConfiguration = (userConfig as any)['@playwright/test']; const privateConfiguration = (userConfig as any)['@playwright/test'];
this.plugins = (privateConfiguration?.plugins || []).map((p: any) => ({ factory: p })); this.plugins = (privateConfiguration?.plugins || []).map((p: any) => ({ factory: p }));
this.singleTSConfigPath = pathResolve(configDir, userConfig.tsconfig); this.singleTSConfigPath = pathResolve(configDir, userConfig.tsconfig);
this.populateGitInfo = takeFirst(userConfig.populateGitInfo, defaultPopulateGitInfo);
this.globalSetups = (Array.isArray(userConfig.globalSetup) ? userConfig.globalSetup : [userConfig.globalSetup]).map(s => resolveScript(s, configDir)).filter(script => script !== undefined); this.globalSetups = (Array.isArray(userConfig.globalSetup) ? userConfig.globalSetup : [userConfig.globalSetup]).map(s => resolveScript(s, configDir)).filter(script => script !== undefined);
this.globalTeardowns = (Array.isArray(userConfig.globalTeardown) ? userConfig.globalTeardown : [userConfig.globalTeardown]).map(s => resolveScript(s, configDir)).filter(script => script !== undefined); this.globalTeardowns = (Array.isArray(userConfig.globalTeardown) ? userConfig.globalTeardown : [userConfig.globalTeardown]).map(s => resolveScript(s, configDir)).filter(script => script !== undefined);
@ -301,7 +299,6 @@ function resolveScript(id: string | undefined, rootDir: string): string | undefi
export const defaultGrep = /.*/; export const defaultGrep = /.*/;
export const defaultReporter = process.env.CI ? 'dot' : 'list'; export const defaultReporter = process.env.CI ? 'dot' : 'list';
const defaultPopulateGitInfo = process.env.GITHUB_ACTIONS === 'true';
const configInternalSymbol = Symbol('configInternalSymbol'); const configInternalSymbol = Symbol('configInternalSymbol');

View file

@ -15,16 +15,22 @@
*/ */
export interface GitCommitInfo { export interface GitCommitInfo {
'revision.id'?: string; revision?: {
'revision.author'?: string; id?: string;
'revision.email'?: string; author?: string;
'revision.subject'?: string; email?: string;
'revision.timestamp'?: number | Date; subject?: string;
'revision.link'?: string; timestamp?: number;
'revision.diff'?: string; link?: string;
'pull.link'?: string; diff?: string;
'pull.diff'?: string; },
'pull.base'?: string; pull_request?: {
'pull.title'?: string; link?: string;
'ci.link'?: string; diff?: string;
base?: string;
title?: string;
},
ci?: {
link?: string;
}
} }

View file

@ -26,7 +26,8 @@ import type { GitCommitInfo } from '../isomorphic/types';
const GIT_OPERATIONS_TIMEOUT_MS = 1500; const GIT_OPERATIONS_TIMEOUT_MS = 1500;
export const addGitCommitInfoPlugin = (fullConfig: FullConfigInternal) => { export const addGitCommitInfoPlugin = (fullConfig: FullConfigInternal) => {
if (fullConfig.populateGitInfo) const commitProperty = fullConfig.config.metadata['git.commit.info'];
if (commitProperty && typeof commitProperty === 'object' && Object.keys(commitProperty).length === 0)
fullConfig.plugins.push({ factory: gitCommitInfo }); fullConfig.plugins.push({ factory: gitCommitInfo });
}; };
@ -35,10 +36,10 @@ export const gitCommitInfo = (options?: GitCommitInfoPluginOptions): TestRunnerP
name: 'playwright:git-commit-info', name: 'playwright:git-commit-info',
setup: async (config: FullConfig, configDir: string) => { setup: async (config: FullConfig, configDir: string) => {
const fromEnv = await linksFromEnv(); const commitInfo = await linksFromEnv();
const fromCLI = await gitStatusFromCLI(options?.directory || configDir, fromEnv); await enrichStatusFromCLI(options?.directory || configDir, commitInfo);
config.metadata = config.metadata || {}; config.metadata = config.metadata || {};
config.metadata['git.commit.info'] = { ...fromEnv, ...fromCLI }; config.metadata['git.commit.info'] = commitInfo;
}, },
}; };
}; };
@ -47,28 +48,39 @@ interface GitCommitInfoPluginOptions {
directory?: string; directory?: string;
} }
async function linksFromEnv() { async function linksFromEnv(): Promise<GitCommitInfo> {
const out: Partial<GitCommitInfo> = {}; const out: GitCommitInfo = {};
// Jenkins: https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables // Jenkins: https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables
if (process.env.BUILD_URL) if (process.env.BUILD_URL) {
out['ci.link'] = process.env.BUILD_URL; out.ci = out.ci || {};
out.ci.link = process.env.BUILD_URL;
}
// GitLab: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html // GitLab: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html
if (process.env.CI_PROJECT_URL && process.env.CI_COMMIT_SHA) if (process.env.CI_PROJECT_URL && process.env.CI_COMMIT_SHA) {
out['revision.link'] = `${process.env.CI_PROJECT_URL}/-/commit/${process.env.CI_COMMIT_SHA}`; out.revision = out.revision || {};
if (process.env.CI_JOB_URL) out.revision.link = `${process.env.CI_PROJECT_URL}/-/commit/${process.env.CI_COMMIT_SHA}`;
out['ci.link'] = process.env.CI_JOB_URL; }
if (process.env.CI_JOB_URL) {
out.ci = out.ci || {};
out.ci.link = process.env.CI_JOB_URL;
}
// GitHub: https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables // GitHub: https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_SHA) if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_SHA) {
out['revision.link'] = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/commit/${process.env.GITHUB_SHA}`; out.revision = out.revision || {};
if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) out.revision.link = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/commit/${process.env.GITHUB_SHA}`;
out['ci.link'] = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`; }
if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {
out.ci = out.ci || {};
out.ci.link = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
}
if (process.env.GITHUB_EVENT_PATH) { if (process.env.GITHUB_EVENT_PATH) {
try { try {
const json = JSON.parse(await fs.promises.readFile(process.env.GITHUB_EVENT_PATH, 'utf8')); const json = JSON.parse(await fs.promises.readFile(process.env.GITHUB_EVENT_PATH, 'utf8'));
if (json.pull_request) { if (json.pull_request) {
out['pull.title'] = json.pull_request.title; out.pull_request = out.pull_request || {};
out['pull.link'] = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/pull/${json.pull_request.number}`; out.pull_request.title = json.pull_request.title;
out['pull.base'] = json.pull_request.base.ref; out.pull_request.link = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/pull/${json.pull_request.number}`;
out.pull_request.base = json.pull_request.base.ref;
} }
} catch { } catch {
} }
@ -76,7 +88,7 @@ async function linksFromEnv() {
return out; return out;
} }
async function gitStatusFromCLI(gitDir: string, envInfo: Pick<GitCommitInfo, 'pull.base'>): Promise<GitCommitInfo | undefined> { async function enrichStatusFromCLI(gitDir: string, commitInfo: GitCommitInfo) {
const separator = `:${createGuid().slice(0, 4)}:`; const separator = `:${createGuid().slice(0, 4)}:`;
const commitInfoResult = await spawnAsync( const commitInfoResult = await spawnAsync(
'git', 'git',
@ -90,23 +102,24 @@ async function gitStatusFromCLI(gitDir: string, envInfo: Pick<GitCommitInfo, 'pu
let timestamp: number = Number.parseInt(rawTimestamp, 10); let timestamp: number = Number.parseInt(rawTimestamp, 10);
timestamp = Number.isInteger(timestamp) ? timestamp * 1000 : 0; timestamp = Number.isInteger(timestamp) ? timestamp * 1000 : 0;
const result: GitCommitInfo = { commitInfo.revision = {
'revision.id': id, ...commitInfo.revision,
'revision.author': author, id,
'revision.email': email, author,
'revision.subject': subject, email,
'revision.timestamp': timestamp, subject,
timestamp,
}; };
const diffLimit = 1_000_000; // 1MB const diffLimit = 1_000_000; // 1MB
if (envInfo['pull.base']) { if (commitInfo.pull_request?.base) {
const pullDiffResult = await spawnAsync( const pullDiffResult = await spawnAsync(
'git', 'git',
['diff', envInfo['pull.base']], ['diff', commitInfo.pull_request?.base],
{ stdio: 'pipe', cwd: gitDir, timeout: GIT_OPERATIONS_TIMEOUT_MS } { stdio: 'pipe', cwd: gitDir, timeout: GIT_OPERATIONS_TIMEOUT_MS }
); );
if (!pullDiffResult.code) if (!pullDiffResult.code)
result['pull.diff'] = pullDiffResult.stdout.substring(0, diffLimit); commitInfo.pull_request!.diff = pullDiffResult.stdout.substring(0, diffLimit);
} else { } else {
const diffResult = await spawnAsync( const diffResult = await spawnAsync(
'git', 'git',
@ -114,8 +127,6 @@ async function gitStatusFromCLI(gitDir: string, envInfo: Pick<GitCommitInfo, 'pu
{ stdio: 'pipe', cwd: gitDir, timeout: GIT_OPERATIONS_TIMEOUT_MS } { stdio: 'pipe', cwd: gitDir, timeout: GIT_OPERATIONS_TIMEOUT_MS }
); );
if (!diffResult.code) if (!diffResult.code)
result['revision.diff'] = diffResult.stdout.substring(0, diffLimit); commitInfo.revision!.diff = diffResult.stdout.substring(0, diffLimit);
} }
return result;
} }

View file

@ -1285,9 +1285,8 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
* Metadata contains key-value pairs to be included in the report. For example, HTML report will display it as * Metadata contains key-value pairs to be included in the report. For example, HTML report will display it as
* key-value pairs, and JSON report will include metadata serialized as json. * key-value pairs, and JSON report will include metadata serialized as json.
* *
* See also * Providing `'git.commit.info': {}` property will populate it with the git commit details. This is useful for CI/CD
* [testConfig.populateGitInfo](https://playwright.dev/docs/api/class-testconfig#test-config-populate-git-info) that * environments.
* populates metadata.
* *
* **Usage** * **Usage**
* *
@ -1360,29 +1359,6 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
*/ */
outputDir?: string; outputDir?: string;
/**
* Whether to populate `'git.commit.info'` field of the
* [testConfig.metadata](https://playwright.dev/docs/api/class-testconfig#test-config-metadata) with Git commit info
* and CI/CD information.
*
* This information will appear in the HTML and JSON reports and is available in the Reporter API.
*
* On Github Actions, this feature is enabled by default.
*
* **Usage**
*
* ```js
* // playwright.config.ts
* import { defineConfig } from '@playwright/test';
*
* export default defineConfig({
* populateGitInfo: !!process.env.CI,
* });
* ```
*
*/
populateGitInfo?: boolean;
/** /**
* Whether to preserve test output in the * Whether to preserve test output in the
* [testConfig.outputDir](https://playwright.dev/docs/api/class-testconfig#test-config-output-dir). Defaults to * [testConfig.outputDir](https://playwright.dev/docs/api/class-testconfig#test-config-output-dir). Defaults to

View file

@ -101,7 +101,7 @@ function Error({ message, error, errorId, sdkLanguage, pageSnapshot, revealInSou
const [showLLM, setShowLLM] = React.useState(false); const [showLLM, setShowLLM] = React.useState(false);
const llmAvailable = useIsLLMAvailable(); const llmAvailable = useIsLLMAvailable();
const gitCommitInfo = useGitCommitInfo(); const gitCommitInfo = useGitCommitInfo();
const diff = gitCommitInfo?.['pull.diff'] ?? gitCommitInfo?.['revision.diff']; const diff = gitCommitInfo?.pull_request?.diff ?? gitCommitInfo?.revision?.diff;
let location: string | undefined; let location: string | undefined;
let longLocation: string | undefined; let longLocation: string | undefined;

View file

@ -1187,13 +1187,12 @@ for (const useIntermediateMergeReport of [true, false] as const) {
]); ]);
}); });
test('should include metadata with populateGitInfo = true', async ({ runInlineTest, writeFiles, showReport, page }) => { test('should include metadata with git.commit.info', async ({ runInlineTest, writeFiles, showReport, page }) => {
const files = { const files = {
'uncommitted.txt': `uncommitted file`, 'uncommitted.txt': `uncommitted file`,
'playwright.config.ts': ` 'playwright.config.ts': `
export default { export default {
populateGitInfo: true, metadata: { 'git.commit.info': {}, foo: 'value1', bar: { prop: 'value2' }, baz: ['value3', 123] }
metadata: { foo: 'value1', bar: { prop: 'value2' }, baz: ['value3', 123] }
}; };
`, `,
'example.spec.ts': ` 'example.spec.ts': `
@ -1230,20 +1229,23 @@ for (const useIntermediateMergeReport of [true, false] as const) {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
await page.getByRole('button', { name: 'Metadata' }).click(); await page.getByRole('button', { name: 'Metadata' }).click();
await expect(page.locator('.metadata-view')).toMatchAriaSnapshot(` await expect(page.locator('.metadata-view')).toMatchAriaSnapshot(`
- list:
- listitem:
- 'link "chore(html): make this test look nice"' - 'link "chore(html): make this test look nice"'
- text: /^William <shakespeare@example.local> on/ - listitem: /William <shakespeare@example\\.local>/
- link /^[a-f0-9]{7}$/ - list:
- text: 'foo : value1 bar : {"prop":"value2"} baz : ["value3",123]' - listitem: "foo : value1"
- listitem: "bar : {\\"prop\\":\\"value2\\"}"
- listitem: "baz : [\\"value3\\",123]"
`); `);
}); });
test('should include metadata with populateGitInfo on GHA', async ({ runInlineTest, writeFiles, showReport, page }) => { test('should include metadata with git.commit.info on GHA', async ({ runInlineTest, writeFiles, showReport, page }) => {
const files = { const files = {
'uncommitted.txt': `uncommitted file`, 'uncommitted.txt': `uncommitted file`,
'playwright.config.ts': ` 'playwright.config.ts': `
export default { export default {
populateGitInfo: true, metadata: { 'git.commit.info': {}, foo: 'value1', bar: { prop: 'value2' }, baz: ['value3', 123] }
metadata: { foo: 'value1', bar: { prop: 'value2' }, baz: ['value3', 123] }
}; };
`, `,
'example.spec.ts': ` 'example.spec.ts': `
@ -1291,18 +1293,23 @@ for (const useIntermediateMergeReport of [true, false] as const) {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
await page.getByRole('button', { name: 'Metadata' }).click(); await page.getByRole('button', { name: 'Metadata' }).click();
await expect(page.locator('.metadata-view')).toMatchAriaSnapshot(` await expect(page.locator('.metadata-view')).toMatchAriaSnapshot(`
- 'link "My PR"' - list:
- text: /^William <shakespeare@example.local> on/ - listitem:
- link "My PR"
- listitem:
- text: /William <shakespeare@example\\.local>/
- link "Logs" - link "Logs"
- link "Pull Request" - list:
- text: 'foo : value1 bar : {"prop":"value2"} baz : ["value3",123]' - listitem: "foo : value1"
- listitem: "bar : {\\"prop\\":\\"value2\\"}"
- listitem: "baz : [\\"value3\\",123]"
`); `);
}); });
test('should not include git metadata with populateGitInfo = false', async ({ runInlineTest, showReport, page }) => { test('should not include git metadata w/o git.commit.info', async ({ runInlineTest, showReport, page }) => {
const result = await runInlineTest({ const result = await runInlineTest({
'playwright.config.ts': ` 'playwright.config.ts': `
export default { populateGitInfo: false }; export default {};
`, `,
'example.spec.ts': ` 'example.spec.ts': `
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
@ -1323,7 +1330,7 @@ for (const useIntermediateMergeReport of [true, false] as const) {
'playwright.config.ts': ` 'playwright.config.ts': `
export default { export default {
metadata: { metadata: {
'git.commit.info': { 'revision.timestamp': 'hi' } 'git.commit.info': { revision: { timestamp: 'hi' } }
}, },
}; };
`, `,
@ -2757,8 +2764,12 @@ for (const useIntermediateMergeReport of [true, false] as const) {
'uncommitted.txt': `uncommitted file`, 'uncommitted.txt': `uncommitted file`,
'playwright.config.ts': ` 'playwright.config.ts': `
export default { export default {
populateGitInfo: true, metadata: {
metadata: { foo: 'value1', bar: { prop: 'value2' }, baz: ['value3', 123] } 'git.commit.info': {},
foo: 'value1',
bar: { prop: 'value2' },
baz: ['value3', 123]
}
}; };
`, `,
'example.spec.ts': ` 'example.spec.ts': `

View file

@ -21,14 +21,14 @@ test('should render html report git info metadata', async ({ runUITest }) => {
'reporter.ts': ` 'reporter.ts': `
module.exports = class Reporter { module.exports = class Reporter {
onBegin(config, suite) { onBegin(config, suite) {
console.log('ci.link:', config.metadata['git.commit.info']['ci.link']); console.log('ci.link:', config.metadata['git.commit.info'].ci.link);
} }
} }
`, `,
'playwright.config.ts': ` 'playwright.config.ts': `
import { defineConfig } from '@playwright/test'; import { defineConfig } from '@playwright/test';
export default defineConfig({ export default defineConfig({
populateGitInfo: true, metadata: { 'git.commit.info': {} },
reporter: './reporter.ts', reporter: './reporter.ts',
}); });
`, `,