import * as vscode from "vscode" import { userInfo } from "os" import { getShell } from "../shell" describe("Shell Detection Tests", () => { let originalPlatform: string let originalEnv: NodeJS.ProcessEnv let originalGetConfig: any let originalUserInfo: any // Helper to mock VS Code configuration function mockVsCodeConfig(platformKey: string, defaultProfileName: string | null, profiles: Record) { vscode.workspace.getConfiguration = () => ({ get: (key: string) => { if (key === `defaultProfile.${platformKey}`) { return defaultProfileName } if (key === `profiles.${platformKey}`) { return profiles } return undefined }, }) as any } beforeEach(() => { // Store original references originalPlatform = process.platform originalEnv = { ...process.env } originalGetConfig = vscode.workspace.getConfiguration originalUserInfo = userInfo // Clear environment variables for a clean test delete process.env.SHELL delete process.env.COMSPEC // Default userInfo() mock ;(userInfo as any) = () => ({ shell: null }) }) afterEach(() => { // Restore everything Object.defineProperty(process, "platform", { value: originalPlatform }) process.env = originalEnv vscode.workspace.getConfiguration = originalGetConfig ;(userInfo as any) = originalUserInfo }) // -------------------------------------------------------------------------- // Windows Shell Detection // -------------------------------------------------------------------------- describe("Windows Shell Detection", () => { beforeEach(() => { Object.defineProperty(process, "platform", { value: "win32" }) }) it("uses explicit PowerShell 7 path from VS Code config (profile path)", () => { mockVsCodeConfig("windows", "PowerShell", { PowerShell: { path: "C:\\Program Files\\PowerShell\\7\\pwsh.exe" }, }) expect(getShell()).toBe("C:\\Program Files\\PowerShell\\7\\pwsh.exe") }) it("uses PowerShell 7 path if source is 'PowerShell' but no explicit path", () => { mockVsCodeConfig("windows", "PowerShell", { PowerShell: { source: "PowerShell" }, }) expect(getShell()).toBe("C:\\Program Files\\PowerShell\\7\\pwsh.exe") }) it("falls back to legacy PowerShell if profile includes 'powershell' but no path/source", () => { mockVsCodeConfig("windows", "PowerShell", { PowerShell: {}, }) expect(getShell()).toBe("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe") }) it("uses WSL bash when profile indicates WSL source", () => { mockVsCodeConfig("windows", "WSL", { WSL: { source: "WSL" }, }) expect(getShell()).toBe("/bin/bash") }) it("uses WSL bash when profile name includes 'wsl'", () => { mockVsCodeConfig("windows", "Ubuntu WSL", { "Ubuntu WSL": {}, }) expect(getShell()).toBe("/bin/bash") }) it("defaults to cmd.exe if no special profile is matched", () => { mockVsCodeConfig("windows", "CommandPrompt", { CommandPrompt: {}, }) expect(getShell()).toBe("C:\\Windows\\System32\\cmd.exe") }) it("handles undefined profile gracefully", () => { // Mock a case where defaultProfileName exists but the profile doesn't mockVsCodeConfig("windows", "NonexistentProfile", {}) expect(getShell()).toBe("C:\\Windows\\System32\\cmd.exe") }) it("respects userInfo() if no VS Code config is available", () => { vscode.workspace.getConfiguration = () => ({ get: () => undefined }) as any ;(userInfo as any) = () => ({ shell: "C:\\Custom\\PowerShell.exe" }) expect(getShell()).toBe("C:\\Custom\\PowerShell.exe") }) it("respects an odd COMSPEC if no userInfo shell is available", () => { vscode.workspace.getConfiguration = () => ({ get: () => undefined }) as any process.env.COMSPEC = "D:\\CustomCmd\\cmd.exe" expect(getShell()).toBe("D:\\CustomCmd\\cmd.exe") }) }) // -------------------------------------------------------------------------- // macOS Shell Detection // -------------------------------------------------------------------------- describe("macOS Shell Detection", () => { beforeEach(() => { Object.defineProperty(process, "platform", { value: "darwin" }) }) it("uses VS Code profile path if available", () => { mockVsCodeConfig("osx", "MyCustomShell", { MyCustomShell: { path: "/usr/local/bin/fish" }, }) expect(getShell()).toBe("/usr/local/bin/fish") }) it("falls back to userInfo().shell if no VS Code config is available", () => { vscode.workspace.getConfiguration = () => ({ get: () => undefined }) as any ;(userInfo as any) = () => ({ shell: "/opt/homebrew/bin/zsh" }) expect(getShell()).toBe("/opt/homebrew/bin/zsh") }) it("falls back to SHELL env var if no userInfo shell is found", () => { vscode.workspace.getConfiguration = () => ({ get: () => undefined }) as any process.env.SHELL = "/usr/local/bin/zsh" expect(getShell()).toBe("/usr/local/bin/zsh") }) it("falls back to /bin/zsh if no config, userInfo, or env variable is set", () => { vscode.workspace.getConfiguration = () => ({ get: () => undefined }) as any expect(getShell()).toBe("/bin/zsh") }) }) // -------------------------------------------------------------------------- // Linux Shell Detection // -------------------------------------------------------------------------- describe("Linux Shell Detection", () => { beforeEach(() => { Object.defineProperty(process, "platform", { value: "linux" }) }) it("uses VS Code profile path if available", () => { mockVsCodeConfig("linux", "CustomProfile", { CustomProfile: { path: "/usr/bin/fish" }, }) expect(getShell()).toBe("/usr/bin/fish") }) it("falls back to userInfo().shell if no VS Code config is available", () => { vscode.workspace.getConfiguration = () => ({ get: () => undefined }) as any ;(userInfo as any) = () => ({ shell: "/usr/bin/zsh" }) expect(getShell()).toBe("/usr/bin/zsh") }) it("falls back to SHELL env var if no userInfo shell is found", () => { vscode.workspace.getConfiguration = () => ({ get: () => undefined }) as any process.env.SHELL = "/usr/bin/fish" expect(getShell()).toBe("/usr/bin/fish") }) it("falls back to /bin/bash if nothing is set", () => { vscode.workspace.getConfiguration = () => ({ get: () => undefined }) as any expect(getShell()).toBe("/bin/bash") }) }) // -------------------------------------------------------------------------- // Unknown Platform & Error Handling // -------------------------------------------------------------------------- describe("Unknown Platform / Error Handling", () => { it("falls back to /bin/sh for unknown platforms", () => { Object.defineProperty(process, "platform", { value: "sunos" }) vscode.workspace.getConfiguration = () => ({ get: () => undefined }) as any expect(getShell()).toBe("/bin/sh") }) it("handles VS Code config errors gracefully, falling back to userInfo shell if present", () => { Object.defineProperty(process, "platform", { value: "linux" }) vscode.workspace.getConfiguration = () => { throw new Error("Configuration error") } ;(userInfo as any) = () => ({ shell: "/bin/bash" }) expect(getShell()).toBe("/bin/bash") }) it("handles userInfo errors gracefully, falling back to environment variable if present", () => { Object.defineProperty(process, "platform", { value: "darwin" }) vscode.workspace.getConfiguration = () => ({ get: () => undefined }) as any ;(userInfo as any) = () => { throw new Error("userInfo error") } process.env.SHELL = "/bin/zsh" expect(getShell()).toBe("/bin/zsh") }) it("falls back fully to default shell paths if everything fails", () => { Object.defineProperty(process, "platform", { value: "linux" }) vscode.workspace.getConfiguration = () => { throw new Error("Configuration error") } ;(userInfo as any) = () => { throw new Error("userInfo error") } delete process.env.SHELL expect(getShell()).toBe("/bin/bash") }) }) })