Skip to content

test(react): type-check the whole test tree, and retire the narrow typetests project (#4040) - #4351

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-4040-react
Aug 11, 2026
Merged

test(react): type-check the whole test tree, and retire the narrow typetests project (#4040)#4351
yinlianghui merged 1 commit into
mainfrom
claude/issue-4040-react

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Part of #4040 (tranche 4, package 1 of 2). Sibling: #4355 (components). Refs #4291, #3181, #3009.

@object-ui/react now type-checks its whole test tree: a tsconfig.test.json
per the tranche template, chained from type-check, plus the 43 code-tier
errors that were hiding behind the exclusion. Its TEST_DEBT entry is deleted
and — per #4291's new ratchet — its narrow tsconfig.typetests.json is retired
in the same PR.

Remeasurement first: the registry said 27, the package has 43

Tranche 3's ruling is that the TEST_DEBT numbers are not to be trusted
(i18n declared 13 and measured 103). Measured here at the branch point, writing
every compiler run to a FILE and counting from the file — never piping a
compiler through head, which is how SIGPIPE produced a plausible false count
once already:

tier what it is errors
raw the package BUILD config with only the test exclusion lifted 46
config-tier dissolved by the template (paths: {}, composite: false, …) 3
code-tier real, fixed in this PR 43

Declared: 27. Measured: 43 (1.6x). Dominant codes: TS2322 x18, TS2769 x11,
TS7006 x4, TS2493 x3.

The 43, by family

1. Mock typings that erased their own assertions (20). let warnSpy: ReturnType< typeof vi.spyOn > resolves to the un-instantiated
MockInstance< Procedure | Constructable >, whose mock.calls degrades to an
implicitly-any element type; a zero-arity vi.fn() types mock.calls[0] as
the EMPTY tuple. So const [term, opts] = ds.searchAll.mock.calls[0] in
useRecordSearch.test.ts was destructuring undefined as far as the compiler
was concerned, in the case whose entire point is that opts.objects carries the
full whitelist. Fixed by naming the real signature — MockInstance< typeof console.warn >, and declared (unused, _-prefixed) parameters on the search
mocks.

2. createElement(C, props, children) where children is required (11).
The third-argument form does not satisfy Attributes & P when P requires
children; the eleven wrappers now pass children in the props object. No
behaviour change — createElement's third argument overwrites props.children
anyway.

3. One fixture with three deliberately-retired keys (11 call-site errors).
sampleActions in useActionEngine.test.ts declares bulkEnabled, shortcut
and locations: ['global_nav'] — all three retired in @objectstack/spec
17.0.0-rc.6, and all three kept ON PURPOSE, because the tests around them are
INVERSE tests (does not harvest the retired bulkEnabled key, does not offer an action declaring the retired global_nav location). Deleting them would delete
the only input that can prove the retirement holds. The array is now annotated
ActionDef[] and each stale key carries @ts-expect-error with the reason —
which makes the retirement compiler-enforced in both directions: re-admit any of
these keys to the spec and the now-unused directive reports TS2578.

4. A fixture using a tombstoned theme key (1). ThemeProvider.test.tsx
authored typography.fontSize, retired to a tombstone in
@objectstack/spec 17.0.0-rc.3 (objectstack#5021) with theme.customVars as the
prescription. Nothing in the file asserted on it.

5. SpecBridge.test.ts — a whole suite authored in a dialect the spec does
not have (8).
This is the one worth reading:

fixture wrote @objectstack/spec declares
rowHeight: 'comfortable' / 'spacious' / 'small' short | compact | medium | tall | extra_tall
link: { href: '/status' } link?: boolean
action: { type: 'navigate' } action?: string
sort: { field, direction } sort?: string | { field, order }[]
filter: { status: 'active' } filter?: { field, operator, value }[]
grouping: { field: 'region' } grouping?: { fields: [...] }

bridgeListView forwards every one of those keys verbatim (if (spec.sort) node.sort = spec.sort), so each assertion passed by round-tripping its own
input — proving pass-through and nothing about the contract. Re-spelled
canonically. The density case now covers all five values RowHeightSchema
admits (and gains medium and tall, which it never had).

That leaves mapDensity's branches for comfortable / spacious / small /
large unreachable from any spec-valid input. Filed as #4352 rather than
deleted here — removing a lenient fallback is a behaviour change for hosts still
passing the legacy spellings, and this PR's scope is the type-check graduation.

Retiring the narrow project (#4291's ratchet)

testsCovered is now true for this package, so keeping
tsconfig.typetests.json is an error by the rule #4291 added. Retired here,
with the same two-part proof:

Coveragetsc -p tsconfig.test.json --listFiles written to a file and
grepped (never piped through head):

total files: 630
matches for offline-nav-performance-spec-parity.test.ts: 1
/home/user/objectui-4040-react/packages/react/src/hooks/__tests__/offline-nav-performance-spec-parity.test.ts

Discrimination — a provably-false assertion in the file's own vocabulary
appended to that file, the FULL project run, then reverted:

=== [3/5] discrimination probe: append a provably-false Assert ===
    type _Probe4040 = Assert< Equal< 1, 2 > >;
probe exit=2
src/hooks/__tests__/offline-nav-performance-spec-parity.test.ts(390,27): error TS2344: Type 'false' does not satisfy the constraint 'true'.
probe reverted; diff vs HEAD for that file:   (empty)
=== [4/5] tsc -p tsconfig.test.json after revert (must be green) ===
post-revert exit=0

Coverage moved, it did not vanish.

The surviving environment is equal or wider. Both projects extends the
root tsconfig.json and neither overrides any strictness flag, so strict and
friends are identical by construction. The differences all go one way: lib
gains DOM.Iterable (["ES2020","DOM"] to ["ES2020","DOM","DOM.Iterable"]),
types gains @testing-library/jest-dom on top of ["node"], jsx and
paths: {} match exactly. A wider lib cannot make a failing spec-parity
assertion pass; it only adds globals the file never references.

Verification

=== [1/5] tsc -p tsconfig.test.json ===   test-project exit=0
=== [2/5] tsc --noEmit (build config) === build-config exit=0
=== [5/5] vitest (repo root, react package) ===
 Test Files  36 passed (36)
      Tests  496 passed (496)
   Duration  34.35s
  • node scripts/check-type-check-coverage.mjs — green:
    36/40 packages compile their tests, 4 declared debt (162 errors outstanding), 3 with a narrow type-assertion project
    (was 35/40, 5 debt, 4 narrow).
  • CI Type Check — green on this branch.
  • Every compiler run above went to a file and was counted from the file; nothing
    was piped through head.

Changeset: empty frontmatter. Only test files, checking-only tsconfig projects,
one type-check script and the coverage registry move; nothing published
changes.

Registry race

This PR and #4355 each delete one line from TEST_DEBT in
scripts/check-type-check-coverage.mjs and each append one name to the
retired-narrow-project list in the gate's own suite. Whichever lands second will
conflict there; GitHub silently disables auto-merge on conflict, so it will need
re-arming after the resolve (merge origin/main, take main's registry minus this
package's line, re-run the coverage script, commit).


Generated by Claude Code

…petests project (#4040)

`@object-ui/react` gains a `tsconfig.test.json` chained from `type-check`, so
its 36 test files are compiled by something for the first time. Measured at the
branch point: 46 raw errors, 43 code-tier once the config-tier noise is gone —
against a TEST_DEBT entry that declared 27.

All 43 are fixed in the tests. Two mock-typing families account for 20 of them
(`ReturnType<typeof vi.spyOn>` / zero-arity `vi.fn` degrade `mock.calls` to the
empty tuple, so several `calls[0][0]` assertions were reading `undefined` as far
as the compiler was concerned), 11 are `React.createElement(C, props, children)`
against a component whose props require `children`, and 11 are one fixture that
had to be annotated so its three deliberately-retired keys could be marked
`@ts-expect-error` instead of hiding among accidents.

The substantive one is `SpecBridge.test.ts`: its ListView fixtures were authored
in a dialect `@objectstack/spec` does not have — `rowHeight: 'comfortable'`,
a `{ href }` object for the boolean `link`, `sort: { field, direction }`,
`filter` as a field→value map, `grouping: { field }`. The bridge forwards those
keys verbatim, so every assertion passed by round-tripping input that no
spec-valid list view can carry. Re-spelled canonically; the density case now
covers all five values `RowHeightSchema` admits.

Per #4291's ratchet, `tsconfig.typetests.json` is retired in the same PR: the
full project compiles the file it named (1 of 630, by `--listFiles`) and a
provably-false `Assert` appended to that file turns the full project red.

Refs #4040, #4291, #3181, #3009.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 11, 2026 8:36pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 29.6 KB 350 KB
Entry file index-BAUts28q.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 489.12KB 108.41KB
core (index.js) 3.04KB 1.15KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 150.04KB 39.79KB
fields (index.js) 228.37KB 56.62KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.33KB 1.20KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 4.52KB 1.96KB
layout (index.js) 38.98KB 10.85KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 45.23KB 12.45KB
plugin-charts (index.js) 62.18KB 17.67KB
plugin-chatbot (index.js) 180.33KB 42.79KB
plugin-dashboard (index.js) 121.56KB 31.56KB
plugin-designer (index.js) 210.91KB 42.67KB
plugin-detail (index.js) 238.95KB 59.76KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 114.58KB 27.68KB
plugin-gantt (index.js) 164.14KB 39.98KB
plugin-grid (index.js) 188.00KB 49.94KB
plugin-kanban (index.js) 48.60KB 13.41KB
plugin-list (index.js) 110.10KB 26.74KB
plugin-map (index.js) 18.05KB 5.80KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.60KB 10.58KB
plugin-timeline (index.js) 26.21KB 7.52KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 20.55KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 23.71KB 7.96KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.23KB 0.66KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@yinlianghui
yinlianghui marked this pull request as ready for review August 11, 2026 21:12
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 11, 2026
Merged via the queue into main with commit b730d99 Aug 11, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4040-react branch August 11, 2026 21:13
yinlianghui pushed a commit that referenced this pull request Aug 11, 2026
… components removal is this PR's; retired-list union
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants