@ollie-shop/cli 0.3.4 → 1.0.1

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.
Files changed (79) hide show
  1. package/.turbo/turbo-build.log +6 -9
  2. package/CHANGELOG.md +27 -0
  3. package/dist/index.js +993 -3956
  4. package/package.json +15 -37
  5. package/src/README.md +126 -0
  6. package/src/cli.tsx +45 -0
  7. package/src/commands/help.tsx +79 -0
  8. package/src/commands/login.tsx +92 -0
  9. package/src/commands/start.tsx +411 -0
  10. package/src/index.tsx +8 -0
  11. package/src/utils/auth.ts +218 -21
  12. package/src/utils/bundle.ts +177 -0
  13. package/src/utils/config.ts +123 -0
  14. package/src/utils/esbuild.ts +541 -0
  15. package/tsconfig.json +10 -15
  16. package/tsup.config.ts +7 -7
  17. package/CLAUDE_CLI.md +0 -265
  18. package/README.md +0 -711
  19. package/__tests__/mocks/console.ts +0 -22
  20. package/__tests__/mocks/core.ts +0 -137
  21. package/__tests__/mocks/index.ts +0 -4
  22. package/__tests__/mocks/inquirer.ts +0 -16
  23. package/__tests__/mocks/progress.ts +0 -19
  24. package/dist/index.d.ts +0 -1
  25. package/src/__tests__/helpers/cli-test-helper.ts +0 -281
  26. package/src/__tests__/mocks/index.ts +0 -142
  27. package/src/actions/component.actions.ts +0 -278
  28. package/src/actions/function.actions.ts +0 -220
  29. package/src/actions/project.actions.ts +0 -131
  30. package/src/actions/version.actions.ts +0 -233
  31. package/src/commands/__tests__/component-validation.test.ts +0 -250
  32. package/src/commands/__tests__/component.test.ts +0 -318
  33. package/src/commands/__tests__/function-validation.test.ts +0 -220
  34. package/src/commands/__tests__/function.test.ts +0 -286
  35. package/src/commands/__tests__/store-version-validation.test.ts +0 -414
  36. package/src/commands/__tests__/store-version.test.ts +0 -402
  37. package/src/commands/component.ts +0 -178
  38. package/src/commands/docs.ts +0 -24
  39. package/src/commands/function.ts +0 -201
  40. package/src/commands/help.ts +0 -18
  41. package/src/commands/index.ts +0 -27
  42. package/src/commands/login.ts +0 -267
  43. package/src/commands/project.ts +0 -107
  44. package/src/commands/store-version.ts +0 -242
  45. package/src/commands/version.ts +0 -51
  46. package/src/commands/whoami.ts +0 -46
  47. package/src/index.ts +0 -116
  48. package/src/prompts/component.prompts.ts +0 -94
  49. package/src/prompts/function.prompts.ts +0 -168
  50. package/src/schemas/command.schema.ts +0 -644
  51. package/src/types/index.ts +0 -183
  52. package/src/utils/__tests__/command-parser.test.ts +0 -159
  53. package/src/utils/__tests__/command-suggestions.test.ts +0 -185
  54. package/src/utils/__tests__/console.test.ts +0 -192
  55. package/src/utils/__tests__/context-detector.test.ts +0 -258
  56. package/src/utils/__tests__/enhanced-error-handler.test.ts +0 -137
  57. package/src/utils/__tests__/error-handler.test.ts +0 -107
  58. package/src/utils/__tests__/rich-progress.test.ts +0 -181
  59. package/src/utils/__tests__/validation-error-formatter.test.ts +0 -175
  60. package/src/utils/__tests__/validation-helpers.test.ts +0 -125
  61. package/src/utils/cli-progress-reporter.ts +0 -84
  62. package/src/utils/command-builder.ts +0 -390
  63. package/src/utils/command-helpers.ts +0 -83
  64. package/src/utils/command-parser.ts +0 -245
  65. package/src/utils/command-suggestions.ts +0 -176
  66. package/src/utils/console.ts +0 -320
  67. package/src/utils/constants.ts +0 -39
  68. package/src/utils/context-detector.ts +0 -177
  69. package/src/utils/deploy-helpers.ts +0 -357
  70. package/src/utils/enhanced-error-handler.ts +0 -264
  71. package/src/utils/error-handler.ts +0 -60
  72. package/src/utils/errors.ts +0 -256
  73. package/src/utils/interactive-builder.ts +0 -325
  74. package/src/utils/rich-progress.ts +0 -331
  75. package/src/utils/store.ts +0 -23
  76. package/src/utils/validation-error-formatter.ts +0 -337
  77. package/src/utils/validation-helpers.ts +0 -325
  78. package/vitest.config.ts +0 -35
  79. package/vitest.setup.ts +0 -29
