diff --git a/packages/html-reporter/src/metadataView.tsx b/packages/html-reporter/src/metadataView.tsx index 188de10fda..8cc66571ab 100644 --- a/packages/html-reporter/src/metadataView.tsx +++ b/packages/html-reporter/src/metadataView.tsx @@ -95,7 +95,18 @@ const GitCommitInfoView: React.FC<{ info: GitCommitInfo }> = ({ info }) => {
{author}
on {shortTimestamp}
- {info['ci.link'] && <>·logs} + {info['ci.link'] && ( + <> + · + Logs + + )} + {info['pull.link'] && ( + <> + · + Pull Request + + )}
{!!info['revision.link'] && diff --git a/packages/playwright/src/isomorphic/types.d.ts b/packages/playwright/src/isomorphic/types.d.ts index 72c6db3533..213f350514 100644 --- a/packages/playwright/src/isomorphic/types.d.ts +++ b/packages/playwright/src/isomorphic/types.d.ts @@ -21,5 +21,9 @@ export interface GitCommitInfo { 'revision.subject'?: string; 'revision.timestamp'?: number | Date; 'revision.link'?: string; + 'revision.diff'?: string; + 'pull.link'?: string; + 'pull.diff'?: string; + 'pull.base'?: string; 'ci.link'?: string; } diff --git a/packages/playwright/src/plugins/gitCommitInfoPlugin.ts b/packages/playwright/src/plugins/gitCommitInfoPlugin.ts index 4c972759d0..e96c122cc6 100644 --- a/packages/playwright/src/plugins/gitCommitInfoPlugin.ts +++ b/packages/playwright/src/plugins/gitCommitInfoPlugin.ts @@ -33,7 +33,7 @@ export const gitCommitInfo = (options?: GitCommitInfoPluginOptions): TestRunnerP setup: async (config: FullConfig, configDir: string) => { const fromEnv = linksFromEnv(); - const fromCLI = await gitStatusFromCLI(options?.directory || configDir); + const fromCLI = await gitStatusFromCLI(options?.directory || configDir, fromEnv); config.metadata = config.metadata || {}; config.metadata['git.commit.info'] = { ...fromEnv, ...fromCLI }; }, @@ -44,8 +44,8 @@ interface GitCommitInfoPluginOptions { directory?: string; } -function linksFromEnv(): Pick { - const out: { 'revision.link'?: string; 'ci.link'?: string; } = {}; +function linksFromEnv() { + const out: Partial = {}; // Jenkins: https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables if (process.env.BUILD_URL) out['ci.link'] = process.env.BUILD_URL; @@ -59,28 +59,54 @@ function linksFromEnv(): Pick { out['revision.link'] = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/commit/${process.env.GITHUB_SHA}`; if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) out['ci.link'] = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`; + if (process.env.GITHUB_REF_NAME && process.env.GITHUB_REF_NAME.endsWith('/merge')) { + const pullId = process.env.GITHUB_REF_NAME.substring(0, process.env.GITHUB_REF_NAME.indexOf('/merge')); + out['pull.link'] = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/pull/${pullId}`; + out['pull.base'] = process.env.GITHUB_BASE_REF; + } return out; } -async function gitStatusFromCLI(gitDir: string): Promise { +async function gitStatusFromCLI(gitDir: string, envInfo: Pick): Promise { const separator = `:${createGuid().slice(0, 4)}:`; - const { code, stdout } = await spawnAsync( + const commitInfoResult = await spawnAsync( 'git', ['show', '-s', `--format=%H${separator}%s${separator}%an${separator}%ae${separator}%ct`, 'HEAD'], { stdio: 'pipe', cwd: gitDir, timeout: GIT_OPERATIONS_TIMEOUT_MS } ); - if (code) + if (commitInfoResult.code) return; - const showOutput = stdout.trim(); + const showOutput = commitInfoResult.stdout.trim(); const [id, subject, author, email, rawTimestamp] = showOutput.split(separator); let timestamp: number = Number.parseInt(rawTimestamp, 10); timestamp = Number.isInteger(timestamp) ? timestamp * 1000 : 0; - return { + const result: GitCommitInfo = { 'revision.id': id, 'revision.author': author, 'revision.email': email, 'revision.subject': subject, 'revision.timestamp': timestamp, }; + + const diffLimit = 1_000_000; // 1MB + if (envInfo['pull.base']) { + const pullDiffResult = await spawnAsync( + 'git', + ['diff', envInfo['pull.base']], + { stdio: 'pipe', cwd: gitDir, timeout: GIT_OPERATIONS_TIMEOUT_MS } + ); + if (!pullDiffResult.code) + result['pull.diff'] = pullDiffResult.stdout.substring(0, diffLimit); + } else { + const diffResult = await spawnAsync( + 'git', + ['diff', 'HEAD~1'], + { stdio: 'pipe', cwd: gitDir, timeout: GIT_OPERATIONS_TIMEOUT_MS } + ); + if (!diffResult.code) + result['revision.diff'] = diffResult.stdout.substring(0, diffLimit); + } + + return result; } diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index fdb026d66c..d5cc7ed50f 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -1213,6 +1213,8 @@ for (const useIntermediateMergeReport of [true, false] as const) { await execGit(['init']); await execGit(['config', '--local', 'user.email', 'shakespeare@example.local']); await execGit(['config', '--local', 'user.name', 'William']); + await execGit(['add', 'playwright.config.ts']); + await execGit(['commit', '-m', 'init']); await execGit(['add', '*.ts']); await execGit(['commit', '-m', 'chore(html): make this test look nice']); @@ -1222,6 +1224,8 @@ for (const useIntermediateMergeReport of [true, false] as const) { GITHUB_RUN_ID: 'example-run-id', GITHUB_SERVER_URL: 'https://playwright.dev', GITHUB_SHA: 'example-sha', + GITHUB_REF_NAME: '42/merge', + GITHUB_BASE_REF: 'HEAD~1', }); await showReport(); @@ -1231,7 +1235,8 @@ for (const useIntermediateMergeReport of [true, false] as const) { await expect(page.locator('.metadata-view')).toMatchAriaSnapshot(` - 'link "chore(html): make this test look nice"' - text: /^William on/ - - link "logs" + - link "Logs" + - link "Pull Request" - link /^[a-f0-9]{7}$/ - text: 'foo: value1 bar: {"prop":"value2"} baz: ["value3",123]' `);