53 lines
3.6 KiB
JavaScript
53 lines
3.6 KiB
JavaScript
/** Native input while a real progressive copy is still publishing. */
|
|
import assert from 'node:assert/strict';
|
|
|
|
export async function editDuringCopy({call,evaluate,wait,count}){
|
|
assert.ok(count>=1000,'concurrent input probe requires at least 1000 source nodes');
|
|
await wait('window.__canvasPerformance.read().firstPresentedFrameMs!==null','copy did not present before native input');
|
|
assert.equal(await evaluate('window.__canvasPerformance.read().localCompleteMs===null'),true,'copy finished before input probe');
|
|
// Copied cards overlap originals. Find an actually exposed point; never send a
|
|
// synthetic double-click through the copied card to the original underneath.
|
|
const point=await evaluate(`(()=>{
|
|
const label=document.querySelector('.react-flow__node[data-id="perf-0"] [aria-label^="单击选中"]');
|
|
if(!label)throw Error('original text label is missing');
|
|
const rect=label.getBoundingClientRect();
|
|
for(let y=Math.max(0,rect.top)+3;y<Math.min(innerHeight,rect.bottom);y+=6)
|
|
for(let x=Math.max(0,rect.left)+3;x<Math.min(innerWidth,rect.right);x+=6)
|
|
if(label.contains(document.elementFromPoint(x,y)))return{x,y};
|
|
throw Error('original text has no exposed native input point');
|
|
})()`);
|
|
await call('Input.dispatchMouseEvent',{type:'mousePressed',...point,button:'left',buttons:1,clickCount:2});
|
|
await call('Input.dispatchMouseEvent',{type:'mouseReleased',...point,button:'left',buttons:0,clickCount:2});
|
|
await wait(`document.activeElement===document.querySelector('.react-flow__node[data-id="perf-0"] textarea')&&!document.activeElement.readOnly`,'original text did not enter editing during copy');
|
|
await evaluate(`(()=>{
|
|
const element=document.activeElement,samples=[];
|
|
const input=()=>{
|
|
const sample={inputAt:performance.now(),value:element.value,copyActive:window.__canvasPerformance.read().localCompleteMs===null};
|
|
samples.push(sample);
|
|
requestAnimationFrame(()=>requestAnimationFrame(()=>{
|
|
sample.frameAt=performance.now();sample.frameDelayMs=sample.frameAt-sample.inputAt;
|
|
sample.retained=element.isConnected&&document.activeElement===element;
|
|
sample.presentedValue=element.value;sample.mountedNodes=document.querySelectorAll('.react-flow__node').length;
|
|
}));
|
|
};
|
|
element.addEventListener('input',input);
|
|
window.__copyInputProbe={element,samples,dispose(){element.removeEventListener('input',input);}};
|
|
})()`);
|
|
await call('Input.imeSetComposition',{text:'复',selectionStart:1,selectionEnd:1});
|
|
await call('Input.insertText',{text:'复制期间输入'});
|
|
for(const text of [' A',' B',' C']){
|
|
await call('Input.insertText',{text});
|
|
await wait(`window.__copyInputProbe.samples.at(-1)?.frameAt!==undefined`,'native input did not reach its next frame');
|
|
}
|
|
const result=await evaluate(`(()=>{
|
|
const probe=window.__copyInputProbe,result={samples:probe.samples,value:probe.element.value,retained:probe.element.isConnected&&document.activeElement===probe.element};
|
|
probe.dispose();probe.element.blur();delete window.__copyInputProbe;return result;
|
|
})()`);
|
|
assert.equal(result.retained,true,'copy discarded native input DOM or focus');
|
|
assert.ok(result.value.endsWith('复制期间输入 A B C'),'native composition/typing was overwritten');
|
|
assert.ok(result.samples.some(sample=>sample.copyActive),'all native input happened after copy completion');
|
|
assert.ok(result.samples.every(sample=>sample.retained),'copy interrupted an input presentation frame');
|
|
assert.ok(result.samples.every(sample=>sample.mountedNodes<100),'native editing mounted offscreen nodes during copy');
|
|
return result;
|
|
}
|