Skip to content

fix(test): runner 的包级 test 入口指回仓根配置,不再落到 app 的 vite.config (#3746) - #3869

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-3746-runner-vitest-config
Aug 8, 2026
Merged

fix(test): runner 的包级 test 入口指回仓根配置,不再落到 app 的 vite.config (#3746)#3869
yinlianghui merged 1 commit into
mainfrom
claude/issue-3746-runner-vitest-config

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#3746

现象与机制(在干净 worktree 的 origin/main @ 6402e253f 上原样复现)

$ pnpm --workspace-concurrency=2 --filter @object-ui/runner test --maxWorkers=2
Test Files 1 failed | 2 passed (3)
Tests 4 failed | 9 passed (13)
ReferenceError: window is not defined
Duration 11.47s (transform 10.81s, setup 0ms, import 21.88s, tests 27ms, environment 0ms)

packages/runner 声明了 "test": "vitest run" 却没有 vitest 配置。pnpm 把 cwd 定在包目录,
vitest 于是把该目录里那份 Vite 应用配置 vite.config.ts 当成自己的配置读进来 —— 它只有
resolve.alias / build,没有 test 块,所以没有 DOM 环境也没有 setup(上面那次运行自己的
汇总就写着 setup 0ms ... environment 0ms),App.navigation.test.tsx 的 4 个用例在 node
环境里撞死在 window 上。仓根入口带 projects/happy-dom/setup,所以 CI 一直是绿的。

修法:把这个入口指回仓根配置,而不是给 runner 补一份包级配置

派单缺省形状是「给 runner 补 vitest 配置」。落地时发现那条路与仓内两处既有事实冲突,故改成
本 PR 这一行 —— 两点都有 file:line 证据:

  1. 17 个包各自带 vitest.config.ts 与根 vitest.config.mts 行为分叉:先回答「哪些包真的需要本地配置」 #3240 的维护者裁决是 A:全删 17 份包级 vitest 配置、统一走仓根配置
    (2026-08-06,评论 5200265431),并在同单重定义包级 test 脚本的语义为「对仓根配置传
    路径过滤」
    。再新增第 18 份是往一个已有拆除方案的债上加码;而且包级配置解析
    @object-ui/* 走 runner 自己那张 15 条的 alias 表(其余落 dist),仓根配置则把 ~40 个
    包名指向 src —— 正是 17 个包各自带 vitest.config.ts 与根 vitest.config.mts 行为分叉:先回答「哪些包真的需要本地配置」 #3240 要消灭的「同一文件两套结论」。
  2. 包级配置会把这次运行从 scripts/vitest-invocation-guard.mjs 底下抬出去。 guard 只在
    vitest.config.mts 顶部被调用(vitest.config.mts:5:23),包级独立配置根本不加载它,
    于是 pnpm --filter <pkg> test -- --run <paths> 静默忽略路径过滤:新加的测试文件根本没跑,而输出看起来正常 #3288-- 转发陷阱对本包不再被拒绝。仓内已有这种独立配置的包(如
    packages/plugin-charts/vitest.config.ts)就处于这个状态。

所以 packages/runner/package.json 的脚本改成 AGENTS.md(§怎么跑测试,第 184 行)与 guard
报错正文都明确指定的那条写法:

- "test": "vitest run"+ "test": "vitest run --root ../.. packages/runner/"

两个入口从此是同一份配置,不存在漂移面;--root 指回仓根后 guard 依旧在链路上(root 等于
仓根 → 放行,-- 转发 → 照样拒绝)。

两个入口都绿、用例数一致

$ pnpm --workspace-concurrency=2 --filter @object-ui/runner test --maxWorkers=2
> vitest run --root ../.. packages/runner/ --maxWorkers=2
RUN v4.1.10 /home/user/objectui-3746
Test Files 3 passed (3)
Tests 13 passed (13)
Duration 13.48s (... setup 453ms ... environment 507ms) # 环境与 setup 真的生效了
$ pnpm exec vitest run packages/runner/src --maxWorkers=2
Test Files 3 passed (3)
Tests 13 passed (13)

仓根 projects 机制不受影响 —— 以注册面为证,runner 的 3 个文件各自只在一个 project 注册,
既不重复也不缺:

$ pnpm exec vitest list --filesOnly packages/runner/
[unit] packages/runner/src/plugin-integration.test.ts
[unit] packages/runner/src/__tests__/spec-symbol-batch7.test.ts
[dom] packages/runner/src/App.navigation.test.tsx

⛔ 未改动仓根 vitest.config.mts(本 PR 只有 2 个文件)。

防回归钉子

scripts/__tests__/runner-package-test-entry-3746.test.ts(独立文件,与在飞单零共享),
7 条断言分两组:

  • runner 这一条入口:目录仍是「有 vite.config.ts 且其中没有 test 块、没有 vitest
    配置」的可劫持形状;脚本把 vitest root 指回仓根;该调用被 guard 判为放行;位置过滤解析得到
    的目录确实在 packages/runner 内,且覆盖 runner 全部测试文件(防「悄悄只跑一部分」)。
  • 全仓棘轮:按谓词「有 vite.config、无 vitest 配置、脚本没把 root 指回仓根」扫全仓,
    runner 已不在其中;其余命中以上限方式记录(见下),断言是子集而非相等 —— 17 个包各自带 vitest.config.ts 与根 vitest.config.mts 行为分叉:先回答「哪些包真的需要本地配置」 #3240
    它们修好时这条依然绿。

顺带测到、⛔ 本 PR 不修的面

issue 正文说 runner 的特殊之处是「只有它目录里有一个 vite.config.ts 可被 vitest 误取」。
实测不止它一个:同形状(有 vite.config.ts、无 vitest 配置、bare vitest run)共 8 个包,
除 runner 外还有 plugin-aiplugin-chatbotplugin-designerplugin-editor
plugin-markdownplugin-reportplugin-tree。是否同红取决于各自的测试是否碰 DOM
(runner 碰了),本 PR 不声称、也不顺手改 —— 它们的整改属于 #3240 的单独批次,已把这份实测清单
评论到 #3240 上,并作为棘轮的 baseline 记在钉子里。

反向验证(方向先判后跑,三条全部与预判一致)

把脚本还原成 "test": "vitest run":

预判实测
包级入口红,同样 4 个 window is not definedTests 4 failed | 9 passed (13) + ReferenceError: window is not defined
钉子红 7 条中的 5 条并点名 runner(另 2 条 —— 形状事实与 guard 纯输入对照 —— 必须仍绿)Tests 5 failed | 2 passed (7),含 expected [ 'packages/runner' ] to deeply equal []
仓根入口保持绿Test Files 3 passed (3) / Tests 13 passed (13)

门禁

与在飞单的关系


Generated by Claude Code

`pnpm --filter @object-ui/runner test` 在干净 main 上 4 红(`window is not
defined`),而同一批 13 个用例从仓根跑全绿。机制:`packages/runner` 声明了
`"test": "vitest run"` 却没有 vitest 配置,pnpm 把 cwd 定在包目录,vitest 于是
把该目录里那份 **Vite 应用**配置 `vite.config.ts` 当成自己的配置读进来 ——
它只有 `resolve.alias` / `build`,没有 `test` 块,于是没有 DOM 环境、没有
setup(那次运行自己的汇总就写着 `setup 0ms ... environment 0ms`),
`App.navigation.test.tsx` 的 4 个用例在 node 环境里撞死在 `window` 上。
修法是把这个入口指回**仓根配置**,而不是给 runner 补一份包级配置:
- 包级配置会是第 18 份「#3240 裁决 A(2026-08-06,全删)」要删的东西,且它解析
`@object-ui/*` 走 runner 自己那张 15 条的 alias 表(其余落 dist),而仓根配置把
~40 个包名指向 `src` —— 正是 #3240 要消灭的「同一文件两套结论」;
- 它还会把这次运行从 `scripts/vitest-invocation-guard.mjs` 底下抬出去(guard 只在
仓根配置里被调用),#3288 的 `--` 转发陷阱对本包就不再被拒绝。
所以脚本改成 AGENTS.md(§怎么跑测试)与 guard 报错正文都指定的那条写法:
`vitest run --root ../.. packages/runner/`。两个入口从此是同一份配置,用例数一致
(3 文件 / 13 用例),`vitest list` 显示 runner 的 3 个文件各自只在一个 project
注册([unit] x2、[dom] x1),仓根 projects 机制不受影响。
钉子 `scripts/__tests__/runner-package-test-entry-3746.test.ts`:钉住该脚本把
vitest root 指回仓根、被 guard 接受、过滤面正好覆盖 runner 自己的全部测试文件;
另有一条全仓棘轮,记录另外 7 个同形状(有 vite.config、无 vitest 配置、bare
`vitest run`)的包为**上限**,只许缩不许长 —— 它们的整改属于 #3240 的单独批次,
本单不碰。
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
Co-authored-by: Claude <noreply@anthropic.com>
@vercel

vercelBot commented Aug 8, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 8, 2026 10:33pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.66KB3.13KB
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.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)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)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)482.39KB106.34KB
core (index.js)2.96KB1.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)139.61KB35.99KB
fields (index.js)230.82KB56.70KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.65KB1.06KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)9.48KB3.27KB
i18n (useObjectLabel.js)27.59KB6.63KB
i18n (useSafeTranslation.js)4.52KB1.96KB
layout (index.js)38.53KB10.71KB
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.71KB3.79KB
plugin-calendar (index.js)44.98KB12.37KB
plugin-charts (index.js)61.04KB17.31KB
plugin-chatbot (index.js)180.33KB42.79KB
plugin-dashboard (index.js)117.21KB30.27KB
plugin-designer (index.js)210.51KB42.51KB
plugin-detail (index.js)236.17KB58.82KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)112.10KB27.10KB
plugin-gantt (index.js)162.55KB39.57KB
plugin-grid (index.js)187.63KB49.66KB
plugin-kanban (index.js)48.30KB13.28KB
plugin-list (index.js)105.12KB25.48KB
plugin-map (index.js)16.81KB5.24KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)40.58KB10.58KB
plugin-timeline (index.js)25.76KB7.33KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.03KB20.55KB
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.71KB1.34KB
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

@yinlianghuiClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ 验收(PM,session session_01GTRjn8xBqp75dk7kFupVRt)—— 形状裁定:采纳 C(已落地),否决派单缺省 A

实物核验:头 9fff15884,仅 2 文件(+254/−1):packages/runner/package.json 一行脚本改 vitest run --root ../.. packages/runner/ + 独立命名钉子;trailer 0;与在飞 #3848 零相交。
CI 终态(独立复核):18 检查全部 completed,16 success + 2 skipped,零失败。

为什么 C 对、缺省 A 错(裁定理由,已在 origin/main 独立取证):

其余要点:前提原样复现(4 红 + environment 0ms 实证配置被劫持);修后两入口 13/13 且 projects 注册面不重不漏(vitest list 为证);反向验证三方向全中、钉子红点名 runner;「runner 唯一可劫持」被证伪(同形状 8 包)→ 以上限棘轮记录而非清单、⛔ 未顺手修别的包;#3240 的增补读数选评论而非 sub-issue(避免 queued 父单的 sub-issue 自动进派发池、绕开维护者「单独空批次」令)—— 判断正确。无 changeset 有 presence 脚本 + 先例双重依据。

转 ready 并挂 auto-merge。


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 8, 2026 22:48
@yinlianghui
yinlianghui added this pull request to the merge queueAug 8, 2026
Merged via the queue into main with commit 39136ccAug 8, 2026
19 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-3746-runner-vitest-config branch August 8, 2026 22:48
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants

@yinlianghui@claude