Files
paseo/packages/app/src/git/diff-document/header-layout.test.ts
T
Mohamed Boudra 3998ea11d9 Keep large diffs responsive across web and mobile (#4174)
* perf(app): keep large diffs responsive

Render file headers on the canvas and bound expensive text shaping to the active viewport while preserving sticky, hover, and horizontal-scroll behavior. Let native scroll cancellation arbitrate header presses so finger scrolling cannot expand files.

* test(app): assert canvas diff header accessibility

* test(app): assert canvas diff header semantics
2026-09-01 23:52:11 +02:00

110 lines
3.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
diffHeaderViewportKey,
diffInteractionWindowTop,
resolveVisibleFileSections,
} from "./header-layout";
import type { DiffFileSection } from "./types";
describe("diff header viewport", () => {
it("keeps a many-file document bounded by the viewport", () => {
const files = Array.from({ length: 2_000 }, (_, index) => collapsedFile(index));
const window = resolveVisibleFileSections({
files,
scrollTop: 30_000,
viewportHeight: 600,
overscan: 600,
});
expect(window.files.length).toBeLessThanOrEqual(61);
expect(window.sticky?.file.fileIndex).toBe(1_000);
expect(window.sticky?.y).toBe(0);
});
it("pins an expanded file whose original header is outside overscan", () => {
const files = [expandedFile(0, 4_000), collapsedFile(1, 4_030)];
const window = resolveVisibleFileSections({
files,
scrollTop: 2_000,
viewportHeight: 600,
overscan: 600,
});
expect(window.files.map((file) => file.path)).toEqual(["file-0.ts"]);
expect(window.sticky).toEqual({ file: files[0], y: 0 });
});
it("pushes the pinned header out one pixel at a time", () => {
const files = [expandedFile(0, 100), collapsedFile(1, 130)];
expect(
resolveVisibleFileSections({ files, scrollTop: 129, viewportHeight: 100, overscan: 0 }).sticky
?.y,
).toBe(-29);
expect(
resolveVisibleFileSections({ files, scrollTop: 130, viewportHeight: 100, overscan: 0 }).sticky
?.y,
).toBe(0);
});
it("reuses a fixed sticky header until its visible layout changes", () => {
const files = [section(0, 0, 4_000, false), section(1, 4_000, 4_030, true)];
const keyAt = (scrollTop: number) =>
diffHeaderViewportKey({ files, scrollTop, viewportHeight: 600, activePath: null });
expect(keyAt(100)).toBe(keyAt(2_000));
expect(keyAt(3_985)).not.toBe(keyAt(2_000));
});
});
describe("diff interaction window", () => {
it("retains one shell window across two viewport heights", () => {
expect(diffInteractionWindowTop(0, 500)).toBe(0);
expect(diffInteractionWindowTop(999, 500)).toBe(0);
expect(diffInteractionWindowTop(1_000, 500)).toBe(1_000);
});
});
function collapsedFile(fileIndex: number, top = fileIndex * 30): DiffFileSection {
return section(fileIndex, top, top + 30, true);
}
function expandedFile(fileIndex: number, bodyHeight: number): DiffFileSection {
const top = fileIndex === 0 ? 0 : bodyHeight - 30;
return section(fileIndex, top, top + 30 + bodyHeight, false);
}
function section(
fileIndex: number,
top: number,
bottom: number,
isCollapsed: boolean,
): DiffFileSection {
const path = `file-${fileIndex}.ts`;
return {
file: {
path,
oldPath: undefined,
additions: 1,
deletions: 1,
isNew: false,
isDeleted: false,
hunks: [],
},
fileIndex,
path,
top,
headerHeight: 30,
bodyTop: top + 30,
bodyHeight: bottom - top - 30,
bottom,
gutterWidth: 30,
contentWidth: 300,
rowStart: 0,
rowEnd: 0,
isCollapsed,
};
}