import test from 'node:test'; import assert from 'node:assert/strict'; import {readFile} from 'node:fs/promises'; import {runInNewContext} from 'node:vm'; async function languageData(extra=''){ const source=await readFile('public/js/data.js','utf8'); const context={LANG:'nl',T:key=>key}; runInNewContext(source+`; ${extra} globalThis.__language={ concepts:LANGUAGE_CONCEPTS, progression:LANGUAGE_NL_PROGRESSION_WORDS, themesNl:languageThemeList("nl"), themesEn:languageThemeList("en"), words:WORDS, catalogNl:languagePictureCatalog("nl") };`,context); return JSON.parse(JSON.stringify(context.__language)); } test('taalwidgets delen ruim duizend extra Nederlandse woorden over leerjaar en thema',async()=>{ const data=await languageData(); assert.ok(data.concepts.length>=300,'verwacht minimaal 300 vaste woordplaatjes'); assert.ok(data.progression.length>=1000,'verwacht minimaal 1000 aanvullende Nederlandse woorden'); assert.equal(data.themesNl.length,16); assert.equal(data.themesEn.length,12); assert.deepEqual(data.themesNl.map(theme=>theme.id),[ 'animals','food','nature','travel','home','body','actions','feelings', 'people','play','places','time','arts','science','learning','society' ]); const all=data.concepts.concat(data.progression); assert.equal(new Set(all.map(item=>item.id)).size,all.length); assert.equal(new Set(all.map(item=>item.nl)).size,all.length); for(const concept of data.concepts){ assert.ok(concept.nl&&concept.en&&concept.pic,concept.id); assert.ok(data.themesNl.some(theme=>theme.id===concept.theme),concept.theme); } for(const word of data.progression){ assert.ok(word.nl&&word.pic,word.id); assert.ok(word.yearGroup>=1&&word.yearGroup<=8,word.nl); assert.ok(data.themesNl.some(theme=>theme.id===word.theme),word.theme); } for(let year=1;year<=8;year++) assert.ok(data.progression.filter(word=>word.yearGroup===year).length>=60,'te weinig nieuwe woorden voor groep '+year); for(const theme of data.themesNl)assert.ok(theme.words.length>=60,theme.id); for(const theme of data.themesEn)assert.ok(theme.words.length>=18,theme.id); assert.equal(data.themesNl.reduce((sum,theme)=>sum+theme.words.length,0),all.length); assert.equal(data.themesEn.reduce((sum,theme)=>sum+theme.words.length,0),data.concepts.length); }); test('alle oude startwoorden blijven staan en krijgen ook een bibliotheekplaatje',async()=>{ const data=await languageData(); const oldNl=['aap','vis','zon','kat','bal','ei','boom','huis','maan','boot','kip','muis','boek','ster','roos','vuur','fiets','taart','jas','bril','klok','peer','kaas','eend','hand','voet','neus','oog','oor','mond']; const oldEn=['cat','dog','sun','egg','ball','fish','tree','moon','boat','star','book','cake','house','mouse','apple','train','fire','rose','bear','duck','frog','bee','cow','pig','hand','foot','nose','eye','ear','milk']; assert.deepEqual(data.words.nl.map(([word])=>word),oldNl); assert.deepEqual(data.words.en.map(([word])=>word),oldEn); const names=new Set(data.catalogNl.images.map(image=>image.name)); for(const word of oldNl)assert.ok(names.has(word),word); }); test('vaste en eigen woordplaatjes worden als herbruikbare catalogusitems aangeboden',async()=>{ const data=await languageData('GENERAL_EXTRA.nl=[["test","🧪"]]; THEMES.nl=[{name:"Boerderij",folder:"School",words:[["stal","🏚️"]]}];'); const builtins=data.catalogNl.images.filter(image=>image.builtin); assert.equal(builtins.length,data.concepts.length+data.progression.length); assert.equal(data.catalogNl.themes.filter(theme=>theme.languageBuiltin).length,16); for(const image of builtins){ assert.ok(image.id<0); assert.ok(image.src.startsWith('data:image/svg+xml;charset=utf-8,')); assert.ok(image.value&&image.themes.length===1); assert.equal(image.canManage,false); } const testPic=data.catalogNl.images.find(image=>!image.builtin&&image.name==='test'); const stablePic=data.catalogNl.images.find(image=>!image.builtin&&image.name==='stal'); assert.equal(testPic.value,'🧪'); assert.equal(stablePic.value,'🏚️'); assert.equal(stablePic.folder,'Taal/Mijn thema’s/School/Boerderij'); for(const word of ['familiegeschiedenis','competitiewedstrijd','mediageletterdheid','waterkringloop']){ const image=data.catalogNl.images.find(item=>item.name===word); assert.ok(image&&image.yearGroup>=7&&image.src.startsWith('data:image/svg+xml'),word); } }); test('alle vier taalspellen gebruiken themabronnen en kunnen de bibliotheek openen',async()=>{ const [data,catalog,picker,letters,css,catalogCss]=await Promise.all([ readFile('public/js/data.js','utf8'), readFile('public/js/image-catalog.js','utf8'), readFile('public/js/picker.js','utf8'), readFile('public/js/widgets/letters.js','utf8'), readFile('public/css/teach.css','utf8'), readFile('public/css/image-catalog.css','utf8'), ]); assert.match(data,/card\.startsWith\("b:"\)/); assert.match(data,/builtinGroup\.label = "📚 "/); for(const widget of ['letters','flits','hak','ball']){ const source=await readFile(`public/js/widgets/${widget}.js`,'utf8'); assert.match(source,/wordSourceList\(/,widget); assert.match(source,/fillWordSourceSel\(/,widget); } assert.match(catalog,/refreshWordPictureCatalog/); assert.match(catalog,/languagePictureCatalog\(LANG\)/); assert.match(catalog,/ic-word-themes/); assert.match(picker,/data-t="library"/); assert.match(picker,/uploadImageToCatalog\(f,/); assert.match(letters,/data-t="library"/); assert.match(letters,/openImageCatalog\(\(src,image\)/); assert.match(letters,/uploadImageToCatalog\(f,/); assert.match(css,/\.ed-tabs\{[^}]*overflow-x:auto/); assert.match(catalogCss,/\.ic-word-themes\{[^}]*overflow-x:auto/); assert.match(catalogCss,/@media\(max-width:760px\)/); });