forked from phenom/healer-man
Release Healer Man 0.1.43
This commit is contained in:
1 parent
c7e8163790
commit
ccc01396cd
86 files changed
+2951
-198
No files matched your search
@@ -0,0 +1,113 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { createDefaultAppearance } from "./characterCatalog";
|
||||
import {
|
||||
createCharacter,
|
||||
listCharacters,
|
||||
updateCharacterProgression,
|
||||
type StorageLike,
|
||||
} from "./accountRepository";
|
||||
import { beginMainMenuCharacterEditing } from "./mainMenuCharacterEditing";
|
||||
import { selectedCharacter, useShellStore } from "./shellStore";
|
||||
import { useCombatStore } from "../game/combatStore";
|
||||
import { talentNodesForTree } from "../game/talentCatalog";
|
||||
|
||||
class MemoryStorage implements StorageLike {
|
||||
values = new Map<string, string>();
|
||||
getItem(key: string) { return this.values.get(key) ?? null; }
|
||||
setItem(key: string, value: string) { this.values.set(key, value); }
|
||||
removeItem(key: string) { this.values.delete(key); }
|
||||
}
|
||||
|
||||
describe("main-menu character editing", () => {
|
||||
let storage: MemoryStorage;
|
||||
let stop: (() => void) | null;
|
||||
const holyTalent = talentNodesForTree("priest-holy")[0];
|
||||
|
||||
beforeEach(() => {
|
||||
storage = new MemoryStorage();
|
||||
stop = null;
|
||||
const created = createCharacter({
|
||||
ownerId: "offline-roster", name: "Menuhealer", classId: "priest",
|
||||
raceId: "human", gender: "female", appearance: createDefaultAppearance(),
|
||||
specializationId: "priest:holy",
|
||||
}, storage).character!;
|
||||
const character = updateCharacterProgression(created.ownerId, created.id, {
|
||||
level: 17, experience: 123, talentRanks: {},
|
||||
}, storage)!;
|
||||
useShellStore.setState({
|
||||
phase: "main-menu", activeCharacter: null, selectedCharacterId: character.id,
|
||||
session: { kind: "offline", ownerId: character.ownerId, displayName: "Offline" },
|
||||
characters: [character], notice: "",
|
||||
});
|
||||
useCombatStore.getState().initializeCharacter({ classId: "mage", level: 1 });
|
||||
});
|
||||
afterEach(() => { stop?.(); });
|
||||
|
||||
it("loads the selected character without rewriting their saved profile or starting a dungeon", () => {
|
||||
const before = [...storage.values];
|
||||
stop = beginMainMenuCharacterEditing(storage);
|
||||
expect(stop).not.toBeNull();
|
||||
expect(useCombatStore.getState()).toMatchObject({ classId: "priest", level: 17, xp: 123 });
|
||||
expect([...storage.values]).toEqual(before);
|
||||
expect(useShellStore.getState()).toMatchObject({ phase: "main-menu", activeCharacter: null });
|
||||
});
|
||||
|
||||
it("persists talent allocation and reset, and reloads the saved build when reopened", () => {
|
||||
stop = beginMainMenuCharacterEditing(storage);
|
||||
expect(useCombatStore.getState().allocateTalent(holyTalent.id)).toBe(true);
|
||||
expect(listCharacters("offline-roster", storage)[0].talentRanks[holyTalent.id]).toBe(1);
|
||||
expect(selectedCharacter(useShellStore.getState())?.talentRanks[holyTalent.id]).toBe(1);
|
||||
stop!();
|
||||
stop = beginMainMenuCharacterEditing(storage);
|
||||
expect(useCombatStore.getState().talentRanks[holyTalent.id]).toBe(1);
|
||||
useCombatStore.getState().resetTalents();
|
||||
expect(listCharacters("offline-roster", storage)[0].talentRanks).toEqual({});
|
||||
expect(listCharacters("offline-roster", storage)[0]).toMatchObject({ level: 17, experience: 123 });
|
||||
});
|
||||
|
||||
it("persists controller assignments and reloads them without touching progression or equipment", () => {
|
||||
const profile = selectedCharacter(useShellStore.getState())!;
|
||||
stop = beginMainMenuCharacterEditing(storage);
|
||||
expect(useCombatStore.getState().setActionBinding("primary", "face-bottom", "priest-flash-heal")).toBe(true);
|
||||
const saved = listCharacters("offline-roster", storage)[0];
|
||||
expect(saved.actionBindings["primary:face-bottom"]).toBe("priest-flash-heal");
|
||||
expect(saved).toMatchObject({ level: profile.level, experience: profile.experience,
|
||||
talentRanks: profile.talentRanks, inventory: profile.inventory, equipment: profile.equipment });
|
||||
stop!();
|
||||
stop = beginMainMenuCharacterEditing(storage);
|
||||
expect(useCombatStore.getState().actionBindings["primary:face-bottom"]).toBe("priest-flash-heal");
|
||||
});
|
||||
|
||||
it.each(["phase", "owner", "selection", "closed"])("stops saving after the editing context changes: %s", (change) => {
|
||||
stop = beginMainMenuCharacterEditing(storage);
|
||||
const before = [...storage.values];
|
||||
if (change === "phase") useShellStore.setState({ phase: "game" });
|
||||
if (change === "owner") useShellStore.setState({ session: { kind: "offline", ownerId: "another-owner", displayName: "Other" } });
|
||||
if (change === "selection") useShellStore.setState({ selectedCharacterId: null });
|
||||
if (change === "closed") stop!();
|
||||
useCombatStore.getState().allocateTalent(holyTalent.id);
|
||||
useCombatStore.getState().setActionBinding("primary", "face-bottom", "priest-flash-heal");
|
||||
expect([...storage.values]).toEqual(before);
|
||||
});
|
||||
|
||||
it("never saves combat XP as a side effect of a talent edit", () => {
|
||||
stop = beginMainMenuCharacterEditing(storage);
|
||||
useCombatStore.setState({ xp: 999 });
|
||||
useCombatStore.getState().allocateTalent(holyTalent.id);
|
||||
expect(listCharacters("offline-roster", storage)[0].experience).toBe(123);
|
||||
});
|
||||
|
||||
it("rejects editing without a selected, owned main-menu character", () => {
|
||||
useShellStore.setState({ selectedCharacterId: null });
|
||||
expect(beginMainMenuCharacterEditing(storage)).toBeNull();
|
||||
expect(useCombatStore.getState().classId).toBe("mage");
|
||||
});
|
||||
|
||||
it("reports storage failures instead of updating the roster as though the save succeeded", () => {
|
||||
stop = beginMainMenuCharacterEditing(storage);
|
||||
storage.setItem = () => { throw new Error("Storage full"); };
|
||||
useCombatStore.getState().allocateTalent(holyTalent.id);
|
||||
expect(useShellStore.getState().notice).toMatch(/could not save/i);
|
||||
expect(selectedCharacter(useShellStore.getState())?.talentRanks).toEqual({});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user