diff --git a/tests/domain/spline-corridor-junction.command.test.ts b/tests/domain/spline-corridor-junction.command.test.ts new file mode 100644 index 00000000..5d255e9b --- /dev/null +++ b/tests/domain/spline-corridor-junction.command.test.ts @@ -0,0 +1,128 @@ +import { describe, expect, it } from "vitest"; + +import { createEditorStore } from "../../src/app/editor-store"; +import { createDeletePathCommand } from "../../src/commands/delete-path-command"; +import { createDeleteSplineCorridorJunctionCommand } from "../../src/commands/delete-spline-corridor-junction-command"; +import { createUpsertPathCommand } from "../../src/commands/upsert-path-command"; +import { createUpsertSplineCorridorJunctionCommand } from "../../src/commands/upsert-spline-corridor-junction-command"; +import { createScenePath } from "../../src/document/paths"; +import { createEmptySceneDocument } from "../../src/document/scene-document"; +import { createSplineCorridorJunction } from "../../src/document/spline-corridor-junctions"; + +describe("spline corridor junction commands", () => { + it("creates, updates, deletes, undoes, and redoes junctions", () => { + const store = createEditorStore({ + initialDocument: createEmptySceneDocument() + }); + const pathA = createScenePath({ id: "path-junction-command-a" }); + const pathB = createScenePath({ id: "path-junction-command-b" }); + const junction = createSplineCorridorJunction({ + id: "junction-command", + center: { x: 1, y: 0, z: 2 }, + radius: 2, + connections: [ + { pathId: pathA.id, progress: 0.5, clipDistance: 2 }, + { pathId: pathB.id, progress: 0.25, clipDistance: 2 } + ] + }); + + store.executeCommand(createUpsertPathCommand({ path: pathA })); + store.executeCommand(createUpsertPathCommand({ path: pathB })); + store.executeCommand( + createUpsertSplineCorridorJunctionCommand({ junction }) + ); + + expect( + store.getState().document.splineCorridorJunctions[junction.id] + ).toEqual(junction); + + store.executeCommand( + createUpsertSplineCorridorJunctionCommand({ + junction: createSplineCorridorJunction({ + ...junction, + radius: 3 + }) + }) + ); + + expect( + store.getState().document.splineCorridorJunctions[junction.id]?.radius + ).toBe(3); + + store.executeCommand(createDeleteSplineCorridorJunctionCommand(junction.id)); + expect( + store.getState().document.splineCorridorJunctions[junction.id] + ).toBeUndefined(); + + expect(store.undo()).toBe(true); + expect( + store.getState().document.splineCorridorJunctions[junction.id]?.radius + ).toBe(3); + + expect(store.undo()).toBe(true); + expect( + store.getState().document.splineCorridorJunctions[junction.id]?.radius + ).toBe(2); + + expect(store.redo()).toBe(true); + expect( + store.getState().document.splineCorridorJunctions[junction.id]?.radius + ).toBe(3); + }); + + it("removes invalid junction connections when deleting paths and restores them on undo", () => { + const pathA = createScenePath({ id: "path-junction-delete-a" }); + const pathB = createScenePath({ id: "path-junction-delete-b" }); + const pathC = createScenePath({ id: "path-junction-delete-c" }); + const junction = createSplineCorridorJunction({ + id: "junction-delete", + center: { x: 0, y: 0, z: 0 }, + connections: [ + { pathId: pathA.id, progress: 0, clipDistance: 1.5 }, + { pathId: pathB.id, progress: 0.5, clipDistance: 1.5 }, + { pathId: pathC.id, progress: 1, clipDistance: 1.5 } + ] + }); + const store = createEditorStore({ + initialDocument: { + ...createEmptySceneDocument(), + paths: { + [pathA.id]: pathA, + [pathB.id]: pathB, + [pathC.id]: pathC + }, + splineCorridorJunctions: { + [junction.id]: junction + } + } + }); + + store.executeCommand(createDeletePathCommand(pathA.id)); + + expect( + store + .getState() + .document.splineCorridorJunctions[junction.id]?.connections.map( + (connection) => connection.pathId + ) + ).toEqual([pathB.id, pathC.id]); + + store.executeCommand(createDeletePathCommand(pathB.id)); + expect( + store.getState().document.splineCorridorJunctions[junction.id] + ).toBeUndefined(); + + expect(store.undo()).toBe(true); + expect( + store.getState().document.splineCorridorJunctions[junction.id] + ?.connections + ).toHaveLength(2); + + expect(store.undo()).toBe(true); + expect( + store.getState().document.splineCorridorJunctions[junction.id] + ?.connections + ).toHaveLength(3); + }); +}); + diff --git a/tests/serialization/scene-document-json.test.ts b/tests/serialization/scene-document-json.test.ts index 577a1ea7..ba6623f7 100644 --- a/tests/serialization/scene-document-json.test.ts +++ b/tests/serialization/scene-document-json.test.ts @@ -68,6 +68,7 @@ import { SCENE_DOCUMENT_VERSION, SHADER_SKY_HORIZON_HEIGHT_SCENE_DOCUMENT_VERSION, SHADER_SKY_SCENE_DOCUMENT_VERSION, + SPLINE_CORRIDOR_JUNCTIONS_SCENE_DOCUMENT_VERSION, SPLINE_CORRIDOR_COLLISION_SCENE_DOCUMENT_VERSION, SPLINE_REPEATERS_SCENE_DOCUMENT_VERSION, SPLINE_ROAD_EDGES_SCENE_DOCUMENT_VERSION, @@ -123,6 +124,7 @@ import { } from "../../src/serialization/scene-document-json"; import { BUNDLED_FOLIAGE_PROTOTYPES } from "../../src/foliage/bundled-foliage-manifest"; import { createFoliageLayer } from "../../src/foliage/foliage"; +import { createSplineCorridorJunction } from "../../src/document/spline-corridor-junctions"; describe("scene document JSON", () => { it("round-trips the current empty schema", () => {