# Playwright testing for frontend code
# Runs the following tests:
# - Playwright tests in Firefox (against compiled frontend code)
# - Playwright tests in Chromium (coverage enabled, against vite frontend code)
# - Build frontend code and upload as artifact

name: Frontend

on:
  push:
    branches-ignore: ["l10*", "dependabot/**", "backport/**"]
  pull_request:
    branches-ignore: ["l10*"]

env:
  python_version: 3.12
  node_version: 24
  plugin_creator_version: 1.20.0
  # The OS version must be set per job
  server_start_sleep: 60

  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  INVENTREE_DB_ENGINE: postgresql
  INVENTREE_DB_NAME: inventree
  INVENTREE_DB_HOST: "127.0.0.1"
  INVENTREE_DB_PORT: 5432
  INVENTREE_DB_USER: inventree_user
  INVENTREE_DB_PASSWORD: inventree_password
  INVENTREE_DEBUG: true
  INVENTREE_PLUGINS_ENABLED: false
  INVENTREE_MEDIA_ROOT: /home/runner/work/InvenTree/test_inventree_media
  INVENTREE_STATIC_ROOT: /home/runner/work/InvenTree/test_inventree_static
  INVENTREE_BACKUP_DIR: /home/runner/work/InvenTree/test_inventree_backup
  INVENTREE_SITE_URL: http://localhost:8000

permissions:
  contents: read

