Files
pitchfast/tests/dashboard-model.test.ts
2026-06-05 16:47:22 +02:00

233 lines
6.4 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import {
getLeadBlacklistStatusLabel,
getLeadContactStatusLabel,
getLeadDuplicateStatusLabel,
getLeadPriorityLabel,
leadBlacklistStatusOptions,
leadContactStatusOptions,
leadDuplicateStatusOptions,
leadPriorityOptions,
dashboardKpis,
dashboardNavigation,
groupLeadFunnelCards,
leadFunnelStages,
pipelineStages,
toLeadFunnelCard,
reviewQueue,
} from "../lib/dashboard-model";
type NavigationItem = { label: string; href: string };
type PipelineStage = { title: string; count: number };
type ReviewQueueItem = { title: string };
test("dashboardNavigation contains the expected sidebar routes in order", () => {
assert.deepEqual(
dashboardNavigation.map((item: NavigationItem) => [item.label, item.href]),
[
["Übersicht", "/dashboard"],
["Kampagnen", "/dashboard/campaigns"],
["Leads", "/dashboard/leads"],
["Audits", "/dashboard/audits"],
["Review", "/dashboard/outreach"],
["Analytics", "/dashboard/analytics"],
["Sperrliste", "/dashboard/blacklist"],
["Einstellungen", "/dashboard/settings"],
],
);
});
test("pipelineStages keep the first-screen workflow focused on pipeline overview", () => {
assert.deepEqual(
pipelineStages.map((stage: PipelineStage) => stage.title),
["Kampagnen", "Lead-Recherche", "Audit-Freigabe", "Outreach"],
);
assert.equal(
pipelineStages.every((stage: PipelineStage) => stage.count >= 0),
true,
);
});
test("leadFunnelStages expose the agreed German funnel columns", () => {
assert.deepEqual(
leadFunnelStages.map((stage) => stage.title),
[
"Kontakt fehlt",
"Audit bereit",
"Freigabe offen",
"Kontaktiert",
"Follow-up",
"Zurückgestellt",
],
);
});
test("toLeadFunnelCard exposes scan data and derives missing contact next action", () => {
const card = toLeadFunnelCard({
id: "lead-1",
companyName: "Malerbetrieb Klein",
niche: "Maler",
city: "Freiburg",
postalCode: "79098",
priority: "high",
contactStatus: "missing_contact",
blacklistStatus: "clear",
});
assert.equal(card.stageId, "missing_contact");
assert.equal(card.company, "Malerbetrieb Klein");
assert.equal(card.niche, "Maler");
assert.equal(card.location, "79098 Freiburg");
assert.equal(card.priorityLabel, "Hoch");
assert.equal(card.contactStatusLabel, "Kontakt fehlt");
assert.equal(card.nextAction, "Kontaktquelle recherchieren");
});
test("groupLeadFunnelCards derives review, follow-up, and deferred columns without schema migration", () => {
const groups = groupLeadFunnelCards([
{
id: "lead-review",
companyName: "Physio am Park",
city: "Freiburg",
priority: "medium",
contactStatus: "outreach_ready",
blacklistStatus: "clear",
outreach: {
approvalStatus: "draft",
sendStatus: "not_sent",
responseStatus: "none",
salesStatus: "follow_up_planned",
},
},
{
id: "lead-follow-up",
companyName: "Tischlerei Weber",
city: "Emmendingen",
priority: "medium",
contactStatus: "contacted",
blacklistStatus: "clear",
outreach: {
approvalStatus: "approved",
sendStatus: "sent",
responseStatus: "follow_up_needed",
salesStatus: "follow_up_planned",
},
},
{
id: "lead-replied",
companyName: "Salon Licht",
city: "Freiburg",
priority: "low",
contactStatus: "replied",
blacklistStatus: "clear",
},
{
id: "lead-defer",
companyName: "Cafe Morgen",
city: "Basel",
priority: "defer",
contactStatus: "new",
blacklistStatus: "clear",
},
]);
assert.deepEqual(
groups.map((group) => [group.stage.id, group.cards.map((card) => card.id)]),
[
["missing_contact", []],
["audit_ready", []],
["review_open", ["lead-review"]],
["contacted", ["lead-replied"]],
["follow_up", ["lead-follow-up"]],
["deferred", ["lead-defer"]],
],
);
});
test("groupLeadFunnelCards keeps approved unsent outreach in the review-open funnel", () => {
const groups = groupLeadFunnelCards([
{
id: "lead-approved-unsent",
companyName: "Optik Meyer",
city: "Freiburg",
priority: "medium",
contactStatus: "new",
blacklistStatus: "clear",
outreach: {
approvalStatus: "approved",
sendStatus: "not_sent",
responseStatus: "none",
salesStatus: "follow_up_planned",
},
},
]);
assert.deepEqual(
groups.map((group) => [group.stage.id, group.cards.map((card) => card.id)]),
[
["missing_contact", []],
["audit_ready", []],
["review_open", ["lead-approved-unsent"]],
["contacted", []],
["follow_up", []],
["deferred", []],
],
);
});
test("toLeadFunnelCard maps blocked priority to deferred stage with blocker label", () => {
const card = toLeadFunnelCard({
id: "lead-blocked",
companyName: "Sperr Beispiel",
city: "Freiburg",
priority: "blocked",
contactStatus: "new",
blacklistStatus: "blocked",
});
assert.equal(card.stageId, "deferred");
assert.equal(card.priorityLabel, "Gesperrt");
assert.equal(card.nextAction, "Zurückstellung prüfen");
});
test("dashboard-model exposes stable lead label helpers for UI mapping", () => {
assert.deepEqual(leadPriorityOptions, [
"high",
"medium",
"low",
"defer",
"blocked",
]);
assert.equal(getLeadPriorityLabel("high"), "Hoch");
assert.equal(getLeadContactStatusLabel("missing_contact"), "Kontakt fehlt");
assert.equal(getLeadBlacklistStatusLabel("blocked"), "Gesperrt");
});
test("dashboard-model exposes duplicate status options and labels", () => {
assert.deepEqual(leadDuplicateStatusOptions, [
"unchecked",
"unique",
"possible_duplicate",
"duplicate",
]);
assert.equal(getLeadDuplicateStatusLabel("duplicate"), "Duplikat");
});
test("dashboard-model exposes contact status options for lead review controls", () => {
assert.equal(leadContactStatusOptions[1], "missing_contact");
assert.equal(leadBlacklistStatusOptions.length, 2);
});
test("dashboardKpis and reviewQueue expose the above-the-fold dashboard summary", () => {
assert.equal(dashboardKpis.length, 4);
assert.equal(reviewQueue.length, 3);
assert.equal(
reviewQueue.some((item: ReviewQueueItem) =>
item.title.includes("Audit-Freigabe"),
),
true,
);
});