Skip to content

test(frontend): add browser tests, starting with the export menu - #365

Merged
thcp merged 1 commit into
mainfrom
feat/frontend-e2e-tests
Aug 12, 2026
Merged

test(frontend): add browser tests, starting with the export menu#365
thcp merged 1 commit into
mainfrom
feat/frontend-e2e-tests

Conversation

@thcp

@thcp thcp commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Closes #339.

CI ran exactly one check on static/js: a syntax parse. This adds browser tests that drive the real UI against the real backend, starting with the area that has already cost the most.

Setup

tests/e2e/serve.sh seeds a throwaway jobs directory with one finished track and execs uvicorn against it, with every data path (JOBS_DIR, DATA_DIR, cache, logs, models, downloads) redirected into it. A run cannot read or touch a developer's real library. Only the separation pipeline is skipped: the endpoints, the registry, and the Range requests for stems are real.

Two fixture details are load-bearing, and I got both wrong first:

  • The sidebar renders from the library store, not /api/jobs. A job that exists on disk but is absent from the store is invisible in the UI, so a test that clicks nothing passes for the wrong reason.
  • stubTauri installs a controllable window.__TAURI__ so the desktop code path actually runs. This is the whole point: [Bug]: Unable to export stems again after exporting stems without restarting the program #335 was invisible in a browser, because there the synthetic <a>.click() closes the chip panel before the busy state is applied and the bug hides. The stub also leaves save_audio_file pending until the test settles it, so the busy state machine is driven rather than raced.

Coverage

Nine tests, all four defects from #335 and #337:

test defect
every row usable after an export #335, the stuck row
a second export still works #335, the user-visible symptom
busy state waits for the save, not a timer #337
a failed export says so and recovers #337
failure does not offer "Try again" #337, it navigated to the URL import field
browser-mode rows recover the fire-and-forget path
format radio group, MP4 hidden without video, menu inert while busy supporting behaviour

Do these tests actually catch anything?

Verified by reintroducing each defect and watching the suite fail.

reintroduced result
reset clears only visible rows 3 tests fail
reset on a fixed timer 1 test fails
export error offers "Try again" 1 test fails

The first one is sharper than the issue describes: flashBusy closes the panel, so by reset time every row has offsetParent === null and a visibility-filtered clear clears nothing. That is why the row stayed disabled for the rest of the session.

Runtime is 11s locally, 9 tests, one worker.

Deferred, still open on #339

  • MP4 format switching. The video format is hidden unless the track has one, so covering it needs a video fixture, which is its own piece of work. What is here pins that MP4 is not offered for audio-only tracks.
  • Beat grid editor and transport basics.

Notes

  • New root package.json; @playwright/test is the only dependency and it is dev-only. The app itself still has no build step and no runtime dependencies.
  • New frontend-e2e CI job, with the Playwright report uploaded on failure.
  • tests/test_stems_api.py::test_all_stems_zip_ogg fails identically on clean main (local ffmpeg has no libvorbis; the skip guard only checks that ffmpeg exists). Unrelated, untouched. 539 passed otherwise.

Implements #339. CI ran one check on static/js -- a syntax parse -- so any
behavioural regression shipped unnoticed until a user hit it. #335 is the
case in point: "Export All Stems" became permanently unclickable after a
single export, for every track, until the app restarted. It shipped in
alpha 15 and a user found it.

Setup is Playwright against the real backend. tests/e2e/serve.sh seeds a
throwaway jobs directory with one finished track and execs uvicorn
against it, with every data path redirected, so a run cannot read or
touch a developer's library. Only the separation pipeline is skipped;
the endpoints, the registry and the Range requests for stems are real.

Two details in the fixtures are load-bearing, both learned by getting
them wrong first:

- The sidebar renders from the library store, not /api/jobs. A job on
  disk but absent from the store is invisible in the UI, and a test that
  clicks nothing passes for the wrong reason.
- stubTauri installs a controllable window.__TAURI__ so the desktop code
  path runs. This is the point of the exercise: #335 was invisible in a
  browser, because there the synthetic <a>.click() closes the chip panel
  before the busy state is applied and the bug hides. The stub also
  leaves save_audio_file pending until the test settles it, so the busy
  state machine is driven rather than raced.

Nine tests cover all four defects from #335 and #337: rows re-enabled
after an export in both host modes, a second export still working, the
busy state waiting on the save rather than a timer, failures surfacing
and recovering, and export errors not offering a "Try again" that sends
the user to the URL import field.

Verified by reintroducing each defect and watching the suite fail:
clearing only the visible rows on reset fails 3 tests (the panel is
closed by then, so a visibility-filtered clear clears nothing), the
fixed-timer reset fails 1, and the retry button fails 1.

MP4 format switching is only partly covered. The video format is hidden
unless the track has one, and a video fixture is its own piece of work,
so what is here pins that MP4 is not offered for audio-only tracks.
Beat grid and transport coverage remain open on #339.

Closes #339
@thcp
thcp marked this pull request as ready for review August 12, 2026 19:18
@thcp
thcp merged commit 1e1cf0b into main Aug 12, 2026
10 checks passed
@thcp
thcp deleted the feat/frontend-e2e-tests branch August 12, 2026 19:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No automated tests for frontend behaviour

1 participant