Files
ui/_docs/02_document/tests/blackbox-tests.md
T
Oleksandr Bezdieniezhnykh 510df68bcf [AZ-447] autodev Steps 1-4 baseline: docs, tests, refactor specs
Captures the full output of autodev existing-code Phase A through
Step 4 (Code Testability Revision) for the Azaion UI workspace:

- Step 1 Document: _docs/02_document/ (FINAL_report, architecture,
  glossary, components/, modules/, diagrams/, system-flows,
  module-layout) plus _docs/00_problem/ + _docs/01_solution/ +
  _docs/legacy/ + _docs/how_to_test + README.
- Step 2 Architecture Baseline: architecture_compliance_baseline.md.
- Step 3 Test Spec: _docs/02_document/tests/ (environment,
  test-data, blackbox/performance/resilience/security/
  resource-limit tests, traceability-matrix), enum_spec_snapshot,
  expected_results/results_report.md (98 rows), plus the
  run-tests.sh + run-performance-tests.sh runners.
- Step 4 Code Testability Revision: 01-testability-refactoring/
  run dir (list-of-changes C01-C07, deferred_to_refactor,
  analysis/research_findings + refactoring_roadmap) and the 7
  child task specs AZ-448..AZ-454 under _docs/02_tasks/todo/
  plus _dependencies_table.md.
- _docs/_autodev_state.md pins the cursor at Step 4 / refactor
  Phase 4 entry so /autodev resumes cleanly.

Epic AZ-447 (UI testability gates) tracks the 7 child tasks that
will land in subsequent commits.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 00:38:49 +03:00

46 KiB
Raw Blame History

Blackbox Tests

Every test is observed at the SPA's public surface — DOM, ARIA, outbound network, EventSource state, browser storage, console — never via src/ imports beyond the typed wire-contract enums (P9). Each test trails an Expected result source: results_report.md row N line; the comparison method, tolerance, and reference file for the assertion come from that row. Profile (fast / e2e / static) decides which runner picks the test up — see environment.md.

Positive Scenarios

FT-P-01: Bootstrap refresh sends credentials:'include'

Summary: On <AuthContext> init, the bootstrap refresh call includes the HttpOnly refresh cookie. Traces to: AC-01 Category: Auth — bootstrap Profile: fast

Preconditions:

  • Refresh cookie present in the browser jar (test stubs it on the test domain).
  • No bearer in memory.

Input data: app mount in a fresh browser session — results_report.md row 02.

Steps:

Step Consumer Action Expected System Response
1 Mount <App> (test renderer or e2e navigation to /) Outbound fetch to /api/admin/auth/refresh is observed
2 Inspect the RequestInit of that fetch credentials === 'include'; cookie sent by the test runtime

Expected outcome: see row 02 (exact: credentials: 'include'). Max execution time: 5s. Expected result source: results_report.md row 02.


FT-P-02: 401-retry sequence — refresh and retry the original request

Summary: A 401 on an authenticated request triggers POST /api/admin/auth/refresh (cookie-bound) then a retry of the original request with the new bearer. Traces to: AC-01, AC-23 Profile: fast (with MSW) and e2e

Preconditions:

  • Authenticated session active; bearer about to expire.

