diff options
| author | Adam Soltys <[email protected]> | 2026-10-13 06:17:39 +0000 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-13 06:17:39 +0000 |
| commit | 8f80948eb174a273f3b1dea963bb91745bcf323b (patch) | |
| tree | a8e704dd6896be17df41c7f844e21f18be5fdb92 /tools/feed-picture-pager-test.js | |
| parent | b330cc707fab62365c29b57d414c46aa198febef (diff) | |
| download | coinosv3-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-picture-pager-test.js')
| -rw-r--r-- | tools/feed-picture-pager-test.js | 35 |
1 files changed, 21 insertions, 14 deletions
diff --git a/tools/feed-picture-pager-test.js b/tools/feed-picture-pager-test.js index 6c45756..32625c9 100644 --- a/tools/feed-picture-pager-test.js +++ b/tools/feed-picture-pager-test.js @@ -44,10 +44,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.children].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.children].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); @@ -56,7 +56,7 @@ 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.children].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); try { @@ -90,25 +90,31 @@ try { check('one tap closes it', await waitFor(() => !document.querySelector('.media-pager'), null, 3000)); check('the feed is where it was', Math.abs((await page.evaluate(() => window.scrollY)) - y0) <= 2); - console.log('\n[arrows walk the pictures, a post\'s own first]'); + console.log('\n[left and right: along a post\'s pictures; up and down: post to post]'); await openPic('two pictures'); + check('a post with two pictures shows two dots, the first lit', await page.evaluate(() => { const d = document.querySelector('.mp-group .mp-dots'); return d && d.children.length === 2 && d.children[0].classList.contains('on'); })); + await page.keyboard.press('ArrowRight'); + check('right: the post\'s second picture', await resting('a2.png'), await curPic()); + check('…its dot lit', await page.evaluate(() => { const d = document.querySelector('.mp-group .mp-dots'); return d.children[1].classList.contains('on') && !d.children[0].classList.contains('on'); })); + await page.keyboard.press('ArrowRight'); await sleep(600); + check('right at the post\'s last: stays (left and right stay in the post)', (await curPic()) === 'a2.png', await curPic()); await page.keyboard.press('ArrowDown'); - check('down: the post\'s second picture', await resting('a2.png'), await curPic()); + check('down: the next post\'s picture', await resting('b.png'), await curPic()); await page.keyboard.press('ArrowDown'); - check('down again: the next post\'s picture', await resting('b.png'), await curPic()); - await page.keyboard.press('ArrowRight'); - check('right: the next picture, past a post with none', await resting('d.png'), await curPic()); - await page.keyboard.press('ArrowLeft'); - check('left: back one', await resting('b.png'), await curPic()); + check('down again: the next, past a post with none', await resting('d.png'), await curPic()); await page.keyboard.press('ArrowUp'); - check('up: back one more', await resting('a2.png'), await curPic()); + check('up: back one post', await resting('b.png'), await curPic()); + await page.keyboard.press('ArrowUp'); + check('up again: the first post, on the picture it was left at', await resting('a2.png'), await curPic()); + await page.keyboard.press('ArrowLeft'); + check('left: its first picture', await resting('a1.png'), await curPic()); check('no history entry per step', await page.evaluate(() => history.length) < 50); console.log('\n[moved on: a tap shows the post, the next closes onto it]'); await page.keyboard.press('ArrowDown'); await resting('b.png'); await tapPager(); await sleep(400); check('the first tap shows the details', await page.evaluate(() => document.querySelector('.media-pager')?.classList.contains('show'))); - check('…with the author and the words', await page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); const s = [...sc.children].find((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); return /Picture Poster/.test(s.textContent) && /one picture/.test(s.textContent); })); + check('…with the author and the words', 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; }); return /Picture Poster/.test(s.textContent) && /one picture/.test(s.textContent); })); await tapPager(); check('the second tap closes it', await waitFor(() => !document.querySelector('.media-pager') && !!document.querySelector('.notes-feed'), null, 3000)); await waitFor((w) => { const r = [...document.querySelectorAll('.notes-feed [data-key]')].find((n) => n.textContent.includes(w)); return r && r.getBoundingClientRect().top < 120 && r.getBoundingClientRect().top > -10; }, 'one picture', 3000); @@ -117,9 +123,10 @@ try { console.log('\n[a swipe, then Escape]'); await page.evaluate(() => window.scrollTo(0, 0)); await sleep(300); await openPic('two pictures'); + await page.evaluate(() => { const r = document.querySelector('.mp-row'); r.scrollTo({ left: r.clientWidth }); }); // what a swipe to the left does + check('a sideways swipe lands on the post\'s next picture', await resting('a2.png'), await curPic()); await page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); sc.scrollTo({ top: sc.clientHeight }); }); // what a swipe up does - check('the swipe lands on the next picture', await resting('a2.png'), await curPic()); - await page.keyboard.press('ArrowDown'); await resting('b.png'); + check('a swipe up lands on the next post\'s', await resting('b.png'), await curPic()); await page.keyboard.press('ArrowDown'); await resting('d.png'); await page.keyboard.press('Escape'); check('Escape closes it', await waitFor(() => !document.querySelector('.media-pager') && !!document.querySelector('.notes-feed'), null, 3000)); |
