ghostguild-org/tests/client/composables/useMemberStatus.test.js
Jennie Robinson Faber 59d6e97787
Some checks failed
Test / vitest (push) Failing after 7m23s
Test / playwright (push) Has been skipped
Test / visual (push) Has been skipped
Test / Notify on failure (push) Successful in 2s
Member/Ecology revamp.
2026-04-14 09:25:09 +01:00

235 lines
7.6 KiB
JavaScript

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();
});
});
});