200 lines
6.8 KiB
TypeScript
200 lines
6.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
applyTeamFields,
|
|
cloneTeamImports,
|
|
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("clones imported teams with new ids and independent members", () => {
|
|
const source = team("source");
|
|
const transfer = {
|
|
title: source.title,
|
|
rotationSeconds: source.rotationSeconds,
|
|
combo: source.combo,
|
|
members: source.members,
|
|
};
|
|
const imported = cloneTeamImports([transfer], () => "imported-1");
|
|
|
|
expect(imported[0]).toEqual({ ...source, clientId: "imported-1" });
|
|
expect(imported[0].members).not.toBe(source.members);
|
|
expect(imported[0].members[0]).not.toBe(source.members[0]);
|
|
});
|
|
|
|
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("ไม่พบตัวละคร");
|
|
});
|
|
});
|