GitHub Actions Testing - Integração CI/CD

Workflows completos de teste do GitHub Actions incluindo testes automatizados, execução paralela, relatórios de teste, caching e padrões CI/CD avançados para pipelines de desenvolvimento modernos

💻 Configuração Básica de Testes com GitHub Actions yaml

🟢 simple ⭐⭐

Configuração completa de testes com GitHub Actions com workflows básicos, execução de testes, relatórios e integração CI/CD

⏱️ 40 min 🏷️ github actions, ci cd, setup, workflows
Prerequisites: Git basics, CI/CD concepts, YAML, JavaScript/TypeScript
# GitHub Actions Testing - Basic Setup and Configuration

# 1. .github/workflows/test.yml - Basic Testing Workflow
name: Tests

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  NODE_VERSION: '18'
  NODE_OPTIONS: '--max-old-space-size=4096'

jobs:
  # Unit Tests
  unit-tests:
    name: Unit Tests
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [16, 18, 20]

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run unit tests
        run: npm run test:unit

      - name: Upload coverage reports
        if: matrix.node-version == '18'
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage/lcov.info
          flags: unittests
          name: codecov-umbrella

  # Integration Tests
  integration-tests:
    name: Integration Tests
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: test_db
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

      redis:
        image: redis:7
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 6379:6379

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Setup test environment
        run: |
          cp .env.test .env
          npm run db:migrate

      - name: Run integration tests
        run: npm run test:integration
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
          REDIS_URL: redis://localhost:6379

  # End-to-End Tests
  e2e-tests:
    name: E2E Tests
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps

      - name: Build application
        run: npm run build

      - name: Run E2E tests
        run: npm run test:e2e

      - name: Upload test results
        uses: actions/upload-artifact@v3
        if: always()
        with:
          name: playwright-report
          path: playwright-report/

# 2. .github/workflows/quality.yml - Code Quality Checks
name: Code Quality

on:
  pull_request:
    branches: [ main ]

jobs:
  lint:
    name: Lint Code
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run ESLint
        run: npm run lint

      - name: Run Prettier check
        run: npm run format:check

      - name: Type checking
        run: npm run type-check

  security:
    name: Security Audit
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Run security audit
        run: npm audit --audit-level=moderate

      - name: Run Snyk security scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

# 3. .github/workflows/performance.yml - Performance Testing
name: Performance Tests

on:
  schedule:
    - cron: '0 2 * * *'  # Run daily at 2 AM UTC
  workflow_dispatch:

jobs:
  lighthouse:
    name: Lighthouse Performance Test
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build application
        run: npm run build

      - name: Run Lighthouse CI
        run: |
          npm install -g @lhci/[email protected]
          lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

  load-testing:
    name: Load Testing
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Install k6
        run: |
          sudo gpg -k
          sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
          echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
          sudo apt-get update
          sudo apt-get install k6

      - name: Run load tests
        run: k6 run --out json=results.json tests/performance/load-test.js

      - name: Upload results
        uses: actions/upload-artifact@v3
        with:
          name: load-test-results
          path: results.json

# 4. package.json scripts configuration
{
  "name": "example-project",
  "version": "1.0.0",
  "scripts": {
    "test": "npm run test:unit && npm run test:integration && npm run test:e2e",
    "test:unit": "jest",
    "test:unit:watch": "jest --watch",
    "test:unit:coverage": "jest --coverage",
    "test:integration": "jest --config=jest.integration.config.js",
    "test:e2e": "playwright test",
    "test:e2e:headed": "playwright test --headed",
    "test:e2e:debug": "playwright test --debug",
    "lint": "eslint src/**/*.js tests/**/*.js",
    "lint:fix": "eslint src/**/*.js tests/**/*.js --fix",
    "format": "prettier --write src/**/* tests/**/*",
    "format:check": "prettier --check src/**/* tests/**/*",
    "type-check": "tsc --noEmit",
    "build": "tsc && npm run build:assets",
    "build:assets": "cp -r src/public dist/",
    "dev": "npm run build && node dist/index.js",
    "db:migrate": "sequelize-cli db:migrate",
    "db:seed": "sequelize-cli db:seed:all"
  },
  "devDependencies": {
    "@playwright/test": "^1.40.0",
    "@lhci/cli": "^0.12.0",
    "eslint": "^8.55.0",
    "prettier": "^3.1.0",
    "jest": "^29.7.0",
    "jest-environment-node": "^29.7.0",
    "snyk": "^1.1291.0"
  }
}

