# Produce a build of element-web with this version of react-sdk
# and any matching branches of element-web and js-sdk, output it
# as an artifact and run end-to-end tests.
name: End to End Tests
on:
    # CRON to run all Projects at 6am UTC
    schedule:
        - cron: "0 6 * * *"
    pull_request: {}
    merge_group:
        types: [checks_requested]
    push:
        branches: [develop, master]
    repository_dispatch:
        types: [element-web-notify]

    # support triggering from other workflows
    workflow_call:
        inputs:
            skip:
                type: boolean
                required: false
                default: false
                description: "A boolean to skip the playwright check itself while still creating the passing check. Useful when only running in Merge Queues."

            matrix-js-sdk-sha:
                type: string
                required: false
                description: "The Git SHA of matrix-js-sdk to build against. By default, will use a matching branch name if it exists, or develop."

concurrency:
    group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
    cancel-in-progress: true

env:
    # fetchdep.sh needs to know our PR number
    PR_NUMBER: ${{ github.event.pull_request.number }}
    # Use 6 runners in the default case, but 4 when running on a schedule where we run all 5 projects (20 runners total)
    NUM_RUNNERS: ${{ github.event_name == 'schedule' && 4 || 6 }}
    NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes

permissions: {} # No permissions required

jobs:
    build:
        name: "Build Element-Web"
        runs-on: ubuntu-24.04
        if: inputs.skip != true
        outputs:
            num-runners: ${{ env.NUM_RUNNERS }}
            runners-matrix: ${{ steps.runner-vars.outputs.matrix }}
        steps:
            - name: Checkout code
              uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
              with:
                  repository: element-hq/element-web
                  persist-credentials: false

            - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
            - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
              with:
                  cache: "pnpm"
                  node-version: "lts/*"

            - name: Fetch layered build
              env:
                  # tell layered.sh to check out the right sha of the JS-SDK & EW, if they were given one
                  JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
              run: scripts/layered.sh

            - name: Copy config
              working-directory: apps/web
              run: cp element.io/develop/config.json config.json

            - name: Build
              env:
                  CI_PACKAGE: true
              working-directory: apps/web
              run: VERSION=$(scripts/get-version-from-git.sh) pnpm build

            - name: Upload Artifact
              uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
              with:
                  name: webapp
                  path: apps/web/webapp
                  retention-days: 1

            - name: Calculate runner variables
              id: runner-vars
              uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
              with:
                  script: |
                      const numRunners = parseInt(process.env.NUM_RUNNERS, 10);
                      const matrix = Array.from({ length: numRunners }, (_, i) => i + 1);
                      core.setOutput("matrix", JSON.stringify(matrix));

    playwright:
        name: "Run Tests [${{ matrix.project }}] ${{ matrix.runner }}/${{ needs.build.outputs.num-runners }}"
        needs: build
        if: inputs.skip != true
        runs-on: ubuntu-24.04
        permissions:
            actions: read
            issues: read
            pull-requests: read
        strategy:
            fail-fast: false
            matrix:
                # Run multiple instances in parallel to speed up the tests
                runner: ${{ fromJSON(needs.build.outputs.runners-matrix) }}
                project:
                    - Chrome
                    - Firefox
                    - WebKit
                    - Dendrite
                    - Pinecone
                runAllTests:
                    - ${{ github.event_name == 'schedule' || contains(github.event.pull_request.labels.*.name, 'X-Run-All-Tests') }}
                # Skip the Firefox & Safari runs unless this was a cron trigger or PR has X-Run-All-Tests label
                exclude:
                    - runAllTests: false
                      project: Firefox
                    - runAllTests: false
                      project: WebKit
                    - runAllTests: false
                      project: Dendrite
                    - runAllTests: false
                      project: Pinecone
        steps:
            - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
              with:
                  persist-credentials: false
                  repository: element-hq/element-web

            - name: 📥 Download artifact
              uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8
              with:
                  name: webapp
                  path: apps/web/webapp

            - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
            - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
              with:
                  cache: "pnpm"
                  cache-dependency-path: pnpm-lock.yaml
                  node-version: "lts/*"

            - name: Install dependencies
              run: pnpm install --frozen-lockfile

            - name: Get installed Playwright version
              id: playwright
              run: echo "version=$(pnpm list @playwright/test --depth=0 --json | jq -r '.[].devDependencies["@playwright/test"].version')" >> $GITHUB_OUTPUT

            - name: Cache playwright binaries
              uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
              id: playwright-cache
              with:
                  path: ~/.cache/ms-playwright
                  key: ${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright.outputs.version }}

            - name: Install Playwright browsers
              if: steps.playwright-cache.outputs.cache-hit != 'true'
              working-directory: apps/web
              run: pnpm playwright install --with-deps --no-shell

            - name: Install system dependencies for WebKit
              # Some WebKit dependencies seem to lay outside the cache and will need to be installed separately
              if: matrix.project == 'WebKit' && steps.playwright-cache.outputs.cache-hit == 'true'
              working-directory: apps/web
              run: pnpm playwright install-deps webkit

            # We skip tests tagged with @mergequeue when running on PRs, but run them in MQ and everywhere else
            - name: Run Playwright tests
              working-directory: apps/web
              run: |
                  pnpm playwright test \
                      --shard "$SHARD" \
                      --project="${{ matrix.project }}" \
                      ${{ (github.event_name == 'pull_request' && matrix.runAllTests == false ) && '--grep-invert @mergequeue' || '' }}
              env:
                  SHARD: ${{ format('{0}/{1}', matrix.runner, needs.build.outputs.num-runners) }}

            - name: Upload blob report to GitHub Actions Artifacts
              if: always()
              uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
              with:
                  name: all-blob-reports-${{ matrix.project }}-${{ matrix.runner }}
                  path: apps/web/blob-report
                  retention-days: 1

    downstream-modules:
        name: Downstream Playwright tests [element-modules]
        needs: build
        if: inputs.skip != true && github.event_name == 'merge_group'
        uses: element-hq/element-modules/.github/workflows/reusable-playwright-tests.yml@main # zizmor: ignore[unpinned-uses]
        with:
            webapp-artifact: webapp

    complete:
        name: end-to-end-tests
        needs:
            - playwright
            - downstream-modules
        if: always()
        runs-on: ubuntu-24.04
        steps:
            - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
              if: inputs.skip != true
              with:
                  persist-credentials: false
                  repository: element-hq/element-web

            - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
              if: inputs.skip != true
            - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
              if: inputs.skip != true
              with:
                  cache: "pnpm"
                  node-version: "lts/*"

            - name: Install dependencies
              if: inputs.skip != true
              run: pnpm install --frozen-lockfile

            - name: Download blob reports from GitHub Actions Artifacts
              if: inputs.skip != true
              uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8
              with:
                  pattern: all-blob-reports-*
                  path: apps/web/all-blob-reports
                  merge-multiple: true

            - name: Merge into HTML Report
              if: inputs.skip != true
              working-directory: apps/web
              run: pnpm playwright merge-reports --reporter=html,./playwright/flaky-reporter.ts,@element-hq/element-web-playwright-common/lib/stale-screenshot-reporter.js ./all-blob-reports
              env:
                  # Only pass creds to the flaky-reporter on main branch runs
                  GITHUB_TOKEN: ${{ github.ref_name == 'develop' && secrets.ELEMENT_BOT_TOKEN || '' }}

            # Upload the HTML report even if one of our reporters fails, this can happen when stale screenshots are detected
            - name: Upload HTML report
              if: always() && inputs.skip != true
              uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
              with:
                  name: html-report
                  path: apps/web/playwright-report
                  retention-days: 14
                  if-no-files-found: error

            - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
              run: exit 1
