Repository navigation
Add collaborative spreadsheet blocks inside documents #539
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: WASM browser tests | |
| # Runs the frontend's wasm-bindgen-test integration suite under headless | |
| # Firefox against a freshly-booted local stack (DynamoDB Local + MinIO + | |
| # Redis + axum server). These tests exercise the WebSocket protocol | |
| # directly from a real browser-like environment — they catch regressions | |
| # that backend integration tests can't see (wire-format mismatches that | |
| # only manifest on the WASM side, browser-side message framing, etc). | |
| # | |
| # Triggers per push + PR rather than nightly (unlike playwright.yml) | |
| # because the wasm-pack invocation is the cheap part — the slow part is | |
| # the docker-compose + axum-build, and we already pay that in the | |
| # `integration` job of ci.yml. ~5-7 min wall-clock on a cold cache. | |
| # | |
| # Two jobs: | |
| # collab-e2e — frontend/tests/collab_e2e.rs (integration target) against | |
| # a live axum stack: WS connect / sync / comment broadcast. | |
| # lib-wasm — the frontend lib's inline `#[wasm_bindgen_test]` tests | |
| # (render_node output, DOM↔model position walkers, focus traps) plus | |
| # the frontend/tests/browser.rs DOM suite (rendering, input rules, | |
| # backspace/list handling, markdown paste — issue #20). Pure | |
| # browser-DOM, no backend. `--test collab_e2e` excludes the `--lib` | |
| # target, so these need their own job or they never run in CI. | |
| # | |
| # To extend collab-e2e: drop new tests into frontend/tests/collab_e2e.rs | |
| # (or a new *.rs under frontend/tests/) and add the `--test` argument to | |
| # the wasm-pack step. To extend lib-wasm: add `#[wasm_bindgen_test]` fns | |
| # in any `#[cfg(all(test, target_arch = "wasm32"))]` module under | |
| # frontend/src/ — `--lib` picks them up automatically. | |
| on: | |
| push: | |
| # `main` is the default branch of the public repo (the pre-OSS | |
| # private repo used `master`; the 2026-07-07 re-publish switched). | |
| branches: [main] | |
| pull_request: | |
| concurrency: | |
| group: wasm-tests-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| # AWS SDK shims so the server + setup_dev binary talk to DynamoDB | |
| # Local + MinIO instead of real AWS. Mirrors playwright.yml exactly | |
| # so the two workflows can share the same Swatinem/rust-cache key | |
| # and warm each other's caches. | |
| AWS_ACCESS_KEY_ID: minioadmin | |
| AWS_SECRET_ACCESS_KEY: minioadmin | |
| AWS_REGION: us-east-1 | |
| AWS_ENDPOINT_URL_DYNAMODB: http://127.0.0.1:8000 | |
| AWS_ENDPOINT_URL_S3: http://127.0.0.1:9000 | |
| DYNAMODB_TABLE_PREFIX: "ci-" | |
| S3_BUCKET: "ci-ogrenotes" | |
| REDIS_URL: "redis://127.0.0.1:6379" | |
| DEV_MODE: "true" | |
| JWT_SECRET: "ci-jwt-secret-that-is-at-least-32-bytes-long" | |
| OAUTH_CLIENT_ID: "ci-not-used" | |
| OAUTH_CLIENT_SECRET: "ci-not-used" | |
| OAUTH_REDIRECT_URI: "http://localhost:3000/auth/callback" | |
| SEARCH_INDEX_PATH: "/tmp/ogrenotes-ci-search" | |
| API_PORT: "3000" | |
| jobs: | |
| collab-e2e: | |
| name: collab_e2e (headless Firefox) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # ─── System libs ────────────────────────────────────────── | |
| # samael's `xmlsec` feature (SAML signature verification) | |
| # needs libxml2 + xmlsec1 + clang at compile time. The | |
| # Dockerfile got these in 45e086e; playwright.yml in 508b1c3; | |
| # ci.yml in 402aae0; this workflow was missed all three times. | |
| # `cargo run --bin setup_dev` (Provision DDB step) compiles | |
| # the full workspace including samael, so the build crashes | |
| # here without these packages. | |
| - name: Install xmlsec1 + clang (samael dependencies) | |
| run: bash scripts/ci-install-native-deps.sh | |
| # ─── Toolchain ──────────────────────────────────────────── | |
| - name: Install Rust (stable + wasm32) | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: wasm32-unknown-unknown | |
| - name: Cache cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| env-vars: LIBCLANG_PATH OGRE_NATIVE_ABI | |
| workspaces: | | |
| . -> target | |
| frontend -> target | |
| # Sharing the playwright cache key would conflict — both jobs | |
| # write to the same target dirs but with different feature | |
| # configurations. Use a dedicated key. | |
| shared-key: wasm-tests | |
| - name: Install wasm-pack | |
| uses: taiki-e/install-action@5488ac5a951645ce95062214902ac4b6bc1a1037 # v2 | |
| with: | |
| tool: wasm-pack@0.15.0 | |
| # geckodriver + Firefox are preinstalled on ubuntu-latest. wasm-pack | |
| # uses them transparently when --firefox is passed. If the runner | |
| # image ever drops them this step starts to fail with "no driver | |
| # found"; fix by adding `sudo apt-get install -y firefox-geckodriver`. | |
| # ─── Infra ──────────────────────────────────────────────── | |
| - name: Start infra (DynamoDB / MinIO / Redis) | |
| run: docker compose up -d | |
| - name: Wait for services | |
| run: | | |
| for port in 8000 9000 6379; do | |
| echo "Waiting for port $port..." | |
| for i in $(seq 1 30); do | |
| if nc -z 127.0.0.1 "$port"; then | |
| echo " port $port ready" | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| done | |
| # Strip the `pdf` feature here for the same reason as the | |
| # axum build below — symmetric to playwright.yml — and apply | |
| # it to `setup_dev` too because it lives in the same crate | |
| # and inherits the same default feature set. setup_dev is a | |
| # pure DDB + S3 provisioner; it doesn't touch the pdf code | |
| # paths. | |
| - name: Provision DynamoDB table + S3 bucket | |
| run: | | |
| cargo run --locked --bin setup_dev -p ogrenotes-api \ | |
| --no-default-features --features "dev-login,xlsx,docx" | |
| # ─── Start API server ───────────────────────────────────── | |
| # collab_e2e expects the server at http://localhost:3000 and uses | |
| # the dev-login endpoint to mint JWTs, so DEV_MODE=true is | |
| # mandatory (set in env: above). | |
| # Strip the `pdf` feature from the axum build — same fix as | |
| # playwright.yml (532fe94). The collab_e2e tests exercise WS | |
| # connect / sync / comment broadcast paths only; no PDF | |
| # import or export is involved. Without this strip, the cold | |
| # cargo build of pdf-extract + printpdf + ~70 transitive | |
| # crates pushes the wall-clock past the 25-minute job budget | |
| # and traps the workflow in a doom loop (cold build → 25m | |
| # timeout → no cache save → cold build again). PDF | |
| # correctness is covered by ci.yml's default-feature build | |
| # via `crates/api/tests/test_import_pdf_e2e.rs`. | |
| - name: Build axum server (release) | |
| run: | | |
| cargo build --release --locked --bin ogrenotes-api \ | |
| --no-default-features --features "dev-login,xlsx,docx" | |
| - name: Start axum server | |
| run: | | |
| mkdir -p /tmp/ci-logs | |
| ./target/release/ogrenotes-api \ | |
| > /tmp/ci-logs/server.log 2>&1 & | |
| echo $! > /tmp/ci-logs/server.pid | |
| - name: Wait for server | |
| run: | | |
| # 90 iterations × 2s = 3 min. Mirrors playwright.yml. | |
| for i in $(seq 1 90); do | |
| if curl -sf http://127.0.0.1:3000/api/v1/auth/dev-login \ | |
| -H 'content-type: application/json' \ | |
| -d '{"email":"healthcheck@ci"}' >/dev/null; then | |
| echo "server ready" | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| echo "server failed to start" >&2 | |
| tail -n 200 /tmp/ci-logs/server.log | |
| exit 1 | |
| # ─── Run wasm-bindgen tests ─────────────────────────────── | |
| # `--test collab_e2e` selects the integration test target; | |
| # everything after the bare `--` is forwarded to cargo by | |
| # wasm-pack. Run from `frontend/` because that's the wasm crate. | |
| - name: Run collab_e2e under headless Firefox | |
| working-directory: frontend | |
| run: | | |
| python3 ../scripts/with-test-cleanup.py -- wasm-pack test --headless --firefox -- --test collab_e2e | |
| # ─── Teardown ───────────────────────────────────────────── | |
| - name: Stop axum server | |
| if: always() | |
| run: | | |
| if [ -f /tmp/ci-logs/server.pid ]; then | |
| kill "$(cat /tmp/ci-logs/server.pid)" || true | |
| fi | |
| - name: Upload server log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wasm-tests-server-log | |
| path: /tmp/ci-logs/server.log | |
| if-no-files-found: warn | |
| retention-days: 14 | |
| - name: Compose logs on failure | |
| if: failure() | |
| run: docker compose logs --no-color | |
| - name: Stop infra | |
| if: always() | |
| run: docker compose down -v | |
| # ── Lib wasm-bindgen tests (no backend) ───────────────────────── | |
| # The inline `#[cfg(all(test, target_arch = "wasm32"))]` tests in the | |
| # frontend lib exercise pure browser-DOM behavior — render_node output, | |
| # the DOM↔model position walkers, focus traps. They need a real | |
| # `web_sys::Document` (hence headless Firefox) but NOT the axum stack, | |
| # so they run in their own fast job without docker-compose. The | |
| # collab-e2e job above runs only `--test collab_e2e`, which excludes | |
| # the `--lib` target — without this job these tests never run in CI. | |
| lib-wasm: | |
| name: lib wasm tests (headless Firefox) | |
| runs-on: ubuntu-latest | |
| # 25 min to match the sibling jobs' headroom for a cold-cache Leptos | |
| # wasm build (dev profile, so typically well under this). | |
| timeout-minutes: 25 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust (stable + wasm32) | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: wasm32-unknown-unknown | |
| - name: Cache cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: | | |
| frontend -> target | |
| # Dedicated key: this job only builds the frontend lib for the | |
| # wasm test target, a different feature/target set than the | |
| # collab-e2e job's `wasm-tests` cache. | |
| shared-key: wasm-tests-lib | |
| - name: Install wasm-pack | |
| uses: taiki-e/install-action@5488ac5a951645ce95062214902ac4b6bc1a1037 # v2 | |
| with: | |
| tool: wasm-pack@0.15.0 | |
| - name: Verify browser process cleanup | |
| run: python3 scripts/test_test_cleanup.py | |
| # geckodriver + Firefox are preinstalled on ubuntu-latest; wasm-pack | |
| # uses them transparently when --firefox is passed. | |
| # `--lib` goes AFTER the `--` separator (a cargo passthrough arg, same | |
| # as collab-e2e's `-- --test collab_e2e`). Keep cargo arguments after | |
| # the separator rather than relying on wasm-pack's argument forwarding. | |
| - name: Run frontend lib wasm-bindgen tests | |
| working-directory: frontend | |
| run: python3 ../scripts/with-test-cleanup.py -- wasm-pack test --headless --firefox -- --lib | |
| # Issue #20: the browser.rs DOM suite (148 tests — rendering, input | |
| # rules, backspace/list handling, markdown paste) existed but was | |
| # never run by CI, so a DOM-observable regression could ship | |
| # unnoticed. Pure browser-DOM, no backend — it shares this job's | |
| # build cache with the --lib target. | |
| - name: Run frontend browser DOM tests and verify cleanup | |
| run: python3 scripts/test_browser_cleanup.py firefox --complete | |
| - name: Frontend binary DOM position regressions | |
| working-directory: frontend | |
| run: python3 ../scripts/with-test-cleanup.py -- wasm-pack test --headless --firefox -- --bin ogrenotes-frontend | |
| - name: Collaboration content preservation | |
| working-directory: frontend | |
| run: python3 ../scripts/with-test-cleanup.py -- wasm-pack test --headless --firefox -- --test collab_semantics | |
| - name: Internationalization browser formatting tests | |
| working-directory: frontend | |
| run: python3 ../scripts/with-test-cleanup.py -- wasm-pack test --headless --firefox -- --test i18n_browser | |
| - name: Verify Firefox cleanup after interruption | |
| run: python3 scripts/test_browser_cleanup.py firefox | |
| - name: Verify Chrome cleanup after interruption | |
| run: python3 scripts/test_browser_cleanup.py chrome |