Repository navigation
Commit 8b9cc71
test(plugin-view): fold nine ObjectView pins over stand-in child renderers onto one shared harness (#30872)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoPC4ctRn1a3NwUssAZhqxBe)_
Part of #29469
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:视图组件 `ObjectView`(`plugin-view` 包)的 9 个单卡测试文件合进一个新的共享测试文件
`ObjectView.harness.test.tsx`。这 9 个文件原本各自 mock
同样三处(`SchemaRenderer`、`ObjectGrid`、`ObjectForm` 换成替身),只是替身记录的东西不同;现在
mock 只写一次,每块在自己的 `beforeEach` 里装上自己的替身,断言一字未改。
- **为什么改**:#29469 推广到第三族 ObjectView 的第一片(席位修订 2:每族最多两个 PR)。本地同机实测,这 9
个文件的 CPU 从约 39 秒降到约 6 秒,`plugin-view` 里整个 ObjectView 测试族快约 13%。
- **风险与回滚**:只动测试文件,不改源码、门禁、CI,不发布任何东西;块之间串状态的风险已用探针(逐块记录注册表、存储、`html`
元素的 `lang`、替身槽位)和三种乱序运行排除。回滚 = revert 本 PR。
- **先看**:`ObjectView.harness.test.tsx` 开头 145 行(三个 mock 怎么读替身槽位,以及
`restoreAfterBlock()`),再看 `ObjectView.formLabelsI18nLabel-10993` 一块(唯一挂
`I18nProvider` 的块,会在 `html` 元素上留下 `lang`)。
- **两分钟验证**:在 `ui/` 下跑 `pnpm exec vitest run
packages/plugin-view/src/__tests__/ObjectView.harness.test.tsx`,应见 1
个文件、55 个测试全过。
- **需要你做的**:无,席位落地。
- **席位意见**:ui 席 2 对照 head `0ec2ac43` 的 diff 复核:10 个文件全在
`plugin-view/src/__tests__/`,9 个共用三件 mock 的 ObjectView 单卡文件并入新的
`ObjectView.harness.test.tsx`,mock 只声明一次、经 `vi.hoisted` 槽位取各块的替身,每块以
`restoreAfterBlock()` 开头复原注册表、存储、`<html lang/dir>` 与槽位;断言与标题一字未改(`it(`
47=47、`expect(` 97=97),乱序三种种子全过;CI 全绿,纯测试改动无发布面,不欠契约复审。同意落地。
## What changes
Slice 3a, the first of the two `ObjectView*` PRs of the shared-setup
rollout (ruling 6583487846 on #29469; seat amendment 2, 6672694520: at
most two PRs per family). Nine pins in
`ui/packages/plugin-view/src/__tests__/` render the real `ObjectView`
over the same three mocks, and become one new file,
`ObjectView.harness.test.tsx`:
- The three mocks every one of them declared: `@object-ui/react` with
`SchemaRenderer` replaced, a fresh `SchemaRendererContext` and the
data-change bus (`subscribeDataChanges`, `notifyDataChanged`) made
no-ops; `@object-ui/plugin-grid` with `ObjectGrid` replaced;
`@object-ui/plugin-form` with `ObjectForm` replaced. The nine differed
only in the three stand-ins (what they record, what they draw). The
harness declares each mock once, reading its stand-in from a
`vi.hoisted` slot (`standIns.schemaRenderer`, `.objectGrid`,
`.objectForm`), the way `ObjectGantt.harness.test.tsx` reads its
`GanttView` stand-in. A mock whose slot is empty throws, naming the
slot, so a block can never draw an earlier block's stand-in.
- Each former file is a top-level `describe` named after its file stem,
with its docblock above it. Its first lines are `restoreAfterBlock()`
and a `beforeEach` that installs its three stand-ins, each copied
verbatim from that file's mock factory; everything else is moved
verbatim, re-indented by two spaces. The comment that sat above
`ObjectView.tableSortKey-10664`'s mocks sits above its `beforeEach`. The
9 files are deleted.
- **`restoreAfterBlock()`**, the first line of every block, is
`ListView.harness.test.tsx`'s helper plus the three slots. A probe that
logged the state at each block's first test, with the helper's body
removed, found: `ObjectView.formLabelsI18nLabel-10993` (the one block
that mounts an `I18nProvider`) left `lang="zh"` and `dir="ltr"` on the
`html` element for every block after it, and every block after the first
started with the previous block's three stand-ins installed. No block
here registers a renderer or swaps `localStorage`; the registry and
storage halves stay for the next block that does. With the helper, the
same probe finds every block starting from the same state (registry:
every key, renderer name and meta equal; `localStorage` the window's own
getter, both storages empty; no `lang` / `dir`; react-i18next's global
`undefined`; all three slots empty; an empty `body`; no new global), in
declaration order and under three shuffled orders.
- Imports are merged into one list. `I18nProvider` comes from
`@object-ui/i18n`, as in the former file.
No other file names any of the 9: `git grep -F` of each stem over `ui`,
`scripts`, `packages`, `content` and `.github` gives no hit outside the
file itself, so no comment needed updating. Two blocks' prose names
files that stay (`ObjectView.sortSink.test.tsx`,
`ObjectView.formTitleI18n.test.tsx`); those references still hold.
**One harness file, in one package.** The `ObjectView` family spans two
packages: these pins test `@object-ui/plugin-view`'s `ObjectView`, and
the `ObjectView.*` pins in `ui/packages/app-shell/src/views/` test the
Console's `ObjectView` in `@object-ui/app-shell`, a different component
over a different mock set. This PR adds only the plugin-view harness;
see Acceptance notes for why this cluster comes first.
Test files only: no source file, no changeset (`skip-changeset`).
`@object-ui/plugin-view` 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 `e603385f` (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. An `it` is a call to `it` /
`test` (an `it.each(...)(...)` counts once); titles are the `describe >
it` paths under the block, compared as sorted lists.
| former file | it before | it after | expect( before | expect( after |
titles identical |
|---|--:|--:|--:|--:|:-:|
| `ObjectView.calendarInitialDate-28102.test.tsx` | 2 | 2 | 5 | 5 | yes
|
| `ObjectView.tableSortKey-10664.test.tsx` | 2 | 2 | 5 | 5 | yes |
| `ObjectView.kanbanDeclaredLanes-28211.test.tsx` | 2 | 2 | 5 | 5 | yes
|
| `ObjectView.namedViewEditInlineFold-5144.test.tsx` | 4 | 4 | 6 | 6 |
yes |
| `ObjectView.formLabelsI18nLabel-10993.test.tsx` | 3 | 3 | 7 | 7 | yes
|
| `ObjectView.refreshSignal.test.tsx` | 3 | 3 | 6 | 6 | yes |
| `ObjectView.timelineBinding-7070.test.tsx` | 8 | 8 | 15 | 15 | yes |
| `ObjectView.readersRetired-6152.test.tsx` | 8 | 8 | 26 | 26 | yes |
| `ObjectView.filterSources.test.tsx` | 15 | 15 | 22 | 22 | yes |
| **total** | 47 | 47 | 97 | 97 | yes |
The 47 `it` calls run as 55 tests (`it.each` rows), in the harness as in
the nine files: the A/B legs below ran 507 tests on both sides.
A line-multiset comparison (every non-blank line of the 9 files,
trimmed, against the harness) finds no test, fixture or comment line
dropped: what the files had and the harness lacks is the 9 license
headers, the import lines, the `vi.mock(` wrappers and their shared
keys, and the stand-ins' first lines, which now open with `standIns.SLOT
=`.
### 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
packages/plugin-view/src/__tests__/ObjectView. --maxWorkers=2`, with a
throwaway reporter reading each module's `diagnostic()`. Fixed =
environment + prepare + setup + collect. The "before" leg restores the 9
files from the base into the branch's tree and removes the harness, so
both legs run the same 46 other files. Run order: before 1, before 2,
after 1, after 2, before 3.
| | files | tests passed | fixed CPU | test CPU | total CPU | wall |
|---|--:|--:|--:|--:|--:|--:|
| ObjectView family in plugin-view, before (run 1 / 2 / 3) | 55 | 507 |
243.2 / 232.8 / 229.9 s | 26.6 / 25.9 / 26.2 s | 269.8 / 258.7 / 256.2 s
| 141.2 / 135.5 / 134.6 s |
| ObjectView family in plugin-view, after (run 1 / 2) | 47 | 507 | 200.7
/ 203.1 s | 25.8 / 25.5 s | 226.5 / 228.5 s | 118.6 / 119.5 s |
| the 9, before (run 1 / 2 / 3) → harness, after (run 1 / 2) | 9 → 1 |
55 → 55 | 38.4 / 36.6 / 35.9 → 4.4 / 4.6 s | 2.0 / 2.0 / 2.0 → 1.5 / 1.5
s | 40.4 / 38.6 / 37.9 → 5.9 / 6.1 s | |
The family went from 261.6 s to 227.5 s of CPU on average (−13.0 %),
wall from 137.1 s to 119.0 s (−13.2 %); the nine files' 39.0 s became
6.0 s in the harness (−85 %, about 33 s saved). The three before runs
spread 13.6 s, so the change is well outside run-to-run noise.
## Acceptance notes
- **Why this cluster, and why these 9.** Neither package has a "mocks
nothing" cluster big enough to lead. In `plugin-view/src/__tests__/` the
largest same-setup cluster is the pins over the three-mock trio above:
31 of the 54 per-issue `ObjectView.*` pins declare exactly those three
mocks with the same overridden keys (`SchemaRenderer`,
`SchemaRendererContext`, `subscribeDataChanges`, `notifyDataChanged`;
`ObjectGrid`; `ObjectForm`), 7,367 lines. `ObjectView.test.tsx` is the
component's main suite, not a per-issue pin, and is not counted. The
app-shell side (`ui/packages/app-shell/src/views/`, 87 files) has 24
files that mock nothing, half of them `.test.ts` in the `unit` project
(not isolated per file, so a fold saves nothing), and its 63 mocked
files spread over more than 40 different mock sets. Of the 31, this PR
takes only files with none of the following: `vi.spyOn`,
`vi.stubGlobal`, `vi.useFakeTimers` or `vi.stubEnv` (4), a `vi.hoisted`
or `globalThis` slot of their own (3), a side-effect import other than
`@testing-library/jest-dom` (0), an entry on the `dom-heavy` list in
`ui/vitest.config.mts` (0), and any other file naming it (18; some files
fail more than one check). One of the named,
`ObjectView.contractEnvelope-6726`, is named by path in
`scripts/check-vi-mock-inherit.mjs`, a ledger outside `__tests__`, so it
stays a file of its own. That leaves 12; this PR folds the 9 smallest,
the most files per changed line: 1,317 lines moved make 2,589 changed
lines, and the next smallest (`ObjectView.tableColumnsForwarding`, 298
lines) would take it past 3,000.
- **Left for slice 3b** (the family's last PR, which carries the
unfolded list): the other 3 clean trio pins (`tableColumnsForwarding`,
`strayGroupByLane-9242`, `canonicalTableKeys`), the trio pins another
test file names (their comments get updated in the same PR, as amendment
2 asks), and the app-shell pins, which would need a second harness file
in `@object-ui/app-shell`.
- The harness header and the docblocks of the moved blocks name the
former files' issue numbers in prose, as the ListView and ObjectGantt
harnesses do; `check:new-line-citations` reads 0 new citations.
- The session's harness asks for the PR's assignee and reviewer to be
set after creation; this dispatch's frame (and
`.claude/agents/os-dev.md` rule 2) reserves both for the seat at
hand-over, so the dev set neither.
## Verification
At head `0ec2ac43`, 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-view^...'` from `ui/`, under
`os-verify-lock`, VERDICT command-exit 0):
- `pnpm exec vitest run packages/plugin-view/ --maxWorkers=2` from `ui/`
under `os-verify-lock` (VERDICT command-exit 0): 71 files, 720 tests,
all passed (this branch removes 9 files and adds 1).
- `pnpm run type-check` in `ui/packages/plugin-view` (`tsc --noEmit &&
tsc -p tsconfig.test.json`): exit 0, 0 `error TS`. The harness is in the
`tsconfig.test.json` program (`--listFilesOnly`).
- `eslint` on the harness from `ui/`: exit 0, 0 errors, 47 warnings (all
`no-explicit-any`), the same 47 the 9 former files give at `e603385f`.
- Order: the harness passes 55 / 55 in declaration order and under
`--sequence.shuffle.tests` with seeds 11, 222 and 3333 (each reorders
the nine blocks). The block-boundary probe above (registry renderer +
meta per key, `localStorage` descriptor and both storage lengths, the
`html` element's `lang` / `dir`, react-i18next `getI18n()`, the three
slots, `body`, globals added since import) reads every block start
identical in all four orders, and with the helper's body removed reads
the `lang` / `dir` and slot leaks named in What changes. The probe was a
throwaway copy of the harness, deleted.
- Derived gates (`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`, change set 10 paths against merge base
`e603385f`, 2,589 changed lines, under 3,000): 26 derived, 26 run with
stdin from `/dev/null`, each exit 0 captured before any pipe; `pnpm
--dir ui exec vitest run scripts/` under `os-verify-lock` (VERDICT
command-exit 0) gave 170 files passed and 2 skipped, 4,979 tests passed
and 2 skipped; `node scripts/check-sdui-manifest.mjs` and its
`--self-test` exit 0. `--ran` reconciliation: 26 derived, 26 run, 0
NOT-MEASURED, 0 UNRUN. `pnpm check:new-line-citations` from `ui/`: exit
0, 0 new citations.
- Not re-merged with `main` before opening: `main` moved past `e603385f`
(to `cf478ea3`), and none of those commits touches
`ui/packages/plugin-view`, `ui/vitest.config.mts` or the
`ui/vitest.setup.*` files.
- No open pull request touches any of the 10 paths: the head trees of
all 43 open PRs, compared with `main` at those paths, differ for none.
- No ablation of a product behaviour: none changed, and the assertions
moved verbatim. The helper's own effect is the probe reading with and
without its body.
## 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 harness 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).
<!-- signed: domain:ui#2 · os-tesla · session_01L9qibw7DekEe3wx5f3zphf
-->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent c50e0df commit 8b9cc71
10 files changed
Lines changed: 1272 additions & 1317 deletions
File tree
- ui/packages/plugin-view/src/__tests__
This file was deleted.
This file was deleted.
0 commit comments