mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
459 lines
13 KiB
JavaScript
459 lines
13 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
buildSidebarDirectoryTree,
|
|
selectedEntryKeyFromUrl,
|
|
listSidebarItems,
|
|
filenameLabelForEntry,
|
|
normalizeCadFileQueryParam,
|
|
normalizeCadRefQueryParams,
|
|
sidebarDirectoryIdForEntry,
|
|
sidebarLabelForEntry
|
|
} from "./sidebar.js";
|
|
import {
|
|
readCadWorkspaceGlassTone,
|
|
readCadWorkspaceSessionState,
|
|
resetCadWorkspacePersistence,
|
|
writeCadWorkspaceSessionState
|
|
} from "./persistence.js";
|
|
import { restoredSidebarWidthForViewport } from "../../components/workbench/hooks/useCadWorkspaceSession.js";
|
|
|
|
function createMemoryStorage() {
|
|
const values = new Map();
|
|
return {
|
|
getItem: (key) => values.get(key) ?? null,
|
|
setItem: (key, value) => {
|
|
values.set(key, String(value));
|
|
},
|
|
removeItem: (key) => {
|
|
values.delete(key);
|
|
}
|
|
};
|
|
}
|
|
|
|
test("filenameLabelForEntry shows canonical step, stl, dxf, and urdf suffixes", () => {
|
|
assert.equal(
|
|
filenameLabelForEntry({
|
|
file: "sample_mount.step",
|
|
kind: "part",
|
|
source: { format: "step", path: "parts/sample_mount.step" }
|
|
}),
|
|
"sample_mount.step"
|
|
);
|
|
|
|
assert.equal(
|
|
filenameLabelForEntry({
|
|
file: "sample_assembly.step",
|
|
kind: "assembly",
|
|
source: { format: "step", path: "assemblies/sample_assembly.step" }
|
|
}),
|
|
"sample_assembly.step"
|
|
);
|
|
|
|
assert.equal(
|
|
filenameLabelForEntry({
|
|
file: "imports/vendor/widget.stp",
|
|
kind: "part",
|
|
source: { format: "stp", path: "imports/vendor/widget.stp" },
|
|
step: { path: "imports/vendor/widget.stp" }
|
|
}),
|
|
"widget.stp"
|
|
);
|
|
|
|
assert.equal(
|
|
filenameLabelForEntry({
|
|
file: "sample_robot.urdf",
|
|
kind: "urdf",
|
|
source: { format: "urdf", path: "sample_robot.urdf" },
|
|
name: "sample_robot (URDF)"
|
|
}),
|
|
"sample_robot.urdf"
|
|
);
|
|
|
|
assert.equal(
|
|
filenameLabelForEntry({
|
|
file: "sample_plate.dxf",
|
|
kind: "dxf",
|
|
source: { format: "dxf", path: "drawings/sample_plate.dxf" }
|
|
}),
|
|
"sample_plate.dxf"
|
|
);
|
|
|
|
assert.equal(
|
|
filenameLabelForEntry({
|
|
file: "fixtures/bracket.stl",
|
|
kind: "stl",
|
|
source: { format: "stl", path: "fixtures/bracket.stl" }
|
|
}),
|
|
"bracket.stl"
|
|
);
|
|
});
|
|
|
|
test("sidebarLabelForEntry uses the same suffix-aware filename labels", () => {
|
|
const entry = {
|
|
file: "sample_assembly.step",
|
|
kind: "assembly",
|
|
source: { format: "step", path: "assemblies/sample_assembly.step" }
|
|
};
|
|
|
|
assert.equal(sidebarLabelForEntry(entry), "sample_assembly.step");
|
|
});
|
|
|
|
test("sidebarDirectoryIdForEntry keeps exact CAD file folders", () => {
|
|
assert.equal(
|
|
sidebarDirectoryIdForEntry({
|
|
file: "parts/sample_plate.step",
|
|
kind: "part",
|
|
source: { format: "step", path: "parts/sample_plate.step" }
|
|
}),
|
|
"parts"
|
|
);
|
|
|
|
assert.equal(
|
|
sidebarDirectoryIdForEntry({
|
|
file: "drawings/sample_plate.dxf",
|
|
kind: "dxf",
|
|
source: { format: "dxf", path: "drawings/sample_plate.dxf" }
|
|
}),
|
|
"drawings"
|
|
);
|
|
|
|
assert.equal(
|
|
sidebarDirectoryIdForEntry({
|
|
file: "sample_robot.urdf",
|
|
kind: "urdf",
|
|
source: { format: "urdf", path: "sample_robot.urdf" }
|
|
}),
|
|
""
|
|
);
|
|
|
|
assert.equal(
|
|
sidebarDirectoryIdForEntry({
|
|
file: "meshes/fixture.stl",
|
|
kind: "stl",
|
|
source: { format: "stl", path: "meshes/fixture.stl" }
|
|
}),
|
|
"meshes"
|
|
);
|
|
|
|
assert.equal(
|
|
sidebarDirectoryIdForEntry({
|
|
file: "parts/mount.step",
|
|
kind: "part",
|
|
source: { format: "step", path: "parts/mount.step" }
|
|
}),
|
|
"parts"
|
|
);
|
|
});
|
|
|
|
test("buildSidebarDirectoryTree lists CAD files in their exact source directory", () => {
|
|
const tree = buildSidebarDirectoryTree([
|
|
{
|
|
file: "parts/sample_plate.step",
|
|
kind: "part",
|
|
source: { format: "step", path: "parts/sample_plate.step" }
|
|
},
|
|
{
|
|
file: "drawings/sample_plate.dxf",
|
|
kind: "dxf",
|
|
source: { format: "dxf", path: "drawings/sample_plate.dxf" }
|
|
}
|
|
]);
|
|
|
|
const partsDirectory = tree.directories.find((directory) => directory.id === "parts");
|
|
assert.ok(partsDirectory);
|
|
const drawingsDirectory = tree.directories.find((directory) => directory.id === "drawings");
|
|
assert.ok(drawingsDirectory);
|
|
assert.deepEqual(
|
|
[
|
|
...listSidebarItems(drawingsDirectory).map((item) => `${item.type}:${item.label}`),
|
|
...listSidebarItems(partsDirectory).map((item) => `${item.type}:${item.label}`),
|
|
],
|
|
["entry:sample_plate.dxf", "entry:sample_plate.step"]
|
|
);
|
|
});
|
|
|
|
test("workspace global state persists expanded directories and URDF entry animation preference", () => {
|
|
const originalWindow = globalThis.window;
|
|
globalThis.window = {
|
|
localStorage: createMemoryStorage(),
|
|
sessionStorage: createMemoryStorage()
|
|
};
|
|
|
|
try {
|
|
writeCadWorkspaceSessionState({
|
|
openTabs: [],
|
|
selectedKey: "",
|
|
query: "sample",
|
|
expandedDirectoryIds: ["parts", "parts/imports", "parts"],
|
|
sidebarOpen: false,
|
|
sidebarWidth: 312,
|
|
tabToolsWidth: 344,
|
|
urdfEntryAnimationEnabled: false
|
|
});
|
|
|
|
const restoredSession = readCadWorkspaceSessionState();
|
|
|
|
assert.deepEqual(
|
|
restoredSession.expandedDirectoryIds,
|
|
["parts", "parts/imports"]
|
|
);
|
|
assert.equal(restoredSession.query, "sample");
|
|
assert.equal(restoredSession.urdfEntryAnimationEnabled, false);
|
|
} finally {
|
|
if (originalWindow === undefined) {
|
|
delete globalThis.window;
|
|
} else {
|
|
globalThis.window = originalWindow;
|
|
}
|
|
}
|
|
});
|
|
|
|
test("workspace global state migrates legacy default panel widths", () => {
|
|
const originalWindow = globalThis.window;
|
|
globalThis.window = {
|
|
localStorage: createMemoryStorage(),
|
|
sessionStorage: createMemoryStorage()
|
|
};
|
|
|
|
try {
|
|
writeCadWorkspaceSessionState({
|
|
openTabs: [],
|
|
selectedKey: "",
|
|
sidebarWidth: 300,
|
|
tabToolsWidth: 300
|
|
});
|
|
|
|
const restoredSession = readCadWorkspaceSessionState();
|
|
|
|
assert.equal(restoredSession.sidebarWidth, 260);
|
|
assert.equal(restoredSession.tabToolsWidth, 260);
|
|
} finally {
|
|
if (originalWindow === undefined) {
|
|
delete globalThis.window;
|
|
} else {
|
|
globalThis.window = originalWindow;
|
|
}
|
|
}
|
|
});
|
|
|
|
test("workspace desktop restore resets auto-collapsed sidebar width", () => {
|
|
assert.equal(
|
|
restoredSidebarWidthForViewport(
|
|
{ sidebarOpen: true, sidebarWidth: 150 },
|
|
{ desktopViewport: true, defaultSidebarWidth: 260, sidebarMinWidth: 150 }
|
|
),
|
|
260
|
|
);
|
|
assert.equal(
|
|
restoredSidebarWidthForViewport(
|
|
{ sidebarOpen: false, sidebarWidth: 420 },
|
|
{ desktopViewport: true, defaultSidebarWidth: 260, sidebarMinWidth: 150 }
|
|
),
|
|
260
|
|
);
|
|
assert.equal(
|
|
restoredSidebarWidthForViewport(
|
|
{ sidebarOpen: true, sidebarWidth: 420 },
|
|
{ desktopViewport: true, defaultSidebarWidth: 260, sidebarMinWidth: 150 }
|
|
),
|
|
420
|
|
);
|
|
assert.equal(
|
|
restoredSidebarWidthForViewport(
|
|
{ sidebarOpen: false, sidebarWidth: 150 },
|
|
{ desktopViewport: false, defaultSidebarWidth: 260, sidebarMinWidth: 150 }
|
|
),
|
|
150
|
|
);
|
|
});
|
|
|
|
test("workspace global state defaults URDF entry animation preference to enabled", () => {
|
|
const originalWindow = globalThis.window;
|
|
globalThis.window = {
|
|
localStorage: createMemoryStorage(),
|
|
sessionStorage: createMemoryStorage()
|
|
};
|
|
|
|
try {
|
|
writeCadWorkspaceSessionState({
|
|
openTabs: [],
|
|
selectedKey: "",
|
|
query: ""
|
|
});
|
|
|
|
const restoredSession = readCadWorkspaceSessionState();
|
|
assert.equal(restoredSession.urdfEntryAnimationEnabled, true);
|
|
} finally {
|
|
if (originalWindow === undefined) {
|
|
delete globalThis.window;
|
|
} else {
|
|
globalThis.window = originalWindow;
|
|
}
|
|
}
|
|
});
|
|
|
|
test("workspace glass tone defaults to cinematic dark tone", () => {
|
|
const originalWindow = globalThis.window;
|
|
globalThis.window = {
|
|
localStorage: createMemoryStorage(),
|
|
sessionStorage: createMemoryStorage()
|
|
};
|
|
|
|
try {
|
|
assert.equal(readCadWorkspaceGlassTone(), "dark");
|
|
globalThis.window.localStorage.setItem("cad-explorer:workbench-glass-tone:v1", "light");
|
|
assert.equal(readCadWorkspaceGlassTone(), "light");
|
|
} finally {
|
|
if (originalWindow === undefined) {
|
|
delete globalThis.window;
|
|
} else {
|
|
globalThis.window = originalWindow;
|
|
}
|
|
}
|
|
});
|
|
|
|
test("resetCadWorkspacePersistence removes current CAD Explorer keys", () => {
|
|
const originalWindow = globalThis.window;
|
|
globalThis.window = {
|
|
localStorage: createMemoryStorage(),
|
|
sessionStorage: createMemoryStorage()
|
|
};
|
|
|
|
try {
|
|
globalThis.window.localStorage.setItem("cad-explorer:workbench-global:v1", "{}");
|
|
globalThis.window.localStorage.setItem("cad-explorer:look-settings", "{}");
|
|
globalThis.window.localStorage.setItem("cad-explorer-theme", "dark");
|
|
globalThis.window.localStorage.setItem("cad-explorer:workbench-glass-tone:v1", "dark");
|
|
globalThis.window.sessionStorage.setItem("cad-explorer:workbench-session:v2", "{}");
|
|
globalThis.window.sessionStorage.setItem("cad-explorer:dxf-bend-overrides:v1", "{}");
|
|
|
|
assert.equal(resetCadWorkspacePersistence(), true);
|
|
|
|
assert.equal(globalThis.window.localStorage.getItem("cad-explorer:workbench-global:v1"), null);
|
|
assert.equal(globalThis.window.localStorage.getItem("cad-explorer:look-settings"), null);
|
|
assert.equal(globalThis.window.localStorage.getItem("cad-explorer-theme"), null);
|
|
assert.equal(globalThis.window.localStorage.getItem("cad-explorer:workbench-glass-tone:v1"), null);
|
|
assert.equal(globalThis.window.sessionStorage.getItem("cad-explorer:workbench-session:v2"), null);
|
|
assert.equal(globalThis.window.sessionStorage.getItem("cad-explorer:dxf-bend-overrides:v1"), null);
|
|
} finally {
|
|
if (originalWindow === undefined) {
|
|
delete globalThis.window;
|
|
} else {
|
|
globalThis.window = originalWindow;
|
|
}
|
|
}
|
|
});
|
|
|
|
test("normalizeCadRefQueryParams accepts canonical models refs", () => {
|
|
assert.deepEqual(
|
|
normalizeCadRefQueryParams(["models/parts/sample_plate#f2", "@cad[models/parts/sample_base#e1]", "parts/ignored#f1"]),
|
|
["@cad[models/parts/sample_plate#f2]", "@cad[models/parts/sample_base#e1]"]
|
|
);
|
|
});
|
|
|
|
test("selectedEntryKeyFromUrl restores the selected file query param", () => {
|
|
const originalWindow = globalThis.window;
|
|
globalThis.window = {
|
|
location: {
|
|
search: "?file=parts%2Fsample_plate.step"
|
|
}
|
|
};
|
|
|
|
try {
|
|
assert.equal(
|
|
selectedEntryKeyFromUrl([
|
|
{
|
|
file: "parts/sample_base.step",
|
|
cadPath: "models/parts/sample_base",
|
|
kind: "part"
|
|
},
|
|
{
|
|
file: "parts/sample_plate.step",
|
|
cadPath: "models/parts/sample_plate",
|
|
kind: "part"
|
|
}
|
|
]),
|
|
"parts/sample_plate.step"
|
|
);
|
|
} finally {
|
|
if (originalWindow === undefined) {
|
|
delete globalThis.window;
|
|
} else {
|
|
globalThis.window = originalWindow;
|
|
}
|
|
}
|
|
});
|
|
|
|
test("selectedEntryKeyFromUrl accepts repo-relative file params inside the active CAD directory", () => {
|
|
const originalWindow = globalThis.window;
|
|
globalThis.window = {
|
|
location: {
|
|
search: "?dir=models&file=models%2Fparts%2Fsample_plate.step"
|
|
}
|
|
};
|
|
|
|
try {
|
|
assert.equal(
|
|
selectedEntryKeyFromUrl([
|
|
{
|
|
file: "parts/sample_base.step",
|
|
cadPath: "models/parts/sample_base",
|
|
kind: "part"
|
|
},
|
|
{
|
|
file: "parts/sample_plate.step",
|
|
cadPath: "models/parts/sample_plate",
|
|
kind: "part"
|
|
}
|
|
]),
|
|
"parts/sample_plate.step"
|
|
);
|
|
} finally {
|
|
if (originalWindow === undefined) {
|
|
delete globalThis.window;
|
|
} else {
|
|
globalThis.window = originalWindow;
|
|
}
|
|
}
|
|
});
|
|
|
|
test("normalizeCadFileQueryParam keeps scan-relative file params unchanged", () => {
|
|
assert.equal(normalizeCadFileQueryParam("parts/sample_plate.step", "models"), "parts/sample_plate.step");
|
|
assert.equal(normalizeCadFileQueryParam("models/parts/sample_plate.step", "models"), "parts/sample_plate.step");
|
|
assert.equal(normalizeCadFileQueryParam("models/imports/widget.step", "models/imports"), "widget.step");
|
|
});
|
|
|
|
test("selectedEntryKeyFromUrl restores the selected canonical ref query param", () => {
|
|
const originalWindow = globalThis.window;
|
|
globalThis.window = {
|
|
location: {
|
|
search: "?refs=models%2Fparts%2Fsample_plate%23f2"
|
|
}
|
|
};
|
|
|
|
try {
|
|
assert.equal(
|
|
selectedEntryKeyFromUrl([
|
|
{
|
|
file: "parts/sample_base.step",
|
|
cadPath: "models/parts/sample_base",
|
|
kind: "part"
|
|
},
|
|
{
|
|
file: "parts/sample_plate.step",
|
|
cadPath: "models/parts/sample_plate",
|
|
kind: "part"
|
|
}
|
|
]),
|
|
"parts/sample_plate.step"
|
|
);
|
|
} finally {
|
|
if (originalWindow === undefined) {
|
|
delete globalThis.window;
|
|
} else {
|
|
globalThis.window = originalWindow;
|
|
}
|
|
}
|
|
});
|