Files
saphid--frame-control/tests/page/framey_cache.mjs
T

64 lines
3.6 KiB
JavaScript

import assert from 'node:assert/strict';
import {readFileSync} from 'node:fs';
import vm from 'node:vm';
const html=readFileSync(new URL('../../ui/headset.html',import.meta.url),'utf8');
const parentSource=html.match(/<script>([\s\S]*?)<\/script>/)[1].split('refresh();refreshGames();')[0];
const hostSource=readFileSync(new URL('../../ui/framey-host.js',import.meta.url),'utf8')
.replace(/\n start\(\);\n\}\)\(\);\s*$/, '\n})();');
assert.match(hostSource,/framey:cache:init/);
const values=new Map();
const localStorage={getItem:key=>values.get(key)??null,setItem:(key,value)=>values.set(key,value)};
const elements=new Map();
const el=id=>{if(!elements.has(id))elements.set(id,{id,hidden:false,removeAttribute(){},scrollIntoView(){}});
return elements.get(id);};
const parentListeners=[];
const parent=vm.createContext({document:{getElementById:el,createElement:()=>({})},
location:{hash:'',pathname:'/'},history:{replaceState(){}},
sessionStorage:{getItem:()=>null,setItem(){}},localStorage,
window:{addEventListener:(type,fn)=>parentListeners.push({type,fn})},
setTimeout(){},setInterval(){},fetch(){throw Error('No network expected');},console});
vm.runInContext(parentSource,parent);
vm.runInContext('deckyState={plugins:[{storeId:14}]};openFrameyPanel(14,"Steam Status")',parent);
let session=vm.runInContext('frameySession',parent);
const onParent=parentListeners.find(x=>x.type==='message').fn;
function iframe(){
const listeners=[];const queued=[];let live=false;
const frameWindow={location:{hash:`#${session}`},React:{Fragment:'fragment',Component:class {},createElement:()=>({}),isValidElement:()=>true},
ReactDOM:{createRoot:()=>({render(){}})}};
const proxy={postMessage:message=>listeners.find(x=>x.type==='message')?.fn({source:hostParent,data:message})};
el('frameyFrame').contentWindow=proxy;
const hostParent={postMessage:message=>live?onParent({source:proxy,data:message}):queued.push(message)};
const context=vm.createContext({window:frameWindow,document:{getElementById:()=>({})},
navigator:{language:'en'},parent:hostParent,DOMException,
addEventListener:(type,fn)=>listeners.push({type,fn}),setTimeout(){},clearTimeout(){}});
vm.runInContext(hostSource,context);
for(const message of queued)onParent({source:proxy,data:message});
live=true;
const send=message=>onParent({source:proxy,data:message});
return {storage:frameWindow.localStorage,send};
}
let first=iframe();
assert.equal(first.storage.getItem('status-cache'),null);
first.storage.setItem('status-cache','{"online":true}');
assert.equal(values.get('framey-plugin-cache-v1:14'),'{"status-cache":"{\\"online\\":true}"}');
first.storage.setItem('temporary','discard');
first.storage.removeItem('temporary');
const oldSession=session;
vm.runInContext('openFrameyPanel(14,"Steam Status")',parent);
session=vm.runInContext('frameySession',parent);
assert.notEqual(session,oldSession);
let reopened=iframe();
assert.equal(reopened.storage.getItem('status-cache'),'{"online":true}');
assert.equal(reopened.storage.length,1);
assert.equal(reopened.storage.key(0),'status-cache');
assert.throws(()=>reopened.storage.setItem('huge','x'.repeat(65537)),/Framey plugin cache is full/);
// A malformed or over-limit message from a plugin must not poison saved data.
reopened.send({type:'framey:cache:save',session,entries:{huge:'x'.repeat(65537)}});
assert.equal(values.get('framey-plugin-cache-v1:14'),'{"status-cache":"{\\"online\\":true}"}');
reopened.send({type:'framey:cache:save',session:oldSession,entries:{poison:'yes'}});
assert.equal(values.get('framey-plugin-cache-v1:14'),'{"status-cache":"{\\"online\\":true}"}');
reopened.storage.clear();
assert.equal(values.get('framey-plugin-cache-v1:14'),'{}');