Files
integreat/docs/testing/behaviors/dashboard.md
Bryce b499d460f3 docs: add comprehensive test behavior documentation for all pages
Add behavior documentation covering all SSR and legacy SPA pages:
- Testing strategy and type definitions (unit/integration/UI)
- Dashboard, Invoice, Payment, Transaction, Ledger pages
- Company/Settings, POS, Admin, Search, Auth pages
- Legacy SPA behavior docs (no UI tests until migrated)
- Edge cases, test data requirements, and dependencies per subsystem

Total: 3,600+ lines of behavior documentation to guide test authorship.
2026-05-04 12:15:20 -07:00

9.3 KiB

Dashboard Behaviors

Overview

The Dashboard is an admin-only, SSR-based overview page that displays financial summary cards across selected clients. It provides at-a-glance visibility into bank account balances, outstanding tasks, recent sales trends, expense breakdowns, and profit/loss summaries. Cards are loaded lazily via HTMX for progressive rendering.

Routes

Route Handler Purpose
GET / ::page Main dashboard page with stub cards
GET /expense-card ::expense-card Expense pie chart (top 5 accounts, last month)
GET /pnl-card ::pnl-card Profit & Loss bar chart (last month)
GET /sales-card ::sales-card Gross sales bar chart (last 14 days)
GET /bank-accounts-card ::bank-accounts-card Bank account balances and sync status
GET /tasks-card ::tasks-card Unpaid invoices and uncategorized transactions

Behaviors

Unit Test Behaviors

  • extract-client-ids returns intersection of user's allowed clients and requested clients
  • is-admin? returns true only when user/role equals "admin"
  • Client trimming takes only first 20 clients from the valid set
  • clients-trimmed? is true when the count of trimmed clients differs from valid clients
  • Bank account card excludes accounts with bank-account-type/cash
  • Bank account card displays ledger balance formatted as $X,XXX.XX
  • Bank account card shows sync timestamp in standard-time format when present
  • Sales chart queries use ISO date range from 14 days ago to tomorrow
  • Expense pie chart queries use date range from 1 month ago to tomorrow
  • P&L card aggregates accounts into :sales and :cogs :payroll :controllable :fixed-overhead :ownership-controllable categories
  • Tasks card queries invoices with invoice-status/unpaid and transactions with transaction-approval-status/requires-feedback
  • Tasks card only renders task sections when counts are non-zero

Integration Test Behaviors

  • Main page GET / returns 200 with base layout and 6 stub cards for admin users
  • Each card endpoint returns 200 with HTML response containing chart canvas or data
  • Bank accounts card performs Datomic pull for each client's bank accounts with nested Intuit/Yodlee/Plaid data
  • Sales card executes Datomic query summing sales-order/total by ISO date
  • Expense card executes Datomic query summing invoice-expense-account/amount by account name
  • P&L card calls GraphQL get-profit-and-loss-raw with trimmed client IDs and last-month date range
  • Tasks card executes two separate Datomic queries for unpaid invoices and uncategorized transactions
  • Expense breakdown card (from company/reports/expense) executes query with optional vendor and account filters
  • All card endpoints apply wrap-trim-clients and wrap-admin middleware
  • Non-admin requests to any dashboard route receive 302 redirect to /login
  • Unauthenticated requests receive 302 redirect to /login with redirect-to parameter

UI Test Behaviors (SSR)

Happy Path: Dashboard Loads Successfully

  1. Admin user navigates to /
  2. Page renders with main navigation, client selector, and breadcrumb "Dashboard"
  3. Six stub cards appear with loading spinners:
    • Expenses (pie chart)
    • Tasks
    • Bank Accounts (tall card, row-span-2)
    • Gross Sales, last 14 days (bar chart)
    • Profit and Loss, last month (horizontal bar chart)
    • Expense breakdown (bar chart, wide card)
  4. Each stub card triggers hx-get on load to fetch its content
  5. Cards progressively render with actual data
  6. User sees bank account balances, task counts, sales chart, expense charts, and P&L summary

Card Interactions

  • Bank Accounts card displays each client's name, account name, ledger balance, and last sync time
  • When a bank account has Intuit/Yodlee/Plaid sync, the external balance and sync time display alongside the ledger balance
  • Yodlee accounts additionally show "Pending Txs" with pending balance
  • Tasks card shows "Pay now" link for unpaid invoices (links to unpaid invoices page with date-range=year)
  • Tasks card shows "Review now" link for uncategorized transactions (links to requires-feedback transactions page)
  • Expense breakdown card has Vendor and Account typeahead filters
  • Changing Vendor or Account filter triggers HTMX request to reload the breakdown chart with filtered data
  • Expense breakdown chart updates URL via hx-push-url with query params

