program-catalogue.test.mjs (3876B)
1 import test from 'node:test'; 2 import assert from 'node:assert/strict'; 3 import {readFile} from 'node:fs/promises'; 4 import {selectorLibraries,libraryLabel,exampleGroup,exampleGroups} from '../dist/program-catalogue.js'; 5 import {packageIndex,packageReleases} from '../dist/packages.js'; 6 const read=name=>readFile(new URL('../'+name,import.meta.url),'utf8').then(JSON.parse); 7 test('selector merges identical native interfaces while preserving publications',async()=>{ 8 const releases=packageIndex(await read('packages.json')),before=JSON.stringify(releases),shown=selectorLibraries(releases); 9 assert.equal(new Set(shown.map(r=>r.manifest)).size,shown.length); 10 assert.equal(JSON.stringify(releases),before); 11 assert(shown.some(r=>r.name==='tricu-lib-native-math'));assert(!shown.some(r=>r.name==='tricu-math')); 12 assert(shown.some(r=>r.name==='tricu-lib-packages-base'));assert(!shown.some(r=>r.name==='tricu-base')); 13 assert(shown.some(r=>r.name==='tricu-compiler'));assert(shown.some(r=>r.name==='tricu-source-builder')); 14 for(const name of new Set(releases.filter(r=>!r.demo).map(r=>r.name))){ 15 const latest=packageReleases(releases,name)[0];assert(shown.some(r=>r.manifest===latest.manifest),name); 16 } 17 assert(shown.every(r=>!libraryLabel(r.name).startsWith('tricu-'))); 18 assert(releases.some(r=>r.demo==='intro'),'Retired publications remain available'); 19 }); 20 test('beginner progression and reference are separated from practical demos',async()=>{ 21 const catalogue=await read('catalogue.json'); 22 assert.deepEqual(exampleGroups,['Basic Examples','Practical Examples','Language & Runtime Explorations']); 23 assert.deepEqual(catalogue.filter(e=>exampleGroup(e)==='Basic Examples').map(e=>e.id),['names-values','arithmetic','functions','lists','transform-list','choices']); 24 assert.equal(catalogue.filter(e=>exampleGroup(e)==='Language & Runtime Explorations')[0].id,'language-reference'); 25 assert.equal(catalogue[0].export,'greeting');assert.equal(catalogue[0].output,'string'); 26 for(const entry of catalogue.filter(e=>exampleGroup(e)==='Basic Examples')){ 27 assert.equal(entry.source,await readFile(new URL('../demos/'+entry.id+'.tri',import.meta.url),'utf8')); 28 assert.match(entry.source,/-- Next:|Choose "Doing arithmetic"/); 29 if(entry.id!=='names-values'){ 30 assert.doesNotMatch(entry.source,/Click or tap|compile after|dropdown|inspection card/); 31 assert.doesNotMatch(entry.source,/^!import .* as /m); 32 } 33 assert.doesNotMatch(entry.source,/Evaluate|Choose .* in Definition|Add import supplies/); 34 } 35 assert.equal(Object.keys(catalogue.find(e=>e.id==='language-reference').presets).length,16); 36 }); 37 38 test('example sources stay synchronized and omit retired evaluator controls',async()=>{ 39 const catalogue=await read('catalogue.json'); 40 for(const entry of catalogue.filter(e=>['Basic Examples','Practical Examples'].includes(exampleGroup(e)))){ 41 assert.equal(entry.source,await readFile(new URL('../demos/'+entry.id+'.tri',import.meta.url),'utf8')); 42 assert.doesNotMatch(entry.source,/Evaluate|their presets|Select v1\/v2|Record fields/); 43 } 44 const html=await readFile(new URL('../index.html',import.meta.url),'utf8'); 45 const app=await readFile(new URL('../dist/app.js',import.meta.url),'utf8'); 46 assert(!html.includes('playground-intro'));assert(!app.includes('playground-intro')); 47 }); 48 49 test('basic examples publish results as well as the functions they teach',async()=>{ 50 const catalogue=await read('catalogue.json'); 51 const results={ 52 'names-values':['greetAndAnswer'], 53 'functions':['sixDoubled','nineDoubled','area'], 54 'lists':['numbers','itemCount','total'], 55 'transform-list':['numbers','doubled','total'], 56 'choices':['emptyBasket','oneItem','fullBasket'], 57 }; 58 for(const [id,names] of Object.entries(results)){ 59 const entry=catalogue.find(e=>e.id===id); 60 for(const name of names)assert.deepEqual(entry.presets[name].arguments,[],id+': '+name); 61 } 62 });