import * as XLSX from "xlsx"; import { EstimateExportFormat, EstimateStatus, EstimateVersionStatus, } from "@capakraken/shared"; import { describe, expect, it } from "vitest"; import { serializeEstimateExport, type EstimateExportSource, } from "../estimate/export-serializer.js"; const createdAt = new Date("2026-03-13T08:00:00.000Z"); function buildSource(): EstimateExportSource { return { estimate: { id: "est_1", projectId: "project_1", name: "CGI Estimate", opportunityId: "OP-42", baseCurrency: "EUR", status: EstimateStatus.APPROVED, createdAt, updatedAt: createdAt, }, project: { id: "project_1", shortCode: "CGI-001", name: "CGI Project", status: "ACTIVE", startDate: "2026-03-01T00:00:00.000Z", endDate: "2026-04-01T00:00:00.000Z", }, version: { id: "ver_1", versionNumber: 1, label: "Approved", status: EstimateVersionStatus.APPROVED, notes: "Ready for export", lockedAt: createdAt, projectSnapshot: {}, createdAt, updatedAt: createdAt, assumptions: [ { id: "assumption_1", category: "commercial", key: "pricingStructure", label: "Pricing Structure", valueType: "string", value: "fixed-bid", sortOrder: 0, notes: null, createdAt, updatedAt: createdAt, }, ], scopeItems: [ { id: "scope_1", sequenceNo: 10, scopeType: "SHOT", packageCode: "PKG-1", name: "Shot 010", description: "Main hero shot", scene: "Scene 01", page: null, location: "Berlin", assumptionCategory: null, technicalSpec: { resolution: "4K" }, frameCount: 120, itemCount: 1, unitMode: "shot", internalComments: null, externalComments: null, metadata: {}, createdAt, updatedAt: createdAt, }, ], demandLines: [ { id: "line_1", scopeItemId: "scope_1", roleId: "role_1", resourceId: "resource_1", lineType: "LABOR", name: "Comp Artist", chapter: "Compositing", hours: 40, days: 5, fte: 1, rateSource: "resource", costRateCents: 5000, billRateCents: 8000, currency: "EUR", costTotalCents: 200000, priceTotalCents: 320000, monthlySpread: { "2026-03": 40 }, staffingAttributes: { location: "Berlin" }, metadata: {}, createdAt, updatedAt: createdAt, }, ], resourceSnapshots: [ { id: "snapshot_1", resourceId: "resource_1", sourceEid: "E100", displayName: "Alex Artist", chapter: "Compositing", roleId: "role_1", currency: "EUR", lcrCents: 5000, ucrCents: 8000, fte: 1, location: "Berlin", country: "DE", level: "Senior", workType: "Remote", attributes: {}, createdAt, updatedAt: createdAt, }, ], metrics: [ { id: "metric_1", key: "total_hours", label: "Total Hours", metricGroup: "summary", valueDecimal: 40, valueCents: null, currency: null, metadata: {}, createdAt, updatedAt: createdAt, }, ], }, }; } describe("estimate export serializer", () => { it("creates a structured JSON export payload", () => { const payload = serializeEstimateExport(buildSource(), EstimateExportFormat.JSON); expect(payload.encoding).toBe("utf8"); expect(payload.mimeType).toBe("application/json; charset=utf-8"); expect(payload.summary.totalHours).toBe(40); expect(payload.content).toContain('"estimateId": "est_1"'); expect(payload.previewText).toContain('"schemaVersion": 1'); }); it("creates a multi-sheet xlsx export payload", () => { const payload = serializeEstimateExport(buildSource(), EstimateExportFormat.XLSX); const workbook = XLSX.read(payload.content, { type: "base64" }); expect(payload.encoding).toBe("base64"); expect(payload.sheetNames).toEqual([ "Overview", "Assumptions", "Scope", "DemandLines", "Resources", "Metrics", ]); expect(workbook.SheetNames).toContain("DemandLines"); expect(payload.byteLength).toBeGreaterThan(100); }); });