Repository navigation
test(browser): click a reply link only after its row settles - #2358
Merged
Merged
Conversation
thread-composer-focus.spec.mjs:168 "a link to a specific reply reveals
it without focusing the composer" failed on its own in WebKit shard
10/14 on two unrelated branches in 36 h (run 41494357757 job
125445893123; run 41529537399 job 125566410581, which passed on retry
of the same SHA). Both failed at :202, `toBeInViewport` on the thread
row: "element(s) not found".
Cause: the thread never opened because the click on the link was lost.
The test clicked the freshly appended message's link before its row
had settled. The product is not at fault: a real press and release on
the link opens the thread.
- The appended row mounts unmeasured (`visibility: hidden`), and its
link label then changes from "Alpha" to "Alice Fixture in #Alpha" when
the byline resolves. The timeline follows both. In both traces,
Playwright's first click attempt failed ("element is not stable" /
"element is not visible").
- Each retry calls scrollIntoView with a forced alignment. That really
scrolls the timeline: both traces show the scroller moving from 2192
to 2214, and in t1 then to 2267 at the next retry. Its scroll event is
handled in the next rendering update. Under CI load, that update ran
after Playwright's press, which passed the hit test on the link, but
before its release.
- Virtua treats that event as a user scroll and sets `pointer-events:
none` on the list for 150 ms. The release then lands on the scroller
`section`, `click` goes to the common ancestor, and the link's
onClick never runs. In run 41529537399 the input snapshot (7133 ms)
has the list hittable at scrollTop 2214. The snapshot after the click
(7261 ms) has the same scrollTop with `pointer-events: none`, and the
URL never gains `messageId`. In run 41494357757 the attempt-1 press
was already intercepted by the `section` (the same hold, caught at
press). The attempt-2 press hit the link, and its after snapshot again
shows `pointer-events: none` at an unchanged scrollTop.
- Locally the first attempt succeeds, so no forced scroll happens.
Change: before pointer input, find the link by role with its resolved
name and wait for it to be visible, then for `virtuaIdle`. This is the
buzz-links.spec.mjs idiom for the same hazard. No product code changes.
Verification (local macOS, pinned Playwright, `--no-deps`):
- Deterministic repro, not committed. An init script delivers the
timeline's row ResizeObserver callbacks 450 ms late, so the click
reaches a forced-scroll retry after Virtua has idled. During the
click, it also holds any timeline scroll event that neither page
script nor layout caused (Playwright's utility-world scrollIntoView)
until the press's mousedown. It runs Virtua's listener there so the
re-render commits before the release, as CI's 85-114 ms click step
allowed. The unchanged test failed 10/10 on WebKit with CI's
signature: press on the link, release and click on the SECTION with
the list at `pointer-events: none`, no `messageId` in the URL, and
"element(s) not found" at :202. The 53 px forced scroll matches CI
and the Linux WebKit container.
- The changed test passed 10/10 with the same injection. There was no
forced-scroll retry.
- Control, not committed: waiting for the visible, named link without
`virtuaIdle` also passed 4/4 under this injection. So the repro
isolates waiting for the settled, named row. `virtuaIdle` is kept for
the re-measure after the name change and for Virtua's hold, which
intercepted t1's attempt-1 press.
- The changed test passed 30/30 on WebKit with `--repeat-each=30`,
24/24 with 6 workers, and 12/12 on Chromium with 4 workers. All 7
cases of thread-composer-focus.spec.mjs pass on WebKit and Chromium.
- After switching to the role-and-name locator, the changed test passed
10/10 on WebKit with `--repeat-each=10`, and all 7 cases pass again on
both engines.
Browser cases added: 0. Removed: 0. Hosted CI has not run on this
change yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
matt2e
marked this pull request as ready for review
November 4, 2026 02:27
kalvinnchau
approved these changes
Nov 4, 2026
gilgtm
added a commit
that referenced
this pull request
Nov 4, 2026
* origin/main: (52 commits) feat(agents): give plugin agent types the Buzz side of a turn (#2364) feat(plugins): pin buzz://plugin-install links to a path and commit (#2315) feat(plugins): tint currentColor SVG page icons like host icons (#2314) feat(notifications): add a soft notification sound (#1536) feat(agents): let agents propose a profile picture (#2267) fix(plugins): default reminders and workflows off for fresh installs (#2291) Show all reactors in compact, separate emoji capsules (#2179) fix(panels): remember page split widths per workspace (#2264) feat(search): browse open channels from a Channels tab in search (#2164) perf(relay): page combined replay filters after a reconnect (#2308) fix(composer): make native Mac text shortcuts work past link chips (#2390) perf(relay): read creator proofs for many channels per filter (#2312) fix(channels): let published leave and delete confirm through access loss (#2357) fix(media): save native context menu media downloads and hide loopback links on macOS (#2131) fix(links): say why a message preview is unavailable, and preview unjoined public channels (#2136) test(browser): serve emoji artwork in the reaction hit-area journey (#2138) test(browser): click a reply link only after its row settles (#2358) fix(agents): restore delegated workflow mentions (#2399) feat(identity): always back up keys in managed builds (#2398) test(threads): render before checking the reader across activity resizes (#2389) ... Signed-off-by: Gil Birman <gil@squareup.com> Co-authored-by: Claude <noreply@anthropic.com> # Conflicts: # src/plugins/author.test.mjs
wpfleger96
added a commit
that referenced
this pull request
Nov 4, 2026
…w-tab * origin/main: (41 commits) [3/5] Add protection to the Claude harness plugin (#1878) fix(settings): keep the bottom padding on long sections (#2361) fix(mermaid): cap diagram output size (#2231) feat(agents): give plugin agent types the Buzz side of a turn (#2364) feat(plugins): pin buzz://plugin-install links to a path and commit (#2315) feat(plugins): tint currentColor SVG page icons like host icons (#2314) feat(notifications): add a soft notification sound (#1536) feat(agents): let agents propose a profile picture (#2267) fix(plugins): default reminders and workflows off for fresh installs (#2291) Show all reactors in compact, separate emoji capsules (#2179) fix(panels): remember page split widths per workspace (#2264) feat(search): browse open channels from a Channels tab in search (#2164) perf(relay): page combined replay filters after a reconnect (#2308) fix(composer): make native Mac text shortcuts work past link chips (#2390) perf(relay): read creator proofs for many channels per filter (#2312) fix(channels): let published leave and delete confirm through access loss (#2357) fix(media): save native context menu media downloads and hide loopback links on macOS (#2131) fix(links): say why a message preview is unavailable, and preview unjoined public channels (#2136) test(browser): serve emoji artwork in the reaction hit-area journey (#2138) test(browser): click a reply link only after its row settles (#2358) ... # Conflicts: # src/buzz-mcp/tools.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes a WebKit flake in
thread-composer-focus.spec.mjs:168, "a link to a specific reply reveals it without focusing the composer". It failed in WebKit shard 10/14 on two unrelated branches within 36 h: run 41494357757, job 125445893123, and run 41529537399, job 125566410581. The second one passed when the same SHA was retried. Both runs failed at:202withtoBeInViewporton the thread row reporting "element(s) not found".Cause
The click on the link got lost, so the thread never opened. The product behaves correctly: a real press and release on the link opens the thread.
visibility: hidden). When the byline resolves, the link label changes from "Alpha" to "Alice Fixture in #Alpha", and the timeline follows both changes. In both traces, Playwright's first click attempt failed with "element is not stable" or "element is not visible".scrollIntoViewwith forced alignment, which really scrolls the timeline (2192 → 2214 px). Under CI load, the resulting scroll event was handled after Playwright's press but before its release.pointer-events: noneon the list for 150 ms. The release then lands on the scrollersection, so the link'sonClicknever runs and the URL never gainsmessageId.Change
Before any pointer input, the test now finds the link by role using its resolved name and waits for it to be visible, then waits for
virtuaIdle.buzz-links.spec.mjsalready uses this pattern for the same problem. Product code is unchanged.Verification (local macOS, pinned Playwright,
--no-deps)section, the list haspointer-events: none, and there is nomessageId.virtuaIdlealso passed 4/4 under the injection.virtuaIdleis kept to cover the re-measure after the name change and Virtua's hold, which intercepted the attempt-1 press in one CI trace.--repeat-each=30, 24/24 with 6 workers, and 12/12 on Chromium with 4 workers. All 7 cases in the file pass on both engines.Browser cases added: 0. Removed: 0.
Deferred checks
🤖 Generated with Claude Code