EA Tool - QA Bug Report

Automated Playwright test results across 113 scenarios (90 smoke + 23 E2E CRUD)

Run: March 18, 2026 Target: localhost:3000 (dev) Viewport: 1920x1080 Auth: bypassed (X-Test-As)

Test Summary

113
Total Scenarios
111
Passed
2
Failed
6
Bugs Found
23
E2E CRUD Tests

Bugs Found

BUG-001 Medium CORS error blocks /api/retool/products on Licenses page

Copy-paste issue

**Bug: CORS error on products endpoint (Licenses page)** `getProducts()` in `src/lib/api/client.ts:645` uses `this.client.get('/products')` which resolves to `https://api.eu.hex-rays.io/api/retool/products` — the direct production URL. Since there's no CORS header for localhost, this fails in dev. Other similar methods (`getCountries`, `getPlans`) use `axios.get()` with a full URL that goes through the Vite proxy, and work fine. **Error:** `Access to XMLHttpRequest at 'https://api.eu.hex-rays.io/api/retool/products' blocked by CORS policy` **Impact:** Product names may be missing in the Licenses grid. Console shows cascading `net::ERR_FAILED` errors.

Proposed fix

// src/lib/api/client.ts — getProducts() (~line 645) // Before: async getProducts() { const { data } = await this.client.get('/products') return data } // After — use raw axios like getCountries/getPlans do: async getProducts() { const { data } = await axios.get(`${this.baseUrl}/products`, { headers: this.getHeaders() }) return data }

Screenshot

Licenses page
BUG-002 Medium 500 error on Customer Pending Charges API

Copy-paste issue

**Bug: 500 Internal Server Error on pending charges** `getCustomerCharges()` in `src/lib/api/client.ts:105` calls `GET /api/retool/customers/{id}/charges`. The backend returns 500 for this endpoint. **Stack trace:** ``` EAApiClient.getCustomerCharges (client.ts:105) → fetchPendingCharges (CustomerDetail.tsx:504) ``` **Impact:** The "Pending Charges" tab on Customer Detail shows a spinner that never resolves. No error is shown to the user. **Suggestion:** Either fix the backend route, or add error handling in the UI (show "Failed to load" instead of infinite spinner).

Proposed fix (frontend — graceful error handling)

// src/pages/CustomerDetail.tsx — fetchPendingCharges (~line 504) // Add user-visible error state instead of silent console.error: const [chargesError, setChargesError] = useState(false); const fetchPendingCharges = async () => { if (!customer?.id) return; setChargesError(false); try { const data = await apiClient.getCustomerCharges(customer.id); setPendingCharges(data); } catch (err) { console.error('Error fetching pending charges:', err); setChargesError(true); } }; // Then in the Pending Charges tab render: {chargesError ? ( <Empty> <EmptyTitle>Failed to load pending charges</EmptyTitle> <EmptyDescription>The server returned an error. Try again later.</EmptyDescription> </Empty> ) : /* existing render */}

Screenshot

Customer detail
BUG-003 Low 403 Forbidden on resource load (multiple pages)

Copy-paste issue

**Bug: 403 Forbidden on resource load** Console shows `Failed to load resource: the server responded with a status of 403` on multiple pages. Likely an API endpoint or static asset that doesn't recognize the test auth headers. Not visible to users in the UI but clutters the console. **Observed on:** Dashboard, Customers list, and other pages. **Note:** May be test-environment specific (X-Test-As header bypass). Verify if this also occurs with real Supabase auth.
BUG-004 Low 400 Bad Request on Quote Wizard initial load

Copy-paste issue

**Bug: 400 Bad Request when navigating to /quotes/wizard** An API request returns 400 when the Quote Wizard loads without a customer context. The wizard still renders and functions, but the initial data fetch fires with missing required params. **Impact:** Non-blocking. The wizard works fine once a customer is selected. But the unnecessary failed request adds noise to the network log and slows initial render. **Suggestion:** Guard the initial fetch with a check for required parameters before firing the request, or lazy-load data only after customer selection.

Screenshot

Quote wizard
BUG-005 Low Missing breadcrumb on all Reports pages

Copy-paste issue

