@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,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 别名管理模块测试
|
|
3
|
+
* 测试别名解析、模板参数替换、别名管理等功能
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
6
|
+
// Mock fs 模块
|
|
7
|
+
vi.mock('fs', () => ({
|
|
8
|
+
default: {
|
|
9
|
+
existsSync: vi.fn(),
|
|
10
|
+
readFileSync: vi.fn(),
|
|
11
|
+
writeFileSync: vi.fn(),
|
|
12
|
+
mkdirSync: vi.fn(),
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
// Mock os 模块
|
|
16
|
+
vi.mock('os', () => ({
|
|
17
|
+
default: {
|
|
18
|
+
homedir: vi.fn(() => '/home/testuser'),
|
|
19
|
+
},
|
|
20
|
+
}));
|
|
21
|
+
// Mock config 模块
|
|
22
|
+
vi.mock('../config.js', () => ({
|
|
23
|
+
getConfig: vi.fn(() => ({
|
|
24
|
+
aliases: {},
|
|
25
|
+
})),
|
|
26
|
+
saveConfig: vi.fn(),
|
|
27
|
+
}));
|
|
28
|
+
// Mock theme 模块
|
|
29
|
+
vi.mock('../ui/theme.js', () => ({
|
|
30
|
+
getCurrentTheme: vi.fn(() => ({
|
|
31
|
+
primary: '#007acc',
|
|
32
|
+
secondary: '#6c757d',
|
|
33
|
+
success: '#4caf50',
|
|
34
|
+
error: '#f44336',
|
|
35
|
+
warning: '#ff9800',
|
|
36
|
+
text: {
|
|
37
|
+
muted: '#666666',
|
|
38
|
+
},
|
|
39
|
+
})),
|
|
40
|
+
}));
|
|
41
|
+
// Mock chalk
|
|
42
|
+
vi.mock('chalk', () => ({
|
|
43
|
+
default: {
|
|
44
|
+
bold: vi.fn((s) => s),
|
|
45
|
+
gray: vi.fn((s) => s),
|
|
46
|
+
hex: vi.fn(() => (s) => s),
|
|
47
|
+
},
|
|
48
|
+
}));
|
|
49
|
+
import { getConfig, saveConfig } from '../config.js';
|
|
50
|
+
// 获取 mock 函数引用
|
|
51
|
+
const mockGetConfig = vi.mocked(getConfig);
|
|
52
|
+
const mockSaveConfig = vi.mocked(saveConfig);
|
|
53
|
+
// 模块状态重置辅助
|
|
54
|
+
async function resetAliasModule() {
|
|
55
|
+
vi.resetModules();
|
|
56
|
+
return await import('../alias.js');
|
|
57
|
+
}
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
vi.clearAllMocks();
|
|
60
|
+
mockGetConfig.mockReturnValue({
|
|
61
|
+
aliases: {},
|
|
62
|
+
});
|
|
63
|
+
mockSaveConfig.mockImplementation(() => { });
|
|
64
|
+
});
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
vi.restoreAllMocks();
|
|
67
|
+
});
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// getAliases 测试
|
|
70
|
+
// ============================================================================
|
|
71
|
+
describe('getAliases', () => {
|
|
72
|
+
it('应该返回空对象(无别名时)', async () => {
|
|
73
|
+
mockGetConfig.mockReturnValue({ aliases: {} });
|
|
74
|
+
const { getAliases } = await resetAliasModule();
|
|
75
|
+
const aliases = getAliases();
|
|
76
|
+
expect(aliases).toEqual({});
|
|
77
|
+
});
|
|
78
|
+
it('应该返回配置中的别名', async () => {
|
|
79
|
+
mockGetConfig.mockReturnValue({
|
|
80
|
+
aliases: {
|
|
81
|
+
disk: { prompt: '检查磁盘空间', description: '磁盘检查' },
|
|
82
|
+
deploy: { prompt: '部署到生产环境' },
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const { getAliases } = await resetAliasModule();
|
|
86
|
+
const aliases = getAliases();
|
|
87
|
+
expect(aliases.disk.prompt).toBe('检查磁盘空间');
|
|
88
|
+
expect(aliases.deploy.prompt).toBe('部署到生产环境');
|
|
89
|
+
});
|
|
90
|
+
it('aliases 为 undefined 时应该返回空对象', async () => {
|
|
91
|
+
mockGetConfig.mockReturnValue({});
|
|
92
|
+
const { getAliases } = await resetAliasModule();
|
|
93
|
+
const aliases = getAliases();
|
|
94
|
+
expect(aliases).toEqual({});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// addAlias 测试
|
|
99
|
+
// ============================================================================
|
|
100
|
+
describe('addAlias', () => {
|
|
101
|
+
it('应该添加新别名', async () => {
|
|
102
|
+
mockGetConfig.mockReturnValue({ aliases: {} });
|
|
103
|
+
const { addAlias } = await resetAliasModule();
|
|
104
|
+
addAlias('disk', '检查磁盘空间');
|
|
105
|
+
expect(mockSaveConfig).toHaveBeenCalled();
|
|
106
|
+
const savedConfig = mockSaveConfig.mock.calls[0][0];
|
|
107
|
+
expect(savedConfig.aliases.disk.prompt).toBe('检查磁盘空间');
|
|
108
|
+
});
|
|
109
|
+
it('应该移除 @ 前缀', async () => {
|
|
110
|
+
mockGetConfig.mockReturnValue({ aliases: {} });
|
|
111
|
+
const { addAlias } = await resetAliasModule();
|
|
112
|
+
addAlias('@disk', '检查磁盘空间');
|
|
113
|
+
const savedConfig = mockSaveConfig.mock.calls[0][0];
|
|
114
|
+
expect(savedConfig.aliases.disk).toBeDefined();
|
|
115
|
+
expect(savedConfig.aliases['@disk']).toBeUndefined();
|
|
116
|
+
});
|
|
117
|
+
it('应该保存可选描述', async () => {
|
|
118
|
+
mockGetConfig.mockReturnValue({ aliases: {} });
|
|
119
|
+
const { addAlias } = await resetAliasModule();
|
|
120
|
+
addAlias('disk', '检查磁盘空间', '这是一个磁盘检查命令');
|
|
121
|
+
const savedConfig = mockSaveConfig.mock.calls[0][0];
|
|
122
|
+
expect(savedConfig.aliases.disk.description).toBe('这是一个磁盘检查命令');
|
|
123
|
+
});
|
|
124
|
+
it('空别名名称应该抛出错误', async () => {
|
|
125
|
+
const { addAlias } = await resetAliasModule();
|
|
126
|
+
expect(() => addAlias('', '检查磁盘空间'))
|
|
127
|
+
.toThrow('别名名称不能为空');
|
|
128
|
+
});
|
|
129
|
+
it('空格别名名称应该抛出错误', async () => {
|
|
130
|
+
const { addAlias } = await resetAliasModule();
|
|
131
|
+
expect(() => addAlias(' ', '检查磁盘空间'))
|
|
132
|
+
.toThrow('别名名称不能为空');
|
|
133
|
+
});
|
|
134
|
+
it('空 prompt 应该抛出错误', async () => {
|
|
135
|
+
const { addAlias } = await resetAliasModule();
|
|
136
|
+
expect(() => addAlias('disk', ''))
|
|
137
|
+
.toThrow('prompt 不能为空');
|
|
138
|
+
});
|
|
139
|
+
it('无效字符的别名名称应该抛出错误', async () => {
|
|
140
|
+
const { addAlias } = await resetAliasModule();
|
|
141
|
+
expect(() => addAlias('disk space', '检查磁盘空间'))
|
|
142
|
+
.toThrow('别名名称只能包含字母、数字、下划线和连字符');
|
|
143
|
+
});
|
|
144
|
+
it('包含特殊字符的别名名称应该抛出错误', async () => {
|
|
145
|
+
const { addAlias } = await resetAliasModule();
|
|
146
|
+
expect(() => addAlias('disk!@#', '检查磁盘空间'))
|
|
147
|
+
.toThrow('别名名称只能包含字母、数字、下划线和连字符');
|
|
148
|
+
});
|
|
149
|
+
it('有效的别名名称应该通过验证', async () => {
|
|
150
|
+
mockGetConfig.mockReturnValue({ aliases: {} });
|
|
151
|
+
const { addAlias } = await resetAliasModule();
|
|
152
|
+
// 这些都应该成功
|
|
153
|
+
expect(() => addAlias('disk', '检查磁盘空间')).not.toThrow();
|
|
154
|
+
expect(() => addAlias('disk_check', '检查磁盘空间')).not.toThrow();
|
|
155
|
+
expect(() => addAlias('disk-check', '检查磁盘空间')).not.toThrow();
|
|
156
|
+
expect(() => addAlias('disk123', '检查磁盘空间')).not.toThrow();
|
|
157
|
+
expect(() => addAlias('DiskCheck', '检查磁盘空间')).not.toThrow();
|
|
158
|
+
});
|
|
159
|
+
it('保留命令应该抛出错误', async () => {
|
|
160
|
+
mockGetConfig.mockReturnValue({ aliases: {} });
|
|
161
|
+
const { addAlias } = await resetAliasModule();
|
|
162
|
+
const reservedCommands = ['config', 'history', 'alias'];
|
|
163
|
+
expect(() => addAlias('config', '配置命令', undefined, reservedCommands))
|
|
164
|
+
.toThrow('"config" 是保留的子命令,不能用作别名');
|
|
165
|
+
});
|
|
166
|
+
it('应该去除 prompt 的首尾空格', async () => {
|
|
167
|
+
mockGetConfig.mockReturnValue({ aliases: {} });
|
|
168
|
+
const { addAlias } = await resetAliasModule();
|
|
169
|
+
addAlias('disk', ' 检查磁盘空间 ');
|
|
170
|
+
const savedConfig = mockSaveConfig.mock.calls[0][0];
|
|
171
|
+
expect(savedConfig.aliases.disk.prompt).toBe('检查磁盘空间');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// removeAlias 测试
|
|
176
|
+
// ============================================================================
|
|
177
|
+
describe('removeAlias', () => {
|
|
178
|
+
it('应该删除存在的别名', async () => {
|
|
179
|
+
mockGetConfig.mockReturnValue({
|
|
180
|
+
aliases: {
|
|
181
|
+
disk: { prompt: '检查磁盘空间' },
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
const { removeAlias } = await resetAliasModule();
|
|
185
|
+
const result = removeAlias('disk');
|
|
186
|
+
expect(result).toBe(true);
|
|
187
|
+
expect(mockSaveConfig).toHaveBeenCalled();
|
|
188
|
+
});
|
|
189
|
+
it('应该支持 @ 前缀删除', async () => {
|
|
190
|
+
mockGetConfig.mockReturnValue({
|
|
191
|
+
aliases: {
|
|
192
|
+
disk: { prompt: '检查磁盘空间' },
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
const { removeAlias } = await resetAliasModule();
|
|
196
|
+
const result = removeAlias('@disk');
|
|
197
|
+
expect(result).toBe(true);
|
|
198
|
+
});
|
|
199
|
+
it('删除不存在的别名应该返回 false', async () => {
|
|
200
|
+
mockGetConfig.mockReturnValue({ aliases: {} });
|
|
201
|
+
const { removeAlias } = await resetAliasModule();
|
|
202
|
+
const result = removeAlias('nonexistent');
|
|
203
|
+
expect(result).toBe(false);
|
|
204
|
+
expect(mockSaveConfig).not.toHaveBeenCalled();
|
|
205
|
+
});
|
|
206
|
+
it('aliases 为空时应该返回 false', async () => {
|
|
207
|
+
mockGetConfig.mockReturnValue({});
|
|
208
|
+
const { removeAlias } = await resetAliasModule();
|
|
209
|
+
const result = removeAlias('disk');
|
|
210
|
+
expect(result).toBe(false);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
// ============================================================================
|
|
214
|
+
// resolveAlias 测试
|
|
215
|
+
// ============================================================================
|
|
216
|
+
describe('resolveAlias', () => {
|
|
217
|
+
describe('基础解析', () => {
|
|
218
|
+
it('应该解析已知别名', async () => {
|
|
219
|
+
mockGetConfig.mockReturnValue({
|
|
220
|
+
aliases: {
|
|
221
|
+
disk: { prompt: '检查磁盘空间' },
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
const { resolveAlias } = await resetAliasModule();
|
|
225
|
+
const result = resolveAlias('disk');
|
|
226
|
+
expect(result.resolved).toBe(true);
|
|
227
|
+
expect(result.prompt).toBe('检查磁盘空间');
|
|
228
|
+
expect(result.aliasName).toBe('disk');
|
|
229
|
+
});
|
|
230
|
+
it('应该支持 @ 前缀', async () => {
|
|
231
|
+
mockGetConfig.mockReturnValue({
|
|
232
|
+
aliases: {
|
|
233
|
+
disk: { prompt: '检查磁盘空间' },
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
const { resolveAlias } = await resetAliasModule();
|
|
237
|
+
const result = resolveAlias('@disk');
|
|
238
|
+
expect(result.resolved).toBe(true);
|
|
239
|
+
expect(result.aliasName).toBe('disk');
|
|
240
|
+
});
|
|
241
|
+
it('未知输入应该返回 resolved: false', async () => {
|
|
242
|
+
mockGetConfig.mockReturnValue({ aliases: {} });
|
|
243
|
+
const { resolveAlias } = await resetAliasModule();
|
|
244
|
+
const result = resolveAlias('unknown command');
|
|
245
|
+
expect(result.resolved).toBe(false);
|
|
246
|
+
expect(result.prompt).toBe('unknown command');
|
|
247
|
+
});
|
|
248
|
+
it('空输入应该返回 resolved: false', async () => {
|
|
249
|
+
const { resolveAlias } = await resetAliasModule();
|
|
250
|
+
const result = resolveAlias(' ');
|
|
251
|
+
expect(result.resolved).toBe(false);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
describe('额外参数追加', () => {
|
|
255
|
+
it('应该追加额外参数到 prompt', async () => {
|
|
256
|
+
mockGetConfig.mockReturnValue({
|
|
257
|
+
aliases: {
|
|
258
|
+
disk: { prompt: '检查磁盘空间' },
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
const { resolveAlias } = await resetAliasModule();
|
|
262
|
+
const result = resolveAlias('disk /home');
|
|
263
|
+
expect(result.prompt).toBe('检查磁盘空间 /home');
|
|
264
|
+
});
|
|
265
|
+
it('应该追加多个额外参数', async () => {
|
|
266
|
+
mockGetConfig.mockReturnValue({
|
|
267
|
+
aliases: {
|
|
268
|
+
list: { prompt: '列出文件' },
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
const { resolveAlias } = await resetAliasModule();
|
|
272
|
+
const result = resolveAlias('list -la /home');
|
|
273
|
+
expect(result.prompt).toBe('列出文件 -la /home');
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
describe('模板参数替换', () => {
|
|
277
|
+
it('应该替换 {{param}} 模板参数', async () => {
|
|
278
|
+
mockGetConfig.mockReturnValue({
|
|
279
|
+
aliases: {
|
|
280
|
+
deploy: { prompt: '部署 {{env}} 环境到 {{server}}' },
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
const { resolveAlias } = await resetAliasModule();
|
|
284
|
+
const result = resolveAlias('deploy env=production server=web1');
|
|
285
|
+
expect(result.prompt).toBe('部署 production 环境到 web1');
|
|
286
|
+
});
|
|
287
|
+
it('应该支持 --key=value 格式', async () => {
|
|
288
|
+
mockGetConfig.mockReturnValue({
|
|
289
|
+
aliases: {
|
|
290
|
+
deploy: { prompt: '部署 {{env}} 环境' },
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
const { resolveAlias } = await resetAliasModule();
|
|
294
|
+
const result = resolveAlias('deploy --env=staging');
|
|
295
|
+
expect(result.prompt).toBe('部署 staging 环境');
|
|
296
|
+
});
|
|
297
|
+
it('应该使用默认值 {{param:default}}', async () => {
|
|
298
|
+
mockGetConfig.mockReturnValue({
|
|
299
|
+
aliases: {
|
|
300
|
+
deploy: { prompt: '部署 {{env:production}} 环境' },
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
const { resolveAlias } = await resetAliasModule();
|
|
304
|
+
const result = resolveAlias('deploy');
|
|
305
|
+
expect(result.prompt).toBe('部署 production 环境');
|
|
306
|
+
});
|
|
307
|
+
it('参数值应该覆盖默认值', async () => {
|
|
308
|
+
mockGetConfig.mockReturnValue({
|
|
309
|
+
aliases: {
|
|
310
|
+
deploy: { prompt: '部署 {{env:production}} 环境' },
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
const { resolveAlias } = await resetAliasModule();
|
|
314
|
+
const result = resolveAlias('deploy env=staging');
|
|
315
|
+
expect(result.prompt).toBe('部署 staging 环境');
|
|
316
|
+
});
|
|
317
|
+
it('缺少必填参数应该抛出错误', async () => {
|
|
318
|
+
mockGetConfig.mockReturnValue({
|
|
319
|
+
aliases: {
|
|
320
|
+
deploy: { prompt: '部署 {{env}} 环境' },
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
const { resolveAlias } = await resetAliasModule();
|
|
324
|
+
expect(() => resolveAlias('deploy'))
|
|
325
|
+
.toThrow('别名 "deploy" 缺少必填参数: env');
|
|
326
|
+
});
|
|
327
|
+
it('应该支持多个必填参数', async () => {
|
|
328
|
+
mockGetConfig.mockReturnValue({
|
|
329
|
+
aliases: {
|
|
330
|
+
deploy: { prompt: '部署 {{app}} 到 {{env}} 环境' },
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
const { resolveAlias } = await resetAliasModule();
|
|
334
|
+
const result = resolveAlias('deploy app=myapp env=production');
|
|
335
|
+
expect(result.prompt).toBe('部署 myapp 到 production 环境');
|
|
336
|
+
});
|
|
337
|
+
it('多个缺失参数应该全部列出', async () => {
|
|
338
|
+
mockGetConfig.mockReturnValue({
|
|
339
|
+
aliases: {
|
|
340
|
+
deploy: { prompt: '部署 {{app}} 到 {{env}}' },
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
const { resolveAlias } = await resetAliasModule();
|
|
344
|
+
expect(() => resolveAlias('deploy'))
|
|
345
|
+
.toThrow('缺少必填参数: app, env');
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
describe('混合参数', () => {
|
|
349
|
+
it('应该追加非 key=value 的额外参数', async () => {
|
|
350
|
+
mockGetConfig.mockReturnValue({
|
|
351
|
+
aliases: {
|
|
352
|
+
deploy: { prompt: '部署 {{env}} 环境' },
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
const { resolveAlias } = await resetAliasModule();
|
|
356
|
+
const result = resolveAlias('deploy env=prod --force');
|
|
357
|
+
expect(result.prompt).toBe('部署 prod 环境 --force');
|
|
358
|
+
});
|
|
359
|
+
it('originalInput 应该保留原始输入', async () => {
|
|
360
|
+
mockGetConfig.mockReturnValue({
|
|
361
|
+
aliases: {
|
|
362
|
+
disk: { prompt: '检查磁盘空间' },
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
const { resolveAlias } = await resetAliasModule();
|
|
366
|
+
const result = resolveAlias('@disk /home');
|
|
367
|
+
expect(result.originalInput).toBe('@disk /home');
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
// ============================================================================
|
|
372
|
+
// getAliasParams 测试
|
|
373
|
+
// ============================================================================
|
|
374
|
+
describe('getAliasParams', () => {
|
|
375
|
+
it('应该返回模板参数列表', async () => {
|
|
376
|
+
mockGetConfig.mockReturnValue({
|
|
377
|
+
aliases: {
|
|
378
|
+
deploy: { prompt: '部署 {{app}} 到 {{env}} 环境' },
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
const { getAliasParams } = await resetAliasModule();
|
|
382
|
+
const params = getAliasParams('deploy');
|
|
383
|
+
expect(params).toEqual(['app', 'env']);
|
|
384
|
+
});
|
|
385
|
+
it('无模板参数应该返回空数组', async () => {
|
|
386
|
+
mockGetConfig.mockReturnValue({
|
|
387
|
+
aliases: {
|
|
388
|
+
disk: { prompt: '检查磁盘空间' },
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
const { getAliasParams } = await resetAliasModule();
|
|
392
|
+
const params = getAliasParams('disk');
|
|
393
|
+
expect(params).toEqual([]);
|
|
394
|
+
});
|
|
395
|
+
it('不存在的别名应该返回空数组', async () => {
|
|
396
|
+
mockGetConfig.mockReturnValue({ aliases: {} });
|
|
397
|
+
const { getAliasParams } = await resetAliasModule();
|
|
398
|
+
const params = getAliasParams('nonexistent');
|
|
399
|
+
expect(params).toEqual([]);
|
|
400
|
+
});
|
|
401
|
+
it('应该忽略默认值部分', async () => {
|
|
402
|
+
mockGetConfig.mockReturnValue({
|
|
403
|
+
aliases: {
|
|
404
|
+
deploy: { prompt: '部署 {{env:production}} 环境' },
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
const { getAliasParams } = await resetAliasModule();
|
|
408
|
+
const params = getAliasParams('deploy');
|
|
409
|
+
expect(params).toEqual(['env']);
|
|
410
|
+
});
|
|
411
|
+
it('重复参数应该去重', async () => {
|
|
412
|
+
mockGetConfig.mockReturnValue({
|
|
413
|
+
aliases: {
|
|
414
|
+
test: { prompt: '{{env}} 和 {{env}} 和 {{app}}' },
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
const { getAliasParams } = await resetAliasModule();
|
|
418
|
+
const params = getAliasParams('test');
|
|
419
|
+
expect(params).toEqual(['env', 'app']);
|
|
420
|
+
});
|
|
421
|
+
});
|