Client Selection Effects

  • User selects different clients from the client dropdown
  • Page triggers clientSelected event on body
  • HTMX swaps #app-contents with fresh dashboard content for newly selected clients
  • All cards re-fetch with new client context
  • If more than 20 clients selected, a yellow warning banner appears: "Warning: These reports are only for twenty of the selected customers. Please select a specific customer to see more detail."

Edge Cases

No Clients Selected

  • Dashboard page renders but all cards show empty state
  • Bank Accounts card renders empty list
  • Sales chart shows empty bar chart with no data points
  • Expense pie chart shows empty pie chart
  • P&L card shows zero income and expenses
  • Tasks card shows no task notifications (sections hidden when count is zero)

Many Clients (Trimming Warning)

  • When user has access to more than 20 clients and selects all, only first 20 are used
  • Yellow warning banner displays below breadcrumb
  • All card queries execute against trimmed client set only
  • Warning persists across client selection changes until fewer than 21 clients selected

No Data for Date Ranges

  • Sales chart (14-day window): Empty chart renders with no bars when no sales orders exist
  • Expense pie chart (last month): Empty pie chart renders when no invoices with expense accounts exist
  • P&L card: Shows $0.00 for both income and expenses
  • Tasks card: No task sections render (conditional on non-zero counts)
  • Bank Accounts: Accounts still display with zero/null balances if they exist but have no transactions

Error Loading Individual Cards

  • Each card loads independently via separate HTMX request
  • Failure in one card endpoint does not prevent other cards from loading
  • Stub card shows spinner until successful response or timeout
  • Server errors in card endpoints return appropriate HTTP status without breaking page layout

Permissions (Admin vs User)

  • Only users with user/role = "admin" can access dashboard routes
  • Non-admin authenticated users receive 302 redirect to /login
  • Unauthenticated users receive 302 redirect to /login with redirect-to parameter
  • Admin role is verified by wrap-admin middleware before any data queries execute

Bank Account Sync States

  • Account with no external sync (Intuit/Yodlee/Plaid): Shows only ledger balance and sync time
  • Account with Intuit sync: Shows Intuit balance and last synced timestamp
  • Account with Yodlee sync: Shows available balance, last synced timestamp, and pending balance
  • Account with Plaid sync: Shows Plaid balance and last synced timestamp
  • Missing/null balances display as $0.00

Test Data Requirements

Users

  • Admin user (:user/role "admin") with access to multiple clients
  • Non-admin user (:user/role "user") to test permission denial

Clients (minimum 2, ideally 25+)

  • Client with client/name and at least one bank account
  • Mix of clients with Intuit, Yodlee, and Plaid synced accounts
  • Client with :bank-account-type/cash account (should be excluded from display)

Bank Accounts

  • Bank account with :bank-account/current-balance and :bank-account/current-balance-synced
  • Bank account linked to Intuit (:bank-account/intuit-bank-account)
  • Bank account linked to Yodlee (:bank-account/yodlee-account with available + pending balance)
  • Bank account linked to Plaid (:bank-account/plaid-account)

Sales Orders

  • Sales orders dated within last 14 days with :sales-order/total
  • Sales orders dated outside 14-day window (should not appear)

Invoices

  • Invoices with :invoice/expense-accounts (with :invoice-expense-account/amount and :account/name) within last month
  • Invoices with :invoice/status :invoice-status/unpaid within last year
  • Invoices with :invoice/outstanding-balance
  • Voided invoices (should be excluded from expense breakdown)

Transactions

  • Transactions with :transaction/approval-status :transaction-approval-status/requires-feedback within last year
  • Transactions with :transaction/amount

P&L Data

  • Chart of accounts with categories :sales, :cogs, :payroll, :controllable, :fixed-overhead, :ownership-controllable
  • Account balances within last month for selected clients

Dependencies

External Services

  • Datomic: All card data queried from Datomic database
  • GraphQL endpoint: P&L card calls get-profit-and-loss-raw (Lacinia GraphQL)
  • Intuit/Yodlee/Plaid: Bank account sync data for external balances (data stored in Datomic)

Frontend Libraries

  • HTMX: Progressive card loading via hx-get/hx-trigger/hx-swap
  • Chart.js: Canvas-based chart rendering (bar, pie, horizontal bar charts)
  • Alpine.js: Chart data binding via x-data/x-init attributes

Middleware Stack

  • wrap-admin: Enforces admin-only access
  • wrap-client-redirect-unauthenticated: Handles auth redirects
  • wrap-trim-clients: Limits client scope to 20
  • wrap-hydrate-clients: Populates client data in request