Skip to content

playwright

playwright #9

name: Enterprise E2E (Playwright)
# Enterprise Playwright suite — exercises premium-key gated features (audit,
# teams, analytics) plus full OAuth + SAML logins via the Keycloak compose
# stacks under testing/compose. Slow and secret-gated, so it runs in three
# situations:
#
# - PRs that touch proprietary / premium / SSO compose / enterprise tests
# (path-filtered against .github/config/.files.yaml `proprietary`),
# - every push to main (post-merge safety net),
# - on a nightly cron schedule (catches Keycloak image drift, license
# expiry, upstream proprietary changes),
# - manual workflow_dispatch.
#
# Auto-skipped when secrets.PREMIUM_KEY_ENTERPRISE is missing (forks, dependabot).
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
schedule:
- cron: "0 4 * * *"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref_name || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
files-changed:
name: detect what files changed
runs-on: ubuntu-latest
timeout-minutes: 3
outputs:
proprietary: ${{ steps.changes.outputs.proprietary }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check for file changes
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: .github/config/.files.yaml
playwright-e2e-enterprise:
# Run on PRs only if relevant files changed; always run on push-to-main,
# cron, and manual dispatch. Fork PRs without the secret will fail at
# the compose step (PREMIUM_KEY empty) — that's intentional, not silent.
if: github.event_name != 'pull_request' || needs.files-changed.outputs.proprietary == 'true'
needs: files-changed
runs-on: ubuntu-latest
timeout-minutes: 45
env:
PREMIUM_KEY: ${{ secrets.PREMIUM_KEY_ENTERPRISE }}
PREMIUM_ENABLED: "true"
SYSTEM_ENABLEANALYTICS: "false"
# The compose stacks default KEYCLOAK_HOST to kubernetes.docker.internal,
# which only resolves on Docker Desktop. Override to localhost so the
# Keycloak-issued redirect URLs are reachable from the GHA runner.
KEYCLOAK_HOST: localhost
steps:
- name: Harden Runner
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up JDK 25
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: "25"
distribution: "temurin"
- name: Set up Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: "22"
cache: "npm"
cache-dependency-path: frontend/package-lock.json
- name: Install Task
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
- name: Install Playwright (chromium only)
run: task frontend:test:e2e:install -- chromium
# ───────── OAuth round-trip ─────────
- name: Bring up Keycloak + Stirling-PDF (OAuth)
working-directory: testing/compose
run: docker compose -f docker-compose-keycloak-oauth.yml up -d --build
- name: Wait for OAuth stack ready
working-directory: testing/compose
run: |
for i in $(seq 1 60); do
if bash validate-oauth-test.sh; then
exit 0
fi
sleep 5
done
docker compose -f docker-compose-keycloak-oauth.yml logs --tail=200
exit 1
- name: Run enterprise OAuth Playwright tests
id: oauth-tests
run: task frontend:test:e2e -- --project=enterprise --grep "OAuth"
- name: Tear down OAuth stack
if: always()
working-directory: testing/compose
run: docker compose -f docker-compose-keycloak-oauth.yml down -v
# ───────── SAML round-trip ─────────
- name: Bring up Keycloak + Stirling-PDF (SAML)
working-directory: testing/compose
run: docker compose -f docker-compose-keycloak-saml.yml up -d --build
- name: Wait for SAML stack ready
working-directory: testing/compose
run: |
for i in $(seq 1 60); do
if bash validate-saml-test.sh; then
exit 0
fi
sleep 5
done
docker compose -f docker-compose-keycloak-saml.yml logs --tail=200
exit 1
- name: Run enterprise SAML Playwright tests
id: saml-tests
run: task frontend:test:e2e -- --project=enterprise --grep "SAML"
- name: Tear down SAML stack
if: always()
working-directory: testing/compose
run: docker compose -f docker-compose-keycloak-saml.yml down -v
# ───────── License-gated feature tests (no IdP needed) ─────────
- name: Start backend for feature tests (premium-enabled, no SSO)
env:
SYSTEM_ENABLEANALYTICS: "false"
run: |
nohup ./gradlew :stirling-pdf:bootRun > /tmp/backend-ent.log 2>&1 &
echo $! > /tmp/backend-ent.pid
- name: Wait for backend ready
run: |
start=$SECONDS
for i in $(seq 1 300); do
if curl -fsS http://localhost:8080/api/v1/info/status >/dev/null 2>&1; then
echo "Backend up after $((SECONDS - start))s"
exit 0
fi
sleep 2
done
tail -200 /tmp/backend-ent.log || true
exit 1
- name: Run enterprise feature Playwright tests
id: feature-tests
run: task frontend:test:e2e -- --project=enterprise --grep "Enterprise license"
- name: Print backend log on failure
if: failure()
run: |
echo "::group::Enterprise backend log"
tail -500 /tmp/backend-ent.log || true
echo "::endgroup::"
- name: Stop backend
if: always()
run: |
if [ -f /tmp/backend-ent.pid ]; then
kill "$(cat /tmp/backend-ent.pid)" 2>/dev/null || true
fi
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: playwright-report-enterprise-${{ github.run_id }}
path: frontend/playwright-report/
retention-days: 7