diff options
| author | Adam Soltys <[email protected]> | 2026-09-23 22:51:58 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-23 22:51:58 -0700 |
| commit | 75679fdd5d24d36e3c54316e88ccefcf954afc82 (patch) | |
| tree | c86a369455da3c2e9532d51d2e72898afda8fccc /tools/feed-window-test.js | |
| parent | fb170b4783c9b49c73e926c08aba9bc4d1bb418e (diff) | |
| download | coinosv3-75679fdd5d24d36e3c54316e88ccefcf954afc82.tar.gz coinosv3-75679fdd5d24d36e3c54316e88ccefcf954afc82.zip | |
Feed: threads cached locally, posts never inserted under the reader, rows keep their first look.
Work by Codex, wrapped up and verified here.
Threads: a viewed conversation is persisted (thread-cache.js, up to 12
threads / 160 events), so a refresh or a cached note link shows the root
and ancestors at once instead of the selected reply and a spinner; relays
are re-asked in the background and an empty answer keeps the cache.
Feed window (feed-window.js): arrivals are merged without inserting
between visible rows — a gap fill under the reader's eyes waits until
that part of the feed is offscreen, prepends anchor the reading position,
the notice counts what went in above, and only its tap goes to the top.
Older pages are prepared a few screens ahead; a page's pictures and faces
are decoded before it is shown.
Rows keep the presentation they were admitted with (feedPaint): background
profile and quote refreshes no longer rewrite text or swap faces mid-read;
media dimensions are known before paint. Thumbnails queue instead of being
dropped past the first three.
Tests: thread-cache, feed-window, feed-stability; feed-live updated for
the new arrival rules (the notice counts up while you read).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'tools/feed-window-test.js')
| -rw-r--r-- | tools/feed-window-test.js | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/tools/feed-window-test.js b/tools/feed-window-test.js new file mode 100644 index 0000000..64e8364 --- /dev/null +++ b/tools/feed-window-test.js @@ -0,0 +1,55 @@ +// Geometry/state regressions that don't require browser or network access. +// Run: bun tools/feed-window-test.js +import assert from 'node:assert/strict'; +import { mergeFeedWindow } from '../src/feed-window.js'; +const note = (id, at) => ({ id, created_at: at }); +const notes = [note('a', 100), note('b', 90), note('c', 80), note('d', 70)]; +const rows = (top) => notes.map((e, i) => ({ id: e.id, top: top + i * 200, bottom: top + (i + 1) * 200 })); +const above = note('above', 110), gap = note('gap', 95), below = note('below', 60); +const check = (name, fn) => { fn(); console.log('✓ ' + name); }; +check('header-visible arrivals wait without changing the displayed window', () => { + const r = mergeFeedWindow(notes, 4, [above, gap], { rows: rows(180), height: 844 }); + assert.equal(r.notes, notes); + assert.equal(r.shown, 4); + assert.deepEqual(r.deferred, [above, gap]); +}); +check('offscreen prepends preserve every displayed post; visible gap fills wait', () => { + const r = mergeFeedWindow(notes, 4, [above, gap, below], { rows: rows(-50), height: 844 }); + assert.deepEqual(r.added, [above, below]); + assert.deepEqual(r.deferred, [gap]); + assert.deepEqual(r.notes.slice(0, r.shown).map((e) => e.id), ['above', 'a', 'b', 'c', 'd']); +}); +check('a deferred gap becomes available once it is above the viewport', () => { + const r = mergeFeedWindow(notes, 4, [gap], { rows: rows(-450), height: 844 }); + assert.deepEqual(r.deferred, []); + assert.deepEqual(r.notes.map((e) => e.id), ['a', 'gap', 'b', 'c', 'd']); +}); +check('hidden posts cannot allow an insertion between visible posts', () => { + const r = mergeFeedWindow([notes[0], note('hidden', 97), ...notes.slice(1)], 4, [note('gap', 98)], { rows: rows(-50), height: 844 }); + assert.equal(r.added.length, 0); + assert.equal(r.deferred.length, 1); +}); +check('equal timestamps retain their order and obey the visible-gap rule', () => { + const r = mergeFeedWindow(notes, 4, [note('equal', 100)], { rows: rows(-50), height: 844 }); + assert.equal(r.added.length, 0); + assert.equal(r.deferred[0].id, 'equal'); +}); +check('large catch-ups retain the reader at the cache boundary', () => { + const old = Array.from({ length: 200 }, (_, i) => note('old' + i, 1000 - i)); + const news = Array.from({ length: 80 }, (_, i) => note('new' + i, 2000 - i)); + const r = mergeFeedWindow(old, 200, news); + assert.equal(r.shown, 280); + assert.equal(r.notes.at(-1).id, 'old199'); +}); +check('older-page requests still advance beyond the cache boundary', () => { + const old = Array.from({ length: 200 }, (_, i) => note('old' + i, 1000 - i)); + const older = Array.from({ length: 40 }, (_, i) => note('older' + i, 500 - i)); + const r = mergeFeedWindow(old, 200, older); + assert.equal(r.notes.length, 220); + assert.equal(r.notes[200].id, 'older0'); +}); +check('duplicate arrivals cannot create repeated rows', () => { + const r = mergeFeedWindow(notes, 4, [notes[0], above, above]); + assert.equal(r.notes.length, 5); + assert.deepEqual(r.added, [above]); +}); |
