import { expect } from "@esm-bundle/chai"; import { VoxelManager } from "../../src/grid/VoxelManager.js"; import { VoxelGrid } from "../../src/grid/VoxelGrid.js"; import * as THREE from "three"; describe("Phase 1: VoxelManager Rendering (WebGL)", function () { // Increase timeout to 60s for slow WebGL initialization in headless/software mode this.timeout(60000); let grid; let scene; let manager; let renderer; before(function () { // Allow extra time specifically for the first context creation this.timeout(30000); // 1. Setup a real WebGL Renderer (Headless) const canvas = document.createElement("canvas"); // FORCE WebGL 2 (Required for Three.js r163+) const context = canvas.getContext("webgl2"); if (!context) { console.warn( "WebGL 2 not supported in this test environment. Skipping render checks." ); this.skip(); return; } renderer = new THREE.WebGLRenderer({ canvas, context }); }); after(function () { if (renderer) { renderer.dispose(); // Explicitly force context loss to free up resources for other test files if (renderer.forceContextLoss) renderer.forceContextLoss(); } }); beforeEach(() => { grid = new VoxelGrid(4, 4, 4); scene = new THREE.Scene(); manager = new VoxelManager(grid, scene); }); it("CoA 1: update() should create separate InstancedMeshes for different IDs", () => { grid.setCell(0, 0, 0, 1); // ID 1 (Stone) grid.setCell(1, 0, 0, 2); // ID 2 (Dirt) // In the new multi-material manager, update() handles initialization manager.update(); // Should have 2 children in the scene (one mesh per ID) + Focus Target // Filter to just find meshes const meshes = scene.children.filter((c) => c.isInstancedMesh); expect(meshes.length).to.equal(2); }); it("CoA 2: update() should correctly position instances", () => { grid.setCell(2, 2, 2, 1); // Specific position manager.update(); // Find the mesh corresponding to ID 1 const mesh = scene.children.find((c) => c.isInstancedMesh); const matrix = new THREE.Matrix4(); // Since there is only 1 voxel of ID 1, it will be at instance index 0 mesh.getMatrixAt(0, matrix); const position = new THREE.Vector3().setFromMatrixPosition(matrix); expect(position.x).to.equal(2); expect(position.y).to.equal(2); expect(position.z).to.equal(2); }); it("CoA 3: render loop should not crash", () => { // Verify we can actually call render() without WebGL errors const camera = new THREE.PerspectiveCamera(); manager.update(); expect(() => renderer.render(scene, camera)).to.not.throw(); }); it("CoA 4: updateMaterials() should apply texture to material", () => { // Mock a generated asset const mockCanvas = document.createElement("canvas"); const assets = { textures: { floor: mockCanvas, }, }; // Create a floor voxel (ID 2) grid.setCell(0, 0, 0, 2); manager.updateMaterials(assets); const mesh = scene.children.find((c) => c.isInstancedMesh); // Material could be single or array (multi-material) // ID 2 uses an array [side, side, top, bottom, front, back] const mat = Array.isArray(mesh.material) ? mesh.material[2] : mesh.material; expect(mat.map).to.exist; expect(mat.map.image).to.equal(mockCanvas); }); it("CoA 5: Should create a Focus Target at the center of the grid", () => { // Grid is 4x4x4. Center is roughly 2, 0, 2. manager.update(); expect(manager.focusTarget).to.exist; expect(manager.focusTarget.parent).to.equal(scene); expect(manager.focusTarget.position.x).to.equal(2); expect(manager.focusTarget.position.z).to.equal(2); }); });