git-history-ui 1.0.0 → 1.0.1

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.
@@ -1,31 +1,426 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const gitService_1 = require("../backend/gitService");
4
+ // Mock simple-git
5
+ const mockGit = {
6
+ log: jest.fn(),
7
+ tags: jest.fn(),
8
+ branch: jest.fn(),
9
+ diff: jest.fn(),
10
+ raw: jest.fn(),
11
+ };
12
+ jest.mock('simple-git', () => {
13
+ return jest.fn(() => mockGit);
14
+ });
4
15
  describe('GitService', () => {
5
16
  let gitService;
6
17
  beforeEach(() => {
7
18
  gitService = new gitService_1.GitService();
19
+ jest.clearAllMocks();
8
20
  });
9
21
  describe('getCommits', () => {
10
22
  it('should return an array of commits', async () => {
11
- const commits = await gitService.getCommits({ limit: 5 });
23
+ const mockCommits = [
24
+ {
25
+ hash: 'abc123',
26
+ date: '2024-01-01',
27
+ message: 'Test commit',
28
+ author_name: 'Test Author',
29
+ author_email: 'test@example.com',
30
+ refs: 'HEAD -> main',
31
+ body: '',
32
+ hash_abbrev: 'abc123',
33
+ tree: 'def456',
34
+ tree_abbrev: 'def456',
35
+ parent: 'ghi789',
36
+ parent_abbrev: 'ghi789'
37
+ }
38
+ ];
39
+ mockGit.log.mockResolvedValue({ all: mockCommits });
40
+ mockGit.diff.mockResolvedValue('');
41
+ mockGit.branch.mockResolvedValue({ all: ['main'] });
42
+ mockGit.raw.mockResolvedValue('');
43
+ const commits = await gitService.getCommits();
12
44
  expect(Array.isArray(commits)).toBe(true);
45
+ expect(commits.length).toBeGreaterThan(0);
46
+ expect(commits[0]).toHaveProperty('hash');
47
+ expect(commits[0]).toHaveProperty('author');
48
+ expect(commits[0]).toHaveProperty('date');
49
+ expect(commits[0]).toHaveProperty('message');
13
50
  });
14
51
  it('should respect the limit parameter', async () => {
52
+ const mockCommits = Array(3).fill(null).map((_, i) => ({
53
+ hash: `hash${i}`,
54
+ date: '2024-01-01',
55
+ message: `Commit ${i}`,
56
+ author_name: 'Test Author',
57
+ author_email: 'test@example.com',
58
+ refs: 'HEAD -> main',
59
+ body: '',
60
+ hash_abbrev: `hash${i}`,
61
+ tree: 'def456',
62
+ tree_abbrev: 'def456',
63
+ parent: 'ghi789',
64
+ parent_abbrev: 'ghi789'
65
+ }));
66
+ mockGit.log.mockResolvedValue({ all: mockCommits });
15
67
  const commits = await gitService.getCommits({ limit: 3 });
16
- expect(commits.length).toBeLessThanOrEqual(3);
68
+ // The GitService should respect the limit parameter
69
+ expect(commits.length).toBe(3);
70
+ });
71
+ it('should handle git log errors', async () => {
72
+ mockGit.log.mockRejectedValue(new Error('Git error'));
73
+ await expect(gitService.getCommits()).rejects.toThrow('Git error');
74
+ });
75
+ it('should handle empty commit list', async () => {
76
+ mockGit.log.mockResolvedValue({ all: [] });
77
+ const commits = await gitService.getCommits();
78
+ expect(Array.isArray(commits)).toBe(true);
79
+ expect(commits.length).toBe(0);
80
+ });
81
+ it('should handle file filter option', async () => {
82
+ const mockCommits = [
83
+ {
84
+ hash: 'abc123',
85
+ date: '2024-01-01',
86
+ message: 'Test commit',
87
+ author_name: 'Test Author',
88
+ author_email: 'test@example.com',
89
+ refs: 'HEAD -> main',
90
+ body: '',
91
+ hash_abbrev: 'abc123',
92
+ tree: 'def456',
93
+ tree_abbrev: 'def456',
94
+ parent: 'ghi789',
95
+ parent_abbrev: 'ghi789'
96
+ }
97
+ ];
98
+ mockGit.log.mockResolvedValue({ all: mockCommits });
99
+ const commits = await gitService.getCommits({ file: 'src/app.js' });
100
+ expect(Array.isArray(commits)).toBe(true);
101
+ expect(commits.length).toBeGreaterThan(0);
102
+ });
103
+ it('should handle since filter option', async () => {
104
+ const mockCommits = [
105
+ {
106
+ hash: 'abc123',
107
+ date: '2024-01-01',
108
+ message: 'Test commit',
109
+ author_name: 'Test Author',
110
+ author_email: 'test@example.com',
111
+ refs: 'HEAD -> main',
112
+ body: '',
113
+ hash_abbrev: 'abc123',
114
+ tree: 'def456',
115
+ tree_abbrev: 'def456',
116
+ parent: 'ghi789',
117
+ parent_abbrev: 'ghi789'
118
+ }
119
+ ];
120
+ mockGit.log.mockResolvedValue({ all: mockCommits });
121
+ const commits = await gitService.getCommits({ since: 'v1.0.0' });
122
+ expect(Array.isArray(commits)).toBe(true);
123
+ expect(commits.length).toBeGreaterThan(0);
124
+ });
125
+ it('should handle author filter option', async () => {
126
+ const mockCommits = [
127
+ {
128
+ hash: 'abc123',
129
+ date: '2024-01-01',
130
+ message: 'Test commit',
131
+ author_name: 'Test Author',
132
+ author_email: 'test@example.com',
133
+ refs: 'HEAD -> main',
134
+ body: '',
135
+ hash_abbrev: 'abc123',
136
+ tree: 'def456',
137
+ tree_abbrev: 'def456',
138
+ parent: 'ghi789',
139
+ parent_abbrev: 'ghi789'
140
+ }
141
+ ];
142
+ mockGit.log.mockResolvedValue({ all: mockCommits });
143
+ mockGit.diff.mockResolvedValue('');
144
+ mockGit.branch.mockResolvedValue({ all: ['main'] });
145
+ mockGit.raw.mockResolvedValue('');
146
+ const commits = await gitService.getCommits({ author: 'Test Author' });
147
+ expect(Array.isArray(commits)).toBe(true);
148
+ expect(commits.length).toBeGreaterThan(0);
149
+ });
150
+ it('should handle private method errors gracefully', async () => {
151
+ const mockCommits = [
152
+ {
153
+ hash: 'abc123',
154
+ date: '2024-01-01',
155
+ message: 'Test commit',
156
+ author_name: 'Test Author',
157
+ author_email: 'test@example.com',
158
+ refs: 'HEAD -> main',
159
+ body: '',
160
+ hash_abbrev: 'abc123',
161
+ tree: 'def456',
162
+ tree_abbrev: 'def456',
163
+ parent: 'ghi789',
164
+ parent_abbrev: 'ghi789'
165
+ }
166
+ ];
167
+ mockGit.log.mockResolvedValue({ all: mockCommits });
168
+ mockGit.diff.mockRejectedValue(new Error('Diff error'));
169
+ mockGit.branch.mockRejectedValue(new Error('Branch error'));
170
+ mockGit.raw.mockRejectedValue(new Error('Tag error'));
171
+ const commits = await gitService.getCommits();
172
+ expect(Array.isArray(commits)).toBe(true);
173
+ expect(commits.length).toBeGreaterThan(0);
174
+ expect(commits[0].files).toEqual([]);
175
+ expect(commits[0].branches).toEqual([]);
176
+ expect(commits[0].tags).toEqual([]);
177
+ });
178
+ it('should handle non-Error exceptions', async () => {
179
+ mockGit.log.mockRejectedValue('String error');
180
+ await expect(gitService.getCommits()).rejects.toThrow('Failed to get commits: Unknown error');
17
181
  });
18
182
  });
19
183
  describe('getTags', () => {
20
184
  it('should return an array of tags', async () => {
185
+ const mockTags = ['v1.0.0', 'v1.1.0', 'v2.0.0'];
186
+ mockGit.tags.mockResolvedValue({ all: mockTags });
187
+ const tags = await gitService.getTags();
188
+ expect(Array.isArray(tags)).toBe(true);
189
+ expect(tags).toEqual(mockTags);
190
+ });
191
+ it('should handle git tags errors', async () => {
192
+ mockGit.tags.mockRejectedValue(new Error('Git tags error'));
193
+ await expect(gitService.getTags()).rejects.toThrow('Git tags error');
194
+ });
195
+ it('should handle empty tags list', async () => {
196
+ mockGit.tags.mockResolvedValue({ all: [] });
21
197
  const tags = await gitService.getTags();
22
198
  expect(Array.isArray(tags)).toBe(true);
199
+ expect(tags.length).toBe(0);
23
200
  });
24
201
  });
25
202
  describe('getBranches', () => {
26
203
  it('should return an array of branches', async () => {
204
+ const mockBranches = ['main', 'develop', 'feature/test'];
205
+ mockGit.branch.mockResolvedValue({ all: mockBranches });
206
+ const branches = await gitService.getBranches();
207
+ expect(Array.isArray(branches)).toBe(true);
208
+ expect(branches).toEqual(mockBranches);
209
+ });
210
+ it('should handle git branch errors', async () => {
211
+ mockGit.branch.mockRejectedValue(new Error('Git branch error'));
212
+ await expect(gitService.getBranches()).rejects.toThrow('Git branch error');
213
+ });
214
+ it('should handle empty branches list', async () => {
215
+ mockGit.branch.mockResolvedValue({ all: [] });
27
216
  const branches = await gitService.getBranches();
28
217
  expect(Array.isArray(branches)).toBe(true);
218
+ expect(branches.length).toBe(0);
219
+ });
220
+ });
221
+ describe('getDiff', () => {
222
+ it('should return diff for a commit', async () => {
223
+ const mockDiff = 'diff --git a/file.js b/file.js\nindex 123..456 100644\n--- a/file.js\n+++ b/file.js\n@@ -1,3 +1,4 @@\n line1\n+line2\n line3';
224
+ mockGit.diff.mockResolvedValue(mockDiff);
225
+ const diff = await gitService.getDiff('abc123');
226
+ expect(Array.isArray(diff)).toBe(true);
227
+ expect(diff.length).toBeGreaterThan(0);
228
+ expect(diff[0]).toHaveProperty('file');
229
+ expect(diff[0]).toHaveProperty('additions');
230
+ expect(diff[0]).toHaveProperty('deletions');
231
+ expect(diff[0]).toHaveProperty('changes');
232
+ });
233
+ it('should handle git diff errors', async () => {
234
+ mockGit.diff.mockRejectedValue(new Error('Git diff error'));
235
+ await expect(gitService.getDiff('abc123')).rejects.toThrow('Git diff error');
236
+ });
237
+ it('should handle empty diff', async () => {
238
+ mockGit.diff.mockResolvedValue('');
239
+ const diff = await gitService.getDiff('abc123');
240
+ expect(Array.isArray(diff)).toBe(true);
241
+ expect(diff.length).toBe(0);
242
+ });
243
+ it('should parse complex diff with multiple files', async () => {
244
+ const mockDiff = `diff --git a/file1.js b/file1.js
245
+ index 123..456 100644
246
+ --- a/file1.js
247
+ +++ b/file1.js
248
+ @@ -1,3 +1,4 @@
249
+ line1
250
+ +line2
251
+ line3
252
+ diff --git a/file2.js b/file2.js
253
+ index 789..012 100644
254
+ --- a/file2.js
255
+ +++ b/file2.js
256
+ @@ -1,2 +1,3 @@
257
+ old1
258
+ +new1
259
+ old2`;
260
+ mockGit.diff.mockResolvedValue(mockDiff);
261
+ const diff = await gitService.getDiff('abc123');
262
+ expect(Array.isArray(diff)).toBe(true);
263
+ expect(diff.length).toBe(2);
264
+ expect(diff[0].file).toBe('file1.js');
265
+ expect(diff[1].file).toBe('file2.js');
266
+ });
267
+ it('should handle diff with no file match', async () => {
268
+ const mockDiff = 'diff --git a/ b/\nindex 123..456 100644\n--- a/\n+++ b/';
269
+ mockGit.diff.mockResolvedValue(mockDiff);
270
+ const diff = await gitService.getDiff('abc123');
271
+ expect(Array.isArray(diff)).toBe(true);
272
+ expect(diff.length).toBeGreaterThan(0);
273
+ expect(diff[0].file).toBe('');
274
+ });
275
+ it('should handle diff with context lines and headers', async () => {
276
+ const mockDiff = `diff --git a/file.js b/file.js
277
+ index 123..456 100644
278
+ --- a/file.js
279
+ +++ b/file.js
280
+ @@ -1,3 +1,4 @@
281
+ line1
282
+ +line2
283
+ line3
284
+ unchanged line`;
285
+ mockGit.diff.mockResolvedValue(mockDiff);
286
+ const diff = await gitService.getDiff('abc123');
287
+ expect(Array.isArray(diff)).toBe(true);
288
+ expect(diff.length).toBeGreaterThan(0);
289
+ });
290
+ it('should handle diff with only deletions', async () => {
291
+ const mockDiff = `diff --git a/file.js b/file.js
292
+ index 123..456 100644
293
+ --- a/file.js
294
+ +++ b/file.js
295
+ @@ -1,3 +1,2 @@
296
+ line1
297
+ -line2
298
+ line3`;
299
+ mockGit.diff.mockResolvedValue(mockDiff);
300
+ const diff = await gitService.getDiff('abc123');
301
+ expect(Array.isArray(diff)).toBe(true);
302
+ expect(diff.length).toBeGreaterThan(0);
303
+ expect(diff[0].deletions).toBeGreaterThan(0);
304
+ });
305
+ });
306
+ describe('getCommit', () => {
307
+ it('should return a single commit', async () => {
308
+ const mockCommit = {
309
+ hash: 'abc123',
310
+ date: '2024-01-01',
311
+ message: 'Test commit',
312
+ author_name: 'Test Author',
313
+ author_email: 'test@example.com',
314
+ refs: 'HEAD -> main',
315
+ body: '',
316
+ hash_abbrev: 'abc123',
317
+ tree: 'def456',
318
+ tree_abbrev: 'def456',
319
+ parent: 'ghi789',
320
+ parent_abbrev: 'ghi789'
321
+ };
322
+ mockGit.log.mockResolvedValue({ all: [mockCommit] });
323
+ const commit = await gitService.getCommit('abc123');
324
+ expect(commit).toHaveProperty('hash', 'abc123');
325
+ expect(commit).toHaveProperty('author', 'Test Author');
326
+ expect(commit).toHaveProperty('date', '2024-01-01');
327
+ expect(commit).toHaveProperty('message', 'Test commit');
328
+ });
329
+ it('should handle commit not found', async () => {
330
+ mockGit.log.mockResolvedValue({ all: [] });
331
+ await expect(gitService.getCommit('nonexistent')).rejects.toThrow('Commit not found');
332
+ });
333
+ it('should handle git log errors', async () => {
334
+ mockGit.log.mockRejectedValue(new Error('Git error'));
335
+ await expect(gitService.getCommit('abc123')).rejects.toThrow('Git error');
336
+ });
337
+ it('should handle non-Error exceptions', async () => {
338
+ mockGit.log.mockRejectedValue('String error');
339
+ await expect(gitService.getCommit('abc123')).rejects.toThrow('Failed to get commit: Unknown error');
340
+ });
341
+ });
342
+ describe('getBlame', () => {
343
+ it('should return blame information for a file', async () => {
344
+ const mockBlame = 'abc123 1 1 1\nauthor Test Author\n\tline1';
345
+ mockGit.raw.mockResolvedValue(mockBlame);
346
+ const blame = await gitService.getBlame('test.js');
347
+ expect(Array.isArray(blame)).toBe(true);
348
+ expect(blame.length).toBeGreaterThan(0);
349
+ expect(blame[0]).toHaveProperty('hash');
350
+ expect(blame[0]).toHaveProperty('author');
351
+ expect(blame[0]).toHaveProperty('line');
352
+ expect(blame[0]).toHaveProperty('content');
353
+ });
354
+ it('should handle git blame errors', async () => {
355
+ mockGit.raw.mockRejectedValue(new Error('Git blame error'));
356
+ await expect(gitService.getBlame('test.js')).rejects.toThrow('Git blame error');
357
+ });
358
+ it('should handle empty blame', async () => {
359
+ mockGit.raw.mockResolvedValue('');
360
+ const blame = await gitService.getBlame('test.js');
361
+ expect(Array.isArray(blame)).toBe(true);
362
+ expect(blame.length).toBe(0);
363
+ });
364
+ it('should handle non-Error exceptions', async () => {
365
+ mockGit.raw.mockRejectedValue('String error');
366
+ await expect(gitService.getBlame('test.js')).rejects.toThrow('Failed to get blame: Unknown error');
367
+ });
368
+ it('should parse blame with author-time', async () => {
369
+ const mockBlame = `abc123 1 1 1
370
+ author Test Author
371
+ author-time 1640995200
372
+ \tline1`;
373
+ mockGit.raw.mockResolvedValue(mockBlame);
374
+ const blame = await gitService.getBlame('test.js');
375
+ expect(Array.isArray(blame)).toBe(true);
376
+ expect(blame.length).toBeGreaterThan(0);
377
+ expect(blame[0]).toHaveProperty('date');
378
+ });
379
+ it('should parse blame without author-time', async () => {
380
+ const mockBlame = `abc123 1 1 1
381
+ author Test Author
382
+ \tline1`;
383
+ mockGit.raw.mockResolvedValue(mockBlame);
384
+ const blame = await gitService.getBlame('test.js');
385
+ expect(Array.isArray(blame)).toBe(true);
386
+ expect(blame.length).toBeGreaterThan(0);
387
+ expect(blame[0]).toHaveProperty('date');
388
+ });
389
+ it('should handle malformed blame data', async () => {
390
+ const mockBlame = 'invalid blame data without proper format';
391
+ mockGit.raw.mockResolvedValue(mockBlame);
392
+ const blame = await gitService.getBlame('test.js');
393
+ expect(Array.isArray(blame)).toBe(true);
394
+ expect(blame.length).toBe(0);
395
+ });
396
+ it('should parse blame with multiple lines and complete properly', async () => {
397
+ const mockBlame = `abc123 1 1 1
398
+ author Test Author
399
+ author-time 1640995200
400
+ \tline1
401
+ def456 2 2 1
402
+ author Another Author
403
+ \tline2`;
404
+ mockGit.raw.mockResolvedValue(mockBlame);
405
+ const blame = await gitService.getBlame('test.js');
406
+ expect(Array.isArray(blame)).toBe(true);
407
+ expect(blame.length).toBe(2);
408
+ expect(blame[0].hash).toBe('abc123');
409
+ expect(blame[1].hash).toBe('def456');
410
+ });
411
+ });
412
+ describe('Error handling', () => {
413
+ it('should handle non-Error exceptions in getTags', async () => {
414
+ mockGit.tags.mockRejectedValue('String error');
415
+ await expect(gitService.getTags()).rejects.toThrow('Failed to get tags: Unknown error');
416
+ });
417
+ it('should handle non-Error exceptions in getBranches', async () => {
418
+ mockGit.branch.mockRejectedValue('String error');
419
+ await expect(gitService.getBranches()).rejects.toThrow('Failed to get branches: Unknown error');
420
+ });
421
+ it('should handle non-Error exceptions in getDiff', async () => {
422
+ mockGit.diff.mockRejectedValue('String error');
423
+ await expect(gitService.getDiff('abc123')).rejects.toThrow('Failed to get diff: Unknown error');
29
424
  });
30
425
  });
31
426
  });