# 5. .github/workflows/notify.yml - Test Result Notifications
name: Test Notifications

on:
  workflow_run:
    workflows: ["Tests"]
    types:
      - completed

jobs:
  notify:
    name: Send Notifications
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'failure' }}

    steps:
      - name: Send Slack notification
        uses: 8398a7/action-slack@v3
        with:
          status: failure
          channel: '#dev-alerts'
          webhook_url: ${{ secrets.SLACK_WEBHOOK }}
          text: |
            Tests failed for ${{ github.repository }}
            Branch: ${{ github.ref }}
            Commit: ${{ github.sha }}
            View details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}

      - name: Send email notification
        uses: dawidd6/action-send-mail@v3
        with:
          server_address: smtp.gmail.com
          server_port: 587
          username: ${{ secrets.EMAIL_USERNAME }}
          password: ${{ secrets.EMAIL_PASSWORD }}
          subject: "Test Failed - ${{ github.repository }}"
          body: |
            Tests failed for ${{ github.repository }}

            Branch: ${{ github.ref }}
            Commit: ${{ github.sha }}
            Author: ${{ github.event.head_commit.author.name }}

            View details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
          to: ${{ secrets.NOTIFICATION_EMAIL }}
          from: GitHub Actions

# 6. .github/workflows/release.yml - Release Testing
name: Release

on:
  release:
    types: [published]

jobs:
  release-tests:
    name: Pre-release Testing
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run full test suite
        run: npm run test

      - name: Build release
        run: npm run build

      - name: Run smoke tests
        run: npm run test:smoke

      - name: Upload release artifacts
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ github.event.release.upload_url }}
          asset_path: ./dist/app.zip
          asset_name: app-v${{ github.event.release.tag_name }}.zip
          asset_content_type: application/zip

# 7. jest.config.js - Jest Configuration for CI
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['<rootDir>/src', '<rootDir>/tests'],
  testMatch: [
    '**/__tests__/**/*.+(ts|tsx|js)',
    '**/*.(test|spec).+(ts|tsx|js)'
  ],
  transform: {
    '^.+\.(ts|tsx)$': 'ts-jest',
  },
  collectCoverageFrom: [
    'src/**/*.{ts,tsx}',
    '!src/**/*.d.ts',
    '!src/index.ts',
  ],
  coverageDirectory: 'coverage',
  coverageReporters: [
    'text',
    'lcov',
    'html',
    'json'
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    }
  },
  setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
  testTimeout: 30000,
};

💻 Padrões Avançados de Testes com GitHub Actions yaml

🔴 complex ⭐⭐⭐⭐

Padrões CI/CD complexos incluindo builds matriciais, estratégias de cache, paralelização de testes, execução condicional e otimização de workflows em nível empresarial

⏱️ 90 min 🏷️ github actions, advanced, matrix, caching, optimization
Prerequisites: GitHub Actions basics, CI/CD advanced, YAML, DevOps concepts
# GitHub Actions Advanced Testing Patterns

