output-reveal.test.mjs (2763B)
1 import test from 'node:test'; 2 import assert from 'node:assert/strict'; 3 import {revealDelta} from '../dist/output-reveal.js'; 4 test('only an offscreen result start needs revealing, not the whole large result',()=>{ 5 assert.equal(revealDelta(250,10000,140,0,844),0); 6 assert.equal(revealDelta(900,10000,140,0,844),748); 7 assert.equal(revealDelta(100,100,140,0,844),-52); 8 assert.equal(revealDelta(810,100,140,0,844),658); 9 }); 10 test('reveal respects visual viewport offsets and small keyboard-height viewports',()=>{ 11 assert.equal(revealDelta(300,100,100,120,360),0); 12 assert.equal(revealDelta(460,100,100,120,360),228); 13 assert.equal(revealDelta(400,100,140,0,100),0); 14 }); 15 16 test('foreground reveal is once-only, cancelable, mobile-only and never steals focus',async()=>{ 17 const saved=Object.fromEntries(['window','matchMedia','requestAnimationFrame','innerHeight'].map(k=>[k,globalThis[k]])); 18 const frames=[],scrolls=[];let mobile=true,inert=false; 19 try{ 20 const win=new EventTarget();win.visualViewport={offsetTop:0,height:844};win.scrollBy=value=>scrolls.push(value); 21 globalThis.window=win;globalThis.innerHeight=844;globalThis.matchMedia=()=>({matches:mobile});globalThis.requestAnimationFrame=fn=>frames.push(fn); 22 const {outputReveal}=await import('../dist/output-reveal.js'); 23 const controller=outputReveal({getBoundingClientRect:()=>({height:140})}); 24 const target={getBoundingClientRect:()=>({top:900,height:2000}),getClientRects:()=>[{}],closest:()=>inert?{}:null,focus:()=>assert.fail('Must not focus the result')}; 25 const flush=()=>{while(frames.length)frames.shift()(0);}; 26 controller.reveal(target);flush();assert.equal(scrolls.length,0,'Background work never arms reveal'); 27 controller.arm();controller.reveal(target);controller.reveal(target);flush();assert.equal(scrolls.length,1,'Only first result/IO event reveals'); 28 assert.deepEqual(scrolls[0],{top:748,behavior:'instant'}); 29 controller.arm();controller.reveal(target);controller.cancel();flush();assert.equal(scrolls.length,1,'Stop invalidates queued reveal'); 30 for(const event of ['wheel','touchmove','pagehide']){controller.arm();win.dispatchEvent(new Event(event));controller.reveal(target);flush();assert.equal(scrolls.length,1,event);} 31 controller.arm();win.dispatchEvent(Object.assign(new Event('keydown'),{key:'PageDown'}));controller.reveal(target);flush();assert.equal(scrolls.length,1); 32 mobile=false;controller.arm();controller.reveal(target);flush();assert.equal(scrolls.length,1,'Desktop stays put'); 33 mobile=true;inert=true;controller.arm();controller.reveal(target);flush();assert.equal(scrolls.length,1,'Modal settings stays put'); 34 }finally{for(const [key,value]of Object.entries(saved))if(value===undefined)delete globalThis[key];else globalThis[key]=value;} 35 });