README from GitHub, MIT
fm1-playwright
Playwright test harness for FM-1 emulator firmware UI testing. Rotate encoders, press buttons, play notes, capture screenshots, and record GIFs.
More demo artifacts
| Artifact | Link |
|---|---|
| Happy Birthday (MP3) | felucca-happy-birthday.mp3 |
| Happy Birthday (MP4) | felucca-happy-birthday.mp4 |
| Happy Birthday (GIF) | felucca-happy-birthday.gif |
| Full demo (MP4) | felucca-demo.mp4 |
| Full demo (GIF) | felucca-demo.gif |
| Home screenshot | felucca-home.png |
| Final screenshot | felucca-final.png |
All artifacts are auto-generated by CI on every push to main.
Install
pip install fm1-playwright
playwright install chromium
System dependencies
Some features require system packages. Install them via your package manager or use the provided flake.nix:
nix develop # provides all dependencies below
| Package | Required for | Without it |
|---|---|---|
| Chromium | Everything (Playwright browser) | Nothing works |
| ffmpeg | GIF recording, video recording, MP4/MP3 conversion, video+audio muxing | Screenshots and raw audio capture still work |
| PulseAudio | Audio recording in headed mode (WebAudio routing) | Audio capture may fail without an audio server |
| Xvfb | Headed mode in CI (virtual display) | Use FM1_HEADLESS=1 instead |
Quick start
Subclass FirmwareUITest and write tests:
from fm1_playwright import FirmwareUITest
from fm1_playwright.testing import BOOT_PATTERN
class MyFirmwareTest(FirmwareUITest):
def test_boot(self):
self._get_page()
self._screenshot('home')
status = self._get_page().text_content('#status')
self.assertIn(BOOT_PATTERN, status)
def test_change_preset(self):
self.rotate_encoder('k2', direction=1, steps=1, delay_ms=800)
self._screenshot('preset-next')
Run with:
FM1_WEB_DIR=$HOME/.fm1-emulator/web \
FIRMWARE_NAME=felucca \
python3 -m unittest my_tests -v
GitHub Actions
Use with setup-fm1-emulator to run in CI:
jobs:
test:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: '3.12'
- uses: dimmkirr/setup-fm1-emulator@v1
- run: pip install fm1-playwright
- env:
FM1_WEB_DIR: ${{ steps.emu.outputs.web-path }}
FIRMWARE_NAME: felucca
FIRMWARE_URL: https://thefirmwarehub.com/fw/felucca-1.6.1.fwsc
run: python3 -m unittest tests.test_firmware -v
API
Emulator control
| Method | Description |
|---|---|
rotate_encoder(name, direction, steps, delay_ms) | Rotate an encoder (select, k1, k2, presets, algorithm, master, knob1-4) |
press_button(name, delay_ms) | Press a button (home, scl, env, lfo, edit, fx, etc.) |
play_note(note, hold_ms, delay_ms) | Play a note (C4..F5) |
hold_button(name) | Context manager: hold a button while performing other actions |
Capture
| Method | Description |
|---|---|
_screenshot(name) | Save a PNG of the device canvas or LCD screen |
record_gif(name, fps) | Context manager: record frames into an animated GIF |
record_video(name, fps) | Context manager: record frames into a webm video |
Audio recording
Record audio output from the emulator's Web Audio API:
from fm1_playwright.recording import (
audio_patch, start_audio_recording, stop_audio_recording,
mux_video_audio, webm_to_mp3,
)
# audio_patch must wrap page creation and navigation
with audio_patch(web_dir):
# ... create page, navigate, boot ...
start_audio_recording(page)
# ... play notes, record video frames ...
stop_audio_recording(page, 'audio.webm')
# Combine video + audio into a single MP4
mux_video_audio('video.webm', 'audio.webm', 'output.mp4')
# Or convert audio to MP3
webm_to_mp3('audio.webm', 'output.mp3')
View modes
Set FM1_VIEW to control screenshot capture:
| Value | Captures | Size |
|---|---|---|
DEVICE (default) | Full device canvas (panel + buttons + keys) | Variable |
SCREEN | LCD screen only | 240x240 |
Environment variables
| Variable | Description | Default |
|---|---|---|
FM1_WEB_DIR | Path to fm1-emu WASM directory | required |
FIRMWARE_NAME | Firmware name for URL and filenames | required |
FIRMWARE_URL | URL to a .fwsc firmware file | optional |
RESULTS_DIR | Output directory for screenshots/GIFs | results |
FM1_VIEW | DEVICE or SCREEN | DEVICE |
FM1_SCALE | LCD pixel scaling (1-4) | 2 |
FM1_HEADLESS | 1 for headless, 0 for headed | 1 |
CHROMIUM_PATH | Custom Chromium binary path | auto-detected |
Examples
See examples/ for a full E2E test suite with screenshots and GIF recording.
Credits
Built on fm1-emu by Joakim Eriksson.