Dashboard Tests — Control Map (Widget-Agnostic)
100% coverage plan for /dashboard shell. Widget catalogs vary per install — never hard-code a fixed widget list.
On this page
Widgets are install-specific. Core ships a default set under src/routes/(app)/dashboard/widgets/; plugins may inject more via slots or additional files.
Tests and docs assert shell contracts and API shapes, never “must show CPU + Memory + Logs”.
Architecture (brief)
| Piece | Role |
|---|---|
+page.server.ts |
Auth, dashboard:read gate, compile-time widget discovery (import.meta.glob) |
+page.svelte |
Toolbar, add/search/reset, grid, DnD reorder, lazy load, AI generative mode |
base-widget.svelte |
Shared fetch/poll/cache/refresh chrome for individual widgets |
dashboard-preferences.svelte.ts |
Persist layout to /api/system-preferences |
widgets/*.svelte |
Per-install catalog — each exports widgetMeta |
Slot name="dashboard" |
Plugin injection zone |
Control → test matrix (shell = 100%)
Page shell
| Control | Unit | Integration | E2E | Notes |
|---|---|---|---|---|
| Load + title | page.server | — | ✅ dashboard.spec |
AdminPageShell |
dashboard:read deny |
page.server 403 | — | RBAC suite | Non-admin without perm |
| Empty state | — | — | ✅ | Install with no saved layout |
| Widget grid | — | — | ✅ | When preferences non-empty |
| Plugin slot attached | — | — | ✅ | May be empty |
Toolbar (install-agnostic)
| Control | Unit | E2E |
|---|---|---|
| Add Widget button / menu | — | ✅ opens menu when catalog has free widgets |
| Widget search | — | ✅ nonsense query → “No widgets found” |
| Reset all | — | ✅ after add → empty state |
| AI toggle present | — | ✅ does not require live MCP |
| Pointer drag-reorder (≥2 widgets) | — | ✅ header-band mouse drag |
| Keyboard Ctrl+Arrow reorder | — | ✅ accessibility path |
Layout persistence
| Control | Unit / integration | E2E |
|---|---|---|
| Save layout POST | system-preferences API (integration elsewhere) | add+reset exercises store |
| CSRF on save | preferences store sends header | covered by store code |
| Secure widget ids | generateUUID (no Math.random) |
code review + unit path |
Per-widget content
| Policy | How we test |
|---|---|
| Do not assert widget names | E2E only counts [data-widget-id] |
| Do test APIs widgets call | tests/integration/api/dashboard.test.ts (health, metrics, logs, …) |
| Optional deep unit per widget | Opt-in; not required for shell 100% |
Dashboard API (shared by many widgets)
Covered by tests/integration/api/dashboard.test.ts:
GET /api/dashboard/healthGET /api/dashboard/metrics(+ detailed)- system-info, logs, last5-content/media, online-user, system-messages, cache-metrics
Product hardening (2026-07-18)
- Replace
Math.randomwidget ids withgenerateUUID - CSRF headers on layout save + AI generate-layout
- Empty-state copy notes install-varying widgets
- Stable
data-testids for shell automation - Replace skipped “not implemented” E2E with real shell suite
- Pointer drag-reorder + keyboard Ctrl+Arrow E2E (widget-agnostic, ≥2 widgets)
Associated files
E2E
Unit
Integration
Run
bun run test:unit -- tests/unit/dashboard tests/unit/routes/dashboard-page-server.test.ts
bun x playwright test tests/e2e/routes/dashboard
Definition of done (shell 100%)
- Shell loads; empty or grid
- Add / search / reset without fixed widget list
- Permission gate unit-tested
- Dashboard APIs integration-tested
- Docs state install-varying widget policy
Was this page helpful?