Skip to content

test(TabList): TabMenu's arrow-key tests wait for the open's focus to land - #8493

Merged
cixzhang merged 3 commits into
meta:mainfrom
vjeux:fix/tabmenu-test-open-focus
Oct 31, 2026
Merged

cixzhang merged 3 commits into
meta:mainfrom
vjeux:fix/tabmenu-test-open-focus

Conversation

@vjeux

@vjeux vjeux commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Review only this PR's change: vjeux/astryx@2b0f9a1...bcac705

Why

Two TabMenu keyboard tests in TabList.test.tsx fail now and then on loaded CI runners, which fails the UI test shard and the required test check of pull requests that never touch TabList (seen on #8045, whose diff is the interaction harness alone). The failure reads "Received element with focus: Reports".

What led to it

We traced it while keeping our own runs green. Opening a TabMenu moves focus to its first item a frame later (requestAnimationFrame(() => focusFirst()), the APG menu-button focus-on-open). The two tests press ArrowDown right after the click and expect it to reach the first item. That holds only while the frame has not fired: with nothing focused, ArrowDown focuses the first item; once focus is on it, ArrowDown moves on to the second. On a busy runner the frame fires first.

Options and tradeoffs

  • Move focus on open synchronously in the component: changes product behavior, and the frame exists so the menu is laid out when focus moves.
  • Fake timers in these tests: hides the frame the product relies on, and the tests would stop checking what a user gets.
  • Wait in the tests for focus to land on the first item, then press arrows (this pull request): the tests check the menu's behavior, whatever the runner's speed.

Why this choice

Test-only, and it asserts what a user sees: the menu opens with its first item focused, then ArrowDown and ArrowUp move from there. The other TabMenu tests (Home/End, Enter, Tab, Escape) don't depend on when that frame fires. The file's newer pointer test (#8791) is unchanged.

How to check it

  1. pnpm exec vitest run --project ui packages/core/src/TabList/TabList.test.tsx: every test passes.
  2. To see the race without a loaded machine, add await new Promise(r => setTimeout(r, 50)); after the click in "moves focus between items with ArrowDown and ArrowUp" and in "moves the roving tab stop with arrow navigation": on main both fail every time, with focus on Reports; here both pass.
  3. Under load, the two tests as written: 24 copies of the file at once with every core busy, five rounds. Main failed 21 of 240 runs; this branch failed 0 of 240.

Evidence

… land

Opening the TabMenu moves focus to its first item a frame later
(requestAnimationFrame, APG menu-button focus-on-open). Two keyboard tests
pressed ArrowDown right after the click and expected it to land on the first
item, which holds only while that frame has not fired: with nothing focused,
ArrowDown focuses the first item; once focus is there, it moves on to the
second. On a loaded CI runner the frame fired first and the test failed
with focus on Reports.

Both tests now wait for focus on the first item before pressing arrows,
and expect ArrowDown to reach the second item and ArrowUp to come back.
@vercel

vercel Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Oct 31, 2026 3:30am UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Oct 31, 2026, 3:29 AM UTC

@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR

🧪 Sandbox Preview

View Sandbox for this PR

Component Size

Component Import before Import after Delta CSS before CSS after Delta
core/Tab 10.5KB 10.5KB 0B 4.1KB 4.1KB 0B
core/TabList 40.8KB 40.8KB 0B 7.3KB 7.3KB 0B
core/TabMenu 56.8KB 56.8KB 0B 10.3KB 10.3KB 0B

Sizes are gzip. Import counts the JavaScript the import loads eagerly; CSS counts the package stylesheet rules referenced by the modules it can load from its own package, lazily loaded ones included.

Bundle Size Summary

Gzip figures measure each package's entry file, not the code an import pulls in.

Package Entry (ESM) Entry gzip before Entry gzip after Delta
@astryxdesign/core 4.9KB 1.2KB 1.2KB 0B
Package stylesheet Gzip before Gzip after Delta
@astryxdesign/core 35.6KB 35.6KB 0B

Accessibility Audit

Status: The accessibility audit did not run on this pull request. It runs after merges to main and before each release; pull requests that touch components run it with the ci:heavy-lanes label.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 26, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This looks good to merge: both TabMenu arrow-key tests now wait for the open's focus to land on the first item, so they pass whether that frame fires before or after the first key, and no shipped code changes. The fix matches the failure it targets: when the frame fires first, the previous assertions fail with focus on Reports while the new ones pass, and the new ones also fail if focus-on-open ever regresses. No changes are needed.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This looks good to merge: both TabMenu arrow-key tests now wait for the open's focus to land on the first option, so they pass whichever way that frame fires, and no shipped code changes. When the frame fires before the first key, the previous assertions fail with focus on Reports while the new ones pass, and the new ones also fail if focus-on-open, which TabMenu.spec.md FR3 promises, is ever removed. No changes are needed.

[Automated review]

@vjeux
vjeux marked this pull request as draft October 30, 2026 03:37
@cixzhang cixzhang added the ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) label Oct 31, 2026
@cixzhang
cixzhang marked this pull request as ready for review October 31, 2026 00:48

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks good to merge: both TabMenu arrow-key tests now wait for the menu's focus to land on the first option, so they pass whichever way that frame fires, and no shipped code changes. Letting the frame fire before the first key fails both tests on the base branch and passes them here, and removing focus-on-open makes the new waits fail, so the tests still catch a broken menu. Nothing further is needed from you.

[Automated review]

@cixzhang
cixzhang merged commit 6751c25 into meta:main Oct 31, 2026
58 checks passed
@vjeux
vjeux deleted the fix/tabmenu-test-open-focus branch October 31, 2026 03:50

This branch was successfully deployed

1 active deployment
Preview — 63afe852 Deployed Oct 31, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants