diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 00000000..0c8a35ea --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,44 @@ +name: Test + +on: + pull_request: + push: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + name: Test + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: "16" + - uses: pnpm/action-setup@v2.1.0 + with: + version: 7 + run_install: false + - name: Get pnpm store directory + id: pnpm-cache + run: | + echo "::set-output name=pnpm_cache_dir::$(pnpm store path)" + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + - name: Install dependencies + run: pnpm i + - name: Install browsers + run: pnpm playwright install chromium --with-deps + - name: E2E test + run: pnpm test:e2e + - name: Upload test results + if: always() + uses: actions/upload-artifact@v3 + with: + name: playwright-report + path: playwright-report diff --git a/.gitignore b/.gitignore index 58f5632c..27b9b335 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dist .env .env.production generated/ +test-results/ \ No newline at end of file diff --git a/e2e/home.spec.ts b/e2e/home.spec.ts new file mode 100644 index 00000000..2c6c4c05 --- /dev/null +++ b/e2e/home.spec.ts @@ -0,0 +1,6 @@ +import { expect, test } from "@playwright/test" + +test("Home page should load", async ({ page }) => { + await page.goto("/") + expect(await page.textContent("h1")).toBe("Proselog") +}) diff --git a/package.json b/package.json index 957b1dd9..7c8a866e 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "scripts": { "build": "pnpm db-generate && next build", "dev": "next", + "test-e2e": "playwright test", "db-generate": "prisma generate", "start": "next start --port $PORT", "store-prisma-client": "node scripts/restore-prisma-client.js" @@ -55,6 +56,7 @@ "zustand": "4.0.0-rc.1" }, "devDependencies": { + "@playwright/test": "^1.22.1", "@types/async-busboy": "^1.1.1", "@types/busboy": "^1.5.0", "@types/cookie": "^0.5.1", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..9323c1eb --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,51 @@ +import { PlaywrightTestConfig, devices } from "@playwright/test" +import path from "path" + +// Reference: https://playwright.dev/docs/test-configuration +const config: PlaywrightTestConfig = { + timeout: 30 * 1000, + testDir: path.join(__dirname, "e2e"), + retries: 2, + outputDir: "test-results/", + webServer: { + command: "npm run dev", + port: 3000, + timeout: 120 * 1000, + reuseExistingServer: !process.env.CI, + }, + use: { + trace: "retry-with-trace", + }, + projects: [ + { + name: "Desktop Chrome", + use: { + ...devices["Desktop Chrome"], + }, + }, + // Keep it simple for now, only testing desktop chrome + // { + // name: 'Desktop Firefox', + // use: { + // ...devices['Desktop Firefox'], + // }, + // }, + // { + // name: 'Desktop Safari', + // use: { + // ...devices['Desktop Safari'], + // }, + // }, + // { + // name: 'Mobile Chrome', + // use: { + // ...devices['Pixel 5'], + // }, + // }, + // { + // name: 'Mobile Safari', + // use: devices['iPhone 13'], + // }, + ], +} +export default config diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2842208d..43f4228c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,7 @@ specifiers: '@codemirror/view': ^0.20.3 '@hapi/iron': ^6.0.0 '@headlessui/react': ^1.6.0 + '@playwright/test': ^1.22.1 '@prisma/client': ^3.14.0 '@proselog/jwt': ^0.1.1 '@trpc/client': ^9.23.2 @@ -107,6 +108,7 @@ dependencies: zustand: 4.0.0-rc.1_react@18.1.0 devDependencies: + '@playwright/test': 1.22.1 '@types/async-busboy': 1.1.1 '@types/busboy': 1.5.0 '@types/cookie': 0.5.1 @@ -688,6 +690,14 @@ packages: resolution: {integrity: sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==} dev: false + /@playwright/test/1.22.1: + resolution: {integrity: sha512-8ouMBUboYslHom41W8bnSEn0TwlAMHhCACwOZeuiAgzukj7KobpZ+UBwrGE0jJ0UblJbKAQNRHXL+z7sDSkb6g==} + engines: {node: '>=14'} + hasBin: true + dependencies: + playwright-core: 1.22.1 + dev: true + /@prisma/client/3.14.0_prisma@3.14.0: resolution: {integrity: sha512-atb41UpgTR1MCst0VIbiHTMw8lmXnwUvE1KyUCAkq08+wJyjRE78Due+nSf+7uwqQn+fBFYVmoojtinhlLOSaA==} engines: {node: '>=12.6'} @@ -1376,22 +1386,12 @@ packages: /debug/2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true dependencies: ms: 2.0.0 dev: true /debug/3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true dependencies: ms: 2.1.2 dev: true @@ -1566,14 +1566,13 @@ packages: eslint: 8.15.0 eslint-import-resolver-node: 0.3.6 eslint-import-resolver-typescript: 2.7.1_gwd37gqv3vjv3xlpl7ju3ag2qu - eslint-plugin-import: 2.26.0_j3mcmpo7om5tltq775lihvikb4 + eslint-plugin-import: 2.26.0_eslint@8.15.0 eslint-plugin-jsx-a11y: 6.5.1_eslint@8.15.0 eslint-plugin-react: 7.29.4_eslint@8.15.0 eslint-plugin-react-hooks: 4.5.0_eslint@8.15.0 next: 12.1.6_ef5jwxihqo6n7gxfmzogljlgcm typescript: 4.6.4 transitivePeerDependencies: - - eslint-import-resolver-webpack - supports-color dev: true @@ -1582,8 +1581,6 @@ packages: dependencies: debug: 3.2.7 resolve: 1.22.0 - transitivePeerDependencies: - - supports-color dev: true /eslint-import-resolver-typescript/2.7.1_gwd37gqv3vjv3xlpl7ju3ag2qu: @@ -1595,7 +1592,7 @@ packages: dependencies: debug: 4.3.4 eslint: 8.15.0 - eslint-plugin-import: 2.26.0_j3mcmpo7om5tltq775lihvikb4 + eslint-plugin-import: 2.26.0_eslint@8.15.0 glob: 7.2.0 is-glob: 4.0.3 resolve: 1.22.0 @@ -1604,51 +1601,27 @@ packages: - supports-color dev: true - /eslint-module-utils/2.7.3_bpv7bvgsekzf76isjsnkhydtce: + /eslint-module-utils/2.7.3: resolution: {integrity: sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==} engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true dependencies: - '@typescript-eslint/parser': 5.23.0_hcfsmds2fshutdssjqluwm76uu debug: 3.2.7 - eslint-import-resolver-node: 0.3.6 - eslint-import-resolver-typescript: 2.7.1_gwd37gqv3vjv3xlpl7ju3ag2qu find-up: 2.1.0 - transitivePeerDependencies: - - supports-color dev: true - /eslint-plugin-import/2.26.0_j3mcmpo7om5tltq775lihvikb4: + /eslint-plugin-import/2.26.0_eslint@8.15.0: resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} engines: {node: '>=4'} peerDependencies: - '@typescript-eslint/parser': '*' eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true dependencies: - '@typescript-eslint/parser': 5.23.0_hcfsmds2fshutdssjqluwm76uu array-includes: 3.1.5 array.prototype.flat: 1.3.0 debug: 2.6.9 doctrine: 2.1.0 eslint: 8.15.0 eslint-import-resolver-node: 0.3.6 - eslint-module-utils: 2.7.3_bpv7bvgsekzf76isjsnkhydtce + eslint-module-utils: 2.7.3 has: 1.0.3 is-core-module: 2.9.0 is-glob: 4.0.3 @@ -1656,10 +1629,6 @@ packages: object.values: 1.1.5 resolve: 1.22.0 tsconfig-paths: 3.14.1 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color dev: true /eslint-plugin-jsx-a11y/6.5.1_eslint@8.15.0: @@ -2678,6 +2647,12 @@ packages: engines: {node: '>=0.10.0'} dev: true + /playwright-core/1.22.1: + resolution: {integrity: sha512-H+ZUVYnceWNXrRf3oxTEKAr81QzFsCKu5Fp//fEjQvqgKkfA1iX3E9DBrPJpPNOrgVzcE+IqeI0fDmYJe6Ynnw==} + engines: {node: '>=14'} + hasBin: true + dev: true + /postcss-import/14.1.0: resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} engines: {node: '>=10.0.0'}