name: Tests · UI

# UI-side unit and end-to-end tests (Vitest + Playwright). The Go side
# of the workspace is tested in `go-unit.yaml`. Both workflows can run
# in parallel for a push that touches Go and UI together.

on:
  push:
    paths:
      - 'ui/**'
      - '.gitea/workflows/ui-test.yaml'
      - '!**/*.md'
  pull_request:
    paths:
      - 'ui/**'
      - '.gitea/workflows/ui-test.yaml'
      - '!**/*.md'

# Playwright launches its own `pnpm dev` on :5173. In host-mode the
# runner shares the host's port namespace, so two parallel ui-test
# jobs (e.g. a push event racing with a pull_request event for the
# same commit) collide on EADDRINUSE. Group by branch/ref and cancel
# the in-progress run so only one ui-test is alive at a time per ref.
concurrency:
  group: ui-test-${{ gitea.ref }}
  cancel-in-progress: true

jobs:
  test:
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Set up pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 11.0.7

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm
          cache-dependency-path: ui/pnpm-lock.yaml

      - name: Install npm dependencies
        working-directory: ui
        run: pnpm install --frozen-lockfile

      - name: Install Playwright browsers
        # `--with-deps` would shell out to `sudo apt-get install` for
        # the system .so libraries, which the host-mode runner cannot
        # run non-interactively. The host has the deps installed once,
        # globally; we only need to fetch the browser binaries here.
        # If a future run fails with missing libraries, install them
        # on the host via `pnpm exec playwright install-deps` (one
        # shot, requires sudo).
        working-directory: ui/frontend
        run: pnpm exec playwright install

      - name: Run Vitest
        working-directory: ui/frontend
        run: pnpm test

      - name: Clear stale Vite from :5173
        # Defence in depth in case a previous job's webServer survived
        # the concurrency-cancel — `pkill` does not fail when there is
        # nothing to kill, and `fuser -k` cleans up anything else
        # holding the port.
        run: |
          pkill -f 'vite dev' || true
          fuser -k 5173/tcp 2>/dev/null || true

      - name: Run Playwright
        working-directory: ui/frontend
        run: pnpm exec playwright test

      - name: Upload Playwright report on failure
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: ui/frontend/playwright-report/
          retention-days: 14

      - name: Upload Playwright traces on failure
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-traces
          path: ui/frontend/test-results/
          retention-days: 14
