























@@ -9,6 +9,7 @@ const {
99 buildVitestArgs,
1010 buildVitestRunPlans,
1111 createVitestRunSpecs,
12+ findUnmatchedExplicitTestTargets,
1213 parseTestProjectsArgs,
1314 resolveChangedTargetArgs,
1415 resolveChangedTestTargetPlan,
@@ -62,6 +63,14 @@ const {
6263pnpmArgs: string[];
6364watchMode: boolean;
6465}>;
66+findUnmatchedExplicitTestTargets: (
67+args: string[],
68+cwd?: string,
69+) => Array<{
70+target: string;
71+reason: "glob-matched-no-files" | "path-does-not-exist" | "target-matched-no-test-files";
72+includePattern?: string;
73+}>;
6574parseTestProjectsArgs: (
6675args: string[],
6776cwd?: string,
@@ -995,6 +1004,64 @@ describe("test-projects args", () => {
9951004expect(spec?.env.OPENCLAW_VITEST_INCLUDE_FILE).toBe(spec?.includeFilePath);
9961005});
99710061007+it("rejects explicit test file targets that do not exist", () => {
1008+expect(findUnmatchedExplicitTestTargets(["src/not-a-real-openclaw-test.test.ts"])).toEqual([
1009+{
1010+target: "src/not-a-real-openclaw-test.test.ts",
1011+reason: "path-does-not-exist",
1012+},
1013+]);
1014+});
1015+1016+it("rejects explicit globs that match no files", () => {
1017+expect(findUnmatchedExplicitTestTargets(["src/**/not-a-real-openclaw-test.test.ts"])).toEqual([
1018+{
1019+target: "src/**/not-a-real-openclaw-test.test.ts",
1020+reason: "glob-matched-no-files",
1021+},
1022+]);
1023+});
1024+1025+it("rejects explicit non-test file targets with no sibling tests", () => {
1026+const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-test-targets-"));
1027+try {
1028+fs.mkdirSync(path.join(tempDir, "src", "lonely"), { recursive: true });
1029+fs.writeFileSync(path.join(tempDir, "src", "lonely", "runtime.ts"), "export {};\n");
1030+1031+expect(findUnmatchedExplicitTestTargets(["src/lonely/runtime.ts"], tempDir)).toEqual([
1032+{
1033+target: "src/lonely/runtime.ts",
1034+reason: "target-matched-no-test-files",
1035+includePattern: "src/lonely/**/*.test.ts",
1036+},
1037+]);
1038+} finally {
1039+fs.rmSync(tempDir, { recursive: true, force: true });
1040+}
1041+});
1042+1043+it("accepts explicit untracked test files that exist on disk", () => {
1044+const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-test-targets-"));
1045+try {
1046+fs.mkdirSync(path.join(tempDir, "src"), { recursive: true });
1047+fs.writeFileSync(path.join(tempDir, "src", "new.test.ts"), "test('new', () => {});\n");
1048+1049+expect(findUnmatchedExplicitTestTargets(["src/new.test.ts"], tempDir)).toEqual([]);
1050+} finally {
1051+fs.rmSync(tempDir, { recursive: true, force: true });
1052+}
1053+});
1054+1055+it("accepts explicit Vitest config targets routed as whole config runs", () => {
1056+expect(
1057+findUnmatchedExplicitTestTargets(["test/vitest/vitest.contracts-channel-surface.config.ts"]),
1058+).toEqual([]);
1059+});
1060+1061+it("accepts sentinel targets routed as whole config runs", () => {
1062+expect(findUnmatchedExplicitTestTargets(["ui/src/test-helpers/control-ui-e2e.ts"])).toEqual([]);
1063+});
1064+9981065it("skips channel contract configs with no matching external include patterns", () => {
9991066const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-contract-include-"));
10001067try {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。