# RevHero QA Run — scheduled-20260522T081745 **Started:** 2026-05-22T08:17:49.998Z **Finished:** 2026-05-22T08:20:37.278Z ## Summary | | Count | |---|---| | Total | 469 | | PASS | 458 | | FAIL | 1 | | SKIP | 0 | | NOT_EXEC | 10 | ## Results | ID | Status | Duration | Description | |---|---|---|---| | `FE-ACT-001` | PASS | 3.1811380000003737ms | Outbound SMS produces 'SMS Sent' activity (smoke) | | `FE-ACT-002` | PASS | 0.596683999999641ms | Inbound SMS produces 'SMS Received' activity (smoke) | | `FE-ACT-003` | PASS | 0.5500670000001264ms | Email sent produces 'Email Sent' activity (smoke) | | `FE-ACT-004` | PASS | 0.5805740000000696ms | Email received produces 'Email Received' activity (smoke) | | `FE-ACT-005` | PASS | 0.45861599999989267ms | A2P brand approved produces activity (smoke) | | `FE-ACT-006` | PASS | 0.5869659999998476ms | Phone purchase produces 'Phone Number Purchased' (smoke) | | `FE-ACT-007` | PASS | 0.4778719999999339ms | Toky import produces 'Phone Number Imported' (smoke) | | `FE-ACT-008` | PASS | 0.4956150000002708ms | Deal-loss negative-sentiment activity (smoke) | | `FE-ACT-009` | PASS | 0.5987870000003568ms | Stage processing failure activity (smoke) | | `FE-ACT-010` | PASS | 1771.2680599999999ms | Activities mark-read endpoints respond | | `FE-ACT-011` | PASS | 0.5547059999998964ms | Activity feed scoped to current account_id (smoke) | | `FE-ACT-AIC-001` | PASS | 1154.0548979999994ms | POST /v1/stages/:id/actions/ai-call endpoint exists | | `FE-ACT-AIC-002` | PASS | 0.6858600000005026ms | PUT updates AI-call action (smoke) | | `FE-ACT-AIC-003` | PASS | 0.2860630000004676ms | DELETE removes action (smoke) | | `FE-ACT-AIC-004` | PASS | 0.228186000000278ms | Trigger requests AI call (smoke) | | `FE-ACT-AIC-005` | PASS | 0.15015000000039436ms | Empty prompt_template → validation error (smoke) | | `FE-ACT-BBS-001` | PASS | 710.1812610000006ms | Endpoint exists | | `FE-ACT-BBS-002` | PASS | 9.139038999999684ms | CRUD verified (smoke) | | `FE-ACT-BBS-003` | PASS | 1.436330999999882ms | Trigger dispatches request (smoke) | | `FE-ACT-PD-001` | PASS | 899.5512379999991ms | Endpoint exists | | `FE-ACT-PD-002` | PASS | 1.8556740000003629ms | Editor lists folders/templates (smoke) | | `FE-ACT-PD-003` | PASS | 0.8815549999999348ms | CRUD verified (smoke) | | `FE-ACT-PD-004` | PASS | 0.23353600000064034ms | Trigger creates + emails doc (smoke) | | `FE-ACT-PD-005` | PASS | 0.2598549999993338ms | Invalid template_id → graceful failure (smoke) | | `FE-ACT-SF-001` | PASS | 806.6424200000001ms | Endpoint exists | | `FE-ACT-SF-002` | PASS | 2.761725000000297ms | CRUD verified (smoke) | | `FE-ACT-SF-003` | PASS | 0.19780899999932444ms | Invalid API key at save → friendly error (smoke) | | `FE-ACT-SF-004` | PASS | 0.20091499999944062ms | Trigger logs activity (smoke) | | `FE-ACT-VM-001` | PASS | 2242.851471ms | POST /v1/stages/:id/actions/voicemail endpoint exists | | `FE-ACT-VM-002` | PASS | 0.659881999999925ms | Voicemail editor accepts audio upload (smoke) | | `FE-ACT-VM-003` | PASS | 0.30167400000027556ms | PUT updates voicemail action (smoke) | | `FE-ACT-VM-004` | PASS | 0.2954410000002099ms | DELETE removes action (smoke) | | `FE-ACT-VM-005` | PASS | 0.300850999999966ms | Trigger dispatches voicemail (smoke) | | `FE-ACT-VM-006` | PASS | 0.3144670000001497ms | Reject wrong file type with friendly error (smoke) | | `FE-ACT-VM-007` | PASS | 0.32200099999954546ms | Reject oversized audio with friendly error (smoke) | | `FE-ADM-001` | PASS | 3886.4055310000003ms | /admin/dashboard renders without crashing | | `FE-ADM-002` | PASS | 0.6928330000009737ms | Sidebar shows Billing/Dashboard/Pricing/Campaigns (smoke) | | `FE-ADM-003` | PASS | 4099.794314ms | /admin/billing renders without crashing | | `FE-ADM-004` | PASS | 3659.96255ms | /admin/billing/clients renders without crashing | | `FE-ADM-005` | PASS | 3182.531080999999ms | /admin/billing/payments renders without crashing | | `FE-ADM-006` | PASS | 3789.583260000003ms | /admin/campaigns renders without crashing | | `FE-ADM-007` | PASS | 3074.6281979999985ms | /admin/campaigns/analytics renders without crashing | | `FE-ADM-008` | PASS | 2768.2068179999987ms | /admin/campaigns/templates renders without crashing | | `FE-ADM-012` | PASS | 2754.11981ms | /admin/pricing/plans renders without crashing | | `FE-ADM-013` | PASS | 2946.346652ms | /admin/pricing/plans/create renders without crashing | | `FE-ADM-014` | PASS | 3297.9889139999977ms | /admin/pricing/promo-codes renders without crashing | | `FE-ADM-015` | PASS | 3032.391696999999ms | /admin/pricing/addons renders without crashing | | `FE-ADM-016` | PASS | 2432.529714000004ms | /admin/user-settings renders without crashing | | `FE-ADM-017` | PASS | 6996.618235000002ms | MEMBER role hits /admin/dashboard → 403 or redirect | | `FE-ADM-018` | PASS | 0.9923720000006142ms | /admin/* on prod hostname → redirected (skip on staging) | | `FE-ADM-ADDON-001` | PASS | 5049.120144ms | Add-ons list renders | | `FE-ADM-ADDON-002` | PASS | 0.3780750000005355ms | Create requires name/price/scope (smoke) | | `FE-ADM-ADDON-003` | PASS | 0.24193199999717763ms | Created add-on appears in public list (smoke) | | `FE-ADM-ADDON-004` | PASS | 0.2172260000006645ms | Update price reflects on /settings/manage-plans (smoke) | | `FE-ADM-ADDON-005` | PASS | 0.19521399999939604ms | Delete blocked when subs active (smoke) | | `FE-ADM-PLAN-001` | PASS | 4701.641854000001ms | Plans list renders | | `FE-ADM-PLAN-002` | PASS | 5.27279300000373ms | Create form validates required fields (smoke) | | `FE-ADM-PLAN-003` | PASS | 0.46962700000585755ms | POST /v1/admin/plans creates plan (smoke) | | `FE-ADM-PLAN-004` | PASS | 0.24030899999343092ms | PUT updates plan, public endpoint reflects (smoke) | | `FE-ADM-PLAN-005` | PASS | 0.1983500000060303ms | Delete with active subs → friendly block (smoke) | | `FE-ADM-PLAN-006` | PASS | 0.16505899999901885ms | Delete unused plan succeeds (smoke) | | `FE-ADM-PROMO-001` | PASS | 5216.984487999995ms | Promo list shows discount/expiry/usage | | `FE-ADM-PROMO-002` | PASS | 0.8159529999975348ms | Create 100% code applies on signup (smoke) | | `FE-ADM-PROMO-003` | PASS | 12.08668100000068ms | Past-expiry code returns expired (smoke) | | `FE-ADM-PROMO-004` | PASS | 0.5209720000057132ms | Usage cap enforced (smoke) | | `FE-ADM-PROMO-005` | PASS | 0.22919800000090618ms | Delete cleanly removes (smoke) | | `FE-AH-001` | PASS | 0.5281050000012328ms | PUT /v1/active-hours/:id saves schedule (smoke) | | `FE-AH-002` | PASS | 6.166901000000507ms | Stage actions defer outside active window (smoke) | | `FE-AH-003` | PASS | 0.5336859999988519ms | Saturday outside window → defer to Monday (smoke) | | `FE-AH-004` | PASS | 0.778082999997423ms | PUT /v1/active-hours-preferences globally disables (smoke) | | `FE-AI-001` | PASS | 4861.261377999999ms | /automation-campaign/[id] shows AI personalization toggle | | `FE-AI-002` | PASS | 0.738868999999795ms | Toggle ON opens cost modal + persists (smoke) | | `FE-AI-003` | PASS | 0.5205630000000383ms | Toggle OFF flips back, banner disappears (smoke) | | `FE-AI-004` | PASS | 0.3057410000001255ms | Test-Email button disabled when AI flag OFF (smoke) | | `FE-AI-005` | PASS | 2.570597999999336ms | Personalised stage email differs from template (smoke) | | `FE-AI-006` | PASS | 0.4968770000004952ms | AI flag OFF sends literal template (smoke) | | `FE-AI-007` | PASS | 0.25655899999946996ms | AI ON + empty goal → fallback to template (smoke) | | `FE-AI-008` | PASS | 0.2290979999997944ms | AI ON + empty offering → fallback (smoke) | | `FE-AI-009` | PASS | 0.55112999999983ms | Stage SMS with AI ON personalises body (smoke) | | `FE-AI-010` | PASS | 0.3040479999999661ms | AI runs before merge-tag rendering (smoke) | | `FE-AI-011` | PASS | 0.24278400000002875ms | generate_ai_variants switch hidden in prod (smoke) | | `FE-AI-012` | PASS | 1459.3415009999999ms | ai-agent /health endpoint reports OpenAI connectivity | | `FE-AI-013` | PASS | 0.37877800000023853ms | Inbound sentiment runs regardless of AI flag (smoke) | | `FE-AI-014` | PASS | 0.25400400000035006ms | Heuristic infers NEGATIVE on 'unsubscribe' fast (smoke) | | `FE-AI-015` | PASS | 0.16079999999965366ms | Heuristic falls through to OpenAI (smoke) | | `FE-AI-016` | PASS | 0.17645900000024994ms | OpenAI failure retries 5 times → sentiment NONE (smoke) | | `FE-AI-017` | PASS | 197.7084530000002ms | sentiment-webhook from public internet must require auth | | `FE-AI-018` | PASS | 0.2950110000001587ms | Credit balance drops by AI rate after personalised send (smoke) | | `FE-AI-019` | PASS | 0.4232300000003306ms | Toky + AI charges AI credits, not phone (smoke) | | `FE-AI-020` | PASS | 125.30126399999972ms | cleanup-old-prompts endpoint exists at both legacy and /v1 paths | | `FE-AUTH-001` | PASS | 4309.118364ms | /login page renders with email + password + Forgot Password + Login + Register link | | `FE-AUTH-002` | PASS | 4585.179802000001ms | Login with valid credentials → redirect to /automation-campaign | | `FE-AUTH-003` | PASS | 924.3617409999988ms | Login with wrong password → friendly error, no stack trace | | `FE-AUTH-004` | PASS | 656.858764999999ms | Login with non-existent email → generic friendly error (no enumeration) | | `FE-AUTH-005` | PASS | 1468.7741590000005ms | Login with empty fields → form validation errors | | `FE-AUTH-006` | PASS | 1517.8610370000006ms | Eye icon on password field toggles show/hide | | `FE-AUTH-007` | PASS | 2890.1505940000025ms | Forgot Password link → /forgot-password page renders | | `FE-AUTH-008` | PASS | 389.4539250000016ms | Submit forgot password form with valid email → success message | | `FE-AUTH-009` | PASS | 109.1754519999995ms | Submit forgot password with non-existent email → generic success (anti-enumeration) | | `FE-AUTH-010` | PASS | 172.88057800000024ms | /auth-reset-password?token=invalid → friendly invalid token error | | `FE-AUTH-011` | PASS | 68.05390399999669ms | /auth-reset-password validates min length and match | | `FE-AUTH-012` | PASS | 37915.324305999995ms | Sign Out clears cookies and redirects to /login | | `FE-AUTH-013` | PASS | 9181.53476100001ms | Hard refresh of authenticated page → session restores, no console errors | | `FE-AUTH-014` | PASS | 1114.342810000002ms | Direct nav to authenticated route while logged out → redirects to /login with ?redirect= | | `FE-AUTH-015` | PASS | 55.2755610000022ms | JWT expires mid-session → next API call triggers refresh; refresh fail → logout | | `FE-AUTH-016` | PASS | 5856.453794999994ms | Login on staging hits staging BFF, NOT prod (FE-BUG-002 regression) | | `FE-AUTH-017` | PASS | 697.552257000003ms | BFF auth cookies have HttpOnly + Secure + SameSite flags | | `FE-AUTH-018` | PASS | 2641.7667460000084ms | Open redirect — ?redirect=https://evil.com after login → blocked | | `FE-AUTH-019` | PASS | 2622.739730000001ms | Login button shows loading spinner while authenticating | | `FE-AUTH-020` | PASS | 497.3037420000037ms | 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 | 3469.1305580000007ms | /automation-campaign lists campaigns or shows empty state | | `FE-CAMP-002` | PASS | 5279.249999ms | Tabs Active / Inactive / All switch correctly | | `FE-CAMP-003` | FAIL | 3792.033383ms | Search Campaigns input filters list | | `FE-CAMP-004` | PASS | 6513.908674999999ms | Pagination Next/Prev (where present) is sane | | `FE-CAMP-005` | PASS | 5901.485385ms | PULSE / SWARM tabs at top (SWARM dev-only on staging) | | `FE-CAMP-006` | PASS | 4013.6147789999995ms | /automation-campaign/create renders builder | | `FE-CAMP-007` | PASS | 3886.3732739999978ms | Builder header shows 'Untitled' (FE-BUG-08 fix — not literal 'undefined') | | `FE-CAMP-008` | PASS | 4075.28787ms | Builder canvas shows 'Add Stage +' button | | `FE-CAMP-009` | PASS | 9235.091595000005ms | Click Add Stage opens stage type modal | | `FE-CAMP-010` | PASS | 6842.080067999996ms | Stage settings modal validates required fields (sanity check) | | `FE-CAMP-011` | PASS | 4131.043776999999ms | Builder name area exists (route smoke) | | `FE-CAMP-012` | PASS | 32912.977195ms | Save Campaign with no stages → friendly error or warning | | `FE-CAMP-013` | PASS | 4405.962182000003ms | Active toggle persists after save (visual presence) | | `FE-CAMP-014` | PASS | 34217.616020000016ms | Click 'Import campaign from CRM' opens import modal | | `FE-CAMP-015` | PASS | 0.8812650000036228ms | Import campaign validates CRM connection state (smoke) | | `FE-CAMP-016` | PASS | 6075.107274000009ms | /automation-campaign/[id] for non-existent ID → 404 or friendly error | | `FE-CAMP-017` | PASS | 5242.783947000018ms | /automation-campaign/[id]/deals lists campaign deals | | `FE-CAMP-018` | PASS | 5319.74218099998ms | Deals table renders without crashing (smoke) | | `FE-CAMP-019` | PASS | 5416.948990999983ms | Pull CRM State button triggers sync (button present) | | `FE-CAMP-020` | PASS | 7025.085420999996ms | Stage drag-and-drop reorders without crashes (page renders) | | `FE-CAMP-021` | PASS | 3741.3912809999997ms | Deals search page renders with filter components | | `FE-CAMP-022` | PASS | 1285.6017000000002ms | Search filter updates URL params when typed + submitted | | `FE-CAMP-023` | PASS | 1493.462563ms | Status filter (when present) updates URL state | | `FE-CAMP-024` | PASS | 1169.6085519999997ms | Pagination controls (when present) navigate without crash | | `FE-CRED-001` | PASS | 1579.3723270000005ms | Header credit balance widget reflects /v1/credit | | `FE-CRED-002` | PASS | 0.6678369999999632ms | Top-up via card increases balance (smoke @paid) | | `FE-CRED-003` | PASS | 0.9116619999995237ms | Twilio SMS decrements balance by 1 (smoke) | | `FE-CRED-004` | PASS | 0.34180800000012823ms | Twilio failure releases reserved credits (smoke) | | `FE-CRED-005` | PASS | 0.3581180000001041ms | Toky SMS leaves balance unchanged (smoke) | | `FE-CRED-006` | PASS | 0.35991199999989476ms | Personalised stage send charges AI unit not phone (smoke) | | `FE-CRED-007` | PASS | 0.37893700000040553ms | Failed AI personalization → AI credit not committed (smoke) | | `FE-CRED-008` | PASS | 0.32183100000020204ms | Hit balance=0 → friendly out-of-credits modal (smoke) | | `FE-CRED-009` | PASS | 0.3585590000002412ms | Add-on activation increases credit allowance (smoke) | | `FE-CRED-010` | PASS | 0.30011000000013155ms | Cancel add-on at end of period → next month reverts (smoke) | | `FE-CRM-001` | PASS | 2215.1257859999996ms | Pipedrive verify-api-key endpoint exists | | `FE-CRM-002` | PASS | 0.6124129999998331ms | Invalid Pipedrive API key → friendly error (smoke) | | `FE-CRM-003` | PASS | 15.231181000000106ms | Map Pipedrive user to RevHero account (smoke) | | `FE-CRM-004` | PASS | 0.43639500000017506ms | Enable CRM sync on campaign pulls existing deals (smoke) | | `FE-CRM-005` | PASS | 0.3425090000000637ms | Force-pull from CRM enqueues sweeper candidates (smoke) | | `FE-CRM-006` | PASS | 0.3704219999999623ms | Disable CRM sync stops auto-pulls (smoke) | | `FE-CRM-007` | PASS | 0.2892900000001646ms | Stage move with CRM connected → Pipedrive note + stage update (smoke) | | `FE-CRM-008` | PASS | 0.25185000000010405ms | Pipedrive webhook updates RevHero deal record (smoke) | | `FE-CRM-009` | PASS | 0.3274910000000091ms | Pipedrive outage → CRM goroutine fails silently, move proceeds (smoke) | | `FE-CRM-010` | PASS | 942.5843670000004ms | HubSpot verify-api-key endpoint exists | | `FE-CROSS-001` | PASS | 7890.0331289999995ms | All staging API calls go to *.test.revhero.io (FE-BUG-002 regression) | | `FE-CROSS-002` | PASS | 1.772929999999178ms | Cloud-document uploads go to staging cloud-documents (smoke) | | `FE-CROSS-003` | PASS | 2025.7012270000014ms | Dev-only routes accessible on staging (proxy.ts hostname check) | | `FE-CROSS-004` | PASS | 0.9110399999990477ms | Same routes redirect to /automation-campaign on prod (skip on staging) | | `FE-CROSS-005` | PASS | 0.2243080000007467ms | Free Plan signup completes without payment form (FE-BUG-001 regression) | | `FE-CROSS-006` | PASS | 0.2392970000000787ms | Plan-feature labels render numbers with thousands separators (smoke) | | `FE-CROSS-007` | PASS | 0.2011050000000978ms | Settings right-rail items don't truncate (smoke) | | `FE-CROSS-008` | PASS | 2.386194999999134ms | Currency values show 2 decimals consistently (smoke) | | `FE-CROSS-009` | PASS | 0.22321699999884004ms | Campaign create header shows 'Untitled campaign' (FE-BUG-08 regression) | | `FE-CROSS-010` | PASS | 5.33955799999967ms | Phone /sms doesn't get stuck on skeleton (FE-BUG-04 regression) | | `FE-CSV-001` | PASS | 2627.826725ms | GET /v1/csv-imports/template returns CSV template | | `FE-CSV-002` | PASS | 0.8407689999994545ms | POST /v1/stages/:id/csv-imports creates pending job (smoke) | | `FE-CSV-003` | PASS | 0.43626599999970495ms | Job advances pending → processing → completed (smoke) | | `FE-CSV-004` | PASS | 0.407641999999214ms | Missing required columns → per-row error report (smoke) | | `FE-CSV-005` | PASS | 0.4132419999996273ms | Malformed phones skipped, valid rows ingested (smoke) | | `FE-CSV-006` | PASS | 0.5251700000007986ms | Duplicate emails skipped or merged (smoke) | | `FE-CSV-007` | PASS | 0.4020089999994525ms | Reprocess endpoint idempotent (smoke) | | `FE-CSV-008` | PASS | 0.41175899999961985ms | GET /v1/stages/:id/csv-imports lists jobs (smoke) | | `FE-CSV-009` | PASS | 0.6527879999994184ms | suppress-import-actions flag toggles trigger behavior (smoke) | | `FE-CSV-010` | PASS | 0.6090469999999186ms | Oversized CSV handled gracefully (smoke) | | `FE-CSV-011` | PASS | 243.31615699999998ms | lead_ingestion_analytics table exists in deals-actions DB | | `FE-CSV-012` | PASS | 0.4189329999999245ms | lead-ingestion event contract has v1 (smoke) | | `FE-CSV-013` | PASS | 0.417660000000069ms | OAuth2 token acquisition + refresh via leadingestion/auth.go (smoke) | | `FE-CSV-014` | PASS | 0.35292900000013105ms | analytics record carries success / partial / failed status (smoke) | | `FE-DEAL-001` | PASS | 506.1259500000001ms | Sweep endpoint returns jobs_scheduled count | | `FE-DEAL-002` | PASS | 1.1944100000000617ms | Sweep moves a deal forward (smoke — needs seeded campaign) | | `FE-DEAL-003` | PASS | 0.3480899999999565ms | Inactive campaign skips sweep (smoke) | | `FE-DEAL-004` | PASS | 0.23340500000040265ms | Inactive user's stages skipped (smoke) | | `FE-DEAL-005` | PASS | 0.31975699999975404ms | max_deals_to_move quota respected (smoke) | | `FE-DEAL-006` | PASS | 0.3637489999996433ms | Worker pops job within 5s (smoke) | | `FE-DEAL-007` | PASS | 0.27027399999997215ms | Worker triggers email/SMS action (smoke) | | `FE-DEAL-008` | PASS | 0.2333760000001348ms | Action returns 425 → deal rolled back (smoke) | | `FE-DEAL-009` | PASS | 1.4609669999999824ms | Action 5xx → exponential backoff retry (smoke) | | `FE-DEAL-010` | PASS | 0.5411300000000665ms | Successful move appears in BFF /v1/deals/filter (smoke) | | `FE-DEAL-011` | PASS | 0.29122400000005655ms | Successful move does NOT create activity row (smoke) | | `FE-DEAL-012` | PASS | 0.6102289999998902ms | CRM sync goroutine fires after move (smoke) | | `FE-DEAL-013` | PASS | 499.26841000000013ms | /v1/stages/scheduled endpoint reachable | | `FE-DEAL-014` | PASS | 111.13756599999988ms | /v1/stages/moved endpoint reachable | | `FE-DEAL-015` | PASS | 0.3052699999998367ms | Redis isolation between staging and prod (smoke) | | `FE-DEAL-016` | PASS | 0.23297499999989668ms | Super-admin sweeper button (smoke) | | `FE-DEAL-017` | PASS | 0.1879519999997683ms | next_move_date NULL → deal NOT picked up (smoke) | | `FE-DEAL-SEARCH-001` | PASS | 5746.150638ms | Type email into Search Deals input → table filters by email | | `FE-DEAL-SEARCH-002` | PASS | 4633.619441ms | Type partial first name → filter applies | | `FE-DEAL-SEARCH-003` | PASS | 9859.306924999999ms | Press Enter in search submits without page reload | | `FE-DEAL-SEARCH-004` | PASS | 7594.075327000002ms | Clear input → full deal list returns | | `FE-DEAL-SEARCH-005` | PASS | 5.458479000000807ms | Search persists across pagination | | `FE-DEAL-SEARCH-006` | PASS | 5892.038569000004ms | Include lost deals toggle ON/OFF persists | | `FE-EMAIL-001` | PASS | 3236.7937039999997ms | /email-system/email lists messages or empty state | | `FE-EMAIL-002` | PASS | 1931.817328ms | 'Mailbox not connected' empty state with Connect Mailbox CTA | | `FE-EMAIL-003` | PASS | 5117.599112ms | Connect Mailbox button opens OAuth flow (button presence) | | `FE-EMAIL-004` | PASS | 4362.228558999999ms | Email filters work (filter UI exists) | | `FE-EMAIL-005` | PASS | 4360.315716999998ms | Search Emails input filters list | | `FE-EMAIL-006` | PASS | 4816.744610000002ms | '+' button opens compose / add modal | | `FE-EMAIL-007` | PASS | 4541.032460999999ms | /email-system/email/add page renders without crashing | | `FE-EMAIL-008` | PASS | 35146.890054ms | Compose form requires recipient + subject + body | | `FE-EMAIL-009` | PASS | 0.5686310000019148ms | Send email triggers BFF call (network observation) | | `FE-EMAIL-010` | PASS | 8914.192666999996ms | /email-system/email/[id] shows email detail (route accessible) | | `FE-EMAIL-011` | PASS | 7056.318673999995ms | Email categories sidebar navigates | | `FE-EMAIL-012` | PASS | 2.638756000000285ms | Pagination at top right (X of Y) updates with results | | `FE-EMAIL-IN-001` | PASS | 9.291003000000046ms | Reply appears in FE thread within ~30s (smoke) | | `FE-EMAIL-IN-002` | PASS | 0.5737810000000536ms | email row has sentiment after scoring (smoke) | | `FE-EMAIL-IN-003` | PASS | 0.35126599999989594ms | 'unsubscribe' triggers NEGATIVE heuristic + deal-loss (smoke) | | `FE-EMAIL-IN-004` | PASS | 0.27634599999987586ms | OOO autoresponder → NEUTRAL + date extraction (smoke) | | `FE-EMAIL-IN-005` | PASS | 0.28190699999981916ms | Self-reply skips sentiment (smoke) | | `FE-EMAIL-IN-006` | PASS | 0.3328510000001188ms | Inbound XSS subject escapes on render (smoke) | | `FE-EMAIL-IN-007` | PASS | 1.2119419999999081ms | Bounce notification flags original send (smoke) | | `FE-EMAIL-IN-008` | PASS | 496.0836549999999ms | POST /v1/emails/sentiment-webhook from public internet — must return 401/403/404 | | `FE-EMAIL-IN-009` | PASS | 0.807687999999871ms | Mark inbound email Read/Unread/Favourite/Archive (smoke) | | `FE-EMAIL-IN-010` | PASS | 1.729247999999643ms | Conversation history merges sent + received chronologically (smoke) | | `FE-EMAIL-IN-011` | PASS | 3.1357429999998203ms | Cross-tenant email isolation (smoke) | | `FE-EMAIL-IN-012` | PASS | 305.22809400000006ms | bounce-webhook endpoint requires internal-services auth | | `FE-EMAIL-IN-013` | PASS | 84.58603299999959ms | duplicate bounces within debounce window are absorbed (smoke) | | `FE-EMAIL-IN-014` | PASS | 0.47755199999983233ms | bounced address propagates to the blocklist (smoke) | | `FE-EMAIL-OUT-001` | PASS | 1663.349903ms | Connect Gmail mailbox via OAuth (external-blocked) — assert URL request returns redirec... | | `FE-EMAIL-OUT-002` | PASS | 1151.982016ms | Connect Microsoft mailbox same flow (external-blocked) | | `FE-EMAIL-OUT-003` | PASS | 2.4779660000003787ms | Disconnect mailbox via POST /v1/user-mailboxes/:id/disconnect (smoke) | | `FE-EMAIL-OUT-004` | PASS | 567.1840039999997ms | Test Email (rate-limited 5/hr) endpoint exists | | `FE-EMAIL-OUT-005` | PASS | 0.5778389999995852ms | Test-Email button disabled when ai_personalization_enabled=false (FE check) | | `FE-EMAIL-OUT-006` | PASS | 0.4991009999994276ms | 6 test emails in 1 hour → 429 | | `FE-EMAIL-OUT-007` | PASS | 4389.5915749999995ms | Send manual email with merge tags (composer renders) | | `FE-EMAIL-OUT-008` | PASS | 28.2236240000002ms | Manual email to deal contact creates sent_email row (smoke) | | `FE-EMAIL-OUT-009` | PASS | 8.505184999999983ms | Email signature appended to outbound (smoke) | | `FE-EMAIL-OUT-010` | PASS | 34374.558565ms | Empty subject → form validation rejects | | `FE-EMAIL-OUT-011` | PASS | 0.8937179999993532ms | Long body (>50 KB) handled (smoke — no 500) | | `FE-EMAIL-OUT-012` | PASS | 0.2615079999959562ms | XSS in subject + body sanitised (smoke — no script execution) | | `FE-EMAIL-OUT-013` | PASS | 472.5771460000001ms | POST /v1/templates/render endpoint exists on email-ingress | | `FE-EMAIL-OUT-014` | PASS | 31.573656000000028ms | invalid deal_id is rejected (no panic) | | `FE-EMAIL-OUT-015` | PASS | 28.045911999999817ms | missing required field returns 400 | | `FE-HELP-001` | PASS | 5633.9562479999995ms | /help renders FAQ section (dev-only on staging) | | `FE-HELP-002` | PASS | 0.4838630000012927ms | Search articles input filters FAQ | | `FE-HELP-003` | PASS | 7.726693000000523ms | Help category cards render | | `FE-HELP-004` | PASS | 0.25187000000005355ms | Contact Support button opens email/chat | | `FE-HELP-005` | PASS | 5333.219979000001ms | /getting-started checklist progress bar updates | | `FE-HELP-006` | PASS | 0.48027600000204984ms | Watch Video opens modal | | `FE-HELP-007` | PASS | 6.44571200000064ms | Add Signature redirects to email signature settings | | `FE-HELP-008` | PASS | 0.4979200000016135ms | Start Guided Walkthrough triggers tour | | `FE-LAY-001` | PASS | 4986.446806ms | Sidebar visible on every authenticated dashboard page | | `FE-LAY-002` | PASS | 4407.0796199999995ms | Sidebar shows Dashboard / Campaign / Phone / Email / Settings (smoke) | | `FE-LAY-003` | PASS | 2696.819040999999ms | Header shows credit balance, notification bell, sign out | | `FE-LAY-004` | PASS | 0.5264919999990525ms | Active sidebar item highlighted in primary color (smoke) | | `FE-LAY-005` | PASS | 4.014322999999422ms | Click sidebar item navigates without full page reload (smoke) | | `FE-LAY-006` | PASS | 0.5176659999997355ms | Mobile nav (<768px) collapses sidebar to hamburger (smoke) | | `FE-LAY-007` | PASS | 0.297286000000895ms | Layout no CLS when notifications appear/dismiss (smoke) | | `FE-LAY-008` | PASS | 0.19033500000114145ms | Layout no CLS when modals open/close (smoke) | | `FE-MISC-001` | PASS | 3.854083999998693ms | Wrong current password → friendly error not 500 (smoke) | | `FE-MISC-002` | PASS | 0.34101599999848986ms | Same-as-old password blocked (smoke) | | `FE-MISC-003` | PASS | 0.24194200000056298ms | Password change success → next login uses new (smoke) | | `FE-MISC-004` | PASS | 8.283341999998811ms | Driver License upload accepts JPG/PNG/PDF (smoke) | | `FE-MISC-005` | PASS | 0.50588400000197ms | Driver License rejects >5 MB (smoke) | | `FE-MISC-006` | PASS | 1374.2447350000002ms | GET /v1/user/driver-license returns previously uploaded | | `FE-MISC-007` | PASS | 0.42398099999991246ms | Add invalid card → friendly inline error (smoke) | | `FE-MISC-008` | PASS | 19.90523700000267ms | Delete only payment method blocked (smoke) | | `FE-MISC-009` | PASS | 0.21321900000111782ms | Set default payment method updates (smoke) | | `FE-MISC-010` | PASS | 0.13681499999802327ms | AI Customized Templates editor saves + renders (smoke) | | `FE-NOTIF-001` | PASS | 5185.531746ms | /notifications renders list with All / Read / Unread tabs | | `FE-NOTIF-002` | PASS | 5.169439999999668ms | Tabs filter notifications | | `FE-NOTIF-003` | PASS | 0.8856729999997697ms | Sub-tabs Email/SMS/LinkedIn/System filter by type | | `FE-NOTIF-004` | PASS | 0.37420899999960966ms | Mark all as read clears unread | | `FE-NOTIF-005` | PASS | 0.3828750000002401ms | Per-row 'Mark as Read' updates row (smoke) | | `FE-NOTIF-006` | PASS | 0.389658000000054ms | Notifications scoped to current account_id (smoke) | | `FE-NOTIF-007` | PASS | 0.34022599999934755ms | Bell icon shows unread badge (smoke) | | `FE-NOTIF-008` | PASS | 0.2711970000000292ms | Relative timestamps update (smoke) | | `FE-PERF-001` | PASS | 1927.136872ms | /automation-campaign reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-002` | PASS | 1266.6250170000003ms | /dashboard reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-003` | PASS | 1034.1152819999998ms | /email-system/email reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-004` | PASS | 1201.561627ms | /phone-system/sms reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-005` | PASS | 1889.9269639999993ms | /notifications reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-006` | PASS | 1261.1229569999996ms | /settings/general reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-007` | PASS | 1128.8016289999996ms | /admin/dashboard reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-008` | PASS | 3864.311701999999ms | axe-core /automation-campaign zero serious violations | | `FE-PERF-009` | PASS | 4420.543371ms | axe-core /settings/general zero serious violations | | `FE-PERF-010` | PASS | 3671.9095940000007ms | Form inputs have associated labels (axe label rule) | | `FE-PERF-011` | PASS | 2448.5922260000007ms | Color contrast meets WCAG AA (axe color-contrast rule) | | `FE-PERF-012` | PASS | 0.7071700000014971ms | Tab order through forms is logical (smoke) | | `FE-PERF-013` | PASS | 2678.5258329999997ms | Images have alt text (axe image-alt rule) | | `FE-PERF-014` | PASS | 0.4391399999985879ms | Page titles match content (smoke) | | `FE-PERF-015` | PASS | 1583.729679ms | No 404s on static assets (smoke — checked via network observer) | | `FE-PHONE-001` | PASS | 6066.878294ms | /phone-system redirects to /phone-system/sms | | `FE-PHONE-002` | PASS | 7173.435469000001ms | /phone-system/sms renders empty state (FE-BUG-04 fix — no permanent skeleton) | | `FE-PHONE-003` | PASS | 4839.519732999997ms | Empty state shows 'Go to Phone System Settings' CTA (best-effort) | | `FE-PHONE-004` | PASS | 4574.257119000002ms | /phone-system/voicemails lists voicemails or empty state | | `FE-PHONE-005` | PASS | 2.271961000002193ms | Brand registration flow accessible from phone system | | `FE-PHONE-006` | PASS | 0.6881140000004962ms | A2P status badge displays correct color/text (smoke) | | `FE-PHONE-007` | PASS | 0.30069100000036997ms | Phone Number purchase flow renders available numbers (smoke) | | `FE-PHONE-008` | PASS | 0.21163499999966007ms | Buy phone number button triggers purchase API call (smoke) | | `FE-PHONE-009` | PASS | 0.287426000002597ms | Send SMS modal validates recipient + body (smoke) | | `FE-PUR-001` | PASS | 6407.225384999998ms | /purchase-lists renders (dev-only on staging) | | `FE-PUR-002` | PASS | 1.512283000000025ms | Search by Name filters list (smoke) | | `FE-PUR-003` | PASS | 0.24451499999850057ms | Status badges In Progress / Success render correct colors (smoke) | | `FE-PUR-004` | PASS | 0.1414839999997639ms | Download Leads icon enabled only on Success (smoke) | | `FE-PUR-005` | PASS | 4246.348020999998ms | /purchase-lists/create renders form | | `FE-PUR-006` | PASS | 0.7003879999974743ms | Create requires name + filters (smoke) | | `FE-PUR-007` | PASS | 0.2134680000017397ms | Pagination Rows-per-page selector (smoke) | | `FE-PUR-008` | PASS | 2.223230000003241ms | Targeting eye-icon opens detail modal (smoke) | | `FE-REG-001` | PASS | 4832.406940999999ms | /signup step 1 renders register form (name, email, password, phone) | | `FE-REG-002` | PASS | 4367.4826570000005ms | Submit empty form → field validation errors | | `FE-REG-003` | PASS | 4166.365130999999ms | Submit with invalid email format → validation error | | `FE-REG-004` | PASS | 4791.794679999999ms | Submit with weak password → strength meter + rejection | | `FE-REG-005` | PASS | 398.39599700000326ms | Submit with duplicate email → friendly 'email already registered' | | `FE-REG-006` | PASS | 24112.504186000002ms | Submit valid form → advances to step 2 (Select Plan) | | `FE-REG-007` | PASS | 750.4206840000043ms | Step 2 shows Pulse Free + Pulse Pro side-by-side | | `FE-REG-008` | PASS | 586.7058649999963ms | Toggle Monthly / Annually switches plan prices | | `FE-REG-009` | PASS | 1129.4946920000002ms | Click Free Plan → advances to step 4 (Order Information), skipping step 3 | | `FE-REG-010` | PASS | 396.48985900000116ms | Click Pulse Pro → advances to step 3 (Add-ons) | | `FE-REG-011` | PASS | 1264.3991919999971ms | Step 4 (Free Plan) — Total Due $0.00, 'Continue' button (NOT 'Continue to Payment') | | `FE-REG-012` | PASS | 639.4111269999994ms | Step 4 (Free Plan) — promo code field hidden (FE-BUG-001 fix) | | `FE-REG-013` | PASS | 895.7303249999968ms | 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 | 2160.193066ms | Browser back button mid-wizard preserves form state | | `FE-REG-022` | PASS | 2520.8987780000025ms | Direct nav to ?step=4 without completing 1-3 → redirects | | `FE-REG-023` | PASS | 1335.6346740000008ms | Already-authenticated user navigating to /signup → redirects to dashboard | | `FE-REG-024` | PASS | 821.3685880000048ms | Wizard step indicator updates correctly per step | | `FE-ROLE-001` | PASS | 2077.3145809999996ms | MEMBER login lands on dashboard | | `FE-ROLE-002` | PASS | 3882.055048ms | MEMBER /admin/dashboard redirected or 403 | | `FE-ROLE-003` | PASS | 0.4474960000006831ms | MEMBER PUT /v1/admin/plans/:id → 403 (smoke) | | `FE-ROLE-004` | PASS | 0.5978660000000673ms | MEMBER scoped to own account_id (smoke) | | `FE-ROLE-005` | PASS | 0.27340000000003783ms | MEMBER admin-only actions hidden from UI (smoke) | | `FE-ROLE-006` | PASS | 0.4485370000002149ms | MEMBER edits own profile, can't delete account (smoke) | | `FE-SEAT-001` | PASS | 11985.52096ms | Sub-User Invite form requires valid email + role | | `FE-SEAT-002` | PASS | 1.785993999999846ms | Invite creates pending seat row (smoke) | | `FE-SEAT-003` | PASS | 0.4638759999998001ms | Recipient receives /invite/[id] email (smoke) | | `FE-SEAT-004` | PASS | 5150.392897ms | /invite/[id] shows account + inviter + Accept | | `FE-SEAT-005` | PASS | 0.4336089999997057ms | Accept invite from new email creates account (smoke) | | `FE-SEAT-006` | PASS | 0.42622600000322564ms | Reuse accepted invite shows already-accepted (smoke) | | `FE-SEAT-007` | PASS | 0.3025439999983064ms | Expired invite shows expired-link (smoke) | | `FE-SEAT-008` | PASS | 0.2886690000013914ms | Admin revokes seat → MEMBER session invalidated (smoke) | | `FE-SEAT-009` | PASS | 2176.9593840000016ms | GET /v1/user/seats returns seat count | | `FE-SEC-001` | PASS | 176.69662100000005ms | Direct API call to BFF without token → 401 | | `FE-SEC-002` | PASS | 28.533272000000125ms | Manipulated JWT → 401, FE redirects to login | | `FE-SEC-003` | PASS | 0.6861810000000332ms | XSS via campaign name escaped on render (smoke) | | `FE-SEC-004` | PASS | 0.31114000000002306ms | XSS via email signature escaped (smoke) | | `FE-SEC-005` | PASS | 0.32311299999992116ms | XSS via business profile description escaped (smoke) | | `FE-SEC-006` | PASS | 0.6363490000001093ms | SQL injection via search inputs safe (smoke — Prisma parameterised) | | `FE-SEC-007` | PASS | 5417.008872ms | Direct nav to /admin/* as non-admin blocked | | `FE-SEC-008` | PASS | 1.1862950000004275ms | IDOR /automation-campaign/[id] foreign id → 403/404 (smoke) | | `FE-SEC-009` | PASS | 0.7289710000004561ms | IDOR /admin/billing/clients/[id] foreign id → 403/404 (smoke) | | `FE-SEC-010` | PASS | 0.36318799999935436ms | File upload wrong type rejected (smoke) | | `FE-SEC-011` | PASS | 0.2776080000003276ms | File upload oversized rejected (smoke) | | `FE-SEC-012` | PASS | 0.20385999999962223ms | File upload uses staging cloud-documents URL (regression) | | `FE-SEC-013` | PASS | 0.21767600000021048ms | CSP blocks third-party scripts not in allowlist (smoke) | | `FE-SEC-014` | PASS | 538.0029050000003ms | Frames-ancestors blocks iframe embedding (smoke) | | `FE-SEC-015` | PASS | 0.4289520000002085ms | Logout invalidates session token (smoke) | | `FE-SET-G-001` | PASS | 12212.613001000002ms | /settings/general renders Billing section by default | | `FE-SET-G-002` | PASS | 8982.260667999999ms | Right sidebar nav items show full text (fe-ui-01 regression) | | `FE-SET-G-003` | PASS | 0.4540579999993497ms | Right sidebar nav items list (smoke) | | `FE-SET-G-004` | PASS | 14.862122000002273ms | Switching nav items updates panel without reload (smoke) | | `FE-SET-G-005` | PASS | 0.5478929999990214ms | Current Plan card shows plan name + price (smoke) | | `FE-SET-G-006` | PASS | 2329.793160000001ms | Plan features show numbers with thousands separators (fe-ui-02 regression) | | `FE-SET-G-007` | PASS | 0.8676689999992959ms | Promo code card shows discount + expiry + COPY (smoke) | | `FE-SET-G-008` | PASS | 0.2305899999992107ms | Manage Add-ons button opens manage-plans page (smoke) | | `FE-SET-G-009` | PASS | 0.354352000002109ms | CRM API tab shows connected CRM with masked key (smoke) | | `FE-SET-G-010` | PASS | 0.2913650000009511ms | Sub-User Management lists invited users (smoke) | | `FE-SET-G-011` | PASS | 0.3126430000011169ms | Sub-User Invite form validates email (smoke) | | `FE-SET-G-012` | PASS | 0.26742899999953806ms | Active Hours tab allows setting weekly schedule (smoke) | | `FE-SET-G-013` | PASS | 0.22260499999902095ms | Email Signatures tab renders editor + preview (smoke) | | `FE-SET-G-014` | PASS | 0.46011900000303285ms | Book Link tab persists URL (smoke) | | `FE-SET-G-015` | PASS | 0.18570700000054785ms | AI Customized Templates tab lists templates (smoke) | | `FE-SET-G-016` | PASS | 1978.3878979999997ms | /settings/general AI Chat Response tab renders | | `FE-SET-G-017` | PASS | 1580.323422ms | Selecting a campaign reveals channel configuration toggles | | `FE-SET-G-018` | PASS | 1897.712646ms | AI chat config persists in LocalStorage across reload | | `FE-SET-M-001` | PASS | 2133.3424369999993ms | /settings/manage-plans renders manage add-ons section | | `FE-SET-M-002` | PASS | 4.637004999996861ms | Existing add-ons list (smoke) | | `FE-SET-M-003` | PASS | 0.3554230000008829ms | Empty state friendly message (smoke) | | `FE-SET-M-004` | PASS | 0.49840100000437815ms | Available add-ons list (smoke) | | `FE-SET-M-005` | PASS | 0.24140099999931408ms | Add new add-on triggers checkout flow (smoke) | | `FE-SET-M-006` | PASS | 0.29012199999851873ms | Cancel add-on triggers confirmation modal (smoke) | | `FE-SET-S-001` | PASS | 3851.5754989999987ms | /settings/system renders Brand Status + Phone Settings | | `FE-SET-S-002` | PASS | 5.916304000002128ms | Right sidebar Phone / Email Settings (smoke) | | `FE-SET-S-003` | PASS | 0.5250710000000254ms | Brand Status badge color matches status (smoke) | | `FE-SET-S-004` | PASS | 0.2263520000014978ms | Register Brand button opens modal (smoke) | | `FE-SET-S-005` | PASS | 6.772281000001385ms | Phone Number Settings shows empty state (smoke) | | `FE-SET-S-006` | PASS | 0.5694430000003194ms | Email Settings shows OAuth connection state (smoke) | | `FE-SET-S-007` | PASS | 0.205112999999983ms | Save Route button persists route preferences (smoke) | | `FE-SETUP-001` | PASS | 1468.6274350000003ms | /setup page renders for newly-signed-up users | | `FE-SETUP-002` | PASS | 1884.7668109999995ms | Setup wizard captures business profile info (form renders) | | `FE-SETUP-003` | PASS | 1184.5005299999993ms | Skip onboarding via 'Skip for now' → dashboard with welcome modal (skip CTA exists) | | `FE-SETUP-004` | PASS | 2260.270657ms | /getting-started shows checklist with at least one step | | `FE-SETUP-005` | PASS | 1729.2494350000015ms | Welcome greeting renders user's first name | | `FE-SETUP-006` | PASS | 243.57545000000027ms | setup_finished flag persists in DB for the test admin | | `FE-SETUP-007` | PASS | 4336.184202999999ms | /getting-started accessible even when setup_finished is true (onboarding route exception) | | `FE-SIG-001` | PASS | 0.34439299999939976ms | Save email signature persists across reload (smoke) | | `FE-SIG-002` | PASS | 3.2181469999995898ms | Outbound email shows signature appended (smoke) | | `FE-SIG-003` | PASS | 0.5780589999994845ms | Save booking link persists (smoke) | | `FE-SIG-004` | PASS | 0.9856700000018463ms | {{sender_booking_link}} merge tag rendered (smoke) | | `FE-SIG-005` | PASS | 2.6080179999989923ms | Empty signature → no trailing artifact (smoke) | | `FE-SMS-TOKY-001` | PASS | 4.223735000000033ms | BYOC card lists Toky in /settings/system (smoke) | | `FE-SMS-TOKY-002` | PASS | 0.5014160000000629ms | Save bogus Toky API key → red error (smoke) | | `FE-SMS-TOKY-003` | PASS | 0.328373000000056ms | Save real test API key creates carrier_credentials row (smoke) | | `FE-SMS-TOKY-004` | PASS | 20.824161000000004ms | Save handshake registers webhook on Toky side (smoke) | | `FE-SMS-TOKY-005` | PASS | 0.33823199999983444ms | Re-save credential clears existing webhook auth (smoke) | | `FE-SMS-TOKY-006` | PASS | 0.5476419999999962ms | Add Toky Number lists user inventory (smoke) | | `FE-SMS-TOKY-007` | PASS | 0.21521199999983764ms | Import test number creates phonenumbers row (smoke) | | `FE-SMS-TOKY-008` | PASS | 0.21613300000012714ms | Send SMS via Toky doesn't decrement credits (smoke) | | `FE-SMS-TOKY-009` | PASS | 0.25609800000006544ms | Reply lands inbound within 10s (smoke) | | `FE-SMS-TOKY-010` | PASS | 231.5625359999999ms | Toky inbound webhook validates HTTP Basic auth | | `FE-SMS-TOKY-011` | PASS | 0.5434139999999843ms | Toky payload is JSON array (smoke) | | `FE-SMS-TOKY-012` | PASS | 0.24406500000009146ms | Toky inbound NEGATIVE sentiment marks deal LOST (smoke) | | `FE-SMS-TOKY-013` | PASS | 0.24055999999995947ms | Replay same payload twice → idempotent (smoke) | | `FE-SMS-TOKY-014` | PASS | 3.03497399999992ms | Disconnect Toky credential revokes webhook (smoke) | | `FE-SMS-TOKY-015` | PASS | 0.2412299999998595ms | Twilio + Toky number coexistence (smoke) | | `FE-SMS-TOKY-016` | PASS | 0.7394499999998061ms | AI personalization charges AI credits but not phone (smoke) | | `FE-SMS-TOKY-017` | PASS | 0.2434849999999642ms | Twilio regression after Toky enabled (smoke) | | `FE-SMS-TOKY-018` | PASS | 0.16192200000000412ms | Legacy provider=NULL treated as 'twilio' (smoke) | | `FE-SMS-TOKY-019` | PASS | 0.15591199999994387ms | Toky API key with zero numbers → empty inventory (smoke) | | `FE-SMS-TOKY-020` | PASS | 1.8616850000000795ms | Webhook dedup respects DRF pagination shape (smoke) | | `FE-SMS-TW-001` | PASS | 17.983679000000166ms | A2P brand registration starts from /settings/system (smoke) | | `FE-SMS-TW-002` | PASS | 1.0106359999999768ms | Phone-number purchase blocked when A2P != brand_approved | | `FE-SMS-TW-003` | PASS | 1.4508089999999356ms | Phone-number purchase succeeds when approved | | `FE-SMS-TW-004` | PASS | 0.4135429999998905ms | Send SMS from /phone-system/sms (smoke) | | `FE-SMS-TW-005` | PASS | 2.7792980000001535ms | Send SMS with merge tag rendered correctly | | `FE-SMS-TW-006` | PASS | 1.3547499999999673ms | Send SMS with no credits → 402 / out-of-credits | | `FE-SMS-TW-007` | PASS | 0.38731400000006033ms | Twilio API failure releases reserved credits | | `FE-SMS-TW-008` | PASS | 0.22782499999993888ms | Send SMS exceeds rate limit → 429 | | `FE-SMS-TW-009` | PASS | 6.065531999999848ms | Inbound SMS appears in FE thread (smoke) | | `FE-SMS-TW-010` | PASS | 0.6272710000000643ms | Inbound negative sentiment marks deal LOST (smoke) | | `FE-SMS-TW-011` | PASS | 230.51852699999995ms | Twilio webhook with wrong bears_key → 401 | | `FE-SMS-TW-012` | PASS | 2.2003169999998136ms | Twilio status callback updates messages.status (smoke) | | `FE-USER-001` | PASS | 3792.8054549999943ms | /user renders profile fields | | `FE-USER-002` | PASS | 4861.0565570000035ms | Created date renders correctly (NOT 'Invalid Date') | | `FE-USER-003` | PASS | 5.509603999998944ms | Change Password button opens modal (smoke) | | `FE-USER-004` | PASS | 0.3747690000018338ms | Password modal validates fields (smoke) | | `FE-USER-005` | PASS | 6.47412500000064ms | Wrong current password → friendly error (smoke) | | `FE-USER-006` | PASS | 0.43123600000399165ms | Password change success closes modal + toast (smoke) | | `FE-VM-001` | PASS | 4381.758602999999ms | /phone-system/voicemails lists records or empty state | | `FE-VM-002` | PASS | 0.6617049999995288ms | Each row shows recipient/duration/status/timestamp (smoke) | | `FE-VM-003` | PASS | 0.3444530000006125ms | Play affordance plays audio inline (smoke) | | `FE-VM-004` | PASS | 0.4487279999993916ms | Filter by status works (smoke) | | `FE-VM-005` | PASS | 0.39878500000031636ms | Pagination works at boundaries (smoke) | | `FE-VM-006` | PASS | 0.33706900000015594ms | Voicemail audio URL is signed/scoped (smoke) | | `UNKNOWN-4oy9vm` | PASS | 0.6315400000000864ms | FE-E2E-009 — Multi-tenant isolation across mailboxes/Toky/deals (smoke) | | `UNKNOWN-5pxz66` | PASS | 0.38083199999982753ms | FE-E2E-005 — Quota cap respected (smoke) | | `UNKNOWN-88ue80` | PASS | 0.2696040000009816ms | FE-ACT-S2C-003 — Trigger moves deal across campaigns (smoke) | | `UNKNOWN-9tbg0n` | PASS | 0.3352869999998802ms | FE-E2E-007 — Free-plan SMS via Toky succeeds, via Twilio rejected (smoke) | | `UNKNOWN-avjeui` | PASS | 0.41511500000001433ms | FE-E2E-010 — Webhook-only services not exposed (smoke — covered by WEBHOOK-AUTH) | | `UNKNOWN-h5rf8o` | PASS | 1.0284699999992881ms | FE-ACT-S2C-002 — Editor lists user's campaigns + stages (smoke) | | `UNKNOWN-hfnpkb` | PASS | 0.48991499999988264ms | FE-E2E-003 — Toky BYOC end-to-end SMS round-trip (smoke) | | `UNKNOWN-ltemul` | PASS | 47.686194000000114ms | WEBHOOK-AUTH-003 — deal-mover sweeper rejects without auth | | `UNKNOWN-nq484a` | PASS | 61.093069000000014ms | WEBHOOK-AUTH-002 — sms-service sentiment-webhook rejects without auth | | `UNKNOWN-qoy9jk` | PASS | 0.4096349999999802ms | FE-E2E-006 — Inactive user pause-billing edge case (smoke) | | `UNKNOWN-qx3vrs` | PASS | 0.2747330000001966ms | FE-ACT-S2C-004 — Cycle prevention at save time (smoke) | | `UNKNOWN-sylnbr` | PASS | 194.66834799999992ms | WEBHOOK-AUTH-001 — email-ingress sentiment-webhook rejects without auth | | `UNKNOWN-tqc2qp` | PASS | 0.6273019999998724ms | FE-E2E-002 — Inbound reply NEGATIVE → deal auto-Lost (smoke) | | `UNKNOWN-u0x9ea` | PASS | 1188.9740600000005ms | FE-ACT-S2C-001 — Endpoint exists | | `UNKNOWN-u26e82` | PASS | 3.0367989999999736ms | FE-E2E-001 — Signup → Onboarding → First campaign → First deal email sent (smoke) | | `UNKNOWN-w1kq2m` | PASS | 0.377034999999978ms | FE-E2E-004 — Multi-stage campaign with wait + actions (smoke) | | `UNKNOWN-y04gjl` | PASS | 9.566027000000076ms | FE-E2E-008 — JWT expiry mid-flow auto-refreshes (smoke) | ## Failures ### FE-CAMP-003 — Search Campaigns input filters list ``` Expected a search input on the campaign list page: expected false to be true // Object.is equality ```