Testing
Test builds and downloadable packages
Downloadable dev/stable packages are Release builds with TestBridge disabled. Use pnpm run android:install or pnpm run ios:build for Debug hosts with test JS. When building bundles manually, set SONGLOFT_TEST_BRIDGE=true; ordinary builds omit the test bridge. Native servers bind to 127.0.0.1:9230; Android connects through adb forward tcp:9230 tcp:9230.
Run pnpm run test:release after a production build to validate release tooling and actual Web output. Rebuild in production mode after E2E before validating release gates. See releasing and 简体中文.
Two layers: Vitest unit/contract tests without devices, and TestBridge E2E tests running against a device/simulator. Architecture is documented in E2E design (Chinese); assertion principles are in AGENTS.
Unit tests
pnpm test
pnpm exec tsc -bCurrent results belong in the handoff, not duplicated counts in this guide. Tests use Vitest/@testing-library and live in tests beside source. Test bodies must not include the literal Vitest environment annotation prohibited by the Lynx testing harness.
Native iOS background playback verification
swiftc -swift-version 5 ios/SongloftLynx/PlaybackBackgroundTask.swift scripts/verify-ios-playback-background.swift -o /tmp/verify-ios-playback-background
/tmp/verify-ios-playback-background
node scripts/verify-ios-playback-preparation.mjsThe Swift script executes the actual task owner to verify single acquisition, no renewal after expiration, stale callback isolation and cleanup. The Node script extracts the engine's actual seek methods and compiles/runs them against a controlled player interface retaining unknown/readiness and cancellation semantics, covering latest position, stale callbacks, pause intent and load generations; two defective variants must fail the corresponding assertions. Both need no UIKit and can run with Swift on Linux; Apple CI runs both. Without host Swift, pass --docker to the Node script to use swift:6.2. These verifiers and the Vitest wiring gates do not replace pnpm run ios:build or device acceptance.
Issue #10 device acceptance covers locked-screen playback across local and plugin tracks, slow responses/network retries, queue end and sleep timers, pause/seek/rapid track changes during loading, EQ and video output. Export client logs and correlate process started, scene activity, native page loaded, source preparation time, track completion and background task acquisition/cleanup/expiration. Diagnose process restarts and plugin reloads separately instead of inferring them from a pause. Plugin keep-alive controls the server JS service and does not guarantee continued execution of an iOS plugin page.
E2E behavior tests
pnpm run test:e2e:android
pnpm run e2e:ios
pnpm run e2e:ios:full
pnpm run e2e:ios:setupThere are 34 scenario files, about 121 test declarations, all requiring devices. Most are shared; iOS adds system appearance, while Android adds floating lyrics/fullscreen video. Reports and screenshots live in ignored e2e/reports and e2e/screenshots.
The documented full run from August 16 was Android 112 passed/8 skipped and iOS 110 passed/10 skipped. Scenarios have expanded since then; those historical counts do not validate current code.
Investigate changed skip counts
Historical Android skips included iOS-only appearance tests and video tests lacking fixtures. iOS skipped Android-only floating lyrics/video. Platform defaults must match createDriver's default Android behavior: (process.env.E2E_PLATFORM ?? 'android') === 'android'.
Missing fixtures must visibly skip with test.skipIf, not silently return from tests and report success.
Four environment checks before E2E
- Terminate a running iOS app before installing a rebuilt bundle:
xcrun simctl terminate <udid> org.songloft.lynx. - Remove leftover Android forwarding before iOS tests:
adb forward --remove tcp:9230. Otherwise the driver can silently connect to Android. - ios setup may reuse an installed app. After rebuilding, explicitly terminate/install the new ios/build/Debug-iphonesimulator/SongloftLynx.app.
- Check port 9230 with
lsof -iTCP:9230 -sTCP:LISTEN -P, and keep only one simulator booted. A bind failure or selecting the first booted device can yield misleading results.
Store handles
NativeModules is unavailable in the evaluator's scope, so native behavior is driven through the handles exposed by src/e2e-bridge.ts. Its current source is authoritative: player/auth/lyrics/EQ/server/config/router/appearance/floating lyrics/video/song overlays/cache/back handles.
See API/store conventions (Chinese) before exposing new stores.
Inspecting actual host events
Use a temporary probe scenario and poll player state to quantify tick cadence and event ordering. Measure before changing code; plausible explanations have repeatedly been disproved by device evidence.
Assert observable state
Screenshots verify rendering; side effects need separate evidence:
- Check settings endpoints after toggling controls.
- Check
pgrep -x ffmpegafter stopping work; keyword-count pipelines include the inspecting shell itself. - Inspect window metadata for floating lyric updates when screenshots cannot distinguish text.
- Compare computed height and scrollHeight to detect compressed dialogs.
