【CI flaky】CreatorPages「回关/取消关注」用例在并发负载下点击被吞,导致 Frontend tests 偶发失败 #644

Closed
opened 2026-10-06 14:03:09 +08:00 by suzmii · 0 comments
Member

1. 现象

CI run 3548(head f0cad9ddd,PR #635)的 Frontend tests attempt 1 失败:

FAIL src/components/creator/CreatorPages.test.tsx > 自己的粉丝支持回关和取消回关,移除须确认且只移除该行
TestingLibraryElementError: Unable to find role=button and name 取消关注(CreatorPages.test.tsx:145)
1 failed | 2924 passed;Test Files 1 failed | 253 passed

重跑同一 job(attempt 2)全绿(2925 passed / 254 files),坐实 flaky。

2. 根因(已取证,含机理与文件行号)

  1. 行渲染与 relationship store seed 不在同一帧 —— seed 由 src/components/creator/hooks/useCreatorRelationship.ts 的 effect 完成。
  2. 「行已在 DOM、value 仍为 null」的那一帧,按钮为 disabled=true(src/components/creator/controls/CreatorRelationshipControl.tsx:34-37),但按钮文本已经是「回关」(followed 默认 false)→ findByRole('回关') 会在这一帧就命中。
  3. React 对 disabled 交互元素不派发 onClick → fireEvent.click 被吞(实测 onClick 命中 0 次),写入从未发生;随后 store 才 seed,按钮保持「回关」,CreatorPages.test.tsx:145 永远等不到「取消关注」。
  4. 加大 findBy 超时无效:该帧之后 DOM 不会再变化。
  5. 为何偶发:正常时序下 RTL 会先 drain 一个 setTimeout(0)、React scheduler 先 flush;只有 CPU 严重超卖(CI runner 上 5 个 job 并发)时才会带着首帧 DOM 继续执行。

3. 复现数据

  • 单跑 10 次 → 0 失败
  • 负载下 138 次 → 2 失败
  • CI-like 调用 3 次 → 0 失败
  • 失败时该文件耗时 1509 / 1516 ms(正常 460–540 ms)

4. 归属

src/components/creator/** 与 packages/shared/src/components/CreatorControls 在 master tip 52c83cc2a 与失败 head f0cad9ddd 之间 diff 为空,来自已并入 master 的 PR #638;与本 PR #635 无关。

5. 最小修法

const back = (await screen.findByRole('button', { name: '回关' })) as HTMLButtonElement;
await waitFor(() => expect(back.disabled).toBe(false));
fireEvent.click(back);

仓库内已有同款先例:src/components/game-distribution/GameDistributionPages.test.tsx:262-271(先等 disabled 变 false 再点)。

6. 无效替代方案

  • 把 findByRole 默认 1s 超时提到 5s(DOM 不再变化,等多久都无用);
  • 改为等待 api.write 被调用(写入根本不会发生);
  • 改组件实现(真实浏览器同样不会向 disabled 按钮派发 click,用户路径本身无问题)。

7. 影响面

会让任何 PR 的 Frontend tests 随机变红,需要人工重跑,浪费 CI 时长与排查成本。

8. 验证计划

  • 修复前后同命令、同迭代数的负载对照(期望 before failures ≥ 1 → after 0);
  • 全量 test:ci:frontend 绿。
## 1. 现象 CI run **3548**(head `f0cad9ddd`,PR #635)的 `Frontend tests` **attempt 1** 失败: ``` FAIL src/components/creator/CreatorPages.test.tsx > 自己的粉丝支持回关和取消回关,移除须确认且只移除该行 TestingLibraryElementError: Unable to find role=button and name 取消关注(CreatorPages.test.tsx:145) 1 failed | 2924 passed;Test Files 1 failed | 253 passed ``` 重跑同一 job(**attempt 2**)全绿(2925 passed / 254 files),坐实 **flaky**。 ## 2. 根因(已取证,含机理与文件行号) 1. 行渲染与 relationship store seed **不在同一帧** —— seed 由 `src/components/creator/hooks/useCreatorRelationship.ts` 的 effect 完成。 2. 「行已在 DOM、`value` 仍为 `null`」的那一帧,按钮为 `disabled=true`(`src/components/creator/controls/CreatorRelationshipControl.tsx:34-37`),但按钮文本已经是「回关」(`followed` 默认 false)→ `findByRole('回关')` 会在这一帧就命中。 3. React 对 **disabled** 交互元素不派发 `onClick` → `fireEvent.click` 被吞(实测 onClick 命中 **0 次**),写入从未发生;随后 store 才 seed,按钮保持「回关」,`CreatorPages.test.tsx:145` 永远等不到「取消关注」。 4. **加大 `findBy` 超时无效**:该帧之后 DOM 不会再变化。 5. 为何偶发:正常时序下 RTL 会先 drain 一个 `setTimeout(0)`、React scheduler 先 flush;只有 CPU 严重超卖(CI runner 上 5 个 job 并发)时才会带着首帧 DOM 继续执行。 ## 3. 复现数据 - 单跑 10 次 → **0 失败** - 负载下 138 次 → **2 失败** - CI-like 调用 3 次 → 0 失败 - 失败时该文件耗时 **1509 / 1516 ms**(正常 460–540 ms) ## 4. 归属 `src/components/creator/**` 与 `packages/shared/src/components/CreatorControls` 在 master tip `52c83cc2a` 与失败 head `f0cad9ddd` 之间 **diff 为空**,来自已并入 master 的 PR #638;与本 PR #635 无关。 ## 5. 最小修法 ```ts const back = (await screen.findByRole('button', { name: '回关' })) as HTMLButtonElement; await waitFor(() => expect(back.disabled).toBe(false)); fireEvent.click(back); ``` 仓库内已有同款先例:`src/components/game-distribution/GameDistributionPages.test.tsx:262-271`(先等 disabled 变 false 再点)。 ## 6. 无效替代方案 - 把 `findByRole` 默认 1s 超时提到 5s(DOM 不再变化,等多久都无用); - 改为等待 `api.write` 被调用(写入根本不会发生); - 改组件实现(真实浏览器同样不会向 disabled 按钮派发 click,用户路径本身无问题)。 ## 7. 影响面 会让**任何** PR 的 `Frontend tests` 随机变红,需要人工重跑,浪费 CI 时长与排查成本。 ## 8. 验证计划 - 修复前后同命令、同迭代数的负载对照(期望 before failures ≥ 1 → after 0); - 全量 `test:ci:frontend` 绿。
suzmii added the Kind/Bug
Priority
Medium
3
labels 2026-10-06 14:03:09 +08:00
suzmii self-assigned this 2026-10-06 14:03:09 +08:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: GenarrativeAI/Genarrative#644