Skip to content

Merge pull request #3014 from Hack23/dependabot/npm_and_yarn/developm… #3863

Merge pull request #3014 from Hack23/dependabot/npm_and_yarn/developm…

Merge pull request #3014 from Hack23/dependabot/npm_and_yarn/developm… #3863

name: TypeScript & JavaScript Testing
on:
push:
branches:
- main
- develop
paths:
- '**/*.ts'
- '**/*.js'
- 'src/browser/**'
- 'package.json'
- 'package-lock.json'
- 'tsconfig*.json'
- 'vite.config.js'
- 'vitest.config.js'
- 'cypress.config.js'
- 'tests/**'
- 'cypress/**'
- '.github/workflows/javascript-testing.yml'
pull_request:
branches:
- main
- develop
paths:
- '**/*.ts'
- '**/*.js'
- 'src/browser/**'
- 'package.json'
- 'package-lock.json'
- 'tsconfig*.json'
- 'vite.config.js'
- 'vitest.config.js'
- 'cypress.config.js'
- 'tests/**'
- 'cypress/**'
workflow_dispatch:
permissions:
contents: read
jobs:
# Vitest Unit Tests
unit-tests:
name: Unit Tests (Vitest)
runs-on: ubuntu-26.04
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: block
allowed-endpoints: >
api.github.com:443
codeload.github.com:443
github.com:443
nodejs.org:443
objects.githubusercontent.com:443
registry.npmjs.org:443
download.cypress.io:443
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '26'
cache: 'npm'
cache-dependency-path: |
package-lock.json
.github/workflows/javascript-testing.yml
- name: Install dependencies
run: npm ci
- name: TypeScript type-check (browser)
run: npx tsc --project tsconfig.browser.json --noEmit
- name: TypeScript type-check (scripts)
run: npx tsc --project tsconfig.scripts.json --noEmit
- name: Generate TypeDoc API documentation
run: npm run typedoc
- name: Run Vitest unit tests
run: npm run test:coverage
- name: Archive coverage results
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-report
path: coverage/
retention-days: 30
# Vite Build Test
build-test:
name: Build Test (Vite)
runs-on: ubuntu-26.04
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: block
allowed-endpoints: >
api.github.com:443
codeload.github.com:443
github.com:443
nodejs.org:443
objects.githubusercontent.com:443
registry.npmjs.org:443
download.cypress.io:443
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '26'
cache: 'npm'
cache-dependency-path: |
package-lock.json
.github/workflows/javascript-testing.yml
- name: Install dependencies
run: npm ci
- name: Build with Vite
# The static-pages-emit plugin (scripts/vite-plugin-static-
# pages.js) keeps the ~3 500 news/*.html, sitemap_*.html and
# political-intelligence_*.html pages out of Rollup's module
# graph; the build now needs <1 GB heap on the current corpus,
# well under Node's default ~4 GB.
run: npm run build
- name: Check build output
run: |
ls -la dist/
echo "✅ Build completed successfully"
- name: Archive build artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: build-dist
path: dist/
retention-days: 30
# dist/ has ~5 700 files including 14 × 4.4 MB political-intelligence_*.html
# bundles (~62 MB) plus 5 200+ news/*.html pages. At the default
# compression-level=6 the upload phase took ~13 min on a hosted runner
# in run #26134023344 and was killed mid-upload by a runner shutdown
# signal. Level 1 (Z_BEST_SPEED) cuts the wall-clock by ~70 % on
# highly-compressible HTML at a marginal size cost, shrinking the
# window in which infra cancellation can hit.
compression-level: 1
# Multi-Language Sanity Tests
# (Other E2E tests moved to separate workflows: test-homepage.yml, test-dashboard.yml, test-news.yml)
multi-language-tests:
name: Multi-Language Sanity Tests
runs-on: ubuntu-26.04
needs: build-test
# This job covers homepage + dashboard + news across all 14 languages
# (~3× the work of the per-page Cypress jobs), so it needs a larger budget
# than the 5-minute per-page jobs. The previous 5-minute timeout caused
# repeated cancellations on both PRs and main (see run 3064).
timeout-minutes: 15
# Use the official Cypress browsers image so Chrome/Firefox/Edge are
# preinstalled at known versions — eliminates cypress↔chrome version
# drift and bundles the system libraries (Xvfb, fonts) Cypress needs.
# Node 26 is installed inside the container via actions/setup-node
# because no Cypress image yet ships Node 26.
# NOTE: the cypress NPM binary itself is still downloaded from
# download.cypress.io during `npm ci` — only the browser binaries
# are preinstalled by the image. cdn.cypress.io / download.cypress.io
# are therefore still required egress; they are no longer in the
# allow-list because harden-runner cannot run in a container job.
# TODO: pin to an immutable @sha256: digest via Renovate.
container:
image: cypress/browsers:latest
options: --user root
steps:
# NOTE: step-security/harden-runner is host-level and is not
# compatible with container jobs, so this job runs WITHOUT
# runtime egress restrictions — accepted trade-off for browser
# version determinism. The harden-runner unit / build jobs above
# (with strict block mode) still gate the artifacts and source
# this job consumes, but do not enforce outbound restrictions for
# this job at runtime.
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '26'
cache: 'npm'
cache-dependency-path: |
package-lock.json
.github/workflows/javascript-testing.yml
- name: Install dependencies
# `--no-audit --no-fund` shaves several seconds; `--prefer-offline`
# is harmless when the npm cache is cold (container job has no
# GitHub Actions cache integration).
run: npm ci --prefer-offline --no-audit --no-fund
- name: Download build artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: build-dist
path: dist/
- name: Run Multi-Language Tests
uses: cypress-io/github-action@948d67d3074f1bbb6379c8bdbb04e95d2f8e593f # v7.4.0
with:
start: npm run preview
wait-on: 'http://localhost:4173'
wait-on-timeout: 30
browser: chrome
spec: 'cypress/e2e/multi-language-sanity.cy.js'
config: video=false
quiet: true
# Skip the action's bundled `npm install` — we already ran
# `npm ci` above and the container ships browsers preinstalled.
install: false
- name: Archive screenshots
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: multi-language-screenshots
path: cypress/screenshots/
retention-days: 7
# Node.js Nightly Compatibility Check
# Runs unit tests against the latest Node.js nightly build to give early warning
# of breaking changes before the next major version becomes current.
# Never blocks the pipeline (continue-on-error: true).
node-next-compat:
name: Node.js Nightly Compat
runs-on: ubuntu-26.04
continue-on-error: true
# Bound the whole job so a hung checkout/fetch (transient GitHub-hosted
# runner flake — see PR #2428 run 25737016620 where `git fetch` stalled
# 11 min until the runner received a shutdown signal) fails fast and
# cleanly via the job-level `continue-on-error: true`, instead of
# consuming the default 360-min budget and waiting for an external kill.
timeout-minutes: 10
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: block
allowed-endpoints: >
api.github.com:443
codeload.github.com:443
github.com:443
nodejs.org:443
objects.githubusercontent.com:443
registry.npmjs.org:443
download.cypress.io:443
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Bound the checkout step itself so a hung `git fetch` (the
# documented failure mode on the previous run) fails inside the
# job's 10-minute envelope rather than dragging out the whole job.
timeout-minutes: 5
- name: Resolve latest Node.js nightly version
id: nightly
run: |
CURL_OUTPUT=$(curl -sfL 'https://nodejs.org/download/nightly/index.json' 2>&1) || { \
echo "::warning::curl failed fetching nodejs.org nightly index: $CURL_OUTPUT"; echo "version=" >> "$GITHUB_OUTPUT"; exit 0; }
# Walk the nightly index from newest to oldest and pick the first
# version whose linux-x64 tarball is actually downloadable. The
# raw newest entry can lag the file mirror by 5-30 minutes (audit
# 2026-05-09: index advertised 27.0.0-nightly20260509bbf51ad24c
# while the tarball was still 404), and setup-node@v6 fails the
# step when the binary 404s — bypassing the job-level
# continue-on-error budget for the rest of the run summary. The
# probe loop keeps the nightly build floating to whichever
# tarball exists *right now*, never blocking on mirror lag.
VERSION=$(echo "$CURL_OUTPUT" | python3 -c "
import json, sys, urllib.request, urllib.error
data = json.load(sys.stdin)
for entry in data[:5]:
v = entry['version'].lstrip('v')
url = f'https://nodejs.org/download/nightly/v{v}/node-v{v}-linux-x64.tar.xz'
req = urllib.request.Request(url, method='HEAD')
try:
with urllib.request.urlopen(req, timeout=10) as resp:
if resp.status == 200:
print(v); sys.exit(0)
except (urllib.error.URLError, urllib.error.HTTPError):
continue
" 2>&1) || { \
echo "::warning::Failed to find a downloadable nightly tarball in the 5 most recent entries: $VERSION"; echo "version=" >> "$GITHUB_OUTPUT"; exit 0; }
if [ -z "$VERSION" ]; then
echo "::warning::No downloadable nightly tarball in the 5 most recent index entries — skipping nightly compat check."
echo "version=" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Latest downloadable nightly: $VERSION"
- name: Setup Node.js nightly
if: steps.nightly.outputs.version != ''
continue-on-error: true
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
id: setup_nightly
with:
node-version: ${{ steps.nightly.outputs.version }}
cache: 'npm'
cache-dependency-path: |
package-lock.json
.github/workflows/javascript-testing.yml
- name: Install dependencies
if: steps.nightly.outputs.version != '' && steps.setup_nightly.outcome == 'success'
run: npm ci
- name: TypeScript type-check (browser)
if: steps.nightly.outputs.version != '' && steps.setup_nightly.outcome == 'success'
run: npx tsc --project tsconfig.browser.json --noEmit
- name: TypeScript type-check (scripts)
if: steps.nightly.outputs.version != '' && steps.setup_nightly.outcome == 'success'
run: npx tsc --project tsconfig.scripts.json --noEmit
- name: Run Vitest unit tests
if: steps.nightly.outputs.version != '' && steps.setup_nightly.outcome == 'success'
run: npm run test
# Summary Report
test-summary:
name: Test Summary
runs-on: ubuntu-26.04
needs: [unit-tests, build-test, multi-language-tests, node-next-compat]
if: always()
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: block
allowed-endpoints: >
api.github.com:443
github.com:443
- name: Test Results Summary
run: |
echo "## 🧪 Core Test Results Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Unit Tests (Vitest)" >> $GITHUB_STEP_SUMMARY
echo "Status: ${{ needs.unit-tests.result }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Build Test (Vite)" >> $GITHUB_STEP_SUMMARY
echo "Status: ${{ needs.build-test.result }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Multi-Language Tests" >> $GITHUB_STEP_SUMMARY
echo "Status: ${{ needs.multi-language-tests.result }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Node.js Nightly Compat (non-blocking)" >> $GITHUB_STEP_SUMMARY
echo "Status: ${{ needs.node-next-compat.result }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Note:** Section-specific E2E tests run in separate workflows:" >> $GITHUB_STEP_SUMMARY
echo "- 🏠 Homepage: test-homepage.yml" >> $GITHUB_STEP_SUMMARY
echo "- 📊 Dashboard: test-dashboard.yml" >> $GITHUB_STEP_SUMMARY
echo "- 📰 News: test-news.yml" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.unit-tests.result }}" == "success" ] && \
[ "${{ needs.build-test.result }}" == "success" ] && \
[ "${{ needs.multi-language-tests.result }}" == "success" ]; then
echo "### ✅ All core tests passed!" >> $GITHUB_STEP_SUMMARY
else
echo "### ❌ Some tests failed" >> $GITHUB_STEP_SUMMARY
fi