diff options
| author | Adam Soltys <[email protected]> | 2026-09-26 07:50:39 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-26 07:50:39 -0700 |
| commit | a4f13b845b9dfb364bdac0932916d99c381db20e (patch) | |
| tree | 16a06b0f3b19153056f329ef156d813e02eb2abf /tools | |
| parent | acac2431b592600be2c37878851cf8e16b6ceba3 (diff) | |
| download | coinosv3-a4f13b845b9dfb364bdac0932916d99c381db20e.tar.gz coinosv3-a4f13b845b9dfb364bdac0932916d99c381db20e.zip | |
Thread: the reply box appears on Reply, full width, with its tools on a row beneath.
A thread opened to read came with an empty field under its first post.
The box now appears under the note whose Reply was tapped (or while a
draft is pending), folds away once the reply is sent or Cancel is tapped,
and takes Amethyst's shape: the field across the full width, attach and
preview on a row beneath it with Cancel and Reply at the right.
Tests: thread-reply's helper follows the new shape and expects the box to
fold after sending; compose-preview and feed-list tap Reply before
expecting a box (the thread card carries .thread-card as the marker).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/compose-preview-test.js | 5 | ||||
| -rw-r--r-- | tools/feed-list-test.js | 6 | ||||
| -rw-r--r-- | tools/thread-reply-test.js | 6 |
3 files changed, 11 insertions, 6 deletions
diff --git a/tools/compose-preview-test.js b/tools/compose-preview-test.js index 08a94e3..d33dcdc 100644 --- a/tools/compose-preview-test.js +++ b/tools/compose-preview-test.js @@ -117,8 +117,11 @@ try { console.log(' — no post in the feed to reply to, skipping the reply half'); } else { await sleep(2500); + check('a thread opened to read has no reply box yet', !(await page.$('.thread-reply-input'))); + await page.evaluate(() => { const b = [...document.querySelectorAll('.note-act')].find((x) => x.getAttribute('aria-label') === 'Reply'); if (b) b.click(); }); + await sleep(400); const box = await page.$('.thread-reply-input'); - check('a thread has a reply box', !!box); + check('Reply opens the box', !!box); if (box) { await type('.thread-reply-input', 'my answer https://coinos.io/punks/3.webp'); await sleep(300); diff --git a/tools/feed-list-test.js b/tools/feed-list-test.js index ac6bbc1..bd88846 100644 --- a/tools/feed-list-test.js +++ b/tools/feed-list-test.js @@ -130,7 +130,7 @@ try { let np = null; try { np = decode(p2.slice(1)); } catch {} check('the address bar names the person (npub)', p2.startsWith('/npub1') && np && np.data === AUTHOR, p2.slice(0, 30)); await page.goBack(); await sleep(600); - check('Back returns to the thread and its address', (await path()) === p1 && await page.evaluate(() => !!document.querySelector('[data-focus-note], .thread-reply-input'))); + check('Back returns to the thread and its address', (await path()) === p1 && await page.evaluate(() => !!document.querySelector('.thread-card'))); await page.goForward(); await sleep(600); check('the profile has an Add to a list button', await page.evaluate(() => !![...document.querySelectorAll('button')].find((b) => b.getAttribute('aria-label') === 'Add to a list'))); await page.evaluate(() => [...document.querySelectorAll('button')].find((b) => b.getAttribute('aria-label') === 'Add to a list').click()); await sleep(400); @@ -161,7 +161,7 @@ try { await page.evaluate(() => [...document.querySelectorAll('.feed-chip')].find((c) => c.textContent.trim() === 'Following').click()); await sleep(800); await page.evaluate(() => { const r = [...document.querySelectorAll('[data-zap-post]')].find((n) => /a post to open/.test(n.textContent)); r.click(); }); await sleep(800); await page.reload({ waitUntil: 'domcontentloaded' }); - check('reloading a thread lands on the thread, at its address', await waitFor(() => !!document.querySelector('.thread-reply-input'), 20000) && (await path()) === p1, await path()); + check('reloading a thread lands on the thread, at its address', await waitFor(() => !!document.querySelector('.thread-card'), 20000) && (await path()) === p1, await path()); check('...with its post', await waitText('a post to open', 5000)); await page.evaluate(() => { const r = [...document.querySelectorAll('[data-zap-post]')].find((n) => /a post to open/.test(n.textContent)); [...r.querySelectorAll('span')].find((s) => s.style.cursor === 'pointer' && s.style.fontWeight === '600').click(); }); await sleep(800); await page.reload({ waitUntil: 'domcontentloaded' }); @@ -169,7 +169,7 @@ try { const hist = () => page.evaluate(() => JSON.stringify({ len: history.length, i: history.state && history.state.i, prof: !!(history.state && history.state.nav && history.state.nav.profilePk), th: !!(history.state && history.state.nav && history.state.nav.noteThread), over: history.state && history.state.nav && history.state.nav.profOverThread, path: location.pathname.slice(0, 12) })); const before = await hist(); await page.goBack(); await sleep(800); - check('Back from it is the thread again', (await path()) === p1 && await page.evaluate(() => !!document.querySelector('.thread-reply-input')), before + ' -> ' + await hist()); + check('Back from it is the thread again', (await path()) === p1 && await page.evaluate(() => !!document.querySelector('.thread-card')), before + ' -> ' + await hist()); check('no page errors', errs.length === 0, errs.slice(0, 2).join(' | ')); } finally { await browser.close(); server.stop(true); } console.log(ok ? '\n✅ lists: a feed of people is a nostr list; posts and people are links' : '\n❌ failed'); diff --git a/tools/thread-reply-test.js b/tools/thread-reply-test.js index 37c22b4..4362468 100644 --- a/tools/thread-reply-test.js +++ b/tools/thread-reply-test.js @@ -65,7 +65,7 @@ page.on('request', r => r.url().startsWith(server.url.origin) ? r.continue() : r const check = (name, value) => { assert(value, name); console.log('✓ ' + name); }; const inline = () => page.evaluate(() => { const input = document.querySelector('.thread-reply-input'); - const box = input?.parentElement.parentElement; + const box = input?.closest('.thread-reply'); // the field sits directly in the box now, tools on a row beneath return !!input && box.previousElementSibling?.dataset.zapPost === test.ui.noteThread.focusId; }); try { @@ -81,7 +81,9 @@ try { await page.waitForFunction(() => test.waiting.length === 2); await page.evaluate(() => test.release()); await page.waitForFunction(() => document.body.innerText.includes('Root post')); - check('a relay result omitting the tapped reply keeps it and its composer', await inline()); + // the composer folds away once the reply is sent; the tapped reply itself stays + check('a relay result omitting the tapped reply keeps it (composer folded after sending)', await page.evaluate(() => + !!document.querySelector('[data-zap-post="' + test.ui.noteThread.focusId + '"]') && !document.querySelector('.thread-reply-input'))); await page.evaluate(() => test.overlay()); check('fixture starts with a profile covering the existing thread', await page.evaluate(() => test.ui.profOverThread && !document.querySelector('.thread-reply-input'))); await page.click('#outside .note-act'); |
