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');