Files
cozsweet-frontend-nextjs/src/app/profile/__tests__/profile-view-model.test.ts
T
admin 9deb320cf6 feat(profile)!: replace sidebar and add avatar navigation
Rename the global Sidebar route, UI, assets, analytics, and payment return context to Profile. Add accessible message-avatar navigation and preserve the source character across auth and logout flows.

BREAKING CHANGE: /sidebar has been removed; use /profile instead.
2026-07-21 18:08:12 +08:00

114 lines
2.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { UserView } from "@/stores/user/user-view";
import {
getProfileViewModel,
getProfileWalletView,
normalizeProfileCount,
} from "../profile-view-model";
const baseUser: UserView = {
id: "user-1",
username: "Chase",
countryCode: "US",
creditBalance: 120,
dailyFreeChatLimit: 30,
dailyFreeChatRemaining: 24,
dailyFreePrivateLimit: 4,
dailyFreePrivateRemaining: 2,
isVip: false,
};
describe("getProfileViewModel", () => {
it("derives guest state for logged-out users", () => {
const view = getProfileViewModel({
loginStatus: "notLoggedIn",
user: {
currentUser: null,
avatarUrl: null,
creditBalance: 0,
},
});
expect(view.state).toBe("guest");
expect(view.canActivateVip).toBe(true);
expect(view.canShowSettings).toBe(false);
});
it("derives member state and shows VIP activation for non-VIP users", () => {
const view = getProfileViewModel({
loginStatus: "email",
user: {
currentUser: baseUser,
avatarUrl: "https://example.com/avatar.png",
creditBalance: 120,
},
});
expect(view.state).toBe("member");
expect(view.name).toBe("Chase");
expect(view.avatarUrl).toBe("https://example.com/avatar.png");
expect(view.canActivateVip).toBe(true);
expect(view.canShowSettings).toBe(true);
});
it("derives VIP state and hides VIP activation for VIP users", () => {
const view = getProfileViewModel({
loginStatus: "email",
user: {
currentUser: { ...baseUser, isVip: true },
avatarUrl: null,
creditBalance: 120,
},
});
expect(view.state).toBe("vip");
expect(view.canActivateVip).toBe(false);
});
it("keeps Guest users in the signed-out profile state", () => {
const view = getProfileViewModel({
loginStatus: "guest",
user: {
currentUser: null,
avatarUrl: null,
creditBalance: null,
},
});
expect(view.state).toBe("guest");
expect(view.name).toBe("User name");
expect(view.isInitializingUser).toBe(false);
});
});
describe("getProfileWalletView", () => {
it("normalizes missing, negative, and fractional wallet values", () => {
const view = getProfileWalletView({
currentUser: {
...baseUser,
dailyFreeChatLimit: -1,
dailyFreeChatRemaining: 12.9,
dailyFreePrivateLimit: Number.NaN,
dailyFreePrivateRemaining: 3.7,
},
avatarUrl: null,
creditBalance: -42.8,
});
expect(view).toEqual({
creditBalance: 0,
dailyFreeChatLimit: 0,
dailyFreeChatRemaining: 12,
dailyFreePrivateLimit: 0,
dailyFreePrivateRemaining: 3,
});
});
it("normalizes undefined and null counts to zero", () => {
expect(normalizeProfileCount(undefined)).toBe(0);
expect(normalizeProfileCount(null)).toBe(0);
});
});