@ollie-shop/cli 0.2.0 → 0.3.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.
Files changed (182) hide show
  1. package/.turbo/turbo-build.log +2 -11
  2. package/CHANGELOG.md +13 -7
  3. package/CLAUDE_CLI.md +265 -0
  4. package/README.md +704 -8
  5. package/__tests__/mocks/console.ts +22 -0
  6. package/__tests__/mocks/core.ts +137 -0
  7. package/__tests__/mocks/index.ts +4 -0
  8. package/__tests__/mocks/inquirer.ts +16 -0
  9. package/__tests__/mocks/progress.ts +19 -0
  10. package/dist/__tests__/helpers/cli-test-helper.d.ts +89 -0
  11. package/dist/__tests__/helpers/cli-test-helper.d.ts.map +1 -0
  12. package/dist/__tests__/helpers/cli-test-helper.js +220 -0
  13. package/dist/__tests__/mocks/index.d.ts +69 -0
  14. package/dist/__tests__/mocks/index.d.ts.map +1 -0
  15. package/dist/__tests__/mocks/index.js +77 -0
  16. package/dist/actions/component.actions.d.ts +14 -0
  17. package/dist/actions/component.actions.d.ts.map +1 -0
  18. package/dist/actions/component.actions.js +273 -0
  19. package/dist/actions/function.actions.d.ts +15 -0
  20. package/dist/actions/function.actions.d.ts.map +1 -0
  21. package/dist/actions/function.actions.js +254 -0
  22. package/dist/actions/project.actions.d.ts +17 -0
  23. package/dist/actions/project.actions.d.ts.map +1 -0
  24. package/dist/actions/project.actions.js +97 -0
  25. package/dist/actions/version.actions.d.ts +19 -0
  26. package/dist/actions/version.actions.d.ts.map +1 -0
  27. package/dist/actions/version.actions.js +216 -0
  28. package/dist/commands/component.d.ts +3 -0
  29. package/dist/commands/component.d.ts.map +1 -0
  30. package/dist/commands/component.js +192 -0
  31. package/dist/commands/docs.d.ts +3 -0
  32. package/dist/commands/docs.d.ts.map +1 -0
  33. package/dist/commands/docs.js +16 -0
  34. package/dist/commands/function.d.ts +3 -0
  35. package/dist/commands/function.d.ts.map +1 -0
  36. package/dist/commands/function.js +243 -0
  37. package/dist/commands/help.d.ts +3 -0
  38. package/dist/commands/help.d.ts.map +1 -0
  39. package/dist/commands/help.js +20 -0
  40. package/dist/commands/index.d.ts +3 -0
  41. package/dist/commands/index.d.ts.map +1 -0
  42. package/dist/commands/index.js +26 -0
  43. package/dist/commands/login.d.ts +3 -0
  44. package/dist/commands/login.d.ts.map +1 -0
  45. package/dist/commands/login.js +175 -0
  46. package/dist/commands/project.d.ts +3 -0
  47. package/dist/commands/project.d.ts.map +1 -0
  48. package/dist/commands/project.js +78 -0
  49. package/dist/commands/store-version.d.ts +3 -0
  50. package/dist/commands/store-version.d.ts.map +1 -0
  51. package/dist/commands/store-version.js +241 -0
  52. package/dist/commands/version.d.ts +3 -0
  53. package/dist/commands/version.d.ts.map +1 -0
  54. package/dist/commands/version.js +46 -0
  55. package/dist/commands/whoami.d.ts +3 -0
  56. package/dist/commands/whoami.d.ts.map +1 -0
  57. package/dist/commands/whoami.js +41 -0
  58. package/dist/index.d.ts +3 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +88 -478
  61. package/dist/prompts/component.prompts.d.ts +14 -0
  62. package/dist/prompts/component.prompts.d.ts.map +1 -0
  63. package/dist/prompts/component.prompts.js +75 -0
  64. package/dist/prompts/function.prompts.d.ts +21 -0
  65. package/dist/prompts/function.prompts.d.ts.map +1 -0
  66. package/dist/prompts/function.prompts.js +127 -0
  67. package/dist/schemas/command.schema.d.ts +516 -0
  68. package/dist/schemas/command.schema.d.ts.map +1 -0
  69. package/dist/schemas/command.schema.js +267 -0
  70. package/dist/types/index.d.ts +147 -0
  71. package/dist/types/index.d.ts.map +1 -0
  72. package/dist/types/index.js +18 -0
  73. package/dist/utils/auth.d.ts +4 -0
  74. package/dist/utils/auth.d.ts.map +1 -0
  75. package/dist/utils/auth.js +26 -0
  76. package/dist/utils/cli-progress-reporter.d.ts +12 -0
  77. package/dist/utils/cli-progress-reporter.d.ts.map +1 -0
  78. package/dist/utils/cli-progress-reporter.js +77 -0
  79. package/dist/utils/command-builder.d.ts +22 -0
  80. package/dist/utils/command-builder.d.ts.map +1 -0
  81. package/dist/utils/command-builder.js +268 -0
  82. package/dist/utils/command-helpers.d.ts +19 -0
  83. package/dist/utils/command-helpers.d.ts.map +1 -0
  84. package/dist/utils/command-helpers.js +79 -0
  85. package/dist/utils/command-parser.d.ts +146 -0
  86. package/dist/utils/command-parser.d.ts.map +1 -0
  87. package/dist/utils/command-parser.js +179 -0
  88. package/dist/utils/command-suggestions.d.ts +35 -0
  89. package/dist/utils/command-suggestions.d.ts.map +1 -0
  90. package/dist/utils/command-suggestions.js +152 -0
  91. package/dist/utils/console.d.ts +44 -0
  92. package/dist/utils/console.d.ts.map +1 -0
  93. package/dist/utils/console.js +233 -0
  94. package/dist/utils/constants.d.ts +8 -0
  95. package/dist/utils/constants.d.ts.map +1 -0
  96. package/dist/utils/constants.js +10 -0
  97. package/dist/utils/context-detector.d.ts +12 -0
  98. package/dist/utils/context-detector.d.ts.map +1 -0
  99. package/dist/utils/context-detector.js +155 -0
  100. package/dist/utils/enhanced-error-handler.d.ts +47 -0
  101. package/dist/utils/enhanced-error-handler.d.ts.map +1 -0
  102. package/dist/utils/enhanced-error-handler.js +221 -0
  103. package/dist/utils/error-handler.d.ts +3 -0
  104. package/dist/utils/error-handler.d.ts.map +1 -0
  105. package/dist/utils/error-handler.js +55 -0
  106. package/dist/utils/errors.d.ts +44 -0
  107. package/dist/utils/errors.d.ts.map +1 -0
  108. package/dist/utils/errors.js +76 -0
  109. package/dist/utils/interactive-builder.d.ts +22 -0
  110. package/dist/utils/interactive-builder.d.ts.map +1 -0
  111. package/dist/utils/interactive-builder.js +246 -0
  112. package/dist/utils/rich-progress.d.ts +59 -0
  113. package/dist/utils/rich-progress.d.ts.map +1 -0
  114. package/dist/utils/rich-progress.js +234 -0
  115. package/dist/utils/store.d.ts +11 -0
  116. package/dist/utils/store.d.ts.map +1 -0
  117. package/dist/utils/store.js +19 -0
  118. package/dist/utils/validation-error-formatter.d.ts +25 -0
  119. package/dist/utils/validation-error-formatter.d.ts.map +1 -0
  120. package/dist/utils/validation-error-formatter.js +258 -0
  121. package/dist/utils/validation-helpers.d.ts +60 -0
  122. package/dist/utils/validation-helpers.d.ts.map +1 -0
  123. package/dist/utils/validation-helpers.js +152 -0
  124. package/package.json +43 -11
  125. package/src/__tests__/helpers/cli-test-helper.ts +281 -0
  126. package/src/__tests__/mocks/index.ts +142 -0
  127. package/src/actions/component.actions.ts +334 -0
  128. package/src/actions/function.actions.ts +313 -0
  129. package/src/actions/project.actions.ts +126 -0
  130. package/src/actions/version.actions.ts +233 -0
  131. package/src/commands/__tests__/component-validation.test.ts +250 -0
  132. package/src/commands/__tests__/component.test.ts +321 -0
  133. package/src/commands/__tests__/function-validation.test.ts +220 -0
  134. package/src/commands/__tests__/function.test.ts +286 -0
  135. package/src/commands/__tests__/store-version-validation.test.ts +414 -0
  136. package/src/commands/__tests__/store-version.test.ts +405 -0
  137. package/src/commands/__tests__/version.test.ts +71 -0
  138. package/src/commands/component.ts +188 -0
  139. package/src/commands/docs.ts +11 -11
  140. package/src/commands/function.ts +252 -0
  141. package/src/commands/help.ts +8 -18
  142. package/src/commands/index.ts +14 -7
  143. package/src/commands/login.ts +19 -79
  144. package/src/commands/project.ts +107 -0
  145. package/src/commands/store-version.ts +242 -0
  146. package/src/commands/version.ts +45 -8
  147. package/src/commands/whoami.ts +8 -13
  148. package/src/index.ts +108 -34
  149. package/src/prompts/component.prompts.ts +94 -0
  150. package/src/prompts/function.prompts.ts +168 -0
  151. package/src/schemas/command.schema.ts +354 -0
  152. package/src/types/index.ts +183 -0
  153. package/src/utils/__tests__/command-parser.test.ts +159 -0
  154. package/src/utils/__tests__/command-suggestions.test.ts +185 -0
  155. package/src/utils/__tests__/console.test.ts +192 -0
  156. package/src/utils/__tests__/context-detector.test.ts +258 -0
  157. package/src/utils/__tests__/enhanced-error-handler.test.ts +137 -0
  158. package/src/utils/__tests__/error-handler.test.ts +107 -0
  159. package/src/utils/__tests__/rich-progress.test.ts +170 -0
  160. package/src/utils/__tests__/validation-error-formatter.test.ts +175 -0
  161. package/src/utils/__tests__/validation-helpers.test.ts +125 -0
  162. package/src/utils/auth.ts +0 -1
  163. package/src/utils/cli-progress-reporter.ts +84 -0
  164. package/src/utils/command-builder.ts +390 -0
  165. package/src/utils/command-helpers.ts +83 -0
  166. package/src/utils/command-parser.ts +250 -0
  167. package/src/utils/command-suggestions.ts +176 -0
  168. package/src/utils/console.ts +291 -0
  169. package/src/utils/context-detector.ts +177 -0
  170. package/src/utils/enhanced-error-handler.ts +264 -0
  171. package/src/utils/error-handler.ts +60 -0
  172. package/src/utils/errors.ts +125 -0
  173. package/src/utils/interactive-builder.ts +271 -0
  174. package/src/utils/rich-progress.ts +320 -0
  175. package/src/utils/validation-error-formatter.ts +337 -0
  176. package/src/utils/validation-helpers.ts +192 -0
  177. package/tsconfig.json +13 -7
  178. package/vitest.config.ts +28 -0
  179. package/vitest.setup.ts +29 -0
  180. package/src/commands/validate.ts +0 -62
  181. package/src/utils/core.ts +0 -105
  182. package/tsup.config.ts +0 -15
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Shared type definitions for the CLI package
3
+ */
4
+
5
+ import type { Ora } from "ora";
6
+
7
+ /**
8
+ * CLI Error types
9
+ */
10
+ export interface CLIError extends Error {
11
+ code?: string;
12
+ context?: ErrorContext;
13
+ }
14
+
15
+ export interface NodeError extends Error {
16
+ code: string;
17
+ errno?: number;
18
+ syscall?: string;
19
+ path?: string;
20
+ }
21
+
22
+ export interface ErrorContext {
23
+ command?: string;
24
+ file?: string;
25
+ input?: string;
26
+ attemptedCommand?: string;
27
+ path?: string;
28
+ }
29
+
30
+ /**
31
+ * Progress and spinner types
32
+ */
33
+ export type Spinner = Ora;
34
+
35
+ /**
36
+ * Command handler types
37
+ */
38
+ export type CommandHandler<T = unknown> = (
39
+ options: T,
40
+ console: typeof import("../utils/console").console,
41
+ ) => Promise<void>;
42
+
43
+ /**
44
+ * Build progress types
45
+ */
46
+ export interface BuildProgressPayload {
47
+ emoji?: string;
48
+ step?: string;
49
+ message?: string;
50
+ startTime?: number;
51
+ }
52
+
53
+ /**
54
+ * CLI Progress bar options
55
+ */
56
+ export interface ProgressBarOptions {
57
+ barCompleteString: string;
58
+ barIncompleteString: string;
59
+ barsize: number;
60
+ }
61
+
62
+ /**
63
+ * Progress bar params from cli-progress
64
+ */
65
+ export interface ProgressParams {
66
+ progress: number;
67
+ value: number;
68
+ total: number;
69
+ eta: number;
70
+ startTime: number;
71
+ stopTime: number | null;
72
+ duration: number;
73
+ }
74
+
75
+ /**
76
+ * Progress bar payload that can contain custom data
77
+ */
78
+ export interface ProgressPayload {
79
+ emoji?: string;
80
+ step?: string;
81
+ message?: string;
82
+ startTime?: number;
83
+ [key: string]: unknown;
84
+ }
85
+
86
+ /**
87
+ * Generic formatter for progress bars
88
+ * Matches cli-progress GenericFormatter interface
89
+ */
90
+ export type ProgressFormatter = (
91
+ options: {
92
+ barCompleteString?: string;
93
+ barIncompleteString?: string;
94
+ barsize?: number;
95
+ [key: string]: unknown;
96
+ },
97
+ params: ProgressParams,
98
+ payload: ProgressPayload,
99
+ ) => string;
100
+
101
+ /**
102
+ * Interactive command types
103
+ */
104
+ export interface InteractiveChoice {
105
+ name: string;
106
+ value: string;
107
+ short?: string;
108
+ }
109
+
110
+ /**
111
+ * Mock data types for development
112
+ */
113
+ export interface MockComponent {
114
+ id: string;
115
+ name: string;
116
+ slot: string;
117
+ version: string;
118
+ enabled: boolean;
119
+ createdAt: string;
120
+ type?: string;
121
+ description?: string;
122
+ }
123
+
124
+ export interface MockFunction {
125
+ id: string;
126
+ name: string;
127
+ runtime?: string;
128
+ version?: string;
129
+ enabled: boolean;
130
+ createdAt: string;
131
+ event?: string;
132
+ timing?: string;
133
+ description?: string;
134
+ }
135
+
136
+ /**
137
+ * CLI Error with exit code for process control
138
+ */
139
+ export interface CLIError extends Error {
140
+ exitCode?: number;
141
+ isValidationError?: boolean;
142
+ }
143
+
144
+ /**
145
+ * Type guards
146
+ */
147
+ export function isNodeError(error: unknown): error is NodeError {
148
+ return (
149
+ error instanceof Error &&
150
+ "code" in error &&
151
+ typeof (error as NodeError).code === "string"
152
+ );
153
+ }
154
+
155
+ export function isCLIError(error: unknown): error is CLIError {
156
+ return error instanceof Error && "context" in error;
157
+ }
158
+
159
+ /**
160
+ * Command option types without index signatures
161
+ */
162
+ export interface BaseCommandOptions {
163
+ verbose?: boolean;
164
+ quiet?: boolean;
165
+ debug?: boolean;
166
+ }
167
+
168
+ export interface PathCommandOptions extends BaseCommandOptions {
169
+ path?: string;
170
+ }
171
+
172
+ export interface WatchCommandOptions extends PathCommandOptions {
173
+ watch?: boolean;
174
+ }
175
+
176
+ export interface BuildCommandOptions extends WatchCommandOptions {
177
+ minify?: boolean;
178
+ sourcemap?: boolean;
179
+ }
180
+
181
+ export interface DeployCommandOptions extends PathCommandOptions {
182
+ wait?: boolean;
183
+ }
@@ -0,0 +1,159 @@
1
+ import { InvalidArgumentError } from "@commander-js/extra-typings";
2
+ import { describe, expect, it } from "vitest";
3
+ import {
4
+ parseBooleanOption,
5
+ parseComponentName,
6
+ parseComponentSlot,
7
+ parseFunctionInvocation,
8
+ parseFunctionName,
9
+ parsePath,
10
+ } from "../command-parser";
11
+
12
+ describe("Command Parsers", () => {
13
+ describe("parseComponentName", () => {
14
+ it("should accept valid component names", () => {
15
+ expect(parseComponentName("header-nav")).toBe("header-nav");
16
+ expect(parseComponentName("shopping-cart")).toBe("shopping-cart");
17
+ expect(parseComponentName("product-list-2")).toBe("product-list-2");
18
+ });
19
+
20
+ it("should reject invalid component names", () => {
21
+ expect(() => parseComponentName("")).toThrow(InvalidArgumentError);
22
+ expect(() => parseComponentName("Test")).toThrow(
23
+ /lowercase with hyphens/,
24
+ );
25
+ expect(() => parseComponentName("test component")).toThrow(
26
+ /lowercase with hyphens/,
27
+ );
28
+ expect(() => parseComponentName("test_component")).toThrow(
29
+ /lowercase with hyphens/,
30
+ );
31
+ expect(() => parseComponentName("Test-Component")).toThrow(
32
+ /lowercase with hyphens/,
33
+ );
34
+ });
35
+
36
+ it("should provide helpful error messages", () => {
37
+ try {
38
+ parseComponentName("TestComponent");
39
+ } catch (error) {
40
+ expect(error).toBeInstanceOf(InvalidArgumentError);
41
+ expect(error.message).toContain("Invalid component name");
42
+ expect(error.message).toContain(
43
+ "Component names must be lowercase with hyphens only",
44
+ );
45
+ expect(error.message).toContain("Examples:");
46
+ }
47
+ });
48
+ });
49
+
50
+ describe("parseFunctionName", () => {
51
+ it("should accept valid function names", () => {
52
+ expect(parseFunctionName("validate-order")).toBe("validate-order");
53
+ expect(parseFunctionName("apply-discount")).toBe("apply-discount");
54
+ expect(parseFunctionName("check-inventory-v2")).toBe(
55
+ "check-inventory-v2",
56
+ );
57
+ });
58
+
59
+ it("should reject invalid function names", () => {
60
+ expect(() => parseFunctionName("")).toThrow(InvalidArgumentError);
61
+ expect(() => parseFunctionName("ValidateOrder")).toThrow(
62
+ /lowercase with hyphens/,
63
+ );
64
+ expect(() => parseFunctionName("validate order")).toThrow(
65
+ /lowercase with hyphens/,
66
+ );
67
+ });
68
+ });
69
+
70
+ describe("parseComponentSlot", () => {
71
+ it("should accept valid slots", () => {
72
+ expect(parseComponentSlot("header")).toBe("header");
73
+ expect(parseComponentSlot("main")).toBe("main");
74
+ expect(parseComponentSlot("sidebar")).toBe("sidebar");
75
+ expect(parseComponentSlot("footer")).toBe("footer");
76
+ });
77
+
78
+ it("should reject invalid slots", () => {
79
+ expect(() => parseComponentSlot("invalid")).toThrow(InvalidArgumentError);
80
+ expect(() => parseComponentSlot("")).toThrow(InvalidArgumentError);
81
+ });
82
+
83
+ it("should provide list of valid slots in error", () => {
84
+ try {
85
+ parseComponentSlot("invalid");
86
+ } catch (error) {
87
+ expect(error.message).toContain(
88
+ "Valid slots: header, footer, sidebar, main, checkout-summary, payment-method, shipping-method, customer-info, order-confirmation",
89
+ );
90
+ }
91
+ });
92
+ });
93
+
94
+ describe("parseFunctionInvocation", () => {
95
+ it("should accept valid invocations", () => {
96
+ expect(parseFunctionInvocation("request")).toBe("request");
97
+ expect(parseFunctionInvocation("response")).toBe("response");
98
+ });
99
+
100
+ it("should reject invalid invocations", () => {
101
+ expect(() => parseFunctionInvocation("invalid")).toThrow(
102
+ InvalidArgumentError,
103
+ );
104
+ });
105
+
106
+ it("should provide list of valid invocations in error", () => {
107
+ try {
108
+ parseFunctionInvocation("invalid");
109
+ } catch (error) {
110
+ expect(error.message).toContain("request");
111
+ expect(error.message).toContain("response");
112
+ }
113
+ });
114
+ });
115
+
116
+ describe("parseBooleanOption", () => {
117
+ it("should parse truthy values", () => {
118
+ expect(parseBooleanOption("true")).toBe(true);
119
+ expect(parseBooleanOption("TRUE")).toBe(true);
120
+ expect(parseBooleanOption("yes")).toBe(true);
121
+ expect(parseBooleanOption("y")).toBe(true);
122
+ expect(parseBooleanOption("1")).toBe(true);
123
+ });
124
+
125
+ it("should parse falsy values", () => {
126
+ expect(parseBooleanOption("false")).toBe(false);
127
+ expect(parseBooleanOption("FALSE")).toBe(false);
128
+ expect(parseBooleanOption("no")).toBe(false);
129
+ expect(parseBooleanOption("n")).toBe(false);
130
+ expect(parseBooleanOption("0")).toBe(false);
131
+ });
132
+
133
+ it("should reject invalid values", () => {
134
+ expect(() => parseBooleanOption("maybe")).toThrow(InvalidArgumentError);
135
+ expect(() => parseBooleanOption("")).toThrow(InvalidArgumentError);
136
+ });
137
+
138
+ it("should provide helpful error message", () => {
139
+ try {
140
+ parseBooleanOption("invalid");
141
+ } catch (error) {
142
+ expect(error.message).toContain("Use true/false, yes/no, or 1/0");
143
+ }
144
+ });
145
+ });
146
+
147
+ describe("parsePath", () => {
148
+ it("should accept valid paths", () => {
149
+ expect(parsePath("./components/header")).toBe("./components/header");
150
+ expect(parsePath("/absolute/path")).toBe("/absolute/path");
151
+ expect(parsePath("relative/path")).toBe("relative/path");
152
+ });
153
+
154
+ it("should reject empty paths", () => {
155
+ expect(() => parsePath("")).toThrow(InvalidArgumentError);
156
+ expect(() => parsePath("")).toThrow(/cannot be empty/);
157
+ });
158
+ });
159
+ });
@@ -0,0 +1,185 @@
1
+ import * as fs from "node:fs";
2
+ import { beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { CommandSuggestions } from "../command-suggestions";
4
+ import type { CliConsole } from "../console";
5
+
6
+ // Mock the fs module
7
+ vi.mock("fs");
8
+
9
+ describe("CommandSuggestions", () => {
10
+ let mockConsole: CliConsole;
11
+ let suggestions: CommandSuggestions;
12
+
13
+ beforeEach(() => {
14
+ // Create a mock console
15
+ mockConsole = {
16
+ log: vi.fn(),
17
+ error: vi.fn(),
18
+ warn: vi.fn(),
19
+ info: vi.fn(),
20
+ success: vi.fn(),
21
+ dim: vi.fn(),
22
+ bold: vi.fn(),
23
+ list: vi.fn(),
24
+ table: vi.fn(),
25
+ json: vi.fn(),
26
+ spinner: vi.fn(),
27
+ confirm: vi.fn(),
28
+ prompt: vi.fn(),
29
+ setOptions: vi.fn(),
30
+ group: vi.fn(),
31
+ groupEnd: vi.fn(),
32
+ separator: vi.fn(),
33
+ suggestions: vi.fn(),
34
+ debug: vi.fn(),
35
+ nextSteps: vi.fn(),
36
+ };
37
+ suggestions = new CommandSuggestions(mockConsole);
38
+ vi.clearAllMocks();
39
+
40
+ // Mock process.cwd
41
+ vi.spyOn(process, "cwd").mockReturnValue("/test/project");
42
+ });
43
+
44
+ describe("show", () => {
45
+ it("should show suggestions for component directory", () => {
46
+ // Mock component detection
47
+ vi.mocked(fs.existsSync).mockImplementation((path) => {
48
+ if (path.toString().includes("package.json")) return true;
49
+ if (path.toString().includes("meta.json")) return true;
50
+ if (path.toString().includes("index.tsx")) return true;
51
+ return false;
52
+ });
53
+
54
+ vi.mocked(fs.readFileSync).mockReturnValue(
55
+ JSON.stringify({ type: "component", name: "header-nav" }),
56
+ );
57
+
58
+ suggestions.show();
59
+
60
+ // Check context display
61
+ expect(mockConsole.log).toHaveBeenCalledWith(
62
+ expect.stringContaining("You're in a component directory: header-nav"),
63
+ );
64
+
65
+ // Check component commands
66
+ expect(mockConsole.log).toHaveBeenCalledWith(
67
+ expect.stringContaining("Common commands for this component:"),
68
+ );
69
+ expect(mockConsole.log).toHaveBeenCalledWith(
70
+ expect.stringContaining("validate"),
71
+ );
72
+ expect(mockConsole.log).toHaveBeenCalledWith(
73
+ expect.stringContaining("build"),
74
+ );
75
+ expect(mockConsole.log).toHaveBeenCalledWith(
76
+ expect.stringContaining("deploy"),
77
+ );
78
+
79
+ // Check tip
80
+ expect(mockConsole.log).toHaveBeenCalledWith(
81
+ expect.stringContaining("Run 'ollieshop dev' to start developing"),
82
+ );
83
+ });
84
+
85
+ it("should show suggestions for function directory", () => {
86
+ // Mock function detection - ensure we ONLY return true for function files
87
+ vi.mocked(fs.existsSync).mockImplementation((path) => {
88
+ const pathStr = path.toString();
89
+ // Check for function-specific files
90
+ const isFunctionFile =
91
+ pathStr.includes("package.json") ||
92
+ pathStr.includes("meta.json") ||
93
+ (pathStr.includes("index.ts") && !pathStr.includes("index.tsx"));
94
+ return isFunctionFile;
95
+ });
96
+
97
+ vi.mocked(fs.readFileSync).mockReturnValue(
98
+ JSON.stringify({ type: "function", name: "validate-order" }),
99
+ );
100
+
101
+ suggestions.show();
102
+
103
+ // Check context display
104
+ expect(mockConsole.log).toHaveBeenCalledWith(
105
+ expect.stringContaining(
106
+ "You're in a function directory: validate-order",
107
+ ),
108
+ );
109
+
110
+ // Check function commands
111
+ expect(mockConsole.log).toHaveBeenCalledWith(
112
+ expect.stringContaining("Common commands for this function:"),
113
+ );
114
+ expect(mockConsole.log).toHaveBeenCalledWith(
115
+ expect.stringContaining("test"),
116
+ );
117
+
118
+ // Check tip
119
+ expect(mockConsole.log).toHaveBeenCalledWith(
120
+ expect.stringContaining("Run 'ollieshop test' to test this function"),
121
+ );
122
+ });
123
+
124
+ it("should show suggestions for project root", () => {
125
+ // Mock project root detection
126
+ vi.mocked(fs.existsSync).mockImplementation((path) => {
127
+ if (path.toString().includes("components")) return true;
128
+ return false;
129
+ });
130
+
131
+ vi.mocked(fs.statSync).mockReturnValue({
132
+ isDirectory: () => true,
133
+ } as fs.Stats);
134
+
135
+ suggestions.show();
136
+
137
+ // Check context display
138
+ expect(mockConsole.log).toHaveBeenCalledWith(
139
+ expect.stringContaining("You're in the project root"),
140
+ );
141
+
142
+ // Check general commands
143
+ expect(mockConsole.log).toHaveBeenCalledWith(
144
+ expect.stringContaining("Common commands:"),
145
+ );
146
+ expect(mockConsole.log).toHaveBeenCalledWith(
147
+ expect.stringContaining("component create"),
148
+ );
149
+ expect(mockConsole.log).toHaveBeenCalledWith(
150
+ expect.stringContaining("function create"),
151
+ );
152
+
153
+ // Check tip
154
+ expect(mockConsole.log).toHaveBeenCalledWith(
155
+ expect.stringContaining(
156
+ "Navigate to a component with 'cd components/<name>'",
157
+ ),
158
+ );
159
+ });
160
+
161
+ it("should show recent commands for component", () => {
162
+ // Mock component detection
163
+ vi.mocked(fs.existsSync).mockImplementation((path) => {
164
+ if (path.toString().includes("package.json")) return true;
165
+ if (path.toString().includes("meta.json")) return true;
166
+ if (path.toString().includes("index.tsx")) return true;
167
+ return false;
168
+ });
169
+
170
+ vi.mocked(fs.readFileSync).mockReturnValue(
171
+ JSON.stringify({ type: "component" }),
172
+ );
173
+
174
+ suggestions.show();
175
+
176
+ // Check recent commands section
177
+ expect(mockConsole.log).toHaveBeenCalledWith(
178
+ expect.stringContaining("Recent commands:"),
179
+ );
180
+ expect(mockConsole.log).toHaveBeenCalledWith(
181
+ expect.stringContaining("validate --fix"),
182
+ );
183
+ });
184
+ });
185
+ });