name: 'Test: E2E'

on:
  workflow_call:
    inputs:
      branch:
        description: 'GitHub branch to test.'
        required: false
        type: string
      test-mode:
        description: 'Test mode: local or docker-artifact'
        required: false
        default: 'local'
        type: string
      test-command:
        description: 'Test command to run'
        required: false
        default: 'pnpm --filter=n8n-playwright test:local'
        type: string
      workers:
        description: 'Number of parallel workers'
        required: false
        default: ''
        type: string
      runner:
        description: 'GitHub runner to use'
        required: false
        default: 'blacksmith-2vcpu-ubuntu-2204'
        type: string
      timeout-minutes:
        description: 'Job timeout in minutes'
        required: false
        default: 30
        type: number
      currents-project-id:
        description: 'Currents project ID for reporting'
        required: false
        default: 'LRxcNt'
        type: string
      pre-generated-matrix:
        description: 'Pre-generated shard matrix JSON. Defaults to 1 shard; multi-shard callers pass their own.'
        required: false
        default: '[{"shard":1,"images":""}]'
        type: string
      n8n-env:
        description: 'JSON string of n8n env vars to inject into test containers, e.g. {"N8N_EXPRESSION_ENGINE":"vm"}'
        required: false
        default: ''
        type: string
      artifact-prefix:
        description: 'Prefix for uploaded shard artifacts'
        required: false
        default: 'e2e'
        type: string
      build-variant:
        description: 'Build variant for docker-artifact mode. Must match the variant used by prepare-docker (standard or coverage).'
        required: false
        default: 'standard'
        type: string

env:
  NODE_OPTIONS: ${{ contains(inputs.runner, '2vcpu') && '--max-old-space-size=6144' || '' }}
  PLAYWRIGHT_WORKERS: ${{ inputs.workers != '' && inputs.workers || '2' }}
  # Browser cache location - must match install-browsers script
  PLAYWRIGHT_BROWSERS_PATH: packages/testing/playwright/.playwright-browsers
  # docker-artifact loads the image locally as n8nio/n8n:local; unused in local mode.
  TEST_IMAGE_N8N: 'n8nio/n8n:local'
  N8N_SKIP_LICENSES: 'true'
  CURRENTS_CI_BUILD_ID: ${{ github.repository }}-${{ github.run_id }}-${{ github.run_attempt }}
  CURRENTS_PROJECT_ID: ${{ inputs.currents-project-id }}

jobs:
  test:
    runs-on: ${{ vars.RUNNER_PROVIDER == 'github' && 'ubuntu-latest' || inputs.runner }}
    timeout-minutes: ${{ inputs.timeout-minutes }}
    permissions:
      contents: read
    strategy:
      fail-fast: false
      matrix:
        include: ${{ fromJSON(inputs.pre-generated-matrix) }}
    name: Shard ${{ matrix.shard }}

    steps:
      - name: Checkout
        uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1
        with:
          fetch-depth: 1
          ref: ${{ inputs.branch || github.ref }}

      - name: Setup Environment
        uses: ./.github/actions/setup-nodejs
        env:
          QA_METRICS_WEBHOOK_URL: ${{ secrets.QA_METRICS_WEBHOOK_URL }}
          QA_METRICS_WEBHOOK_USER: ${{ secrets.QA_METRICS_WEBHOOK_USER }}
          QA_METRICS_WEBHOOK_PASSWORD: ${{ secrets.QA_METRICS_WEBHOOK_PASSWORD }}

      - name: Install Browsers
        run: pnpm turbo run install-browsers --filter=n8n-playwright

      - name: Load n8n image from cache
        if: ${{ inputs.test-mode == 'docker-artifact' }}
        uses: ./.github/actions/load-n8n-docker
        with:
          build-variant: ${{ inputs.build-variant }}

      - name: Pre-pull Test Container Images
        if: ${{ !contains(inputs.test-command, 'test:local') }}
        run: npx tsx packages/testing/containers/pull-test-images.ts ${{ matrix.images }} || true

      - name: Filter specs to previous-attempt failures
        if: github.run_attempt > 1 && matrix.specs != ''
        id: filter-shard
        env:
          CANDIDATES: ${{ matrix.specs }}
          GITHUB_RUN_ID: ${{ github.run_id }}
          GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }}
        run: |
          FILTERED=$(echo "$CANDIDATES" | node packages/testing/janitor/dist/cli.js filter-shard | tr '\n' ' ' | sed 's/ *$//')
          echo "specs=$FILTERED" >> "$GITHUB_OUTPUT"

      - name: Run Tests
        run: |
          # shellcheck disable=SC2086
          $TEST_COMMAND --workers="$WORKERS" $SHARD_ARGS
        env:
          # Protect args from template injections
          TEST_COMMAND: ${{ inputs.test-command }}
          # Uses pre-distributed specs if orchestration enabled, otherwise falls back to Playwright sharding
          WORKERS: ${{ env.PLAYWRIGHT_WORKERS }}
          # On retry, prefer the filtered subset (failed specs from previous attempt)
          SHARD_ARGS: ${{ steps.filter-shard.outputs.specs || matrix.specs || format('--shard={0}/{1}', matrix.shard, strategy.job-total) }}
          # Args for actual test command runner
          CURRENTS_RECORD_KEY: ${{ secrets.CURRENTS_RECORD_KEY }}
          QA_METRICS_WEBHOOK_URL: ${{ secrets.QA_METRICS_WEBHOOK_URL }}
          QA_METRICS_WEBHOOK_USER: ${{ secrets.QA_METRICS_WEBHOOK_USER }}
          QA_METRICS_WEBHOOK_PASSWORD: ${{ secrets.QA_METRICS_WEBHOOK_PASSWORD }}
          N8N_LICENSE_ACTIVATION_KEY: ${{ secrets.N8N_LICENSE_ACTIVATION_KEY }}
          N8N_LICENSE_CERT: ${{ secrets.N8N_LICENSE_CERT }}
          N8N_ENCRYPTION_KEY: ${{ secrets.N8N_ENCRYPTION_KEY }}
          N8N_TEST_ENV: ${{ inputs.n8n-env }}

      - name: Upload Shard Artifacts
        if: always()
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: ${{ inputs.artifact-prefix }}-shard-${{ matrix.shard }}
          path: |
            packages/testing/playwright/test-results/
            packages/testing/playwright/playwright-report/
            packages/testing/playwright/.nyc_output/
          retention-days: 1
          if-no-files-found: ignore
          # upload-artifact@v7 defaults this to false, which silently drops
          # `.nyc_output/` (dotfile) — needed for the weekly coverage workflow.
          include-hidden-files: true

      - name: Cancel Currents run if workflow is cancelled
        if: ${{ cancelled() }}
        env:
          CURRENTS_API_KEY: ${{ secrets.CURRENTS_API_KEY }}
        run: |
          if [ -n "$CURRENTS_API_KEY" ]; then
            curl --location --request PUT \
              "https://api.currents.dev/v1/runs/cancel-ci/github" \
              --header "Authorization: Bearer $CURRENTS_API_KEY" \
              --header "Content-Type: application/json" \
              --data "{\"githubRunId\": \"${{ github.run_id }}\", \"githubRunAttempt\": \"${{ github.run_attempt }}\"}"
          fi
