chore: add Playwright E2E testing infrastructure (#673)
* feat: add Playwright testing infrastructure - Add @playwright/test dependency - Add playwright.config.ts with Chromium setup - Add test scripts (test, test:ui, test:headed) - Add hello world test to verify setup works This is the foundation for adding comprehensive E2E tests. Tests run against the Vite dev server on port 1420. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ci: add E2E test workflow Runs Playwright tests on pull requests using Chromium. Uploads test artifacts on failure. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: rename e2e to playwright, fix CI test - Rename workflow from "E2E Tests" to "Playwright" - Rename scripts from test:e2e to test:playwright - Fix test to not require Tauri APIs (just check server responds) - Run prettier Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: CJ Pais <cj@cjpais.com>
This commit is contained in:
parent
9ea1555816
commit
9ef71046ba
6 changed files with 91 additions and 1 deletions
29
.github/workflows/playwright.yml
vendored
Normal file
29
.github/workflows/playwright.yml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
name: "Playwright"
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
playwright:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: oven-sh/setup-bun@v1
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Install Playwright browsers
|
||||||
|
run: bunx playwright install chromium
|
||||||
|
|
||||||
|
- name: Run Playwright tests
|
||||||
|
run: bun run test:playwright
|
||||||
|
|
||||||
|
- name: Upload test results
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: playwright-report/
|
||||||
|
retention-days: 7
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -29,3 +29,8 @@ dist-ssr
|
||||||
/target/
|
/target/
|
||||||
recording_*
|
recording_*
|
||||||
.crush/
|
.crush/
|
||||||
|
|
||||||
|
# Playwright
|
||||||
|
playwright-report/
|
||||||
|
test-results/
|
||||||
|
blob-report/
|
||||||
|
|
|
||||||
9
bun.lock
9
bun.lock
|
|
@ -31,6 +31,7 @@
|
||||||
"zustand": "^5.0.8",
|
"zustand": "^5.0.8",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.58.0",
|
||||||
"@tauri-apps/cli": "^2.9.1",
|
"@tauri-apps/cli": "^2.9.1",
|
||||||
"@types/node": "^24.9.1",
|
"@types/node": "^24.9.1",
|
||||||
"@types/react": "^18.3.26",
|
"@types/react": "^18.3.26",
|
||||||
|
|
@ -204,6 +205,8 @@
|
||||||
|
|
||||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||||
|
|
||||||
|
"@playwright/test": ["@playwright/test@1.58.0", "", { "dependencies": { "playwright": "1.58.0" }, "bin": { "playwright": "cli.js" } }, "sha512-fWza+Lpbj6SkQKCrU6si4iu+fD2dD3gxNHFhUPxsfXBPhnv3rRSQVd0NtBUT9Z/RhF/boCBcuUaMUSTRTopjZg=="],
|
||||||
|
|
||||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
|
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="],
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="],
|
||||||
|
|
@ -608,6 +611,10 @@
|
||||||
|
|
||||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||||
|
|
||||||
|
"playwright": ["playwright@1.58.0", "", { "dependencies": { "playwright-core": "1.58.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-2SVA0sbPktiIY/MCOPX8e86ehA/e+tDNq+e5Y8qjKYti2Z/JG7xnronT/TXTIkKbYGWlCbuucZ6dziEgkoEjQQ=="],
|
||||||
|
|
||||||
|
"playwright-core": ["playwright-core@1.58.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-aaoB1RWrdNi3//rOeKuMiS65UCcgOVljU46At6eFcOFPFHWtd2weHRRow6z/n+Lec0Lvu0k9ZPKJSjPugikirw=="],
|
||||||
|
|
||||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||||
|
|
||||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||||
|
|
@ -730,6 +737,8 @@
|
||||||
|
|
||||||
"eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
"eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||||
|
|
||||||
|
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@
|
||||||
"format": "prettier --write . && cd src-tauri && cargo fmt",
|
"format": "prettier --write . && cd src-tauri && cargo fmt",
|
||||||
"format:check": "prettier --check . && cd src-tauri && cargo fmt -- --check",
|
"format:check": "prettier --check . && cd src-tauri && cargo fmt -- --check",
|
||||||
"format:frontend": "prettier --write .",
|
"format:frontend": "prettier --write .",
|
||||||
"format:backend": "cd src-tauri && cargo fmt"
|
"format:backend": "cd src-tauri && cargo fmt",
|
||||||
|
"test:playwright": "playwright test",
|
||||||
|
"test:playwright:ui": "playwright test --ui"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.1.16",
|
"@tailwindcss/vite": "^4.1.16",
|
||||||
|
|
@ -42,6 +44,7 @@
|
||||||
"zustand": "^5.0.8"
|
"zustand": "^5.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.58.0",
|
||||||
"@tauri-apps/cli": "^2.9.1",
|
"@tauri-apps/cli": "^2.9.1",
|
||||||
"@types/node": "^24.9.1",
|
"@types/node": "^24.9.1",
|
||||||
"@types/react": "^18.3.26",
|
"@types/react": "^18.3.26",
|
||||||
|
|
|
||||||
26
playwright.config.ts
Normal file
26
playwright.config.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: "./tests",
|
||||||
|
fullyParallel: true,
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
reporter: "html",
|
||||||
|
use: {
|
||||||
|
baseURL: "http://localhost:1420",
|
||||||
|
trace: "on-first-retry",
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: "chromium",
|
||||||
|
use: { ...devices["Desktop Chrome"] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
webServer: {
|
||||||
|
command: "bunx vite dev",
|
||||||
|
url: "http://localhost:1420",
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
timeout: 30000,
|
||||||
|
},
|
||||||
|
});
|
||||||
18
tests/app.spec.ts
Normal file
18
tests/app.spec.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
|
||||||
|
test.describe("Handy App", () => {
|
||||||
|
test("dev server responds", async ({ page }) => {
|
||||||
|
// Just verify the dev server is running and responds
|
||||||
|
const response = await page.goto("/");
|
||||||
|
expect(response?.status()).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("page has html structure", async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
|
||||||
|
// Verify basic HTML structure exists
|
||||||
|
const html = await page.content();
|
||||||
|
expect(html).toContain("<html");
|
||||||
|
expect(html).toContain("<body");
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue