Add real-stack Playwright e2e smoke suite #1808
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build | |
| # For more information on this YAML file, please reference blog: https://agramont.net/blog/devops-intro-deploy-net-aspire-azure-github-actions | |
| on: | |
| workflow_dispatch: | |
| push: | |
| # Run when commits are pushed to mainline branch (main or master) | |
| # Set this to the mainline branch you are using | |
| branches: | |
| - master | |
| - main | |
| pull_request: | |
| branches: ["dev", "master", "main"] | |
| permissions: | |
| contents: read | |
| # Based from https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-net | |
| jobs: | |
| changes: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| outputs: | |
| backend: ${{ steps.filter.outputs.backend || 'true' }} | |
| openapi: ${{ steps.filter.outputs.openapi || 'true' }} | |
| frontend: ${{ steps.filter.outputs.frontend || 'true' }} | |
| e2e: ${{ steps.filter.outputs.e2e || 'true' }} | |
| steps: | |
| - name: Checkout | |
| if: github.event_name == 'pull_request' | |
| uses: actions/checkout@v7 | |
| - name: Check for file changes | |
| if: github.event_name == 'pull_request' | |
| uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 | |
| id: filter | |
| with: | |
| filters: | | |
| backend: | |
| - 'backend/**' | |
| - '.github/workflows/main.yml' | |
| openapi: | |
| - 'backend/**' | |
| - 'ui/**' | |
| - 'open-api/**' | |
| - '.github/workflows/main.yml' | |
| frontend: | |
| - 'ui/**' | |
| - 'open-api/**' | |
| - '.github/workflows/main.yml' | |
| e2e: | |
| - 'backend/**' | |
| - 'ui/**' | |
| - 'open-api/**' | |
| - '.github/workflows/main.yml' | |
| dependency-review: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Dependency Review | |
| uses: actions/dependency-review-action@v5 | |
| backend-build: | |
| name: Create OpenAPI spec | |
| needs: changes | |
| if: | | |
| github.event_name != 'pull_request' || | |
| needs.changes.outputs.openapi == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| actions: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup dotnet 10.x | |
| uses: actions/setup-dotnet@v6 | |
| with: | |
| dotnet-version: "10.0.x" | |
| - name: Restore | |
| run: dotnet restore MenuApi.sln | |
| working-directory: ./backend | |
| - name: Build solution (generate OpenAPI) | |
| run: dotnet build MenuApi.sln --configuration Release --no-restore | |
| working-directory: ./backend | |
| - name: Upload OpenAPI document | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: openapi-spec | |
| path: open-api/menu-api.json | |
| if-no-files-found: error | |
| backend-tests: | |
| name: Backend tests | |
| needs: changes | |
| if: | | |
| github.event_name != 'pull_request' || | |
| needs.changes.outputs.backend == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| actions: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup dotnet 10.x | |
| uses: actions/setup-dotnet@v6 | |
| with: | |
| dotnet-version: "10.0.x" | |
| - name: Restore | |
| run: dotnet restore MenuApi.sln | |
| working-directory: ./backend | |
| - name: Build solution (generate OpenAPI) | |
| run: dotnet build MenuApi.sln --configuration Release --no-restore | |
| working-directory: ./backend | |
| - name: Test with the dotnet CLI | |
| run: | | |
| Get-ChildItem -Recurse -Filter "*.Tests.csproj" | Where-Object { $_.FullName -notlike "*Integration*" } | ForEach-Object { | |
| dotnet test $_.FullName --configuration Release --no-build | |
| } | |
| shell: pwsh | |
| working-directory: ./backend | |
| backend-integration-tests: | |
| name: Backend integration tests | |
| needs: changes | |
| if: | | |
| github.event_name != 'pull_request' || | |
| needs.changes.outputs.backend == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| actions: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup dotnet 10.x | |
| uses: actions/setup-dotnet@v6 | |
| with: | |
| dotnet-version: "10.0.x" | |
| - name: Restore | |
| run: dotnet restore MenuApi.sln | |
| working-directory: ./backend | |
| - name: Build solution (generate OpenAPI) | |
| run: dotnet build MenuApi.sln --configuration Release --no-restore | |
| working-directory: ./backend | |
| - name: clean ssl cert | |
| run: dotnet dev-certs https --clean | |
| - name: trust ssl cert | |
| run: | | |
| dotnet dev-certs https --trust || true | |
| echo "SSL_CERT_DIR=$HOME/.aspnet/dev-certs/trust:${SSL_CERT_DIR:-/usr/lib/ssl/certs}" >> "$GITHUB_ENV" | |
| - name: Test with the dotnet CLI | |
| run: dotnet test --project MenuApi.Integration.Tests/MenuApi.Integration.Tests.csproj --configuration Release --no-build | |
| working-directory: ./backend | |
| env: | |
| parameters__Auth0TestClientId: ${{ vars.AUTH0_CLIENT_ID }} | |
| parameters__Auth0TestClientSecret: ${{ secrets.AUTH0_CLIENT_SECRET }} | |
| parameters__Auth0Domain: ${{ vars.AUTH0_DOMAIN }} | |
| parameters__Auth0Audience: http://localhost:65273 | |
| frontend: | |
| name: Frontend validation | |
| runs-on: ubuntu-latest | |
| needs: [changes, backend-build] | |
| if: | | |
| !cancelled() && | |
| ( | |
| github.event_name != 'pull_request' || | |
| needs.changes.outputs.openapi == 'true' || | |
| needs.changes.outputs.frontend == 'true' | |
| ) && | |
| (needs.backend-build.result == 'success' || needs.backend-build.result == 'skipped') | |
| permissions: | |
| contents: read | |
| actions: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Download OpenAPI document | |
| if: needs.backend-build.result == 'success' | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: openapi-spec | |
| path: open-api | |
| merge-multiple: true | |
| - name: Use Node.js 22.x | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22.x | |
| - name: Install latest pnpm | |
| run: npm install -g pnpm@latest | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT" | |
| working-directory: ./ui/menu-website | |
| - name: Cache pnpm store | |
| uses: actions/cache@v6 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('ui/menu-website/pnpm-lock.yaml', 'ui/menu-website/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: Install frontend dependencies | |
| run: pnpm install --frozen-lockfile | |
| working-directory: ./ui/menu-website | |
| - name: Generate OpenAPI types | |
| run: pnpm run generate-openapi | |
| working-directory: ./ui/menu-website | |
| - name: Lint frontend | |
| run: pnpm run lint | |
| working-directory: ./ui/menu-website | |
| - name: Build frontend | |
| run: pnpm run build | |
| working-directory: ./ui/menu-website | |
| - name: Get installed Playwright version | |
| id: playwright-version | |
| run: | | |
| PLAYWRIGHT_VERSION=$(node -p "require('playwright/package.json').version") | |
| echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> "$GITHUB_OUTPUT" | |
| working-directory: ./ui/menu-website | |
| - name: Cache Playwright browsers | |
| uses: actions/cache@v6 | |
| id: playwright-cache | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.PLAYWRIGHT_VERSION }} | |
| - name: Install Playwright browsers | |
| if: steps.playwright-cache.outputs.cache-hit != 'true' | |
| run: pnpm exec playwright install --with-deps | |
| working-directory: ./ui/menu-website | |
| - name: Run frontend tests | |
| run: pnpm run test | |
| working-directory: ./ui/menu-website | |
| frontend-e2e: | |
| name: Frontend E2E (shard ${{ matrix.shard }}) | |
| needs: [changes, backend-build] | |
| if: | | |
| !cancelled() && | |
| ( | |
| github.event_name != 'pull_request' || | |
| ( | |
| needs.changes.outputs.e2e == 'true' && | |
| github.event.pull_request.head.repo.full_name == github.repository | |
| ) | |
| ) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2, 3] | |
| timeout-minutes: 30 | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| actions: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Download OpenAPI document | |
| if: needs.backend-build.result == 'success' | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: openapi-spec | |
| path: open-api | |
| merge-multiple: true | |
| - name: Setup dotnet 10.x | |
| uses: actions/setup-dotnet@v6 | |
| with: | |
| dotnet-version: "10.0.x" | |
| - name: Restore backend | |
| run: dotnet restore MenuApi.sln | |
| working-directory: ./backend | |
| - name: Install Aspire CLI | |
| run: | | |
| dotnet tool install --global Aspire.Cli | |
| export PATH="$HOME/.dotnet/tools:$PATH" | |
| echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" | |
| aspire --version | |
| - name: Use Node.js 22.x | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22.x | |
| - name: Install latest pnpm | |
| run: npm install -g pnpm@latest | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT" | |
| working-directory: ./ui/menu-website | |
| - name: Cache pnpm store | |
| uses: actions/cache@v6 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('ui/menu-website/pnpm-lock.yaml', 'ui/menu-website/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: Install frontend dependencies | |
| run: pnpm install --frozen-lockfile | |
| working-directory: ./ui/menu-website | |
| - name: Generate OpenAPI types | |
| run: pnpm run generate-openapi | |
| working-directory: ./ui/menu-website | |
| - name: Get installed Playwright version | |
| id: playwright-version | |
| run: | | |
| PLAYWRIGHT_VERSION=$(node -p "require('playwright/package.json').version") | |
| echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> "$GITHUB_OUTPUT" | |
| working-directory: ./ui/menu-website | |
| - name: Cache Playwright browsers | |
| uses: actions/cache@v6 | |
| id: playwright-cache | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.PLAYWRIGHT_VERSION }} | |
| - name: Install Playwright browsers | |
| if: steps.playwright-cache.outputs.cache-hit != 'true' | |
| run: pnpm exec playwright install --with-deps | |
| working-directory: ./ui/menu-website | |
| - name: Run Playwright shard | |
| run: pnpm run test:e2e -- --shard=${{ matrix.shard }}/${{ strategy.job-total }} --reporter=blob | |
| working-directory: ./ui/menu-website | |
| env: | |
| CI: true | |
| parameters__Auth0Domain: ${{ vars.AUTH0_DOMAIN }} | |
| parameters__Auth0Audience: http://localhost:65273 | |
| E2E_AUTH0_USERNAME: ${{ secrets.E2E_AUTH0_USERNAME }} | |
| E2E_AUTH0_PASSWORD: ${{ secrets.E2E_AUTH0_PASSWORD }} | |
| - name: Upload Playwright blob report | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-blob-${{ matrix.shard }} | |
| path: ui/menu-website/blob-report | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| - name: Upload Playwright failure artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-failures-${{ matrix.shard }} | |
| path: | | |
| ui/menu-website/test-results | |
| ui/menu-website/playwright-report | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| frontend-e2e-fork-skip: | |
| name: Frontend E2E skipped (fork PR) | |
| needs: changes | |
| if: github.event_name == 'pull_request' && needs.changes.outputs.e2e == 'true' && github.event.pull_request.head.repo.full_name != github.repository | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Explain skipped E2E run | |
| run: echo "Frontend E2E is skipped for fork pull requests because CI secrets are unavailable." | |
| merge-e2e-reports: | |
| name: Merge frontend E2E reports | |
| needs: frontend-e2e | |
| if: always() && (needs.frontend-e2e.result == 'success' || needs.frontend-e2e.result == 'failure') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| actions: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Node.js 22.x | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22.x | |
| - name: Install pnpm | |
| run: npm install -g pnpm@latest | |
| - name: Install frontend dependencies | |
| run: pnpm install --frozen-lockfile --ignore-scripts | |
| working-directory: ./ui/menu-website | |
| - name: Download shard reports | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: playwright-blob-* | |
| path: ui/menu-website/all-blob-reports | |
| - name: Merge reports into HTML | |
| run: pnpm exec playwright merge-reports --reporter html ./all-blob-reports | |
| working-directory: ./ui/menu-website | |
| - name: Upload merged Playwright report | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-report | |
| path: ui/menu-website/playwright-report | |
| if-no-files-found: ignore | |
| retention-days: 14 |