docs(tests): explain renderer timing and migration probes
This commit is contained in:
@@ -46,6 +46,7 @@ export async function editDuringCopy({call,evaluate,wait,count}){
|
||||
return Number(status.match(/·\\s*(\\d+)\\s*项/)?.[1]??0);
|
||||
},dispose(){element.removeEventListener('input',input);}};
|
||||
})()`);
|
||||
// CDP 直接驱动 Chromium 的组合与插入接口,不启动操作系统输入法候选窗;用它检查编辑器对 composition 的保留。
|
||||
// 先启动中文输入法组合再提交文本,随后分次插入 A/B/C;每次都等对应样本进入后续帧。
|
||||
await call('Input.imeSetComposition',{text:'复',selectionStart:1,selectionEnd:1});
|
||||
await call('Input.insertText',{text:'复制期间输入'});
|
||||
@@ -70,6 +71,7 @@ export async function editDuringCopy({call,evaluate,wait,count}){
|
||||
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;
|
||||
})()`);
|
||||
// frameDelayMs 是 input 回调至第二次 RAF 回调的间隔,不包含 CDP 发命令前的等待,也不是像素实际呈现延迟。
|
||||
// 检查同一原生输入节点/焦点留存及完整文本后缀,复制过程不能重建编辑 DOM 或覆盖用户输入。
|
||||
assert.equal(result.retained,true,'copy discarded native input DOM or focus');
|
||||
assert.ok(result.value.endsWith(count>=10000?'复制期间输入 A B C M T':'复制期间输入 A B C'),'native composition/typing was overwritten');
|
||||
|
||||
@@ -19,7 +19,7 @@ export function createRendererMigrationFixture({id,account,workspace,boards,docu
|
||||
return{states,async handle(request,path,reply,bytes){
|
||||
const state=states.find(value=>path.includes('/materials/'+value.root+'/canvas'));if(!state)return false;
|
||||
const base=`/materials/${state.root}/canvas`,raw=bytes.length?JSON.parse(bytes.toString()):undefined;
|
||||
// 未迁移时 canonical model 返回带 source_versions 的明确 migration_required;此 header 才能开启升级入口。
|
||||
// 未迁移时 canonical model 返回 409,响应正文含 migration_required 和 source_versions;不是靠响应 header 开启升级入口。
|
||||
if(path.endsWith(base+'/model')&&!state.ready){reply(409,{error:'canonical_migration_required',root_id:state.root,document_format_version:1,source_versions:{[state.root]:state.version}});return true;}
|
||||
// 旧快照 GET 返回当前 document;PATCH 校验账户和 If-Match,并禁止 ready 后旧 writer 再写入。
|
||||
if(path.endsWith(base)){
|
||||
@@ -75,6 +75,7 @@ export async function verifyRendererMigration({fixture,call,evaluate,until,port,
|
||||
// 旧编辑器可用条件同时检查升级入口和画布非 inert,避免只凭按钮出现就认为旧写入已放开。
|
||||
const editable=()=>`!!${button('升级画布')}&&!document.querySelector('.react-flow').parentElement.inert`;
|
||||
// 账户/workspace 级持久化迁移键,稍后比较保存的冻结 body 与服务实际收到字节。
|
||||
// 原请求尚未知时保留同一 key/body;只有 terminal not-applied 证明零写入后,显式重新准备才可以取新版本生成新请求。
|
||||
const storageKey=`endoleap_canvas_migration_v1:${account}:${workspace}`;
|
||||
// 五份状态按下方场景依次使用,互不污染版本、回执和旧 PATCH 数量。
|
||||
const[a,b,c,d,e]=fixture.states;
|
||||
|
||||
@@ -107,6 +107,7 @@ export async function measureRenderer({call,evaluate,documents,calls=[],root,par
|
||||
// 捕获复制开始前的原文,用来证明并发输入不进入已经捕获的副本。
|
||||
const sourceText=documents.get(root).elements.get('perf-0').data.text;
|
||||
// 页面脚本以 rAF 采样新 DOM、进度完成、节点峰值、帧间隔,并在支持时观察 longtask;tick 反复采样,read 汇总,stop 取消并断开 observer。
|
||||
// maxMountedNodes 仅是采样帧看到的最大值;prepareMs 是从采样开始到准备提示消失的时间,未见提示时为 null,不是准备阶段独立耗时。
|
||||
await evaluate(`(()=>{
|
||||
const originals=new Set(['perf-source','perf-frame',...Array.from({length:${count}},(_,i)=>'perf-'+i)]),start=performance.now(),tasks=[],gaps=[];
|
||||
let raf,last=start,sawPreparing=false,firstDOM=null,firstFrame=null,localComplete=null,localCompleteFrame=null,prepared=null,maxNodes=0;
|
||||
@@ -125,6 +126,8 @@ export async function measureRenderer({call,evaluate,documents,calls=[],root,par
|
||||
await evaluate('new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve)))');
|
||||
// 完成进度与已同步后再等两帧,要求 renderer 测量出现合批;统计批量大小而非只计渲染总数。
|
||||
const internalsBatches=await evaluate('window.__fixtureInternalsBatches??[]');assert.ok(internalsBatches.some(size=>size>1),'mounted nodes did not share a renderer measurement batch');
|
||||
// 原文档有 count 张卡片、source、frame 和入边;副本增加 count 张卡片、frame′ 和新入边,因此总元素为 2*count+5。
|
||||
// 进度 count+3 还计入新子 canvas,canvas 记录不属于 document.elements,不能拿进度数直接推元素总量。
|
||||
// 停止页面采样并验证服务元素数、复制 canvas 和上级连线。firstPresentedFrame 是检测新 DOM 后的下一次 rAF 近似值,不是 compositor 上屏证明。
|
||||
const measurements=await evaluate('window.__canvasPerformance.stop()'),document=documents.get(root),copiedCanvas=[...document.canvases.values()].find(canvas=>canvas.parent&&canvas.id!==partition);
|
||||
assert.equal(document.elements.size,2*count+5);assert.ok(copiedCanvas);assert.ok([...document.elements.values()].some(element=>element.structure==='connection'&&element.source.elementId==='perf-source'&&element.target.elementId===copiedCanvas.parent.entryElementId));assert.ok(document.elements.has('perf-incoming'));assert.ok(measurements.firstPresentedFrameMs!==null&&measurements.localCompleteFrameMs!==null);if(count>=100)assert.ok(measurements.nodeCount<target,'offscreen DOM was not culled');assert.ok(await evaluate(`document.querySelector('[aria-label="画布导入"]').textContent.includes('${count+3} 项')`),'copy progress did not include all source records');
|
||||
|
||||
Reference in New Issue
Block a user