Skip to content

Test gap (#878 follow-up): SPA aim-axis wizard state machine (~180 LOC) has no Playwright or integration coverage #884

@SlyWombat

Description

@SlyWombat

Summary

The #878 SPA aim-axis wizard shipped a ~180 LOC client-side state machine in desktop/shared/spa/js/setup-ui.js (_gyroAimWizardOpen at :2910, _gyroAimWizardRender at :2928, capture/retry/cancel/submit handlers at :2967-3066). Server math _aim_wizard_compute (parent_server.py:12330-12486) is well-covered from #826 reuse, but the SPA wizard itself has no Playwright or integration test. The audit surfaced this on 2026-05-11.

The closing comment on #878 explicitly notes "Math layer reused (no new tests)." Reused math is fine to leave; the new UI layer is not.

Why it matters

The wizard has multiple independent failure modes the operator could hit:

  • Step-state corruption (Retry from step 2 → step 0 → forgets earlier captures vs preserves them — current code keeps state in a closure, but no regression test pins the contract)
  • Diagnostic endpoint returns rawQuat=null (gyro not streaming yet) — UI surfaces "No quaternion received…" at :2996 but no test verifies the message renders, the retry path is reachable, and Submit is correctly disabled
  • Validation error rendering (r.err → modal at :3034-3046) — the four error codes from _aim_wizard_compute (bad_quaternion / insufficient_pitch/yaw / degenerate_axes / non_orthogonal_frame) need to surface specific strings, currently untested
  • Double-click on Capture fires two ra('GET'…) to /diagnostic — second can land first and capture a frame from a different orientation. No debounce, no race test.
  • Success path renders derived forward + up axes at :3050-3066 — no test verifies the numbers are correctly extracted from the server response

Proposed tests

Unit-style (tests/test_878_spa_wizard.py, Flask test_client + jsdom)

Drive the wizard from the server's perspective:

def test_aim_wizard_happy_path_persists_axes():
    # Pre-register gyro-192.168.10.250 with last_quat_world set
    # POST {poses: [neutral, pitch_forward, yaw_left]} → 200
    # GET /api/remotes/<id> → forward_local + up_local match cross-product math
def test_aim_wizard_validation_errors_each_case():
    # 4 cases, one per error code; assert err string + detail returned
def test_aim_wizard_unknown_remote_auto_registers():
    # POST with deviceId=gyro-1.2.3.4 (not pre-registered)
    # → 200, remote auto-registered, axes persisted
    # (Note: this is also the regression for #(new KIND_GYRO bug issue))

Playwright (tests/test_878_spa_wizard_ui.py, headless Chromium)

Build on tests/capture_manual_screenshots_v1_7_119.py for browser lifecycle. Drive the actual SPA:

def test_calibrate_button_only_visible_for_gyro_fixture():
    # Open Setup → DMX fixture edit modal → no Calibrate button
    # Open Setup → Gyro fixture edit modal → Calibrate button visible
def test_wizard_three_step_navigation():
    # Click Calibrate → step 0 visible (Neutral prompt)
    # Click Capture → step 1 visible (Pitch forward prompt)
    # Click Capture → step 2 visible (Yaw left prompt)
    # Click Capture → submit fires → success modal with axes
def test_wizard_retry_per_step():
    # Capture step 0 → step 1 → click Retry-last → back to step 0
    # Capture step 0 again → step 1, verify captures buffer was reset correctly
def test_wizard_cancel_at_any_step():
    # Open wizard, capture step 0, click Cancel → modal closes
    # Open again → state is fresh (no leaked step counter)
def test_wizard_renders_no_quat_error():
    # Stop the gyro orient stream → diagnostic.rawQuat=null
    # Click Capture → "No quaternion received from the gyro yet" surfaced
def test_wizard_double_click_debounce():
    # Click Capture twice within 100 ms → only one /diagnostic GET fires
    # (Regression for the double-fire concern from the audit)
def test_wizard_validation_error_rendering():
    # Capture three poses with axes too parallel (degenerate)
    # Submit → modal shows "those gestures looked the same, please retry"
    # Retry-from-start button visible and resets the wizard
def test_wizard_success_displays_derived_axes():
    # Capture three valid orthogonal poses → success modal
    # Assert displayed forward_local + up_local strings match server response

Add to manual smoke-test list

Append to docs/help-smoke-test.md (created in #881 if/when it lands, or as a new file):

[ ] Setup → Gyro fixture → Calibrate button visible
[ ] Click Calibrate → three-step wizard renders
[ ] Capture each pose → final modal shows derived forward + up axes
[ ] Re-open Setup → Gyro fixture → button now says "Re-calibrate"
[ ] Force a validation failure (capture three near-parallel poses)
    → specific error message + Retry-from-start works

Acceptance

  • tests/test_878_spa_wizard.py exists with the unit-style cases above.
  • tests/test_878_spa_wizard_ui.py exists with the Playwright cases above; all pass on a CI runner.
  • Smoke-test checklist updated.
  • No regression in existing tests/test_orient_contract.py:651-661 or tests/test_parent.py:3490-3530 (which exercise the underlying endpoint).

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions