tricu

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

definition-name-inputs.mjs (8937B)


      1 // Trusted Chromium input, complementary to the cross-browser DOM/geometry probe.
      2 import assert from 'node:assert/strict';
      3 import {writeFile} from 'node:fs/promises';
      4 const pause=ms=>new Promise(resolve=>setTimeout(resolve,ms));
      5 export async function definitionNameInputs({send,evaluate,report,prefix}){
      6  const target=`document.querySelector('[data-definition="answer"]')`;
      7  const closed=`document.querySelector('.definition-card').hidden`;
      8  const wait=async(expression,label)=>{for(let i=0;i<800;i++){if(await evaluate(expression))return;await pause(25);}throw Error('Definition input timeout: '+label+' / '+JSON.stringify(await evaluate(`({active:document.activeElement.outerHTML.slice(0,300),hidden:document.querySelector('.definition-card').hidden,result:document.querySelector('.definition-card pre').textContent,status:document.getElementById('status').textContent})`)));};
      9  const point=async selector=>evaluate(`(()=>{const e=${selector};e.scrollIntoView({block:'center',behavior:'instant'});const r=e.getBoundingClientRect();return {x:r.x+r.width/2,y:r.y+r.height/2};})()`);
     10  const mouse=async selector=>{
     11   const {x,y}=await point(selector);
     12   await send('Input.dispatchMouseEvent',{type:'mousePressed',x,y,button:'left',clickCount:1});
     13   await send('Input.dispatchMouseEvent',{type:'mouseReleased',x,y,button:'left',clickCount:1});
     14  };
     15  const key=async(key,code,windowsVirtualKeyCode,modifiers=0)=>{
     16   await send('Input.dispatchKeyEvent',{type:'keyDown',key,code,windowsVirtualKeyCode,modifiers,...(key==='Enter'?{text:'\r',unmodifiedText:'\r'}:key===' '?{text:' ',unmodifiedText:' '}: {})});
     17   await send('Input.dispatchKeyEvent',{type:'keyUp',key,code,windowsVirtualKeyCode,modifiers});
     18  };
     19  const close=async()=>{
     20   if(!await evaluate(closed))await mouse(`document.querySelector('[aria-label="Close definition inspection"]')`);
     21   await wait(closed,'close');
     22  };
     23  const inspect=async()=>wait(`!${closed}&&!document.querySelector('.definition-card select').disabled&&document.querySelector('.definition-card pre').textContent==='42'`,'actual value');
     24  for(const width of [390,1440]){
     25   await send('Emulation.setDeviceMetricsOverride',{width,height:1000,deviceScaleFactor:1,mobile:width<741});
     26   await evaluate("new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve)))");
     27   await mouse(target);await inspect();
     28   assert(await evaluate("document.activeElement!==globalThis.__sourceEditor.view.contentDOM"),'Inspection keeps focus outside the editing surface');
     29   const expected=await evaluate("globalThis.__sourceEditor.getSource()");
     30   await mouse(`document.querySelector('.edit-definition-name')`);
     31   assert(await evaluate("document.activeElement===globalThis.__sourceEditor.view.contentDOM&&globalThis.__sourceEditor.getSource().slice(globalThis.__sourceEditor.getSelection().from,globalThis.__sourceEditor.getSelection().to)==='answer'"),'Trusted Edit name focuses and selects the native identifier');
     32   await send('Input.insertText',{text:'renamed'});
     33   assert.equal(await evaluate("globalThis.__sourceEditor.getSource()"),expected.replace('answer =','renamed ='),'Native typing replaces only the declaration name');
     34   await key('z','KeyZ',90,2);
     35   assert.equal(await evaluate("globalThis.__sourceEditor.getSource()"),expected,'Rename has native undo');
     36   await wait("document.getElementById('status').textContent==='Compiled'",'undo compile');
     37   await evaluate(`globalThis.__sourceEditor.focus()`);await key('Tab','Tab',9);
     38   assert(await evaluate(`document.activeElement===${target}`),'Tab reaches the declaration name');
     39   await key('Enter','Enter',13);await inspect();
     40   assert(await evaluate("document.activeElement===document.querySelector('.definition-card pre')"),'Keyboard inspection focuses the result');
     41   await key('Escape','Escape',27);
     42   assert(await evaluate(`${closed}&&document.activeElement===${target}`),'Escape returns keyboard focus to the definition name / '+JSON.stringify(await evaluate(`({active:document.activeElement.outerHTML.slice(0,250),hidden:${closed},target:${target}.outerHTML,scroll:document.getElementById('source-editor').scrollTop,status:document.getElementById('status').textContent})`)));
     43   await key(' ','Space',32);await inspect();await close();
     44   // Clicking the RHS places the editing caret.
     45   const rhs=await evaluate("(()=>{const s=[...document.querySelectorAll('.cm-content .syntax-definition')].find(s=>s.textContent==='answer').getBoundingClientRect();return {x:s.right+parseFloat(getComputedStyle(document.getElementById('source-editor')).fontSize)*1.5,y:s.y+s.height/2};})()");
     46   await send('Input.dispatchMouseEvent',{type:'mousePressed',...rhs,button:'left',clickCount:1});
     47   await send('Input.dispatchMouseEvent',{type:'mouseReleased',...rhs,button:'left',clickCount:1});
     48   assert(await evaluate(`${closed}&&document.activeElement===globalThis.__sourceEditor.view.contentDOM`),'RHS click edits normally');
     49   const p=await point(target);
     50   await send('Input.dispatchTouchEvent',{type:'touchStart',touchPoints:[{...p,radiusX:2,radiusY:2}]});
     51   await send('Input.dispatchTouchEvent',{type:'touchEnd',touchPoints:[]});await inspect();
     52   assert(await evaluate("document.activeElement!==globalThis.__sourceEditor.view.contentDOM"),'Touch inspection leaves the editing keyboard closed');
     53   await close();
     54 
     55   const hold=await point(target);
     56   await send('Input.dispatchTouchEvent',{type:'touchStart',touchPoints:[{...hold,radiusX:2,radiusY:2}]});
     57   await pause(750);await send('Input.dispatchTouchEvent',{type:'touchEnd',touchPoints:[]});await pause(100);
     58   assert(await evaluate(closed),'Real touch hold does not inspect');
     59   assert(await evaluate("document.activeElement===globalThis.__sourceEditor.view.contentDOM&&globalThis.__sourceEditor.getSource().slice(globalThis.__sourceEditor.getSelection().from,globalThis.__sourceEditor.getSelection().to)==='answer'"),'Touch hold selects the identifier in the native editor / '+JSON.stringify(await evaluate(`({active:document.activeElement.outerHTML.slice(0,200),start:globalThis.__sourceEditor.getSelection().from,end:globalThis.__sourceEditor.getSelection().to,target:${target}.outerHTML})`)));
     60   await evaluate('document.getSelection().removeAllRanges()');
     61   await evaluate(`globalThis.__sourceEditor.select(0);${target}.focus({preventScroll:true})`);
     62   const pan=await point(target);
     63   await send('Input.dispatchTouchEvent',{type:'touchStart',touchPoints:[pan]});
     64   await send('Input.dispatchTouchEvent',{type:'touchMove',touchPoints:[{x:pan.x,y:pan.y+40}]});
     65   await pause(550);await send('Input.dispatchTouchEvent',{type:'touchEnd',touchPoints:[]});
     66   assert(await evaluate(`${closed}&&globalThis.__sourceEditor.getSelection().from===0&&globalThis.__sourceEditor.getSelection().to===0`),'Touch panning cancels the hold without selecting or inspecting');
     67   // Drag from ordinary text across a clickable name using editor selection.
     68   const drag=await evaluate("(()=>{const w=document.getElementById('source-editor'),a=w.querySelector('.syntax-directive').getBoundingClientRect(),b=w.querySelector('[data-definition=identity]').getBoundingClientRect();return {from:{x:a.left+2,y:a.top+a.height/2},to:{x:b.left+b.width/2,y:b.top+b.height/2}};})()");
     69   await send('Input.dispatchMouseEvent',{type:'mousePressed',...drag.from,button:'left',clickCount:1});
     70   await send('Input.dispatchMouseEvent',{type:'mouseMoved',...drag.to,button:'left',buttons:1});
     71   await send('Input.dispatchMouseEvent',{type:'mouseReleased',...drag.to,button:'left',clickCount:1});
     72   assert(await evaluate(`${closed}&&globalThis.__sourceEditor.getSource().slice(globalThis.__sourceEditor.getSelection().from,globalThis.__sourceEditor.getSelection().to).includes('answer = 42')`),'Native drag selection crosses name decorations without inspecting');
     73   // Select an identifier range in the canonical document.
     74   await evaluate("(()=>{const e=globalThis.__sourceEditor,start=e.getSource().indexOf('identity x');e.select(start,start+14);})()");
     75   assert(await evaluate("globalThis.__sourceEditor.getSelection().to>globalThis.__sourceEditor.getSelection().from"),'Native source selections survive name decoration');
     76   if(prefix){
     77    await evaluate("document.activeElement.blur();window.scrollTo({top:0,behavior:'instant'})");
     78    const capture=async suffix=>{
     79     const {cssContentSize:r}=await send('Page.getLayoutMetrics');
     80     const shot=await send('Page.captureScreenshot',{format:'png',captureBeyondViewport:true,clip:{x:0,y:0,width:r.width,height:r.height,scale:1}});
     81     await writeFile(prefix+'-'+suffix+'-'+width+'.png',Buffer.from(shot.data,'base64'));
     82    };
     83    await capture('names');await mouse(target);await inspect();await capture('card');await close();
     84   }
     85  }
     86  report.checks.push('trusted mouse/touch name activation without editing keyboard; native Edit name typing/undo; Enter/Space/Escape focus; RHS caret placement; touch-hold name selection; panning cancellation; native cross-name drag selection at 390/1440px');
     87 }