14 — QA Checklist (Dashboard Module)
- 1. Data correctness (what the numbers mean)
- 2. Role gating & permission behaviour
- 3. Caching staleness (critical)
- 4. Interaction states
- 5. Accessibility (per 00-shared/09)
- 6. Performance
- 7. Offline & degraded
- 8. Blueprint-conformance regressions to track
Verification baseline for dashboard screens. Builds on 00-shared/10 (QA baseline) and 00-shared/09 (a11y). Items cite the source they guard.
1. Data correctness (what the numbers mean)
-
Students card shows active-only count (
status:'active'—dashboard.service.ts:31), not all students. -
Attendance is today-only (
{$gte: startOfDay, $lt: tomorrow}—:34); rate = server-roundedpresent/total×100,0when no records (:57-64). -
Finance card counts only
issued|partial|overdueinvoices (:36-37) and sumstotalAmount − paidAmount(:45-48); currency formattedINR. - Client never recalculates; it formats only (09_User_Behaviour.md B9) — assert in widget tests that the raw payload values are displayed verbatim.
-
Zero states are real zeros, not errors (
:31-33,:64).
2. Role gating & permission behaviour
-
With
org_admin(all perms —role.schema.ts:23): full overview renders. -
Roles without
dashboard.read(teacher :31,staff :39,accountant :47,parent :55,student :63): dashboard route hidden, no 403 dead-end (00-shared/06 §5). -
Finance widgets must be role-restricted when server-side widget filtering ships
(planned)(Dashboard.md:52); accountant finance-only layout(planned). -
Customize entry visible only with
dashboard.widget.manage(permissions.constants.ts:38); absent permission → entry not rendered. -
Open question: RBAC guard is not yet enforced on
GET /dashboard/overview(dashboard.controller.ts:8— JwtAuthGuard only) — track enforcement(planned)(permissions audit,IMPLEMENTATION_PLAN.mdPhase 5).
3. Caching staleness (critical)
-
Server TTL 60 s (
Dashboard.md:41) — verify the overview keysl:{tenantId}:dashboard:overviewexpires on time(planned); today the service live-scans (dashboard.service.ts:24-38) — regression guard: if/when caching lands, assert no live scan on warm cache. -
Event invalidation: after
AttendanceMarked/PaymentRecorded/InvoiceGenerated/ResultPublished(Dashboard.md:34) the next fetch returns fresh data(planned). -
Client: stale data past 60 s shows
StaleBannerwith correct age; ≥10 min → offline treatment (06_Screen_Specifications.md §1.3). - Client cache never written on error; last-good survives refresh failure.
- WS-triggered refetch is debounced (2 s) — bulk attendance marking (many events per second) coalesces to ≤ a few requests (10_Interaction_Specification.md §4).
- Poll budget: 60 s poll + pull + WS ≤ api tier 100/min (12_API_Mapping.md rate limits).
4. Interaction states
-
First load: skeletons only, no partial numbers; failure →
AppErrorState+ retry. - Pull-to-refresh: no skeleton flash; success silent, failure keeps numbers + snackbar ≥ 4 s.
- Single-flight: double pull/in-flight triggers coalesce, no duplicate requests.
-
KPI tap drills to owning module with matching filters (
issued/partial/overdue—:36-37). - Back from drill: dashboard refreshes by staleness check, scroll preserved.
-
Customize
(planned): optimistic toggle rollback on failure; conflict (409) reload; per-widget saving indicator.
5. Accessibility (per 00-shared/09)
-
Chart summaries: every
ChartCardhas a text caption +semanticsLabel("Attendance 93% today, 812 of 874 present") — screen readers never hit a silent chart (06_Screen_Specifications.md §1.5). -
KPI card semantics: value-first single node ("93 percent, Attendance"),
buttonwhen tappable. - Live region announces KPI deltas ≥ 1% after WS/poll updates; silent below.
-
Trend colour never sole signal:
▲/▼glyphs + hidden text (07_Component_Library.md §2). -
Touch targets ≥ 48 dp;
Taborder cards → charts → AppBar actions. - Dynamic type 200%: KPI row reflows vertical, no overflow; charts keep aspect, caption reflows.
- Dark/light AA contrast for all tokens incl. chart series (11_Design_System_Mapping.md §8).
6. Performance
- Dashboard paints KPI values before charts (values-first, 09_User_Behaviour.md B1); charts below fold load non-blocking.
-
No jank on WS value swap:
AnimatedSwitcher200 ms only; no full-list rebuild — cards keyed by widget id. -
Skeleton shimmer disabled on
reduceMotion(00-shared/02 §6). - Poll timer paused when backgrounded / route not active.
- One in-flight request at a time (single-flight) — no request storms.
-
Rebuilds:
KpiRowchildrenconst-eligible where possible;Overviewcompares via==to skip rebuilds.
7. Offline & degraded
-
Offline: last-good payload +
AppOfflineBanner; retry works after reconnect. -
No offline writes (module is read-only today; customize
(planned)disabled offline). - WS reconnect handled by socket layer; poll covers gaps; no duplicate refresh on reconnect + timer overlap (coalesce).
8. Blueprint-conformance regressions to track
-
"KPIs served from Redis, never live-scanned on request" (
Dashboard.md:49) — currently violated by the service; track until the caching layer lands (01_Product_Overview.md §3 gap). -
"Cache rebuilt by scheduled worker + on relevant events" (
Dashboard.md:50) — no worker exists(planned). -
"Role determines which KPIs are visible" (
Dashboard.md:52) — enforcement(planned).