Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/vitest-config-unification-3240.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
---

Repo tooling only — no published behaviour changes.

objectui#3240 deletes the 17 per-package `vitest.config.ts` files (plus
`examples/schema-catalog`'s) so the root `vitest.config.mts` is the repo's single
test config, and rewrites every package `test` script to reach it by path filter
(`vitest run --root ../.. <pkgdir>/`). The three files this touches under a
released package's `src/` are COMMENTS in test files, each naming a config or
setup file this change deletes; no runtime or type surface moves. The collected
test population is byte-identical before and after — 2459 (project, file) pairs,
diffed programmatically.
44 changes: 29 additions & 15 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,14 @@ pnpm exec vitest run packages/<pkg>/ # 只跑一个包
pnpm test # 全量(CI 就是它,可加 --shard=1/4)
```

AGENTS.md 的「只跑受影响的包」指的是**用上面的路径过滤缩小范围**,不是 `cd` 进包里、也不是
`pnpm --filter <pkg> test` —— 那两条恰好就是下面的陷阱。

- **陷阱一:让 vitest 的 cwd 落在包目录里(objectui#3378)。** `pnpm --filter <pkg> test`、
`turbo run test`、`cd packages/x && pnpm exec vitest` 都属于这类。vitest 把 root 定成该
AGENTS.md 的「只跑受影响的包」指的是**用上面的路径过滤缩小范围**,不是 `cd` 进包里。
`pnpm --filter <pkg> test` 与 `turbo run test` **现在是安全的**(objectui#3240):每个包的
`test` 脚本都改成了显式指回仓根的 `vitest run --root ../.. packages/<pkg>/`,跑的就是仓根
那一份配置、和 CI 同一个结论;它们只是比上面的写法多绕一层。

- **陷阱一:让 vitest 的 cwd 落在包目录里(objectui#3378)。** 今天只剩
`cd packages/x && pnpm exec vitest` 这一种写法(改造前 `pnpm --filter <pkg> test` 和
`turbo run test` 也在其中)。vitest 把 root 定成该
目录,根级 projects(`unit`/`dom`/`dom-heavy`)的 include(`packages/**`、`examples/**`、
`scripts/**`)相对它匹配不到任何文件;只有以**绝对路径**引入的 `apps/console` project 仍解析
成功。于是跑的是 `@object-ui/console` 的 22 个文件、报 `Test Files 22 passed (22)`,而本包
Expand All @@ -189,19 +192,30 @@ AGENTS.md 的「只跑受影响的包」指的是**用上面的路径过滤缩
- **两条现在都会直接失败**,由 `scripts/vitest-invocation-guard.mjs` 拦下:vitest root 不是仓根
→ 拒绝;`--` 后面还有参数 → 拒绝。报错正文会指出机制并给出上面的正确命令。包级 `test` 脚本的
存废是 objectui#3240;在那之前它们只失败,不撒谎。
- **拦截点不止 `vitest.config.mts` 一处**(objectui#5406)。vitest 只加载「启动目录里的那份」
config,所以根 config 顶部那一次调用,只覆盖得到「本包没有 config(向上找到根 config)」或
「本包 config import 了根 config(import 即执行其模块作用域)」这两条路。11 个**独立**的
`packages/plugin-*/vitest.config.ts` 两条都不占——它们自带 `happy-dom` + `globals` + 本地
setup 且**完全没有 alias 表**,于是从包目录跑就用上了一份 CI 从不使用的 config,而 guard
根本没被 import。实测:`cd packages/plugin-grid && pnpm exec vitest run
src/__tests__/ObjectGrid.exportOptionsKeys.test.ts` 曾经报 `Test Files 1 passed (1)` /
`Tests 5 passed (5)` 并以 0 退出。这 11 份现在各自调用 guard;新增任何一份 `vitest.config.*`
若两条路都不占,`scripts/__tests__/vitest-invocation-guard.test.ts` 会红。
- **拦截点不止 `vitest.config.mts` 一处**(objectui#5406 / objectui#3240)。vitest 只加载
「启动目录里的那份」config,所以根 config 顶部那一次调用,只覆盖得到「本目录没有任何
config(向上找到根 config)」或「本目录 config import 了根 config」这两条路。
- #5406 关的是第三条:11 个**独立**的 `packages/plugin-*/vitest.config.ts` 两条都不占,
它们自带 `happy-dom` + `globals` + 本地 setup 且**完全没有 alias 表**,于是从包目录跑
就用上了一份 CI 从不使用的 config。
- ⭐ #3240 删掉那 17 份包级 config 之前,必须先关**第四条**:vitest 的回退不止于
`vitest.config.*` —— 没有它就用同目录的 `vite.config.*`,而 `packages/*` 每个都有一份。
实测 `cd packages/plugin-ai && pnpm exec vitest run` 报 `RUN v4.1.10 /…/packages/plugin-ai`,
guard 一声不吭。所以每份 `packages/<pkg>/vite.config.ts` 现在也调用 guard,调用**以
`process.env.VITEST` 为门**(实测:vitest 读 config 时它是 `"true"`,`vite build` 读同
一个文件时是 `undefined`)—— 测试跑被拒,构建永远不被拒;那些 `vite.config` 里残留的
`test` 块(`passWithNoTests: true` + 一份根 config 从不加载的 setup)也一并删掉了。
新增任何一份 `vitest.config.*` / `packages/*/vite.config.*` 若不占其中一条路,
`scripts/__tests__/vitest-invocation-guard.test.ts` 会红。
- **路径过滤零匹配也不再是绿的**:一旦命令行点名了文件,`passWithNoTests` 自动关闭 ——
写错的路径 / 相对错目录的路径 → 非零退出,而不是「跑了 0 个文件然后绿」。
- 确需从包目录启动,把 root 显式指回仓根:`pnpm exec vitest run --root ../.. packages/<pkg>/`。
- 确需从包目录启动,把 root 显式指回仓根:`pnpm exec vitest run --root ../.. packages/<pkg>/`
—— 这正是 objectui#3240 给每个包级 `test` 脚本定下的写法。
真要临时绕过 guard(自担风险):`OBJECTUI_VITEST_GUARD=off`。
- **包级测试配置只有一份,就是仓根那份。** objectui#3240 删掉了 17 个包 +
`examples/schema-catalog` 的 `vitest.config.*`(维护者 2026-08-06 裁决 A);某个包确实需要
不同的 environment / setup / include,就在 `vitest.config.mts` 的 `projects` 里**加一个
project**,不要在包里新开一份 config —— 一份 config 一个结论,是这条裁决的全部内容。

### 测试纪律(flaky 测试:先找竞态,别调超时)

Expand Down
17 changes: 10 additions & 7 deletions QUICK_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@ pnpm exec vitest run apps/console/ # Run just the console te
pnpm test:e2e # End-to-end tests (playwright)
```