# 1. .github/workflows/matrix-testing.yml - Advanced Matrix Strategy
name: Matrix Testing

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  # Matrix strategy for multiple environments
  test-matrix:
    name: Test (${{ matrix.os }}, Node ${{ matrix.node-version }})
    runs-on: ${{ matrix.os }}

    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [16, 18, 20]
        include:
          - os: ubuntu-latest
            node-version: 20
            coverage: true
          - os: windows-latest
            node-version: 18
            edge-cases: true
          - os: macos-latest
            node-version: 16
            browser-tests: true
        exclude:
          - os: windows-latest
            node-version: 16

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Cache node modules
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-${{ matrix.node-version }}-

      - name: Install browsers
        if: matrix.browser-tests
        run: npx playwright install chromium firefox webkit

      - name: Run unit tests
        run: npm run test:unit

      - name: Run tests with coverage
        if: matrix.coverage
        run: npm run test:coverage

      - name: Run edge case tests
        if: matrix.edge-cases
        run: npm run test:edge-cases

      - name: Run browser tests
        if: matrix.browser-tests
        run: npm run test:browser

      - name: Upload coverage to Codecov
        if: matrix.coverage && matrix.os == 'ubuntu-latest'
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage/lcov.info
          flags: ${{ matrix.os }}-node${{ matrix.node-version }}
          name: codecov-${{ matrix.os }}-${{ matrix.node-version }}

  # Parallel execution with test splitting
  parallel-tests:
    name: Parallel Tests (Shard ${{ matrix.shard }}/${{ strategy.job-total }})
    runs-on: ubuntu-latest

    strategy:
      matrix:
        shard: [1, 2, 3, 4]

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Split tests
        id: split-tests
        run: |
          # Get total number of tests and split them
          TEST_FILES=$(npm run test:list 2>/dev/null | grep -E "\.test\.|\.spec\." | wc -l)
          echo "total-tests=$TEST_FILES" >> $GITHUB_OUTPUT

          # Calculate which tests to run for this shard
          SHARD_SIZE=$((TEST_FILES / 4 + 1))
          START=$(((matrix.shard - 1) * SHARD_SIZE + 1))
          END=$((matrix.shard * SHARD_SIZE))

          echo "start=$START" >> $GITHUB_OUTPUT
          echo "end=$END" >> $GITHUB_OUTPUT

      - name: Run shard tests
        run: |
          npm run test:shard -- --shard=${{ matrix.shard }}/4

      - name: Upload test results
        uses: actions/upload-artifact@v3
        with:
          name: test-results-shard-${{ matrix.shard }}
          path: test-results/

# 2. .github/workflows/conditional-testing.yml - Conditional Execution
name: Conditional Testing

on:
  push:
    branches: [ main ]
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  detect-changes:
    name: Detect Changes
    runs-on: ubuntu-latest
    outputs:
      backend-changed: ${{ steps.changes.outputs.backend }}
      frontend-changed: ${{ steps.changes.outputs.frontend }}
      docs-changed: ${{ steps.changes.outputs.docs }}
      all-changed: ${{ steps.changes.outputs.all }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: Detect file changes
        id: changes
        uses: dorny/paths-filter@v2
        with:
          filters: |
            backend:
              - 'src/backend/**'
              - 'package.json'
            frontend:
              - 'src/frontend/**'
              - 'public/**'
            docs:
              - 'docs/**'
              - '**/*.md'
            all:
              - '**'

  # Conditional job execution
  backend-tests:
    name: Backend Tests
    needs: detect-changes
    if: needs.detect-changes.outputs.backend-changed == 'true'
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run backend tests
        run: |
          npm run test:backend
          npm run test:api

      - name: Run integration tests
        run: npm run test:integration

  frontend-tests:
    name: Frontend Tests
    needs: detect-changes
    if: needs.detect-changes.outputs.frontend-changed == 'true'
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps

      - name: Build application
        run: npm run build:frontend

      - name: Run frontend tests
        run: npm run test:frontend

      - name: Run E2E tests
        run: npm run test:e2e

  full-suite:
    name: Full Test Suite
    needs: detect-changes
    if: |
      needs.detect-changes.outputs.all-changed == 'true' ||
      github.event_name == 'schedule' ||
      contains(github.event.head_commit.message, '[full-test]')
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run complete test suite
        run: npm run test:all

# 3. .github/workflows/caching-optimization.yml - Advanced Caching
name: Optimized Testing with Caching

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test-with-caching:
    name: Tests with Advanced Caching
    runs-on: ubuntu-latest

    env:
      CACHE_VERSION: v2

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'

      - name: Cache node modules
        id: cache-node
        uses: actions/cache@v3
        with:
          path: |
            ~/.npm
            node_modules
          key: ${{ env.CACHE_VERSION }}-npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ env.CACHE_VERSION }}-npm-${{ runner.os }}-

      - name: Install dependencies
        if: steps.cache-node.outputs.cache-hit != 'true'
        run: npm ci

      - name: Cache build artifacts
        id: cache-build
        uses: actions/cache@v3
        with:
          path: |
            dist/
            .next/
            build/
          key: ${{ env.CACHE_VERSION }}-build-${{ github.sha }}
          restore-keys: |
            ${{ env.CACHE_VERSION }}-build-

      - name: Build application
        if: steps.cache-build.outputs.cache-hit != 'true'
        run: npm run build

      - name: Cache Playwright browsers
        uses: actions/cache@v3
        with:
          path: ~/.cache/ms-playwright
          key: ${{ env.CACHE_VERSION }}-playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

      - name: Install Playwright browsers
        run: npx playwright install --with-deps

      - name: Cache test database
        uses: actions/cache@v3
        with:
          path: |
            test-data/
            fixtures/
          key: ${{ env.CACHE_VERSION }}-test-data-${{ hashFiles('test-data/**') }}

      - name: Setup test database
        run: |
          npm run db:setup
          npm run db:seed

      - name: Run tests
        run: npm run test

      - name: Cache test results
        uses: actions/cache@v3
        with:
          path: |
            coverage/
            test-results/
            playwright-report/
          key: ${{ env.CACHE_VERSION }}-test-results-${{ github.sha }}

