From 7d3cb289adee56036aa663e385b86b52ed1fb2c7 Mon Sep 17 00:00:00 2001 From: Aryan Prince Date: Sun, 21 Jul 2024 21:24:09 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=B7=20ci:=20Create=20CI=20workflow=20f?= =?UTF-8?q?or=20Playwright=20E2E=20tests=20(#45)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✅ test: Remove auth setup test * 🔧 chore: Create constant baseUrl, remove other browsers * 👷 ci: Create CI workflow for Playwright E2E tests * 👷 ci: Now `cd` just before running tests * 👷 ci: Now creates a `.env` before tests * 🔧 chore: Playwright now runs `turbo dev` for all apps * ✅ test: Add test to create new user on Student * 👷 ci: Now runs all local DBs using Docker Compose * 👷 fix: Update syntax for multiple run steps in a job * ✅ test: Add longer timeout for test * 👷 ci: Run Playwright E2E tests on Vercel Preview deployments * 👷 ci: Remove `if` check for workflow * 👷 ci: Remove env to allow testing on Vercel Preview deployments * 👷 ci: Add step to setup DB with migrations and seed data * 👷 ci: Now builds monorepo, generates PR comment summary * 👷 ci: Update logic for `JSON` reports in CI * 👷 ci: Hardcode `JSON` report option * 👷 ci: Create `JSON` test result at monorepo root * 👷 ci: Add `write` permission for PRs * 👷 ci: Add Turbo caching, remove auth token * 👷 ci: Update path to `HTML` report, fix PR comment typo * 👷 ci: Generate both `HTML` and `JSON` test results * 👷 ci: Consolidate tests to single workflow * 🔧 chore: Add Firefox and WebKit testing * 🔧 chore: Add branded and mobile browser testing * ⚰️ chore(student): Remove slow test timeout * ♻️ chore: Capitalize Turbo in workflow step * 🔧 chore: Update reporter logic in CI and local dev --- .github/workflows/ci.yml | 60 +++++++++++++++++++ .gitignore | 2 + apps/student/playwright.config.ts | 58 +++++++++++------- .../tests/e2e-tests/auth/auth.setup.ts | 2 +- apps/student/tests/e2e-tests/example.spec.ts | 4 +- 5 files changed, 100 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ec3e0b..1b4e924 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,3 +73,63 @@ jobs: - name: Validate PR commits with commitlint if: github.event_name == 'pull_request' run: pnpm commitlint --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose + + e2e-tests: + timeout-minutes: 60 + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v4 + + - name: Cache Turbo build setup + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- + + - name: Setup + uses: ./tooling/github-actions/setup + + - name: Install Playwright Browsers + run: pnpm exec playwright install --with-deps + + - name: Run local databases with Docker Compose + uses: hoverkraft-tech/compose-action@v2.0.1 + + - name: Setup environment variables for Student + run: cp .env.example .env + working-directory: apps/student/ + + - name: Setup environment variables for Library + run: cp .env.example .env + working-directory: apps/library/ + + - name: Setup environment variables for Finance + run: cp .env.example .env + working-directory: apps/finance/ + + - name: Setup database with migrations and seed data + run: pnpm db:setup + + - name: Build monorepo + run: pnpm build + + - name: Run Playwright tests + run: cd apps/student/ && pnpm exec playwright test + + - uses: daun/playwright-report-summary@v3 + if: always() + with: + report-file: results.json + comment-title: "Playwright Test Results" + custom-info: "Generated by GitHub Actions [(Link to Action)](https://github.com/marketplace/actions/playwright-report-comment)" + + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 0968f01..8530b65 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,5 @@ apps/student/playwright/.auth/user.json .env apps/finance/src/tests/k6/ .vercel +playwright-report/ +results.json diff --git a/apps/student/playwright.config.ts b/apps/student/playwright.config.ts index 4a7d458..5a76d80 100644 --- a/apps/student/playwright.config.ts +++ b/apps/student/playwright.config.ts @@ -6,6 +6,9 @@ import { defineConfig, devices } from "@playwright/test"; */ // require('dotenv').config(); +// Base URL for the Student Portal app. +const baseURL = "http://localhost:3001"; + /** * See https://playwright.dev/docs/test-configuration. */ @@ -20,11 +23,20 @@ export default defineConfig({ /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: "html", + reporter: process.env.CI + ? [ + ["list", { printSteps: true }], + ["json", { outputFile: "../../results.json" }], + ["html", { outputFolder: "../../playwright-report/" }], + ] + : [ + ["list", { printSteps: true }], + ["html", { outputFolder: "playwright-report/" }], + ], /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ - // baseURL: 'http://127.0.0.1:3000', + baseURL: process.env.BASE_URL ?? baseURL, /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: "on-first-retry", @@ -53,30 +65,30 @@ export default defineConfig({ }, /* Test against mobile viewports. */ - // { - // name: 'Mobile Chrome', - // use: { ...devices['Pixel 5'] }, - // }, - // { - // name: 'Mobile Safari', - // use: { ...devices['iPhone 12'] }, - // }, + { + name: "Mobile Chrome", + use: { ...devices["Pixel 5"] }, + }, + { + name: "Mobile Safari", + use: { ...devices["iPhone 12"] }, + }, /* Test against branded browsers. */ - // { - // name: 'Microsoft Edge', - // use: { ...devices['Desktop Edge'], channel: 'msedge' }, - // }, - // { - // name: 'Google Chrome', - // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, - // }, + { + name: "Microsoft Edge", + use: { ...devices["Desktop Edge"], channel: "msedge" }, + }, + { + name: "Google Chrome", + use: { ...devices["Desktop Chrome"], channel: "chrome" }, + }, ], /* Run your local dev server before starting the tests */ - // webServer: { - // command: 'npm run start', - // url: 'http://127.0.0.1:3000', - // reuseExistingServer: !process.env.CI, - // }, + webServer: { + command: "cd ../../ && pnpm dev", + url: baseURL, + reuseExistingServer: !process.env.CI, + }, }); diff --git a/apps/student/tests/e2e-tests/auth/auth.setup.ts b/apps/student/tests/e2e-tests/auth/auth.setup.ts index 44e0036..43c6f0a 100644 --- a/apps/student/tests/e2e-tests/auth/auth.setup.ts +++ b/apps/student/tests/e2e-tests/auth/auth.setup.ts @@ -10,7 +10,7 @@ test("should create a random new user", async ({ page }) => { const email = faker.internet.email(); const password = faker.internet.password(); - await page.goto("http://localhost:3001/signup"); + await page.goto("/signup"); await page.getByLabel("First Name").fill(firstName); await page.getByLabel("Last Name").fill(lastName); await page.getByLabel("Username").fill(username); diff --git a/apps/student/tests/e2e-tests/example.spec.ts b/apps/student/tests/e2e-tests/example.spec.ts index a1c18ce..9cd8e62 100644 --- a/apps/student/tests/e2e-tests/example.spec.ts +++ b/apps/student/tests/e2e-tests/example.spec.ts @@ -2,11 +2,11 @@ import { expect, test } from "@playwright/test"; test("should navigate to the about page", async ({ page }) => { // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) - await page.goto("http://localhost:3001/test"); + await page.goto("/test"); // Find an element with the text 'About' and click on it await page.click("text=About"); // The new URL should be "/about" (baseURL is used there) - await expect(page).toHaveURL("http://localhost:3001/test/about"); + await expect(page).toHaveURL("/test/about"); // The new page should contain an h1 with "About" await expect(page.locator("h1")).toContainText("About"); });