Skip to content

Commit 2436b2f

Browse files
test(plugin-gantt): fold four more ObjectGantt pins onto the shared harness (#30745)
<!-- 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 ## 维护者速读 - **改了什么**:甘特图组件再把 4 个用同一套 mock 的单卡测试文件合进 #30725 建的共享测试文件 `ObjectGantt.harness.test.tsx`,每个原文件变成一个顶层 `describe`,断言一字未改;另把 12 个测试文件注释里还写着已合并文件名的地方,改成指向共享文件里对应的块。 - **为什么改**:#29469 的推广,甘特图族的第二片也是最后一片(席位修订 2:每族最多两个 PR)。本地同机实测,这 4 个文件并入后,共享文件加这 4 个的 CPU 从约 20.3 秒降到约 8.9 秒,整个甘特图测试族再快约 8%。 - **风险与回滚**:只动测试文件(含注释),不改源码、门禁、CI,不发布任何东西;块之间串状态的风险已用全包测试排除。回滚 = revert 本 PR。 - **先看**:`ObjectGantt.harness.test.tsx` 开头 75 行(`sonner` mock 改成并集、槽位类型改成 `(props: never)`);再看 `ObjectGantt.invalidationRefetch-10035` 块的 `beforeEach`(带 hook 的替身改成具名组件)。 - **两分钟验证**:在 `ui/` 下跑 `pnpm exec vitest run packages/plugin-gantt/src/ObjectGantt.harness.test.tsx`,应见 1 个文件、52 个测试全过。 - **需要你做的**:无,席位落地。 ## What changes Slice 1b, the second and last `ObjectGantt*` PR of the shared-setup rollout (ruling 6583487846 on #29469; seat amendment 2, 6672694520: at most two PRs per family). Four more pins render the real `ObjectGantt` over the same three mocks as the seven blocks #30725 folded (`sonner`'s toast, a `./GanttView` stand-in, `@object-ui/plugin-detail` with `RecordDetailDrawer` and `deriveRecordPageHref` stubbed out), so they join `ui/packages/plugin-gantt/src/ObjectGantt.harness.test.tsx` the same way: - `ObjectGantt.colorFieldLadder-7243`, `ObjectGantt.invalidationRefetch-10035`, `ObjectGantt.propertiesBag-10859` and `ObjectGantt.retiredApiProvider-12325` are each a top-level `describe` named after the former file, docblock above it, stand-in installed in a `beforeEach` through the `standIns` slot, everything else moved verbatim and re-indented by two spaces. - Three changes to the harness's shared part, each needed by one of the four: - The `sonner` mock is now the union of the blocks' mocks, `{ error, success }`: `invalidationRefetch-10035` mocked `success` too. No block asserts on a toast, and `ObjectGantt.tsx` calls only `toast.error`. - The slot is typed `(props: never) => unknown` and called with `props as never`, so a stand-in keeps its own props annotation (`propertiesBag-10859` types its `tasks`). - The `invalidationRefetch-10035` stand-in calls `React.useState`, so its `beforeEach` declares it as `const GanttView = …` and assigns that to the slot; an arrow assigned straight to `standIns.ganttView` fails `react-hooks/rules-of-hooks`. The body is unchanged, and it still runs inside the mocked `GanttView`. - The comment above `import './index'` is `propertiesBag-10859`'s, which said why the import is at module scope. - `ObjectGantt.test.tsx` names the `retiredApiProvider-12325` block instead of the deleted file. **Comments that named a folded file (amendment 2).** Twelve test files in eight workspace packages named one of the eleven files #30725 and this PR fold. Each now names the block and the harness file (comment text only): `plugin-gantt` `ObjectGantt.hostRowClick-27057`, `plugin-list` `ListView.ganttPagingChrome-7210`, `ListView.ganttToolbarFilter-10037`, `ListView.selfQueryingViewsToolbarState-10250`, `ListView.test.tsx`, `ObjectGallery.hostOwnsRows-7390`, `plugin-view` `ObjectView.fetchAllowList-10982`, `react` `useSettledSchema.equalPayload-10106`, `app-shell` `ObjectView.refreshInPlace-10035`, `core` `record-color`, `types` `object-gantt-properties-bag-10859-b6`, and one `pins` description string in `ui/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts` (the `object-gantt.gantt` row). That ledger checks a pin's `file` (exists, is collected, names the block and the key), never its `pins` text. The five source files that name a folded file (`ObjectGantt.tsx`, `index.tsx`, `ObjectTimeline.tsx`, `ListView.tsx`, `ObjectView.tsx`) are unchanged, as amendment 2 says; each stem still `git grep`s to its `describe`. Test files only: no source file, no changeset (`skip-changeset`). No package entry imports a test file: the plugins and the console bundle from their entries with `vite build`, the built `core`, `types` and `react` `dist` hold no module that imports `vitest`, and `app-shell` is private. ## Tests ### Former file → describe (no title or assertion dropped) TypeScript AST walk of each former file at `f18fd963` (the #30725 head, 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 | |---|--:|--:|--:|--:|:-:| | `ObjectGantt.colorFieldLadder-7243.test.tsx` | 6 | 6 | 9 | 9 | yes | | `ObjectGantt.invalidationRefetch-10035.test.tsx` | 3 | 3 | 10 | 10 | yes | | `ObjectGantt.propertiesBag-10859.test.tsx` | 6 | 6 | 21 | 21 | yes | | `ObjectGantt.retiredApiProvider-12325.test.tsx` | 2 | 2 | 7 | 7 | yes | | **total** | 17 | 17 | 47 | 47 | yes | The harness now holds 11 top-level `describe` blocks and 52 tests (35 from #30725, 17 from here), all passing. ### CPU before and after (local A/B, one machine) **CI after: NOT MEASURED**, for the reason #30725 gives: 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, two runs each, from `ui/`: `pnpm exec vitest run <every ObjectGantt*.test.ts(x) in plugin-gantt> --maxWorkers=2`, with a throwaway reporter reading each module's `diagnostic()`. Fixed = environment + prepare + setup + import. The "before" leg restores the four files and the #30725 harness from `f18fd963` into this branch's tree, so both legs run the same 45 other files. | | files | tests passed | fixed CPU | test CPU | total CPU | wall | |---|--:|--:|--:|--:|--:|--:| | ObjectGantt family, before (run 1 / run 2) | 50 | 327 | 143.4 / 144.6 s | 10.5 / 10.6 s | 153.9 / 155.2 s | 82.3 / 83.1 s | | ObjectGantt family, after (run 1 / run 2) | 46 | 327 | 131.8 / 131.6 s | 10.3 / 10.8 s | 142.1 / 142.4 s | 75.8 / 77.1 s | | harness + the four, before → harness, after (run 1) | 5 → 1 | 52 → 52 | 18.6 → 7.2 s | 1.8 → 1.6 s | 20.4 → 8.8 s | | | harness + the four, before → harness, after (run 2) | 5 → 1 | 52 → 52 | 18.5 → 7.4 s | 1.7 → 1.6 s | 20.2 → 8.9 s | | The four files cost 12.4 / 12.3 s on their own; folded, they add about 0.8 s to the harness. The family went from 154.6 s to 142.3 s of CPU (−8.0 %), wall −8 %. The A/B ran on `e673f7c6`, the pre-merge commit of this fold; its `plugin-gantt` tree is byte-identical to this branch's `7cbfc512`, and the head adds one comment-only commit (`6a0388d8`). ## Acceptance notes - **Why this cluster and not the biggest one left.** The largest cluster still unfolded is 18 pins that mock `./GanttView` only, 3,722 lines; moving them into a harness changes about twice that, over 7,000 lines, which no single PR under the 3,000-line bar can carry. Amendment 2 has a family's second PR fold the next cluster that fits, and these four are the pins left on the harness's own mock set. - **`ObjectGantt.elementDataSource` stays its own file.** It shares the harness's three mocks, but `ui/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts` names its path as the `object-gantt.dataSource` pin, and that ledger fails when the file is gone. The ledger is outside the claim's file surface. - **Unfolded `ObjectGantt*` pins (amendment 2: listed in the family's last PR).** 39 files in `ui/packages/plugin-gantt/src/`, by the setup they share; `ObjectGantt.test.tsx` is the component's main suite, not a per-issue pin, and is not counted. Lines at this PR's head. | setup the files share | files (lines) | lines | |---|---|--:| | `./GanttView` stand-in only (real `sonner`, real `@object-ui/plugin-detail`) | `contractEnvelope-6839` (328), `currencyDep` (296), `currencyMode-10422` (136), `dateLocale` (132), `discardedConfigMemo` (124), `invalidBlockRefusal-13459` (102), `numberLocale` (333), `numberResolvedWidth-11254` (195), `percentResolvedWidth-11254` (158), `persistfilters` (113), `quickfilter.i18n.fallback` (84), `quickfilter.i18n` (149), `quickfilter` (377), `readback` (152), `referenceArms-6837` (316), `staleReloadFinally-7231` (359), `unconfiguredRefusal-7070` (187), `writeGrant-12082` (181) | 3722 | | `./GanttView` stand-in and a `sonner` mock (`error` + `success`; `writeerror` mocks `error` only), real `@object-ui/plugin-detail` | `errorClears-10578` (339), `refreshInPlace-7237` (288), `silentOvertake-10633` (367), `writeerror` (112) | 1106 | | no `vi.mock` at all (real `GanttView`) | `configPin` (218), `exportFileName.i18n` (222), `pluginDriverTree` (136), `taskColumnLabel-27386` (96) | 672 | | `./GanttView` and `./ResourceWorkload` stand-ins (`viewmode`'s workload stand-in also writes `viewMode`) | `blockPrecedence` (281), `dependencyAlias` (140), `viewmode` (112) | 533 | | `@object-ui/plugin-detail` with `RecordDetailPanel` replaced (a different stand-in in each), plus `sonner` in `drawerfetch` and `hostRowClick-27057` | `drawerfetch` (171), `drawerlock` (91), `hostRowClick-27057` (155) | 417 | | an `@object-ui/react` `useNavigationOverlay` spy and `sonner`; `navigationModeResolution-7334` stubs the record drawer, `navigationPageSink-7334` replaces `RecordDetailPanel` | `navigationModeResolution-7334` (225), `navigationPageSink-7334` (253) | 478 | | `@object-ui/components` `NavigationOverlay` wrapped, plus `sonner` (`overlayShellModes-9299` also mocks `@object-ui/plugin-detail` and `@object-ui/react`) | `navWidthDefault` (235), `overlayShellModes-9299` (292) | 527 | | the harness's three mocks plus a patched `react` (`useMemo`, `useCallback`) | `discardedReloadIdentity-10036` (348) | 348 | | `@object-ui/permissions` (`usePermissions`) mocked | `expandFls-7230` (225) | 225 | | the harness's three mocks, but `ui/apps/console`'s `registry-inputs-spec-parity.test.ts` ledger names this path (outside the claim), so it stays a file | `elementDataSource` (243) | 243 | | **total** | 39 files | 8271 | - Whether to fold further is a new card the seat files after #29469 closes (amendment 2). ## Verification At head `6a0388d8`, 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-gantt^...'` from `ui/`), under `os-verify-lock`, VERDICT command-exit 0: - `pnpm exec vitest run packages/plugin-gantt/ --maxWorkers=2` from `ui/`: 96 files passed, 1 skipped; 788 tests passed, 114 skipped. Four fewer files than at `main`, the same 788 / 114 tests; the skipped file and tests are skipped on `main` too. - `pnpm run type-check` in `ui/packages/plugin-gantt` (`tsc --noEmit && tsc -p tsconfig.test.json`): exit 0, 0 `error TS`. - `pnpm run lint` in `ui/packages/plugin-gantt`: exit 0, 0 errors. `eslint` from `ui/` on the 14 added or modified files: exit 0, 0 errors, 173 warnings. The harness's 63 warnings are the 43 the #30725 harness had plus the 20 the four former files gave at `f18fd963`; the other 13 files changed comment or description text only. - Derived gates (`node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, change set 18 paths against merge base `447226b2`): 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, 4970 tests passed. `--ran` reconciliation: 26 derived, 26 run, 0 NOT-MEASURED, 0 UNRUN. - Size: `git diff --shortstat origin/main HEAD` is 18 files, +734 / -788, 1,522 changed lines. No open pull request touches any of the 18 files (file lists of all open PRs read after #30725 merged). - No ablation: no tested behaviour changed; the assertions moved verbatim. ## 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 lints are 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 081fb4a commit 2436b2f

18 files changed

Lines changed: 734 additions & 788 deletions

‎ui/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3418,7 +3418,7 @@ const MEMBER_PINS: Record<string, MemberPin> = {
34183418
// files, each read end to end first.
34193419
'object-gantt.gantt': {
34203420
file: 'packages/plugin-gantt/src/__tests__/objectGanttInputs-11168.test.tsx',
3421-
pins: 'Members are FIELD NAMES, each on the observable only it moves: `titleField` titles a task\'s row and `startDateField` / `endDateField` give its start and end, with a block naming other fields moving all three (the control). The block is taken WHOLE: flat `startDateField` / `endDateField` / `titleField` composed on the node beside it are ignored. The spec side is pinned with it: a block missing `titleField` is refused at `gantt.titleField`, and `percentageField` — which this key\'s description used to name, and nothing reads — is refused by name, while the description no longer names it. `exportFileName` is pinned with `object-gantt.label`; `colorField`, `dependenciesField` and `viewMode` keep their narrow pins (`ObjectGantt.colorFieldLadder-7243.test.tsx`, `ObjectGantt.dependencyAlias.test.tsx`, `ObjectGantt.viewmode.test.tsx`) and are not re-asserted (objectui#11168 slice 4).',
3421+
pins: 'Members are FIELD NAMES, each on the observable only it moves: `titleField` titles a task\'s row and `startDateField` / `endDateField` give its start and end, with a block naming other fields moving all three (the control). The block is taken WHOLE: flat `startDateField` / `endDateField` / `titleField` composed on the node beside it are ignored. The spec side is pinned with it: a block missing `titleField` is refused at `gantt.titleField`, and `percentageField` — which this key\'s description used to name, and nothing reads — is refused by name, while the description no longer names it. `exportFileName` is pinned with `object-gantt.label`; `colorField`, `dependenciesField` and `viewMode` keep their narrow pins (the `ObjectGantt.colorFieldLadder-7243` block of `ObjectGantt.harness.test.tsx`, `ObjectGantt.dependencyAlias.test.tsx`, `ObjectGantt.viewmode.test.tsx`) and are not re-asserted (objectui#11168 slice 4).',
34223422
},
34233423
'object-gantt.data': {
34243424
file: 'packages/plugin-gantt/src/__tests__/recordSourceInputs-10394.test.tsx',

‎ui/packages/app-shell/src/views/ObjectView.refreshInPlace-10035.test.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@
5353
* registered as `object-gantt` (the `ObjectView.stableFilterIdentity-7237`
5454
* precedent: `app-shell` does not depend on `plugin-gantt`). It reads the REAL
5555
* bus hook and queries on it exactly as `ObjectGantt` now does —
56-
* `ObjectGantt.invalidationRefetch-10035.test.tsx` proves the real component
56+
* the `ObjectGantt.invalidationRefetch-10035` block of `plugin-gantt`'s
57+
* `ObjectGantt.harness.test.tsx` proves the real component
5758
* does that — and carries an instance id from a `useState` initializer, so a
5859
* remount of the visualization itself is visible too.
5960
*/

‎ui/packages/core/src/utils/__tests__/record-color.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
* has a different right answer for "neither an option colour nor a literal"
1414
* (gantt derives a semantic hex, calendar hashes onto its class palette,
1515
* timeline draws its default marker). The renderer-side fixtures pinning that
16-
* split are `ObjectGantt.colorFieldLadder-7243.test.tsx`,
16+
* split are the `ObjectGantt.colorFieldLadder-7243` block of
17+
* `ObjectGantt.harness.test.tsx`,
1718
* `ObjectCalendar.colorFieldLadder-7243.test.tsx` and
1819
* `ObjectTimeline.colorFieldLadder-7243.test.tsx`.
1920
*/

‎ui/packages/plugin-gantt/src/ObjectGantt.colorFieldLadder-7243.test.tsx‎

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

0 commit comments

Comments
 (0)