# 4. .github/workflows/reusable-workflows.yml - Reusable Workflows
# This file defines reusable workflows that can be called from other workflows

# .github/workflows/test-reusable.yml
name: Reusable Test Workflow

on:
  workflow_call:
    inputs:
      node-version:
        required: false
        type: string
        default: '18'
      test-type:
        required: false
        type: string
        default: 'all'
      cache-key:
        required: false
        type: string
        default: 'default'
    secrets:
      NPM_TOKEN:
        required: false
      CODECOV_TOKEN:
        required: false

jobs:
  run-tests:
    name: Tests (${{ inputs.test-type }})
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Configure private registry
        if: secrets.NPM_TOKEN
        run: |
          echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" >> ~/.npmrc
          echo "@example:registry=https://npm.example.com" >> ~/.npmrc

      - name: Run tests based on type
        run: |
          case "${{ inputs.test-type }}" in
            "unit")
              npm run test:unit
              ;;
            "integration")
              npm run test:integration
              ;;
            "e2e")
              npm run test:e2e
              ;;
            "all")
              npm run test
              ;;
          esac

      - name: Upload coverage
        if: inputs.test-type != 'e2e' && secrets.CODECOV_TOKEN
        uses: codecov/codecov-action@v3
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          flags: ${{ inputs.test-type }}

# .github/workflows/main.yml - Using reusable workflow
name: Main Testing Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  unit-tests:
    uses: ./.github/workflows/test-reusable.yml
    with:
      node-version: '18'
      test-type: 'unit'
      cache-key: 'unit-tests'

  integration-tests:
    uses: ./.github/workflows/test-reusable.yml
    with:
      node-version: '18'
      test-type: 'integration'
      cache-key: 'integration-tests'
    secrets:
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
      CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

  e2e-tests:
    uses: ./.github/workflows/test-reusable.yml
    with:
      node-version: '20'
      test-type: 'e2e'
      cache-key: 'e2e-tests'

# 5. .github/workflows/test-metrics.yml - Test Metrics and Reporting
name: Test Metrics and Reporting

on:
  workflow_run:
    workflows: ["Tests"]
    types:
      - completed

jobs:
  generate-report:
    name: Generate Test Report
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion != 'skipped' }}

    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v3
        with:
          name: test-results
          path: ./test-results

      - name: Generate test report
        run: |
          # Create comprehensive test report
          node scripts/generate-test-report.js

      - name: Update PR comment with results
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v6
        with:
          script: |
            const fs = require('fs');
            const report = JSON.parse(fs.readFileSync('./test-report.json', 'utf8'));

            const comment = `## Test Results

            **Total Tests:** ${report.total}
            **Passed:** ${report.passed} ✅
            **Failed:** ${report.failed} ❌
            **Skipped:** ${report.skipped} ⏭️
            **Coverage:** ${report.coverage}%

            [View detailed report](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
            `;

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: comment
            });

      - name: Update test metrics
        run: |
          # Update metrics in external system
          node scripts/update-metrics.js

      - name: Create test summary
        id: test-summary
        run: |
          # Create markdown summary
          echo "summary<<EOF" >> $GITHUB_OUTPUT
          cat test-summary.md >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Comment on PR
        uses: peter-evans/create-or-update-comment@v3
        if: github.event_name == 'pull_request'
        with:
          issue-number: ${{ github.event.pull_request.number }}
          body: ${{ steps.test-summary.outputs.summary }}