14 — QA Checklist (Organizations Module)
- 1. Functional (P0)
- 2. Security & isolation (P0)
- 3. Visual & design tokens
- 4. A11y (P0 per changed screen, A §12)
- 5. Performance (10 §1)
- 6. Offline matrix (10 §2)
- 7. Tablet / desktop / foldable
- 8. Localization
- 9. Animations & motion
- 10. Analytics (proposed contract)
- 11. Regression guardrails (code facts to never break)
Module checklist layered on 00-shared/10 baseline. Severity: P0 ship-blocking.
1. Functional (P0)
-
Provision:
POST /organizationswith name only → slug auto-derived exactly perslugify(organizations.service.ts:161-169); status =onboarding; 7 default roles seeded (organizations.service.ts:58;role.schema.ts:8-65). -
Slug conflict → 409
DUPLICATE_RESOURCEinline; domain conflict on update with own-org exemption (organizations.service.ts:115-122). -
Full-replace semantics:
PATCH :id/settingswith only{attendance}wipes academic/theme — client must always submit full object; verify no wipe path in UI (S4). -
Full-replace flags:
PATCH :id/feature-flagswith{a:true}removes keyb— UI must submit merged map; rollback restores prior map exactly. -
Delete → soft-deleted org absent from
GET /organizations+GET /:id(404) (organizations.repository.ts:15-19,60-66). -
Invalid ObjectId → 400
VALIDATION_ERROR"Invalid resource identifier." (http-exception.filter.ts:47-55,92). -
Settings collection:
PUT /settingsupsert idempotent; bulkPUT /settings/bulk; delete 404 path (settings.service.ts:36-41). -
Flags catalog: upsert persists only enabled+label (
feature-flag.repository.ts:32-43); delete soft. -
Register: duplicate email 409; password <8 → 400; tokens usable immediately (
auth.service.ts:54-121). -
Pagination contract:
meta.totalPages/hasNext/hasPreviousexact (pagination-query.dto.ts:41-54); page > totalPages → empty data,hasNext:false. -
qsearch case-insensitive on name+slug; sort-createdAtdefault (organizations.service.ts:78-87).
2. Security & isolation (P0)
-
OQ-4: tenants list + tenant detail reachable by tenant admin today (unguarded controller, unscoped repo) — client MUST gate to
isPlatformAdmin; server guard(planned). Test: tenant-admin token → route hidden + 403 screen. -
Cross-tenant
:idaccess → 404 (never 403-leak-of-existence); verify both self + platform surfaces. -
JWT
tenantIdnever sent in body (except public registertenantId— by design,register.dto.ts:27-29); all other requests derive from token. - Tokens in secure storage; no token in analytics/logs (10_QA_Baseline.md §3).
- 5xx internals never rendered — generic + requestId only.
- Typed-confirm delete: destructive button disabled until org name typed exactly.
-
No
Idempotency-Keydependency assumed for PATCH/DELETE (retry-safe by nature); register POST is rate-limited, no auto-retry.
3. Visual & design tokens
- No literal colors/spacing/durations in widget code (D §7); tokens only.
- Light + dark parity on all 8 screens (surface/cards contrast ≥3:1 chrome, ≥4.5:1 text — 09 §2).
- Status/plan badges: icon + label, never color-only (A §9); colorblind check (primary/error/success pairs with icons).
-
Slug/domain rendered
mono; numerals tabular (02 §2). - Golden tests: 8 screens × 3 sizes × light/dark (10 §9).
4. A11y (P0 per changed screen, A §12)
- TalkBack + VoiceOver full walkthrough: S1 → S4 save flow, S5 toggle, S8 typed-confirm.
-
Keyboard-only desktop: focus order toolbar → search → chips → list; ESC closes everything;
Nnew tenant; typed-confirm traps focus. - Text scale 2×: no clipping in cards/forms/badges (S4 chip rows wrap; header wraps).
- Toggle results announced via live region; errors announced + focus to first invalid field.
- Color picker hex fallback; contrast warning announced.
-
Focus ring visible
primary2 dp offset 2.
5. Performance (10 §1)
-
Tenants list: skeleton ≤200 ms; content ≤2 s network; 0 dropped frames scrolling 100-row list (
ListView.builder). -
PATCHround-trip feedback ≤100 ms press-state; save spinner consistent. - No duplicate parallel requests for same org id (cache + single-flight).
-
Flag map merge on 50+ flags: no rebuild jank (grouped
AnimatedSize, list virtualization). - Memory: no growth across 20 min of tab-switching (keep-alive disposal policy).
6. Offline matrix (10 §2)
| Scenario | Expected |
|---|---|
| Airplane → S1/S7 | cached data + AppOfflineBanner + stale badge |
| Offline → S2/S4 submit | blocked with guidance; draft kept in-memory; no silent loss on back (guard) |
| Offline → S5 toggle | switch disabled + banner (no offline queue) |
| Offline → online | auto-refresh on next interaction; no partial writes |
| 3G throttle | skeletons, no ANR, retry works |
| Network loss mid-save | pending → error snackbar "Couldn't save — retry"; safe retry (PATCH idempotent) |
7. Tablet / desktop / foldable
- ≥840 dp: S7 master-detail; breadcrumbs at depth ≥2; hover states; scrollbars visible.
- 600–839 dp: S1 two-column info grid; settings tabs readable.
- Foldable: cover + unfolded layouts no overlap; 200% text reflow.
- Landscape tablet: keyboard doesn't cover form fields (scrollable form).
8. Localization
-
All strings via keys (
app_en.arb+ org-locale); server business messages mapped to keys where available, fallback for 4xx business text (07 §11). -
Date/number formatting via
Intl(academic year, created dates). - RTL readiness: no hardcoded alignments (badges/rows use logical direction).
- Working-day chips localized weekday names (Sunday-first per locale).
- Smoke strings (fr/hi/es) don't break S4 labels or S1 cards.
9. Animations & motion
-
All transitions use
m-*tokens; reduced-motion: fades ≤m-instant, no stagger/shimmer/hero. - No dropped frames during hero S7→S8 (profile 120 fps).
- Haptics only per 10_Interaction_Specification.md §3; none on desktop.
10. Analytics (proposed contract)
-
Events fire per 05 inventory:
organizations.overview.view,organizations.edit.save.success|error,organizations.settings.save.*,organizations.flags.toggle.{key},organizations.tenants.list.view|search|delete.confirmed. - No PII beyond necessity; consent-gated (A4).
11. Regression guardrails (code facts to never break)
- Settings/flag save always full-object (test asserts sibling keys survive).
-
Slug change flows through confirm dialog; domain conflict check compares
existing.id !== id(organizations.service.ts:117). -
Create never accepts
statusfrom client (forcedonboarding,:55); update can change status. -
Register rate limit 5/min — UI countdown, no auto-retry (
auth.controller.ts:31).