1
0
Fork 0
polaris-task-force/tests/int/tech-tree-prereq-edits.int.spec.ts
Z8MB1E 5f7fd508a5 test(tech-tree): add integration and e2e coverage
- `tech-tree-layout.int.spec.ts`: pure layout (wrapping, manual overrides,
  category bands, forward-edge warnings, cycles, catalog-stability).
- `tech-tree-prereq-edits.int.spec.ts`: add/remove/switch prerequisites with
  cycle rejection and the pin-on-connect stability contract.
- `tech-tree-stability.int.spec.ts`: freeze pins every relocated card on a
  data change, plus edge visuals (per-path colors, per-source lanes).
- `tech-categories.int.spec.ts`: CRUD, RBAC, division gating, FK set-null,
  position round-trip.
- `tech-tree.e2e.spec.ts`: nav entry, render, preview/panel edits, both
  quick-create paths, drag persistence + Auto-arrange, save positions
  surviving a reload, connect/disconnect, and multi-column wrapping.
2026-10-01 10:04:07 -04:00

110 lines
3.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
addPrerequisite,
buildPrereqRows,
dependsOn,
removePrerequisite,
setPrerequisiteKind,
withPrereqs,
} from "@/components/admin/tech-tree/prereqEdits";
import type { TreeTech } from "@/components/admin/tech-tree/types";
const tech = (over: Partial<TreeTech> & { id: number }): TreeTech => ({
name: `Tech ${over.id}`,
type: "upgrade",
approvalStatus: "in_progress",
summary: "",
categoryId: null,
prereqAnd: [],
prereqOr: [],
resourceCosts: [],
treePosition: null,
...over,
});
describe("tech tree prerequisite edits (pure)", () => {
it("adds an AND prerequisite", () => {
const techs = [tech({ id: 1 }), tech({ id: 2 })];
const result = addPrerequisite(techs, 2, 1, "and");
expect(result.ok).toBe(true);
if (result.ok) expect(result.lists).toEqual({ and: [1], or: [] });
});
it("adds an OR prerequisite", () => {
const techs = [tech({ id: 1 }), tech({ id: 2 })];
const result = addPrerequisite(techs, 2, 1, "or");
expect(result.ok).toBe(true);
if (result.ok) expect(result.lists).toEqual({ and: [], or: [1] });
});
it("rejects self references", () => {
const result = addPrerequisite([tech({ id: 1 })], 1, 1, "and");
expect(result.ok).toBe(false);
if (!result.ok) expect(result.error).toMatch(/own prerequisite/i);
});
it("rejects duplicates", () => {
const techs = [tech({ id: 1 }), tech({ id: 2, prereqAnd: [1] })];
const result = addPrerequisite(techs, 2, 1, "or");
expect(result.ok).toBe(false);
if (!result.ok) expect(result.error).toMatch(/already a prerequisite/i);
});
it("rejects a direct cycle", () => {
const techs = [tech({ id: 1, prereqAnd: [2] }), tech({ id: 2 })];
// Adding 1 as a prerequisite of 2 would close 2 -> 1 -> 2.
const result = addPrerequisite(techs, 2, 1, "and");
expect(result.ok).toBe(false);
if (!result.ok) expect(result.error).toMatch(/circular/i);
});
it("rejects a transitive cycle", () => {
const techs = [
tech({ id: 1 }),
tech({ id: 2, prereqAnd: [1] }),
tech({ id: 3, prereqAnd: [2] }),
];
// 3 depends on 1 transitively; making 3 a prerequisite of 1 closes a loop.
expect(dependsOn(techs, 3, 1)).toBe(true);
const result = addPrerequisite(techs, 1, 3, "and");
expect(result.ok).toBe(false);
});
it("allows a forward chain", () => {
const techs = [tech({ id: 1 }), tech({ id: 2, prereqAnd: [1] }), tech({ id: 3 })];
const result = addPrerequisite(techs, 3, 2, "and");
expect(result.ok).toBe(true);
if (result.ok) expect(result.lists).toEqual({ and: [2], or: [] });
});
it("removes a prerequisite from both groups", () => {
const techs = [tech({ id: 1 }), tech({ id: 2, prereqAnd: [1], prereqOr: [1, 3] })];
const result = removePrerequisite(techs, 2, 1);
expect(result.ok).toBe(true);
if (result.ok) expect(result.lists).toEqual({ and: [], or: [3] });
});
it("moves a prerequisite between AND and OR", () => {
const techs = [tech({ id: 1 }), tech({ id: 2, prereqAnd: [1] })];
const toOr = setPrerequisiteKind(techs, 2, 1, "or");
expect(toOr.ok).toBe(true);
if (toOr.ok) expect(toOr.lists).toEqual({ and: [], or: [1] });
const back = setPrerequisiteKind(techs, 2, 1, "and");
expect(back.ok).toBe(true);
if (back.ok) expect(back.lists).toEqual({ and: [1], or: [] });
});
it("builds a single document-shaped prerequisite row", () => {
expect(buildPrereqRows({ and: [1, 2], or: [3] })).toEqual([
{ requiresAnd: { technology: [1, 2] }, requiresOr: { technology: [3] } },
]);
});
it("withPrereqs returns a tech carrying the new lists", () => {
const updated = withPrereqs(tech({ id: 1 }), { and: [4], or: [5] });
expect(updated.prereqAnd).toEqual([4]);
expect(updated.prereqOr).toEqual([5]);
});
});