// Session 2 (A1 board) — compliance + approval pack. The legal/trust surfaces // are asserted against their source text (plain-JS Jest config, no TS // transform — same pattern as vyndrAppShell.test.js). const fs = require('fs'); const path = require('path'); const ROOT = path.join(__dirname, '..', '..'); const WEB = path.join(ROOT, 'web', 'src'); const read = (rel) => fs.readFileSync(path.join(WEB, rel), 'utf8'); const readRoot = (rel) => fs.readFileSync(path.join(ROOT, rel), 'utf8'); const exists = (rel) => fs.existsSync(path.join(WEB, rel)); describe('S2 — /methodology page', () => { it('exists as a server component with metadata', () => { expect(exists('app/methodology/page.tsx')).toBe(true); const src = read('app/methodology/page.tsx'); expect(src).not.toContain("'use client'"); expect(src).toContain('export const metadata'); }); const src = read('app/methodology/page.tsx'); it('covers the pipeline → engine → letter grades', () => { expect(src).toContain('The pipeline'); expect(src).toContain('feature vector'); expect(src).toMatch(/11-step letter grade/); expect(src).toContain('A+'); }); it('covers refusals when the model has no projection', () => { expect(src).toContain('When the model refuses'); expect(src).toContain('refuses to grade'); expect(src).toContain('Absent beats wrong'); }); it('covers the VYNDR Originals archetype system', () => { expect(src).toContain('VYNDR Originals'); expect(src).toContain('41'); ['TORCH', 'BOMBER', 'GHOST', 'CONDUCTOR'].forEach((a) => expect(src).toContain(a)); }); it('covers ledger settlement: lock at grade time, box-score settle, CLV, n≥20', () => { expect(src).toContain('How the Ledger settles'); expect(src).toContain('locked with the exact line, the odds, and a timestamp'); expect(src).toContain('real box score'); expect(src).toContain('closing line'); expect(src).toContain('n≥20'); expect(src).toContain('RECORD BUILDING'); }); it('covers why misses are public', () => { expect(src).toContain('Why the misses are public'); expect(src).toContain('same format'); }); it('states the not-a-sportsbook position and links the ledger', () => { expect(src).toContain('not a sportsbook'); expect(src).toContain('/ledger'); }); it('is registered as an open (ungated) route and linked from the footer', () => { const routes = require('../../web/src/lib/routes'); expect(routes.OPEN_ROUTES).toContain('/methodology'); expect(routes.isGatedRoute('/methodology')).toBe(false); expect(read('components/Footer.tsx')).toContain("href: '/methodology'"); }); }); describe('S2 — /terms and /privacy drafts', () => { it('terms page exists with the non-negotiable disclosures', () => { const src = read('app/terms/page.tsx'); expect(src).toContain('not a sportsbook'); expect(src).toContain('21 years of age'); expect(src).toContain('Stripe'); expect(src).toContain('1-800-GAMBLER'); }); it('privacy page exists with the real sub-processor list', () => { const src = read('app/privacy/page.tsx'); expect(src).toContain('never sell'); ['Stripe', 'Supabase', 'PostHog', 'Resend', 'Sentry'].forEach((p) => expect(src).toContain(p)); }); it('entity details are placeholder tokens, not invented facts', () => { const terms = read('app/terms/page.tsx'); const privacy = read('app/privacy/page.tsx'); ['[ENTITY NAME]', '[STATE OF FORMATION]', '[ARBITRATION VENUE]', '[CONTACT EMAIL]'].forEach((t) => expect(terms).toContain(t), ); ['[ENTITY NAME]', '[CONTACT EMAIL]'].forEach((t) => expect(privacy).toContain(t)); // The previous draft asserted an unverified LLC + emails — keep them out // until Kev fills the placeholders. [terms, privacy].forEach((src) => { expect(src).not.toContain('VYNDR Intelligence LLC'); expect(src).not.toContain('legal@vyndr.app'); expect(src).not.toContain('privacy@vyndr.app'); }); }); }); describe('S2 — /responsible-gambling (sincere rebuild)', () => { const src = read('app/responsible-gambling/page.tsx'); it('carries 1-800-GAMBLER as the primary helpline and the 21+ mark', () => { expect(src).toContain('1-800-GAMBLER'); expect(src).toContain('tel:18004262537'); expect(src).toContain('21+'); }); it('lists state resources for the major legal states', () => { ['Michigan', 'New Jersey', 'New York', 'Pennsylvania', 'Ohio', 'Illinois'].forEach((s) => expect(src).toContain(s), ); }); it('links the existing self-exclusion / settings surfaces', () => { expect(src).toContain('/settings'); expect(src).toContain('Self-exclusion'); }); it('links national support resources', () => { ['ncpgambling.org', 'gamblersanonymous.org', 'gamtalk.org'].forEach((r) => expect(src).toContain(r)); }); it('has no marketing adjacency (no pricing/upgrade/scan funnels)', () => { ['/pricing', '/upgrade', '/scan', '__goPaywall'].forEach((bad) => expect(src).not.toContain(bad)); }); }); describe('S2 — compliance footer (global)', () => { it('the root layout mounts the single global Footer', () => { expect(read('app/layout.tsx')).toContain('