镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

test(browser): click a reply link only after its row settles - #2358

Merged
matt2e merged 1 commit into
mainfrom
flakey-ci-wednesday
Nov 4, 2026
Merged

matt2e merged 1 commit into
mainfrom
flakey-ci-wednesday

Conversation

@matt2e

@matt2e matt2e commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

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 :202 with toBeInViewport on 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.

  • The test clicked the newly appended message's link before its row had settled. The row first mounts unmeasured (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".
  • Each retry calls scrollIntoView with 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.
  • 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, so the link's onClick never runs and the URL never gains messageId.

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.mjs already uses this pattern for the same problem. Product code is unchanged.

Verification (local macOS, pinned Playwright, --no-deps)

  • Fail-before: I built a deterministic repro and did not commit it. An init script delays the timeline row's ResizeObserver callbacks by 450 ms. It also holds the forced-alignment scroll event until the press's mousedown, which matches the 85–114 ms click step seen in CI. With this injection, the unchanged test failed 10/10 on WebKit with the CI signature: the release lands on the section, the list has pointer-events: none, and there is no messageId.
  • Pass-after: With the same injection, the changed test passed 10/10, and no forced-scroll retry happened.
  • Control: Waiting for the visible, named link without virtuaIdle also passed 4/4 under the injection. virtuaIdle is 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 runs: 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 in the file pass on both engines.

Browser cases added: 0. Removed: 0.

Deferred checks

  • Hosted CI has not run on this change yet.
  • A green run does not prove the flake is gone.

🤖 Generated with Claude Code

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
matt2e marked this pull request as ready for review November 4, 2026 02:27
@matt2e
matt2e requested a review from a team as a code owner November 4, 2026 02:27
@matt2e
matt2e merged commit bb46c36 into main Nov 4, 2026
43 checks passed
@matt2e
matt2e deleted the flakey-ci-wednesday branch November 4, 2026 07:08
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
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.

2 participants