im-pickle-rick 0.1.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/README.md +242 -0
- package/bin.js +3 -0
- package/dist/pickle +0 -0
- package/dist/worker-executor.js +207 -0
- package/package.json +53 -0
- package/src/games/GameSidebarManager.test.ts +64 -0
- package/src/games/GameSidebarManager.ts +78 -0
- package/src/games/gameboy/GameboyView.test.ts +25 -0
- package/src/games/gameboy/GameboyView.ts +100 -0
- package/src/games/gameboy/gameboy-polyfills.ts +313 -0
- package/src/games/index.test.ts +9 -0
- package/src/games/index.ts +4 -0
- package/src/games/snake/SnakeGame.test.ts +35 -0
- package/src/games/snake/SnakeGame.ts +145 -0
- package/src/games/snake/SnakeView.test.ts +25 -0
- package/src/games/snake/SnakeView.ts +290 -0
- package/src/index.test.ts +24 -0
- package/src/index.ts +141 -0
- package/src/services/commands/worker.test.ts +14 -0
- package/src/services/commands/worker.ts +262 -0
- package/src/services/config/index.ts +2 -0
- package/src/services/config/settings.test.ts +42 -0
- package/src/services/config/settings.ts +220 -0
- package/src/services/config/state.test.ts +88 -0
- package/src/services/config/state.ts +130 -0
- package/src/services/config/types.ts +39 -0
- package/src/services/execution/index.ts +1 -0
- package/src/services/execution/pickle-source.test.ts +88 -0
- package/src/services/execution/pickle-source.ts +264 -0
- package/src/services/execution/prompt.test.ts +93 -0
- package/src/services/execution/prompt.ts +322 -0
- package/src/services/execution/sequential.test.ts +91 -0
- package/src/services/execution/sequential.ts +422 -0
- package/src/services/execution/worker-client.ts +94 -0
- package/src/services/execution/worker-executor.ts +41 -0
- package/src/services/execution/worker.test.ts +73 -0
- package/src/services/git/branch.test.ts +147 -0
- package/src/services/git/branch.ts +128 -0
- package/src/services/git/diff.test.ts +113 -0
- package/src/services/git/diff.ts +323 -0
- package/src/services/git/index.ts +4 -0
- package/src/services/git/pr.test.ts +104 -0
- package/src/services/git/pr.ts +192 -0
- package/src/services/git/worktree.test.ts +99 -0
- package/src/services/git/worktree.ts +141 -0
- package/src/services/providers/base.test.ts +86 -0
- package/src/services/providers/base.ts +438 -0
- package/src/services/providers/codex.test.ts +39 -0
- package/src/services/providers/codex.ts +208 -0
- package/src/services/providers/gemini.test.ts +40 -0
- package/src/services/providers/gemini.ts +169 -0
- package/src/services/providers/index.test.ts +28 -0
- package/src/services/providers/index.ts +41 -0
- package/src/services/providers/opencode.test.ts +64 -0
- package/src/services/providers/opencode.ts +228 -0
- package/src/services/providers/types.ts +44 -0
- package/src/skills/code-implementer.md +105 -0
- package/src/skills/code-researcher.md +78 -0
- package/src/skills/implementation-planner.md +105 -0
- package/src/skills/plan-reviewer.md +100 -0
- package/src/skills/prd-drafter.md +123 -0
- package/src/skills/research-reviewer.md +79 -0
- package/src/skills/ruthless-refactorer.md +52 -0
- package/src/skills/ticket-manager.md +135 -0
- package/src/types/index.ts +2 -0
- package/src/types/rpc.ts +14 -0
- package/src/types/tasks.ts +50 -0
- package/src/types.d.ts +9 -0
- package/src/ui/common.ts +28 -0
- package/src/ui/components/FilePickerView.test.ts +79 -0
- package/src/ui/components/FilePickerView.ts +161 -0
- package/src/ui/components/MultiLineInput.test.ts +27 -0
- package/src/ui/components/MultiLineInput.ts +233 -0
- package/src/ui/components/SessionChip.test.ts +69 -0
- package/src/ui/components/SessionChip.ts +481 -0
- package/src/ui/components/ToyboxSidebar.test.ts +36 -0
- package/src/ui/components/ToyboxSidebar.ts +329 -0
- package/src/ui/components/refactor_plan.md +35 -0
- package/src/ui/controllers/DashboardController.integration.test.ts +43 -0
- package/src/ui/controllers/DashboardController.ts +650 -0
- package/src/ui/dashboard.test.ts +43 -0
- package/src/ui/dashboard.ts +309 -0
- package/src/ui/dialogs/DashboardDialog.test.ts +146 -0
- package/src/ui/dialogs/DashboardDialog.ts +399 -0
- package/src/ui/dialogs/Dialog.test.ts +50 -0
- package/src/ui/dialogs/Dialog.ts +241 -0
- package/src/ui/dialogs/DialogSidebar.test.ts +60 -0
- package/src/ui/dialogs/DialogSidebar.ts +71 -0
- package/src/ui/dialogs/DiffViewDialog.test.ts +57 -0
- package/src/ui/dialogs/DiffViewDialog.ts +510 -0
- package/src/ui/dialogs/PRPreviewDialog.test.ts +50 -0
- package/src/ui/dialogs/PRPreviewDialog.ts +346 -0
- package/src/ui/dialogs/test-utils.ts +232 -0
- package/src/ui/file-picker-utils.test.ts +71 -0
- package/src/ui/file-picker-utils.ts +200 -0
- package/src/ui/input-chrome.test.ts +62 -0
- package/src/ui/input-chrome.ts +172 -0
- package/src/ui/logger.test.ts +68 -0
- package/src/ui/logger.ts +45 -0
- package/src/ui/mock-factory.ts +6 -0
- package/src/ui/spinner.test.ts +65 -0
- package/src/ui/spinner.ts +41 -0
- package/src/ui/test-setup.ts +300 -0
- package/src/ui/theme.test.ts +23 -0
- package/src/ui/theme.ts +16 -0
- package/src/ui/views/LandingView.integration.test.ts +21 -0
- package/src/ui/views/LandingView.test.ts +24 -0
- package/src/ui/views/LandingView.ts +221 -0
- package/src/ui/views/LogView.test.ts +24 -0
- package/src/ui/views/LogView.ts +277 -0
- package/src/ui/views/ToyboxView.test.ts +46 -0
- package/src/ui/views/ToyboxView.ts +323 -0
- package/src/utils/clipboard.test.ts +86 -0
- package/src/utils/clipboard.ts +100 -0
- package/src/utils/index.test.ts +68 -0
- package/src/utils/index.ts +95 -0
- package/src/utils/persona.test.ts +12 -0
- package/src/utils/persona.ts +8 -0
- package/src/utils/project-root.test.ts +38 -0
- package/src/utils/project-root.ts +22 -0
- package/src/utils/resources.test.ts +64 -0
- package/src/utils/resources.ts +92 -0
- package/src/utils/search.test.ts +48 -0
- package/src/utils/search.ts +103 -0
- package/src/utils/session-tracker.test.ts +46 -0
- package/src/utils/session-tracker.ts +67 -0
- package/src/utils/spinner.test.ts +54 -0
- package/src/utils/spinner.ts +87 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BoxRenderable,
|
|
3
|
+
MouseEvent,
|
|
4
|
+
CliRenderer,
|
|
5
|
+
TextRenderable,
|
|
6
|
+
TextAttributes,
|
|
7
|
+
ScrollBoxRenderable,
|
|
8
|
+
createTimeline,
|
|
9
|
+
} from "@opentui/core";
|
|
10
|
+
import { THEME } from "../theme.js";
|
|
11
|
+
import { formatDuration } from "../../utils/index.js";
|
|
12
|
+
import { launchSnake } from "../../games/snake/SnakeView.js";
|
|
13
|
+
import { sessionTracker, type TrackedSession } from "../../utils/session-tracker.js";
|
|
14
|
+
import { loadState } from "../../services/config/state.js";
|
|
15
|
+
|
|
16
|
+
export class ToyboxSidebar {
|
|
17
|
+
public root: BoxRenderable;
|
|
18
|
+
private content: ScrollBoxRenderable;
|
|
19
|
+
private renderer: CliRenderer;
|
|
20
|
+
private isVisible = false;
|
|
21
|
+
public onHide?: () => void;
|
|
22
|
+
private ticker: ReturnType<typeof setInterval> | null = null;
|
|
23
|
+
|
|
24
|
+
private titleText: TextRenderable;
|
|
25
|
+
|
|
26
|
+
constructor(renderer: CliRenderer) {
|
|
27
|
+
this.renderer = renderer;
|
|
28
|
+
this.root = new BoxRenderable(renderer, {
|
|
29
|
+
id: "toybox-sidebar-root",
|
|
30
|
+
width: 45,
|
|
31
|
+
height: "100%",
|
|
32
|
+
position: "absolute",
|
|
33
|
+
right: -45, // Start off-screen
|
|
34
|
+
flexDirection: "column",
|
|
35
|
+
backgroundColor: THEME.bg,
|
|
36
|
+
visible: false,
|
|
37
|
+
zIndex: 25000,
|
|
38
|
+
border: ["left"],
|
|
39
|
+
borderColor: THEME.darkAccent,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
this.content = new ScrollBoxRenderable(renderer, {
|
|
43
|
+
id: "toybox-sidebar-content",
|
|
44
|
+
width: "100%",
|
|
45
|
+
flexGrow: 1,
|
|
46
|
+
scrollY: true,
|
|
47
|
+
scrollX: false,
|
|
48
|
+
scrollbarOptions: {
|
|
49
|
+
trackOptions: {
|
|
50
|
+
backgroundColor: THEME.darkAccent,
|
|
51
|
+
foregroundColor: THEME.accent,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Header
|
|
57
|
+
const header = new BoxRenderable(renderer, {
|
|
58
|
+
id: "toybox-sidebar-header",
|
|
59
|
+
width: "100%",
|
|
60
|
+
height: 3,
|
|
61
|
+
flexDirection: "column",
|
|
62
|
+
justifyContent: "center",
|
|
63
|
+
paddingLeft: 2,
|
|
64
|
+
paddingRight: 2,
|
|
65
|
+
border: ["bottom"],
|
|
66
|
+
borderColor: THEME.darkAccent,
|
|
67
|
+
flexShrink: 0,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
this.titleText = new TextRenderable(renderer, {
|
|
71
|
+
id: "toybox-sidebar-title",
|
|
72
|
+
content: "Sessions",
|
|
73
|
+
fg: THEME.accent,
|
|
74
|
+
attributes: TextAttributes.BOLD,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
header.add(this.titleText);
|
|
78
|
+
|
|
79
|
+
this.root.add(header);
|
|
80
|
+
this.root.add(this.content);
|
|
81
|
+
|
|
82
|
+
this.setupMouseHandlers();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private setupMouseHandlers() {
|
|
86
|
+
this.root.onMouse = (event: MouseEvent) => {
|
|
87
|
+
if ((event.type as any) === "click") {
|
|
88
|
+
// Allow clicking through to content
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public async show() {
|
|
95
|
+
if (this.isVisible) return;
|
|
96
|
+
|
|
97
|
+
this.isVisible = true;
|
|
98
|
+
this.root.visible = true;
|
|
99
|
+
|
|
100
|
+
// Animate in
|
|
101
|
+
createTimeline().add(this.root, {
|
|
102
|
+
right: 0,
|
|
103
|
+
duration: 200,
|
|
104
|
+
ease: "outQuad",
|
|
105
|
+
onUpdate: () => this.renderer.requestRender(),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await this.refreshContent();
|
|
109
|
+
this.startTicker();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public hide() {
|
|
113
|
+
if (!this.isVisible) return;
|
|
114
|
+
|
|
115
|
+
this.stopTicker();
|
|
116
|
+
|
|
117
|
+
// Animate out
|
|
118
|
+
createTimeline().add(this.root, {
|
|
119
|
+
right: -45,
|
|
120
|
+
duration: 200,
|
|
121
|
+
ease: "inQuad",
|
|
122
|
+
onUpdate: () => this.renderer.requestRender(),
|
|
123
|
+
onComplete: () => {
|
|
124
|
+
this.isVisible = false;
|
|
125
|
+
this.root.visible = false;
|
|
126
|
+
this.onHide?.();
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public destroy() {
|
|
132
|
+
this.stopTicker();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public isOpen(): boolean {
|
|
136
|
+
return this.isVisible;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private async refreshContent() {
|
|
140
|
+
// Clear all children - collect IDs first, then remove in reverse order
|
|
141
|
+
const children = this.content.getChildren();
|
|
142
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
143
|
+
this.content.remove(children[i].id);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Add sessions section
|
|
147
|
+
await this.addSessionsSection();
|
|
148
|
+
|
|
149
|
+
this.renderer.requestRender();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private startTicker() {
|
|
153
|
+
if (this.ticker) return;
|
|
154
|
+
this.ticker = setInterval(async () => {
|
|
155
|
+
if (!this.isVisible) return;
|
|
156
|
+
try {
|
|
157
|
+
await this.refreshContent();
|
|
158
|
+
} catch (e) {
|
|
159
|
+
// Silently ignore refresh errors
|
|
160
|
+
}
|
|
161
|
+
}, 2000);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private stopTicker() {
|
|
165
|
+
if (this.ticker) {
|
|
166
|
+
clearInterval(this.ticker);
|
|
167
|
+
this.ticker = null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private async addSessionsSection() {
|
|
172
|
+
try {
|
|
173
|
+
// Get tracked sessions (created from this UI instance)
|
|
174
|
+
const trackedSessions = sessionTracker.getTrackedSessions();
|
|
175
|
+
|
|
176
|
+
if (trackedSessions.length === 0) {
|
|
177
|
+
const emptyState = new TextRenderable(this.renderer, {
|
|
178
|
+
id: "empty-sessions",
|
|
179
|
+
content: "No active sessions",
|
|
180
|
+
fg: THEME.dim,
|
|
181
|
+
alignSelf: "center",
|
|
182
|
+
marginTop: 1,
|
|
183
|
+
});
|
|
184
|
+
this.content.add(emptyState);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Get current status for each tracked session
|
|
189
|
+
const sessionsWithStatus = await Promise.all(
|
|
190
|
+
trackedSessions.map(async (trackedSession) => {
|
|
191
|
+
const state = await loadState(trackedSession.sessionDir);
|
|
192
|
+
// Always prefer fresh state from disk over cached status
|
|
193
|
+
const status = state && state.active && state.step !== "done"
|
|
194
|
+
? `${state.step.toUpperCase()} (Iteration ${state.iteration})`
|
|
195
|
+
: state?.step === "done"
|
|
196
|
+
? "Done"
|
|
197
|
+
: trackedSession.status ?? "Starting...";
|
|
198
|
+
const iteration = state?.iteration ?? trackedSession.iteration ?? 0;
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
...trackedSession,
|
|
202
|
+
status,
|
|
203
|
+
iteration,
|
|
204
|
+
};
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
// Create session cards
|
|
209
|
+
for (let i = 0; i < sessionsWithStatus.length; i++) {
|
|
210
|
+
const session = sessionsWithStatus[i];
|
|
211
|
+
const sessionCard = this.createSessionCard(session, i);
|
|
212
|
+
this.content.add(sessionCard);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
} catch (error) {
|
|
216
|
+
const errorText = new TextRenderable(this.renderer, {
|
|
217
|
+
id: "error-text",
|
|
218
|
+
content: "Failed to load sessions",
|
|
219
|
+
fg: "#ff6b6b",
|
|
220
|
+
alignSelf: "center",
|
|
221
|
+
marginTop: 2,
|
|
222
|
+
});
|
|
223
|
+
this.content.add(errorText);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private createSessionCard(session: TrackedSession & { status: string }, index: number): BoxRenderable {
|
|
228
|
+
const shortId = session.id.substring(0, 8);
|
|
229
|
+
const cardId = `${index}-${shortId}`;
|
|
230
|
+
|
|
231
|
+
const card = new BoxRenderable(this.renderer, {
|
|
232
|
+
id: `session-card-${cardId}`,
|
|
233
|
+
width: "100%",
|
|
234
|
+
flexDirection: "column",
|
|
235
|
+
paddingLeft: 2,
|
|
236
|
+
paddingRight: 2,
|
|
237
|
+
paddingTop: 1,
|
|
238
|
+
paddingBottom: 1,
|
|
239
|
+
border: ["bottom"],
|
|
240
|
+
borderColor: THEME.darkAccent,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Status indicator and title
|
|
244
|
+
const statusRow = new BoxRenderable(this.renderer, {
|
|
245
|
+
id: `status-row-${cardId}`,
|
|
246
|
+
width: "100%",
|
|
247
|
+
flexDirection: "row",
|
|
248
|
+
justifyContent: "space-between",
|
|
249
|
+
marginBottom: 1,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
const isActive = session.status.toLowerCase() !== "done";
|
|
253
|
+
const statusColor = isActive ? THEME.green : THEME.accent;
|
|
254
|
+
|
|
255
|
+
const statusIndicator = new TextRenderable(this.renderer, {
|
|
256
|
+
id: `status-indicator-${cardId}`,
|
|
257
|
+
content: isActive ? "🟢" : "✅",
|
|
258
|
+
fg: statusColor,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const statusText = new TextRenderable(this.renderer, {
|
|
262
|
+
id: `status-text-${cardId}`,
|
|
263
|
+
content: session.status.toUpperCase(),
|
|
264
|
+
fg: statusColor,
|
|
265
|
+
attributes: TextAttributes.BOLD,
|
|
266
|
+
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
statusRow.add(statusIndicator);
|
|
270
|
+
statusRow.add(statusText);
|
|
271
|
+
|
|
272
|
+
// Session prompt (truncated)
|
|
273
|
+
const promptText = session.prompt.length > 50
|
|
274
|
+
? session.prompt.substring(0, 47) + "..."
|
|
275
|
+
: session.prompt;
|
|
276
|
+
|
|
277
|
+
const promptRenderable = new TextRenderable(this.renderer, {
|
|
278
|
+
id: `prompt-${cardId}`,
|
|
279
|
+
content: promptText,
|
|
280
|
+
fg: THEME.text,
|
|
281
|
+
marginBottom: 1,
|
|
282
|
+
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Session metadata
|
|
286
|
+
const metaRow = new BoxRenderable(this.renderer, {
|
|
287
|
+
id: `meta-row-${cardId}`,
|
|
288
|
+
width: "100%",
|
|
289
|
+
flexDirection: "row",
|
|
290
|
+
justifyContent: "space-between",
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const timeAgo = formatDuration(Date.now() - session.createdAt);
|
|
294
|
+
|
|
295
|
+
const timeText = new TextRenderable(this.renderer, {
|
|
296
|
+
id: `time-${cardId}`,
|
|
297
|
+
content: `Started ${timeAgo} ago`,
|
|
298
|
+
fg: THEME.dim,
|
|
299
|
+
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const sessionIdText = new TextRenderable(this.renderer, {
|
|
303
|
+
id: `session-id-${cardId}`,
|
|
304
|
+
content: shortId,
|
|
305
|
+
fg: THEME.darkAccent,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
metaRow.add(timeText);
|
|
309
|
+
metaRow.add(sessionIdText);
|
|
310
|
+
|
|
311
|
+
card.add(statusRow);
|
|
312
|
+
card.add(promptRenderable);
|
|
313
|
+
card.add(metaRow);
|
|
314
|
+
|
|
315
|
+
// Add hover effect
|
|
316
|
+
card.onMouse = (event: MouseEvent) => {
|
|
317
|
+
switch (event.type) {
|
|
318
|
+
case "over":
|
|
319
|
+
card.backgroundColor = "#2d372d";
|
|
320
|
+
break;
|
|
321
|
+
case "out":
|
|
322
|
+
card.backgroundColor = THEME.bg;
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
return card;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Refactor Plan: Clean up MultiLineInput Tests
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
Remove `any` types and redundant comments from `MultiLineInput.test.ts`.
|
|
5
|
+
|
|
6
|
+
## Current State Analysis
|
|
7
|
+
- `mockCtx` is typed as `any`.
|
|
8
|
+
- Multiple casts to `any` for private property access.
|
|
9
|
+
- `keyEvent` is `any`.
|
|
10
|
+
|
|
11
|
+
## The Kill List
|
|
12
|
+
- `mockCtx: any`
|
|
13
|
+
- `(input as any)`
|
|
14
|
+
- `keyEvent: any`
|
|
15
|
+
|
|
16
|
+
## Consolidation Map
|
|
17
|
+
- Use a partial of `RenderContext` for `mockCtx`.
|
|
18
|
+
- Create an `InternalMultiLineInput` interface for private access.
|
|
19
|
+
- Use `KeyEvent` from `@opentui/core` for `keyEvent`.
|
|
20
|
+
|
|
21
|
+
## Changes Required:
|
|
22
|
+
#### cli/src/ui/components/MultiLineInput.test.ts
|
|
23
|
+
**Changes**: Add `InternalMultiLineInput` interface and fix types.
|
|
24
|
+
```typescript
|
|
25
|
+
interface InternalMultiLineInput {
|
|
26
|
+
virtualLineCount: number;
|
|
27
|
+
onContentChange: () => void;
|
|
28
|
+
adjustHeight: () => void;
|
|
29
|
+
emit: (event: any, ...args: any[]) => void;
|
|
30
|
+
_placeholder: any;
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Verification
|
|
35
|
+
- [ ] `bun test cli/src/ui/components/MultiLineInput.test.ts`
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { expect, test, describe, beforeEach, afterEach, mock, spyOn } from "bun:test";
|
|
2
|
+
import "../test-setup.js";
|
|
3
|
+
import { createMockRenderer } from "../mock-factory.js";
|
|
4
|
+
import { DashboardController } from "./DashboardController.js";
|
|
5
|
+
import * as search from "../../utils/search.js";
|
|
6
|
+
import * as state from "../../services/config/state.js";
|
|
7
|
+
|
|
8
|
+
describe("DashboardController Integration", () => {
|
|
9
|
+
let mockRenderer: any;
|
|
10
|
+
let mockSessionContainer: any;
|
|
11
|
+
let mockSidebar: any;
|
|
12
|
+
let spies: any[] = [];
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
mockRenderer = createMockRenderer();
|
|
16
|
+
mockSessionContainer = { add: mock(() => {}), getChildren: mock(() => []) };
|
|
17
|
+
mockSidebar = { onHide: null, root: { visible: false }, isOpen: () => false, hide: mock(() => {}), update: mock(() => {}) };
|
|
18
|
+
|
|
19
|
+
spies = [
|
|
20
|
+
spyOn(search, "recursiveSearch").mockImplementation(async (dir, query) => {
|
|
21
|
+
if (query === "test") {
|
|
22
|
+
return { files: ["/root/test.ts"], truncated: false };
|
|
23
|
+
}
|
|
24
|
+
return { files: [], truncated: false };
|
|
25
|
+
}),
|
|
26
|
+
spyOn(state, "listSessions").mockResolvedValue([]),
|
|
27
|
+
spyOn(state, "createSession").mockResolvedValue({ session_dir: "/tmp/session" } as any),
|
|
28
|
+
];
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
spies.forEach(s => s.mockRestore());
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("should initialize controller", () => {
|
|
36
|
+
const controller = new DashboardController(
|
|
37
|
+
mockRenderer,
|
|
38
|
+
mockSessionContainer
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
expect(controller).toBeDefined();
|
|
42
|
+
});
|
|
43
|
+
});
|