Input data: any authenticated GET /api/admin/* issued via apiClientresults_report.md row 03.

Steps:

Step Consumer Action Expected System Response
1 Trigger an authenticated call whose first response is 401 (stub) The SPA issues POST /api/admin/auth/refresh with credentials:'include'
2 Refresh stub returns 200 with new bearer The SPA retries the original request with the new bearer
3 Inspect the final response observed by the calling code 200 (success)

Expected outcome: sequence exactly per row 03; exactly one refresh call per refresh cycle (row 12). Max execution time: 5s. Expected result source: results_report.md rows 03, 12.


FT-P-03: Refresh transparency — <ProtectedRoute> does not unmount

Summary: Auth refresh occurring mid-session does not unmount the routed view. Traces to: AC-23 Profile: fast

Preconditions:

  • User on /flights; bearer about to expire.

Input data: in-flight refresh while <FlightsPage> is mounted — results_report.md row 11.

Steps:

Step Consumer Action Expected System Response
1 Mount /flights; record render counter on <FlightsPage> initial render = 1
2 Force a 401 → refresh → retry cycle refresh handled
3 Inspect render counter post-refresh delta ≤ 1 re-render

Expected outcome: row 11 — <ProtectedRoute> children stay mounted; ≤1 re-render delta. Max execution time: 5s. Expected result source: results_report.md row 11.


FT-P-04: AnnotationStatus enum on the wire

Summary: Outbound annotation save body's status is a number from the spec set {0,10,20,30,40}. Traces to: AC-04 Profile: fast (static + payload assertion)

Preconditions: _docs/00_problem/input_data/enum_spec_snapshot.json is up-to-date (Phase 3 gate).

Input data: POST /api/annotations/annotations body captured at save — results_report.md row 18; also static check of src/types/index.ts per row 14.

Steps:

Step Consumer Action Expected System Response
1 Static read AnnotationStatus from src/types/index.ts {None:0, Created:10, Edited:20, Validated:30, Deleted:40}
2 Trigger an annotation save in <AnnotationsPage> (test mount) POST /api/annotations/annotations issued
3 Inspect body body.status ∈ {0,10,20,30,40}

Expected outcome: rows 14 (enum map) and 18 (wire payload). Max execution time: 3s. Expected result source: results_report.md rows 14, 18.


FT-P-05: MediaStatus / Affiliation / CombatReadiness enums match the spec

Summary: Each enum in src/types/index.ts matches the spec member set and numeric values pinned in enum_spec_snapshot.json. Traces to: AC-04 Profile: static

Preconditions: snapshot present (Phase 3 gate).

Input data: static read of src/types/index.ts against the snapshot — results_report.md rows 15, 16, 17.

Steps:

Step Consumer Action Expected System Response
1 Parse src/types/index.ts for enum MediaStatus, Affiliation, CombatReadiness parsed without error
2 Compare member sets to snapshot sets equal per row
3 Compare numeric values member-by-member equal per row

Expected outcome: rows 15, 16, 17 (set_contains + exact). Max execution time: 1s. Expected result source: results_report.md rows 15, 16, 17.


FT-P-06: Detection wire payload — affiliation + combatReadiness in spec value sets

Summary: Every Detection element of an outbound annotation save has valid enum members. Traces to: AC-04 Profile: fast

Preconditions: snapshot present; an annotation with N detections about to be saved.

Input data: POST /api/annotations/annotations body — results_report.md row 19.

Steps:

Step Consumer Action Expected System Response
1 Save an annotation with N≥2 detections, mixed affiliations and readiness request observed
2 Inspect every detections[i] affiliation and combatReadiness are in spec value sets

Expected outcome: row 19. Max execution time: 3s. Expected result source: results_report.md row 19.


FT-P-07: Annotation save endpoint URL is doubly-prefixed

Summary: Save POSTs to /api/annotations/annotations, not the single-prefix path. Traces to: AC-05 Profile: fast and e2e

Preconditions: user is editing an annotation in <AnnotationsPage>.

Input data: click Save — results_report.md row 22.

Steps:

Step Consumer Action Expected System Response
1 Trigger Save exactly one POST observed
2 Inspect URL matches ^/api/annotations/annotations$

Expected outcome: row 22. Max execution time: 3s. Expected result source: results_report.md row 22.


FT-P-08: Annotation save body contains all required fields

Summary: Save body has {Source, WaypointId, videoTime, mediaId, detections, status} and NOT the legacy time key. Traces to: AC-05 Profile: fast

Preconditions: an annotation with all spec-required fields present in the editor state.

Input data: click Save — results_report.md row 23.

Steps:

Step Consumer Action Expected System Response
1 Trigger Save with Source=AI, a WaypointId, a videoTime POST observed
2 Inspect body keys {Source, WaypointId, videoTime, mediaId, detections, status} ⊆ keys; time absent

Expected outcome: row 23. Max execution time: 3s. Expected result source: results_report.md row 23.


FT-P-09: Annotation-status SSE opens on <AnnotationsPage> mount

Traces to: AC-09 Profile: fast (EventSource test double) and e2e

Input data: mount /annotationsresults_report.md row 24.

Steps:

Step Consumer Action Expected System Response
1 Mount /annotations exactly one EventSource constructed
2 Inspect URL matches `^/api/annotations/annotations/events(?

Expected outcome: row 24. Max execution time: 3s. Expected result source: results_report.md row 24.


FT-P-10: Annotation-status SSE closes on unmount

Traces to: AC-09 Profile: fast

Preconditions: continuation of FT-P-09.

Input data: unmount /annotationsresults_report.md row 25.

Steps:

Step Consumer Action Expected System Response
1 Unmount the route EventSource.readyState → CLOSED (2) within 1 s

Expected outcome: row 25 (CLOSED within ≤ 1 000 ms). Max execution time: 3s. Expected result source: results_report.md row 25.


FT-P-11: Sync image detect endpoint

Traces to: AC-25 (sync path) Profile: fast and e2e

Input data: click Detect on a MediaType.Imageresults_report.md row 26.

Steps:

Step Consumer Action Expected System Response
1 Open <AnnotationsSidebar> on an image; click Detect exactly one POST observed
2 Inspect URL matches ^/api/detect/[0-9]+$

Expected outcome: row 26. Max execution time: 30s (server-side detect time included). Expected result source: results_report.md row 26.


FT-P-12: Async video detect endpoint + SSE (target — Phase B)

Traces to: AC-25 (async path) Profile: fast (mocked) — quarantined until F7 lands Status: target — UI does not implement async video detect today (04_verification_log.md F7). Test is written so it activates the day the feature ships.

Input data: click Detect on a MediaType.Video (behind feature flag) — results_report.md row 27.

Steps:

Step Consumer Action Expected System Response
1 Trigger detect on a video one POST ^/api/detect/video/[0-9]+$
2 Inspect response JSON {jobId: <int>}
3 Wait EventSource opens to `^/api/detect/stream/[0-9]+(?

Expected outcome: row 27 (3 assertions). Max execution time: 10s. Expected result source: results_report.md row 27.


FT-P-13: Long-video detect carries X-Refresh-Token header

Traces to: AC-25 Profile: fast — quarantined until F7 lands (and the header is added per Step 4) Status: target.

Input data: long-video async detect — results_report.md row 28.

Steps:

Step Consumer Action Expected System Response
1 Trigger long-video detect request headers observed
2 Inspect headers X-Refresh-Token present and non-empty

Expected outcome: row 28. Max execution time: 5s. Expected result source: results_report.md row 28.


FT-P-14: Overlay membership at the lower in-window edge

Summary: An annotation at videoTime = T - 30 ms (inside [-50, +150] window around T) renders. Traces to: AC-28 Profile: fast (component test on <CanvasEditor>)

Input data: currentTime = T, annotation at T - 30 msresults_report.md row 29.

Steps:

Step Consumer Action Expected System Response
1 Render <CanvasEditor> with the annotation and set currentTime = T overlay rect rendered

Expected outcome: row 29 (range check). Max execution time: 1s. Expected result source: results_report.md row 29.


FT-P-15: Overlay membership at the upper in-window edge

Summary: An annotation at videoTime = T + 120 ms renders. Traces to: AC-28 Profile: fast

Input data: currentTime = T, annotation at T + 120 ms — derived from results_report.md row 29.

Steps:

Step Consumer Action Expected System Response
1 Render <CanvasEditor> with the annotation and currentTime = T overlay rendered

Expected outcome: present. Max execution time: 1s. Expected result source: results_report.md row 29.


FT-P-16: Flight selection persists via PUT /api/annotations/settings/user

Traces to: AC-06 Profile: fast

Input data: FlightContext.selectFlight(<id>)results_report.md row 32.

Steps:

Step Consumer Action Expected System Response
1 Trigger selectFlight for a known flight id exactly one PUT observed
2 Inspect URL matches ^/api/annotations/settings/user$
3 Inspect body {selectedFlightId: <id>} (subset)
4 Code-search the test domain NO call to /api/flights/select

Expected outcome: row 32. Max execution time: 3s. Expected result source: results_report.md row 32.


FT-P-17: Selected-flight rehydration on boot

Traces to: AC-06 Profile: e2e (and fast with stubbed UserSettings)

Preconditions: UserSettings.selectedFlightId known in seed.

Input data: full reload — results_report.md row 33.

Steps:

Step Consumer Action Expected System Response
1 Hard reload as the seeded user app boots
2 Inspect FlightContext state via DOM (selected flight indicator) matches UserSettings.selectedFlightId

Expected outcome: row 33. Max execution time: 10s. Expected result source: results_report.md row 33.


FT-P-18: Live-GPS SSE opens within 5 s of flight select

Traces to: AC-08 Profile: e2e (uses flights/ live-gps simulator)

Input data: select a flight — results_report.md row 34.

Steps:

Step Consumer Action Expected System Response
1 Select a flight in <Header> EventSource opens
2 Inspect URL matches `^/api/flights/[0-9]+/live-gps(?
3 Wait readyState === OPEN (1) within ≤ 5 000 ms

Expected outcome: row 34. Max execution time: 10s. Expected result source: results_report.md row 34.


FT-P-19: Live-GPS SSE closes within 1 s of deselect

Traces to: AC-08 Profile: e2e

Input data: deselect — results_report.md row 35.

Steps:

Step Consumer Action Expected System Response
1 Deselect the flight EventSource state transitions
2 Inspect all ^/api/flights/[0-9]+/live-gps sources CLOSED (2) within ≤ 1 000 ms

Expected outcome: row 35. Max execution time: 5s. Expected result source: results_report.md row 35.


FT-P-20: Bulk-validate request URL and body

Traces to: AC-07 Profile: fast and e2e

Input data: select N items, click Validate — results_report.md row 36.

Steps:

Step Consumer Action Expected System Response
1 Select N items on <DatasetPage> selection state observable
2 Click Validate one POST observed
3 Inspect URL + body /api/annotations/dataset/bulk-status; body {ids: <length-N>, targetStatus: 30}

Expected outcome: row 36 (targetStatus: 30 after AC-04 fix). Max execution time: 5s. Expected result source: results_report.md row 36.


FT-P-21: Bulk-validate UI reflects new status within 2 s

Traces to: AC-07 Profile: fast

Preconditions: continuation of FT-P-20.

Input data: server returns 200 — results_report.md row 37.

Steps:

Step Consumer Action Expected System Response
1 Stub a 200 response UI updates
2 Inspect each selected row within ≤ 2 000 ms status badge reads Validated

Expected outcome: row 37. Max execution time: 5s. Expected result source: results_report.md row 37.


FT-P-22: i18n key parity en ↔ ua

Traces to: AC-12 Profile: static

Input data: src/i18n/en.json, src/i18n/ua.jsonresults_report.md row 45.

Steps:

Step Consumer Action Expected System Response
1 Read both files parsed
2 Compute deep, sorted key sets set_equals

Expected outcome: row 45. Max execution time: 1s. Expected result source: results_report.md row 45.


FT-P-23: No raw user-visible strings outside t(...)

Traces to: AC-12 Profile: static (lint rule)

Input data: lint over src/**/*.tsxresults_report.md row 46.

