diff options
| author | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-01-30 11:31:24 +0100 |
|---|---|---|
| committer | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-01-30 13:05:48 +0100 |
| commit | 0d4b61993cba37c180ac91e40a0fb362711de8de (patch) | |
| tree | f41dd68420b51766c1ca6fc1ee2c20dc5df830d1 /src/store/store.test.ts | |
Initial implementation
Diffstat (limited to 'src/store/store.test.ts')
| -rw-r--r-- | src/store/store.test.ts | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/src/store/store.test.ts b/src/store/store.test.ts new file mode 100644 index 0000000..24cac06 --- /dev/null +++ b/src/store/store.test.ts @@ -0,0 +1,307 @@ +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); + }); + }); +}); |