refactor: collapse dual-mode plumbing — the DOM port is the only WASM build

The canvas (wxUniversal) mode is gone (wxwidgets submodule); remove every
piece of side-by-side plumbing so there is exactly one build and one test
flow:

- scripts/build-wxuniversal-wasm.sh -> scripts/build-wx-wasm.sh; no
  --dom/--enable-universal; builds into build-wasm/wxwidgets
- build-wasm-test.sh: no DOM_BUILD / apps-dom rsync mirror / PORT=dom;
  apps build straight into tests/apps (Makefile.wasm PORT conditionals
  collapsed; wx.js + wx-dom.js always pre-js)
- docker/build.sh, build-kicad-target.sh, env.sh: WX_PORT / -dom /
  -universal suffixes removed; kicad builds to kicad-<app>, outputs to
  output/; wx.js/wx-dom.js copied from the real source path
  (/workspace/wxwidgets/build/wasm — the old build-wasm path never
  existed and silently failed)
- setup-kicad-wasm.sh: single target dir; the perl wx-dom.js injection is
  gone — the 7 checked-in kicad pages now reference wx-dom.js directly
- playwright configs serve apps/; fixtures drop the test-results/dom and
  logs/wxwidgets/dom namespacing; boot.spec asserts wxDomPort
  unconditionally; pcbnew.spec uses one reference image;
  appearance.spec assertions unconditional
- compare/update-baseline-screenshots.sh: --port removed
- tests/gal-regression/wasm/Makefile: links build-wasm/wxwidgets and
  carries wx-dom.js as a second pre-js — the gal-webgl suite (30 specs)
  now actually builds and runs here (it needed host-side boost+glm via
  scripts/deps; the bundle had been missing, timing the whole spec out)
- tests: clickCanvas() dispatches via page.mouse (DOM widgets
  legitimately cover the canvas; locator actionability refused the
  click); the comprehensive spec drives wxChoice through its native
  <select> (browser-owned popup cannot be coordinate-clicked)
- docs: README/CLAUDE.md/build.md script names and dirs;
  features/wx-dom-port README reframed (DOM is THE port), visual-notes
  bugs 26-28; FindwxWidgets.cmake config label drops 'wasmuniv'
- wxwidgets submodule -> 9dbacc9448 (DOM-only port, fork diff shrunk)

Gate: full wx e2e suite 292 passed / 1 skipped / 0 failed — first run
ever with the gal-webgl specs green (28 scenarios + load + sequential).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2026-06-12 10:18:16 +02:00
commit 7dc1bf27e0
32 changed files with 199 additions and 335 deletions

1
.gitignore vendored
View file