jobs:
  paths-filter:
    name: Filter
    runs-on: ubuntu-latest

    outputs:
      frontend: ${{ steps.filter.outputs.frontend }}
      force: ${{ steps.force.outputs.force }}

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
        id: filter
        with:
          filters: |
            frontend:
              - 'src/frontend/**'
      - name: Is CI being forced?
        run: echo "force=true" >> $GITHUB_OUTPUT
        id: force
        if: |
          contains(github.event.pull_request.labels.*.name, 'dependency') ||
          contains(github.event.pull_request.labels.*.name, 'full-run')


  build:
    name: Build
    runs-on: ubuntu-24.04
    timeout-minutes: 60

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          npm: true
      - name: Install dependencies
        run: cd src/frontend && yarn install
      - name: Build frontend
        run: cd src/frontend && yarn run compile && yarn run lib && yarn run build
      - name: Write version file - SHA
        run: cd src/backend/InvenTree/web/static/web/.vite && echo "$GITHUB_SHA" > sha.txt
      - name: Zip frontend
        run: |
          cd src/backend/InvenTree/web/static
          zip -r frontend-build.zip web/ web/.vite
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: frontend-build
          path: src/backend/InvenTree/web/static/web
          include-hidden-files: true

  firefox:
    name: Tests [Firefox ${{ matrix.shard }} / 2]
    runs-on: ubuntu-24.04
    timeout-minutes: 60
    needs: ["build", "paths-filter"]
    if: github.ref == 'refs/heads/master' || needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.force == 'true'
    strategy:
      fail-fast: false
      matrix:
        shard: [1, 2]
    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_DB: inventree
          POSTGRES_USER: inventree_user
          POSTGRES_PASSWORD: inventree_password
        ports:
          - 5432:5432
        options: >-
          --health-cmd "pg_isready -U testuser"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    permissions:
      contents: read # Required for actions/checkout
      id-token: write # Required for GitHub OIDC
    env:
      VITE_COVERAGE: false

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          npm: true
          install: true
          update: true
          apt-dependency: gettext postgresql-client libpq-dev
          pip-dependency: psycopg2
      - name: Set up test data
        run: |
          invoke dev.setup-test -iv
          invoke int.rebuild-thumbnails
      - name: Install dependencies
        run: invoke int.frontend-compile --extract
      - name: Cache Playwright browsers
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
        id: playwright-cache
        with:
          path: ~/.cache/ms-playwright
          key: ${{ runner.os }}-playwright-${{ hashFiles('src/frontend/yarn.lock') }}
      - name: Install Playwright browsers
        if: steps.playwright-cache.outputs.cache-hit != 'true'
        run: cd src/frontend && npx playwright install --with-deps
      - name: Install Playwright OS dependencies
        if: steps.playwright-cache.outputs.cache-hit == 'true'
        run: cd src/frontend && npx playwright install-deps
      - name: Install Sample Plugin
        run: |
          pip install -U inventree-plugin-creator==${{ env.plugin_creator_version }}
          create-inventree-plugin --default
          cd MyCustomPlugin && pip install -e . && cd frontend && npm install && npm run translate && npm run build
      - name: Run Playwright tests
        id: tests
        run: |
          cd src/frontend
          cp ./tests/fixtures/playwright_custom_logo.png ../backend/InvenTree/InvenTree/static/img/playwright_custom_logo.png
          cp ./tests/fixtures/playwright_custom_splash.png ../backend/InvenTree/InvenTree/static/img/playwright_custom_splash.png
          invoke static
          env INVENTREE_CUSTOM_SPLASH="img/playwright_custom_splash.png" INVENTREE_CUSTOM_LOGO="img/playwright_custom_logo.png" PLAYWRIGHT_BASE_URL=http://localhost:8000 npx playwright test --project=firefox --shard=${{ matrix.shard }}/2
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        if: ${{ !cancelled() && steps.tests.outcome == 'failure' }}
        with:
          name: playwright-report-firefox-${{ matrix.shard }}
          path: src/frontend/playwright-report/
          retention-days: 14

  chromium:
    name: Tests [Chromium ${{ matrix.shard }} / 4]
    runs-on: ubuntu-24.04
    timeout-minutes: 60
    needs: ["build", "paths-filter"]
    if: github.ref == 'refs/heads/master' || needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.force == 'true'
    strategy:
      fail-fast: false
      matrix:
        shard: [1, 2, 3, 4]
    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_DB: inventree
          POSTGRES_USER: inventree_user
          POSTGRES_PASSWORD: inventree_password
        ports:
          - 5432:5432
        options: >-
          --health-cmd "pg_isready -U testuser"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    permissions:
      contents: read # Required for actions/checkout
      id-token: write # Required for GitHub OIDC

    env:
      COVERAGE: true
      VITE_COVERAGE: true

    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          npm: true
          install: true
          update: true
          apt-dependency: gettext postgresql-client libpq-dev
          pip-dependency: psycopg2
      - name: Set up test data
        run: |
          invoke dev.setup-test -iv
          invoke int.rebuild-thumbnails
      - name: Install dependencies
        run: invoke int.frontend-compile --extract
      - name: Cache Playwright browsers
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
        id: playwright-cache
        with:
          path: ~/.cache/ms-playwright
          key: ${{ runner.os }}-playwright-${{ hashFiles('src/frontend/yarn.lock') }}
      - name: Install Playwright browsers
        if: steps.playwright-cache.outputs.cache-hit != 'true'
        run: cd src/frontend && npx playwright install --with-deps
      - name: Install Playwright OS dependencies
        if: steps.playwright-cache.outputs.cache-hit == 'true'
        run: cd src/frontend && npx playwright install-deps
      - name: Install Sample Plugin
        run: |
          pip install -U inventree-plugin-creator==${{ env.plugin_creator_version }}
          create-inventree-plugin --default
          cd MyCustomPlugin && pip install -e . && cd frontend && npm install && npm run translate && npm run build
      - name: Playwright [${{ matrix.shard }} / 4]
        id: tests
        run: |
          cd src/frontend
          npx nyc playwright test --project=chromium --shard=${{ matrix.shard }}/4
      - name: Playwright Report [${{ matrix.shard }} / 4]
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        if: ${{ !cancelled() && steps.tests.outcome == 'failure' }}
        with:
          name: playwright-report-chromium-${{ matrix.shard }}
          path: src/frontend/playwright-report/
          if-no-files-found: error
          retention-days: 7
      - name: Upload Coverage Artifact [${{ matrix.shard }} / 4]
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        id: coverage-upload
        if: ${{ !cancelled() && steps.tests.outcome != 'failure' }}
        with:
          name: coverage-${{ matrix.shard }}
          path: src/frontend/.nyc_output/
          if-no-files-found: error
          include-hidden-files: true
          retention-days: 1

  # Recombine the coverage reports from the 4 shards, and upload to Codecov
  coverage:
    name: Merge coverage reports and upload to Codecov
    runs-on: ubuntu-latest
    needs: chromium
    timeout-minutes: 30
    if: always()

    steps:
      - name: Checkout
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Environment Setup
        uses: ./.github/actions/setup
        with:
          npm: true
          install: false
          update: false

      - name: Download Coverage Artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: coverage-*
          path: all-coverage/
          merge-multiple: true

      - name: Merge Coverage Reports
        run: |
          mkdir -p .nyc_output
          cp all-coverage/*.json .nyc_output/ 2>/dev/null || true
          npx nyc merge .nyc_output merged-coverage.json
          npx nyc report \
            --tempdir .nyc_output \
            --reporter=lcov \
            --reporter=text-summary \
            --report-dir ./coverage \

      - name: Upload coverage reports to Codecov
        if: ${{ !cancelled() && github.ref == 'refs/heads/master' }}
        uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          slug: inventree/InvenTree
          flags: web
          files: ./coverage/lcov.info
