2025-11-28 13:10:05 +01:00
# KiCad WASM Tests
Playwright tests for verifying the wxWidgets WASM port.
## Prerequisites
- Node.js 18+
- Emscripten SDK (for building)
## Building the Test App
```bash
../scripts/build-wasm-test.sh
```
2025-12-27 14:06:23 +01:00
This builds `apps/minimal_test.{html,js,wasm}` and standalone test apps.
2025-11-28 13:10:05 +01:00
## Running Tests
```bash
npm install
2026-06-13 14:10:05 +02:00
npm test # wx e2e specs + the asyncify race harness
2025-11-28 13:10:05 +01:00
```
2026-06-13 14:10:05 +02:00
KiCad application tests are a separate, heavier suite (they need the
docker-built KiCad WASM): `npm run test:kicad` . Run only the wx e2e specs with
`npm run test:wx` .
2025-11-29 20:54:54 +01:00
To run specific tests:
```bash
npx playwright test menu.spec.ts # Run menu tests only
npx playwright test --grep "wxTimer" # Run tests matching pattern
```
## Test Structure
```
tests/
├── e2e/ # Playwright test specs
│ ├── utils/ # Shared test utilities
│ │ ├── fixtures.ts # Playwright fixtures with auto-logging
2025-12-29 12:11:33 +01:00
│ │ ├── element-tracker.ts # Element registry utilities (clickByLabel, etc.)
2025-11-29 20:54:54 +01:00
│ │ └── test-utils.ts # Logging and helper functions
│ ├── menu.spec.ts # wxMenuBar tests
│ ├── timer.spec.ts # wxTimer tests
│ ├── dialog.spec.ts # wxDialog/wxMessageBox tests
│ ├── tree.spec.ts # wxTreeCtrl tests
│ ├── grid.spec.ts # wxGrid/wxSpinCtrl/wxSearchCtrl tests
│ ├── wxwidgets.spec.ts # Comprehensive UI interaction tests
│ └── ...
├── logs/ # Test logs (auto-generated)
├── test-results/ # Screenshots (auto-generated)
├── baseline-screenshots/ # Reference screenshots for comparison
2025-12-27 14:06:23 +01:00
├── apps/ # Built WASM test applications
2025-11-29 20:54:54 +01:00
│ ├── minimal_test.html # Main test app
│ └── standalone/ # Individual component test apps
└── playwright.config.ts # Playwright configuration
```
## Logging
Each test automatically captures:
- Console logs with timestamps and log levels
- Page errors with full stack traces
Log files are written to `logs/` after each test:
- `<test-name>.log` - All console output
- `<test-name>.errors.log` - Errors only (created if errors occurred)
Example log format:
```
[2025-11-29T19:39:42.165Z] [LOG] [EVENT] Application started
[2025-11-29T19:39:42.733Z] [WARNING] GPU stall due to ReadPixels
[2025-11-29T19:39:42.801Z] [ERROR] Some error message
```
## Screenshots
Tests capture screenshots to `test-results/` . Compare against baselines:
```bash
../scripts/compare-screenshots.sh
```
2025-11-28 13:10:05 +01:00
## Viewing the App Directly
2025-12-27 14:06:23 +01:00
Start a local server in the apps directory:
2025-11-28 13:10:05 +01:00
```bash
2025-12-27 14:06:23 +01:00
cd apps
2025-11-28 13:10:05 +01:00
npx serve .
```
Then open http://localhost:3000/minimal_test.html in your browser.
Alternative using Python:
```bash
2025-12-27 14:06:23 +01:00
cd apps
2025-11-28 13:10:05 +01:00
python3 -m http.server 8000
```
Then open http://localhost:8000/minimal_test.html
2025-11-29 20:54:54 +01:00
## Test Categories
| Spec File | Tests | Description |
|-----------|-------|-------------|
2026-06-10 13:40:00 +02:00
| `wxwidgets.spec.ts` | Comprehensive | Full UI interaction, stability |
2025-11-29 20:54:54 +01:00
| `menu.spec.ts` | wxMenuBar | Menu bar visibility and interactions |
| `timer.spec.ts` | wxTimer | Timer start/stop/reset functionality |
| `dialog.spec.ts` | wxDialog | Message boxes and custom dialogs |
| `tree.spec.ts` | wxTreeCtrl | Tree control with expand/collapse |
| `grid.spec.ts` | wxGrid | Grid, SpinCtrl, SearchCtrl |
| `aui.spec.ts` | wxAuiManager | Dockable panels |
| `clipboard.spec.ts` | wxClipboard | Copy/paste operations |
2025-12-03 11:27:00 +01:00
| `dataview.spec.ts` | wxDataViewCtrl | List and tree data views (Zone Manager-like) |
2025-11-29 20:54:54 +01:00
| `filedialog.spec.ts` | wxFileDialog | File open/save dialogs |
2025-12-03 11:27:00 +01:00
| `htmlwin.spec.ts` | wxHtmlWindow | HTML rendering (About dialogs, error formatting) |
2025-11-29 20:54:54 +01:00
| `layout.spec.ts` | wxSplitter | Splitter and scrolled windows |
| `toolbar.spec.ts` | wxToolBar | Toolbar buttons and status bar |
2025-11-29 22:15:56 +01:00
## Debugging WASM Crashes
When a test fails with a WASM crash (e.g., "memory access out of bounds"), you can build with debug symbols to get meaningful stack traces:
### Debug Build
```bash
# Build test apps with DWARF symbols and source maps
../scripts/build-wasm-test.sh --debug
```
This enables:
- `-g` for DWARF debug info
- `-gsource-map` for browser source maps
- `-O0` for no optimization (preserves debugging context)
### Reading Stack Traces
With a debug build, WASM stack traces show actual function names:
**Before (release build):**
```
RuntimeError: memory access out of bounds
at wasm-function[102]:0xfdf8
at wasm-function[99]:0xe6e0
```
**After (debug build):**
```
RuntimeError: memory access out of bounds
at grid_test.wasm.GridTestFrame::LogEvent(wxString const&)
at grid_test.wasm.GridTestFrame::OnGridCellSelect(wxGridEvent&)
at grid_test.wasm.wxEventFunctorMethod<...>::operator()
```
### Using LLVM Tools
For deeper analysis, use Emscripten's LLVM tools:
```bash
LLVM_DIR="/opt/homebrew/Cellar/emscripten/4.0.20/libexec/llvm/bin"
# Check if WASM has DWARF info
2025-12-27 14:06:23 +01:00
$LLVM_DIR/llvm-dwarfdump --debug-info apps/standalone/grid/grid_test.wasm
2025-11-29 22:15:56 +01:00
# Disassemble with function names
$LLVM_DIR/llvm-objdump -d grid_test.wasm | head -200
```
2025-12-29 12:11:33 +01:00
## Element Registry (Recommended)
2025-12-03 10:19:50 +01:00
2025-12-29 12:11:33 +01:00
The wxWidgets WASM port includes an element registry that tracks all wxWindow instances with their positions, labels, and types. This enables tests to find UI elements by semantic identifiers instead of hardcoded pixel coordinates.
### Usage
```typescript
import { waitForRegistry, clickByLabel, findByLabel, findByType } from './utils/fixtures';
// Wait for registry to be available
await waitForRegistry(page);
// Click buttons by label text
await clickByLabel(page, 'Copy to Clipboard');
await clickByLabel(page, 'Save File...');
// Find elements for inspection
const button = await findByLabel(page, 'OK');
if (button) {
console.log(`Button at (${button.centerX}, ${button.centerY})` );
}
// Find all elements of a type
const buttons = await findByType(page, 'wxButton');
```
### Available Functions
| Function | Description |
|----------|-------------|
| `waitForRegistry(page)` | Wait for element registry to initialize |
| `findByLabel(page, label, options?)` | Find element by label text |
| `findByName(page, name, options?)` | Find element by wxWindow name |
| `findByType(page, typeName, options?)` | Find all elements of a type (e.g., 'wxButton') |
| `clickByLabel(page, label, options?)` | Click element by label |
| `clickByName(page, name, options?)` | Click element by name |
### Options
```typescript
interface FindOptions {
visible?: boolean; // Filter by visibility (default: true)
enabled?: boolean; // Filter by enabled state
exact?: boolean; // Exact label match (default: substring)
type?: string; // Filter by type name
}
```
### When to Use
Use the element registry for tests that click on **wxButton ** and other wxWindow-based controls. The registry tracks:
- wxButton, wxTextCtrl, wxStaticText, wxPanel, wxFrame, etc.
**Not trackable** (use pixel coordinates instead):
- wxToolBar tool items (rendered by toolbar)
- wxMenuBar menu items (rendered by menu system)
- wxAuiManager panel controls (title bars, close buttons)
- wxGrid cells (rendered by grid)
- wxSplitterWindow sash (rendered by splitter)
### Migrated Tests
These tests use the element registry:
- `clipboard.spec.ts` - Copy, Paste, Check, Clear buttons
- `dialog.spec.ts` - Info, Yes/No, Error, Custom dialog buttons
- `timer.spec.ts` - Start, Stop, Reset buttons
- `filedialog.spec.ts` - Open, Save, Open Multiple buttons
- `logerror.spec.ts` - Trigger Error, Flush Log buttons
2025-12-03 10:19:50 +01:00
### Available Test Apps
| App URL | Description |
|---------|-------------|
| `/standalone/clipboard/clipboard_test.html` | Copy, Paste, Check, Clear buttons |
2025-12-03 11:27:00 +01:00
| `/standalone/dataview/dataview_test.html` | wxDataViewListCtrl and wxDataViewTreeCtrl (Zone Manager-like data) |
2025-12-03 10:19:50 +01:00
| `/standalone/dialog/dialog_test.html` | Info, Yes/No, Error, Custom dialog buttons |
2025-12-03 11:27:00 +01:00
| `/standalone/htmlwin/htmlwin_test.html` | wxHtmlWindow with various HTML content |
2025-12-03 10:19:50 +01:00
| `/standalone/tree/tree_test.html` | Expand All, Collapse All, etc. |
| `/standalone/menu/menu_test.html` | Menu bar testing |
| `/standalone/grid/grid_test.html` | Grid controls |
| `/standalone/aui/aui_test.html` | AUI panel controls |
| `/standalone/toolbar/toolbar_test.html` | Toolbar buttons |
| `/standalone/timer/timer_test.html` | Timer controls |
| `/standalone/filedialog/filedialog_test.html` | File dialog buttons |
| `/standalone/layout/layout_test.html` | Layout controls |
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `APP_URL` | (required) | URL path to scan |
| `STEP` | `10` | Pixel step size for scanning (smaller = more accurate but slower) |
| `START_X` | `0` | X coordinate to start scanning |
| `END_X` | canvas width | X coordinate to end scanning |
| `START_Y` | `0` | Y coordinate to start scanning |
| `END_Y` | canvas height | Y coordinate to end scanning |
### Output
The utility outputs:
- Button positions with labels (from console log keywords)
- Generated test code snippets
- Results JSON file at `test-results/button-finder-results.json`
Example output:
```
RESULTS: Found 4 buttons
Button positions (relative to canvas):
Copy at (352, 196)
Log: [CLIPBOARD_EVENT] Attempting to copy text to clipboard...
Paste at (600, 196)
Log: [CLIPBOARD_EVENT] Attempting to paste from clipboard...
```
2025-11-29 20:54:54 +01:00
## Known Issues
- **Timer tests**: May fail due to timing sensitivity
- **Tree tests**: Button click positions may vary
2026-06-29 19:50:18 +02:00
## Open tasks
- **Research: are the Asyncify fiber shims still needed under native-EH?** Two
`scripts/common/inject-dyncall-shims.sh` fixes — the fiber trampoline self-heal
(§3c) and the nested-Asyncify handleSleep save/restore (§3) — were written for the
legacy-EH * park-throw * model. Under native wasm-EH the top loop is a per-frame-yield
while-loop with no park-throw, so ablating either shim
(`SHIM_DISABLE_TRAMPOLINE_HEAL` / `SHIM_DISABLE_HANDLESLEEP` , wired in
`tests/apps/Makefile.wasm` ) no longer reproduces the disease it guarded — the old
red "ablation pins" in `asyncify/asyncify-races.spec.ts` have been flipped to
green "shim-redundancy pins" that now prove the native-EH path stays clean *with the
shim ablated*. **To do: ** sweep the real apps (modals, nested fibers, long sleeps,
pthread pool) with each shim ablated; if all stay green, drop the shim injection and
these pins. Until proven, they stay injected (belt-and-suspenders).
test(ysync): repro tests for review bugs 01-07 + v2 items-wire e2e port (miss 11)
The 2026-07-02 sync review (docs/features/ysync-review, ysync-review branch)
found 7 bugs and that the two-tab e2e only exercised the DEAD legacy scalar
wire. This lands plan doc 15 in full; results + empirical findings in doc 16.
- tests/collab/browser-entry-v2.ts (+build.mjs): the PRODUCTION v2 stack
bundled for e2e (connectKicadDoc + attachKicadCollab, kdoc_* keys), with
in-page renderActiveDoc/singleSeedRender/driftReport helpers and yjs forced
to ONE copy (the two web pnpm workspaces otherwise bundle two
instanceof-incompatible instances).
- tests/kicad/ysync-two-tab.spec.ts: pl_editor green baseline (A↔B edits,
ITEM-level drift silence) + divergent-uuid adopt; bug-01 pcb/ee fresh-room
repros (Chromium-only: two kicad_editor tabs exceed Firefox's per-process
wasm budget); bug-06 concurrent-seed race; bug-03 Y-half.
- tests/kicad/ysync-repros-{pcbnew,eeschema}.spec.ts: bugs 02/03/05 + the
bug-04 matrix (anchor-centred fp rotation, pad resize, endpoint drag,
symbol rotation, Value-field edit), each with green landed-preconditions;
the "local move emits" controls double as headless-emit probes — GREEN on
both tools, so every emit-dependent repro is a live test.fail.
- wasm/bindings: 7 local-edit test hooks via real commits
(CallAfter+COROUTINE) — TestRemoveItem/TestRotateItem (both tools,
dispatched in the merged image), TestSetPadSize/TestMoveEndpoint (pcbnew),
TestSetFieldText (eeschema).
- web/standalone ysync-repros.test.ts: bug-01 units (C++-faithful fake gating
emit on ensureBridge) + bug-07a/b (stale DOWN hook, real sheet-manager gap).
- web/pcbjam-shared bump: bug-03/06 unit repros.
Convention: every repro asserts the CORRECT behavior and is expected-fail
(test.fail/it.fails) naming its bug doc; a fix flips it to "unexpected pass",
forcing marker removal — the repro becomes the regression test. Every
expected failure verified (JSON reporter) to fail at its documented assert.
Suite state: 39 passed / 0 failed / 0 flaky / 5 skipped (2 firefox guards,
2 pre-existing legacy two-tab skips, 1 pre-existing roundtrip fixme).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DPfrVhfYgPPgtawjSssZfn
2026-07-03 12:43:35 +02:00
## Collab e2e — legacy vs v2 bundles, and repro markers
Two esbuild bundles (`npm run build:collab` , rebuilt by the specs' `beforeAll` ):
- `apps/kicad/collab-bundle.js` — the LEGACY scalar wire (`startCollab` /
`kicadCollabSnapshot/Apply` / `onDelta` ). Dead in production (nothing registers
`onDelta` ); driven by the pre-existing `*-collab.spec.ts` two-tab tests. Kept only
until the scalar wire is deleted.
- `apps/kicad/collab-bundle-v2.js` — the PRODUCTION v2 "items" stack
(`bindKicadCollab` over `kicadCollabSnapshotItems/ApplyItems/onItems` , Y keys
`kdoc_*` ), from `collab/browser-entry-v2.ts` . Driven by `kicad/ysync-two-tab.spec.ts` .
The build aliases `yjs` to ONE copy (the two web pnpm workspaces otherwise bundle two,
and Y types are instanceof-checked).
### ysync repro tests
`kicad/ysync-two-tab.spec.ts` , `kicad/ysync-repros-{pcbnew,eeschema}.spec.ts` reproduce
the bugs of the 2026-07-02 Yjs⇄KiCad sync review (`docs/features/ysync-review/` on the
`ysync-review` branch; unit-level repros live in
`web/pcbjam-shared/test/ysync-repros.test.ts` and
`web/standalone/src/wasm/collab/ysync-repros.test.ts` ).
Convention: a repro asserts the CORRECT behavior and is marked expected-fail
(`test.fail()` / vitest `it.fails` ) with a comment naming the bug doc. The suite stays
green while the bug is open; fixing the bug flips the repro to "unexpected pass",
forcing the marker's removal — the repro becomes the regression test. Green companion
tests pin each repro's preconditions (harness, apply path, emit path) so an expected
failure can only come from the bug itself. The "local move emits" controls double as
the headless-emit probes gating the emit-dependent repros.
Follow-up (tracked in review miss 11): once the v2 specs are trusted, un-skip/retire
the legacy two-tab specs together with the legacy wire.