Files
LEDMatrix/docs/DEV_PREVIEW.md
Chuck 2bff30038e feat: add dev preview server, CLI render script, and visual test display manager
Adds local development tools for rapid plugin iteration without deploying to RPi:

- VisualTestDisplayManager: renders real pixels via PIL (same fonts/interface as production)
- Dev preview server (Flask): interactive web UI with plugin picker, auto-generated config
  forms, zoom/grid controls, and mock data support for API-dependent plugins
- CLI render script: render any plugin to PNG for AI-assisted visual feedback loops
- Updated test runner and conftest to auto-detect plugin-repos/ directory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 14:09:35 -05:00

167 lines
5.2 KiB
Markdown

# Dev Preview & Visual Testing
Tools for rapid plugin development without deploying to the RPi.
## Dev Preview Server
Interactive web UI for tweaking plugin configs and seeing the rendered display in real time.
### Quick Start
```bash
python scripts/dev_server.py
# Opens at http://localhost:5001
```
### Options
```bash
python scripts/dev_server.py --port 8080 # Custom port
python scripts/dev_server.py --extra-dir /path/to/custom-plugin # 3rd party plugins
python scripts/dev_server.py --debug # Flask debug mode
```
### Workflow
1. Select a plugin from the dropdown (auto-discovers from `plugins/` and `plugin-repos/`)
2. The config form auto-generates from the plugin's `config_schema.json`
3. Tweak any config value — the display preview updates automatically
4. Toggle "Auto" off for plugins with slow `update()` calls, then click "Render" manually
5. Use the zoom slider to scale the tiny display (128x32) up for detailed inspection
6. Toggle the grid overlay to see individual pixel boundaries
### Mock Data for API-dependent Plugins
Many plugins fetch data from APIs (sports scores, weather, stocks). To render these locally, expand "Mock Data" and paste a JSON object with cache keys the plugin expects.
To find the cache keys a plugin uses, search its `manager.py` for `self.cache_manager.set(` calls.
Example for a sports plugin:
```json
{
"football_scores": {
"games": [
{"home": "Eagles", "away": "Chiefs", "home_score": 24, "away_score": 21, "status": "Final"}
]
}
}
```
---
## CLI Render Script
Render any plugin to a PNG image from the command line. Useful for AI-assisted development and scripted workflows.
### Usage
```bash
# Basic — renders with default config
python scripts/render_plugin.py --plugin hello-world --output /tmp/hello.png
# Custom config
python scripts/render_plugin.py --plugin clock-simple \
--config '{"timezone":"America/New_York","time_format":"12h"}' \
--output /tmp/clock.png
# Different display dimensions
python scripts/render_plugin.py --plugin hello-world --width 64 --height 32 --output /tmp/small.png
# 3rd party plugin from a custom directory
python scripts/render_plugin.py --plugin my-plugin --plugin-dir /path/to/repo --output /tmp/my.png
# With mock API data
python scripts/render_plugin.py --plugin football-scoreboard \
--mock-data /tmp/mock_scores.json \
--output /tmp/football.png
```
### Using with Claude Code / AI
Claude can run the render script, then read the output PNG (Claude is multimodal and can see images). This enables a visual feedback loop:
```
Claude → bash: python scripts/render_plugin.py --plugin hello-world --output /tmp/render.png
Claude → Read /tmp/render.png ← Claude sees the actual rendered display
Claude → (makes code changes based on what it sees)
Claude → bash: python scripts/render_plugin.py --plugin hello-world --output /tmp/render2.png
Claude → Read /tmp/render2.png ← verifies the visual change
```
---
## VisualTestDisplayManager (for test suites)
A display manager that renders real pixels for use in pytest, without requiring hardware.
### Basic Usage
```python
from src.plugin_system.testing import VisualTestDisplayManager, MockCacheManager, MockPluginManager
def test_my_plugin_renders_title():
display = VisualTestDisplayManager(width=128, height=32)
cache = MockCacheManager()
pm = MockPluginManager()
plugin = MyPlugin(
plugin_id='my-plugin',
config={'enabled': True, 'title': 'Hello'},
display_manager=display,
cache_manager=cache,
plugin_manager=pm
)
plugin.update()
plugin.display(force_clear=True)
# Verify pixels were drawn (not just that methods were called)
pixels = list(display.image.getdata())
assert any(p != (0, 0, 0) for p in pixels), "Display should not be blank"
# Save snapshot for manual inspection
display.save_snapshot('/tmp/test_my_plugin.png')
```
### Pytest Fixture
A `visual_display_manager` fixture is available in plugin tests:
```python
def test_rendering(visual_display_manager):
visual_display_manager.draw_text("Test", x=10, y=10, color=(255, 255, 255))
assert visual_display_manager.width == 128
pixels = list(visual_display_manager.image.getdata())
assert any(p != (0, 0, 0) for p in pixels)
```
### Key Differences from MockDisplayManager
| Feature | MockDisplayManager | VisualTestDisplayManager |
|---------|-------------------|--------------------------|
| Renders pixels | No (logs calls only) | Yes (real PIL rendering) |
| Loads fonts | No | Yes (same fonts as production) |
| Save to PNG | No | Yes (`save_snapshot()`) |
| Call tracking | Yes | Yes (backwards compatible) |
| Use case | Unit tests (method call assertions) | Visual tests, dev preview |
---
## Plugin Test Runner
The test runner auto-detects `plugin-repos/` for monorepo development:
```bash
# Auto-detect (tries plugins/ then plugin-repos/)
python scripts/run_plugin_tests.py
# Test specific plugin
python scripts/run_plugin_tests.py --plugin clock-simple
# Explicit directory
python scripts/run_plugin_tests.py --plugins-dir plugin-repos/
# With coverage
python scripts/run_plugin_tests.py --coverage --verbose
```