abstract href generator

This commit is contained in:
Simon Knott 2024-12-11 16:16:37 +01:00
parent 2ae3918401
commit f04234b3df
No known key found for this signature in database
GPG key ID: 8CEDC00028084AEC
4 changed files with 23 additions and 11 deletions

View file

@ -14,7 +14,7 @@
limitations under the License.
*/
import type { TestAttachment } from './types';
import type { TestAttachment, TestCase, TestCaseSummary, TestResult, TestResultSummary } from './types';
import * as React from 'react';
import * as icons from './icons';
import { TreeItem } from './treeItem';
@ -22,6 +22,7 @@ import { CopyToClipboard } from './copyToClipboard';
import './links.css';
import { linkifyText } from '@web/renderUtils';
import { clsx } from '@web/uiUtils';
import { URLSearchParams } from 'url';
export function navigate(href: string) {
window.history.pushState({}, '', href);
@ -149,3 +150,14 @@ export function Anchor({ id, children }: React.PropsWithChildren<{ id: AnchorID
return <div ref={ref}>{children}</div>;
}
export function testResultHref({ test, result, anchor }: { test?: TestCase | TestCaseSummary, result?: TestResult | TestResultSummary, anchor?: string }) {
const params = new URLSearchParams();
if (test)
params.set('testId', test.testId);
if (test && result)
params.set('run', '' + test.results.indexOf(result as any));
if (anchor)
params.set('anchor', anchor);
return `#?` + params;
}

View file

@ -19,7 +19,7 @@ import * as React from 'react';
import { TabbedPane } from './tabbedPane';
import { AutoChip } from './chip';
import './common.css';
import { Link, ProjectLink, SearchParamsContext } from './links';
import { Link, ProjectLink, SearchParamsContext, testResultHref } from './links';
import { statusIcon } from './statusIcon';
import './testCaseView.css';
import { TestResultView } from './testResultView';
@ -53,9 +53,9 @@ export const TestCaseView: React.FC<{
{test && <div className='hbox'>
<div className='test-case-path'>{test.path.join(' ')}</div>
<div style={{ flex: 'auto' }}></div>
<div className={clsx(!prev && 'hidden')}><Link href={`#?testId=${prev?.testId}${filterParam}`}>« previous</Link></div>
<div className={clsx(!prev && 'hidden')}><Link href={testResultHref({ test: prev }) + filterParam}>« previous</Link></div>
<div style={{ width: 10 }}></div>
<div className={clsx(!next && 'hidden')}><Link href={`#?testId=${next?.testId}${filterParam}`}>next »</Link></div>
<div className={clsx(!next && 'hidden')}><Link href={testResultHref({ test: next }) + filterParam}>next »</Link></div>
</div>}
{test && <div className='test-case-title'>{test?.title}</div>}
{test && <div className='hbox'>

View file

@ -19,7 +19,7 @@ import * as React from 'react';
import { hashStringToInt, msToString } from './utils';
import { Chip } from './chip';
import { filterWithToken } from './filter';
import { generateTraceUrl, Link, navigate, ProjectLink, SearchParamsContext } from './links';
import { generateTraceUrl, Link, navigate, ProjectLink, SearchParamsContext, testResultHref } from './links';
import { statusIcon } from './statusIcon';
import './testFileView.css';
import { video, image, trace } from './icons';
@ -48,7 +48,7 @@ export const TestFileView: React.FC<React.PropsWithChildren<{
{statusIcon(test.outcome)}
</span>
<span>
<Link href={`#?testId=${test.testId}${filterParam}`} title={[...test.path, test.title].join(' ')}>
<Link href={testResultHref({ test }) + filterParam} title={[...test.path, test.title].join(' ')}>
<span className='test-file-title'>{[...test.path, test.title].join(' ')}</span>
</Link>
{projectNames.length > 1 && !!test.projectName &&
@ -59,7 +59,7 @@ export const TestFileView: React.FC<React.PropsWithChildren<{
<span data-testid='test-duration' style={{ minWidth: '50px', textAlign: 'right' }}>{msToString(test.duration)}</span>
</div>
<div className='test-file-details-row'>
<Link href={`#?testId=${test.testId}`} title={[...test.path, test.title].join(' ')} className='test-file-path-link'>
<Link href={testResultHref({ test })} title={[...test.path, test.title].join(' ')} className='test-file-path-link'>
<span className='test-file-path'>{test.location.file}:{test.location.line}</span>
</Link>
{imageDiffBadge(test)}
@ -75,14 +75,14 @@ function imageDiffBadge(test: TestCaseSummary): JSX.Element | undefined {
for (const result of test.results) {
for (const attachment of result.attachments) {
if (attachment.contentType.startsWith('image/') && !!attachment.name.match(/-(expected|actual|diff)/))
return <Link href={`#?` + new URLSearchParams({ testId: test.testId, anchor: `attachment-${attachment.name}`, run: '' + test.results.indexOf(result) })} title='View images' className='test-file-badge'>{image()}</Link>;
return <Link href={testResultHref({ test, result, anchor: `attachment-${attachment.name}` })} title='View images' className='test-file-badge'>{image()}</Link>;
}
}
}
function videoBadge(test: TestCaseSummary): JSX.Element | undefined {
const resultWithVideo = test.results.find(result => result.attachments.some(attachment => attachment.name === 'video'));
return resultWithVideo ? <Link href={`#?` + new URLSearchParams({ testId: test.testId, anchor: `attachment-video`, run: '' + test.results.indexOf(resultWithVideo) })} title='View video' className='test-file-badge'>{video()}</Link> : undefined;
return resultWithVideo ? <Link href={testResultHref({ test, result: resultWithVideo, anchor: 'attachment-video' })} title='View video' className='test-file-badge'>{video()}</Link> : undefined;
}
function traceBadge(test: TestCaseSummary): JSX.Element | undefined {

View file

@ -20,7 +20,7 @@ import { TreeItem } from './treeItem';
import { msToString } from './utils';
import { AutoChip } from './chip';
import { traceImage } from './images';
import { Anchor, AttachmentLink, generateTraceUrl } from './links';
import { Anchor, AttachmentLink, generateTraceUrl, testResultHref } from './links';
import { statusIcon } from './statusIcon';
import type { ImageDiff } from '@web/shared/imageDiffView';
import { ImageDiffView } from '@web/shared/imageDiffView';
@ -177,7 +177,7 @@ const StepTreeItem: React.FC<{
const attachmentName = step.title.match(/^attach "(.*)"$/)?.[1];
return <TreeItem title={<span aria-label={step.title}>
<span style={{ float: 'right' }}>{msToString(step.duration)}</span>
{attachmentName && <a style={{ float: 'right' }} title='link to attachment' href={`#?testId=${test.testId}&anchor=attachment-${encodeURIComponent(attachmentName)}&run=${test.results.indexOf(result)}`} onClick={evt => { evt.stopPropagation(); }}>{icons.attachment()}</a>}
{attachmentName && <a style={{ float: 'right' }} title='link to attachment' href={testResultHref({ test, result, anchor: `attachment-${attachmentName}` })} onClick={evt => { evt.stopPropagation(); }}>{icons.attachment()}</a>}
{statusIcon(step.error || step.duration === -1 ? 'failed' : 'passed')}
<span>{step.title}</span>
{step.count > 1 && <> <span className='test-result-counter'>{step.count}</span></>}