From c91014f65742d11ef7a11053b3351be4decc8eee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:34:55 +0000 Subject: [PATCH 1/4] Initial plan From 402b864602627986e3ec9ec76333a5c03f58a373 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:42:38 +0000 Subject: [PATCH 2/4] Move playwright config and action from nextjs to root with Docker-based CI Co-authored-by: qw-in <19194187+qw-in@users.noreply.github.com> --- .github/workflows/playwright.yml | 66 +++++++++++++++++++ .gitignore | 8 ++- .../nextjs/.github/workflows/playwright.yml | 37 ----------- examples/nextjs/.gitignore | 6 -- examples/nextjs/package-lock.json | 63 ------------------ examples/nextjs/package.json | 5 +- package-lock.json | 64 ++++++++++++++++++ package.json | 5 +- ...aywright.config.ts => playwright.config.ts | 31 +++++---- 9 files changed, 159 insertions(+), 126 deletions(-) create mode 100644 .github/workflows/playwright.yml delete mode 100644 examples/nextjs/.github/workflows/playwright.yml rename examples/nextjs/playwright.config.ts => playwright.config.ts (73%) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..91d3e60 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,66 @@ +name: Playwright Tests + +permissions: + contents: read + +on: + push: + branches: [main] + pull_request: + branches: [main] + types: [opened, synchronize, reopened] + +jobs: + example-nextjs: + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1 + with: + egress-policy: audit + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - 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: Extract build manifest + run: | + mkdir -p examples/nextjs/.next + docker create --name temp-nextjs example-nextjs + docker cp temp-nextjs:/app/.next/app-path-routes-manifest.json examples/nextjs/.next/app-path-routes-manifest.json + docker rm temp-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 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 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/.github/workflows/playwright.yml b/examples/nextjs/.github/workflows/playwright.yml deleted file mode 100644 index 908d473..0000000 --- a/examples/nextjs/.github/workflows/playwright.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Playwright Tests - -permissions: - contents: read - -on: - push: - branches: [main] - pull_request: - branches: [main] - types: [opened, synchronize, reopened] - -jobs: - test: - timeout-minutes: 10 - runs-on: ubuntu-latest - steps: - - name: Harden Runner - uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1 - with: - egress-policy: audit - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - 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: npx playwright test - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: ${{ !cancelled() }} - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 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/package-lock.json b/package-lock.json index e682829..fb7477c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "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", @@ -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,6 +113,38 @@ "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", diff --git a/package.json b/package.json index d3c40bd..6277cab 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "devDependencies": { + "@playwright/test": "1.58.2", "@types/node": "24.10.1", "npm-check-updates": "19.3.2", "simple-git": "3.30.0", @@ -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", + }, + }, }); From ca28be081c6ed1ce85429a4bbf7ffd9175e55dd3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 21:21:42 +0000 Subject: [PATCH 3/4] Replace build manifest with static path list in playwright tests Co-authored-by: qw-in <19194187+qw-in@users.noreply.github.com> --- .github/workflows/playwright.yml | 6 ------ examples/nextjs/tests/screenshots.test.ts | 21 +++++++++++---------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 91d3e60..e081457 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -25,12 +25,6 @@ jobs: node-version: lts/* - name: Build Docker image run: docker build -t example-nextjs examples/nextjs - - name: Extract build manifest - run: | - mkdir -p examples/nextjs/.next - docker create --name temp-nextjs example-nextjs - docker cp temp-nextjs:/app/.next/app-path-routes-manifest.json examples/nextjs/.next/app-path-routes-manifest.json - docker rm temp-nextjs - name: Start container run: | docker run -d --name example-nextjs-app -p 3000:3000 \ 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); From 4560e34899092eb31181a925823b5721539ef238 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 21:22:59 +0000 Subject: [PATCH 4/4] deps(root): update simple-git past GHSA-vulnerability for RCE bypass Co-authored-by: qw-in <19194187+qw-in@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb7477c..09889d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@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" } }, @@ -146,9 +146,9 @@ } }, "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 6277cab..a702ed4 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "@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",