# RevHero QA Run — scheduled-20260921T135534 **Started:** 2026-09-21T13:55:42.838Z **Finished:** 2026-09-21T14:06:32.391Z ## Summary | | Count | |---|---| | Total | 478 | | PASS | 460 | | FAIL | 8 | | SKIP | 0 | | NOT_EXEC | 10 | ## Results | ID | Status | Duration | Description | |---|---|---|---| | `ANALYTICS-001` | PASS | 438.2024449999999ms | Unauth GET /v1/dashboard/health rejected | | `ANALYTICS-002` | PASS | 16051.036044ms | Authed GET /v1/dashboard/health does not fail closed (service up) | | `ANALYTICS-003` | PASS | 217.83472400000028ms | Bogus sibling path 404s distinctly from the real route's 401 (route-exists sanity) | | `FE-ACT-001` | PASS | 7.555855000000065ms | Outbound SMS produces 'SMS Sent' activity (smoke) | | `FE-ACT-002` | PASS | 0.9479890000002342ms | Inbound SMS produces 'SMS Received' activity (smoke) | | `FE-ACT-003` | PASS | 4.597255999999561ms | Email sent produces 'Email Sent' activity (smoke) | | `FE-ACT-004` | PASS | 0.5197199999997792ms | Email received produces 'Email Received' activity (smoke) | | `FE-ACT-005` | PASS | 0.30251399999997375ms | A2P brand approved produces activity (smoke) | | `FE-ACT-006` | PASS | 0.47753000000011525ms | Phone purchase produces 'Phone Number Purchased' (smoke) | | `FE-ACT-007` | PASS | 0.28485100000034436ms | Toky import produces 'Phone Number Imported' (smoke) | | `FE-ACT-008` | PASS | 0.2707649999997557ms | Deal-loss negative-sentiment activity (smoke) | | `FE-ACT-009` | PASS | 15.977124999999887ms | Stage processing failure activity (smoke) | | `FE-ACT-010` | PASS | 14064.825373ms | Activities mark-read endpoints respond | | `FE-ACT-011` | PASS | 0.477601999999024ms | Activity feed scoped to current account_id (smoke) | | `FE-ACT-AIC-001` | PASS | 12087.317078999999ms | POST /v1/stages/:id/actions/ai-call endpoint exists | | `FE-ACT-AIC-002` | PASS | 6.753173999997671ms | PUT updates AI-call action (smoke) | | `FE-ACT-AIC-003` | PASS | 5.00527399999919ms | DELETE removes action (smoke) | | `FE-ACT-AIC-004` | PASS | 1.9740019999990182ms | Trigger requests AI call (smoke) | | `FE-ACT-AIC-005` | PASS | 6.413310999996611ms | Empty prompt_template → validation error (smoke) | | `FE-ACT-BBS-001` | PASS | 6279.7161879999985ms | Endpoint exists | | `FE-ACT-BBS-002` | PASS | 0.47795099999711965ms | CRUD verified (smoke) | | `FE-ACT-BBS-003` | PASS | 0.23130999999921187ms | Trigger dispatches request (smoke) | | `FE-ACT-PD-001` | PASS | 5445.706542ms | Endpoint exists | | `FE-ACT-PD-002` | PASS | 1.7885569999998552ms | Editor lists folders/templates (smoke) | | `FE-ACT-PD-003` | PASS | 0.46846499999810476ms | CRUD verified (smoke) | | `FE-ACT-PD-004` | PASS | 0.19519399999990128ms | Trigger creates + emails doc (smoke) | | `FE-ACT-PD-005` | PASS | 0.15678299999854062ms | Invalid template_id → graceful failure (smoke) | | `FE-ACT-S2C-001` | PASS | 7356.053691000001ms | Endpoint exists | | `FE-ACT-S2C-002` | PASS | 0.552270000000135ms | Editor lists user's campaigns + stages (smoke) | | `FE-ACT-S2C-003` | PASS | 0.2529209999993327ms | Trigger moves deal across campaigns (smoke) | | `FE-ACT-S2C-004` | PASS | 3.6061960000006366ms | Cycle prevention at save time (smoke) | | `FE-ACT-SF-001` | PASS | 11489.020427999996ms | Endpoint exists | | `FE-ACT-SF-002` | PASS | 0.5460490000041318ms | CRUD verified (smoke) | | `FE-ACT-SF-003` | PASS | 0.24339399999735178ms | Invalid API key at save → friendly error (smoke) | | `FE-ACT-SF-004` | PASS | 0.4605589999991935ms | Trigger logs activity (smoke) | | `FE-ACT-VM-001` | PASS | 8919.699519000002ms | POST /v1/stages/:id/actions/voicemail endpoint exists | | `FE-ACT-VM-002` | PASS | 21.360717999999906ms | Voicemail editor accepts audio upload (smoke) | | `FE-ACT-VM-003` | PASS | 8.70256100000006ms | PUT updates voicemail action (smoke) | | `FE-ACT-VM-004` | PASS | 0.28518199999962235ms | DELETE removes action (smoke) | | `FE-ACT-VM-005` | PASS | 0.31305299999985436ms | Trigger dispatches voicemail (smoke) | | `FE-ACT-VM-006` | PASS | 0.32593699999961245ms | Reject wrong file type with friendly error (smoke) | | `FE-ACT-VM-007` | PASS | 0.2623590000002878ms | Reject oversized audio with friendly error (smoke) | | `FE-ADM-001` | PASS | 24621.246245000002ms | /admin/dashboard renders without crashing | | `FE-ADM-002` | PASS | 0.695828000025358ms | Sidebar shows Billing/Dashboard/Pricing/Campaigns (smoke) | | `FE-ADM-003` | PASS | 19563.228357999997ms | /admin/billing renders without crashing | | `FE-ADM-004` | FAIL | 77302.22414899999ms | /admin/billing/clients renders without crashing | | `FE-ADM-005` | PASS | 14834.097899ms | /admin/billing/payments renders without crashing | | `FE-ADM-006` | PASS | 11891.806308ms | /admin/campaigns renders without crashing | | `FE-ADM-007` | PASS | 17465.833552999975ms | /admin/campaigns/analytics renders without crashing | | `FE-ADM-008` | PASS | 17346.864299000008ms | /admin/campaigns/templates renders without crashing | | `FE-ADM-012` | PASS | 14115.533777999983ms | /admin/pricing/plans renders without crashing | | `FE-ADM-013` | PASS | 9635.65391199998ms | /admin/pricing/plans/create renders without crashing | | `FE-ADM-014` | PASS | 27800.975108000013ms | /admin/pricing/promo-codes renders without crashing | | `FE-ADM-015` | PASS | 14137.595749999979ms | /admin/pricing/addons renders without crashing | | `FE-ADM-016` | PASS | 9555.600127999991ms | /admin/user-settings renders without crashing | | `FE-ADM-017` | PASS | 15824.550991000026ms | MEMBER role hits /admin/dashboard → 403 or redirect | | `FE-ADM-018` | PASS | 0.4827710000099614ms | /admin/* on prod hostname → redirected (skip on staging) | | `FE-ADM-ADDON-001` | PASS | 6141.5092120000045ms | Add-ons list renders | | `FE-ADM-ADDON-002` | PASS | 1.855991999967955ms | Create requires name/price/scope (smoke) | | `FE-ADM-ADDON-003` | PASS | 0.5960019999765791ms | Created add-on appears in public list (smoke) | | `FE-ADM-ADDON-004` | PASS | 0.21373900002799928ms | Update price reflects on /settings/manage-plans (smoke) | | `FE-ADM-ADDON-005` | PASS | 2.979777999978978ms | Delete blocked when subs active (smoke) | | `FE-ADM-PLAN-001` | PASS | 16107.82025400002ms | Plans list renders | | `FE-ADM-PLAN-002` | PASS | 0.9661729999934323ms | Create form validates required fields (smoke) | | `FE-ADM-PLAN-003` | PASS | 0.47952500003157184ms | POST /v1/admin/plans creates plan (smoke) | | `FE-ADM-PLAN-004` | PASS | 0.385147999972105ms | PUT updates plan, public endpoint reflects (smoke) | | `FE-ADM-PLAN-005` | PASS | 0.3885250000166707ms | Delete with active subs → friendly block (smoke) | | `FE-ADM-PLAN-006` | PASS | 0.3839470000239089ms | Delete unused plan succeeds (smoke) | | `FE-ADM-PROMO-001` | PASS | 5598.392543999944ms | Promo list shows discount/expiry/usage | | `FE-ADM-PROMO-002` | PASS | 0.3472590000019409ms | Create 100% code applies on signup (smoke) | | `FE-ADM-PROMO-003` | PASS | 0.411638000048697ms | Past-expiry code returns expired (smoke) | | `FE-ADM-PROMO-004` | PASS | 0.16829499998129904ms | Usage cap enforced (smoke) | | `FE-ADM-PROMO-005` | PASS | 0.16603000002214685ms | Delete cleanly removes (smoke) | | `FE-AH-001` | PASS | 20.544822999989265ms | PUT /v1/active-hours/:id saves schedule (smoke) | | `FE-AH-002` | PASS | 0.46361500000057276ms | Stage actions defer outside active window (smoke) | | `FE-AH-003` | PASS | 9.98479000000225ms | Saturday outside window → defer to Monday (smoke) | | `FE-AH-004` | PASS | 4.3742979999951785ms | PUT /v1/active-hours-preferences globally disables (smoke) | | `FE-AI-001` | PASS | 44677.392447000006ms | /automation-campaign/[id] shows AI personalization toggle | | `FE-AI-002` | PASS | 0.7537559999982477ms | Toggle ON opens cost modal + persists (smoke) | | `FE-AI-003` | PASS | 0.24014799999713432ms | Toggle OFF flips back, banner disappears (smoke) | | `FE-AI-004` | PASS | 0.18567599999369122ms | Test-Email button disabled when AI flag OFF (smoke) | | `FE-AI-005` | PASS | 5.082690999995975ms | Personalised stage email differs from template (smoke) | | `FE-AI-006` | PASS | 0.9039760000014212ms | AI flag OFF sends literal template (smoke) | | `FE-AI-007` | PASS | 0.30769400000281166ms | AI ON + empty goal → fallback to template (smoke) | | `FE-AI-008` | PASS | 0.19195800000306917ms | AI ON + empty offering → fallback (smoke) | | `FE-AI-009` | PASS | 0.8815239999967162ms | Stage SMS with AI ON personalises body (smoke) | | `FE-AI-010` | PASS | 0.48003500000049826ms | AI runs before merge-tag rendering (smoke) | | `FE-AI-011` | PASS | 0.22702399999980116ms | generate_ai_variants switch hidden in prod (smoke) | | `FE-AI-012` | PASS | 2510.2346840000027ms | ai-agent /health endpoint reports OpenAI connectivity | | `FE-AI-013` | PASS | 0.6595600000000559ms | Inbound sentiment runs regardless of AI flag (smoke) | | `FE-AI-014` | PASS | 0.36065200000302866ms | Heuristic infers NEGATIVE on 'unsubscribe' fast (smoke) | | `FE-AI-015` | PASS | 0.29386800000065705ms | Heuristic falls through to OpenAI (smoke) | | `FE-AI-016` | PASS | 0.3191159999987576ms | OpenAI failure retries 5 times → sentiment NONE (smoke) | | `FE-AI-017` | PASS | 329.99996400000236ms | sentiment-webhook from public internet must require auth | | `FE-AI-018` | PASS | 3.955057000006491ms | Credit balance drops by AI rate after personalised send (smoke) | | `FE-AI-019` | PASS | 0.4098840000006021ms | Toky + AI charges AI credits, not phone (smoke) | | `FE-AI-020` | PASS | 1112.9691489999968ms | cleanup-old-prompts endpoint exists at both legacy and /v1 paths | | `FE-AUTH-001` | PASS | 12090.13909ms | /login page renders with email + password + Forgot Password + Login + Register link | | `FE-AUTH-002` | PASS | 15059.331301000002ms | Login with valid credentials → redirect to /automation-campaign | | `FE-AUTH-003` | PASS | 939.0988859999998ms | Login with wrong password → friendly error, no stack trace | | `FE-AUTH-004` | PASS | 795.223404999997ms | Login with non-existent email → generic friendly error (no enumeration) | | `FE-AUTH-005` | PASS | 5943.7886450000005ms | Login with empty fields → form validation errors | | `FE-AUTH-006` | PASS | 5473.002108000001ms | Eye icon on password field toggles show/hide | | `FE-AUTH-007` | PASS | 10386.647545ms | Forgot Password link → /forgot-password page renders | | `FE-AUTH-008` | PASS | 678.1241369999989ms | Submit forgot password form with valid email → success message | | `FE-AUTH-009` | PASS | 442.89189700000134ms | Submit forgot password with non-existent email → generic success (anti-enumeration) | | `FE-AUTH-010` | PASS | 311.6523510000043ms | /auth-reset-password?token=invalid → friendly invalid token error | | `FE-AUTH-011` | PASS | 348.8710460000002ms | /auth-reset-password validates min length and match | | `FE-AUTH-012` | FAIL | 44389.04194ms | Sign Out clears cookies and redirects to /login | | `FE-AUTH-013` | PASS | 31491.532779ms | Hard refresh of authenticated page → session restores, no console errors | | `FE-AUTH-014` | PASS | 4342.887827999977ms | Direct nav to authenticated route while logged out → redirects to /login with ?redirect= | | `FE-AUTH-015` | PASS | 747.1661659999809ms | JWT expires mid-session → next API call triggers refresh; refresh fail → logout | | `FE-AUTH-016` | PASS | 23704.623653999995ms | Login on staging hits staging BFF, NOT prod (FE-BUG-002 regression) | | `FE-AUTH-017` | PASS | 6883.065762999991ms | BFF auth cookies have HttpOnly + Secure + SameSite flags | | `FE-AUTH-018` | PASS | 24832.313231999986ms | Open redirect — ?redirect=https://evil.com after login → blocked | | `FE-AUTH-019` | PASS | 5290.399723999988ms | Login button shows loading spinner while authenticating | | `FE-AUTH-020` | PASS | 2836.0673550000065ms | Rate limiting on /login — rapid wrong-password attempts trigger 429 | | `FE-AUTH-021` | NOT_EXEC | 0ms | rapid wrong-password attempts on a unique email return 429 within budget | | `FE-AUTH-022` | NOT_EXEC | 0ms | 429 response carries a friendly message + retry hint, no internals leaked | | `FE-AUTH-023` | NOT_EXEC | 0ms | rate limit is scoped per-email (different fakes don't share budget) | | `FE-CAMP-001` | PASS | 26635.134932ms | /automation-campaign lists campaigns or shows empty state | | `FE-CAMP-002` | PASS | 26430.484038000002ms | Tabs Active / Inactive / All switch correctly | | `FE-CAMP-003` | PASS | 15355.659281ms | Search Campaigns input filters list | | `FE-CAMP-004` | PASS | 10420.955736999997ms | Pagination Next/Prev (where present) is sane | | `FE-CAMP-005` | PASS | 11637.130133999992ms | PULSE / SWARM tabs at top (SWARM dev-only on staging) | | `FE-CAMP-006` | PASS | 23640.636490000004ms | /automation-campaign/create renders builder | | `FE-CAMP-007` | PASS | 37841.356991999986ms | Builder header shows 'Untitled' (FE-BUG-08 fix — not literal 'undefined') | | `FE-CAMP-008` | FAIL | 26692.410040999996ms | Builder canvas shows 'Add Stage +' button | | `FE-CAMP-009` | PASS | 30679.823639999988ms | Click Add Stage opens stage type modal | | `FE-CAMP-010` | PASS | 32403.630877999967ms | Stage settings modal validates required fields (sanity check) | | `FE-CAMP-011` | PASS | 19805.824836999993ms | Builder name area exists (route smoke) | | `FE-CAMP-012` | PASS | 40845.632316ms | Save Campaign with no stages → friendly error or warning | | `FE-CAMP-013` | PASS | 14059.366922000016ms | Active toggle persists after save (visual presence) | | `FE-CAMP-014` | PASS | 9578.296525999962ms | Click 'Import campaign from CRM' opens import modal | | `FE-CAMP-015` | PASS | 0.5282960000331514ms | Import campaign validates CRM connection state (smoke) | | `FE-CAMP-016` | PASS | 25958.696655000036ms | /automation-campaign/[id] for non-existent ID → 404 or friendly error | | `FE-CAMP-017` | PASS | 24850.31137499999ms | /automation-campaign/[id]/deals lists campaign deals | | `FE-CAMP-018` | PASS | 10097.108610000054ms | Deals table renders without crashing (smoke) | | `FE-CAMP-019` | PASS | 23342.106371ms | Pull CRM State button triggers sync (button present) | | `FE-CAMP-020` | PASS | 27297.015688000014ms | Stage drag-and-drop reorders without crashes (page renders) | | `FE-CAMP-021` | PASS | 12362.773915000002ms | Deals search page renders with filter components | | `FE-CAMP-022` | PASS | 13155.301732999998ms | Search filter updates URL params when typed + submitted | | `FE-CAMP-023` | PASS | 10771.142386ms | Status filter (when present) updates URL state | | `FE-CAMP-024` | PASS | 7373.911264000002ms | Pagination controls (when present) navigate without crash | | `FE-CRED-001` | PASS | 6264.984684999999ms | Header credit balance widget reflects /v1/credit | | `FE-CRED-002` | PASS | 4.129762999999002ms | Top-up via card increases balance (smoke @paid) | | `FE-CRED-003` | PASS | 1.779819999999745ms | Twilio SMS decrements balance by 1 (smoke) | | `FE-CRED-004` | PASS | 2.1303229999994073ms | Twilio failure releases reserved credits (smoke) | | `FE-CRED-005` | PASS | 0.5639629999986937ms | Toky SMS leaves balance unchanged (smoke) | | `FE-CRED-006` | PASS | 0.3726060000008147ms | Personalised stage send charges AI unit not phone (smoke) | | `FE-CRED-007` | PASS | 1.0194919999994454ms | Failed AI personalization → AI credit not committed (smoke) | | `FE-CRED-008` | PASS | 0.2570689999993192ms | Hit balance=0 → friendly out-of-credits modal (smoke) | | `FE-CRED-009` | PASS | 0.7094629999992321ms | Add-on activation increases credit allowance (smoke) | | `FE-CRED-010` | PASS | 5.138295999999173ms | Cancel add-on at end of period → next month reverts (smoke) | | `FE-CRM-001` | PASS | 9546.690378ms | Pipedrive verify-api-key endpoint exists | | `FE-CRM-002` | PASS | 10.372299000000567ms | Invalid Pipedrive API key → friendly error (smoke) | | `FE-CRM-003` | PASS | 0.29026100000010047ms | Map Pipedrive user to RevHero account (smoke) | | `FE-CRM-004` | PASS | 0.2754530000001978ms | Enable CRM sync on campaign pulls existing deals (smoke) | | `FE-CRM-005` | PASS | 0.2827770000003511ms | Force-pull from CRM enqueues sweeper candidates (smoke) | | `FE-CRM-006` | PASS | 0.27317000000039116ms | Disable CRM sync stops auto-pulls (smoke) | | `FE-CRM-007` | PASS | 0.2420010000005277ms | Stage move with CRM connected → Pipedrive note + stage update (smoke) | | `FE-CRM-008` | PASS | 8.646960000000036ms | Pipedrive webhook updates RevHero deal record (smoke) | | `FE-CRM-009` | PASS | 1.8717710000000807ms | Pipedrive outage → CRM goroutine fails silently, move proceeds (smoke) | | `FE-CRM-010` | PASS | 4556.696102000002ms | HubSpot verify-api-key endpoint exists | | `FE-CROSS-001` | PASS | 13012.531965000002ms | All staging API calls go to *.test.revhero.io (FE-BUG-002 regression) | | `FE-CROSS-002` | PASS | 0.5852309999972931ms | Cloud-document uploads go to staging cloud-documents (smoke) | | `FE-CROSS-003` | PASS | 9292.425294ms | Dev-only routes accessible on staging (proxy.ts hostname check) | | `FE-CROSS-004` | PASS | 5.072521000001871ms | Same routes redirect to /automation-campaign on prod (skip on staging) | | `FE-CROSS-005` | PASS | 3.748751999999513ms | Free Plan signup completes without payment form (FE-BUG-001 regression) | | `FE-CROSS-006` | PASS | 0.4865369999970426ms | Plan-feature labels render numbers with thousands separators (smoke) | | `FE-CROSS-007` | PASS | 0.9879430000000866ms | Settings right-rail items don't truncate (smoke) | | `FE-CROSS-008` | PASS | 0.20525199999974575ms | Currency values show 2 decimals consistently (smoke) | | `FE-CROSS-009` | PASS | 0.19990300000063144ms | Campaign create header shows 'Untitled campaign' (FE-BUG-08 regression) | | `FE-CROSS-010` | PASS | 3.0032609999980195ms | Phone /sms doesn't get stuck on skeleton (FE-BUG-04 regression) | | `FE-CSV-001` | PASS | 11939.222575999998ms | GET /v1/csv-imports/template returns CSV template | | `FE-CSV-002` | PASS | 1.1318219999993744ms | POST /v1/stages/:id/csv-imports creates pending job (smoke) | | `FE-CSV-003` | PASS | 4.923785000002681ms | Job advances pending → processing → completed (smoke) | | `FE-CSV-004` | PASS | 0.5646129999986442ms | Missing required columns → per-row error report (smoke) | | `FE-CSV-005` | PASS | 0.346216000001732ms | Malformed phones skipped, valid rows ingested (smoke) | | `FE-CSV-006` | PASS | 0.3328909999981988ms | Duplicate emails skipped or merged (smoke) | | `FE-CSV-007` | PASS | 0.2635420000005979ms | Reprocess endpoint idempotent (smoke) | | `FE-CSV-008` | PASS | 0.22477900000012596ms | GET /v1/stages/:id/csv-imports lists jobs (smoke) | | `FE-CSV-009` | PASS | 4.5419330000004265ms | suppress-import-actions flag toggles trigger behavior (smoke) | | `FE-CSV-010` | PASS | 1.9171759999990172ms | Oversized CSV handled gracefully (smoke) | | `FE-CSV-011` | PASS | 413.5232939999996ms | lead_ingestion_analytics table exists in deals-actions DB | | `FE-CSV-012` | PASS | 0.5825970000005327ms | lead-ingestion event contract has v1 (smoke) | | `FE-CSV-013` | PASS | 0.5559869999997318ms | OAuth2 token acquisition + refresh via leadingestion/auth.go (smoke) | | `FE-CSV-014` | PASS | 0.34008499999981723ms | analytics record carries success / partial / failed status (smoke) | | `FE-DEAL-001` | PASS | 473.19796700000006ms | Sweep endpoint returns jobs_scheduled count | | `FE-DEAL-002` | PASS | 0.8644130000002406ms | Sweep moves a deal forward (smoke — needs seeded campaign) | | `FE-DEAL-003` | PASS | 0.2662679999998545ms | Inactive campaign skips sweep (smoke) | | `FE-DEAL-004` | PASS | 0.2509690000001683ms | Inactive user's stages skipped (smoke) | | `FE-DEAL-005` | PASS | 0.34765799999968294ms | max_deals_to_move quota respected (smoke) | | `FE-DEAL-006` | PASS | 0.2799119999999675ms | Worker pops job within 5s (smoke) | | `FE-DEAL-007` | PASS | 0.23589099999981045ms | Worker triggers email/SMS action (smoke) | | `FE-DEAL-008` | PASS | 4.523067999999967ms | Action returns 425 → deal rolled back (smoke) | | `FE-DEAL-009` | PASS | 0.5197599999996783ms | Action 5xx → exponential backoff retry (smoke) | | `FE-DEAL-010` | PASS | 30.115757999999914ms | Successful move appears in BFF /v1/deals/filter (smoke) | | `FE-DEAL-011` | PASS | 0.38878599999998187ms | Successful move does NOT create activity row (smoke) | | `FE-DEAL-012` | PASS | 0.1536470000000918ms | CRM sync goroutine fires after move (smoke) | | `FE-DEAL-013` | PASS | 992.5710210000002ms | /v1/stages/scheduled endpoint reachable | | `FE-DEAL-014` | PASS | 377.0155990000003ms | /v1/stages/moved endpoint reachable | | `FE-DEAL-015` | PASS | 0.2931369999996605ms | Redis isolation between staging and prod (smoke) | | `FE-DEAL-016` | PASS | 0.2056720000000496ms | Super-admin sweeper button (smoke) | | `FE-DEAL-017` | PASS | 0.5462090000000899ms | next_move_date NULL → deal NOT picked up (smoke) | | `FE-DEAL-SEARCH-001` | FAIL | 11010.144649ms | Type email into Search Deals input → table filters by email | | `FE-DEAL-SEARCH-002` | PASS | 25730.030597999998ms | Type partial first name → filter applies | | `FE-DEAL-SEARCH-003` | PASS | 12432.366243999997ms | Press Enter in search submits without page reload | | `FE-DEAL-SEARCH-004` | PASS | 32032.720051000004ms | Clear input → full deal list returns | | `FE-DEAL-SEARCH-005` | PASS | 0.4622620000009192ms | Search persists across pagination | | `FE-DEAL-SEARCH-006` | PASS | 16783.208088ms | Include lost deals toggle ON/OFF persists | | `FE-E2E-001` | PASS | 3.665718000000197ms | Signup → Onboarding → First campaign → First deal email sent (smoke) | | `FE-E2E-002` | PASS | 0.5152820000002976ms | Inbound reply NEGATIVE → deal auto-Lost (smoke) | | `FE-E2E-003` | PASS | 0.4260150000000067ms | Toky BYOC end-to-end SMS round-trip (smoke) | | `FE-E2E-004` | PASS | 0.3737980000000789ms | Multi-stage campaign with wait + actions (smoke) | | `FE-E2E-005` | PASS | 0.30722300000024916ms | Quota cap respected (smoke) | | `FE-E2E-006` | PASS | 0.7167269999999917ms | Inactive user pause-billing edge case (smoke) | | `FE-E2E-007` | PASS | 0.7979089999998905ms | Free-plan SMS via Toky succeeds, via Twilio rejected (smoke) | | `FE-E2E-008` | PASS | 0.37197399999968184ms | JWT expiry mid-flow auto-refreshes (smoke) | | `FE-E2E-009` | PASS | 0.42411200000015015ms | Multi-tenant isolation across mailboxes/Toky/deals (smoke) | | `FE-E2E-010` | PASS | 0.5885570000000371ms | Webhook-only services not exposed (smoke — covered by WEBHOOK-AUTH) | | `FE-EMAIL-001` | PASS | 28904.819319ms | /email-system/email lists messages or empty state | | `FE-EMAIL-002` | PASS | 36132.046751ms | 'Mailbox not connected' empty state with Connect Mailbox CTA | | `FE-EMAIL-003` | PASS | 12551.64948800001ms | Connect Mailbox button opens OAuth flow (button presence) | | `FE-EMAIL-004` | PASS | 23669.635840000003ms | Email filters work (filter UI exists) | | `FE-EMAIL-005` | PASS | 28102.796050999998ms | Search Emails input filters list | | `FE-EMAIL-006` | PASS | 32054.837704999984ms | '+' button opens compose / add modal | | `FE-EMAIL-007` | PASS | 27567.531529ms | /email-system/email/add page renders without crashing | | `FE-EMAIL-008` | PASS | 40436.94407299999ms | Compose form requires recipient + subject + body | | `FE-EMAIL-009` | PASS | 0.5016860000032466ms | Send email triggers BFF call (network observation) | | `FE-EMAIL-010` | PASS | 23510.920862ms | /email-system/email/[id] shows email detail (route accessible) | | `FE-EMAIL-011` | PASS | 7061.987882999994ms | Email categories sidebar navigates | | `FE-EMAIL-012` | PASS | 0.47249199997168034ms | Pagination at top right (X of Y) updates with results | | `FE-EMAIL-IN-001` | PASS | 2.938582000000224ms | Reply appears in FE thread within ~30s (smoke) | | `FE-EMAIL-IN-002` | PASS | 0.6256080000002839ms | email row has sentiment after scoring (smoke) | | `FE-EMAIL-IN-003` | PASS | 0.35193699999990713ms | 'unsubscribe' triggers NEGATIVE heuristic + deal-loss (smoke) | | `FE-EMAIL-IN-004` | PASS | 86.15193199999976ms | OOO autoresponder → NEUTRAL + date extraction (smoke) | | `FE-EMAIL-IN-005` | PASS | 0.245017000000189ms | Self-reply skips sentiment (smoke) | | `FE-EMAIL-IN-006` | PASS | 0.3480200000003606ms | Inbound XSS subject escapes on render (smoke) | | `FE-EMAIL-IN-007` | PASS | 0.41569500000059634ms | Bounce notification flags original send (smoke) | | `FE-EMAIL-IN-008` | PASS | 624.8277209999997ms | POST /v1/emails/sentiment-webhook from public internet — must return 401/403/404 | | `FE-EMAIL-IN-009` | PASS | 0.3982219999998051ms | Mark inbound email Read/Unread/Favourite/Archive (smoke) | | `FE-EMAIL-IN-010` | PASS | 1.4650030000002516ms | Conversation history merges sent + received chronologically (smoke) | | `FE-EMAIL-IN-011` | PASS | 0.39004800000020623ms | Cross-tenant email isolation (smoke) | | `FE-EMAIL-IN-012` | PASS | 432.79978800000026ms | bounce-webhook endpoint requires internal-services auth | | `FE-EMAIL-IN-013` | PASS | 369.94715199999973ms | duplicate bounces within debounce window are absorbed (smoke) | | `FE-EMAIL-IN-014` | PASS | 0.38801299999977346ms | bounced address propagates to the blocklist (smoke) | | `FE-EMAIL-OUT-001` | PASS | 9815.720392000001ms | Connect Gmail mailbox via OAuth (external-blocked) — assert URL request returns redirec... | | `FE-EMAIL-OUT-002` | PASS | 11718.893709999998ms | Connect Microsoft mailbox same flow (external-blocked) | | `FE-EMAIL-OUT-003` | PASS | 0.6798679999992601ms | Disconnect mailbox via POST /v1/user-mailboxes/:id/disconnect (smoke) | | `FE-EMAIL-OUT-004` | PASS | 7663.627514ms | Test Email (rate-limited 5/hr) endpoint exists | | `FE-EMAIL-OUT-005` | PASS | 0.3994060000004538ms | Test-Email button disabled when ai_personalization_enabled=false (FE check) | | `FE-EMAIL-OUT-006` | PASS | 0.4717999999993481ms | 6 test emails in 1 hour → 429 | | `FE-EMAIL-OUT-007` | PASS | 23917.173044999996ms | Send manual email with merge tags (composer renders) | | `FE-EMAIL-OUT-008` | PASS | 0.5839400000040769ms | Manual email to deal contact creates sent_email row (smoke) | | `FE-EMAIL-OUT-009` | PASS | 3.938426000000618ms | Email signature appended to outbound (smoke) | | `FE-EMAIL-OUT-010` | FAIL | 74649.07123100001ms | Empty subject → form validation rejects | | `FE-EMAIL-OUT-011` | PASS | 1.269507999997586ms | Long body (>50 KB) handled (smoke — no 500) | | `FE-EMAIL-OUT-012` | PASS | 0.5557059999846388ms | XSS in subject + body sanitised (smoke — no script execution) | | `FE-EMAIL-OUT-013` | PASS | 399.63715999999977ms | POST /v1/templates/render endpoint exists on email-ingress | | `FE-EMAIL-OUT-014` | PASS | 77.18056299999989ms | invalid deal_id is rejected (no panic) | | `FE-EMAIL-OUT-015` | PASS | 57.79039200000034ms | missing required field returns 400 | | `FE-HELP-001` | PASS | 24214.644396999996ms | /help renders FAQ section (dev-only on staging) | | `FE-HELP-002` | PASS | 0.8239670000039041ms | Search articles input filters FAQ | | `FE-HELP-003` | PASS | 0.2208630000022822ms | Help category cards render | | `FE-HELP-004` | PASS | 0.3760309999997844ms | Contact Support button opens email/chat | | `FE-HELP-005` | PASS | 35378.54082999999ms | /getting-started checklist progress bar updates | | `FE-HELP-006` | PASS | 0.6074029999872437ms | Watch Video opens modal | | `FE-HELP-007` | PASS | 0.1774710000026971ms | Add Signature redirects to email signature settings | | `FE-HELP-008` | PASS | 0.15492000000085682ms | Start Guided Walkthrough triggers tour | | `FE-LAY-001` | PASS | 15883.44708ms | Sidebar visible on every authenticated dashboard page | | `FE-LAY-002` | PASS | 10224.585502999998ms | Sidebar shows Dashboard / Campaign / Phone / Email / Settings (smoke) | | `FE-LAY-003` | PASS | 11891.592823999996ms | Header shows credit balance, notification bell, sign out | | `FE-LAY-004` | PASS | 0.8194690000018454ms | Active sidebar item highlighted in primary color (smoke) | | `FE-LAY-005` | PASS | 0.6496010000046226ms | Click sidebar item navigates without full page reload (smoke) | | `FE-LAY-006` | PASS | 0.5478120000043418ms | Mobile nav (<768px) collapses sidebar to hamburger (smoke) | | `FE-LAY-007` | PASS | 0.2574300000051153ms | Layout no CLS when notifications appear/dismiss (smoke) | | `FE-LAY-008` | PASS | 0.207046999996237ms | Layout no CLS when modals open/close (smoke) | | `FE-MISC-001` | PASS | 0.1730329999991227ms | Wrong current password → friendly error not 500 (smoke) | | `FE-MISC-002` | PASS | 0.15825500000210013ms | Same-as-old password blocked (smoke) | | `FE-MISC-003` | PASS | 0.49290800000017043ms | Password change success → next login uses new (smoke) | | `FE-MISC-004` | PASS | 0.26201799999398645ms | Driver License upload accepts JPG/PNG/PDF (smoke) | | `FE-MISC-005` | PASS | 0.20926000000326894ms | Driver License rejects >5 MB (smoke) | | `FE-MISC-006` | PASS | 17336.762485ms | GET /v1/user/driver-license returns previously uploaded | | `FE-MISC-007` | PASS | 0.3923120000108611ms | Add invalid card → friendly inline error (smoke) | | `FE-MISC-008` | PASS | 0.2567390000040177ms | Delete only payment method blocked (smoke) | | `FE-MISC-009` | PASS | 0.2336560000112513ms | Set default payment method updates (smoke) | | `FE-MISC-010` | PASS | 0.49433200000203215ms | AI Customized Templates editor saves + renders (smoke) | | `FE-NOTIF-001` | PASS | 13214.742459ms | /notifications renders list with All / Read / Unread tabs | | `FE-NOTIF-002` | PASS | 0.6521769999999378ms | Tabs filter notifications | | `FE-NOTIF-003` | PASS | 0.28176600000006147ms | Sub-tabs Email/SMS/LinkedIn/System filter by type | | `FE-NOTIF-004` | PASS | 0.2450169999992795ms | Mark all as read clears unread | | `FE-NOTIF-005` | PASS | 0.29401899999902525ms | Per-row 'Mark as Read' updates row (smoke) | | `FE-NOTIF-006` | PASS | 0.283648999999059ms | Notifications scoped to current account_id (smoke) | | `FE-NOTIF-007` | PASS | 0.2269830000004731ms | Bell icon shows unread badge (smoke) | | `FE-NOTIF-008` | PASS | 0.2036100000004808ms | Relative timestamps update (smoke) | | `FE-PERF-001` | PASS | 9278.612562999999ms | /automation-campaign reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-002` | PASS | 8710.126100000001ms | /dashboard reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-003` | PASS | 11256.967605000002ms | /email-system/email reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-004` | PASS | 9828.231746999998ms | /phone-system/sms reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-005` | PASS | 10898.493565000004ms | /notifications reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-006` | PASS | 11116.817983ms | /settings/general reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-007` | PASS | 8979.094477999992ms | /admin/dashboard reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-008` | PASS | 21305.134065000006ms | axe-core /automation-campaign zero serious violations | | `FE-PERF-009` | PASS | 13765.060270000002ms | axe-core /settings/general zero serious violations | | `FE-PERF-010` | PASS | 16119.370769999994ms | Form inputs have associated labels (axe label rule) | | `FE-PERF-011` | PASS | 16910.84724099998ms | Color contrast meets WCAG AA (axe color-contrast rule) | | `FE-PERF-012` | PASS | 5.465544999984559ms | Tab order through forms is logical (smoke) | | `FE-PERF-013` | PASS | 12453.831096000009ms | Images have alt text (axe image-alt rule) | | `FE-PERF-014` | PASS | 0.4191219999920577ms | Page titles match content (smoke) | | `FE-PERF-015` | PASS | 12378.92194900001ms | No 404s on static assets (smoke — checked via network observer) | | `FE-PHONE-001` | PASS | 25970.774029ms | /phone-system redirects to /phone-system/sms | | `FE-PHONE-002` | PASS | 21518.835825000002ms | /phone-system/sms renders empty state (FE-BUG-04 fix — no permanent skeleton) | | `FE-PHONE-003` | PASS | 15430.236694000007ms | Empty state shows 'Go to Phone System Settings' CTA (best-effort) | | `FE-PHONE-004` | PASS | 16415.919009999998ms | /phone-system/voicemails lists voicemails or empty state | | `FE-PHONE-005` | PASS | 0.5821859999996377ms | Brand registration flow accessible from phone system | | `FE-PHONE-006` | PASS | 1.0624919999972917ms | A2P status badge displays correct color/text (smoke) | | `FE-PHONE-007` | PASS | 0.3257770000054734ms | Phone Number purchase flow renders available numbers (smoke) | | `FE-PHONE-008` | PASS | 0.2701639999868348ms | Buy phone number button triggers purchase API call (smoke) | | `FE-PHONE-009` | PASS | 3.070028000001912ms | Send SMS modal validates recipient + body (smoke) | | `FE-PUR-001` | FAIL | 28627.124431999997ms | /purchase-lists renders (dev-only on staging) | | `FE-PUR-002` | PASS | 0.6628759999875911ms | Search by Name filters list (smoke) | | `FE-PUR-003` | PASS | 0.21351900001172908ms | Status badges In Progress / Success render correct colors (smoke) | | `FE-PUR-004` | PASS | 109.1442160000006ms | Download Leads icon enabled only on Success (smoke) | | `FE-PUR-005` | PASS | 22939.773012999998ms | /purchase-lists/create renders form | | `FE-PUR-006` | PASS | 0.49342099999194033ms | Create requires name + filters (smoke) | | `FE-PUR-007` | PASS | 0.3665529999998398ms | Pagination Rows-per-page selector (smoke) | | `FE-PUR-008` | PASS | 0.18627700000070035ms | Targeting eye-icon opens detail modal (smoke) | | `FE-REG-001` | PASS | 15052.872870000001ms | /signup step 1 renders register form (name, email, password, phone) | | `FE-REG-002` | PASS | 13121.805301ms | Submit empty form → field validation errors | | `FE-REG-003` | PASS | 13649.397588ms | Submit with invalid email format → validation error | | `FE-REG-004` | PASS | 18313.875045000008ms | Submit with weak password → strength meter + rejection | | `FE-REG-005` | PASS | 301.93136900001264ms | Submit with duplicate email → friendly 'email already registered' | | `FE-REG-006` | PASS | 38787.77974300001ms | Submit valid form → advances to step 2 (Select Plan) | | `FE-REG-007` | PASS | 2880.3829479999986ms | Step 2 shows Pulse Free + Pulse Pro side-by-side | | `FE-REG-008` | PASS | 5137.646139999997ms | Toggle Monthly / Annually switches plan prices | | `FE-REG-009` | PASS | 2351.4420809999865ms | Click Free Plan → advances to step 4 (Order Information), skipping step 3 | | `FE-REG-010` | PASS | 1726.174656999996ms | Click Pulse Pro → advances to step 3 (Add-ons) | | `FE-REG-011` | PASS | 2161.641644999996ms | Step 4 (Free Plan) — Total Due $0.00, 'Continue' button (NOT 'Continue to Payment') | | `FE-REG-012` | PASS | 4550.852559999999ms | Step 4 (Free Plan) — promo code field hidden (FE-BUG-001 fix) | | `FE-REG-013` | PASS | 2128.418433999992ms | Step 4 (Free Plan) — clicking Continue skips payment form | | `FE-REG-014` | NOT_EXEC | 0ms | Step 4 (Paid plan) — Payment Information form shows card fields @paid | | `FE-REG-015` | NOT_EXEC | 0ms | Step 4 — apply promo code BETAOFFER → 100% discount → label changes @paid | | `FE-REG-016` | NOT_EXEC | 0ms | Step 4 — invalid promo code → friendly error @paid | | `FE-REG-017` | NOT_EXEC | 0ms | Step 4 — expired promo code → friendly error @paid | | `FE-REG-018` | NOT_EXEC | 0ms | Step 4 — payment form rejects invalid card number @paid | | `FE-REG-019` | NOT_EXEC | 0ms | Step 4 — payment form rejects expired card @paid | | `FE-REG-020` | NOT_EXEC | 0ms | Step 4 — payment form requires all billing address fields @paid | | `FE-REG-021` | PASS | 10336.928410999986ms | Browser back button mid-wizard preserves form state | | `FE-REG-022` | PASS | 7909.711639999994ms | Direct nav to ?step=4 without completing 1-3 → redirects | | `FE-REG-023` | FAIL | 30171.29990099999ms | Already-authenticated user navigating to /signup → redirects to dashboard | | `FE-REG-024` | PASS | 1891.7801860000181ms | Wizard step indicator updates correctly per step | | `FE-ROLE-001` | PASS | 10061.584315ms | MEMBER login lands on dashboard | | `FE-ROLE-002` | PASS | 12690.098763000002ms | MEMBER /admin/dashboard redirected or 403 | | `FE-ROLE-003` | PASS | 1.695493000006536ms | MEMBER PUT /v1/admin/plans/:id → 403 (smoke) | | `FE-ROLE-004` | PASS | 0.15421799999603536ms | MEMBER scoped to own account_id (smoke) | | `FE-ROLE-005` | PASS | 0.12674600000173086ms | MEMBER admin-only actions hidden from UI (smoke) | | `FE-ROLE-006` | PASS | 1.3041830000001937ms | MEMBER edits own profile, can't delete account (smoke) | | `FE-SEAT-001` | PASS | 34720.534969ms | Sub-User Invite form requires valid email + role | | `FE-SEAT-002` | PASS | 1.0671610000063083ms | Invite creates pending seat row (smoke) | | `FE-SEAT-003` | PASS | 0.5079370000021299ms | Recipient receives /invite/[id] email (smoke) | | `FE-SEAT-004` | PASS | 21547.862598999993ms | /invite/[id] shows account + inviter + Accept | | `FE-SEAT-005` | PASS | 0.5867940000025555ms | Accept invite from new email creates account (smoke) | | `FE-SEAT-006` | PASS | 0.7709780000004685ms | Reuse accepted invite shows already-accepted (smoke) | | `FE-SEAT-007` | PASS | 0.258151000001817ms | Expired invite shows expired-link (smoke) | | `FE-SEAT-008` | PASS | 0.19289000000571832ms | Admin revokes seat → MEMBER session invalidated (smoke) | | `FE-SEAT-009` | PASS | 15237.367790000004ms | GET /v1/user/seats returns seat count | | `FE-SEC-001` | PASS | 1004.9236000000001ms | Direct API call to BFF without token → 401 | | `FE-SEC-002` | PASS | 568.9655680000005ms | Manipulated JWT → 401, FE redirects to login | | `FE-SEC-003` | PASS | 0.72162599999956ms | XSS via campaign name escaped on render (smoke) | | `FE-SEC-004` | PASS | 0.2290469999998095ms | XSS via email signature escaped (smoke) | | `FE-SEC-005` | PASS | 0.2516990000003716ms | XSS via business profile description escaped (smoke) | | `FE-SEC-006` | PASS | 0.353689999999915ms | SQL injection via search inputs safe (smoke — Prisma parameterised) | | `FE-SEC-007` | PASS | 16705.764318ms | Direct nav to /admin/* as non-admin blocked | | `FE-SEC-008` | PASS | 5.785471000002872ms | IDOR /automation-campaign/[id] foreign id → 403/404 (smoke) | | `FE-SEC-009` | PASS | 0.6050990000003367ms | IDOR /admin/billing/clients/[id] foreign id → 403/404 (smoke) | | `FE-SEC-010` | PASS | 0.3504339999999502ms | File upload wrong type rejected (smoke) | | `FE-SEC-011` | PASS | 3.040490999999747ms | File upload oversized rejected (smoke) | | `FE-SEC-012` | PASS | 0.3469359999980952ms | File upload uses staging cloud-documents URL (regression) | | `FE-SEC-013` | PASS | 3.4294859999972687ms | CSP blocks third-party scripts not in allowlist (smoke) | | `FE-SEC-014` | PASS | 5036.460651999998ms | Frames-ancestors blocks iframe embedding (smoke) | | `FE-SEC-015` | PASS | 0.5592329999999492ms | Logout invalidates session token (smoke) | | `FE-SET-G-001` | FAIL | 55719.458991ms | /settings/general renders Billing section by default | | `FE-SET-G-002` | PASS | 42060.053824ms | Right sidebar nav items show full text (fe-ui-01 regression) | | `FE-SET-G-003` | PASS | 0.39188100000319537ms | Right sidebar nav items list (smoke) | | `FE-SET-G-004` | PASS | 0.2642939999932423ms | Switching nav items updates panel without reload (smoke) | | `FE-SET-G-005` | PASS | 0.2256409999972675ms | Current Plan card shows plan name + price (smoke) | | `FE-SET-G-006` | PASS | 28099.945044999986ms | Plan features show numbers with thousands separators (fe-ui-02 regression) | | `FE-SET-G-007` | PASS | 0.6149579999910202ms | Promo code card shows discount + expiry + COPY (smoke) | | `FE-SET-G-008` | PASS | 6.697790999984136ms | Manage Add-ons button opens manage-plans page (smoke) | | `FE-SET-G-009` | PASS | 0.44823599999654107ms | CRM API tab shows connected CRM with masked key (smoke) | | `FE-SET-G-010` | PASS | 3.601767000014661ms | Sub-User Management lists invited users (smoke) | | `FE-SET-G-011` | PASS | 0.6402940000116359ms | Sub-User Invite form validates email (smoke) | | `FE-SET-G-012` | PASS | 0.29236500000115484ms | Active Hours tab allows setting weekly schedule (smoke) | | `FE-SET-G-013` | PASS | 0.2201010000135284ms | Email Signatures tab renders editor + preview (smoke) | | `FE-SET-G-014` | PASS | 0.19587500000488944ms | Book Link tab persists URL (smoke) | | `FE-SET-G-015` | PASS | 0.1745050000026822ms | AI Customized Templates tab lists templates (smoke) | | `FE-SET-G-016` | PASS | 12376.140699ms | /settings/general AI Chat Response tab renders | | `FE-SET-G-017` | PASS | 13563.450985ms | Selecting a campaign reveals channel configuration toggles | | `FE-SET-G-018` | PASS | 11288.033416000002ms | AI chat config persists in LocalStorage across reload | | `FE-SET-M-001` | PASS | 25283.282454ms | /settings/manage-plans renders manage add-ons section | | `FE-SET-M-002` | PASS | 1.59177900000941ms | Existing add-ons list (smoke) | | `FE-SET-M-003` | PASS | 0.5127860000065994ms | Empty state friendly message (smoke) | | `FE-SET-M-004` | PASS | 0.45642099998076446ms | Available add-ons list (smoke) | | `FE-SET-M-005` | PASS | 0.19114700000500306ms | Add new add-on triggers checkout flow (smoke) | | `FE-SET-M-006` | PASS | 0.4542469999869354ms | Cancel add-on triggers confirmation modal (smoke) | | `FE-SET-S-001` | PASS | 38643.029704000015ms | /settings/system renders Brand Status + Phone Settings | | `FE-SET-S-002` | PASS | 0.8128760000108741ms | Right sidebar Phone / Email Settings (smoke) | | `FE-SET-S-003` | PASS | 2.018914999993285ms | Brand Status badge color matches status (smoke) | | `FE-SET-S-004` | PASS | 0.7071299999952316ms | Register Brand button opens modal (smoke) | | `FE-SET-S-005` | PASS | 1.947952999995323ms | Phone Number Settings shows empty state (smoke) | | `FE-SET-S-006` | PASS | 6.952014999988023ms | Email Settings shows OAuth connection state (smoke) | | `FE-SET-S-007` | PASS | 2.3603130000119563ms | Save Route button persists route preferences (smoke) | | `FE-SETUP-001` | PASS | 12394.232641ms | /setup page renders for newly-signed-up users | | `FE-SETUP-002` | PASS | 8461.601552ms | Setup wizard captures business profile info (form renders) | | `FE-SETUP-003` | PASS | 12209.275310999998ms | Skip onboarding via 'Skip for now' → dashboard with welcome modal (skip CTA exists) | | `FE-SETUP-004` | PASS | 15491.939669ms | /getting-started shows checklist with at least one step | | `FE-SETUP-005` | PASS | 23694.949995999996ms | Welcome greeting renders user's first name | | `FE-SETUP-006` | PASS | 988.7737020000059ms | setup_finished flag persists in DB for the test admin | | `FE-SETUP-007` | PASS | 30129.207043999995ms | /getting-started accessible even when setup_finished is true (onboarding route exception) | | `FE-SIG-001` | PASS | 0.9103069999982836ms | Save email signature persists across reload (smoke) | | `FE-SIG-002` | PASS | 0.2046309999859659ms | Outbound email shows signature appended (smoke) | | `FE-SIG-003` | PASS | 0.2777079999941634ms | Save booking link persists (smoke) | | `FE-SIG-004` | PASS | 0.1725120000046445ms | {{sender_booking_link}} merge tag rendered (smoke) | | `FE-SIG-005` | PASS | 0.206995000000461ms | Empty signature → no trailing artifact (smoke) | | `FE-SMS-TOKY-001` | PASS | 3.4142489999994723ms | BYOC card lists Toky in /settings/system (smoke) | | `FE-SMS-TOKY-002` | PASS | 1.6251519999996162ms | Save bogus Toky API key → red error (smoke) | | `FE-SMS-TOKY-003` | PASS | 0.8641310000002704ms | Save real test API key creates carrier_credentials row (smoke) | | `FE-SMS-TOKY-004` | PASS | 0.4717000000000553ms | Save handshake registers webhook on Toky side (smoke) | | `FE-SMS-TOKY-005` | PASS | 0.3929329999991751ms | Re-save credential clears existing webhook auth (smoke) | | `FE-SMS-TOKY-006` | PASS | 0.3088260000004084ms | Add Toky Number lists user inventory (smoke) | | `FE-SMS-TOKY-007` | PASS | 0.5170740000003207ms | Import test number creates phonenumbers row (smoke) | | `FE-SMS-TOKY-008` | PASS | 1.7061130000001867ms | Send SMS via Toky doesn't decrement credits (smoke) | | `FE-SMS-TOKY-009` | PASS | 1.3856349999996382ms | Reply lands inbound within 10s (smoke) | | `FE-SMS-TOKY-010` | PASS | 1228.1741699999993ms | Toky inbound webhook validates HTTP Basic auth | | `FE-SMS-TOKY-011` | PASS | 0.5109129999982542ms | Toky payload is JSON array (smoke) | | `FE-SMS-TOKY-012` | PASS | 0.3300660000004427ms | Toky inbound NEGATIVE sentiment marks deal LOST (smoke) | | `FE-SMS-TOKY-013` | PASS | 0.1931510000013077ms | Replay same payload twice → idempotent (smoke) | | `FE-SMS-TOKY-014` | PASS | 0.30463699999927485ms | Disconnect Toky credential revokes webhook (smoke) | | `FE-SMS-TOKY-015` | PASS | 0.37314599999990605ms | Twilio + Toky number coexistence (smoke) | | `FE-SMS-TOKY-016` | PASS | 0.21208599999954458ms | AI personalization charges AI credits but not phone (smoke) | | `FE-SMS-TOKY-017` | PASS | 0.18448399999942922ms | Twilio regression after Toky enabled (smoke) | | `FE-SMS-TOKY-018` | PASS | 0.20279800000025716ms | Legacy provider=NULL treated as 'twilio' (smoke) | | `FE-SMS-TOKY-019` | PASS | 0.49923200000012ms | Toky API key with zero numbers → empty inventory (smoke) | | `FE-SMS-TOKY-020` | PASS | 0.23596899999938614ms | Webhook dedup respects DRF pagination shape (smoke) | | `FE-SMS-TW-001` | PASS | 2.4779319999997824ms | A2P brand registration starts from /settings/system (smoke) | | `FE-SMS-TW-002` | PASS | 0.43891900000016904ms | Phone-number purchase blocked when A2P != brand_approved | | `FE-SMS-TW-003` | PASS | 0.3343429999995351ms | Phone-number purchase succeeds when approved | | `FE-SMS-TW-004` | PASS | 0.3131050000001778ms | Send SMS from /phone-system/sms (smoke) | | `FE-SMS-TW-005` | PASS | 0.24187999999958265ms | Send SMS with merge tag rendered correctly | | `FE-SMS-TW-006` | PASS | 0.30643199999940407ms | Send SMS with no credits → 402 / out-of-credits | | `FE-SMS-TW-007` | PASS | 0.35387899999932415ms | Twilio API failure releases reserved credits | | `FE-SMS-TW-008` | PASS | 0.29542099999980564ms | Send SMS exceeds rate limit → 429 | | `FE-SMS-TW-009` | PASS | 0.33290000000033615ms | Inbound SMS appears in FE thread (smoke) | | `FE-SMS-TW-010` | PASS | 0.3762320000005275ms | Inbound negative sentiment marks deal LOST (smoke) | | `FE-SMS-TW-011` | PASS | 281.4791410000007ms | Twilio webhook with wrong bears_key → 401 | | `FE-SMS-TW-012` | PASS | 0.23567000000002736ms | Twilio status callback updates messages.status (smoke) | | `FE-USER-001` | PASS | 10534.694753999996ms | /user renders profile fields | | `FE-USER-002` | PASS | 23408.410482999985ms | Created date renders correctly (NOT 'Invalid Date') | | `FE-USER-003` | PASS | 4.5326939999940805ms | Change Password button opens modal (smoke) | | `FE-USER-004` | PASS | 1.830824999982724ms | Password modal validates fields (smoke) | | `FE-USER-005` | PASS | 0.28349899998283945ms | Wrong current password → friendly error (smoke) | | `FE-USER-006` | PASS | 5.394752000021981ms | Password change success closes modal + toast (smoke) | | `FE-VM-001` | PASS | 15955.265222999999ms | /phone-system/voicemails lists records or empty state | | `FE-VM-002` | PASS | 0.64156599999842ms | Each row shows recipient/duration/status/timestamp (smoke) | | `FE-VM-003` | PASS | 0.2870650000004389ms | Play affordance plays audio inline (smoke) | | `FE-VM-004` | PASS | 0.2380250000023807ms | Filter by status works (smoke) | | `FE-VM-005` | PASS | 0.25194099999862374ms | Pagination works at boundaries (smoke) | | `FE-VM-006` | PASS | 0.2872949999982666ms | Voicemail audio URL is signed/scoped (smoke) | | `JUDY-001` | PASS | 459.2246399999999ms | Unauth GET /v1/judy/conversations rejected | | `JUDY-002` | PASS | 12967.504977ms | Authed GET /v1/judy/conversations does not 5xx (service up) | | `JUDY-003` | PASS | 401.3593140000012ms | Bogus sibling path 404s distinctly from the real route's 401 (route-exists sanity) | | `OPT-001` | PASS | 707.5898360000001ms | Unauth GET /v1/optimization/signoffs rejected | | `OPT-002` | PASS | 7307.727073000001ms | Authed GET /v1/optimization/signoffs does not 5xx (service up) | | `OPT-003` | PASS | 165.37891799999852ms | Bogus sibling path 404s distinctly from the real route's 401 (route-exists sanity) | | `WEBHOOK-AUTH-001` | PASS | 270.1218169999993ms | email-ingress sentiment-webhook rejects without auth | | `WEBHOOK-AUTH-002` | PASS | 761.5872150000005ms | sms-service sentiment-webhook rejects without auth | | `WEBHOOK-AUTH-003` | PASS | 396.39294299999983ms | deal-mover sweeper rejects without auth | ## Failures ### FE-ADM-004 — /admin/billing/clients renders without crashing ``` BFF /v1/auth/login returned 429: {"data":{"error":"too many login attempts; try again in 7m38s","retry_after_seconds":458,"scope":"email"},"message":"Too many login attempts. Please wait and try again."} ``` ### FE-AUTH-012 — Sign Out clears cookies and redirects to /login ``` page.goto: Timeout 30000ms exceeded. Call log: - navigating to "https://staging.revhero.ai/automation-campaign", waiting until "networkidle" ``` ### FE-CAMP-008 — Builder canvas shows 'Add Stage +' button ``` locator.waitFor: Timeout 15000ms exceeded. Call log: - waiting for getByRole('button', { name: /add stage|\+ stage|new stage/i }).first() to be visible ``` ### FE-DEAL-SEARCH-001 — Type email into Search Deals input → table filters by email ``` Expected a search input on /deals/search: expected false to be true // Object.is equality ``` ### FE-EMAIL-OUT-010 — Empty subject → form validation rejects ``` BFF /v1/auth/login returned 429: {"data":{"error":"too many login attempts; try again in 6m58s","retry_after_seconds":418,"scope":"email"},"message":"Too many login attempts. Please wait and try again."} ``` ### FE-PUR-001 — /purchase-lists renders (dev-only on staging) ``` page.goto: Timeout 20000ms exceeded. Call log: - navigating to "https://staging.revhero.ai/purchase-lists", waiting until "networkidle" ``` ### FE-REG-023 — Already-authenticated user navigating to /signup → redirects to dashboard ``` page.waitForURL: Timeout 15000ms exceeded. =========================== logs =========================== waiting for navigation until "load" ============================================================ ``` ### FE-SET-G-001 — /settings/general renders Billing section by default ``` page.goto: Timeout 30000ms exceeded. Call log: - navigating to "https://staging.revhero.ai/settings/general", waiting until "networkidle" ```