Skip to content

test(app-shell): stub useMetadataClient in metadata-admin inspector suites - #4700

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-4697-metadata-client-doubles
Aug 15, 2026
Merged

test(app-shell): stub useMetadataClient in metadata-admin inspector suites#4700
yinlianghui merged 1 commit into
mainfrom
claude/issue-4697-metadata-client-doubles

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#4697

What

packages/app-shell/src/views/metadata-admin/previews/useObjectFields.ts and
useObjectOptions.ts both call useMetadataClient() unconditionally in a
mount-time useEffect, which escapes to the real network under happy-dom
(ECONNREFUSED 127.0.0.1:3000 — harmless noise, since both hooks already
degrade to an empty result on any transport error). Stubs useMetadataClient
in the 9 files the card named, in
packages/app-shell/src/views/metadata-admin/inspectors/:

  • PageBlockInspector.i18n.test.tsx
  • PageBlockInspector.sectionName.test.tsx
  • ConditionBuilder.celGate.test.tsx
  • HookDefaultInspector.condition.test.tsx
  • ViewVariantInspector.celGate.test.tsx
  • ViewVariantInspector.homeGate.test.tsx
  • ActionDefaultInspector.celGate.test.tsx
  • ConditionBuilder.test.tsx
  • HookDefaultInspector.celGate.test.tsx

Each file gets its own vi.hoisted() + vi.mock('../useMetadata', …) block
(mirroring the in-package precedent, FlowReferenceField.lookup.test.tsx),
with a stable stub client ({ get, list } both resolving to an empty
result) — stable identity matters because the hooks' effects key off
[client, …], so a fresh object per call would re-fire them every render.
No shared helper file: 9 short, self-contained blocks stayed easier to review
than a new cross-file abstraction for this size of change.

Tests-only — no production source touched. .changeset/metadata-admin-inspector-client-doubles.md
declares an empty-frontmatter release (test-only, precedent #4641/#4666/#4671).

Acceptance = measured zero

Every file re-run isolated (pnpm exec vitest run <file> --maxWorkers=1
— batched runs undercount, per the card):

fileECONNREFUSED beforeafter
PageBlockInspector.i18n.test.tsx700
PageBlockInspector.sectionName.test.tsx400
ConditionBuilder.celGate.test.tsx120
HookDefaultInspector.condition.test.tsx120
ViewVariantInspector.celGate.test.tsx120
ViewVariantInspector.homeGate.test.tsx120
ActionDefaultInspector.celGate.test.tsx80
ConditionBuilder.test.tsx80
HookDefaultInspector.celGate.test.tsx60

Sum 180 → 0, exactly matching the card's baseline. Every assertion unchanged
(same pass counts before/after: 22, 9, 6, 6, 4, 2, 4, 4, 3 — 60 tests total).

Full directory suites also re-run clean:
packages/app-shell/src/views/metadata-admin/inspectors/ → 53 files, 590
passed | 1 skipped (pre-existing skip, unrelated), 0 ECONNREFUSED.
packages/app-shell/src/views/metadata-admin/ (the whole area) → 167 files,
1671 passed | 1 skipped, 0 ECONNREFUSED.

Reverse verification (direction predicted first: reverting the stub
should return the count to baseline with tests still green — proving the stub,
not something else, is what silences the noise): committed the fix, then
git checkout HEAD~1 -- .../PageBlockInspector.i18n.test.tsx (the biggest
file) → re-ran isolated → 70 ECONNREFUSED, 22/22 still passing, confirming
prediction. Restored with git checkout <fix-sha> -- <path> → back to 0.

Out-of-scope finding

While measuring the full metadata-admin/ suite for step 2 of the verification,
found a different escape (ECONNRESET/"socket hang up", not
ECONNREFUSED — a different transport mechanism) in
previews/FlowCanvas.test.tsx (19 lines isolated). Filed unassigned as #4699,
not touched here — out of scope for this card.

Local gate union at HEAD b65801b2d

  • pnpm --filter '@object-ui/app-shell^...' build — dependency closure built first (green).
  • pnpm --filter @object-ui/app-shell run type-checktsc --noEmit && tsc -p tsconfig.test.json — green.
  • pnpm exec eslint --quiet <the 9 files> — clean, no output.
  • The 9 files isolated + the inspectors/ and full metadata-admin/ directory suites (above).
  • node scripts/check-changeset-presence.mjs — green (empty-frontmatter changeset declared).
  • pnpm run check:control-bytes — green.

scripts/pm/dispatch-gates.mjs does not exist in this repo (checked — it
appears to be an objectstack script, not objectui); re-derived the
implicated gate families by hand against the actual diff (9 .test.tsx files
under packages/app-shell/src/views/metadata-admin/inspectors/ + one
changeset) — no new error codes, no .claude/agents/** changes, no new fake
engine, no t() call-site changes, no doc changes, so no family beyond the
ones already named/run above is implicated.

Generated by Claude Code


Generated by Claude Code

…uites
useObjectFields/useObjectOptions fire a mount-time fetch with no override,
which escaped to the real network under happy-dom (ECONNREFUSED noise,
different endpoint than #4688's grid explain probe). Stub the shared
useMetadataClient in the 9 named inspector test files with a stable client
whose get()/list() resolve empty — the same end state the real client's
.catch() already produced on a transport error, so no assertion changes.
Fixes#4697
@vercel

vercelBot commented Aug 15, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 15, 2026 11:10am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)24.7 KB350 KB
Entry fileindex-CkVjEjmE.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)9.56KB3.59KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)8.92KB3.41KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)25.13KB5.40KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.13KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.64KB2.21KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)38.46KB10.17KB
auth (createAuthenticatedFetch.js)6.34KB2.43KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.02KB0.88KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)26.07KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.65KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)489.91KB108.67KB
core (index.js)3.79KB1.52KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)163.56KB44.83KB
fields (index.js)230.37KB57.17KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)3.35KB1.38KB
i18n (pickLocalized.js)3.69KB1.73KB
i18n (provider.js)23.12KB7.62KB
i18n (useDisplayLocale.js)2.84KB1.45KB
i18n (useObjectLabel.js)27.59KB6.63KB
i18n (useSafeTranslation.js)7.77KB3.13KB
layout (index.js)38.98KB10.85KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.86KB12.91KB
plugin-charts (index.js)64.44KB18.28KB
plugin-chatbot (index.js)181.21KB43.14KB
plugin-dashboard (index.js)121.90KB31.76KB
plugin-designer (index.js)212.39KB42.83KB
plugin-detail (index.js)239.93KB60.01KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)114.72KB27.70KB
plugin-gantt (index.js)164.30KB40.02KB
plugin-grid (index.js)192.10KB51.24KB
plugin-kanban (index.js)52.74KB14.53KB
plugin-list (index.js)111.82KB27.23KB
plugin-map (index.js)18.16KB5.81KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)41.38KB11.09KB
plugin-timeline (index.js)26.68KB7.66KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)83.81KB20.49KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)27.64KB9.44KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.26KB0.67KB
react (schema-input.js)1.45KB0.83KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)3.05KB1.52KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

Sign up for freeto 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.

app-shell metadata-admin inspectors escape to the real network via useObjectFields/useObjectOptions (ECONNREFUSED noise, different probe than #4688)

2 participants

@yinlianghui@claude