Experimental Bluesky client for agents
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308import { afterAll, beforeAll, describe, expect, test } from 'bun:test';import { chromium, type Browser } from 'playwright';import type { ReadingView } from '../src/model.ts';import { renderHtml, renderText } from '../src/render.ts';
function profileView(description: string | undefined): ReadingView { return { id: 'v-profile', snapshot: { kind: 'profile', requested: 'reader.example', fetchedAt: '2026-09-05T12:00:00.000Z', source: { mode: 'public', service: 'https://public.api.bsky.app/?source=profile&mode=exact', }, author: { did: 'did:plc:profilefixture', handle: 'reader.example', displayName: 'Profile Reader', }, description, counts: {}, labels: [], }, refs: { 'v-profile-a1': { kind: 'profile', value: 'did:plc:profilefixture', label: 'Profile Reader', }, }, };}
let browser: Browser;beforeAll(async () => { browser = await chromium.launch(); });afterAll(async () => { await browser?.close(); });
describe('profile rendering', () => { test('keeps missing optional profile data distinct from explicit empty data without empty portrait furniture', () => { const missingView = profileView(undefined); const emptyView = profileView(''); if (emptyView.snapshot.kind !== 'profile') throw new Error('profile fixture mismatch'); emptyView.snapshot.author.avatar = ''; emptyView.snapshot.author.pronouns = ''; emptyView.snapshot.banner = ''; emptyView.snapshot.website = ''; emptyView.snapshot.warnings = []; emptyView.snapshot.enrichments = [];
const missingText = renderText(missingView); const missingHtml = renderHtml(missingView); expect(missingText).toContain('Avatar: <not supplied>'); expect(missingText).toContain('Native profile pronouns: <not supplied>'); expect(missingText).toContain('Description: <not supplied>'); expect(missingText).toContain('Banner: <not supplied>'); expect(missingText).toContain('Website: <not supplied>'); expect(missingText).toContain('Warnings: <not supplied; historical snapshot>'); expect(missingText).toContain('Enrichments: <not supplied; historical snapshot>'); expect(missingHtml).not.toContain('class="profile-about"'); expect(missingHtml).not.toContain('class="profile-banner'); expect(missingHtml).not.toContain('class="profile-pronouns"'); expect(missingHtml).not.toContain('class="profile-website"'); expect(missingHtml).not.toContain('class="profile-enrichments"'); expect(missingHtml).not.toContain('class="profile-recent-posts"'); expect(missingText).toContain(`Recent author posts ref: [no stored ref] · author-feed did:plc:profilefixture`); expect(missingHtml).toContain('<dt>Description field</dt><dd>not supplied</dd>'); expect(missingHtml).toContain('<dt>Followers count</dt><dd>not supplied</dd>'); expect(missingHtml).toContain('<dt>Following count</dt><dd>not supplied</dd>'); expect(missingHtml).toContain('<dt>Posts count</dt><dd>not supplied</dd>');
const zeroCountView = profileView(undefined); if (zeroCountView.snapshot.kind !== 'profile') throw new Error('profile fixture mismatch'); zeroCountView.snapshot.counts.followers = 0; zeroCountView.snapshot.author.displayName = ' '; const zeroCountHtml = renderHtml(zeroCountView); expect(zeroCountHtml).toContain('<h1>Meet @reader.example</h1>'); expect(zeroCountHtml).toContain('<dt>Followers count</dt><dd>0</dd>'); expect(zeroCountHtml).toContain('<dt>Following count</dt><dd>not supplied</dd>'); expect(zeroCountHtml).toContain('<dt>Posts count</dt><dd>not supplied</dd>');
const emptyText = renderText(emptyView); const emptyHtml = renderHtml(emptyView); expect(emptyText).toContain('Avatar: <present but empty>'); expect(emptyText).toContain('Native profile pronouns: <present but empty>'); expect(emptyText).toContain('Description: <present but empty>'); expect(emptyText).toContain('Banner: <present but empty>'); expect(emptyText).toContain('Website: <present but empty>'); expect(emptyText).toContain('Warnings: <present and empty>'); expect(emptyText).toContain('Enrichments: <present and empty>'); expect(emptyHtml).not.toContain('class="profile-about"'); expect(emptyHtml).toContain('class="profile-banner-failure" data-source-presence="empty"'); expect(emptyHtml).not.toContain('class="profile-pronouns"'); expect(emptyHtml).not.toContain('class="profile-website"'); expect(emptyHtml).not.toContain('class="profile-enrichments"'); expect(emptyHtml).toContain('<dt>Description field</dt><dd>present and empty</dd>'); });
test('preserves exact nonempty description and source data without treating prose as markup', () => { const description = 'First <field> & "quoted" line.\nSecond line: 日本語 🪶'; const view = profileView(description); const original = JSON.stringify(view); const text = renderText(view); const html = renderHtml(view);
expect(text).toContain(`Description: ${description}`); expect(text).toContain('Source service: https://public.api.bsky.app/?source=profile&mode=exact'); expect(html).toContain('First <field> & "quoted" line.\nSecond line: 日本語 🪶'); expect(html).toContain('https://public.api.bsky.app/?source=profile&mode=exact'); expect(html).not.toContain('<field>'); expect(JSON.stringify(view)).toBe(original); });
test('renders navigable identity, qualified labels, and sourced enrichment without executing untrusted content', async () => { const authorDid = 'did:plc:profilefixture'; const hostile = '<img src=x onerror="globalThis.profileInjected=true">'; const view: ReadingView = { id: 'v-profile-rich', snapshot: { kind: 'profile', requested: 'reader.example', fetchedAt: '2026-09-05T12:00:00.000Z', source: { mode: 'authenticated', service: 'https://appview.example/?surface=<profile>', viewerDid: 'did:plc:viewer', }, author: { did: authorDid, handle: 'reader.example', displayName: 'Profile Reader', avatar: `https://cdn.example/avatar.png?caption=${hostile}`, pronouns: 'she/her', following: true, followedBy: false, }, description: `A person, not markup. ${hostile}`, banner: `https://cdn.example/banner.png?caption=${hostile}`, website: `https://reader.example/?about=${hostile}`, createdAt: '2025-01-02T03:04:05.000Z', indexedAt: '2026-09-05T11:59:59.000Z', associated: { lists: 2 }, verification: { trustedVerifierStatus: 'valid' }, pinnedPost: { uri: 'at://did:plc:profilefixture/app.bsky.feed.post/pinned', cid: 'bafypinned' }, counts: { followers: 12, follows: 34, posts: 56 }, labels: [ `legacy ${hostile}`, { value: 'same-label', issuerDid: authorDid, uri: 'at://did:plc:profilefixture/app.bsky.actor.profile/self', cid: '', createdAt: '', expiresAt: '', signature: '', }, { value: 'same-label', issuerDid: 'did:plc:independentlabeler', uri: 'at://did:plc:profilefixture/app.bsky.actor.profile/self', negated: true, expiresAt: '2027-01-02T03:04:05.000Z', }, ], warnings: [`Warning retained literally: ${hostile}`], enrichments: [ { id: 'tangled-profile', title: 'Tangled profile', status: 'available', items: [ { label: 'Tangled pronouns', value: `they/them ${hostile}`, uri: 'at://did:plc:profilefixture/sh.tangled.actor.profile/self' }, { label: 'Tangled description', value: 'Distinct public-PDS biography.' }, { label: 'Present-empty value', value: '', uri: '' }, { label: 'Present-blank value', value: ' ' }, ], source: { service: 'https://public-pds.example/?source=<tangled>', actorDid: authorDid, fetchedAt: '2026-09-05T11:58:00.000Z', uri: 'at://did:plc:profilefixture/sh.tangled.actor.profile/self', cid: '', }, note: `Exact provider note: ${hostile}`, }, { id: 'known-collections', title: 'Known public repository collections', status: 'absent', items: [], source: { service: 'https://public-pds.example', actorDid: authorDid, fetchedAt: '2026-09-05T11:58:00.000Z', }, note: 'No additional allowlisted collection matched.', }, { id: 'greengale-publication', title: 'GreenGale publication', status: 'absent', items: [], source: { service: 'https://public-pds.example', actorDid: authorDid, fetchedAt: '2026-09-05T11:58:00.000Z', uri: '', cid: '', }, note: '', }, { id: 'secondary-service', title: 'Secondary service', status: 'unavailable', items: [], source: { service: 'https://other-public-pds.example', actorDid: authorDid, fetchedAt: '2026-09-05T11:57:00.000Z', }, note: `Provider timeout: ${hostile}`, }, ], }, refs: { 'v-profile-a1': { kind: 'profile', value: authorDid, label: 'Profile Reader' }, 'v-profile-r1': { kind: 'author-feed', value: authorDid, label: 'Recent posts by Profile Reader' }, }, }; const original = JSON.stringify(view); const text = renderText(view); const html = renderHtml(view); expect(text).toContain('Enrichment 1 item 3 URI: <present but empty>'); expect(text).toContain('Enrichment 1 item 4 URI: <not supplied>'); const page = await browser.newPage({ viewport: { width: 1280, height: 960 } }); try { await page.setContent(html); expect(await page.locator('script, iframe, [onerror], [onload]').count()).toBe(0); expect(await page.locator('a').count()).toBe(0); expect(await page.evaluate('globalThis.profileInjected')).toBeUndefined(); expect(await page.locator('.profile-pronouns strong').innerText()).toBe('she/her'); expect(await page.locator('.profile-relationships').innerText()).toBe('Following'); expect(await page.locator('.profile-record-details').textContent()).toMatch(/Author follows viewer\s*false/iu); expect(await page.locator('.profile-about').innerText()).toContain(hostile); expect(await page.locator('.profile-website-value').innerText()).toContain(hostile);
const navigation = page.locator('.profile-recent-posts'); expect(await navigation.innerText()).toContain('Recent posts'); expect(await navigation.innerText()).toContain('[v-profile-r1]'); expect(await navigation.innerText()).not.toContain(authorDid); expect(text).toContain(`Recent author posts ref: [v-profile-r1] · author-feed ${authorDid}`); const references = await page.locator('.reference').allInnerTexts(); expect(references).toEqual(['[v-profile-a1]', '[v-profile-r1]']); expect(references.every((reference) => reference === reference.toLowerCase())).toBe(true); const referenceStyle = await page.locator('.reference').first().evaluate((element) => { const style = getComputedStyle(element); return { color: style.color, backgroundColor: style.backgroundColor, textTransform: style.textTransform }; }); expect(referenceStyle.color).not.toBe(referenceStyle.backgroundColor); expect(referenceStyle.textTransform).toBe('none');
const historicalLabel = page.locator('[data-label-format="historical"]'); expect((await historicalLabel.innerText()).toLowerCase()).toContain('source unknown'); expect((await historicalLabel.innerText()).toLowerCase()).not.toContain('self-issued'); expect(await historicalLabel.locator('details').getAttribute('open')).toBeNull(); expect(await historicalLabel.locator('details').textContent()).toContain('no recorded issuer, target, polarity, or expiry'); const selfIssued = page.locator('[data-profile-label="2"]'); const thirdParty = page.locator('[data-profile-label="3"]'); expect((await selfIssued.locator('.badge.self-issued').innerText()).toLowerCase()).toBe('self-issued'); expect(await thirdParty.locator('.badge.self-issued').count()).toBe(0); expect((await thirdParty.locator('.badge.third-party').innerText()).toLowerCase()).toContain('did:plc:independentlabeler'); expect((await thirdParty.locator('.badge.negated').innerText()).toLowerCase()).toBe('negation · retracted'); expect(await thirdParty.locator('.profile-label-expiry').innerText()).toContain('2027-01-02T03:04:05.000Z'); expect(await selfIssued.innerText()).not.toContain('Target URI'); expect(await selfIssued.locator('details').textContent()).toContain('present and empty');
const activeEnrichments = page.locator('.enrichment-card'); expect(await activeEnrichments.count()).toBe(2); const tangled = page.locator('[data-enrichment-id="tangled-profile"]'); expect(await tangled.locator('.enrichment-items > div').count()).toBe(2); expect(await tangled.locator('.enrichment-items').innerText()).toContain('they/them'); expect(await tangled.locator('.enrichment-omission').innerText()).toContain('2 additional exact values'); expect(await tangled.locator('details').getAttribute('open')).toBeNull(); expect(await tangled.locator('details').textContent()).toContain('Exact provider note'); expect(await tangled.locator('details').textContent()).toContain('present and empty'); const unavailable = page.locator('[data-enrichment-id="secondary-service"]'); expect(await unavailable.getAttribute('data-enrichment-status')).toBe('unavailable'); expect(await unavailable.innerText()).toContain('Provider status: unavailable.');
const absentSources = page.locator('.enrichment-absent-sources'); expect(await absentSources.locator(':scope > summary').innerText()).toBe('Other checked sources (2 absent)'); expect(await absentSources.getAttribute('open')).toBeNull(); expect(await absentSources.locator(':scope > ol > li').count()).toBe(2); expect(await absentSources.innerText()).not.toContain('No additional allowlisted collection matched.'); expect(await absentSources.textContent()).toContain('No additional allowlisted collection matched.'); expect(await absentSources.textContent()).toContain('present and empty'); expect(await page.locator('body').innerText()).not.toContain('No record found at this public source'); expect(await page.locator('details[open]').count()).toBe(0); expect(await page.evaluate(() => document.documentElement.scrollWidth <= document.documentElement.clientWidth)).toBe(true); } finally { await page.close(); } expect(JSON.stringify(view)).toBe(original); });});