Skip to content

Commit 2121f01

Browse files
committed
test: push coverage to 97.8% lines / 90.3% branches / 96.6% functions
+38 tests across three batches that fill the long-tail gaps: Helper-function branches: - Reflog relativeTime: hours / days / months / years / invalid-date branches (was only the minute branch); plus Escape-with-empty-query blurring the input - ActivityLog formatDuration: ms vs s branches - BisectBanner culpritAuthor: Author: line present / missing — derived value not currently displayed but still runs - md5: 2-byte UTF-8 (Latin-1 supplement), surrogate pair (emoji, 4-byte UTF-8), and mixed-input branches UI edge cases: - ContextMenu: top-level + child item icon rendering; viewport-edge clamping (right overflow → adjustedX, bottom overflow → adjustedY); submenu flips to on-left when right would overflow. Stubs Element.prototype.getBoundingClientRect for the test duration so the $effect sees the overflowing dimensions on its first run. - SearchBar: Enter with empty query → clear(); Escape closes the branch-filter dropdown without clearing the search query git-service edge cases: - stashList swallows errors and returns [] - log() stash insertion: parent in scope (splice), parent out of scope with no filter (unshift), parent out of scope with branch filter (drop), stash log exec failure (warn + continue) - log() uncommitted porcelain: staged/unstaged/untracked counting, empty porcelain (no UNCOMMITTED entry), porcelain exec failure - log() arg construction: sortOrder topological/date/author-date branches, --max-count / --skip / remoteFilter glob list / branch positional arg / unsafe-ref rejection Earlier-but-uncommitted rounds bundled here too: integration test timeout bump (testTimeout/hookTimeout 5s → 30s) for slower CI runners on the flow/lfs/submodule tests; defensive ?? '' fallback tests for parsers; repo-discovery cache + error-path tests.
1 parent ebac9d5 commit 2121f01

14 files changed

Lines changed: 1301 additions & 12 deletions

File tree

src/git/__tests__/git-parser.edge.test.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
parseStashList,
99
parseDiff,
1010
parseWorktreeList,
11+
parseLfsFiles,
12+
parseLfsLocks,
1113
} from '../git-parser';
1214

1315
describe('parseRefs — edge cases', () => {
@@ -172,6 +174,118 @@ describe('parseWorktreeList — edge cases', () => {
172174
});
173175
});
174176

177+
describe('parseLog — defensive field fallbacks', () => {
178+
it('handles a record with all fields missing (every ?? "" branch)', () => {
179+
// RECORD_SEP only, no FIELD_SEP — fields[1..11] are undefined
180+
const raw = '\x01';
181+
const result = parseLog(raw);
182+
// Empty trimmed record is filtered out by parseLog
183+
expect(result).toEqual([]);
184+
});
185+
186+
it('handles a record with only a hash (rest of fields undefined)', () => {
187+
const raw = '\x01abc123';
188+
const result = parseLog(raw);
189+
expect(result[0].hash).toBe('abc123');
190+
expect(result[0].abbreviatedHash).toBe('');
191+
expect(result[0].author.name).toBe('');
192+
expect(result[0].author.email).toBe('');
193+
expect(result[0].subject).toBe('');
194+
expect(result[0].body).toBe('');
195+
expect(result[0].parents).toEqual([]);
196+
expect(result[0].refs).toEqual([]);
197+
});
198+
199+
it('strips trailing whitespace from hash field', () => {
200+
const raw = '\x01 hashy \x00short\x00';
201+
expect(parseLog(raw)[0].hash).toBe('hashy');
202+
});
203+
});
204+
205+
describe('parseBranches — defensive fallbacks', () => {
206+
it('handles a line with only a name (other fields undefined)', () => {
207+
const raw = ' lonely';
208+
const result = parseBranches(raw);
209+
expect(result[0].name).toBe('lonely');
210+
expect(result[0].hash).toBe('');
211+
expect(result[0].upstream).toBeUndefined();
212+
expect(result[0].ahead).toBe(0);
213+
expect(result[0].behind).toBe(0);
214+
});
215+
216+
it('drops the trailing "heads/" prefix only when not a remote branch', () => {
217+
const raw = ' heads/foo\x00h\x00\x00\x00refs/heads/heads/foo';
218+
expect(parseBranches(raw)[0].name).toBe('foo');
219+
});
220+
});
221+
222+
describe('parseTags — annotated tag edge cases', () => {
223+
it('handles a tag line with no fields beyond name', () => {
224+
const raw = 'v1';
225+
const result = parseTags(raw);
226+
expect(result[0].name).toBe('v1');
227+
expect(result[0].hash).toBe('');
228+
expect(result[0].isAnnotated).toBe(false);
229+
});
230+
});
231+
232+
describe('parseStashList — defensive fallbacks', () => {
233+
it('handles a line with all fields missing except refStr', () => {
234+
const raw = 'stash@{0}';
235+
const result = parseStashList(raw);
236+
expect(result[0].index).toBe(0);
237+
expect(result[0].message).toBe('');
238+
expect(result[0].date).toBe('');
239+
expect(result[0].parentHash).toBeUndefined();
240+
expect(result[0].hash).toBeUndefined();
241+
});
242+
});
243+
244+
describe('parseDiff — hunk header without context tail', () => {
245+
it('hunk header with no trailing context comment still parses lines', () => {
246+
const raw = `diff --git a/f.ts b/f.ts
247+
@@ -10,2 +10,2 @@
248+
ctx
249+
-old
250+
+new`;
251+
const result = parseDiff(raw);
252+
expect(result[0].hunks).toHaveLength(1);
253+
expect(result[0].hunks[0].oldStart).toBe(10);
254+
expect(result[0].hunks[0].newStart).toBe(10);
255+
});
256+
257+
it('hunk header missing whole new range falls back to defaults', () => {
258+
// Malformed: should still bail out (no hunkMatch) — produces empty hunks
259+
const raw = `diff --git a/f.ts b/f.ts
260+
not a hunk
261+
+line`;
262+
const result = parseDiff(raw);
263+
expect(result[0].hunks).toEqual([]);
264+
});
265+
});
266+
267+
describe('parseLfsFiles — defensive fallbacks', () => {
268+
it('handles lines without the recognized delimiter (single token)', () => {
269+
const raw = 'just-a-single-line';
270+
const result = parseLfsFiles(raw);
271+
expect(result[0]).toEqual({ oid: 'just-a-single-line', path: '' });
272+
});
273+
});
274+
275+
describe('parseLfsLocks — defensive fallbacks', () => {
276+
it('handles lines missing some tab-separated fields', () => {
277+
const raw = 'path-only';
278+
const result = parseLfsLocks(raw);
279+
expect(result[0]).toEqual({ path: 'path-only', owner: '', id: '' });
280+
});
281+
282+
it('handles lines with just path and owner (no id)', () => {
283+
const raw = 'logo.png\talice';
284+
const result = parseLfsLocks(raw);
285+
expect(result[0]).toEqual({ path: 'logo.png', owner: 'alice', id: '' });
286+
});
287+
});
288+
175289
describe('parseDiff — git path unescaping edge cases', () => {
176290
it('decodes \\n, \\r, \\\\ and \\" escape forms in quoted paths', () => {
177291
const raw = [

0 commit comments

Comments
 (0)