import { describe, it, expect, vi, beforeEach } from "vitest"; import { ref, computed } from "vue"; import { MEMBER_STATUSES, MEMBER_STATUS_CONFIG, useMemberStatus, } from "../../../app/composables/useMemberStatus.js"; // Stub Vue's computed as a global (Nuxt auto-import) vi.stubGlobal("computed", computed); // Shared reactive ref for controlling member status in tests const memberData = ref({ status: "active" }); vi.stubGlobal("useAuth", () => ({ memberData })); describe("MEMBER_STATUSES", () => { it("has all four status keys", () => { expect(Object.keys(MEMBER_STATUSES)).toEqual([ "PENDING_PAYMENT", "ACTIVE", "SUSPENDED", "CANCELLED", ]); }); it("maps to expected string values", () => { expect(MEMBER_STATUSES.PENDING_PAYMENT).toBe("pending_payment"); expect(MEMBER_STATUSES.ACTIVE).toBe("active"); expect(MEMBER_STATUSES.SUSPENDED).toBe("suspended"); expect(MEMBER_STATUSES.CANCELLED).toBe("cancelled"); }); }); describe("MEMBER_STATUS_CONFIG", () => { const requiredFields = [ "label", "color", "canRSVP", "canAccessMembers", "canPeerSupport", ]; it("has config for every status value", () => { for (const status of Object.values(MEMBER_STATUSES)) { expect(MEMBER_STATUS_CONFIG).toHaveProperty(status); } }); it("each config has required fields", () => { for (const [key, config] of Object.entries(MEMBER_STATUS_CONFIG)) { for (const field of requiredFields) { expect(config, `${key} missing ${field}`).toHaveProperty(field); } } }); it("active has full permissions", () => { const cfg = MEMBER_STATUS_CONFIG.active; expect(cfg.canRSVP).toBe(true); expect(cfg.canAccessMembers).toBe(true); expect(cfg.canPeerSupport).toBe(true); }); it("pending_payment can access members but not RSVP or peer support", () => { const cfg = MEMBER_STATUS_CONFIG.pending_payment; expect(cfg.canRSVP).toBe(false); expect(cfg.canAccessMembers).toBe(true); expect(cfg.canPeerSupport).toBe(false); }); it("suspended has all permissions false", () => { const cfg = MEMBER_STATUS_CONFIG.suspended; expect(cfg.canRSVP).toBe(false); expect(cfg.canAccessMembers).toBe(false); expect(cfg.canPeerSupport).toBe(false); }); it("cancelled has all permissions false", () => { const cfg = MEMBER_STATUS_CONFIG.cancelled; expect(cfg.canRSVP).toBe(false); expect(cfg.canAccessMembers).toBe(false); expect(cfg.canPeerSupport).toBe(false); }); }); describe("useMemberStatus composable", () => { beforeEach(() => { memberData.value = { status: "active" }; }); describe("status detection", () => { it("defaults to pending_payment when memberData has no status", () => { memberData.value = {}; const { status } = useMemberStatus(); expect(status.value).toBe("pending_payment"); }); it("defaults to pending_payment when memberData is null", () => { memberData.value = null; const { status } = useMemberStatus(); expect(status.value).toBe("pending_payment"); }); it("isActive is true when status is active", () => { memberData.value = { status: "active" }; const { isActive } = useMemberStatus(); expect(isActive.value).toBe(true); }); it("isActive is false when status is not active", () => { memberData.value = { status: "suspended" }; const { isActive, isInactive } = useMemberStatus(); expect(isActive.value).toBe(false); expect(isInactive.value).toBe(true); }); }); describe("permissions", () => { it("canRSVP is true when active", () => { memberData.value = { status: "active" }; const { canRSVP } = useMemberStatus(); expect(canRSVP.value).toBe(true); }); it("canRSVP is false when pending_payment", () => { memberData.value = { status: "pending_payment" }; const { canRSVP } = useMemberStatus(); expect(canRSVP.value).toBe(false); }); it("canAccessMembers is true for active and pending_payment", () => { for (const status of ["active", "pending_payment"]) { memberData.value = { status }; const { canAccessMembers } = useMemberStatus(); expect(canAccessMembers.value, `expected true for ${status}`).toBe( true, ); } }); it("canAccessMembers is false for suspended and cancelled", () => { for (const status of ["suspended", "cancelled"]) { memberData.value = { status }; const { canAccessMembers } = useMemberStatus(); expect(canAccessMembers.value, `expected false for ${status}`).toBe( false, ); } }); }); describe("getNextAction", () => { it("returns Complete Payment for pending_payment", () => { memberData.value = { status: "pending_payment" }; const { getNextAction } = useMemberStatus(); const action = getNextAction(); expect(action.label).toBe("Complete Payment"); expect(action.link).toBe("/member/account"); }); it("returns Reactivate Membership for cancelled", () => { memberData.value = { status: "cancelled" }; const { getNextAction } = useMemberStatus(); const action = getNextAction(); expect(action.label).toBe("Reactivate Membership"); expect(action.link).toBe("/member/account"); }); it("returns Contact Support for suspended", () => { memberData.value = { status: "suspended" }; const { getNextAction } = useMemberStatus(); const action = getNextAction(); expect(action.label).toBe("Contact Support"); expect(action.link).toBe("mailto:support@ghostguild.org"); }); it("returns null for active", () => { memberData.value = { status: "active" }; const { getNextAction } = useMemberStatus(); expect(getNextAction()).toBeNull(); }); }); describe("getBannerMessage", () => { it("returns payment message for pending_payment", () => { memberData.value = { status: "pending_payment" }; const { getBannerMessage } = useMemberStatus(); expect(getBannerMessage()).toContain("pending payment"); }); it("returns suspended message for suspended", () => { memberData.value = { status: "suspended" }; const { getBannerMessage } = useMemberStatus(); expect(getBannerMessage()).toContain("suspended"); }); it("returns cancelled message for cancelled", () => { memberData.value = { status: "cancelled" }; const { getBannerMessage } = useMemberStatus(); expect(getBannerMessage()).toContain("cancelled"); }); it("returns null for active", () => { memberData.value = { status: "active" }; const { getBannerMessage } = useMemberStatus(); expect(getBannerMessage()).toBeNull(); }); }); describe("getRSVPMessage", () => { it("returns payment message for pending_payment", () => { memberData.value = { status: "pending_payment" }; const { getRSVPMessage } = useMemberStatus(); expect(getRSVPMessage()).toContain("payment"); }); it("returns restriction message for suspended", () => { memberData.value = { status: "suspended" }; const { getRSVPMessage } = useMemberStatus(); expect(getRSVPMessage()).toContain("reactivate"); }); it("returns restriction message for cancelled", () => { memberData.value = { status: "cancelled" }; const { getRSVPMessage } = useMemberStatus(); expect(getRSVPMessage()).toContain("reactivate"); }); it("returns null for active", () => { memberData.value = { status: "active" }; const { getRSVPMessage } = useMemberStatus(); expect(getRSVPMessage()).toBeNull(); }); }); });