Manual Testing β Marionette
Step-by-step procedure for manually testing every user-facing feature.
Run through this checklist after significant changes, on each platform
(Linux, macOS, Windows), and on each browser (Chrome, Firefox, Safari).
Prerequisites:
- Reachy Mini robot powered on and connected (Wireless or Lite)
- App deployed and running on the robot (see below)
- Browser open at
http://reachy-mini.local:8042
- A short WAV or MP3 file ready for upload tests (~2-5s)
- Hugging Face CLI logged in (
huggingface-cli login) for HF tests
Starting the app (Wireless):
The fastest way to deploy and start marionette on a Wireless robot:
cd marionette
./deploy_wireless.sh
This syncs your local code to the robot, starts the app via the daemon,
and streams logs to your terminal. See the script header for prerequisites
(SSH key setup, daemon status). Pass an IP if mDNS doesn't resolve:
./deploy_wireless.sh 192.168.1.42
To stop the app: ./stop_app.sh (or use the dashboard).
Starting the app (Lite or local dev):
cd marionette && python -m marionette
1. Startup & Page Load
| # |
Step |
Expected result |
| 1.1 |
Start the app with robot connected |
Robot plays intro audio, head moves to init pose then sleep pose, antennas splay at 15 deg, motors disable. Terminal shows no errors. |
| 1.2 |
Open the web UI during startup |
Mode pill shows STARTING_UP, status shows "Starting up...". All buttons are disabled. |
| 1.3 |
Wait for startup to complete |
Mode pill changes to IDLE, status shows "Ready to capture moves". Record button enables. |
| 1.4 |
Check initial form state |
Duration field is pre-filled (default 5.0s). Name field is empty (or restored from last session). Dataset dropdown is populated. Moves list shows existing moves or "No moves recorded yet." |
| 1.5 |
Check mode pill during idle |
Shows IDLE in the header, no buttons say "Stop". |
2. Recording β Silent Mode
| # |
Step |
Expected result |
| 2.1 |
Expand Options, select Silent radio |
Upload area stays hidden. Audio hint clears. |
| 2.2 |
Type a name (e.g. test-silent), set duration to 3, click Record |
Button briefly says "Queuing...", mode pill changes to QUEUED. |
| 2.3 |
Watch the countdown |
Mode changes to COUNTDOWN. Large blue countdown shows 3, 2, 1. Progress bar fills. "Get ready" label visible. Stop Recording button appears. |
| 2.4 |
Watch the recording phase |
Mode changes to RECORDING. Display turns red, shows remaining seconds. Progress bar counts down for 3s. |
| 2.5 |
Wait for recording to finish |
Mode returns to IDLE. Status shows "Recorded test-silent - N poses (X.X/s)". Recording stats hint shows frame count. |
| 2.6 |
Check the moves list |
New move test-silent appears at top. Shows "3.0s", silent icon, creation timestamp. Play and Delete buttons visible. |
| 2.7 |
Verify files on disk |
<dataset-dir>/test-silent.json exists. No .wav file. JSON has time, set_target_data arrays with ~300 frames. |
3. Recording β Mic Audio
| # |
Step |
Expected result |
| 3.1 |
Expand Options, select Mic radio |
Audio hint shows "Live audio captured via robot's microphone." (If mic unavailable: hint says so, radio is disabled.) |
| 3.2 |
Set duration to 2, click Record |
Countdown starts (3s), then recording starts (2s). |
| 3.3 |
Speak or make noise during recording |
Recording completes normally. |
| 3.4 |
Check the moves list |
New move shows microphone icon. |
| 3.5 |
Verify files on disk |
Both .json and .wav exist. WAV duration is ~2s. |
4. Recording β Uploaded Audio
| # |
Step |
Expected result |
| 4.1 |
Expand Options, select Upload radio |
Upload area appears with "Choose file..." button. |
| 4.2 |
Click Choose file... and select a WAV |
Upload status shows "Uploading...", then "Uploaded: filename.wav (X.Xs). Adjust duration if needed." Duration field auto-fills with audio length. |
| 4.3 |
Click Record |
Countdown starts. When recording starts, the uploaded audio plays through robot's speaker. You can move the robot in sync with the audio. |
| 4.4 |
Wait for recording to finish |
Move saved. .wav file is a copy of the uploaded audio (not mic recording). |
| 4.5 |
Drag-and-drop test: drag a WAV onto the upload area |
Upload area highlights with border. File uploads on drop. |
| 4.6 |
Drag non-audio file (e.g. .txt) |
Status shows "Please drop a WAV or MP3 file." No upload happens. |
| 4.7 |
MP3 upload: select an MP3 file |
Status shows "Uploading and converting MP3 to WAV..." Conversion completes. Duration field updates. |
| 4.8 |
Switch audio source away from Upload, then back |
Uploaded file reference is cleared. Must re-upload. |
| 4.9 |
Try to Record with Upload selected but no file uploaded |
Status shows "Please upload an audio file first." No recording starts. |
5. Stop Recording
| # |
Step |
Expected result |
| 5.1 |
Start a 10s recording, click Stop Recording during countdown |
Recording is cancelled immediately. Status: "Recording cancelled". No move saved. |
| 5.2 |
Start a 10s recording, wait for recording phase, click Stop Recording after ~2s |
Recording stops early. Move is saved with ~2s of data. Status: "Saved ... (stopped early) - N poses". |
| 5.3 |
Verify partial recording on disk |
JSON file exists with ~200 frames (not 1000). |
6. Playback
| # |
Step |
Expected result |
| 6.1 |
Click Play on a silent move |
Mode pill shows PLAYING. Robot enables motors, moves to start pose, replays the motion, returns to init pose, disables motors. Mode returns to IDLE. |
| 6.2 |
Click Play on a move with audio |
Same as above, plus audio plays through robot's speaker in sync with motion. |
| 6.3 |
During playback, verify UI state |
Record button disabled. All Play/Delete buttons disabled. Dataset selector disabled. Stop Playback button visible. |
| 6.4 |
After playback completes |
Status: "Finished playing ...". Stop Playback button hides. All controls re-enable. |
7. Stop Playback
| # |
Step |
Expected result |
| 7.1 |
Start playback on a 5s+ move, click Stop Playback mid-stream |
Playback stops immediately. Robot stops moving. Audio stops. Status: "Playback stopped for ...". Mode returns to IDLE. |
| 7.2 |
Click Stop Playback again when already idle |
Nothing happens (button should be hidden). |
8. Delete a Move
| # |
Step |
Expected result |
| 8.1 |
Click Delete on a move |
Browser confirm dialog: "Delete move 'move-id'? This cannot be undone." |
| 8.2 |
Click Cancel in dialog |
Move stays in list. No files deleted. |
| 8.3 |
Click OK in dialog |
Move disappears from list. JSON and WAV files are deleted from disk. |
9. Dataset Management
| # |
Step |
Expected result |
| 9.1 |
Click + New next to dataset dropdown |
Inline form appears. "+ New" button hides. |
| 9.2 |
Type a name with spaces/caps (e.g. My Dataset) |
Input auto-transforms to my_dataset (lowercase, spaces to underscores). |
| 9.3 |
Click Create |
Dataset created. Dropdown now includes my_dataset. Moves list is empty (new dataset). |
| 9.4 |
Click Cancel (or press Escape) without typing |
Form hides. "+ New" button reappears. No dataset created. |
| 9.5 |
Try to create a dataset with the same name |
Error shown: "Dataset name already used." |
| 9.6 |
Switch dataset in dropdown |
Active dataset changes. Moves list updates to show that dataset's moves. Path hint updates. |
| 9.7 |
Switch to a downloaded dataset |
Record button disables with tooltip "Switch to a local dataset to record". Play still works. |
10. Hugging Face Upload
| # |
Step |
Expected result |
| 10.1 |
If HF CLI logged in: check username field |
Auto-filled with detected username, read-only, green tint, label says "Logged in as". |
| 10.2 |
If not logged in: type a username |
Field is editable. Value persists after page reload (localStorage). |
| 10.3 |
Select one or more move checkboxes |
Sync button enables (if username present). |
| 10.4 |
Click Upload selected |
Button says "Synchronizing...". Status shows "Uploading selected moves...". |
| 10.5 |
Wait for upload to complete |
Status shows "N moves synced to user/dataset." with clickable link. |
| 10.6 |
Check uploaded moves |
Green "UPLOADED" badge appears on synced moves. |
| 10.7 |
Clear username and try to upload |
Sync button is disabled. Hint: "Enter your Hugging Face username." |
| 10.8 |
Select no moves |
Sync button disabled. Hint: "Select at least one move to upload." |
11. Community Datasets
| # |
Step |
Expected result |
| 11.1 |
Expand Community section |
"Fetch community datasets" button visible. |
| 11.2 |
Click Fetch community datasets |
Status: "Searching Hugging Face...". After loading, list of community datasets appears with names, authors, likes, tags. |
| 11.3 |
Select one or more datasets, click Download selected |
Status: "Downloading...". After completion, new datasets appear in dropdown with "down-arrow" prefix. |
| 11.4 |
Switch to downloaded dataset |
Moves list shows downloaded moves. Record is disabled (read-only). Play works. |
| 11.5 |
Try to download the same dataset again |
Error: "Dataset '...' already exists locally." |
12. Settings
| # |
Step |
Expected result |
| 12.1 |
Expand Settings section |
"Datasets folder" input with current path shown. |
| 12.2 |
Change the path and click Update |
Button says "Updating...". Path changes. All datasets are remapped to new root. |
| 12.3 |
Enter a non-existent path |
Path is created automatically. Datasets folder now lives there. |
| 12.4 |
Expand Experimental section |
"Enable motion compensation models" checkbox visible. |
| 12.5 |
Toggle motion models on |
Model selector appears below. |
| 12.6 |
Select a model, play a move |
Playback uses filtered trajectory (visual difference may be subtle). |
| 12.7 |
Toggle motion models off |
Model selector hides. |
13. Edge Cases & Stress Tests
| # |
Step |
Expected result |
| 13.1 |
Empty name: leave Name blank, record |
Auto-generated name take-YYYYMMDD-HHMMSS used. |
| 13.2 |
Long name: type 80 characters, record |
Accepted. Truncated to 80 chars. |
| 13.3 |
Special characters: name with @#$! spaces |
Slugified (lowercase, special chars removed, spaces to dashes). |
| 13.4 |
Min duration: set duration to 0.5, record |
Rejected (must be > 0.5, not >= 0.5). Try 0.6 β accepted. |
| 13.5 |
Max duration: set duration to 300, record |
Accepted. (Don't wait 5 min β stop it early to verify.) |
| 13.6 |
Close browser tab mid-recording |
Recording continues server-side. Reopen tab: move appears in list when done. |
| 13.7 |
Two browser tabs |
Open same URL in two tabs. Record from tab 1 β tab 2 shows mode changes on next poll. Try to record from tab 2 while tab 1 is recording β rejected ("Robot is busy"). |
| 13.8 |
Rapid record/play cycles |
Record 1.5s, play it, record again, play again. Repeat 5 times. Robot should stay responsive, no stuck states. |
| 13.9 |
Delete a file externally |
While app is running, delete a .json file from the dataset folder. Click Refresh. Move disappears from list. No crash. |
| 13.10 |
Replace a .wav file |
Copy a different WAV file over an existing move's .wav. Click Refresh. Play the move β new audio plays. |
| 13.11 |
Server unavailable |
Stop the app (./stop_app.sh). UI shows "Unable to reach backend." Restart (./deploy_wireless.sh) β UI recovers on next poll. |
| 13.12 |
No robot connected |
Start app without robot (python -m marionette). App may fail to start or show error in terminal. UI should not hang. |
14. Cross-Browser Checks
Test these on each browser (Chrome, Firefox, Safari/WebKit):
| # |
Check |
Notes |
| 14.1 |
Page loads, all sections render |
CSS layout, fonts, spacing |
| 14.2 |
Form validation works |
Duration min/max, pattern validation on dataset name |
| 14.3 |
Progress bar animates smoothly |
requestAnimationFrame timing |
| 14.4 |
Drag-and-drop works |
Audio upload area |
| 14.5 |
localStorage persistence |
Name and HF username survive reload |
| 14.6 |
confirm() dialog works |
Delete move |
| 14.7 |
Details/summary elements toggle |
Options, Community, Settings |
15. Mobile / Responsive
| # |
Step |
Expected result |
| 15.1 |
Open UI on a phone or narrow window (<720px) |
Form fields stack vertically. Record button is full-width. |
| 15.2 |
Record form works on touch |
Can tap Record, tap Stop. Duration input works with on-screen keyboard. |
| 15.3 |
Move list is scrollable |
Long list doesn't break layout. |
| 15.4 |
All collapsible sections work |
Tap to expand/collapse Options, Community, Settings. |
Test Matrix
Track results in this grid. Mark each cell with PASS, FAIL, or SKIP:
β Chrome β Firefox β Safari β Mobile
βββββββββββββββββββΌβββββββββΌββββββββββΌβββββββββΌβββββββ
Startup (1.x) β β β β
Silent record (2) β β β β
Mic record (3) β β β β
Upload record (4) β β β β
Stop record (5) β β β β
Playback (6) β β β β
Stop playback (7) β β β β
Delete move (8) β β β β
Datasets (9) β β β β
HF upload (10) β β β β
Community (11) β β β β
Settings (12) β β β β
Edge cases (13) β β β β
Responsive (15) β β β β
What Automated Tests Already Cover
Before doing a full manual pass, you can run the automated suites to catch
regressions quickly:
cd marionette
pytest tests/test_api.py -q
pytest tests/e2e --browser chromium --browser firefox
pytest tests/test_hardware.py -m hardware
pytest tests/ --browser chromium
Current coverage: 95 unit + 44 E2E + 37 hardware = 176 automated tests.
The manual testing procedure above focuses on what automated tests cannot
cover: subjective UX quality, audio sync perception, motor smoothness,
visual layout, and cross-browser rendering. Automated tests handle API
validation, state machine transitions, and form logic.