import { describe, expect, it } from "vitest"; import { applyTeamFields, findInvalidTeamCatalogReference, findUnsupportedTeamWeapon, limitPresets, parseTeamTransfer, parseTeamPresets, serializeTeamTransfer, teamTransferFileName, type TeamToolValue, } from "./team-tools"; const team = (id: string, title = id): TeamToolValue => ({ clientId: id, title, rotationSeconds: "20", combo: "E > Q", members: [1, 2, 3, 4].map((n) => ({ characterKey: `${id}-${n}`, weaponKey: null, artifactKey: null, secondArtifactKey: null, constellation: 0, refinement: 1, damage: String(n) })), }); describe("team bulk tools", () => { it("copies selected fields without changing titles or mutating source", () => { const source = team("source", "Source"); const target = team("target", "Keep title"); const result = applyTeamFields([source, target], "source", new Set(["target"]), ["loadout"]); expect(result[1].title).toBe("Keep title"); expect(result[1].members.map((member) => member.characterKey)).toEqual(["source-1", "source-2", "source-3", "source-4"]); expect(result[1].members.map((member) => member.damage)).toEqual(["1", "2", "3", "4"]); expect(source.members[0].characterKey).toBe("source-1"); }); it("does not apply to the source team", () => { const source = team("source"); expect(applyTeamFields([source], "source", new Set(["source"]), ["combo"])).toEqual([source]); }); it("copies one field for one character slot only", () => { const source = team("source"); const target = team("target"); source.members[0] = { ...source.members[0], characterKey: "mavuika", refinement: 1 }; target.members[0] = { ...target.members[0], characterKey: "keep-character", refinement: 5 }; const result = applyTeamFields( [source, target], "source", new Set(["target"]), ["member:0:refinement"], ); expect(result[1].members[0].refinement).toBe(1); expect(result[1].members[0].characterKey).toBe("keep-character"); expect(result[1].members.slice(1)).toEqual(target.members.slice(1)); }); it("applies fields to the matching character in a different slot", () => { const source = team("source"); const target = team("target"); source.members[1] = { ...source.members[1], characterKey: "nicole", weaponKey: "source-weapon", }; target.members[3] = { ...target.members[3], characterKey: "nicole", weaponKey: "old-weapon", }; const result = applyTeamFields( [source, target], "source", new Set(["target"]), ["member:1:weaponKey"], ); expect(result[1].members[3].weaponKey).toBe("source-weapon"); expect(result[1].members[1]).toEqual(target.members[1]); }); it("falls back to the same slot when the character is not present", () => { const source = team("source"); const target = team("target"); source.members[1] = { ...source.members[1], characterKey: "nicole", refinement: 5, }; const result = applyTeamFields( [source, target], "source", new Set(["target"]), ["member:1:refinement"], ); expect(result[1].members[1].refinement).toBe(5); }); it("detects an unsupported character and weapon after bulk apply", () => { const target = team("target"); target.members[0] = { ...target.members[0], characterKey: "varka", weaponKey: "wolf-fang", }; expect( findUnsupportedTeamWeapon( [target], new Set(["target"]), [{ key: "varka", name: "Varka", weaponType: "WEAPON_CLAYMORE" }], [{ key: "wolf-fang", name: "Wolf-Fang", weaponType: "WEAPON_SWORD_ONE_HAND" }], ), ).toEqual({ characterName: "Varka", weaponName: "Wolf-Fang" }); }); it("deduplicates and limits presets", () => { const presets = Array.from({ length: 7 }, (_, index) => ({ id: String(index), name: String(index), createdAt: String(index), team: { ...team(String(index)), clientId: undefined as never } })); expect(limitPresets([presets[0], presets[0], ...presets.slice(1)])).toHaveLength(5); }); it("rejects malformed preset storage", () => { expect(parseTeamPresets("not-json")).toEqual([]); expect(parseTeamPresets(JSON.stringify([{ id: 1 }]))).toEqual([]); }); it("round-trips every exported team field without the client id", () => { const source = team("source", "Ineffa Exai"); source.rotationSeconds = "21.5"; source.members[0] = { ...source.members[0], weaponKey: "weapon-1", artifactKey: "artifact-1", secondArtifactKey: "artifact-2", constellation: 2, refinement: 4, damage: "123456.78", }; expect(parseTeamTransfer(serializeTeamTransfer(source))).toEqual({ title: source.title, rotationSeconds: source.rotationSeconds, combo: source.combo, members: source.members, }); }); it("rejects malformed and unsupported team transfers", () => { expect(() => parseTeamTransfer("not-json")).toThrow(); expect(() => parseTeamTransfer(JSON.stringify({ type: "buzz-sheet.team", version: 2, team: team("source"), }))).toThrow(); }); it("uses the team name as a safe JSON download filename", () => { expect(teamTransferFileName("Ineffa Exai")).toBe("Ineffa Exai.json"); expect(teamTransferFileName('A/B: Team?')).toBe("A-B- Team-.json"); expect(teamTransferFileName("... ")).toBe("team.json"); }); it("reports missing and incompatible imported catalog references", () => { const source = team("source"); source.members[0] = { ...source.members[0], characterKey: "varka", weaponKey: "wolf-fang", }; const imported = { ...source, clientId: undefined as never }; const characters = [ { key: "varka", name: "Varka", weaponType: "WEAPON_CLAYMORE" }, ...source.members.slice(1).map((member) => ({ key: member.characterKey, name: member.characterKey })), ]; expect(findInvalidTeamCatalogReference( imported, characters, [{ key: "wolf-fang", name: "Wolf-Fang", weaponType: "WEAPON_SWORD_ONE_HAND" }], [], )).toBe("Varka ไม่รองรับอาวุธ Wolf-Fang"); expect(findInvalidTeamCatalogReference(imported, [], [], [])).toContain("ไม่พบตัวละคร"); }); });