Skip to content

Commit 8b9cc71

Browse files
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

‎ui/packages/plugin-view/src/__tests__/ObjectView.calendarInitialDate-28102.test.tsx‎

Lines changed: 0 additions & 79 deletions
This file was deleted.

‎ui/packages/plugin-view/src/__tests__/ObjectView.filterSources.test.tsx‎

Lines changed: 0 additions & 254 deletions
This file was deleted.

0 commit comments

Comments
 (0)