From e57a105133706c5708600285f039b55d82298307 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Sun, 12 Apr 2026 04:35:13 +0200 Subject: [PATCH] Add tests for project time settings and runtime project time, update build-runtime-scene test --- tests/domain/build-runtime-scene.test.ts | 6 ++ .../project-time-settings.command.test.ts | 32 ++++++++++ tests/domain/runtime-project-time.test.ts | 62 +++++++++++++++++++ .../project-document-json.test.ts | 28 +++++++++ 4 files changed, 128 insertions(+) create mode 100644 tests/domain/project-time-settings.command.test.ts create mode 100644 tests/domain/runtime-project-time.test.ts diff --git a/tests/domain/build-runtime-scene.test.ts b/tests/domain/build-runtime-scene.test.ts index ed349b3c..54161cb0 100644 --- a/tests/domain/build-runtime-scene.test.ts +++ b/tests/domain/build-runtime-scene.test.ts @@ -298,9 +298,15 @@ describe("buildRuntimeSceneFromDocument", () => { y: 1.2, z: 0.1 }; + document.time = { + startTimeOfDayHours: 18.5, + dayLengthMinutes: 16 + }; const runtimeScene = buildRuntimeSceneFromDocument(document); + expect(runtimeScene.time).toEqual(document.time); + expect(runtimeScene.time).not.toBe(document.time); expect(runtimeScene.world).toEqual(document.world); expect(runtimeScene.world).not.toBe(document.world); expect(runtimeScene.world.sunLight.direction).not.toBe(document.world.sunLight.direction); diff --git a/tests/domain/project-time-settings.command.test.ts b/tests/domain/project-time-settings.command.test.ts new file mode 100644 index 00000000..f11ac1be --- /dev/null +++ b/tests/domain/project-time-settings.command.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; + +import { createEditorStore } from "../../src/app/editor-store"; +import { createSetProjectTimeSettingsCommand } from "../../src/commands/set-project-time-settings-command"; +import { cloneProjectTimeSettings } from "../../src/document/project-time-settings"; + +describe("createSetProjectTimeSettingsCommand", () => { + it("updates project time settings and restores them through undo", () => { + const store = createEditorStore(); + const originalTime = cloneProjectTimeSettings( + store.getState().projectDocument.time + ); + const nextTime = { + startTimeOfDayHours: 18.5, + dayLengthMinutes: 12 + }; + + store.executeCommand( + createSetProjectTimeSettingsCommand({ + label: "Set project time", + time: nextTime + }) + ); + + expect(store.getState().projectDocument.time).toEqual(nextTime); + expect(store.getState().document.time).toEqual(nextTime); + + expect(store.undo()).toBe(true); + expect(store.getState().projectDocument.time).toEqual(originalTime); + expect(store.getState().document.time).toEqual(originalTime); + }); +}); \ No newline at end of file diff --git a/tests/domain/runtime-project-time.test.ts b/tests/domain/runtime-project-time.test.ts new file mode 100644 index 00000000..642321dd --- /dev/null +++ b/tests/domain/runtime-project-time.test.ts @@ -0,0 +1,62 @@ +import { describe, expect, it } from "vitest"; + +import { createDefaultWorldSettings } from "../../src/document/world-settings"; +import { + advanceRuntimeClockState, + createRuntimeClockState, + resolveRuntimeDayNightWorldState +} from "../../src/runtime-three/runtime-project-time"; + +describe("runtime project time", () => { + it("advances and wraps the global clock using the authored day duration", () => { + const clock = createRuntimeClockState({ + startTimeOfDayHours: 23.5, + dayLengthMinutes: 24 + }); + + const advancedClock = advanceRuntimeClockState(clock, 60); + + expect(advancedClock.timeOfDayHours).toBeCloseTo(0.5); + expect(advancedClock.dayCount).toBe(1); + expect(advancedClock.dayLengthMinutes).toBe(24); + }); + + it("derives darker night lighting and background from the authored world", () => { + const world = createDefaultWorldSettings(); + world.background = { + mode: "verticalGradient", + topColorHex: "#88ccff", + bottomColorHex: "#f2b774" + }; + + const noon = resolveRuntimeDayNightWorldState(world, { + timeOfDayHours: 12, + dayCount: 0, + dayLengthMinutes: 24 + }); + const midnight = resolveRuntimeDayNightWorldState(world, { + timeOfDayHours: 0, + dayCount: 0, + dayLengthMinutes: 24 + }); + + expect(midnight.sunLight.intensity).toBeLessThan(noon.sunLight.intensity); + expect(midnight.ambientLight.intensity).toBeLessThan( + noon.ambientLight.intensity + ); + expect(noon.sunLight.direction.y).toBeGreaterThan(0); + expect(midnight.sunLight.direction.y).toBeLessThan(0); + + if ( + noon.background.mode !== "verticalGradient" || + midnight.background.mode !== "verticalGradient" + ) { + throw new Error("Expected a gradient background for the day/night test."); + } + + expect(midnight.background.topColorHex).not.toBe(noon.background.topColorHex); + expect(midnight.background.bottomColorHex).not.toBe( + noon.background.bottomColorHex + ); + }); +}); \ No newline at end of file diff --git a/tests/serialization/project-document-json.test.ts b/tests/serialization/project-document-json.test.ts index 198793ee..52f3bbce 100644 --- a/tests/serialization/project-document-json.test.ts +++ b/tests/serialization/project-document-json.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from "vitest"; import { + AUTHORED_OBJECT_STATE_SCENE_DOCUMENT_VERSION, DEFAULT_PROJECT_NAME, DEFAULT_SCENE_EDITOR_SNAP_STEP, PLAYER_START_GAMEPAD_CAMERA_LOOK_SCENE_DOCUMENT_VERSION, @@ -9,6 +10,7 @@ import { createEmptyProjectDocument, createEmptyProjectScene } from "../../src/document/scene-document"; +import { createDefaultProjectTimeSettings } from "../../src/document/project-time-settings"; import { createSceneEntryEntity, createSceneExitEntity @@ -106,12 +108,38 @@ describe("project document JSON", () => { normalStrength: 0.85 } }; + document.time = { + startTimeOfDayHours: 18.5, + dayLengthMinutes: 16 + }; const serializedDocument = serializeProjectDocument(document); expect(parseProjectDocumentJson(serializedDocument)).toEqual(document); }); + it("migrates pre-project-time multi-scene documents to default project time settings", () => { + const migratedDocument = parseProjectDocumentJson( + JSON.stringify({ + version: AUTHORED_OBJECT_STATE_SCENE_DOCUMENT_VERSION, + name: "Legacy Project", + activeSceneId: "scene-main", + scenes: { + "scene-main": createEmptyProjectScene({ + id: "scene-main", + name: "Legacy Entry" + }) + }, + materials: createEmptyProjectDocument().materials, + textures: {}, + assets: {} + }) + ); + + expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION); + expect(migratedDocument.time).toEqual(createDefaultProjectTimeSettings()); + }); + it("migrates pre-project-name multi-scene documents to Untitled Project", () => { const migratedDocument = parseProjectDocumentJson( JSON.stringify({