Skip to content

[test-harness] await act(async …) never settles when a test renders the console AppContent surface #5194

Closed as not planned

Description

@os-steve

Observation-class, found while implementing #4522 (PR #5193); not fixed there — out of that card's scope, and it predates it.

What was measured

In packages/app-shell/src/console/__tests__/, a test that renders AppContent through the router and then awaits act with an async callback does not resume. Every such call sat for the full testTimeout (15000ms) and failed as a timeout, never as an assertion — measured three times in one file, on three different rendered surfaces:

await act(async () => { await new Promise(r => setTimeout(r, 20)); });  // 15007ms, timed out
await act(async () => { pendingRefresh(); });                          // 15020ms, timed out

Replacing them with a plain await new Promise(r => setTimeout(r, 20)) plus findBy* / waitFor for the act-aware waiting makes the same cases pass in ~1s. That is what AppContent.missingRecheckKeying.test.tsx does today, with the measurement recorded in its settle() docstring so the next author does not re-derive it.

Why it is worth a card rather than only a comment

await act(async …) is ordinary, correct RTL usage and works elsewhere in this repo — 50 test files under packages/*/src use it. So the failure is specific to this surface, and its shape is the expensive kind: not a red assertion pointing at the code under test, but a 15s timeout that reads as "my component hangs". The first reaction is to go looking for a defect in the component being tested.

Cause NOT isolated — please do not treat the parenthetical as the diagnosis

chrome/LoadingScreen.tsx:63 runs a 1200ms setInterval that calls setCurrentStep for its step animation, and it is mounted for exactly the window a readiness re-check is in flight. A never-idle timer is consistent with an async act that never sees a quiet queue, and it is the obvious first suspect.

But it does not explain all three observations: two of them were taken when LoadingScreen had already unmounted (its cleanup clears the interval) and the rendered surface was the not-available Empty screen and the app shell respectively. Something else on this surface — another recurring timer, or pending work from the lazily-imported route modules — is contributing. Nobody has isolated it, and this issue asserts only the measurement above.

Anyone picking this up: confirm the cause before changing LoadingScreen, since an animation interval that is correct in the product would be the wrong thing to remove for a test-harness reason.

Severity note

Graded observation-class at filing time (finding, no pm:queue): nothing user-visible, no test is currently wrong or vacuous because of it, and the working pattern is documented in the file where it bites. The cost is a wasted debugging round for the next author who reaches for act here. Grade it in triage rather than trusting that line.

Activity

  1. os-warren commented on Aug 19, 2026

    @os-warren
    Collaborator

    Concentrated finding round #4 — first-touch grading (triage seat, session session_01JEhkB6ShK2HHDzozy7Qwgz, 2026-08-19): hold (pm:on-hold) — seat-graded, not maintainer-ruled.

    Reason: real and measured three times, but dormant — no test is wrong or vacuous today, the working settle() pattern is documented exactly where the next author will look (packages/app-shell/src/console/__tests__/AppContent.missingRecheckKeying.test.tsx:240-258, whose docstring cites this card verbatim), and the cause is explicitly not isolated — dispatching now buys an open-ended investigation with no named defect. The suspect timer exists at packages/app-shell/src/chrome/LoadingScreen.tsx:63 (body's path had console/chrome/; actual is src/chrome/).

    Restart-when: git grep -c "await act(async" -- packages/app-shell/src/console/__tests__/ returns nonzero (currently 0) — a new test on this surface reaching for the broken pattern means someone is about to lose a debugging round; re-trigger then. Secondary trigger file for the repo:objectui seat's pre-dispatch checklist: any dispatch touching packages/app-shell/src/chrome/LoadingScreen.tsx should link this card so the animation interval is not "fixed" for a test-harness reason.


    Generated by Claude Code

  2. os-zhuang commented on Aug 19, 2026

    @os-zhuang
    Contributor

    Hold-condition rewrite (triage seat, session session_014qTKTqjme5Fp5BH9iRmy6t, on-hold weak-hit audit): the current Restart-when grep is permanently self-triggered — its only match on origin/main is line 243 of AppContent.missingRecheckKeying.test.tsx, inside the settle() DOCSTRING that documents this very card, so the condition was already nonzero the day it was written ("currently 0" was false at write time). Verified both directions: anchoring the match to non-comment lines excludes the docstring while still finding real await act(async code usage in 64 files repo-wide. The hold itself stands (dormant, cause not isolated, workaround documented in place); the LoadingScreen.tsx secondary trigger also stands.

    Restart-when: git grep -qE '^[^*/]*await act\(async' origin/main -- packages/app-shell/src/console/__tests__/ exits 0 (code-line usage only; the comment-prefixed settle() docstring is excluded by construction)


    Generated by Claude Code

  3. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Aug 23, 2026
  4. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    Contributor

    Triage: domain:devx (lane only — pm:on-hold untouched). Landing: the shared test harness — await act(async …) never settling. Test infrastructure, so the engineering stream.

    ⚠️ Same harness family as #4249. Worth pricing together.

    Routed via the maintainer direct-dispatch channel, this session, verbatim: 「然后 批 4–5」. PM session session_0124Qg8rLvpXnQDwCmpKUmaJ. objectui three-stream split (maintainer 2026-08-21); not a Routine triage fire — the triage seat may re-grade.


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    关闭 not_planned —— 维护者同意:测试环境问题,绕开的写法已经写在下一位作者一定会看到的地方

    分诊席(session_01Tw7jnJinGHvoGSi8aFkhPJ,objectstack 座位贴 #6015),2026-09-24T12:50Z。objectui 清理第八批(#8587 的挂起卡一次性清点):本席读完了卡面和评论(3 条,全部读到),并在本地 main(62597c5)上重新测量,把建议报给维护者。维护者答:「第⑧批 同意」。

    本卡说的是什么

    测试渲染 console 的 AppContent 时,await act(async …) 永远等不到结束,15 秒后超时。原因没有查清,LoadingScreen 的 1200ms 定时器只是嫌疑。绕开的写法(普通等待加 findBy / waitFor)已经写在 AppContent.missingRecheckKeying.test.tsx:240-258,并引用了本卡。和 #4249 属于同一类测试环境问题。

    本席实测(本地 main 62597c5)

    重启条件 5345566910 给出了一条可以直接运行的检查:

    git grep -qE '^[^*/]*await act\(async' -- packages/app-shell/src/console/__tests__/
    

    实测代码行命中 0 处(:243 只是注释里引用了 #5194)⇒ 没有触发。

    为什么关

    • 这是测试工具层面的问题,不影响用户。按北极星第 3 条,产品的 P0/P1 还没清完时,不排 p2/p3 的工具类工作。
    • 绕开的写法就写在那个测试文件里,下一个在这里写测试的人一定会看到。

    什么时候重开

    上面那条 git grep 命中(有人在 console 的测试里又写回了 await act(async …));或者有人愿意专门查清卡死的根因时。

    标签:摘 pm:on-hold;以 not_planned 关闭。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions