Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,141 @@ jobs:
if-no-files-found: ignore
retention-days: 7

# Multi-mode (EWC.MODE=2) suite: many browser contexts against ONE server,
# asserting per-session namespace cloning, teardown and WG routing.
#
# A separate job rather than a project inside the e2e matrix, deliberately.
# A Dyalog process runs EWC in exactly one mode, so this needs its own
# server; folding it into the matrix would make all 5 shards start a second
# container that 4 of them would never use.
#
# Not sharded: the suite is small, and its assertions are on SERVER-WIDE
# state (which clones exist, what the close log holds), which two concurrent
# backends' worth of sessions would make meaningless.
e2e-multi:
needs: check
runs-on: ubuntu-latest
timeout-minutes: 25
if: |
needs.check.outputs.skip != 'true' &&
github.actor != 'github-actions[bot]'
steps:
- name: Checkout ewc-client
uses: actions/checkout@v7

- name: Checkout Dyalog/ewc
id: ewc
uses: ./.github/actions/checkout-ewc-server
with:
ref: ${{ inputs.ewc_server_ref || '' }}
branch: ${{ github.head_ref || github.ref_name }}
pr-body: ${{ github.event.pull_request.body || '' }}

- name: Check the resolved ewc ref carries the Multi test app
# The app is test-apps/multitest in Dyalog/ewc, so a ref predating it
# fails inside APL on multitest.Run — a VALUE ERROR buried in the
# container log, 2 minutes after wait-on gives up. Say so up front.
# Ref via env, not inline: same precaution as checkout-ewc-server.
env:
EWC_REF: ${{ steps.ewc.outputs.ref }}
run: |
if [ ! -d ewc-server/test-apps/multitest ]; then
echo "::error::Dyalog/ewc@${EWC_REF} has no test-apps/multitest."
echo "The Multi-mode test app lives in Dyalog/ewc. Point this job"
echo "at a ref that has it with an 'EWC-REF: <branch>' line in the"
echo "PR body, or land the app on that branch first."
exit 1
fi
echo "Multi test app present in Dyalog/ewc@${EWC_REF}"

- uses: actions/setup-node@v6
with:
node-version: lts/*
cache: yarn

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Cache Playwright browsers
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
playwright-${{ runner.os }}-

- name: Install Playwright system deps (apt)
timeout-minutes: 12
run: |
for i in 1 2 3; do
sudo apt-get update && npx playwright install-deps chromium && break
echo "::warning::apt attempt $i failed; retrying in 15s"; sleep 15
done

- name: Install Playwright browser
timeout-minutes: 6
run: npx playwright install chromium

- name: Build ewc-client
run: yarn build

- name: Start Dyalog EWC server in Multi mode (Docker)
run: |
# SETUP_APL swaps ci/setup-ewc.apl (Browser, :22322) for
# ci/setup-ewc-multi.apl (Multi, :22323) — see ci/run-server.sh.
#
# The test app comes from Dyalog/ewc (test-apps/multitest), so it
# arrives inside the ewc-server mount and tracks whichever ref
# checkout-ewc-server resolved.
docker run -d --name ewc-multi --network host \
-e SETUP_APL=/scripts/setup-ewc-multi.apl \
--entrypoint /scripts/run-server.sh \
-v "${{ github.workspace }}/ewc-server:/work/ewc:ro" \
-v "${{ github.workspace }}/dist:/work/ewc-client/dist:ro" \
-v "${{ github.workspace }}/ci:/scripts:ro" \
dyalog/dyalog:latest

- name: Diagnostics — what's running before wait-on
run: |
sleep 5
echo '=== Listening ports ==='
(ss -tln 2>/dev/null || netstat -tln) | head -30 || true
echo
echo '=== Docker container status ==='
docker ps -a --filter name=ewc-multi || true
echo
echo '=== Dyalog container logs ==='
docker logs ewc-multi 2>&1 | tail -80 || true

- name: Wait for EWC Multi server
# tcp: only — EWC's WSS answers GET but not HEAD, and wait-on's
# http probe uses HEAD.
run: npx wait-on tcp:22323 --timeout 120000

- name: Run Multi-mode Playwright tests
# Step timeout below the job cap so a wedged run is SIGINT-stopped
# and the log-capture step below still runs.
timeout-minutes: 15
env:
MULTI_URL: http://localhost:22323
run: npx playwright test --config playwright.multi.config.ts --reporter=list,html

- name: Capture Dyalog container logs (post-run)
# The Multi server's log is where session create/close, thread kills
# and namespace expunges are recorded — the server-side counterpart to
# whatever the specs asserted. Worth keeping even on success.
if: always()
run: docker logs ewc-multi || true

- name: Upload Multi-mode Playwright report
if: always()
uses: actions/upload-artifact@v7
with:
name: playwright-report-multi
path: playwright-report-multi/
if-no-files-found: ignore
retention-days: 7

# Recombine the per-shard blob reports into ONE browsable html report
# with aggregate pass/fail. Runs even when shards fail (visual diffs, a
# wedged shard) via !cancelled(); skipped only if the whole e2e matrix
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ TODO.md
/e2e/demo/test-results
/e2e/demo/playwright-report
/e2e/demo/playwright/.cache
# Multi-mode suite output (playwright.multi.config.ts writes to its own dirs so
# it doesn't clobber the demo suite's report)
/test-results-multi
/playwright-report-multi
# Manual-inspection screenshots regenerated by *-inspect.spec.ts runs (not baselines)
/e2e/demo/tests/_inspect/

Expand Down
19 changes: 19 additions & 0 deletions ci/ewc-multi-ensure.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
# Make sure a Multi-mode EWC server is answering on :22323, starting one only
# if it isn't. Cheap when the server is already warm, which matters for the
# interactive paths (yarn multitests:watch, yarn ewc-multi:observe) where you
# re-run repeatedly and don't want a container rebuild each time.
#
# ewc-multi-start.sh always destroys and recreates; this doesn't.

set -e

PORT="${MULTI_PORT:-22323}"

if curl -sf -o /dev/null --max-time 3 "http://localhost:${PORT}/" 2>/dev/null; then
echo "EWC Multi server already running on :${PORT}"
exit 0
fi

echo "No EWC Multi server on :${PORT} — starting one…"
exec bash "$(dirname "$0")/ewc-multi-start.sh"
77 changes: 77 additions & 0 deletions ci/ewc-multi-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/bin/bash
# Start an `ewc-multi` Docker container running the multitest app in EWC's
# MULTI mode on port 22323 (with RIDE on :4503). Used by:
#
# yarn ewc-multi:start # this script
# yarn multitests # (assumes server already running)
# yarn ewc-multi:stop # docker rm -f ewc-multi
#
# Deliberately a SEPARATE container and port from ewc-demo (:22322,
# RIDE :4502): a Dyalog process runs EWC in exactly one mode, so Multi
# mode cannot share the Browser-mode server. Distinct ports mean both
# suites can run concurrently without tearing each other down.

set -e

NAME="ewc-multi"
PORT=22323
RIDE_PORT=4503

# EWC_SRC=... overrides the default sibling `ewc` directory (worktree pairs).
EWC_SRC="${EWC_SRC:-$PWD/../ewc}"
if [ ! -d "$EWC_SRC" ]; then
echo "ERROR: EWC source not found at $EWC_SRC" >&2
echo " Set EWC_SRC=/path/to/ewc to override." >&2
exit 1
fi

# The test app lives in Dyalog/ewc, so an EWC checkout predating it would
# otherwise fail deep inside APL with a VALUE ERROR on multitest.Run.
if [ ! -d "$EWC_SRC/test-apps/multitest" ]; then
echo "ERROR: $EWC_SRC has no test-apps/multitest." >&2
echo " The Multi-mode test app lives in Dyalog/ewc; this checkout" >&2
echo " predates it. Update it, or point EWC_SRC at one that has it." >&2
exit 1
fi

# Without dist/, EWC's JSClientFolder auto-discovery falls back to the
# bundled client inside Dyalog/ewc — i.e. not your local changes.
if [ ! -d dist ]; then
echo "WARNING: dist/ is missing. EWC will fall back to the bundled" >&2
echo " client in Dyalog/ewc, not your local changes." >&2
echo " Run 'yarn build' first if you're testing UI work." >&2
echo >&2
fi

docker rm -f "$NAME" >/dev/null 2>&1 || true

# The test app rides along inside the ewc mount, at
# /work/ewc/test-apps/multitest — so EWC_SRC swaps the backend and the
# fixture app together.
docker run -d --name "$NAME" \
-e RIDE_INIT="SERVE:*:${RIDE_PORT}" \
-e SETUP_APL=/scripts/setup-ewc-multi.apl \
-p "${RIDE_PORT}:${RIDE_PORT}" \
-p "${PORT}:${PORT}" \
--entrypoint /scripts/run-server.sh \
-v "$EWC_SRC:/work/ewc:ro" \
-v "$PWD/dist:/work/ewc-client/dist:ro" \
-v "$PWD/ci:/scripts:ro" \
dyalog/dyalog:latest >/dev/null

echo "Starting EWC Multi server (waiting for :${PORT})..."

# A listening TCP socket isn't enough — the WSS binds a moment before it
# can serve. curl GET / (not HEAD: EWC's WSS only handles GET).
for i in $(seq 1 60); do
if curl -sf -o /dev/null "http://localhost:${PORT}/" 2>/dev/null; then
echo "EWC Multi server ready: http://localhost:${PORT} (RIDE: :${RIDE_PORT})"
exit 0
fi
sleep 1
done

echo "ERROR: EWC Multi server did not come up within 60 seconds." >&2
echo "Container logs:" >&2
docker logs "$NAME" >&2 || true
exit 1
6 changes: 5 additions & 1 deletion ci/run-server.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ cat <<'BANNER'
|_____/ |_/_/ \_\______\____/ \_____|
BANNER

# Which APL setup to feed in. Defaults to the Browser-mode demo server;
# ci/ewc-multi-start.sh overrides it to bring up Multi mode instead.
SETUP_APL="${SETUP_APL:-/scripts/setup-ewc.apl}"

# Feed the APL setup, then hold stdin with a long sleep so dyalog
# stays alive in demo.Run's wait loop.
(cat /scripts/setup-ewc.apl; sleep infinity) | "${DYALOG}/dyalog" +s -q
(cat "${SETUP_APL}"; sleep infinity) | "${DYALOG}/dyalog" +s -q
26 changes: 26 additions & 0 deletions ci/setup-ewc-multi.apl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
⍝ APL commands fed to `dyalog +s -q` via stdin to bring up EWC in MULTI
⍝ mode for the multi-session Playwright suite (e2e/multi/).
⍝ Counterpart of ci/setup-ewc.apl, which starts Browser mode on :22322.
⍝ This one starts Multi mode on :22323, so both servers can run side by
⍝ side without either disturbing the other.
⍝ The runner script (ci/run-server.sh, invoked with
⍝ SETUP_APL=/scripts/setup-ewc-multi.apl) cats this file into dyalog and
⍝ then `sleep`s on the same pipe so stdin stays open. That matters more
⍝ here than in Browser mode: EWC.Init returns immediately under Multi, so
⍝ without the held-open stdin dyalog would read EOF and exit before any
⍝ browser ever connected.
⍝ Mount layout (set up by ci/ewc-multi-start.sh / the CI job):
⍝ /work/ewc → the Dyalog/ewc repo, which carries the test app
⍝ at test-apps/multitest — so EWC_SRC picks the
⍝ backend and its fixture app as one unit
⍝ /work/ewc-client/dist → freshly-built ewc-client (sibling of
⍝ /work/ewc, so EWC.Init's auto-discovery
⍝ picks it up)

]link.create /work/ewc/EWC
]link.create /work/ewc/test-apps/multitest
EWC.FOLDER←'/work/ewc'
multitest.Run
981 changes: 0 additions & 981 deletions dist/assets/index-BmbAwXR0.js

This file was deleted.

981 changes: 981 additions & 0 deletions dist/assets/index-DYeTtQEi.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion e2e/demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ End-to-end Playwright tests for the EWC demo. Exercises the full stack:
the React UI (this package's `dist/`), EWC's WebSocket server (Dyalog APL),
and the demos under `Dyalog/ewc/demo/`.

> **Looking for the multi-user tests?** They live in [`../multi/`](../multi/README.md)
> and drive a **Multi**-mode (`EWC.MODE=2`) server on `:22323`, with one browser
> context per simulated user. This suite drives a **Browser**-mode server on
> `:22322`. A Dyalog process runs EWC in exactly one mode, so the two suites need
> separate servers — but they use different ports and can run concurrently.

## How tests connect to EWC

Tests connect via the `BROWSER_URL` env var. Three modes are supported:
Expand Down Expand Up @@ -122,7 +128,7 @@ Three workflows live in `.github/workflows/`:
| Workflow | Trigger | What it does |
|---|---|---|
| `build-and-commit.yml` | push | Builds and commits `dist/` |
| `tests.yml` | push, PR, manual (`workflow_dispatch`) | Runs Playwright against `:22322` (EWC-served browser mode) |
| `tests.yml` | push, PR, manual (`workflow_dispatch`) | `e2e` job: Playwright against `:22322` (EWC-served browser mode), sharded 5×. `e2e-multi` job: the [multi-user suite](../multi/README.md) against a Multi-mode server on `:22323`, unsharded |
| `update-baselines.yml` | manual (`workflow_dispatch`) | Regenerates visual baselines on the runner; opens a PR for review |

`tests.yml` and `update-baselines.yml` both use `ci/run-server.sh` to bring up
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading