chore(html-report): clean up git metadata display (#34713)
This commit is contained in:
parent
6704370c3f
commit
91f46bb5d0
|
@ -39,7 +39,7 @@ export const CopyToClipboard: React.FunctionComponent<CopyToClipboardProps> = ({
|
||||||
});
|
});
|
||||||
}, [value]);
|
}, [value]);
|
||||||
const iconElement = icon === 'check' ? icons.check() : icon === 'cross' ? icons.cross() : icons.copy();
|
const iconElement = icon === 'check' ? icons.check() : icon === 'cross' ? icons.cross() : icons.copy();
|
||||||
return <button className='copy-icon' aria-label='Copy to clipboard' onClick={handleCopy}>{iconElement}</button>;
|
return <button className='copy-icon' title='Copy to clipboard' aria-label='Copy to clipboard' onClick={handleCopy}>{iconElement}</button>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type CopyToClipboardContainerProps = CopyToClipboardProps & {
|
type CopyToClipboardContainerProps = CopyToClipboardProps & {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
color: var(--color-fg-default);
|
||||||
}
|
}
|
||||||
|
|
||||||
.metadata-view {
|
.metadata-view {
|
||||||
|
@ -26,16 +27,46 @@
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.metadata-view .metadata-section {
|
||||||
|
margin: 8px 10px 8px 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-view span:not(.copy-button-container),
|
||||||
|
.metadata-view a {
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-section {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-properties {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: normal;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-properties > div {
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
.metadata-separator {
|
.metadata-separator {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
border-bottom: 1px solid var(--color-border-default);
|
border-bottom: 1px solid var(--color-border-default);
|
||||||
}
|
}
|
||||||
|
|
||||||
.metadata-view .copy-value-container {
|
|
||||||
margin-top: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.git-commit-info a {
|
.git-commit-info a {
|
||||||
color: var(--color-fg-default);
|
color: var(--color-fg-default);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.copyable-property {
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyable-property > span {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ export const MetadataContext = React.createContext<MetadataEntries>([]);
|
||||||
export function MetadataProvider({ metadata, children }: React.PropsWithChildren<{ metadata: Metadata }>) {
|
export function MetadataProvider({ metadata, children }: React.PropsWithChildren<{ metadata: Metadata }>) {
|
||||||
const entries = React.useMemo(() => {
|
const entries = React.useMemo(() => {
|
||||||
// TODO: do not plumb actualWorkers through metadata.
|
// TODO: do not plumb actualWorkers through metadata.
|
||||||
|
|
||||||
return Object.entries(metadata).filter(([key]) => key !== 'actualWorkers');
|
return Object.entries(metadata).filter(([key]) => key !== 'actualWorkers');
|
||||||
}, [metadata]);
|
}, [metadata]);
|
||||||
|
|
||||||
|
@ -88,30 +87,43 @@ const InnerMetadataView = () => {
|
||||||
<GitCommitInfoView info={gitCommitInfo}/>
|
<GitCommitInfoView info={gitCommitInfo}/>
|
||||||
{entries.length > 0 && <div className='metadata-separator' />}
|
{entries.length > 0 && <div className='metadata-separator' />}
|
||||||
</>}
|
</>}
|
||||||
{entries.map(([key, value]) => {
|
<div className='metadata-section metadata-properties'>
|
||||||
|
{entries.map(([propertyName, value]) => {
|
||||||
const valueString = typeof value !== 'object' || value === null || value === undefined ? String(value) : JSON.stringify(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;
|
const trimmedValue = valueString.length > 1000 ? valueString.slice(0, 1000) + '\u2026' : valueString;
|
||||||
return <div className='m-1 ml-5' key={key}>
|
return (
|
||||||
<span style={{ fontWeight: 'bold' }} title={key}>{key}</span>
|
<div key={propertyName} className='copyable-property'>
|
||||||
{valueString && <CopyToClipboardContainer value={valueString}>: <span title={trimmedValue}>{linkifyText(trimmedValue)}</span></CopyToClipboardContainer>}
|
<CopyToClipboardContainer value={valueString}>
|
||||||
</div>;
|
<span style={{ fontWeight: 'bold' }} title={propertyName}>{propertyName}</span>
|
||||||
|
: <span title={trimmedValue}>{linkifyText(trimmedValue)}</span>
|
||||||
|
</CopyToClipboardContainer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GitCommitInfoView: React.FC<{ info: GitCommitInfo }> = ({ info }) => {
|
const GitCommitInfoView: React.FC<{ info: GitCommitInfo }> = ({ info }) => {
|
||||||
const email = info['revision.email'] ? ` <${info['revision.email']}>` : '';
|
const email = info['revision.email'] ? ` <${info['revision.email']}>` : '';
|
||||||
const author = `${info['revision.author'] || ''}${email}`;
|
const author = `${info['revision.author'] || ''}${email}`;
|
||||||
|
const subject = info['revision.subject'] || '';
|
||||||
const shortTimestamp = Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(info['revision.timestamp']);
|
const shortTimestamp = Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(info['revision.timestamp']);
|
||||||
const longTimestamp = Intl.DateTimeFormat(undefined, { dateStyle: 'full', timeStyle: 'long' }).format(info['revision.timestamp']);
|
const longTimestamp = Intl.DateTimeFormat(undefined, { dateStyle: 'full', timeStyle: 'long' }).format(info['revision.timestamp']);
|
||||||
return <div className='hbox pl-4 pr-2 git-commit-info' style={{ alignItems: 'center' }}>
|
return <div className='hbox git-commit-info metadata-section'>
|
||||||
<div className='vbox'>
|
<div className='vbox metadata-properties'>
|
||||||
<a className='m-2' href={info['revision.link']} target='_blank' rel='noopener noreferrer'>
|
<div>
|
||||||
<span title={info['revision.subject'] || ''}>{info['revision.subject'] || ''}</span>
|
{info['revision.link'] ? (
|
||||||
|
<a href={info['revision.link']} target='_blank' rel='noopener noreferrer' title={subject}>
|
||||||
|
{subject}
|
||||||
</a>
|
</a>
|
||||||
<div className='hbox m-2 mt-1'>
|
) : <span title={subject}>
|
||||||
<div className='mr-1'>{author}</div>
|
{subject}
|
||||||
<div title={longTimestamp}> on {shortTimestamp}</div>
|
</span>}
|
||||||
|
</div>
|
||||||
|
<div className='hbox'>
|
||||||
|
<span className='mr-1'>{author}</span>
|
||||||
|
<span title={longTimestamp}> on {shortTimestamp}</span>
|
||||||
{info['ci.link'] && (
|
{info['ci.link'] && (
|
||||||
<>
|
<>
|
||||||
<span className='mx-2'>·</span>
|
<span className='mx-2'>·</span>
|
||||||
|
@ -126,9 +138,10 @@ const GitCommitInfoView: React.FC<{ info: GitCommitInfo }> = ({ info }) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!!info['revision.link'] && <a href={info['revision.link']} target='_blank' rel='noopener noreferrer'>
|
{!!info['revision.link'] ? (
|
||||||
<span title='View commit details'>{info['revision.id']?.slice(0, 7) || 'unknown'}</span>
|
<a href={info['revision.link']} target='_blank' rel='noopener noreferrer' title='View commit details'>
|
||||||
</a>}
|
{info['revision.id']?.slice(0, 7) || 'unknown'}
|
||||||
{!info['revision.link'] && !!info['revision.id'] && <span>{info['revision.id'].slice(0, 7)}</span>}
|
</a>
|
||||||
|
) : !!info['revision.id'] && <span>{info['revision.id'].slice(0, 7)}</span>}
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -70,3 +70,10 @@
|
||||||
.test-file-test-status-icon {
|
.test-file-test-status-icon {
|
||||||
flex: none;
|
flex: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.test-file-header-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
color: var(--color-fg-subtle);
|
||||||
|
}
|
|
@ -69,14 +69,16 @@ export const TestFilesHeader: React.FC<{
|
||||||
}> = ({ report, filteredStats, metadataVisible, toggleMetadataVisible }) => {
|
}> = ({ report, filteredStats, metadataVisible, toggleMetadataVisible }) => {
|
||||||
const metadataEntries = useMetadata();
|
const metadataEntries = useMetadata();
|
||||||
if (!report)
|
if (!report)
|
||||||
return;
|
return null;
|
||||||
return <>
|
return <>
|
||||||
<div className='mx-1' style={{ display: 'flex', marginTop: 10 }}>
|
<div className='mx-1' style={{ display: 'flex', marginTop: 10 }}>
|
||||||
|
<div className='test-file-header-info'>
|
||||||
{metadataEntries.length > 0 && <div className='metadata-toggle' role='button' onClick={toggleMetadataVisible} title={metadataVisible ? 'Hide metadata' : 'Show metadata'}>
|
{metadataEntries.length > 0 && <div className='metadata-toggle' role='button' onClick={toggleMetadataVisible} title={metadataVisible ? 'Hide metadata' : 'Show metadata'}>
|
||||||
{metadataVisible ? icons.downArrow() : icons.rightArrow()}Metadata
|
{metadataVisible ? icons.downArrow() : icons.rightArrow()}Metadata
|
||||||
</div>}
|
</div>}
|
||||||
{report.projectNames.length === 1 && !!report.projectNames[0] && <div data-testid='project-name' style={{ color: 'var(--color-fg-subtle)' }}>Project: {report.projectNames[0]}</div>}
|
{report.projectNames.length === 1 && !!report.projectNames[0] && <div data-testid='project-name'>Project: {report.projectNames[0]}</div>}
|
||||||
{filteredStats && <div data-testid='filtered-tests-count' style={{ color: 'var(--color-fg-subtle)', padding: '0 10px' }}>Filtered: {filteredStats.total} {!!filteredStats.total && ('(' + msToString(filteredStats.duration) + ')')}</div>}
|
{filteredStats && <div data-testid='filtered-tests-count'>Filtered: {filteredStats.total} {!!filteredStats.total && ('(' + msToString(filteredStats.duration) + ')')}</div>}
|
||||||
|
</div>
|
||||||
<div style={{ flex: 'auto' }}></div>
|
<div style={{ flex: 'auto' }}></div>
|
||||||
<div data-testid='overall-time' style={{ color: 'var(--color-fg-subtle)', marginRight: '10px' }}>{report ? new Date(report.startTime).toLocaleString() : ''}</div>
|
<div data-testid='overall-time' style={{ color: 'var(--color-fg-subtle)', marginRight: '10px' }}>{report ? new Date(report.startTime).toLocaleString() : ''}</div>
|
||||||
<div data-testid='overall-duration' style={{ color: 'var(--color-fg-subtle)' }}>Total time: {msToString(report.duration ?? 0)}</div>
|
<div data-testid='overall-duration' style={{ color: 'var(--color-fg-subtle)' }}>Total time: {msToString(report.duration ?? 0)}</div>
|
||||||
|
|
Loading…
Reference in New Issue