Steps:

Step Consumer Action Expected System Response
1 Run the lint rule reports 0 findings
2 Allow-list applies to proper-noun acronyms only allow-list respected

Expected outcome: row 46. Max execution time: 30s. Expected result source: results_report.md row 46.


FT-P-24: i18n detector path used at first boot

Traces to: AC-13 Profile: fast — quarantined until the detector is added in Step 4

Input data: first boot in a clean profile — results_report.md row 47.

Steps:

Step Consumer Action Expected System Response
1 Mount the app with a profile providing Accept-Language: uk i18next initialises
2 Inspect i18next.language resolves from detector (not hardcoded 'en')
3 Static read src/i18n/i18n.ts no lng: 'en' hardcoded init present

Expected outcome: row 47. Max execution time: 3s. Expected result source: results_report.md row 47.


FT-P-25: i18n persistence across reload

Traces to: AC-13 Profile: e2e — quarantined until the detector + persistence land

Input data: toggle language, reload — results_report.md row 48.

Steps:

Step Consumer Action Expected System Response
1 Click language toggle in <Header> to uk UI re-renders in Ukrainian
2 Hard reload app boots
3 Inspect i18next.language equals uk

Expected outcome: row 48. Max execution time: 10s. Expected result source: results_report.md row 48.


FT-P-26: Class-delete with confirmation — happy path

