import test from 'node:test'; import assert from 'node:assert/strict'; import {readFile} from 'node:fs/promises'; import {runInNewContext} from 'node:vm'; async function loadAvatarRenderer(){ const source=await readFile('public/js/avatar.js','utf8'); const cutoff=source.indexOf('/* bouwt de interactieve kiezer'); assert.ok(cutoff>0,'rendergedeelte van avatar.js ontbreekt'); const created=[]; const context={ T:key=>key, document:{ createElementNS(){ const element={ attributes:{}, innerHTML:'', setAttribute(name,value){this.attributes[name]=String(value);}, }; created.push(element); return element; }, }, }; runInNewContext(source.slice(0,cutoff)+ ';globalThis.__avatar={renderAvatarFace,avatarBodyMarkup,bodyAccessories:AVATAR_BODY_ACC_MARKUP,headAccessories:AVATAR_HEAD_ACC_MARKUP};', context); return {api:context.__avatar,created}; } const base={ species:'dino', bodyColor:'#3fae6a', hairStyle:'none', hairColor:'#5b3a29', accessoryHead:'none', accessoryBody:'none', }; test('avatars hebben verbonden en herkenbare lichaamsdelen per soort',async()=>{ const {api}=await loadAvatarRenderer(); for(const species of ['dino','monster','animal']){ const markup=api.avatarBodyMarkup(species,base.bodyColor); assert.equal((markup.match(/av-arm av-arm-/g)||[]).length,2,species+' mist twee armen'); assert.equal((markup.match(/av-leg av-leg-/g)||[]).length,2,species+' mist twee benen'); for(const part of ['av-neck','av-torso','av-head','av-eye']){ assert.ok(markup.includes(part),species+' mist '+part); } assert.ok(markup.indexOf('av-arm'){ const {api}=await loadAvatarRenderer(); for(const id of ['none','scarf','cape','badge','necklace','backpack']){ assert.equal(typeof api.bodyAccessories[id].back,'function',id+' mist achterlaag'); assert.equal(typeof api.bodyAccessories[id].front,'function',id+' mist voorlaag'); } const cape=api.renderAvatarFace({...base,accessoryBody:'cape'},'lg').innerHTML; assert.ok(cape.indexOf('av-cape\"')cape.indexOf('av-head\"')); const backpack=api.renderAvatarFace({...base,accessoryBody:'backpack'},'lg').innerHTML; assert.ok(backpack.indexOf('av-backpack\"')backpack.indexOf('av-head\"')); for(const [id,part] of [['scarf','av-scarf'],['badge','av-badge'],['necklace','av-necklace']]){ const markup=api.renderAvatarFace({...base,accessoryBody:id},'lg').innerHTML; assert.ok(markup.indexOf(part)>markup.indexOf('av-head\"'),id+' hoort vóór op het lichaam'); } }); test('alle avatarcombinaties renderen volledig binnen de verruimde SVG',async()=>{ const {api}=await loadAvatarRenderer(); const species=['dino','monster','animal']; const hair=['none','spiky','curly','ponytail','mohawk']; const head=['none','cap','crown','glasses','sunglasses','bow','headphones','partyhat']; const body=['none','scarf','cape','badge','necklace','backpack']; for(const speciesId of species){ for(const hairId of hair){ for(const headId of head){ for(const bodyId of body){ const svg=api.renderAvatarFace({ ...base, species:speciesId, hairStyle:hairId, accessoryHead:headId, accessoryBody:bodyId, },'sm'); assert.equal(svg.attributes.viewBox,'-6 -12 112 124'); assert.equal(svg.attributes.preserveAspectRatio,'xMidYMid meet'); assert.doesNotMatch(svg.innerHTML,/undefined|NaN/); assert.ok(svg.innerHTML.includes('av-head')); } } } } const cap=api.renderAvatarFace({...base,hairStyle:'spiky',hairColor:'#123456',accessoryHead:'cap'},'lg').innerHTML; const crown=api.renderAvatarFace({...base,hairStyle:'spiky',hairColor:'#123456',accessoryHead:'crown'},'lg').innerHTML; assert.ok(!cap.includes('#123456'),'pet hoort het bovenhaar af te dekken'); assert.ok(crown.includes('#123456'),'kroon hoort op het haar te rusten'); for(const id of head)assert.equal(typeof api.headAccessories[id],'function'); });