Repository navigation
Commit 1371b9d
test(plugin-list): fold thirteen ListView pins that mock nothing onto one shared harness (#30769)
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuDYk72QQ4SmR4iyCBay2TWJ)_
Part of #29469
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:列表组件 `ListView` 的 13 个单卡测试文件(都不 mock 任何模块)合进一个新的共享测试文件
`ListView.harness.test.tsx`,每个原文件变成一个顶层 `describe`,断言一字未改;每块第一行调用
`restoreAfterBlock()`,块结束后把注册表、`localStorage` 和 `<html>` 的 `lang`/`dir`
复原,下一块从新文件的状态起步。
- **为什么改**:#29469 推广到第二族 ListView 的第一片(席位修订 2:每族最多两个 PR,未合并的 pin
清单放在第二片)。本地同机实测,这 13 个文件的 CPU 从约 39 秒降到约 5 秒,整个 ListView 测试族快约 8%。
-
**风险与回滚**:只动测试文件,不改源码、门禁、CI,不发布任何东西;块之间串状态的风险已用探针(逐块记录注册表、`localStorage`、`<html
lang>`)和三种乱序运行排除。回滚 = revert 本 PR。
- **先看**:`ListView.harness.test.tsx` 开头 90 行(块之间怎么隔离,以及
`restoreAfterBlock()`),再看 `ListView.displayMenuClose-12621` 往后的 5 块(会往
`ComponentRegistry` 注册替身,所以排在最后)。
- **两分钟验证**:在 `ui/` 下跑 `pnpm exec vitest run
packages/plugin-list/src/__tests__/ListView.harness.test.tsx`,应见 1
个文件、41 个测试全过。
- **需要你做的**:无,席位落地。
## What changes
Slice 2a, the first of the two `ListView*` PRs of the shared-setup
rollout (ruling 6583487846 on #29469; seat amendment 2, 6672694520: at
most two PRs per family). Thirteen pins in
`ui/packages/plugin-list/src/__tests__/` render the real `ListView` with
no `vi.mock` at all, and become one new file,
`ListView.harness.test.tsx`:
- Each former file is a top-level `describe` named after its file stem,
with its docblock above it and everything else moved verbatim,
re-indented by two spaces. The 13 files are deleted.
- The harness's header says how the blocks stay apart: RTL `cleanup()`
after every test (the `dom` setup registers it), every fixture and hook
inside its own block, no module mocks.
- **One addition the former files did not need: `restoreAfterBlock()`,
the first line of every block.** A former file got a fresh window and a
fresh registry; a block shares both with the blocks after it. Before
adding it, a probe that logged the state at each block's first test
found:
- `displayMenuClose-12621` left stand-ins for `object-kanban`,
`object-gallery`, `object-calendar` and `object-timeline` registered
(its hooks put back only entries that existed);
- `recordDrawerCount-27545` left the real `object-grid` unregistered,
and `rowAdornment-13330` then left its own `object-grid` stand-in
registered;
- `mutationRefresh` left its `localStorage` stand-in installed for the
seven blocks after it;
- the view preference `displayMenuClose-12621` saved
(`listview-opportunity-view`) was still in storage for the four blocks
after it.
All 41 tests passed anyway, but each later block was running against
state its file never had. The helper snapshots the registry and the
`localStorage` property in a `beforeAll` declared before the block's own
hooks, and after the block's last test (its `afterAll` runs after the
block's own) unregisters what the block added, re-registers each entry
it replaced or removed from that entry's own config, puts `localStorage`
back, clears both storages, and removes the `lang` / `dir` that
`I18nProvider` sets on `<html>`. With it, the same probe finds every
block starting from the post-import registry (every key, renderer and
meta equal), the window's own empty `localStorage` and no `lang`, and
the file passes in three shuffled orders (`--sequence.shuffle.tests`,
seeds 11, 222, 3333), which put registering blocks before the others.
- The five blocks that register a stand-in renderer in
`ComponentRegistry` (`displayMenuClose-12621`, `inPageScroll-12463`,
`recordDrawerCount-27545`, `rowAdornment-13330`, `setupChrome-12606`)
still sit after the eight that register nothing.
- `I18nProvider` is imported once, from `@object-ui/i18n`. Some former
files took it from `@object-ui/react`, which re-exports that same
binding.
No other file names any of the 13: `git grep` of each stem over `ui`,
`scripts`, `packages`, `content` and `.github` gives no hit outside the
file itself, so no comment needed updating.
Test files only: no source file, no changeset (`skip-changeset`).
`@object-ui/plugin-list` builds from its entry with `vite build`, and no
entry imports a test file.
## Tests
### Former file → describe (no title or assertion dropped)
TypeScript AST walk of each former file at `e00bcc6a` (the `main` this
was cut from, where they still exist) against the `describe` of the same
name in the harness at this PR's head.
| former file | `it` before | `it` after | `expect(` before | `expect(`
after | titles identical |
|---|--:|--:|--:|--:|:-:|
| `ListView.concurrencyTokenProjection-12292.test.tsx` | 2 | 2 | 7 | 7 |
yes |
| `ListView.displayMenuClose-12621.test.tsx` | 4 | 4 | 8 | 8 | yes |
| `ListView.emptyVsErrorRole-7132.test.tsx` | 3 | 3 | 7 | 7 | yes |
| `ListView.errorStateComponent-7143.test.tsx` | 6 | 6 | 15 | 15 | yes |
| `ListView.forbiddenNoRetry.test.tsx` | 3 | 3 | 7 | 7 | yes |
| `ListView.galleryBlockCanonical-6152.test.tsx` | 7 | 7 | 7 | 7 | yes |
| `ListView.inPageScroll-12463.test.tsx` | 2 | 2 | 14 | 14 | yes |
| `ListView.mutationRefresh.test.tsx` | 2 | 2 | 4 | 4 | yes |
| `ListView.onRowsChange-26679.test.tsx` | 2 | 2 | 5 | 5 | yes |
| `ListView.recordDrawerCount-27545.test.tsx` | 3 | 3 | 4 | 4 | yes |
| `ListView.refreshIndicatorLocale-10580.test.tsx` | 2 | 2 | 7 | 7 | yes
|
| `ListView.rowAdornment-13330.test.tsx` | 3 | 3 | 5 | 5 | yes |
| `ListView.setupChrome-12606.test.tsx` | 2 | 2 | 5 | 5 | yes |
| **total** | 41 | 41 | 95 | 95 | yes |
A line-multiset comparison (every non-blank line of the 13 files,
trimmed, against the harness) finds no code or comment line dropped:
what the files had and the harness lacks is the 13 license headers and
the duplicate import lines.
### CPU before and after (local A/B, one machine)
**CI after: NOT MEASURED**: the per-file timings reporter (#29517)
uploads on `main`'s full-battery runs only. Its first after-reading is
the first full-battery `main` run after this merges.
Local A/B on one 4-vCPU container, under `os-verify-lock` (shared-box
seconds), same command both legs, from `ui/`: `pnpm exec vitest run
<every ListView.*.test.ts(x) in plugin-list/src/__tests__>
--maxWorkers=2`, with a throwaway reporter reading each module's
`diagnostic()`. Fixed = environment + prepare + setup + import. The
"before" leg restores the 13 files from the base into the branch's tree
and removes the harness, so both legs run the same 116 other files. Runs
1 and 2 are on `937fec9d`, this fold before `restoreAfterBlock()` was
added, over `05a30e6e`; run 3 is on this head, over `e00bcc6a`.
| | files | tests passed | fixed CPU | test CPU | total CPU | wall |
|---|--:|--:|--:|--:|--:|--:|
| ListView family, before (run 1 / 2 / 3) | 129 | 985 | 377.4 / 375.7 /
375.2 s | 63.9 / 62.9 / 64.0 s | 441.3 / 438.6 / 439.1 s | 231.4 / 230.1
/ 230.4 s |
| ListView family, after (run 1 / 2 / 3) | 117 | 985 | 342.6 / 343.0 /
340.8 s | 62.8 / 62.8 / 62.1 s | 405.4 / 405.8 / 402.9 s | 212.6 / 212.7
/ 211.2 s |
| the 13, before → harness, after (run 1) | 13 → 1 | 41 → 41 | 37.1 →
3.0 s | 2.7 → 1.8 s | 39.7 → 4.8 s | |
| the 13, before → harness, after (run 2) | 13 → 1 | 41 → 41 | 36.0 →
2.8 s | 2.7 → 1.8 s | 38.7 → 4.6 s | |
| the 13, before → harness, after (run 3, this head) | 13 → 1 | 41 → 41
| 35.9 → 2.7 s | 2.6 → 1.8 s | 38.6 → 4.5 s | |
The family went from 439.7 s to 404.7 s of CPU on average (−8.0 %), wall
from 230.6 s to 212.2 s (−8.0 %). Run 3 puts `restoreAfterBlock()`'s
cost inside the run-to-run spread: the harness read 4.5 s against 4.8 /
4.6 s without it.
## Acceptance notes
- **Why these 13.** ListView's largest same-setup cluster is the pins
that mock nothing: 113 of the 129 per-issue `ListView.*` pins in
`plugin-list/src/__tests__/`, 23,229 lines, far past one PR under the
3,000-line bar (a fold changes about twice the lines it moves).
`ListView.test.tsx` is the component's main suite, not a per-issue pin,
and is not counted. Of the 113, this PR takes only files with none of
the following, which `restoreAfterBlock()` does not undo: no statement
at module scope or collection time other than imports, declarations and
test calls (39 files have one), no `vi.spyOn`, `vi.stubGlobal` or
`vi.useFakeTimers` (15), not on the `dom-heavy` list in
`ui/vitest.config.mts` (2), no side-effect import other than
`@testing-library/jest-dom` (4), and no other file naming it (49; some
files fail more than one test). That leaves 31, of which two are
`.test.ts` files in the `unit` project, which is not isolated per file
and so has nothing to save. This PR folds the 13 smallest of the
remaining 29, the most files per changed line: 1,375 lines moved make
2,729 changed lines, and the next smallest (140 lines) would take it
past 3,000. Slice 2b folds the next ones that fit.
- **ListView's unfolded pins** are listed (file, setup, lines) in slice
2b, the family's last PR, as amendment 2 asks.
- The one `ListView*` pin in `ui/packages/app-shell/src/__tests__/` is
in another package, outside this harness.
## Verification
At head `f2ea524d`, after `pnpm install --frozen-lockfile` and building
the closure (`turbo run build` for `@objectstack/spec`, `formula`,
`lint`, `client`, `metadata-protocol`, then
`--filter='@object-ui/plugin-list^...'` from `ui/`), under
`os-verify-lock`, VERDICT command-exit 0:
- `pnpm exec vitest run packages/plugin-list/ --maxWorkers=2` from
`ui/`: 179 files, 1,826 tests, all passed. That is 12 files fewer than
`main` (13 removed, 1 added) and the same tests: the A/B's family legs
ran 985 tests on both sides.
- `pnpm run type-check` in `ui/packages/plugin-list` (`tsc --noEmit &&
tsc -p tsconfig.test.json`): exit 0, 0 `error TS`. The harness is in the
`tsconfig.test.json` program (`--listFilesOnly`).
- `pnpm run lint` in `ui/packages/plugin-list`: exit 0, 0 errors (780
warnings). `eslint` on the harness from `ui/`: exit 0, 0 errors, 26
warnings, the same 26 the 13 former files give at `main`;
`restoreAfterBlock()` adds none.
- Order: the harness passes 41 / 41 in declaration order and under
`--sequence.shuffle.tests` with seeds 11, 222 and 3333, whose orders put
registering blocks first. A probe that logged the registry (every key's
renderer and meta), the `localStorage` object and its length, and `<html
lang>` at each block's first test found every block starting from the
post-import state.
- Derived gates (`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`, change set 14 paths against merge base
`e00bcc6a`): 26 derived, 26 run under `os-verify-lock` (VERDICT
command-exit 0), each exit 0 captured before any pipe; `pnpm --dir ui
exec vitest run scripts/` gave 170 files passed and 2 skipped, 4,970
tests passed and 2 skipped. `--ran` reconciliation: 26 derived, 26 run,
0 NOT-MEASURED, 0 UNRUN.
- Size: `git diff --shortstat origin/main HEAD` is 14 files, +1,354 /
-1,375, 2,729 changed lines. No open pull request touches any of the 14
paths: the head trees of all 58 open PRs, compared with `main` at those
paths after #30745 merged, differ only for #28646, a mailbox PR whose
file list is empty.
- No ablation of a product behaviour: none changed, and the assertions
moved verbatim. The helper's own effect is the probe reading above
against the same probe without it (What changes).
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(`packages/spec` not touched.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes. (`ui/` is linted by its
own config; the package lint is above.)
- [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. (Nothing
publishes: `skip-changeset`.)
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (Not touched.)
- [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 (the maintainer
quick-read is the allowed Chinese section).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L6bhgXNVxCQk1CCy55gFjQ)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L6bhgXNVxCQk1CCy55gFjQ
Co-authored-by: Claude <noreply@anthropic.com>1 parent 5268f50 commit 1371b9d
14 files changed
Lines changed: 1354 additions & 1375 deletions
File tree
- ui/packages/plugin-list/src/__tests__
This file was deleted.
This file was deleted.
This file was deleted.
0 commit comments