Traces to: AC-14, AC-30 Profile: fast

Input data: click Delete on a class entry in <AdminPage> and Confirm — results_report.md row 49.

Steps:

Step Consumer Action Expected System Response
1 Click Delete <ConfirmDialog> mounts
2 Click Confirm exactly one DELETE observed
3 Inspect URL matches ^/api/admin/classes/[0-9]+$

Expected outcome: row 49 (confirm path). Max execution time: 5s. Expected result source: results_report.md row 49.


FT-P-27: Destructive policy — dialog before request for every destructive surface

Traces to: AC-14 Profile: fast

Input data: each destructive surface listed in _docs/ui_design/ (class delete, user deactivate, dataset bulk-overwrite, irreversible bulk) — results_report.md row 51.

Steps:

Step Consumer Action Expected System Response
1 For each surface, trigger the destructive action <ConfirmDialog> opens BEFORE any HTTP fires
2 Inspect request log up to dialog open empty for the destructive route

Expected outcome: row 51. Max execution time: 10s. Expected result source: results_report.md row 51.


FT-P-28: ConfirmDialog has dialog + modal a11y attributes

Traces to: AC-15 Profile: fast

Input data: render <ConfirmDialog open>results_report.md row 52.

Steps:

Step Consumer Action Expected System Response
1 Open the dialog root element rendered
2 Inspect root attributes role="dialog" and aria-modal="true"

Expected outcome: row 52. Max execution time: 1s. Expected result source: results_report.md row 52.


FT-P-29: ConfirmDialog focus trap (Tab cycles inside)

Traces to: AC-15 Profile: fast

Input data: open dialog and Tab through — results_report.md row 53.

Steps:

Step Consumer Action Expected System Response
1 Open dialog; focus on first focusable element initial focus inside dialog
2 Tab repeatedly focus stays inside the dialog subtree; first ↔ last cycles

Expected outcome: row 53. Max execution time: 3s. Expected result source: results_report.md row 53.


FT-P-30: Header flight dropdown closed-state a11y

Traces to: AC-16 Profile: fast

Input data: render <Header> with dropdown closed — results_report.md row 55.

Steps:

Step Consumer Action Expected System Response
1 Mount Header dropdown trigger rendered
2 Inspect attributes role="combobox", aria-expanded="false", aria-haspopup="listbox"

Expected outcome: row 55. Max execution time: 1s. Expected result source: results_report.md row 55.


FT-P-31: Header flight dropdown open-state a11y

Traces to: AC-16 Profile: fast

Input data: open the dropdown — results_report.md row 56.

Steps:

Step Consumer Action Expected System Response
1 Click trigger dropdown opens
2 Inspect aria-expanded="true"; outside-click handler attached (was NOT attached while closed — observable via DOM event listener count or via behavior under a synthetic outside-click while closed: no-op)

Expected outcome: row 56. Max execution time: 3s. Expected result source: results_report.md row 56.


FT-P-32: ProtectedRoute spinner a11y

Traces to: AC-17 Profile: fast

Input data: render <ProtectedRoute> in loading state — results_report.md row 58.

Steps:

Step Consumer Action Expected System Response
1 Mount in loading state spinner rendered
2 Inspect role="status" + non-empty accessible label

