diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..2ac6839 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm install -g pnpm && pnpm install + - name: Install Playwright Browsers + run: pnpm exec playwright install --with-deps + - name: Run Playwright tests + run: pnpm exec playwright test + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index fe13b9a..1ee17c5 100644 --- a/.gitignore +++ b/.gitignore @@ -53,4 +53,12 @@ error.log prisma/eventArchive.json prisma/forumArchive.json prisma/forumBackup.json + +# app images public/hytkylogo.png + +# app test results +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/README.md b/README.md index 6468064..4a57c58 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # HYTKY +![Playwright tests](https://github.com/zeeket/hytky/actions/workflows/playwright.yml/badge.svg) + Site and forum for a student organization centered around electronic music culture. This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`. The forum login is designed to be exclusive to members of certain Telegram groups. A Telegram user's membership status in the groups is checked with [HYTKYbot](https://github.com/zeeket/HYTKYbot). diff --git a/package.json b/package.json index 9fb1e6c..e07d6f7 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "zod": "^3.22.4" }, "devDependencies": { + "@playwright/test": "^1.45.2", "@svgr/webpack": "^7.0.0", "@types/eslint": "^8.56.3", "@types/node": "^18.19.18", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..54310be --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,79 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* 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', + /* 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:80', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + + /* Test against mobile viewports. */ + // { + // 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' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'make dev', + url: 'http://127.0.0.1:80', + timeout: 2 * 60000, // 2 minutes + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0158128..195b142 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,6 +67,9 @@ dependencies: version: 3.22.4 devDependencies: + '@playwright/test': + specifier: ^1.45.2 + version: 1.45.2 '@svgr/webpack': specifier: ^7.0.0 version: 7.0.0(typescript@5.3.3) @@ -1663,6 +1666,14 @@ packages: dev: true optional: true + /@playwright/test@1.45.2: + resolution: {integrity: sha512-JxG9eq92ET75EbVi3s+4sYbcG7q72ECeZNbdBlaMkGcNbiDQ4cAi8U2QP5oKkOx+1gpaiL1LDStmzCaEM1Z6fQ==} + engines: {node: '>=18'} + hasBin: true + dependencies: + playwright: 1.45.2 + dev: true + /@prisma/client@4.16.2(prisma@4.16.2): resolution: {integrity: sha512-qCoEyxv1ZrQ4bKy39GnylE8Zq31IRmm8bNhNbZx7bF2cU5aiCCnSa93J2imF88MBjn7J9eUQneNxUQVJdl/rPQ==} engines: {node: '>=14.17'} @@ -3426,6 +3437,14 @@ packages: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + /fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -4431,6 +4450,22 @@ packages: engines: {node: '>= 6'} dev: true + /playwright-core@1.45.2: + resolution: {integrity: sha512-ha175tAWb0dTK0X4orvBIqi3jGEt701SMxMhyujxNrgd8K0Uy5wMSwwcQHtyB4om7INUkfndx02XnQ2p6dvLDw==} + engines: {node: '>=18'} + hasBin: true + dev: true + + /playwright@1.45.2: + resolution: {integrity: sha512-ReywF2t/0teRvNBpfIgh5e4wnrI/8Su8ssdo5XsQKpjxJj+jspm00jSoz9BTg91TT0c9HRjXO7LBNVrgYj9X0g==} + engines: {node: '>=18'} + hasBin: true + dependencies: + playwright-core: 1.45.2 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} diff --git a/tests/static.finnish.spec.ts b/tests/static.finnish.spec.ts new file mode 100644 index 0000000..c79dcff --- /dev/null +++ b/tests/static.finnish.spec.ts @@ -0,0 +1,28 @@ +import { test, expect } from "@playwright/test"; + +test("has title", async ({ page }) => { + await page.goto("/"); + + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/HYTKY/); +}); + +test("navigate to rental page and back", async ({ page }) => { + await page.goto("/"); + + // Click the rental. + await page.click('text="Vuokraus →"'); + + // Wait for the text "Laitteistoa:" to be visible + const laitteistoaText = await page.waitForSelector('text="Laitteistoa:"'); + expect(laitteistoaText).not.toBeNull(); + + // Wait for the "Takaisin" text (with arrow) to be visible and click it + const takaisinLink = await page.waitForSelector('a:has-text("Takaisin")'); + expect(takaisinLink).not.toBeNull(); + + await takaisinLink.click(); + + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/HYTKY/); +});