basic-examples-probe.mjs (6175B)
1 const el=id=>document.getElementById(id),delay=ms=>new Promise(r=>setTimeout(r,ms)); 2 const assert=(ok,message)=>{if(!ok)throw Error(message);}; 3 const input=(id,value)=>{el(id).value=value;el(id).dispatchEvent(new Event('input'));}; 4 const change=(id,value)=>{el(id).value=value;el(id).dispatchEvent(new Event('change'));}; 5 async function status(want){for(let i=0;i<4500;i++){if(el('status').textContent===want)return;await delay(20);}throw Error(want+': '+el('status').textContent+' / '+el('compile-diagnostic').textContent+' / '+el('result').textContent);} 6 export async function run(control,backend='haskell-wasm'){ 7 await status('Ready'); 8 const groups=[...el('program-picker').querySelectorAll('optgroup')]; 9 assert(groups.map(g=>g.label).join('|')==='Basic Examples|Practical Examples|Language & Runtime Explorations|Libraries','Purposeful selector groups'); 10 const basics=['names-values','arithmetic','functions','lists','transform-list','choices']; 11 assert([...groups[0].children].map(o=>o.value).join('|')===basics.map(n=>'demo:'+n).join('|'),'Basic progression'); 12 assert(groups[2].firstElementChild.value==='demo:language-reference','Reference leads explorations'); 13 assert(![...el('program-picker').options].some(o=>o.value==='demo:intro'||o.value==='package:intro'),'Start Here retired from selector'); 14 assert(el('program-picker').value==='demo:names-values'&&el('expression-editor').value==='greeting'&&el('output-type').value==='string','Beginner arrival is ready to evaluate'); 15 assert(el('result').textContent===''&&el('terminal').textContent==='','Arrival waits for Evaluate'); 16 change('runtime-backend',backend);await status('Ready'); 17 const base=new URL('../',document.querySelector('script[type=module][src]').src); 18 const releases=(await(await fetch(new URL('packages.json',base))).json()).releases; 19 const visible=[...groups[3].children],manifests=visible.map(o=>releases.find(r=>r.name===o.value.slice(8))?.manifest); 20 assert(new Set(manifests).size===manifests.length,'One selector row per module identity'); 21 assert(visible.every(o=>!o.textContent.startsWith('tricu-')),'Friendly library labels'); 22 assert(visible.some(o=>o.value==='package:tricu-lib-native-math')&&!visible.some(o=>o.value==='package:tricu-math'),'Complete library publication preferred'); 23 const select=async name=>{change('program-picker','demo:'+name);await status('Ready');}; 24 const evaluate=async(expression,output,expected)=>{input('expression-editor',expression);change('output-type',output);el('run').click();await status('Finished');assert(el('result').textContent===expected,expression+' expected '+expected+' got '+el('result').textContent);}; 25 const defaults=[['greeting','string','Hello, world!'],['total','number','12'],['double 6','number','12'],['count','number','3'],['total','number','24'],['message 0','string','Your basket is empty.']]; 26 for(let i=0;i<basics.length;i++){ 27 await select(basics[i]);assert(el('expression-editor').value===defaults[i][0]&&el('output-type').value===defaults[i][1],'Default expression/settings '+basics[i]); 28 el('run').click();await status('Finished');assert(el('result').textContent===defaults[i][2],'Default result '+basics[i]); 29 const original=el('source-editor').value; 30 if(i===0){input('source-editor',original.replace('answer = 42','answer = 7'));await status('Compiled');await evaluate('answer','number','7');} 31 if(i===1){input('source-editor',original.replace('Math.add 5 7','Math.add 8 9'));await status('Compiled');await evaluate('total','number','17');} 32 if(i===2){input('source-editor',original.replace('double number = Math.multiply number 2','triple number = Math.multiply number 3'));await status('Compiled');await evaluate('triple 6','number','18');} 33 if(i===3){input('source-editor',original.replace('numbers = [2 4 6]','numbers = [2 4 6 8]'));await status('Compiled');await evaluate('count','number','4');await evaluate('total','number','20');input('source-editor',original.replace('numbers = [2 4 6]','numbers = []'));await status('Compiled');await evaluate('count','number','0');await evaluate('total','number','0');} 34 if(i===4){input('source-editor',original.replace('Math.multiply number 2','Math.multiply number 3'));await status('Compiled');await evaluate('total','number','36');} 35 if(i===5){input('source-editor',original.replace('message _ =','message 2 = "You have two items."\nmessage _ ='));await status('Compiled');await evaluate('message 2','string','You have two items.');await evaluate('message 4','string','You have several items.');} 36 el('restore-source').click();await status('Ready');assert(el('source-editor').value===original,'Restore '+basics[i]); 37 } 38 await select('language-reference'); 39 // Each preset goes through the actual published page (before editing), so the 40 // examples must work using the public interface rather than hidden imports. 41 const catalogue=await(await fetch(new URL('catalogue.json',base))).json(); 42 const reference=catalogue.find(d=>d.id==='language-reference'); 43 for(const name of Object.keys(reference.presets)){ 44 change('export',name);el('run').click();await status('Finished');assert(el('result').textContent.length>0,'Reference preset '+name); 45 } 46 await evaluate('describe (t t)','string','true');await evaluate('describe "hi"','string','greeting');await evaluate('firstOr 99 []','number','99'); 47 input('source-editor',reference.source+'\n-- my reference notes');await status('Compiled');await evaluate('sequenced 9','number','9'); 48 // A retired example's immutable publication still loads through its old share link. 49 const old=releases.find(r=>r.name==='intro'&&r.version==='0.2.0'); 50 location.hash=new URLSearchParams({bundle:old.hash,demo:'intro',export:'answer'}).toString();await delay(50);await status('Ready'); 51 assert(el('program-picker').value==='hash','Retired link remains a direct bundle selection');await evaluate('answer','number','42'); 52 await select('names-values'); 53 return {userAgent:navigator.userAgent,checks:['beginner default; grouped selector; identity-based library deduplication; all six lesson defaults and exercises; restoration; all reference presets before editing; editable reference; retained historical link'],backend}; 54 }