Expected outcome: row 58. Max execution time: 1s. Expected result source: results_report.md row 58.


FT-P-33: ProtectedRoute timeout fallback after 10 s

Traces to: AC-17 Profile: fast (with fake timers)

Input data: loading exceeds the timeout — results_report.md row 59.

Steps:

Step Consumer Action Expected System Response
1 Mount loading; advance fake time to 10 s timer fires
2 Inspect DOM fallback (retry CTA or error message) present; spinner unmounted

Expected outcome: row 59. Max execution time: 3s. Expected result source: results_report.md row 59.


FT-P-34: Browser-support smoke (Chromium + Firefox)

Traces to: AC-18 Profile: e2e (manual smoke per AC — no enforcement today)

Input data: render /flights, /annotations, /dataset on each supported browser — results_report.md row 60.

Steps:

Step Consumer Action Expected System Response
1 Navigate to each route page renders
2 Inspect console + DOM landmarks no console errors; main landmark roles present

Expected outcome: row 60. Max execution time: 30s per browser. Expected result source: results_report.md row 60.


FT-P-35: Mobile bottom-nav variant at 480 px

Traces to: AC-19 Profile: e2e

Input data: render at viewport 480×800 — results_report.md row 61.

Steps:

Step Consumer Action Expected System Response
1 Resize browser to 480×800 layout reflows
2 Inspect DOM bottom-nav variant present; top-bar variant absent

Expected outcome: row 61. Max execution time: 5s. Expected result source: results_report.md row 61.


FT-P-36: Desktop top-bar variant at 1024 px

Traces to: AC-19 Profile: e2e

Input data: render at viewport 1024×768 — results_report.md row 62.

Steps:

Step Consumer Action Expected System Response
1 Resize to 1024×768 reflow
2 Inspect DOM top-bar present; bottom-nav absent

Expected outcome: row 62. Max execution time: 5s. Expected result source: results_report.md row 62.


FT-P-37: Panel-width persistence — debounced PUT on resize end

Traces to: AC-21 Profile: fast — quarantined until useResizablePanel writes back (Step 4)

Input data: drag divider — results_report.md row 64.

Steps:

Step Consumer Action Expected System Response
1 Simulate drag-end on a useResizablePanel divider PUT observed within 1 s (debounce-aware)
2 Inspect URL + body matches /api/annotations/settings/user; body contains panelWidths key

Expected outcome: row 64. Max execution time: 5s. Expected result source: results_report.md row 64.


FT-P-38: Panel-width rehydration on reload

Traces to: AC-21 Profile: e2e — quarantined until Step 4

Input data: reload after resize — results_report.md row 65.

Steps:

Step Consumer Action Expected System Response
1 Resize panels; capture widths W widths observable
2 Reload app boots
3 Re-measure widths equal W within ± 1 px

Expected outcome: row 65. Max execution time: 15s. Expected result source: results_report.md row 65.


FT-P-39: Manual bounding-box draw on <CanvasEditor>

Traces to: AC-35 Profile: fast (synthetic pointer events)

Input data: mousedown(x1,y1) → mousemove(x2,y2) → mouseup with selectedClassNum = C, photoMode = Presults_report.md row 73.

Steps:

Step Consumer Action Expected System Response
1 Fire pointer sequence on the canvas one new local detection appended
2 Inspect detection classNum == C + P; x,y,w,h normalised within ± 1 px of the drawn rect

Expected outcome: row 73. Max execution time: 1s. Expected result source: results_report.md row 73.


FT-P-40: 8-handle bbox resize

Traces to: AC-36 (a) Profile: fast

Input data: drag each of the 8 handles in turn — results_report.md row 74.

Steps:

Step Consumer Action Expected System Response
1 For h ∈ {NW,N,NE,W,E,SW,S,SE}: mousedown on h, drag (dx,dy), mouseup bbox edges adjacent to h move; opposite edges unchanged
2 Force the drag past zero size resulting bbox has w>0 and h>0 (clamped)

Expected outcome: row 74. Max execution time: 5s. Expected result source: results_report.md row 74.


FT-P-41: Ctrl+click multi-select on canvas

Traces to: AC-36 (b) Profile: fast

Input data: Ctrl+click an unselected bbox; second Ctrl+click on the same — results_report.md row 75.

Steps:

Step Consumer Action Expected System Response
1 Have one bbox already selected initial selection = {A}
2 Ctrl+click bbox B selection set = {A, B}; prior selection preserved
3 Ctrl+click bbox B again selection set = {A} (toggle off)

Expected outcome: row 75. Max execution time: 1s. Expected result source: results_report.md row 75.


FT-P-42: Ctrl+wheel zoom-around-cursor

Traces to: AC-36 (c) Profile: fast

Input data: Ctrl+wheel at (cx, cy) — results_report.md row 76.

Steps:

Step Consumer Action Expected System Response
1 Record the world coordinate at (cx, cy) before zoom W₀
2 Dispatch Ctrl+wheel zoom level changes
3 Inverse-map (cx, cy) → world coordinate after zoom equals W₀ within ± 1 viewport px

