#!/bin/bash # Build what the browser MoQ client's tests need, and run them. # # e2e/run.sh [--only compose,webtransport,websocket,goaway,dvr,service,e2ee,age,co,ageco,binding,cowatch,source,stitched,injector,origin,termination,remote,world,privateworld,captioner,messaging,call,offers,conform] [--runs N] # # 1. joule-moq-relay, joule-moq-room, joule-moq-remote and joule-moq-agent (each its own workspace) and # wai_live_web (native, release: the test room, the host's composition, and the remote # desktop host agent); # 2. joule-moq-web and wai-live-web for wasm32-unknown-unknown, through wasm-bindgen's web # target into web/pkg/, each with its own --out-name; and joule-moq-web again with its # feature e2ee (the member of an end-to-end encrypted room), under the workspace's `wasm` # profile and with no name section, as joule_moq_web_e2ee, which a page loads for such a # room alone; and wai-live-web again with its feature int_video, as wai_live_web_decode, # the decoder on the browser sink's wasm route, which the binding page loads; # 3. the browser-automation package from e2e/package-lock.json (`npm ci --ignore-scripts`: # no install script runs), outside the tree unless PLAYWRIGHT_HOST names one installed; # 4. the Node unit tests of the page's own modules; # 5. WAI's local stack, `wai dev --check` (`wai`, wai-rs with quickstart and browser_sink, debug): # the relay and the room service built in step 1, from the configuration wai dev writes, and # the page and its WebAssembly from web/, each checked through its one origin, then stopped; # the relay and the room service must be of wai's toolchain, or the stack is refused as stale; # 6. e2e/room.e2e.mjs, in the installed browser whose executable BROWSER_BIN names (the # CI job names the host's), otherwise the package's own, which PLAYWRIGHT_BROWSERS_PATH # must then hold; # 7. e2e/messaging.e2e.mjs, the messaging view and the call flow (WAI live-messaging), in the # same browser. `--only` picks scenarios of either file by name; # 8. `conform`: the page web/binding/sink.html as a browser sink through WAI's conformance # runner (`wai`, wai-rs with quickstart and browser_sink, debug), by e2e/conform_sink.mjs in # the same browser: every call of live-conformance/browser answered by the page's wasm and # its side table, the record signed and admitted (`wai verify --conformant`); and a planted # page that shows a refused frame as verified, whose run must fail at that case; # 9. `offers`: web/offers/run.mjs in the same browser, an offer presented and acted on against a # fixture merchant on loopback (WAI shoppable-events), its intent and the merchant's order # held to the offer by wai-rs's vectors binary (feature shoppable_events, debug), and its # planted faults, each refused with its own reason. # # Environment: CARGO_TARGET_DIR (one target directory for both workspaces), BROWSER_BIN, # PLAYWRIGHT_HOST, MOQ_WEB_OUT (where artifacts go), CARGO_LOCKED (default --locked). # Nothing listens beyond loopback: the relay and the page server bind 127.0.0.1. set -euo pipefail HERE=$(cd "$(dirname "$0")" && pwd) CRATE=$(cd "$HERE/.." && pwd) JC=$(cd "$CRATE/../.." && pwd) ROOT=$(cd "$JC/../.." && pwd) WLW="$ROOT/wai/wai-live-web" LOCKED=${CARGO_LOCKED---locked} TARGET=${CARGO_TARGET_DIR:-$JC/target} export CARGO_TARGET_DIR="$TARGET" OUT=${MOQ_WEB_OUT:-${TMPDIR:-/tmp}/moq-web-e2e} mkdir -p "$OUT" # Each run starts from no results: a scenario writes keys and logs there, and the room # service's keygen refuses a key file a previous run left. rm -rf "$OUT/results" PKG="$CRATE/web/pkg" mkdir -p "$PKG" step() { echo "== $*"; } step "native: wai-relay, wai-room, wai_live_web" (cd "$JC" && cargo build $LOCKED --release -p joule-moq-relay --bin wai-relay) (cd "$CRATE/../joule-moq-room" && cargo build $LOCKED --release --bin wai-room) (cd "$CRATE/../joule-moq-remote" && cargo build $LOCKED --release --bin wai-remote-host) # The captioner hears the pages' microphones, which they send as Opus: its feature `opus`, libopus # from pkg-config where the host has it, else built from the crate's source (CMake 4 needs the # policy floor below to read that source's CMakeLists). (cd "$CRATE/../joule-moq-agent" && CMAKE_POLICY_VERSION_MINIMUM=3.5 cargo build $LOCKED --release --features opus --bin wai-room-agent) (cd "$WLW" && cargo build $LOCKED --release --bin wai_live_web) step "wasm: joule-moq-web, wai-live-web" 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 both crates pin"; exit 2; } (cd "$JC" && cargo build $LOCKED --release -p joule-moq-web --lib --target wasm32-unknown-unknown) wasm-bindgen --target web --out-dir "$PKG" --out-name joule_moq_web "$TARGET/wasm32-unknown-unknown/release/joule_moq_web.wasm" (cd "$JC" && cargo build $LOCKED --profile wasm -p joule-moq-web --lib --target wasm32-unknown-unknown --features e2ee) wasm-bindgen --target web --remove-name-section --remove-producers-section --out-dir "$PKG" --out-name joule_moq_web_e2ee "$TARGET/wasm32-unknown-unknown/wasm/joule_moq_web.wasm" (cd "$WLW" && cargo build $LOCKED --release --lib --target wasm32-unknown-unknown) wasm-bindgen --target web --out-dir "$PKG" --out-name wai_live_web "$TARGET/wasm32-unknown-unknown/release/wai_live_web.wasm" (cd "$WLW" && cargo build $LOCKED --release --lib --target wasm32-unknown-unknown --features int_video) wasm-bindgen --target web --out-dir "$PKG" --out-name wai_live_web_decode "$TARGET/wasm32-unknown-unknown/release/wai_live_web.wasm" ls -l "$PKG"/*.wasm step "browser automation" if [ -z "${PLAYWRIGHT_HOST:-}" ]; then PW="$OUT/pw" mkdir -p "$PW" cp "$HERE/package.json" "$HERE/package-lock.json" "$PW/" (cd "$PW" && npm ci --ignore-scripts --no-audit --no-fund) export PLAYWRIGHT_HOST="$PW/node_modules" fi BROWSER_BIN=${BROWSER_BIN:-} step "unit tests of the page's modules" node --test "$CRATE/e2e/compose.test.mjs" "$CRATE/e2e/remote_input.test.mjs" "$CRATE/web/moq/capsule.test.mjs" "$CRATE/web/moq/media.test.mjs" "$CRATE/web/moq/session.test.mjs" "$CRATE/web/ads/presentation.test.mjs" "$CRATE/web/ads/traffic.test.mjs" "$CRATE/web/offers/offers.test.mjs" "$CRATE/web/room/privacy-pass.test.mjs" "$CRATE/web/room/binding.test.mjs" "$CRATE/web/room/cowatch.test.mjs" "$CRATE/web/room/co.test.mjs" "$CRATE/web/room/remote.test.mjs" "$CRATE/web/room/markup.test.mjs" "$CRATE/web/room/endpoint.test.mjs" "$CRATE/web/room/termination.test.mjs" "$CRATE/web/room/catalog.test.mjs" "$CRATE/web/room/tempo.test.mjs" "$CRATE/web/room/source.test.mjs" "$CRATE/web/room/running.test.mjs" "$CRATE/web/room/captions.test.mjs" step "the local stack: wai dev --check" (cd "$ROOT/wai/wai-rs" && cargo build $LOCKED --no-default-features --features quickstart,browser_sink --bin wai) "$TARGET/debug/wai" dev --check --port 0 --dir "$OUT/results/dev" \ --relay "$TARGET/release/wai-relay" --room "$TARGET/release/wai-room" --web "$CRATE/web" step "end-to-end" browser=() [ -n "$BROWSER_BIN" ] && browser=(--browser "$BROWSER_BIN") # `--only` names scenarios of room.e2e.mjs and of messaging.e2e.mjs: each file runs its own. room_args=() msg_only="messaging,call" room_only="" only=0 conform=1 offers=1 while [ $# -gt 0 ]; do if [ "$1" = "--only" ] && [ $# -ge 2 ]; then only=1 msg_only=$(echo "$2" | tr ',' '\n' | grep -xE 'messaging|call' | paste -sd, - || true) room_only=$(echo "$2" | tr ',' '\n' | grep -vxE 'messaging|call|conform|offers' | paste -sd, - || true) echo "$2" | tr ',' '\n' | grep -qx conform || conform=0 echo "$2" | tr ',' '\n' | grep -qx offers || offers=0 shift 2 else room_args+=("$1") shift fi done if [ "$only" = 0 ] || [ -n "$room_only" ]; then node "$HERE/room.e2e.mjs" \ --relay "$TARGET/release/wai-relay" \ --room "$TARGET/release/wai-room" \ --wai "$TARGET/release/wai_live_web" \ --remote "$TARGET/release/wai-remote-host" \ --agent "$TARGET/release/wai-room-agent" \ --web "$CRATE/web" \ --corpus "$ROOT/wai/live-conformance/compose/vectors.json" \ --wai-dir "$ROOT/wai" \ --out "$OUT/results" \ ${browser[@]+"${browser[@]}"} \ ${room_only:+--only "$room_only"} \ ${room_args[@]+"${room_args[@]}"} fi if [ -n "$msg_only" ]; then step "end-to-end: conversations and calls" node "$HERE/messaging.e2e.mjs" \ --relay "$TARGET/release/wai-relay" \ --room "$TARGET/release/wai-room" \ --web "$CRATE/web" \ --out "$OUT/results/messaging" \ --only "$msg_only" \ ${browser[@]+"${browser[@]}"} \ ${room_args[@]+"${room_args[@]}"} fi if [ "$offers" = 1 ]; then # web/offers/: an offer presented and acted on against a fixture merchant on loopback (WAI # shoppable-events), and its planted faults, each refused with its own reason. step "an offer presented and acted on" (cd "$ROOT/wai/wai-rs" && cargo build $LOCKED --no-default-features --features shoppable_events --bin wai_shoppable_events_vectors) mkdir -p "$OUT/results/offers" NODE_PATH="$PLAYWRIGHT_HOST" node "$CRATE/web/offers/run.mjs" "$TARGET/debug/wai_shoppable_events_vectors" "$OUT/results/offers" fi if [ "$conform" = 1 ]; then step "the page as a browser sink, through wai conform" C="$OUT/results/conform" mkdir -p "$C/planted" node "$HERE/conform_sink.mjs" --web "$CRATE/web" ${BROWSER_BIN:+--browser "$BROWSER_BIN"} \ --wai "$TARGET/debug/wai" --wai-dir "$ROOT/wai" --out "$C/browser-page.wai-conform.json" # The planted page must fail as a run (exit 1), at the frame it shows wrongly. set +e node "$HERE/conform_sink.mjs" --web "$CRATE/web" ${BROWSER_BIN:+--browser "$BROWSER_BIN"} --plant \ --wai "$TARGET/debug/wai" --wai-dir "$ROOT/wai" --out "$C/planted/browser-page.wai-conform.json" > "$C/planted.txt" 2>&1 c=$? set -e # Its record still verifies, and admission refuses it at rule 2. set +e "$TARGET/debug/wai" verify --conformant --root "$ROOT/wai" "$C/planted" > "$C/planted-admit.txt" 2>&1 a=$? set -e if [ "$c" = 1 ] && grep -q '^FAIL frames/object_refused$' "$C/planted.txt" && [ "$a" = 1 ] && grep -q 'FAIL admission rule 2 not_conformant' "$C/planted-admit.txt"; then echo "conform: the planted page was refused at frames/object_refused, and its record at admission rule 2" else cat "$C/planted-admit.txt" cat "$C/planted.txt" echo "FAIL conform: the planted page was not refused (exit $c)" exit 1 fi fi