blob: 3ca50981b383f381d03a14bac4bf34c77dc8cce7 [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 Zeyliger272a90e2025-05-16 14:49:51 -070048 },
49 {
Autoformatter8c463622025-05-16 21:54:17 +000050 path: "src/components/RangePicker.js",
51 status: "A",
52 new_mode: "100644",
53 old_mode: "000000",
54 old_hash: "0000000000000000000000000000000000000000",
55 new_hash: "cde0123456789abcdef0123456789abcdef0123",
Philip Zeyliger272a90e2025-05-16 14:49:51 -070056 },
57 {
Autoformatter8c463622025-05-16 21:54:17 +000058 path: "src/components/App.js",
59 status: "M",
60 new_mode: "100644",
61 old_mode: "100644",
62 old_hash: "abc0123456789abcdef0123456789abcdef0123",
63 new_hash: "bcd0123456789abcdef0123456789abcdef0123",
Philip Zeyliger272a90e2025-05-16 14:49:51 -070064 },
65 {
Autoformatter8c463622025-05-16 21:54:17 +000066 path: "src/styles/main.css",
67 status: "M",
68 new_mode: "100644",
69 old_mode: "100644",
70 old_hash: "fgh0123456789abcdef0123456789abcdef0123",
71 new_hash: "ghi0123456789abcdef0123456789abcdef0123",
72 },
Philip Zeyliger272a90e2025-05-16 14:49:51 -070073 ];
74
75 // Mock file content for different files and commits
76 private appJSOriginal = `function App() {
77 return (
78 <div className="app">
79 <header>
80 <h1>Git Diff Viewer</h1>
81 </header>
82 <main>
83 <p>Select a file to view differences</p>
84 </main>
85 </div>
86 );
87}`;
88
89 private appJSModified = `function App() {
90 const [files, setFiles] = useState([]);
91 const [selectedFile, setSelectedFile] = useState(null);
92
93 // Load commits and files
94 useEffect(() => {
95 // Code to load commits would go here
96 // setCommits(...);
97 }, []);
98
99 return (
100 <div className="app">
101 <header>
102 <h1>Git Diff Viewer</h1>
103 </header>
104 <main>
105 <FilePicker files={files} onFileSelect={setSelectedFile} />
106 <div className="diff-view">
107 {selectedFile ? (
108 <div>Diff view for {selectedFile.path}</div>
109 ) : (
110 <p>Select a file to view differences</p>
111 )}
112 </div>
113 </main>
114 </div>
115 );
116}`;
117
118 private filePickerJS = `function FilePicker({ files, onFileSelect }) {
119 const [selectedIndex, setSelectedIndex] = useState(0);
120
121 useEffect(() => {
122 // Reset selection when files change
123 setSelectedIndex(0);
124 if (files.length > 0) {
125 onFileSelect(files[0]);
126 }
127 }, [files, onFileSelect]);
128
129 const handleNext = () => {
130 if (selectedIndex < files.length - 1) {
131 const newIndex = selectedIndex + 1;
132 setSelectedIndex(newIndex);
133 onFileSelect(files[newIndex]);
134 }
135 };
136
137 const handlePrevious = () => {
138 if (selectedIndex > 0) {
139 const newIndex = selectedIndex - 1;
140 setSelectedIndex(newIndex);
141 onFileSelect(files[newIndex]);
142 }
143 };
144
145 return (
146 <div className="file-picker">
147 <select value={selectedIndex} onChange={(e) => {
148 const index = parseInt(e.target.value, 10);
149 setSelectedIndex(index);
150 onFileSelect(files[index]);
151 }}>
152 {files.map((file, index) => (
153 <option key={file.path} value={index}>
154 {file.status} {file.path}
155 </option>
156 ))}
157 </select>
158
159 <div className="navigation-buttons">
160 <button
161 onClick={handlePrevious}
162 disabled={selectedIndex === 0}
163 >
164 Previous
165 </button>
166 <button
167 onClick={handleNext}
168 disabled={selectedIndex === files.length - 1}
169 >
170 Next
171 </button>
172 </div>
173 </div>
174 );
175}`;
176
177 private rangePickerJS = `function RangePicker({ commits, onRangeChange }) {
178 const [rangeType, setRangeType] = useState('range');
179 const [startCommit, setStartCommit] = useState(commits[0]);
180 const [endCommit, setEndCommit] = useState(commits[commits.length - 1]);
181
182 const handleTypeChange = (e) => {
183 setRangeType(e.target.value);
184 if (e.target.value === 'single') {
185 onRangeChange({ type: 'single', commit: startCommit });
186 } else {
187 onRangeChange({ type: 'range', from: startCommit, to: endCommit });
188 }
189 };
190
191 return (
192 <div className="range-picker">
193 <div className="range-type-selector">
194 <label>
195 <input
196 type="radio"
197 value="range"
198 checked={rangeType === 'range'}
199 onChange={handleTypeChange}
200 />
201 Commit Range
202 </label>
203 <label>
204 <input
205 type="radio"
206 value="single"
207 checked={rangeType === 'single'}
208 onChange={handleTypeChange}
209 />
210 Single Commit
211 </label>
212 </div>
213 </div>
214 );
215}`;
216
217 private mainCSSOriginal = `body {
218 font-family: sans-serif;
219 margin: 0;
220 padding: 0;
221}
222
223.app {
224 max-width: 1200px;
225 margin: 0 auto;
226 padding: 20px;
227}
228
229header {
230 margin-bottom: 20px;
231}
232
233h1 {
234 color: #333;
235}`;
236
237 private mainCSSModified = `body {
238 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
239 margin: 0;
240 padding: 0;
241 background-color: #f5f5f5;
242}
243
244.app {
245 max-width: 1200px;
246 margin: 0 auto;
247 padding: 20px;
248}
249
250header {
251 margin-bottom: 20px;
252 border-bottom: 1px solid #ddd;
253 padding-bottom: 10px;
254}
255
256h1 {
257 color: #333;
258}
259
260.file-picker {
261 display: flex;
262 gap: 8px;
263 align-items: center;
264 margin-bottom: 20px;
265}
266
267.file-picker select {
268 flex: 1;
269 padding: 8px;
270 border-radius: 4px;
271 border: 1px solid #ddd;
272}
273
274.navigation-buttons {
275 display: flex;
276 gap: 8px;
277}
278
279button {
280 padding: 8px 12px;
281 background-color: #4a7dfc;
282 color: white;
283 border: none;
284 border-radius: 4px;
285 cursor: pointer;
286}`;
287
288 async getCommitHistory(initialCommit?: string): Promise<GitLogEntry[]> {
Autoformatter8c463622025-05-16 21:54:17 +0000289 console.log(
290 `[MockGitDataService] Getting commit history from ${initialCommit || "beginning"}`,
291 );
292
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700293 // If initialCommit is provided, return commits from that commit to HEAD
294 if (initialCommit) {
Autoformatter8c463622025-05-16 21:54:17 +0000295 const startIndex = this.mockCommits.findIndex(
296 (commit) => commit.hash === initialCommit,
297 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700298 if (startIndex >= 0) {
299 return this.mockCommits.slice(0, startIndex + 1);
300 }
301 }
Autoformatter8c463622025-05-16 21:54:17 +0000302
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700303 return [...this.mockCommits];
304 }
305
306 async getDiff(from: string, to: string): Promise<GitDiffFile[]> {
307 console.log(`[MockGitDataService] Getting diff from ${from} to ${to}`);
Autoformatter8c463622025-05-16 21:54:17 +0000308
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700309 return [...this.mockDiffFiles];
310 }
311
312 async getCommitDiff(commit: string): Promise<GitDiffFile[]> {
313 console.log(`[MockGitDataService] Getting diff for commit ${commit}`);
Autoformatter8c463622025-05-16 21:54:17 +0000314
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700315 // Return a subset of files for specific commits
Autoformatter8c463622025-05-16 21:54:17 +0000316 if (commit === "abc123456789") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700317 return this.mockDiffFiles.slice(0, 2);
Autoformatter8c463622025-05-16 21:54:17 +0000318 } else if (commit === "def987654321") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700319 return this.mockDiffFiles.slice(1, 3);
320 }
Autoformatter8c463622025-05-16 21:54:17 +0000321
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700322 // For other commits, return all files
323 return [...this.mockDiffFiles];
324 }
325
326 async getFileContent(fileHash: string): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000327 console.log(
328 `[MockGitDataService] Getting file content for hash: ${fileHash}`,
329 );
330
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700331 // Return different content based on the file hash
Autoformatter8c463622025-05-16 21:54:17 +0000332 if (fileHash === "bcd0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700333 return this.appJSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000334 } else if (fileHash === "abc0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700335 return this.appJSOriginal;
Autoformatter8c463622025-05-16 21:54:17 +0000336 } else if (fileHash === "def0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700337 return this.filePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000338 } else if (fileHash === "cde0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700339 return this.rangePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000340 } else if (fileHash === "ghi0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700341 return this.mainCSSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000342 } else if (fileHash === "fgh0123456789abcdef0123456789abcdef0123") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700343 return this.mainCSSOriginal;
344 }
Autoformatter8c463622025-05-16 21:54:17 +0000345
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700346 // Return empty string for unknown file hashes
Autoformatter8c463622025-05-16 21:54:17 +0000347 return "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700348 }
349
350 async getBaseCommitRef(): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000351 console.log("[MockGitDataService] Getting base commit ref");
352
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700353 // Find the commit with the sketch-base ref
Autoformatter8c463622025-05-16 21:54:17 +0000354 const baseCommit = this.mockCommits.find(
355 (commit) => commit.refs && commit.refs.includes("sketch-base"),
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700356 );
Autoformatter8c463622025-05-16 21:54:17 +0000357
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700358 if (baseCommit) {
359 return baseCommit.hash;
360 }
Autoformatter8c463622025-05-16 21:54:17 +0000361
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700362 // Fallback to the last commit in our list
363 return this.mockCommits[this.mockCommits.length - 1].hash;
364 }
365
366 // Helper to simulate network delay
367 private delay(ms: number): Promise<void> {
Autoformatter8c463622025-05-16 21:54:17 +0000368 return new Promise((resolve) => setTimeout(resolve, ms));
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700369 }
Autoformatter8c463622025-05-16 21:54:17 +0000370
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700371 async getWorkingCopyContent(filePath: string): Promise<string> {
Autoformatter8c463622025-05-16 21:54:17 +0000372 console.log(
373 `[MockGitDataService] Getting working copy content for path: ${filePath}`,
374 );
375
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700376 // Return different content based on the file path
Autoformatter8c463622025-05-16 21:54:17 +0000377 if (filePath === "src/components/App.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700378 return this.appJSModified;
Autoformatter8c463622025-05-16 21:54:17 +0000379 } else if (filePath === "src/components/FilePicker.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700380 return this.filePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000381 } else if (filePath === "src/components/RangePicker.js") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700382 return this.rangePickerJS;
Autoformatter8c463622025-05-16 21:54:17 +0000383 } else if (filePath === "src/styles/main.css") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700384 return this.mainCSSModified;
385 }
Autoformatter8c463622025-05-16 21:54:17 +0000386
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700387 // Return empty string for unknown file paths
Autoformatter8c463622025-05-16 21:54:17 +0000388 return "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700389 }
Autoformatter8c463622025-05-16 21:54:17 +0000390
391 async getUnstagedChanges(from: string = "HEAD"): Promise<GitDiffFile[]> {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700392 console.log(`[MockGitDataService] Getting unstaged changes from ${from}`);
Autoformatter8c463622025-05-16 21:54:17 +0000393
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700394 // Create a new array of files with 0000000... as the new hashes
395 // to simulate unstaged changes
Autoformatter8c463622025-05-16 21:54:17 +0000396 return this.mockDiffFiles.map((file) => ({
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700397 ...file,
Autoformatter8c463622025-05-16 21:54:17 +0000398 newHash: "0000000000000000000000000000000000000000",
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700399 }));
400 }
Autoformatter8c463622025-05-16 21:54:17 +0000401
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700402 async saveFileContent(filePath: string, content: string): Promise<void> {
Autoformatter8c463622025-05-16 21:54:17 +0000403 console.log(
404 `[MockGitDataService] Saving file content for path: ${filePath}`,
405 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700406 // Simulate a network delay
407 await this.delay(500);
408 // In a mock implementation, we just log the save attempt
Autoformatter8c463622025-05-16 21:54:17 +0000409 console.log(
410 `File would be saved: ${filePath} (${content.length} characters)`,
411 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700412 // Return void as per interface
413 }
Autoformatter8c463622025-05-16 21:54:17 +0000414}