@@ -1,192 +0,0 @@
1
- import chalk from "chalk";
2
- import { beforeEach, describe, expect, it, vi } from "vitest";
3
- import { Console } from "../console";
4
-
5
- // Mock chalk
6
- vi.mock("chalk", () => ({
7
- default: {
8
- green: vi.fn((str: string) => `[GREEN]${str}[/GREEN]`),
9
- red: Object.assign(
10
- vi.fn((str: string) => `[RED]${str}[/RED]`),
11
- {
12
- bold: vi.fn((str: string) => `[RED-BOLD]${str}[/RED-BOLD]`),
13
- },
14
- ),
15
- yellow: vi.fn((str: string) => `[YELLOW]${str}[/YELLOW]`),
16
- blue: Object.assign(
17
- vi.fn((str: string) => `[BLUE]${str}[/BLUE]`),
18
- {
19
- bold: vi.fn((str: string) => `[BLUE-BOLD]${str}[/BLUE-BOLD]`),
20
- },
21
- ),
22
- gray: vi.fn((str: string) => `[GRAY]${str}[/GRAY]`),
23
- cyan: vi.fn((str: string) => `[CYAN]${str}[/CYAN]`),
24
- level: 0,
25
- },
26
- }));
27
-
28
- // Mock ora
29
- vi.mock("ora", () => ({
30
- default: vi.fn(() => ({
31
- start: vi.fn().mockReturnThis(),
32
- succeed: vi.fn().mockReturnThis(),
33
- fail: vi.fn().mockReturnThis(),
34
- warn: vi.fn().mockReturnThis(),
35
- stop: vi.fn().mockReturnThis(),
36
- })),
37
- }));
38
-
39
- describe("Console Class", () => {
40
- let consoleInstance: Console;
41
-
42
- beforeEach(() => {
43
- vi.clearAllMocks();
44
- vi.spyOn(global.console, "log").mockImplementation(() => {
45
- // Intentionally empty for test isolation
46
- });
47
- vi.spyOn(global.console, "error").mockImplementation(() => {
48
- // Intentionally empty for test isolation
49
- });
50
- consoleInstance = new Console();
51
- });
52
-
53
- afterEach(() => {
54
- vi.restoreAllMocks();
55
- });
56
-
57
- describe("setOptions", () => {
58
- it("should set noColor option", () => {
59
- // Act
60
- consoleInstance.setOptions({ noColor: true });
61
-
62
- // Assert
63
- expect(chalk.level).toBe(0);
64
- });
65
- });
66
-
67
- describe("success", () => {
68
- it("should log success message in green", () => {
69
- // Act
70
- consoleInstance.success("Operation completed");
71
-
72
- // Assert
73
- expect(global.console.log).toHaveBeenCalledWith(
74
- expect.stringContaining(
75
- "[BLUE-BOLD][ollie][/BLUE-BOLD] [GREEN]āœ“[/GREEN] [GREEN]Operation completed[/GREEN]",
76
- ),
77
- );
78
- });
79
- });
80
-
81
- describe("error", () => {
82
- it("should log error message in red", () => {
83
- // Act
84
- consoleInstance.error("Operation failed");
85
-
86
- // Assert
87
- expect(global.console.error).toHaveBeenCalledWith(
88
- expect.stringContaining(
89
- "[BLUE-BOLD][ollie][/BLUE-BOLD] [RED]āœ—[/RED] [RED-BOLD]Operation failed[/RED-BOLD]",
90
- ),
91
- );
92
- });
93
- });
94
-
95
- describe("info", () => {
96
- it("should log info message", () => {
97
- // Act
98
- consoleInstance.info("Information message");
99
-
100
- // Assert
101
- expect(global.console.log).toHaveBeenCalledWith(
102
- expect.stringContaining("Information message"),
103
- );
104
- });
105
- });
106
-
107
- describe("warn", () => {
108
- it("should log warning message in yellow", () => {
109
- // Act
110
- consoleInstance.warn("Warning message");
111
-
112
- // Assert
113
- expect(global.console.warn).toHaveBeenCalledWith(
114
- expect.stringContaining(
115
- "[BLUE-BOLD][ollie][/BLUE-BOLD] [YELLOW]⚠[/YELLOW] [YELLOW]Warning message[/YELLOW]",
116
- ),
117
- );
118
- });
119
- });
120
-
121
- describe("hint", () => {
122
- it("should log hint message in gray", () => {
123
- // Act
124
- consoleInstance.hint("Hint message");
125
-
126
- // Assert
127
- expect(global.console.log).toHaveBeenCalledWith(
128
- expect.stringContaining("[GRAY]Hint message[/GRAY]"),
129
- );
130
- });
131
- });
132
-
133
- describe("spinner", () => {
134
- it("should create and return a spinner", () => {
135
- // Act
136
- const spinner = consoleInstance.spinner("Loading...");
137
-
138
- // Assert
139
- expect(spinner).toBeDefined();
140
- expect(spinner.start).toBeDefined();
141
- });
142
- });
143
-
144
- describe("quiet mode", () => {
145
- it("should not log messages when quiet mode is enabled", () => {
146
- // Arrange
147
- consoleInstance.setOptions({ quiet: true });
148
-
149
- // Act
150
- consoleInstance.info("Should not appear");
151
- consoleInstance.success("Should not appear");
152
- consoleInstance.warn("Should not appear");
153
-
154
- // Assert
155
- expect(global.console.log).not.toHaveBeenCalled();
156
- });
157
-
158
- it("should still log errors in quiet mode", () => {
159
- // Arrange
160
- consoleInstance.setOptions({ quiet: true });
161
-
162
- // Act
163
- consoleInstance.error("Error should appear");
164
-
165
- // Assert
166
- expect(global.console.error).toHaveBeenCalled();
167
- });
168
- });
169
-
170
- describe("verbose mode", () => {
171
- it("should log debug messages when verbose mode is enabled", () => {
172
- // Arrange
173
- consoleInstance.setOptions({ verbose: true });
174
-
175
- // Act
176
- consoleInstance.debug("Debug message");
177
-
178
- // Assert
179
- expect(global.console.log).toHaveBeenCalledWith(
180
- expect.stringContaining("[GRAY]Debug message[/GRAY]"),
181
- );
182
- });
183
-
184
- it("should not log debug messages when verbose mode is disabled", () => {
185
- // Act
186
- consoleInstance.debug("Debug message");
187
-
188
- // Assert
189
- expect(global.console.log).not.toHaveBeenCalled();
190
- });
191
- });
192
- });
@@ -1,258 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { beforeEach, describe, expect, it, vi } from "vitest";
4
- import { detectProjectContext, getContextualPath } from "../context-detector";
5
-
6
- // Mock fs module
7
- vi.mock("fs", () => ({
8
- existsSync: vi.fn(),
9
- readFileSync: vi.fn(),
10
- statSync: vi.fn(),
11
- }));
12
-
13
- describe("Context Detector", () => {
14
- beforeEach(() => {
15
- vi.clearAllMocks();
16
- });
17
-
18
- describe("detectProjectContext", () => {
19
- it("should detect component directory by meta.json", () => {
20
- const mockPath = "/project/components/header";
21
- vi.mocked(fs.existsSync).mockImplementation((p) => {
22
- if (p === path.join(mockPath, "package.json")) return true;
23
- if (p === path.join(mockPath, "meta.json")) return true;
24
- return false;
25
- });
26
-
27
- vi.mocked(fs.readFileSync).mockReturnValue(
28
- JSON.stringify({
29
- type: "component",
30
- name: "header",
31
- }),
32
- );
33
-
34
- const result = detectProjectContext(mockPath);
35
-
36
- expect(result.type).toBe("component");
37
- expect(result.path).toBe(mockPath);
38
- expect(result.name).toBe("header");
39
- });
40
-
41
- it("should detect component directory by index.tsx", () => {
42
- const mockPath = "/project/components/footer";
43
- vi.mocked(fs.existsSync).mockImplementation((p) => {
44
- if (p === path.join(mockPath, "package.json")) return true;
45
- if (p === path.join(mockPath, "meta.json")) return true;
46
- if (p === path.join(mockPath, "index.tsx")) return true;
47
- return false;
48
- });
49
-
50
- vi.mocked(fs.readFileSync).mockReturnValue("{}");
51
-
52
- const result = detectProjectContext(mockPath);
53
-
54
- expect(result.type).toBe("component");
55
- expect(result.path).toBe(mockPath);
56
- expect(result.name).toBe("footer");
57
- });
58
-
59
- it("should detect function directory by meta.json", () => {
60
- const mockPath = "/project/functions/validate-order";
61
- vi.mocked(fs.existsSync).mockImplementation((p) => {
62
- if (p === path.join(mockPath, "package.json")) return true;
63
- if (p === path.join(mockPath, "meta.json")) return true;
64
- return false;
65
- });
66
-
67
- vi.mocked(fs.readFileSync).mockReturnValue(
68
- JSON.stringify({
69
- type: "function",
70
- name: "validate-order",
71
- }),
72
- );
73
-
74
- const result = detectProjectContext(mockPath);
75
-
76
- expect(result.type).toBe("function");
77
- expect(result.path).toBe(mockPath);
78
- expect(result.name).toBe("validate-order");
79
- });
80
-
81
- it("should detect function directory by index.ts", () => {
82
- const mockPath = "/project/functions/process-payment";
83
- vi.mocked(fs.existsSync).mockImplementation((p) => {
84
- if (p === path.join(mockPath, "package.json")) return true;
85
- if (p === path.join(mockPath, "meta.json")) return true;
86
- if (p === path.join(mockPath, "index.ts")) return true;
87
- return false;
88
- });
89
-
90
- vi.mocked(fs.readFileSync).mockReturnValue("{}");
91
-
92
- const result = detectProjectContext(mockPath);
93
-
94
- expect(result.type).toBe("function");
95
- expect(result.path).toBe(mockPath);
96
- expect(result.name).toBe("process-payment");
97
- });
98
-
99
- it("should detect project root with components directory", () => {
100
- const mockPath = "/project";
101
- vi.mocked(fs.existsSync).mockImplementation((p) => {
102
- if (p === path.join(mockPath, "components")) return true;
103
- return false;
104
- });
105
-
106
- vi.mocked(fs.statSync).mockReturnValue({
107
- isDirectory: () => true,
108
- } as fs.Stats);
109
-
110
- const result = detectProjectContext(mockPath);
111
-
112
- expect(result.type).toBe("unknown");
113
- expect(result.path).toBe(mockPath);
114
- expect(result.parentPath).toBe(mockPath);
115
- });
116
-
117
- it("should detect when inside components/name directory structure", () => {
118
- const mockPath = "/project/components/my-component";
119
- vi.mocked(fs.existsSync).mockReturnValue(false);
120
-
121
- const result = detectProjectContext(mockPath);
122
-
123
- expect(result.type).toBe("component");
124
- expect(result.path).toBe(mockPath);
125
- expect(result.name).toBe("my-component");
126
- });
127
-
128
- it("should detect when inside functions/name directory structure", () => {
129
- const mockPath = "/project/functions/my-function";
130
- vi.mocked(fs.existsSync).mockReturnValue(false);
131
-
132
- const result = detectProjectContext(mockPath);
133
-
134
- expect(result.type).toBe("function");
135
- expect(result.path).toBe(mockPath);
136
- expect(result.name).toBe("my-function");
137
- });
138
-
139
- it("should return unknown for unrecognized directory", () => {
140
- const mockPath = "/some/random/path";
141
- vi.mocked(fs.existsSync).mockReturnValue(false);
142
-
143
- const result = detectProjectContext(mockPath);
144
-
145
- expect(result.type).toBe("unknown");
146
- expect(result.path).toBe(mockPath);
147
- expect(result.name).toBeUndefined();
148
- });
149
-
150
- it("should handle invalid meta.json gracefully", () => {
151
- const mockPath = "/project/components/broken";
152
- vi.mocked(fs.existsSync).mockImplementation((p) => {
153
- if (p === path.join(mockPath, "package.json")) return true;
154
- if (p === path.join(mockPath, "meta.json")) return true;
155
- if (p === path.join(mockPath, "index.tsx")) return true;
156
- return false;
157
- });
158
-
159
- vi.mocked(fs.readFileSync).mockReturnValue("invalid json");
160
-
161
- const result = detectProjectContext(mockPath);
162
-
163
- // Should fall back to file detection
164
- expect(result.type).toBe("component");
165
- expect(result.path).toBe(mockPath);
166
- });
167
- });
168
-
169
- describe("getContextualPath", () => {
170
- it("should return explicit path when provided", () => {
171
- const explicitPath = "/explicit/path";
172
- const result = getContextualPath(explicitPath, "component");
173
-
174
- expect(result).toBe(explicitPath);
175
- });
176
-
177
- it("should return current directory when in matching context", () => {
178
- const mockPath = "/project/components/header";
179
- vi.mocked(fs.existsSync).mockImplementation((p) => {
180
- if (p === path.join(mockPath, "package.json")) return true;
181
- if (p === path.join(mockPath, "meta.json")) return true;
182
- if (p === path.join(mockPath, "index.tsx")) return true;
183
- return false;
184
- });
185
-
186
- vi.mocked(fs.readFileSync).mockReturnValue("{}");
187
-
188
- // Mock process.cwd to return our test path
189
- const originalCwd = process.cwd;
190
- process.cwd = vi.fn().mockReturnValue(mockPath);
191
-
192
- const result = getContextualPath(undefined, "component");
193
-
194
- expect(result).toBe(mockPath);
195
-
196
- process.cwd = originalCwd;
197
- });
198
-
199
- it("should throw error when in parent directory", () => {
200
- const mockPath = "/project";
201
- vi.mocked(fs.existsSync).mockImplementation((p) => {
202
- if (p === path.join(mockPath, "components")) return true;
203
- return false;
204
- });
205
-
206
- vi.mocked(fs.statSync).mockReturnValue({
207
- isDirectory: () => true,
208
- } as fs.Stats);
209
-
210
- // Mock process.cwd to return our test path
211
- const originalCwd = process.cwd;
212
- process.cwd = vi.fn().mockReturnValue(mockPath);
213
-
214
- expect(() => getContextualPath(undefined, "component")).toThrowError(
215
- /No component found in current directory/,
216
- );
217
-
218
- process.cwd = originalCwd;
219
- });
220
-
221
- it("should throw error when in wrong context type", () => {
222
- const mockPath = "/project/functions/my-function";
223
- vi.mocked(fs.existsSync).mockImplementation((p) => {
224
- if (p === path.join(mockPath, "package.json")) return true;
225
- if (p === path.join(mockPath, "meta.json")) return true;
226
- if (p === path.join(mockPath, "index.ts")) return true;
227
- return false;
228
- });
229
-
230
- vi.mocked(fs.readFileSync).mockReturnValue("{}");
231
-
232
- // Mock process.cwd to return our test path
233
- const originalCwd = process.cwd;
234
- process.cwd = vi.fn().mockReturnValue(mockPath);
235
-
236
- expect(() => getContextualPath(undefined, "component")).toThrowError(
237
- /Current directory appears to be a function, not a component/,
238
- );
239
-
240
- process.cwd = originalCwd;
241
- });
242
-
243
- it("should return current directory for unknown context", () => {
244
- const mockPath = "/some/random/path";
245
- vi.mocked(fs.existsSync).mockReturnValue(false);
246
-
247
- // Mock process.cwd to return our test path
248
- const originalCwd = process.cwd;
249
- process.cwd = vi.fn().mockReturnValue(mockPath);
250
-
251
- const result = getContextualPath(undefined, "component");
252
-
253
- expect(result).toBe(mockPath);
254
-
255
- process.cwd = originalCwd;
256
- });
257
- });
258
- });
@@ -1,137 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import type { CliConsole } from "../console";
3
- import {
4
- EnhancedErrorHandler,
5
- createContextualError,
6
- } from "../enhanced-error-handler";
7
-
8
- describe("EnhancedErrorHandler", () => {
9
- let mockConsole: CliConsole;
10
- let handler: EnhancedErrorHandler;
11
-
12
- beforeEach(() => {
13
- // Create a mock console
14
- mockConsole = {
15
- log: vi.fn(),
16
- error: vi.fn(),
17
- warn: vi.fn(),
18
- info: vi.fn(),
19
- success: vi.fn(),
20
- dim: vi.fn(),
21
- bold: vi.fn(),
22
- list: vi.fn(),
23
- table: vi.fn(),
24
- json: vi.fn(),
25
- spinner: vi.fn(),
26
- confirm: vi.fn(),
27
- prompt: vi.fn(),
28
- setOptions: vi.fn(),
29
- group: vi.fn(),
30
- groupEnd: vi.fn(),
31
- separator: vi.fn(),
32
- suggestions: vi.fn(),
33
- debug: vi.fn(),
34
- nextSteps: vi.fn(),
35
- };
36
- handler = new EnhancedErrorHandler(mockConsole);
37
- vi.clearAllMocks();
38
- });
39
-
40
- describe("handle", () => {
41
- it("should display basic error message", () => {
42
- const error = new Error("Something went wrong");
43
- handler.handle(error);
44
-
45
- expect(mockConsole.error).toHaveBeenCalledWith(
46
- "\nāŒ Something went wrong\n",
47
- );
48
- });
49
-
50
- it("should show recovery suggestions for ENOENT errors", () => {
51
- const error = createContextualError("File not found", "ENOENT");
52
- handler.handle(error);
53
-
54
- expect(mockConsole.warn).toHaveBeenCalledWith("šŸ’” Possible solutions:\n");
55
- expect(mockConsole.log).toHaveBeenCalledWith(
56
- expect.stringContaining("Check if the path exists"),
57
- );
58
- expect(mockConsole.log).toHaveBeenCalledWith(
59
- expect.stringContaining("--path"),
60
- );
61
- });
62
-
63
- it("should show recovery suggestions for ECONNREFUSED errors", () => {
64
- const error = createContextualError("Connection failed", "ECONNREFUSED");
65
- handler.handle(error);
66
-
67
- expect(mockConsole.warn).toHaveBeenCalledWith("šŸ’” Possible solutions:\n");
68
- expect(mockConsole.log).toHaveBeenCalledWith(
69
- expect.stringContaining("Check your internet connection"),
70
- );
71
- expect(mockConsole.log).toHaveBeenCalledWith(
72
- expect.stringContaining("ollieshop login"),
73
- );
74
- });
75
-
76
- it("should show recovery suggestions for component exists errors", () => {
77
- const error = new Error("Component header-nav already exists");
78
- handler.handle(error, { name: "header-nav" });
79
-
80
- expect(mockConsole.warn).toHaveBeenCalledWith("šŸ’” Possible solutions:\n");
81
- expect(mockConsole.log).toHaveBeenCalledWith(
82
- expect.stringContaining("Use a different name"),
83
- );
84
- });
85
-
86
- it("should display contextual information", () => {
87
- const error = new Error("Test error");
88
- handler.handle(error, { path: "/test/path" });
89
-
90
- expect(mockConsole.log).toHaveBeenCalledWith(
91
- expect.stringContaining("šŸ“ Context:"),
92
- );
93
- expect(mockConsole.log).toHaveBeenCalledWith(
94
- expect.stringContaining("Current directory:"),
95
- );
96
- expect(mockConsole.log).toHaveBeenCalledWith(
97
- expect.stringContaining("Target path: /test/path"),
98
- );
99
- });
100
-
101
- it("should suggest similar commands for command errors", () => {
102
- const error = createContextualError(
103
- "Unknown command",
104
- "COMMAND_NOT_FOUND",
105
- {
106
- attemptedCommand: "component crate",
107
- },
108
- );
109
- handler.handle(error, { attemptedCommand: "component crate" });
110
-
111
- expect(mockConsole.log).toHaveBeenCalledWith(
112
- expect.stringContaining("šŸ’” Did you mean:"),
113
- );
114
- expect(mockConsole.log).toHaveBeenCalledWith(
115
- expect.stringContaining("component create"),
116
- );
117
- });
118
- });
119
-
120
- describe("createContextualError", () => {
121
- it("should create error with code", () => {
122
- const error = createContextualError("Test error", "TEST_CODE");
123
-
124
- expect(error.message).toBe("Test error");
125
- expect((error as Error & { code?: string }).code).toBe("TEST_CODE");
126
- });
127
-
128
- it("should create error with context", () => {
129
- const context = { foo: "bar" };
130
- const error = createContextualError("Test error", undefined, context);
131
-
132
- expect(
133
- (error as Error & { context?: Record<string, unknown> }).context,
134
- ).toEqual(context);
135
- });
136
- });
137
- });
@@ -1,107 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import { console as cliConsole } from "../console";
3
- import { handleError } from "../error-handler";
4
- import { CommandError, ValidationError } from "../errors";
5
-
6
- // Mock console with all required methods
7
- vi.mock("../console", () => ({
8
- console: {
9
- log: vi.fn(),
10
- error: vi.fn(),
11
- warn: vi.fn(),
12
- info: vi.fn(),
13
- success: vi.fn(),
14
- dim: vi.fn(),
15
- bold: vi.fn(),
16
- list: vi.fn(),
17
- table: vi.fn(),
18
- json: vi.fn(),
19
- spinner: vi.fn(),
20
- confirm: vi.fn(),
21
- prompt: vi.fn(),
22
- setOptions: vi.fn(),
23
- group: vi.fn(),
24
- groupEnd: vi.fn(),
25
- separator: vi.fn(),
26
- suggestions: vi.fn(),
27
- debug: vi.fn(),
28
- nextSteps: vi.fn(),
29
- },
30
- }));
31
-
32
- // Mock process.exit to follow CLI testing best practices
33
- const _mockExit = vi.spyOn(process, "exit").mockImplementation((code) => {
34
- throw new Error(`process.exit(${code})`);
35
- });
36
-
37
- describe("Error Handler", () => {
38
- beforeEach(() => {
39
- vi.clearAllMocks();
40
- process.env.DEBUG = undefined;
41
- });
42
-
43
- it("should handle CommandError with specific message", () => {
44
- // Arrange
45
- const error = new CommandError("Command failed", "COMMAND_FAILED");
46
-
47
- // Act & Assert
48
- expect(() => handleError(error)).toThrow(/process\.exit.*1/);
49
- expect(cliConsole.error).toHaveBeenCalledWith("\nCommand failed");
50
- });
51
-
52
- it("should handle ValidationError with details", () => {
53
- // Arrange
54
- const error = new ValidationError("Validation failed", [
55
- { field: "name", message: "Name is required" },
56
- { field: "version", message: "Invalid version format" },
57
- ]);
58
-
59
- // Act & Assert
60
- expect(() => handleError(error)).toThrow(/process\.exit.*1/);
61
- expect(cliConsole.error).toHaveBeenCalledWith("\nValidation failed");
62
- expect(cliConsole.dim).toHaveBeenCalledWith("\nContext:");
63
- expect(cliConsole.dim).toHaveBeenCalledWith(" fields:");
64
- expect(cliConsole.dim).toHaveBeenCalledWith(" name: Name is required");
65
- expect(cliConsole.dim).toHaveBeenCalledWith(
66
- " version: Invalid version format",
67
- );
68
- });
69
-
70
- it("should handle generic Error", () => {
71
- // Arrange
72
- const error = new Error("Something went wrong");
73
-
74
- // Act & Assert
75
- expect(() => handleError(error)).toThrow(/process\.exit.*1/);
76
- // Enhanced error handler formats with emoji
77
- expect(cliConsole.error).toHaveBeenCalledWith(
78
- "\nāŒ Something went wrong\n",
79
- );
80
- });
81
-
82
- it("should handle non-Error objects", () => {
83
- // Arrange
84
- const error = "String error";
85
-
86
- // Act & Assert
87
- expect(() => handleError(error)).toThrow(/process\.exit.*1/);
88
- expect(cliConsole.error).toHaveBeenCalledWith(
89
- "\nAn unexpected error occurred",
90
- );
91
- expect(cliConsole.dim).toHaveBeenCalledWith("String error");
92
- });
93
-
94
- it("should show debug information when NODE_ENV is development", () => {
95
- // Arrange
96
- process.env.DEBUG = "1";
97
- const error = new Error("Debug error");
98
- error.stack = "Error: Debug error\n at test.js:1:1";
99
-
100
- // Act & Assert
101
- expect(() => handleError(error)).toThrow(/process\.exit.*1/);
102
- // Enhanced error handler shows context instead of stack trace in dim
103
- expect(cliConsole.log).toHaveBeenCalledWith(
104
- expect.stringContaining("šŸ“ Context:"),
105
- );
106
- });
107
- });