mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat: add new config prop populateGitInfo (#34329)
Co-authored-by: Yury Semikhatsky <yurys@chromium.org>
This commit is contained in:
parent
cea5dad686
commit
61d595ae48
@ -321,6 +321,22 @@ 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 [`property: TestConfig.metadata`] with Git info. The metadata will automatically appear in the HTML report and is available in Reporter API.
|
||||||
|
|
||||||
|
**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">>
|
||||||
|
@ -46,6 +46,7 @@ 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;
|
||||||
@ -75,10 +76,15 @@ 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, false);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
// Make sure we reuse same metadata instance between FullConfigInternal instances,
|
||||||
|
// so that plugins such as gitCommitInfoPlugin can populate metadata once.
|
||||||
|
userConfig.metadata = userConfig.metadata || {};
|
||||||
|
|
||||||
this.config = {
|
this.config = {
|
||||||
configFile: resolvedConfigFile,
|
configFile: resolvedConfigFile,
|
||||||
rootDir: pathResolve(configDir, userConfig.testDir) || configDir,
|
rootDir: pathResolve(configDir, userConfig.testDir) || configDir,
|
||||||
@ -90,7 +96,7 @@ export class FullConfigInternal {
|
|||||||
grep: takeFirst(userConfig.grep, defaultGrep),
|
grep: takeFirst(userConfig.grep, defaultGrep),
|
||||||
grepInvert: takeFirst(userConfig.grepInvert, null),
|
grepInvert: takeFirst(userConfig.grepInvert, null),
|
||||||
maxFailures: takeFirst(configCLIOverrides.debug ? 1 : undefined, configCLIOverrides.maxFailures, userConfig.maxFailures, 0),
|
maxFailures: takeFirst(configCLIOverrides.debug ? 1 : undefined, configCLIOverrides.maxFailures, userConfig.maxFailures, 0),
|
||||||
metadata: takeFirst(userConfig.metadata, {}),
|
metadata: userConfig.metadata,
|
||||||
preserveOutput: takeFirst(userConfig.preserveOutput, 'always'),
|
preserveOutput: takeFirst(userConfig.preserveOutput, 'always'),
|
||||||
reporter: takeFirst(configCLIOverrides.reporter, resolveReporters(userConfig.reporter, configDir), [[defaultReporter]]),
|
reporter: takeFirst(configCLIOverrides.reporter, resolveReporters(userConfig.reporter, configDir), [[defaultReporter]]),
|
||||||
reportSlowTests: takeFirst(userConfig.reportSlowTests, { max: 5, threshold: 15000 }),
|
reportSlowTests: takeFirst(userConfig.reportSlowTests, { max: 5, threshold: 15000 }),
|
||||||
|
@ -17,9 +17,15 @@
|
|||||||
import { createGuid, spawnAsync } from 'playwright-core/lib/utils';
|
import { createGuid, spawnAsync } from 'playwright-core/lib/utils';
|
||||||
import type { TestRunnerPlugin } from './';
|
import type { TestRunnerPlugin } from './';
|
||||||
import type { FullConfig } from '../../types/testReporter';
|
import type { FullConfig } from '../../types/testReporter';
|
||||||
|
import type { FullConfigInternal } from '../common/config';
|
||||||
|
|
||||||
const GIT_OPERATIONS_TIMEOUT_MS = 1500;
|
const GIT_OPERATIONS_TIMEOUT_MS = 1500;
|
||||||
|
|
||||||
|
export const addGitCommitInfoPlugin = (fullConfig: FullConfigInternal) => {
|
||||||
|
if (fullConfig.populateGitInfo)
|
||||||
|
fullConfig.plugins.push({ factory: gitCommitInfo });
|
||||||
|
};
|
||||||
|
|
||||||
export const gitCommitInfo = (options?: GitCommitInfoPluginOptions): TestRunnerPlugin => {
|
export const gitCommitInfo = (options?: GitCommitInfoPluginOptions): TestRunnerPlugin => {
|
||||||
return {
|
return {
|
||||||
name: 'playwright:git-commit-info',
|
name: 'playwright:git-commit-info',
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
import type { FullResult, TestError } from '../../types/testReporter';
|
import type { FullResult, TestError } from '../../types/testReporter';
|
||||||
import { webServerPluginsForConfig } from '../plugins/webServerPlugin';
|
import { webServerPluginsForConfig } from '../plugins/webServerPlugin';
|
||||||
|
import { addGitCommitInfoPlugin } from '../plugins/gitCommitInfoPlugin';
|
||||||
import { collectFilesForProject, filterProjects } from './projectUtils';
|
import { collectFilesForProject, filterProjects } from './projectUtils';
|
||||||
import { createErrorCollectingReporter, createReporters } from './reporters';
|
import { createErrorCollectingReporter, createReporters } from './reporters';
|
||||||
import { TestRun, createApplyRebaselinesTask, createClearCacheTask, createGlobalSetupTasks, createLoadTask, createPluginSetupTasks, createReportBeginTask, createRunTestsTasks, createStartDevServerTask, runTasks } from './tasks';
|
import { TestRun, createApplyRebaselinesTask, createClearCacheTask, createGlobalSetupTasks, createLoadTask, createPluginSetupTasks, createReportBeginTask, createRunTestsTasks, createStartDevServerTask, runTasks } from './tasks';
|
||||||
@ -70,6 +71,8 @@ export class Runner {
|
|||||||
const config = this._config;
|
const config = this._config;
|
||||||
const listOnly = config.cliListOnly;
|
const listOnly = config.cliListOnly;
|
||||||
|
|
||||||
|
addGitCommitInfoPlugin(config);
|
||||||
|
|
||||||
// Legacy webServer support.
|
// Legacy webServer support.
|
||||||
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ import { baseFullConfig } from '../isomorphic/teleReceiver';
|
|||||||
import { InternalReporter } from '../reporters/internalReporter';
|
import { InternalReporter } from '../reporters/internalReporter';
|
||||||
import type { ReporterV2 } from '../reporters/reporterV2';
|
import type { ReporterV2 } from '../reporters/reporterV2';
|
||||||
import { internalScreen } from '../reporters/base';
|
import { internalScreen } from '../reporters/base';
|
||||||
|
import { addGitCommitInfoPlugin } from '../plugins/gitCommitInfoPlugin';
|
||||||
|
|
||||||
const originalStdoutWrite = process.stdout.write;
|
const originalStdoutWrite = process.stdout.write;
|
||||||
const originalStderrWrite = process.stderr.write;
|
const originalStderrWrite = process.stderr.write;
|
||||||
@ -406,6 +407,7 @@ export class TestServerDispatcher implements TestServerInterface {
|
|||||||
// Preserve plugin instances between setup and build.
|
// Preserve plugin instances between setup and build.
|
||||||
if (!this._plugins) {
|
if (!this._plugins) {
|
||||||
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
||||||
|
addGitCommitInfoPlugin(config);
|
||||||
this._plugins = config.plugins || [];
|
this._plugins = config.plugins || [];
|
||||||
} else {
|
} else {
|
||||||
config.plugins.splice(0, config.plugins.length, ...this._plugins);
|
config.plugins.splice(0, config.plugins.length, ...this._plugins);
|
||||||
|
18
packages/playwright/types/test.d.ts
vendored
18
packages/playwright/types/test.d.ts
vendored
@ -1293,6 +1293,24 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
|||||||
*/
|
*/
|
||||||
outputDir?: string;
|
outputDir?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to populate [testConfig.metadata](https://playwright.dev/docs/api/class-testconfig#test-config-metadata)
|
||||||
|
* with Git info. The metadata will automatically appear in the HTML report and is available in Reporter API.
|
||||||
|
*
|
||||||
|
* **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
|
||||||
|
@ -1142,14 +1142,12 @@ for (const useIntermediateMergeReport of [true, false] as const) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test.describe('gitCommitInfo plugin', () => {
|
test.describe('gitCommitInfo plugin', () => {
|
||||||
test('should include metadata', async ({ runInlineTest, writeFiles, showReport, page }) => {
|
test('should include metadata with populateGitInfo = true', async ({ runInlineTest, writeFiles, showReport, page }) => {
|
||||||
const files = {
|
const files = {
|
||||||
'uncommitted.txt': `uncommitted file`,
|
'uncommitted.txt': `uncommitted file`,
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
import { gitCommitInfo } from 'playwright/lib/plugins';
|
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
const plugins = [gitCommitInfo()];
|
export default { populateGitInfo: true };
|
||||||
export default { '@playwright/test': { plugins } };
|
|
||||||
`,
|
`,
|
||||||
'example.spec.ts': `
|
'example.spec.ts': `
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
@ -1195,6 +1193,25 @@ for (const useIntermediateMergeReport of [true, false] as const) {
|
|||||||
await expect.soft(page.getByTestId('metadata-error')).not.toBeVisible();
|
await expect.soft(page.getByTestId('metadata-error')).not.toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should not include metadata with populateGitInfo = false', async ({ runInlineTest, showReport, page }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'uncommitted.txt': `uncommitted file`,
|
||||||
|
'playwright.config.ts': `
|
||||||
|
export default { populateGitInfo: false };
|
||||||
|
`,
|
||||||
|
'example.spec.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('my sample test', async ({}) => { expect(2).toBe(2); });
|
||||||
|
`,
|
||||||
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' }, undefined);
|
||||||
|
|
||||||
|
await showReport();
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
await expect.soft(page.locator('text="my sample test"')).toBeVisible();
|
||||||
|
await expect.soft(page.getByTestId('metadata-error')).not.toBeVisible();
|
||||||
|
await expect.soft(page.getByTestId('metadata-chip')).not.toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
test('should use explicitly supplied metadata', async ({ runInlineTest, showReport, page }) => {
|
test('should use explicitly supplied metadata', async ({ runInlineTest, showReport, page }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
|
48
tests/playwright-test/ui-mode-metadata.spec.ts
Normal file
48
tests/playwright-test/ui-mode-metadata.spec.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { test, expect } from './ui-mode-fixtures';
|
||||||
|
|
||||||
|
test('should render html report git info metadata', async ({ runUITest }) => {
|
||||||
|
const { page } = await runUITest({
|
||||||
|
'reporter.ts': `
|
||||||
|
module.exports = class Reporter {
|
||||||
|
onBegin(config, suite) {
|
||||||
|
console.log('ci.link:', config.metadata['ci.link']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'playwright.config.ts': `
|
||||||
|
import { defineConfig } from '@playwright/test';
|
||||||
|
export default defineConfig({
|
||||||
|
populateGitInfo: true,
|
||||||
|
reporter: './reporter.ts',
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'a.test.js': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('should work', async ({}) => {});
|
||||||
|
`
|
||||||
|
}, {
|
||||||
|
BUILD_URL: 'https://playwright.dev',
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.getByTitle('Run all').click();
|
||||||
|
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
|
||||||
|
await page.getByTitle('Toggle output').click();
|
||||||
|
|
||||||
|
await expect(page.getByTestId('output')).toContainText('ci.link: https://playwright.dev');
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user