Skip to content

Convert eligible ref-mirror effects to useEffectEvent (bd-t18384ia) #2564

Convert eligible ref-mirror effects to useEffectEvent (bd-t18384ia)

Convert eligible ref-mirror effects to useEffectEvent (bd-t18384ia) #2564

Workflow file for this run

name: TS Test Suite
on:
workflow_dispatch:
push:
branches:
- main
- kyoto
pull_request:
branches:
- main
- kyoto
concurrency:
group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
PANDOC_VERSION: "3.12.1"
# Typst — the Pandoc-hybrid typst leg's `L`-tier tests (typst_compile.rs)
# require a real `typst` binary on PATH (TYPST_VERSION_FLOOR is 0.8+).
TYPST_VERSION: "0.15.1"
jobs:
test-suite:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
name: Run test suite
if: github.repository == 'quarto-dev/q2'
steps:
- name: Checkout Repo
uses: actions/checkout@v6
# Fix mtimes IMMEDIATELY after checkout, before anything else
- name: Restore file modification times
shell: bash
run: |
git ls-files | while read file; do
time=$(git log -1 --format='@%ct' -- "$file" 2>/dev/null || echo '@0')
[ "$time" != "@0" ] && touch -d "$time" "$file" 2>/dev/null || true
done
- name: Set up Homebrew
if: runner.os == 'macOS'
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@main
# Consistent Rust setup for both platforms. Use the named `@nightly`
# ref (not `@master`, which hard-errors without an explicit
# `toolchain:` input) — it installs the nightly toolchain; cargo
# then reads the pinned dated nightly from rust-toolchain.toml
# (bd-at72) at build time. Matches the existing pattern on main.
- name: Set up Rust nightly
uses: dtolnay/rust-toolchain@nightly
with:
targets: wasm32-unknown-unknown
components: rust-src
- name: Output rust version
shell: bash
run: rustup --version
# # Cache Rust AFTER toolchain is set up
# - name: Cache Rust dependencies
# uses: Swatinem/rust-cache@v2
# with:
# cache-on-failure: true
# Cache cargo-nextest and insta separately to avoid reinstalling
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Install cargo-insta
uses: taiki-e/install-action@v2
with:
tool: cargo-insta
# Pandoc setup (pinned to match Quarto 1.9)
- name: Set up Pandoc (Linux)
if: runner.os == 'Linux'
run: |
curl -LO "https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-1-amd64.deb"
sudo dpkg -i "pandoc-${PANDOC_VERSION}-1-amd64.deb"
shell: bash
- name: Set up Pandoc (macOS)
if: runner.os == 'macOS'
run: |
curl -LO "https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-arm64-macOS.pkg"
sudo installer -pkg "pandoc-${PANDOC_VERSION}-arm64-macOS.pkg" -target /
shell: bash
# Typst setup — the docx/pptx Pandoc-hybrid leg also has a typst
# backend whose `L`-tier tests require a real binary (see TYPST_VERSION above).
- name: Set up Typst (Linux)
if: runner.os == 'Linux'
run: |
curl -LO "https://github.com/typst/typst/releases/download/v${TYPST_VERSION}/typst-x86_64-unknown-linux-musl.tar.xz"
tar -xf "typst-x86_64-unknown-linux-musl.tar.xz"
sudo mv "typst-x86_64-unknown-linux-musl/typst" /usr/local/bin/typst
shell: bash
- name: Set up Typst (macOS)
if: runner.os == 'macOS'
run: brew install typst
shell: bash
# tree-sitter setup
- name: Set up tree-sitter CLI (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libc6-dev gcc-multilib
curl -LO https://github.com/tree-sitter/tree-sitter/releases/download/v0.25.8/tree-sitter-linux-x86.gz
gunzip tree-sitter-linux-x86.gz
chmod +x tree-sitter-linux-x86
sudo mv tree-sitter-linux-x86 /usr/local/bin/tree-sitter
- name: Set up tree-sitter CLI (macOS)
if: runner.os == 'macOS'
run: brew install tree-sitter-cli
# TypeScript workspace build
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
- name: Install npm dependencies
shell: bash
run: npm ci
# design-system.md's token/physical-prop rules are only enforced by
# this script — nothing else gates them. Runs right after npm ci so
# a violation fails in seconds, not after the multi-minute WASM
# build. (Added after ConnectionStatusDialog.css landed with 9
# violations one day after the rule itself merged — bd-owmzraf6.)
- name: Lint hub-client CSS
shell: bash
run: npm run lint:css -w hub-client --if-present
# WASM build for hub-client (must happen before TypeScript build)
- name: Set up Clang (Linux)
if: runner.os == 'Linux'
uses: egor-tensin/setup-clang@v2
with:
version: latest
platform: x64
- name: Set up LLVM (macOS)
if: runner.os == 'macOS'
run: brew install llvm
shell: bash
- name: Install wasm-bindgen-cli
shell: bash
run: cargo install wasm-bindgen-cli --version 0.2.108
# wasm-opt (binaryen) is `build:wasm` step 3 (live-share payload
# plan Phase 1). Pinned to the version local dev uses (Homebrew
# binaryen 132) so CI and dev builds produce the same bytes.
- name: Install binaryen (wasm-opt)
shell: bash
run: npm install -g binaryen@132.0.0
- name: Build WASM module
shell: bash
run: |
cd hub-client
npm run build:all
# Real-wasm pandoc-host tests (hub-client/src/pandoc/*.wasm.test.ts) need the
# pinned pandoc.wasm (sha-verified against resources/pandoc-wasm.json) and the
# semantic output comparer. A cache miss during a GitHub outage fails the job.
- name: Cache pandoc.wasm and typst assets
uses: actions/cache@v4
with:
path: |
.cache/pandoc-wasm
.cache/typst-assets
key: pandoc-wasm-${{ hashFiles('resources/pandoc-wasm.json', 'resources/typst-wasm.json') }}
- name: Fetch pandoc.wasm
shell: bash
run: node scripts/fetch-pandoc-wasm.mjs --require
- name: Build quarto-output-extract
shell: bash
run: cargo build --release -p quarto-output-extract
- name: Run hub-client tests
shell: bash
run: |
cd hub-client
npm run test:ci
# engine-host-node: vitest suite (harness tests + the TS<->Rust wire-parity gate)
- name: Run engine-host-node tests (vitest)
shell: bash
run: npm run test -w @quarto/engine-host-node
# Bundle freshness gate: rebuild engine-host-node.mjs from source and assert
# the committed bytes are unchanged. Fails if someone edited TS but forgot
# to run `npm run bundle`. build-info.json is gitignored (volatile builtAt);
# the diff targets only the bundle itself.
- name: Check engine-host-node bundle freshness
shell: bash
run: |
npm run bundle -w @quarto/engine-host-node
git diff --exit-code -- ts-packages/quarto-engine-host-node/dist/engine-host-node.mjs
# ts-bundle: vitest suite (the rollup driver behind `q2 call build-ts-extension`)
- name: Run ts-bundle tests (vitest)
shell: bash
run: npm run test -w @quarto/ts-bundle
# Same freshness gate for the embedded ts-bundle driver: rollup and its
# plugins are pinned exactly, so the bytes depend only on the sources.
- name: Check ts-bundle driver freshness
shell: bash
run: |
npm run bundle -w @quarto/ts-bundle
git diff --exit-code -- ts-packages/quarto-ts-bundle/dist/ts-bundle.mjs
# preview-renderer's integration tier imports wasm-quarto-hub-client
# (aliased in vitest.integration.config.ts:53-55 to the committed
# hub-client/wasm-quarto-hub-client symlink -> crates/.../pkg). Without it
# 26 of 50 files fail on module resolution, which is why this one suite
# lives here rather than in workspace-ts-suites: it must run below the
# `Build WASM module` step. GH #250.
- name: Run preview-renderer integration tests
shell: bash
run: npm run test:integration -w ts-packages/preview-renderer
# quarto-hub-mcp's parser tier (CAP-11): the qmd-structure tool tests
# need the wasm-qmd-parser nodejs pkg, which only a wasm32 toolchain
# can build. workspace-ts-suites is deliberately toolchain-free (the
# suite's global setup writes a marker and the tier skips there), so
# the tier runs here alongside the other WASM-needing suites. Beyond
# the qmd-* files, the tier includes: conformance.test.ts, whose
# parser-gated golden cases (e.g. get_outline) skip there but must
# execute somewhere; bundle.test.ts, which stages the pkg into
# dist-bundle at build time and so cannot run toolchain-free at all;
# and npm-pack.test.ts / mcpb-pack.test.ts (CAP-15), whose pack
# scripts rebuild that bundle.
#
# QUARTO_QMD_PARSER_REQUIRED=1: this step has the toolchain, so a
# parser build failure here means the parser does not compile. Without
# it the global setup writes its skip marker and the step goes green on
# skipped tests, which is how a pampa feature-gating break reached the
# v0.34.0 release (bd-yt3x1gwk).
- name: Install wasm-pack
uses: taiki-e/install-action@wasm-pack
- name: Run quarto-hub-mcp parser-tier tests
shell: bash
env:
QUARTO_QMD_PARSER_REQUIRED: "1"
run: |
npm run build --if-present -w ts-packages/quarto-sync-client -w ts-packages/quarto-automerge-schema
cd ts-packages/quarto-hub-mcp
npx vitest run src/qmd-ast.test.ts src/qmd-outline.test.ts src/qmd-sections.test.ts src/conformance.test.ts src/bundle.test.ts src/npm-pack.test.ts src/mcpb-pack.test.ts
# ── Workspace TS suites (GH #250) ────────────────────────────────────────
#
# A separate job on purpose. These suites need only Node + npm ci + the
# ts-packages dists — no Rust, no WASM, no pandoc/tree-sitter. Keeping
# them out of `test-suite` means a red hub-client test or the
# bundle-freshness gate cannot mask them, and they are not serialised behind
# an uncached WASM build on both matrix legs. The one suite that does need
# the WASM (preview-renderer's integration tier) lives in `test-suite`.
workspace-ts-suites:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
name: Workspace TS suites
if: github.repository == 'quarto-dev/q2'
steps:
- name: Checkout Repo
uses: actions/checkout@v6
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
- name: Install npm dependencies
shell: bash
run: npm ci
# quarto-sync-client, quarto-hub-mcp and annotated-qmd resolve workspace
# siblings through the `"import": "./dist/index.js"` export condition, and
# hub-client bundles these packages from *source*, so nothing else builds
# them. Build order is irrelevant — types resolve via src/ (see
# crates/xtask/src/ts_packages.rs). Same guarded loop as
# hub-client-e2e.yml:144-153.
- name: Build ts-packages workspaces
shell: bash
run: |
for pkg in ts-packages/*/; do
if [ -f "$pkg/package.json" ]; then
npm run build --workspace "$pkg" --if-present
fi
done
# `--help` exits 0 only after the whole ESM graph links, so a missing or
# stale dependency dist fails here rather than in someone's MCP session.
# This is the same smoke check cargo xtask verify step 6 runs.
- name: Smoke-check quarto-hub-mcp module graph
shell: bash
run: node ts-packages/quarto-hub-mcp/dist/index.js --help
# Suites that are green as-is (GH #250 census, 2026-08-22). Explicit
# `npm test` rather than each package's `test:ci`: kanban's test:ci chains
# a test:wasm leg whose glob matches zero files, and preview-runtime's
# test:wasm names a config file that does not exist.
- name: Run preview-renderer unit tests
shell: bash
run: npm test -w ts-packages/preview-renderer
- name: Run preview-runtime unit tests
shell: bash
run: npm test -w ts-packages/preview-runtime
- name: Run pdf-viewer unit tests
shell: bash
run: npm test -w ts-packages/pdf-viewer
- name: Run pandoc-host unit tests
shell: bash
run: |
npm run typecheck:tests -w ts-packages/pandoc-host
npm test -w ts-packages/pandoc-host
# typst-host runs the real typst.ts wasm (from node_modules), the default fonts
# (typst-assets crate, SHA-pinned) and, for the parity test, the pdf.js build: all
# three come from `fetch-pandoc-wasm.mjs`.
- name: Fetch pandoc, typst and pdf.js assets
shell: bash
run: node scripts/fetch-pandoc-wasm.mjs --require
# The browser compiler is typst 0.14.2 (resources/typst-wasm.json) while the Rust tests
# above use TYPST_VERSION (0.15.1). The parity test compares the two compilers' page
# count and text, so it needs a native binary of the *browser's* version, pinned here
# (not brew, which is unpinned).
- name: Set up native typst for browser parity (pinned)
shell: bash
run: |
TYPST_PARITY_VERSION=$(node -p "require('./resources/typst-wasm.json').typst_version")
if [ "$RUNNER_OS" = "Linux" ]; then TRIPLE=x86_64-unknown-linux-musl; else TRIPLE=aarch64-apple-darwin; fi
curl -fsSL -o typst-parity.tar.xz "https://github.com/typst/typst/releases/download/v${TYPST_PARITY_VERSION}/typst-${TRIPLE}.tar.xz"
mkdir typst-parity && tar -xf typst-parity.tar.xz -C typst-parity --strip-components=1
echo "TYPST_PARITY_BIN=$PWD/typst-parity/typst" >> "$GITHUB_ENV"
echo "TYPST_PARITY_REQUIRED=1" >> "$GITHUB_ENV"
- name: Run typst-host unit tests (includes browser/native parity)
shell: bash
run: |
npm run typecheck:tests -w ts-packages/typst-host
npm test -w ts-packages/typst-host
- name: Run quarto-api tests
shell: bash
run: npm test -w ts-packages/quarto-api
- name: Run quarto-automerge-schema tests
shell: bash
run: npm test -w ts-packages/quarto-automerge-schema
- name: Run wasm-js-bridge tests
shell: bash
run: npm test -w ts-packages/wasm-js-bridge
- name: Run q2-preview-spa tests
shell: bash
run: |
npm test -w q2-preview-spa
npm run test:integration -w q2-preview-spa
- name: Run kanban demo tests
shell: bash
run: |
npm test -w q2-demos/kanban
npm run test:integration -w q2-demos/kanban
- name: Run trace-viewer tests
shell: bash
run: npm test -w trace-viewer
# These two resolve workspace siblings through dist/, so they need the
# "Build ts-packages workspaces" step above. hub-mcp's parser tier
# (qmd-*, conformance's parser-gated cases, bundle.test.ts,
# npm-pack.test.ts, mcpb-pack.test.ts) skips on this toolchain-free
# leg and runs in the test-suite job instead.
#
# --retry=2: `src/doc-inventory.test.ts > getDocInventory > reports
# index, text, and binary docs with states and heads` fails
# intermittently on an async peer-connection race — measured at 2
# failures in 8 local runs (~25%), which across this job's two OS legs
# is a ~44% false-red rate per PR. `--retry=2` brings that to roughly 3%
# while keeping all 137 tests gating. The underlying race is tracked as
# bd-7hd2gzf1.
- name: Run quarto-sync-client tests
shell: bash
run: npm test -w ts-packages/quarto-sync-client -- --retry=2
- name: Run quarto-hub-mcp tests
shell: bash
run: npm test -w ts-packages/quarto-hub-mcp