2025-11-28 13:10:05 +01:00
{
"name" : "kicad-wasm-tests" ,
"version" : "1.0.0" ,
2025-12-12 15:34:15 +01:00
"description" : "Playwright tests for wxWidgets WASM and KiCad" ,
2025-11-28 13:10:05 +01:00
"scripts" : {
2026-06-13 14:10:05 +02:00
"test" : "npm run test:wx && npm run test:asyncify:firefox" ,
"test:wx" : "playwright test" ,
2025-11-28 13:10:05 +01:00
"test:ui" : "playwright test --ui" ,
"test:headed" : "playwright test --headed" ,
2025-12-27 14:06:23 +01:00
"build-wasm" : "cd apps && make -f Makefile.wasm" ,
feat(pl_editor): Yjs collaborative bridge — differ/apply + generic reconciler (yjs-bridge commit 2)
Bidirectional bridge between pl_editor's DS_DATA_MODEL and a Yjs doc, two same-origin
tabs syncing over BroadcastChannel. Full architecture in features/yjs-bridge/0001-0002.
C++ (wasm layer, wasm/bindings/pl_editor_embind.cpp — public DS_DATA_MODEL API only,
zero added fork divergence beyond the OnModify hook):
- snapshot-differ ChangeSource: diff model vs last-emitted snapshot on OnModify,
emit per-item delta JSON via EM_ASM window.kicadCollab.onDelta
- kicadCollabApply(json): apply remote delta by uuid — scalars (text/segment/rect)
by field, polygon/bitmap via SetPageLayout-append blob; reseed snapshot + HardRedraw
- kicadCollabSnapshot() (seed/baseline), s_applyingRemote echo guard, and a
kicadCollabTestAddText() PoC local-edit hook
- wire format: {added:[item],changed:[item],removed:[uuid]}, item = {id,type,...fields}
JS (web/apps/frontend/src/wasm/collab/, generic + schema-agnostic):
- reconciler: uuid-keyed Y.Map of per-item Y.Map; down = onDelta→Y, up = observe→apply,
origin-tagged echo suppression; seed-once join adopts the doc authoritatively
- broadcast-transport: minimal BroadcastChannel Yjs provider (query/state catch-up)
- WasmTool wiring behind ?collab=1 (pl_editor only); gated debug logging
Tests: tests/kicad/pl_editor-collab.spec.ts — single-page C++ contract (snapshot/apply
changed+removed+added/echo-suppression) + two-tab BroadcastChannel A<->B propagation.
Reconciler+yjs bundled via esbuild (tests/collab/build.mjs, npm run build:collab).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:25:22 +02:00
"build:collab" : "node collab/build.mjs" ,
2025-12-27 14:06:23 +01:00
"serve" : "npx serve apps -p 8080 -c ../serve.json" ,
2025-12-12 15:34:15 +01:00
"setup:kicad" : "./scripts/setup-kicad-wasm.sh" ,
2026-06-03 08:52:34 +02:00
"test:web" : "npm run setup:kicad && playwright test --config=playwright-web.config.ts --project=firefox" ,
"test:web:headed" : "npm run setup:kicad && playwright test --config=playwright-web.config.ts --project=chromium --headed" ,
feat(mobile): Figma-like hide-UI toggle — Cmd/Ctrl+\ hotkey + floating button, snapshot-exact chrome restore
Chrome visibility is now a runtime toggle on any device instead of being
device-wired: mobile defaults to canvas-only, desktop to full UI, and the
floating top-right button (or Figma's Cmd/Ctrl+\ chord — free in KiCad,
only bare \ is bound) flips between them live.
- chrome-visibility.ts: module-global store (default isMobileMode(),
session-only) + pure hotkey matcher (rejects AltGr backslash + repeats)
- WasmTool: capture-phase hotkey (stopped before the wx layer), floating
toggle pill (matches the comment FAB design), useLayoutEffect apply with
sync first call + retry; overlays follow the toggle, capability-gated on
the kicad_editor bundle's kicadSetChrome export
- boot.ts mobile opt now installs touch gestures only
- kicadSetChrome: frame-keyed hide-time snapshot so restore re-shows ONLY
what hide took away (blanket Show(true) surfaced KiCad's default-hidden
Search/Properties/Net-Inspector panes); toolbars-only fallback otherwise
- tests: chrome-toggle.spec.ts (desktop hide/restore + geometric
restore-exactness ±3px), mobile toggle round-trip, 12 new unit tests,
test:web:mobile npm script
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 16:11:19 +02:00
"test:web:mobile" : "npm run setup:kicad && playwright test --config=playwright-web.config.ts --project=mobile-chromium --headed" ,
2026-05-25 15:07:10 +02:00
"test:kicad:firefox" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox" ,
"test:kicad:chrome" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed" ,
"test:kicad" : "npm run test:kicad:firefox" ,
fix(e2e): rescope the 3D fixes for the webgl-era viewer (rebased onto main)
The 3d-webgl merge (kicad eb13ff3bdc: the viewer now defaults to the real
OpenGL renderer via wasm/gl1, and occ-split moves STEP parsing into the
occ_service worker) made the raytracer-era orchestration on this branch moot —
main's chromium-ci phase is green at 15-way parallelism (28666407570 /
28698861536). Drop what no longer earns its complexity, keep the diagnostics,
fix main's live flake, and make the deadlock spec test what it was written for.
- REVERT the chromium-ci-3d serial project, the two-phase test:kicad:ci, the
SwiftShader GPU-process flags, and the resize-drag/models skips: config and
package.json are byte-for-byte back to main's shape. The raytracer contention
they guarded is no longer on the CI path.
- FIX main's live flake: run 28698861536 is green only via retry
(3d-viewer.spec:26 flaky) and 28666407570's deadlock red sampled an ALL-ZERO
pixel signature — the viewer's first frame lags the canvas's creation on
software WebGL under parallel load, and sampling too early reads an all-black
backbuffer. New waitForThreeDRender() gates render assertions on actual
pixels (1s-interval full-frame CPU reads) instead of fixed sleeps, used by
3d-viewer.spec:26 and the models render tail.
- KEEP the storm-proofed samplers (one full-frame getImageData on a
willReadFrequently canvas replacing 256 per-pixel GPU round-trips per sample
— the "GPU stall due to ReadPixels" trigger) and the logThreeDDiag
instrumentation: engine-independent, and they de-risk every remaining
software-GL pixel read.
- models spec: bridge assertions stay front-loaded (the protocol regression
signal is independent of the render); the occ_service parse verdict is now
POLLED — it lands async relative to the bridge ensures, so asserting it
immediately raced the worker; the render tail runs again everywhere. (The
pre-webgl raytracer+models renderer-death documented in a17f3be does not
affect the OpenGL default path — the raytracer-toggle+models combination
remains untested product surface, tracked outside this branch.)
- deadlock spec: the deadlock it guards is raytracer-specific and the viewer
now defaults to OpenGL — on the GL engine it either passes vacuously (fast
renders make every liveness assertion trivial, 28698861536) or fails on the
black first frame (28666407570). It now flips the engine via the "Use
raytracing" toolbar toggle (loud assert if the toggle moved) and
cross-checks engagement by requiring the canvas pixels to CHANGE after the
flip with no input in between (the raytraced frame is lit differently; a GL
re-render reproduces identical pixels; heap growth is unusable — mimalloc
satisfies the raytracer from freed arena pages). That guard immediately
caught a REAL defect: on the webgl-era wasm build the toggle is INERT (the
click lands and "Reload time" updates, but the canvas never changes —
suspects: DoRePaint's silent catch(runtime_error) freezing the canvas after
a raytracer Redraw throw, or ToggleRaytracing writing m_boardAdapter.m_Cfg
while RenderEngineChanged() reads GetAppSettings<…>(), possibly different
instances in the merged bundle). The spec is therefore test.skip-annotated
as a KNOWN ISSUE with the full engine-force machinery in place — unskipping
it self-validates the product fix. The CI-skip also stays (raytracer
liveness needs real-GPU pacing; the Worker-boot deadlock mechanism is
covered on CI by the standalone wx harnesses).
- 180s viewer-open waits kept as pure CI headroom (never slow a passing run).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 11:37:27 +02:00
"test:kicad:ci" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox --project=chromium-ci" ,
test(wasm): coroutine crash reproduction harness + per-engine runner
Investigation scaffolding for the Chrome-only KiCad coroutine renderer crash.
Adds isolated reproduction probes exercising the coroutine/Asyncify/fiber layer
under KiCad-like conditions, runnable in BOTH Firefox and system Chrome.
- tests/playwright-coroutine.config.ts + test:coroutine:firefox|chrome npm
scripts: run the coroutine specs in Firefox AND system Chrome (the old e2e
config only used bundled Chromium, which never reproduced the crash).
- tests/apps/standalone/coroutine-pthread/: no-wx + pthreads reproduction probes
(fiber-in-main, nested invoke_/dynCall boundaries, RunMainStack, embind,
main-loop/rAF activation) + worker_dom_stub.js for wx+pthreads builds.
- tests/apps/Makefile.wasm: coroutine-pthread{,-main,-nested,-nested-ex,-wx,
-embind,-mainloop} targets.
- scripts/common/shims/diagnostics.js: add EM_ASYNC_JS handleSleep enter/wake
tracking (DIAG_SLEEP) to detect nested-async at the crash.
Findings (details in research notes): every isolated factor so far — direct /
nested / RunMainStack fiber, wx event loop + all 13 scenarios incl EM_ASYNC_JS,
pthreads, and main-loop/rAF activation — runs CLEAN in system Chrome. The
coroutine/Asyncify layer is exonerated; GL/WebGL is the remaining untested factor
(next). The reliable FF-pass/Chrome-fail repro is still the KiCad pcbnew e2e.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 18:44:12 +02:00
"test:kicad:headed" : "npm run test:kicad:chrome" ,
2026-07-01 14:43:54 +02:00
"test:perf" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=perf --workers=1" ,
2026-05-29 06:39:23 +02:00
"test:pcbnew:firefox" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox kicad/pcbnew.spec.ts" ,
"test:pcbnew:chrome" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed kicad/pcbnew.spec.ts" ,
"test:eeschema:firefox" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox kicad/eeschema.spec.ts" ,
"test:eeschema:chrome" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed kicad/eeschema.spec.ts" ,
"test:eeschema" : "npm run test:eeschema:firefox" ,
"test:eeschema:headed" : "npm run test:eeschema:chrome" ,
2026-05-29 12:01:44 +02:00
"test:calculator:firefox" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox kicad/calculator.spec.ts" ,
"test:calculator:chrome" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed kicad/calculator.spec.ts" ,
"test:calculator" : "npm run test:calculator:firefox" ,
"test:calculator:headed" : "npm run test:calculator:chrome" ,
2026-06-02 13:35:37 +02:00
"test:symbol_editor:firefox" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox kicad/symbol_editor.spec.ts" ,
"test:symbol_editor:chrome" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed kicad/symbol_editor.spec.ts" ,
"test:symbol_editor" : "npm run test:symbol_editor:firefox" ,
"test:symbol_editor:headed" : "npm run test:symbol_editor:chrome" ,
2026-06-03 14:23:57 +02:00
"test:gerbview:firefox" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=firefox kicad/gerbview.spec.ts" ,
"test:gerbview:chrome" : "npm run setup:kicad && playwright test --config=playwright-kicad.config.ts --project=chromium --headed kicad/gerbview.spec.ts" ,
"test:gerbview" : "npm run test:gerbview:firefox" ,
"test:gerbview:headed" : "npm run test:gerbview:chrome" ,
test(wasm): coroutine crash reproduction harness + per-engine runner
Investigation scaffolding for the Chrome-only KiCad coroutine renderer crash.
Adds isolated reproduction probes exercising the coroutine/Asyncify/fiber layer
under KiCad-like conditions, runnable in BOTH Firefox and system Chrome.
- tests/playwright-coroutine.config.ts + test:coroutine:firefox|chrome npm
scripts: run the coroutine specs in Firefox AND system Chrome (the old e2e
config only used bundled Chromium, which never reproduced the crash).
- tests/apps/standalone/coroutine-pthread/: no-wx + pthreads reproduction probes
(fiber-in-main, nested invoke_/dynCall boundaries, RunMainStack, embind,
main-loop/rAF activation) + worker_dom_stub.js for wx+pthreads builds.
- tests/apps/Makefile.wasm: coroutine-pthread{,-main,-nested,-nested-ex,-wx,
-embind,-mainloop} targets.
- scripts/common/shims/diagnostics.js: add EM_ASYNC_JS handleSleep enter/wake
tracking (DIAG_SLEEP) to detect nested-async at the crash.
Findings (details in research notes): every isolated factor so far — direct /
nested / RunMainStack fiber, wx event loop + all 13 scenarios incl EM_ASYNC_JS,
pthreads, and main-loop/rAF activation — runs CLEAN in system Chrome. The
coroutine/Asyncify layer is exonerated; GL/WebGL is the remaining untested factor
(next). The reliable FF-pass/Chrome-fail repro is still the KiCad pcbnew e2e.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 18:44:12 +02:00
"test:coroutine:firefox" : "playwright test --config=playwright-coroutine.config.ts --project=firefox" ,
2026-06-12 16:59:07 +02:00
"test:coroutine:chrome" : "playwright test --config=playwright-coroutine.config.ts --project=chromium --headed" ,
"test:asyncify:firefox" : "playwright test --config=playwright-asyncify.config.ts --project=firefox" ,
2026-06-29 19:50:18 +02:00
"test:asyncify:chrome" : "playwright test --config=playwright-asyncify.config.ts --project=chromium --headed" ,
"test:asyncify:safari" : "playwright test --config=playwright-asyncify.config.ts --project=webkit" ,
2026-07-01 18:01:15 +02:00
"test:asyncify:all" : "npm run test:asyncify:firefox && npm run test:asyncify:safari && npm run test:asyncify:chrome" ,
2026-07-14 10:57:46 +02:00
"corpus:lint" : "tsx tools/corpus-lint.ts" ,
2026-07-07 10:50:24 +02:00
"lint:determinism" : "tsx tools/lint-determinism.ts" ,
2026-07-01 18:01:15 +02:00
"screenshots:check" : "tsx tools/screenshots/compare.ts" ,
"screenshots:promote" : "tsx tools/screenshots/promote.ts" ,
"screenshots:noise" : "tsx tools/screenshots/noise.ts" ,
"screenshots:report" : "tsx tools/screenshots/post-discord.ts" ,
"screenshots:changelog" : "tsx tools/screenshots/changelog.ts" ,
2026-07-03 09:26:43 +02:00
"screenshots:manifest" : "tsx tools/screenshots/gen-manifest.ts" ,
"3d:compare" : "tsx tools/screenshots/compare-dirs.ts" ,
"3d:check" : "tsx tools/screenshots/compare-dirs.ts --old 3d-regression/baseline --new 3d-regression/output/native --out 3d-regression/output/diff/native-self --floors 3d-regression/floors.json --level native-self --label 3d-native --fail-on-change" ,
"3d:check:webgl" : "tsx tools/screenshots/compare-dirs.ts --old 3d-regression/baseline-webgl --new 3d-regression/output/webgl --out 3d-regression/output/diff/webgl-self --floors 3d-regression/floors.json --level webgl-self --label 3d-webgl --fail-on-change" ,
"3d:check:parity" : "tsx tools/screenshots/compare-dirs.ts --old 3d-regression/baseline --new 3d-regression/output/webgl --out 3d-regression/output/diff/parity --floors 3d-regression/floors.json --level webgl-vs-native --label 3d-parity" ,
2026-07-03 15:25:51 +02:00
"3d:review" : "tsx tools/screenshots/compare-dirs.ts --old 3d-regression/baseline --new 3d-regression/output/webgl --out 3d-regression/output/diff/parity-review --floors 3d-regression/floors.json --level webgl-vs-native --label 3d-parity --artifacts always" ,
2026-07-14 13:54:11 +02:00
"3d:test:webgl" : "playwright test e2e/3d-webgl.spec.ts" ,
"tools:contract" : "tsx tools/cli-contract.ts"
2025-11-28 13:10:05 +01:00
} ,
"devDependencies" : {
"@playwright/test" : "^1.40.0" ,
2025-11-29 12:39:30 +01:00
"@types/node" : "^24.10.1" ,
2026-07-01 18:01:15 +02:00
"@types/pixelmatch" : "^5.2.6" ,
"@types/pngjs" : "^6.0.5" ,
feat(pl_editor): Yjs collaborative bridge — differ/apply + generic reconciler (yjs-bridge commit 2)
Bidirectional bridge between pl_editor's DS_DATA_MODEL and a Yjs doc, two same-origin
tabs syncing over BroadcastChannel. Full architecture in features/yjs-bridge/0001-0002.
C++ (wasm layer, wasm/bindings/pl_editor_embind.cpp — public DS_DATA_MODEL API only,
zero added fork divergence beyond the OnModify hook):
- snapshot-differ ChangeSource: diff model vs last-emitted snapshot on OnModify,
emit per-item delta JSON via EM_ASM window.kicadCollab.onDelta
- kicadCollabApply(json): apply remote delta by uuid — scalars (text/segment/rect)
by field, polygon/bitmap via SetPageLayout-append blob; reseed snapshot + HardRedraw
- kicadCollabSnapshot() (seed/baseline), s_applyingRemote echo guard, and a
kicadCollabTestAddText() PoC local-edit hook
- wire format: {added:[item],changed:[item],removed:[uuid]}, item = {id,type,...fields}
JS (web/apps/frontend/src/wasm/collab/, generic + schema-agnostic):
- reconciler: uuid-keyed Y.Map of per-item Y.Map; down = onDelta→Y, up = observe→apply,
origin-tagged echo suppression; seed-once join adopts the doc authoritatively
- broadcast-transport: minimal BroadcastChannel Yjs provider (query/state catch-up)
- WasmTool wiring behind ?collab=1 (pl_editor only); gated debug logging
Tests: tests/kicad/pl_editor-collab.spec.ts — single-page C++ contract (snapshot/apply
changed+removed+added/echo-suppression) + two-tab BroadcastChannel A<->B propagation.
Reconciler+yjs bundled via esbuild (tests/collab/build.mjs, npm run build:collab).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:25:22 +02:00
"esbuild" : "^0.28.0" ,
2026-07-01 18:01:15 +02:00
"pixelmatch" : "^5.3.0" ,
"pngjs" : "^7.0.0" ,
2025-11-29 12:39:30 +01:00
"serve" : "^14.2.0" ,
2026-07-01 18:01:15 +02:00
"tsx" : "^4.22.4" ,
feat(pl_editor): Yjs collaborative bridge — differ/apply + generic reconciler (yjs-bridge commit 2)
Bidirectional bridge between pl_editor's DS_DATA_MODEL and a Yjs doc, two same-origin
tabs syncing over BroadcastChannel. Full architecture in features/yjs-bridge/0001-0002.
C++ (wasm layer, wasm/bindings/pl_editor_embind.cpp — public DS_DATA_MODEL API only,
zero added fork divergence beyond the OnModify hook):
- snapshot-differ ChangeSource: diff model vs last-emitted snapshot on OnModify,
emit per-item delta JSON via EM_ASM window.kicadCollab.onDelta
- kicadCollabApply(json): apply remote delta by uuid — scalars (text/segment/rect)
by field, polygon/bitmap via SetPageLayout-append blob; reseed snapshot + HardRedraw
- kicadCollabSnapshot() (seed/baseline), s_applyingRemote echo guard, and a
kicadCollabTestAddText() PoC local-edit hook
- wire format: {added:[item],changed:[item],removed:[uuid]}, item = {id,type,...fields}
JS (web/apps/frontend/src/wasm/collab/, generic + schema-agnostic):
- reconciler: uuid-keyed Y.Map of per-item Y.Map; down = onDelta→Y, up = observe→apply,
origin-tagged echo suppression; seed-once join adopts the doc authoritatively
- broadcast-transport: minimal BroadcastChannel Yjs provider (query/state catch-up)
- WasmTool wiring behind ?collab=1 (pl_editor only); gated debug logging
Tests: tests/kicad/pl_editor-collab.spec.ts — single-page C++ contract (snapshot/apply
changed+removed+added/echo-suppression) + two-tab BroadcastChannel A<->B propagation.
Reconciler+yjs bundled via esbuild (tests/collab/build.mjs, npm run build:collab).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:25:22 +02:00
"typescript" : "^5.9.3" ,
"yjs" : "^13.6.31"
2025-11-28 13:10:05 +01:00
}
}