Files
vyndr/tests/unit/vyndrAppShell.test.js
builtbykev c8790fde55 Session 57: Phase 0 — Kill the Lies (2309 tests)
Work-order Phase 0 (Jul 10 live audit): every fabricated UI element deleted
or rewired to real data. Deletion sprint — no new product features.

0.1 Fake NBA game: root cause was scheduleService fetching the ESPN
    scoreboard with no ?dates= param or date filter — off-season ESPN
    returns the NEAREST slate (Jun 13 NYK@SA Finals rendered as tonight).
    Now pinned to the requested ET date + defensive filter; undated events
    dropped. Honest month-aware per-sport empty states (lib/emptyState.js).
0.2 Fake header counters: liveTick stripped to a bare 1s pulse (the
    auto-incrementing "247 graded", sin-driven brain-%, aPlus/cascades are
    dead). New GET /api/snapshot/summary (cache-only, before /:sport) +
    Next proxy; HeartbeatBar shows the real graded count and SYNC =
    elapsed since the last pipeline run (amber past 5 min).
0.3 Ticker: hardcoded fallback items deleted (real snapshot exhaust only);
    MOVE kept — computeLineDeltas is real movement. <4 real items → no
    ticker; bar publishes --ticker-h so the fixed header collapses cleanly.
0.4 /terminal retired: route redirects to /dashboard; VVI/injury-wire/
    leaders layouts preserved unrouted as §12 content-engine templates.
    Nav PRIMARY = Slate/Scan/Ledger; BottomTabBar Terminal→Explore; PWA
    shortcut Terminal→Ledger; #terminal alias → /dashboard.
0.5 ›Query nav pill deleted (duplicate /scan link).

Backend 2289 → 2309 tests (199 suites), web build exit 0.
Spec: specs/phase-0-kill-the-lies.md. Next: work-order Phase 1 (ledger
persistence + settlement).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 20:17:49 -04:00

