APP_CONTEXT.md — forsyt.io Application Knowledge
Living document. Update this file whenever Claude discovers new information about the app — real selectors, confirmed flows, new pages, UI changes. This is the domain knowledge base that lets Claude write tests with minimal prompting.
What the App Is
forsyt.io is a mobile-first sports web application — likely a sports betting or live sports tracking platform. It is designed primarily for mobile browsers (402×874 viewport, matching the Figma canvas). A desktop layout also exists.
- Live URL:
https://forsyt.io - Primary device: Mobile (402×874, touch,
is_mobile=true) - Browser default: Chromium (configurable via
.env) - Auth: Session-based. Saved auth state:
.auth/state.json
Known Pages
| Page | URL | Auth | Status | Page Object |
|---|---|---|---|---|
| Splash / Root | / | No | Confirmed loads (test passes) | None yet |
| Sports Feed | /sports | No | Unknown — not yet tested | None yet |
| Live / In-Play | /live | No | Unknown — not yet tested | None yet |
| Login | /login | No | Skeleton page object exists | pages/login_page.py (placeholder selectors) |
| Dashboard | /dashboard | Yes | Skeleton page object exists | pages/dashboard_page.py (placeholder selectors) |
| My Bets | /my-bets | Yes | Not yet tested | None yet |
| Account / Profile | /account | Yes | Not yet tested | None yet |
| Event Detail | /event/<id> | No | Not yet tested | None yet |
| 404 / Error | /nonexistent | No | Not yet tested | None yet |
Note on placeholder selectors:
login_page.pyanddashboard_page.pyuse[data-testid='...']selectors that do not yet correspond to real elements in the live app. Tests using these page objects will fail until real selectors are discovered and updated.
UI Structure — From Figma Analysis
Splash Screen (Figma node 837:22442)
- Viewport: 402×874 (iPhone-sized)
- Background: Dark/solid colour
- Centre: forsyt.io logo (
logo_white2) — white logo centred vertically and horizontally - Top: iPhone-style Status Bar (
iPhone / Status Bar) — time, signal, battery indicators - Bottom: Navigation bar frame is present in the DOM but hidden on the splash screen
- Behaviour: Transitions to the main app automatically or via user action
Key Figma nodes observed:
837:22442 → Splash Screen frame (root)
837:22443 → iPhone / Status Bar
837:22465 → Logo group (logo_white2 1)
837:22467 → Frame 43474 — bottom nav (hidden="true" on splash)
Bottom Navigation Bar (Mobile)
Observed from Figma node 837:22467 (hidden on splash, visible on other pages):
- 5 navigation slots (icon + optional label each)
- Tab 1: Sports — has icon + text label "Sports"
- Tabs 2–5: Icons only (no labels confirmed yet)
- Notification badge: Visible on at least one tab — shows number
"2"(Figma node837:22497) - Structure: Horizontal bar pinned to bottom of screen
- Touch targets: Must be ≥ 44×44px each
Frame 43474 (bottom nav root)
├── Frame 43473 → Sports tab (icon + "Sports" label)
│ ├── Frame (icon slot)
│ └── Text "Sports"
├── Frame (tab 2 icon)
├── Frame (tab 3 icon)
├── Frame (tab 4 icon)
├── Frame (tab 5 icon)
└── Frame 43474 → badge container → Text "2"
Status Bar (Mobile)
- Present at top of every screen
- Contains: left side (time display), right side (signal, wifi, battery icons)
- Height: ~44px
- Content below must not overlap with the status bar
Known User Flows
Flow 1: App Entry (Splash → Home)
User opens forsyt.io
→ Splash screen appears (logo visible, status bar visible, bottom nav hidden)
→ Auto-transition OR user taps to continue
→ Lands on Sports Feed / Home
→ Bottom nav becomes visible
Flow 2: Tab Navigation (Mobile)
User is on Sports Feed
→ Taps "Live" tab in bottom nav
→ Live / In-Play page loads
→ Active tab indicator moves to "Live"
User taps "My Bets" tab
→ If authenticated → My Bets page loads
→ If not authenticated → Redirected to Login
Flow 3: Login
User arrives at /login (directly or via redirect)
→ Email field visible
→ Password field visible
→ Submit button visible
→ User enters credentials → taps submit
→ Valid credentials → redirects to main app (dashboard or sports feed)
→ Invalid credentials → error banner appears, stays on login page
→ Empty form submit → field-level validation shown
Flow 4: Protected Page Access (Unauthenticated)
User navigates to /my-bets or /account without being logged in
→ App redirects to /login
Flow 5: Event Navigation
User is on Sports Feed
→ Sees list of event cards (teams, odds, time/status)
→ Taps an event card
→ Event detail page loads (match info, full odds)
→ Taps browser back
→ Returns to Sports Feed at the same scroll position
UI Patterns Confirmed / Observed
| Pattern | Where | Notes |
|---|---|---|
| Logo centred on dark background | Splash | White logo, logo_white2 |
| iPhone status bar | All pages (mobile) | Time + icons, ~44px height |
| Bottom nav — 5 tabs | All pages except Splash (mobile) | Tab 1 = Sports confirmed |
| Notification badge | Bottom nav | Shows "2" in Figma |
| Event cards with odds | Sports Feed | Teams + 2+ odds values + time |
| Loading spinner | Any data page | Appears during fetch, disappears when ready |
| Error banner | Login, form pages | Appears inline on bad credentials |
| Field validation | Login form | Appears on empty submit |
Confirmed Selectors
Selectors are confirmed only once tested against the live site. Do not use unconfirmed selectors in tests. Mark them with
# UNCONFIRMEDuntil verified.
| Element | Selector | Status | Page object location |
|---|---|---|---|
| (none confirmed yet) | — | — | — |
When a selector is discovered from the live site (via browser DevTools, Playwright inspector, or a passing test), add it to the table above AND update the corresponding page object.
Placeholder Selectors (Not Yet Verified)
These exist in the codebase but are not confirmed to work on the live site:
| Selector | Used in | Likely real? |
|---|---|---|
[data-testid='username-input'] | pages/login_page.py | Unknown |
[data-testid='password-input'] | pages/login_page.py | Unknown |
[data-testid='login-submit'] | pages/login_page.py | Unknown |
[data-testid='login-error'] | pages/login_page.py | Unknown |
h1[data-testid='login-heading'] | pages/login_page.py | Unknown |
h1[data-testid='dashboard-heading'] | pages/dashboard_page.py | Unknown |
[data-testid='welcome-message'] | pages/dashboard_page.py | Unknown |
[data-testid='stats-card'] | pages/dashboard_page.py | Unknown |
nav[role='navigation'] | components/navbar.py | Unknown |
How to Discover Real Selectors
When the user asks to implement a test for a page with no confirmed selectors:
- Flag it clearly — state that selectors are unknown and tests will be skeletons
- Use the most stable selector strategy — prefer
data-testid, then ARIA role, then text - Mark unconfirmed selectors with
# UNCONFIRMED — update after live site inspection - Update this file once selectors are confirmed from a passing test or DevTools inspection
To inspect selectors on the live site:
# Launch Playwright inspector on forsyt.io
HEADLESS=false python -c "
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
b = p.chromium.launch(headless=False)
ctx = b.new_context(viewport={'width':402,'height':874}, is_mobile=True)
page = ctx.new_page()
page.goto('https://forsyt.io')
page.pause() # opens Playwright Inspector
"
What Claude Should Infer from This Document
- If asked to "add a test for the bottom nav" → bottom nav has 5 tabs, tab 1 is Sports, one tab has badge "2", hidden on splash screen
- If asked to "add a test for the splash screen" → check logo visibility, check status bar, check bottom nav is hidden, check no console errors
- If asked about selectors → assume
data-testidattributes exist (common pattern), but mark as unconfirmed until a test passes - If asked to "add a smoke test for any page" → page load + key element visible + no console errors is the minimum smoke set
- If told "the selector is X" → update the page object
_PRIVATE_CONSTANTand this file's Confirmed Selectors table