fix(terminal): resolve Windows custom shell executables (#487)

This commit is contained in:
jackie-cqz
2026-09-03 09:58:42 +08:00
committed by Menghuan1918
parent 396017b459
commit 74993839e7
3 changed files with 149 additions and 8 deletions
+48 -1
View File
@@ -13,7 +13,13 @@ vi.mock('node:os', async (importOriginal) => {
})
import { resolveSidebarConfig } from '../src/config.ts'
import { defaultShell, ensureSpawnHelper, shellDisplayName, shellSpawnArgs } from '../src/pty-manager.ts'
import {
defaultShell,
ensureSpawnHelper,
resolveShellExecutable,
shellDisplayName,
shellSpawnArgs,
} from '../src/pty-manager.ts'
describe('pty helpers', () => {
it('prefers an explicit shell, then SHELL, then the account login shell on POSIX', () => {
@@ -66,6 +72,47 @@ describe('pty helpers', () => {
expect(defaultShell({ platform: 'win32', env: {}, exists: () => false })).toBe('powershell.exe')
})
it('Windows: resolves bare custom shells through PATH/PATHEXT before node-pty spawn', () => {
const files = new Set([
'C:/Tools/PowerShell/pwsh.exe',
'C:/Windows/System32/cmd.exe',
])
const options = {
platform: 'win32' as const,
// Mixed-case Path/PATHEXT pins the case-insensitive environment lookup
// that real Windows process.env provides but plain test objects do not.
env: {
Path: 'C:\\Tools\\PowerShell',
PathExt: '.COM;.EXE;.BAT;.CMD',
SystemRoot: 'C:\\Windows',
},
exists: (path: string) => files.has(path.replaceAll('\\', '/')),
}
expect(resolveShellExecutable('pwsh', options).replaceAll('\\', '/'))
.toBe('C:/Tools/PowerShell/pwsh.exe')
expect(resolveShellExecutable('cmd', options).replaceAll('\\', '/'))
.toBe('C:/Windows/System32/cmd.exe')
})
it('Windows: accepts .exe names and absolute paths, and names a missing configured shell', () => {
const options = {
platform: 'win32' as const,
env: { PATH: 'C:\\Tools' },
exists: (path: string) => path.replaceAll('\\', '/') === 'C:/Tools/pwsh.exe'
|| path.replaceAll('\\', '/') === 'D:/Portable Shell/custom.exe',
}
expect(resolveShellExecutable('pwsh.exe', options).replaceAll('\\', '/'))
.toBe('C:/Tools/pwsh.exe')
expect(resolveShellExecutable('D:\\Portable Shell\\custom.exe', options).replaceAll('\\', '/'))
.toBe('D:/Portable Shell/custom.exe')
expect(() => resolveShellExecutable('missing-shell', options))
.toThrow('shell executable not found: "missing-shell"')
})
it('keeps POSIX bare shell resolution delegated to execvp', () => {
expect(resolveShellExecutable(' zsh ', { platform: 'linux', env: {}, exists: () => false })).toBe('zsh')
})
it('trims the configured shell and defaults it to auto for old documents', () => {
expect(resolveSidebarConfig(undefined).shell).toBe('')
expect(resolveSidebarConfig({ shell: ' pwsh.exe ' }).shell).toBe('pwsh.exe')