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
31 changes: 31 additions & 0 deletions .changeset/vitest-invocation-guard-package-cwd-coverage-5406.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
---
---

Test tooling only — this publishes nothing, declared explicitly with an empty frontmatter
rather than left undeclared.

`assertCanonicalVitestInvocation` refuses a package-cwd Vitest run because such a run uses
a different config than CI does, so its green says nothing about CI. Its docstring claimed
that "every per-package `vitest.config.ts` re-exports the root config, and a package
without one resolves upward to it, so no package-level path skips this file". Measured, by
running `pnpm exec vitest run` from every directory that carries a config: 8 package
configs import the root config and were refused, 2 packages carry no config and resolve
upward and were refused, and **11 standalone configs never mention the root file at all**
(`plugin-calendar`, `-charts`, `-detail`, `-form`, `-gantt`, `-grid`, `-kanban`, `-list`,
`-map`, `-timeline`, `-view`) — nothing imported the guard from there, so it never ran.
From `packages/plugin-grid`, one such run printed `Test Files 1 passed (1)` /
`Tests 5 passed (5)` and exited 0, under a config carrying no `@object-ui/*` alias table
at all where the root config maps ~40 specifiers at sibling `src/`. The guard's hole sat
exactly where the divergence — and therefore the false-green risk — was largest.

Those 11 configs now call the guard themselves, through a new
`repoRootFrom(import.meta.url)` landmark search rather than a hand-counted `../..` (which
resolves to a real directory when the count is wrong, so the guard would keep issuing
verdicts computed against the wrong root). The docstring and the root config's call-site
comment now describe the three routes a config can take instead of asserting one of them,
and the claim is enforced rather than restated: the guard's own test walks every
`vitest.config.*` in the repo and fails on any that takes neither route.

No package `src/` is touched and the configs' test semantics are unchanged, so no
`@object-ui/*` package changes behaviour and there is nothing here for a consumer to
upgrade to.
15 changes: 12 additions & 3 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -186,9 +186,18 @@ AGENTS.md 的「只跑受影响的包」指的是**用上面的路径过滤缩
pnpm 把 `--` **原样**转发进脚本,vitest 的 CLI 解析在 `--` 处停止,后面的一切(包括你的路径)
在 vitest 看到之前就没了 —— 不是「被忽略并警告」,是压根不存在。于是退回默认集合(叠加陷阱一
就是别人的包),新加的测试文件零执行、输出全绿。
- **两条现在都会直接失败**,由 `scripts/vitest-invocation-guard.mjs` 在 `vitest.config.mts` 顶部
拦下:vitest root 不是仓根 → 拒绝;`--` 后面还有参数 → 拒绝。报错正文会指出机制并给出上面的
正确命令。包级 `test` 脚本的存废是 objectui#3240;在那之前它们只失败,不撒谎。
- **两条现在都会直接失败**,由 `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` 会红。
- **路径过滤零匹配也不再是绿的**:一旦命令行点名了文件,`passWithNoTests` 自动关闭 ——
写错的路径 / 相对错目录的路径 → 非零退出,而不是「跑了 0 个文件然后绿」。
- 确需从包目录启动,把 root 显式指回仓根:`pnpm exec vitest run --root ../.. packages/<pkg>/`。
Expand Down
7 changes: 5 additions & 2 deletions QUICK_REFERENCE.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,8 +36,11 @@ Not `pnpm --filter <pkg> test`, not `turbo run test`, not `cd packages/x && pnpm
vitest`, and never a path behind `--`. Each of those moved 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 in `vitest.config.mts` now **exits non-zero** on all of
them and prints the correct invocation:
(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):

```
vitest 调用被拒绝:从包目录跑 vitest 会静默跑错测试集 (objectui#3378)
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-calendar/vitest.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,28 @@ import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

// objectui#5406 — this config is STANDALONE: it does not import the root
// `vitest.config.mts`, so nothing routed a package-cwd run here through the
// invocation guard, the way a root-importing config does as a side effect of
// the import. Without the call below, `cd` into this package and `pnpm exec
// vitest run` executes the suite under a config CI never uses — no
// `@object-ui/*` alias table, a different setup file, no project split — so
// its green says nothing about CI. (Measured on the sibling `plugin-grid`,
// whose config was identical to this one: one such run printed
// `Test Files 1 passed (1)` / `Tests 5 passed (5)` and exited 0.)
//
// Deleting this reopens the hole for this package alone, silently — which is
// why it is enforced rather than written down:
// `scripts/__tests__/vitest-invocation-guard.test.ts` walks every
// `vitest.config.*` in the repo and fails on any that neither imports the root
// config nor calls the guard.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });

export default defineConfig({
plugins: [react()],
test: {
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-charts/vitest.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,28 @@ import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

// objectui#5406 — this config is STANDALONE: it does not import the root
// `vitest.config.mts`, so nothing routed a package-cwd run here through the
// invocation guard, the way a root-importing config does as a side effect of
// the import. Without the call below, `cd` into this package and `pnpm exec
// vitest run` executes the suite under a config CI never uses — no
// `@object-ui/*` alias table, a different setup file, no project split — so
// its green says nothing about CI. (Measured on the sibling `plugin-grid`,
// whose config was identical to this one: one such run printed
// `Test Files 1 passed (1)` / `Tests 5 passed (5)` and exited 0.)
//
// Deleting this reopens the hole for this package alone, silently — which is
// why it is enforced rather than written down:
// `scripts/__tests__/vitest-invocation-guard.test.ts` walks every
// `vitest.config.*` in the repo and fails on any that neither imports the root
// config nor calls the guard.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });

export default defineConfig({
plugins: [react()],
test: {
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-detail/vitest.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,28 @@ import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

// objectui#5406 — this config is STANDALONE: it does not import the root
// `vitest.config.mts`, so nothing routed a package-cwd run here through the
// invocation guard, the way a root-importing config does as a side effect of
// the import. Without the call below, `cd` into this package and `pnpm exec
// vitest run` executes the suite under a config CI never uses — no
// `@object-ui/*` alias table, a different setup file, no project split — so
// its green says nothing about CI. (Measured on the sibling `plugin-grid`,
// whose config was identical to this one: one such run printed
// `Test Files 1 passed (1)` / `Tests 5 passed (5)` and exited 0.)
//
// Deleting this reopens the hole for this package alone, silently — which is
// why it is enforced rather than written down:
// `scripts/__tests__/vitest-invocation-guard.test.ts` walks every
// `vitest.config.*` in the repo and fails on any that neither imports the root
// config nor calls the guard.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });

export default defineConfig({
plugins: [react()],
test: {
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-form/vitest.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,28 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

// objectui#5406 — this config is STANDALONE: it does not import the root
// `vitest.config.mts`, so nothing routed a package-cwd run here through the
// invocation guard, the way a root-importing config does as a side effect of
// the import. Without the call below, `cd` into this package and `pnpm exec
// vitest run` executes the suite under a config CI never uses — no
// `@object-ui/*` alias table, a different setup file, no project split — so
// its green says nothing about CI. (Measured on the sibling `plugin-grid`,
// whose config was identical to this one: one such run printed
// `Test Files 1 passed (1)` / `Tests 5 passed (5)` and exited 0.)
//
// Deleting this reopens the hole for this package alone, silently — which is
// why it is enforced rather than written down:
// `scripts/__tests__/vitest-invocation-guard.test.ts` walks every
// `vitest.config.*` in the repo and fails on any that neither imports the root
// config nor calls the guard.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });

export default defineConfig({
plugins: [react()],
test: {
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-gantt/vitest.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,28 @@ import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

// objectui#5406 — this config is STANDALONE: it does not import the root
// `vitest.config.mts`, so nothing routed a package-cwd run here through the
// invocation guard, the way a root-importing config does as a side effect of
// the import. Without the call below, `cd` into this package and `pnpm exec
// vitest run` executes the suite under a config CI never uses — no
// `@object-ui/*` alias table, a different setup file, no project split — so
// its green says nothing about CI. (Measured on the sibling `plugin-grid`,
// whose config was identical to this one: one such run printed
// `Test Files 1 passed (1)` / `Tests 5 passed (5)` and exited 0.)
//
// Deleting this reopens the hole for this package alone, silently — which is
// why it is enforced rather than written down:
// `scripts/__tests__/vitest-invocation-guard.test.ts` walks every
// `vitest.config.*` in the repo and fails on any that neither imports the root
// config nor calls the guard.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });

export default defineConfig({
plugins: [react()],
test: {
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-grid/vitest.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,28 @@ import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

// objectui#5406 — this config is STANDALONE: it does not import the root
// `vitest.config.mts`, so nothing routed a package-cwd run here through the
// invocation guard, the way a root-importing config does as a side effect of
// the import. Without the call below, `cd` into this package and `pnpm exec
// vitest run` executes the suite under a config CI never uses — no
// `@object-ui/*` alias table, a different setup file, no project split — so
// its green says nothing about CI. (Measured on the sibling `plugin-grid`,
// whose config was identical to this one: one such run printed
// `Test Files 1 passed (1)` / `Tests 5 passed (5)` and exited 0.)
//
// Deleting this reopens the hole for this package alone, silently — which is
// why it is enforced rather than written down:
// `scripts/__tests__/vitest-invocation-guard.test.ts` walks every
// `vitest.config.*` in the repo and fails on any that neither imports the root
// config nor calls the guard.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });

export default defineConfig({
plugins: [react()],
test: {
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-kanban/vitest.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,28 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

// objectui#5406 — this config is STANDALONE: it does not import the root
// `vitest.config.mts`, so nothing routed a package-cwd run here through the
// invocation guard, the way a root-importing config does as a side effect of
// the import. Without the call below, `cd` into this package and `pnpm exec
// vitest run` executes the suite under a config CI never uses — no
// `@object-ui/*` alias table, a different setup file, no project split — so
// its green says nothing about CI. (Measured on the sibling `plugin-grid`,
// whose config was identical to this one: one such run printed
// `Test Files 1 passed (1)` / `Tests 5 passed (5)` and exited 0.)
//
// Deleting this reopens the hole for this package alone, silently — which is
// why it is enforced rather than written down:
// `scripts/__tests__/vitest-invocation-guard.test.ts` walks every
// `vitest.config.*` in the repo and fails on any that neither imports the root
// config nor calls the guard.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });

export default defineConfig({
plugins: [react()],
test: {
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-list/vitest.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,28 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

// objectui#5406 — this config is STANDALONE: it does not import the root
// `vitest.config.mts`, so nothing routed a package-cwd run here through the
// invocation guard, the way a root-importing config does as a side effect of
// the import. Without the call below, `cd` into this package and `pnpm exec
// vitest run` executes the suite under a config CI never uses — no
// `@object-ui/*` alias table, a different setup file, no project split — so
// its green says nothing about CI. (Measured on the sibling `plugin-grid`,
// whose config was identical to this one: one such run printed
// `Test Files 1 passed (1)` / `Tests 5 passed (5)` and exited 0.)
//
// Deleting this reopens the hole for this package alone, silently — which is
// why it is enforced rather than written down:
// `scripts/__tests__/vitest-invocation-guard.test.ts` walks every
// `vitest.config.*` in the repo and fails on any that neither imports the root
// config nor calls the guard.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });

export default defineConfig({
plugins: [react()],
test: {
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-map/vitest.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,28 @@ import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

// objectui#5406 — this config is STANDALONE: it does not import the root
// `vitest.config.mts`, so nothing routed a package-cwd run here through the
// invocation guard, the way a root-importing config does as a side effect of
// the import. Without the call below, `cd` into this package and `pnpm exec
// vitest run` executes the suite under a config CI never uses — no
// `@object-ui/*` alias table, a different setup file, no project split — so
// its green says nothing about CI. (Measured on the sibling `plugin-grid`,
// whose config was identical to this one: one such run printed
// `Test Files 1 passed (1)` / `Tests 5 passed (5)` and exited 0.)
//
// Deleting this reopens the hole for this package alone, silently — which is
// why it is enforced rather than written down:
// `scripts/__tests__/vitest-invocation-guard.test.ts` walks every
// `vitest.config.*` in the repo and fails on any that neither imports the root
// config nor calls the guard.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });

export default defineConfig({
plugins: [react()],
test: {
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-timeline/vitest.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,28 @@ import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

// objectui#5406 — this config is STANDALONE: it does not import the root
// `vitest.config.mts`, so nothing routed a package-cwd run here through the
// invocation guard, the way a root-importing config does as a side effect of
// the import. Without the call below, `cd` into this package and `pnpm exec
// vitest run` executes the suite under a config CI never uses — no
// `@object-ui/*` alias table, a different setup file, no project split — so
// its green says nothing about CI. (Measured on the sibling `plugin-grid`,
// whose config was identical to this one: one such run printed
// `Test Files 1 passed (1)` / `Tests 5 passed (5)` and exited 0.)
//
// Deleting this reopens the hole for this package alone, silently — which is
// why it is enforced rather than written down:
// `scripts/__tests__/vitest-invocation-guard.test.ts` walks every
// `vitest.config.*` in the repo and fails on any that neither imports the root
// config nor calls the guard.
import {
assertCanonicalVitestInvocation,
repoRootFrom,
} from '../../scripts/vitest-invocation-guard.mjs';

assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) });

export default defineConfig({
plugins: [react()],
test: {
Expand Down
Loading
Loading