aboutsummaryrefslogtreecommitdiffstats
path: root/tools/feed-pager-ahead-test.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-13 06:17:39 +0000
committerAdam Soltys <[email protected]>2026-10-13 06:17:39 +0000
commit8f80948eb174a273f3b1dea963bb91745bcf323b (patch)
treea8e704dd6896be17df41c7f844e21f18be5fdb92 /tools/feed-pager-ahead-test.js
parentb330cc707fab62365c29b57d414c46aa198febef (diff)
downloadcoinosv3-8f80948eb174a273f3b1dea963bb91745bcf323b.tar.gz
coinosv3-8f80948eb174a273f3b1dea963bb91745bcf323b.zip
Media pager: up and down go from post to post, left and right along a post's pictures. Adam: swipes and arrows up/down should go to the next post's media, left/right to the next within a post. The pager is now posts stacked top to bottom, each a row of its pictures or clips side by side — a quoted post's join the post that quotes it — with dots along the top when a post has more than one, saying how many and which. Up and down (swipe, arrows, j/k, Page) move a post at a time and land on a post where its row was left; left and right (swipe, arrows, h/l) move within the post and stop at its ends. A post's pictures all load as it comes near (those along its row too); a clip's head start goes to the next post's (where its row rests), the next along this one, and the post after next, at half; the look-ahead (the feed paging on, the media crawl) counts posts. Pictures pan sideways as well as up and down at 1×. Tests find the slide on screen by where it sits (the pager's children are posts now); feed-picture-pager walks right within a post, down past one with none, back up to where a post was left, left to its first, and a sideways swipe next to a swipe up; quote-pager walks the quoting post's row (its own picture, then the quote's).
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
Diffstat (limited to 'tools/feed-pager-ahead-test.js')
-rw-r--r--tools/feed-pager-ahead-test.js20
1 files changed, 10 insertions, 10 deletions
diff --git a/tools/feed-pager-ahead-test.js b/tools/feed-pager-ahead-test.js
index 90cbe96..057ac3e 100644
--- a/tools/feed-pager-ahead-test.js
+++ b/tools/feed-pager-ahead-test.js
@@ -70,10 +70,10 @@ const waitFor = async (fn, arg, ms = 10000) => { for (let i = 0; i < ms / 100; i
// the slide the pager rests on, by its picture
const curPic = () => page.evaluate(() => {
const sc = document.querySelector('.mp-scroll'); if (!sc) return null;
- const s = [...sc.querySelectorAll(':scope > .mp-slide')].find((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4);
+ const s = [...sc.querySelectorAll('.mp-slide')].find((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; });
return s ? s.getAttribute('data-k').split('|')[1].split('/').pop() : 'between';
});
-const resting = async (name) => waitFor((n) => { const sc = document.querySelector('.mp-scroll'); if (!sc) return false; const s = [...sc.querySelectorAll(':scope > .mp-slide')].find((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); return !!s && s.getAttribute('data-k').endsWith('/' + n); }, name, 4000);
+const resting = async (name) => waitFor((n) => { const sc = document.querySelector('.mp-scroll'); if (!sc) return false; const s = [...sc.querySelectorAll('.mp-slide')].find((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }); return !!s && s.getAttribute('data-k').endsWith('/' + n); }, name, 4000);
const openPic = async (word) => {
await page.evaluate((w) => { const r = [...document.querySelectorAll('.notes-feed [data-key]')].find((n) => n.textContent.includes(w)); r.scrollIntoView({ block: 'center' }); }, word);
await sleep(400);
@@ -82,12 +82,12 @@ const openPic = async (word) => {
await waitFor(() => !!document.querySelector('.media-pager'), null, 4000);
return at.y0;
};
-const tapPager = async () => { const at = await page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); const s = [...sc.querySelectorAll(':scope > .mp-slide')].find((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4) || sc.firstChild; const r = s.querySelector('img.mp-media').getBoundingClientRect(); return { x: r.x + r.width / 2, y: r.y + r.height / 2 }; }); await page.mouse.click(at.x, at.y); };
+const tapPager = async () => { const at = await page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); const s = [...sc.querySelectorAll('.mp-slide')].find((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }) || sc.firstChild; const r = s.querySelector('img.mp-media').getBoundingClientRect(); return { x: r.x + r.width / 2, y: r.y + r.height / 2 }; }); await page.mouse.click(at.x, at.y); };
const rowTop = (word) => page.evaluate((w) => { const r = [...document.querySelectorAll('.notes-feed [data-key]')].find((n) => n.textContent.includes(w)); return r ? Math.round(r.getBoundingClientRect().top) : null; }, word);
-const slides = () => page.evaluate(() => document.querySelectorAll('.mp-scroll > .mp-slide').length);
-const curIdx = () => page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); if (!sc) return -1; return [...sc.querySelectorAll(':scope > .mp-slide')].findIndex((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); });
-const curName = () => page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); const s = sc && [...sc.querySelectorAll(':scope > .mp-slide')].find((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); return s ? s.getAttribute('data-k').split('/').pop() : null; });
+const slides = () => page.evaluate(() => document.querySelectorAll('.mp-scroll .mp-slide').length);
+const curIdx = () => page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); if (!sc) return -1; return [...sc.querySelectorAll('.mp-slide')].findIndex((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }); });
+const curName = () => page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); const s = sc && [...sc.querySelectorAll('.mp-slide')].find((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }); return s ? s.getAttribute('data-k').split('/').pop() : null; });
try {
await page.setViewport({ width: 390, height: 844 });
@@ -128,11 +128,11 @@ try {
maxSlides = Math.max(maxSlides, n);
if (i >= 0 && n - 1 - i <= 4) {
// give the look-ahead a moment: it should get past three ahead again (or the feed's end)
- await waitFor((k) => { const sc = document.querySelector('.mp-scroll'); const j = [...sc.querySelectorAll(':scope > .mp-slide')].findIndex((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); return sc.querySelectorAll(':scope > .mp-slide').length - 1 - j > 4 || sc.querySelectorAll(':scope > .mp-slide').length >= k; }, TOTAL, 6000);
+ await waitFor((k) => { const sc = document.querySelector('.mp-scroll'); const j = [...sc.querySelectorAll('.mp-slide')].findIndex((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }); return sc.querySelectorAll('.mp-slide').length - 1 - j > 4 || sc.querySelectorAll('.mp-slide').length >= k; }, TOTAL, 6000);
}
const before = await curIdx();
await page.keyboard.press('ArrowDown');
- await waitFor((b) => { const sc = document.querySelector('.mp-scroll'); return [...sc.querySelectorAll(':scope > .mp-slide')].findIndex((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4) > b; }, before, 2000);
+ await waitFor((b) => { const sc = document.querySelector('.mp-scroll'); return [...sc.querySelectorAll('.mp-slide')].findIndex((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }) > b; }, before, 2000);
}
// past the last picture is the pager's end (its loading screen): back up onto the last
if (await page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); const t = sc.querySelector('.mp-tail'); return t && Math.abs(t.offsetTop - sc.scrollTop) < 4; })) { await page.keyboard.press('ArrowUp'); await sleep(800); }
@@ -140,11 +140,11 @@ try {
check('the pager grew as the end came near', grew, first + ' → ' + (await slides()));
check('every picture in the feed became reachable', (await slides()) === TOTAL, (await slides()) + ' of ' + TOTAL);
check('…past fifteen posts with none', (await curName()) === 'p' + (TOTAL - 1) + '.png', await curName());
- check('…in the feed\'s order', await page.evaluate((t) => [...document.querySelectorAll('.mp-scroll > .mp-slide')].every((s, i) => s.getAttribute('data-k').endsWith('/p' + i + '.png')), TOTAL));
+ check('…in the feed\'s order', await page.evaluate((t) => [...document.querySelectorAll('.mp-scroll .mp-slide')].every((s, i) => s.getAttribute('data-k').endsWith('/p' + i + '.png')), TOTAL));
console.log('\n[closing lands the feed on the last picture\'s post]');
await page.keyboard.press('ArrowUp');
- await waitFor((n) => { const sc = document.querySelector('.mp-scroll'); const s = [...sc.querySelectorAll(':scope > .mp-slide')].find((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); return s && s.getAttribute('data-k').endsWith('/p' + n + '.png'); }, TOTAL - 2, 3000);
+ await waitFor((n) => { const sc = document.querySelector('.mp-scroll'); const s = [...sc.querySelectorAll('.mp-slide')].find((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }); return s && s.getAttribute('data-k').endsWith('/p' + n + '.png'); }, TOTAL - 2, 3000);
const want = 'picture number ' + (TOTAL - 2) + ' ';
await page.keyboard.press('Escape');
check('the pager closes', await waitFor(() => !document.querySelector('.media-pager') && !!document.querySelector('.notes-feed'), null, 3000));