Skip to content

test(plugin-calendar): partial-mock @object-ui/react so both vitest configs agree (#3219) - #3241

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-3219-calendar-mock-importoriginal
Aug 3, 2026
Merged

test(plugin-calendar): partial-mock @object-ui/react so both vitest configs agree (#3219)#3241
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-3219-calendar-mock-importoriginal

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #3219

按 PM 在认领评论里的裁定,本 PR 只做两件事:把 registration.test.tsx 改成 importOriginal 的部分 mock,并补一条断言钉住这个文件在两条运行路径下结论一致。全仓 17 个包的 vitest 配置治理不在本单,已另立 #3240

根因(实测坐实,不是推测)

同一份 packages/plugin-calendar/src/registration.test.tsx,两套配置结论相反:

路径 实际执行 修复前
CI(根 vitest.config.mts,vitest run --shard=N/4) @object-ui/* alias 到源码 绿
pnpm --filter @object-ui/plugin-calendar test / turbo run test(包内 vitest.config.ts) 无 alias,解析到 dist

原来的写法是整模块替换,只列了 useSchemaContext + SchemaRendererContext。真正 import subscribeDataChanges 的不是 plugin-calendar 自己,而是传递依赖 @object-ui/componentssrc/hooks/related-count-store.ts

关键的一层:根配置那一侧的"绿"是偶然的。该文件在根配置的 heavyDomTests 名单里,vitest.setup.dom.tsx预先 import '@object-ui/components',于是 related-count-store.tsvi.mock 生效之前就已按真实模块求值完毕;包配置没有这个 setup,@object-ui/components 首次求值发生在被 mock 的图里,vitest 4 对整模块 mock 的缺失导出是硬报错(不再静默给 undefined),于是整份文件加载失败。

所以分叉有两个叠加变量:alias 解析目标(源码 vs dist),以及 setupFiles 是否预热了消费者模块。

修法

改用 importOriginal 的部分 mock,只覆盖这份测试真正要控制的 useSchemaContext:

vi.mock(import('@object-ui/react'), async (importOriginal) => ({
  ...(await importOriginal()),
  useSchemaContext: vi.fn(() => ({ dataSource: { type: 'mock-datasource' } })),
}));

这样 mock 在任一解析路径下都是真实模块的超集,传递依赖不可能再撞上一个本测试从未打算替换的导出。SchemaRendererContext 不必再手写——原模块的那个会被 spread 进来。

vi.mock('./ObjectCalendar', …) 保持整模块替换:那是本测试刻意的隔离边界(且该模块唯一的运行时导出就是组件本身,ObjectCalendarProps 是类型、运行时已擦除)。

防回归断言

新增一条断言,钉的正是"分叉"本身的不变式——mock 必须是真实模块的超集:

const actual = await vi.importActual< typeof ObjectUIReact >('@object-ui/react');
const missing = Object.keys(actual).filter((name) => !(name in ObjectUIReact)).sort();
expect(missing).toEqual([]);

刻意比较导出集合而不是点名 subscribeDataChanges:点名只会把同一个陷阱推迟到下一个新增导出(这正是原 issue 指出的问题)。该不变式成立时,任何传递 importer 都不会遇到缺失导出,于是文件在哪套配置下解析、setup 有没有预热消费者,结论都一样。

验证

修复前,包路径(issue 报告的那条):

FAIL  src/registration.test.tsx [ src/registration.test.tsx ]
Error: [vitest] No "subscribeDataChanges" export is defined on the "@object-ui/react" mock.
 ❯ src/CalendarView.tsx:13:1
 Test Files  1 failed (1) /  Tests  no tests

修复后,两条路径一致:

$ pnpm --filter @object-ui/plugin-calendar exec vitest run src/registration.test.tsx
 Test Files  1 passed (1) /  Tests  3 passed (3)

$ pnpm exec vitest run --project dom-heavy packages/plugin-calendar/src/registration.test.tsx
 Test Files  1 passed (1) /  Tests  3 passed (3)

包全量:Test Files 3 passed (3) / Tests 16 passed (16)(原为 1 failed | 2 passed)。type-check 通过,lint 0 error。

断言的反向验证(把整模块 mock 改回去,断言留着):

# 根路径 —— 即原缺陷"看不见"的那条,现在也红了:
AssertionError: expected [ 'ActionCtxReact', …(125) ] to deeply equal []
+   "subscribeDataChanges",
      Tests  1 failed | 2 passed (3)

# 包路径:
Error: [vitest] No "subscribeDataChanges" export is defined on the "@object-ui/react" mock.
 Test Files  1 failed (1) /  Tests  no tests

两条路径同红同绿,断言确实拦得住。

关于 changeset

未加,理由:本 PR 只改动一个 *.test.tsx,不进 dist(包的 files 只发 dist/README/CHANGELOG),对使用者零可见变化。按 AGENTS.md「功能改进需写 changeset;纯 bug 修复不需要」,测试基础设施修复比纯 bugfix 更靠内。加了反而会让 fixed 组 39 个包为一次测试改动整体升版。如维护者认为仍需登记,我补一条 patch 即可。

越界发现

🤖 Generated with Claude Code

https://claude.ai/code/session_01NVPjPzmmAJ2Ngtvgg5MSRa


Generated by Claude Code

…onfigs agree (#3219)

`registration.test.tsx` whole-module-mocked `@object-ui/react` with just
`useSchemaContext` + `SchemaRendererContext`. That made the file's result
depend on how the module graph resolved, so the repo's two vitest configs
disagreed about it:

- root `vitest.config.mts` (CI): the file is in `heavyDomTests`, and
  `vitest.setup.dom.tsx` eagerly imports `@object-ui/components`, which
  evaluates `related-count-store.ts` (imports `subscribeDataChanges` from
  `@object-ui/react`) against the REAL module before the mock applies —
  green by accident.
- `packages/plugin-calendar/vitest.config.ts` (`pnpm --filter … test`,
  `turbo run test`): no such setup, so `@object-ui/components` is first
  evaluated inside the mocked graph. Vitest 4 hard-errors on a missing
  export, so the suite failed to load at all.

Switch to an `importOriginal` partial mock: the mock is now a superset of
the real module under either resolution, so a transitive consumer can never
trip over an export this test never intended to replace.

Add a regression guard asserting the mock exposes every real export. It
compares export SETS rather than naming `subscribeDataChanges`, so adding a
new `@object-ui/react` export cannot re-arm the same trap. Verified to fail
on the root path — the path where the original defect was invisible.

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

vercel Bot commented Aug 3, 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 3, 2026 7:10am

Request Review

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.47KB 3.09KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
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.12KB 3.41KB
auth (LoginForm.js) 17.86KB 5.29KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.43KB 2.09KB
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) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.25KB 0.53KB
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) 476.12KB 104.47KB
core (index.js) 2.25KB 0.80KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 136.23KB 34.75KB
fields (index.js) 223.43KB 54.66KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.46KB 0.96KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 5.37KB 1.72KB
i18n (useObjectLabel.js) 26.14KB 6.07KB
i18n (useSafeTranslation.js) 3.26KB 1.44KB
layout (index.js) 37.91KB 10.53KB
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.05KB 1.53KB
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) 44.90KB 12.35KB
plugin-charts (index.js) 60.53KB 17.12KB
plugin-chatbot (index.js) 180.09KB 42.72KB
plugin-dashboard (index.js) 111.87KB 28.82KB
plugin-designer (index.js) 210.51KB 42.50KB
plugin-detail (index.js) 230.54KB 56.77KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 111.39KB 26.94KB
plugin-gantt (index.js) 162.26KB 39.53KB
plugin-grid (index.js) 185.04KB 49.00KB
plugin-kanban (index.js) 47.82KB 13.18KB
plugin-list (index.js) 104.86KB 25.30KB
plugin-map (index.js) 16.80KB 5.24KB
plugin-markdown (index.js) 13.65KB 4.67KB
plugin-report (index.js) 40.48KB 10.57KB
plugin-timeline (index.js) 25.76KB 7.32KB
plugin-tree (index.js) 8.34KB 2.82KB
plugin-view (index.js) 83.54KB 20.39KB
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) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
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 (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) 2.46KB 1.21KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
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

@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 3, 2026 07:15
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit 24641d6 Aug 3, 2026
16 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-3219-calendar-mock-importoriginal branch August 3, 2026 07:16
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.

plugin-calendar 的 registration.test.tsx 在 main 上就是红的:@object-ui/react 的 vi.mock 缺 subscribeDataChanges

2 participants