@sundaeswap/sprinkles 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Sprinkle/__tests__/enhancements.test.js +19 -7
- package/dist/cjs/Sprinkle/__tests__/enhancements.test.js.map +1 -1
- package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js +114 -0
- package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
- package/dist/cjs/Sprinkle/__tests__/show-menu.test.js +109 -98
- package/dist/cjs/Sprinkle/__tests__/show-menu.test.js.map +1 -1
- package/dist/cjs/Sprinkle/index.js +480 -179
- package/dist/cjs/Sprinkle/index.js.map +1 -1
- package/dist/cjs/Sprinkle/types.js.map +1 -1
- package/dist/esm/Sprinkle/__tests__/enhancements.test.js +19 -7
- package/dist/esm/Sprinkle/__tests__/enhancements.test.js.map +1 -1
- package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js +114 -0
- package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
- package/dist/esm/Sprinkle/__tests__/show-menu.test.js +110 -99
- package/dist/esm/Sprinkle/__tests__/show-menu.test.js.map +1 -1
- package/dist/esm/Sprinkle/index.js +483 -182
- package/dist/esm/Sprinkle/index.js.map +1 -1
- package/dist/esm/Sprinkle/types.js.map +1 -1
- package/dist/types/Sprinkle/index.d.ts +29 -0
- package/dist/types/Sprinkle/index.d.ts.map +1 -1
- package/dist/types/Sprinkle/types.d.ts +6 -0
- package/dist/types/Sprinkle/types.d.ts.map +1 -1
- package/dist/types/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/Sprinkle/__tests__/enhancements.test.ts +20 -7
- package/src/Sprinkle/__tests__/fill-in-struct.test.ts +112 -1
- package/src/Sprinkle/__tests__/show-menu.test.ts +132 -116
- package/src/Sprinkle/index.ts +552 -188
- package/src/Sprinkle/types.ts +6 -0
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
var _bunTest = require("bun:test");
|
|
4
4
|
var _index = require("../index.js");
|
|
5
5
|
var _testHelpers = require("./test-helpers.js");
|
|
6
|
+
const mockSearch = (0, _bunTest.mock)();
|
|
6
7
|
const mockSelect = (0, _bunTest.mock)();
|
|
7
8
|
const mockInput = (0, _bunTest.mock)();
|
|
8
9
|
const mockSelectCancellable = (0, _bunTest.mock)();
|
|
9
10
|
const mockInputCancellable = (0, _bunTest.mock)();
|
|
10
11
|
_bunTest.mock.module("@inquirer/prompts", () => ({
|
|
12
|
+
search: mockSearch,
|
|
11
13
|
select: mockSelect,
|
|
12
14
|
input: mockInput
|
|
13
15
|
}));
|
|
@@ -16,10 +18,21 @@ _bunTest.mock.module("../prompts.js", () => ({
|
|
|
16
18
|
inputCancellable: mockInputCancellable,
|
|
17
19
|
passwordCancellable: (0, _bunTest.mock)(),
|
|
18
20
|
confirmCancellable: (0, _bunTest.mock)(),
|
|
19
|
-
searchCancellable:
|
|
21
|
+
searchCancellable: mockSearch,
|
|
20
22
|
select: mockSelectCancellable
|
|
21
23
|
}));
|
|
22
|
-
|
|
24
|
+
async function collectChoices(call) {
|
|
25
|
+
const source = call[0].source;
|
|
26
|
+
return source(undefined);
|
|
27
|
+
}
|
|
28
|
+
function pickByName(choices, name) {
|
|
29
|
+
const match = choices.find(c => c.name === name);
|
|
30
|
+
if (!match) {
|
|
31
|
+
throw new Error(`No choice named "${name}". Available: ${choices.map(c => c.name).join(", ")}`);
|
|
32
|
+
}
|
|
33
|
+
return match.value;
|
|
34
|
+
}
|
|
35
|
+
(0, _bunTest.describe)("showMenu (search-based)", () => {
|
|
23
36
|
let sprinkle;
|
|
24
37
|
(0, _bunTest.beforeEach)(() => {
|
|
25
38
|
const schema = _index.Type.Object({
|
|
@@ -29,12 +42,16 @@ _bunTest.mock.module("../prompts.js", () => ({
|
|
|
29
42
|
sprinkle.settings = {
|
|
30
43
|
name: "test"
|
|
31
44
|
};
|
|
45
|
+
mockSearch.mockClear();
|
|
32
46
|
mockSelect.mockClear();
|
|
33
47
|
mockSelectCancellable.mockClear();
|
|
34
48
|
mockInput.mockClear();
|
|
35
49
|
});
|
|
36
|
-
(0, _bunTest.test)("exits when Exit is selected
|
|
37
|
-
|
|
50
|
+
(0, _bunTest.test)("exits when the Exit leaf is selected", async () => {
|
|
51
|
+
mockSearch.mockImplementationOnce(async opts => {
|
|
52
|
+
const choices = await opts.source(undefined);
|
|
53
|
+
return pickByName(choices, "Exit");
|
|
54
|
+
});
|
|
38
55
|
const menu = {
|
|
39
56
|
title: "Test Menu",
|
|
40
57
|
items: [{
|
|
@@ -43,10 +60,17 @@ _bunTest.mock.module("../prompts.js", () => ({
|
|
|
43
60
|
}]
|
|
44
61
|
};
|
|
45
62
|
await sprinkle.showMenu(menu);
|
|
63
|
+
(0, _bunTest.expect)(mockSearch).toHaveBeenCalledTimes(1);
|
|
46
64
|
});
|
|
47
|
-
(0, _bunTest.test)("executes action
|
|
65
|
+
(0, _bunTest.test)("executes a leaf action then loops back to the search prompt", async () => {
|
|
48
66
|
const actionFn = (0, _bunTest.mock)(async () => {});
|
|
49
|
-
|
|
67
|
+
mockSearch.mockImplementationOnce(async opts => {
|
|
68
|
+
const choices = await opts.source(undefined);
|
|
69
|
+
return pickByName(choices, "My Action");
|
|
70
|
+
}).mockImplementationOnce(async opts => {
|
|
71
|
+
const choices = await opts.source(undefined);
|
|
72
|
+
return pickByName(choices, "Exit");
|
|
73
|
+
});
|
|
50
74
|
const menu = {
|
|
51
75
|
title: "Test Menu",
|
|
52
76
|
items: [{
|
|
@@ -56,48 +80,84 @@ _bunTest.mock.module("../prompts.js", () => ({
|
|
|
56
80
|
};
|
|
57
81
|
await sprinkle.showMenu(menu);
|
|
58
82
|
(0, _bunTest.expect)(actionFn).toHaveBeenCalledTimes(1);
|
|
83
|
+
(0, _bunTest.expect)(mockSearch).toHaveBeenCalledTimes(2);
|
|
59
84
|
});
|
|
60
|
-
(0, _bunTest.test)("
|
|
61
|
-
|
|
85
|
+
(0, _bunTest.test)("flattens submenus into breadcrumb leaves and surfaces Settings & Profiles + Exit", async () => {
|
|
86
|
+
mockSearch.mockImplementationOnce(async opts => {
|
|
87
|
+
const choices = await opts.source(undefined);
|
|
88
|
+
return pickByName(choices, "Exit");
|
|
89
|
+
});
|
|
62
90
|
const menu = {
|
|
63
|
-
title: "
|
|
91
|
+
title: "Main",
|
|
64
92
|
items: [{
|
|
65
|
-
title: "
|
|
93
|
+
title: "Strategies",
|
|
94
|
+
items: [{
|
|
95
|
+
title: "Create limit order",
|
|
96
|
+
action: async () => {}
|
|
97
|
+
}, {
|
|
98
|
+
title: "List strategies",
|
|
99
|
+
action: async () => {}
|
|
100
|
+
}]
|
|
101
|
+
}, {
|
|
102
|
+
title: "Quit (custom)",
|
|
66
103
|
action: async () => {}
|
|
67
104
|
}]
|
|
68
105
|
};
|
|
69
106
|
await sprinkle.showMenu(menu);
|
|
70
|
-
const choices =
|
|
107
|
+
const choices = await collectChoices(mockSearch.mock.calls[0]);
|
|
71
108
|
const names = choices.map(c => c.name);
|
|
72
|
-
(0, _bunTest.expect)(names).toContain("
|
|
109
|
+
(0, _bunTest.expect)(names).toContain("Strategies › Create limit order");
|
|
110
|
+
(0, _bunTest.expect)(names).toContain("Strategies › List strategies");
|
|
111
|
+
(0, _bunTest.expect)(names).toContain("Quit (custom)");
|
|
112
|
+
(0, _bunTest.expect)(names).toContain("Settings & Profiles › Edit settings");
|
|
113
|
+
(0, _bunTest.expect)(names).toContain("Settings & Profiles › Import profile from file");
|
|
73
114
|
(0, _bunTest.expect)(names).toContain("Exit");
|
|
74
|
-
(0, _bunTest.expect)(names).not.toContain("Switch profile");
|
|
75
|
-
(0, _bunTest.expect)(names).not.toContain("Manage profiles");
|
|
76
|
-
(0, _bunTest.expect)(names).not.toContain("Edit settings");
|
|
77
115
|
});
|
|
78
|
-
(0, _bunTest.test)("
|
|
79
|
-
|
|
116
|
+
(0, _bunTest.test)("filters leaves by typed term (case-insensitive, all tokens, leaf-title boost)", async () => {
|
|
117
|
+
let filtered = [];
|
|
118
|
+
mockSearch.mockImplementationOnce(async opts => {
|
|
119
|
+
filtered = await opts.source("strateg");
|
|
120
|
+
// pick Exit so the loop terminates
|
|
121
|
+
const exitChoice = (await opts.source(undefined)).find(c => c.name === "Exit");
|
|
122
|
+
return exitChoice.value;
|
|
123
|
+
});
|
|
80
124
|
const menu = {
|
|
81
125
|
title: "Main",
|
|
82
126
|
items: [{
|
|
83
|
-
title: "
|
|
127
|
+
title: "Strategies",
|
|
84
128
|
items: [{
|
|
85
|
-
title: "
|
|
129
|
+
title: "Create limit order",
|
|
130
|
+
action: async () => {}
|
|
131
|
+
}, {
|
|
132
|
+
title: "Cancel strategy",
|
|
86
133
|
action: async () => {}
|
|
87
134
|
}]
|
|
135
|
+
}, {
|
|
136
|
+
title: "Send payment",
|
|
137
|
+
action: async () => {}
|
|
88
138
|
}]
|
|
89
139
|
};
|
|
90
140
|
await sprinkle.showMenu(menu);
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
(0, _bunTest.expect)(
|
|
94
|
-
(0, _bunTest.expect)(
|
|
141
|
+
const filteredNames = filtered.map(c => c.name);
|
|
142
|
+
// Everything under "Strategies" matches via the breadcrumb token
|
|
143
|
+
(0, _bunTest.expect)(filteredNames).toContain("Strategies › Create limit order");
|
|
144
|
+
(0, _bunTest.expect)(filteredNames).toContain("Strategies › Cancel strategy");
|
|
145
|
+
// Direct leaf-title hit outranks ancestor-only match
|
|
146
|
+
(0, _bunTest.expect)(filteredNames[0]).toBe("Strategies › Cancel strategy");
|
|
147
|
+
// Unrelated items are filtered out
|
|
148
|
+
(0, _bunTest.expect)(filteredNames).not.toContain("Send payment");
|
|
95
149
|
});
|
|
96
|
-
(0, _bunTest.test)("action returning
|
|
150
|
+
(0, _bunTest.test)("action returning a Sprinkle persists updated settings", async () => {
|
|
97
151
|
sprinkle.settings = {
|
|
98
152
|
name: "original"
|
|
99
153
|
};
|
|
100
|
-
|
|
154
|
+
mockSearch.mockImplementationOnce(async opts => {
|
|
155
|
+
const choices = await opts.source(undefined);
|
|
156
|
+
return pickByName(choices, "Update");
|
|
157
|
+
}).mockImplementationOnce(async opts => {
|
|
158
|
+
const choices = await opts.source(undefined);
|
|
159
|
+
return pickByName(choices, "Exit");
|
|
160
|
+
});
|
|
101
161
|
const menu = {
|
|
102
162
|
title: "Test",
|
|
103
163
|
items: [{
|
|
@@ -115,84 +175,35 @@ _bunTest.mock.module("../prompts.js", () => ({
|
|
|
115
175
|
name: "updated"
|
|
116
176
|
});
|
|
117
177
|
});
|
|
118
|
-
(0, _bunTest.test)("
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// Second call is the settings submenu
|
|
134
|
-
const settingsChoices = mockSelectCancellable.mock.calls[1][0].choices;
|
|
135
|
-
const settingsNames = settingsChoices.map(c => c.name);
|
|
136
|
-
(0, _bunTest.expect)(settingsNames).toContain("View settings");
|
|
137
|
-
});
|
|
138
|
-
(0, _bunTest.test)("View settings displays masked settings", async () => {
|
|
139
|
-
const consoleSpy = (0, _bunTest.spyOn)(console, "log").mockImplementation(() => {});
|
|
140
|
-
|
|
141
|
-
// Select "Settings & Profiles" (-5), then "View settings" (0),
|
|
142
|
-
// then "Continue" for the press Enter prompt, then "Back" (-1), then "Exit" (-1)
|
|
143
|
-
mockSelectCancellable.mockResolvedValueOnce(-5) // Settings & Profiles
|
|
144
|
-
.mockResolvedValueOnce(0) // View settings (first item)
|
|
145
|
-
.mockResolvedValueOnce("continue") // Press Enter to continue
|
|
146
|
-
.mockResolvedValueOnce(-1) // Back
|
|
147
|
-
.mockResolvedValueOnce(-1); // Exit
|
|
148
|
-
|
|
149
|
-
const menu = {
|
|
150
|
-
title: "Test",
|
|
151
|
-
items: [{
|
|
152
|
-
title: "Action",
|
|
153
|
-
action: async () => {}
|
|
154
|
-
}]
|
|
155
|
-
};
|
|
156
|
-
await sprinkle.showMenu(menu);
|
|
157
|
-
|
|
158
|
-
// Should have called console.log with the settings (find the JSON output among breadcrumbs)
|
|
159
|
-
(0, _bunTest.expect)(consoleSpy).toHaveBeenCalled();
|
|
160
|
-
const jsonOutput = consoleSpy.mock.calls.find(call => call[0] && typeof call[0] === "string" && call[0].includes('"name"'));
|
|
161
|
-
(0, _bunTest.expect)(jsonOutput).toBeDefined();
|
|
162
|
-
consoleSpy.mockRestore();
|
|
163
|
-
});
|
|
164
|
-
(0, _bunTest.test)("Create profile restores state when FillInStruct is cancelled", async () => {
|
|
165
|
-
// Capture original state
|
|
166
|
-
const originalProfileId = sprinkle.currentProfile?.id;
|
|
167
|
-
const originalSettings = {
|
|
168
|
-
...sprinkle.settings
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
// Settings menu indices: 0=View, 1=Edit, 2=Switch, 3=Create new profile
|
|
172
|
-
mockSelectCancellable.mockResolvedValueOnce(-5) // Settings & Profiles
|
|
173
|
-
.mockResolvedValueOnce(3); // Create new profile
|
|
174
|
-
|
|
175
|
-
// promptProfileMeta: name then description
|
|
176
|
-
mockInputCancellable.mockResolvedValueOnce("New Profile") // Profile name
|
|
177
|
-
.mockResolvedValueOnce("") // Profile description
|
|
178
|
-
.mockResolvedValueOnce(null); // Cancel during FillInStruct (name field)
|
|
179
|
-
|
|
180
|
-
// After cancellation, back out of menus
|
|
181
|
-
mockSelectCancellable.mockResolvedValueOnce(-1) // Back from settings
|
|
182
|
-
.mockResolvedValueOnce(-1); // Exit
|
|
183
|
-
|
|
178
|
+
(0, _bunTest.test)("submenu beforeShow fires only when a leaf under it is selected", async () => {
|
|
179
|
+
const order = [];
|
|
180
|
+
const subBeforeShow = (0, _bunTest.mock)(async () => {
|
|
181
|
+
order.push("sub.beforeShow");
|
|
182
|
+
});
|
|
183
|
+
const actionFn = (0, _bunTest.mock)(async () => {
|
|
184
|
+
order.push("action");
|
|
185
|
+
});
|
|
186
|
+
mockSearch.mockImplementationOnce(async opts => {
|
|
187
|
+
const choices = await opts.source(undefined);
|
|
188
|
+
return pickByName(choices, "Strategies › Create");
|
|
189
|
+
}).mockImplementationOnce(async opts => {
|
|
190
|
+
const choices = await opts.source(undefined);
|
|
191
|
+
return pickByName(choices, "Exit");
|
|
192
|
+
});
|
|
184
193
|
const menu = {
|
|
185
|
-
title: "
|
|
194
|
+
title: "Main",
|
|
186
195
|
items: [{
|
|
187
|
-
title: "
|
|
188
|
-
|
|
196
|
+
title: "Strategies",
|
|
197
|
+
beforeShow: subBeforeShow,
|
|
198
|
+
items: [{
|
|
199
|
+
title: "Create",
|
|
200
|
+
action: actionFn
|
|
201
|
+
}]
|
|
189
202
|
}]
|
|
190
203
|
};
|
|
191
204
|
await sprinkle.showMenu(menu);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
(0, _bunTest.expect)(sprinkle.currentProfile?.id).toBe(originalProfileId);
|
|
195
|
-
(0, _bunTest.expect)(sprinkle.settings).toEqual(originalSettings);
|
|
205
|
+
(0, _bunTest.expect)(subBeforeShow).toHaveBeenCalledTimes(1);
|
|
206
|
+
(0, _bunTest.expect)(order).toEqual(["sub.beforeShow", "action"]);
|
|
196
207
|
});
|
|
197
208
|
});
|
|
198
209
|
//# sourceMappingURL=show-menu.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"show-menu.test.js","names":["_bunTest","require","_index","_testHelpers","mockSelect","mock","mockInput","mockSelectCancellable","mockInputCancellable","module","select","input","selectCancellable","inputCancellable","passwordCancellable","confirmCancellable","searchCancellable","describe","sprinkle","beforeEach","schema","Type","Object","name","String","withProfile","Sprinkle","settings","mockClear","test","mockResolvedValueOnce","menu","title","items","action","showMenu","actionFn","expect","toHaveBeenCalledTimes","choices","calls","names","map","c","toContain","not","subChoices","subNames","s","toEqual","settingsChoices","settingsNames","consoleSpy","spyOn","console","mockImplementation","toHaveBeenCalled","jsonOutput","find","call","includes","toBeDefined","mockRestore","originalProfileId","currentProfile","id","originalSettings","toBe"],"sources":["../../../../src/Sprinkle/__tests__/show-menu.test.ts"],"sourcesContent":["import { describe, expect, test, mock, beforeEach, spyOn } from \"bun:test\";\nimport { Sprinkle, Type, type IMenu } from \"../index.js\";\nimport { withProfile } from \"./test-helpers.js\";\n\nconst mockSelect = mock();\nconst mockInput = mock();\nconst mockSelectCancellable = mock();\nconst mockInputCancellable = mock();\n\nmock.module(\"@inquirer/prompts\", () => ({\n select: mockSelect,\n input: mockInput,\n}));\n\nmock.module(\"../prompts.js\", () => ({\n selectCancellable: mockSelectCancellable,\n inputCancellable: mockInputCancellable,\n passwordCancellable: mock(),\n confirmCancellable: mock(),\n searchCancellable: mock(),\n select: mockSelectCancellable,\n}));\n\ndescribe(\"showMenu\", () => {\n let sprinkle: Sprinkle<any>;\n\n beforeEach(() => {\n const schema = Type.Object({ name: Type.String() });\n sprinkle = withProfile(new Sprinkle(schema, \"/tmp/test\"));\n sprinkle.settings = { name: \"test\" } as any;\n mockSelect.mockClear();\n mockSelectCancellable.mockClear();\n mockInput.mockClear();\n });\n\n test(\"exits when Exit is selected on main menu\", async () => {\n mockSelectCancellable.mockResolvedValueOnce(-1);\n\n const menu: IMenu<any> = {\n title: \"Test Menu\",\n items: [\n {\n title: \"Action 1\",\n action: async () => {},\n },\n ],\n };\n\n await sprinkle.showMenu(menu);\n });\n\n test(\"executes action and re-shows menu\", async () => {\n const actionFn = mock(async () => {});\n\n mockSelectCancellable.mockResolvedValueOnce(0).mockResolvedValueOnce(-1);\n\n const menu: IMenu<any> = {\n title: \"Test Menu\",\n items: [\n {\n title: \"My Action\",\n action: actionFn,\n },\n ],\n };\n\n await sprinkle.showMenu(menu);\n expect(actionFn).toHaveBeenCalledTimes(1);\n });\n\n test(\"main menu includes Settings & Profiles submenu and Exit\", async () => {\n mockSelectCancellable.mockResolvedValueOnce(-1);\n\n const menu: IMenu<any> = {\n title: \"Test\",\n items: [{ title: \"Action\", action: async () => {} }],\n };\n\n await sprinkle.showMenu(menu);\n\n const choices = mockSelectCancellable.mock.calls[0][0].choices;\n const names = choices.map((c: any) => c.name);\n expect(names).toContain(\"Settings & Profiles\");\n expect(names).toContain(\"Exit\");\n expect(names).not.toContain(\"Switch profile\");\n expect(names).not.toContain(\"Manage profiles\");\n expect(names).not.toContain(\"Edit settings\");\n });\n\n test(\"submenu includes Back instead of Exit\", async () => {\n mockSelectCancellable\n .mockResolvedValueOnce(0)\n .mockResolvedValueOnce(-1)\n .mockResolvedValueOnce(-1);\n\n const menu: IMenu<any> = {\n title: \"Main\",\n items: [\n {\n title: \"Sub\",\n items: [{ title: \"Sub Action\", action: async () => {} }],\n },\n ],\n };\n\n await sprinkle.showMenu(menu);\n\n const subChoices = mockSelectCancellable.mock.calls[1][0].choices;\n const subNames = subChoices.map((c: any) => c.name);\n expect(subNames).toContain(\"Back\");\n expect(subNames).not.toContain(\"Exit\");\n });\n\n test(\"action returning sprinkle instance saves settings\", async () => {\n sprinkle.settings = { name: \"original\" } as any;\n\n mockSelectCancellable.mockResolvedValueOnce(0).mockResolvedValueOnce(-1);\n\n const menu: IMenu<any> = {\n title: \"Test\",\n items: [\n {\n title: \"Update\",\n action: async (s: Sprinkle<any>) => {\n s.settings = { name: \"updated\" } as any;\n return s;\n },\n },\n ],\n };\n\n await sprinkle.showMenu(menu);\n expect(sprinkle.settings).toEqual({ name: \"updated\" });\n });\n\n test(\"Settings submenu includes View settings option\", async () => {\n // Select \"Settings & Profiles\" (-5), then \"Back\" (-1), then \"Exit\" (-1)\n mockSelectCancellable\n .mockResolvedValueOnce(-5) // Settings & Profiles\n .mockResolvedValueOnce(-1) // Back\n .mockResolvedValueOnce(-1); // Exit\n\n const menu: IMenu<any> = {\n title: \"Test\",\n items: [{ title: \"Action\", action: async () => {} }],\n };\n\n await sprinkle.showMenu(menu);\n\n // Second call is the settings submenu\n const settingsChoices = mockSelectCancellable.mock.calls[1][0].choices;\n const settingsNames = settingsChoices.map((c: any) => c.name);\n expect(settingsNames).toContain(\"View settings\");\n });\n\n test(\"View settings displays masked settings\", async () => {\n const consoleSpy = spyOn(console, \"log\").mockImplementation(() => {});\n\n // Select \"Settings & Profiles\" (-5), then \"View settings\" (0),\n // then \"Continue\" for the press Enter prompt, then \"Back\" (-1), then \"Exit\" (-1)\n mockSelectCancellable\n .mockResolvedValueOnce(-5) // Settings & Profiles\n .mockResolvedValueOnce(0) // View settings (first item)\n .mockResolvedValueOnce(\"continue\") // Press Enter to continue\n .mockResolvedValueOnce(-1) // Back\n .mockResolvedValueOnce(-1); // Exit\n\n const menu: IMenu<any> = {\n title: \"Test\",\n items: [{ title: \"Action\", action: async () => {} }],\n };\n\n await sprinkle.showMenu(menu);\n\n // Should have called console.log with the settings (find the JSON output among breadcrumbs)\n expect(consoleSpy).toHaveBeenCalled();\n const jsonOutput = consoleSpy.mock.calls.find((call: any[]) =>\n call[0] && typeof call[0] === \"string\" && call[0].includes('\"name\"')\n );\n expect(jsonOutput).toBeDefined();\n\n consoleSpy.mockRestore();\n });\n\n test(\"Create profile restores state when FillInStruct is cancelled\", async () => {\n // Capture original state\n const originalProfileId = sprinkle.currentProfile?.id;\n const originalSettings = { ...sprinkle.settings };\n\n // Settings menu indices: 0=View, 1=Edit, 2=Switch, 3=Create new profile\n mockSelectCancellable\n .mockResolvedValueOnce(-5) // Settings & Profiles\n .mockResolvedValueOnce(3); // Create new profile\n\n // promptProfileMeta: name then description\n mockInputCancellable\n .mockResolvedValueOnce(\"New Profile\") // Profile name\n .mockResolvedValueOnce(\"\") // Profile description\n .mockResolvedValueOnce(null); // Cancel during FillInStruct (name field)\n\n // After cancellation, back out of menus\n mockSelectCancellable\n .mockResolvedValueOnce(-1) // Back from settings\n .mockResolvedValueOnce(-1); // Exit\n\n const menu: IMenu<any> = {\n title: \"Test\",\n items: [{ title: \"Action\", action: async () => {} }],\n };\n\n await sprinkle.showMenu(menu);\n\n // State should be restored to original\n expect(sprinkle.currentProfile?.id).toBe(originalProfileId);\n expect(sprinkle.settings).toEqual(originalSettings);\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"show-menu.test.js","names":["_bunTest","require","_index","_testHelpers","mockSearch","mock","mockSelect","mockInput","mockSelectCancellable","mockInputCancellable","module","search","select","input","selectCancellable","inputCancellable","passwordCancellable","confirmCancellable","searchCancellable","collectChoices","call","source","undefined","pickByName","choices","name","match","find","c","Error","map","join","value","describe","sprinkle","beforeEach","schema","Type","Object","String","withProfile","Sprinkle","settings","mockClear","test","mockImplementationOnce","opts","menu","title","items","action","showMenu","expect","toHaveBeenCalledTimes","actionFn","calls","names","toContain","filtered","exitChoice","filteredNames","toBe","not","s","toEqual","order","subBeforeShow","push","beforeShow"],"sources":["../../../../src/Sprinkle/__tests__/show-menu.test.ts"],"sourcesContent":["import { describe, expect, test, mock, beforeEach, spyOn } from \"bun:test\";\nimport { Sprinkle, Type, type IMenu } from \"../index.js\";\nimport { withProfile } from \"./test-helpers.js\";\n\nconst mockSearch = mock();\nconst mockSelect = mock();\nconst mockInput = mock();\nconst mockSelectCancellable = mock();\nconst mockInputCancellable = mock();\n\nmock.module(\"@inquirer/prompts\", () => ({\n search: mockSearch,\n select: mockSelect,\n input: mockInput,\n}));\n\nmock.module(\"../prompts.js\", () => ({\n selectCancellable: mockSelectCancellable,\n inputCancellable: mockInputCancellable,\n passwordCancellable: mock(),\n confirmCancellable: mock(),\n searchCancellable: mockSearch,\n select: mockSelectCancellable,\n}));\n\ntype SearchSource = (\n term: string | undefined,\n) => Promise<{ name: string; value: any }[]>;\n\nasync function collectChoices(call: any): Promise<{ name: string; value: any }[]> {\n const source: SearchSource = call[0].source;\n return source(undefined);\n}\n\nfunction pickByName(\n choices: { name: string; value: any }[],\n name: string,\n): any {\n const match = choices.find((c) => c.name === name);\n if (!match) {\n throw new Error(\n `No choice named \"${name}\". Available: ${choices.map((c) => c.name).join(\", \")}`,\n );\n }\n return match.value;\n}\n\ndescribe(\"showMenu (search-based)\", () => {\n let sprinkle: Sprinkle<any>;\n\n beforeEach(() => {\n const schema = Type.Object({ name: Type.String() });\n sprinkle = withProfile(new Sprinkle(schema, \"/tmp/test\"));\n sprinkle.settings = { name: \"test\" } as any;\n mockSearch.mockClear();\n mockSelect.mockClear();\n mockSelectCancellable.mockClear();\n mockInput.mockClear();\n });\n\n test(\"exits when the Exit leaf is selected\", async () => {\n mockSearch.mockImplementationOnce(async (opts: any) => {\n const choices = await opts.source(undefined);\n return pickByName(choices, \"Exit\");\n });\n\n const menu: IMenu<any> = {\n title: \"Test Menu\",\n items: [{ title: \"Action 1\", action: async () => {} }],\n };\n\n await sprinkle.showMenu(menu);\n expect(mockSearch).toHaveBeenCalledTimes(1);\n });\n\n test(\"executes a leaf action then loops back to the search prompt\", async () => {\n const actionFn = mock(async () => {});\n\n mockSearch\n .mockImplementationOnce(async (opts: any) => {\n const choices = await opts.source(undefined);\n return pickByName(choices, \"My Action\");\n })\n .mockImplementationOnce(async (opts: any) => {\n const choices = await opts.source(undefined);\n return pickByName(choices, \"Exit\");\n });\n\n const menu: IMenu<any> = {\n title: \"Test Menu\",\n items: [{ title: \"My Action\", action: actionFn }],\n };\n\n await sprinkle.showMenu(menu);\n expect(actionFn).toHaveBeenCalledTimes(1);\n expect(mockSearch).toHaveBeenCalledTimes(2);\n });\n\n test(\"flattens submenus into breadcrumb leaves and surfaces Settings & Profiles + Exit\", async () => {\n mockSearch.mockImplementationOnce(async (opts: any) => {\n const choices = await opts.source(undefined);\n return pickByName(choices, \"Exit\");\n });\n\n const menu: IMenu<any> = {\n title: \"Main\",\n items: [\n {\n title: \"Strategies\",\n items: [\n { title: \"Create limit order\", action: async () => {} },\n { title: \"List strategies\", action: async () => {} },\n ],\n },\n { title: \"Quit (custom)\", action: async () => {} },\n ],\n };\n\n await sprinkle.showMenu(menu);\n\n const choices = await collectChoices(mockSearch.mock.calls[0]);\n const names = choices.map((c) => c.name);\n expect(names).toContain(\"Strategies › Create limit order\");\n expect(names).toContain(\"Strategies › List strategies\");\n expect(names).toContain(\"Quit (custom)\");\n expect(names).toContain(\"Settings & Profiles › Edit settings\");\n expect(names).toContain(\"Settings & Profiles › Import profile from file\");\n expect(names).toContain(\"Exit\");\n });\n\n test(\"filters leaves by typed term (case-insensitive, all tokens, leaf-title boost)\", async () => {\n let filtered: { name: string; value: any }[] = [];\n\n mockSearch.mockImplementationOnce(async (opts: any) => {\n filtered = await opts.source(\"strateg\");\n // pick Exit so the loop terminates\n const exitChoice = (await opts.source(undefined)).find(\n (c: any) => c.name === \"Exit\",\n );\n return exitChoice.value;\n });\n\n const menu: IMenu<any> = {\n title: \"Main\",\n items: [\n {\n title: \"Strategies\",\n items: [\n { title: \"Create limit order\", action: async () => {} },\n { title: \"Cancel strategy\", action: async () => {} },\n ],\n },\n { title: \"Send payment\", action: async () => {} },\n ],\n };\n\n await sprinkle.showMenu(menu);\n\n const filteredNames = filtered.map((c) => c.name);\n // Everything under \"Strategies\" matches via the breadcrumb token\n expect(filteredNames).toContain(\"Strategies › Create limit order\");\n expect(filteredNames).toContain(\"Strategies › Cancel strategy\");\n // Direct leaf-title hit outranks ancestor-only match\n expect(filteredNames[0]).toBe(\"Strategies › Cancel strategy\");\n // Unrelated items are filtered out\n expect(filteredNames).not.toContain(\"Send payment\");\n });\n\n test(\"action returning a Sprinkle persists updated settings\", async () => {\n sprinkle.settings = { name: \"original\" } as any;\n\n mockSearch\n .mockImplementationOnce(async (opts: any) => {\n const choices = await opts.source(undefined);\n return pickByName(choices, \"Update\");\n })\n .mockImplementationOnce(async (opts: any) => {\n const choices = await opts.source(undefined);\n return pickByName(choices, \"Exit\");\n });\n\n const menu: IMenu<any> = {\n title: \"Test\",\n items: [\n {\n title: \"Update\",\n action: async (s: Sprinkle<any>) => {\n s.settings = { name: \"updated\" } as any;\n return s;\n },\n },\n ],\n };\n\n await sprinkle.showMenu(menu);\n expect(sprinkle.settings).toEqual({ name: \"updated\" });\n });\n\n test(\"submenu beforeShow fires only when a leaf under it is selected\", async () => {\n const order: string[] = [];\n const subBeforeShow = mock(async () => {\n order.push(\"sub.beforeShow\");\n });\n const actionFn = mock(async () => {\n order.push(\"action\");\n });\n\n mockSearch\n .mockImplementationOnce(async (opts: any) => {\n const choices = await opts.source(undefined);\n return pickByName(choices, \"Strategies › Create\");\n })\n .mockImplementationOnce(async (opts: any) => {\n const choices = await opts.source(undefined);\n return pickByName(choices, \"Exit\");\n });\n\n const menu: IMenu<any> = {\n title: \"Main\",\n items: [\n {\n title: \"Strategies\",\n beforeShow: subBeforeShow,\n items: [{ title: \"Create\", action: actionFn }],\n },\n ],\n };\n\n await sprinkle.showMenu(menu);\n expect(subBeforeShow).toHaveBeenCalledTimes(1);\n expect(order).toEqual([\"sub.beforeShow\", \"action\"]);\n });\n});\n"],"mappings":"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","ignoreList":[]}
|