# Legacy SPA Behaviors ## Overview These pages are rendered client-side via Reagent/Re-frame and use GraphQL for data fetching. They are being migrated to HTMX SSR. **No UI tests should be written for these pages until migrated.** **Testing Philosophy** - Prefer unit tests for pure business logic (calculations, validations, transformations) - Use integration tests for GraphQL queries, mutations, and data flows - Use UI tests only after migration to SSR; until then, mark as "UI (when migrated)" - Every behavior must be user-visible; no tests for implementation details --- ## Testing Patterns ### Pattern: GraphQL Data Fetching All data fetching uses re-frame `graphql` effect with JWT token from `:user` in app-db: 1. Queries use `owns-state` to track loading status per page 2. Results flow through `::data-page/received` event which stores data and syncs URL query params 3. Filter changes are debounced (800ms) via `dispatch-debounce` **Test implications:** Integration test the GraphQL query resolution and response shape. Do not test the re-frame effect machinery. ### Pattern: Re-frame State Management - **Data pages**: `data-page` namespace provides reusable pagination/filtering state - `::data-page/params` — merged filters + table params + query params - `::data-page/data` — GraphQL response data - `::data-page/checked` — selected rows for bulk operations - **Forms**: `forms` namespace manages edit dialogs with `start-form`, `change-handler`, `save-succeeded` - **Status**: `status` namespace tracks async operation states (`:loading`, `:complete`, `:error`) **Test implications:** Test via integration tests that verify the correct data appears after state transitions. Do not test subscription internals. ### Pattern: Client-Side Routing - Navbar uses Bidi `path-for` with `routes/routes` for legacy SPA links - Some navbar items (Payments, POS, Invoices) now link to `ssr-routes/only-routes` instead - Page components dispatch `::mounted` on mount and `::unmounted` on unmount **Test implications:** After migration, integration test route redirects from old SPA routes to new SSR routes. --- ## Home ### Dashboard Display | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 1.1 | It should display a dashboard with three chart sections: top expense categories, upcoming bills, and cash flow projection | UI (when migrated) | [ ] | | 1.2 | It should load data for the currently selected client on page load | Integration | [ ] | | 1.3 | It should display a note: "these reports are for [client]. Please choose a specific customer for their report." when the user has access to multiple clients and has not selected a specific one | UI (when migrated) | [ ] | | 1.4 | It should display an interactive bar chart for cash flow projection | UI (when migrated) | [ ] | | 1.5 | It should display a table below the cash flow chart showing invoices, upcoming debits, and upcoming credits with days-until due | UI (when migrated) | [ ] | | 1.6 | It should allow switching the cash flow range between 7, 30, 60, 90, 120, 150, and 180 days | UI (when migrated) | [ ] | | 1.7 | Given the user switches the cash flow range, then the chart and table should update to reflect the selected range | Integration | [ ] | | 1.8 | Given the user clicks a cash flow bar, then it should redirect to the unpaid invoices page for that date | UI (when migrated) | [ ] | | 1.9 | It should show empty charts gracefully when there is no data for the selected client | UI (when migrated) | [ ] | | 1.10 | It should show a loading state while GraphQL data is being fetched | UI (when migrated) | [ ] | | 1.11 | It should handle GraphQL errors by showing the loading state appropriately | UI (when migrated) | [ ] | --- ## Login ### Authentication | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 2.1 | It should display a "Login with Google" button on the login page | UI (when migrated) | [ ] | | 2.2 | It should link the login button to Google OAuth | UI (when migrated) | [ ] | | 2.3 | It should preserve the `redirect-to` query parameter in the Google OAuth URL | Integration | [ ] | | 2.4 | It should display a warning notification with the logout reason when the `logout-reason` query parameter is set | UI (when migrated) | [ ] | | 2.5 | It should redirect an already authenticated user away from the login page | Integration | [ ] | ### Needs Activation | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 2.6 | It should display the message: "Sorry, your user is not activated yet. Please have Ben Skinner enable your account." when the user's account is inactive | UI (when migrated) | [ ] | | 2.7 | It should provide a "here" link that clears user state and redirects to the login page | Integration | [ ] | --- ## Transactions ### List Display | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 3.1 | It should display a table of transactions with columns for amount, memo, location, approval status, vendor, accounts, date, and description | UI (when migrated) | [ ] | | 3.2 | It should load the transaction list with a default date filter of the last 1 month | Integration | [ ] | | 3.3 | It should allow filtering by vendor, account, bank account, date range, amount range, location, import batch, description, and linked status via a sidebar | UI (when migrated) | [ ] | | 3.4 | It should debounce sidebar filter changes by 800ms before refreshing data | Integration | [ ] | | 3.5 | It should support pagination with start and per-page parameters | Integration | [ ] | | 3.6 | It should display checkboxes for row selection when the user is an admin | UI (when migrated) | [ ] | | 3.7 | It should not display checkboxes or bulk action buttons for non-admin users | UI (when migrated) | [ ] | ### Transaction Edit | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 3.8 | It should open an edit sidebar when the user clicks a transaction row | UI (when migrated) | [ ] | | 3.9 | It should allow updating the vendor, approval status, memo, and expense accounts | UI (when migrated) | [ ] | | 3.10 | It should validate that the sum of expense account amounts equals the transaction amount | Unit + Integration | [ ] | | 3.11 | It should validate that locations are valid for the selected accounts | Unit + Integration | [ ] | | 3.12 | It should block editing locked transactions | Integration | [ ] | | 3.13 | It should display validation errors inline in the edit form | UI (when migrated) | [ ] | ### Bulk Operations (Admin) | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 3.14 | It should allow deleting selected transactions or all visible transactions | Integration | [ ] | | 3.15 | It should allow suppressing selected transactions instead of deleting them | Integration | [ ] | | 3.16 | It should allow bulk coding by applying vendor, account, approval status, and account rules to multiple transactions | Integration | [ ] | | 3.17 | It should allow importing transactions via a manual Yodlee import dialog | Integration | [ ] | ### Route Variants | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 3.18 | It should apply the correct default approval status filter for each route variant: all, unapproved, approved, requires-feedback, excluded | Integration | [ ] | --- ## Ledger ### General Ledger | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 4.1 | It should display a table of journal entries with expandable line items | UI (when migrated) | [ ] | | 4.2 | It should load the ledger with a default date filter of the last 1 month | Integration | [ ] | | 4.3 | It should allow filtering by vendor, account, bank account, date range, amount range, and location via a sidebar | UI (when migrated) | [ ] | | 4.4 | It should support virtual pagination controls | Integration | [ ] | | 4.5 | It should allow admin users to export filtered results as a CSV download | Integration | [ ] | | 4.6 | It should display "Not authorized" for users with the manager role | UI (when migrated) | [ ] | ### Profit and Loss | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 4.7 | It should generate a profit and loss report for a single client with a default period | Integration | [ ] | | 4.8 | It should allow selecting multiple companies via a multi-select typeahead and generating a combined report | UI (when migrated) | [ ] | | 4.9 | It should provide period preset buttons: 13 periods, 12 months, last week, week-to-date, last month, month-to-date, year-to-date, last calendar year, and full year | UI (when migrated) | [ ] | | 4.10 | It should populate the correct date ranges when a period preset is selected | Unit | [ ] | | 4.11 | It should allow custom period selection via start and end date pickers in advanced mode | UI (when migrated) | [ ] | | 4.12 | It should optionally include period-over-period deltas | UI (when migrated) | [ ] | | 4.13 | It should optionally break out the report by location, which is mutually exclusive with including deltas | Integration | [ ] | | 4.14 | It should allow admin users to generate and download a PDF export | Integration | [ ] | | 4.15 | It should provide an email composition link for single-client PDF exports | UI (when migrated) | [ ] | | 4.16 | It should open a ledger detail sidebar when the user clicks a report cell | UI (when migrated) | [ ] | | 4.17 | It should display "Not authorized" for users with the manager role | UI (when migrated) | [ ] | ### Cash Flows | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 4.18 | It should generate a cash flows statement report | Integration | [ ] | | 4.19 | It should use the same company, period, delta, and location column controls as the profit and loss report | Integration | [ ] | | 4.20 | It should allow admin users to export the report to PDF | Integration | [ ] | | 4.21 | It should open a ledger detail sidebar when the user clicks a report cell | UI (when migrated) | [ ] | | 4.22 | It should display "Not authorized" for users with the manager role | UI (when migrated) | [ ] | ### Profit and Loss Detail | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 4.23 | It should generate a detailed journal entry report with a default 2-week date range | Integration | [ ] | | 4.24 | It should group journal entries by category: sales, COGS, payroll, controllable, fixed overhead, and ownership controllable | Integration | [ ] | | 4.25 | It should display Gross Profit, Overhead, and Net Profit summaries | UI (when migrated) | [ ] | | 4.26 | It should filter journal entries by the selected start and end dates | Integration | [ ] | | 4.27 | It should allow admin users to export the report to PDF with an email link for single client | Integration | [ ] | | 4.28 | It should display "Not authorized" for users with the manager role | UI (when migrated) | [ ] | ### Balance Sheet | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 4.29 | It should generate a balance sheet report as of a specific date | Integration | [ ] | | 4.30 | It should allow selecting multiple companies and combining them into a single report | Integration | [ ] | | 4.31 | It should optionally include a prior-year comparison with a side-by-side view | UI (when migrated) | [ ] | | 4.32 | It should allow admin users to export the report to PDF with an email composition link for single client | Integration | [ ] | | 4.33 | It should open ledger entries filtered by account and date range when the user clicks a cell | UI (when migrated) | [ ] | | 4.34 | It should display "Not authorized" for users with the manager role | UI (when migrated) | [ ] | ### External Ledger | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 4.35 | It should display only externally-imported journal entries with an external_id | Integration | [ ] | | 4.36 | It should allow admin users to delete selected entries, with a maximum of 1000 at once | Integration | [ ] | | 4.37 | It should allow admin users to export to CSV | Integration | [ ] | | 4.38 | It should display "Not authorized" for non-admin users | UI (when migrated) | [ ] | ### External Import | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 4.39 | It should allow admin users to paste tab-separated data into a textarea | UI (when migrated) | [ ] | | 4.40 | It should provide a checkbox to indicate whether the first row is a header | UI (when migrated) | [ ] | | 4.41 | It should parse the pasted data into a table with columns: Id, Client, Source, Vendor, Date, Account, Location, Debit, Credit, Note, and Cleared against | Integration | [ ] | | 4.42 | It should validate that the client code exists | Unit + Integration | [ ] | | 4.43 | It should validate that the vendor exists | Unit + Integration | [ ] | | 4.44 | It should validate that the date is in MM/dd/yyyy format | Unit + Integration | [ ] | | 4.45 | It should validate that total debits equal total credits | Unit + Integration | [ ] | | 4.46 | It should validate that all amounts are greater than 0 | Unit + Integration | [ ] | | 4.47 | It should validate that entries are dated after the client's locked-until date | Integration | [ ] | | 4.48 | It should validate that the location belongs to the client or is "A" | Unit + Integration | [ ] | | 4.49 | It should validate that the account exists and the location matches the account's required location | Unit + Integration | [ ] | | 4.50 | It should display errors per row with a dropdown explanation | UI (when migrated) | [ ] | | 4.51 | It should show status icons indicating success, ignored, or existing per row after import | UI (when migrated) | [ ] | | 4.52 | It should display the total success count, ignored count, and error count after import | UI (when migrated) | [ ] | | 4.53 | It should provide an "Only show errors" filter | UI (when migrated) | [ ] | | 4.54 | It should display "Not authorized" for non-admin users | UI (when migrated) | [ ] | --- ## Payments | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 5.1 | It should be fully migrated to SSR at `/payment/`; the legacy client route exists only for navbar highlighting | N/A | [x] | --- ## Reports | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 6.1 | It should be fully migrated to SSR at `/company/reports`; the legacy client route exists only for navbar highlighting | N/A | [x] | --- ## Vendors ### Admin Vendor Management | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 7.1 | It should be fully migrated to SSR at `/admin/vendor`; the legacy client route exists only for navbar highlighting | N/A | [x] | ### New Vendor Dialog | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 7.2 | It should load the home dashboard with the vendor creation dialog pre-opened when navigating to `/vendor/new` | UI (when migrated) | [ ] | | 7.3 | It should only open the vendor dialog if the user has `:vendor :create` permission | Integration | [ ] | | 7.4 | It should allow creating a new vendor with name, terms, address, contacts, and default account | UI (when migrated) | [ ] | | 7.5 | It should validate that only one terms override exists per client | Unit + Integration | [ ] | | 7.6 | It should validate that only one schedule payment DOM override exists per client | Unit + Integration | [ ] | | 7.7 | It should validate that only one account override exists per client | Unit + Integration | [ ] | --- ## Cross-Cutting Behaviors ### GraphQL Query Patterns | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 8.1 | It should include the JWT token from the `:user` app-db state in all GraphQL requests | Integration | [ ] | | 8.2 | It should track loading status per page using `owns-state` | Integration | [ ] | | 8.3 | It should store GraphQL response data and sync URL query params via the `::data-page/received` event | Integration | [ ] | | 8.4 | It should debounce filter changes by 800ms before dispatching GraphQL requests | Integration | [ ] | ### Re-frame State Management | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 8.5 | It should merge filters, table params, and query params into `::data-page/params` | Integration | [ ] | | 8.6 | It should store GraphQL response data in `::data-page/data` | Integration | [ ] | | 8.7 | It should track selected rows for bulk operations in `::data-page/checked` | Integration | [ ] | | 8.8 | It should manage edit dialog state with `start-form`, `change-handler`, and `save-succeeded` events | Integration | [ ] | | 8.9 | It should track async operation states as `:loading`, `:complete`, or `:error` | Integration | [ ] | ### Navigation | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 8.10 | It should redirect old SPA routes to new SSR routes after migration | Integration | [ ] | | 8.11 | It should set up data subscriptions, forward event listeners, and parameter change watchers on page mount | Integration | [ ] | | 8.12 | It should tear down data subscriptions, forward event listeners, and parameter change watchers on page unmount | Integration | [ ] | --- ## Test Data Requirements | Entity | Requirements | |--------|-------------| | **Users** | Admin, power-user, manager, user, and read-only roles | | **Clients** | Multiple clients with different locations; some with locked-until dates | | **Vendors** | With/without default accounts, terms, and autopay settings | | **Accounts** | Expense accounts with/without invoice allowance; different locations | | **Bank Accounts** | Check, cash, and credit types | | **Transactions** | Various approval statuses, dates, amounts, locked/unlocked states | | **Journal Entries** | With/without external_id; various categories and accounts | | **Invoices** | Various statuses and due dates for cash flow projections | ## Existing Tests to Preserve - Test GraphQL query resolution for `HomeDashboard`, `TransactionPage`, `LedgerPage`, `ProfitAndLoss`, `CashFlows`, `JournalDetailReport`, `BalanceSheet`, and `ExternalLedger` - Test re-frame event handlers for data page state transitions - Test form validation logic for transaction editing and external ledger import ## Dependencies - GraphQL API (data fetching) - Re-frame/Reagent (client-side state and rendering) - Bidi (client-side routing) - Recharts (chart rendering on Home page) - Server-side PDF generation (P&L, Cash Flows, Balance Sheet, P&L Detail) ## Migration Notes ### Already Migrated to SSR - **Payments** (`/payments/`) - Fully migrated to `/payment/` SSR routes - **Reports** (`/reports/`) - Fully migrated to `/company/reports` SSR routes - **Admin Vendors** (`/admin/vendors`) - Fully migrated to `/admin/vendor` SSR routes ### Still Legacy SPA (prioritized by complexity) 1. **Transactions** - High complexity (filters, edit form, bulk operations, manual import) 2. **Home/Dashboard** - Medium complexity (charts, cash flow calculations) 3. **Ledger** - Medium complexity (filters, CSV export) 4. **External Ledger** - Low-medium complexity (subset of ledger + delete) 5. **External Import** - Medium complexity (TSV parsing, validation, batch import) 6. **Profit and Loss** - High complexity (multi-company, periods, PDF export) 7. **Cash Flows** - High complexity (shares P&L infrastructure) 8. **Profit and Loss Detail** - Medium complexity (category filtering, running balances) 9. **Balance Sheet** - Medium complexity (comparison mode, drill-down) 10. **Login** - Low complexity (static page) 11. **Needs Activation** - Low complexity (static page) 12. **New Vendor** - Low complexity (dialog on home page) ### Migration Risks - **Chart libraries**: Home page uses Recharts (React). Replacement needed for SSR. - **PDF generation**: P&L, Cash Flows, Balance Sheet, P&L Detail all support PDF export via server-side generation. - **Bulk operations**: Transactions page has complex bulk coding/deletion with validation. - **External import**: TSV parsing and validation logic lives entirely in SPA; server-side equivalent needed.