blob: b643e501516388d756dcb5c504e33988034dc786 [file] [log] [blame]
Philip Zeyliger272a90e2025-05-16 14:49:51 -07001// mock-git-data-service.ts
2// Mock implementation of GitDataService for the demo environment
3
Autoformatter8c463622025-05-16 21:54:17 +00004import { GitDataService, GitDiffFile } from "../git-data-service";
5import { GitLogEntry } from "../../types";
Philip Zeyliger272a90e2025-05-16 14:49:51 -07006
7/**
8 * Demo implementation of GitDataService with canned responses
9 */
10export class MockGitDataService implements GitDataService {
11 constructor() {
Autoformatter8c463622025-05-16 21:54:17 +000012 console.log("MockGitDataService instance created");
Philip Zeyliger254c49f2025-07-17 17:26:24 -070013 // Setup mock push endpoints when service is created
14 setupMockPushEndpoints();
Philip Zeyliger272a90e2025-05-16 14:49:51 -070015 }
16
17 // Mock commit history
18 private mockCommits: GitLogEntry[] = [
19 {
Autoformatter8c463622025-05-16 21:54:17 +000020 hash: "abc123456789",
21 subject: "Implement new file picker UI",
Philip Zeyliger364b35a2025-06-21 16:39:04 -070022 refs: ["HEAD", "origin/main", "refs/heads/feature/file-picker"],
Philip Zeyliger272a90e2025-05-16 14:49:51 -070023 },
24 {
Autoformatter8c463622025-05-16 21:54:17 +000025 hash: "def987654321",
Philip Zeyliger364b35a2025-06-21 16:39:04 -070026 subject: "Add range picker component and improve styling",
27 refs: [
28 "origin/feature/range-picker",
29 "refs/heads/feature/ui-improvements",
30 "refs/remotes/origin/dev",
31 ],
Philip Zeyliger272a90e2025-05-16 14:49:51 -070032 },
33 {
Autoformatter8c463622025-05-16 21:54:17 +000034 hash: "ghi456789123",
Philip Zeyliger364b35a2025-06-21 16:39:04 -070035 subject: "Fix styling issues in navigation and add responsive design",
36 refs: ["refs/heads/hotfix/styling", "refs/tags/v1.2.0"],
Philip Zeyliger272a90e2025-05-16 14:49:51 -070037 },
38 {
Autoformatter8c463622025-05-16 21:54:17 +000039 hash: "jkl789123456",
40 subject: "Initial commit",
41 refs: ["sketch-base"],
42 },
Philip Zeyliger272a90e2025-05-16 14:49:51 -070043 ];
44
45 // Mock diff files for various scenarios
46 private mockDiffFiles: GitDiffFile[] = [
47 {
Autoformatter8c463622025-05-16 21:54:17 +000048 path: "src/components/FilePicker.js",
Josh Bleecher Snyderbcc1c412025-05-29 00:36:49 +000049 old_path: "",
Autoformatter8c463622025-05-16 21:54:17 +000050 status: "A",
51 new_mode: "100644",
52 old_mode: "000000",
53 old_hash: "0000000000000000000000000000000000000000",
54 new_hash: "def0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +000055 additions: 54,
56 deletions: 0,
Philip Zeyliger272a90e2025-05-16 14:49:51 -070057 },
58 {
Autoformatter8c463622025-05-16 21:54:17 +000059 path: "src/components/RangePicker.js",
Josh Bleecher Snyderbcc1c412025-05-29 00:36:49 +000060 old_path: "",
Autoformatter8c463622025-05-16 21:54:17 +000061 status: "A",
62 new_mode: "100644",
63 old_mode: "000000",
64 old_hash: "0000000000000000000000000000000000000000",
65 new_hash: "cde0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +000066 additions: 32,
67 deletions: 0,
Philip Zeyliger272a90e2025-05-16 14:49:51 -070068 },
69 {
Autoformatter8c463622025-05-16 21:54:17 +000070 path: "src/components/App.js",
Josh Bleecher Snyderbcc1c412025-05-29 00:36:49 +000071 old_path: "",
Autoformatter8c463622025-05-16 21:54:17 +000072 status: "M",
73 new_mode: "100644",
74 old_mode: "100644",
75 old_hash: "abc0123456789abcdef0123456789abcdef0123",
76 new_hash: "bcd0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +000077 additions: 15,
78 deletions: 3,
Philip Zeyliger272a90e2025-05-16 14:49:51 -070079 },
80 {
Josh Bleecher Snyderb3aff882025-07-01 02:17:27 +000081 path: "src/components/DialogPicker.js",
82 old_path: "src/components/FilePicker.js",
83 status: "C85",
84 new_mode: "100644",
85 old_mode: "100644",
86 old_hash: "def0123456789abcdef0123456789abcdef0123",
87 new_hash: "hij0123456789abcdef0123456789abcdef0123",
88 additions: 8,
89 deletions: 2,
90 },
91 {
Autoformatter9556fcf2025-07-02 22:48:51 +000092 path: "src/components/RangeSelector.js",
Josh Bleecher Snyderb3aff882025-07-01 02:17:27 +000093 old_path: "src/components/RangePicker.js",
94 status: "R95",
95 new_mode: "100644",
96 old_mode: "100644",
97 old_hash: "cde0123456789abcdef0123456789abcdef0123",
98 new_hash: "klm0123456789abcdef0123456789abcdef0123",
99 additions: 5,
100 deletions: 3,
101 },
102 {
Autoformatter8c463622025-05-16 21:54:17 +0000103 path: "src/styles/main.css",
Josh Bleecher Snyderbcc1c412025-05-29 00:36:49 +0000104 old_path: "",
Autoformatter8c463622025-05-16 21:54:17 +0000105 status: "M",
106 new_mode: "100644",
107 old_mode: "100644",
108 old_hash: "fgh0123456789abcdef0123456789abcdef0123",
109 new_hash: "ghi0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +0000110 additions: 25,
111 deletions: 8,
Autoformatter8c463622025-05-16 21:54:17 +0000112 },
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700113 ];
114
115 // Mock file content for different files and commits
116 private appJSOriginal = `function App() {
117 return (
118 <div className="app">
119 <header>
120 <h1>Git Diff Viewer</h1>
121 </header>
122 <main>
123 <p>Select a file to view differences</p>
124 </main>
125 </div>
126 );
127}`;
128
129 private appJSModified = `function App() {
130 const [files, setFiles] = useState([]);
131 const [selectedFile, setSelectedFile] = useState(null);
132
133 // Load commits and files
134 useEffect(() => {
135 // Code to load commits would go here
136 // setCommits(...);
137 }, []);
138
139 return (
140 <div className="app">
141 <header>
142 <h1>Git Diff Viewer</h1>
143 </header>
144 <main>
145 <FilePicker files={files} onFileSelect={setSelectedFile} />
146 <div className="diff-view">
147 {selectedFile ? (
148 <div>Diff view for {selectedFile.path}</div>
149 ) : (
150 <p>Select a file to view differences</p>
151 )}
152 </div>
153 </main>
154 </div>
155 );
156}`;
157
158 private filePickerJS = `function FilePicker({ files, onFileSelect }) {
159 const [selectedIndex, setSelectedIndex] = useState(0);
160
161 useEffect(() => {
162 // Reset selection when files change
163 setSelectedIndex(0);
164 if (files.length > 0) {
165 onFileSelect(files[0]);
166 }
167 }, [files, onFileSelect]);
168
169 const handleNext = () => {
170 if (selectedIndex < files.length - 1) {
171 const newIndex = selectedIndex + 1;
172 setSelectedIndex(newIndex);
173 onFileSelect(files[newIndex]);
174 }
175 };
176
177 const handlePrevious = () => {
178 if (selectedIndex > 0) {
179 const newIndex = selectedIndex - 1;
180 setSelectedIndex(newIndex);
181 onFileSelect(files[newIndex]);
182 }
183 };
184
185 return (
186 <div className="file-picker">
187 <select value={selectedIndex} onChange={(e) => {
188 const index = parseInt(e.target.value, 10);
189 setSelectedIndex(index);
190 onFileSelect(files[index]);
191 }}>
192 {files.map((file, index) => (
193 <option key={file.path} value={index}>
194 {file.status} {file.path}
195 </option>
196 ))}
197 </select>
198
199 <div className="navigation-buttons">
200 <button
201 onClick={handlePrevious}
202 disabled={selectedIndex === 0}
203 >
204 Previous
205 </button>
206 <button
207 onClick={handleNext}
208 disabled={selectedIndex === files.length - 1}
209 >
210 Next
211 </button>
212 </div>
213 </div>
214 );
215}`;
216
217 private rangePickerJS = `function RangePicker({ commits, onRangeChange }) {
218 const [rangeType, setRangeType] = useState('range');
219 const [startCommit, setStartCommit] = useState(commits[0]);
220 const [endCommit, setEndCommit] = useState(commits[commits.length - 1]);
221
222 const handleTypeChange = (e) => {
223 setRangeType(e.target.value);
224 if (e.target.value === 'single') {
225 onRangeChange({ type: 'single', commit: startCommit });
226 } else {
227 onRangeChange({ type: 'range', from: startCommit, to: endCommit });
228 }
229 };
230
231 return (
232 <div className="range-picker">
233 <div className="range-type-selector">
234 <label>
235 <input
236 type="radio"
237 value="range"
238 checked={rangeType === 'range'}
239 onChange={handleTypeChange}
240 />
241 Commit Range
242 </label>
243 <label>
244 <input
245 type="radio"
246 value="single"
247 checked={rangeType === 'single'}
248 onChange={handleTypeChange}
249 />
250 Single Commit
251 </label>
252 </div>
253 </div>
254 );
255}`;
256
Josh Bleecher Snyderb3aff882025-07-01 02:17:27 +0000257 private dialogPickerJS = `function DialogPicker({ files, onFileSelect, onClose }) {
258 const [selectedIndex, setSelectedIndex] = useState(0);
259 const [showDialog, setShowDialog] = useState(false);
260
261 // Similar to FilePicker but with modal dialog functionality
262 useEffect(() => {
263 setSelectedIndex(0);
264 if (files.length > 0) {
265 onFileSelect(files[0]);
266 }
267 }, [files, onFileSelect]);
268
269 return (
270 <div className="dialog-picker">
271 <button onClick={() => setShowDialog(true)}>
272 Open File Dialog
273 </button>
274 {showDialog && (
275 <div className="modal-overlay" onClick={() => setShowDialog(false)}>
276 <div className="modal-content" onClick={(e) => e.stopPropagation()}>
277 <h3>Select File</h3>
278 <select value={selectedIndex} onChange={(e) => {
279 const index = parseInt(e.target.value, 10);
280 setSelectedIndex(index);
281 onFileSelect(files[index]);
282 }}>
283 {files.map((file, index) => (
284 <option key={file.path} value={index}>
285 {file.status} {file.path}
286 </option>
287 ))}
288 </select>
289 <div className="modal-buttons">
290 <button onClick={() => setShowDialog(false)}>Close</button>
291 </div>
292 </div>
293 </div>
294 )}
295 </div>
296 );
297}`;
298
299 private rangeSelectorJS = `function RangeSelector({ commits, onRangeChange, allowCustomRange = true }) {
300 const [rangeType, setRangeType] = useState('range');
301 const [startCommit, setStartCommit] = useState(commits[0]);
302 const [endCommit, setEndCommit] = useState(commits[commits.length - 1]);
303 const [customRange, setCustomRange] = useState('');
304
305 const handleTypeChange = (e) => {
306 setRangeType(e.target.value);
307 if (e.target.value === 'single') {
308 onRangeChange({ type: 'single', commit: startCommit });
309 } else if (e.target.value === 'custom' && customRange) {
310 onRangeChange({ type: 'custom', range: customRange });
311 } else {
312 onRangeChange({ type: 'range', from: startCommit, to: endCommit });
313 }
314 };
315
316 return (
317 <div className="range-selector">
318 <div className="range-type-selector">
319 <label>
320 <input
321 type="radio"
322 value="range"
323 checked={rangeType === 'range'}
324 onChange={handleTypeChange}
325 />
326 Commit Range
327 </label>
328 <label>
329 <input
330 type="radio"
331 value="single"
332 checked={rangeType === 'single'}
333 onChange={handleTypeChange}
334 />
335 Single Commit
336 </label>
337 {allowCustomRange && (
338 <label>
339 <input
340 type="radio"
341 value="custom"
342 checked={rangeType === 'custom'}
343 onChange={handleTypeChange}
344 />
345 Custom Range
346 </label>
347 )}
348 </div>
349 {rangeType === 'custom' && (
350 <input
351 type="text"
352 placeholder="Enter custom range (e.g., HEAD~5..HEAD)"
353 value={customRange}
354 onChange={(e) => setCustomRange(e.target.value)}
355 />
356 )}
357 </div>
358 );
359}`;
360
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700361 private mainCSSOriginal = `body {
362 font-family: sans-serif;
363 margin: 0;
364 padding: 0;
365}
366
367.app {
368 max-width: 1200px;
369 margin: 0 auto;
370 padding: 20px;
371}
372
373header {
374 margin-bottom: 20px;
375}
376
377h1 {
378 color: #333;
379}`;
380
381 private mainCSSModified = `body {
382 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
383 margin: 0;
384 padding: 0;
385 background-color: #f5f5f5;
386}
387
388.app {
389 max-width: 1200px;
390 margin: 0 auto;
391 padding: 20px;
392}
393
394header {
395 margin-bottom: 20px;
396 border-bottom: 1px solid #ddd;
397 padding-bottom: 10px;
398}
399
400h1 {
401 color: #333;
402}
403
404.file-picker {
405 display: flex;
406 gap: 8px;
407 align-items: center;
408 margin-bottom: 20px;
409}
410
411.file-picker select {
412 flex: 1;
413 padding: 8px;
414 border-radius: 4px;
415 border: 1px solid #ddd;
416}
417
418.navigation-buttons {
419 display: flex;
420 gap: 8px;
421}
422
423button {
424 padding: 8px 12px;
425 background-color: #4a7dfc;
426 color: white;
427 border: none;
428 border-radius: 4px;
429 cursor: pointer;
430}`;
431
432 async getCommitHistory(initialCommit?: string): Promise<GitLogEntry[]> {
Autoformatter8c463622025-05-16 21:54:17 +0000433 console.log(
434 `[MockGitDataService] Getting commit history from ${initialCommit || "beginning"}`,
435 );
436
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700437 // If initialCommit is provided, return commits from that commit to HEAD
438 if (initialCommit) {
Autoformatter8c463622025-05-16 21:54:17 +0000439 const startIndex = this.mockCommits.findIndex(
440 (commit) => commit.hash === initialCommit,
441 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700442 if (startIndex >= 0) {
443 return this.mockCommits.slice(0, startIndex + 1);
444 }
445 }
Autoformatter8c463622025-05-16 21:54:17 +0000446
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700447 return [...this.mockCommits];
448 }
449
450 async getDiff(from: string, to: string): Promise<GitDiffFile[]> {
451 console.log(`[MockGitDataService] Getting diff from ${from} to ${to}`);
Autoformatter8c463622025-05-16 21:54:17 +0000452
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700453 return [...this.mockDiffFiles];
454 }
455
456 async getCommitDiff(commit: string): Promise<GitDiffFile[]> {
457 console.log(`[MockGitDataService] Getting diff for commit ${commit}`);
Autoformatter8c463622025-05-16 21:54:17 +0000458
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700459 // Return a subset of files for specific commits
Autoformatter8c463622025-05-16 21:54:17 +0000460 if (commit === "abc123456789") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700461 return this.mockDiffFiles.slice(0, 2);
Autoformatter8c463622025-05-16 21:54:17 +0000462 } else if (commit === "def987654321") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700463 return this.mockDiffFiles.slice(1, 3);
464 }
Autoformatter8c463622025-05-16 21:54:17 +0000465
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700466 // For other commits, return all files
467 return [...this.mockDiffFiles];
468 }
469
470 async getFileContent(fileHash: string): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000471 console.log(
472 `[MockGitDataService] Getting file content for hash: ${fileHash}`,
473 );
474
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700475 // Return different content based on the file hash
Autoformatter8c463622025-05-16 21:54:17 +0000476 if (fileHash === "bcd0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700477 return this.appJSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000478 } else if (fileHash === "abc0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700479 return this.appJSOriginal;
Autoformatter8c463622025-05-16 21:54:17 +0000480 } else if (fileHash === "def0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700481 return this.filePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000482 } else if (fileHash === "cde0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700483 return this.rangePickerJS;
Josh Bleecher Snyderb3aff882025-07-01 02:17:27 +0000484 } else if (fileHash === "hij0123456789abcdef0123456789abcdef0123") {
485 return this.dialogPickerJS;
486 } else if (fileHash === "klm0123456789abcdef0123456789abcdef0123") {
487 return this.rangeSelectorJS;
Autoformatter8c463622025-05-16 21:54:17 +0000488 } else if (fileHash === "ghi0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700489 return this.mainCSSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000490 } else if (fileHash === "fgh0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700491 return this.mainCSSOriginal;
492 }
Autoformatter8c463622025-05-16 21:54:17 +0000493
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700494 // Return empty string for unknown file hashes
Autoformatter8c463622025-05-16 21:54:17 +0000495 return "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700496 }
497
498 async getBaseCommitRef(): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000499 console.log("[MockGitDataService] Getting base commit ref");
500
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700501 // Find the commit with the sketch-base ref
Autoformatter8c463622025-05-16 21:54:17 +0000502 const baseCommit = this.mockCommits.find(
503 (commit) => commit.refs && commit.refs.includes("sketch-base"),
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700504 );
Autoformatter8c463622025-05-16 21:54:17 +0000505
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700506 if (baseCommit) {
507 return baseCommit.hash;
508 }
Autoformatter8c463622025-05-16 21:54:17 +0000509
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700510 // Fallback to the last commit in our list
511 return this.mockCommits[this.mockCommits.length - 1].hash;
512 }
513
514 // Helper to simulate network delay
515 private delay(ms: number): Promise<void> {
Autoformatter8c463622025-05-16 21:54:17 +0000516 return new Promise((resolve) => setTimeout(resolve, ms));
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700517 }
Autoformatter8c463622025-05-16 21:54:17 +0000518
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700519 async getWorkingCopyContent(filePath: string): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000520 console.log(
521 `[MockGitDataService] Getting working copy content for path: ${filePath}`,
522 );
523
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700524 // Return different content based on the file path
Autoformatter8c463622025-05-16 21:54:17 +0000525 if (filePath === "src/components/App.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700526 return this.appJSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000527 } else if (filePath === "src/components/FilePicker.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700528 return this.filePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000529 } else if (filePath === "src/components/RangePicker.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700530 return this.rangePickerJS;
Josh Bleecher Snyderb3aff882025-07-01 02:17:27 +0000531 } else if (filePath === "src/components/DialogPicker.js") {
532 return this.dialogPickerJS;
533 } else if (filePath === "src/components/RangeSelector.js") {
534 return this.rangeSelectorJS;
Autoformatter8c463622025-05-16 21:54:17 +0000535 } else if (filePath === "src/styles/main.css") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700536 return this.mainCSSModified;
537 }
Autoformatter8c463622025-05-16 21:54:17 +0000538
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700539 // Return empty string for unknown file paths
Autoformatter8c463622025-05-16 21:54:17 +0000540 return "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700541 }
Autoformatter8c463622025-05-16 21:54:17 +0000542
543 async getUnstagedChanges(from: string = "HEAD"): Promise<GitDiffFile[]> {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700544 console.log(`[MockGitDataService] Getting unstaged changes from ${from}`);
Autoformatter8c463622025-05-16 21:54:17 +0000545
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700546 // Create a new array of files with 0000000... as the new hashes
547 // to simulate unstaged changes
Autoformatter8c463622025-05-16 21:54:17 +0000548 return this.mockDiffFiles.map((file) => ({
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700549 ...file,
Philip Zeyligere89b3082025-05-29 03:16:06 +0000550 new_hash: "0000000000000000000000000000000000000000",
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700551 }));
552 }
Autoformatter8c463622025-05-16 21:54:17 +0000553
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700554 async saveFileContent(filePath: string, content: string): Promise<void> {
Autoformatter8c463622025-05-16 21:54:17 +0000555 console.log(
556 `[MockGitDataService] Saving file content for path: ${filePath}`,
557 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700558 // Simulate a network delay
559 await this.delay(500);
560 // In a mock implementation, we just log the save attempt
Autoformatter8c463622025-05-16 21:54:17 +0000561 console.log(
562 `File would be saved: ${filePath} (${content.length} characters)`,
563 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700564 // Return void as per interface
565 }
Josh Bleecher Snydera8561f72025-07-15 23:47:59 +0000566
567 async getUntrackedFiles(): Promise<string[]> {
568 console.log("[MockGitDataService] Getting untracked files");
569 // Return some mock untracked files for demo purposes
570 return [
571 "temp.txt",
572 "debug.log",
573 "config/local.json",
Autoformatter390f8472025-07-16 20:09:45 +0000574 "node_modules/.cache/something",
Josh Bleecher Snydera8561f72025-07-15 23:47:59 +0000575 ];
576 }
Autoformatter8c463622025-05-16 21:54:17 +0000577}
Philip Zeyliger254c49f2025-07-17 17:26:24 -0700578
579// Mock HTTP endpoints for push demo
580export function setupMockPushEndpoints() {
581 // Mock the git/pushinfo endpoint
582 const originalFetch = window.fetch;
583
584 window.fetch = async (url: RequestInfo | URL, init?: RequestInit) => {
585 const urlString = typeof url === "string" ? url : url.toString();
586
587 // Mock pushinfo endpoint
588 if (urlString.includes("/git/pushinfo")) {
589 await new Promise((resolve) => setTimeout(resolve, 500)); // Simulate network delay
590
591 return new Response(
592 JSON.stringify({
593 hash: "abc123456789",
594 subject: "Implement new file picker UI",
595 remotes: [
596 {
597 name: "origin",
598 url: "https://github.com/boldsoftware/bold.git",
599 display_name: "boldsoftware/bold",
600 is_github: true,
601 },
602 {
Josh Bleecher Snyder7de3bdd2025-07-18 01:51:53 +0000603 name: "gitlab",
604 url: "https://gitlab.com/testuser/bold.git",
605 display_name: "gitlab.com/testuser/bold",
606 is_github: false,
607 },
608 {
609 name: "selfhosted",
610 url: "https://git.company.com/team/bold.git",
611 display_name: "git.company.com/team/bold",
612 is_github: false,
Philip Zeyliger254c49f2025-07-17 17:26:24 -0700613 },
614 ],
615 }),
616 {
617 status: 200,
618 headers: { "Content-Type": "application/json" },
619 },
620 );
621 }
622
623 // Mock push endpoint
624 if (urlString.includes("/git/push")) {
625 await new Promise((resolve) => setTimeout(resolve, 1500)); // Simulate push delay
626
627 const body = init?.body ? JSON.parse(init.body as string) : {};
628 const isDryRun = body.dry_run || false;
629
630 const mockOutput = isDryRun
631 ? `To https://github.com/boldsoftware/bold.git\n abc1234..def5678 ${body.branch || "main"} -> ${body.branch || "main"} (dry-run)`
632 : `To https://github.com/boldsoftware/bold.git\n abc1234..def5678 ${body.branch || "main"} -> ${body.branch || "main"}\n\nCreate a pull request for '${body.branch || "main"}' on GitHub by visiting:\n https://github.com/boldsoftware/bold/pull/new/${body.branch || "main"}`;
633
634 return new Response(
635 JSON.stringify({
636 success: true,
637 output: mockOutput,
638 dry_run: isDryRun,
639 }),
640 {
641 status: 200,
642 headers: { "Content-Type": "application/json" },
643 },
644 );
645 }
646
647 // Fall back to original fetch for other requests
648 return originalFetch(url, init);
649 };
650}