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)=>({
...(awaitimportOriginal()),useSchemaContext: vi.fn(()=>({dataSource: {type: 'mock-datasource'}})),}));

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

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

防回归断言

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

constactual=awaitvi.importActual<typeofObjectUIReact>('@object-ui/react');constmissing=Object.keys(actual).filter((name)=>!(nameinObjectUIReact)).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

vercelBot commented Aug 3, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 3, 2026 7:10am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)28.1 KB350 KB
Entry fileindex-B76hz360.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.47KB3.09KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)22.10KB4.37KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.12KB3.41KB
auth (LoginForm.js)17.86KB5.29KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.43KB2.09KB
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)35.76KB9.11KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
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)4.91KB0.87KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)18.38KB4.49KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)3.65KB1.42KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.25KB0.53KB
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)476.12KB104.47KB
core (index.js)2.25KB0.80KB
create-plugin (index.js)9.28KB2.98KB
data-objectstack (index.js)136.23KB34.75KB
fields (index.js)223.43KB54.66KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.46KB0.96KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)5.37KB1.72KB
i18n (useObjectLabel.js)26.14KB6.07KB
i18n (useSafeTranslation.js)3.26KB1.44KB
layout (index.js)37.91KB10.53KB
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.05KB1.53KB
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.71KB3.79KB
plugin-calendar (index.js)44.90KB12.35KB
plugin-charts (index.js)60.53KB17.12KB
plugin-chatbot (index.js)180.09KB42.72KB
plugin-dashboard (index.js)111.87KB28.82KB
plugin-designer (index.js)210.51KB42.50KB
plugin-detail (index.js)230.54KB56.77KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)111.39KB26.94KB
plugin-gantt (index.js)162.26KB39.53KB
plugin-grid (index.js)185.04KB49.00KB
plugin-kanban (index.js)47.82KB13.18KB
plugin-list (index.js)104.86KB25.30KB
plugin-map (index.js)16.80KB5.24KB
plugin-markdown (index.js)13.65KB4.67KB
plugin-report (index.js)40.48KB10.57KB
plugin-timeline (index.js)25.76KB7.32KB
plugin-tree (index.js)8.34KB2.82KB
plugin-view (index.js)83.54KB20.39KB
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)19.28KB6.38KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.02KB0.55KB
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 (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)2.46KB1.21KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)0.20KB0.18KB
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

@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 queueAug 3, 2026
Merged via the queue into main with commit 24641d6Aug 3, 2026
16 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-3219-calendar-mock-importoriginal branch August 3, 2026 07:16
@xuyushun441-sys
xuyushun441-sys restored the claude/issue-3219-calendar-mock-importoriginal branch August 4, 2026 00:08
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

@xuyushun441-sys@claude