| name | manual-testing |
| description | Block-diagram editor manual testing recipes for playwright-cli. Covers common UI operations (adding blocks, connecting ports, running simulations, editing parameters) using real mouse/keyboard events. Use when told to "test manually", "test in the browser", or "verify with playwright-cli". |
Manual Testing with playwright-cli
Prerequisites
-
Start the dev server (if not already running):
npm start &
-
Open the browser:
PORT=$(bash scripts/port.sh)
playwright-cli open http://localhost:$PORT
-
Bypass the auth gate:
playwright-cli localstorage-set auth-token \
{{AUTH_BYPASS_VALUE}}
playwright-cli reload
-
Take a snapshot to confirm the editor loaded:
playwright-cli snapshot
Core Workflow
Every manual test follows this cycle:
- Snapshot — get current page state and element refs
- Interact — use real mouse/keyboard commands (click, type, drag, mousemove, etc.)
- Snapshot — verify the UI updated as expected
- Screenshot — capture visual evidence when needed
Golden rule: Never use eval or run-code to simulate user actions. Real events
only. eval is reserved for setup (auth, localStorage) and assertions (reading model
state).
Adding a Block
Method A: Drag from Library Panel
playwright-cli snapshot
playwright-cli click <category-ref>
playwright-cli snapshot
playwright-cli drag <block-item-ref> --x=400 --y=300
Method B: quick-add dialog (double-click canvas)
playwright-cli dblclick "{{CANVAS_SELECTOR}}" --x=400 --y=300
playwright-cli type "Gain"
playwright-cli snapshot
playwright-cli press Enter
playwright-cli snapshot
Tip: Pick a method at random to exercise different code paths each time.
Copying an Existing Block
Method A: Ctrl+D (Duplicate)
playwright-cli click <block-ref>
playwright-cli press Control+d
Method B: Ctrl+C / Ctrl+V (Copy-Paste)
playwright-cli click <block-ref>
playwright-cli press Control+c
playwright-cli press Control+v
Method C: Right-click Drag
playwright-cli mousemove <block-x> <block-y>
playwright-cli mousedown right
playwright-cli mousemove <new-x> <new-y>
playwright-cli mouseup right
Connecting Two Blocks
Connect output port of Block A to input port of Block B:
playwright-cli snapshot
playwright-cli eval "(() => {
const sel = '[data-block-id=\"BLOCK_A_ID\"] \
{{PORT_SELECTOR}}[data-port-side=\"out\"]';
const p = document.querySelector(sel);
const r = p.getBoundingClientRect();
return { x: r.x + r.width/2, y: r.y + r.height/2 };
})()"
playwright-cli mousemove <out-x> <out-y>
playwright-cli mousedown
playwright-cli mousemove <midpoint-x> <midpoint-y>
playwright-cli eval "(() => {
const sel = '[data-block-id=\"BLOCK_B_ID\"] \
{{PORT_SELECTOR}}[data-port-side=\"in\"]';
const p = document.querySelector(sel);
const r = p.getBoundingClientRect();
return { x: r.x + r.width/2, y: r.y + r.height/2 };
})()"
playwright-cli mousemove <in-x> <in-y>
playwright-cli mouseup
playwright-cli snapshot
Note: Using eval to read port coordinates is fine — it's reading DOM state,
not simulating user actions. The actual connection happens through real mouse events.
Editing Block Parameters
playwright-cli dblclick <block-ref>
playwright-cli snapshot
playwright-cli click <field-ref>
playwright-cli press Control+a
playwright-cli type "5"
playwright-cli click <apply-btn-ref>
playwright-cli snapshot
Alternative: Select the block, then press Enter to open properties.
Running a Simulation
playwright-cli click <stop-time-ref>
playwright-cli press Control+a
playwright-cli type "10"
playwright-cli press Enter
playwright-cli click <run-btn-ref>
playwright-cli screenshot
playwright-cli snapshot
Tip: After running, look for Scope blocks — they open popup windows showing
output graphs. Screenshot those for visual verification.
Keyboard Shortcuts
| Shortcut | Action |
|---|
Ctrl+Z | Undo |
Ctrl+Shift+Z | Redo |
Delete/Backspace | Delete selected |
Ctrl+A | Select all |
| Double-click canvas | quick-add dialog (add block) |
Ctrl+G | Group into Subsystem |
Ctrl+D | Duplicate selected |
Ctrl+C/Ctrl+V | Copy / Paste |
F2 | Rename selected block |
Enter | Open properties (block selected) |
Escape | Cancel current action / deselect |
Ctrl+S | Save model |
Example Models
Always check examples/ before building test models from scratch.
Run ls examples/ to see available models. Load them via URL:
http://localhost:<port>/?file=examples/<name>/<name>.model
Available examples include: amplifier-circuit, motor-controller,
signal-filter, feedback-loop, and more. Check the directory — don't guess.
Common Selectors
Use these to locate elements when snapshot refs aren't sufficient:
Canvas & Viewport
| Selector | Description |
|---|
{{CANVAS_SELECTOR}} | Main SVG canvas |
{{VIEWPORT_SELECTOR}} | Pan/zoom viewport group |
Blocks & Ports
| Selector | Description |
|---|
[data-block-id="ID"] | Block by UUID |
{{BLOCK_BODY_SELECTOR}} | Block rectangle |
{{BLOCK_LABEL_SELECTOR}} | Block name label |
{{PORT_SELECTOR}}[data-port-side="out"] | Output port circle |
{{PORT_SELECTOR}}[data-port-side="in"] | Input port circle |
[data-port-index="0"] | First port |
{{SELECTED_SELECTOR}} | Any selected element |
Lines
| Selector | Description |
|---|
[data-line-id="ID"] | Signal line by UUID |
{{LINE_SELECTOR}} | Any signal line |
Library Panel
| Selector | Description |
|---|
{{LIBRARY_SELECTOR}} | Library sidebar |
[data-category="sources"] | Category by name |
[data-block-type="Gain"] | Block item by type name |
{{LIBRARY_SEARCH_SELECTOR}} input | Library search input |
Toolbar
| Selector | Description |
|---|
{{RUN_BTN_SELECTOR}} | Run simulation |
{{STOP_BTN_SELECTOR}} | Stop simulation |
{{PAUSE_BTN_SELECTOR}} | Pause simulation |
{{SIM_TIME_SELECTOR}} | Stop time input |
Property Panel
| Selector | Description |
|---|
{{PROPERTY_PANEL_SELECTOR}} | Properties dialog |
[data-key="Gain"] | Parameter by key |
{{PROPERTY_APPLY_SELECTOR}} | Apply button |
{{PROPERTY_CANCEL_SELECTOR}} | Cancel button |
quick-add dialog
| Selector | Description |
|---|
{{QUICK_INSERT_SELECTOR}} | quick-add dialog popup |
{{QUICK_INSERT_INPUT_SELECTOR}} | Search text field |
{{QUICK_INSERT_ITEM_SELECTOR}} | Result item |
SVG Gotchas
-
Pointer capture: SVG elements use setPointerCapture() during drags. After
release, e.target may still point to the capture element. The app uses
document.elementFromPoint() for hit detection after release — this is correct
behavior, not a bug.
-
Double-click on SVG: dblclick events can be unreliable on SVG elements in
some browsers. If dblclick on a block ref doesn't open properties, try selecting
the block first (click), then pressing Enter.
-
Coordinate systems: Block positions are in SVG viewport coordinates, but
mousemove/mousedown use screen coordinates. When using eval to read element
positions, use getBoundingClientRect() which returns screen coordinates matching
what playwright-cli expects.
-
Zoom level matters: If the canvas is zoomed, screen coordinates and SVG
coordinates diverge. Use getBoundingClientRect() for screen coords. If tests
seem to miss targets, check zoom level with:
playwright-cli eval \
"document.querySelector('{{VIEWPORT_SELECTOR}}').getAttribute('transform')"