open-standards/wai/wai-studio/e2e/run.sh
2026-10-28 01:50:13 -04:00

91 lines
5.5 KiB
Shell

#!/bin/bash
# Build what WAI Studio's tests need, and run them: the unit tests, the drift check, and the
# browser end-to-end run in Chromium, WebKit and Firefox against a `wai dev` stack.
#
# bash wai/wai-studio/e2e/run.sh [--browsers chromium,webkit,firefox]
#
# Two phases, both by default (STUDIO_PHASES="build node"): `build` (1 and 2: Rust and
# wasm-bindgen) and `node` (3 to 5: Node and the browsers). They may run in different places
# sharing the same paths: WebKit runs where its session can open a window (on Linux, the
# Playwright image), the builds where Rust is.
#
# 1. native: `wai` (the archive's build, `quickstart`), the relay and the room service;
# 2. wasm, each through wasm-bindgen's web target: Studio's core (core/), the room client's
# packages (wai/quickstart/build-web.sh, the files `wai dev` serves), and the quantum
# engine (wai-web, feature quantum_engine) for Quantum Studio;
# 3. Quantum Studio's build, then Studio's (build.mjs --core-pkg --quantum), its packages
# installed from package-lock.json with no install script run;
# 4. the unit tests (test/), the core's among them, and the drift check;
# 5. e2e/studio.e2e.mjs: `wai dev` serving Studio at /_wai/, both hosts, every panel, in each
# browser, and axe on each.
#
# Environment: CARGO_TARGET_DIR (under which each workspace gets a target directory of its
# own: workspaces that build the `wai` crate with different features must not share one, or
# one's library is taken for another's), STUDIO_OUT (where
# builds and results go), PLAYWRIGHT_BROWSERS_PATH (the browsers Playwright drives),
# CARGO_LOCKED (default --locked), STUDIO_PHASES. Nothing listens beyond loopback.
set -euo pipefail
HERE=$(cd "$(dirname "$0")" && pwd)
STUDIO=$(cd "$HERE/.." && pwd)
ROOT=$(cd "$STUDIO/../.." && pwd)
JC="$ROOT/joule-code/joule-code-rs"
LOCKED=${CARGO_LOCKED---locked}
TARGET=${CARGO_TARGET_DIR:-$ROOT/target-studio}
# A workspace's own target directory: `inside <dir> <name> cargo …`.
inside() { local d="$1" n="$2"; shift 2; (cd "$d" && CARGO_TARGET_DIR="$TARGET/$n" "$@"); }
OUT=${STUDIO_OUT:-${TMPDIR:-/tmp}/wai-studio-e2e}
mkdir -p "$OUT"
step() { echo "== $*"; }
PHASES=${STUDIO_PHASES:-build node}
phase() { case " $PHASES " in *" $1 "*) return 0 ;; *) return 1 ;; esac; }
# The binaries by the names this tree gives them.
bin() { local t="$1"; shift; for n in "$@"; do [ -x "$TARGET/$t/release/$n" ] && { echo "$TARGET/$t/release/$n"; return; }; done; echo "none of $* was built" >&2; exit 1; }
if phase build; then
command -v wasm-bindgen >/dev/null || { echo "wasm-bindgen CLI 0.2.127 is needed (cargo install --locked wasm-bindgen-cli --version 0.2.127)"; exit 2; }
wasm-bindgen --version | grep -q ' 0\.2\.127$' || { echo "the wasm-bindgen CLI must be 0.2.127, the version the crates pin"; exit 2; }
step "native: wai, the relay, the room service"
inside "$ROOT/wai/wai-rs" wai-rs cargo build $LOCKED --release --no-default-features --features quickstart --bin wai
inside "$JC" joule-code cargo build $LOCKED --release -p joule-moq-relay
inside "$JC/crates/joule-moq-room" joule-moq-room cargo build $LOCKED --release
step "wasm: Studio's core"
# The floor's zstd is C, compiled for wasm32: the archive needs an archiver that indexes
# WebAssembly objects (llvm-ar). macOS's own ar writes one the linker finds no symbol in.
if [ -z "${AR_wasm32_unknown_unknown:-}" ]; then
for a in llvm-ar /opt/homebrew/opt/llvm/bin/llvm-ar /usr/local/opt/llvm/bin/llvm-ar; do
if command -v "$a" >/dev/null 2>&1; then export AR_wasm32_unknown_unknown="$(command -v "$a")"; break; fi
done
fi
if [ -z "${CC_wasm32_unknown_unknown:-}" ] && [ -n "${AR_wasm32_unknown_unknown:-}" ] && [ -x "$(dirname "$AR_wasm32_unknown_unknown")/clang" ]; then
export CC_wasm32_unknown_unknown="$(dirname "$AR_wasm32_unknown_unknown")/clang"
fi
inside "$STUDIO/core" core cargo build $LOCKED --release --target wasm32-unknown-unknown
rm -rf "$OUT/core-pkg"
wasm-bindgen --target web --out-dir "$OUT/core-pkg" --out-name wai_studio_core "$TARGET/core/wasm32-unknown-unknown/release/wai_studio_core.wasm"
rm -f "$OUT"/core-pkg/*.d.ts
step "wasm: the room client, as wai dev serves it"
CARGO_TARGET_DIR="$TARGET/web" bash "$ROOT/wai/quickstart/build-web.sh" "$OUT/web"
step "wasm: the quantum engine"
inside "$ROOT/wai/wai-web" wai-web cargo build $LOCKED --release --target wasm32-unknown-unknown --features quantum_engine
rm -rf "$OUT/quantum-pkg"
wasm-bindgen --target web --out-dir "$OUT/quantum-pkg" --out-name wai_quantum_ide "$TARGET/wai-web/wasm32-unknown-unknown/release/wai_web.wasm"
rm -f "$OUT"/quantum-pkg/*.d.ts
fi
phase node || exit 0
WAI=$(bin wai-rs wai)
RELAY=$(bin joule-code wai-relay joule-moq-relay)
ROOM=$(bin joule-moq-room wai-room joule-moq-room)
step "Quantum Studio, then WAI Studio"
(cd "$ROOT/wai/quantum-studio" && npm ci --ignore-scripts --no-audit --no-fund && node build.mjs --pkg "$OUT/quantum-pkg")
(cd "$STUDIO" && npm ci --ignore-scripts --no-audit --no-fund && node build.mjs --core-pkg "$OUT/core-pkg" --quantum "$ROOT/wai/quantum-studio/dist")
step "unit tests, and the drift check"
(cd "$STUDIO" && WAI_STUDIO_CORE_PKG="$OUT/core-pkg" node --test --test-reporter=tap test/*.test.mjs | tee "$OUT/unit.log")
grep -qE '^# fail 0$' "$OUT/unit.log" && ! grep -qE '^# skipped [1-9]' "$OUT/unit.log" || { echo "a unit test failed or was skipped"; exit 1; }
(cd "$STUDIO" && node scripts/actions.mjs --check)
step "end-to-end"
node "$HERE/studio.e2e.mjs" --wai "$WAI" --relay "$RELAY" --room "$ROOM" --web "$OUT/web" --studio "$STUDIO/dist" --wai-dir "$ROOT/wai" --out "$OUT/results" "$@"