Files
ui/_docs/02_tasks/done/AZ-466_test_destructive_ux.md
T
Oleksandr Bezdieniezhnykh 1dd25edee3 [AZ-460] [AZ-462] [AZ-466] [AZ-475] Batch 4 - destructive UX/forms/overlay/save
AZ-466 — Destructive UX policy + ConfirmDialog a11y + no-alert (4pts):
  src/components/ConfirmDialog.test.tsx (8 fast),
  tests/destructive_ux.test.tsx (4 fast, AdminPage class-delete drift),
  e2e/tests/destructive_ux.e2e.ts. New static checks STC-SEC7 (alert
  allowlist) + STC-SEC8 (destructive-surfaces gated/drift) wired through
  scripts/check-banned-deps.mjs reading tests/security/banned-deps.json.

AZ-475 — Numeric form input rejection (2pts):
  tests/form_hygiene.test.tsx (3 fast). Documents two SettingsPage drifts:
  silent zero coercion via parseInt(v)||0 and labels missing htmlFor.

AZ-462 — Overlay membership at in-window edges (2pts):
  tests/overlay_membership.test.tsx (6 fast). Documents getTimeWindowDetections
  strict < drift; AC-1 boundary tests are it.fails(); AC-2 / control PASS.
  Mocks HTMLCanvasElement.getContext to capture strokeRect.

AZ-460 — Annotation save URL + payload contract (2pts):
  tests/annotations_endpoint.test.tsx (6 fast),
  e2e/tests/annotations_endpoint.e2e.ts. AC-1 URL canary PASSes; AC-2
  payload missing 4 fields documented as it.fails(); AC-3 manual-draw
  PASS, AI-suggestion-accept + bulk-edit-save QUARANTINE skip.

Test infrastructure:
  - tests/setup.ts: NoopResizeObserver + NoopEventSource JSDOM polyfills.
  - tests/msw/handlers/annotations.ts: doubly-prefixed paths matching
    production calls (e.g. /api/annotations/annotations).
  - tests/msw/handlers/flights.ts: plural /aircrafts paths.

Verification: bun run test:fast → 80 passed, 13 skipped (14 files).
scripts/run-tests.sh --static-only → 24/24 PASS (was 22; +STC-SEC7/SEC8).
Per-batch self-review verdict: PASS_WITH_WARNINGS. Cumulative review
of batches 04-06 due after batch 6 per implement/SKILL.md Step 14.5.
Report: _docs/03_implementation/batch_04_report.md.

Also includes the previously-untracked
_docs/03_implementation/cumulative_review_batches_01-03_report.md
generated at the start of this session before batch 4 began.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 04:15:01 +03:00

3.7 KiB

Test — Destructive UX & ConfirmDialog

Task: AZ-466_test_destructive_ux Name: Destructive UX policy + ConfirmDialog a11y + no-alert + cancel paths Description: Implement the 8 blackbox tests that pin the destructive-action policy: every destructive surface (delete class, delete user, etc.) shows a <ConfirmDialog> before issuing the request; the dialog has proper a11y; cancel suppresses the request; no alert() is ever used. Complexity: 4 points Dependencies: AZ-456_test_infrastructure Component: 03_shared-ui (ConfirmDialog) + 08_admin (Blackbox Tests) Tracker: AZ-466 Epic: AZ-455

Problem

Without a uniform destructive-action gate, regressions add a delete button that fires directly — a one-click-data-loss bug. The policy is "no destructive action without ConfirmDialog, no alert() anywhere".

Outcome

  • 8 scenarios pass per the policy.
  • A static check enumerates every destructive surface to keep the policy enforceable.

Scope

Included

Scenario Profile Source file
FT-P-26 — class-delete with confirmation — happy path fast + e2e blackbox-tests.md
FT-P-27 — destructive policy — dialog before request for every destructive surface fast (static enumeration) blackbox-tests.md
FT-P-28 — ConfirmDialog has dialog + modal a11y attributes fast blackbox-tests.md
FT-P-29 — ConfirmDialog focus trap (Tab cycles inside) fast blackbox-tests.md
FT-N-07 — class-delete Cancel path — NO DELETE request issued fast blackbox-tests.md
FT-N-08 — Escape on <ConfirmDialog> cancels — no destructive request fast blackbox-tests.md
NFT-SEC-07 — alert() is forbidden anywhere in the SPA static security-tests.md
NFT-SEC-08 — ConfirmDialog gates every destructive action static + fast security-tests.md

Excluded

  • ConfirmDialog content / phrasing (covered by i18n parity in 10_test_i18n).
  • Specific delete-target wire shapes (covered by per-feature tasks).

Acceptance Criteria

AC-1: Happy path FT-P-26 simulates user clicking Delete → confirming → asserts the DELETE request fires AFTER the confirm.

AC-2: Cancel paths FT-N-07 / FT-N-08 assert that pressing Cancel / Escape on the dialog suppresses the DELETE request entirely.

AC-3: a11y FT-P-28 / FT-P-29 assert role="dialog", aria-modal="true", aria-labelledby / aria-describedby linkage, and a focus trap that keeps Tab inside the dialog.

AC-4: Policy enforcement FT-P-27 / NFT-SEC-08 — a static check enumerates every surface with a data-destructive (or equivalent) attribute and asserts each one mounts a <ConfirmDialog> before its mutating handler runs.

AC-5: No alert() NFT-SEC-07 — ripgrep static check grep -rn 'alert(' src/ returns no hits outside test files.

System Under Test Boundary

  • System under test: <ConfirmDialog> + every destructive surface (delete class, delete user, etc.).
  • Allowed stubs: MSW for the suite's delete endpoints (fast); real services (e2e).
  • Disallowed: stubbing <ConfirmDialog>; reading its React state.
  • Expected observables per results_report.md rows 49-51 + the rows for NFT-SEC-07, 08.

Constraints

  • Static check (FT-P-27 / NFT-SEC-08) requires a discoverable marker on destructive surfaces; this task lands the test, and per-component tasks (already in scope above) wire the markers.

Risks & Mitigation

Risk 1 — A destructive surface is added without the marker

  • Risk: a new feature adds a delete-button that bypasses the static check.
  • Mitigation: the marker is on the shared <DestructiveButton> wrapper; using raw <button> for destructive actions is flagged by an ESLint rule landed in this task.