From dfde0ce1a6151d725d993aface2a5cdfb6d048bc Mon Sep 17 00:00:00 2001 From: Shakker Date: Thu, 4 Jun 2026 19:35:00 +0100 Subject: [PATCH] test: explain skipped changed vitest targets --- scripts/test-projects.mjs | 23 +++++++++++++++++- test/scripts/test-projects.test.ts | 38 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/scripts/test-projects.mjs b/scripts/test-projects.mjs index 2f3a5ee1c0ee..fe9ed21fb70d 100644 --- a/scripts/test-projects.mjs +++ b/scripts/test-projects.mjs @@ -30,6 +30,7 @@ import { orderFullSuiteSpecsForParallelRun, parseTestProjectsArgs, resolveParallelFullSuiteConcurrency, + resolveChangedTestTargetPlanForArgs, resolveChangedTargetArgs, shouldAcquireLocalHeavyCheckLock, shouldRetryVitestNoOutputTimeout, @@ -169,6 +170,26 @@ function isFullExtensionsProjectRun(specs) { ); } +function printNoChangedTestTargets(args, cwd, baseEnv) { + const plan = resolveChangedTestTargetPlanForArgs(args, cwd, undefined, { env: baseEnv }); + const skippedBroadFallbackPaths = plan?.skippedBroadFallbackPaths ?? []; + if (skippedBroadFallbackPaths.length === 0) { + console.error("[test] no changed test targets; skipping Vitest."); + return; + } + + console.error("[test] no precise changed test targets; skipping Vitest."); + console.error( + `[test] ${skippedBroadFallbackPaths.length} changed path${ + skippedBroadFallbackPaths.length === 1 ? "" : "s" + } require broad Vitest fallback:`, + ); + for (const changedPath of skippedBroadFallbackPaths) { + console.error(`[test] ${changedPath}`); + } + console.error("[test] run `OPENCLAW_TEST_CHANGED_BROAD=1 pnpm test:changed` for broad coverage."); +} + async function runVitestSpecsParallel(specs, concurrency) { let nextIndex = 0; let exitCode = 0; @@ -263,7 +284,7 @@ async function main() { ); if (runSpecs.length === 0) { - console.error("[test] no changed test targets; skipping Vitest."); + printNoChangedTestTargets(args, process.cwd(), baseEnv); printTestSummary("skipped", 0, performance.now() - suiteStartedAt); return; } diff --git a/test/scripts/test-projects.test.ts b/test/scripts/test-projects.test.ts index 72eb9a451787..2437cc869e13 100644 --- a/test/scripts/test-projects.test.ts +++ b/test/scripts/test-projects.test.ts @@ -158,6 +158,21 @@ function withTinyGitRepo(files: Record, test: (cwd: string) => v } } +function commitTinyGitRepo(cwd: string): void { + const commit = spawnSync("git", ["commit", "-m", "initial"], { + cwd, + env: { + ...process.env, + GIT_AUTHOR_EMAIL: "test@example.com", + GIT_AUTHOR_NAME: "OpenClaw Test", + GIT_COMMITTER_EMAIL: "test@example.com", + GIT_COMMITTER_NAME: "OpenClaw Test", + }, + stdio: "ignore", + }); + expect(commit.status).toBe(0); +} + function withTinyFileTree(files: Record, test: (cwd: string) => void): void { const cwd = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-test-projects-")); try { @@ -1162,6 +1177,29 @@ describe("scripts/test-projects changed-target routing", () => { }); }); + it("explains changed paths that need explicit broad fallback before skipping", () => { + withTinyGitRepo({ "package.json": '{"scripts":{}}\n' }, (cwd) => { + commitTinyGitRepo(cwd); + fs.writeFileSync(path.join(cwd, "package.json"), '{"scripts":{"test":"node"}}\n'); + + const result = spawnSync( + process.execPath, + [path.resolve(process.cwd(), "scripts/test-projects.mjs"), "--changed", "HEAD"], + { + cwd, + encoding: "utf8", + }, + ); + + expect(result.status).toBe(0); + expect(result.stderr).toContain("[test] no precise changed test targets; skipping Vitest."); + expect(result.stderr).toContain("[test] package.json"); + expect(result.stderr).toContain( + "[test] run `OPENCLAW_TEST_CHANGED_BROAD=1 pnpm test:changed` for broad coverage.", + ); + }); + }); + it("keeps the broad changed run available for unknown root surfaces", () => { expect( resolveChangedTargetArgs(