**Bug: Breadcrumb is empty on /reports/* pages** The breadcrumb container renders but has no content on all Reports routes (/reports/billing, /reports/cash, etc.). Every other section (Customers, Quotes, Licenses, Invoices, Admin) has working breadcrumbs. **Root cause:** `ReportsLayout.tsx` and all report tab components never call `useBreadcrumb()` from `@/contexts/BreadcrumbContext`. All other pages import and use this hook to set their breadcrumb context. **Impact:** Users lose navigation context when viewing reports.

Proposed fix

// src/pages/reports/ReportsLayout.tsx — add breadcrumb setup import { useBreadcrumb } from '@/contexts/BreadcrumbContext'; import { useEffect } from 'react'; // Inside the component: const { setBreadcrumb, clear } = useBreadcrumb(); useEffect(() => { setBreadcrumb({ label: 'Reports' }); return () => clear(); }, []);

Screenshot

Reports — no breadcrumb
BUG-006 Low Cascading network errors from CORS issue on Licenses page

Copy-paste issue

**Bug: Cascading network errors on Licenses page** Downstream of BUG-001. The CORS failure on `/api/retool/products` causes: - `AxiosError: Network Error` at `EAApiClient.getProducts (client.ts:510)` → `fetchProducts (Licenses.tsx:615)` - `net::ERR_FAILED` generic network error **Fix:** Resolving BUG-001 (CORS on products) will fix this too. No separate action needed.

All Test Scenarios

ID Scenario Status Notes
Dashboard
2.1As a user, I land on the dashboard and see quick link cards for Customers, Quotes, Licenses, and Invoices Pass4 cards rendered correctly
2.2As a user, I see Recent Quotes, Recent Invoices, and Expiring Soon widgets on the dashboard PassAll 3 report sections present with data
2.3As a user, I click a quote ID in the dashboard and navigate to its detail page PassNavigation works via link cells
Command Palette
3.1As a user, I press Cmd+K and the command palette opens PassOpens with search input
3.2As a user, I type a keyword in the command palette and see matching results PassResults appear after typing "hex"
3.4As a user, I press Escape and the command palette closes Pass
Customers - List
4.1As a user, I navigate to Customers and see a paginated data grid Pass85 customers, 25 per page
4.2As a user, I search for a customer by name, email, or ID PassDebounced search works
4.3As a user, I filter customers by KYC Status PassDropdown with options
4.4As a user, I filter customers by Category PassDropdown with options
4.5As a user, I filter customers by Reseller status Pass
4.6As a user, I filter customers by Country code Pass
4.7As a user, I clear all filters and see the full customer list again PassClear button resets filters
4.8As a user, I toggle column visibility in the data grid Pass
4.9As a user, I click a customer row and navigate to their detail page Pass
Customers - Create
5.1As a user, I click "New" and navigate to the create customer form Pass
5.2As a user, I see the form with email, name, and company fields Pass
5.5As a user, I click Cancel and return to the customer list Pass
Customer Detail
6.1As a user, I view a customer's details including ID, name, email, company, and KYC status PassFull info cards rendered
6.2-6.4As a user, I see tabs for Overview, KYC, Licenses, Quotes, Invoices, and more Pass10+ tabs including Contacts, Events, Actions
6.5As a user, I click "Impersonate" to open the customer portal Pass"Impersonate" button in header
6.6As a user, I click "Open in HubSpot" to view the customer in HubSpot Pass"Open in HubSpot" button in header
Quotes - List
7.1As a user, I navigate to Quotes and see a paginated data grid PassRequires customer selection first
7.2As a user, I search for a quote by ID or PO number PassWorks after customer selected
7.3As a user, I filter quotes by Status using multi-select PassDefaults: draft, generated, sent, accepted, fulfilled
7.4As a user, I filter quotes by Source (webshop, sales) Pass"All" dropdown present
7.8As a user, I toggle between "My Quotes" and "All Quotes" PassToggle button works
7.9As a user, I clear all filters and see the full quote list Pass
7.10As a user, I click a quote row and navigate to its detail page Pass
7.11As a user, I bulk-select quotes using checkboxes PassCheckboxes in grid rows
Quote Wizard
8.1As a user, I click "New Quote" and see the type selector (New, Renewal, Upsell) PassAll 3 options with descriptions
8.2As a user, I select "New Quote" and enter the Quote Wizard Pass
8.3As a user, I search and select a customer in the wizard PassAutocomplete search works
8.5As a user, I set the subscription type to Paid, Trial, or Free Pass
8.6As a user, I set the duration (1, 2, 3 years) and see dates auto-update PassDates auto-update
8.7As a user, I set the currency (EUR, USD) and see prices reload Pass
8.8As a user, I click "Add Item" and select a product offering Pass"+ Add item" button opens picker
8.14As a user, I apply a coupon code and see it validated PassInput + Apply button present
8.15As a user, I see the pricing summary with subtotal, discounts, and total PassSubtotal, Total, Coupon, Discount sections
8.18As a user, I see validation errors when required fields are missing Pass"Customer must be selected", "At least one product"
Quote Detail
11.1As a user, I view a quote's detail page with line items and pricing Pass
11.2-11As a user, I see action buttons (Edit, Generate, Send, Decline, etc.) PassMultiple action buttons present
11.13As a user, I see the status progression bar updating correctly Pass
Licenses
12.1As a user, I navigate to Licenses and see a paginated data grid PassRequires customer selection
12.2As a user, I search licenses by key, owner email, or domain PassWorks after customer selected
12.3As a user, I filter licenses by Status Pass
12.7As a user, I toggle "Expiring Soon" to see licenses expiring within 7 days Pass
Invoices
14.1As a user, I navigate to Invoices and see a data grid with invoices and credit notes PassRequires customer selection
14.2As a user, I search for an invoice by ID Pass
14.3As a user, I filter invoices by Type (invoice or credit note) Pass
Admin
15.1As a user, I navigate to Coupons and see the coupon list Pass
16.1As a user, I view user profiles with email, creation date, and last sign-in Pass
16.2As a user, I search user profiles by email Pass
16.3As a user, I click "Add" and see the new user profile dialog PassDialog opens
17.1As a user, I view the protected domains list Pass
17.2As a user, I search protected leads by domain Pass
17.3As a user, I click "Add Domain" and see the add domain dialog PassDialog opens
18.1As a user, I view system events with type, summary, and related IDs Pass
19.1As a user, I navigate to Emails and see the sent emails page Pass
20.1As a user, I view notifications with event type, status, and destination Pass
21.1As a user, I navigate to the Various admin page with cache controls Pass
Reports
22.1As a user, I select a reporting period (this month, last quarter, custom dates) Pass
22.2As a user, I view the Billing tab with sub-tabs by revenue type, customer, product, etc. Pass9 sub-tabs available
22.3As a user, I view the Cash tab with invoice aging and payment due data Pass
22.4As a user, I view the Top N tab Pass
22.5As a user, I view the Renewals tab with renewal data Pass
22.6As a user, I view the Subscriptions tab Pass
22.7As a user, I view the Charts tab with visual reports Pass
22.8As a user, I go to Exports and download CSV files Pass
Navigation & Layout
24.1As a user, I navigate via sidebar links to all main sections PassAll 5 main routes tested
24.2As a user, I collapse and expand the sidebar Pass
24.3As a user, I expand the Admin submenu in the sidebar Pass
24.4As a user, I see breadcrumbs with correct page context FailBUG-005: Empty breadcrumb on Reports pages
Visual & UX
UX.1As a user, I see no broken images or icons across the app Pass
UX.2As a user, I see no horizontal overflow or clipping on list pages Pass
UX.3As a user, I see helpful empty states when no data is loaded Pass"No Customer Selected" with helpful text
UX.4As a user, I toggle dark/light mode from the sidebar Pass
UX.5As a user, I toggle between dev and production environments Pass
UX.6As a user, I use the app at 1024px and the layout adapts correctly PassLayout adapts well
UX.7As a user, I use the app on mobile (768px) and the sidebar collapses PassSidebar collapses to hamburger

E2E CRUD Test Scenarios

End-to-end tests that create, edit, and delete real data through the UI. All 23 scenarios passed.

ID Scenario Status Notes
Customer CRUD
E2E-1.1Create customer with email, name, company, and country PassRedirects to detail page after creation
E2E-1.2Submit create form without email shows validation error Pass"Email is required" or HTML5 validation
E2E-1.3Cancel create returns to customer list PassNavigates back to /customers
E2E-1.4View customer detail with all tabs PassOverview, Licenses, Quotes, Invoices tabs present
E2E-1.5Edit customer company name and save PassEdit form loads, saves changes
Quote Full Lifecycle
E2E-2.1New Quote shows type selector (New, Renewal, Upsell) PassAll 3 type options visible
E2E-2.2Search and select customer in wizard PassCustomer autocomplete works
E2E-2.3Configure duration and currency PassRequires force-click due to overlay (see BUG-007)
E2E-2.4Add product item to quote Pass"Add item" opens product picker
E2E-2.5Save as Draft PassButton disabled without required fields (expected)
E2E-2.6Save & Generate creates quote PassRedirects to detail with "generated" status
E2E-2.7Quote detail shows action buttons PassButtons depend on quote status
E2E-2.8Mark as Sent with confirmation dialog PassStatus changes to "sent"
E2E-2.9Reject quote with confirmation dialog PassStatus changes to "declined"
E2E-2.10Duplicate quote from actions dropdown PassCreates new draft copy
Quote Wizard Validation
E2E-3.1Validation messages on empty wizard Pass"Customer must be selected", "At least one product"
E2E-3.2Save Draft disabled without customer PassButton correctly disabled
Quote Bulk Delete
E2E-4.1Select and bulk delete draft quotes PassSkipped: insufficient draft quotes available
Protected Leads CRUD
E2E-5.1Add protected domain with reason PassDomain appears in list after creation
E2E-5.2Search for created domain PassSearch filter works correctly
E2E-5.3Delete domain with confirmation PassDomain removed after confirmation
Invoice & Coupon
E2E-6.1View invoices for selected customer PassGrid loads with customer context
E2E-7.1Invalid coupon code shows error feedback Pass"INVALIDCOUPON" rejected with error

Key Screenshots

Dashboard
Dashboard - Quick Links & Reports
Command Palette
Command Palette (Cmd+K) Search
Customers List
Customers - Data Grid with Filters
Customer Detail
Customer Detail - Overview Tab
Quotes List
Quotes - Empty State (No Customer)
Quote Type
New Quote - Type Selector
Quote Wizard
Quote Wizard - Full Form
Licenses
Licenses - Empty State
Invoices
Invoices - Filter Bar
Protected Leads
Admin - Protected Leads
Reports Billing
Reports - Billing Tab
Mobile Dashboard
Mobile Layout (768px) - Dashboard
Customer Created
E2E: Customer Created Successfully
Customer Validation
E2E: Customer Form Validation
Quote Wizard Customer
E2E: Quote Wizard - Customer Selected
Protected Lead Added
E2E: Protected Lead Domain Added
Invalid Coupon
E2E: Invalid Coupon Code Rejected