import assert from "node:assert/strict"; import { appendPlacement, dashboardMasonryRowSpan, dashboardWidgetColumnSpan, defaultDashboardLayout, layoutUpdatePayload, normalizeDashboardColumnStart, reconcileDashboardLayout, removePlacement, reorderPlacement } from "../src/features/dashboard/dashboardLayout.ts"; import { dashboardColumnStartFromPointer, dashboardGridPreview } from "../src/features/dashboard/dashboardEditorTypes.ts"; const widgets = [ { id: "one", surfaceId: "one.widget", title: "One", moduleId: "one", defaultSize: "small" as const, supportedSizes: ["small", "medium"] as const, defaultConfiguration: { count: 3 }, render: () => null }, { id: "two", surfaceId: "two.widget", title: "Two", moduleId: "two", defaultVisible: false, defaultSize: "medium" as const, render: () => null } ]; const defaults = defaultDashboardLayout(widgets); assert.deepEqual(defaults.knownWidgetIds, ["one", "two"]); assert.equal(defaults.placements.length, 1); assert.equal(defaults.placements[0].widgetId, "one"); assert.equal(defaults.placements[0].columnStart, 1); assert.deepEqual(defaults.placements[0].configuration, { count: 3 }); const withTwo = appendPlacement(defaults, widgets[1]); assert.deepEqual( withTwo.placements.map((placement) => placement.widgetId), ["one", "two"] ); assert.equal( withTwo.placements[1].columnStart, 2, "new widgets should use the next available horizontal position" ); assert.equal( appendPlacement(withTwo, widgets[1]).placements.length, 2, "single-instance widgets must not be duplicated" ); const reordered = reorderPlacement( withTwo, withTwo.placements[1].instanceId, withTwo.placements[0].instanceId, "before", 3 ); assert.deepEqual( reordered.placements.map((placement) => placement.widgetId), ["two", "one"] ); assert.equal( reordered.placements[0].columnStart, 3, "moving a widget must change its column without moving other columns" ); assert.equal(reordered.placements[1].columnStart, 1); const removed = removePlacement(reordered, reordered.placements[0].instanceId); assert.deepEqual( removed.placements.map((placement) => placement.widgetId), ["one"] ); const placementPreview = dashboardGridPreview( withTwo.placements, { kind: "placement", instanceId: withTwo.placements[0].instanceId }, { kind: "placement", instanceId: withTwo.placements[1].instanceId, edge: "after", columnStart: 3 } ); assert.deepEqual( placementPreview.map((item) => item.kind === "placement" ? `${item.placement.widgetId}:${item.placeholder}` : item.widgetId ), ["two:false", "one:true"], "placement previews must show the prospective order" ); assert.equal( placementPreview[1].kind === "placement" ? placementPreview[1].placement.columnStart : null, 3, "placement previews must show the prospective column" ); const cataloguePreview = dashboardGridPreview( withTwo.placements, { kind: "catalogue", widgetId: "three" }, { kind: "placement", instanceId: withTwo.placements[0].instanceId, edge: "after", columnStart: 2 }, "wide" ); assert.deepEqual( cataloguePreview.map((item) => item.kind === "placement" ? item.placement.widgetId : `${item.widgetId}:${item.size}` ), ["one", "three:wide", "two"], "catalogue previews must reserve the prospective widget size" ); assert.equal( cataloguePreview[1].kind === "catalogue" ? cataloguePreview[1].columnStart : null, 2, "catalogue previews must reserve the selected horizontal position" ); const unavailablePlacement = { instanceId: "unavailable-instance", widgetId: "temporarily-disabled", size: "wide" as const, columnStart: 2, configuration: { mode: "summary" } }; const reconciled = reconcileDashboardLayout( { layoutVersion: 1, revision: 4, knownWidgetIds: ["temporarily-disabled"], placements: [unavailablePlacement] }, widgets ); assert.equal( reconciled.placements[0].columnStart, 2, "temporarily unavailable contributions must retain their placement" ); assert.equal( reconciled.placements[1].widgetId, "one", "new default-visible widgets are announced into existing layouts" ); assert.equal(reconciled.revision, 4); const payload = layoutUpdatePayload(reconciled); assert.equal(payload.expected_revision, 4); assert.equal(payload.placements[0].instance_id, "unavailable-instance"); assert.equal(payload.placements[0].column_start, 2); assert.equal(payload.placements[0].configuration.mode, "summary"); assert.equal(dashboardWidgetColumnSpan("small"), 1); assert.equal(dashboardWidgetColumnSpan("medium"), 2); assert.equal(dashboardWidgetColumnSpan("wide"), 3); assert.equal(dashboardWidgetColumnSpan("full"), 4); assert.equal( normalizeDashboardColumnStart("medium", 4), 3, "column starts must be clamped so the widget stays in the four-column grid" ); assert.equal( dashboardColumnStartFromPointer(500, 0, 1000, 20, 2, 4), 2, "a two-column widget must be placeable in the middle columns" ); assert.equal( dashboardColumnStartFromPointer(950, 0, 1000, 20, 3, 4), 2, "a three-column widget must be placeable from column two" ); const repeatingWidget = { ...widgets[0], id: "repeating", allowMultiple: true, defaultVisible: false }; let bounded = defaultDashboardLayout([repeatingWidget]); for (let index = 0; index < 105; index += 1) { bounded = appendPlacement(bounded, repeatingWidget); } assert.equal( bounded.placements.length, 100, "client layout helpers must enforce the server placement limit" ); assert.equal( dashboardMasonryRowSpan(142, 4, 18), 40, "masonry spans must reserve the widget height and inter-widget gap" ); assert.equal( dashboardMasonryRowSpan(0, Number.NaN, Number.NaN), 1, "masonry spans must remain valid while a widget is mounting" ); console.log("Dashboard layout tests passed.");