diff --git a/src/features/run/javaModule.test.ts b/src/features/run/javaModule.test.ts index d0193958..e5b270f6 100644 --- a/src/features/run/javaModule.test.ts +++ b/src/features/run/javaModule.test.ts @@ -87,6 +87,44 @@ it("finds the source module for an old saved configuration without a module", as expect(ipc.runStart).toHaveBeenCalledWith(expect.objectContaining({ cwd: "/outside/modules" }), expect.anything()); }); +it.each([["-cp", "lib/*"], []])("colours Spring Boot runs without classpath resolution (%j)", async (...classpath) => { + const args = ["-Xmx256m", ...classpath, "a.Main", "--spring.profiles.active=dev"]; + const boot = { ...config, kind: "spring-boot", args }; + const session = await startRun(boot); + expect(session?.status).toBe("running"); + expect(ipc.runStart).toHaveBeenCalledWith( + expect.objectContaining({ args: ["-Dspring.output.ansi.enabled=always", ...args] }), + expect.anything(), + ); + expect(ipc.javaResolveLaunch).not.toHaveBeenCalled(); + expect(boot.args).toBe(args); + expect(args).not.toContain("-Dspring.output.ansi.enabled=always"); +}); + +it.each(["never", "detect", "always"])("preserves an explicit Spring Boot console setting (%s)", async (value) => { + const args = [`-Dspring.output.ansi.enabled=${value}`, "-cp", "lib/*", "a.Main"]; + await startRun({ ...config, kind: "spring-boot", args }); + expect(ipc.runStart).toHaveBeenCalledWith(expect.objectContaining({ args }), expect.anything()); + expect(ipc.javaResolveLaunch).not.toHaveBeenCalled(); +}); + +it("keeps plain Java runs with an explicit classpath free of Spring Boot options", async () => { + const args = ["-cp", "lib/*", "a.Main"]; + await startRun({ ...config, args }); + expect(ipc.runStart).toHaveBeenCalledWith(expect.objectContaining({ args }), expect.anything()); + expect(ipc.javaResolveLaunch).not.toHaveBeenCalled(); +}); + +it("adds the Spring Boot console option once when resolving the module classpath", async () => { + await startRun({ ...config, kind: "spring-boot", module: "app" }); + expect(ipc.runStart).toHaveBeenCalledWith( + expect.objectContaining({ + args: ["-Dspring.output.ansi.enabled=always", "-cp", "/p/app/target/classes", "a.Main"], + }), + expect.anything(), + ); +}); + it("keeps an explicitly stored root module", async () => { expect(await findJavaModule({ ...config, module: "", cwd: "sibling" })).toBe(""); expect(ipc.findFiles).not.toHaveBeenCalled(); diff --git a/src/features/run/store.ts b/src/features/run/store.ts index 3d7f3552..0e038ae6 100644 --- a/src/features/run/store.ts +++ b/src/features/run/store.ts @@ -17,7 +17,13 @@ import { type RunId, type TestCaseReport, } from "@/platform/ipc"; -import { findJavaModule, isJvmApplication, parseJavaArgs, prepareJavaLaunch } from "@/platform/javaLaunch"; +import { + consoleVmArgs, + findJavaModule, + isJvmApplication, + parseJavaArgs, + prepareJavaLaunch, +} from "@/platform/javaLaunch"; import { downloadJdkActions } from "@/platform/jdk"; import { useProject } from "@/platform/project"; import { executeAction } from "@/platform/actions"; @@ -721,7 +727,7 @@ async function resolveJvmApplication(sessionId: string, config: RunConfig): Prom say(`No main class in '${config.name}': expected java [options] -cp \${classpath}
[args]\n`, "stderr"); return null; } - if (!parsed.classpath) return config; + if (!parsed.classpath) return { ...config, args: [...consoleVmArgs(config), ...config.args] }; const rt = runtime(sessionId)!; const request = { id: nextRequestId(), controller: new AbortController() }; rt.resolveRequest = request;