Expected outcome: row 76. Max execution time: 1s. Expected result source: results_report.md row 76.


FT-P-43: Ctrl+drag pan on empty canvas

Traces to: AC-36 (d) Profile: fast

Input data: Ctrl+drag on empty area — results_report.md row 77.

Steps:

Step Consumer Action Expected System Response
1 Ctrl+drag from (x1,y1) by (dx,dy) viewport origin translates by (-dx,-dy) within ± 1 px
2 Inspect bbox state no bbox created or modified

Expected outcome: row 77. Max execution time: 1s. Expected result source: results_report.md row 77.


FT-P-44: DetectionClasses loads from /api/annotations/classes

Traces to: AC-37 (load path) Profile: fast

Input data: mount with a successful response of N classes — results_report.md row 78.

Steps:

Step Consumer Action Expected System Response
1 Mount <DetectionClasses> GET observed
2 Inspect rendered entries N entries; active-mode filter applied; no fallback indicator

Expected outcome: row 78. Max execution time: 3s. Expected result source: results_report.md row 78.


FT-P-45: Class hotkey 19 selects classes[(key-1) + P]

Traces to: AC-37 (hotkey path) Profile: fast (synthetic keydown on window)

Input data: keys '1'..'9', photoMode = P, classes mode-ordered — results_report.md row 79.

Steps:

Step Consumer Action Expected System Response
1 For each key k ∈ {1..9}: dispatch keydown onSelect fires once
2 Inspect arg class.id == classes[(k-1) + P].id
3 Inspect rendered list element label index i+1 equals k

Expected outcome: row 79. Max execution time: 3s. Expected result source: results_report.md row 79.


FT-P-46: Class click path

Traces to: AC-37 (click path) Profile: fast

Input data: click a class entry — results_report.md row 80.

Steps:

Step Consumer Action Expected System Response
1 Click entry for class C onSelect fires once with C.id

Expected outcome: row 80. Max execution time: 1s. Expected result source: results_report.md row 80.


FT-P-47: Fallback class list on API empty/5xx

Traces to: AC-37 (fallback) Profile: fast

Input data: GET /api/annotations/classes returns [] (or 5xx) — results_report.md row 81.

Steps:

Step Consumer Action Expected System Response
1 Stub empty response fallback list rendered
2 Inspect rendered IDs FALLBACK_CLASS_NAMES × 3 PhotoMode offsets (set equals [0..N-1, 20..20+N-1, 40..40+N-1])

Expected outcome: row 81. Max execution time: 3s. Expected result source: results_report.md row 81.


FT-P-48: PhotoMode switch — mode set + filter

Traces to: AC-38 (mode set + filter) Profile: fast

Input data: click Winter while photoMode = 0results_report.md row 82.

Steps:

Step Consumer Action Expected System Response
1 Click Winter onPhotoModeChange(20) fires once
2 Inspect rendered class list filtered to entries with photoMode == 20

Expected outcome: row 82. Max execution time: 1s. Expected result source: results_report.md row 82.


FT-P-49: PhotoMode auto-select when prior class no longer valid

Traces to: AC-38 (auto-select) Profile: fast

Input data: switch mode where previously selected class is not in the new filtered set — results_report.md row 83.

Steps:

Step Consumer Action Expected System Response
1 Pre-select a Regular class initial state
2 Switch to Night (photoMode = 40) onSelect fires once with modeClasses[0].id

Expected outcome: row 83. Max execution time: 1s. Expected result source: results_report.md row 83.


FT-P-50: yoloId on the wire — classNum == classId + photoModeOffset

Traces to: AC-38 (wire) Profile: fast

Input data: save annotation after drawing with C, P — results_report.md row 84.

Steps:

Step Consumer Action Expected System Response
1 Draw a bbox with selectedClassNum = C, photoMode = P local detection appended
2 Save POST observed
3 Inspect new detection in body classNum == C + P

Expected outcome: row 84. Max execution time: 3s. Expected result source: results_report.md row 84.


FT-P-51: Tile-split endpoint contract

Traces to: AC-39 (endpoint) Profile: fast — quarantined until the split action surfaces on the dataset page

Input data: click Split tile on a dataset item — results_report.md row 85.

Steps:

Step Consumer Action Expected System Response
1 Trigger Split tile one POST observed
2 Inspect URL + status ^/api/annotations/dataset/[0-9]+/split$; 200 JSON

Expected outcome: row 85. Max execution time: 5s. Expected result source: results_report.md row 85.


FT-P-52: YOLO label parser — happy path

Traces to: AC-39 (parser happy) Profile: fast

Input data: isSplit: true, splitTile: "3 0.5 0.5 0.2 0.2"results_report.md row 86.

Steps:

Step Consumer Action Expected System Response
1 Pass the item through the parser/render path no exception
2 Inspect parsed result {classNum:3, cx:0.5, cy:0.5, w:0.2, h:0.2}

