Skip to content

Commit 61d595a

Browse files
vitaletsyury-s
andauthored
feat: add new config prop populateGitInfo (#34329)
Co-authored-by: Yury Semikhatsky <[email protected]>
1 parent cea5dad commit 61d595a

File tree

8 files changed

+121
-5
lines changed

8 files changed

+121
-5
lines changed

docs/src/test-api/class-testconfig.md

+16
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,22 @@ This path will serve as the base directory for each test file snapshot directory
321321
## property: TestConfig.snapshotPathTemplate = %%-test-config-snapshot-path-template-%%
322322
* since: v1.28
323323

324+
## property: TestConfig.populateGitInfo
325+
* since: v1.51
326+
- type: ?<[boolean]>
327+
328+
Whether to populate [`property: TestConfig.metadata`] with Git info. The metadata will automatically appear in the HTML report and is available in Reporter API.
329+
330+
**Usage**
331+
332+
```js title="playwright.config.ts"
333+
import { defineConfig } from '@playwright/test';
334+
335+
export default defineConfig({
336+
populateGitInfo: !!process.env.CI,
337+
});
338+
```
339+
324340
## property: TestConfig.preserveOutput
325341
* since: v1.10
326342
- type: ?<[PreserveOutput]<"always"|"never"|"failures-only">>

packages/playwright/src/common/config.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export class FullConfigInternal {
4646
readonly plugins: TestRunnerPluginRegistration[];
4747
readonly projects: FullProjectInternal[] = [];
4848
readonly singleTSConfigPath?: string;
49+
readonly populateGitInfo: boolean;
4950
cliArgs: string[] = [];
5051
cliGrep: string | undefined;
5152
cliGrepInvert: string | undefined;
@@ -75,10 +76,15 @@ export class FullConfigInternal {
7576
const privateConfiguration = (userConfig as any)['@playwright/test'];
7677
this.plugins = (privateConfiguration?.plugins || []).map((p: any) => ({ factory: p }));
7778
this.singleTSConfigPath = pathResolve(configDir, userConfig.tsconfig);
79+
this.populateGitInfo = takeFirst(userConfig.populateGitInfo, false);
7880

7981
this.globalSetups = (Array.isArray(userConfig.globalSetup) ? userConfig.globalSetup : [userConfig.globalSetup]).map(s => resolveScript(s, configDir)).filter(script => script !== undefined);
8082
this.globalTeardowns = (Array.isArray(userConfig.globalTeardown) ? userConfig.globalTeardown : [userConfig.globalTeardown]).map(s => resolveScript(s, configDir)).filter(script => script !== undefined);
8183

84+
// Make sure we reuse same metadata instance between FullConfigInternal instances,
85+
// so that plugins such as gitCommitInfoPlugin can populate metadata once.
86+
userConfig.metadata = userConfig.metadata || {};
87+
8288
this.config = {
8389
configFile: resolvedConfigFile,
8490
rootDir: pathResolve(configDir, userConfig.testDir) || configDir,
@@ -90,7 +96,7 @@ export class FullConfigInternal {
9096
grep: takeFirst(userConfig.grep, defaultGrep),
9197
grepInvert: takeFirst(userConfig.grepInvert, null),
9298
maxFailures: takeFirst(configCLIOverrides.debug ? 1 : undefined, configCLIOverrides.maxFailures, userConfig.maxFailures, 0),
93-
metadata: takeFirst(userConfig.metadata, {}),
99+
metadata: userConfig.metadata,
94100
preserveOutput: takeFirst(userConfig.preserveOutput, 'always'),
95101
reporter: takeFirst(configCLIOverrides.reporter, resolveReporters(userConfig.reporter, configDir), [[defaultReporter]]),
96102
reportSlowTests: takeFirst(userConfig.reportSlowTests, { max: 5, threshold: 15000 }),

packages/playwright/src/plugins/gitCommitInfoPlugin.ts

+6
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@
1717
import { createGuid, spawnAsync } from 'playwright-core/lib/utils';
1818
import type { TestRunnerPlugin } from './';
1919
import type { FullConfig } from '../../types/testReporter';
20+
import type { FullConfigInternal } from '../common/config';
2021

2122
const GIT_OPERATIONS_TIMEOUT_MS = 1500;
2223

24+
export const addGitCommitInfoPlugin = (fullConfig: FullConfigInternal) => {
25+
if (fullConfig.populateGitInfo)
26+
fullConfig.plugins.push({ factory: gitCommitInfo });
27+
};
28+
2329
export const gitCommitInfo = (options?: GitCommitInfoPluginOptions): TestRunnerPlugin => {
2430
return {
2531
name: 'playwright:git-commit-info',

packages/playwright/src/runner/runner.ts

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import type { FullResult, TestError } from '../../types/testReporter';
1919
import { webServerPluginsForConfig } from '../plugins/webServerPlugin';
20+
import { addGitCommitInfoPlugin } from '../plugins/gitCommitInfoPlugin';
2021
import { collectFilesForProject, filterProjects } from './projectUtils';
2122
import { createErrorCollectingReporter, createReporters } from './reporters';
2223
import { TestRun, createApplyRebaselinesTask, createClearCacheTask, createGlobalSetupTasks, createLoadTask, createPluginSetupTasks, createReportBeginTask, createRunTestsTasks, createStartDevServerTask, runTasks } from './tasks';
@@ -70,6 +71,8 @@ export class Runner {
7071
const config = this._config;
7172
const listOnly = config.cliListOnly;
7273

74+
addGitCommitInfoPlugin(config);
75+
7376
// Legacy webServer support.
7477
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
7578

packages/playwright/src/runner/testServer.ts

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { baseFullConfig } from '../isomorphic/teleReceiver';
3939
import { InternalReporter } from '../reporters/internalReporter';
4040
import type { ReporterV2 } from '../reporters/reporterV2';
4141
import { internalScreen } from '../reporters/base';
42+
import { addGitCommitInfoPlugin } from '../plugins/gitCommitInfoPlugin';
4243

4344
const originalStdoutWrite = process.stdout.write;
4445
const originalStderrWrite = process.stderr.write;
@@ -406,6 +407,7 @@ export class TestServerDispatcher implements TestServerInterface {
406407
// Preserve plugin instances between setup and build.
407408
if (!this._plugins) {
408409
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
410+
addGitCommitInfoPlugin(config);
409411
this._plugins = config.plugins || [];
410412
} else {
411413
config.plugins.splice(0, config.plugins.length, ...this._plugins);

packages/playwright/types/test.d.ts

+18
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,24 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
12931293
*/
12941294
outputDir?: string;
12951295

1296+
/**
1297+
* Whether to populate [testConfig.metadata](https://playwright.dev/docs/api/class-testconfig#test-config-metadata)
1298+
* with Git info. The metadata will automatically appear in the HTML report and is available in Reporter API.
1299+
*
1300+
* **Usage**
1301+
*
1302+
* ```js
1303+
* // playwright.config.ts
1304+
* import { defineConfig } from '@playwright/test';
1305+
*
1306+
* export default defineConfig({
1307+
* populateGitInfo: !!process.env.CI,
1308+
* });
1309+
* ```
1310+
*
1311+
*/
1312+
populateGitInfo?: boolean;
1313+
12961314
/**
12971315
* Whether to preserve test output in the
12981316
* [testConfig.outputDir](https://playwright.dev/docs/api/class-testconfig#test-config-output-dir). Defaults to

tests/playwright-test/reporter-html.spec.ts

+21-4
Original file line numberDiff line numberDiff line change
@@ -1142,14 +1142,12 @@ for (const useIntermediateMergeReport of [true, false] as const) {
11421142
});
11431143

11441144
test.describe('gitCommitInfo plugin', () => {
1145-
test('should include metadata', async ({ runInlineTest, writeFiles, showReport, page }) => {
1145+
test('should include metadata with populateGitInfo = true', async ({ runInlineTest, writeFiles, showReport, page }) => {
11461146
const files = {
11471147
'uncommitted.txt': `uncommitted file`,
11481148
'playwright.config.ts': `
1149-
import { gitCommitInfo } from 'playwright/lib/plugins';
11501149
import { test, expect } from '@playwright/test';
1151-
const plugins = [gitCommitInfo()];
1152-
export default { '@playwright/test': { plugins } };
1150+
export default { populateGitInfo: true };
11531151
`,
11541152
'example.spec.ts': `
11551153
import { test, expect } from '@playwright/test';
@@ -1195,6 +1193,25 @@ for (const useIntermediateMergeReport of [true, false] as const) {
11951193
await expect.soft(page.getByTestId('metadata-error')).not.toBeVisible();
11961194
});
11971195

1196+
test('should not include metadata with populateGitInfo = false', async ({ runInlineTest, showReport, page }) => {
1197+
const result = await runInlineTest({
1198+
'uncommitted.txt': `uncommitted file`,
1199+
'playwright.config.ts': `
1200+
export default { populateGitInfo: false };
1201+
`,
1202+
'example.spec.ts': `
1203+
import { test, expect } from '@playwright/test';
1204+
test('my sample test', async ({}) => { expect(2).toBe(2); });
1205+
`,
1206+
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' }, undefined);
1207+
1208+
await showReport();
1209+
1210+
expect(result.exitCode).toBe(0);
1211+
await expect.soft(page.locator('text="my sample test"')).toBeVisible();
1212+
await expect.soft(page.getByTestId('metadata-error')).not.toBeVisible();
1213+
await expect.soft(page.getByTestId('metadata-chip')).not.toBeVisible();
1214+
});
11981215

11991216
test('should use explicitly supplied metadata', async ({ runInlineTest, showReport, page }) => {
12001217
const result = await runInlineTest({
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { test, expect } from './ui-mode-fixtures';
18+
19+
test('should render html report git info metadata', async ({ runUITest }) => {
20+
const { page } = await runUITest({
21+
'reporter.ts': `
22+
module.exports = class Reporter {
23+
onBegin(config, suite) {
24+
console.log('ci.link:', config.metadata['ci.link']);
25+
}
26+
}
27+
`,
28+
'playwright.config.ts': `
29+
import { defineConfig } from '@playwright/test';
30+
export default defineConfig({
31+
populateGitInfo: true,
32+
reporter: './reporter.ts',
33+
});
34+
`,
35+
'a.test.js': `
36+
import { test, expect } from '@playwright/test';
37+
test('should work', async ({}) => {});
38+
`
39+
}, {
40+
BUILD_URL: 'https://playwright.dev',
41+
});
42+
43+
await page.getByTitle('Run all').click();
44+
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
45+
await page.getByTitle('Toggle output').click();
46+
47+
await expect(page.getByTestId('output')).toContainText('ci.link: https://playwright.dev');
48+
});

0 commit comments

Comments
 (0)