Skip to content

test(desktop): stop the picker spec racing two upstream guards - #4225

Open
orangeCatDeveloper wants to merge 2 commits into
apache:mainfrom
orangeCatDeveloper:fix/4224-picker-reopen-race
Open

test(desktop): stop the picker spec racing two upstream guards#4225
orangeCatDeveloper wants to merge 2 commits into
apache:mainfrom
orangeCatDeveloper:fix/4224-picker-reopen-race

Conversation

@orangeCatDeveloper

@orangeCatDeveloper orangeCatDeveloper commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Fixes #4224

new-task-draft-target.spec.ts fails every run on macOS: after choosing 无项目 it reopens the workspace picker, and that click opens nothing, so the wait for the Project menu item times out at 30s. Two separate races were in the way; both live in @astryxdesign/core, and neither is reachable at human speed.

The reopen. DropdownMenu drops a trigger click that lands within 50ms of the menu hiding:

if (Date.now() - lastHideTimeRef.current < 50) {
  return;
}

The guard exists so iOS Safari's light dismiss — pointerdown hides the menu, then the trigger's own click arrives — cannot instantly reopen it. But usePopover({ onHide: handleLayerHide }) stamps that timestamp on every hide, so choosing a menu item arms it the same way. Whether the reopen survives then depends on how long the assertions between the two clicks happen to take. The spec now waits for the menu it asked for instead of assuming the click took, which also holds if that 50ms ever changes.

The typing. With the reopen fixed, a ~4% failure the first one had been masking surfaced: the draft came back scrambled ("idraft written before choosing a project", "drafet written before choosing a project" — one character displaced to an earlier offset). ChatComposerInput absorbs exactly one echo of its own onChange; a second keystroke inside the same frame overwrites that marker, so the pending echo is read as an external override:

editable.textContent = controlledValue;
// Setting `textContent` tears down the existing text node,
// which collapses any Selection inside this editable to
// offset 0.

A keystroke landing between that rewrite and the caret restore goes in at offset 0. page.keyboard.type with no delay is the only thing that types two characters inside one frame, so the spec now types at 20ms/char.

Before / after, 50 runs of this spec each:

2 failed
  e2e/new-task-draft-target.spec.ts:62:1 › the new-task draft follows the Project chosen under the composer
  e2e/new-task-draft-target.spec.ts:62:1 › the new-task draft follows the Project chosen under the composer
48 passed (3.1m)

50 passed (3.1m)

(The 50-run "before" is already past the reopen fix — with the original spec the failure is 100%, not 4%.)

Both are test-side fixes on purpose. The dropdown guard is load-bearing on iOS Safari, and the composer's echo window is one frame: no user clicks a trigger 50ms after picking from its menu, and none types two characters in 16ms.

Generative tooling: Claude Code (Opus 5) investigated and wrote this change; the commits carry a Generated-by trailer.

@github-actions github-actions Bot added the effort/S Under 100 readable lines label Aug 29, 2026
@orangeCatDeveloper orangeCatDeveloper changed the title test(desktop): wait for the picker menu to open test(desktop): stop the picker spec racing two upstream guards Aug 29, 2026
Reopening the picker right after choosing an item raced DropdownMenu's 50ms
light-dismiss guard, which every hide arms, so the click was dropped and the
spec failed on whichever machine ran the assertions between them fast enough.

Generated-by: Claude Code
ChatComposerInput absorbs one echo of its own onChange, so two keystrokes
inside one frame make it treat the DOM as an external override, rewrite the
editor and collapse the caret to offset 0. Zero-delay typing is the only
thing that reaches that window.

Generated-by: Claude Code
@orangeCatDeveloper
orangeCatDeveloper force-pushed the fix/4224-picker-reopen-race branch from 22819fb to 686105e Compare August 29, 2026 20:56

@jackwener jackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found no blocking issues on 686105e8c514488589d85dc3f25c98cbb3168963.

The test changes keep the user contract intact while removing two automation-only timing assumptions. The real DropdownMenu stamps its 50 ms suppression window on every hide; in a controlled-clock probe through the built Electron app, a reopen inside that window left aria-expanded=false with no menu items, while the same trigger opened after the clock advanced. openPicker therefore retries only the dropped trigger action and still requires the requested menu to become visible. The 20 ms typing delay also remains far faster than ordinary human input while keeping successive synthetic keystrokes out of the composer's one-frame controlled-echo race.

An uncontrolled old-spec control happened to pass 10/10 on this machine, so I did not treat the issue's local failure rate as proof. I independently verified the actual dropped-click branch above, then ran the exact changed spec 10 times: both journeys passed 20/20. The full Desktop dependency/application build, Biome, diff-check, ASF headers, the exact-head hosted test check, and the current-main merge result also passed.


Automated review notice: This comment was posted by an automated review agent operated by WAWQAQ. It is not an independent human review and does not replace one.

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Synthesis at exact head 686105e8c514488589d85dc3f25c98cbb3168963 (686105e) — test(desktop): stop the picker spec racing two upstream guards.

All findings in this comment are from @DSv4F-Qronos-AstroHan independent review at this exact head. I did not read this diff myself; I verified that the current head has not drifted and the exact-head CI status is green (test SUCCESS, MERGEABLE/CLEAN per Luna coordination re-check).

Actual change (per reviewer, 1 file +14/-3)

  • picker.click() with toPass({timeout:10_000}) retry for DropdownMenu 50ms reopen吞点击竞态,page.keyboard.type(DRAFT,{delay:20}) 人速输入避免同帧双键重写 caret。
  • toPass 10s 超时仍失败,不掩盖真实失败;delay:20 > 16.7ms/帧保证单键单帧;无其他 spec 使用该 picker,无一致性缺口。

Gate

  • Code: GO, no P0–P2 (1×P3 low risk: openPicker uses page.getByRole('menuitem').first() global query, but picker is sole menu source in this spec).
  • CI: exact-head test SUCCESS (13m20s), MERGEABLE/CLEAN, reviews: prior jackwener APPROVED at this head is automated and does not count as independent human review.
  • MERGEABLE/CLEAN, REVIEW_REQUIRED — requires human formal Review before merge.

What this synthesis could not verify

  • No local Electron E2E run on this head by synthesizer; relied on reviewer's code-path deduction and hosted CI. Storybook visual state not re-run here.

Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

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

Labels

effort/S Under 100 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: new-task-draft-target e2e fails on macOS — picker reopen races DropdownMenu's 50ms guard

3 participants