name: Playwright WebKit Tests on: workflow_call: # Called by release pipeline workflow_dispatch: # Manual trigger schedule: - cron: '0 2 * * *' # Daily at 2 AM UTC permissions: {} # Minimal top-level for OSSF Scorecard Token-Permissions jobs: # Shared setup: start server, register user, then run Desktop Safari tests desktop-safari: runs-on: ubuntu-latest permissions: contents: read steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: egress-policy: audit - 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' version: 2.26.2 cache: true - name: Install Python dependencies run: | pdm install -d - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: '24' cache: 'npm' cache-dependency-path: | package-lock.json tests/ui_tests/playwright/package-lock.json - name: Build Vite frontend assets run: | npm ci npm run build - name: Install Playwright dependencies (npm) working-directory: tests/ui_tests/playwright run: npm ci # ``playwright install --with-deps`` downloads ~150 MB of browser # binaries from the playwright CDN per attempt. Single CDN # hiccups have failed the whole release pipeline in the past # (e.g. transient 5xx mid-download); a single retry costs little # and removes the dominant transient-network failure mode for # this workflow. Two attempts only — three attempts of a 150 MB # download starts hiding real outages. - name: Install Playwright browsers uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 with: timeout_minutes: 10 max_attempts: 2 retry_on: error shell: bash # Install chromium for auth setup step + webkit for Safari tests command: | cd tests/ui_tests/playwright npx playwright install chromium webkit --with-deps - name: Setup test directories run: | mkdir -p data/encrypted_databases echo "Created data directories for tests" - name: Start LDR server env: CI: true TEST_ENV: true FLASK_ENV: testing LDR_DATA_DIR: ${{ github.workspace }}/data # The server MUST open test_admin's encrypted DB with the same KDF it # was created with. init_test_database.py creates it at 1000 iterations # (LDR_TEST_MODE relaxes the floor to allow it); without these two vars # here the requested 1000 is below the production floor, so it falls # back to the 256000 default, the server derives the wrong key, and # every login fails with a 401 — see #4558, which set these on the init # step but missed this server step. LDR_DB_CONFIG_KDF_ITERATIONS: "1000" LDR_TEST_MODE: "1" LDR_DISABLE_RATE_LIMITING: true SECRET_KEY: test-secret-key-for-ci run: | export PYTHONPATH="$PWD/src:$PYTHONPATH" echo "Starting server with LDR_DATA_DIR=$LDR_DATA_DIR" pdm run python -m local_deep_research.web.app & SERVER_PID=$! echo "SERVER_PID=$SERVER_PID" >> "$GITHUB_ENV" # Wait for server to be ready. # --connect-timeout/--max-time bound TCP and total request time so a # hung connection fails fast instead of eating the job timeout. for i in {1..30}; do if curl -fsS --connect-timeout 2 --max-time 5 http://127.0.0.1:5000/api/v1/health > /dev/null; then echo "Server is ready" break fi echo "Waiting for server... ($i/30)" sleep 2 done # Verify server is running if ! curl -fsS --connect-timeout 2 --max-time 5 http://127.0.0.1:5000/api/v1/health > /dev/null; then echo "Server failed to start" exit 1 fi - name: Initialize test database env: TEST_ENV: true LDR_DATA_DIR: ${{ github.workspace }}/data LDR_DB_CONFIG_KDF_ITERATIONS: "1000" # Without LDR_TEST_MODE the 1000 is clamped to the production # minimum (256000), making SQLCipher ~256x slower — see #4430. LDR_TEST_MODE: "1" run: | export PYTHONPATH="$PWD/src:$PYTHONPATH" pdm run python scripts/ci/init_test_database.py - name: Register CI test user working-directory: tests/ui_tests run: | npm ci node register_ci_user.js http://127.0.0.1:5000 - name: Run Desktop Safari Tests working-directory: tests/ui_tests/playwright env: CI: true TEST_BASE_URL: http://127.0.0.1:5000 TEST_USERNAME: test_admin TEST_PASSWORD: testpass123 # Safari CI runs a critical subset for fast release gate: # - all-pages-mobile: overflow, touch targets, nav clearance across all pages # - auth-pages-mobile: login, register, change-password mobile layouts # - desktop-layout: sidebar positioning and breakpoint testing # - interactive-states: advanced options, search, help panels, mobile menus # - settings-subpages-mobile: settings tab navigation and form layouts # - mobile-ui-audit: comprehensive mobile UI quality checks # - embedding-settings-dropdown: regression for #3863 (model dropdown reset) # - settings-mobile-collapse: sections collapse by default on mobile (#4032), # stay expanded on desktop, and search force-expands survivors # Full suite: run locally via `cd tests/ui_tests/playwright && npm test` # When adding Safari-critical tests, update this filter pattern. run: | npx playwright test \ --project="Desktop Safari" \ "all-pages-mobile|auth-pages-mobile|desktop-layout|interactive-states|settings-subpages-mobile|mobile-ui-audit|embedding-settings-dropdown|settings-mobile-collapse" - name: Upload Playwright Report if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: playwright-report-desktop-safari path: | tests/ui_tests/playwright/playwright-report/ tests/ui_tests/playwright/test-results/ - name: Stop server if: always() run: | if [ -n "$SERVER_PID" ]; then kill "$SERVER_PID" || true fi pkill -f "python -m local_deep_research.web.app" || true # Mobile Safari tests run in parallel on a separate runner mobile-safari: runs-on: ubuntu-latest permissions: contents: read steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: egress-policy: audit - 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' version: 2.26.2 cache: true - name: Install Python dependencies run: | pdm install -d - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: '24' cache: 'npm' cache-dependency-path: | package-lock.json tests/ui_tests/playwright/package-lock.json - name: Build Vite frontend assets run: | npm ci npm run build - name: Install Playwright dependencies (npm) working-directory: tests/ui_tests/playwright run: npm ci # ``playwright install --with-deps`` downloads ~150 MB of browser # binaries from the playwright CDN per attempt. Single CDN # hiccups have failed the whole release pipeline in the past # (e.g. transient 5xx mid-download); a single retry costs little # and removes the dominant transient-network failure mode for # this workflow. Two attempts only — three attempts of a 150 MB # download starts hiding real outages. - name: Install Playwright browsers uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 with: timeout_minutes: 10 max_attempts: 2 retry_on: error shell: bash # Install chromium for auth setup step + webkit for Safari tests command: | cd tests/ui_tests/playwright npx playwright install chromium webkit --with-deps - name: Setup test directories run: | mkdir -p data/encrypted_databases echo "Created data directories for tests" - name: Start LDR server env: CI: true TEST_ENV: true FLASK_ENV: testing LDR_DATA_DIR: ${{ github.workspace }}/data # The server MUST open test_admin's encrypted DB with the same KDF it # was created with. init_test_database.py creates it at 1000 iterations # (LDR_TEST_MODE relaxes the floor to allow it); without these two vars # here the requested 1000 is below the production floor, so it falls # back to the 256000 default, the server derives the wrong key, and # every login fails with a 401 — see #4558, which set these on the init # step but missed this server step. LDR_DB_CONFIG_KDF_ITERATIONS: "1000" LDR_TEST_MODE: "1" LDR_DISABLE_RATE_LIMITING: true SECRET_KEY: test-secret-key-for-ci run: | export PYTHONPATH="$PWD/src:$PYTHONPATH" echo "Starting server with LDR_DATA_DIR=$LDR_DATA_DIR" pdm run python -m local_deep_research.web.app & SERVER_PID=$! echo "SERVER_PID=$SERVER_PID" >> "$GITHUB_ENV" # Wait for server to be ready. # --connect-timeout/--max-time bound TCP and total request time so a # hung connection fails fast instead of eating the job timeout. for i in {1..30}; do if curl -fsS --connect-timeout 2 --max-time 5 http://127.0.0.1:5000/api/v1/health > /dev/null; then echo "Server is ready" break fi echo "Waiting for server... ($i/30)" sleep 2 done # Verify server is running if ! curl -fsS --connect-timeout 2 --max-time 5 http://127.0.0.1:5000/api/v1/health > /dev/null; then echo "Server failed to start" exit 1 fi - name: Initialize test database env: TEST_ENV: true LDR_DATA_DIR: ${{ github.workspace }}/data LDR_DB_CONFIG_KDF_ITERATIONS: "1000" # Without LDR_TEST_MODE the 1000 is clamped to the production # minimum (256000), making SQLCipher ~256x slower — see #4430. LDR_TEST_MODE: "1" run: | export PYTHONPATH="$PWD/src:$PYTHONPATH" pdm run python scripts/ci/init_test_database.py - name: Register CI test user working-directory: tests/ui_tests run: | npm ci node register_ci_user.js http://127.0.0.1:5000 - name: Run Mobile Safari Tests working-directory: tests/ui_tests/playwright env: CI: true TEST_BASE_URL: http://127.0.0.1:5000 TEST_USERNAME: test_admin TEST_PASSWORD: testpass123 # Safari CI runs a critical subset for fast release gate: # - all-pages-mobile: overflow, touch targets, nav clearance across all pages # - auth-pages-mobile: login, register, change-password mobile layouts # - desktop-layout: sidebar positioning and breakpoint testing # - interactive-states: advanced options, search, help panels, mobile menus # - settings-subpages-mobile: settings tab navigation and form layouts # - mobile-ui-audit: comprehensive mobile UI quality checks # - settings-mobile-collapse: sections collapse by default on mobile (#4032); # this is the viewport where the >16384px overflow regression bites # Note: embedding-settings-dropdown is desktop-only (skips on isMobile), # so it intentionally only appears in the Desktop Safari filter above. # Full suite: run locally via `cd tests/ui_tests/playwright && npm test` # When adding Safari-critical tests, update this filter pattern. run: | npx playwright test \ --project="Mobile Safari" \ "all-pages-mobile|auth-pages-mobile|desktop-layout|interactive-states|settings-subpages-mobile|mobile-ui-audit|settings-mobile-collapse" - name: Upload Playwright Report if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: playwright-report-mobile-safari path: | tests/ui_tests/playwright/playwright-report/ tests/ui_tests/playwright/test-results/ - name: Stop server if: always() run: | if [ -n "$SERVER_PID" ]; then kill "$SERVER_PID" || true fi pkill -f "python -m local_deep_research.web.app" || true