import test from 'node:test'; import assert from 'node:assert/strict'; import {promisify} from 'node:util'; import {execFile} from 'node:child_process'; import {mkdtemp,readFile,rm} from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import {inspectModel,modelDocument} from '../engine/model.ts'; const exec=promisify(execFile), binary=process.env.FORMA_BLENDER; test('Blender exports shape keys, curves, cameras, lights and driver-baked animated textures', {skip:!binary,timeout:180000}, async()=>{ const dir=await mkdtemp(path.join(os.tmpdir(),'forma-blender-')); try{ const input=path.join(dir,'input.blend'),output=path.join(dir,'output.glb'); await exec(binary!,['--background','--factory-startup','--disable-autoexec','--python-exit-code','1','--python',path.resolve('tests/blender-fixture.py'),'--',input],{maxBuffer:4*1024*1024}); const original=await readFile(input); await exec(process.execPath,['scripts/export-blender.mjs',input,'--output',output,'--bake-materials','--resolution','16','--samples','1'],{maxBuffer:4*1024*1024}); assert.deepEqual(await readFile(input),original,'the source .blend is never saved or modified'); const bytes=new Uint8Array(await readFile(output)),doc=modelDocument(bytes,'output.glb'),meta=inspectModel(bytes,'output.glb'); assert.equal(doc.scenes.length,1);assert.equal(meta.cameras.length,1);assert.equal(meta.lights,1);assert.equal(meta.morphTargets,1); assert.equal(meta.activeCamera,'ActiveCameraData'); assert.ok(Math.abs(doc.extensions.KHR_lights_punctual.lights[0].intensity-100/(4*Math.PI))<.001,'Forma-compatible light units avoid overexposure'); assert.ok(doc.nodes.some((n:any)=>n.name==='ConvertedCurve'&&n.mesh!==undefined)); assert.ok(!doc.nodes.some((n:any)=>n.name==='UnrelatedObject')); assert.ok(meta.animatedProperties.some(p=>p.endsWith('KHR_texture_transform/offset'))); const report=JSON.parse(await readFile(output.replace('.glb','.report.json'),'utf8')); assert.equal(report.bakedMaterials[0].frames,3); const material=doc.materials.find((m:any)=>m.name.startsWith('FormaBake_')); assert.ok(material.pbrMetallicRoughness.baseColorTexture.extensions.KHR_texture_transform); }finally{await rm(dir,{recursive:true,force:true});} });