171 lines
4.3 KiB
TypeScript
171 lines
4.3 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import {
|
|
appendPlacement,
|
|
dashboardMasonryRowSpan,
|
|
defaultDashboardLayout,
|
|
layoutUpdatePayload,
|
|
reconcileDashboardLayout,
|
|
removePlacement,
|
|
reorderPlacement
|
|
} from "../src/features/dashboard/dashboardLayout.ts";
|
|
import { 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.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(
|
|
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"
|
|
);
|
|
assert.deepEqual(
|
|
reordered.placements.map((placement) => placement.widgetId),
|
|
["two", "one"]
|
|
);
|
|
|
|
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"
|
|
}
|
|
);
|
|
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"
|
|
);
|
|
|
|
const cataloguePreview = dashboardGridPreview(
|
|
withTwo.placements,
|
|
{ kind: "catalogue", widgetId: "three" },
|
|
{
|
|
kind: "placement",
|
|
instanceId: withTwo.placements[0].instanceId,
|
|
edge: "after"
|
|
},
|
|
"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"
|
|
);
|
|
|
|
const unavailablePlacement = {
|
|
instanceId: "unavailable-instance",
|
|
widgetId: "temporarily-disabled",
|
|
size: "wide" as const,
|
|
configuration: { mode: "summary" }
|
|
};
|
|
const reconciled = reconcileDashboardLayout(
|
|
{
|
|
layoutVersion: 1,
|
|
revision: 4,
|
|
knownWidgetIds: ["temporarily-disabled"],
|
|
placements: [unavailablePlacement]
|
|
},
|
|
widgets
|
|
);
|
|
assert.equal(
|
|
reconciled.placements[0],
|
|
unavailablePlacement,
|
|
"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].configuration.mode, "summary");
|
|
|
|
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.");
|