1b561c22d9
- Changes card beside lane detail with five modes: Files tree, overview, Review (whole diff, per-file collapse), single file, commit history. - LaneDiffView: unified / side-by-side with independent scroll on both axes and mirrored scrollLeft, expand/collapse context, intra-line emphasis. - Staged-only commit box; git actions (fetch/pull/push/stage/unstage/ discard/commit/switch/branch-create/branch-delete) guarded in git-ops.js. - Server: git-diff.js + git-ops.js behind GET /api/lanes/:id/git/* and POST /api/lanes/:id/git/:action; gitFacts gains upstream/ahead/behind. - Add lane: new/existing branch, current branch, worktree list, prefill from the git bar's "New lane here"; /api/run/cwds gains a repo kind. - GlobalTooltip upgrades every native title into a themed anchored bubble; chart/info popovers use theme tokens.
75 lines
2.8 KiB
TypeScript
75 lines
2.8 KiB
TypeScript
/**
|
|
* @file LaneDiffView.test.tsx
|
|
* @description Pins the side-by-side renderer: two independently scrolling
|
|
* columns (a long line must be reachable, not painted over its neighbour)
|
|
* whose horizontal scroll is mirrored, so the two halves of a line stay
|
|
* opposite each other.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { describe, it, expect } from "vitest";
|
|
import { fireEvent, render, screen } from "@testing-library/react";
|
|
import { LaneDiffView } from "../LaneDiffView";
|
|
import type { LaneChangeFile } from "../../../lib/types";
|
|
|
|
const FILE: LaneChangeFile = {
|
|
path: "server/lib/git-ops.js",
|
|
status: " M",
|
|
untracked: false,
|
|
added: 1,
|
|
deleted: 1,
|
|
binary: false,
|
|
hunks: [
|
|
{
|
|
header: "@@ -1,2 +1,2 @@",
|
|
context: "",
|
|
oldStart: 1,
|
|
newStart: 1,
|
|
lines: [
|
|
{ type: "del", oldLine: 1, newLine: null, text: `const x = "${"a".repeat(400)}";` },
|
|
{ type: "add", oldLine: null, newLine: 1, text: `const x = "${"b".repeat(400)}";` },
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
describe("LaneDiffView — side by side", () => {
|
|
it("gives each column its own scroller", () => {
|
|
render(<LaneDiffView file={FILE} layout="split" />);
|
|
expect(screen.getByTestId("lane-diff-split-left").className).toContain("overflow-auto");
|
|
expect(screen.getByTestId("lane-diff-split-right").className).toContain("overflow-auto");
|
|
});
|
|
|
|
it("bounds its own height so the horizontal bar stays on screen", () => {
|
|
// A stacked review list caps each file; the single-file view takes the
|
|
// height its parent gives it. Either way the diff scrolls itself rather
|
|
// than growing a 2000-line container whose scrollbar is off the bottom.
|
|
const { rerender } = render(<LaneDiffView file={FILE} layout="unified" />);
|
|
expect(screen.getByTestId("lane-diff-view").className).toContain("max-h-[60vh]");
|
|
|
|
rerender(<LaneDiffView file={FILE} layout="unified" fill />);
|
|
expect(screen.getByTestId("lane-diff-view").className).toContain("flex-1");
|
|
expect(screen.getByTestId("lane-diff-view").className).not.toContain("max-h-[60vh]");
|
|
});
|
|
|
|
it("mirrors one column's horizontal scroll onto the other", () => {
|
|
render(<LaneDiffView file={FILE} layout="split" />);
|
|
const left = screen.getByTestId("lane-diff-split-left");
|
|
const right = screen.getByTestId("lane-diff-split-right");
|
|
|
|
left.scrollLeft = 320;
|
|
fireEvent.scroll(left);
|
|
expect(right.scrollLeft).toBe(320);
|
|
|
|
right.scrollLeft = 0;
|
|
fireEvent.scroll(right);
|
|
expect(left.scrollLeft).toBe(0);
|
|
});
|
|
|
|
it("keeps the unified layout on one scroller", () => {
|
|
render(<LaneDiffView file={FILE} layout="unified" />);
|
|
expect(screen.queryByTestId("lane-diff-split-left")).toBeNull();
|
|
expect(screen.getByTestId("lane-diff-view").className).toContain("overflow-auto");
|
|
});
|
|
});
|