Файли
openidea/src/features/vcs/__tests__/graph.test.ts
T
l0ng-ai a247f1ce67 feat(vcs): draw lane changes in the Git log graph as straight diagonals (#1406)
Lane changes bent within half a row as S-curves. A lane change and the vertical half of the neighbouring row that continues it now meet halfway between the lanes, so edges between adjacent rows run straight from node to node; only drawn positions change. Fixes #800
2026-11-05 09:12:41 +08:00

109 рядки
4.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { computeGraph, straightenLaneChanges } from "../graph";
const c = (id: string, ...parents: string[]) => ({ id, parents });
describe("computeGraph", () => {
it("keeps a linear history in lane 0", () => {
const { rows } = computeGraph([c("c", "b"), c("b", "a"), c("a")]);
expect(rows.map((r) => r.lane)).toEqual([0, 0, 0]);
expect(rows[0]!.up).toEqual([]);
expect(rows[0]!.down).toEqual([{ from: 0, to: 0, color: 0 }]);
expect(rows[2]!.down).toEqual([]);
expect(new Set(rows.map((r) => r.color)).size).toBe(1);
});
it("opens a lane for a merge's second parent and joins it back", () => {
// m merges f into main; f branched from a.
const { rows } = computeGraph([c("m", "b", "f"), c("f", "a"), c("b", "a"), c("a")]);
const [m, f, b, a] = rows;
expect(m!.lane).toBe(0);
expect(m!.down).toEqual([
{ from: 0, to: 0, color: m!.color },
{ from: 0, to: 1, color: f!.color },
]);
expect(f!.lane).toBe(1);
expect(f!.color).not.toBe(m!.color);
// b passes lane 1 (f's parent a) through while sitting in lane 0.
expect(b!.lane).toBe(0);
expect(b!.down).toContainEqual({ from: 1, to: 1, color: f!.color });
// a is expected by both lanes: both lines converge into its node.
expect(a!.lane).toBe(0);
expect(a!.up).toEqual([
{ from: 0, to: 0, color: m!.color },
{ from: 1, to: 0, color: f!.color },
]);
expect(a!.width).toBe(2);
});
it("reuses freed lanes and continues across pages", () => {
const first = computeGraph([c("t1", "x"), c("t2", "y")]);
expect(first.rows.map((r) => r.lane)).toEqual([0, 1]);
const next = computeGraph([c("x"), c("y"), c("z")], first.state);
expect(next.rows.map((r) => r.lane)).toEqual([0, 1, 0]);
expect(next.state.lanes).toEqual([]);
});
it("converges lanes that lead to the same parent at its node", () => {
const { rows } = computeGraph([c("t1", "p"), c("t2", "p"), c("p")]);
expect(rows[1]!.lane).toBe(1);
expect(rows[1]!.down).toContainEqual({ from: 1, to: 1, color: rows[1]!.color });
expect(rows[2]!.lane).toBe(0);
expect(rows[2]!.up).toEqual([
{ from: 0, to: 0, color: rows[0]!.color },
{ from: 1, to: 0, color: rows[1]!.color },
]);
});
it("joins a merge parent that is already on another lane", () => {
// m2 merges x, which lane 1 (from t) already leads to.
const { rows } = computeGraph([c("t", "x"), c("m2", "b", "x"), c("x", "b"), c("b")]);
const m2 = rows[1]!;
expect(m2.down).toContainEqual({ from: m2.lane, to: 0, color: rows[0]!.color });
});
});
describe("straightenLaneChanges", () => {
it("draws a lane change as one diagonal across the row boundary, keeping the lanes", () => {
// m merges f into main; f branched from a; b sits in lane 0 while lane 1 passes it.
const rows = straightenLaneChanges(computeGraph([c("m", "b", "f"), c("f", "a"), c("b", "a"), c("a")]).rows);
const [m, f, b, a] = rows;
// m → f: leaves m's node towards lane 1 and reaches f's node in a straight line.
expect(m!.down[1]).toMatchObject({ from: 0, to: 1, toX: 0.5 });
expect(f!.up.find((s) => s.from === 1)).toMatchObject({ from: 1, to: 1, fromX: 0.5 });
// Lane 1 converging into a: the vertical line of b's row meets it halfway.
expect(a!.up[1]).toMatchObject({ from: 1, to: 0, fromX: 0.5 });
expect(b!.down.find((s) => s.from === 1)).toMatchObject({ from: 1, to: 1, toX: 0.5 });
// Straight lines stay where they are.
expect(m!.down[0]!.toX).toBeUndefined();
expect(a!.up[0]!.fromX).toBeUndefined();
});
it("leaves a lane that another line also ends in, and arrows, as they are", () => {
const rows = straightenLaneChanges([
{
lane: 0,
color: 0,
up: [],
down: [
{ from: 0, to: 1, color: 0 },
{ from: 1, to: 1, color: 1 },
],
width: 2,
},
{
lane: 1,
color: 1,
up: [{ from: 1, to: 1, color: 1 }],
down: [{ from: 1, to: 2, color: 1, arrow: "down" }],
width: 3,
},
{ lane: 2, color: 1, up: [{ from: 2, to: 2, color: 1 }], down: [], width: 3 },
]);
expect(rows[0]!.down[0]!.toX).toBeUndefined();
expect(rows[1]!.up[0]!.fromX).toBeUndefined();
expect(rows[1]!.down[0]!.toX).toBeUndefined();
expect(rows[2]!.up[0]!.fromX).toBeUndefined();
});
});