Files

153 lines
4.3 KiB
TypeScript

import type { DiffContent } from "@bindings/diff/DiffContent";
import type { FileDiff } from "@bindings/diff/FileDiff";
import { beforeEach, describe, expect, test, vi } from "vitest";
import {
checkedFileProgress,
checkedPaths,
fileDiffPath,
fileMetaOf,
fullFileLoader,
gitStatusEntries,
} from "@app/lib/diffText";
const { invoke } = vi.hoisted(() => ({ invoke: vi.fn() }));
vi.mock("@app/lib/invoke", () => ({ invoke }));
const plain: DiffContent = {
type: "plain",
stats: { additions: 1, deletions: 0 },
};
const added: FileDiff = { status: "added", path: "new.ts", diff: plain };
const deleted: FileDiff = { status: "deleted", path: "old.ts", diff: plain };
const modified: FileDiff = { status: "modified", path: "a.ts", diff: plain };
const moved: FileDiff = {
status: "moved",
oldPath: "from.ts",
newPath: "to.ts",
diff: plain,
};
const copied: FileDiff = {
status: "copied",
oldPath: "orig.ts",
newPath: "copy.ts",
diff: plain,
};
test("fileDiffPath names renames and copies by their new path", () => {
expect([added, deleted, modified, moved, copied].map(fileDiffPath)).toEqual([
"new.ts",
"old.ts",
"a.ts",
"to.ts",
"copy.ts",
]);
});
test("gitStatusEntries marks a rename as renamed and a copy as added", () => {
expect(gitStatusEntries([added, deleted, modified, moved, copied])).toEqual([
{ path: "new.ts", status: "added" },
{ path: "old.ts", status: "deleted" },
{ path: "a.ts", status: "modified" },
{ path: "to.ts", status: "renamed" },
{ path: "copy.ts", status: "added" },
]);
});
test("fileMetaOf notes unrenderable files and lockfiles", () => {
const meta = fileMetaOf([
moved,
{ status: "added", path: "logo.png", diff: { type: "binary" } },
{ status: "added", path: "empty.txt", diff: { type: "empty" } },
{ status: "modified", path: "web/package-lock.json", diff: plain },
]);
expect(Object.fromEntries(meta.statuses)).toEqual({
"to.ts": "moved",
"logo.png": "added",
"empty.txt": "added",
"web/package-lock.json": "modified",
});
expect(Object.fromEntries(meta.notes)).toEqual({
"logo.png": "binary",
"empty.txt": "empty",
});
expect([...meta.ignored]).toEqual(["web/package-lock.json"]);
});
test("checkedFileProgress ignores checks on files the diff no longer has", () => {
expect(
checkedFileProgress([modified, moved], ["a.ts", "from.ts", "gone.ts"]),
).toEqual({ filesChecked: 1, filesTotal: 2 });
});
test("checkedPaths names checked files by their diff path", () => {
expect([...checkedPaths([modified, moved], path => path !== "a.ts")]).toEqual(
["to.ts"],
);
});
describe("fullFileLoader", () => {
beforeEach(() => {
invoke.mockReset();
invoke.mockImplementation(
(_cmd, { path, sha }: { path: string; sha: string }) =>
Promise.resolve(
path.endsWith(".png")
? { binary: true, content: "\u0089PNG" }
: { binary: false, content: `${path}@${sha}` },
),
);
});
const load = (files: FileDiff[]) =>
fullFileLoader("rad:z", "base", "head", () => files);
test("reads a rename's old side from its old path", async () => {
expect(await load([moved])("to.ts")).toEqual({
oldContents: "from.ts@base",
newContents: "to.ts@head",
});
});
test("reads only the side a file has", async () => {
expect(await load([added])("new.ts")).toEqual({
oldContents: "",
newContents: "new.ts@head",
});
expect(await load([deleted])("old.ts")).toEqual({
oldContents: "old.ts@base",
newContents: "",
});
expect(invoke).toHaveBeenCalledTimes(2);
});
test("has no old side without a base", async () => {
const rootCommit = fullFileLoader("rad:z", undefined, "head", () => [
modified,
]);
expect(await rootCommit("a.ts")).toEqual({
oldContents: "",
newContents: "a.ts@head",
});
});
test("gives binary blobs and unknown paths no contents", async () => {
const binary: FileDiff = {
status: "modified",
path: "logo.png",
diff: { type: "binary" },
};
expect(await load([binary])("logo.png")).toEqual({
oldContents: "",
newContents: "",
});
expect(await load([modified])("missing.ts")).toEqual({
oldContents: "",
newContents: "",
});
});
});