-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(html): link from attachment step to attachment #33267
Conversation
This comment has been minimized.
This comment has been minimized.
demo: Screen.Recording.2024-10-24.at.14.40.25.mov |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A screenshot of some sorts would be nice to see.
<span style={{ float: 'right' }}>{msToString(step.duration)}</span> | ||
{attachmentName && <a style={{ float: 'right' }} title='link to attachment' href={'#' + componentID(params => params.set('attachment', attachmentName))} onClick={(evt) => { evt.stopPropagation(); }}>{icons.attachment()}</a>} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this rely on the browser to automatically scroll to the anchor? How does that work together with the router? I am confused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's how it works. Didn't even know we had a router until now - i'll see if I can switch this to our Link
component.
I figured using the anchor to target an element is the most browser-standards way of making this work. But I can also change it to use scrollIntoView
if you'd prefer that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#33295 is a refactoring that allows us to use one scrollIntoView
implementation for all attachments. It makes this implementation a lot easier.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I've updated this PR to use the |
This comment has been minimized.
This comment has been minimized.
sorry, i'll need to look more into this to make links to |
Here's a demo of it working: Screen.Recording.2024-11-20.at.10.15.00.mov |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
}, [result]); | ||
|
||
const screenshotAnchor = React.useMemo(() => screenshots.map(a => `attachment-${a.name}`), [screenshots]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
screenshotAnchors
?- why not return this from the previous
useMemo()
?
Same for otherAttachmentsAnchor
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That'd also work, i'll make that change.
<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>} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I wonder why do we use
<Link>
in some places, and plain<a>
in others? - Can we simplify link generation to avoid passing
test
andresult
around somehow? Perhaps take existing ones from the current url? Or put them into a context? Something for a followup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- no idea 🤷 I guess I can also rewrite this to
<Link>
, but I don't think there'll be a big difference since it's not a text link but a button - agree, that'd be nice. i'll look at it in a followup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i've tried rewriting, but that doesn't work as well because <Link>
doesn't allow passing in the float: 'right'
style. Let's stick with <a>
.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
packages/html-reporter/src/links.tsx
Outdated
onReveal(); | ||
}; | ||
window.addEventListener('popstate', listener); | ||
|
||
// check if we're already on the anchor. required to make it work on page load. | ||
listener(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this scroll away on some re-renders when effect is re-registered for whatever reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. The effect re-registers when id
or onReveal
change. We can work against the onReveal
change using useCallback
, but id
might change for example when some UI element is added dynamically and now an enclosing Anchor needs to list more IDs. I'll need to rethink this.
How hard can it be to design a Reveal API in React?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I ended up reverting to the useEffect
-based version in 6b3bad4. That makes the code easier to reason about and also ensures that the page-load works as expected.
In a previous iteration, this approach had the problem that it wouldn't re-reveal an element if the current anchor is navigated to again. This iteration fixes that by making the searchParams
an effect dependency. So everytime there's a new searchParams
object, even if it contains the same parameter strings, the effect will re-fire.
return () => window.removeEventListener('popstate', listener); | ||
}, [id, onReveal]); | ||
} | ||
|
||
export function useIsAnchored(id: AnchorID) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we merge this with useAnchor()
and make it return boolean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that would only make it harder to use I'm afraid. This one is good for making CSS dependent on anchor, while the other one is good for calling imperative code on anchor change.
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={`#?testId=${test.testId}&anchor=attachment-${attachment.name}&run=${test.results.indexOf(result)}`} title='View images' className='test-file-badge'>{image()}</Link>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attachment.name
for sure needs escaping. Might as well use URLSearchParams
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 2ae3918
<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>} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we extract a helper function for attachmentAnchorURL()
and use everywhere? It gets pretty complicated with all the parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in f04234b
const navState = new URLSearchParams(url.hash.slice(1)); | ||
return navState.get('anchor') === 'attachment-foo'; | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we can use expect().toBeInViewport()
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 91ef9c6
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@@ -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'> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder whether testResultHref()
should automatically append filterParam
? For example, here and below at line 78 we lose it, which looks unintentional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. I'd rather fix that in a separate PR though, so it's easier to revert if we don't want it for some reason.
<ImageDiffView diff={diff}/> | ||
</AutoChip> | ||
</Anchor> | ||
)} | ||
|
||
{!!screenshots.length && <AutoChip header='Screenshots'> | ||
{!!screenshots.length && <Anchor id={screenshotAnchors}><AutoChip header='Screenshots' revealOnAnchorId={screenshotAnchors}> | ||
{screenshots.map((a, i) => { | ||
return <div key={`screenshot-${i}`}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to wrap each screenshot in Anchor
to be able to scroll to a particular one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch, added in d098a57
This comment has been minimized.
This comment has been minimized.
<ImageDiffView diff={diff}/> | ||
</AutoChip> | ||
</Anchor> | ||
)} | ||
|
||
{!!screenshots.length && <AutoChip header='Screenshots'> | ||
{!!screenshots.length && <Anchor id={screenshotAnchors}><AutoChip header='Screenshots' revealOnAnchorId={screenshotAnchors}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: do we need a big Anchor
here? I think expanding AutoChip
should just work, and each screenshot will be scrolled to by the inner Anchor
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point
Test results for "tests 1"1 failed 6 flaky37305 passed, 650 skipped Merge workflow run. |
Similar to #33265. Adds a link from the attachment step to the attachment view.