The data tab's pages rendered from the Worker's real answers (worker/src/data.ts against testD1): they show what the answers hold, and everything a listener wrote as text, never as markup.
4import { createElement } from 'react'; 5import { renderToString } from 'react-dom/server'; 6import { beforeAll, describe, expect, it } from 'vitest'; 7import { memoryBudgets } from '../../../worker/test/budget'; 8import { testD1 } from '../../../worker/test/d1'; 9import { memoryKV } from '../../../worker/test/kv'; 10import { memoryR2 } from '../../../worker/test/r2'; 11import { memoryTabHubs } from '../../../worker/test/tab-hubs'; 12import { d1Accounts } from '../../../worker/src/accounts-store'; 13import { d1Content } from '../../../worker/src/content-store'; 14import { data } from '../../../worker/src/data'; 15import { d1Listening } from '../../../worker/src/listening-store'; 16import { d1Radio } from '../../../worker/src/radio'; 17import { newSessionToken, SESSION_TTL_MS } from '../../../worker/src/session'; 18import { AccountPage, PerformancePage, SongPage } from './DataTab.jsx';
20const NOON = Date.UTC(2026, 8, 27, 12); 21const HOSTILE_CODE = 's("bd") // <script>alert(1)</script>'; 22const HOSTILE_COMMENT = '<img src=x onerror=alert(1)>'; 23let db; 24let userId; 25 26const env = () => ({ 27 DB: db, 28 CACHE: memoryKV(), 29 OAUTH_KV: memoryKV(), 30 COVERS: memoryR2(), 31 BUDGET: memoryBudgets(1000, () => NOON), 32 TAB_HUB: memoryTabHubs(), 33 JEV_DAILY_PER_USER: 1000, 34 DATA_LIMIT: { limit: async () => ({ success: true }) }, 35 LOBBY: { idFromName: () => ({}), get: () => ({ summary: async () => ({ open: 1, listed: 0, playing: 0 }) }) }, 36 PARTY_DIRECTORY: { idFromName: () => ({}), get: () => ({ live: async () => [] }) }, 37}); 38const read = async (path) => 39 (await data(new Request(`https://jevstrudel.example/jev/data${path}`), env(), () => NOON)).json();
React marks where one text node ends and the next begins; the text is what counts
41const html = (Page, body) => renderToString(createElement(Page, { body, open: () => {} })).replaceAll('<!-- -->', '');
43beforeAll(async () => { 44 db = testD1(); 45 userId = newSessionToken().slice(0, 22); 46 await d1Accounts(db, () => NOON).createUser( 47 { id: userId, displayName: 'Navi' }, 48 { id: 'credential-id-CCCCCCCCCCCC', publicKey: new Uint8Array([1, 2, 3]), signCount: 0, transports: ['hybrid'] }, 49 newSessionToken(), 50 SESSION_TTL_MS, 51 ); 52 await d1Radio(db, () => NOON).add(userId, 'jev/hey-listen'); 53 const content = d1Content(db, () => NOON); 54 await content.createSong(userId, 'S'.repeat(22), 'R'.repeat(22), { 55 title: 'Hey <b>listen</b>', 56 description: 'a fairy', 57 spec: '# spec', 58 code: HOSTILE_CODE, 59 }); 60 await content.recordScreen('revision', 'R'.repeat(22), 'abuse', 0.91); 61 await content.addComment('C'.repeat(22), userId, { site: 'jev/hey-listen' }, HOSTILE_COMMENT); 62 await d1Listening(db).record('P'.repeat(22), '2026-09-27', { 63 song: 'jev/hey-listen', 64 codeHash: 'e'.repeat(64), 65 model: 'jev-1.13.0', 66 every: 4, 67 ended: 'stopped', 68 form: { jev: 0, question: 'section' }, 69 jevs: [ 70 { 71 segments: [ 72 { status: 'opening', values: { section: { choice: 'intro' }, energy: { score: 1.4 } } }, 73 { status: 'fallback', ms: 5000, values: { section: { choice: 'drop' }, energy: { score: 2 } } }, 74 ], 75 }, 76 ], 77 }); 78}); 79 80describe('the data tab', () => { 81 it('shows an account: its name, passkeys by date, radio, held song and comment', async () => { 82 const out = html(AccountPage, await read(`/accounts/${userId}`)); 83 expect(out).toContain('Navi'); 84 expect(out).toContain('hybrid'); 85 expect(out).toContain('jev/hey-listen'); 86 expect(out).toContain('abuse'); 87 expect(out).toContain('0 of 1000 calls'); 88 expect(out).not.toContain('credential-id-CCCCCCCCCCCC'); 89 // a listener's writing is text 90 expect(out).toContain('<img src=x onerror=alert(1)>'); 91 expect(out).not.toContain(HOSTILE_COMMENT); 92 }); 93 94 it('shows a held song whole, as text', async () => { 95 const out = html(SongPage, await read(`/songs/${'S'.repeat(22)}`)); 96 expect(out).toContain('Hey <b>listen</b>'); 97 expect(out).toContain('<script>alert(1)</script>'); 98 expect(out).not.toContain('<script>'); 99 expect(out).toContain('abuse 91%'); 100 }); 101 102 it('shows a performance: each decision, its section and time', async () => { 103 const out = html(PerformancePage, await read(`/performances/${'P'.repeat(22)}`)); 104 expect(out).toContain('jev/hey-listen'); 105 expect(out).toContain('fallback'); 106 expect(out).toContain('5000 ms'); 107 expect(out).toContain('section drop'); 108 expect(out).toContain('energy 2.0'); 109 expect(out).toContain(`?performance=${'P'.repeat(22)}`); 110 }); 111});