@zjex/git-workflow 0.4.5 → 0.4.7
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/CHANGELOG.md +135 -364
- package/README.md +1 -1
- package/dist/index.js +129 -30
- package/docs/.vitepress/config.ts +2 -0
- package/docs/guide/command-quotes-handling.md +279 -0
- package/docs/guide/debug-mode.md +384 -0
- package/package.json +1 -1
- package/scripts/generate-changelog-manual.js +15 -64
- package/src/commands/tag.ts +42 -10
- package/src/commands/update.ts +25 -9
- package/src/index.ts +11 -3
- package/src/utils.ts +93 -10
- package/tests/command-with-quotes.test.ts +378 -0
- package/tests/debug-mode.test.ts +503 -0
- package/tests/update.test.ts +85 -69
package/tests/update.test.ts
CHANGED
|
@@ -7,20 +7,48 @@ import { join } from "path";
|
|
|
7
7
|
// Mock 所有外部依赖
|
|
8
8
|
vi.mock("child_process", () => ({
|
|
9
9
|
execSync: vi.fn(),
|
|
10
|
-
spawn: vi.fn(() =>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
spawn: vi.fn((command: string, args: string[]) => {
|
|
11
|
+
// 检查是否是 npm view 命令
|
|
12
|
+
if (command === "npm" && args[0] === "view") {
|
|
13
|
+
const mockProcess = {
|
|
14
|
+
stdout: {
|
|
15
|
+
on: vi.fn((event: string, callback: (data: Buffer) => void) => {
|
|
16
|
+
if (event === "data") {
|
|
17
|
+
// 立即同步调用回调,避免异步问题
|
|
18
|
+
callback(Buffer.from("1.2.4\n"));
|
|
19
|
+
}
|
|
20
|
+
}),
|
|
21
|
+
},
|
|
22
|
+
on: vi.fn((event: string, callback: (code?: number) => void) => {
|
|
23
|
+
if (event === "close") {
|
|
24
|
+
// 使用 queueMicrotask 而不是 setTimeout,确保在当前测试上下文中执行
|
|
25
|
+
queueMicrotask(() => callback(0));
|
|
26
|
+
}
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
return mockProcess;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 其他 spawn 调用(如 npm install)
|
|
33
|
+
return {
|
|
34
|
+
on: vi.fn((event: string, callback: (code?: number) => void) => {
|
|
35
|
+
if (event === "close") {
|
|
36
|
+
queueMicrotask(() => callback(0));
|
|
37
|
+
}
|
|
38
|
+
}),
|
|
39
|
+
stdin: {
|
|
40
|
+
write: vi.fn(),
|
|
41
|
+
end: vi.fn(),
|
|
42
|
+
on: vi.fn(),
|
|
43
|
+
},
|
|
44
|
+
stdout: {
|
|
45
|
+
on: vi.fn(),
|
|
46
|
+
},
|
|
47
|
+
stderr: {
|
|
48
|
+
on: vi.fn(),
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}),
|
|
24
52
|
}));
|
|
25
53
|
|
|
26
54
|
vi.mock("fs", () => ({
|
|
@@ -79,9 +107,10 @@ describe("Update 模块测试", () => {
|
|
|
79
107
|
vi.clearAllMocks();
|
|
80
108
|
// Mock console methods
|
|
81
109
|
vi.spyOn(console, "log").mockImplementation(() => {});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
110
|
+
// Mock process.exit 但不抛出错误,只是阻止退出
|
|
111
|
+
vi.spyOn(process, "exit").mockImplementation((() => {
|
|
112
|
+
// 不做任何事,只是阻止进程退出
|
|
113
|
+
}) as any);
|
|
85
114
|
|
|
86
115
|
// Default mocks
|
|
87
116
|
mockHomedir.mockReturnValue("/home/user");
|
|
@@ -94,8 +123,6 @@ describe("Update 模块测试", () => {
|
|
|
94
123
|
|
|
95
124
|
describe("版本检查", () => {
|
|
96
125
|
it("应该正确获取最新版本", async () => {
|
|
97
|
-
mockExecSync.mockReturnValueOnce("1.2.4\n");
|
|
98
|
-
|
|
99
126
|
const semver = await import("semver");
|
|
100
127
|
vi.mocked(semver.default.gte).mockReturnValue(true);
|
|
101
128
|
|
|
@@ -103,19 +130,35 @@ describe("Update 模块测试", () => {
|
|
|
103
130
|
|
|
104
131
|
await update("1.2.3");
|
|
105
132
|
|
|
106
|
-
|
|
107
|
-
|
|
133
|
+
// 验证调用了 spawn 来获取版本
|
|
134
|
+
const { spawn } = await import("child_process");
|
|
135
|
+
expect(spawn).toHaveBeenCalledWith(
|
|
136
|
+
"npm",
|
|
137
|
+
["view", "@zjex/git-workflow", "version"],
|
|
108
138
|
expect.objectContaining({
|
|
109
|
-
|
|
110
|
-
timeout: 3000,
|
|
111
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
139
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
112
140
|
}),
|
|
113
141
|
);
|
|
114
142
|
});
|
|
115
143
|
|
|
116
144
|
it("应该处理网络错误", async () => {
|
|
117
|
-
|
|
118
|
-
|
|
145
|
+
// 临时修改 spawn mock 来模拟错误
|
|
146
|
+
const { spawn } = await import("child_process");
|
|
147
|
+
vi.mocked(spawn).mockImplementationOnce(() => {
|
|
148
|
+
const mockProcess: any = {
|
|
149
|
+
stdout: {
|
|
150
|
+
on: vi.fn(),
|
|
151
|
+
},
|
|
152
|
+
on: vi.fn((event: string, callback: (code?: number) => void) => {
|
|
153
|
+
if (event === "error") {
|
|
154
|
+
setTimeout(() => callback(), 0);
|
|
155
|
+
}
|
|
156
|
+
if (event === "close") {
|
|
157
|
+
setTimeout(() => callback(1), 10);
|
|
158
|
+
}
|
|
159
|
+
}),
|
|
160
|
+
};
|
|
161
|
+
return mockProcess;
|
|
119
162
|
});
|
|
120
163
|
|
|
121
164
|
const { update } = await import("../src/commands/update.js");
|
|
@@ -128,10 +171,7 @@ describe("Update 模块测试", () => {
|
|
|
128
171
|
});
|
|
129
172
|
|
|
130
173
|
it("应该正确比较版本号", async () => {
|
|
131
|
-
mockExecSync
|
|
132
|
-
.mockReturnValueOnce("/usr/local/bin/gw\n") // which gw (isUsingVolta)
|
|
133
|
-
.mockReturnValueOnce("1.2.4\n") // npm view
|
|
134
|
-
.mockReturnValueOnce("update success"); // npm install
|
|
174
|
+
mockExecSync.mockReturnValueOnce("/usr/local/bin/gw\n"); // which gw (isUsingVolta)
|
|
135
175
|
|
|
136
176
|
const semver = await import("semver");
|
|
137
177
|
vi.mocked(semver.default.gte).mockReturnValue(false);
|
|
@@ -151,9 +191,7 @@ describe("Update 模块测试", () => {
|
|
|
151
191
|
|
|
152
192
|
describe("Volta 检测", () => {
|
|
153
193
|
it("应该正确检测 Volta 环境", async () => {
|
|
154
|
-
mockExecSync
|
|
155
|
-
.mockReturnValueOnce("/home/user/.volta/bin/gw\n") // which gw (isUsingVolta)
|
|
156
|
-
.mockReturnValueOnce("1.2.4\n"); // npm view
|
|
194
|
+
mockExecSync.mockReturnValueOnce("/home/user/.volta/bin/gw\n"); // which gw (isUsingVolta)
|
|
157
195
|
|
|
158
196
|
const semver = await import("semver");
|
|
159
197
|
vi.mocked(semver.default.gte).mockReturnValue(false);
|
|
@@ -172,9 +210,7 @@ describe("Update 模块测试", () => {
|
|
|
172
210
|
});
|
|
173
211
|
|
|
174
212
|
it("应该正确检测非 Volta 环境", async () => {
|
|
175
|
-
mockExecSync
|
|
176
|
-
.mockReturnValueOnce("/usr/local/bin/gw\n") // which gw
|
|
177
|
-
.mockReturnValueOnce("1.2.4\n"); // npm view
|
|
213
|
+
mockExecSync.mockReturnValueOnce("/usr/local/bin/gw\n"); // which gw
|
|
178
214
|
|
|
179
215
|
const semver = await import("semver");
|
|
180
216
|
vi.mocked(semver.default.gte).mockReturnValue(false);
|
|
@@ -193,12 +229,10 @@ describe("Update 模块测试", () => {
|
|
|
193
229
|
});
|
|
194
230
|
|
|
195
231
|
it("应该处理 which 命令失败", async () => {
|
|
196
|
-
mockExecSync
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
})
|
|
201
|
-
.mockReturnValueOnce("1.2.4\n"); // npm view
|
|
232
|
+
mockExecSync.mockImplementationOnce(() => {
|
|
233
|
+
// which gw (isUsingVolta)
|
|
234
|
+
throw new Error("Command not found");
|
|
235
|
+
});
|
|
202
236
|
|
|
203
237
|
const semver = await import("semver");
|
|
204
238
|
vi.mocked(semver.default.gte).mockReturnValue(false);
|
|
@@ -219,9 +253,7 @@ describe("Update 模块测试", () => {
|
|
|
219
253
|
|
|
220
254
|
describe("更新流程", () => {
|
|
221
255
|
it("应该在已是最新版本时显示提示", async () => {
|
|
222
|
-
mockExecSync
|
|
223
|
-
.mockReturnValueOnce("/usr/local/bin/gw\n") // which gw (isUsingVolta)
|
|
224
|
-
.mockReturnValueOnce("1.2.3\n"); // npm view (same version)
|
|
256
|
+
mockExecSync.mockReturnValueOnce("/usr/local/bin/gw\n"); // which gw (isUsingVolta)
|
|
225
257
|
|
|
226
258
|
const semver = await import("semver");
|
|
227
259
|
vi.mocked(semver.default.gte).mockReturnValue(true);
|
|
@@ -236,9 +268,7 @@ describe("Update 模块测试", () => {
|
|
|
236
268
|
});
|
|
237
269
|
|
|
238
270
|
it("应该成功执行更新", async () => {
|
|
239
|
-
mockExecSync
|
|
240
|
-
.mockReturnValueOnce("/usr/local/bin/gw\n") // which gw
|
|
241
|
-
.mockReturnValueOnce("1.2.4\n"); // npm view
|
|
271
|
+
mockExecSync.mockReturnValueOnce("/usr/local/bin/gw\n"); // which gw
|
|
242
272
|
|
|
243
273
|
const semver = await import("semver");
|
|
244
274
|
vi.mocked(semver.default.gte).mockReturnValue(false);
|
|
@@ -258,9 +288,7 @@ describe("Update 模块测试", () => {
|
|
|
258
288
|
});
|
|
259
289
|
|
|
260
290
|
it("应该处理更新失败", async () => {
|
|
261
|
-
mockExecSync
|
|
262
|
-
.mockReturnValueOnce("/usr/local/bin/gw\n") // which gw
|
|
263
|
-
.mockReturnValueOnce("1.2.4\n"); // npm view
|
|
291
|
+
mockExecSync.mockReturnValueOnce("/usr/local/bin/gw\n"); // which gw
|
|
264
292
|
|
|
265
293
|
const semver = await import("semver");
|
|
266
294
|
vi.mocked(semver.default.gte).mockReturnValue(false);
|
|
@@ -284,9 +312,7 @@ describe("Update 模块测试", () => {
|
|
|
284
312
|
describe("缓存管理", () => {
|
|
285
313
|
it("应该在更新成功后清理缓存", async () => {
|
|
286
314
|
mockExistsSync.mockReturnValue(true);
|
|
287
|
-
mockExecSync
|
|
288
|
-
.mockReturnValueOnce("/usr/local/bin/gw\n") // which gw
|
|
289
|
-
.mockReturnValueOnce("1.2.4\n"); // npm view
|
|
315
|
+
mockExecSync.mockReturnValueOnce("/usr/local/bin/gw\n"); // which gw
|
|
290
316
|
|
|
291
317
|
const semver = await import("semver");
|
|
292
318
|
vi.mocked(semver.default.gte).mockReturnValue(false);
|
|
@@ -307,9 +333,7 @@ describe("Update 模块测试", () => {
|
|
|
307
333
|
|
|
308
334
|
it("应该处理缓存文件不存在的情况", async () => {
|
|
309
335
|
mockExistsSync.mockReturnValue(false);
|
|
310
|
-
mockExecSync
|
|
311
|
-
.mockReturnValueOnce("/usr/local/bin/gw\n") // which gw
|
|
312
|
-
.mockReturnValueOnce("1.2.4\n"); // npm view
|
|
336
|
+
mockExecSync.mockReturnValueOnce("/usr/local/bin/gw\n"); // which gw
|
|
313
337
|
|
|
314
338
|
const semver = await import("semver");
|
|
315
339
|
vi.mocked(semver.default.gte).mockReturnValue(false);
|
|
@@ -332,9 +356,7 @@ describe("Update 模块测试", () => {
|
|
|
332
356
|
throw new Error("Permission denied");
|
|
333
357
|
});
|
|
334
358
|
|
|
335
|
-
mockExecSync
|
|
336
|
-
.mockReturnValueOnce("/usr/local/bin/gw\n") // which gw
|
|
337
|
-
.mockReturnValueOnce("1.2.4\n"); // npm view
|
|
359
|
+
mockExecSync.mockReturnValueOnce("/usr/local/bin/gw\n"); // which gw
|
|
338
360
|
|
|
339
361
|
const semver = await import("semver");
|
|
340
362
|
vi.mocked(semver.default.gte).mockReturnValue(false);
|
|
@@ -352,9 +374,7 @@ describe("Update 模块测试", () => {
|
|
|
352
374
|
|
|
353
375
|
describe("用户界面", () => {
|
|
354
376
|
it("应该显示检查更新的提示", async () => {
|
|
355
|
-
mockExecSync
|
|
356
|
-
.mockReturnValueOnce("/usr/local/bin/gw\n") // which gw
|
|
357
|
-
.mockReturnValueOnce("1.2.3\n");
|
|
377
|
+
mockExecSync.mockReturnValueOnce("/usr/local/bin/gw\n"); // which gw
|
|
358
378
|
|
|
359
379
|
const semver = await import("semver");
|
|
360
380
|
vi.mocked(semver.default.gte).mockReturnValue(true);
|
|
@@ -367,9 +387,7 @@ describe("Update 模块测试", () => {
|
|
|
367
387
|
});
|
|
368
388
|
|
|
369
389
|
it("应该显示版本比较信息", async () => {
|
|
370
|
-
mockExecSync
|
|
371
|
-
.mockReturnValueOnce("/usr/local/bin/gw\n") // which gw
|
|
372
|
-
.mockReturnValueOnce("1.2.4\n"); // npm view
|
|
390
|
+
mockExecSync.mockReturnValueOnce("/usr/local/bin/gw\n"); // which gw
|
|
373
391
|
|
|
374
392
|
const semver = await import("semver");
|
|
375
393
|
vi.mocked(semver.default.gte).mockReturnValue(false);
|
|
@@ -390,9 +408,7 @@ describe("Update 模块测试", () => {
|
|
|
390
408
|
});
|
|
391
409
|
|
|
392
410
|
it("应该显示验证命令提示", async () => {
|
|
393
|
-
mockExecSync
|
|
394
|
-
.mockReturnValueOnce("/usr/local/bin/gw\n") // which gw
|
|
395
|
-
.mockReturnValueOnce("1.2.4\n"); // npm view
|
|
411
|
+
mockExecSync.mockReturnValueOnce("/usr/local/bin/gw\n"); // which gw
|
|
396
412
|
|
|
397
413
|
const semver = await import("semver");
|
|
398
414
|
vi.mocked(semver.default.gte).mockReturnValue(false);
|