diff --git a/packages/html-reporter/src/metadataView.tsx b/packages/html-reporter/src/metadataView.tsx
index 03a5ed06f4..0f54bb4249 100644
--- a/packages/html-reporter/src/metadataView.tsx
+++ b/packages/html-reporter/src/metadataView.tsx
@@ -107,14 +107,24 @@ const InnerMetadataView = () => {
const GitCommitInfoView: React.FC<{ info: GitCommitInfo }> = ({ info }) => {
const email = info['revision.email'] ? ` <${info['revision.email']}>` : '';
const author = `${info['revision.author'] || ''}${email}`;
- const subject = info['revision.subject'] || '';
+
+ let subject = info['revision.subject'] || '';
+ let link = info['revision.link'];
+ let shortSubject = info['revision.id']?.slice(0, 7) || 'unknown';
+
+ if (info['pull.link'] && info['pull.title']) {
+ subject = info['pull.title'];
+ link = info['pull.link'];
+ shortSubject = link ? 'Pull Request' : '';
+ }
+
const shortTimestamp = Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(info['revision.timestamp']);
const longTimestamp = Intl.DateTimeFormat(undefined, { dateStyle: 'full', timeStyle: 'long' }).format(info['revision.timestamp']);
return
;
};
diff --git a/packages/playwright/src/isomorphic/types.d.ts b/packages/playwright/src/isomorphic/types.d.ts
index 213f350514..2619c0df33 100644
--- a/packages/playwright/src/isomorphic/types.d.ts
+++ b/packages/playwright/src/isomorphic/types.d.ts
@@ -25,5 +25,6 @@ export interface GitCommitInfo {
'pull.link'?: string;
'pull.diff'?: string;
'pull.base'?: string;
+ 'pull.title'?: string;
'ci.link'?: string;
}
diff --git a/packages/playwright/src/plugins/gitCommitInfoPlugin.ts b/packages/playwright/src/plugins/gitCommitInfoPlugin.ts
index 29010183c7..7ade4a005c 100644
--- a/packages/playwright/src/plugins/gitCommitInfoPlugin.ts
+++ b/packages/playwright/src/plugins/gitCommitInfoPlugin.ts
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import fs from 'fs';
+
import { createGuid, spawnAsync } from 'playwright-core/lib/utils';
import type { TestRunnerPlugin } from './';
@@ -33,7 +35,7 @@ export const gitCommitInfo = (options?: GitCommitInfoPluginOptions): TestRunnerP
name: 'playwright:git-commit-info',
setup: async (config: FullConfig, configDir: string) => {
- const fromEnv = linksFromEnv();
+ const fromEnv = await linksFromEnv();
const fromCLI = await gitStatusFromCLI(options?.directory || configDir, fromEnv);
config.metadata = config.metadata || {};
config.metadata['git.commit.info'] = { ...fromEnv, ...fromCLI };
@@ -45,7 +47,7 @@ interface GitCommitInfoPluginOptions {
directory?: string;
}
-function linksFromEnv() {
+async function linksFromEnv() {
const out: Partial = {};
// Jenkins: https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables
if (process.env.BUILD_URL)
@@ -55,15 +57,21 @@ function linksFromEnv() {
out['revision.link'] = `${process.env.CI_PROJECT_URL}/-/commit/${process.env.CI_COMMIT_SHA}`;
if (process.env.CI_JOB_URL)
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)
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;
+ if (process.env.GITHUB_EVENT_PATH) {
+ try {
+ const json = JSON.parse(await fs.promises.readFile(process.env.GITHUB_EVENT_PATH, 'utf8'));
+ if (json.pull_request) {
+ out['pull.title'] = json.pull_request.title;
+ out['pull.link'] = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/pull/${json.pull_request.number}`;
+ out['pull.base'] = json.pull_request.base.ref;
+ }
+ } catch {
+ }
}
return out;
}
diff --git a/tests/playwright-test/playwright-test-fixtures.ts b/tests/playwright-test/playwright-test-fixtures.ts
index 789ed1feb6..43503497d2 100644
--- a/tests/playwright-test/playwright-test-fixtures.ts
+++ b/tests/playwright-test/playwright-test-fixtures.ts
@@ -227,6 +227,7 @@ export function cleanEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
GITHUB_RUN_ID: undefined,
GITHUB_SERVER_URL: undefined,
GITHUB_SHA: undefined,
+ GITHUB_EVENT_PATH: undefined,
// END: Reserved CI
PW_TEST_HTML_REPORT_OPEN: undefined,
PLAYWRIGHT_HTML_OPEN: undefined,
diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts
index bf88f52ade..78ef741415 100644
--- a/tests/playwright-test/reporter-html.spec.ts
+++ b/tests/playwright-test/reporter-html.spec.ts
@@ -1221,11 +1221,8 @@ for (const useIntermediateMergeReport of [true, false] as const) {
const result = await runInlineTest(files, { reporter: 'dot,html' }, {
PLAYWRIGHT_HTML_OPEN: 'never',
GITHUB_REPOSITORY: 'microsoft/playwright-example-for-test',
- 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();
@@ -1235,9 +1232,69 @@ 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 /^[a-f0-9]{7}$/
+ - text: 'foo : value1 bar : {"prop":"value2"} baz : ["value3",123]'
+ `);
+ });
+
+ test('should include metadata with populateGitInfo on GHA', async ({ runInlineTest, writeFiles, showReport, page }) => {
+ const files = {
+ 'uncommitted.txt': `uncommitted file`,
+ 'playwright.config.ts': `
+ export default {
+ populateGitInfo: true,
+ metadata: { foo: 'value1', bar: { prop: 'value2' }, baz: ['value3', 123] }
+ };
+ `,
+ 'example.spec.ts': `
+ import { test, expect } from '@playwright/test';
+ test('sample', async ({}) => { expect(2).toBe(2); });
+ `,
+ };
+ const baseDir = await writeFiles(files);
+
+ const execGit = async (args: string[]) => {
+ const { code, stdout, stderr } = await spawnAsync('git', args, { stdio: 'pipe', cwd: baseDir });
+ if (!!code)
+ throw new Error(`Non-zero exit of:\n$ git ${args.join(' ')}\nConsole:\nstdout:\n${stdout}\n\nstderr:\n${stderr}\n\n`);
+ return;
+ };
+
+ 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']);
+
+ const eventPath = path.join(baseDir, 'event.json');
+ await fs.promises.writeFile(eventPath, JSON.stringify({
+ pull_request: {
+ title: 'My PR',
+ number: 42,
+ base: { ref: 'main' },
+ },
+ }));
+
+ const result = await runInlineTest(files, { reporter: 'dot,html' }, {
+ PLAYWRIGHT_HTML_OPEN: 'never',
+ GITHUB_REPOSITORY: 'microsoft/playwright-example-for-test',
+ GITHUB_RUN_ID: 'example-run-id',
+ GITHUB_SERVER_URL: 'https://playwright.dev',
+ GITHUB_SHA: 'example-sha',
+ GITHUB_EVENT_PATH: eventPath,
+ });
+
+ await showReport();
+
+ expect(result.exitCode).toBe(0);
+ await page.getByRole('button', { name: 'Metadata' }).click();
+ await expect(page.locator('.metadata-view')).toMatchAriaSnapshot(`
+ - 'link "My PR"'
+ - text: /^William on/
- link "Logs"
- link "Pull Request"
- - link /^[a-f0-9]{7}$/
- text: 'foo : value1 bar : {"prop":"value2"} baz : ["value3",123]'
`);
});