anchor by attachment index
This commit is contained in:
parent
9b333832e0
commit
6c61ab3353
|
|
@ -68,11 +68,12 @@ export const ProjectLink: React.FunctionComponent<{
|
||||||
|
|
||||||
export const AttachmentLink: React.FunctionComponent<{
|
export const AttachmentLink: React.FunctionComponent<{
|
||||||
attachment: TestAttachment,
|
attachment: TestAttachment,
|
||||||
|
result: TestResult,
|
||||||
href?: string,
|
href?: string,
|
||||||
linkName?: string,
|
linkName?: string,
|
||||||
openInNewTab?: boolean,
|
openInNewTab?: boolean,
|
||||||
}> = ({ attachment, href, linkName, openInNewTab }) => {
|
}> = ({ attachment, result, href, linkName, openInNewTab }) => {
|
||||||
const isAnchored = useIsAnchored('attachment-' + attachment.name);
|
const isAnchored = useIsAnchored('attachment-' + result.attachments.indexOf(attachment));
|
||||||
return <TreeItem title={<span>
|
return <TreeItem title={<span>
|
||||||
{attachment.contentType === kMissingContentType ? icons.warning() : icons.attachment()}
|
{attachment.contentType === kMissingContentType ? icons.warning() : icons.attachment()}
|
||||||
{attachment.path && <a href={href || attachment.path} download={downloadFileNameForAttachment(attachment)}>{linkName || attachment.name}</a>}
|
{attachment.path && <a href={href || attachment.path} download={downloadFileNameForAttachment(attachment)}>{linkName || attachment.name}</a>}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ function imageDiffBadge(test: TestCaseSummary): JSX.Element | undefined {
|
||||||
for (const result of test.results) {
|
for (const result of test.results) {
|
||||||
for (const attachment of result.attachments) {
|
for (const attachment of result.attachments) {
|
||||||
if (attachment.contentType.startsWith('image/') && !!attachment.name.match(/-(expected|actual|diff)/))
|
if (attachment.contentType.startsWith('image/') && !!attachment.name.match(/-(expected|actual|diff)/))
|
||||||
return <Link href={testResultHref({ test, result, anchor: `attachment-${attachment.name}` })} title='View images' className='test-file-badge'>{image()}</Link>;
|
return <Link href={testResultHref({ test, result, anchor: `attachment-${result.attachments.indexOf(attachment)}` })} title='View images' className='test-file-badge'>{image()}</Link>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ interface ImageDiffWithAnchors extends ImageDiff {
|
||||||
anchors: string[];
|
anchors: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function groupImageDiffs(screenshots: Set<TestAttachment>): ImageDiffWithAnchors[] {
|
function groupImageDiffs(screenshots: Set<TestAttachment>, result: TestResult): ImageDiffWithAnchors[] {
|
||||||
const snapshotNameToImageDiff = new Map<string, ImageDiffWithAnchors>();
|
const snapshotNameToImageDiff = new Map<string, ImageDiffWithAnchors>();
|
||||||
for (const attachment of screenshots) {
|
for (const attachment of screenshots) {
|
||||||
const match = attachment.name.match(/^(.*)-(expected|actual|diff|previous)(\.[^.]+)?$/);
|
const match = attachment.name.match(/^(.*)-(expected|actual|diff|previous)(\.[^.]+)?$/);
|
||||||
|
|
@ -45,7 +45,7 @@ function groupImageDiffs(screenshots: Set<TestAttachment>): ImageDiffWithAnchors
|
||||||
imageDiff = { name: snapshotName, anchors: [`attachment-${name}`] };
|
imageDiff = { name: snapshotName, anchors: [`attachment-${name}`] };
|
||||||
snapshotNameToImageDiff.set(snapshotName, imageDiff);
|
snapshotNameToImageDiff.set(snapshotName, imageDiff);
|
||||||
}
|
}
|
||||||
imageDiff.anchors.push(`attachment-${attachment.name}`);
|
imageDiff.anchors.push(`attachment-${result.attachments.indexOf(attachment)}`);
|
||||||
if (category === 'actual')
|
if (category === 'actual')
|
||||||
imageDiff.actual = { attachment };
|
imageDiff.actual = { attachment };
|
||||||
if (category === 'expected')
|
if (category === 'expected')
|
||||||
|
|
@ -72,15 +72,15 @@ export const TestResultView: React.FC<{
|
||||||
result: TestResult,
|
result: TestResult,
|
||||||
}> = ({ test, result }) => {
|
}> = ({ test, result }) => {
|
||||||
const { screenshots, videos, traces, otherAttachments, diffs, errors, otherAttachmentAnchors, screenshotAnchors } = React.useMemo(() => {
|
const { screenshots, videos, traces, otherAttachments, diffs, errors, otherAttachmentAnchors, screenshotAnchors } = React.useMemo(() => {
|
||||||
const attachments = result?.attachments || [];
|
const attachments = result.attachments;
|
||||||
const screenshots = new Set(attachments.filter(a => a.contentType.startsWith('image/')));
|
const screenshots = new Set(attachments.filter(a => a.contentType.startsWith('image/')));
|
||||||
const screenshotAnchors = [...screenshots].map(a => `attachment-${a.name}`);
|
const screenshotAnchors = [...screenshots].map(a => `attachment-${attachments.indexOf(a)}`);
|
||||||
const videos = attachments.filter(a => a.contentType.startsWith('video/'));
|
const videos = attachments.filter(a => a.contentType.startsWith('video/'));
|
||||||
const traces = attachments.filter(a => a.name === 'trace');
|
const traces = attachments.filter(a => a.name === 'trace');
|
||||||
const otherAttachments = new Set<TestAttachment>(attachments);
|
const otherAttachments = new Set<TestAttachment>(attachments);
|
||||||
[...screenshots, ...videos, ...traces].forEach(a => otherAttachments.delete(a));
|
[...screenshots, ...videos, ...traces].forEach(a => otherAttachments.delete(a));
|
||||||
const otherAttachmentAnchors = [...otherAttachments].map(a => `attachment-${a.name}`);
|
const otherAttachmentAnchors = [...otherAttachments].map(a => `attachment-${attachments.indexOf(a)}`);
|
||||||
const diffs = groupImageDiffs(screenshots);
|
const diffs = groupImageDiffs(screenshots, result);
|
||||||
const errors = classifyErrors(result.errors, diffs);
|
const errors = classifyErrors(result.errors, diffs);
|
||||||
return { screenshots: [...screenshots], videos, traces, otherAttachments, diffs, errors, otherAttachmentAnchors, screenshotAnchors };
|
return { screenshots: [...screenshots], videos, traces, otherAttachments, diffs, errors, otherAttachmentAnchors, screenshotAnchors };
|
||||||
}, [result]);
|
}, [result]);
|
||||||
|
|
@ -107,11 +107,11 @@ export const TestResultView: React.FC<{
|
||||||
|
|
||||||
{!!screenshots.length && <AutoChip header='Screenshots' revealOnAnchorId={screenshotAnchors}>
|
{!!screenshots.length && <AutoChip header='Screenshots' revealOnAnchorId={screenshotAnchors}>
|
||||||
{screenshots.map((a, i) => {
|
{screenshots.map((a, i) => {
|
||||||
return <Anchor key={`screenshot-${i}`} id={`attachment-${a.name}`}>
|
return <Anchor key={`screenshot-${i}`} id={`attachment-${result.attachments.indexOf(a)}`}>
|
||||||
<a href={a.path}>
|
<a href={a.path}>
|
||||||
<img className='screenshot' src={a.path} />
|
<img className='screenshot' src={a.path} />
|
||||||
</a>
|
</a>
|
||||||
<AttachmentLink attachment={a}></AttachmentLink>
|
<AttachmentLink attachment={a} result={result}></AttachmentLink>
|
||||||
</Anchor>;
|
</Anchor>;
|
||||||
})}
|
})}
|
||||||
</AutoChip>}
|
</AutoChip>}
|
||||||
|
|
@ -121,7 +121,7 @@ export const TestResultView: React.FC<{
|
||||||
<a href={generateTraceUrl(traces)}>
|
<a href={generateTraceUrl(traces)}>
|
||||||
<img className='screenshot' src={traceImage} style={{ width: 192, height: 117, marginLeft: 20 }} />
|
<img className='screenshot' src={traceImage} style={{ width: 192, height: 117, marginLeft: 20 }} />
|
||||||
</a>
|
</a>
|
||||||
{traces.map((a, i) => <AttachmentLink key={`trace-${i}`} attachment={a} linkName={traces.length === 1 ? 'trace' : `trace-${i + 1}`}></AttachmentLink>)}
|
{traces.map((a, i) => <AttachmentLink key={`trace-${i}`} attachment={a} result={result} linkName={traces.length === 1 ? 'trace' : `trace-${i + 1}`}></AttachmentLink>)}
|
||||||
</div>}
|
</div>}
|
||||||
</AutoChip></Anchor>}
|
</AutoChip></Anchor>}
|
||||||
|
|
||||||
|
|
@ -130,14 +130,14 @@ export const TestResultView: React.FC<{
|
||||||
<video controls>
|
<video controls>
|
||||||
<source src={a.path} type={a.contentType}/>
|
<source src={a.path} type={a.contentType}/>
|
||||||
</video>
|
</video>
|
||||||
<AttachmentLink attachment={a}></AttachmentLink>
|
<AttachmentLink attachment={a} result={result}></AttachmentLink>
|
||||||
</div>)}
|
</div>)}
|
||||||
</AutoChip></Anchor>}
|
</AutoChip></Anchor>}
|
||||||
|
|
||||||
{!!otherAttachments.size && <AutoChip header='Attachments' revealOnAnchorId={otherAttachmentAnchors}>
|
{!!otherAttachments.size && <AutoChip header='Attachments' revealOnAnchorId={otherAttachmentAnchors}>
|
||||||
{[...otherAttachments].map((a, i) =>
|
{[...otherAttachments].map((a, i) =>
|
||||||
<Anchor key={`attachment-link-${i}`} id={`attachment-${a.name}`}>
|
<Anchor key={`attachment-link-${i}`} id={`attachment-${result.attachments.indexOf(a)}`}>
|
||||||
<AttachmentLink attachment={a} openInNewTab={a.contentType.startsWith('text/html')} />
|
<AttachmentLink attachment={a} result={result} openInNewTab={a.contentType.startsWith('text/html')} />
|
||||||
</Anchor>
|
</Anchor>
|
||||||
)}
|
)}
|
||||||
</AutoChip>}
|
</AutoChip>}
|
||||||
|
|
@ -174,10 +174,9 @@ const StepTreeItem: React.FC<{
|
||||||
step: TestStep;
|
step: TestStep;
|
||||||
depth: number,
|
depth: number,
|
||||||
}> = ({ test, step, result, depth }) => {
|
}> = ({ test, step, result, depth }) => {
|
||||||
const attachmentName = step.title.match(/^attach "(.*)"$/)?.[1];
|
|
||||||
return <TreeItem title={<span aria-label={step.title}>
|
return <TreeItem title={<span aria-label={step.title}>
|
||||||
<span style={{ float: 'right' }}>{msToString(step.duration)}</span>
|
<span style={{ float: 'right' }}>{msToString(step.duration)}</span>
|
||||||
{attachmentName && <a style={{ float: 'right' }} title='link to attachment' href={testResultHref({ test, result, anchor: `attachment-${attachmentName}` })} onClick={evt => { evt.stopPropagation(); }}>{icons.attachment()}</a>}
|
{step.attachments.length > 0 && <a style={{ float: 'right' }} title='link to attachment' href={testResultHref({ test, result, anchor: `attachment-${step.attachments[0]}` })} onClick={evt => { evt.stopPropagation(); }}>{icons.attachment()}</a>}
|
||||||
{statusIcon(step.error || step.duration === -1 ? 'failed' : 'passed')}
|
{statusIcon(step.error || step.duration === -1 ? 'failed' : 'passed')}
|
||||||
<span>{step.title}</span>
|
<span>{step.title}</span>
|
||||||
{step.count > 1 && <> ✕ <span className='test-result-counter'>{step.count}</span></>}
|
{step.count > 1 && <> ✕ <span className='test-result-counter'>{step.count}</span></>}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue