Skip to content

🔨 Cypress config #5142

🔨 Cypress config

🔨 Cypress config #5142

name: GitHub Actions
permissions:
contents: read
pull-requests: write
on:
push:
branches:
- sandbox
- dev
paths-ignore:
- "site/**"
- "**/*.md"
pull_request:
branches:
- dev
- sandbox
types:
- opened
- synchronize
- reopened
- ready_for_review
workflow_dispatch:
inputs:
pr_number:
description: "PR number to publish under pr-<number>"
required: true
type: string
spec:
description: "E2E spec globs (comma-separated), e.g. cypress/e2e/login.cy.js,cypress/e2e/activite-suite.cy.ts"
required: false
type: string
# Permet de filtrer les tests API lors de tests exécutés manuellement
api_grep:
description: "Mocha --grep filter for API tests (optional)"
required: false
type: string
# Permet de sauter les tests E2E lors de tests exécutés manuellement
skip_e2e:
description: "Skip E2E tests (run only API tests)"
required: false
type: boolean
default: false
# Allow concurrent deployments to github-pages
concurrency:
group: github-pages-deploy
cancel-in-progress: false
jobs:
###########################
####### API TESTS ########
###########################
api-tests:
runs-on: ubuntu-latest
steps:
- run: echo "API TESTS"
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "22"
- run: docker compose version
- name: Install dependencies
run: npm ci
### Execute API test with optional --grep
- name: Run API tests (Mocha + Mochawesome)
run: |
set -e
API_GREP="${{ github.event.inputs.api_grep }}"
if [ -n "$API_GREP" ]; then
echo "Running API tests with --grep: $API_GREP"
npm run test:api -- --grep "$API_GREP"
else
npm run test:api
fi
# continue-on-error: true
- name: Debug - List API generated files
if: always()
run: |
# echo "API test directory structure:"
# find ./api -type f -name "*.json" -o -name "*.html"
echo "Files in api/test/:"
find ./api/test/ -type f -exec ls -la {} \; 2>/dev/null || echo "ERROR: No API test directory found"
echo -e "\n######################################\n"
echo "API test reports:"
ls -la ./api/test/reports || echo "ERROR: API test report directory not found"
echo -e "\n######################################\n"
echo "Mochawesome specific files:"
find ./api/ -name "*.json" -o -name "*.html" -o -name "mochawesome*" 2>/dev/null || echo "No mochawesome files found"
- name: Upload Mocha test reports (.json and .html)
if: always()
uses: actions/upload-artifact@v5
with:
name: mocha-report
path: ./api/test/reports
if-no-files-found: warn
###########################
######## E2E TESTS #######
###########################
e2e-tests:
if: ${{ github.event.inputs.skip_e2e != 'true' }}
runs-on: ubuntu-latest
steps:
- run: echo "E2E TESTS"
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "22"
- run: docker compose version
- name: Install dependencies
run: npm ci
### Ensure E2E artifact directories exist by creating them
- name: Ensure E2E artifact directories exist
if: always()
run: |
mkdir -p ./end-to-end/cypress/screenshots
mkdir -p ./end-to-end/cypress/videos
mkdir -p ./end-to-end/cypress/reports
mkdir -p ./end-to-end/cypress/downloads
### Execute E2E test
- name: Run E2E tests
run: |
npm run test:e2e
# continue-on-error: true
- name: Fix permissions for end-to-end artifacts
if: always()
run: sudo chown -R "$(id -u)":"$(id -g)" ./end-to-end || true
- name: Debug - List E2E generated files
if: always()
run: |
echo "=== END-TO-END TESTS DEBUG ==="
echo "Files in end-to-end/:"
find ./end-to-end -type f -not -path "*/node_modules/*" -not -path "*/.*" \( -name "*.json" -o -name "*.mp4" -o -name "*.cy.ts" -o -name "*.png" \) 2>/dev/null
echo -e "\n######################################\n"
echo "E2E test reports:"
echo "directory: ./end-to-end/cypress/reports"
ls -la ./end-to-end/cypress/reports 2>/dev/null || echo "Reports directory not found"
echo -e "\n######################################\n"
echo "Fallback mochawesome-report directory content:"
echo "directory: ./end-to-end/cypress/mochawesome-report"
ls -laR ./end-to-end/cypress/mochawesome-report 2>/dev/null || echo "Fallback mochawesome-report directory not found"
echo -e "\n######################################\n"
echo "Videos directory content:"
echo "directory: ./end-to-end/cypress/videos"
ls -la ./end-to-end/cypress/videos/ 2>/dev/null || echo "Videos directory not found"
echo -e "\n######################################\n"
echo "Screenshots directory content:"
echo "directory: ./end-to-end/cypress/screenshots"
ls -laR ./end-to-end/cypress/screenshots/ 2>/dev/null || echo "Screenshots directory not found"
- name: Collect Cypress JSON reports (normalize)
if: always()
run: |
set -e
mkdir -p ./end-to-end/cypress/reports/results.jsons
echo "Collecting JSONs from cypress/reports and mochawesome-report into cypress/reports/results.jsons"
find ./end-to-end/cypress/reports -type f -name "*.json" -print -exec cp -n {} ./end-to-end/cypress/reports/results.jsons/ \; 2>/dev/null || true
find ./end-to-end/cypress/mochawesome-report -type f -name "*.json" -print -exec cp -n {} ./end-to-end/cypress/reports/results.jsons/ \; 2>/dev/null || true
echo -e "\n######################################\n"
echo "Final JSONs to upload:"
ls -la ./end-to-end/cypress/reports/results.jsons 2>/dev/null || true
- name: Upload Logs if tests failed
if: always()
uses: actions/upload-artifact@v5
with:
name: e2e-docker-logs
path: ./end-to-end/logs
- name: Upload Cypress video
if: always()
uses: actions/upload-artifact@v5
with:
name: cypress-video
path: ./end-to-end/cypress/videos
- name: Upload Cypress screenshots
if: always()
uses: actions/upload-artifact@v5
with:
name: cypress-screenshots
path: ./end-to-end/cypress/screenshots
if-no-files-found: warn
- name: Upload Cypress test reports and Mochawesome reports
if: always()
uses: actions/upload-artifact@v5
with:
name: cypress-reports
path: |
./end-to-end/cypress/reports/*.json
./end-to-end/cypress/reports/**/*.json
./end-to-end/cypress/mochawesome-report/*.json
./end-to-end/cypress/mochawesome-report/**/*.json
# ./end-to-end/cypress/mochawesome-report/*.html
# ./end-to-end/cypress/mochawesome-report/**/*.html
include-hidden-files: true
if-no-files-found: warn
- name: Upload Cypress downloads (Excel files)
if: always()
uses: actions/upload-artifact@v5
with:
name: cypress-downloads
path: |
./end-to-end/cypress/downloads/*.xlsx
./end-to-end/cypress/downloads/*.xls
if-no-files-found: warn
##########################################################################
## Non-regression calculation end-to-end tests (requires SANDBOX stack) ##
##########################################################################
e2e-nonreg-vs-sandbox-tests:
if: ${{ github.event.inputs.skip_e2e != 'true' }}
runs-on: ubuntu-latest
steps:
- run: echo "E2E NON-REGRESSION TESTS"
- name: Checkout code
uses: actions/checkout@v4
- name: Checkout sandbox branch (for local sandbox stack)
uses: actions/checkout@v4
with:
ref: sandbox
path: sandbox-code
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "22"
- run: docker compose version
- name: Install dependencies
run: npm ci
- name: Ensure E2E artifact directories exist (nonreg)
if: always()
run: |
mkdir -p ./end-to-end/cypress/screenshots
mkdir -p ./end-to-end/cypress/videos
mkdir -p ./end-to-end/cypress/reports
mkdir -p ./end-to-end/cypress/downloads
- name: Run Non-Regression E2E tests
run: |
chmod +x ./scripts/run-non-regression-tests.sh
export SANDBOX_CODE_DIR="$GITHUB_WORKSPACE/sandbox-code"
./scripts/run-non-regression-tests.sh
# continue-on-error: true
- name: Fix permissions for end-to-end artifacts
if: always()
run: sudo chown -R "$(id -u)":"$(id -g)" ./end-to-end || true
- name: Debug - List E2E generated files (nonreg)
if: always()
run: |
echo "=== END-TO-END NONREG DEBUG ==="
echo "Files in end-to-end/:"
find ./end-to-end -type f -not -path "*/node_modules/*" -not -path "*/.*" \( -name "*.json" -o -name "*.mp4" -o -name "*.cy.ts" -o -name "*.png" \) 2>/dev/null || echo "ERROR: No e2E test files found"
echo -e "\n######################################\n"
echo "E2E test reports:"
echo "directory: ./end-to-end/cypress/reports"
ls -la ./end-to-end/cypress/reports 2>/dev/null || echo "Reports directory not found"
echo -e "\n######################################\n"
echo "Fallback mochawesome-report directory content:"
echo "directory: ./end-to-end/cypress/mochawesome-report"
ls -laR ./end-to-end/cypress/mochawesome-report 2>/dev/null || echo "Fallback mochawesome-report directory not found"
echo -e "\n######################################\n"
echo "Videos directory content:"
echo "directory: ./end-to-end/cypress/videos"
ls -la ./end-to-end/cypress/videos/ 2>/dev/null || echo "Videos directory not found"
echo -e "\n######################################\n"
echo "Screenshots directory content:"
echo "directory: ./end-to-end/cypress/screenshots"
ls -laR ./end-to-end/cypress/screenshots/ 2>/dev/null || echo "Screenshots directory not found"
- name: Collect Cypress JSON reports (normalize, nonreg)
if: always()
run: |
set -e
mkdir -p ./end-to-end/cypress/reports/results.jsons
echo "Collecting JSONs from cypress/reports and mochawesome-report into cypress/reports/results.jsons"
find ./end-to-end/cypress/reports -type f -name "*.json" -print -exec cp -n {} ./end-to-end/cypress/reports/results.jsons/ \; 2>/dev/null || true
find ./end-to-end/cypress/mochawesome-report -type f -name "*.json" -print -exec cp -n {} ./end-to-end/cypress/reports/results.jsons/ \; 2>/dev/null || true
echo -e "\n######################################\n"
echo "Final JSONs to upload (nonreg):"
ls -la ./end-to-end/cypress/reports/results.jsons || true
- name: Upload Logs
if: always()
uses: actions/upload-artifact@v5
with:
name: e2e-docker-logs
path: ./end-to-end/logs
- name: Upload Cypress video (nonreg)
if: always()
uses: actions/upload-artifact@v5
with:
name: cypress-video-nonreg
path: ./end-to-end/cypress/videos
- name: Upload Cypress screenshots (nonreg)
if: always()
uses: actions/upload-artifact@v5
with:
name: cypress-screenshots-nonreg
path: ./end-to-end/cypress/screenshots
if-no-files-found: warn
- name: Upload Cypress test reports and Mochawesome reports (nonreg)
if: always()
uses: actions/upload-artifact@v5
with:
name: cypress-reports-nonreg
path: |
./end-to-end/cypress/reports/*.json
./end-to-end/cypress/reports/**/*.json
./end-to-end/cypress/mochawesome-report/*.json
./end-to-end/cypress/mochawesome-report/**/*.json
# ./end-to-end/cypress/mochawesome-report/*.html
# ./end-to-end/cypress/mochawesome-report/**/*.html
include-hidden-files: true
if-no-files-found: warn
###########################
####### DEPLOY ###########
###########################
deploy:
needs: [api-tests, e2e-tests, e2e-nonreg-vs-sandbox-tests]
runs-on: ubuntu-latest
if: |
always() && (
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event_name == 'workflow_dispatch') ||
(github.event_name == 'push')
)
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
permissions:
pages: write
id-token: write
contents: read
pull-requests: write
issues: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Find pull request for push event
id: findpr
if: ${{ github.event_name == 'push' }}
uses: actions/github-script@v8
with:
script: |
const branch = context.ref.replace('refs/heads/','');
const { data: prs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
head: `${context.repo.owner}:${branch}`,
});
core.setOutput('pull-request-number', prs.length ? String(prs[0].number) : '');
- name: Determine PR number
id: prnum
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "pr=${{ github.event.number }}" >> "$GITHUB_OUTPUT"
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "pr=${{ inputs.pr_number }}" >> "$GITHUB_OUTPUT"
elif [ "${{ github.event_name }}" = "push" ]; then
echo "pr=${{ steps.findpr.outputs.pull-request-number }}" >> "$GITHUB_OUTPUT"
else
echo "pr=" >> "$GITHUB_OUTPUT"
fi
- name: Download Cypress reports artifacts
uses: actions/download-artifact@v6
continue-on-error: true
with:
name: cypress-reports
path: ./cypress-reports
- name: Download Cypress reports artifacts (nonreg)
uses: actions/download-artifact@v6
continue-on-error: true
with:
name: cypress-reports-nonreg
path: ./cypress-reports-nonreg
- name: Download Cypress videos artifacts
uses: actions/download-artifact@v6
continue-on-error: true
with:
name: cypress-video
path: ./cypress-videos
- name: Download Cypress videos artifacts (nonreg)
uses: actions/download-artifact@v6
continue-on-error: true
with:
name: cypress-video-nonreg
path: ./cypress-videos-nonreg
- name: Download Cypress screenshots artifacts
uses: actions/download-artifact@v6
continue-on-error: true
with:
name: cypress-screenshots
path: ./cypress-screenshots
- name: Download Cypress screenshots artifacts (nonreg)
uses: actions/download-artifact@v6
continue-on-error: true
with:
name: cypress-screenshots-nonreg
path: ./cypress-screenshots-nonreg
- name: Download Mocha report artifacts
uses: actions/download-artifact@v6
continue-on-error: true
with:
name: mocha-report
path: ./mocha-report
- name: Create PR report directory structure
env:
PR_NUM: ${{ steps.prnum.outputs.pr }}
RUN_NUM: ${{ github.run_number }}
run: |
set -e
mkdir -p site/pr-${PR_NUM}/run-${RUN_NUM}
mkdir -p site/pr-${PR_NUM}/run-${RUN_NUM}/cypress
# Copy E2E reports (excluding test-contexts.json for now)
find cypress-reports -type f -name "*.json" ! -name "test-contexts.json" -exec cp {} site/pr-${PR_NUM}/run-${RUN_NUM}/cypress/ \; 2>/dev/null || echo "No E2E reports to copy"
# Copy non-reg reports (excluding test-contexts.json for now)
find cypress-reports-nonreg -type f -name "*.json" ! -name "test-contexts.json" -exec cp {} site/pr-${PR_NUM}/run-${RUN_NUM}/cypress/ \; 2>/dev/null || true
# Merge test-contexts.json files from both E2E and non-reg
node -e "
const fs = require('fs');
const path = require('path');
let merged = {};
// Read E2E contexts
const e2ePaths = [
'cypress-reports/test-contexts.json',
'cypress-reports/cypress/reports/test-contexts.json',
'cypress-reports/.jsons/test-contexts.json'
];
for (const p of e2ePaths) {
if (fs.existsSync(p)) {
try {
const data = JSON.parse(fs.readFileSync(p, 'utf8'));
Object.assign(merged, data);
console.log(\`Merged \${Object.keys(data).length} contexts from \${p}\`);
} catch (e) {
console.warn(\`Failed to read \${p}:\`, e.message);
}
}
}
// Read non-reg contexts
const nonregPaths = [
'cypress-reports-nonreg/test-contexts.json',
'cypress-reports-nonreg/cypress/reports/test-contexts.json',
'cypress-reports-nonreg/.jsons/test-contexts.json'
];
for (const p of nonregPaths) {
if (fs.existsSync(p)) {
try {
const data = JSON.parse(fs.readFileSync(p, 'utf8'));
Object.assign(merged, data);
console.log(\`Merged \${Object.keys(data).length} contexts from \${p}\`);
} catch (e) {
console.warn(\`Failed to read \${p}:\`, e.message);
}
}
}
// Write merged contexts
const outPath = 'site/pr-${PR_NUM}/run-${RUN_NUM}/cypress/test-contexts.json';
fs.writeFileSync(outPath, JSON.stringify(merged, null, 2));
console.log(\`Wrote \${Object.keys(merged).length} total contexts to \${outPath}\`);
" || echo "Failed to merge test-contexts.json"
mkdir -p site/pr-${PR_NUM}/run-${RUN_NUM}/videos
cp -r cypress-videos/* site/pr-${PR_NUM}/run-${RUN_NUM}/videos/ 2>/dev/null || echo "No videos to copy"
cp -r cypress-videos-nonreg/* site/pr-${PR_NUM}/run-${RUN_NUM}/videos/ 2>/dev/null || true
mkdir -p site/pr-${PR_NUM}/run-${RUN_NUM}/screenshots
cp -r cypress-screenshots/* site/pr-${PR_NUM}/run-${RUN_NUM}/screenshots/ 2>/dev/null || echo "No screenshots to copy"
cp -r cypress-screenshots-nonreg/* site/pr-${PR_NUM}/run-${RUN_NUM}/screenshots/ 2>/dev/null || true
mkdir -p site/pr-${PR_NUM}/run-${RUN_NUM}/mocha
cp -r mocha-report/* site/pr-${PR_NUM}/run-${RUN_NUM}/mocha/ 2>/dev/null || echo "No mocha test reports to copy"
- name: Build combined static HTML (merged JSON -> bespoke report)
continue-on-error: true
env:
PR_NUM: ${{ steps.prnum.outputs.pr }}
RUN_NUM: ${{ github.run_number }}
run: |
set -e
HOST_UID=$(id -u)
HOST_GID=$(id -g)
docker run --rm --user "$HOST_UID:$HOST_GID" -e PR_NUM="$PR_NUM" -e RUN_NUM="$RUN_NUM" -e GITHUB_RUN_ID="${{ github.run_id }}" -e GITHUB_REPO="${{ github.repository }}" -e HOME=/tmp -e NPM_CONFIG_CACHE=/tmp/.npm -v "$GITHUB_WORKSPACE:/work" -w /work node:22-alpine sh -lc '
set -e
mkdir -p "$HOME" "$NPM_CONFIG_CACHE"
mkdir -p site/pr-${PR_NUM}/run-${RUN_NUM}/combined
JSONS="$(
{ find site/pr-${PR_NUM}/run-${RUN_NUM}/mocha -type f -name "*.json" ! -name "test-contexts.json" 2>/dev/null || true; }
{ find site/pr-${PR_NUM}/run-${RUN_NUM}/cypress -type f -name "*.json" ! -name "test-contexts.json" 2>/dev/null || true; }
)"
if [ -n "$JSONS" ]; then
npx -y -p mochawesome-merge mochawesome-merge $JSONS > site/pr-${PR_NUM}/run-${RUN_NUM}/combined/merged.json || true
fi
if [ -s site/pr-${PR_NUM}/run-${RUN_NUM}/combined/merged.json ]; then
node scripts/section-mochawesome.js site/pr-${PR_NUM}/run-${RUN_NUM}/combined/merged.json site/pr-${PR_NUM}/run-${RUN_NUM}/combined/sectioned.json || cp site/pr-${PR_NUM}/run-${RUN_NUM}/combined/merged.json site/pr-${PR_NUM}/run-${RUN_NUM}/combined/sectioned.json
GITHUB_RUN_URL="https://github.com/${GITHUB_REPO}/actions/runs/${GITHUB_RUN_ID}"
node scripts/build-static-report.js site/pr-${PR_NUM}/run-${RUN_NUM}/combined/sectioned.json site/pr-${PR_NUM}/run-${RUN_NUM}/videos site/pr-${PR_NUM}/run-${RUN_NUM}/screenshots site/pr-${PR_NUM}/run-${RUN_NUM}/combined/a-just-tests-report.html "$GITHUB_RUN_URL" || echo "Static report generation failed"
else
echo "No JSONs to merge for combined report"
fi
'
- name: Append Cypress Videos section to combined HTML
env:
PR_NUM: ${{ steps.prnum.outputs.pr }}
RUN_NUM: ${{ github.run_number }}
run: |
node scripts/append-videos-to-report.js \
"site/pr-${PR_NUM}/run-${RUN_NUM}/combined/a-just-tests-report.html" \
"site/pr-${PR_NUM}/run-${RUN_NUM}/videos"
- name: Check combined HTML exists
id: combined_exists
env:
PR_NUM: ${{ steps.prnum.outputs.pr }}
RUN_NUM: ${{ github.run_number }}
run: |
if [ -s "site/pr-${PR_NUM}/run-${RUN_NUM}/combined/a-just-tests-report.html" ]; then
echo "ok=true" >> "$GITHUB_OUTPUT"
else
echo "ok=false" >> "$GITHUB_OUTPUT"
fi
- name: Create per-PR landing index.html
env:
PR_NUM: ${{ steps.prnum.outputs.pr }}
run: |
mkdir -p site/pr-${PR_NUM}
cat > site/pr-${PR_NUM}/index.html << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PR Reports</title>
</head>
<body>
<h1>Reports for PR ${PR_NUM}</h1>
<ul>
<li><a href="./combined/a-just-tests-report.html" target="_blank">Combined (API + E2E)</a></li>
<li><a href="./mocha/" target="_blank">Mocha (API)</a></li>
<li><a href="./cypress/" target="_blank">Cypress (E2E)</a></li>
<li><a href="./videos/" target="_blank">Cypress Videos</a></li>
</ul>
</body>
</html>
EOF
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
with:
path: ./site
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
- name: Comment PR with Combined report URL
if: ${{ steps.prnum.outputs.pr != '' && steps.combined_exists.outputs.ok == 'true' }}
uses: actions/github-script@v8
env:
PR_NUM: ${{ steps.prnum.outputs.pr }}
RUN_NUM: ${{ github.run_number }}
DEPLOYMENT_PAGE_URL: ${{ steps.deployment.outputs.page_url }}
with:
script: |
let pageUrl = process.env.DEPLOYMENT_PAGE_URL || 'https://betagouv.github.io/a-just/';
// Ensure trailing slash for proper path construction
if (!pageUrl.endsWith('/')) {
pageUrl += '/';
}
const pr = process.env.PR_NUM;
const runNum = process.env.RUN_NUM;
const url = `${pageUrl}pr-${pr}/run-${runNum}/combined/a-just-tests-report.html`;
const body = `✅ Combined test report available (run #${runNum}): ${url}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(pr),
body
});