import { describe, expect, it } from "vitest"; import { getFirstIncompleteWizardStep, getPortfolioWizardProgress, isPortfolioAssetReady, isPortfolioSectionReady, } from "@/lib/portfolio-form-progress"; const titles = { titleAr: "ع", titleEn: "en", titleDe: "de" }; function section(overrides: Record = {}) { return { type: "RICH_TEXT" as const, ...titles, bodyAr: "ب", bodyEn: "body", bodyDe: "koerper", linkUrl: "", mediaAssetId: "", ...overrides, }; } describe("isPortfolioSectionReady", () => { it("requires titles in all languages", () => { expect(isPortfolioSectionReady(section({ titleEn: "" }))).toBe(false); }); it("RICH_TEXT/STATS/DELIVERABLES require body in all languages", () => { for (const type of ["RICH_TEXT", "STATS", "DELIVERABLES"] as const) { expect(isPortfolioSectionReady(section({ type }))).toBe(true); expect(isPortfolioSectionReady(section({ type, bodyDe: "" }))).toBe(false); } }); it("GALLERY requires a media asset id", () => { expect(isPortfolioSectionReady(section({ type: "GALLERY", mediaAssetId: "asset_1" }))).toBe(true); expect(isPortfolioSectionReady(section({ type: "GALLERY", mediaAssetId: "" }))).toBe(false); }); it("LINK requires a link url", () => { expect(isPortfolioSectionReady(section({ type: "LINK", linkUrl: "https://x" }))).toBe(true); expect(isPortfolioSectionReady(section({ type: "LINK", linkUrl: "" }))).toBe(false); }); }); describe("isPortfolioAssetReady", () => { it("requires media asset id and alt text in all languages", () => { expect(isPortfolioAssetReady({ mediaAssetId: "a", altAr: "ع", altEn: "e", altDe: "d" })).toBe(true); expect(isPortfolioAssetReady({ mediaAssetId: "", altAr: "ع", altEn: "e", altDe: "d" })).toBe(false); expect(isPortfolioAssetReady({ mediaAssetId: "a", altAr: "ع", altEn: "", altDe: "d" })).toBe(false); }); }); describe("getPortfolioWizardProgress", () => { const completeInput = { basics: { categoryId: "cat_1", slug: "case-study", clientName: "Client", projectYear: "2025", sortOrder: "1", viewMode: "GRID" as const, }, content: { titleAr: "ع", titleEn: "t", titleDe: "t", serviceLabelAr: "خ", serviceLabelEn: "s", serviceLabelDe: "s", summaryAr: "م", summaryEn: "sum", summaryDe: "zus", }, sections: [section()], assets: [{ mediaAssetId: "asset_1", altAr: "ع", altEn: "e", altDe: "d" }], }; it("marks all steps complete for a fully filled project", () => { const progress = getPortfolioWizardProgress(completeInput); expect(progress.every((step) => step.complete)).toBe(true); expect(getFirstIncompleteWizardStep(progress)).toBeNull(); }); it("rejects an invalid slug", () => { const progress = getPortfolioWizardProgress({ ...completeInput, basics: { ...completeInput.basics, slug: "Invalid Slug" }, }); expect(progress.find((s) => s.key === "basics")?.complete).toBe(false); expect(getFirstIncompleteWizardStep(progress)).toBe("basics"); }); it("enforces year bounds 2000..2100", () => { const before = getPortfolioWizardProgress({ ...completeInput, basics: { ...completeInput.basics, projectYear: "1999" }, }); expect(before.find((s) => s.key === "basics")?.complete).toBe(false); const after = getPortfolioWizardProgress({ ...completeInput, basics: { ...completeInput.basics, projectYear: "2101" }, }); expect(after.find((s) => s.key === "basics")?.complete).toBe(false); }); it("enforces sortOrder bounds 0..9999", () => { const progress = getPortfolioWizardProgress({ ...completeInput, basics: { ...completeInput.basics, sortOrder: "-1" }, }); expect(progress.find((s) => s.key === "basics")?.complete).toBe(false); }); it("content is incomplete when a summary locale is missing", () => { const progress = getPortfolioWizardProgress({ ...completeInput, content: { ...completeInput.content, summaryEn: "" }, }); expect(progress.find((s) => s.key === "content")?.complete).toBe(false); expect(getFirstIncompleteWizardStep(progress)).toBe("content"); }); it("sections/assets steps require at least one ready entry", () => { const noEntries = getPortfolioWizardProgress({ ...completeInput, sections: [], assets: [] }); expect(noEntries.find((s) => s.key === "sections")?.complete).toBe(false); expect(noEntries.find((s) => s.key === "assets")?.complete).toBe(false); }); it("summarizes section/asset readiness counts", () => { const progress = getPortfolioWizardProgress({ ...completeInput, sections: [section(), section({ titleEn: "" })], }); expect(progress.find((s) => s.key === "sections")?.summary).toBe("1/2 sections ready."); expect(progress.find((s) => s.key === "sections")?.complete).toBe(false); }); });