@yivan-lab/pretty-please 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -2
- package/bin/pls.tsx +153 -35
- package/dist/bin/pls.js +126 -23
- package/dist/package.json +10 -2
- package/dist/src/__integration__/command-generation.test.d.ts +5 -0
- package/dist/src/__integration__/command-generation.test.js +508 -0
- package/dist/src/__integration__/error-recovery.test.d.ts +5 -0
- package/dist/src/__integration__/error-recovery.test.js +511 -0
- package/dist/src/__integration__/shell-hook-workflow.test.d.ts +5 -0
- package/dist/src/__integration__/shell-hook-workflow.test.js +375 -0
- package/dist/src/__tests__/alias.test.d.ts +5 -0
- package/dist/src/__tests__/alias.test.js +421 -0
- package/dist/src/__tests__/chat-history.test.d.ts +5 -0
- package/dist/src/__tests__/chat-history.test.js +372 -0
- package/dist/src/__tests__/config.test.d.ts +5 -0
- package/dist/src/__tests__/config.test.js +822 -0
- package/dist/src/__tests__/history.test.d.ts +5 -0
- package/dist/src/__tests__/history.test.js +439 -0
- package/dist/src/__tests__/remote-history.test.d.ts +5 -0
- package/dist/src/__tests__/remote-history.test.js +641 -0
- package/dist/src/__tests__/remote.test.d.ts +5 -0
- package/dist/src/__tests__/remote.test.js +689 -0
- package/dist/src/__tests__/shell-hook-install.test.d.ts +5 -0
- package/dist/src/__tests__/shell-hook-install.test.js +413 -0
- package/dist/src/__tests__/shell-hook-remote.test.d.ts +5 -0
- package/dist/src/__tests__/shell-hook-remote.test.js +507 -0
- package/dist/src/__tests__/shell-hook.test.d.ts +5 -0
- package/dist/src/__tests__/shell-hook.test.js +440 -0
- package/dist/src/__tests__/sysinfo.test.d.ts +5 -0
- package/dist/src/__tests__/sysinfo.test.js +572 -0
- package/dist/src/__tests__/system-history.test.d.ts +5 -0
- package/dist/src/__tests__/system-history.test.js +457 -0
- package/dist/src/components/Chat.js +9 -28
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.js +30 -2
- package/dist/src/mastra-chat.js +6 -3
- package/dist/src/multi-step.js +6 -3
- package/dist/src/project-context.d.ts +22 -0
- package/dist/src/project-context.js +168 -0
- package/dist/src/prompts.d.ts +4 -4
- package/dist/src/prompts.js +23 -6
- package/dist/src/shell-hook.d.ts +13 -0
- package/dist/src/shell-hook.js +163 -33
- package/dist/src/sysinfo.d.ts +38 -9
- package/dist/src/sysinfo.js +245 -21
- package/dist/src/system-history.d.ts +5 -0
- package/dist/src/system-history.js +64 -18
- package/dist/src/ui/__tests__/theme.test.d.ts +5 -0
- package/dist/src/ui/__tests__/theme.test.js +688 -0
- package/dist/src/upgrade.js +3 -0
- package/dist/src/user-preferences.d.ts +44 -0
- package/dist/src/user-preferences.js +147 -0
- package/dist/src/utils/__tests__/platform-capabilities.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform-capabilities.test.js +214 -0
- package/dist/src/utils/__tests__/platform-exec.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform-exec.test.js +212 -0
- package/dist/src/utils/__tests__/platform-shell.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform-shell.test.js +300 -0
- package/dist/src/utils/__tests__/platform.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform.test.js +137 -0
- package/dist/src/utils/platform.d.ts +88 -0
- package/dist/src/utils/platform.js +331 -0
- package/package.json +10 -2
- package/src/__integration__/command-generation.test.ts +602 -0
- package/src/__integration__/error-recovery.test.ts +620 -0
- package/src/__integration__/shell-hook-workflow.test.ts +457 -0
- package/src/__tests__/alias.test.ts +545 -0
- package/src/__tests__/chat-history.test.ts +462 -0
- package/src/__tests__/config.test.ts +1043 -0
- package/src/__tests__/history.test.ts +538 -0
- package/src/__tests__/remote-history.test.ts +791 -0
- package/src/__tests__/remote.test.ts +866 -0
- package/src/__tests__/shell-hook-install.test.ts +510 -0
- package/src/__tests__/shell-hook-remote.test.ts +679 -0
- package/src/__tests__/shell-hook.test.ts +564 -0
- package/src/__tests__/sysinfo.test.ts +718 -0
- package/src/__tests__/system-history.test.ts +608 -0
- package/src/components/Chat.tsx +10 -37
- package/src/config.ts +29 -2
- package/src/mastra-chat.ts +8 -3
- package/src/multi-step.ts +7 -2
- package/src/project-context.ts +191 -0
- package/src/prompts.ts +26 -5
- package/src/shell-hook.ts +179 -33
- package/src/sysinfo.ts +326 -25
- package/src/system-history.ts +67 -14
- package/src/ui/__tests__/theme.test.ts +869 -0
- package/src/upgrade.ts +5 -0
- package/src/user-preferences.ts +178 -0
- package/src/utils/__tests__/platform-capabilities.test.ts +265 -0
- package/src/utils/__tests__/platform-exec.test.ts +278 -0
- package/src/utils/__tests__/platform-shell.test.ts +353 -0
- package/src/utils/__tests__/platform.test.ts +170 -0
- package/src/utils/platform.ts +431 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 命令执行配置专项测试
|
|
3
|
+
* 测试不同 Shell 的命令执行配置构建
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
7
|
+
import { buildShellExecConfig, type ShellType } from '../platform'
|
|
8
|
+
|
|
9
|
+
describe('Shell Exec Config - Bash', () => {
|
|
10
|
+
const originalEnv = { ...process.env }
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
delete process.env.SHELL
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
process.env = { ...originalEnv }
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('应该为 Bash 添加 pipefail', () => {
|
|
21
|
+
const config = buildShellExecConfig('ls -la', 'bash')
|
|
22
|
+
|
|
23
|
+
expect(config.shell).toBe('/bin/bash')
|
|
24
|
+
expect(config.args).toEqual(['-c', 'set -o pipefail; ls -la'])
|
|
25
|
+
expect(config.command).toBe('set -o pipefail; ls -la')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('应该使用 $SHELL 环境变量', () => {
|
|
29
|
+
process.env.SHELL = '/usr/local/bin/bash'
|
|
30
|
+
const config = buildShellExecConfig('ls -la', 'bash')
|
|
31
|
+
|
|
32
|
+
expect(config.shell).toBe('/usr/local/bin/bash')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('应该处理多行命令', () => {
|
|
36
|
+
const command = 'echo "hello"\necho "world"'
|
|
37
|
+
const config = buildShellExecConfig(command, 'bash')
|
|
38
|
+
|
|
39
|
+
expect(config.command).toBe(`set -o pipefail; ${command}`)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('应该处理包含特殊字符的命令', () => {
|
|
43
|
+
const command = 'echo "test$VAR"; cat file | grep "pattern"'
|
|
44
|
+
const config = buildShellExecConfig(command, 'bash')
|
|
45
|
+
|
|
46
|
+
expect(config.command).toBe(`set -o pipefail; ${command}`)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('Shell Exec Config - Zsh', () => {
|
|
51
|
+
const originalEnv = { ...process.env }
|
|
52
|
+
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
delete process.env.SHELL
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
process.env = { ...originalEnv }
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('应该为 Zsh 添加 pipefail', () => {
|
|
62
|
+
const config = buildShellExecConfig('ls -la', 'zsh')
|
|
63
|
+
|
|
64
|
+
expect(config.shell).toBe('/bin/zsh')
|
|
65
|
+
expect(config.args).toEqual(['-c', 'setopt pipefail; ls -la'])
|
|
66
|
+
expect(config.command).toBe('setopt pipefail; ls -la')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('应该使用 $SHELL 环境变量', () => {
|
|
70
|
+
process.env.SHELL = '/opt/homebrew/bin/zsh'
|
|
71
|
+
const config = buildShellExecConfig('ls -la', 'zsh')
|
|
72
|
+
|
|
73
|
+
expect(config.shell).toBe('/opt/homebrew/bin/zsh')
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('Shell Exec Config - Fish', () => {
|
|
78
|
+
const originalEnv = { ...process.env }
|
|
79
|
+
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
delete process.env.SHELL
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
afterEach(() => {
|
|
85
|
+
process.env = { ...originalEnv }
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('Fish 不应该添加 pipefail', () => {
|
|
89
|
+
delete process.env.SHELL // 确保使用默认路径
|
|
90
|
+
const config = buildShellExecConfig('ls -la', 'fish')
|
|
91
|
+
|
|
92
|
+
expect(config.shell).toBe('/usr/bin/fish')
|
|
93
|
+
expect(config.args).toEqual(['-c', 'ls -la'])
|
|
94
|
+
expect(config.command).toBe('ls -la') // 注意:Fish 不需要 pipefail
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('应该使用 $SHELL 环境变量', () => {
|
|
98
|
+
process.env.SHELL = '/usr/local/bin/fish'
|
|
99
|
+
const config = buildShellExecConfig('ls -la', 'fish')
|
|
100
|
+
|
|
101
|
+
expect(config.shell).toBe('/usr/local/bin/fish')
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
describe('Shell Exec Config - PowerShell', () => {
|
|
106
|
+
it('PowerShell 5 应该使用 -NoProfile -Command', () => {
|
|
107
|
+
const config = buildShellExecConfig('Get-Process', 'powershell5')
|
|
108
|
+
|
|
109
|
+
expect(config.shell).toBe('powershell.exe')
|
|
110
|
+
expect(config.args).toEqual(['-NoProfile', '-Command', 'Get-Process'])
|
|
111
|
+
expect(config.command).toBe('Get-Process')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('PowerShell 7 应该使用 -NoProfile -Command', () => {
|
|
115
|
+
const config = buildShellExecConfig('Get-Process', 'powershell7')
|
|
116
|
+
|
|
117
|
+
expect(config.shell).toBe('pwsh.exe')
|
|
118
|
+
expect(config.args).toEqual(['-NoProfile', '-Command', 'Get-Process'])
|
|
119
|
+
expect(config.command).toBe('Get-Process')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('应该处理多行 PowerShell 脚本', () => {
|
|
123
|
+
const command = 'Get-Process | Where-Object {$_.CPU -gt 10}'
|
|
124
|
+
const config = buildShellExecConfig(command, 'powershell7')
|
|
125
|
+
|
|
126
|
+
expect(config.command).toBe(command)
|
|
127
|
+
expect(config.args).toEqual(['-NoProfile', '-Command', command])
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe('Shell Exec Config - CMD', () => {
|
|
132
|
+
const originalEnv = { ...process.env }
|
|
133
|
+
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
// 清除 COMSPEC 以确保使用默认值
|
|
136
|
+
delete process.env.COMSPEC
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
afterEach(() => {
|
|
140
|
+
process.env = { ...originalEnv }
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('CMD 应该使用 /c 参数', () => {
|
|
144
|
+
const config = buildShellExecConfig('dir', 'cmd')
|
|
145
|
+
|
|
146
|
+
expect(config.shell).toContain('cmd.exe')
|
|
147
|
+
expect(config.args).toEqual(['/c', 'dir'])
|
|
148
|
+
expect(config.command).toBe('dir')
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('应该使用 $COMSPEC 环境变量', () => {
|
|
152
|
+
process.env.COMSPEC = 'C:\\Windows\\System32\\cmd.exe'
|
|
153
|
+
const config = buildShellExecConfig('dir', 'cmd')
|
|
154
|
+
|
|
155
|
+
expect(config.shell).toBe('C:\\Windows\\System32\\cmd.exe')
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('应该处理 CMD 批处理命令', () => {
|
|
159
|
+
const command = 'echo hello && dir && cd ..'
|
|
160
|
+
const config = buildShellExecConfig(command, 'cmd')
|
|
161
|
+
|
|
162
|
+
expect(config.command).toBe(command)
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
describe('Shell Exec Config - Unknown/Default', () => {
|
|
167
|
+
const originalPlatform = process.platform
|
|
168
|
+
|
|
169
|
+
afterEach(() => {
|
|
170
|
+
Object.defineProperty(process, 'platform', {
|
|
171
|
+
value: originalPlatform,
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('Unix 平台未知 Shell 应该降级到 /bin/sh', () => {
|
|
176
|
+
Object.defineProperty(process, 'platform', { value: 'linux', writable: true })
|
|
177
|
+
const config = buildShellExecConfig('ls', 'unknown')
|
|
178
|
+
|
|
179
|
+
expect(config.shell).toBe('/bin/sh')
|
|
180
|
+
expect(config.args).toEqual(['-c', 'ls'])
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('Windows 平台未知 Shell 应该降级到 powershell.exe', () => {
|
|
184
|
+
Object.defineProperty(process, 'platform', { value: 'win32', writable: true })
|
|
185
|
+
const config = buildShellExecConfig('dir', 'unknown')
|
|
186
|
+
|
|
187
|
+
expect(config.shell).toBe('powershell.exe')
|
|
188
|
+
expect(config.args).toEqual(['-Command', 'dir'])
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
describe('Shell Exec Config - 自动检测 Shell', () => {
|
|
193
|
+
const originalPlatform = process.platform
|
|
194
|
+
const originalEnv = { ...process.env }
|
|
195
|
+
|
|
196
|
+
beforeEach(() => {
|
|
197
|
+
vi.stubGlobal('process', {
|
|
198
|
+
...process,
|
|
199
|
+
platform: 'darwin',
|
|
200
|
+
env: {
|
|
201
|
+
SHELL: '/bin/zsh',
|
|
202
|
+
},
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
afterEach(() => {
|
|
207
|
+
vi.unstubAllGlobals()
|
|
208
|
+
Object.defineProperty(process, 'platform', {
|
|
209
|
+
value: originalPlatform,
|
|
210
|
+
})
|
|
211
|
+
process.env = { ...originalEnv }
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('不指定 Shell 时应该自动检测', () => {
|
|
215
|
+
vi.stubGlobal('process', {
|
|
216
|
+
...process,
|
|
217
|
+
platform: 'darwin',
|
|
218
|
+
env: {
|
|
219
|
+
SHELL: '/bin/zsh',
|
|
220
|
+
},
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
const config = buildShellExecConfig('ls -la')
|
|
224
|
+
|
|
225
|
+
expect(config.shell).toBe('/bin/zsh')
|
|
226
|
+
expect(config.command).toBe('setopt pipefail; ls -la')
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('Windows 环境不指定 Shell 时应该自动检测', () => {
|
|
230
|
+
vi.stubGlobal('process', {
|
|
231
|
+
...process,
|
|
232
|
+
platform: 'win32',
|
|
233
|
+
env: {
|
|
234
|
+
PSModulePath: 'C:\\Program Files\\PowerShell\\7\\Modules',
|
|
235
|
+
},
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
const config = buildShellExecConfig('Get-Process')
|
|
239
|
+
|
|
240
|
+
expect(config.shell).toBe('pwsh.exe')
|
|
241
|
+
expect(config.args).toEqual(['-NoProfile', '-Command', 'Get-Process'])
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
describe('Shell Exec Config - 边界情况', () => {
|
|
246
|
+
it('应该处理空命令', () => {
|
|
247
|
+
const config = buildShellExecConfig('', 'bash')
|
|
248
|
+
|
|
249
|
+
expect(config.command).toBe('set -o pipefail; ')
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it('应该处理只有空格的命令', () => {
|
|
253
|
+
const config = buildShellExecConfig(' ', 'bash')
|
|
254
|
+
|
|
255
|
+
expect(config.command).toBe('set -o pipefail; ')
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('应该处理包含引号的命令', () => {
|
|
259
|
+
const command = 'echo "hello \'world\'"'
|
|
260
|
+
const config = buildShellExecConfig(command, 'bash')
|
|
261
|
+
|
|
262
|
+
expect(config.command).toContain(command)
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('应该处理包含管道的命令', () => {
|
|
266
|
+
const command = 'cat file.txt | grep pattern | wc -l'
|
|
267
|
+
const config = buildShellExecConfig(command, 'bash')
|
|
268
|
+
|
|
269
|
+
expect(config.command).toBe(`set -o pipefail; ${command}`)
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
it('应该处理包含重定向的命令', () => {
|
|
273
|
+
const command = 'echo "test" > file.txt 2>&1'
|
|
274
|
+
const config = buildShellExecConfig(command, 'bash')
|
|
275
|
+
|
|
276
|
+
expect(config.command).toContain(command)
|
|
277
|
+
})
|
|
278
|
+
})
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell 检测专项测试
|
|
3
|
+
* 测试各种平台和环境下的 Shell 类型检测
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
7
|
+
import { detectShell, type ShellType } from '../platform'
|
|
8
|
+
import { execSync } from 'child_process'
|
|
9
|
+
|
|
10
|
+
// Mock child_process 用于控制 commandExists 的行为
|
|
11
|
+
vi.mock('child_process', () => ({
|
|
12
|
+
execSync: vi.fn(),
|
|
13
|
+
exec: vi.fn(),
|
|
14
|
+
spawn: vi.fn(),
|
|
15
|
+
}))
|
|
16
|
+
|
|
17
|
+
const mockExecSync = vi.mocked(execSync)
|
|
18
|
+
|
|
19
|
+
describe('Shell Detection - Windows', () => {
|
|
20
|
+
// 保存原始环境
|
|
21
|
+
const originalPlatform = process.platform
|
|
22
|
+
const originalEnv = { ...process.env }
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks()
|
|
26
|
+
// 默认让 commandExists 返回 false(execSync 抛出错误)
|
|
27
|
+
mockExecSync.mockImplementation(() => {
|
|
28
|
+
throw new Error('Command not found')
|
|
29
|
+
})
|
|
30
|
+
// 清空环境变量
|
|
31
|
+
vi.stubGlobal('process', {
|
|
32
|
+
...process,
|
|
33
|
+
platform: 'win32',
|
|
34
|
+
env: {},
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
// 恢复原始环境
|
|
40
|
+
vi.unstubAllGlobals()
|
|
41
|
+
Object.defineProperty(process, 'platform', {
|
|
42
|
+
value: originalPlatform,
|
|
43
|
+
})
|
|
44
|
+
process.env = { ...originalEnv }
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('应该检测到 PowerShell 7+ (路径包含 PowerShell\\7)', () => {
|
|
48
|
+
vi.stubGlobal('process', {
|
|
49
|
+
...process,
|
|
50
|
+
platform: 'win32',
|
|
51
|
+
env: {
|
|
52
|
+
PSModulePath: 'C:\\Program Files\\PowerShell\\7\\Modules',
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const result = detectShell()
|
|
57
|
+
expect(result).toBe('powershell7')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('应该检测到 PowerShell 7+ (路径包含 PowerShell/7)', () => {
|
|
61
|
+
vi.stubGlobal('process', {
|
|
62
|
+
...process,
|
|
63
|
+
platform: 'win32',
|
|
64
|
+
env: {
|
|
65
|
+
PSModulePath: 'C:/Program Files/PowerShell/7/Modules',
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const result = detectShell()
|
|
70
|
+
expect(result).toBe('powershell7')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('应该检测到 PowerShell 7+ (大小写不敏感)', () => {
|
|
74
|
+
vi.stubGlobal('process', {
|
|
75
|
+
...process,
|
|
76
|
+
platform: 'win32',
|
|
77
|
+
env: {
|
|
78
|
+
PSModulePath: 'C:\\PROGRAM FILES\\powershell\\7\\modules',
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const result = detectShell()
|
|
83
|
+
expect(result).toBe('powershell7')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('应该检测到 PowerShell 5.x (路径包含 WindowsPowerShell)', () => {
|
|
87
|
+
vi.stubGlobal('process', {
|
|
88
|
+
...process,
|
|
89
|
+
platform: 'win32',
|
|
90
|
+
env: {
|
|
91
|
+
PSModulePath: 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules',
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const result = detectShell()
|
|
96
|
+
expect(result).toBe('powershell5')
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('应该检测到 PowerShell 5.x (大小写不敏感)', () => {
|
|
100
|
+
vi.stubGlobal('process', {
|
|
101
|
+
...process,
|
|
102
|
+
platform: 'win32',
|
|
103
|
+
env: {
|
|
104
|
+
PSModulePath: 'C:\\Windows\\System32\\WINDOWSPOWERSHELL\\v1.0\\Modules',
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const result = detectShell()
|
|
109
|
+
expect(result).toBe('powershell5')
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('应该检测到 CMD (PROMPT 存在且无 PSModulePath)', () => {
|
|
113
|
+
vi.stubGlobal('process', {
|
|
114
|
+
...process,
|
|
115
|
+
platform: 'win32',
|
|
116
|
+
env: {
|
|
117
|
+
PROMPT: '$P$G',
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
const result = detectShell()
|
|
122
|
+
expect(result).toBe('cmd')
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('应该忽略 PROMPT 如果 PSModulePath 存在 (避免 PowerShell 中运行 CMD 的误判)', () => {
|
|
126
|
+
vi.stubGlobal('process', {
|
|
127
|
+
...process,
|
|
128
|
+
platform: 'win32',
|
|
129
|
+
env: {
|
|
130
|
+
PROMPT: '$P$G',
|
|
131
|
+
PSModulePath: 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules',
|
|
132
|
+
},
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const result = detectShell()
|
|
136
|
+
expect(result).toBe('powershell5') // 应该是 PowerShell,不是 CMD
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('应该默认降级到 PowerShell 5 (Windows 环境无明确特征)', () => {
|
|
140
|
+
vi.stubGlobal('process', {
|
|
141
|
+
...process,
|
|
142
|
+
platform: 'win32',
|
|
143
|
+
env: {},
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
const result = detectShell()
|
|
147
|
+
expect(result).toBe('powershell5')
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('应该处理空的 PSModulePath', () => {
|
|
151
|
+
vi.stubGlobal('process', {
|
|
152
|
+
...process,
|
|
153
|
+
platform: 'win32',
|
|
154
|
+
env: {
|
|
155
|
+
PSModulePath: '',
|
|
156
|
+
},
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
const result = detectShell()
|
|
160
|
+
expect(result).toBe('powershell5')
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
describe('Shell Detection - Unix', () => {
|
|
165
|
+
const originalPlatform = process.platform
|
|
166
|
+
const originalEnv = { ...process.env }
|
|
167
|
+
|
|
168
|
+
beforeEach(() => {
|
|
169
|
+
vi.stubGlobal('process', {
|
|
170
|
+
...process,
|
|
171
|
+
platform: 'darwin',
|
|
172
|
+
env: {},
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
afterEach(() => {
|
|
177
|
+
vi.unstubAllGlobals()
|
|
178
|
+
Object.defineProperty(process, 'platform', {
|
|
179
|
+
value: originalPlatform,
|
|
180
|
+
})
|
|
181
|
+
process.env = { ...originalEnv }
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('应该检测到 Zsh', () => {
|
|
185
|
+
vi.stubGlobal('process', {
|
|
186
|
+
...process,
|
|
187
|
+
platform: 'darwin',
|
|
188
|
+
env: {
|
|
189
|
+
SHELL: '/bin/zsh',
|
|
190
|
+
},
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
const result = detectShell()
|
|
194
|
+
expect(result).toBe('zsh')
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('应该检测到 Bash', () => {
|
|
198
|
+
vi.stubGlobal('process', {
|
|
199
|
+
...process,
|
|
200
|
+
platform: 'linux',
|
|
201
|
+
env: {
|
|
202
|
+
SHELL: '/bin/bash',
|
|
203
|
+
},
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
const result = detectShell()
|
|
207
|
+
expect(result).toBe('bash')
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('应该检测到 Fish', () => {
|
|
211
|
+
vi.stubGlobal('process', {
|
|
212
|
+
...process,
|
|
213
|
+
platform: 'linux',
|
|
214
|
+
env: {
|
|
215
|
+
SHELL: '/usr/bin/fish',
|
|
216
|
+
},
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
const result = detectShell()
|
|
220
|
+
expect(result).toBe('fish')
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('应该处理 SHELL 路径的多种格式 (Zsh)', () => {
|
|
224
|
+
const testCases = [
|
|
225
|
+
'/usr/local/bin/zsh',
|
|
226
|
+
'/opt/homebrew/bin/zsh',
|
|
227
|
+
'zsh',
|
|
228
|
+
]
|
|
229
|
+
|
|
230
|
+
for (const shellPath of testCases) {
|
|
231
|
+
vi.stubGlobal('process', {
|
|
232
|
+
...process,
|
|
233
|
+
platform: 'darwin',
|
|
234
|
+
env: {
|
|
235
|
+
SHELL: shellPath,
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
const result = detectShell()
|
|
240
|
+
expect(result).toBe('zsh')
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('应该处理 SHELL 路径的多种格式 (Bash)', () => {
|
|
245
|
+
const testCases = [
|
|
246
|
+
'/usr/local/bin/bash',
|
|
247
|
+
'/opt/homebrew/bin/bash',
|
|
248
|
+
'bash',
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
for (const shellPath of testCases) {
|
|
252
|
+
vi.stubGlobal('process', {
|
|
253
|
+
...process,
|
|
254
|
+
platform: 'linux',
|
|
255
|
+
env: {
|
|
256
|
+
SHELL: shellPath,
|
|
257
|
+
},
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
const result = detectShell()
|
|
261
|
+
expect(result).toBe('bash')
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('应该降级到 unknown (SHELL 为空)', () => {
|
|
266
|
+
vi.stubGlobal('process', {
|
|
267
|
+
...process,
|
|
268
|
+
platform: 'linux',
|
|
269
|
+
env: {
|
|
270
|
+
SHELL: '',
|
|
271
|
+
},
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
const result = detectShell()
|
|
275
|
+
expect(result).toBe('unknown')
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
it('应该降级到 unknown (SHELL 不匹配)', () => {
|
|
279
|
+
vi.stubGlobal('process', {
|
|
280
|
+
...process,
|
|
281
|
+
platform: 'linux',
|
|
282
|
+
env: {
|
|
283
|
+
SHELL: '/usr/bin/tcsh',
|
|
284
|
+
},
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
const result = detectShell()
|
|
288
|
+
expect(result).toBe('unknown')
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it('应该降级到 unknown (无 SHELL 环境变量)', () => {
|
|
292
|
+
vi.stubGlobal('process', {
|
|
293
|
+
...process,
|
|
294
|
+
platform: 'linux',
|
|
295
|
+
env: {},
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
const result = detectShell()
|
|
299
|
+
expect(result).toBe('unknown')
|
|
300
|
+
})
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
describe('Shell Detection - Edge Cases', () => {
|
|
304
|
+
const originalPlatform = process.platform
|
|
305
|
+
const originalEnv = { ...process.env }
|
|
306
|
+
|
|
307
|
+
afterEach(() => {
|
|
308
|
+
vi.unstubAllGlobals()
|
|
309
|
+
Object.defineProperty(process, 'platform', {
|
|
310
|
+
value: originalPlatform,
|
|
311
|
+
})
|
|
312
|
+
process.env = { ...originalEnv }
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
it('应该正确处理 macOS 平台', () => {
|
|
316
|
+
vi.stubGlobal('process', {
|
|
317
|
+
...process,
|
|
318
|
+
platform: 'darwin',
|
|
319
|
+
env: {
|
|
320
|
+
SHELL: '/bin/zsh',
|
|
321
|
+
},
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
const result = detectShell()
|
|
325
|
+
expect(result).toBe('zsh')
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
it('应该正确处理 Linux 平台', () => {
|
|
329
|
+
vi.stubGlobal('process', {
|
|
330
|
+
...process,
|
|
331
|
+
platform: 'linux',
|
|
332
|
+
env: {
|
|
333
|
+
SHELL: '/bin/bash',
|
|
334
|
+
},
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
const result = detectShell()
|
|
338
|
+
expect(result).toBe('bash')
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it('应该正确处理 Windows 平台', () => {
|
|
342
|
+
vi.stubGlobal('process', {
|
|
343
|
+
...process,
|
|
344
|
+
platform: 'win32',
|
|
345
|
+
env: {
|
|
346
|
+
PSModulePath: 'C:\\Program Files\\PowerShell\\7\\Modules',
|
|
347
|
+
},
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
const result = detectShell()
|
|
351
|
+
expect(result).toBe('powershell7')
|
|
352
|
+
})
|
|
353
|
+
})
|