diff options
| author | Adam Soltys <[email protected]> | 2026-09-14 12:59:57 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-14 12:59:57 -0700 |
| commit | d730ea49feb389b43362b1044964f5a9e03ae4ff (patch) | |
| tree | 5f99d011d6c994b57f45d657d3eb8868e1d4ded8 /tools | |
| parent | dd1489303f59ccef6b322142f94e08d750c06e79 (diff) | |
| download | coinosv3-d730ea49feb389b43362b1044964f5a9e03ae4ff.tar.gz coinosv3-d730ea49feb389b43362b1044964f5a9e03ae4ff.zip | |
A post arrives whole.
A feed row painted the moment its event landed and then finished itself
over the next second or two: a punk turning into a photograph, a blank
gap becoming the picture. Now a post waits at the door until its author's
profile has landed and their face and the images in its body are fetched
and decoded — bounded by two seconds, so a slow host holds a post briefly
rather than the feed forever. This covers the first load, live arrivals
(which are counted behind the pill only once whole), paging older posts,
and profile pages. The cached page at boot still paints instantly and
warms in the background.
feed-live-test serves a deliberately slow picture and checks, from the
first instant the row exists, that its image is already decoded.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QYJfZ4A1TDCEY4Foq3CApR
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/feed-live-test.js | 39 |
1 files changed, 37 insertions, 2 deletions
diff --git a/tools/feed-live-test.js b/tools/feed-live-test.js index fd50191..afbbf10 100644 --- a/tools/feed-live-test.js +++ b/tools/feed-live-test.js @@ -30,7 +30,15 @@ const signed = (content, ago) => finalizeEvent( const seeded = Array.from({ length: 25 }, (_, i) => signed('post number ' + (i + 1), 600 + i * 60)); const html = await buildHtml({ minify: true, pwa: false }); -const server = Bun.serve({ port: 5296, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); +// a picture that takes its time, so a post showing it must have waited +const PNG = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==', 'base64'); +const server = Bun.serve({ port: 5296, fetch: async (req) => { + if (new URL(req.url).pathname === '/slow.png') { + await sleep(700); + return new Response(PNG, { headers: { 'content-type': 'image/png', 'cache-control': 'no-store' } }); + } + return new Response(html, { headers: { 'content-type': 'text/html' } }); +} }); const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); await page.evaluateOnNewDocument(() => { @@ -111,7 +119,10 @@ try { const fresh = signed('BRAND NEW POST', 0); const delivered = await page.evaluate((ev) => window.__inject(ev), fresh); check('...a post really arrives on a live subscription', delivered > 0, delivered + ' socket(s)'); - await sleep(900); + // A post waits at the door for its author's face and its pictures (or the + // two-second deadline, for a face nobody has) before it is even counted — + // so give it that long, and no longer. + await sleep(2600); const after = await page.evaluate(() => ({ y: Math.round(window.scrollY), pill: (document.querySelector('.feed-new-pill') || {}).textContent || '', @@ -145,6 +156,30 @@ try { await page.evaluate(() => /ANOTHER ONE/.test(document.querySelector('.notes-feed')?.innerText || '') && !document.querySelector('.feed-new-pill'))); + console.log('\n[a post arrives whole]'); + // A row used to paint first and fill in its picture after. Watch the feed + // from the instant the post is injected: the first time the row exists, + // its image must already be decoded. + await page.evaluate(() => { + window.__firstSight = null; + const feed = document.querySelector('.notes-feed'); + const look = () => { + if (window.__firstSight) return; + const row = [...feed.querySelectorAll('.row')].find((n) => /PICTURE POST/.test(n.innerText || '')); + if (!row) return; + const img = row.querySelector('img.note-img'); + window.__firstSight = { img: !!img, ready: !!(img && img.complete && img.naturalWidth > 0), at: performance.now() }; + }; + new MutationObserver(look).observe(feed, { childList: true, subtree: true }); + }); + const t0 = await page.evaluate(() => performance.now()); + await page.evaluate((ev) => window.__inject(ev), signed('PICTURE POST http://localhost:5296/slow.png?' + Date.now(), 0)); + await sleep(3000); + const sight = await page.evaluate(() => window.__firstSight); + check('the post is in the feed', !!sight && sight.img, JSON.stringify(sight)); + check('...and its picture was already decoded the first time the row existed', !!sight && sight.ready); + check('...having waited for the slow host', !!sight && sight.at - t0 >= 700, sight ? Math.round(sight.at - t0) + 'ms' : ''); + console.log('\n[coming back after being away]'); // the throttle would otherwise swallow a refresh this soon after the last // the app was hidden for a while, then focused again |
