mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
From the second IC8 pass (launch ready; P3s closed here): - A lost WebGL context is redrawn on a new canvas (the old one only gives back its lost context), through the core, and only if no newer photo is opening; verified live with WEBGL_lose_context, twice. - The 16 MB answer cap counts bytes while reading: KartaView sends chunked answers with no Content-Length, so the header check never fired. - A lookup gets 8 s, then 15 s once more: KartaView sometimes sits on a lookup for 11 s and answers the same one in 3 s. Left as follow-ups: cancelling abandoned sequence pages; finding a page by binary search on the index when thousands of photos are missing. Co-Authored-By: Claude Opus 5.5 <[email protected]>
82 lines
2.4 KiB
JavaScript
82 lines
2.4 KiB
JavaScript
// DOM stand-ins for the KartaView viewer's tests (no browser in Node).
|
|
|
|
/** A DOM element with what the viewer touches: children, events, attributes. */
|
|
export function fakeElement(tag) {
|
|
const listeners = new Map();
|
|
const element = {
|
|
tagName: tag.toUpperCase(),
|
|
children: [],
|
|
parentNode: null,
|
|
style: {},
|
|
dataset: {},
|
|
attributes: {},
|
|
hidden: false,
|
|
disabled: false,
|
|
clientWidth: 400,
|
|
clientHeight: 250,
|
|
classList: {
|
|
values: new Set(),
|
|
add: (name) => element.classList.values.add(name),
|
|
remove: (name) => element.classList.values.delete(name),
|
|
},
|
|
append(...nodes) {
|
|
for (const node of nodes) {
|
|
node.parentNode = element;
|
|
element.children.push(node);
|
|
}
|
|
},
|
|
remove() {
|
|
const list = element.parentNode?.children;
|
|
if (list) list.splice(list.indexOf(element), 1);
|
|
element.parentNode = null;
|
|
},
|
|
replaceWith(next) {
|
|
const parent = element.parentNode;
|
|
if (!parent) return;
|
|
parent.children[parent.children.indexOf(element)] = next;
|
|
next.parentNode = parent;
|
|
next.ownerDocument = element.ownerDocument;
|
|
element.parentNode = null;
|
|
},
|
|
setAttribute: (name, value) => (element.attributes[name] = value),
|
|
removeAttribute: (name) => {
|
|
delete element.attributes[name];
|
|
if (name === 'src') element.src = '';
|
|
},
|
|
addEventListener: (type, handler) => listeners.set(type, handler),
|
|
removeEventListener: (type) => listeners.delete(type),
|
|
setPointerCapture() {},
|
|
/** Fire an event at the element's listener. */
|
|
fire(type, event = {}) {
|
|
return listeners.get(type)?.({
|
|
preventDefault() {
|
|
this.defaultPrevented = true;
|
|
},
|
|
stopPropagation() {},
|
|
...event,
|
|
});
|
|
},
|
|
};
|
|
return element;
|
|
}
|
|
|
|
export function fakeHost() {
|
|
const doc = { createElement: (tag) => fakeElement(tag) };
|
|
const host = fakeElement('div');
|
|
host.ownerDocument = doc;
|
|
return host;
|
|
}
|
|
|
|
/**
|
|
* Wait, a macrotask at a time, until `condition` holds; false if it never
|
|
* does within `ticks`. A test asserting that something did not happen waits
|
|
* on a sign that the work is done instead of a fixed count of ticks.
|
|
*/
|
|
export async function until(condition, { ticks = 200 } = {}) {
|
|
for (let i = 0; i < ticks; i++) {
|
|
if (await condition()) return true;
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
}
|
|
return Boolean(await condition());
|
|
}
|