Skip to content

test(remote-react-components): keep the pointer on a hovered trigger while capturing - #2991

Closed
mfal wants to merge 1 commit into
mainfrom
fix/tooltip-visual-hover-race
Closed

test(remote-react-components): keep the pointer on a hovered trigger while capturing#2991
mfal wants to merge 1 commit into
mainfrom
fix/tooltip-visual-hover-race

Conversation

@mfal

@mfal mfal commented Aug 28, 2026

Copy link
Copy Markdown
Member

What & why

The Tooltip scenario has kept the scheduled visual run red in both
environments since 2026-08-27
, and it is not a rendering regression. The
firefox-linux baseline for Tooltip - visible shows the button with no
tooltip
— the three baselines beside it all show one:

Baseline Size Tooltip visible?
Tooltip-visible-firefox-darwin.png 9,172 B yes
Tooltip-visible-webkit-darwin.png 10,071 B yes
Tooltip-visible-webkit-linux.png 10,043 B yes
Tooltip-visible-firefox-linux.png 6,640 B no

Local and Remote share one baseline per description, which is why a single
bad file fails both.

Why the scenario was racy

It is the only visual scenario that hovers before capturing (checked across all
84 files). testScreenshot then parks the pointer, un-hovering the trigger.
react-aria keeps a tooltip painted for closeDelay — 500ms by default
(TOOLTIP_COOLDOWN, useTooltipTriggerState.mjs:17) — so the capture normally
still catches it. That is a race, not a guarantee: on a slow runner the frame
comes out tooltip-less. The scenario only ever passed by outrunning a timer it
does not control.

How the bad baseline got committed

git log on the file points at 6bf57c7fix(CodeBlock): balance the padding and unify the editor line colors (#2945). It regenerated 21
baselines: CodeBlock, CodeEditor, Markdown — all legitimate — plus this one
Tooltip file
, which has nothing to do with CodeBlock, and only the
firefox-linux variant of it.

That is the update-screenshots label working as built: it runs
pnpm test:visual --update over the whole suite and then git add -A, with no
scoping to the PR's diff. #2945 happened to lose the tooltip race and committed
that frame as the new truth.

The fix

Stop parking the pointer for a scenario whose subject is a hover state.
keepPointerPosition leaves the pointer where the scenario put it, and a tooltip
held open by a pointer that never leaves has no closing window at all.

Restoring the pre-#2945 file would make the suite green again while leaving a
test that fails whenever a runner is slow, so it is not the fix here.

The trigger is now captured in its hover state — which is what a user actually
sees with a tooltip open — so all four baselines change. The two -darwin
ones are regenerated in this PR.

⚠️ Merge order — please read

  1. Merge test(remote-react-components): settle the tree before parking the pointer #2985 first. This branch is based on main, which still has the
    focus-ring bug test(remote-react-components): settle the tree before parking the pointer #2985 fixes.
  2. Only then add update-screenshots here, to regenerate the two -linux
    baselines. Applying it before test(remote-react-components): settle the tree before parking the pointer #2985 lands would run the whole suite with
    --update while List items (Remote) and List date range filter (Remote)
    are still failing, and commit those broken frames too — the exact mechanism
    that produced this bug.

Until step 2 runs, run-visual-tests on this PR will fail on the two -linux
Tooltip baselines. That is expected.

Verification

Check Result
Tooltip.browser.test.tsx, firefox passes 3/3
Tooltip.browser.test.tsx, webkit passes 3/3
Regenerated -darwin baselines tooltip visible, trigger hovered — inspected both
test:compile, eslint, prettier clean

Also in here

Two rows in the root AGENTS.md Common failures table, both verified while
diagnosing this:

Checklist

  • PR title is a Conventional Commit and matches the base branch above
  • pnpm lint is clean; browser tests pass in both browsers
  • Generated code is committed — this change touches no generated artifact
  • User-facing strings added to both de-DE and en-US — n/a, no UI text
  • Docs updated if a public API changed — n/a; snapshot updates are
    intentional and explained above (-linux half pending, see Merge order)

🤖 Generated with Claude Code

…while capturing

The `Tooltip` scenario has kept the scheduled visual run red in both
environments since 2026-08-27, and it is not a rendering regression: the
`firefox-linux` baseline for `Tooltip - visible` shows the button with no
tooltip. The three baselines beside it all show one.

It is the only visual scenario that hovers before capturing, and
`testScreenshot` then parks the pointer, un-hovering the trigger. react-aria
keeps a tooltip painted for `closeDelay` — 500ms by default — so the capture
normally still catches it. That is a race, not a guarantee: on a slow runner the
frame comes out tooltip-less. The scenario passed by outrunning a timer it does
not control.

#2945 lost that race. It regenerated screenshots for a CodeBlock change, and
because the `update-screenshots` label runs the whole suite with `--update` and
then `git add -A`, it also committed the tooltip-less frame as this baseline —
one stray file in an otherwise unrelated PR. Local and Remote share a baseline
per description, which is why one bad file fails both.

So stop parking the pointer for a scenario whose subject is a hover state.
`keepPointerPosition` leaves it where the scenario put it, and a tooltip held
open by a pointer that never leaves has no closing window at all. The trigger is
then captured hovered, which is what a user actually sees with a tooltip open,
so all four baselines change; the two `-darwin` ones are regenerated here.

Also document the two traps behind this in the root AGENTS.md: the unscoped
`update-screenshots` sweep, and `vitest run --update <file>` — where `--update`
takes an optional value and swallows the positional filter, so the run updates
every baseline and only the test count (354, not 2) gives it away. Both cost
real time while diagnosing this, and the second nearly re-committed the very
frames #2985 fixes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mfal
mfal requested a review from a team August 28, 2026 09:33
@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for ./packages/components/

Status Category Percentage Covered / Total
🔵 Lines 76% 627 / 825
🔵 Statements 75.91% 643 / 847
🔵 Functions 77.95% 145 / 186
🔵 Branches 66.66% 298 / 447
File CoverageNo changed files found.
Generated in workflow #6382 for commit 19eb408 by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Preview Deployment

Preview environments are ready:

Type URL
docs pr-2991.docs.review.flow-components.de
storybook pr-2991.storybook.review.flow-components.de

Images:

  • docs: ghcr.io/mittwald/flow/docs:pr-2991
  • storybook: ghcr.io/mittwald/flow/storybook:pr-2991

@mfal

mfal commented Aug 28, 2026

Copy link
Copy Markdown
Member Author

Folded into #2985.

Both fixes have to land together: this branch needed update-screenshots to regenerate its two -linux Tooltip baselines, but running that sweep while main still had the focus-ring bug would have committed the broken List frames too — the exact mechanism that produced this bug. With the focus-ring fix and the tooltip fix in one PR, the sweep has no other failing scenario to bake in.

The commit is carried over unchanged apart from the conflict resolution in environments.tsx, where keepPointerPosition is now threaded through prepareForScreenshot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant