feedBack/tests/browser
ChrisBeWithYou fef870047b
fix(player): reliable Escape "Back" + resumable, optionally-confirmed song exit (#619)
* fix(player): make Escape a reliable Back; resumable + optionally-confirmed song exit

Escape didn't always leave a song: clicking a transport control (play/FF/RW/
restart) left that <button> focused, and _shortcutDispatchBlocked() bails the
shortcut dispatcher for any focused INPUT/SELECT/TEXTAREA/BUTTON — so the
player-scope Escape=Back shortcut never fired until the user clicked empty
canvas to blur the control. Space already had a player-screen carve-out (#593);
Escape did not. That asymmetry was the bug.

Phase 1 — focus fix: generalize the Space carve-out in _shortcutDispatchBlocked
to Escape, on the player AND settings screens (both register Escape=Back;
settings had the identical latent bug). The earlier guards still win: text
inputs are exempted first, the Section Practice popover already claims Escape,
and a true modal (role=dialog aria-modal=true / .feedBack-modal) still traps it.
Plugins' player-scope Escape shortcuts are fixed identically.

Phase 2 — resume: leaving the player snapshots {song, arrangement, position,
speed} to localStorage; a non-blocking "Resume practice" pill offers it back on
the next non-player screen / next launch. playSong() gains a {resume} option
that restores speed + seeks to the saved position on song:ready instead of the
normal autostart. Conservative (ignores <3s / near-end), cleared on natural
song-end and once consumed, expires after 24h.

Phase 3 — opt-in "Ask before leaving a song" (Gameplay tab, default OFF). A
true-modal confirm with monotonic Escape (the second Escape leaves) and
Space/Enter = Leave. The player Escape shortcut and the v3 close button route
through window.requestExitSong(); auto-exit on song-end and a results screen's
own Close stay unguarded.

Design rationale: a multi-seat design charrette (engagement, learning-design,
operability, codebase-reality) — leaving a song should be reliable and
recoverable, not gated; the confirm is opt-in only.

Tests: tests/browser/{keyboard-shortcuts,resume-session,exit-confirm}.spec.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF

* test(browser): suppress first-run onboarding in keyboard/resume/exit specs

The first-run onboarding overlay (#v3-onboarding) is a modal that intercepts
pointer/keyboard events; on a fresh profile it covers the player and breaks any
test that presses Escape or clicks. Stub GET /api/profile to an onboarded
profile in each beforeEach so the app behaves like a returning user (the state
these tests assume).

Also tighten the Section Practice Escape test to assert the guarantee the fix
actually provides — Escape does not exit the song while the popover is open (the
line-447 guard wins over the carve-out) — rather than asserting the popover's
own close handler fires, which isn't wired for a synthetic bar.

Verified locally against a worktree server (Chromium): all 16 new specs pass
(5 Escape + 6 resume + 5 exit-confirm) plus the existing #593 Space tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF

* fix(player): exit-confirm — Escape cancels back to song, pause on open/resume on stay

Refinements from tester feedback on the exit-confirm (default stays OFF):

- Escape on the open prompt now = Stay (dismiss + return to the song), matching
  every other modal and the generic _confirmDialog (Esc=cancel). A second
  Escape therefore returns to the song instead of leaving it. Leaving stays the
  explicit, default-focused "Leave" button, so Space/Enter/click = "just get me
  out" (the OP's "Space always hits leave").
- Opening the prompt PAUSES the song (via the canonical togglePlay path, HTML5
  + _juceMode) so it isn't running/being scored behind the modal; Stay resumes
  exactly what we paused. Guards: cancel any count-in on open; resume only if we
  paused (wasPlaying), only if still the same live song on the player
  (_audioSeekGen unchanged), and never auto-resume a song the user had paused.
- Trap Tab inside the dialog; backdrop click was already Stay.

Specs: exit-confirm.spec.ts updated — the monotonic "second Escape leaves" test
becomes "second Escape stays", plus a backdrop-click-stays test. The audio
pause/resume itself is verified manually on web + desktop (the mock song has no
backing track); these specs lock the navigation + keyboard semantics.

NOTE: the pause/resume adds a new pause→resume cycle on the desktop JUCE
transport (known play/pause-desync path) — smoke-test on the desktop build
before merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF

* fix(player): accurate exit-confirm copy + keep resume snapshot on failed load

Two review follow-ups on the Escape/resume/confirm work:

- Settings copy said "a second Escape (or Space/Enter) still leaves",
  but Escape dismisses the confirm (Stay) like every other modal — only
  Space/Enter/Leave exit. Corrected the Gameplay-tab description so it
  matches the implementation (and the committed exit-confirm specs).

- resumeLastSession() cleared the snapshot BEFORE awaiting playSong(), so
  a transient load/connect failure permanently lost the Resume pill with
  no retry. Clear only after the load resolves; on failure keep the
  snapshot (and drop the pending in-memory resume) so the pill re-offers
  it on the next non-player screen.

All 16 Escape/resume/exit-confirm Playwright specs still pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: byrongamatos <xasiklas@gmail.com>
2026-06-28 12:32:33 +02:00
..
audio-session-compat.spec.ts rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
audio-session.spec.ts rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
basic-load.spec.ts rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
capability-inspector.spec.ts Clean release snapshot 2026-06-16 18:47:13 +02:00
check-errors.spec.ts rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
default-arrangement-pin.spec.ts rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
exit-confirm.spec.ts fix(player): reliable Escape "Back" + resumable, optionally-confirmed song exit (#619) 2026-06-28 12:32:33 +02:00
highway-3d-lefty.spec.ts rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
keyboard-shortcuts.spec.ts fix(player): reliable Escape "Back" + resumable, optionally-confirmed song exit (#619) 2026-06-28 12:32:33 +02:00
progress-shop.spec.ts rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
README.md rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
resume-session.spec.ts fix(player): reliable Escape "Back" + resumable, optionally-confirmed song exit (#619) 2026-06-28 12:32:33 +02:00
settings-tabbed.spec.ts feat(v3): tabbed, card-row settings page + per-plugin settings category (#584) 2026-06-23 18:06:46 +02:00
v3-section-map-leftmost-click.spec.ts fix(v3): keep Section Map's leftmost section clickable under the rail catcher (#617) 2026-06-27 21:07:44 +02:00
v3-tree-select.spec.ts Fix list/tree view: select mode, parts visibility, song actions (#585) 2026-06-24 21:49:02 +02:00
wake-lock.spec.ts rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00

Browser Tests

This directory contains Playwright browser tests for FeedBack keyboard shortcuts.

Prerequisites

  1. FeedBack web server: Tests need the server reachable at http://localhost:8000. Playwright auto-starts it via webServer.command in playwright.config.ts, so manual startup is optional. Start it manually if you want to debug the server, run tests outside Playwright, or skip the per-run boot delay:

    LIBRARY_PATH=/path/to/your/library docker compose up -d
    

    Playwright reuses an already-running server locally (reuseExistingServer: true).

  2. Node.js installed: Required for running Playwright tests

    node --version  # Should be v18 or higher
    

Installation

Install dependencies:

npm install

Install Playwright browsers:

npm run install:playwright

Running Tests

Run all tests:

npm test

Run tests in headed mode (watch the browser):

npm run test:headed

Debug tests with interactive inspector:

npm run test:debug

Test Files

  • basic-load.spec.ts - Basic app load and shortcut registry availability
  • check-errors.spec.ts - Check for console errors
  • keyboard-shortcuts.spec.ts - Comprehensive keyboard shortcut tests

Writing Tests

Tests use Playwright's test API. Example:

import { test, expect } from '@playwright/test';

test('my test', async ({ page }) => {
  await page.goto('/');
  await page.waitForSelector('.screen.active');
  
  // Interact with the page
  await page.keyboard.press('?');
  
  // Assert
  await expect(page.locator('#shortcuts-modal')).toBeVisible();
});

Troubleshooting

Container won't start

If the Docker container exits immediately, check the logs:

docker compose logs

Common issue: Missing dependencies. Rebuild the container:

docker compose build --no-cache
docker compose up -d

Tests timeout

Increase timeout in playwright.config.ts if needed.

LIBRARY_PATH not set

Make sure to set the LIBRARY_PATH environment variable:

LIBRARY_PATH=~/feedBack-library docker compose up -d

CI/CD

In CI environments, Playwright will:

  • Run tests in headless mode
  • Retry failed tests up to 2 times
  • Generate HTML report with screenshots/videos on failure