Not `pnpm --filter <pkg> test`, not `turbo run test`, not `cd packages/x && pnpm exec
vitest`, and never a path behind `--`. Each of those moved vitest's root into a package,
`pnpm --filter <pkg> test` and `turbo run test` are fine — objectui#3240 rewrote every
package's `test` script to name the repo root (`vitest run --root ../.. packages/<pkg>/`),
so they run the same single config CI does. What is still wrong is `cd packages/x && pnpm
exec vitest`, and a path behind `--`. Each of those moves vitest's root into a package,
where the root `unit`/`dom`/`dom-heavy` projects match nothing and only `apps/console`
resolves: 22 foreign files passed, your package never ran, output green
(objectui#3378/#3288). A guard now **exits non-zero** on all of them and prints the
correct invocation. It is wired into `vitest.config.mts` *and* into each of the 11
standalone per-package configs (`packages/plugin-grid/vitest.config.ts` and its ten
siblings), because Vitest loads the config in the directory it was launched from — the
root call alone left those 11 uncovered (objectui#5406):
(objectui#3378/#3288). A guard **exits non-zero** on them and prints the correct
invocation. Vitest loads the config in the directory it was launched from, so the guard is
wired into every file that can be picked up as one: `vitest.config.mts` (the repo's only
vitest config since objectui#3240), `apps/console/vitest.config.ts`, and each
`packages/<pkg>/vite.config.ts` — the build config Vitest falls back to now that no
package carries a vitest config of its own (objectui#5406 / #3240):

```
vitest 调用被拒绝:从包目录跑 vitest 会静默跑错测试集 (objectui#3378)
Expand Down
6 changes: 3 additions & 3 deletions apps/console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@
"lint": "eslint .",
"build:analyze": "pnpm build && echo 'Bundle analysis available at dist/stats.html'",
"preview": "vite preview",
"test": "vitest run",
"test:watch": "vitest",
"test:ui": "vitest --ui",
"test": "vitest run --root ../.. apps/console/",
"test:watch": "vitest --root ../.. apps/console/",
"test:ui": "vitest --ui --root ../.. apps/console/",
"prepublishOnly": "pnpm build"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion examples/schema-catalog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"clean": "rm -rf dist",
"regenerate": "python3 ../../scripts/regenerate-catalog-index.py",
"regenerate:check": "python3 ../../scripts/regenerate-catalog-index.py --check",
"test": "vitest run",
"test": "vitest run --root ../.. examples/schema-catalog/",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
"lint": "eslint ."
},
Expand Down
14 changes: 0 additions & 14 deletions examples/schema-catalog/vitest.config.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/app-shell/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
},
"scripts": {
"build": "tsc && node ../../scripts/check-dist-completeness.mjs",
"test": "vitest run",
"test": "vitest run --root ../.. packages/app-shell/",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
"lint": "eslint ."
},
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"scripts": {
"build": "tsc && node ../../scripts/check-dist-completeness.mjs",
"clean": "rm -rf dist tsconfig.tsbuildinfo",
"test": "vitest run",
"test": "vitest run --root ../.. packages/auth/",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
"lint": "eslint ."
},
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"build": "tsup",
"dev": "tsup --watch",
"lint": "eslint src",
"test": "vitest run",
"test": "vitest run --root ../.. packages/cli/",
"type-check": "tsc --noEmit"
},
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion packages/collaboration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"scripts": {
"build": "tsc && node ../../scripts/check-dist-completeness.mjs",
"clean": "rm -rf dist tsconfig.tsbuildinfo",
"test": "vitest run",
"test": "vitest run --root ../.. packages/collaboration/",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
"lint": "eslint ."
},
Expand Down
2 changes: 1 addition & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"build": "vite build && node scripts/build-css.mjs",
"prebuild": "pnpm --filter @object-ui/types build && pnpm --filter @object-ui/core build && pnpm --filter @object-ui/react build",
"pretest": "pnpm run prebuild",
"test": "vitest run",
"test": "vitest run --root ../.. packages/components/",
"test:dist": "OBJECTUI_DIST_PINS=1 vitest run --root ../.. --config vitest.config.mts --project dist",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
"lint": "eslint ."
Expand Down
26 changes: 16 additions & 10 deletions packages/components/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ import { resolve } from 'path';
import { createDtsExplicitExtensions } from '../../scripts/vite-dts-explicit-extensions.ts';
import { createDtsFailOnTypeErrors } from '../../scripts/vite-dts-fail-on-type-errors.ts';

// objectui#3240 — with the per-package `vitest.config.*` files gone, Vitest
// launched from THIS directory falls back to this file and uses it as its test
// config. Guard that entry point (route 4 in
// scripts/vitest-invocation-guard.mjs, which carries the mechanism and the
// measurement). Gated on `VITEST` because the same file is the BUILD config:
// Vitest sets that variable when it loads a config, `vite build` does not, so
// a test run is refused and a build never is.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

if (process.env.VITEST) {
assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });
}

export default defineConfig({
plugins: [
react(),
Expand Down Expand Up @@ -80,14 +96,4 @@ export default defineConfig({
},
},
},
test: {
globals: true,
environment: 'happy-dom',
setupFiles: ['../../vitest.setup.tsx'],
passWithNoTests: true,
// Ensure dependencies are resolved properly for tests
deps: {
inline: ['@object-ui/core', '@object-ui/react'],
},
},
});
5 changes: 0 additions & 5 deletions packages/components/vitest.config.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
"scripts": {
"build": "tsc && node ../../scripts/check-dist-completeness.mjs",
"test": "vitest run",
"test": "vitest run --root ../.. packages/core/",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
"lint": "eslint ."
},
Expand Down
2 changes: 0 additions & 2 deletions packages/core/vitest.config.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/create-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"build": "tsup",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
"dev": "tsup --watch",
"test": "vitest run",
"test": "vitest run --root ../.. packages/create-plugin/",
"lint": "eslint ."
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/data-objectstack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dev": "tsup --watch",
"clean": "rm -rf dist",
"type-check": "tsc --noEmit",
"test": "vitest run",
"test": "vitest run --root ../.. packages/data-objectstack/",
"lint": "eslint ."
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/fields/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"build": "tsc && vite build && node scripts/build-css.mjs",
"clean": "rm -rf dist",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
"test": "vitest run",
"test": "vitest run --root ../.. packages/fields/",
"lint": "eslint ."
},
"dependencies": {
Expand Down
22 changes: 16 additions & 6 deletions packages/fields/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ import path from 'path';

import { createDtsExplicitExtensions } from '../../scripts/vite-dts-explicit-extensions.ts';

// objectui#3240 — with the per-package `vitest.config.*` files gone, Vitest
// launched from THIS directory falls back to this file and uses it as its test
// config. Guard that entry point (route 4 in
// scripts/vitest-invocation-guard.mjs, which carries the mechanism and the
// measurement). Gated on `VITEST` because the same file is the BUILD config:
// Vitest sets that variable when it loads a config, `vite build` does not, so
// a test run is refused and a build never is.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

if (process.env.VITEST) {
assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });
}

export default defineConfig({
plugins: [
react(),
Expand Down Expand Up @@ -64,10 +80,4 @@ export default defineConfig({
},
},
},
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['../../vitest.setup.tsx'],
passWithNoTests: true,
},
});
5 changes: 0 additions & 5 deletions packages/fields/vitest.config.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/i18n/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
},
"scripts": {
"build": "tsc && node ../../scripts/check-dist-completeness.mjs",
"test": "vitest run",
"test": "vitest run --root ../.. packages/i18n/",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
"lint": "eslint ."
},
Expand Down
19 changes: 16 additions & 3 deletions packages/layout/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ import { resolve } from 'path';
import { createDtsExplicitExtensions } from '../../scripts/vite-dts-explicit-extensions.ts';
import { createDtsFailOnTypeErrors } from '../../scripts/vite-dts-fail-on-type-errors.ts';

// objectui#3240 — with the per-package `vitest.config.*` files gone, Vitest
// launched from THIS directory falls back to this file and uses it as its test
// config. Guard that entry point (route 4 in
// scripts/vitest-invocation-guard.mjs, which carries the mechanism and the
// measurement). Gated on `VITEST` because the same file is the BUILD config:
// Vitest sets that variable when it loads a config, `vite build` does not, so
// a test run is refused and a build never is.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

if (process.env.VITEST) {
assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });
}

export default defineConfig({
plugins: [
react(),
Expand Down Expand Up @@ -50,7 +66,4 @@ export default defineConfig({
},
},
},
test: {
passWithNoTests: true,
},
});
2 changes: 1 addition & 1 deletion packages/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"scripts": {
"build": "tsc && node ../../scripts/check-dist-completeness.mjs",
"clean": "rm -rf dist tsconfig.tsbuildinfo",
"test": "vitest run",
"test": "vitest run --root ../.. packages/mobile/",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
"lint": "eslint ."
},
Expand Down
2 changes: 1 addition & 1 deletion packages/permissions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"scripts": {
"build": "tsc && node ../../scripts/check-dist-completeness.mjs",
"clean": "rm -rf dist tsconfig.tsbuildinfo",
"test": "vitest run",
"test": "vitest run --root ../.. packages/permissions/",
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
"lint": "eslint ."
},
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-ai/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"build": "vite build",
"type-check": "tsc --noEmit",
"clean": "rm -rf dist",
"test": "vitest run --passWithNoTests",
"test": "vitest run --passWithNoTests --root ../.. packages/plugin-ai/",
"lint": "eslint ."
},
"peerDependencies": {
Expand Down
Loading
Loading