380ddaf303
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
122 lines
3.7 KiB
TypeScript
122 lines
3.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import {
|
||
ancestorIds,
|
||
buildTree,
|
||
checkCreate,
|
||
checkMove,
|
||
descendantIds,
|
||
rollupTotals,
|
||
toOptions,
|
||
type FlatCategory,
|
||
} from "./tree";
|
||
|
||
const c = (
|
||
id: string,
|
||
name: string,
|
||
parentId: string | null,
|
||
order = 0,
|
||
kind: "expense" | "income" = "expense",
|
||
): FlatCategory => ({
|
||
id,
|
||
kind,
|
||
name,
|
||
parentId,
|
||
order,
|
||
archived: false,
|
||
});
|
||
|
||
const items: FlatCategory[] = [
|
||
c("fat", "Faturalar", null, 1),
|
||
c("elk", "Elektrik", "fat", 0),
|
||
c("su", "Su", "fat", 1),
|
||
c("mar", "Market", null, 0),
|
||
c("gida", "Gıda", "mar", 0),
|
||
c("meyve", "Meyve", "gida", 0),
|
||
c("maas", "Maaş", null, 0, "income"),
|
||
];
|
||
|
||
describe("buildTree / toOptions", () => {
|
||
it("sıra ve derinlikle ağaç kurar, yol etiketleri üretir", () => {
|
||
const tree = buildTree(items.filter((i) => i.kind === "expense"));
|
||
expect(tree.map((n) => n.name)).toEqual(["Market", "Faturalar"]);
|
||
expect(toOptions(tree).map((o) => [o.path, o.depth])).toEqual([
|
||
["Market", 0],
|
||
["Market › Gıda", 1],
|
||
["Market › Gıda › Meyve", 2],
|
||
["Faturalar", 0],
|
||
["Faturalar › Elektrik", 1],
|
||
["Faturalar › Su", 1],
|
||
]);
|
||
});
|
||
|
||
it("arşivlenenleri varsayılan olarak seçeneklere koymaz", () => {
|
||
const tree = buildTree([c("a", "A", null), { ...c("b", "B", "a"), archived: true }]);
|
||
expect(toOptions(tree).map((o) => o.id)).toEqual(["a"]);
|
||
expect(toOptions(tree, { includeArchived: true }).map((o) => o.id)).toEqual(["a", "b"]);
|
||
});
|
||
|
||
it("üstü olmayan düğümü köke alır, döngüde takılmaz", () => {
|
||
const loop = [c("x", "X", "y"), c("y", "Y", "x"), c("z", "Z", "yok")];
|
||
expect(() => buildTree(loop)).not.toThrow();
|
||
expect(buildTree(loop).map((n) => n.id)).toContain("z");
|
||
});
|
||
});
|
||
|
||
describe("ancestorIds / descendantIds", () => {
|
||
it("üst ve alt kategorileri bulur", () => {
|
||
expect(ancestorIds(items, "meyve")).toEqual(["mar", "gida"]);
|
||
expect(descendantIds(items, "mar").sort()).toEqual(["gida", "meyve"]);
|
||
expect(descendantIds(items, "elk")).toEqual([]);
|
||
});
|
||
});
|
||
|
||
describe("checkMove", () => {
|
||
it("kendine, alt kategorisine ve farklı türe taşımayı reddeder", () => {
|
||
expect(checkMove(items, "mar", "mar")).toBe("self");
|
||
expect(checkMove(items, "mar", "meyve")).toBe("descendant");
|
||
expect(checkMove(items, "elk", "maas")).toBe("kind");
|
||
expect(checkMove(items, "elk", "yok")).toBe("parentNotFound");
|
||
});
|
||
|
||
it("geçerli taşımaya ve köke taşımaya izin verir", () => {
|
||
expect(checkMove(items, "su", "mar")).toBeNull();
|
||
expect(checkMove(items, "gida", null)).toBeNull();
|
||
});
|
||
|
||
it("derinlik sınırını uygular (en fazla 5 seviye: derinlik 0–4)", () => {
|
||
const deep = [
|
||
c("1", "1", null),
|
||
c("2", "2", "1"),
|
||
c("3", "3", "2"),
|
||
c("4", "4", "3"),
|
||
c("5", "5", "4"),
|
||
c("x", "X", null),
|
||
c("y", "Y", "x"),
|
||
];
|
||
expect(checkCreate(deep, "expense", "4")).toBeNull(); // yeni düğüm derinlik 4
|
||
expect(checkCreate(deep, "expense", "5")).toBe("depth"); // derinlik 5
|
||
expect(checkMove(deep, "y", "4")).toBeNull();
|
||
expect(checkMove(deep, "y", "5")).toBe("depth");
|
||
expect(checkMove(deep, "x", "4")).toBe("depth"); // x derinlik 4, alt kategorisi y derinlik 5
|
||
expect(checkCreate(deep, "income", "3")).toBe("kind");
|
||
});
|
||
});
|
||
|
||
describe("rollupTotals", () => {
|
||
it("alt kategori tutarlarını üst kategorilere toplar", () => {
|
||
const totals = rollupTotals(
|
||
items,
|
||
new Map([
|
||
["meyve", 300],
|
||
["gida", 500],
|
||
["elk", 1000],
|
||
["su", 250],
|
||
]),
|
||
);
|
||
expect(totals.get("mar")).toBe(800);
|
||
expect(totals.get("gida")).toBe(800);
|
||
expect(totals.get("fat")).toBe(1250);
|
||
expect(totals.get("maas") ?? 0).toBe(0);
|
||
});
|
||
});
|