feat : update
This commit is contained in:
@@ -35,6 +35,13 @@ describe("structured guide validation", () => {
|
||||
expect(artifactSectionSchema.safeParse({ ...base, talents: [{ type: "NA", priority: 1, level: "11" }] }).success).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts talent comparison relations while keeping legacy talents valid", () => {
|
||||
const base = { expectedVersion: 1, groups: artifactGroups, sands: [], goblet: [], circlet: [], substats: [], targets: [], note: null };
|
||||
expect(artifactSectionSchema.safeParse({ ...base, talents: [{ type: "E", priority: 1, relationToNext: "gte" }] }).success).toBe(true);
|
||||
expect(artifactSectionSchema.safeParse({ ...base, talents: [{ type: "E", priority: 1 }] }).success).toBe(true);
|
||||
expect(artifactSectionSchema.safeParse({ ...base, talents: [{ type: "E", priority: 1, relationToNext: "equal" }] }).success).toBe(false);
|
||||
});
|
||||
|
||||
it("requires four unique team members and non-zero damage", () => {
|
||||
const member = (characterKey: string) => ({ characterKey, weaponKey: null, artifactKey: null, constellation: 0, refinement: 1, damage: 100 });
|
||||
const base = { expectedVersion: 1, teams: [{ title: "Test", rotationSeconds: 20, combo: "E > Q", members: ["1", "2", "3", "4"].map(member) }] };
|
||||
|
||||
@@ -38,6 +38,7 @@ export const mainStatOptions = {
|
||||
|
||||
export const substatOptions = ["CRIT Rate%/CRIT DMG%", "HP%", "ATK%", "DEF%", "EM", "ER"] as const;
|
||||
export const talentLevelOptions = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] as const;
|
||||
export const talentRelationOptions = ["gt", "gte"] as const;
|
||||
|
||||
const artifactGroupSchema = (name: "Late Game" | "Early Game") => z.strictObject({
|
||||
name: z.literal(name),
|
||||
@@ -66,6 +67,7 @@ export const artifactSectionSchema = z.strictObject({
|
||||
type: z.enum(["NA", "E", "Q"]),
|
||||
priority: z.number().int().min(1).max(3),
|
||||
level: z.enum(talentLevelOptions).optional(),
|
||||
relationToNext: z.enum(talentRelationOptions).optional(),
|
||||
})).max(3),
|
||||
note: z.string().trim().max(2_000).nullable(),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { normalizeTalentPriorities, reorderTalentPriorities, talentRelationSymbol } from "@/lib/guides/talents";
|
||||
|
||||
describe("talent priorities", () => {
|
||||
it("normalizes legacy talents to strict comparisons", () => {
|
||||
expect(normalizeTalentPriorities([
|
||||
{ type: "Q", priority: 2 },
|
||||
{ type: "E", priority: 1 },
|
||||
{ type: "NA", priority: 3 },
|
||||
])).toEqual([
|
||||
{ type: "E", priority: 1, relationToNext: "gt" },
|
||||
{ type: "Q", priority: 2, relationToNext: "gt" },
|
||||
{ type: "NA", priority: 3, relationToNext: undefined },
|
||||
]);
|
||||
});
|
||||
|
||||
it("reorders talents while preserving relations between positions", () => {
|
||||
expect(reorderTalentPriorities([
|
||||
{ type: "E", priority: 1, relationToNext: "gte" },
|
||||
{ type: "Q", priority: 2, relationToNext: "gt" },
|
||||
{ type: "NA", priority: 3 },
|
||||
], "NA", "E")).toEqual([
|
||||
{ type: "NA", priority: 1, relationToNext: "gte" },
|
||||
{ type: "E", priority: 2, relationToNext: "gt" },
|
||||
{ type: "Q", priority: 3, relationToNext: undefined },
|
||||
]);
|
||||
});
|
||||
|
||||
it("formats strict and greater-than-or-equal relations", () => {
|
||||
expect(talentRelationSymbol("gt")).toBe(">");
|
||||
expect(talentRelationSymbol("gte")).toBe("≥");
|
||||
expect(talentRelationSymbol()).toBe(">");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
export type TalentRelation = "gt" | "gte";
|
||||
|
||||
export interface TalentPriority {
|
||||
type: "NA" | "E" | "Q";
|
||||
priority: number;
|
||||
level?: string;
|
||||
relationToNext?: TalentRelation;
|
||||
}
|
||||
|
||||
export function normalizeTalentPriorities(talents: readonly TalentPriority[]): TalentPriority[] {
|
||||
const ordered = [...talents].sort((a, b) => a.priority - b.priority);
|
||||
return ordered.map((talent, index) => ({
|
||||
...talent,
|
||||
priority: index + 1,
|
||||
relationToNext: index < ordered.length - 1 ? talent.relationToNext ?? "gt" : undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
export function reorderTalentPriorities(
|
||||
talents: readonly TalentPriority[],
|
||||
activeType: TalentPriority["type"],
|
||||
overType: TalentPriority["type"],
|
||||
): TalentPriority[] {
|
||||
const ordered = normalizeTalentPriorities(talents);
|
||||
const from = ordered.findIndex((talent) => talent.type === activeType);
|
||||
const to = ordered.findIndex((talent) => talent.type === overType);
|
||||
if (from < 0 || to < 0 || from === to) return ordered;
|
||||
|
||||
const relations = ordered.slice(0, -1).map((talent) => talent.relationToNext ?? "gt");
|
||||
const next = [...ordered];
|
||||
const [moved] = next.splice(from, 1);
|
||||
next.splice(to, 0, moved);
|
||||
|
||||
return next.map((talent, index) => ({
|
||||
...talent,
|
||||
priority: index + 1,
|
||||
relationToNext: index < next.length - 1 ? relations[index] : undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
export function talentRelationSymbol(relation?: TalentRelation): ">" | "≥" {
|
||||
return relation === "gte" ? "≥" : ">";
|
||||
}
|
||||
Reference in New Issue
Block a user