@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.
- package/.turbo/turbo-build.log +6 -9
- package/CHANGELOG.md +27 -0
- package/dist/index.js +993 -3956
- package/package.json +15 -37
- package/src/README.md +126 -0
- package/src/cli.tsx +45 -0
- package/src/commands/help.tsx +79 -0
- package/src/commands/login.tsx +92 -0
- package/src/commands/start.tsx +411 -0
- package/src/index.tsx +8 -0
- package/src/utils/auth.ts +218 -21
- package/src/utils/bundle.ts +177 -0
- package/src/utils/config.ts +123 -0
- package/src/utils/esbuild.ts +541 -0
- package/tsconfig.json +10 -15
- package/tsup.config.ts +7 -7
- package/CLAUDE_CLI.md +0 -265
- package/README.md +0 -711
- package/__tests__/mocks/console.ts +0 -22
- package/__tests__/mocks/core.ts +0 -137
- package/__tests__/mocks/index.ts +0 -4
- package/__tests__/mocks/inquirer.ts +0 -16
- package/__tests__/mocks/progress.ts +0 -19
- package/dist/index.d.ts +0 -1
- package/src/__tests__/helpers/cli-test-helper.ts +0 -281
- package/src/__tests__/mocks/index.ts +0 -142
- package/src/actions/component.actions.ts +0 -278
- package/src/actions/function.actions.ts +0 -220
- package/src/actions/project.actions.ts +0 -131
- package/src/actions/version.actions.ts +0 -233
- package/src/commands/__tests__/component-validation.test.ts +0 -250
- package/src/commands/__tests__/component.test.ts +0 -318
- package/src/commands/__tests__/function-validation.test.ts +0 -220
- package/src/commands/__tests__/function.test.ts +0 -286
- package/src/commands/__tests__/store-version-validation.test.ts +0 -414
- package/src/commands/__tests__/store-version.test.ts +0 -402
- package/src/commands/component.ts +0 -178
- package/src/commands/docs.ts +0 -24
- package/src/commands/function.ts +0 -201
- package/src/commands/help.ts +0 -18
- package/src/commands/index.ts +0 -27
- package/src/commands/login.ts +0 -267
- package/src/commands/project.ts +0 -107
- package/src/commands/store-version.ts +0 -242
- package/src/commands/version.ts +0 -51
- package/src/commands/whoami.ts +0 -46
- package/src/index.ts +0 -116
- package/src/prompts/component.prompts.ts +0 -94
- package/src/prompts/function.prompts.ts +0 -168
- package/src/schemas/command.schema.ts +0 -644
- package/src/types/index.ts +0 -183
- package/src/utils/__tests__/command-parser.test.ts +0 -159
- package/src/utils/__tests__/command-suggestions.test.ts +0 -185
- package/src/utils/__tests__/console.test.ts +0 -192
- package/src/utils/__tests__/context-detector.test.ts +0 -258
- package/src/utils/__tests__/enhanced-error-handler.test.ts +0 -137
- package/src/utils/__tests__/error-handler.test.ts +0 -107
- package/src/utils/__tests__/rich-progress.test.ts +0 -181
- package/src/utils/__tests__/validation-error-formatter.test.ts +0 -175
- package/src/utils/__tests__/validation-helpers.test.ts +0 -125
- package/src/utils/cli-progress-reporter.ts +0 -84
- package/src/utils/command-builder.ts +0 -390
- package/src/utils/command-helpers.ts +0 -83
- package/src/utils/command-parser.ts +0 -245
- package/src/utils/command-suggestions.ts +0 -176
- package/src/utils/console.ts +0 -320
- package/src/utils/constants.ts +0 -39
- package/src/utils/context-detector.ts +0 -177
- package/src/utils/deploy-helpers.ts +0 -357
- package/src/utils/enhanced-error-handler.ts +0 -264
- package/src/utils/error-handler.ts +0 -60
- package/src/utils/errors.ts +0 -256
- package/src/utils/interactive-builder.ts +0 -325
- package/src/utils/rich-progress.ts +0 -331
- package/src/utils/store.ts +0 -23
- package/src/utils/validation-error-formatter.ts +0 -337
- package/src/utils/validation-helpers.ts +0 -325
- package/vitest.config.ts +0 -35
- 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
|
-
});
|