blob: 648ca652d13ffed80ace469b2549fda16c461a5b [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",
43 status: "A",
44 new_mode: "100644",
45 old_mode: "000000",
46 old_hash: "0000000000000000000000000000000000000000",
47 new_hash: "def0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +000048 additions: 54,
49 deletions: 0,
Philip Zeyliger272a90e2025-05-16 14:49:51 -070050 },
51 {
Autoformatter8c463622025-05-16 21:54:17 +000052 path: "src/components/RangePicker.js",
53 status: "A",
54 new_mode: "100644",
55 old_mode: "000000",
56 old_hash: "0000000000000000000000000000000000000000",
57 new_hash: "cde0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +000058 additions: 32,
59 deletions: 0,
Philip Zeyliger272a90e2025-05-16 14:49:51 -070060 },
61 {
Autoformatter8c463622025-05-16 21:54:17 +000062 path: "src/components/App.js",
63 status: "M",
64 new_mode: "100644",
65 old_mode: "100644",
66 old_hash: "abc0123456789abcdef0123456789abcdef0123",
67 new_hash: "bcd0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +000068 additions: 15,
69 deletions: 3,
Philip Zeyliger272a90e2025-05-16 14:49:51 -070070 },
71 {
Autoformatter8c463622025-05-16 21:54:17 +000072 path: "src/styles/main.css",
73 status: "M",
74 new_mode: "100644",
75 old_mode: "100644",
76 old_hash: "fgh0123456789abcdef0123456789abcdef0123",
77 new_hash: "ghi0123456789abcdef0123456789abcdef0123",
Philip Zeyligere89b3082025-05-29 03:16:06 +000078 additions: 25,
79 deletions: 8,
Autoformatter8c463622025-05-16 21:54:17 +000080 },
Philip Zeyliger272a90e2025-05-16 14:49:51 -070081 ];
82
83 // Mock file content for different files and commits
84 private appJSOriginal = `function App() {
85 return (
86 <div className="app">
87 <header>
88 <h1>Git Diff Viewer</h1>
89 </header>
90 <main>
91 <p>Select a file to view differences</p>
92 </main>
93 </div>
94 );
95}`;
96
97 private appJSModified = `function App() {
98 const [files, setFiles] = useState([]);
99 const [selectedFile, setSelectedFile] = useState(null);
100
101 // Load commits and files
102 useEffect(() => {
103 // Code to load commits would go here
104 // setCommits(...);
105 }, []);
106
107 return (
108 <div className="app">
109 <header>
110 <h1>Git Diff Viewer</h1>
111 </header>
112 <main>
113 <FilePicker files={files} onFileSelect={setSelectedFile} />
114 <div className="diff-view">
115 {selectedFile ? (
116 <div>Diff view for {selectedFile.path}</div>
117 ) : (
118 <p>Select a file to view differences</p>
119 )}
120 </div>
121 </main>
122 </div>
123 );
124}`;
125
126 private filePickerJS = `function FilePicker({ files, onFileSelect }) {
127 const [selectedIndex, setSelectedIndex] = useState(0);
128
129 useEffect(() => {
130 // Reset selection when files change
131 setSelectedIndex(0);
132 if (files.length > 0) {
133 onFileSelect(files[0]);
134 }
135 }, [files, onFileSelect]);
136
137 const handleNext = () => {
138 if (selectedIndex < files.length - 1) {
139 const newIndex = selectedIndex + 1;
140 setSelectedIndex(newIndex);
141 onFileSelect(files[newIndex]);
142 }
143 };
144
145 const handlePrevious = () => {
146 if (selectedIndex > 0) {
147 const newIndex = selectedIndex - 1;
148 setSelectedIndex(newIndex);
149 onFileSelect(files[newIndex]);
150 }
151 };
152
153 return (
154 <div className="file-picker">
155 <select value={selectedIndex} onChange={(e) => {
156 const index = parseInt(e.target.value, 10);
157 setSelectedIndex(index);
158 onFileSelect(files[index]);
159 }}>
160 {files.map((file, index) => (
161 <option key={file.path} value={index}>
162 {file.status} {file.path}
163 </option>
164 ))}
165 </select>
166
167 <div className="navigation-buttons">
168 <button
169 onClick={handlePrevious}
170 disabled={selectedIndex === 0}
171 >
172 Previous
173 </button>
174 <button
175 onClick={handleNext}
176 disabled={selectedIndex === files.length - 1}
177 >
178 Next
179 </button>
180 </div>
181 </div>
182 );
183}`;
184
185 private rangePickerJS = `function RangePicker({ commits, onRangeChange }) {
186 const [rangeType, setRangeType] = useState('range');
187 const [startCommit, setStartCommit] = useState(commits[0]);
188 const [endCommit, setEndCommit] = useState(commits[commits.length - 1]);
189
190 const handleTypeChange = (e) => {
191 setRangeType(e.target.value);
192 if (e.target.value === 'single') {
193 onRangeChange({ type: 'single', commit: startCommit });
194 } else {
195 onRangeChange({ type: 'range', from: startCommit, to: endCommit });
196 }
197 };
198
199 return (
200 <div className="range-picker">
201 <div className="range-type-selector">
202 <label>
203 <input
204 type="radio"
205 value="range"
206 checked={rangeType === 'range'}
207 onChange={handleTypeChange}
208 />
209 Commit Range
210 </label>
211 <label>
212 <input
213 type="radio"
214 value="single"
215 checked={rangeType === 'single'}
216 onChange={handleTypeChange}
217 />
218 Single Commit
219 </label>
220 </div>
221 </div>
222 );
223}`;
224
225 private mainCSSOriginal = `body {
226 font-family: sans-serif;
227 margin: 0;
228 padding: 0;
229}
230
231.app {
232 max-width: 1200px;
233 margin: 0 auto;
234 padding: 20px;
235}
236
237header {
238 margin-bottom: 20px;
239}
240
241h1 {
242 color: #333;
243}`;
244
245 private mainCSSModified = `body {
246 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
247 margin: 0;
248 padding: 0;
249 background-color: #f5f5f5;
250}
251
252.app {
253 max-width: 1200px;
254 margin: 0 auto;
255 padding: 20px;
256}
257
258header {
259 margin-bottom: 20px;
260 border-bottom: 1px solid #ddd;
261 padding-bottom: 10px;
262}
263
264h1 {
265 color: #333;
266}
267
268.file-picker {
269 display: flex;
270 gap: 8px;
271 align-items: center;
272 margin-bottom: 20px;
273}
274
275.file-picker select {
276 flex: 1;
277 padding: 8px;
278 border-radius: 4px;
279 border: 1px solid #ddd;
280}
281
282.navigation-buttons {
283 display: flex;
284 gap: 8px;
285}
286
287button {
288 padding: 8px 12px;
289 background-color: #4a7dfc;
290 color: white;
291 border: none;
292 border-radius: 4px;
293 cursor: pointer;
294}`;
295
296 async getCommitHistory(initialCommit?: string): Promise<GitLogEntry[]> {
Autoformatter8c463622025-05-16 21:54:17 +0000297 console.log(
298 `[MockGitDataService] Getting commit history from ${initialCommit || "beginning"}`,
299 );
300
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700301 // If initialCommit is provided, return commits from that commit to HEAD
302 if (initialCommit) {
Autoformatter8c463622025-05-16 21:54:17 +0000303 const startIndex = this.mockCommits.findIndex(
304 (commit) => commit.hash === initialCommit,
305 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700306 if (startIndex >= 0) {
307 return this.mockCommits.slice(0, startIndex + 1);
308 }
309 }
Autoformatter8c463622025-05-16 21:54:17 +0000310
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700311 return [...this.mockCommits];
312 }
313
314 async getDiff(from: string, to: string): Promise<GitDiffFile[]> {
315 console.log(`[MockGitDataService] Getting diff from ${from} to ${to}`);
Autoformatter8c463622025-05-16 21:54:17 +0000316
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700317 return [...this.mockDiffFiles];
318 }
319
320 async getCommitDiff(commit: string): Promise<GitDiffFile[]> {
321 console.log(`[MockGitDataService] Getting diff for commit ${commit}`);
Autoformatter8c463622025-05-16 21:54:17 +0000322
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700323 // Return a subset of files for specific commits
Autoformatter8c463622025-05-16 21:54:17 +0000324 if (commit === "abc123456789") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700325 return this.mockDiffFiles.slice(0, 2);
Autoformatter8c463622025-05-16 21:54:17 +0000326 } else if (commit === "def987654321") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700327 return this.mockDiffFiles.slice(1, 3);
328 }
Autoformatter8c463622025-05-16 21:54:17 +0000329
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700330 // For other commits, return all files
331 return [...this.mockDiffFiles];
332 }
333
334 async getFileContent(fileHash: string): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000335 console.log(
336 `[MockGitDataService] Getting file content for hash: ${fileHash}`,
337 );
338
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700339 // Return different content based on the file hash
Autoformatter8c463622025-05-16 21:54:17 +0000340 if (fileHash === "bcd0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700341 return this.appJSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000342 } else if (fileHash === "abc0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700343 return this.appJSOriginal;
Autoformatter8c463622025-05-16 21:54:17 +0000344 } else if (fileHash === "def0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700345 return this.filePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000346 } else if (fileHash === "cde0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700347 return this.rangePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000348 } else if (fileHash === "ghi0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700349 return this.mainCSSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000350 } else if (fileHash === "fgh0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700351 return this.mainCSSOriginal;
352 }
Autoformatter8c463622025-05-16 21:54:17 +0000353
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700354 // Return empty string for unknown file hashes
Autoformatter8c463622025-05-16 21:54:17 +0000355 return "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700356 }
357
358 async getBaseCommitRef(): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000359 console.log("[MockGitDataService] Getting base commit ref");
360
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700361 // Find the commit with the sketch-base ref
Autoformatter8c463622025-05-16 21:54:17 +0000362 const baseCommit = this.mockCommits.find(
363 (commit) => commit.refs && commit.refs.includes("sketch-base"),
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700364 );
Autoformatter8c463622025-05-16 21:54:17 +0000365
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700366 if (baseCommit) {
367 return baseCommit.hash;
368 }
Autoformatter8c463622025-05-16 21:54:17 +0000369
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700370 // Fallback to the last commit in our list
371 return this.mockCommits[this.mockCommits.length - 1].hash;
372 }
373
374 // Helper to simulate network delay
375 private delay(ms: number): Promise<void> {
Autoformatter8c463622025-05-16 21:54:17 +0000376 return new Promise((resolve) => setTimeout(resolve, ms));
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700377 }
Autoformatter8c463622025-05-16 21:54:17 +0000378
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700379 async getWorkingCopyContent(filePath: string): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000380 console.log(
381 `[MockGitDataService] Getting working copy content for path: ${filePath}`,
382 );
383
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700384 // Return different content based on the file path
Autoformatter8c463622025-05-16 21:54:17 +0000385 if (filePath === "src/components/App.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700386 return this.appJSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000387 } else if (filePath === "src/components/FilePicker.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700388 return this.filePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000389 } else if (filePath === "src/components/RangePicker.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700390 return this.rangePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000391 } else if (filePath === "src/styles/main.css") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700392 return this.mainCSSModified;
393 }
Autoformatter8c463622025-05-16 21:54:17 +0000394
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700395 // Return empty string for unknown file paths
Autoformatter8c463622025-05-16 21:54:17 +0000396 return "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700397 }
Autoformatter8c463622025-05-16 21:54:17 +0000398
399 async getUnstagedChanges(from: string = "HEAD"): Promise<GitDiffFile[]> {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700400 console.log(`[MockGitDataService] Getting unstaged changes from ${from}`);
Autoformatter8c463622025-05-16 21:54:17 +0000401
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700402 // Create a new array of files with 0000000... as the new hashes
403 // to simulate unstaged changes
Autoformatter8c463622025-05-16 21:54:17 +0000404 return this.mockDiffFiles.map((file) => ({
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700405 ...file,
Philip Zeyligere89b3082025-05-29 03:16:06 +0000406 new_hash: "0000000000000000000000000000000000000000",
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700407 }));
408 }
Autoformatter8c463622025-05-16 21:54:17 +0000409
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700410 async saveFileContent(filePath: string, content: string): Promise<void> {
Autoformatter8c463622025-05-16 21:54:17 +0000411 console.log(
412 `[MockGitDataService] Saving file content for path: ${filePath}`,
413 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700414 // Simulate a network delay
415 await this.delay(500);
416 // In a mock implementation, we just log the save attempt
Autoformatter8c463622025-05-16 21:54:17 +0000417 console.log(
418 `File would be saved: ${filePath} (${content.length} characters)`,
419 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700420 // Return void as per interface
421 }
Autoformatter8c463622025-05-16 21:54:17 +0000422}