import { describe, expect, it } from "vitest"; import { actionKeyForEvent, bindDesktopSlot, defaultUiLayout, desktopSlotForKey, exportUiPreferences, importUiPreferences, normalizeActionKey, normalizeUiPreferences, } from "./uiLayout"; describe("personal UI layouts", () => { it("migrates old accounts to independent Mobile and PC layouts with exactly six empty slots", () => { const ui = normalizeUiPreferences(undefined); expect(ui.slots.mobile).toEqual([null, null, null]); expect(ui.slots.pc).toEqual([null, null, null]); ui.current.pc.elements.party.visible = false; expect(ui.current.mobile.elements.party.visible).toBe(true); expect(ui.current.pc.bars).toHaveLength(5); expect(ui.current.pc.bars.every((bar) => bar.slots.length === 12)).toBe(true); }); it("round trips named slots, positions, hidden elements, geometry, assignments, and key chords", () => { const ui = normalizeUiPreferences(null); const layout = ui.current.pc; layout.wowBars = true; layout.barCount = 5; layout.elements.party = { visible: false, position: { x: .32, y: .45 }, width: 200 }; layout.bars[4].columns = 3; layout.bars[4].slots[11] = { abilityId: "test-heal", key: "Ctrl+Shift+KeyQ" }; ui.slots.pc[2] = { name: "Raid night", layout }; ui.slots.mobile[0] = { name: "Travel", layout: defaultUiLayout() }; expect(importUiPreferences(exportUiPreferences(ui))).toEqual(ui); }); it("reassigns conflicts even from hidden bars, accepts modifiers and clears a binding", () => { let layout = defaultUiLayout(); layout.wowBars = true; layout.barCount = 5; layout = bindDesktopSlot(layout, 4, 11, "Digit1"); expect(layout.bars[0].slots[0].key).toBeNull(); expect(desktopSlotForKey(layout, "Digit1")).toBe(layout.bars[4].slots[11]); layout.elements["bar-5"].visible = false; expect(desktopSlotForKey(layout, "Digit1")).not.toBeNull(); layout.barCount = 4; expect(desktopSlotForKey(layout, "Digit1")).toBeNull(); layout = bindDesktopSlot(layout, 4, 11, null); expect(layout.bars[4].slots[11].key).toBeNull(); expect(actionKeyForEvent({ code: "KeyQ", ctrlKey: true, shiftKey: true, altKey: false, metaKey: false })).toBe("Ctrl+Shift+KeyQ"); expect(normalizeActionKey("Escape")).toBe("Escape"); expect(normalizeActionKey("ShiftLeft")).toBeNull(); }); it.each([ ["unsupported versions", (data: any) => { data.version = 99; }], ["extra slots", (data: any) => { data.slots.mobile.push(null); }], ["invalid coordinates", (data: any) => { data.current.pc.elements.party.position = { x: "bad", y: 0 }; }], ["invalid bar geometry", (data: any) => { data.current.pc.bars[0].columns = 7; }], ["invalid keys", (data: any) => { data.current.pc.bars[0].slots[0].key = "NotAKey"; }], ["duplicate chords", (data: any) => { data.current.pc.bars[0].slots[1].key = "Digit1"; }], ["missing buttons", (data: any) => { data.current.pc.bars[0].slots.pop(); }], ])("rejects %s without mutating the original preferences", (_, mutate) => { const ui = normalizeUiPreferences(null); const original = exportUiPreferences(ui); const data = JSON.parse(original); mutate(data); expect(() => importUiPreferences(JSON.stringify(data))).toThrow(); expect(exportUiPreferences(ui)).toBe(original); }); it("rejects malformed and oversized imports", () => { expect(() => importUiPreferences("{")).toThrow("valid JSON"); expect(() => importUiPreferences(" ".repeat(256001))).toThrow("256 KB"); }); });