Something went wrong. Try again.
Browser extension: detect and subscribe to standard.site publications on ATProto
Something went wrong. Try again.
7.9 kB · 194 lines
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195// Does the built extension actually detect publications?//// The unit tests cover every module in isolation and the whole of v1.2.1// still shipped with sign-in dead, because what broke was the wiring between// them. This is the other end of that: load the real build into a real// Chrome, visit real publications over the real network, and assert the state// the worker ends up in.//// Each site is here because it reaches detection by a different route, so a// pass means the routes work, not just that one lucky page resolved://// standard.site no <link> hint at all; found by the origin well-known// atproto.com/blog a <link> hint, and an origin well-known that 404s, so it// verifies through the path-scoped probe// permadeath.com both: the hint and the origin well-known// example.com no publication; the control, and the case that catches a// build that "detects" everything//// Sites and projects only, never a person's personal blog (see CLAUDE.md);// the one that belongs to an individual is the author's own.//// This talks to the live internet, so it is not a prek hook and not part of// `npm test`. It is a release gate: run it on the tree that is about to be// packaged.//// npm run smoke # against dist/// node scripts/smoke-test.mjs --dist some/other/dist// node scripts/smoke-test.mjs --headful # watch it happen
import { existsSync, mkdtempSync, rmSync } from 'node:fs'import { tmpdir } from 'node:os'import { join, resolve } from 'node:path'import { poll, withChrome } from './cdp.mjs'
/** * `did` is asserted where the publication is stable and public; permadeath.com * is left to `verified` alone so rotating the record does not fail the build. */export const CASES = [ { name: 'standard.site', url: 'https://standard.site/', route: 'origin well-known, no link tag', expect: { publication: true, verified: true, did: 'did:plc:re3ebnp5v7ffagz6rb6xfei4' }, }, { name: 'atproto.com/blog', url: 'https://atproto.com/blog', route: 'link tag + path-scoped well-known (origin 404s)', expect: { publication: true, verified: true, did: 'did:plc:ewvi7nxzyoun6zhxrhs64oiz' }, }, { name: 'permadeath.com', url: 'https://permadeath.com/', route: 'link tag and origin well-known', expect: { publication: true, verified: true }, }, { name: 'example.com', url: 'https://example.com/', route: 'control: no publication anywhere', expect: { publication: false }, },]
/** * Compare one tab's detection state against what the case expected. Pure, so * the interesting half is testable without a browser. */export function checkCase(testCase, state, iconState) { const problems = [] const want = testCase.expect
if (!want.publication) { if (state?.pub) { problems.push(`expected no publication, got "${state.pub.record?.name}" (${state.pub.uri})`) } // A control page must not merely fail to find a publication — it must have // finished looking. `failed` here would mean detection errored. if (iconState !== 'none') { problems.push(`expected icon state "none", got "${iconState}"`) } return problems }
if (!state?.pub) { problems.push(`no publication detected${state?.error ? ` (error: ${state.error})` : ''}`) return problems } if (want.verified && !state.pub.verified) { problems.push(`publication found but not verified: ${state.pub.uri}`) } if (want.did && state.pub.did !== want.did) { problems.push(`expected did ${want.did}, got ${state.pub.did}`) } if (!state.pub.record?.name) { problems.push('publication record has no name; the record fetch did not resolve') } // Signed out, a verified publication reads as "detected, subscription // unknown". Anything else means the badge would be wrong on a real toolbar. if (want.verified && iconState !== 'signedout') { problems.push(`expected icon state "signedout" while signed out, got "${iconState}"`) } return problems}
async function run(argv) { const root = resolve(import.meta.dirname, '..') const distFlag = argv.indexOf('--dist') const dist = resolve(distFlag === -1 ? join(root, 'dist') : argv[distFlag + 1]) const headless = !argv.includes('--headful')
if (!existsSync(join(dist, 'manifest.json'))) { console.error(`smoke-test: ${dist} is not a built extension — run \`npm run build\` first`) process.exit(1) }
const profile = mkdtempSync(join(tmpdir(), 'substandard-smoke-')) console.log(`smoke-test: ${dist}`)
const failures = [] try { await withChrome({ extensionDir: dist, profile, headless }, async ({ inWorker }) => { for (const testCase of CASES) { let tab try { tab = await inWorker( `chrome.tabs.create({ url: ${JSON.stringify(testCase.url)}, active: true })`, ) // Best effort only. Detection runs from the content script at // document_idle, so a page that keeps a connection open never // reaches `complete` and does not need to: the settled state below // is the signal being waited on. await poll( () => inWorker(`chrome.tabs.get(${tab.id}).then((t) => t.status === 'complete')`), `${testCase.name} to finish loading`, 10_000, ).catch(() => {})
// Detection is asynchronous and driven by the content script's // report, so wait for the worker to record a settled answer rather // than reading whatever is there the instant the page loads. const key = `tab:${tab.id}` const state = await poll( () => inWorker( `chrome.storage.session.get(${JSON.stringify(key)}).then((r) => { const s = r[${JSON.stringify(key)}] return s && !s.error ? s : false })`, ), `a settled detection state for ${testCase.name}`, 25_000, ).catch(async () => { // Report the failed state rather than only the timeout. return await inWorker( `chrome.storage.session.get(${JSON.stringify(key)}).then((r) => r[${JSON.stringify(key)}] ?? null)`, ) })
const iconState = await inWorker( `__substandard.iconStateFor(${JSON.stringify(state)})`, ) const problems = checkCase(testCase, state, iconState) if (problems.length === 0) { const what = state?.pub ? `"${state.pub.record.name}"` : 'no publication' console.log(` ok ${testCase.name.padEnd(18)} ${what} [${testCase.route}]`) } else { failures.push({ name: testCase.name, problems }) console.log(` FAIL ${testCase.name.padEnd(18)} [${testCase.route}]`) for (const p of problems) console.log(` ${p}`) } } catch (err) { // One unreachable site must not abandon the rest of the gate. failures.push({ name: testCase.name, problems: [String(err.message ?? err)] }) console.log(` FAIL ${testCase.name.padEnd(18)} [${testCase.route}]`) console.log(` ${err.message ?? err}`) } finally { if (tab) await inWorker(`chrome.tabs.remove(${tab.id})`).catch(() => {}) } } }) } finally { rmSync(profile, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }) }
if (failures.length > 0) { console.error(`\nsmoke-test: FAILED — ${failures.length} of ${CASES.length} case(s)`) process.exit(1) } console.log(`\nsmoke-test: OK — ${CASES.length} cases, every detection route reached`)}
if (process.argv[1] === import.meta.filename) await run(process.argv.slice(2))