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:
parent
2c1b0d409b
commit
7dc1bf27e0
32 changed files with 199 additions and 335 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 0–7). 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 (2–4 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` (2–4 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.
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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..."
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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']!);
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Loading…
Reference in a new issue