diff --git a/examples/nextjs/.github/workflows/playwright.yml b/.github/workflows/playwright.yml similarity index 55% rename from examples/nextjs/.github/workflows/playwright.yml rename to .github/workflows/playwright.yml index 908d473..e081457 100644 --- a/examples/nextjs/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -11,7 +11,7 @@ on: types: [opened, synchronize, reopened] jobs: - test: + example-nextjs: timeout-minutes: 10 runs-on: ubuntu-latest steps: @@ -23,10 +23,33 @@ jobs: - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: lts/* + - name: Build Docker image + run: docker build -t example-nextjs examples/nextjs + - name: Start container + run: | + docker run -d --name example-nextjs-app -p 3000:3000 \ + -e ARCJET_ENV=development \ + -e AUTH_SECRET=playwright \ + -e AUTH_TRUST_HOST=http://127.0.0.1:3000 \ + -e AUTH_GITHUB_ID=playwright \ + -e AUTH_GITHUB_SECRET=playwright \ + example-nextjs - name: Install dependencies run: npm ci - name: Install Playwright Browsers run: npx playwright install --with-deps + - name: Wait for server to be ready + run: | + for i in $(seq 1 30); do + if curl -s http://127.0.0.1:3000 > /dev/null 2>&1; then + echo "Server is ready!" + exit 0 + fi + echo "Waiting for server... ($i/30)" + sleep 2 + done + echo "Server did not start in time" + exit 1 - name: Run Playwright tests run: npx playwright test - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 diff --git a/.gitignore b/.gitignore index a76a69f..0f2be0b 100644 --- a/.gitignore +++ b/.gitignore @@ -136,4 +136,10 @@ dist .pnp.* # Turborepo -.turbo \ No newline at end of file +.turbo + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ \ No newline at end of file diff --git a/examples/nextjs/.gitignore b/examples/nextjs/.gitignore index b344bb3..cb91252 100644 --- a/examples/nextjs/.gitignore +++ b/examples/nextjs/.gitignore @@ -34,9 +34,3 @@ yarn-error.log* .contentlayer .env - -# Playwright -/test-results/ -/playwright-report/ -/blob-report/ -/playwright/.cache/ diff --git a/examples/nextjs/package-lock.json b/examples/nextjs/package-lock.json index 6c3835b..8ce9c8d 100644 --- a/examples/nextjs/package-lock.json +++ b/examples/nextjs/package-lock.json @@ -24,7 +24,6 @@ "zod": "4.3.6" }, "devDependencies": { - "@playwright/test": "1.58.2", "@types/node": "22.19.11", "@types/react": "19.2.13", "@types/react-dom": "19.2.3", @@ -944,22 +943,6 @@ "nanoid": "^5.1.6" } }, - "node_modules/@playwright/test": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", - "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.58.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@standard-schema/utils": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", @@ -1094,20 +1077,6 @@ "node": ">=8" } }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/mdn-data": { "version": "2.12.2", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", @@ -1210,38 +1179,6 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, - "node_modules/playwright": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", - "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.58.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", - "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 79b7dcd..aac9af1 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -22,9 +22,7 @@ "build": "next build", "start": "next start", "lint": "next lint", - "lint:fix": "next lint --fix", - "test:open": "playwright test --ui-host=0.0.0.0", - "test:run": "playwright test" + "lint:fix": "next lint --fix" }, "dependencies": { "@arcjet/decorate": "1.3.0", @@ -44,7 +42,6 @@ "zod": "4.3.6" }, "devDependencies": { - "@playwright/test": "1.58.2", "@types/node": "22.19.11", "@types/react": "19.2.13", "@types/react-dom": "19.2.3", diff --git a/examples/nextjs/tests/screenshots.test.ts b/examples/nextjs/tests/screenshots.test.ts index 4fefc32..c3c99f2 100644 --- a/examples/nextjs/tests/screenshots.test.ts +++ b/examples/nextjs/tests/screenshots.test.ts @@ -1,16 +1,17 @@ import { expect, test } from "@playwright/test"; -// @ts-ignore build manifest may or may not exist -import appPathManifest from "../.next/app-path-routes-manifest.json"; - -const manifest = appPathManifest as Record; - -for (const [file, pathname] of Object.entries(manifest)) { - // Skip over dynamic and api routes - if (file.endsWith("/route") || file === "/_not-found/page") { - continue; - } +const paths = [ + "/", + "/attack", + "/bots", + "/rate-limiting", + "/sensitive-info", + "/sensitive-info/submitted", + "/signup", + "/signup/submitted", +]; +for (const pathname of paths) { test(`"${pathname}" screenshot matches`, async ({ page }) => { const response = await page.goto(pathname); await expect(response?.status()).toEqual(200); diff --git a/package-lock.json b/package-lock.json index e682829..09889d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,10 @@ "name": "@arcjet-examples/root", "version": "0.0.0", "devDependencies": { + "@playwright/test": "1.58.2", "@types/node": "24.10.1", "npm-check-updates": "19.3.2", - "simple-git": "3.30.0", + "simple-git": "3.32.3", "typescript": "5.9.3" } }, @@ -31,6 +32,22 @@ "dev": true, "license": "MIT" }, + "node_modules/@playwright/test": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", + "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@types/node": { "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", @@ -59,6 +76,21 @@ } } }, + "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, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -81,10 +113,42 @@ "npm": ">=8.12.1" } }, + "node_modules/playwright": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", + "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", + "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/simple-git": { - "version": "3.30.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.30.0.tgz", - "integrity": "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==", + "version": "3.32.3", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.32.3.tgz", + "integrity": "sha512-56a5oxFdWlsGygOXHWrG+xjj5w9ZIt2uQbzqiIGdR/6i5iococ7WQ/bNPzWxCJdEUGUCmyMH0t9zMpRJTaKxmw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index d3c40bd..a702ed4 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "devDependencies": { + "@playwright/test": "1.58.2", "@types/node": "24.10.1", "npm-check-updates": "19.3.2", - "simple-git": "3.30.0", + "simple-git": "3.32.3", "typescript": "5.9.3" }, "name": "@arcjet-examples/root", @@ -10,7 +11,9 @@ "private": true, "scripts": { "prepare-to-publish": "node scripts/prepare-to-publish.ts", - "sort-staged-imports": "npx @biomejs/biome check --formatter-enabled=false --linter-enabled=false --staged --write" + "sort-staged-imports": "npx @biomejs/biome check --formatter-enabled=false --linter-enabled=false --staged --write", + "test:open": "playwright test --ui-host=0.0.0.0", + "test:run": "playwright test" }, "type": "module", "version": "0.0.0" diff --git a/examples/nextjs/playwright.config.ts b/playwright.config.ts similarity index 73% rename from examples/nextjs/playwright.config.ts rename to playwright.config.ts index 7aaa5cd..f910535 100644 --- a/examples/nextjs/playwright.config.ts +++ b/playwright.config.ts @@ -4,7 +4,7 @@ import { defineConfig, devices } from "@playwright/test"; * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testDir: "./tests", + testDir: "./examples/nextjs/tests", /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ @@ -18,7 +18,7 @@ export default defineConfig({ /* 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:4000", + 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", @@ -53,16 +53,19 @@ export default defineConfig({ ], /* Run your local dev server before starting the tests */ - webServer: { - command: "npm run build && npm run start -- -p 4000", - url: "http://127.0.0.1:4000", - reuseExistingServer: !process.env.CI, - env: { - ARCJET_ENV: "development", - AUTH_SECRET: "playwright", - AUTH_TRUST_HOST: "http://127.0.0.1:4000", - AUTH_GITHUB_ID: "playwright", - AUTH_GITHUB_SECRET: "playwright", - }, - }, + webServer: process.env.CI + ? undefined + : { + command: "npm run build && npm run start -- -p 3000", + url: "http://127.0.0.1:3000", + reuseExistingServer: true, + cwd: "./examples/nextjs", + env: { + ARCJET_ENV: "development", + AUTH_SECRET: "playwright", + AUTH_TRUST_HOST: "http://127.0.0.1:3000", + AUTH_GITHUB_ID: "playwright", + AUTH_GITHUB_SECRET: "playwright", + }, + }, });