From 064a3d7c194ff87db48a5142e3cbd6cb690c152a Mon Sep 17 00:00:00 2001 From: Olive Vaughn Date: Sun, 4 Oct 2026 02:14:02 -0400 Subject: [PATCH] Cover large timeline keyframe selections in the browser suite --- frontend/test/browser/automation.mjs | 42 +++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/frontend/test/browser/automation.mjs b/frontend/test/browser/automation.mjs index 05767ea..b5f5e7a 100644 --- a/frontend/test/browser/automation.mjs +++ b/frontend/test/browser/automation.mjs @@ -247,8 +247,48 @@ try { await evaluate(`document.querySelector('.tl-tracks').dispatchEvent(new KeyboardEvent('keydown',{key:'a',metaKey:true,bubbles:true}))`); await sleep(100); assert.equal(await evaluate('document.querySelectorAll("button.tl-key.selected").length'), 4, 'Command-A selects all visible keyframes'); + await evaluate(`(() => { + const k=cljs.core.keyword,v=cljs.core.vector,m=cljs.core.hash_map; + let c=cljs.core.get(arthur.footage.store.entry(cljs.core.get(cljs.core.deref(re_frame.db.app_db),k('clip/current'))),k('clip')); + let keys=m(); + for(let i=0;i<1000;i++) keys=cljs.core.assoc(keys,i,v(i,0)); + for(const channel of ['pos','scale']) c=cljs.core.assoc_in(c,v(k('symbols'),k('main'),k('nodes'),k('a'),k('channels'),v(k('xform'),k(channel))),arthur.domain.channel.keyed(keys,k('linear'))); + c=cljs.core.assoc_in(c,v(k('symbols'),k('main'),k('frames')),1200); + const id=arthur.footage.store.install_BANG_(m(k('clip'),c,k('store'),m()),'large-selection-browser'); + cljs.core.swap_BANG_(re_frame.db.app_db,db=>cljs.core.assoc(db,k('clip/current'),id)); + })()`); + await sleep(300); + assert.equal(await evaluate('document.querySelectorAll("button.tl-key").length'),2000); + await evaluate(`(() => { + window.shiftCalls=0; + window.originalShifted=arthur.domain.keyframes.shifted; + arthur.domain.keyframes.shifted=(...args)=>{window.shiftCalls++;return window.originalShifted(...args);}; + document.querySelector('.tl-tracks').dispatchEvent(new KeyboardEvent('keydown',{key:'a',metaKey:true,bubbles:true})); + })()`); + await sleep(150); + assert.equal(await evaluate('document.querySelectorAll("button.tl-key.selected").length'),2000); + assert.equal(await evaluate('window.shiftCalls'),0,'selection performs no group shift calculations per marker'); + await evaluate(`(() => { + const key=document.querySelector('button.tl-key'), b=key.parentElement.getBoundingClientRect(); + key.dispatchEvent(new PointerEvent('pointerdown',{bubbles:true,pointerId:90,clientX:b.left})); + key.dispatchEvent(new PointerEvent('pointermove',{bubbles:true,pointerId:90,clientX:b.left+b.width*2/1200})); + })()`); + await sleep(150); + assert.equal(await evaluate('window.shiftCalls'),1,'large group drag computes shifted positions once for all 2000 markers'); + await evaluate(`(() => { + const key=document.querySelector('button.tl-key'); + key.dispatchEvent(new PointerEvent('pointerup',{bubbles:true,pointerId:90})); + arthur.domain.keyframes.shifted=window.originalShifted; + const k=cljs.core.keyword,v=cljs.core.vector; + const c=arthur.footage.store.entry(cljs.core.get(cljs.core.deref(re_frame.db.app_db),k('clip/current'))); + return cljs.core.clj__GT_js(cljs.core.sort(cljs.core.keys(cljs.core.get_in(c, + v(k('clip'),k('symbols'),k('main'),k('nodes'),k('a'),k('channels'),v(k('xform'),k('pos')),k('keys')))))); + })()`).then(keys=>assert.equal(keys[0],2,'drop commits before the pointerup handler returns')); + await sleep(150); + assert.equal(await evaluate('document.querySelectorAll("button.tl-key.selected").length'),2000,'drop preserves the entire large selection'); + assert.equal(await evaluate('[...document.querySelectorAll("button.tl-key")].filter(el=>el.style.transform).length'),0,'committed keys replace the preview without leftover transforms'); assert.equal(errors.length, 0, JSON.stringify(errors)); - console.log('PASS: passive overview ticks; automation keyframe selection, dragging, marquee, keyboard editing, and undo'); + console.log('PASS: passive overview ticks; automation editing, group edits, undo, and 2000-key selection performance'); } finally { if (ws?.readyState === WebSocket.OPEN) ws.close(); chrome.kill('SIGTERM');