import test from 'node:test'; import assert from 'node:assert/strict'; import {readFile} from 'node:fs/promises'; const ids=['birthday','numberline','sentence']; const learningIds=['fractions','clock','placevalue','money','wordtypes','schedule','mood','poll']; test('nieuwe widgets zijn geladen vóór het widgetregister', async()=>{ const html=await readFile('public/index.html','utf8'); const board=html.indexOf('js/board.js'); for(const id of ids){ const pos=html.indexOf(`js/widgets/${id}.js`); assert.ok(pos>0 && pos{ const registry=await readFile('public/js/board.js','utf8'); for(const id of ids){ assert.match(registry,new RegExp(`id:"${id}"`)); const source=await readFile(`public/js/widgets/${id}.js`,'utf8'); assert.match(source,/getState/); assert.doesNotMatch(source,/innerHTML\s*=.*(?:name|word\.value)/); } }); test('nieuwe widgetnamen en beschrijvingen zijn tweetalig', async()=>{ const core=await readFile('public/js/core.js','utf8'); for(const id of ids){ assert.equal((core.match(new RegExp(`wg_${id}:`,'g'))||[]).length,2); assert.equal((core.match(new RegExp(`wg_${id}_d:`,'g'))||[]).length,2); } }); test('educatieve widgetmodule laadt en registreert alle acht widgets', async()=>{ const html=await readFile('public/index.html','utf8'); assert.ok(html.indexOf('js/widgets/learning.js'){ const core=await readFile('public/js/core.js','utf8'); for(const id of learningIds){ assert.equal((core.match(new RegExp('wg_'+id+':','g'))||[]).length,2,id); assert.equal((core.match(new RegExp('wg_'+id+'_d:','g'))||[]).length,2,id); } }); test('widgetbibliotheek bevat zoeken favorieten sjablonen en borddata-acties', async()=>{ const [html,board]=await Promise.all([readFile('public/index.html','utf8'),readFile('public/js/board.js','utf8')]); for(const id of ['gallerySearch','galleryFavOnly','galleryTemplates','boardUndo','boardRedo','boardExport','boardImport'])assert.match(html,new RegExp('id="'+id+'"')); for(const feature of ['teachWidgetFavorites','structuredClone','application/json','widgetDuplicate','widgetLock','widgetPreview'])assert.ok(board.includes(feature),feature); }); test('oefenklok biedt niveaus leerwijzen en meerdere spellen', async()=>{ const [source,core]=await Promise.all([readFile('public/js/widgets/learning.js','utf8'),readFile('public/js/core.js','utf8')]); for(const value of ["'hour'","'quarter'","'five'","'minute'","'analogue'","'digital'","'words'","'both'","'set'","'read'","'choose'"])assert.ok(source.includes(value),value); for(const key of ['clockLevel','clockLearningStyle','clockGame','clockScore'])assert.equal((core.match(new RegExp(key+':','g'))||[]).length,2,key); }); test('analoge oefenklok is bedienbaar en houdt opdrachtacties buiten instellingen', async()=>{ const [source,css,core]=await Promise.all([readFile('public/js/widgets/learning.js','utf8'),readFile('public/css/teach.css','utf8'),readFile('public/js/core.js','utf8')]); for(const feature of ['onpointerdown','onpointermove','setPointerCapture','releasePointerCapture','getBoundingClientRect','clock-time-blocks','dotGrid'])assert.ok(source.includes(feature),feature); assert.match(css,/clock\.interactive[^}]*touch-action:none/); assert.match(source,/learningShell\(root,\[field\(T\('clockLevel'\),level\),field\(T\('clockLearningStyle'\),style\),field\(T\('clockGame'\),game\)\],stage\)/); const clockSource=source.slice(source.indexOf('function mountClock'),source.indexOf('function mountPlacevalue')); assert.doesNotMatch(clockSource,/learningShell\(root,\[[^\]]*(?:numberlineCheck|numberlineNew|clockHour|clockMinute)/); for(const feature of ['answerDigits','clock-answer-slots','clock-block-tray','dragClone','droptarget',"'0123456789'.split('')"])assert.ok(clockSource.includes(feature),feature); assert.match(clockSource,/String\(hour\)\.padStart\(2,'0'\)\+String\(minute\)\.padStart\(2,'0'\)/); assert.equal((core.match(/clockIncomplete:/g)||[]).length,2); assert.ok(clockSource.includes('if(i%5===0)continue'), 'uurstrepen bij cijfers worden overgeslagen'); assert.match(css,/translateY\(calc\(var\(--clock-size\)\*-.34\)\)/); });