diff --git a/scripts/test-extension.mjs b/scripts/test-extension.mjs index 6442556c778..4d9f7a9575e 100644 --- a/scripts/test-extension.mjs +++ b/scripts/test-extension.mjs @@ -185,11 +185,25 @@ function printUsage() { console.error( " node scripts/test-extension.mjs --list-changed --base [--head ]", ); + console.error(" node scripts/test-extension.mjs --require-tests"); +} + +function printNoTestsMessage(plan, requireTests) { + const message = `No tests found for ${plan.extensionDir}. Run "pnpm test:extension ${plan.extensionId} -- --dry-run" to inspect the resolved roots.`; + if (requireTests) { + console.error(message); + return 1; + } + console.log(`[test-extension] ${message} Skipping.`); + return 0; } async function run() { const rawArgs = process.argv.slice(2); const dryRun = rawArgs.includes("--dry-run"); + const requireTests = + rawArgs.includes("--require-tests") || + process.env.OPENCLAW_TEST_EXTENSION_REQUIRE_TESTS === "1"; const json = rawArgs.includes("--json"); const list = rawArgs.includes("--list"); const listChanged = rawArgs.includes("--list-changed"); @@ -197,6 +211,7 @@ async function run() { (arg) => arg !== "--" && arg !== "--dry-run" && + arg !== "--require-tests" && arg !== "--json" && arg !== "--list" && arg !== "--list-changed", @@ -271,13 +286,6 @@ async function run() { process.exit(1); } - if (plan.testFiles.length === 0) { - console.error( - `No tests found for ${plan.extensionDir}. Run "pnpm test:extension ${plan.extensionId} -- --dry-run" to inspect the resolved roots.`, - ); - process.exit(1); - } - if (dryRun) { if (json) { process.stdout.write(`${JSON.stringify(plan, null, 2)}\n`); @@ -290,6 +298,10 @@ async function run() { return; } + if (plan.testFiles.length === 0) { + process.exit(printNoTestsMessage(plan, requireTests)); + } + console.log( `[test-extension] Running ${plan.testFiles.length} test files for ${plan.extensionId} with ${plan.config}`, ); diff --git a/test/scripts/test-extension.test.ts b/test/scripts/test-extension.test.ts index 8919130c19a..06ba5343844 100644 --- a/test/scripts/test-extension.test.ts +++ b/test/scripts/test-extension.test.ts @@ -17,6 +17,13 @@ function readPlan(args: string[], cwd = process.cwd()) { return JSON.parse(stdout) as ReturnType; } +function runScript(args: string[], cwd = process.cwd()) { + return execFileSync(process.execPath, [scriptPath, ...args], { + cwd, + encoding: "utf8", + }); +} + describe("scripts/test-extension.mjs", () => { it("resolves channel-root extensions onto the channel vitest config", () => { const plan = resolveExtensionTestPlan({ targetArg: "slack", cwd: process.cwd() }); @@ -72,4 +79,18 @@ describe("scripts/test-extension.mjs", () => { [...extensionIds].toSorted((left, right) => left.localeCompare(right)), ); }); + + it("dry-run still reports a plan for extensions without tests", () => { + const plan = readPlan(["copilot-proxy"]); + + expect(plan.extensionId).toBe("copilot-proxy"); + expect(plan.testFiles).toEqual([]); + }); + + it("treats extensions without tests as a no-op by default", () => { + const stdout = runScript(["copilot-proxy"]); + + expect(stdout).toContain("No tests found for extensions/copilot-proxy."); + expect(stdout).toContain("Skipping."); + }); });