import { describe, it, beforeEach } from "node:test"; import assert from "node:assert"; import "../test-setup.ts"; import { useStore, isValidCameraView } from "./store.ts"; describe("Store", () => { beforeEach(() => { useStore.setState({ models: [], selectedModelId: null, cameraView: "perspective", backdropUrl: null, availableModels: [ { id: "servbot", name: "Servbot", url: "/models/servbot.gltf" }, { id: "megaman", name: "Megaman", url: "/models/megaman_volnutt.gltf" }, { id: "tron", name: "Tron Bonne", url: "/models/tron_bonne.gltf" }, ], }); }); describe("addModel", () => { it("should add a model to the scene", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const models = useStore.getState().models; assert.strictEqual(models.length, 1); assert.strictEqual(models[0].sourceId, "servbot"); assert.deepStrictEqual(models[0].position, { x: 0, y: 0, z: 0 }); }); it("should generate unique IDs for each model instance", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); state.addModel("servbot", { x: 1, y: 0, z: 0 }); const models = useStore.getState().models; assert.strictEqual(models.length, 2); assert.notStrictEqual(models[0].id, models[1].id); }); it("should initialize with default hue shifts", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const model = useStore.getState().models[0]; assert.deepStrictEqual(model.hueShift, { red: 0, green: 0, blue: 0 }); }); it("should initialize with zero rotation", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const model = useStore.getState().models[0]; assert.deepStrictEqual(model.rotation, { x: 0, y: 0, z: 0 }); }); it("should not add model with invalid sourceId", () => { const state = useStore.getState(); state.addModel("nonexistent", { x: 0, y: 0, z: 0 }); const models = useStore.getState().models; assert.strictEqual(models.length, 0); }); it("should auto-select newly added model", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const { models, selectedModelId } = useStore.getState(); assert.strictEqual(selectedModelId, models[0].id); }); }); describe("removeModel", () => { it("should remove a model from the scene", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const modelId = useStore.getState().models[0].id; useStore.getState().removeModel(modelId); assert.strictEqual(useStore.getState().models.length, 0); }); it("should clear selection if removed model was selected", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const modelId = useStore.getState().models[0].id; useStore.getState().selectModel(modelId); useStore.getState().removeModel(modelId); assert.strictEqual(useStore.getState().selectedModelId, null); }); it("should not affect state when removing non-existent model", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const modelsBefore = useStore.getState().models; useStore.getState().removeModel("non-existent-id"); const modelsAfter = useStore.getState().models; assert.strictEqual(modelsAfter.length, modelsBefore.length); }); it("should preserve selection when removing a different model", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); state.addModel("megaman", { x: 1, y: 0, z: 0 }); const models = useStore.getState().models; useStore.getState().selectModel(models[0].id); useStore.getState().removeModel(models[1].id); assert.strictEqual(useStore.getState().selectedModelId, models[0].id); }); }); describe("selectModel", () => { it("should select a model", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const modelId = useStore.getState().models[0].id; useStore.getState().selectModel(modelId); assert.strictEqual(useStore.getState().selectedModelId, modelId); }); it("should deselect when selecting null", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const modelId = useStore.getState().models[0].id; useStore.getState().selectModel(modelId); useStore.getState().selectModel(null); assert.strictEqual(useStore.getState().selectedModelId, null); }); }); describe("updateModel", () => { it("should update model position", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const modelId = useStore.getState().models[0].id; useStore .getState() .updateModel(modelId, { position: { x: 5, y: 3, z: 2 } }); const model = useStore.getState().models[0]; assert.deepStrictEqual(model.position, { x: 5, y: 3, z: 2 }); }); it("should update model rotation", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const modelId = useStore.getState().models[0].id; useStore.getState().updateModel(modelId, { rotation: { x: Math.PI, y: 0, z: Math.PI / 2 }, }); const model = useStore.getState().models[0]; assert.deepStrictEqual(model.rotation, { x: Math.PI, y: 0, z: Math.PI / 2, }); }); it("should update red hue shift", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const modelId = useStore.getState().models[0].id; useStore.getState().updateModel(modelId, { hueShift: { red: 0.5, green: 0, blue: 0 }, }); const model = useStore.getState().models[0]; assert.strictEqual(model.hueShift.red, 0.5); }); it("should update all hue shifts", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const modelId = useStore.getState().models[0].id; useStore.getState().updateModel(modelId, { hueShift: { red: 0.2, green: 0.4, blue: 0.6 }, }); const model = useStore.getState().models[0]; assert.deepStrictEqual(model.hueShift, { red: 0.2, green: 0.4, blue: 0.6, }); }); it("should update multiple properties at once", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); const modelId = useStore.getState().models[0].id; useStore.getState().updateModel(modelId, { position: { x: 10, y: 20, z: 30 }, rotation: { x: 1, y: 2, z: 3 }, }); const model = useStore.getState().models[0]; assert.deepStrictEqual(model.position, { x: 10, y: 20, z: 30 }); assert.deepStrictEqual(model.rotation, { x: 1, y: 2, z: 3 }); }); it("should not affect other models when updating one", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); state.addModel("megaman", { x: 1, y: 0, z: 0 }); const models = useStore.getState().models; useStore.getState().updateModel(models[0].id, { position: { x: 99, y: 99, z: 99 }, }); const updatedModels = useStore.getState().models; assert.deepStrictEqual(updatedModels[1].position, { x: 1, y: 0, z: 0 }); }); it("should handle update for non-existent model gracefully", () => { const state = useStore.getState(); state.addModel("servbot", { x: 0, y: 0, z: 0 }); useStore.getState().updateModel("non-existent-id", { position: { x: 99, y: 99, z: 99 }, }); const model = useStore.getState().models[0]; assert.deepStrictEqual(model.position, { x: 0, y: 0, z: 0 }); }); }); describe("setCameraView", () => { it("should set camera view to perspective", () => { useStore.getState().setCameraView("perspective"); assert.strictEqual(useStore.getState().cameraView, "perspective"); }); it("should set camera view to orthographic top", () => { useStore.getState().setCameraView("ortho-top"); assert.strictEqual(useStore.getState().cameraView, "ortho-top"); }); it("should set camera view to orthographic front", () => { useStore.getState().setCameraView("ortho-front"); assert.strictEqual(useStore.getState().cameraView, "ortho-front"); }); it("should set camera view to orthographic side", () => { useStore.getState().setCameraView("ortho-side"); assert.strictEqual(useStore.getState().cameraView, "ortho-side"); }); }); describe("setBackdrop", () => { it("should set backdrop URL", () => { useStore.getState().setBackdrop("blob:http://localhost/12345"); assert.strictEqual( useStore.getState().backdropUrl, "blob:http://localhost/12345", ); }); it("should clear backdrop when set to null", () => { useStore.getState().setBackdrop("blob:http://localhost/12345"); useStore.getState().setBackdrop(null); assert.strictEqual(useStore.getState().backdropUrl, null); }); }); describe("availableModels", () => { it("should have default available models", () => { const availableModels = useStore.getState().availableModels; assert.strictEqual(availableModels.length, 3); assert.strictEqual(availableModels[0].name, "Servbot"); }); }); describe("isValidCameraView", () => { it("should return true for valid camera views", () => { assert.strictEqual(isValidCameraView("perspective"), true); assert.strictEqual(isValidCameraView("ortho-top"), true); assert.strictEqual(isValidCameraView("ortho-front"), true); assert.strictEqual(isValidCameraView("ortho-side"), true); }); it("should return false for invalid camera views", () => { assert.strictEqual(isValidCameraView("invalid"), false); assert.strictEqual(isValidCameraView(""), false); assert.strictEqual(isValidCameraView("PERSPECTIVE"), false); }); }); });