diff options
| author | Adam Soltys <[email protected]> | 2026-11-11 21:35:38 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-11-11 21:35:38 -0700 |
| commit | c7455ec7416fb39d4c57e92f5094cc3368b0edfb (patch) | |
| tree | 8e4a994a5522f3ee850044d7099e48eba77ac258 /tools | |
| parent | 0f2407e0fcf0c8267262275367f5bd01edd94532 (diff) | |
| download | coinosv3-c7455ec7416fb39d4c57e92f5094cc3368b0edfb.tar.gz coinosv3-c7455ec7416fb39d4c57e92f5094cc3368b0edfb.zip | |
Search: Enter on typed words (no suggestion picked) opens a feed of the notes and replies that say them. "if i submit just a string without selecting one of the suggestions i'd like it to do full text search on kind 1 posts and replies. so if i search fiatjaf i should find posts that are mentioning him without necessarily tagging him" (Adam, 2026-11-11). A session feed with def.search asks NIP-50 relays ({ kinds: [1], search }) — relay.ditto.pub, search.nos.today, nostr.wine, the three that answered a kind-1 search probe (nostr.band and primal answered nothing) — keeps replies as rows of their own, pages back with until, and has no live subscription. Enter in the search page's field or the desktop header field opens it, and the search page lists a “words” row ("Posts and replies that say this") above the topic and people rows. Its address is /feed?q=… so it can be shared. tools/post-search-test.js answers the search relays in the page
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6UKGJyuGyZFxddYRxS
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/post-search-test.js | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/tools/post-search-test.js b/tools/post-search-test.js new file mode 100644 index 0000000..2f32812 --- /dev/null +++ b/tools/post-search-test.js @@ -0,0 +1,77 @@ +// Full-text search: words typed into Search and submitted with Enter (no +// suggestion picked) open a feed of the notes AND replies that say them, +// asked of NIP-50 relays ({ kinds: [1], search }). The search relays are +// answered in the page; nothing leaves the machine. +// Run: bun tools/post-search-test.js +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; +import { generateSecretKey, finalizeEvent } from 'nostr-tools/pure'; +import { buildHtml } from '../build.js'; +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); +let ok = true; +const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' — ' + d : ''}`); if (!c) ok = false; }; +const SK = generateSecretKey(); +const now = Math.floor(Date.now() / 1000); +const post = finalizeEvent({ kind: 1, created_at: now - 300, tags: [], content: 'talked to fiatjaf about relays today' }, SK); +const parent = 'a'.repeat(64); +const reply = finalizeEvent({ kind: 1, created_at: now - 200, tags: [['e', parent, '', 'reply']], content: 'fiatjaf would say just use a relay' }, SK); + +const html = await buildHtml({ minify: true, pwa: false }); +const server = Bun.serve({ port: 0, fetch: () => 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((hits) => { + window.__searches = []; + const Real = window.WebSocket; + window.WebSocket = function (...a) { + const ws = new Real(...a); + const send = ws.send.bind(ws); + ws.send = (data) => { + try { + const m = JSON.parse(data); + if (m[0] === 'REQ' && m[2] && m[2].search) { + window.__searches.push({ url: ws.url, f: m[2] }); + const r = send(data); + setTimeout(() => { for (const ev of hits) ws.dispatchEvent(new MessageEvent('message', { data: JSON.stringify(['EVENT', m[1], ev]) })); }, 2); + return r; + } + } catch {} + return send(data); + }; + return ws; + }; + window.WebSocket.prototype = Real.prototype; + Object.assign(window.WebSocket, Real); +}, [post, reply]); +const errs = []; +page.on('pageerror', (e) => errs.push(String(e))); +const click = (t) => page.evaluate((x) => { const e = [...document.querySelectorAll('button')].find((n) => n.textContent.trim().toLowerCase().includes(x)); if (e) { e.click(); return true; } return false; }, t); +const waitText = async (x, ms = 20000) => { for (let i = 0; i < ms / 250; i++) { if ((await page.evaluate(() => document.body.innerText)).toLowerCase().includes(x)) return true; await sleep(250); } return false; }; + +try { + await page.setViewport({ width: 390, height: 844 }); + await page.goto(server.url.href, { waitUntil: 'domcontentloaded' }); + await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); + await page.reload({ waitUntil: 'domcontentloaded' }); + await sleep(400); + await click('create a new wallet'); await sleep(600); + await seedWords(page); + await skipSeedCheck(page); + await waitText('receive', 20000); + await page.click('[aria-label="Search"]'); + await page.waitForSelector('.user-search-input'); + await page.type('.user-search-input', 'fiatjaf'); + await sleep(300); + check('the search page offers the words themselves', await waitText('posts and replies that say this', 3000)); + await page.keyboard.press('Enter'); + check('Enter opens a feed of posts that say it', await waitText('talked to fiatjaf about relays', 10000)); + check('...replies too', await waitText('fiatjaf would say just use a relay', 3000)); + const s = await page.evaluate(() => window.__searches.filter((x) => x.f.search === 'fiatjaf' && !(x.f.kinds || []).includes(0))); + check('asked NIP-50 relays for kind 1 with the words', s.length > 0 && s.every((x) => JSON.stringify(x.f.kinds) === '[1]'), JSON.stringify(s.map((x) => x.url))); + check('the feed is named for the search', await page.evaluate(() => [...document.querySelectorAll('.feed-chip')].some((c) => c.classList.contains('on') && c.textContent.includes('fiatjaf'))), + JSON.stringify(await page.evaluate(() => [...document.querySelectorAll('.feed-chip')].map((c) => c.textContent.trim())))); + check('no page errors', !errs.length, errs.join(' | ')); +} catch (e) { console.error(e); ok = false; } finally { + await browser.close(); server.stop(); +} +console.log(ok ? '\nPASS' : '\nFAIL'); +process.exit(ok ? 0 : 1); |
