Skip to content

hub-client: UI/UX modernization Phase 1 — component consistency #1672

hub-client: UI/UX modernization Phase 1 — component consistency

hub-client: UI/UX modernization Phase 1 — component consistency #1672

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.8.3"
# Deno version for engine-host-deno vitest + deno-test + bundle freshness gate.
DENO_VERSION: "2.9.0"
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
# 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
# Deno — needed for engine-host-deno deno-test and the freshness gate.
# denoland/setup-deno is safe here: this workflow has no "Free disk space"
# step (and no remove_tool_cache invariant to preserve).
- name: Set up Deno
uses: denoland/setup-deno@v2
with:
deno-version: ${{ env.DENO_VERSION }}
# 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
- name: Run hub-client tests
shell: bash
run: |
cd hub-client
npm run test:ci
# engine-host-deno: vitest suite (105+ Node-side harness tests)
- name: Run engine-host-deno tests (vitest)
shell: bash
run: npm run test -w @quarto/engine-host-deno
# engine-host-deno: deno-native test (the one leg that requires Deno)
- name: Run engine-host-deno deno test
shell: bash
run: deno test --allow-all ts-packages/quarto-engine-host-deno/src/deno-host.deno-test.ts
# plan1a.6 Phase 2 (Deno dial-back) — seam #8: connectControl round-trip
# over a REAL loopback socket (Deno.listen({ port: 0 }) in-test); no mock
# Deno.Conn. CI-only tier (cannot be a vitest test — the module references
# Deno.*). `--sloppy-imports` lets deno's type-checker resolve
# @quarto/types' `.js` internal specifiers pulled in transitively via
# ./types.ts (same reason the wire-parity step below needs it).
- name: Run engine-host-deno control-transport deno test
shell: bash
run: deno test --allow-all --sloppy-imports ts-packages/quarto-engine-host-deno/src/control-transport.deno-test.ts
# T-Gate-parity: TS↔Rust wire-dual parity (Plan 2 Phase B gate). Reads the
# Rust-serialized fixture (crates/quarto-core/tests/fixtures/ts_wire_parity.json,
# produced by the regen-gated #[test] in ts_protocol.rs) and set-equates each
# instance's keys against a KEYS list pinned to the TS wire type via
# `satisfies`/`_Exhaustive`. `--sloppy-imports` lets deno's type-checker
# resolve @quarto/types' `.js` internal specifiers (so the compile guards
# are checked here, not just the runtime set-equality). @quarto/types is
# mapped in the repo-root deno.jsonc.
- name: Run engine-host-deno wire-parity deno test
shell: bash
run: deno test --allow-all --sloppy-imports ts-packages/quarto-engine-host-deno/src/wire-parity.deno-test.ts
# Bundle freshness gate: rebuild engine-host-deno.js 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-deno bundle freshness
shell: bash
run: |
npm run bundle -w @quarto/engine-host-deno
git diff --exit-code -- ts-packages/quarto-engine-host-deno/dist/engine-host-deno.js
# 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
# ── 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/deno. Keeping
# them out of `test-suite` means a red hub-client test, a deno step 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 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 is the heaviest
# suite in this job (~32s: bundle.test.ts runs esbuild in beforeAll).
#
# --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