live-ux-probe.mjs (5084B)
1 import {el,input,change,status,wait,pause,inspect,result,card} from './source-probe-helpers.mjs'; 2 const assert=(condition,message)=>{if(!condition)throw Error(message);}; 3 export async function run(control,backend='haskell-wasm'){ 4 await status('Ready'); 5 change('program-picker','demo:language-reference');await status('Ready');assert(await inspect('answer','number')==='42','Initial published value'); 6 const NativeWorker=globalThis.Worker,held=[],submitted=[];let hold=true; 7 // Delay real native replies to make late-completion ordering deterministic. 8 globalThis.Worker=class extends NativeWorker{ 9 postMessage(message,...args){if(message.type==='compile')submitted.push(message.source);super.postMessage(message,...args);} 10 set onmessage(handler){super.onmessage=event=>{if(hold&&['compiled','error'].includes(event.data.type))held.push(()=>handler(event));else handler(event);};} 11 }; 12 try{ 13 const original=globalThis.__sourceEditor.getSource(); 14 input('source-editor',original.replace('first 42 7','first 43 7')); 15 assert(card().querySelector('select').disabled&&result()==='','Edits invalidate old inline output immediately'); 16 await status('Compiling');assert(!globalThis.__sourceEditor.isDisabled,'Editing continues during compilation'); 17 input('source-editor',original.replace('first 42 7','first 44 7'));input('source-editor',original.replace('first 42 7','first 45 7')); 18 await wait(()=>held.length>0,'held compilation');assert(submitted.length===1,'Keystrokes do not restart current compile'); 19 hold=false;held.shift()();await status('Compiled');assert(submitted.length===2&&submitted[1].includes('first 45 7'),'Only newest queued revision compiles'); 20 assert(await inspect('answer','number')==='45','No stale compiled value'); 21 hold=true;input('source-editor','answer = missingStaleName');await wait(()=>held.length>0,'held diagnostic');input('source-editor','answer = 45'); 22 hold=false;held.shift()();await status('Compiled');assert(el('compile-diagnostic').hidden,'Stale diagnostics discarded'); 23 input('source-editor','answer = missingLatestName');await status('Compilation failed');assert(!el('compile-diagnostic').hidden&&card().querySelector('select').disabled&&result()==='','Latest diagnostic invalidates old inspection'); 24 input('source-editor','identity value = value\nanswer =\n identity\n 46');await status('Compiled');assert(await inspect('answer','number')==='46','Indented continuation'); 25 el('stop').click();el('compile').click();await status('Compiled');assert(await inspect('answer','number')==='46','Runtime restart rebuilds source'); 26 el('stop').click();el('compile').click();await status('Compiled'); 27 input('source-editor','answer = 47');el('stop').click();await pause(900);assert(el('status').textContent==='Stopped','Stop suppresses queued compilation'); 28 input('source-editor','answer = 48');await status('Compiled');assert(await inspect('answer','number')==='48','Source edits recover after Stop'); 29 el('source-editor').dispatchEvent(new CompositionEvent('compositionstart'));input('source-editor','answer = 49');await pause(900);assert(el('status').textContent==='Changes pending','IME does not compile mid-composition'); 30 el('source-editor').dispatchEvent(new CompositionEvent('compositionend'));await status('Compiled');assert(await inspect('answer','number')==='49','IME completion compiles'); 31 input('source-editor','answer = 50');change('program-picker','demo:language-reference');await status('Ready');await pause(900);assert(el('status').textContent==='Ready','Package change cancels queued compilation'); 32 document.querySelector('[data-compiler-example=input]').click();await status('Compiled');assert(!el('input-form')&&el('terminal-io').textContent==='','Compilation never runs IO'); 33 await inspect('io');assert(!el('terminal-io').querySelector('form'),'Recognition never runs IO');el('io-return-format').value='string';el('run-io').click();await wait(()=>el('terminal-io').querySelector('form input'),'structured input'); 34 assert(globalThis.__sourceEditor.isDisabled&&el('reset').disabled,'Active IO cannot be replaced by background edits or Clear');el('terminal-io').querySelector('form input').value='hello λ🙂';el('terminal-io').querySelector('form').requestSubmit();await status('Finished');assert(result()==='hello λ🙂','Inline IO return'); 35 input('source-editor','id x = x\nanswer = id 51\nbomb = (x : x x) (x : x x)');await status('Compiled');assert(await inspect('answer','number')==='51','Unused divergent binding is not forced'); 36 document.querySelector('[aria-label="Inspect compiled definition bomb"]').click();await wait(()=>el('status').textContent==='Inspecting bomb','divergent observation');await pause(150);el('stop').click();await status('Stopped'); 37 }finally{globalThis.Worker=NativeWorker;} 38 return {userAgent:navigator.userAgent,backend,checks:['real native delayed completion; revision invalidation; stale-error rejection; compile coalescing; editable compilation; Stop suppresses queued work; IME; runtime rebuilding; no implicit IO; unused divergence; CPU-bound Stop']}; 39 }