Files
paseo/packages/app/src/git/diff-order.test.ts
T
Christoph Leiter 2755596b9f fix(app): align the scrolling diff order with the Changes tree (#3913)
The Changes view listed the same files in two different orders. The
tree rail put directories before files at each level of the path.
The scrolling diff compared the full path byte by byte. A root file
such as .gitlab-ci.yml therefore opened the diff, but sat below every
directory in the tree. Scrolling the diff did not walk the tree from
top to bottom.

Two sorts caused this. sortTree in git/diff-tree.ts sorted the tree.
compareCheckoutDiffPaths in git/diff-order.ts sorted the flat list at
ingest.

The tree is now the only ordering authority.
orderCheckoutDiffFiles builds the tree and reads the file sequence
back out of it. compareCheckoutDiffPaths is deleted, so the two
surfaces can no longer disagree.

The daemon keeps its own whole-path sort, so daemons that are not
updated also get this fix. Commit diffs keep the order that git
gives them, because that panel shows no tree rail.
2026-08-27 08:00:25 +00:00

72 lines
2.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { orderCheckoutDiffFiles } from "./diff-order";
function createFile(path: string, additions = 0) {
return {
path,
isNew: false,
isDeleted: false,
additions,
deletions: 0,
hunks: [],
};
}
function order(paths: string[]): string[] {
return orderCheckoutDiffFiles(paths.map((path) => createFile(path))).map((file) => file.path);
}
describe("checkout diff ordering", () => {
it("sorts sibling files by name", () => {
expect(order(["zeta.ts", "alpha.ts", "beta.ts"])).toEqual(["alpha.ts", "beta.ts", "zeta.ts"]);
});
it("puts a directory before a file that sits beside it", () => {
expect(order(["z.ts", "a/b.ts"])).toEqual(["a/b.ts", "z.ts"]);
});
it("keeps loose root files below root directories", () => {
// The reported bug: comparing whole paths byte-for-byte put ".gitlab-ci.yml"
// first (because "." < "b"), while the tree rail listed it last.
expect(
order([
".gitlab-ci.yml",
"process-compose.yaml",
"bin/lib/node-stamp.sh",
"samera-one/package.json",
]),
).toEqual([
"bin/lib/node-stamp.sh",
"samera-one/package.json",
".gitlab-ci.yml",
"process-compose.yaml",
]);
});
it("ranks a nested directory above a shallower file with the same prefix", () => {
// "src/a.ts" < "src/a/z.ts" as whole strings, but "a" is a directory here.
expect(order(["src/a.ts", "src/a/z.ts"])).toEqual(["src/a/z.ts", "src/a.ts"]);
});
it("orders a directory ahead of a file sharing its name", () => {
// Converting an extensionless file into a directory puts both in one diff.
expect(order(["bin/stack", "bin/stack/run"])).toEqual(["bin/stack/run", "bin/stack"]);
});
it("returns lists too short to reorder untouched", () => {
const single = [createFile("only.ts")];
expect(orderCheckoutDiffFiles([])).toEqual([]);
expect(orderCheckoutDiffFiles(single)).toBe(single);
});
it("preserves relative order for equal paths", () => {
const ordered = orderCheckoutDiffFiles([
createFile("same.ts", 1),
createFile("same.ts", 2),
createFile("same.ts", 3),
]);
expect(ordered.map((file) => file.additions)).toEqual([1, 2, 3]);
});
});