tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

import-fold-inputs.mjs (5706B)


      1 // Trusted input on the packaged Playground's canonical editor document.
      2 import assert from 'node:assert/strict';
      3 import {writeFile} from 'node:fs/promises';
      4 const pause=ms=>new Promise(r=>setTimeout(r,ms));
      5 export async function importFoldInputs({send,evaluate,report,prefix}){
      6  const e='globalThis.__sourceEditor',initial=report.source;
      7  const wait=async condition=>{for(let i=0;!await evaluate(condition);i++){assert(i<1600,condition);await pause(25);}};
      8  const key=async(key,code,vk,modifiers=0)=>{await send('Input.dispatchKeyEvent',{type:'keyDown',key,code,windowsVirtualKeyCode:vk,modifiers,...(key==='Enter'?{text:'\r'}:{})});await send('Input.dispatchKeyEvent',{type:'keyUp',key,code,windowsVirtualKeyCode:vk,modifiers});await pause(30);};
      9  const point=async expression=>evaluate(`(()=>{const element=${expression};element.scrollIntoView({block:'center',behavior:'instant'});const r=element.getBoundingClientRect();return {x:r.left+r.width*.25,y:r.top+r.height/2};})()`);
     10  const click=async expression=>{const p=await point(expression);await send('Input.dispatchMouseEvent',{type:'mousePressed',...p,button:'left',clickCount:1});await send('Input.dispatchMouseEvent',{type:'mouseReleased',...p,button:'left',clickCount:1});await pause(50);};
     11  const widget=`${e}.dom.querySelector('.import-hash')`,alias=`[...${e}.dom.querySelectorAll('.syntax-name')].find(span=>span.textContent==='N')`;
     12  const counts=`({folded:${e}.dom.querySelectorAll('.import-hash').length,full:${e}.dom.querySelectorAll('.import-hash-full').length})`;
     13  await send('Browser.grantPermissions',{origin:await evaluate('location.origin'),permissions:['clipboardReadWrite','clipboardSanitizedWrite']});
     14  for(const width of [390,1440]){
     15   await send('Emulation.setDeviceMetricsOverride',{width,height:1000,deviceScaleFactor:1,mobile:width<741});
     16   await evaluate(`${e}.replaceSource(${JSON.stringify(initial)},true)`);await wait("document.getElementById('status').textContent==='Compiled'");
     17   assert.deepEqual(await evaluate(counts),{folded:3,full:0});
     18   await click(widget);assert.deepEqual(await evaluate(counts),{folded:2,full:1},'Folded click moves cursor to import');
     19   await click(alias);assert.deepEqual(await evaluate(counts),{folded:2,full:1});
     20   assert.equal(await evaluate(`${e}.getSelection().from`),initial.indexOf(' as N')+4,'Caret maps past hidden 64-character hash');
     21   await send('Input.insertText',{text:'M'});await key('z','KeyZ',90,2);assert.equal(await evaluate(`${e}.getSource()`),initial,'Typing undo');
     22   await evaluate(`${e}.select(${initial.indexOf('!import')},${initial.indexOf('!import '+report.math+' as P')})`);
     23   assert.deepEqual(await evaluate(counts),{folded:2,full:1});
     24   await key('c','KeyC',67,2);const selected=await evaluate('navigator.clipboard.readText()');assert(selected.includes(report.math)&&!selected.includes('...'),'Partial selection copies complete hashes');
     25   await key('a','KeyA',65,2);await key('c','KeyC',67,2);assert.equal(await evaluate('navigator.clipboard.readText()'),initial,'Native full-source clipboard');
     26   await evaluate(`${e}.select(0)`);const inserted=`!import ${report.math} as Q\n`;
     27   await send('Input.insertText',{text:inserted});await pause(30);assert.equal((await evaluate(counts)).folded,4,'Pasted import gains fold');
     28   await key('z','KeyZ',90,2);assert.equal(await evaluate(`${e}.getSource()`),initial,'Undo restores source after dynamic decoration');
     29   await key('Z','KeyZ',90,10);assert.equal(await evaluate(`${e}.getSource()`),inserted+initial,'Redo restores source after dynamic decoration');await key('z','KeyZ',90,2);
     30   await evaluate(`${e}.select(${initial.length})`);const p=await point(alias);
     31   await send('Input.dispatchTouchEvent',{type:'touchStart',touchPoints:[p]});await send('Input.dispatchTouchEvent',{type:'touchEnd',touchPoints:[]});await pause(80);
     32   assert.deepEqual(await evaluate(counts),{folded:2,full:1},'Touch reveals only the touched import line');
     33   await evaluate(`${e}.select(${initial.indexOf(report.math)+64})`);await key('Backspace','Backspace',8);assert.equal((await evaluate(`${e}.getSource()`)).length,initial.length-1,'Hash editing deletes one character');await key('z','KeyZ',90,2);assert.equal(await evaluate(`${e}.getSource()`),initial);
     34   await wait("document.getElementById('status').textContent==='Compiled'");await evaluate(`${e}.setAvailability(true,false)`);
     35   await send('Input.insertText',{text:'MUST NOT EDIT'});assert.equal(await evaluate(`${e}.getSource()`),initial,'Busy/read-only blocks native edits');await evaluate(`${e}.setAvailability(false,false)`);
     36   if(prefix){const {cssContentSize:r}=await send('Page.getLayoutMetrics');const shot=await send('Page.captureScreenshot',{format:'png',captureBeyondViewport:true,clip:{x:0,y:0,width:r.width,height:r.height,scale:1}});await writeFile(prefix+'-folds-'+width+'.png',Buffer.from(shot.data,'base64'));}
     37  }
     38  // Enter inserts a plain newline; read-only state also applies to command keys.
     39  await evaluate(`${e}.replaceSource('  answer = 42',true);${e}.select(${('  answer = 42').length})`);await key('Enter','Enter',13);
     40  assert.equal(await evaluate(`${e}.getSource()`),'  answer = 42\n','Enter inserts exactly one newline');
     41  await evaluate(`${e}.setAvailability(true,false)`);await key('Enter','Enter',13);assert.equal(await evaluate(`${e}.getSource()`),'  answer = 42\n','Read-only also rejects Enter');
     42  await evaluate(`${e}.setAvailability(false,false);${e}.replaceSource(${JSON.stringify(initial)},true)`);await wait("document.getElementById('status').textContent==='Compiled'");
     43  report.checks.push('trusted per-line mouse/touch; canonical alias caret; full and selected-source clipboard; pasted-import native undo/redo; read-only at 390/1440px');
     44 }