diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..55335e3 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,43 @@ +name: Playwright Tests +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] +jobs: + test: + timeout-minutes: 10 + runs-on: ubuntu-latest + services: + postgres: + image: postgres:latest + env: + POSTGRES_USER: test + POSTGRES_PASSWORD: test + POSTGRES_DB: db + POSTGRESQL_FSYNC: "off" + ports: + - 5432:5432 + # needed because the postgres container does not provide a healthcheck + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npm run db:push && npx playwright test + env: + DATABASE_URL: postgres://test:test@localhost:5432/db + NEXTAUTH_SECRET: secret + NEXTAUTH_URL: http://localhost:3000 + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 1849819..9355c54 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,7 @@ yarn-error.log* .idea .vscode + +# playwright +playwright-report +test-results \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 68dc820..6a24927 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ "zustand": "^4.5.2" }, "devDependencies": { + "@playwright/test": "^1.48.2", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.0.1", "@types/eslint": "^8.56.2", @@ -1504,6 +1505,21 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@playwright/test": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz", + "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==", + "devOptional": true, + "dependencies": { + "playwright": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@prisma/client": { "version": "5.20.0", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.20.0.tgz", @@ -10938,6 +10954,50 @@ "node": ">= 6" } }, + "node_modules/playwright": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz", + "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==", + "devOptional": true, + "dependencies": { + "playwright-core": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz", + "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==", + "devOptional": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", diff --git a/package.json b/package.json index 70fa61c..66d3457 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "lint:fix": "next lint --fix && prisma format", "test": "SKIP_ENV_VALIDATION=true vitest", "test:coverage": "SKIP_ENV_VALIDATION=true vitest run --coverage", + "test:e2e": "npx playwright test", + "test:e2e:ui": "npx playwright test --ui", "start": "next start", "ragequit": "rm -rf .next && npm run db:push && npm run dev", "seed": "prisma db seed" @@ -65,6 +67,7 @@ "zustand": "^4.5.2" }, "devDependencies": { + "@playwright/test": "^1.48.2", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.0.1", "@types/eslint": "^8.56.2", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..baf32d4 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,74 @@ +import { defineConfig, devices } from '@playwright/test'; + +// import dotenv from 'dotenv'; +// dotenv.config({ path: './.env.e2e' }); + +/** + * 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:3000', + + /* 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: 'npm run build', + url: 'http://127.0.0.1:3000', + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/tests/pageObjects/basePage.ts b/tests/pageObjects/basePage.ts new file mode 100644 index 0000000..44a2bea --- /dev/null +++ b/tests/pageObjects/basePage.ts @@ -0,0 +1,16 @@ +import { expect, Page } from '@playwright/test' +export class BasePage { + page: Page + + constructor(page: Page) { + this.page = page + } + + async goto(path = '') { + await this.page.goto(path) + } + + async hasH1() { + await expect(this.page.locator('h1')).toBeVisible() + } +} \ No newline at end of file diff --git a/tests/pageObjects/signUpPage.ts b/tests/pageObjects/signUpPage.ts new file mode 100644 index 0000000..6a27ec4 --- /dev/null +++ b/tests/pageObjects/signUpPage.ts @@ -0,0 +1,8 @@ +import { expect } from "playwright/test"; +import { BasePage } from "./basePage"; + +export class SignUpPage extends BasePage { + async hasPresidentQuote() { + await expect(this.page.locator('blockquote')).toBeVisible(); + } +} \ No newline at end of file diff --git a/tests/signup.spec.ts b/tests/signup.spec.ts new file mode 100644 index 0000000..daa5ab8 --- /dev/null +++ b/tests/signup.spec.ts @@ -0,0 +1,17 @@ +import { test } from '@playwright/test' +import { SignUpPage } from './pageObjects/signUpPage' + +test.describe('Signup page', () => { + let signUpPage: SignUpPage + + test.beforeEach(async ({ page }) => { + signUpPage = new SignUpPage(page) + }) + + test('Signup page is loading and has the basic elements', async ({ page }) => { + await signUpPage.goto('http://localhost:3000/signup') + + await signUpPage.hasH1() + await signUpPage.hasPresidentQuote() + }) +}) \ No newline at end of file diff --git a/vitest.config.ts b/vitest.config.ts index 71e13a8..4ce1b8e 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from 'vitest/config' +import { configDefaults, defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react' import path from 'path' @@ -8,6 +8,10 @@ export default defineConfig({ environment: 'jsdom', globals: true, reporters: process.env.GITHUB_ACTIONS ? ['basic', 'github-actions'] : ['basic'], + exclude:[ + ...configDefaults.exclude, + 'tests/*' + ] }, resolve: { alias: {