Expected outcome: row 86. Max execution time: 1s. Expected result source: results_report.md row 86.


FT-P-53: DatasetItem.isSplit is honored on the dataset list path

Traces to: AC-39 (dataset list) Profile: fast

Input data: GET /api/annotations/dataset response contains isSplit: trueresults_report.md row 88.

Steps:

Step Consumer Action Expected System Response
1 Stub the response render no crash
2 Inspect that the field is read into the item state (DOM marker present for split items) field present

Expected outcome: row 88. Max execution time: 3s. Expected result source: results_report.md row 88.


FT-P-54: Tile auto-zoom viewport matches tile rect

Traces to: AC-40 (viewport) Profile: fast — quarantined (UX missing today)

Input data: open a splitTile-bearing annotation — results_report.md row 89.

Steps:

Step Consumer Action Expected System Response
1 Double-click the annotation in <AnnotationsSidebar> <CanvasEditor> opens to that annotation
2 Inspect viewport rect equals tile rect within ± 1 px per edge

Expected outcome: row 89. Max execution time: 5s. Expected result source: results_report.md row 89.


FT-P-55: Tile-zoom indicator visible while active

Traces to: AC-40 (indicator) Profile: fast — quarantined

Input data: tile zoom active → cleared — results_report.md row 90.

Steps:

Step Consumer Action Expected System Response
1 Activate tile zoom indicator icon/badge present in canvas chrome
2 Clear tile zoom indicator removed

Expected outcome: row 90. Max execution time: 3s. Expected result source: results_report.md row 90.


Negative Scenarios

FT-N-01: Overlay annotation below the lower bound is NOT rendered

Summary: At currentTime = T, an annotation at T - 60 ms (outside [-50, +150] ms) is excluded. Traces to: AC-28 Profile: fast

Input data: currentTime = T, annotation at T - 60 msresults_report.md row 30.

Steps:

Step Consumer Action Expected System Response
1 Render with the off-window annotation overlay NOT rendered

Expected outcome: row 30 (absent). Max execution time: 1s. Expected result source: results_report.md row 30.


FT-N-02: Overlay annotation above the upper bound is NOT rendered

Traces to: AC-28 Profile: fast

Input data: currentTime = T, annotation at T + 160 msresults_report.md row 31.

Steps:

Step Consumer Action Expected System Response
1 Render overlay NOT rendered

Expected outcome: row 31. Max execution time: 1s. Expected result source: results_report.md row 31.


FT-N-03: Authenticated non-admin navigating to /admin is redirected to /flights

Traces to: AC-22 Profile: e2e — quarantined until role-gate is added (Step 4 / Step 8)

Input data: log in as op_alice (Operator) → navigate /adminresults_report.md row 08.

Steps:

Step Consumer Action Expected System Response
1 Authenticate as Operator session active
2 Navigate to /admin redirect
3 Inspect final URL + tree URL is /flights; <AdminPage> NOT mounted

Expected outcome: row 08. Max execution time: 10s. Expected result source: results_report.md row 08.


FT-N-04: Unauthenticated user navigating to /admin is redirected to /login

Traces to: AC-22 Profile: e2e

Input data: no session → navigate /adminresults_report.md row 09.

Steps:

Step Consumer Action Expected System Response
1 Clear cookies and bearer unauthenticated
2 Navigate to /admin redirect
3 Inspect final URL /login

Expected outcome: row 09. Max execution time: 10s. Expected result source: results_report.md row 09.


FT-N-05: Authenticated user without SETTINGS permission navigating to /settings

Traces to: AC-22 Profile: e2e — quarantined

Input data: op_bob (no SETTINGS) → /settingsresults_report.md row 10.

Steps:

Step Consumer Action Expected System Response
1 Authenticate as op_bob session active
2 Navigate to /settings redirect
3 Inspect final URL + tree /flights; <SettingsPage> NOT mounted

Expected outcome: row 10. Max execution time: 10s. Expected result source: results_report.md row 10.


FT-N-06: Upload of 501 MB file surfaces a user-visible 413 error

Traces to: AC-10 Profile: e2e

Input data: 501 MB synthetic file via dropzone — results_report.md row 39.

Steps:

Step Consumer Action Expected System Response
1 Drop 501 MB onto <MediaList> upload request issued
2 nginx returns 413 rejected upstream
3 Inspect UI user-visible error containing the i18n "file too large" string; NO alert()

Expected outcome: row 39. Max execution time: 30s. Expected result source: results_report.md row 39.


FT-N-07: Class-delete Cancel path — NO DELETE request issued

Traces to: AC-14, AC-30 Profile: fast

Input data: click Delete → click Cancel — derived from results_report.md row 49.

Steps:

Step Consumer Action Expected System Response
1 Click Delete <ConfirmDialog> mounts
2 Click Cancel dialog unmounts
3 Inspect network log zero DELETE requests fired

Expected outcome: cancel branch of row 49. Max execution time: 3s. Expected result source: results_report.md row 49.


FT-N-08: Escape on <ConfirmDialog> cancels — no destructive request

