Repository navigation
Commit 9dec054
test(app-shell): the launcher narrowing test waits on the effect-recorded entries it asserts (#27234)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ85Ns5E5tXtLApKFvjm2mLQP)_
Fixes #27216
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:`useLauncherApps-26838.test.tsx` 第一个用例改为等待 effect 记录的 `seen`
本身,而不是只等 DOM 文本;断言不变(仍是 `toEqual([Setup, CRM])`、身份 `toBe`、只读一次)。
- 为什么改:`seen` 由 passive effect 写入,它可能晚于 DOM 提交才刷新;`waitFor` 看到 DOM
就返回,读到的还是待定渲染的三个应用(含 Showcase),于是 main 随机变红。
- 风险与回滚:只动一个测试文件,不改产品代码,规则照旧严格;回滚即 revert 本 PR。
-
先看:`ui/packages/app-shell/src/hooks/__tests__/useLauncherApps-26838.test.tsx`
第 98–103 行。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/hooks/__tests__/useLauncherApps-26838.test.tsx`,预期
8/8 通过。
- 需要你做的:无,席位落地。
## What changes
The first case of `useLauncherApps-26838.test.tsx` now waits on the
effect-recorded entries it asserts:
```tsx
await waitFor(() => expect(seen.mock.lastCall?.[0]).toEqual([SETUP, CRM]));
const entries = seen.mock.lastCall?.[0];
expect(entries?.[0]).toBe(SETUP);
```
The DOM wait, the `data-pending` check, the identity check and the
single-fetch check stay as they were. The assertion is not loosened
(still `toEqual`, never `toContain`), nothing is skipped or retried, and
no product source changes.
## Why
`seen` is written by a `useEffect` in `Entries`; the old code waited
only on `Probe`'s DOM text. `Probe` and `Entries` commit together, so
when the DOM shows `setup,crm` the matching array exists, but its
passive effect may not have flushed yet. When the render runs past the
scheduler's frame, React yields after the commit and flushes passive
effects in a later task. The DOM wait's MutationObserver can resolve in
that gap, so `lastCall` was still the pending render's `[Setup, CRM,
Showcase]`. That is exactly the main red: `expected [ …(3) ] to deeply
equal [ …(2) ]` with Showcase third.
The triage's alternative, state shared across tests, does not fit the
evidence. The hook has no module-level store, `state` is reset in
`beforeEach`, and a leak would also show in `Probe`'s DOM text, which
the passing `waitFor` had just read as `setup,crm`.
The sibling `CommandPalette.launcherHiddenApps-26838.test.tsx` and the
other three `-26838` files assert DOM text inside `waitFor` and read no
effect-recorded value, so they are untouched.
## Tests
Repeat loop: a scratch copy of the file with case 1 expanded through
`it.each` into N runs (scratch copy deleted after; nothing permanent
added). "Loaded" means three busy-loop CPU burners on the 4-core box
while the run held the verify lock.
| Tree | Load | Case-1 runs | Red |
|:--|:--|--:|--:|
| main `9dacccf9` (before) | idle | 300 | 2 |
| main `9dacccf9` (before) | idle | 1000 | 3 |
| main `9dacccf9` (before) | loaded | 1000 | 5 |
| this PR `feca0a0c` | idle | 1000 | 0 |
| this PR `feca0a0c` | loaded | 1000 | 0 |
| ablation of `feca0a0c` (old two lines restored) | loaded | 2000 | 7 |
Every red was the main-red shape, with Showcase as the third entry. The
ablation went through `scripts/ablation-replace.mjs`: anchor 1 to 0,
blob `598c8e36` to `6772eb84` and back. Afterwards `git hash-object`
equals the HEAD blob `598c8e36` and `git status` is clean.
The five `-26838` launcher files at `feca0a0c`: 5 files, 20 tests
passed. `eslint` on the changed file: exit 0.
## Acceptance notes
- Changeset: none. The diff is a test in `@object-ui/app-shell`, which
is `private: true`, so it carries `skip-changeset`.
## Verification
Derived gates at `feca0a0c` (`node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack`, 24 families), every one
run locally, exit codes captured before any pipe: all 24 exit 0,
including `node scripts/check-sdui-manifest.mjs` and its `--self-test`,
and `pnpm --dir ui exec vitest run scripts/`. `dispatch-gates --ran`: 24
derived, 24 run, 0 NOT-MEASURED. Every family CI runs is read from this
PR's check-runs.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbY2R68kfSCyjfFu4gU)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 14e9685 commit 9dec054
1 file changed
Lines changed: 4 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | | - | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
99 | 102 | | |
100 | | - | |
101 | 103 | | |
102 | 104 | | |
103 | 105 | | |
| |||
0 commit comments