136 lines
4.7 KiB
TypeScript
136 lines
4.7 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import type { AdminDashboardDataSources } from "./admin-dashboard-personalization.server";
|
|
import {
|
|
loadPersonalizedAdminDashboard,
|
|
selectAdminDashboardWidgetIds,
|
|
} from "./admin-dashboard-personalization.server";
|
|
|
|
const sources = {
|
|
salesOwn: vi.fn(async () => [
|
|
{ id: "sales_today" as const, value: "2", href: "/sales/tasks/today" },
|
|
{ id: "sales_own_pipeline" as const, value: "10 €", href: "/sales/deals" },
|
|
{ id: "sales_appointments" as const, value: "1", href: "/sales/bookings" },
|
|
{ id: "sales_followups" as const, value: "3", href: "/sales/tasks/today" },
|
|
]),
|
|
salesTeam: vi.fn(async () => [
|
|
{ id: "sales_team_pipeline" as const, value: "20 €", href: "/sales/deals" },
|
|
{ id: "sales_unassigned_leads" as const, value: "4", href: "/leads" },
|
|
{ id: "sales_team_activity" as const, value: "5", href: "/sales/tasks/today" },
|
|
]),
|
|
support: vi.fn(async () => [
|
|
{ id: "support_inbox" as const, value: "6", href: "/support" },
|
|
{ id: "support_customer_events" as const, value: "7", href: "/audit" },
|
|
]),
|
|
engineering: vi.fn(async () => [
|
|
{ id: "engineering_health" as const, value: "OK", href: "/system" },
|
|
{ id: "engineering_queues" as const, value: "0", href: "/system/jobs" },
|
|
{ id: "engineering_errors" as const, value: "0", href: "/events" },
|
|
{ id: "engineering_deploy" as const, value: "abc123", href: "/system/deploys" },
|
|
]),
|
|
executive: vi.fn(async () => [
|
|
{ id: "executive_forecast" as const, value: "1 €", href: "/sales/deals" },
|
|
{ id: "executive_revenue" as const, value: "2 €", href: "/sales/deals" },
|
|
{ id: "executive_mrr" as const, value: "3 €", href: "/dashboard" },
|
|
{ id: "executive_arr" as const, value: "36 €", href: "/dashboard" },
|
|
{ id: "executive_churn" as const, value: "0 %", href: "/dashboard" },
|
|
]),
|
|
} satisfies AdminDashboardDataSources;
|
|
|
|
describe("admin dashboard personalization", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("unions combined role widgets without duplicates", () => {
|
|
const ids = selectAdminDashboardWidgetIds({
|
|
roleKeys: ["sales", "sales_manager", "sales"],
|
|
permissions: [
|
|
"admin_sales.crm.read",
|
|
"admin_sales.crm.read_all",
|
|
"admin_sales.booking.manage",
|
|
"admin_sales.reporting.read_all",
|
|
],
|
|
});
|
|
|
|
expect(ids).toEqual([
|
|
"sales_own_pipeline",
|
|
"sales_today",
|
|
"sales_appointments",
|
|
"sales_followups",
|
|
"sales_team_pipeline",
|
|
"sales_unassigned_leads",
|
|
"sales_team_activity",
|
|
]);
|
|
});
|
|
|
|
it("loads only the data sources selected by roles and effective permissions", async () => {
|
|
const vm = await loadPersonalizedAdminDashboard(
|
|
{
|
|
adminUserId: "admin_sales",
|
|
roleKeys: ["sales"],
|
|
permissions: [
|
|
"admin.dashboard.view",
|
|
"admin_sales.crm.read",
|
|
"admin_sales.booking.manage",
|
|
],
|
|
now: new Date("2026-07-26T08:00:00.000Z"),
|
|
locale: "en",
|
|
},
|
|
sources
|
|
);
|
|
|
|
expect(sources.salesOwn).toHaveBeenCalledOnce();
|
|
expect(sources.salesOwn).toHaveBeenCalledWith(
|
|
expect.objectContaining({ locale: "en" })
|
|
);
|
|
expect(sources.salesTeam).not.toHaveBeenCalled();
|
|
expect(sources.support).not.toHaveBeenCalled();
|
|
expect(sources.engineering).not.toHaveBeenCalled();
|
|
expect(sources.executive).not.toHaveBeenCalled();
|
|
expect(vm.widgets.map((entry) => entry.id)).toEqual([
|
|
"sales_own_pipeline",
|
|
"sales_today",
|
|
"sales_appointments",
|
|
"sales_followups",
|
|
]);
|
|
});
|
|
|
|
it("does not query a role source after its effective permission is removed", async () => {
|
|
const vm = await loadPersonalizedAdminDashboard(
|
|
{
|
|
adminUserId: "admin_support",
|
|
roleKeys: ["support"],
|
|
permissions: ["admin.dashboard.view"],
|
|
},
|
|
sources
|
|
);
|
|
|
|
expect(sources.support).not.toHaveBeenCalled();
|
|
expect(vm.widgets).toEqual([]);
|
|
});
|
|
|
|
it("loads combined role sources once each", async () => {
|
|
const vm = await loadPersonalizedAdminDashboard(
|
|
{
|
|
adminUserId: "admin_combined",
|
|
roleKeys: ["support", "engineering"],
|
|
permissions: [
|
|
"admin.dashboard.view",
|
|
"admin_identity.impersonation.start",
|
|
"admin.metrics.view",
|
|
"admin.events.view",
|
|
],
|
|
},
|
|
sources
|
|
);
|
|
|
|
expect(sources.support).toHaveBeenCalledOnce();
|
|
expect(sources.engineering).toHaveBeenCalledOnce();
|
|
expect(new Set(vm.widgets.map((entry) => entry.id)).size).toBe(
|
|
vm.widgets.length
|
|
);
|
|
expect(sources.salesOwn).not.toHaveBeenCalled();
|
|
expect(sources.executive).not.toHaveBeenCalled();
|
|
});
|
|
});
|