7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
376 lines
14 KiB
YAML
376 lines
14 KiB
YAML
name: Responsive UI Tests
|
|
|
|
on:
|
|
workflow_call:
|
|
workflow_dispatch:
|
|
|
|
# No concurrency group — intentionally omitted.
|
|
# This workflow runs only via workflow_call (from release.yml's
|
|
# responsive-test-gate) and workflow_dispatch. The pull_request trigger
|
|
# was deliberately removed in #2248 to reduce PR CI load on what is a
|
|
# heavy ~20-minute matrix build (mobile + desktop). A shared concurrency
|
|
# key here previously caused workflow_call invocations to cancel each
|
|
# other mid-flight; see #3554 (reverted in #3599) for that history.
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
ui-tests:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
viewport: [mobile, desktop] # Test mobile and desktop on each PR
|
|
|
|
services:
|
|
postgres:
|
|
image: postgres:14@sha256:ca25035f7e6f74552655a1c5e4a9eb21f85e9d316f1f70371f790ef70095dd58 # v14
|
|
env:
|
|
POSTGRES_USER: postgres
|
|
POSTGRES_PASSWORD: postgres
|
|
POSTGRES_DB: ldr_test
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--health-cmd pg_isready
|
|
--health-interval 10s
|
|
--health-timeout 5s
|
|
--health-retries 5
|
|
|
|
steps:
|
|
- name: Harden the runner (Audit all outbound calls)
|
|
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
|
|
with:
|
|
egress-policy: audit
|
|
|
|
- name: Checkout code
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Set up PDM
|
|
uses: pdm-project/setup-pdm@973541a5febeafcfdadf8a51211435be6ecfd90f # v4.5
|
|
with:
|
|
python-version: '3.12'
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
with:
|
|
node-version: '24'
|
|
cache: 'npm'
|
|
cache-dependency-path: tests/ui_tests/package-lock.json
|
|
|
|
- name: Free up disk space
|
|
run: |
|
|
# Remove unnecessary large packages to prevent disk space issues during cache save
|
|
sudo rm -rf /usr/share/dotnet
|
|
sudo rm -rf /usr/local/lib/android
|
|
sudo rm -rf /opt/ghc
|
|
sudo rm -rf /opt/hostedtoolcache/CodeQL
|
|
df -h
|
|
|
|
- name: Install system dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y \
|
|
wget \
|
|
gnupg \
|
|
ca-certificates \
|
|
fonts-liberation \
|
|
libasound2t64 \
|
|
libatk-bridge2.0-0 \
|
|
libatk1.0-0 \
|
|
libcups2 \
|
|
libdbus-1-3 \
|
|
libdrm2 \
|
|
libgbm1 \
|
|
libgtk-3-0 \
|
|
libnspr4 \
|
|
libnss3 \
|
|
libx11-xcb1 \
|
|
libxcomposite1 \
|
|
libxdamage1 \
|
|
libxrandr2 \
|
|
xdg-utils \
|
|
imagemagick
|
|
|
|
- name: Install Python dependencies
|
|
run: pdm install
|
|
|
|
- name: Install root frontend dependencies
|
|
run: npm ci
|
|
|
|
- name: Build Vite frontend bundle
|
|
# Generates src/local_deep_research/web/static/dist/, which the
|
|
# Flask app loads via vite_helper. Without this the responsive UI
|
|
# tests run against an unstyled page (no styles.css), which means
|
|
# any CSS source changes between PRs are invisible to the test
|
|
# baseline. See follow-up to PR #3985 for context.
|
|
run: npm run build
|
|
|
|
- name: Install Node test dependencies
|
|
working-directory: tests/ui_tests
|
|
run: |
|
|
npm ci
|
|
# Keep this version in sync with tests/ui_tests/package-lock.json's
|
|
# puppeteer entry. A mismatch makes npx fetch a second puppeteer
|
|
# that targets a different Chrome build, corrupting the browser
|
|
# cache ("folder exists but executable is missing"). Matching the
|
|
# locked version lets npx reuse the puppeteer just installed above.
|
|
npx puppeteer@25.1.0 browsers install chrome
|
|
|
|
- name: Set up test directories
|
|
run: |
|
|
mkdir -p ${{ github.workspace }}/data/encrypted_databases
|
|
mkdir -p tests/ui_tests/screenshots
|
|
echo "Created data and screenshots directories for tests"
|
|
|
|
- name: Start test server
|
|
env:
|
|
CI: true
|
|
FLASK_ENV: testing
|
|
TEST_ENV: true
|
|
SECRET_KEY: test-secret-key-for-ci # Security: CI test credential, not production secret
|
|
LDR_DISABLE_RATE_LIMITING: true
|
|
LDR_DATA_DIR: ${{ github.workspace }}/data
|
|
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/ldr_test # Security: CI test database credentials, not production secrets
|
|
run: |
|
|
cd src
|
|
# Start server and get its PID
|
|
pdm run python -m local_deep_research.web.app > server.log 2>&1 &
|
|
SERVER_PID=$!
|
|
echo "Server PID: $SERVER_PID"
|
|
# Persist the PID for the "Stop application server" step. That step
|
|
# runs at the workspace root (no working-directory) while we are in
|
|
# src/ here, so write an absolute path — otherwise the pidfile never
|
|
# lines up and cleanup is a silent no-op.
|
|
echo "$SERVER_PID" > "$GITHUB_WORKSPACE/server.pid"
|
|
|
|
# Wait for server to start
|
|
SERVER_READY=false
|
|
for i in {1..60}; do
|
|
# Probe the health endpoint, not bare `/`: `/` 302-redirects to
|
|
# /auth/login and `curl -f` treats a 302 as success the instant
|
|
# the socket binds — before DB/app init finishes — so it can pass
|
|
# while the app is still not ready. /api/v1/health is a true
|
|
# readiness signal and matches the docker-tests.yml gate.
|
|
if curl -fsS --connect-timeout 2 --max-time 5 http://127.0.0.1:5000/api/v1/health 2>/dev/null; then
|
|
echo "Server is ready after $i seconds"
|
|
SERVER_READY=true
|
|
break
|
|
fi
|
|
# Check if process is still running
|
|
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
|
|
echo "Server process died!"
|
|
echo "Server log:"
|
|
cat server.log
|
|
exit 1
|
|
fi
|
|
echo "Waiting for server... ($i/60)"
|
|
sleep 1
|
|
done
|
|
|
|
# Fail fast if the server is alive but never became ready in time.
|
|
# Without this guard the loop fell through silently on a slow-but-
|
|
# not-crashed start, so "Register CI test user" and the UI tests ran
|
|
# against a server that was not yet listening and failed with a
|
|
# confusing `net::ERR_CONNECTION_REFUSED` in the *test* step instead
|
|
# of a clear startup failure here. Window was also bumped 30s -> 60s
|
|
# to match the proven puppeteer-e2e-tests.yml / docker-tests.yml
|
|
# startup gates (Postgres-backed boot can exceed 30s on a loaded
|
|
# runner).
|
|
if [ "$SERVER_READY" != "true" ]; then
|
|
echo "❌ Server did not become ready within 60 seconds"
|
|
echo "Server log:"
|
|
cat server.log
|
|
exit 1
|
|
fi
|
|
|
|
- name: Register CI test user
|
|
working-directory: tests/ui_tests
|
|
run: node register_ci_user.js http://127.0.0.1:5000
|
|
|
|
- name: Run responsive UI tests - ${{ matrix.viewport }}
|
|
id: run-tests
|
|
working-directory: tests/ui_tests
|
|
env:
|
|
VIEWPORT: ${{ matrix.viewport }}
|
|
run: |
|
|
set +e # Don't exit on test failure
|
|
|
|
# Run tests and capture output
|
|
HEADLESS=true node test_responsive_ui_comprehensive.js "$VIEWPORT" 2>&1 | tee test-output.log
|
|
TEST_EXIT_CODE="${PIPESTATUS[0]}"
|
|
|
|
# Extract summary for PR comment
|
|
echo "### 📱 $VIEWPORT Test Results" > test-summary.md
|
|
echo "" >> test-summary.md
|
|
|
|
# Extract pass/fail counts
|
|
PASSED=$(grep -oP '\d+(?= passed)' test-output.log | tail -1 || echo "0")
|
|
FAILED=$(grep -oP '\d+(?= failed)' test-output.log | tail -1 || echo "0")
|
|
WARNINGS=$(grep -oP '\d+(?= warnings)' test-output.log | tail -1 || echo "0")
|
|
|
|
if [ "$FAILED" -eq "0" ]; then
|
|
echo "✅ **All tests passed!**" >> test-summary.md
|
|
else
|
|
echo "❌ **$FAILED critical issues found**" >> test-summary.md
|
|
fi
|
|
|
|
{
|
|
echo ""
|
|
echo "- ✅ Passed: $PASSED"
|
|
echo "- ❌ Failed: $FAILED"
|
|
echo "- ⚠️ Warnings: $WARNINGS"
|
|
} >> test-summary.md
|
|
|
|
{
|
|
echo "test_exit_code=$TEST_EXIT_CODE"
|
|
echo "test_failed=$FAILED"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
# Only fail if critical failures
|
|
exit "$TEST_EXIT_CODE"
|
|
|
|
- name: Upload viewport test results
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: ui-test-results-${{ matrix.viewport }}
|
|
path: |
|
|
tests/ui_tests/test-output.log
|
|
tests/ui_tests/test-summary.md
|
|
tests/ui_tests/screenshots/
|
|
tests/ui_tests/responsive/
|
|
src/server.log
|
|
retention-days: 7
|
|
if-no-files-found: ignore
|
|
|
|
# NOTE: Earlier screenshot generation/upload steps were removed to fix actionlint warnings
|
|
# caused by "if: false" disablement. Screenshots are now captured into the artifact above
|
|
# when they exist. Visual regression workflows can re-enable dedicated screenshot steps later.
|
|
|
|
- name: Upload to GitHub Pages (if available)
|
|
if: always() && github.event_name == 'pull_request'
|
|
continue-on-error: true
|
|
env:
|
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
VIEWPORT: ${{ matrix.viewport }}
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
GH_REPOSITORY: ${{ github.repository }}
|
|
GH_REPO_OWNER: ${{ github.repository_owner }}
|
|
GH_REPO_NAME: ${{ github.event.repository.name }}
|
|
run: |
|
|
# This requires GitHub Pages to be enabled for the repo
|
|
# Create a branch for the screenshots
|
|
BRANCH_NAME="pr-screenshots-$PR_NUMBER-$VIEWPORT"
|
|
|
|
# Check if screenshots directory exists
|
|
if [ ! -d "tests/ui_tests/responsive" ]; then
|
|
echo "No screenshots directory found, skipping upload"
|
|
exit 0
|
|
fi
|
|
|
|
cd tests/ui_tests/responsive
|
|
git init
|
|
git config user.name "GitHub Actions"
|
|
git config user.email "actions@github.com"
|
|
|
|
# Add all screenshots and gallery
|
|
git add responsive-ui-tests screenshot-gallery.html
|
|
git commit -m "Screenshots for PR #$PR_NUMBER"
|
|
|
|
# Push to a dedicated branch (requires write permissions)
|
|
git push --force "https://x-access-token:$GH_TOKEN@github.com/$GH_REPOSITORY.git" "HEAD:$BRANCH_NAME" 2>/dev/null || true
|
|
|
|
# The URL would be: https://[owner].github.io/[repo]/pr-screenshots-[number]-[viewport]/screenshot-gallery.html
|
|
echo "Screenshots available at: https://$GH_REPO_OWNER.github.io/$GH_REPO_NAME/$BRANCH_NAME/screenshot-gallery.html" || true
|
|
|
|
- name: Stop application server
|
|
if: always()
|
|
run: |
|
|
if [ -f server.pid ]; then
|
|
kill "$(cat server.pid)" || true
|
|
rm server.pid
|
|
fi
|
|
# `pdm run` stays alive as a wrapper, so the pidfile holds the
|
|
# wrapper PID and killing it can orphan the actual python server.
|
|
# Add the same pkill fallback the webkit workflow uses so the
|
|
# python process (and port 5000) is actually released.
|
|
pkill -f "python -m local_deep_research.web.app" || true
|
|
|
|
post-results:
|
|
needs: ui-tests
|
|
runs-on: ubuntu-latest
|
|
# Workflow has no pull_request trigger; gating on event_name == 'pull_request' kept this job
|
|
# from ever running. Always run so the combined report is built for workflow_dispatch / release.
|
|
if: always()
|
|
|
|
steps:
|
|
- name: Harden the runner (Audit all outbound calls)
|
|
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
|
|
with:
|
|
egress-policy: audit
|
|
|
|
- name: Checkout code
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Download all artifacts
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
path: test-artifacts/
|
|
|
|
- name: Generate combined report
|
|
run: |
|
|
# Create a combined markdown report with screenshot links
|
|
{
|
|
echo "# 📊 Responsive UI Test Report"
|
|
echo ""
|
|
echo "## Test Summary"
|
|
echo ""
|
|
} > combined-report.md
|
|
|
|
# Process each viewport's results
|
|
for dir in test-artifacts/ui-test-results-*/; do
|
|
if [ -d "$dir" ]; then
|
|
viewport=$(basename "$dir" | sed 's/ui-test-results-//')
|
|
echo "### $viewport" >> combined-report.md
|
|
|
|
if [ -f "$dir/test-summary.md" ]; then
|
|
cat "$dir/test-summary.md" >> combined-report.md
|
|
fi
|
|
|
|
# Count screenshots
|
|
screenshot_count=$(find "$dir" -name "*.png" 2>/dev/null | wc -l)
|
|
if [ "$screenshot_count" -gt "0" ]; then
|
|
{
|
|
echo ""
|
|
echo "📸 **$screenshot_count screenshots captured**"
|
|
} >> combined-report.md
|
|
fi
|
|
|
|
echo "" >> combined-report.md
|
|
fi
|
|
done
|
|
|
|
{
|
|
echo "## 📥 Download Options"
|
|
echo ""
|
|
echo "- [Download all test artifacts](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})"
|
|
echo "- View artifacts in the 'Artifacts' section below the workflow summary"
|
|
} >> combined-report.md
|
|
|
|
- name: Upload combined report
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: combined-test-report
|
|
path: combined-report.md
|
|
retention-days: 30
|