Skip to content

FIX Keep original_float_value in line with the verdict for inverted and combined scores #6755

FIX Keep original_float_value in line with the verdict for inverted and combined scores

FIX Keep original_float_value in line with the verdict for inverted and combined scores #6755

Workflow file for this run

# Frontend testing workflow for PyRIT Frontend
# Runs unit tests, coverage checks, and E2E tests
name: Frontend Tests
on:
push:
branches:
- "main"
- "releases/v*"
pull_request:
branches:
- "main"
- "releases/**"
merge_group:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: "22"
PYTHON_VERSION: "3.11"
jobs:
unit-tests:
name: Frontend Unit Tests & Coverage
runs-on: ubuntu-latest
permissions:
contents: read
defaults:
run:
working-directory: frontend
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run unit tests with coverage
run: npm run test:coverage
- name: Upload coverage report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: coverage-report
path: frontend/coverage/
retention-days: 7
e2e-shards:
name: Frontend E2E (${{ matrix.project }}, shard ${{ matrix.shard }}/${{ matrix.total }})
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- project: mock
shard: 1
total: 2
- project: mock
shard: 2
total: 2
- project: seeded
shard: 1
total: 1
outputs:
expected-reports: ${{ strategy.job-total }}
permissions:
contents: read
defaults:
run:
working-directory: frontend
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
cache-dependency-path: frontend/package-lock.json
- name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: "0.9.17"
enable-cache: true
cache-dependency-glob: |
**/pyproject.toml
**/uv.lock
- name: Install Python dependencies
run: |
cd ..
uv sync
- name: Install frontend dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Run E2E shard (no credentials needed)
run: npx playwright test --fail-on-flaky-tests --project=${{ matrix.project }} --shard=${{ matrix.shard }}/${{ matrix.total }}
env:
CI: true
# Mock specs still use the backend for auth bootstrap and some API calls.
E2E_SEEDED_MODE: true
FAIL_ON_SKIPPED_TESTS: true
- name: Upload Playwright blob report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: playwright-blob-${{ matrix.project }}-${{ matrix.shard }}
path: frontend/blob-report/
if-no-files-found: error
# Keep successful shards available when only failed jobs are rerun.
overwrite: true
retention-days: 7
- name: Upload test results
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: failure() || cancelled()
with:
name: test-results-${{ matrix.project }}-${{ matrix.shard }}-attempt-${{ github.run_attempt }}
path: frontend/test-results/
retention-days: 7
e2e-tests:
name: Frontend E2E Tests
needs: e2e-shards
if: always()
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
defaults:
run:
working-directory: frontend
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Download Playwright blob reports
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
if: always()
with:
pattern: playwright-blob-*
path: frontend/blob-report/
merge-multiple: false
- name: Prepare and validate shard reports
if: always()
env:
EXPECTED_REPORTS: ${{ needs.e2e-shards.outputs.expected-reports }}
run: |
node <<'NODE'
const fs = require('node:fs');
const path = require('node:path');
const directory = 'blob-report';
fs.mkdirSync(directory, { recursive: true });
const artifacts = fs.readdirSync(directory, { withFileTypes: true })
.filter(entry => entry.isDirectory() && entry.name.startsWith('playwright-blob-'));
let complete = artifacts.length > 0 && artifacts.length === Number(process.env.EXPECTED_REPORTS);
for (const artifact of artifacts) {
const artifactPath = path.join(directory, artifact.name);
const reports = fs.readdirSync(artifactPath, { withFileTypes: true })
.filter(entry => entry.isFile() && entry.name.endsWith('.zip'));
if (reports.length !== 1) {
console.error(`::error::${artifact.name} must contain exactly one blob report (found ${reports.length}).`);
complete = false;
}
for (const report of reports) {
fs.copyFileSync(path.join(artifactPath, report.name), path.join(directory, `${artifact.name}-${report.name}`));
}
}
if (!complete) {
console.error(`::error::Expected one blob report per shard (${process.env.EXPECTED_REPORTS || 'unknown'} shards); found ${artifacts.length} artifacts.`);
process.exitCode = 1;
}
NODE
- name: Merge Playwright reports
if: always()
run: npx playwright merge-reports --reporter=html blob-report
- name: Upload Playwright report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: playwright-report
path: frontend/playwright-report/
if-no-files-found: error
overwrite: true
retention-days: 7
- name: Fail unless every E2E shard passed
if: always() && (needs.e2e-shards.result != 'success' || cancelled())
env:
SHARD_RESULT: ${{ needs.e2e-shards.result }}
run: |
echo "::error::All E2E shards must succeed without cancellation (result=$SHARD_RESULT)."
exit 1
lint:
name: Frontend Lint & Type Check
runs-on: ubuntu-latest
permissions:
contents: read
defaults:
run:
working-directory: frontend
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Run TypeScript type check
run: npx tsc --noEmit
- name: Check E2E discovery before generating provenance
run: npx playwright test --list --reporter=list
- name: Build production frontend
run: npm run build