diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index 1ef926448c..2b19a176ac 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -234,7 +234,9 @@ export default defineConfig({ * since: v1.10 - type: ?<[Metadata]> -Metadata that will be put directly to the test report 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. **Usage** @@ -242,7 +244,7 @@ Metadata that will be put directly to the test report serialized as JSON. import { defineConfig } from '@playwright/test'; export default defineConfig({ - metadata: 'acceptance tests', + metadata: { title: 'acceptance tests' }, }); ``` @@ -325,7 +327,9 @@ This path will serve as the base directory for each test file snapshot directory * 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. +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. **Usage** @@ -647,7 +651,7 @@ export default defineConfig({ - `timeout` ?<[int]> How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. - `gracefulShutdown` ?<[Object]> How to shut down the process. If unspecified, the process group is forcefully `SIGKILL`ed. If set to `{ signal: 'SIGTERM', timeout: 500 }`, the process group is sent a `SIGTERM` signal, followed by `SIGKILL` if it doesn't exit within 500ms. You can also use `SIGINT` as the signal instead. A `0` timeout means no `SIGKILL` will be sent. Windows doesn't support `SIGTERM` and `SIGINT` signals, so this option is ignored on Windows. Note that shutting down a Docker container requires `SIGTERM`. - `signal` <["SIGINT"|"SIGTERM"]> - - `timeout` <[int]> + - `timeout` <[int]> - `url` ?<[string]> The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is checked. Either `port` or `url` should be specified. Launch a development web server (or multiple) during the tests. diff --git a/packages/html-reporter/src/icons.tsx b/packages/html-reporter/src/icons.tsx index 9609a2e23f..ffe6a08fcf 100644 --- a/packages/html-reporter/src/icons.tsx +++ b/packages/html-reporter/src/icons.tsx @@ -69,22 +69,6 @@ export const blank = () => { return ; }; -export const externalLink = () => { - return ; -}; - -export const calendar = () => { - return ; -}; - -export const person = () => { - return ; -}; - -export const commit = () => { - return ; -}; - export const image = () => { return

An error was encountered when trying to render Commit Metainfo. Please file a GitHub issue to report this error.

+
+

An error was encountered when trying to render metadata.

{this.state.error?.message}
{this.state.error?.stack}
{this.state.errorInfo?.componentStack}

- +
); } @@ -59,79 +57,50 @@ class ErrorBoundary extends React.Component, { error } } -export const MetadataView: React.FC = metadata => ; - -const InnerMetadataView: React.FC = metadata => { - if (!Object.keys(metadata).find(k => k.startsWith('revision.') || k.startsWith('ci.'))) - return null; - - return ( - - {metadata['revision.id'] && - {metadata['revision.id'].slice(0, 7)} - } - {metadata['revision.subject'] || 'Commit Metainfo'} - } initialExpanded={false} dataTestId='metadata-chip'> - {metadata['revision.subject'] && - {metadata['revision.subject']}} - /> - } - {metadata['revision.id'] && - {metadata['revision.id']}} - href={metadata['revision.link']} - icon='commit' - /> - } - {(metadata['revision.author'] || metadata['revision.email']) && - - } - {metadata['revision.timestamp'] && - - {Intl.DateTimeFormat(undefined, { dateStyle: 'full' }).format(metadata['revision.timestamp'])} - {' '} - {Intl.DateTimeFormat(undefined, { timeStyle: 'long' }).format(metadata['revision.timestamp'])} - - } - icon='calendar' - /> - } - {metadata['ci.link'] && - - } - {metadata['timestamp'] && - - Report generated on {Intl.DateTimeFormat(undefined, { dateStyle: 'full', timeStyle: 'long' }).format(metadata['timestamp'])} - }> - } - - ); +export const MetadataView: React.FC<{ metadataEntries: MetadataEntries }> = ({ metadataEntries }) => { + return ; }; -const MetadataViewItem: React.FC<{ content: JSX.Element | string; icon?: keyof typeof icons, href?: string, testId?: string }> = ({ content, icon, href, testId }) => { - return ( -
-
- {icons[icon || 'blank']()} -
-
- {href ? {content} : content} +const InnerMetadataView: React.FC<{ metadataEntries: MetadataEntries }> = ({ metadataEntries }) => { + const gitCommitInfo = metadataEntries.find(([key]) => key === 'git.commit.info')?.[1] as GitCommitInfo | undefined; + const entries = metadataEntries.filter(([key]) => key !== 'git.commit.info'); + if (!gitCommitInfo && !entries.length) + return null; + return
+ {gitCommitInfo && <> + + {entries.length > 0 &&
} + } + {entries.map(([key, 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; + return
+ {key} + {valueString && : {linkifyText(trimmedValue)}} +
; + })} +
; +}; + +const GitCommitInfoView: React.FC<{ info: GitCommitInfo }> = ({ info }) => { + const email = info['revision.email'] ? ` <${info['revision.email']}>` : ''; + const author = `${info['revision.author'] || ''}${email}`; + 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
+
+ + {info['revision.subject'] || ''} + +
+
{author}
+
on {shortTimestamp}
+ {info['ci.link'] && <>ยทlogs}
- ); + {!!info['revision.link'] && + {info['revision.id']?.slice(0, 7) || 'unknown'} + } + {!info['revision.link'] && !!info['revision.id'] && {info['revision.id'].slice(0, 7)}} +
; }; diff --git a/packages/html-reporter/src/reportView.tsx b/packages/html-reporter/src/reportView.tsx index cf0f5e5e56..e48064201c 100644 --- a/packages/html-reporter/src/reportView.tsx +++ b/packages/html-reporter/src/reportView.tsx @@ -23,8 +23,6 @@ import { HeaderView } from './headerView'; import { Route, SearchParamsContext } from './links'; import type { LoadedReport } from './loadedReport'; import './reportView.css'; -import type { Metainfo } from './metadataView'; -import { MetadataView } from './metadataView'; import { TestCaseView } from './testCaseView'; import { TestFilesHeader, TestFilesView } from './testFilesView'; import './theme.css'; @@ -50,6 +48,7 @@ export const ReportView: React.FC<{ const searchParams = React.useContext(SearchParamsContext); const [expandedFiles, setExpandedFiles] = React.useState>(new Map()); const [filterText, setFilterText] = React.useState(searchParams.get('q') || ''); + const [metadataVisible, setMetadataVisible] = React.useState(false); const testIdToFileIdMap = React.useMemo(() => { const map = new Map(); @@ -76,9 +75,8 @@ export const ReportView: React.FC<{ return
{report?.json() && } - {report?.json().metadata && } - + setMetadataVisible(visible => !visible)}/> = ({ report, filteredStats }) => { + metadataVisible: boolean, + toggleMetadataVisible: () => void, +}> = ({ report, filteredStats, metadataVisible, toggleMetadataVisible }) => { if (!report) return; + const metadataEntries = filterMetadata(report.metadata || {}); return <> -
+
+ {metadataEntries.length > 0 &&
+ {metadataVisible ? icons.downArrow() : icons.rightArrow()}Metadata +
} {report.projectNames.length === 1 && !!report.projectNames[0] &&
Project: {report.projectNames[0]}
} {filteredStats &&
Filtered: {filteredStats.total} {!!filteredStats.total && ('(' + msToString(filteredStats.duration) + ')')}
}
{report ? new Date(report.startTime).toLocaleString() : ''}
Total time: {msToString(report.duration ?? 0)}
+ {metadataVisible && } {!!report.errors.length && {report.errors.map((error, index) => )} } diff --git a/packages/html-reporter/tsconfig.json b/packages/html-reporter/tsconfig.json index 4fe82eab4e..e642923b96 100644 --- a/packages/html-reporter/tsconfig.json +++ b/packages/html-reporter/tsconfig.json @@ -20,6 +20,7 @@ "@protocol/*": ["../protocol/src/*"], "@web/*": ["../web/src/*"], "@playwright/*": ["../playwright/src/*"], + "@testIsomorphic/*": ["../playwright/src/isomorphic/*"], "playwright-core/lib/*": ["../playwright-core/src/*"], "playwright/lib/*": ["../playwright/src/*"], } diff --git a/packages/playwright/src/isomorphic/types.d.ts b/packages/playwright/src/isomorphic/types.d.ts new file mode 100644 index 0000000000..72c6db3533 --- /dev/null +++ b/packages/playwright/src/isomorphic/types.d.ts @@ -0,0 +1,25 @@ +/** + * 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. + */ + +export interface GitCommitInfo { + 'revision.id'?: string; + 'revision.author'?: string; + 'revision.email'?: string; + 'revision.subject'?: string; + 'revision.timestamp'?: number | Date; + 'revision.link'?: string; + 'ci.link'?: string; +} diff --git a/packages/playwright/src/plugins/gitCommitInfoPlugin.ts b/packages/playwright/src/plugins/gitCommitInfoPlugin.ts index 2244d80a9c..c69d953b62 100644 --- a/packages/playwright/src/plugins/gitCommitInfoPlugin.ts +++ b/packages/playwright/src/plugins/gitCommitInfoPlugin.ts @@ -18,6 +18,7 @@ import { createGuid, spawnAsync } from 'playwright-core/lib/utils'; import type { TestRunnerPlugin } from './'; import type { FullConfig } from '../../types/testReporter'; import type { FullConfigInternal } from '../common/config'; +import type { GitCommitInfo } from '../isomorphic/types'; const GIT_OPERATIONS_TIMEOUT_MS = 1500; @@ -31,38 +32,23 @@ export const gitCommitInfo = (options?: GitCommitInfoPluginOptions): TestRunnerP name: 'playwright:git-commit-info', setup: async (config: FullConfig, configDir: string) => { - const info = { - ...linksFromEnv(), - ...options?.info ? options.info : await gitStatusFromCLI(options?.directory || configDir), - timestamp: Date.now(), - }; - // Normalize dates - const timestamp = info['revision.timestamp']; - if (timestamp instanceof Date) - info['revision.timestamp'] = timestamp.getTime(); + const fromEnv = linksFromEnv(); + const fromCLI = await gitStatusFromCLI(options?.directory || configDir); + const info = { ...fromEnv, ...fromCLI }; + if (info['revision.timestamp'] instanceof Date) + info['revision.timestamp'] = info['revision.timestamp'].getTime(); config.metadata = config.metadata || {}; - Object.assign(config.metadata, info); + config.metadata['git.commit.info'] = info; }, }; }; -export interface GitCommitInfoPluginOptions { - directory?: string; - info?: Info; +interface GitCommitInfoPluginOptions { + directory?: string; } -export interface Info { - 'revision.id'?: string; - 'revision.author'?: string; - 'revision.email'?: string; - 'revision.subject'?: string; - 'revision.timestamp'?: number | Date; - 'revision.link'?: string; - 'ci.link'?: string; -} - -const linksFromEnv = (): Pick => { +function linksFromEnv(): Pick { const out: { 'revision.link'?: string; 'ci.link'?: string; } = {}; // Jenkins: https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables if (process.env.BUILD_URL) @@ -78,9 +64,9 @@ const linksFromEnv = (): Pick => { 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}`; return out; -}; +} -export const gitStatusFromCLI = async (gitDir: string): Promise => { +async function gitStatusFromCLI(gitDir: string): Promise { const separator = `:${createGuid().slice(0, 4)}:`; const { code, stdout } = await spawnAsync( 'git', @@ -101,4 +87,4 @@ export const gitStatusFromCLI = async (gitDir: string): Promise { maxFailures?: number; /** - * Metadata that will be put directly to the test report 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 + * [testConfig.populateGitInfo](https://playwright.dev/docs/api/class-testconfig#test-config-populate-git-info) that + * populates metadata. * * **Usage** * @@ -1229,7 +1234,7 @@ interface TestConfig { * import { defineConfig } from '@playwright/test'; * * export default defineConfig({ - * metadata: 'acceptance tests', + * metadata: { title: 'acceptance tests' }, * }); * ``` * @@ -1294,8 +1299,11 @@ interface TestConfig { 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. + * 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. * * **Usage** * diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index 746eeea159..a4f41874aa 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -978,8 +978,8 @@ for (const useIntermediateMergeReport of [true, false] as const) { await test.step('step', async () => { testInfo.attachments.push({ name: 'attachment', body: 'content', contentType: 'text/plain' }); - }) - + }) + }); `, }, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' }); @@ -1095,7 +1095,7 @@ for (const useIntermediateMergeReport of [true, false] as const) { const result = await runInlineTest({ 'a.spec.js': ` import { test as base, expect } from '@playwright/test'; - + const test = base.extend({ fixture1: [async ({}, use) => { await use(); @@ -1141,161 +1141,97 @@ for (const useIntermediateMergeReport of [true, false] as const) { ]); }); - test.describe('gitCommitInfo plugin', () => { - test('should include metadata with populateGitInfo = true', async ({ runInlineTest, writeFiles, showReport, page }) => { - const files = { - 'uncommitted.txt': `uncommitted file`, - 'playwright.config.ts': ` - import { test, expect } from '@playwright/test'; - export default { populateGitInfo: true }; - `, - 'example.spec.ts': ` - import { test, expect } from '@playwright/test'; - test('sample', async ({}) => { expect(2).toBe(2); }); - `, - }; - const baseDir = await writeFiles(files); + test('should include metadata with populateGitInfo = true', 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; - }; + 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', '*.ts']); - await execGit(['commit', '-m', 'awesome commit message']); + await execGit(['init']); + await execGit(['config', '--local', 'user.email', 'shakespeare@example.local']); + await execGit(['config', '--local', 'user.name', 'William']); + await execGit(['add', '*.ts']); + await execGit(['commit', '-m', 'chore(html): make this test look nice']); - 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', - }); - - await showReport(); - - expect(result.exitCode).toBe(0); - await page.click('text=awesome commit message'); - await expect.soft(page.getByTestId('revision.id')).toContainText(/^[a-f\d]+$/i); - await expect.soft(page.getByTestId('revision.id').locator('a')).toHaveAttribute('href', 'https://playwright.dev/microsoft/playwright-example-for-test/commit/example-sha'); - await expect.soft(page.getByTestId('revision.timestamp')).toContainText(/AM|PM/); - await expect.soft(page.locator('text=awesome commit message')).toHaveCount(2); - await expect.soft(page.locator('text=William')).toBeVisible(); - await expect.soft(page.locator('text=shakespeare@example.local')).toBeVisible(); - await expect.soft(page.locator('text=CI/CD Logs')).toHaveAttribute('href', 'https://playwright.dev/microsoft/playwright-example-for-test/actions/runs/example-run-id'); - await expect.soft(page.locator('text=Report generated on')).toContainText(/AM|PM/); - await expect.soft(page.getByTestId('metadata-chip')).toBeVisible(); - await expect.soft(page.getByTestId('metadata-error')).not.toBeVisible(); + 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', }); - 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(); - await showReport(); + expect(result.exitCode).toBe(0); + await page.getByRole('button', { name: 'Metadata' }).click(); + await expect(page.locator('.metadata-view')).toMatchAriaSnapshot(` + - 'link "chore(html): make this test look nice"' + - text: /^William on/ + - link "logs" + - link /^[a-f0-9]{7}$/ + - text: 'foo: value1 bar: {"prop":"value2"} baz: ["value3",123]' + `); + }); - 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 not include git metadata with populateGitInfo = false', async ({ runInlineTest, showReport, page }) => { + const result = await runInlineTest({ + '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); - test('should use explicitly supplied metadata', async ({ runInlineTest, showReport, page }) => { - const result = await runInlineTest({ - 'uncommitted.txt': `uncommitted file`, - 'playwright.config.ts': ` - import { gitCommitInfo } from 'playwright/lib/plugins'; - import { test, expect } from '@playwright/test'; - const plugin = gitCommitInfo({ - info: { - 'revision.id': '1234567890', - 'revision.subject': 'a better subject', - 'revision.timestamp': new Date(), - 'revision.author': 'William', - 'revision.email': 'shakespeare@example.local', - }, - }); - export default { '@playwright/test': { plugins: [plugin] } }; - `, - 'example.spec.ts': ` - import { gitCommitInfo } from 'playwright/lib/plugins'; - import { test, expect } from '@playwright/test'; - test('sample', async ({}) => { expect(2).toBe(2); }); - `, - }, { 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' }, undefined); + await showReport(); - await showReport(); + expect(result.exitCode).toBe(0); + await expect.soft(page.getByRole('button', { name: 'Metadata' })).toBeHidden(); + await expect.soft(page.locator('.metadata-view')).toBeHidden(); + }); - expect(result.exitCode).toBe(0); - await page.click('text=a better subject'); - await expect.soft(page.getByTestId('revision.id')).toContainText(/^[a-f\d]+$/i); - await expect.soft(page.getByTestId('revision.id').locator('a')).toHaveAttribute('href', 'https://playwright.dev/microsoft/playwright-example-for-test/commit/example-sha'); - await expect.soft(page.getByTestId('revision.timestamp')).toContainText(/AM|PM/); - await expect.soft(page.locator('text=a better subject')).toHaveCount(2); - await expect.soft(page.locator('text=William')).toBeVisible(); - await expect.soft(page.locator('text=shakespeare@example.local')).toBeVisible(); - await expect.soft(page.locator('text=CI/CD Logs')).toHaveAttribute('href', 'https://playwright.dev/microsoft/playwright-example-for-test/actions/runs/example-run-id'); - await expect.soft(page.locator('text=Report generated on')).toContainText(/AM|PM/); - await expect.soft(page.getByTestId('metadata-chip')).toBeVisible(); - await expect.soft(page.getByTestId('metadata-error')).not.toBeVisible(); - }); + test('should show an error when metadata has invalid fields', async ({ runInlineTest, showReport, page }) => { + const result = await runInlineTest({ + 'uncommitted.txt': `uncommitted file`, + 'playwright.config.ts': ` + export default { + metadata: { + 'git.commit.info': { 'revision.timestamp': 'hi' } + }, + }; + `, + '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' }); - test('should not have metadata by default', async ({ runInlineTest, showReport, page }) => { - const result = await runInlineTest({ - 'uncommitted.txt': `uncommitted file`, - 'playwright.config.ts': ` - export default {}; - `, - '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(); - 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 not include metadata if user supplies invalid values via metadata field', async ({ runInlineTest, showReport, page }) => { - const result = await runInlineTest({ - 'uncommitted.txt': `uncommitted file`, - 'playwright.config.ts': ` - export default { - metadata: { - 'revision.timestamp': 'hi', - }, - }; - `, - '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' }); - - await showReport(); - - expect(result.exitCode).toBe(0); - await expect.soft(page.locator('text="my sample test"')).toBeVisible(); - await expect.soft(page.getByTestId('metadata-error')).toBeVisible(); - await expect.soft(page.getByTestId('metadata-chip')).not.toBeVisible(); - }); + expect(result.exitCode).toBe(0); + await page.getByRole('button', { name: 'Metadata' }).click(); + await expect(page.locator('.metadata-view')).toMatchAriaSnapshot(` + - paragraph: An error was encountered when trying to render metadata. + `); }); test('should report clashing folders', async ({ runInlineTest, useIntermediateMergeReport }) => { diff --git a/tests/playwright-test/ui-mode-metadata.spec.ts b/tests/playwright-test/ui-mode-metadata.spec.ts index bac8464bed..8f032ea987 100644 --- a/tests/playwright-test/ui-mode-metadata.spec.ts +++ b/tests/playwright-test/ui-mode-metadata.spec.ts @@ -21,7 +21,7 @@ test('should render html report git info metadata', async ({ runUITest }) => { 'reporter.ts': ` module.exports = class Reporter { onBegin(config, suite) { - console.log('ci.link:', config.metadata['ci.link']); + console.log('ci.link:', config.metadata['git.commit.info']['ci.link']); } } `,