155 lines
6.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// VYNDR 2.0 — Phase C app shell (Session 34): routing config + auth gate,
// nav, footer, 404, route stubs. Routing LOGIC is exercised directly via the
// CommonJS routes module; the .tsx shell is asserted against its source text
// (the plain-JS Jest config has no TS/Babel transform).
const fs = require('fs');
const path = require('path');
const WEB = path.join(__dirname, '..', '..', 'web', 'src');
const read = (rel) => fs.readFileSync(path.join(WEB, rel), 'utf8');
const exists = (rel) => fs.existsSync(path.join(WEB, rel));
const routes = require('../../web/src/lib/routes');
describe('Phase C — routing config (lib/routes)', () => {
it('gates the personal-data routes', () => {
['/ledger', '/tracker', '/account', '/profile', '/notifications', '/invite'].forEach((r) => {
expect(routes.isGatedRoute(r)).toBe(true);
});
});
it('gates nested paths under a gated route', () => {
expect(routes.isGatedRoute('/ledger/2026-06')).toBe(true);
expect(routes.isGatedRoute('/settings/security')).toBe(true);
});
it('leaves the landing + free funnel open (dashboard, scan stay public)', () => {
['/', '/dashboard', '/scan', '/pricing', '/blog', '/login'].forEach((r) => {
expect(routes.isGatedRoute(r)).toBe(false);
});
});
it('handles empty/undefined pathnames without throwing', () => {
expect(routes.isGatedRoute('')).toBe(false);
expect(routes.isGatedRoute(undefined)).toBe(false);
});
it('resolves hash deep-link aliases to real routes', () => {
expect(routes.resolveHashAlias('#scan')).toBe('/scan');
// Session 57 (Phase 0) — /terminal retired; old links land on the slate.
expect(routes.resolveHashAlias('#terminal')).toBe('/dashboard');
expect(routes.resolveHashAlias('#slate')).toBe('/dashboard');
expect(routes.resolveHashAlias('#nope')).toBeNull();
expect(routes.resolveHashAlias('')).toBeNull();
});
it('drops the retired /terminal surface from OPEN_ROUTES', () => {
expect(routes.OPEN_ROUTES).not.toContain('/terminal');
});
it('keeps GATED and OPEN route lists disjoint', () => {
const overlap = routes.GATED_ROUTES.filter((r) => routes.OPEN_ROUTES.includes(r));
expect(overlap).toEqual([]);
});
});
describe('Phase C — AuthGate (client-side gate, keeps Supabase auth)', () => {
const src = read('components/AuthGate.tsx');
it('decides gating from lib/routes, not a server middleware', () => {
expect(src).toContain("from '@/lib/routes'");
expect(src).toContain('isGatedRoute');
expect(src).toContain('useAuth');
});
it('redirects to /login carrying the intended path via ?next=', () => {
expect(src).toContain('/login?next=');
});
it('waits for auth to finish loading before redirecting', () => {
expect(src).toMatch(/if \(loading\) return/);
});
});
describe('Phase C — Nav conversion', () => {
const src = read('components/Nav.tsx');
it('uses the new VYNDR 2.0 Wordmark (.wm), not the legacy one', () => {
expect(src).toContain("from '@/components/vyndr'");
expect(src).not.toContain("from '@/components/Wordmark'");
});
it('renders the Ticker under the bar', () => {
expect(src).toContain('<Ticker');
});
it('styles nav links in JetBrains Mono with grade-green active state', () => {
expect(src).toContain("fontFamily: 'var(--mono)'");
expect(src).toContain("'var(--g-a)'");
});
it('exposes the primary Slate/Scan/Ledger routes', () => {
['/dashboard', '/scan', '/ledger'].forEach((href) => {
expect(src).toContain(href);
});
});
// Session 57 (Phase 0) — Terminal (fabricated surface) and the duplicate
// Query pill are gone from the nav. Don't re-add either.
it('does not link /terminal or render the Query duplicate', () => {
expect(src).not.toContain("href: '/terminal'");
expect(src).not.toContain('data-search-trigger');
expect(src).not.toContain('>Query<');
});
});
describe('Phase C — Footer (system voice)', () => {
const src = read('components/Footer.tsx');
it('uses the new Wordmark and mono type', () => {
expect(src).toContain("from '@/components/vyndr'");
expect(src).toContain('var(--mono)');
});
it('carries the Detroit signature + legal/21+ system language', () => {
expect(src).toContain('DETROIT');
expect(src).toContain('21+');
expect(src).toContain('not a sportsbook');
expect(src).toContain('Not financial advice');
});
});
describe('Phase C — 404 north star', () => {
const src = read('app/not-found.tsx');
it('renders the amber 404 with system language', () => {
expect(src).toContain('TRANSMISSION INTERRUPTED');
expect(src).toContain('404');
expect(src).toContain('var(--amber)');
});
it('uses the scanline texture and a CRT sweep on load', () => {
expect(src).toContain('scanlines');
expect(src).toContain('crt-sweep');
});
});
describe('Phase C — layout wiring', () => {
const src = read('app/layout.tsx');
it('mounts AuthGate, global Footer, and the hash bridge', () => {
expect(src).toContain('<AuthGate>');
expect(src).toContain('<Footer />');
expect(src).toContain('<HashRedirect />');
});
});
describe('Phase C — route stubs for not-yet-built screens', () => {
// /terminal became real in Session 35; /compare /invite /help /about became
// real in Session 36 (Phase E). Only /notifications remains a stub.
const stubs = ['notifications'];
it.each(stubs)('/%s has a page that uses the design-system RouteStub', (name) => {
expect(exists(`app/${name}/page.tsx`)).toBe(true);
expect(read(`app/${name}/page.tsx`)).toContain('RouteStub');
});
it('does NOT stub over existing real pages (ledger/tracker/blog/game)', () => {
// these predate Session 34 with real content — must not be RouteStubs
['app/ledger/page.tsx', 'app/tracker/page.tsx', 'app/blog/page.tsx'].forEach((p) => {
expect(read(p)).not.toContain('RouteStub');
});
});
it('RouteStub speaks the system language', () => {
const src = read('components/vyndr/RouteStub.tsx');
expect(src).toContain('ROUTE UNDER CONSTRUCTION');
expect(src).toContain('SectionHead');
});
});