import type { FileDiffMetadata } from "@pierre/diffs"; import { describe, expect, it } from "vite-plus/test"; import { isFileDiffCollapsed, isLineInFileDiff } from "./pullRequestDiff.logic"; /** Only the hunk ranges matter here; the viewer fills the rest in when it renders. */ function fileWithHunks( hunks: ReadonlyArray<{ deletionStart: number; deletionCount: number; additionStart: number; additionCount: number; }>, ): FileDiffMetadata { return { name: "src/app.ts", hunks } as unknown as FileDiffMetadata; } describe("isLineInFileDiff", () => { const file = fileWithHunks([ { deletionStart: 10, deletionCount: 3, additionStart: 10, additionCount: 5 }, { deletionStart: 40, deletionCount: 0, additionStart: 42, additionCount: 2 }, ]); it("places a line inside a hunk, on the side that hunk counts", () => { expect(isLineInFileDiff(file, "right", 12)).toBe(true); expect(isLineInFileDiff(file, "left", 11)).toBe(true); }); it("includes the first line of a hunk and excludes the one past its last", () => { // The boundaries are where an off-by-one would quietly move a conversation between lists. expect(isLineInFileDiff(file, "right", 10)).toBe(true); expect(isLineInFileDiff(file, "right", 14)).toBe(true); expect(isLineInFileDiff(file, "right", 15)).toBe(false); expect(isLineInFileDiff(file, "left", 9)).toBe(false); expect(isLineInFileDiff(file, "left", 12)).toBe(true); expect(isLineInFileDiff(file, "left", 13)).toBe(false); }); it("keeps the two sides apart, since one line number means two lines", () => { // The second hunk is a pure insertion: it deletes nothing, so nothing is on its left. expect(isLineInFileDiff(file, "right", 43)).toBe(true); expect(isLineInFileDiff(file, "left", 40)).toBe(false); }); it("places nothing in a file whose hunks the host withheld", () => { expect(isLineInFileDiff(fileWithHunks([]), "right", 1)).toBe(false); }); }); describe("isFileDiffCollapsed", () => { const NO_TOGGLES: ReadonlySet = new Set(); it("folds every file before the reader has touched anything", () => { expect(isFileDiffCollapsed("a.ts", null, NO_TOGGLES)).toBe(true); expect(isFileDiffCollapsed("b.ts", null, NO_TOGGLES)).toBe(true); }); it("opens every file once the toolbar has asked for it", () => { // Pressing the toolbar clears the reader's own toggles, which is why the set is empty here. expect(isFileDiffCollapsed("a.ts", "expanded", NO_TOGGLES)).toBe(false); expect(isFileDiffCollapsed("b.ts", "expanded", NO_TOGGLES)).toBe(false); }); it("folds every file again on the second press", () => { expect(isFileDiffCollapsed("a.ts", "folded", NO_TOGGLES)).toBe(true); expect(isFileDiffCollapsed("b.ts", "folded", NO_TOGGLES)).toBe(true); }); it("keeps a file the reader opened open as the next slice arrives", () => { // The file keys grow with every slice, so the answer for one already open must not depend on // how many of them there are by then. const toggled = new Set(["b.ts"]); expect(isFileDiffCollapsed("b.ts", null, toggled)).toBe(false); expect(isFileDiffCollapsed("c.ts", null, toggled)).toBe(true); }); it("still answers to a toggle after either toolbar press", () => { expect(isFileDiffCollapsed("a.ts", "expanded", new Set(["a.ts"]))).toBe(true); expect(isFileDiffCollapsed("a.ts", "folded", new Set(["a.ts"]))).toBe(false); }); });