Traces to: AC-15 Profile: fast

Input data: open dialog → press Escape — results_report.md row 54.

Steps:

Step Consumer Action Expected System Response
1 Open dialog mounted
2 Dispatch keydown Escape dialog unmounts; cancel callback fires exactly once
3 Inspect network log no destructive HTTP fired

Expected outcome: row 54. Max execution time: 1s. Expected result source: results_report.md row 54.


FT-N-09: Header dropdown Escape — close + handler detached

Traces to: AC-16 Profile: fast

Input data: open dropdown → press Escape — results_report.md row 57.

Steps:

Step Consumer Action Expected System Response
1 Open dropdown open
2 Dispatch Escape dropdown closes
3 Inspect attributes aria-expanded="false"; outside-click handler detached

Expected outcome: row 57. Max execution time: 1s. Expected result source: results_report.md row 57.


FT-N-10: Malformed YOLO label surfaces a user-visible error (no silent swallow, no NaN render)

Traces to: AC-39 Profile: fast

Input data: isSplit: true, splitTile: "garbage"results_report.md row 87.

Steps:

Step Consumer Action Expected System Response
1 Stub the item; render parse attempted
2 Inspect UI error toast / inline error present
3 Inspect rendered overlay no NaN values, no tile region rendered

Expected outcome: row 87. Max execution time: 3s. Expected result source: results_report.md row 87.


FT-N-11: Numeric field with empty input — no silent zero

Traces to: AC-26 Profile: fast — quarantined until form hygiene fix (Step 4)

Input data: clear a 09_settings numeric input and submit — results_report.md row 66.

Steps:

Step Consumer Action Expected System Response
1 Clear the field form state observed
2 Click Save form blocks save
3 Inspect network log no PUT fires
4 Inspect DOM submit disabled OR explicit validation error rendered

Expected outcome: row 66. Max execution time: 3s. Expected result source: results_report.md row 66.


FT-N-12: Numeric field with non-numeric input — rejected

Traces to: AC-26 Profile: fast — quarantined

Input data: type "abc" and submit — results_report.md row 67.

Steps:

Step Consumer Action Expected System Response
1 Type non-numeric value state updates
2 Click Save rejected
3 Inspect validation error rendered; no PUT

Expected outcome: row 67. Max execution time: 3s. Expected result source: results_report.md row 67.


FT-N-13: Settings save with 500 response — saving flag reset; error surfaced

Traces to: AC-27 Profile: fast — quarantined until try/finally fix (Step 4)

Input data: upstream PUT returns 500 — results_report.md row 68.

Steps:

Step Consumer Action Expected System Response
1 Trigger save request issued
2 Stub responds 500 within 2 s failure handled
3 Inspect within ≤ 2 000 ms toast / inline error present; saving === false; no navigation away

Expected outcome: row 68. Max execution time: 5s. Expected result source: results_report.md row 68.


FT-N-14: Settings save with network failure — try/finally state reset

Traces to: AC-27 Profile: fast — quarantined

Input data: PUT throws (network drop) — results_report.md row 69.

Steps:

Step Consumer Action Expected System Response
1 Trigger save request issued
2 Stub throws rejection handled
3 Inspect saving === false; user-visible error present

Expected outcome: row 69. Max execution time: 5s. Expected result source: results_report.md row 69.


FT-N-15: MediaType magic-literal / magic-string hygiene

Traces to: AC-29 Profile: static

Input data: regex sweep of src/ for mediaType\s*[!=]==?\s*[0-9] and mediaType\s*[!=]==?\s*['"]results_report.md rows 20, 21.

Steps:

Step Consumer Action Expected System Response
1 Run the two regexes results captured
2 Inspect match_count == 0 for both

Expected outcome: rows 20, 21. Max execution time: 5s. Expected result source: results_report.md rows 20, 21.


Notes carried into Phase 3

  • All tests tagged quarantined correspond to features either pending a Step 4 fix (e.g., AC-13 i18n detector, AC-21 panel persistence, AC-22 role-gate, AC-26/27 form hygiene, AC-39 split surface, AC-40 tile zoom) or pending Phase B implementation (AC-11 bundle gate, AC-24 SSE refresh, AC-25 async video, AC-40 tile zoom). The test is written so it activates the day the implementation lands; Phase 3 will surface them for downgrade or accept.
  • FT-P-13 / FT-P-14 / FT-P-37 / FT-P-38 / FT-P-51 / FT-P-54 / FT-P-55 / FT-N-03 / FT-N-05 / FT-N-11 / FT-N-12 / FT-N-13 / FT-N-14 are all on the quarantine list above.
  • Tests that depend on the enum-spec numeric snapshot (AC-04 rows 15-17) are blocked on populating enum_spec_snapshot.json from the suite spec — Phase 3 surfaces this as the blocking input.
  • AC-37 row 79 depends on backend ordering returning [0..N-1, 20..20+N-1, 40..40+N-1]. If the seed reveals otherwise, this test fails — fix can land either side per data_parameters.md.