@ -52,7 +52,6 @@ wxwidgets-clean/
/tests/apps/kicad/*.wasm
# DOM-port test bundles (mirrored + built by scripts/build-wasm-test.sh --dom)
/tests/apps-dom/
# Worktree-local canvas reference snapshot for regression gating (see
# features/wx-dom-port docs); derived from a local run, not a project asset

View file

@ -13,7 +13,7 @@ Always check screenshots for validating tests
Run e2e tests from /tests folder: `npm run test:kicad` or `npm run test:e2e` (not playwright directly)
Build kicad with docker/build.sh (includes wxwidgets build, runs in docker)
Build wxwidgets standalone with scripts/build-wxuniversal-wasm.sh (runs on machine, for wxwidgets-only changes)
Build wxwidgets standalone with scripts/build-wx-wasm.sh (runs on machine, for wxwidgets-only changes)
Build CPP wxwidgets tests with scripts/builds-wasm-test.sh
The build scripts pipe their outputs into log files so that they won't clog the LLM context.
Don't pipe outputs, just run the scripts. Maybe with flex if you need that.

View file

@ -14,7 +14,7 @@ git submodule update --init --recursive
./docker/build.sh
# 3. Build wxWidgets for local testing
./scripts/build-wxuniversal-wasm.sh
./scripts/build-wx-wasm.sh
# 4. Build wxWidgets test apps
./scripts/build-wasm-test.sh
@ -29,7 +29,7 @@ npm run test:kicad # KiCad tests (2 tests)
```bash
# Requires: Node.js 18+ (Emscripten SDK auto-installed on first build)
./scripts/build-wxuniversal-wasm.sh
./scripts/build-wx-wasm.sh
./scripts/build-wasm-test.sh
cd tests && npm install && npm test
```
@ -48,7 +48,7 @@ kicad-wasm/
│ ├── shims/ # Runtime JavaScript shims
│ └── stubs/ # Stub implementations (libgit2, curl)
├── scripts/ # Build scripts
│ ├── build-wxuniversal-wasm.sh # Build wxWidgets for WASM
│ ├── build-wx-wasm.sh # Build wxWidgets for WASM
│ ├── build-wasm-test.sh # Build wxWidgets test apps
│ ├── deps/ # Dependency build scripts
│ ├── kicad/ # KiCad build scripts
@ -98,7 +98,7 @@ Build standalone wxWidgets test apps for feature testing:
```bash
# Build wxWidgets for WASM
./scripts/build-wxuniversal-wasm.sh
./scripts/build-wx-wasm.sh
# Build test apps
./scripts/build-wasm-test.sh

View file

@ -178,20 +178,14 @@ compile_app() {
echo ""
echo "=== Building ${app} (${index}/${total}) ==="
# WX_PORT=dom builds against the DOM wxWidgets into separate build and
# output trees (kicad-<app>-dom, output/dom/).
local out_dir
out_dir=$(out_dir_for)
local out_dir="output"
local kicad_build="kicad-${app}"
if [[ "${WX_PORT:-}" == "dom" ]]; then
kicad_build="kicad-${app}-dom"
fi
# Run build inside the container.
# -e EMSDK=/emsdk: `docker compose exec` bypasses the entrypoint that sources
# emsdk_env.sh, so the build shell would lack emcc/embuilder on PATH. Setting
# EMSDK lets scripts/common/env.sh source /emsdk/emsdk_env.sh and activate the toolchain.
docker compose -f docker/docker-compose.yml exec -e EMSDK=/emsdk -e WX_PORT="${WX_PORT:-}" \
docker compose -f docker/docker-compose.yml exec -e EMSDK=/emsdk \
kicad-wasm-builder \
"/workspace/scripts/kicad/build-${app}.sh" "${ARGS[@]}"
@ -204,8 +198,8 @@ compile_app() {
cp /workspace/build-wasm/${kicad_build}/${subdir}/${app}.{js,wasm,wasm.debug.wasm,wasm.map,worker.js} /workspace/${out_dir}/ 2>/dev/null || \
cp /workspace/build-wasm/${kicad_build}/${subdir}/${app}.{js,wasm} /workspace/${out_dir}/; \
cp /workspace/build-wasm/${kicad_build}/resources/images.tar.gz /workspace/${out_dir}/ 2>/dev/null || true; \
cp /workspace/build-wasm/wxwidgets/build/wasm/wx.js /workspace/${out_dir}/ 2>/dev/null || true; \
if [ -n \"${WX_PORT:-}\" ]; then cp /workspace/build-wasm/wxwidgets/build/wasm/wx-dom.js /workspace/${out_dir}/ 2>/dev/null || true; fi"
cp /workspace/wxwidgets/build/wasm/wx.js /workspace/${out_dir}/ 2>/dev/null || true; \
cp /workspace/wxwidgets/build/wasm/wx-dom.js /workspace/${out_dir}/ 2>/dev/null || true"
# The container runs as root, so files in the bind-mounted ./output land
# root-owned on the host. macOS Docker Desktop remaps ownership to the host
@ -215,24 +209,13 @@ compile_app() {
chown -R "$(id -u):$(id -g)" /workspace/output || true
}
# Output dir for the current flavor: the DOM port (WX_PORT=dom) keeps its
# bundles in output/dom/ so the canvas bundles are never clobbered.
out_dir_for() {
if [[ "${WX_PORT:-}" == "dom" ]]; then
echo "output/dom"
else
echo "output"
fi
}
# Phase 2 of one app: host-side post-processing (dyncall shims, finalize,
# asyncify + -O2). Pure host work on output/${app}.* — independent of the
# container, which is what makes it safe to run in the background while the
# next app compiles.
postprocess_app() {
local app="$1"
local out_dir
out_dir=$(out_dir_for)
local out_dir="output"
# Inject dynCall shims (fixes "dynCall_* is not defined" errors in Emscripten 4.x)
kw_stage dyncall-shims

View file

@ -143,7 +143,7 @@ Import patterns used:
**Full rebuild removes:**
- `build-wasm/stamps/*` - All build stamps
- `build-wasm/deps/*` - All dependency builds
- `build-wasm/wxwidgets-universal` - wxWidgets build
- `build-wasm/wxwidgets` - wxWidgets build
- `build-wasm/sysroot/*` - Installed headers/libraries
- `build-wasm/kicad-pcbnew` - KiCad build
@ -208,7 +208,7 @@ After changing build flags (debug/release), use `--full` to force a complete reb
|--------|---------|
| `docker/build.sh` | Host entry point (starts Docker, runs build) |
| `scripts/kicad/build-pcbnew.sh` | KiCad PCBnew build (runs inside Docker) |
| `scripts/build-wxuniversal-wasm.sh` | wxWidgets build |
| `scripts/build-wx-wasm.sh` | wxWidgets build |
| `scripts/build-wasm-test.sh` | Build wxWidgets test apps |
| `scripts/deps/build-all-deps.sh` | All dependencies |
| `scripts/deps/build-*.sh` | Individual dependency builds |
@ -240,7 +240,7 @@ OpenCASCADE is the longest dependency to build (~30 minutes).
### Incremental build not picking up changes
- For KiCad: use `--clean-kicad` to force rebuild
- For wxWidgets: delete `build-wasm/wxwidgets-universal/Makefile` to force reconfigure
- For wxWidgets: delete `build-wasm/wxwidgets/Makefile` to force reconfigure
### WASM exception with numeric error (e.g., `3788888`)
- Build with debug symbols (default): No `--release` flag

View file

@ -1,65 +1,71 @@
# wxWidgets DOM port (`feature/wx-dom-port`)
A second WASM build mode where wxWidgets widgets are **real HTML elements**
THE wxWidgets WASM port: widgets are **real HTML elements**
(`wxButton``<button>`, `wxTextCtrl``<input>`, menus → popup divs)
instead of wxUniversal pixels on a canvas. Goals: styleable modern UI,
native text input/IME, accessibility, crisp rendering — while the existing
canvas port keeps working unchanged from the same source tree.
instead of pixels on a canvas. Goals: styleable modern UI, native text
input/IME, accessibility, crisp rendering.
Status: COMPLETE (Phases 07). wxWidgets-side port done, both wx e2e
suites green from one tree, and all six KiCad apps (pcbnew, eeschema,
calculator, pl_editor, symbol_editor, gerbview) build and run on the DOM
port — full kicad e2e suite green under `WX_PORT=dom` (28/0, 1 flaky,
2 skipped). Build with `WX_PORT=dom docker/build.sh <app>` (outputs in
`output/dom/`); test with `WX_PORT=dom npm run test:kicad`. Work
intentionally stays on `feature/wx-dom-port` (root + wxwidgets + kicad) —
not merged to `main`/`wasm-port`.
Status: COMPLETE and consolidated (2026-06-12). The original canvas
(wxUniversal) mode and all dual-build plumbing have been **removed**
`--with-wasm` builds the DOM port, full stop. All `src/univ/` +
`include/wx/univ/` modifications were reverted to upstream v3.2.6, so the
wxwidgets fork is upstream + new `src/wasm/` + `include/wx/wasm/` +
`build/wasm/` files + thin hooks. All six KiCad apps (pcbnew, eeschema,
calculator, pl_editor, symbol_editor, gerbview) build and run on it.
Build: `docker/build.sh <app>` (outputs in `output/`), wx standalone via
`scripts/build-wx-wasm.sh`, test apps via `scripts/build-wasm-test.sh`.
Test: `npm test` / `npm run test:kicad` from `tests/` (no env vars).
Work intentionally stays on `feature/wx-dom-port` (root + wxwidgets +
kicad) — not merged to `main`/`wasm-port`.
## Architecture in one page
- **One toolkit, two modes.** `--with-wasm --enable-universal` = canvas
(untouched); `--with-wasm` alone = DOM. Selection is file-level via the
build system: `src/univ/` only in canvas builds, `WASM_SRC` (native
controls) only under `WXUNIV_0`. Source of truth is
`build/bakefiles/files.bkl` → dockerized bakefile regenerates
`Makefile.in`/`autoconf_inc.m4` → autoconf regenerates `configure`
(`scripts/build-wxuniversal-wasm.sh` auto-detects each stage's
staleness; `--dom` flag selects the mode).
- **C++ owns state; the DOM is a projection.** Controls call
`WasmCreateDomNode("type")` once in `Create()`; the shared
`wxWindowWasm` machinery then syncs geometry (TLW-relative absolute
positioning, recursing into descendants), visibility
(`IsShownOnScreen`, whole subtree), enabled/font/focus/destruction.
DOM-native state (typed text, checked, selection) syncs back through
events into C++ caches so getters stay synchronous.
positioning, recursing into descendants; ancestor-viewport clipping via
`clip-path`), visibility (`IsShownOnScreen`, whole subtree),
enabled/font/focus/destruction. DOM-native state (typed text, checked,
selection) syncs back through events into C++ caches so getters stay
synchronous. NB: `GetScreenPosition()` is the CLIENT-AREA origin
(`ClientToScreen(0,0)`), not the top-left — the projection subtracts
`GetClientAreaOrigin()` (matters for wxNotebook's tab strip).
- **Layout is wx sizers, not CSS.** DOM contributes intrinsic measurement
only (clone-based, in an offscreen always-rendered host — sizers run
before `Show()`); positions are written as absolute `left/top/w/h`.
- **Events** flow element listener → `ccall('wx_dom_event')`
`domevents.cpp` routing table → virtual `wxWindowWasm::OnDomEvent`
the same direct-dispatch pattern as the port's mouse callbacks (proven
against Asyncify-suspended modals). Keyboard arbitration: keystrokes go
untouched to focused DOM editables (Escape excepted).
against Asyncify-suspended modals). Document-level listeners forward
mouse activity over DOM children into the wx hit-test pipeline
(`wx_dom_mouse`); wheel events walk up the window hierarchy. Keyboard
arbitration: keystrokes go untouched to focused DOM editables (Escape
excepted).
- **Canvas islands.** Owner-drawn/generic widgets (wxGrid, listctrl,
tree, AUI, STC, calendar...) keep painting via the shared `dc.cpp`
Canvas2D path inside per-window canvases; they render near-pixel-equal
to the canvas port.
Canvas2D path inside per-window canvases — the same architecture native
ports use for owner-drawn widgets.
- **Menus/toolbars** serialize C++→JS as JSON; popups are DOM divs;
command ids route back via `wxDOM_EVENT_MENU`/`TOOL`. Menu titles,
items, and tools register as rendered elements with the same
type strings the canvas port uses, so registry-based e2e utilities work
identically on both ports.
command ids route back via `wxDOM_EVENT_MENU`/`TOOL`. The DOM-native
wxNotebook renders a real tab strip (`<button role=tab>`).
- **E2E registry.** `window.wxElementRegistry` (wx.js) carries element
geometry for Playwright. wxWindow-level entries come from
`src/wasm/window.cpp`; canvas-island content (grid cells, list rows,
calendar dates, AUI parts...) is published from thin paint-site hooks
that call helpers in `src/wasm/elementtracker.cpp`; DOM-native
composites (tabs, menu items, tools, spin arrows, text fields) are
mirrored by wx-dom.js. One contract for everything.
Key files: `wxwidgets/build/wasm/wx-dom.js` (the whole JS control layer),
`wxwidgets/include/wx/wasm/private/dom.h` (C++→JS bridge),
`wxwidgets/src/wasm/domevents.cpp` (event routing + bitmap data URLs),
`wxwidgets/src/wasm/window.cpp` (DOM-backing machinery, guarded
`#ifndef __WXUNIVERSAL__`).
`wxwidgets/src/wasm/window.cpp` (DOM-backing machinery),
`wxwidgets/src/wasm/elementtracker.cpp` (e2e registry bridge).
## What works (e2e-verified, WX_PORT=dom)
## What works (e2e-verified)
Full `tests/e2e` suite green (256/0; canvas 263/0 from the same tree, its
stable screenshot set byte-identical to the pre-feature reference).
Full `tests/e2e` suite green; full kicad suite green.
- Native DOM: stattext, button (stock labels, default size), textctrl
(single/multi/password, two-way sync, wxEVT_TEXT/_ENTER), checkbox,
@ -67,8 +73,9 @@ stable screenshot set byte-identical to the pre-feature reference).
togglebutton, gauge, slider, statline, statbox, statbmp (PNG data
URLs), bitmap buttons, choice, listbox, editable combobox
(input+datalist), checklistbox, spinbutton (drives generic wxSpinCtrl),
menubar+menus, toolbar, tooltips (title attrs), dialogs (shared
Asyncify ShowModal), frame bar geometry (univ-parity).
notebook (DOM tab strip), menubar+menus, toolbar, tooltips (#wx-tooltip
layer, island widgets included), dialogs (shared Asyncify ShowModal),
frame bar geometry.
- Canvas islands verified visually: AUI, calendar, virtual listctrl,
grid, tree, propgrid, STC.
@ -78,19 +85,22 @@ stable screenshot set byte-identical to the pre-feature reference).
sizing); bitmap-button vertical centering; checklistbox selection
highlight; wxLB_SINGLE uses a multiple `<select>`; native scrollbars
replace univ gutters.
- Tracker hooks in `src/generic/`+aui/stc/propgrid are shared with the
canvas port and stay until the canvas port retires (the DOM port itself
doesn't need them — its controls register from creation + ARIA). The
univ hooks die with univ automatically.
- Element-registry hooks remain in `src/generic/` + aui/stc/propgrid as
thin guarded one-liners (canvas islands have no per-item DOM, so tests
need C++-fed geometry); everything heavier lives in
`src/wasm/elementtracker.cpp`.
## Upstreaming surface (vs `wasm-port` base)
## Fork surface (vs upstream wxWidgets v3.2.6)
8 commits, ~107 files: the port directory itself (new files), 35
dispatch-header branches (24 lines each), build-system entries
(bakefile-generated), one-line `prntbase.cpp` PostScript condition, and
two genuine upstream bug fixes in the generic notebook
(`tabg.cpp` uninitialized background brush; `notebook.cpp` first-page
selection never syncing the tab view). Zero changes to univ/aui/stc.
Upstream + new files (`src/wasm/`, `include/wx/wasm/`, `build/wasm/`),
the `__WXWASM__` dispatch branches in `include/wx/*.h` (24 lines each),
WASM toolkit registration in the build system (bakefile-generated),
wasm-generic fixes in `src/common/`, a handful of unguarded island
rendering tweaks in `src/generic/` (renderg/gridctrl/stattextg/msgdlgg/
spinctlg/filedlgg), and thin `#ifdef __EMSCRIPTEN__` tracker shims at
paint sites. `src/univ/`, `include/wx/univ/`, `src/generic/notebook.cpp`,
`tabg.cpp`, `build/msw/`, `tests/makefile.vc` are byte-identical to
upstream.
See `visual-notes.md` for the 15-bug log the cross-port screenshot
See `visual-notes.md` for the full bug log the cross-port screenshot
comparison protocol produced.

View file

@ -184,3 +184,33 @@ User-reported pcbnew issues, all fixed and e2e-pinned
25. Universal tooltip layer (#wx-tooltip div, 600 ms, hover hit-test
driven) — island widgets (color swatches, visibility toggles) get
KiCad's SetToolTip texts; title attrs replaced by aria-label.
## DOM-only consolidation (2026-06-12)
The canvas (wxUniversal) mode and dual-build plumbing were removed; the
DOM port is THE WASM port. Rebuilding everything from clean surfaced
three latent bugs (all pre-existing — verified by rebuilding the
pre-consolidation tree, which failed identically):
26. The DOM projection treated GetScreenPosition() as the window's
top-left; it is ClientToScreen(0,0) — the CLIENT-AREA origin. The
first widget with a non-zero client origin (the DOM notebook's tab
strip) rendered its whole box a strip-height too low, overlapping
its own pages, and ComputeAncestorClip double-added the origin,
clipping page content (dataview/grid/minimal buttons cut to
slivers, clicks swallowed by the strip). This was bug-logged
earlier as "tab-strip/content slight overlap" and believed
cosmetic. Fixed in UpdateDomGeometryRecursive (+ eager strip
re-measure + re-projection in wxNotebook::WasmRebuildTabs).
27. The DOM port never mirrored spin arrows and text fields into the
e2e registry (the canvas port published them from univ paint
hooks, which died with univ). wx-dom.js now registers
'spinbutton' (up/down) and 'textctrl' (singleline/multiline)
rendered elements, and clears a control's mirrored entries on
destroy.
28. tests: clickCanvas() used locator.click(), whose actionability
check refuses points covered by DOM elements — in the DOM port
real widgets legitimately cover the canvas. It now dispatches via
page.mouse. The minimal-app comprehensive spec drives the wxChoice
through its native <select> (browsers own that popup; it cannot be
coordinate-clicked).

View file

@ -10,7 +10,6 @@ source "$(dirname "$0")/common/env.sh"
# ./build-wasm-test.sh # Incremental build (default)
# ./build-wasm-test.sh --clean # Clean build from scratch
# ./build-wasm-test.sh --debug # Build with debug symbols
# ./build-wasm-test.sh --dom # Build against the DOM port into tests/apps-dom
# ./build-wasm-test.sh menu # Build only the menu test
# ./build-wasm-test.sh --debug menu # Build menu test with debug symbols
@ -18,7 +17,6 @@ set -e
DEBUG_BUILD=0
CLEAN_BUILD=0
DOM_BUILD=0
TARGET=""
# Parse arguments
@ -27,8 +25,6 @@ for arg in "$@"; do
DEBUG_BUILD=1
elif [ "$arg" = "--clean" ]; then
CLEAN_BUILD=1
elif [ "$arg" = "--dom" ]; then
DOM_BUILD=1
elif [ "$arg" != "" ]; then
TARGET="$arg"
fi
@ -38,29 +34,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
TESTS_DIR="$PROJECT_ROOT/tests"
if [ "$DOM_BUILD" = "1" ]; then
BUILD_DIR="$PROJECT_ROOT/build-wasm/wxwidgets-dom"
# DOM apps build in a mirrored source tree so canvas and DOM bundles
# coexist; relative ../../ paths in Makefile.wasm resolve identically.
WASM_APP_DIR="$TESTS_DIR/apps-dom"
echo "Mirroring test app sources into apps-dom..."
# kicad/ is excluded: apps-dom/kicad is owned by
# tests/scripts/setup-kicad-wasm.sh (DOM bundles + injected pages) and
# apps/kicad only has the checked-in HTML — mirroring with --delete
# would wipe the synced multi-hundred-MB kicad artifacts.
rsync -a --delete \
--exclude 'kicad/' \
--exclude '*.o' --exclude '*.d' \
--exclude '*_test.html' --exclude '*_test.js' --exclude '*_test.wasm' \
--exclude '*_repro.html' --exclude '*_repro.js' --exclude '*_repro.wasm' \
--exclude '*.wasm.map' \
"$TESTS_DIR/apps/" "$WASM_APP_DIR/"
MAKE_PORT_ARGS="PORT=dom"
else
BUILD_DIR="$PROJECT_ROOT/build-wasm/wxwidgets-universal"
WASM_APP_DIR="$TESTS_DIR/apps"
MAKE_PORT_ARGS=""
fi
BUILD_DIR="$PROJECT_ROOT/build-wasm/wxwidgets"
WASM_APP_DIR="$TESTS_DIR/apps"
STANDALONE_DIR="$WASM_APP_DIR/standalone"
echo "=== Building wxWidgets WASM Test Applications ==="
@ -80,7 +55,7 @@ echo "Test app dir: $WASM_APP_DIR"
# Verify wxWidgets is built
if [ ! -f "$BUILD_DIR/wx-config" ]; then
echo "ERROR: wxWidgets not built. Run build-wxuniversal-wasm.sh first"
echo "ERROR: wxWidgets not built. Run build-wx-wasm.sh first"
exit 1
fi
@ -120,9 +95,9 @@ fi
# Build (pass DEBUG flag if requested). App links are independent, so honor
# JOBS/PARALLEL_JOBS from env.sh (each emcc link is slow due to Asyncify).
if [ "$DEBUG_BUILD" = "1" ]; then
make -j"${JOBS:-1}" -f Makefile.wasm DEBUG=1 $MAKE_PORT_ARGS "$MAKE_TARGET"
make -j"${JOBS:-1}" -f Makefile.wasm DEBUG=1 "$MAKE_TARGET"
else
make -j"${JOBS:-1}" -f Makefile.wasm $MAKE_PORT_ARGS "$MAKE_TARGET"
make -j"${JOBS:-1}" -f Makefile.wasm "$MAKE_TARGET"
fi
echo ""

View file

@ -1,5 +1,6 @@
#!/bin/bash
# Build wxWidgets with wxUniversal for WebAssembly
# Build wxWidgets for WebAssembly (the DOM port: widgets are real HTML
# elements; owner-drawn widgets render into per-window canvas islands)
# Redirect all output to a log file (re-execs script with redirection)
source "$(dirname "$0")/common/logging.sh"
@ -20,10 +21,8 @@ source "$(dirname "$0")/common/stages.sh"
# ghcr.io/vslavik/bakefile:0.2 bakefile_gen
#
# Usage:
# ./build-wxuniversal-wasm.sh # Incremental build (default)
# ./build-wxuniversal-wasm.sh --clean # Clean build from scratch
# ./build-wxuniversal-wasm.sh --dom # Build the DOM port (native widgets,
# # no wxUniversal) into build-wasm/wxwidgets-dom
# ./build-wx-wasm.sh # Incremental build (default)
# ./build-wx-wasm.sh --clean # Clean build from scratch
set -e
@ -31,34 +30,19 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
WX_SOURCE="$PROJECT_ROOT/wxwidgets"
# Parse arguments: --clean, --no-clean (default; kept for the kicad
# pipeline's explicit call) and/or --dom, in any order. WX_PORT=dom env is
# equivalent to --dom so the flavor can flow through the docker pipeline.
# Parse arguments: --clean or --no-clean (default; kept for the kicad
# pipeline's explicit call), in any order.
CLEAN_BUILD=0
DOM_BUILD=0
[ "${WX_PORT:-}" = "dom" ] && DOM_BUILD=1
for arg in "$@"; do
case "$arg" in
--clean) CLEAN_BUILD=1 ;;
--no-clean) CLEAN_BUILD=0 ;;
--dom) DOM_BUILD=1 ;;
*) echo "Unknown argument: $arg"; exit 1 ;;
esac
done
if [ "$DOM_BUILD" = "1" ]; then
BUILD_DIR="$PROJECT_ROOT/build-wasm/wxwidgets-dom"
# Native (non-universal) widget set: controls are real DOM elements.
UNIVERSAL_FLAG=""
# Tooltips are DOM title attributes (wx/wasm/tooltip.h).
PORT_EXTRA_FLAGS=""
WXLIB_PREFIX="libwx_wasmu"
else
BUILD_DIR="$PROJECT_ROOT/build-wasm/wxwidgets-universal"
UNIVERSAL_FLAG="--enable-universal"
PORT_EXTRA_FLAGS=""
WXLIB_PREFIX="libwx_wasmunivu"
fi
BUILD_DIR="$PROJECT_ROOT/build-wasm/wxwidgets"
WXLIB_PREFIX="libwx_wasmu"
# Use our config.sub wrapper for autoconf projects
# CONFIG_SHELL is critical: nested configures (pcre, etc.) do SHELL=${CONFIG_SHELL-/bin/sh}
@ -72,7 +56,7 @@ export AUTOM4TE="$SCRIPT_DIR/config/autom4te-wrapper.sh"
# Use JOBS from env.sh if set, otherwise use all available cores
JOBS="${JOBS:-$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)}"
echo "=== Building wxWidgets wxUniversal for WASM ==="
echo "=== Building wxWidgets for WASM ==="
echo "Project root: $PROJECT_ROOT"
echo "Build dir: $BUILD_DIR"
echo "wxWidgets source: $WX_SOURCE"
@ -130,7 +114,6 @@ if [ $NEEDS_CONFIGURE -eq 1 ]; then
# Configure with emconfigure
# Key flags based on wxWidgets-wasm:
# --host=emscripten Host system (detected via config.sub)
# --enable-universal Use wxUniversal (draws widgets directly)
# --disable-shared Build static libraries
# --with-opengl Enable OpenGL/WebGL support
# --enable-exceptions Enable C++ exceptions (needed for KiCad debug builds)
@ -183,8 +166,6 @@ if [ $NEEDS_CONFIGURE -eq 1 ]; then
emconfigure "$WX_SOURCE/configure" \
--host=emscripten \
--without-subdirs \
${UNIVERSAL_FLAG} \
${PORT_EXTRA_FLAGS} \
--disable-shared \
--with-opengl \
--enable-exceptions \

View file

@ -31,7 +31,7 @@ export STUBS_DIR="$PROJECT_ROOT/stubs"
export CMAKE_MODULES="$PROJECT_ROOT/cmake"
# wxWidgets build location
export WX_BUILD="$BUILD_ROOT/wxwidgets-universal"
export WX_BUILD="$BUILD_ROOT/wxwidgets"
# Emscripten settings
export EMSDK_QUIET=1

View file

@ -8,25 +8,8 @@ set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# --port dom compares the DOM-port artifacts (WX_PORT=dom test runs) against
# their own baseline set; canvas paths are the default and stay unchanged.
PORT=""
ARGS=()
while [ $# -gt 0 ]; do
case "$1" in
--port) PORT="$2"; shift 2 ;;
*) ARGS+=("$1"); shift ;;
esac
done
set -- "${ARGS[@]+"${ARGS[@]}"}"
if [ "$PORT" = "dom" ]; then
BASELINE_DIR="$PROJECT_ROOT/tests/baseline-screenshots-dom"
TEST_RESULTS_DIR="$PROJECT_ROOT/tests/test-results/dom"
else
BASELINE_DIR="$PROJECT_ROOT/tests/baseline-screenshots"
TEST_RESULTS_DIR="$PROJECT_ROOT/tests/test-results"
fi
BASELINE_DIR="$PROJECT_ROOT/tests/baseline-screenshots"
TEST_RESULTS_DIR="$PROJECT_ROOT/tests/test-results"
# Check if directories exist
if [ ! -d "$BASELINE_DIR" ]; then

View file

@ -75,17 +75,9 @@ source "${SCRIPT_DIR}/../common/stages.sh"
KICAD_DIR="${PROJECT_ROOT}/kicad"
WASM_LAYER="${PROJECT_ROOT}/wasm"
# WX_PORT=dom links against the DOM (non-universal) wxWidgets build and
# keeps its KiCad build tree separate from the canvas one.
if [ "${WX_PORT:-}" = "dom" ]; then
KICAD_BUILD="${BUILD_ROOT}/kicad-${APP_NAME}-dom"
KICAD_STAMP="${BUILD_ROOT}/stamps/kicad-${APP_NAME}-dom.stamp"
WX_BUILD="${BUILD_ROOT}/wxwidgets-dom"
else
KICAD_BUILD="${BUILD_ROOT}/kicad-${APP_NAME}"
KICAD_STAMP="${BUILD_ROOT}/stamps/kicad-${APP_NAME}.stamp"
WX_BUILD="${BUILD_ROOT}/wxwidgets-universal"
fi
KICAD_BUILD="${BUILD_ROOT}/kicad-${APP_NAME}"
KICAD_STAMP="${BUILD_ROOT}/stamps/kicad-${APP_NAME}.stamp"
WX_BUILD="${BUILD_ROOT}/wxwidgets"
# Parse arguments - incremental build by default (optimized for development)
NO_CLEAN=1
@ -167,7 +159,7 @@ if [ $FULL_CLEAN -eq 1 ]; then
log_info "Full clean: removing all stamps and build directories..."
rm -rf "${STAMPS_DIR}"/*
rm -rf "${BUILD_ROOT}/deps"/*
rm -rf "${BUILD_ROOT}/wxwidgets-universal"
rm -rf "${BUILD_ROOT}/wxwidgets"
rm -rf "${BUILD_ROOT}/stubs"
rm -rf "${KICAD_BUILD}"
rm -rf "${SYSROOT}"/*
@ -196,7 +188,7 @@ fi
# Step 4: Build wxWidgets (incremental - only recompiles changed files)
kw_stage wxwidgets
log_info "Building wxWidgets..."
"${SCRIPT_DIR}/../build-wxuniversal-wasm.sh" --no-clean
"${SCRIPT_DIR}/../build-wx-wasm.sh" --no-clean
log_info "Building KiCad ${APP_NAME} ${KICAD_VERSION} for WASM..."

View file

@ -7,30 +7,22 @@
# Usage:
# ./update-baseline-screenshots.sh # Only copy NEW screenshots
# ./update-baseline-screenshots.sh --all # Copy new + significantly different
# ./update-baseline-screenshots.sh --port dom # Same, for the DOM-port baseline set
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TESTS_DIR="$SCRIPT_DIR/../tests"
COPY_ALL=0
PORT=""
THRESHOLD=5 # Percent threshold for "significant" difference
while [ $# -gt 0 ]; do
case "$1" in
--all) COPY_ALL=1; shift ;;
--port) PORT="$2"; shift 2 ;;
*) shift ;;
esac
done
if [ "$PORT" = "dom" ]; then
SOURCE_DIR="$TESTS_DIR/test-results/dom"
DEST_DIR="$TESTS_DIR/baseline-screenshots-dom"
else
SOURCE_DIR="$TESTS_DIR/test-results"
DEST_DIR="$TESTS_DIR/baseline-screenshots"
fi
SOURCE_DIR="$TESTS_DIR/test-results"
DEST_DIR="$TESTS_DIR/baseline-screenshots"
if [ $COPY_ALL -eq 1 ]; then
echo "Mode: Copy NEW + SIGNIFICANTLY DIFFERENT screenshots"

View file

@ -8,16 +8,8 @@
TOOLS_ROOT = ../../wxwidgets/build/wasm
KICAD_ROOT = ../../kicad
# PORT=dom links against the DOM (non-universal) wxWidgets build and adds the
# wx-dom.js shim. Invoked by scripts/build-wasm-test.sh --dom from a mirrored
# source tree in tests/apps-dom (so canvas and DOM bundles coexist).
ifeq ($(PORT),dom)
WXCONFIG = ../../build-wasm/wxwidgets-dom/wx-config
WXLIB_PREFIX = libwx_wasmu
else
WXCONFIG = ../../build-wasm/wxwidgets-universal/wx-config
WXLIB_PREFIX = libwx_wasmunivu
endif
WXCONFIG = ../../build-wasm/wxwidgets/wx-config
WXLIB_PREFIX = libwx_wasmu
CXX = em++
WX_CXXFLAGS := $(shell $(WXCONFIG) --cxxflags)
@ -113,25 +105,16 @@ LDFLAGS_COROUTINE = $(DEBUG_LDFLAGS) $(COROUTINE_BASE_LDFLAGS) $(WX_LDFLAGS_NOGL
# forbids the very multi-parked-sleep states the harness exists to exercise.
LDFLAGS_RACES = $(DEBUG_LDFLAGS) $(COROUTINE_BASE_LDFLAGS) -sASSERTIONS=0 $(WX_LDFLAGS_NOGL)
# In dom mode a second pre-js carries the DOM-control shim ("--pre-js A --pre-js B"
# A second pre-js carries the DOM-control shim ("--pre-js A --pre-js B"
# after expansion — emcc accepts repeated --pre-js).
# JS_FILES lists the actual pre-js paths (no flags) so the link targets can
# depend on them — otherwise editing a shim wouldn't trigger a relink.
ifeq ($(PORT),dom)
JS = $(TOOLS_ROOT)/wx.js --pre-js $(TOOLS_ROOT)/wx-dom.js
JS_FILES = $(TOOLS_ROOT)/wx.js $(TOOLS_ROOT)/wx-dom.js
else
JS = $(TOOLS_ROOT)/wx.js
JS_FILES = $(TOOLS_ROOT)/wx.js
endif
JS = $(TOOLS_ROOT)/wx.js --pre-js $(TOOLS_ROOT)/wx-dom.js
JS_FILES = $(TOOLS_ROOT)/wx.js $(TOOLS_ROOT)/wx-dom.js
HTML = $(TOOLS_ROOT)/template.html
# wxWidgets library directory - used as dependency to rebuild when libs change
ifeq ($(PORT),dom)
WX_LIB_DIR = ../../build-wasm/wxwidgets-dom/lib
else
WX_LIB_DIR = ../../build-wasm/wxwidgets-universal/lib
endif
WX_LIB_DIR = ../../build-wasm/wxwidgets/lib
# Key library that changes when wxWidgets is rebuilt
WX_CORE_LIB = $(WX_LIB_DIR)/$(WXLIB_PREFIX)_core-3.2-emscripten.a

View file

@ -193,6 +193,7 @@
<!-- wxWidgets WASM glue code (defines getConfigEntryLength, etc.) -->
<script src="wx.js"></script>
<script src="wx-dom.js"></script>
<script async src="calculator.js"></script>
</body>
</html>

View file

@ -225,6 +225,7 @@
<!-- wxWidgets WASM glue code (defines getConfigEntryLength, etc.) -->
<script src="wx.js"></script>
<script src="wx-dom.js"></script>
<script async src="eeschema.js"></script>
</body>
</html>

View file

@ -195,6 +195,7 @@
<!-- wxWidgets WASM glue code (defines getConfigEntryLength, etc.) -->
<script src="wx.js"></script>
<script src="wx-dom.js"></script>
<script async src="gerbview.js"></script>
</body>
</html>

View file

@ -229,6 +229,7 @@
<!-- wxWidgets WASM glue code (defines getConfigEntryLength, etc.) -->
<script src="wx.js"></script>
<script src="wx-dom.js"></script>
<script async src="pcbnew.js"></script>
</body>
</html>

View file

@ -194,6 +194,7 @@
<!-- wxWidgets WASM glue code (defines getConfigEntryLength, etc.) -->
<script src="wx.js"></script>
<script src="wx-dom.js"></script>
<script async src="pcbnew.js"></script>
</body>
</html>

View file

@ -219,6 +219,7 @@
<!-- wxWidgets WASM glue code (defines getConfigEntryLength, etc.) -->
<script src="wx.js"></script>
<script src="wx-dom.js"></script>
<script async src="pl_editor.js"></script>
</body>
</html>

View file

@ -218,6 +218,7 @@
<!-- wxWidgets WASM glue code (defines getConfigEntryLength, etc.) -->
<script src="wx.js"></script>
<script src="wx-dom.js"></script>
<script async src="symbol_editor.js"></script>
</body>
</html>

View file

@ -1,8 +1,7 @@
import { test, expect, waitForApp } from './utils/fixtures';
// Port-agnostic boot check: the app starts, the wx element registry fills,
// and (in DOM mode) the wx-dom shim is present. Runs under both WX_PORT
// values; intentionally takes no screenshots.
// Boot check: the app starts, the wx element registry fills, and the
// wx-dom shim is present. Intentionally takes no screenshots.
test.describe('Application boot', () => {
test('minimal app boots and registers elements', async ({ page, testLogger }) => {
const fatal: string[] = [];
@ -24,7 +23,7 @@ test.describe('Application boot', () => {
expect(elementCount).toBeGreaterThan(0);
const isDomPort = await page.evaluate(() => (window as any).wxDomPort === true);
expect(isDomPort).toBe(process.env.WX_PORT === 'dom');
expect(isDomPort, 'wx-dom.js shim should be loaded').toBe(true);
expect(fatal, `page errors: ${fatal.join('\n')}`).toHaveLength(0);
});

View file

@ -2,31 +2,10 @@ import { test as base } from '@playwright/test';
import * as path from 'path';
import { setupTestLogger, writeTestLogs, TestLogger, MAIN_CANVAS, waitForApp, tryLoadApp, getCanvasBox, WXWIDGETS_LOGS_DIR, getTestFileName } from './test-utils';
// WX_PORT=dom runs the same specs against the DOM-port bundles; screenshots
// and logs are namespaced into a dom/ subdirectory so the canvas artifacts
// stay untouched (baselines: tests/baseline-screenshots-dom/).
const IS_DOM_PORT = process.env.WX_PORT === 'dom';
// Extend base test with automatic logging
export const test = base.extend<{
testLogger: TestLogger;
}>({
page: async ({ page }, use) => {
if (IS_DOM_PORT) {
const origScreenshot = page.screenshot.bind(page);
page.screenshot = ((options?: Parameters<typeof origScreenshot>[0]) => {
if (options?.path) {
const dir = path.dirname(options.path);
// Specs write to test-results/<name>.png; redirect to test-results/dom/.
if (path.basename(dir) === 'test-results') {
options = { ...options, path: path.join(dir, 'dom', path.basename(options.path)) };
}
}
return origScreenshot(options);
}) as typeof page.screenshot;
}
await use(page);
},
testLogger: async ({ page }, use, testInfo) => {
// Build test name from describe block + test title
const testName = testInfo.titlePath.join(' - ');
@ -35,11 +14,9 @@ export const test = base.extend<{
await use(logger);
// Write logs to wxwidgets/<test-file>/ directory (wxwidgets/dom/<test-file>/ for the DOM port)
// Write logs to wxwidgets/<test-file>/ directory
const testFileName = getTestFileName(testInfo.file);
const logsDir = IS_DOM_PORT
? path.join(WXWIDGETS_LOGS_DIR, 'dom', testFileName)
: path.join(WXWIDGETS_LOGS_DIR, testFileName);
const logsDir = path.join(WXWIDGETS_LOGS_DIR, testFileName);
writeTestLogs(testName, logger, logsDir);
logger.cleanup();
},

View file

@ -10,9 +10,6 @@ import {
clickTextCtrl,
findSingleLineTextCtrl,
findMultiLineTextCtrl,
clickComboButton,
selectComboItem,
clickListboxItem,
clickListboxItemByIndex
} from './utils/element-tracker';
@ -44,10 +41,14 @@ function isKnownWarning(error: string): boolean {
error.includes('Debug:'); // wxWidgets debug prefix
}
// Click at specific canvas coordinates
// Click at specific canvas coordinates. Uses page.mouse (no actionability
// check): in the DOM port real elements legitimately cover the canvas, and
// the click goes through the app's normal input routing either way.
async function clickCanvas(page: Page, x: number, y: number) {
const canvas = page.locator(MAIN_CANVAS);
await canvas.click({ position: { x, y } });
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
await page.mouse.click(box.x + x, box.y + y);
}
// Drag on canvas from one point to another
@ -183,34 +184,21 @@ test.describe('wxWidgets WASM - Diagnostics', () => {
await page.screenshot({ path: 'test-results/10-lists-clicked.png', fullPage: true });
// Test wxChoice dropdown using element tracking
// Find the Choice dropdown by its current value ("Red") and click to open it
const { findAllComboButtons } = await import('./utils/element-tracker');
const comboButtons = await findAllComboButtons(page);
const choiceCombo = comboButtons.find(c => c.label === 'Red');
expect(choiceCombo, 'Should find Choice dropdown with "Red" value').not.toBeNull();
// Test the wxChoice dropdown. It renders as a native <select> (the
// browser owns the popup, so it cannot be driven by coordinate
// clicks) — drive it through the DOM and verify the value sticks.
const choice = page
.locator('select:not([multiple])')
.filter({ has: page.locator('option', { hasText: 'Red' }) })
.first();
expect(await choice.count(), 'Choice <select> should exist').toBe(1);
// Open the dropdown
await page.mouse.click(choiceCombo!.centerX, choiceCombo!.centerY);
await page.waitForTimeout(300);
// Select "Green" from the dropdown
const greenClicked = await clickListboxItem(page, 'Green');
expect(greenClicked, 'Should be able to click "Green" item').toBe(true);
await choice.selectOption({ label: 'Green' });
await page.waitForTimeout(300);
await page.screenshot({ path: 'test-results/10b-choice-selected.png', fullPage: true });
// Open the dropdown again to verify it works
const reopened = await clickComboButton(page);
expect(reopened, 'Should be able to open dropdown').toBe(true);
await page.waitForTimeout(500);
await page.screenshot({ path: 'test-results/10c-choice-dropdown-reopen.png', fullPage: true });
// Close the dropdown by pressing Escape
await page.keyboard.press('Escape');
await page.waitForTimeout(300);
expect(await choice.inputValue(), 'Choice should now be Green').toBe('Green');
// === Menu interaction ===
// Click File menu using element registry

View file

@ -13,7 +13,7 @@ CXX = em++
# Paths relative to this Makefile
PROJECT_ROOT = ../../..
KICAD_ROOT = $(PROJECT_ROOT)/kicad
WX_BUILD = $(PROJECT_ROOT)/build-wasm/wxwidgets-universal
WX_BUILD = $(PROJECT_ROOT)/build-wasm/wxwidgets
TOOLS_ROOT = $(PROJECT_ROOT)/wxwidgets/build/wasm
# Output directory
@ -85,7 +85,9 @@ LEGACY_GL_STUBS = --js-library=$(MAKEFILE_DIR)legacy_gl_stubs.js
LDFLAGS = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) $(EM_GL_FLAGS) $(LEGACY_GL_STUBS) $(WX_LDFLAGS)
# Templates
JS = $(TOOLS_ROOT)/wx.js
# wx-dom.js rides as a second pre-js ("--pre-js A --pre-js B" after
# expansion) — the WASM port's DOM layer is required at runtime.
JS = $(TOOLS_ROOT)/wx.js --pre-js $(TOOLS_ROOT)/wx-dom.js
HTML_TEMPLATE = $(TOOLS_ROOT)/template.html
# Source files

View file

@ -107,13 +107,11 @@ test.describe('Appearance panel (Layers/Objects/Nets)', () => {
await selectTab(page, 'Layers');
await page.screenshot({ path: 'test-results/appearance-03-layers-again.png' });
// DOM port: layer rows must survive the tab round-trip (regression:
// Layer rows must survive the tab round-trip (regression:
// pages came back blank after switching away and back)
if (await page.evaluate(() => !!window.wxDomPort)) {
const tops = await rowLabelTops(page, ['F.Cu', 'B.Cu']);
expect(tops['F.Cu'], 'F.Cu row visible after tab round-trip').not.toBeNull();
expect(tops['B.Cu'], 'B.Cu row visible after tab round-trip').not.toBeNull();
}
});
test('layer list scrolls with the wheel and clips at the pane', async ({ page }) => {
@ -123,37 +121,31 @@ test.describe('Appearance panel (Layers/Objects/Nets)', () => {
const layersTab = tabs.find(t => t.label === 'Layers');
expect(layersTab).toBeTruthy();
const isDom = await page.evaluate(() => !!window.wxDomPort);
// hover INSIDE the layer list (just below the tab strip)
const hoverX = layersTab!.centerX;
const hoverY = layersTab!.centerY + 120;
await page.mouse.move(hoverX, hoverY);
const before = isDom ? await rowLabelTops(page, ['B.Cu', 'F.Mask']) : null;
const before = await rowLabelTops(page, ['B.Cu', 'F.Mask']);
await page.mouse.wheel(0, 240);
await page.waitForTimeout(800);
await page.screenshot({ path: 'test-results/appearance-10-layers-scrolled.png' });
if (isDom && before) {
const after = await rowLabelTops(page, ['B.Cu', 'F.Mask']);
expect(after['B.Cu'], 'B.Cu moved up after wheel scroll')
.toBeLessThan(before['B.Cu']!);
expect(after['F.Mask'], 'F.Mask moved up after wheel scroll')
.toBeLessThan(before['F.Mask']!);
}
// scroll back up restores the start of the list
await page.mouse.wheel(0, -480);
await page.waitForTimeout(800);
await page.screenshot({ path: 'test-results/appearance-11-layers-scrolled-back.png' });
if (isDom && before) {
const restored = await rowLabelTops(page, ['B.Cu']);
expect(restored['B.Cu'], 'B.Cu back at its original position')
.toBe(before['B.Cu']);
}
});
test('objects page scrolls with the wheel', async ({ page }) => {
@ -163,17 +155,16 @@ test.describe('Appearance panel (Layers/Objects/Nets)', () => {
const tabs = await appearanceTabs(page);
const objectsTab = tabs.find(t => t.label === 'Objects')!;
const isDom = await page.evaluate(() => !!window.wxDomPort);
await page.mouse.move(objectsTab.centerX, objectsTab.centerY + 120);
const before = isDom ? await rowLabelTops(page, ['Ratsnest']) : null;
const before = await rowLabelTops(page, ['Ratsnest']);
await page.mouse.wheel(0, 240);
await page.waitForTimeout(800);
await page.screenshot({ path: 'test-results/appearance-20-objects-scrolled.png' });
if (isDom && before && before['Ratsnest'] !== null) {
if (before['Ratsnest'] !== null) {
const after = await rowLabelTops(page, ['Ratsnest']);
expect(after['Ratsnest'], 'Ratsnest row moved after wheel scroll')
.toBeLessThan(before['Ratsnest']!);

View file

@ -9,7 +9,6 @@
"build-wasm": "cd apps && make -f Makefile.wasm",
"build:collab": "node collab/build.mjs",
"serve": "npx serve apps -p 8080 -c ../serve.json",
"serve:dom": "npx serve apps-dom -p 8080 -c ../serve.json",
"setup:kicad": "./scripts/setup-kicad-wasm.sh",
"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",

View file

@ -74,9 +74,7 @@ const PCBNEW_FAMILY_SPECS = [
'**/load-pcb-probe.spec.ts',
];
// WX_PORT=dom runs against the DOM-port bundles (setup-kicad-wasm.sh syncs
// them into tests/apps-dom/kicad).
const appsDir = process.env.WX_PORT === 'dom' ? 'apps-dom' : 'apps';
const appsDir = 'apps';
export default defineConfig({
globalSetup: './global-setup.ts',

View file

@ -62,9 +62,7 @@ function findFreePort(): number {
const port = resolvePort();
// WX_PORT=dom runs the suite against the DOM-port bundles (built by
// scripts/build-wasm-test.sh --dom into tests/apps-dom with identical layout).
const appsDir = process.env.WX_PORT === 'dom' ? 'apps-dom' : 'apps';
const appsDir = 'apps';
export default defineConfig({
globalSetup: './global-setup.ts',

View file

@ -11,17 +11,8 @@ set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# WX_PORT=dom serves the DOM-flavor bundles (built by docker/build.sh with
# WX_PORT=dom into output-dom/) from tests/apps-dom/kicad.
if [ "${WX_PORT:-}" = "dom" ]; then
KICAD_TEST="$PROJECT_ROOT/tests/apps-dom/kicad"
OUTPUT_DIR="$PROJECT_ROOT/output/dom"
KICAD_BUILD_SUFFIX="-dom"
else
KICAD_TEST="$PROJECT_ROOT/tests/apps/kicad"
OUTPUT_DIR="$PROJECT_ROOT/output"
KICAD_BUILD_SUFFIX=""
fi
KICAD_TEST="$PROJECT_ROOT/tests/apps/kicad"
OUTPUT_DIR="$PROJECT_ROOT/output"
mkdir -p "$KICAD_TEST"
@ -72,15 +63,15 @@ copy_app() {
echo "Output ${app} not found locally, trying Docker volume..."
if docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}${KICAD_BUILD_SUFFIX}/${subdir}/${app}.js "$KICAD_TEST/" 2>/dev/null \
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${subdir}/${app}.js "$KICAD_TEST/" 2>/dev/null \
&& docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}${KICAD_BUILD_SUFFIX}/${subdir}/${app}.wasm "$KICAD_TEST/" 2>/dev/null; then
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${subdir}/${app}.wasm "$KICAD_TEST/" 2>/dev/null; then
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}${KICAD_BUILD_SUFFIX}/${subdir}/${app}.wasm.map "$KICAD_TEST/" 2>/dev/null || true
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${subdir}/${app}.wasm.map "$KICAD_TEST/" 2>/dev/null || true
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}${KICAD_BUILD_SUFFIX}/${subdir}/${app}.worker.js "$KICAD_TEST/" 2>/dev/null || true
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/${subdir}/${app}.worker.js "$KICAD_TEST/" 2>/dev/null || true
docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}${KICAD_BUILD_SUFFIX}/resources/images.tar.gz "$KICAD_TEST/" 2>/dev/null || true
kicad-wasm-builder:/workspace/build-wasm/kicad-${app}/resources/images.tar.gz "$KICAD_TEST/" 2>/dev/null || true
return 0
fi
@ -107,33 +98,19 @@ if [ -f "$OUTPUT_DIR/wx.js" ]; then
smart_cp "$OUTPUT_DIR/wx.js" "$KICAD_TEST"
else
if docker compose -f "$PROJECT_ROOT/docker/docker-compose.yml" cp \
kicad-wasm-builder:/workspace/build-wasm/wxwidgets/build/wasm/wx.js "$KICAD_TEST/" 2>/dev/null; then
kicad-wasm-builder:/workspace/wxwidgets/build/wasm/wx.js "$KICAD_TEST/" 2>/dev/null; then
:
else
smart_cp "$PROJECT_ROOT/wxwidgets/build/wasm/wx.js" "$KICAD_TEST"
fi
fi
if [ "${WX_PORT:-}" = "dom" ]; then
# DOM port: the control-layer shim loads after wx.js.
if [ -f "$OUTPUT_DIR/wx-dom.js" ]; then
# The control-layer shim loads after wx.js (the checked-in kicad pages
# reference both via <script> tags).
if [ -f "$OUTPUT_DIR/wx-dom.js" ]; then
smart_cp "$OUTPUT_DIR/wx-dom.js" "$KICAD_TEST"
else
else
smart_cp "$PROJECT_ROOT/wxwidgets/build/wasm/wx-dom.js" "$KICAD_TEST"
fi
# The checked-in kicad pages load wx.js via a <script> tag; the DOM
# flavor needs wx-dom.js right after it. Sync the pages and inject the
# tag (idempotent).
for page in "$PROJECT_ROOT"/tests/apps/kicad/*.html; do
[ -f "$page" ] || continue
dest="$KICAD_TEST/$(basename "$page")"
cp "$page" "$dest"
if ! grep -q 'wx-dom.js' "$dest"; then
perl -i -pe 's{(<script src="wx.js"></script>)}{$1\n <script src="wx-dom.js"></script>}' "$dest"
fi
done
echo " + kicad pages with wx-dom.js injected"
fi
echo "KiCad WASM files synced to $KICAD_TEST"

View file

@ -71,7 +71,7 @@ if(EMSCRIPTEN AND wxWidgets_CONFIG_EXECUTABLE)
# Set the wx configuration for port detection
# For WASM, we use our own port implementation
set(_wx_selected_config "wasm-wasmuniv-unicode-static-3.2" CACHE INTERNAL "")
set(_wx_selected_config "wasm-unicode-static-3.2" CACHE INTERNAL "")
set(wxWidgets_FIND_STYLE "unix" CACHE INTERNAL "")
# Set KICAD_WX_PORT to "wasm" - kiplatform will need our custom source files

@ -1 +1 @@
Subproject commit 21f2da07f81cf7a0f00726fc1d10f7c76b6a45d2
Subproject commit 9dbacc9448ef540ed963e22b27d60c078ebaa9b2