-
Notifications
You must be signed in to change notification settings - Fork 6.8k
playwright #6025
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
playwright #6025
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
0d3d743
playwright
Frooodle f264c44
fixes and adjustments
Frooodle fe941ad
codeql
Frooodle ca56e38
move tests
Frooodle 0ea40ab
missed some
Frooodle 8a6591b
Merge branch 'main' into playwright
Frooodle 3bfeb4a
Merge branch 'main' into playwright
Frooodle b8e2ca7
Align Convert E2E tests with main's mock-based approach
Frooodle d0b936a
Heal 3 Playwright test failures after merge with main
Frooodle dbd3928
Dismiss tour tooltip in ConvertE2E before clicking convert
Frooodle a5697ac
Use sidebar Tools link instead of breadcrumb for home navigation
Frooodle 0a6854c
Apply prettier formatting to test files and playwright config
Frooodle 0990c11
move tests to stub
Frooodle 42b34f0
fix setup-java sha in playwright-e2e-live job
Frooodle c0ebb5d
fixes and speed
Frooodle 3dbf869
suppress analytics modal in CI live suite
Frooodle 5997b00
use java 25 for live e2e job
Frooodle 5626aee
enterprise and fixes
Frooodle 36830c1
split enterprise into separate workflow + path-gated triggers
Frooodle 7eb0484
test cleanup: rename, dedupe, merge, share helpers
Frooodle 85657d6
cleanup
Frooodle c9deff9
fix bootstrap: use placeholder for password inputs
Frooodle 3d852fd
Merge branch 'main' into playwright
Frooodle 73261ef
remove flaky tests + fix stubbed enterprise UI assertions
Frooodle ff8c660
fix enterprise workflow: drop secrets ref from job if
Frooodle 207df5e
fix validate scripts: keycloak 24 moved /health endpoint
Frooodle e1498f4
set KEYCLOAK_HOST=localhost for CI runner
Frooodle b2d2b47
swap docker stirling-pdf for bootRun -PbuildWithFrontend=true
Frooodle 7b0993b
compose extra_hosts mapping + /etc/hosts override for keycloak host
Frooodle 8b951f5
gitignore generated pdfjs assets
Frooodle 1efd69f
drop tool-run from SSO specs (post-OAuth navigation flake; covered by…
Frooodle 015281c
generate SAML certs in CI (gitignored locally)
Frooodle 6945c14
fix SAML SP entity id to match realm clientId
Frooodle feda2b7
fix SAML host: realm KC_HOSTNAME=localhost not kubernetes.docker.inte…
Frooodle 9d55a95
provision admin/adminadmin for enterprise feature tests
Frooodle 55c47c4
wipe DB between enterprise phases so admin/adminadmin gets created
Frooodle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,284 @@ | ||
| 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" | ||
| 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 | ||
|
|
||
| - name: Resolve kubernetes.docker.internal to localhost | ||
| # The compose stacks set KC_HOSTNAME=kubernetes.docker.internal so | ||
| # Keycloak issues redirect URIs against that host. Docker Desktop | ||
| # auto-resolves it; GHA runners don't. Map it to 127.0.0.1 so the | ||
| # browser-driven OAuth flow lands back on Stirling-PDF correctly. | ||
| run: | | ||
| echo "127.0.0.1 kubernetes.docker.internal" | sudo tee -a /etc/hosts | ||
|
|
||
| # Helper function used by all phases — boots `:stirling-pdf:bootRun` | ||
| # with the React frontend baked in (-PbuildWithFrontend=true) so the | ||
| # SPA serves on :8080 and OAuth/SAML callbacks land on the same host | ||
| # that the browser is interacting with. | ||
| - name: Define helpers | ||
| run: | | ||
| { | ||
| echo 'wait_for_backend() {' | ||
| echo ' start=$SECONDS' | ||
| echo ' for i in $(seq 1 300); do' | ||
| echo ' if curl -fsS http://localhost:8080/api/v1/info/status >/dev/null 2>&1; then' | ||
| echo ' echo "Backend up after $((SECONDS - start))s"; return 0' | ||
| echo ' fi; sleep 2' | ||
| echo ' done' | ||
| echo ' tail -200 /tmp/backend.log || true; return 1' | ||
| echo '}' | ||
| echo 'stop_backend() {' | ||
| echo ' if [ -f /tmp/backend.pid ]; then' | ||
| echo ' kill "$(cat /tmp/backend.pid)" 2>/dev/null || true' | ||
| echo ' rm -f /tmp/backend.pid' | ||
| echo ' fi' | ||
| echo ' pkill -f "gradlew :stirling-pdf:bootRun" 2>/dev/null || true' | ||
| echo ' for i in $(seq 1 30); do' | ||
| echo ' curl -fsS http://localhost:8080/api/v1/info/status >/dev/null 2>&1 || return 0' | ||
| echo ' sleep 1' | ||
| echo ' done' | ||
| echo '}' | ||
| } > /tmp/helpers.sh | ||
| chmod +x /tmp/helpers.sh | ||
|
|
||
| # ───────── OAuth round-trip ───────── | ||
| - name: Bring up Keycloak (OAuth realm) | ||
| working-directory: testing/compose | ||
| run: docker compose -f docker-compose-keycloak-oauth.yml up -d --no-deps keycloak-oauth-db keycloak-oauth | ||
| - name: Wait for Keycloak (OAuth) ready | ||
| working-directory: testing/compose | ||
| run: | | ||
| for i in $(seq 1 60); do | ||
| bash validate-oauth-test.sh 2>/dev/null && exit 0 || true | ||
| # validate script also pings stirling on :8080 — accept just the | ||
| # keycloak realm as our gate here, stirling boots in the next step | ||
| curl -fsS http://localhost:9080/realms/stirling-oauth >/dev/null 2>&1 && exit 0 | ||
| sleep 5 | ||
| done | ||
| docker compose -f docker-compose-keycloak-oauth.yml logs --tail=200 keycloak-oauth | ||
| exit 1 | ||
| - name: Boot Stirling-PDF (frontend baked in, OAuth env) | ||
| env: | ||
| SECURITY_ENABLELOGIN: "true" | ||
| SECURITY_LOGINMETHOD: "all" | ||
| SECURITY_OAUTH2_ENABLED: "true" | ||
| SECURITY_OAUTH2_AUTOCREATEUSER: "true" | ||
| # Keycloak issues redirect URIs against KC_HOSTNAME, which the | ||
| # compose default sets to kubernetes.docker.internal. Match here | ||
| # (resolves to localhost via /etc/hosts mapping above). | ||
| SECURITY_OAUTH2_CLIENT_KEYCLOAK_ISSUER: "http://kubernetes.docker.internal:9080/realms/stirling-oauth" | ||
| SECURITY_OAUTH2_CLIENT_KEYCLOAK_CLIENTID: "stirling-pdf-client" | ||
| SECURITY_OAUTH2_CLIENT_KEYCLOAK_CLIENTSECRET: "test-client-secret-change-in-production" | ||
| SECURITY_OAUTH2_CLIENT_KEYCLOAK_USEASUSERNAME: "email" | ||
| SECURITY_OAUTH2_CLIENT_KEYCLOAK_SCOPES: "openid,profile,email" | ||
| run: | | ||
| source /tmp/helpers.sh | ||
| nohup ./gradlew :stirling-pdf:bootRun -PbuildWithFrontend=true > /tmp/backend.log 2>&1 & | ||
| echo $! > /tmp/backend.pid | ||
| wait_for_backend | ||
| - name: Run enterprise OAuth Playwright tests | ||
| id: oauth-tests | ||
| run: task frontend:test:e2e -- --project=enterprise --grep "OAuth" | ||
| - name: Stop backend + tear down OAuth Keycloak | ||
| if: always() | ||
| run: | | ||
| source /tmp/helpers.sh | ||
| stop_backend | ||
| (cd testing/compose && docker compose -f docker-compose-keycloak-oauth.yml down -v) | ||
|
|
||
| # ───────── SAML round-trip ───────── | ||
| - name: Bring up Keycloak (SAML realm) | ||
| working-directory: testing/compose | ||
| run: docker compose -f docker-compose-keycloak-saml.yml up -d --no-deps keycloak-saml-db keycloak-saml | ||
| - name: Wait for Keycloak (SAML) ready | ||
| working-directory: testing/compose | ||
| run: | | ||
| for i in $(seq 1 60); do | ||
| curl -fsS http://localhost:9080/realms/stirling-saml >/dev/null 2>&1 && exit 0 | ||
| sleep 5 | ||
| done | ||
| docker compose -f docker-compose-keycloak-saml.yml logs --tail=200 keycloak-saml | ||
| exit 1 | ||
| - name: Generate SAML SP certs + fetch Keycloak IdP cert | ||
| # The .pem/.crt/.key files are gitignored (test-only certs); the | ||
| # docker-based start-saml-test.sh generates them at runtime, so do | ||
| # the same in CI before bootRun reads them. | ||
| working-directory: testing/compose | ||
| run: | | ||
| openssl req -x509 -newkey rsa:2048 \ | ||
| -keyout saml-private-key.key \ | ||
| -out saml-public-cert.crt \ | ||
| -days 3650 -nodes \ | ||
| -subj "/CN=stirling-pdf-saml-sp" >/dev/null 2>&1 | ||
| # Fetch Keycloak's SAML signing cert from the realm descriptor | ||
| CERT_BODY=$(curl -sf http://localhost:9080/realms/stirling-saml/protocol/saml/descriptor \ | ||
| | awk 'BEGIN{RS="<[^>]*X509Certificate>|</[^>]*X509Certificate>"} NR==2{gsub(/[[:space:]]+/,""); print; exit}') | ||
| { | ||
| echo "-----BEGIN CERTIFICATE-----" | ||
| echo "$CERT_BODY" | ||
| echo "-----END CERTIFICATE-----" | ||
| } > keycloak-saml-cert.pem | ||
| test -s saml-private-key.key | ||
| test -s saml-public-cert.crt | ||
| test -s keycloak-saml-cert.pem | ||
| echo "✓ SAML certs prepared" | ||
| - name: Boot Stirling-PDF (frontend baked in, SAML env) | ||
| env: | ||
| SECURITY_ENABLELOGIN: "true" | ||
| SECURITY_LOGINMETHOD: "all" | ||
| SECURITY_SAML2_ENABLED: "true" | ||
| SECURITY_SAML2_AUTOCREATEUSER: "true" | ||
| SECURITY_SAML2_PROVIDER: "keycloak" | ||
| SECURITY_SAML2_REGISTRATIONID: "keycloak" | ||
| SECURITY_SAML2_IDP_ISSUER: "http://localhost:9080/realms/stirling-saml" | ||
| SECURITY_SAML2_IDP_ENTITYID: "http://localhost:9080/realms/stirling-saml" | ||
| SECURITY_SAML2_IDP_METADATAURI: "http://localhost:9080/realms/stirling-saml/protocol/saml/descriptor" | ||
| SECURITY_SAML2_IDPSINGLELOGINURL: "http://localhost:9080/realms/stirling-saml/protocol/saml" | ||
| SECURITY_SAML2_IDPSINGLELOGOUTURL: "http://localhost:9080/realms/stirling-saml/protocol/saml" | ||
| SECURITY_SAML2_IDP_CERT: "${{ github.workspace }}/testing/compose/keycloak-saml-cert.pem" | ||
| SECURITY_SAML2_PRIVATEKEY: "${{ github.workspace }}/testing/compose/saml-private-key.key" | ||
| SECURITY_SAML2_SP_CERT: "${{ github.workspace }}/testing/compose/saml-public-cert.crt" | ||
| # Realm registers the SP entity as the metadata URL — see | ||
| # keycloak-realm-saml.json `clientId`. Match it here so Keycloak | ||
| # accepts the AuthnRequest issuer. | ||
| SECURITY_SAML2_SP_ENTITYID: "http://localhost:8080/saml2/service-provider-metadata/keycloak" | ||
| SECURITY_SAML2_SP_ACS: "http://localhost:8080/login/saml2/sso/keycloak" | ||
| SECURITY_SAML2_SP_SLS: "http://localhost:8080/logout/saml2/slo" | ||
| run: | | ||
| source /tmp/helpers.sh | ||
| nohup ./gradlew :stirling-pdf:bootRun -PbuildWithFrontend=true > /tmp/backend.log 2>&1 & | ||
| echo $! > /tmp/backend.pid | ||
| wait_for_backend | ||
| - name: Run enterprise SAML Playwright tests | ||
| id: saml-tests | ||
| run: task frontend:test:e2e -- --project=enterprise --grep "SAML" | ||
| - name: Stop backend + tear down SAML Keycloak | ||
| if: always() | ||
| run: | | ||
| source /tmp/helpers.sh | ||
| stop_backend | ||
| (cd testing/compose && docker compose -f docker-compose-keycloak-saml.yml down -v) | ||
|
|
||
| # ───────── License-gated feature tests (no IdP needed) ───────── | ||
| - name: Wipe DB so InitialSecuritySetup re-runs with admin/adminadmin | ||
| # Earlier phases (OAuth, SAML) create the default admin/stirling user. | ||
| # InitialSecuritySetup only honours SECURITY_INITIALLOGIN_* when the | ||
| # admin user doesn't already exist, so the persisted DB has to be | ||
| # cleared between phases for the feature env vars to take effect. | ||
| run: | | ||
| rm -f app/core/configs/stirling-pdf-DB*.mv.db | ||
| rm -rf app/core/configs/backup | ||
| - name: Boot Stirling-PDF (frontend baked in, premium only) | ||
| env: | ||
| SECURITY_INITIALLOGIN_USERNAME: admin | ||
| SECURITY_INITIALLOGIN_PASSWORD: adminadmin | ||
| SECURITY_ENABLELOGIN: "true" | ||
| SECURITY_LOGINMETHOD: "all" | ||
| run: | | ||
| source /tmp/helpers.sh | ||
| nohup ./gradlew :stirling-pdf:bootRun -PbuildWithFrontend=true > /tmp/backend.log 2>&1 & | ||
| echo $! > /tmp/backend.pid | ||
| wait_for_backend | ||
| - 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.log || true | ||
| echo "::endgroup::" | ||
| - name: Stop backend (final) | ||
| if: always() | ||
| run: | | ||
| source /tmp/helpers.sh | ||
| stop_backend | ||
| - 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 | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.