chore(html-report): render metainfo as a chip (#13166)

This commit is contained in:
Pavel Feldman 2022-03-29 17:13:08 -08:00 committed by GitHub
parent a9989852d5
commit 81e7c0a77c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 112 additions and 125 deletions

View File

@ -0,0 +1,100 @@
/*
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 * as React from 'react';
import './colors.css';
import './common.css';
import * as icons from './icons';
import { Metadata } from './index';
import { AutoChip } from './chip';
import './reportView.css';
import './theme.css';
export const MetadataView: React.FC<Metadata> = metadata => {
return (
<AutoChip header={
<span>
{metadata['revision.id'] && <span style={{ float: 'right', fontFamily: 'var(--monospace-font)' }}>
{metadata['revision.id'].slice(0, 7)}
</span>}
{metadata['revision.subject'] && metadata['revision.subject'] || 'no subject>'}
{!metadata['revision.subject'] && 'Commit metainfo'}
</span>} initialExpanded={false}>
{metadata['revision.subject'] &&
<MetadatViewItem
testId='revision.subject'
content={<span>{metadata['revision.subject']}</span>}
/>
}
{metadata['revision.id'] &&
<MetadatViewItem
testId='revision.id'
content={<span style={{ fontFamily: 'var(--monospace-font)' }}>{metadata['revision.id']}</span>}
href={metadata['revision.link']}
icon='commit'
/>
}
{(metadata['revision.author'] || metadata['revision.email']) &&
<MetadatViewItem
content={(
metadata['revision.author'] && metadata['revision.email']
? <>{metadata['revision.author']} {metadata['revision.email']}</>
: (metadata['revision.author'] || metadata['revision.email'])
)!}
icon='person'
/>
}
{metadata['revision.timestamp'] &&
<MetadatViewItem
testId='revision.timestamp'
content={
<>
{Intl.DateTimeFormat(undefined, { dateStyle: 'full' }).format(metadata['revision.timestamp'])}
{Intl.DateTimeFormat(undefined, { timeStyle: 'long' }).format(metadata['revision.timestamp'])}
</>
}
icon='calendar'
/>
}
{metadata['ci.link'] &&
<MetadatViewItem
content='CI/CD Logs'
href={metadata['ci.link']}
icon='externalLink'
/>
}
{metadata['generatedAt'] &&
<MetadatViewItem
content={<span style={{ color: 'var(--color-fg-subtle)' }}>
Report generated on {Intl.DateTimeFormat(undefined, { dateStyle: 'full', timeStyle: 'long' }).format(metadata['generatedAt'])}
</span>}></MetadatViewItem>
}
</AutoChip>
);
};
const MetadatViewItem: React.FC<{ content: JSX.Element | string; icon?: keyof typeof icons, href?: string, testId?: string }> = ({ content, icon, href, testId }) => {
return (
<div className='my-1 hbox' data-test-id={testId} >
<div className='mr-2'>
{icons[icon || 'blank']()}
</div>
<div style={{ flex: 1 }}>
{href ? <a href={href} target='_blank' rel='noopener noreferrer'>{content}</a> : content}
</div>
</div>
);
};

View File

@ -24,6 +24,7 @@ html, body {
body {
overflow: auto;
max-width: 1024px;
margin: 0 auto;
width: 100%;
}
@ -32,58 +33,8 @@ body {
border-top: 1px solid var(--color-border-default);
}
.htmlreport {
gap: 24px;
display: flex;
flex-direction: row;
justify-content: center;
}
.htmlreport header {
width: 300px;
order: 2;
}
.htmlreport main {
max-width: 1024px;
width: 100%;
order: 1;
}
.metadata-view a {
color: var(--color-accent-fg);
text-decoration: none;
}
.metadata-view a:hover {
text-decoration: underline;
}
.metadata-view h1 {
font-size: 16px;
font-weight: 600;
margin-bottom: 16px;
margin-top: 0;
line-height: 1.5;
}
@media only screen and (max-width: 600px) {
.report {
padding: 0 !important;
}
}
@media only screen and (max-width: 900px) {
.htmlreport {
flex-direction: column;
}
.htmlreport header {
order: 1;
}
.htmlreport main {
order: 2;
}
}

View File

@ -23,11 +23,10 @@ import { HeaderView } from './headerView';
import { Route } from './links';
import { LoadedReport } from './loadedReport';
import './reportView.css';
import { MetadataView } from './metadataView';
import { TestCaseView } from './testCaseView';
import { TestFilesView } from './testFilesView';
import './theme.css';
import * as icons from './icons';
import { Metadata } from './index';
declare global {
interface Window {
@ -45,10 +44,9 @@ export const ReportView: React.FC<{
const filter = React.useMemo(() => Filter.parse(filterText), [filterText]);
return <div className='htmlreport vbox px-4 pb-4'>
{report?.json().metadata && <MetadataView {...report?.json().metadata!} />}
<main>
{report?.json() && <HeaderView stats={report.json().stats} filterText={filterText} setFilterText={setFilterText}></HeaderView>}
{report?.json().metadata && <MetadataView {...report?.json().metadata!} />}
<Route params=''>
<TestFilesView report={report?.json()} filter={filter} expandedFiles={expandedFiles} setExpandedFiles={setExpandedFiles}></TestFilesView>
</Route>
@ -62,68 +60,6 @@ export const ReportView: React.FC<{
</div>;
};
const MetadataView: React.FC<Metadata> = metadata => {
return (
<header className='metadata-view pt-3'>
<h1>{metadata['revision.subject'] || 'Playwright Test Report'}</h1>
{metadata['revision.id'] &&
<MetadatViewItem
testId='revision.id'
content={<span style={{ fontFamily: 'monospace' }}>{metadata['revision.id'].slice(0, 7)}</span>}
href={metadata['revision.link']}
icon='commit'
/>
}
{(metadata['revision.author'] || metadata['revision.email']) &&
<MetadatViewItem
content={(
metadata['revision.author'] && metadata['revision.email']
? <>{metadata['revision.author']}<br/>{metadata['revision.email']}</>
: (metadata['revision.author'] || metadata['revision.email'])
)!}
icon='person'
/>
}
{metadata['revision.timestamp'] &&
<MetadatViewItem
testId='revision.timestamp'
content={
<>
{Intl.DateTimeFormat(undefined, { dateStyle: 'full' }).format(metadata['revision.timestamp'])}
<br />
{Intl.DateTimeFormat(undefined, { timeStyle: 'long' }).format(metadata['revision.timestamp'])}
</>
}
icon='calendar'
/>
}
{metadata['ci.link'] &&
<MetadatViewItem
content='CI/CD Logs'
href={metadata['ci.link']}
icon='externalLink'
/>
}
{metadata['generatedAt'] &&
<p style={{ fontStyle: 'italic', color: 'var(--color-fg-subtle)' }}>Report generated on {Intl.DateTimeFormat(undefined, { dateStyle: 'full', timeStyle: 'long' }).format(metadata['generatedAt'])}</p>
}
</header>
);
};
const MetadatViewItem: React.FC<{ content: JSX.Element | string; icon: keyof typeof icons, href?: string, testId?: string }> = ({ content, icon, href, testId }) => {
return (
<div className='mt-2 hbox' data-test-id={testId} >
<div className='mr-2'>
{icons[icon]()}
</div>
<div style={{ flex: 1 }}>
{href ? <a href={href} target='_blank' rel='noopener noreferrer'>{content}</a> : content}
</div>
</div>
);
};
const TestCaseViewLoader: React.FC<{
report: LoadedReport,
}> = ({ report }) => {

View File

@ -703,13 +703,13 @@ test('should include metadata', async ({ runInlineTest, showReport, page }) => {
await showReport();
expect(result.exitCode).toBe(0);
const metadata = page.locator('.metadata-view');
await expect.soft(metadata.locator('data-test-id=revision.id')).toContainText(/^[a-f\d]{7}$/i);
await expect.soft(metadata.locator('data-test-id=revision.id >> a')).toHaveAttribute('href', 'https://playwright.dev/microsoft/playwright-example-for-test/commit/example-sha');
await expect.soft(metadata.locator('data-test-id=revision.timestamp')).toContainText(/AM|PM/);
await expect.soft(metadata).toContainText('awesome commit message');
await expect.soft(metadata).toContainText('William');
await expect.soft(metadata).toContainText('shakespeare@example.local');
await expect.soft(metadata.locator('text=CI/CD Logs')).toHaveAttribute('href', 'https://playwright.dev/microsoft/playwright-example-for-test/actions/runs/example-run-id');
await expect.soft(metadata.locator('text=Report generated on')).toContainText(/AM|PM/);
await page.click('text=awesome commit message');
await expect.soft(page.locator('data-test-id=revision.id')).toContainText(/^[a-f\d]+$/i);
await expect.soft(page.locator('data-test-id=revision.id >> a')).toHaveAttribute('href', 'https://playwright.dev/microsoft/playwright-example-for-test/commit/example-sha');
await expect.soft(page.locator('data-test-id=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/);
});