blob: 894d9aab07f1934942423130cbbdc318abd45c32 [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 Zeyliger272a90e2025-05-16 14:49:51 -070013 }
14
15 // Mock commit history
16 private mockCommits: GitLogEntry[] = [
17 {
Autoformatter8c463622025-05-16 21:54:17 +000018 hash: "abc123456789",
19 subject: "Implement new file picker UI",
20 refs: ["HEAD", "main"],
Philip Zeyliger272a90e2025-05-16 14:49:51 -070021 },
22 {
Autoformatter8c463622025-05-16 21:54:17 +000023 hash: "def987654321",
24 subject: "Add range picker component",
25 refs: [],
Philip Zeyliger272a90e2025-05-16 14:49:51 -070026 },
27 {
Autoformatter8c463622025-05-16 21:54:17 +000028 hash: "ghi456789123",
29 subject: "Fix styling issues in navigation",
30 refs: [],
Philip Zeyliger272a90e2025-05-16 14:49:51 -070031 },
32 {
Autoformatter8c463622025-05-16 21:54:17 +000033 hash: "jkl789123456",
34 subject: "Initial commit",
35 refs: ["sketch-base"],
36 },
Philip Zeyliger272a90e2025-05-16 14:49:51 -070037 ];
38
39 // Mock diff files for various scenarios
40 private mockDiffFiles: GitDiffFile[] = [
41 {
Autoformatter8c463622025-05-16 21:54:17 +000042 path: "src/components/FilePicker.js",
Josh Bleecher Snyderbcc1c412025-05-29 00:36:49 +000043 old_path: "",
Autoformatter8c463622025-05-16 21:54:17 +000044 status: "A",
45 new_mode: "100644",
46 old_mode: "000000",
47 old_hash: "0000000000000000000000000000000000000000",
48 new_hash: "def0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +000049 additions: 54,
50 deletions: 0,
Philip Zeyliger272a90e2025-05-16 14:49:51 -070051 },
52 {
Autoformatter8c463622025-05-16 21:54:17 +000053 path: "src/components/RangePicker.js",
Josh Bleecher Snyderbcc1c412025-05-29 00:36:49 +000054 old_path: "",
Autoformatter8c463622025-05-16 21:54:17 +000055 status: "A",
56 new_mode: "100644",
57 old_mode: "000000",
58 old_hash: "0000000000000000000000000000000000000000",
59 new_hash: "cde0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +000060 additions: 32,
61 deletions: 0,
Philip Zeyliger272a90e2025-05-16 14:49:51 -070062 },
63 {
Autoformatter8c463622025-05-16 21:54:17 +000064 path: "src/components/App.js",
Josh Bleecher Snyderbcc1c412025-05-29 00:36:49 +000065 old_path: "",
Autoformatter8c463622025-05-16 21:54:17 +000066 status: "M",
67 new_mode: "100644",
68 old_mode: "100644",
69 old_hash: "abc0123456789abcdef0123456789abcdef0123",
70 new_hash: "bcd0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +000071 additions: 15,
72 deletions: 3,
Philip Zeyliger272a90e2025-05-16 14:49:51 -070073 },
74 {
Autoformatter8c463622025-05-16 21:54:17 +000075 path: "src/styles/main.css",
Josh Bleecher Snyderbcc1c412025-05-29 00:36:49 +000076 old_path: "",
Autoformatter8c463622025-05-16 21:54:17 +000077 status: "M",
78 new_mode: "100644",
79 old_mode: "100644",
80 old_hash: "fgh0123456789abcdef0123456789abcdef0123",
81 new_hash: "ghi0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +000082 additions: 25,
83 deletions: 8,
Autoformatter8c463622025-05-16 21:54:17 +000084 },
Philip Zeyliger272a90e2025-05-16 14:49:51 -070085 ];
86
87 // Mock file content for different files and commits
88 private appJSOriginal = `function App() {
89 return (
90 <div className="app">
91 <header>
92 <h1>Git Diff Viewer</h1>
93 </header>
94 <main>
95 <p>Select a file to view differences</p>
96 </main>
97 </div>
98 );
99}`;
100
101 private appJSModified = `function App() {
102 const [files, setFiles] = useState([]);
103 const [selectedFile, setSelectedFile] = useState(null);
104
105 // Load commits and files
106 useEffect(() => {
107 // Code to load commits would go here
108 // setCommits(...);
109 }, []);
110
111 return (
112 <div className="app">
113 <header>
114 <h1>Git Diff Viewer</h1>
115 </header>
116 <main>
117 <FilePicker files={files} onFileSelect={setSelectedFile} />
118 <div className="diff-view">
119 {selectedFile ? (
120 <div>Diff view for {selectedFile.path}</div>
121 ) : (
122 <p>Select a file to view differences</p>
123 )}
124 </div>
125 </main>
126 </div>
127 );
128}`;
129
130 private filePickerJS = `function FilePicker({ files, onFileSelect }) {
131 const [selectedIndex, setSelectedIndex] = useState(0);
132
133 useEffect(() => {
134 // Reset selection when files change
135 setSelectedIndex(0);
136 if (files.length > 0) {
137 onFileSelect(files[0]);
138 }
139 }, [files, onFileSelect]);
140
141 const handleNext = () => {
142 if (selectedIndex < files.length - 1) {
143 const newIndex = selectedIndex + 1;
144 setSelectedIndex(newIndex);
145 onFileSelect(files[newIndex]);
146 }
147 };
148
149 const handlePrevious = () => {
150 if (selectedIndex > 0) {
151 const newIndex = selectedIndex - 1;
152 setSelectedIndex(newIndex);
153 onFileSelect(files[newIndex]);
154 }
155 };
156
157 return (
158 <div className="file-picker">
159 <select value={selectedIndex} onChange={(e) => {
160 const index = parseInt(e.target.value, 10);
161 setSelectedIndex(index);
162 onFileSelect(files[index]);
163 }}>
164 {files.map((file, index) => (
165 <option key={file.path} value={index}>
166 {file.status} {file.path}
167 </option>
168 ))}
169 </select>
170
171 <div className="navigation-buttons">
172 <button
173 onClick={handlePrevious}
174 disabled={selectedIndex === 0}
175 >
176 Previous
177 </button>
178 <button
179 onClick={handleNext}
180 disabled={selectedIndex === files.length - 1}
181 >
182 Next
183 </button>
184 </div>
185 </div>
186 );
187}`;
188
189 private rangePickerJS = `function RangePicker({ commits, onRangeChange }) {
190 const [rangeType, setRangeType] = useState('range');
191 const [startCommit, setStartCommit] = useState(commits[0]);
192 const [endCommit, setEndCommit] = useState(commits[commits.length - 1]);
193
194 const handleTypeChange = (e) => {
195 setRangeType(e.target.value);
196 if (e.target.value === 'single') {
197 onRangeChange({ type: 'single', commit: startCommit });
198 } else {
199 onRangeChange({ type: 'range', from: startCommit, to: endCommit });
200 }
201 };
202
203 return (
204 <div className="range-picker">
205 <div className="range-type-selector">
206 <label>
207 <input
208 type="radio"
209 value="range"
210 checked={rangeType === 'range'}
211 onChange={handleTypeChange}
212 />
213 Commit Range
214 </label>
215 <label>
216 <input
217 type="radio"
218 value="single"
219 checked={rangeType === 'single'}
220 onChange={handleTypeChange}
221 />
222 Single Commit
223 </label>
224 </div>
225 </div>
226 );
227}`;
228
229 private mainCSSOriginal = `body {
230 font-family: sans-serif;
231 margin: 0;
232 padding: 0;
233}
234
235.app {
236 max-width: 1200px;
237 margin: 0 auto;
238 padding: 20px;
239}
240
241header {
242 margin-bottom: 20px;
243}
244
245h1 {
246 color: #333;
247}`;
248
249 private mainCSSModified = `body {
250 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
251 margin: 0;
252 padding: 0;
253 background-color: #f5f5f5;
254}
255
256.app {
257 max-width: 1200px;
258 margin: 0 auto;
259 padding: 20px;
260}
261
262header {
263 margin-bottom: 20px;
264 border-bottom: 1px solid #ddd;
265 padding-bottom: 10px;
266}
267
268h1 {
269 color: #333;
270}
271
272.file-picker {
273 display: flex;
274 gap: 8px;
275 align-items: center;
276 margin-bottom: 20px;
277}
278
279.file-picker select {
280 flex: 1;
281 padding: 8px;
282 border-radius: 4px;
283 border: 1px solid #ddd;
284}
285
286.navigation-buttons {
287 display: flex;
288 gap: 8px;
289}
290
291button {
292 padding: 8px 12px;
293 background-color: #4a7dfc;
294 color: white;
295 border: none;
296 border-radius: 4px;
297 cursor: pointer;
298}`;
299
300 async getCommitHistory(initialCommit?: string): Promise<GitLogEntry[]> {
Autoformatter8c463622025-05-16 21:54:17 +0000301 console.log(
302 `[MockGitDataService] Getting commit history from ${initialCommit || "beginning"}`,
303 );
304
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700305 // If initialCommit is provided, return commits from that commit to HEAD
306 if (initialCommit) {
Autoformatter8c463622025-05-16 21:54:17 +0000307 const startIndex = this.mockCommits.findIndex(
308 (commit) => commit.hash === initialCommit,
309 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700310 if (startIndex >= 0) {
311 return this.mockCommits.slice(0, startIndex + 1);
312 }
313 }
Autoformatter8c463622025-05-16 21:54:17 +0000314
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700315 return [...this.mockCommits];
316 }
317
318 async getDiff(from: string, to: string): Promise<GitDiffFile[]> {
319 console.log(`[MockGitDataService] Getting diff from ${from} to ${to}`);
Autoformatter8c463622025-05-16 21:54:17 +0000320
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700321 return [...this.mockDiffFiles];
322 }
323
324 async getCommitDiff(commit: string): Promise<GitDiffFile[]> {
325 console.log(`[MockGitDataService] Getting diff for commit ${commit}`);
Autoformatter8c463622025-05-16 21:54:17 +0000326
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700327 // Return a subset of files for specific commits
Autoformatter8c463622025-05-16 21:54:17 +0000328 if (commit === "abc123456789") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700329 return this.mockDiffFiles.slice(0, 2);
Autoformatter8c463622025-05-16 21:54:17 +0000330 } else if (commit === "def987654321") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700331 return this.mockDiffFiles.slice(1, 3);
332 }
Autoformatter8c463622025-05-16 21:54:17 +0000333
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700334 // For other commits, return all files
335 return [...this.mockDiffFiles];
336 }
337
338 async getFileContent(fileHash: string): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000339 console.log(
340 `[MockGitDataService] Getting file content for hash: ${fileHash}`,
341 );
342
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700343 // Return different content based on the file hash
Autoformatter8c463622025-05-16 21:54:17 +0000344 if (fileHash === "bcd0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700345 return this.appJSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000346 } else if (fileHash === "abc0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700347 return this.appJSOriginal;
Autoformatter8c463622025-05-16 21:54:17 +0000348 } else if (fileHash === "def0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700349 return this.filePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000350 } else if (fileHash === "cde0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700351 return this.rangePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000352 } else if (fileHash === "ghi0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700353 return this.mainCSSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000354 } else if (fileHash === "fgh0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700355 return this.mainCSSOriginal;
356 }
Autoformatter8c463622025-05-16 21:54:17 +0000357
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700358 // Return empty string for unknown file hashes
Autoformatter8c463622025-05-16 21:54:17 +0000359 return "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700360 }
361
362 async getBaseCommitRef(): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000363 console.log("[MockGitDataService] Getting base commit ref");
364
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700365 // Find the commit with the sketch-base ref
Autoformatter8c463622025-05-16 21:54:17 +0000366 const baseCommit = this.mockCommits.find(
367 (commit) => commit.refs && commit.refs.includes("sketch-base"),
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700368 );
Autoformatter8c463622025-05-16 21:54:17 +0000369
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700370 if (baseCommit) {
371 return baseCommit.hash;
372 }
Autoformatter8c463622025-05-16 21:54:17 +0000373
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700374 // Fallback to the last commit in our list
375 return this.mockCommits[this.mockCommits.length - 1].hash;
376 }
377
378 // Helper to simulate network delay
379 private delay(ms: number): Promise<void> {
Autoformatter8c463622025-05-16 21:54:17 +0000380 return new Promise((resolve) => setTimeout(resolve, ms));
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700381 }
Autoformatter8c463622025-05-16 21:54:17 +0000382
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700383 async getWorkingCopyContent(filePath: string): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000384 console.log(
385 `[MockGitDataService] Getting working copy content for path: ${filePath}`,
386 );
387
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700388 // Return different content based on the file path
Autoformatter8c463622025-05-16 21:54:17 +0000389 if (filePath === "src/components/App.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700390 return this.appJSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000391 } else if (filePath === "src/components/FilePicker.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700392 return this.filePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000393 } else if (filePath === "src/components/RangePicker.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700394 return this.rangePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000395 } else if (filePath === "src/styles/main.css") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700396 return this.mainCSSModified;
397 }
Autoformatter8c463622025-05-16 21:54:17 +0000398
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700399 // Return empty string for unknown file paths
Autoformatter8c463622025-05-16 21:54:17 +0000400 return "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700401 }
Autoformatter8c463622025-05-16 21:54:17 +0000402
403 async getUnstagedChanges(from: string = "HEAD"): Promise<GitDiffFile[]> {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700404 console.log(`[MockGitDataService] Getting unstaged changes from ${from}`);
Autoformatter8c463622025-05-16 21:54:17 +0000405
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700406 // Create a new array of files with 0000000... as the new hashes
407 // to simulate unstaged changes
Autoformatter8c463622025-05-16 21:54:17 +0000408 return this.mockDiffFiles.map((file) => ({
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700409 ...file,
Philip Zeyligere89b3082025-05-29 03:16:06 +0000410 new_hash: "0000000000000000000000000000000000000000",
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700411 }));
412 }
Autoformatter8c463622025-05-16 21:54:17 +0000413
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700414 async saveFileContent(filePath: string, content: string): Promise<void> {
Autoformatter8c463622025-05-16 21:54:17 +0000415 console.log(
416 `[MockGitDataService] Saving file content for path: ${filePath}`,
417 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700418 // Simulate a network delay
419 await this.delay(500);
420 // In a mock implementation, we just log the save attempt
Autoformatter8c463622025-05-16 21:54:17 +0000421 console.log(
422 `File would be saved: ${filePath} (${content.length} characters)`,
423 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700424 // Return void as per interface
425 }
Autoformatter8c463622025-05-16 21:54:17 +0000426}