// node --test test/page.test.mjs (after scripts/stage.sh: dist/ as served; skips without Chrome) // index.html in headless Chrome, signed in with a stub session: Running shows a bidder an account you vouch for // runs (its operator confirmed), Vouches writes and deletes a vouch, the atlibs view lists the instances, and the // page fits a phone. Stubbed: the PLC directory, the PDS (an in-memory repo), the profiles appview. // atlibs and the other imports come from their CDNs. import assert from 'node:assert/strict' import { existsSync } from 'node:fs' import { test } from 'node:test' import { browse, CHROME } from './browser.mjs' const ME = 'did:plc:alice' const FRIEND = 'did:plc:friend' const BID = 'did:plc:bid' const T = 'com.publicdomainrelay.temp' const now = new Date().toISOString() const store = new Map( Object.entries({ [`${ME}/sh.tangled.graph.vouch/${FRIEND}`]: { kind: 'vouch', reason: 'runs a bidder', createdAt: now }, [`${FRIEND}/computer.socialweb.bidders/self`]: { bidders: [BID] }, [`${BID}/${T}.market.offering/self`]: { appliesTo: [`${T}.compute.vm`], endpoints: [{ kind: 'webrtc', address: 'x' }], createdAt: now }, [`${BID}/${T}.market.heartbeat/self`]: { offering: `at://${BID}/${T}.market.offering/self`, createdAt: now }, [`${BID}/${T}.badgeBlueKeys/association`]: { challenge: FRIEND, keyId: BID, service: 'bidder_associate' }, [`${FRIEND}/${T}.market.association/bidder_associate-did-plc-bid`]: { subject: BID, service: 'bidder_associate', createdAt: now }, }), ) function pds(url, method, body) { const p = Object.fromEntries(url.searchParams) const nsid = url.pathname.split('/').pop() const key = (b) => `${b.repo}/${b.collection}/${b.rkey}` if (nsid === 'com.atproto.repo.getRecord') { const value = store.get(key(p)) return value ? [200, { uri: `at://${key(p)}`, cid: 'bafy', value }] : [400, { error: 'RecordNotFound', message: 'Could not locate record' }] } if (nsid === 'com.atproto.repo.listRecords') { const prefix = `${p.repo}/${p.collection}/` return [200, { records: [...store].filter(([k]) => k.startsWith(prefix)).map(([k, value]) => ({ uri: `at://${k}`, cid: 'bafy', value })) }] } if (nsid === 'com.atproto.repo.putRecord' || nsid === 'com.atproto.repo.createRecord') { store.set(key(body), body.record) return [200, { uri: `at://${key(body)}`, cid: 'bafy' }] } if (nsid === 'com.atproto.repo.deleteRecord') return store.delete(key(body)), [200, {}] if (nsid === 'com.atproto.repo.describeRepo') return [200, { handle: 'alice.test', did: p.repo, collections: [] }] if (nsid === 'com.atproto.identity.resolveHandle') return [200, { did: `did:plc:${p.handle.split('.')[0]}` }] return [400, { error: 'MethodNotImplemented' }] } const SESSION = `globalThis.__ATLIBS_SESSION__ = { did: ${JSON.stringify(ME)}, fetchHandler: (path, init = {}) => fetch('https://pds.test' + path, init) }` const CORS = [{ name: 'access-control-allow-origin', value: '*' }, { name: 'access-control-allow-headers', value: '*' }, { name: 'access-control-allow-methods', value: '*' }] const skip = (!CHROME && 'no Chrome: set CHROME') || (!existsSync(new URL('../dist/stage.json', import.meta.url)) && 'no dist/: scripts/stage.sh') test('signed in: running, vouches, atlibs, on a phone', { skip }, async () => { await browse( async ({ send, on, open, evaluate }) => { const errors = [] on('Runtime.exceptionThrown', (e) => errors.push(e.exceptionDetails.exception?.description ?? e.exceptionDetails.text)) on('Fetch.requestPaused', async ({ requestId, request }) => { const url = new URL(request.url) let status = 200 let body = {} if (request.method === 'OPTIONS') body = '' else if (url.origin === 'https://plc.directory') body = { id: url.pathname.slice(1), alsoKnownAs: ['at://x.test'], service: [{ id: '#atproto_pds', type: 'AtprotoPersonalDataServer', serviceEndpoint: 'https://pds.test' }] } else if (url.origin === 'https://public.api.bsky.app') body = { profiles: [{ did: FRIEND, handle: 'friend.test' }] } else [status, body] = pds(url, request.method, request.postData && JSON.parse(request.postData)) const text = typeof body === 'string' ? body : JSON.stringify(body) await send('Fetch.fulfillRequest', { requestId, responseCode: status, responseHeaders: [{ name: 'content-type', value: 'application/json' }, ...CORS], body: Buffer.from(text).toString('base64') }) }) await send('Runtime.enable') await send('Fetch.enable', { patterns: ['https://plc.directory/*', 'https://pds.test/*', 'https://public.api.bsky.app/*'].map((urlPattern) => ({ urlPattern })) }) await send('Page.addScriptToEvaluateOnNewDocument', { source: SESSION }) await send('Emulation.setDeviceMetricsOverride', { width: 390, height: 844, deviceScaleFactor: 1, mobile: true }) await open('/index.html') const until = async (expression, ms = 30_000) => { for (const end = Date.now() + ms; Date.now() < end; await new Promise((ok) => setTimeout(ok, 200))) if (await evaluate(expression).catch(() => false)) return throw new Error(`timed out: ${expression}\n${errors.join('\n')}\n${await evaluate('document.getElementById("error").textContent')}`) } await until('document.body.dataset.ready === "true"') await until(`document.querySelectorAll('#vouched-bidders .card').length > 0`) const card = await evaluate(`document.querySelector('#vouched-bidders .card').textContent`) assert.match(card, /did:plc:bid/) assert.match(card, /operator friend\.test/) assert.match(card, /heartbeat \d+s ago/) assert.match(await evaluate(`document.getElementById('my-bidders').textContent`), /No bidders of yours/) assert.equal(await evaluate('document.documentElement.scrollWidth <= innerWidth'), true, 'no sideways scroll on a phone') await evaluate(`location.hash = '#vouches'`) await until(`document.querySelectorAll('#vouch-list .card').length === 1`) await evaluate(`(() => { const f = document.getElementById('vouch'); f.subject.value = 'carol.test'; f.reason.value = 'hi'; f.requestSubmit() })()`) await until(`document.querySelectorAll('#vouch-list .card').length === 2`) assert.deepEqual(store.get(`${ME}/sh.tangled.graph.vouch/did:plc:carol`).subject, 'did:plc:carol') await evaluate(`[...document.querySelectorAll('#vouch-list .card')].find((c) => c.textContent.includes('carol')).querySelector('button').click()`) await until(`document.querySelectorAll('#vouch-list .card').length === 1`) assert.equal(store.has(`${ME}/sh.tangled.graph.vouch/did:plc:carol`), false) await evaluate(`location.hash = '#atlibs'`) await until(`document.querySelectorAll('#instances .card').length === 10`) assert.equal(await evaluate(`document.getElementById('error').textContent`), '') assert.deepEqual(errors, []) }, { root: new URL('../dist/', import.meta.url).pathname }, ) })