test(vi-mock): inherit the real @object-ui/plugin-form surface in 32 vi.mock factories - #8116
Conversation
…vi.mock factories Part of #6892 (slice 5). Every frozen `vi.mock('@object-ui/plugin-form', ...)` factory in the tree now obtains the real module and spreads it FIRST, with the file's own stubs applied as overrides afterwards, and `@object-ui/plugin-form` joins the `check-vi-mock-inherit` gate's `COVERED_SPECIFIERS` in the same commit. Three syntactic shapes, across three owning packages: 16 single-line object literals and 11 multi-line ones under `packages/plugin-view` and `packages/app-shell`, plus 4 `packages/plugin-designer` sites that delegated the whole factory to a shared manual mock module and so inherited nothing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
…lice Part of #6892 (slice 5). Merging `origin/main` brought in one more frozen factory on this specifier — `ObjectView.expandFls-7429.test.tsx`, landed by objectui#7429 while this slice was being verified. It is swept the same way, so the gate reads zero frozen for `@object-ui/plugin-form` on the tree this pull request actually ships. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
|
Standing down on
Every other check on Generated by Claude Code |
|
Armed — squash auto-merge enabled and the PR is in the merge queue,
On MERGED: content probe on re-fetched Generated by Claude Code |
|
LANDED — merged at 2026-09-06T17:38:50Z as Content probe on re-fetched
#6892 stays open ( Generated by Claude Code |
Part of #6892 — slice 5 of the per-specifier
vi.mocksweep worklist:@object-ui/plugin-form.Every frozen
vi.mock('@object-ui/plugin-form', ...)factory in the tree now obtains the real module and spreads it FIRST, with the file's own stubs applied as overrides afterwards, and@object-ui/plugin-formjoinsCOVERED_SPECIFIERSinscripts/check-vi-mock-inherit.mjsin the same pull request. The gate reads 0 frozen for the specifier on the tree this branch ships, so the constant line is paid for by the sweep rather than by judgement.This is the first slice whose sites span three owning packages, and the first to meet a third syntactic shape.
Step 0 — what does inheriting actually RUN?
Mandatory before any conversion, and this is the slice that was flagged as most likely to fail it. The static import graph was walked from
packages/plugin-form/src/index.tsxwith the TypeScript parser and every top-level statement of every reached module classified.28 modules reached, 75 module-scope statements. Verdict: PROCEED, with a named, benign side effect — not the "inert" verdict slices 3 and 4 got.
Six of the 75 are module-scope registrations in the barrel itself:
index.tsx:184ComponentRegistry.register('object-form', ...)namespaceplugin-formindex.tsx:243ComponentRegistry.register('form', ...)namespaceview,skipFallbackindex.tsx:295ComponentRegistry.register('embeddable-form', ...)namespaceplugin-formindex.tsx:316ComponentRegistry.register('form-analytics', ...)namespaceplugin-formindex.tsx:360ComponentRegistry.register('object-master-detail-form', ...)namespaceplugin-formindex.tsx:457ComponentRegistry.register('line_items', ...)namespacerecord,skipFallbackregister()was read rather than assumed: it is aMap.setinto an in-memory registry plus aconsole.warnon the un-namespaced spelling, and all six pass a namespace, so not even the warning fires. This is the same effect slice 2 absorbed forplugin-chartsandplugin-dashboard.The other 69 statements are pure allocation —
Sets of literal strings (autoLayout.ts,sanitize.ts,deriveMasterDetail.ts), style and threshold literals, one regular expression atsubmitRedirect.ts:96, fourcreateSafeTranslation(...)factory calls (occSave.tsx:49,WizardForm.tsx:186,DrawerForm.tsx:59,ModalForm.tsx:60, measured pure by slice 3) and fourelementDataSourceBlock(...)marks, which add the renderer to a module-levelSetin@object-ui/core.Absent: zero timers, globals, storage access,
fetch, connections; zero side-effect-only imports. Two column-anchored greps over all 28 modules agree with the parser walk exactly — the globals and timers grep matches nothing (exit 1) and the registration grep returns exactly those six lines and nothing else.The ruling's failure class was checked on the consuming side too. Of the 32 converted files exactly one names
ComponentRegistryat all:ObjectView.dataSourceContextFallback-7842.test.tsxREADS one entry (get('object-view')) rather than asserting emptiness or a registration count. No converted assertion can be broken by the six new entries. The onlyconsolespy in the 32 is aconsole.errorspy, andregister()only ever writes toconsole.warn.The free confirmation slice 4 lacked exists here.
packages/app-shell/src/views/studio-design/StudioDesignSurface.formFields.test.tsxalready inherited the real barrel onmainand passed, so the real module was known to load in that test environment before anything was converted.Step 1 — the derived census
Re-derived on the merge base with the FIXED classifier, through the gate's exported
scan()withcoveredset to every workspace specifier any call site in the tree names, soCOVERED_SPECIFIERSwas never widened-and-reverted.@object-ui/plugin-formon the merge based5c1f527e52019690d(after mergingmain)All-specifier population: 178 to 147 frozen over 643 judged on the original base, and 148 over 654 on the shipped head. Direction check: every one of the other 21 rows is byte-identical between the two runs — a
diffof the per-specifier tables with theplugin-formrow removed is empty. Zero sites moved the other way.Site distribution, exactly matching the dispatch's own grep: 20 under
packages/plugin-view, 8 underpackages/app-shell(7 frozen plus the 1 already inheriting), 4 underpackages/plugin-designer.The growth warning fired for the third consecutive slice. Merging
origin/mainmid-slice brought in a thirty-second frozen site on this same specifier —packages/plugin-view/src/__tests__/ObjectView.expandFls-7429.test.tsx, landed by #8089 while this branch was being verified. It is swept the same way in this pull request, so the constant is widened against a gate run on the tree that ships and not against the derivation taken on the base.The three shapes
plugin-view).vi.mock('@object-ui/plugin-form', () => ({ ObjectForm: ... }))becomes an async factory that spreadsimportOriginalof Record-of-string-to-unknown first.app-shelland 4plugin-view). Same conversion, one added spread line; every existing override body is untouched.plugin-designer) — new on this slice.vi.mock('@object-ui/plugin-form', () => import('./__mocks__/plugin-form'))handed the entire factory to a shared manual mock module that exports onlyModalFormandDrawerForm, and so inherited nothing at all. These become an async factory that spreads the real barrel FIRST and the manual mock module SECOND, so the two hand-written components still win and every other export is now the real one. That the override ordering is right is not asserted, it is measured: those four suites assert ondata-testid="mock-modal-form"and stay green.No site was converted blindly and none deliberately freezes to prove a module is not loaded, so there is no residual and the constant line ships in full.
Residuals and exclusions
Residuals: none. Both exclusions were verified rather than assumed, and both cost nothing:
FlowNodeInspector.specKeys.test.tsx(held for Burn down the 21 known network escapes: serve each probe from a double and emptyKNOWN_ESCAPESin the network-escape guard #7307 batch 6) carries no@object-ui/plugin-formreference at all, so it is not a site.changed_files— and intersects this slice's sites in zero paths.Ablation — one leg per shape, blob-hash proven
Each leg mutates the committed tree, proves the mutation reached disk by anchored text counts BEFORE the result is read, runs the gate, then restores under a trap on EXIT INT TERM and proves the restore by
git hash-objectequalling the HEAD blob and bygit diff HEADnaming zero paths — never by a checkout exit code.ObjectView.sortSink.test.tsx:75onlyRecordFormPage.i18n.test.tsx:83onlyObjectManager.test.tsx:16onlyLegs A and B are load-bearing rather than tautological: both files carry TWO inheriting factories on different specifiers, the mutation removes exactly one, and the gate names exactly the one site. Every leg returns the gate to exit 0 after the restore.
No dist preflight is needed: the root Vitest config aliases
@object-ui/plugin-formtopackages/plugin-form/src, and the gate is a source-text scanner.Nothing died during COLLECTION
The 32 converted files were run at this head and again with their merge-base blobs restored over exactly those 32 paths under a trap:
52019690d59a3a233dIdentical, so no test was lost or gained and no file died before it ran. The base restore is proven on disk — async-factory markers across the 32 paths drop from 42 to 10, the frozen object-literal head reappears in 28 files and the frozen delegation head in 4 — and the restore leaves
git diff HEADnaming zero paths.Gates
All pinned to the final head
52019690d, exit codes captured by redirect-then-capture, never through a pipe.pnpm check:vi-mock-inheritplugin-form0 frozenpnpm exec vitest run scripts/__tests__/check-vi-mock-inherit.test.tspnpm exec vitest run packages/plugin-view/ packages/plugin-designer/pnpm exec vitest run packages/app-shell/pnpm check:vi-mock-specifierspnpm type-check:scriptspnpm check:control-bytesgrep -naPself-scan of all 34 changed paths, no matchnode scripts/check-changeset-presence.mjspnpm changeset:checknode scripts/check-governed-queue-guard.mjs --testtype-checkof the three owning packages--listFilesnames 21 of 21, 4 of 4 and 7 of 7 edited files in programs of 1636, 655 and 4424 fileslintof the three owning packages--format jsonlists 1191 files and all 32 edited files, and intersecting every message against this branch's own 106 added line numbers gives 0The whole
packages/app-shellsuite ran as four disjoint shards rather than one invocation because a single run exceeds this container's foreground cap. This is not a narrowing: the four shards partition the same file set, their union is the package's 634 test files, each ran in the foreground to completion, and all four exit 0.Live E2E (informational)is red on every branch today for an upstream reason (#7990) and is not this branch's reading.Generated by Claude Code