verstak-desktop/docs/GUI_TESTING.md

144 lines
4.7 KiB
Markdown

# GUI Testing
## Overview
Verstak Desktop uses **Playwright** for frontend E2E tests that run in a real
Chromium browser with mocked Wails bindings. This tests the Svelte component
logic, user interactions, and UI state transitions — without needing the actual
Wails desktop shell.
## What is tested
### Frontend E2E (Playwright)
Located in `frontend/e2e/`, run via `npm run test:e2e`.
These tests:
- Launch a Vite dev server with mock Wails bindings
- Open the app in a real Chromium browser via Playwright
- Simulate user clicks, wait for UI transitions, assert DOM state
- Collect console errors and page errors on failure
- Capture screenshots on failure
### Test suites
| File | Suite | Tests | Status |
|------|-------|-------|--------|
| `plugin-manager-disable-enable.spec.js` | A: Disable/Enable refresh | 4 | 3 pass, 1 fail* |
| `sidebar-opens-view.spec.js` | B: Sidebar → view routing | 3 | 3 pass |
| `reload-updates-state.spec.js` | C: Reload updates UI | 3 | 2 pass, 1 fail* |
\* Failing tests document **known bugs** (see below).
## Known bugs detected by tests
### Bug M5-1: Sidebar does not update when plugin state changes
**Symptom:** After disabling a plugin in Plugin Manager, the sidebar item for
that plugin remains visible. After re-enabling, it stays visible (doesn't
disappear then reappear — it was never gone).
**Root cause:** `Sidebar.svelte` loads plugin/contribution data once in
`onMount` and stores it in local `sidebarItems`. When `PluginManager`
disables/enables a plugin and calls `ReloadPlugins`, the `PluginManager`
component re-fetches data, but `Sidebar` does not react to the change — it
still holds the stale list.
**Affected tests:**
- `A: Disable plugin: button changes to Enable, sidebar item disappears`
- `A: Disable → Enable full flow in sequence`
- `C: Reload after mock state change reflects new plugin status`
**Fix needed:** Sidebar must either:
1. Re-fetch contributions when it receives a custom event (e.g.
`verstak:plugins-reloaded`), or
2. Read plugin state reactively from a shared store that both
PluginManager and Sidebar subscribe to.
## What is NOT tested
### Real desktop GUI (WebKitGTK + Wails native shell)
The Playwright tests run the frontend in a **standard Chromium browser** with
mocked Wails bindings. They do **not** test:
- Actual WebKitGTK rendering (Wails uses WebKitGTK, not Chromium)
- Native window management (minimize, maximize, resize)
- Native file dialogs (SelectDirectory, SelectVaultForOpen)
- Clipboard integration
- System tray / menu bar
- Plugin frontend bundle loading from real filesystem
- Wails event system (window.runtime.EventsOn/Emit)
For real Wails smoke tests, a separate layer is needed using:
- **AT-SPI2** (Linux accessibility tree inspection)
- **xdotool** / **ydotool** (input simulation)
- **scrot** / **import** (screenshot capture)
## Running tests
```bash
cd frontend
# Run all E2E tests (headless)
npm run test:e2e
# Run with Playwright UI (interactive)
npm run test:e2e:ui
# Run in headed browser (visible)
npm run test:e2e:headed
```
## Test infrastructure
### Mock bridge (`src/lib/test/wails-mock.js`)
Replaces `window['go']['api']['App']` with in-memory mock implementations of
all Wails backend methods. Provides:
- Mutable plugin state (enable/disable/status)
- Mutable vault state
- Mutable contributions (views, commands, sidebar items, settings panels)
- Test helpers via `window.__wailsMock`:
- `reset()` — reset all state to defaults
- `setPluginStatus(id, status, enabled)` — change plugin state
- `getPluginState(id)` — read current state
- `setVaultStatus(status)` — change vault state
### Test harness (`index.html`)
The same `index.html` is used for both production and test. It detects whether
the Wails runtime (`window['go']`) is present. If not (i.e. running in a plain
browser), it loads the mock bridge before the Svelte app.
### Playwright config (`playwright.config.js`)
- Dev server: `vite --mode test --port 5174`
- Browser: Chromium headless
- Timeouts: 30s test, 10s expect
- Workers: 1 (sequential)
- Screenshots: on failure
- Traces: on first retry
- Results: `e2e-results/test-results.json`
## Adding new tests
1. Create `e2e/your-test.spec.js`
2. Import helpers from `./helpers.js`
3. Use `test.beforeEach` to reset mock state and navigate to `/`
4. Use `test.afterEach` to assert no console errors
5. Write scenarios as user actions + assertions
6. Run with `npm run test:e2e`
### Selector conventions
- Plugin cards: `.plugin-card` filtered by text
- Buttons: `.btn-disable`, `.btn-enable`, `.btn-settings`, `.reload-btn`
- Sidebar items: `.sidebar .plugin-item`
- View container: `.view-container`
- View header: `.view-header h2`
- Status badges: `.status-badge`
- Toast: `.toast`