aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-08 13:50:44 -0700
committerAdam Soltys <[email protected]>2026-10-08 13:50:44 -0700
commite76d643835c45f6624856850137e71e986315eb0 (patch)
treea84e1ffffe4389f4a55c0fa797da6ab24c4e0df9 /tools
parent175e513273bfb2a362f9a08e3da5d5460a1d91c0 (diff)
downloadcoinosv3-e76d643835c45f6624856850137e71e986315eb0.tar.gz
coinosv3-e76d643835c45f6624856850137e71e986315eb0.zip
Prefetch visible post threads after a bounded reading dwell
Diffstat (limited to 'tools')
-rw-r--r--tools/thread-cache-test.js18
-rw-r--r--tools/thread-prefetch-test.js31
2 files changed, 49 insertions, 0 deletions
diff --git a/tools/thread-cache-test.js b/tools/thread-cache-test.js
index 6bac353..67b191b 100644
--- a/tools/thread-cache-test.js
+++ b/tools/thread-cache-test.js
@@ -139,6 +139,24 @@ try {
assert.equal(restored.root.id, r.id);
assert.equal(restored.replies.length, 2);
});
+ for (const clickWhileLoading of [false, true]) {
+ const warm = makeFeature(), seed = note(clickWhileLoading ? 501 : 500);
+ const answer = note(clickWhileLoading ? 511 : 510, [['e', seed.id, '', 'root']]);
+ requests = [];
+ const prefetched = warm.feature.testThread(seed, true);
+ await wait(() => waiting.length === 1);
+ if (clickWhileLoading) assert.equal(warm.feature.testThread(seed), prefetched);
+ for (const q of waiting.splice(0)) q.resolve([answer]);
+ await prefetched.loading;
+ if (!clickWhileLoading) assert.equal(store.find(seed.id), null, 'speculation does not evict persisted visits');
+ const opened = warm.feature.testThread(seed);
+ assert.equal(opened, prefetched);
+ assert.equal(opened.status, 'ready');
+ assert.equal(opened.replies[0].id, answer.id);
+ assert.equal(requests.length, 1, 'click shares the existing relay query');
+ assert.equal(store.find(seed.id).replies[0].id, answer.id);
+ }
+ console.log('✓ clicks reuse completed and in-flight prefetches and persist visited threads');
const deepLink = makeFeature();
requests = [];
await deepLink.feature.testOpen({ id: e.id });
diff --git a/tools/thread-prefetch-test.js b/tools/thread-prefetch-test.js
new file mode 100644
index 0000000..6329e64
--- /dev/null
+++ b/tools/thread-prefetch-test.js
@@ -0,0 +1,31 @@
+import assert from 'node:assert/strict';
+import { createThreadPrefetch } from '../src/thread-prefetch.js';
+let time = 0, task, calls = [], rows = [], resolve;
+const cached = new Set();
+const doc = { hidden: false, documentElement: { clientHeight: 800 }, querySelectorAll: () => rows };
+const nav = { onLine: true, connection: {} };
+const p = createThreadPrefetch({ doc, nav, now: () => time,
+ schedule: (fn) => { task = fn; return 1; }, cancel: () => { task = null; },
+ key: (e) => e.id, cached: (e) => cached.has(e.id),
+ load: (e) => { calls.push(e.id); cached.add(e.id); return new Promise((r) => { resolve = r; }); },
+});
+const row = (id, top = 0, height = 300) => ({ id, top, height,
+ getAttribute() { return this.id; },
+ getBoundingClientRect() { return { top: this.top, bottom: this.top + this.height, height: this.height }; },
+});
+const tick = async (ms = 250) => { time += ms; const fn = task; task = null; fn?.(); await Promise.resolve(); await Promise.resolve(); await Promise.resolve(); };
+const watch = (...rs) => { rows = rs; for (const r of rs) p.watch({ id: r.id }); };
+const dwell = async () => { for (let i = 0; i < 7; i++) await tick(); };
+watch(row('passing')); await tick(); rows = []; await dwell(); assert.deepEqual(calls, []);
+watch(row('moving')); for (let i = 0; i < 8; i++) { rows[0].top += 30; await tick(); } assert.deepEqual(calls, []);
+watch(row('hidden')); doc.hidden = true; await dwell(); assert.deepEqual(calls, []); doc.hidden = false;
+nav.connection.saveData = true; await dwell(); assert.deepEqual(calls, []); nav.connection.saveData = false;
+watch(row('tall', -200, 1800), row('second', 100)); await dwell(); assert.deepEqual(calls, ['tall']);
+await dwell(); assert.deepEqual(calls, ['tall'], 'only one in-flight fetch');
+resolve(); await tick(); await tick(); assert.deepEqual(calls, ['tall', 'second']);
+resolve(); await tick(); await dwell(); assert.equal(calls.length, 2, 'cached posts are reused');
+for (let i = 0; i < 5; i++) { watch(row('rate' + i)); await dwell(); resolve(); await tick(); }
+assert.equal(calls.length, 6, 'six starts per minute');
+await tick(60000); assert.equal(calls.length, 7); resolve(); await tick();
+watch(row('cancelled')); p.stop(); await dwell(); assert.equal(calls.length, 7);
+console.log('✓ dwell, fast scrolling, unmount, hidden tab, data saver, tall posts, concurrency, cache, rate limit and stop');