@@ -1 +1 @@
1
- {"version":3,"file":"gitService.test.js","sourceRoot":"","sources":["../../src/__tests__/gitService.test.ts"],"names":[],"mappings":";;AAAA,sDAAmD;AAEnD,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,IAAI,UAAsB,CAAC;IAE3B,UAAU,CAAC,GAAG,EAAE;QACd,UAAU,GAAG,IAAI,uBAAU,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;YACjD,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YAC1D,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YAC1D,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE;QACvB,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC;YACxC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,WAAW,EAAE,CAAC;YAChD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"gitService.test.js","sourceRoot":"","sources":["../../src/__tests__/gitService.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/__tests__/setup.ts"],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // Test setup file
4
+ const globals_1 = require("@jest/globals");
5
+ // Mock console methods to reduce noise in tests
6
+ global.console = {
7
+ ...console,
8
+ log: globals_1.jest.fn(),
9
+ debug: globals_1.jest.fn(),
10
+ info: globals_1.jest.fn(),
11
+ warn: globals_1.jest.fn(),
12
+ error: globals_1.jest.fn(),
13
+ };
14
+ // Mock process.exit to prevent tests from exiting
15
+ process.exit = globals_1.jest.fn();
16
+ // Set test timeout
17
+ globals_1.jest.setTimeout(10000);
18
+ // Add a simple test to satisfy Jest requirements
19
+ describe('Setup', () => {
20
+ it('should be configured correctly', () => {
21
+ expect(true).toBe(true);
22
+ });
23
+ });
24
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/__tests__/setup.ts"],"names":[],"mappings":";;AAAA,kBAAkB;AAClB,2CAAqC;AAErC,gDAAgD;AAChD,MAAM,CAAC,OAAO,GAAG;IACf,GAAG,OAAO;IACV,GAAG,EAAE,cAAI,CAAC,EAAE,EAAE;IACd,KAAK,EAAE,cAAI,CAAC,EAAE,EAAE;IAChB,IAAI,EAAE,cAAI,CAAC,EAAE,EAAE;IACf,IAAI,EAAE,cAAI,CAAC,EAAE,EAAE;IACf,KAAK,EAAE,cAAI,CAAC,EAAE,EAAE;CACjB,CAAC;AAEF,kDAAkD;AAClD,OAAO,CAAC,IAAI,GAAG,cAAI,CAAC,EAAE,EAAS,CAAC;AAEhC,mBAAmB;AACnB,cAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AAEvB,iDAAiD;AACjD,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/jest.config.js CHANGED
@@ -9,5 +9,18 @@ module.exports = {
9
9
  collectCoverageFrom: [
10
10
  'src/**/*.ts',
11
11
  '!src/**/*.d.ts',
12
+ '!src/**/*.test.ts',
13
+ '!src/**/*.spec.ts',
12
14
  ],
15
+ coverageDirectory: 'coverage',
16
+ coverageReporters: ['text', 'lcov', 'html'],
17
+ coverageThreshold: {
18
+ global: {
19
+ branches: 80,
20
+ functions: 80,
21
+ lines: 80,
22
+ statements: 80
23
+ }
24
+ },
25
+ setupFilesAfterEnv: ['<rootDir>/src/__tests__/setup.ts'],
13
26
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-history-ui",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Beautiful git history visualization in your browser",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -15,6 +15,8 @@
15
15
  "dev:frontend": "cd frontend && ng serve --port 4200",
16
16
  "start": "node dist/backend/server.js",
17
17
  "test": "jest",
18
+ "test:coverage": "jest --coverage",
19
+ "test:watch": "jest --watch",
18
20
  "lint": "eslint src/**/*.ts",
19
21
  "prepare": "npm run build"
20
22
  },
@@ -1,35 +1,533 @@
1
1
  import { GitService } from '../backend/gitService';
2
2
 
3
+ // Mock simple-git
4
+ const mockGit = {
5
+ log: jest.fn(),
6
+ tags: jest.fn(),
7
+ branch: jest.fn(),
8
+ diff: jest.fn(),
9
+ raw: jest.fn(),
10
+ };
11
+
12
+ jest.mock('simple-git', () => {
13
+ return jest.fn(() => mockGit);
14
+ });
15
+
3
16
  describe('GitService', () => {
4
17
  let gitService: GitService;
5
18
 
6
19
  beforeEach(() => {
7
20
  gitService = new GitService();
21
+ jest.clearAllMocks();
8
22
  });
9
23
 
10
24
  describe('getCommits', () => {
11
25
  it('should return an array of commits', async () => {
12
- const commits = await gitService.getCommits({ limit: 5 });
26
+ const mockCommits = [
27
+ {
28
+ hash: 'abc123',
29
+ date: '2024-01-01',
30
+ message: 'Test commit',
31
+ author_name: 'Test Author',
32
+ author_email: 'test@example.com',
33
+ refs: 'HEAD -> main',
34
+ body: '',
35
+ hash_abbrev: 'abc123',
36
+ tree: 'def456',
37
+ tree_abbrev: 'def456',
38
+ parent: 'ghi789',
39
+ parent_abbrev: 'ghi789'
40
+ }
41
+ ];
42
+
43
+ mockGit.log.mockResolvedValue({ all: mockCommits });
44
+ mockGit.diff.mockResolvedValue('');
45
+ mockGit.branch.mockResolvedValue({ all: ['main'] });
46
+ mockGit.raw.mockResolvedValue('');
47
+
48
+ const commits = await gitService.getCommits();
49
+
13
50
  expect(Array.isArray(commits)).toBe(true);
51
+ expect(commits.length).toBeGreaterThan(0);
52
+ expect(commits[0]).toHaveProperty('hash');
53
+ expect(commits[0]).toHaveProperty('author');
54
+ expect(commits[0]).toHaveProperty('date');
55
+ expect(commits[0]).toHaveProperty('message');
14
56
  });
15
57
 
16
58
  it('should respect the limit parameter', async () => {
59
+ const mockCommits = Array(3).fill(null).map((_, i) => ({
60
+ hash: `hash${i}`,
61
+ date: '2024-01-01',
62
+ message: `Commit ${i}`,
63
+ author_name: 'Test Author',
64
+ author_email: 'test@example.com',
65
+ refs: 'HEAD -> main',
66
+ body: '',
67
+ hash_abbrev: `hash${i}`,
68
+ tree: 'def456',
69
+ tree_abbrev: 'def456',
70
+ parent: 'ghi789',
71
+ parent_abbrev: 'ghi789'
72
+ }));
73
+
74
+ mockGit.log.mockResolvedValue({ all: mockCommits });
75
+
17
76
  const commits = await gitService.getCommits({ limit: 3 });
18
- expect(commits.length).toBeLessThanOrEqual(3);
77
+
78
+ // The GitService should respect the limit parameter
79
+ expect(commits.length).toBe(3);
80
+ });
81
+
82
+ it('should handle git log errors', async () => {
83
+ mockGit.log.mockRejectedValue(new Error('Git error'));
84
+
85
+ await expect(gitService.getCommits()).rejects.toThrow('Git error');
86
+ });
87
+
88
+ it('should handle empty commit list', async () => {
89
+ mockGit.log.mockResolvedValue({ all: [] });
90
+
91
+ const commits = await gitService.getCommits();
92
+
93
+ expect(Array.isArray(commits)).toBe(true);
94
+ expect(commits.length).toBe(0);
95
+ });
96
+
97
+ it('should handle file filter option', async () => {
98
+ const mockCommits = [
99
+ {
100
+ hash: 'abc123',
101
+ date: '2024-01-01',
102
+ message: 'Test commit',
103
+ author_name: 'Test Author',
104
+ author_email: 'test@example.com',
105
+ refs: 'HEAD -> main',
106
+ body: '',
107
+ hash_abbrev: 'abc123',
108
+ tree: 'def456',
109
+ tree_abbrev: 'def456',
110
+ parent: 'ghi789',
111
+ parent_abbrev: 'ghi789'
112
+ }
113
+ ];
114
+
115
+ mockGit.log.mockResolvedValue({ all: mockCommits });
116
+
117
+ const commits = await gitService.getCommits({ file: 'src/app.js' });
118
+
119
+ expect(Array.isArray(commits)).toBe(true);
120
+ expect(commits.length).toBeGreaterThan(0);
121
+ });
122
+
123
+ it('should handle since filter option', async () => {
124
+ const mockCommits = [
125
+ {
126
+ hash: 'abc123',
127
+ date: '2024-01-01',
128
+ message: 'Test commit',
129
+ author_name: 'Test Author',
130
+ author_email: 'test@example.com',
131
+ refs: 'HEAD -> main',
132
+ body: '',
133
+ hash_abbrev: 'abc123',
134
+ tree: 'def456',
135
+ tree_abbrev: 'def456',
136
+ parent: 'ghi789',
137
+ parent_abbrev: 'ghi789'
138
+ }
139
+ ];
140
+
141
+ mockGit.log.mockResolvedValue({ all: mockCommits });
142
+
143
+ const commits = await gitService.getCommits({ since: 'v1.0.0' });
144
+
145
+ expect(Array.isArray(commits)).toBe(true);
146
+ expect(commits.length).toBeGreaterThan(0);
147
+ });
148
+
149
+ it('should handle author filter option', async () => {
150
+ const mockCommits = [
151
+ {
152
+ hash: 'abc123',
153
+ date: '2024-01-01',
154
+ message: 'Test commit',
155
+ author_name: 'Test Author',
156
+ author_email: 'test@example.com',
157
+ refs: 'HEAD -> main',
158
+ body: '',
159
+ hash_abbrev: 'abc123',
160
+ tree: 'def456',
161
+ tree_abbrev: 'def456',
162
+ parent: 'ghi789',
163
+ parent_abbrev: 'ghi789'
164
+ }
165
+ ];
166
+
167
+ mockGit.log.mockResolvedValue({ all: mockCommits });
168
+ mockGit.diff.mockResolvedValue('');
169
+ mockGit.branch.mockResolvedValue({ all: ['main'] });
170
+ mockGit.raw.mockResolvedValue('');
171
+
172
+ const commits = await gitService.getCommits({ author: 'Test Author' });
173
+
174
+ expect(Array.isArray(commits)).toBe(true);
175
+ expect(commits.length).toBeGreaterThan(0);
176
+ });
177
+
178
+ it('should handle private method errors gracefully', async () => {
179
+ const mockCommits = [
180
+ {
181
+ hash: 'abc123',
182
+ date: '2024-01-01',
183
+ message: 'Test commit',
184
+ author_name: 'Test Author',
185
+ author_email: 'test@example.com',
186
+ refs: 'HEAD -> main',
187
+ body: '',
188
+ hash_abbrev: 'abc123',
189
+ tree: 'def456',
190
+ tree_abbrev: 'def456',
191
+ parent: 'ghi789',
192
+ parent_abbrev: 'ghi789'
193
+ }
194
+ ];
195
+
196
+ mockGit.log.mockResolvedValue({ all: mockCommits });
197
+ mockGit.diff.mockRejectedValue(new Error('Diff error'));
198
+ mockGit.branch.mockRejectedValue(new Error('Branch error'));
199
+ mockGit.raw.mockRejectedValue(new Error('Tag error'));
200
+
201
+ const commits = await gitService.getCommits();
202
+
203
+ expect(Array.isArray(commits)).toBe(true);
204
+ expect(commits.length).toBeGreaterThan(0);
205
+ expect(commits[0].files).toEqual([]);
206
+ expect(commits[0].branches).toEqual([]);
207
+ expect(commits[0].tags).toEqual([]);
208
+ });
209
+
210
+ it('should handle non-Error exceptions', async () => {
211
+ mockGit.log.mockRejectedValue('String error');
212
+
213
+ await expect(gitService.getCommits()).rejects.toThrow('Failed to get commits: Unknown error');
19
214
  });
20
215
  });
21
216
 
22
217
  describe('getTags', () => {
23
218
  it('should return an array of tags', async () => {
219
+ const mockTags = ['v1.0.0', 'v1.1.0', 'v2.0.0'];
220
+ mockGit.tags.mockResolvedValue({ all: mockTags });
221
+
222
+ const tags = await gitService.getTags();
223
+
224
+ expect(Array.isArray(tags)).toBe(true);
225
+ expect(tags).toEqual(mockTags);
226
+ });
227
+
228
+ it('should handle git tags errors', async () => {
229
+ mockGit.tags.mockRejectedValue(new Error('Git tags error'));
230
+
231
+ await expect(gitService.getTags()).rejects.toThrow('Git tags error');
232
+ });
233
+
234
+ it('should handle empty tags list', async () => {
235
+ mockGit.tags.mockResolvedValue({ all: [] });
236
+
24
237
  const tags = await gitService.getTags();
238
+
25
239
  expect(Array.isArray(tags)).toBe(true);
240
+ expect(tags.length).toBe(0);
26
241
  });
27
242
  });
28
243
 
29
244
  describe('getBranches', () => {
30
245
  it('should return an array of branches', async () => {
246
+ const mockBranches = ['main', 'develop', 'feature/test'];
247
+ mockGit.branch.mockResolvedValue({ all: mockBranches });
248
+
31
249
  const branches = await gitService.getBranches();
250
+
32
251
  expect(Array.isArray(branches)).toBe(true);
252
+ expect(branches).toEqual(mockBranches);
253
+ });
254
+
255
+ it('should handle git branch errors', async () => {
256
+ mockGit.branch.mockRejectedValue(new Error('Git branch error'));
257
+
258
+ await expect(gitService.getBranches()).rejects.toThrow('Git branch error');
259
+ });
260
+
261
+ it('should handle empty branches list', async () => {
262
+ mockGit.branch.mockResolvedValue({ all: [] });
263
+
264
+ const branches = await gitService.getBranches();
265
+
266
+ expect(Array.isArray(branches)).toBe(true);
267
+ expect(branches.length).toBe(0);
268
+ });
269
+ });
270
+
271
+ describe('getDiff', () => {
272
+ it('should return diff for a commit', async () => {
273
+ const mockDiff = 'diff --git a/file.js b/file.js\nindex 123..456 100644\n--- a/file.js\n+++ b/file.js\n@@ -1,3 +1,4 @@\n line1\n+line2\n line3';
274
+ mockGit.diff.mockResolvedValue(mockDiff);
275
+
276
+ const diff = await gitService.getDiff('abc123');
277
+
278
+ expect(Array.isArray(diff)).toBe(true);
279
+ expect(diff.length).toBeGreaterThan(0);
280
+ expect(diff[0]).toHaveProperty('file');
281
+ expect(diff[0]).toHaveProperty('additions');
282
+ expect(diff[0]).toHaveProperty('deletions');
283
+ expect(diff[0]).toHaveProperty('changes');
284
+ });
285
+
286
+ it('should handle git diff errors', async () => {
287
+ mockGit.diff.mockRejectedValue(new Error('Git diff error'));
288
+
289
+ await expect(gitService.getDiff('abc123')).rejects.toThrow('Git diff error');
290
+ });
291
+
292
+ it('should handle empty diff', async () => {
293
+ mockGit.diff.mockResolvedValue('');
294
+
295
+ const diff = await gitService.getDiff('abc123');
296
+
297
+ expect(Array.isArray(diff)).toBe(true);
298
+ expect(diff.length).toBe(0);
299
+ });
300
+
301
+ it('should parse complex diff with multiple files', async () => {
302
+ const mockDiff = `diff --git a/file1.js b/file1.js
303
+ index 123..456 100644
304
+ --- a/file1.js
305
+ +++ b/file1.js
306
+ @@ -1,3 +1,4 @@
307
+ line1
308
+ +line2
309
+ line3
310
+ diff --git a/file2.js b/file2.js
311
+ index 789..012 100644
312
+ --- a/file2.js
313
+ +++ b/file2.js
314
+ @@ -1,2 +1,3 @@
315
+ old1
316
+ +new1
317
+ old2`;
318
+ mockGit.diff.mockResolvedValue(mockDiff);
319
+
320
+ const diff = await gitService.getDiff('abc123');
321
+
322
+ expect(Array.isArray(diff)).toBe(true);
323
+ expect(diff.length).toBe(2);
324
+ expect(diff[0].file).toBe('file1.js');
325
+ expect(diff[1].file).toBe('file2.js');
326
+ });
327
+
328
+ it('should handle diff with no file match', async () => {
329
+ const mockDiff = 'diff --git a/ b/\nindex 123..456 100644\n--- a/\n+++ b/';
330
+ mockGit.diff.mockResolvedValue(mockDiff);
331
+
332
+ const diff = await gitService.getDiff('abc123');
333
+
334
+ expect(Array.isArray(diff)).toBe(true);
335
+ expect(diff.length).toBeGreaterThan(0);
336
+ expect(diff[0].file).toBe('');
337
+ });
338
+
339
+ it('should handle diff with context lines and headers', async () => {
340
+ const mockDiff = `diff --git a/file.js b/file.js
341
+ index 123..456 100644
342
+ --- a/file.js
343
+ +++ b/file.js
344
+ @@ -1,3 +1,4 @@
345
+ line1
346
+ +line2
347
+ line3
348
+ unchanged line`;
349
+ mockGit.diff.mockResolvedValue(mockDiff);
350
+
351
+ const diff = await gitService.getDiff('abc123');
352
+
353
+ expect(Array.isArray(diff)).toBe(true);
354
+ expect(diff.length).toBeGreaterThan(0);
355
+ });
356
+
357
+ it('should handle diff with only deletions', async () => {
358
+ const mockDiff = `diff --git a/file.js b/file.js
359
+ index 123..456 100644
360
+ --- a/file.js
361
+ +++ b/file.js
362
+ @@ -1,3 +1,2 @@
363
+ line1
364
+ -line2
365
+ line3`;
366
+ mockGit.diff.mockResolvedValue(mockDiff);
367
+
368
+ const diff = await gitService.getDiff('abc123');
369
+
370
+ expect(Array.isArray(diff)).toBe(true);
371
+ expect(diff.length).toBeGreaterThan(0);
372
+ expect(diff[0].deletions).toBeGreaterThan(0);
373
+ });
374
+ });
375
+
376
+ describe('getCommit', () => {
377
+ it('should return a single commit', async () => {
378
+ const mockCommit = {
379
+ hash: 'abc123',
380
+ date: '2024-01-01',
381
+ message: 'Test commit',
382
+ author_name: 'Test Author',
383
+ author_email: 'test@example.com',
384
+ refs: 'HEAD -> main',
385
+ body: '',
386
+ hash_abbrev: 'abc123',
387
+ tree: 'def456',
388
+ tree_abbrev: 'def456',
389
+ parent: 'ghi789',
390
+ parent_abbrev: 'ghi789'
391
+ };
392
+
393
+ mockGit.log.mockResolvedValue({ all: [mockCommit] });
394
+
395
+ const commit = await gitService.getCommit('abc123');
396
+
397
+ expect(commit).toHaveProperty('hash', 'abc123');
398
+ expect(commit).toHaveProperty('author', 'Test Author');
399
+ expect(commit).toHaveProperty('date', '2024-01-01');
400
+ expect(commit).toHaveProperty('message', 'Test commit');
401
+ });
402
+
403
+ it('should handle commit not found', async () => {
404
+ mockGit.log.mockResolvedValue({ all: [] });
405
+
406
+ await expect(gitService.getCommit('nonexistent')).rejects.toThrow('Commit not found');
407
+ });
408
+
409
+ it('should handle git log errors', async () => {
410
+ mockGit.log.mockRejectedValue(new Error('Git error'));
411
+
412
+ await expect(gitService.getCommit('abc123')).rejects.toThrow('Git error');
413
+ });
414
+
415
+ it('should handle non-Error exceptions', async () => {
416
+ mockGit.log.mockRejectedValue('String error');
417
+
418
+ await expect(gitService.getCommit('abc123')).rejects.toThrow('Failed to get commit: Unknown error');
419
+ });
420
+ });
421
+
422
+ describe('getBlame', () => {
423
+ it('should return blame information for a file', async () => {
424
+ const mockBlame = 'abc123 1 1 1\nauthor Test Author\n\tline1';
425
+ mockGit.raw.mockResolvedValue(mockBlame);
426
+
427
+ const blame = await gitService.getBlame('test.js');
428
+
429
+ expect(Array.isArray(blame)).toBe(true);
430
+ expect(blame.length).toBeGreaterThan(0);
431
+ expect(blame[0]).toHaveProperty('hash');
432
+ expect(blame[0]).toHaveProperty('author');
433
+ expect(blame[0]).toHaveProperty('line');
434
+ expect(blame[0]).toHaveProperty('content');
435
+ });
436
+
437
+ it('should handle git blame errors', async () => {
438
+ mockGit.raw.mockRejectedValue(new Error('Git blame error'));
439
+
440
+ await expect(gitService.getBlame('test.js')).rejects.toThrow('Git blame error');
441
+ });
442
+
443
+ it('should handle empty blame', async () => {
444
+ mockGit.raw.mockResolvedValue('');
445
+
446
+ const blame = await gitService.getBlame('test.js');
447
+
448
+ expect(Array.isArray(blame)).toBe(true);
449
+ expect(blame.length).toBe(0);
450
+ });
451
+
452
+ it('should handle non-Error exceptions', async () => {
453
+ mockGit.raw.mockRejectedValue('String error');
454
+
455
+ await expect(gitService.getBlame('test.js')).rejects.toThrow('Failed to get blame: Unknown error');
456
+ });
457
+
458
+ it('should parse blame with author-time', async () => {
459
+ const mockBlame = `abc123 1 1 1
460
+ author Test Author
461
+ author-time 1640995200
462
+ \tline1`;
463
+ mockGit.raw.mockResolvedValue(mockBlame);
464
+
465
+ const blame = await gitService.getBlame('test.js');
466
+
467
+ expect(Array.isArray(blame)).toBe(true);
468
+ expect(blame.length).toBeGreaterThan(0);
469
+ expect(blame[0]).toHaveProperty('date');
470
+ });
471
+
472
+ it('should parse blame without author-time', async () => {
473
+ const mockBlame = `abc123 1 1 1
474
+ author Test Author
475
+ \tline1`;
476
+ mockGit.raw.mockResolvedValue(mockBlame);
477
+
478
+ const blame = await gitService.getBlame('test.js');
479
+
480
+ expect(Array.isArray(blame)).toBe(true);
481
+ expect(blame.length).toBeGreaterThan(0);
482
+ expect(blame[0]).toHaveProperty('date');
483
+ });
484
+
485
+ it('should handle malformed blame data', async () => {
486
+ const mockBlame = 'invalid blame data without proper format';
487
+ mockGit.raw.mockResolvedValue(mockBlame);
488
+
489
+ const blame = await gitService.getBlame('test.js');
490
+
491
+ expect(Array.isArray(blame)).toBe(true);
492
+ expect(blame.length).toBe(0);
493
+ });
494
+
495
+ it('should parse blame with multiple lines and complete properly', async () => {
496
+ const mockBlame = `abc123 1 1 1
497
+ author Test Author
498
+ author-time 1640995200
499
+ \tline1
500
+ def456 2 2 1
501
+ author Another Author
502
+ \tline2`;
503
+ mockGit.raw.mockResolvedValue(mockBlame);
504
+
505
+ const blame = await gitService.getBlame('test.js');
506
+
507
+ expect(Array.isArray(blame)).toBe(true);
508
+ expect(blame.length).toBe(2);
509
+ expect(blame[0].hash).toBe('abc123');
510
+ expect(blame[1].hash).toBe('def456');
511
+ });
512
+ });
513
+
514
+ describe('Error handling', () => {
515
+ it('should handle non-Error exceptions in getTags', async () => {
516
+ mockGit.tags.mockRejectedValue('String error');
517
+
518
+ await expect(gitService.getTags()).rejects.toThrow('Failed to get tags: Unknown error');
519
+ });
520
+
521
+ it('should handle non-Error exceptions in getBranches', async () => {
522
+ mockGit.branch.mockRejectedValue('String error');
523
+
524
+ await expect(gitService.getBranches()).rejects.toThrow('Failed to get branches: Unknown error');
525
+ });
526
+
527
+ it('should handle non-Error exceptions in getDiff', async () => {
528
+ mockGit.diff.mockRejectedValue('String error');
529
+
530
+ await expect(gitService.getDiff('abc123')).rejects.toThrow('Failed to get diff: Unknown error');
33
531
  });
34
532
  });
35
533
  });
@@ -0,0 +1,25 @@
1
+ // Test setup file
2
+ import { jest } from '@jest/globals';
3
+
4
+ // Mock console methods to reduce noise in tests
5
+ global.console = {
6
+ ...console,
7
+ log: jest.fn(),
8
+ debug: jest.fn(),
9
+ info: jest.fn(),
10
+ warn: jest.fn(),
11
+ error: jest.fn(),
12
+ };
13
+
14
+ // Mock process.exit to prevent tests from exiting
15
+ process.exit = jest.fn() as any;
16
+
17
+ // Set test timeout
18
+ jest.setTimeout(10000);
19
+
20
+ // Add a simple test to satisfy Jest requirements
21
+ describe('Setup', () => {
22
+ it('should be configured correctly', () => {
23
+ expect(true).toBe(true);
24
+ });
25
+ });