Skip to content

Commit b72992c

Browse files
authored
Expand App edge interaction coverage (#23)
1 parent 8085b6f commit b72992c

4 files changed

Lines changed: 133 additions & 3 deletions

File tree

src/ui/App.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { buildFileListEntry } from "./lib/files";
1313
import { buildHunkCursors, findNextHunkCursor } from "./lib/hunks";
1414
import { diffHunkId, diffSectionId, fileRowId } from "./lib/ids";
1515
import { resolveResponsiveLayout } from "./lib/responsive";
16+
import { resizeSidebarWidth } from "./lib/sidebar";
1617
import { resolveTheme, THEMES } from "./themes";
1718
import { HunkHostClient } from "../mcp/client";
1819
import type { LiveComment, SessionServerMessage } from "../mcp/types";
@@ -376,8 +377,9 @@ export function App({
376377
return;
377378
}
378379

379-
const nextWidth = resizeStartWidth + (event.x - resizeDragOriginX);
380-
setFilesPaneWidth(clamp(nextWidth, FILES_MIN_WIDTH, maxFilesPaneWidth));
380+
setFilesPaneWidth(
381+
resizeSidebarWidth(resizeStartWidth, resizeDragOriginX, event.x, FILES_MIN_WIDTH, maxFilesPaneWidth),
382+
);
381383
event.preventDefault();
382384
event.stopPropagation();
383385
};

src/ui/lib/sidebar.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/** Clamp a dragged sidebar width into the shell's allowed range. */
2+
export function resizeSidebarWidth(
3+
startWidth: number,
4+
dragOriginX: number,
5+
currentX: number,
6+
minWidth: number,
7+
maxWidth: number,
8+
) {
9+
const nextWidth = startWidth + (currentX - dragOriginX);
10+
return Math.min(Math.max(nextWidth, minWidth), maxWidth);
11+
}

test/app-interactions.test.tsx

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ function createFileScrollBootstrap(): AppBootstrap {
151151
};
152152
}
153153

154-
155154
async function flush(setup: Awaited<ReturnType<typeof testRender>>) {
156155
await act(async () => {
157156
await setup.renderOnce();
@@ -413,6 +412,117 @@ describe("App interactions", () => {
413412
}
414413
});
415414

415+
test("filtering away the selected file reselects the first visible match", async () => {
416+
const setup = await testRender(<App bootstrap={createBootstrap()} />, { width: 240, height: 24 });
417+
418+
try {
419+
await flush(setup);
420+
421+
await act(async () => {
422+
await setup.mockInput.pressTab();
423+
});
424+
await flush(setup);
425+
await act(async () => {
426+
await setup.mockInput.typeText("beta");
427+
});
428+
await flush(setup);
429+
430+
let frame = setup.captureCharFrame();
431+
expect(frame).toContain("filter:");
432+
expect(frame).toContain("beta");
433+
expect(frame).toContain("M beta.ts");
434+
expect(frame).not.toContain("M alpha.ts");
435+
expect(frame).toContain("beta.ts");
436+
expect(frame).not.toContain("Annotation for alpha.ts");
437+
438+
await act(async () => {
439+
await setup.mockInput.pressTab();
440+
});
441+
await flush(setup);
442+
443+
frame = setup.captureCharFrame();
444+
expect(frame).toContain("filter=beta");
445+
expect(frame).toContain("beta.ts");
446+
} finally {
447+
await act(async () => {
448+
setup.renderer.destroy();
449+
});
450+
}
451+
});
452+
453+
test("menu navigation wraps across the first and last top-level menus", async () => {
454+
const setup = await testRender(<App bootstrap={createBootstrap()} />, { width: 220, height: 24 });
455+
456+
try {
457+
await flush(setup);
458+
459+
await act(async () => {
460+
setup.mockInput.pressKey("F10");
461+
});
462+
await flush(setup);
463+
464+
let frame = setup.captureCharFrame();
465+
expect(frame).toContain("Focus files");
466+
expect(frame).not.toContain("Keyboard help");
467+
468+
await act(async () => {
469+
await setup.mockInput.pressArrow("left");
470+
});
471+
await flush(setup);
472+
473+
frame = setup.captureCharFrame();
474+
expect(frame).toContain("Keyboard help");
475+
expect(frame).not.toContain("Focus files");
476+
477+
await act(async () => {
478+
await setup.mockInput.pressArrow("right");
479+
});
480+
await flush(setup);
481+
482+
frame = setup.captureCharFrame();
483+
expect(frame).toContain("Focus files");
484+
expect(frame).not.toContain("Keyboard help");
485+
} finally {
486+
await act(async () => {
487+
setup.renderer.destroy();
488+
});
489+
}
490+
});
491+
492+
test("sidebar visibility can toggle off and back on", async () => {
493+
const setup = await testRender(<App bootstrap={createBootstrap()} />, { width: 240, height: 24 });
494+
495+
try {
496+
await flush(setup);
497+
498+
let frame = setup.captureCharFrame();
499+
expect(frame).toContain("M alpha.ts");
500+
501+
await act(async () => {
502+
await setup.mockInput.typeText("s");
503+
});
504+
await flush(setup);
505+
506+
frame = setup.captureCharFrame();
507+
expect(frame).not.toContain("M alpha.ts");
508+
expect(frame).toContain("alpha.ts");
509+
expect(frame).not.toContain("drag divider resize");
510+
511+
await act(async () => {
512+
await setup.mockInput.typeText("s");
513+
});
514+
await flush(setup);
515+
516+
frame = setup.captureCharFrame();
517+
expect(frame).toContain("M alpha.ts");
518+
expect(frame).toContain("drag divider resize");
519+
} finally {
520+
await act(async () => {
521+
setup.renderer.destroy();
522+
});
523+
}
524+
});
525+
416526
test("quit shortcuts route through the provided onQuit handler in regular and pager modes", async () => {
417527
const regularQuit = mock(() => undefined);
418528
const regularSetup = await testRender(<App bootstrap={createBootstrap()} onQuit={regularQuit} />, { width: 220, height: 24 });

test/ui-lib.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { DiffFile } from "../src/core/types";
44
import { buildMenuSpecs, menuBoxHeight, menuWidth, nextMenuItemIndex, type MenuEntry } from "../src/ui/components/chrome/menu";
55
import { fitText, padText } from "../src/ui/lib/text";
66
import { estimateDiffBodyRows } from "../src/ui/lib/sectionHeights";
7+
import { resizeSidebarWidth } from "../src/ui/lib/sidebar";
78
import { resolveTheme } from "../src/ui/themes";
89

910
function createDiffFile(): DiffFile {
@@ -75,6 +76,12 @@ describe("ui helpers", () => {
7576
expect(padText("ok", 4)).toBe("ok ");
7677
});
7778

79+
test("resizeSidebarWidth clamps drag updates into the allowed sidebar range", () => {
80+
expect(resizeSidebarWidth(34, 33, 60, 22, 80)).toBe(61);
81+
expect(resizeSidebarWidth(34, 33, 0, 22, 80)).toBe(22);
82+
expect(resizeSidebarWidth(34, 33, 120, 22, 80)).toBe(80);
83+
});
84+
7885
test("estimateDiffBodyRows matches split and stack row counts for hidden-context diffs", async () => {
7986
const file = createDiffFile();
8087

0 commit comments

Comments
 (0)