File size: 3,036 Bytes
7105d63 2c1d035 7105d63 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | const fs = require('fs');
const vm = require('vm');
const assert = require('assert');
const path = require('path');
const root = process.argv[2] ? path.resolve(process.argv[2]) : path.resolve(__dirname, '..');
const source = require('./reader_source_contract').readReaderSource(root);
const helper = source.match(/function createHeadingLocationCache\(root, entries\) \{[\s\S]*?\n\}/)[0];
const observers = [], listeners = new Map();
class Observer {
constructor(callback) { this.callback = callback; this.records = []; this.closed = false; observers.push(this); }
observe() {}
disconnect() { this.closed = true; }
takeRecords() { return this.records.splice(0); }
}
const add=(name,fn)=>{if(!listeners.has(name))listeners.set(name,new Set());listeners.get(name).add(fn);};
const remove=(name,fn)=>{listeners.get(name)?.delete(fn);if(!listeners.get(name)?.size)listeners.delete(name);};
const region = {clientWidth:400, scrollHeight:10000,
addEventListener:add,removeEventListener:remove,
ownerDocument:{defaultView:{getComputedStyle:node=>({position:node.position||'static'})},
fonts:{addEventListener:add,removeEventListener:remove}}};
const context = {MutationObserver:Observer, ResizeObserver:Observer, documentState:{zoom:100}};
vm.runInNewContext(helper, context);
let reads = 0, scroll = 0;
const entries = Array.from({length:1000}, (_,index)=>({target:{isConnected:true,
getBoundingClientRect:()=>{reads++;return {top:index*10-scroll};}}}));
const cache = context.createHeadingLocationCache(region, entries);
assert.equal(cache.at(8,scroll),0);
assert.equal(reads,1000);
reads=0;
for(let index=1;index<100;index++){scroll=index*10;assert.equal(cache.at(8,scroll),index);}
assert.equal(reads,0);
entries[99].target={isConnected:true,getBoundingClientRect:()=>{reads++;return {top:1005-scroll};}};
assert.equal(cache.at(8,scroll),98);
assert.equal(reads,1000);
reads=0;entries[98].target.isConnected=false;
assert.equal(cache.at(8,scroll),97);
assert.equal(reads,999);
const unordered=[entries[500],entries[10],entries[200],entries[200]];
const alternative=context.createHeadingLocationCache(region,unordered);
scroll=2000;
assert.equal(alternative.at(0,scroll),3);
scroll=0;assert.equal(alternative.at(0,scroll),1);
for(const change of [()=>observers[0].records.push({}),()=>listeners.get('loadingdone').forEach(fn=>fn()),
()=>region.clientWidth++,()=>context.documentState.zoom++]) {
reads=0;change();cache.at(8,scroll);assert.equal(reads,999);
}
cache.dispose();alternative.dispose();
const pinned=context.createHeadingLocationCache(region,[
{target:{isConnected:true,position:'fixed',getBoundingClientRect:()=>({top:0})}},
{target:{isConnected:true,getBoundingClientRect:()=>({top:500-scroll})}}]);
scroll=0;assert.equal(pinned.at(8,scroll),0);
scroll=510;assert.equal(pinned.at(8,scroll),0);
pinned.dispose();
assert.ok(observers.every(observer=>observer.closed));
assert.equal(listeners.size,0);
console.log('heading geometry reuse, unordered ties, mutation/layout invalidation and cleanup passed');
|