mirror of
https://github.com/mosh-hamedani/helpdesk.git
synced 2026-05-21 11:58:19 +02:00
Set up Playwright
This commit is contained in:
parent
d7f4291b95
commit
e82aaba60d
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules
|
||||||
|
e2e/test-results/
|
||||||
|
playwright-report/
|
||||||
13
CLAUDE.md
13
CLAUDE.md
|
|
@ -17,6 +17,7 @@ A ticket management system that uses AI to classify, respond to, and route suppo
|
||||||
```
|
```
|
||||||
/client - React frontend (Vite)
|
/client - React frontend (Vite)
|
||||||
/server - Express backend
|
/server - Express backend
|
||||||
|
/e2e - Playwright E2E tests
|
||||||
```
|
```
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
@ -29,7 +30,7 @@ cd server && bun run dev
|
||||||
cd client && bun run dev
|
cd client && bun run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
The client proxies `/api/*` requests to the server via Vite config.
|
The client proxies `/api/*` requests to the server via Vite config (target is configurable via `VITE_API_URL` env var, defaults to `http://localhost:3000`).
|
||||||
|
|
||||||
## Key Conventions
|
## Key Conventions
|
||||||
|
|
||||||
|
|
@ -50,3 +51,13 @@ The client proxies `/api/*` requests to the server via Vite config.
|
||||||
- **Admin route protection (client)**: `AdminRoute` component wraps admin-only routes; redirects non-admins to `/`
|
- **Admin route protection (client)**: `AdminRoute` component wraps admin-only routes; redirects non-admins to `/`
|
||||||
- **Sign-up is disabled** — users are seeded via `prisma/seed.ts`
|
- **Sign-up is disabled** — users are seeded via `prisma/seed.ts`
|
||||||
- **User roles**: `admin` and `agent` (defined as Prisma enum, default `agent`)
|
- **User roles**: `admin` and `agent` (defined as Prisma enum, default `agent`)
|
||||||
|
- **Rate limiting**: Auth routes are rate-limited, but only enforced when `NODE_ENV=production`
|
||||||
|
|
||||||
|
## E2E Testing
|
||||||
|
|
||||||
|
- **Framework**: Playwright (config at root `playwright.config.ts`)
|
||||||
|
- **Test database**: `helpdesk_test` (isolated from dev `helpdesk` DB), configured in `server/.env.test`
|
||||||
|
- **Ports**: Test server on 3001, test client on 5174 (dev uses 3000/5173)
|
||||||
|
- **Global setup** (`e2e/global-setup.ts`): Runs `prisma migrate reset --force` then seeds the test DB
|
||||||
|
- **Tests directory**: `e2e/tests/`
|
||||||
|
- **Run tests**: `bun run test:e2e` from root (also `test:e2e:ui`, `test:e2e:headed`)
|
||||||
|
|
|
||||||
28
bun.lock
Normal file
28
bun.lock
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "helpdesk",
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.58.2",
|
||||||
|
"@types/node": "^25.2.3",
|
||||||
|
"dotenv": "^17.3.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@playwright/test": ["@playwright/test@1.58.2", "", { "dependencies": { "playwright": "1.58.2" }, "bin": { "playwright": "cli.js" } }, "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="],
|
||||||
|
|
||||||
|
"dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="],
|
||||||
|
|
||||||
|
"fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
|
||||||
|
|
||||||
|
"playwright": ["playwright@1.58.2", "", { "dependencies": { "playwright-core": "1.58.2" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A=="],
|
||||||
|
|
||||||
|
"playwright-core": ["playwright-core@1.58.2", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ export default defineConfig({
|
||||||
port: 5173,
|
port: 5173,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:3000',
|
target: process.env.VITE_API_URL || 'http://localhost:3000',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
33
e2e/global-setup.ts
Normal file
33
e2e/global-setup.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { execSync } from "child_process";
|
||||||
|
import path from "path";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
|
export default function globalSetup() {
|
||||||
|
const envPath = path.resolve(__dirname, "../server/.env.test");
|
||||||
|
const env = dotenv.config({ path: envPath });
|
||||||
|
|
||||||
|
console.log("Resetting test database...");
|
||||||
|
|
||||||
|
const serverDir = path.resolve(__dirname, "../server");
|
||||||
|
const execEnv = {
|
||||||
|
...process.env,
|
||||||
|
...env.parsed,
|
||||||
|
PRISMA_USER_CONSENT_FOR_DANGEROUS_AI_ACTION: "Yes",
|
||||||
|
};
|
||||||
|
|
||||||
|
execSync("bunx prisma migrate reset --force", {
|
||||||
|
cwd: serverDir,
|
||||||
|
stdio: "inherit",
|
||||||
|
env: execEnv,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Running seed...");
|
||||||
|
|
||||||
|
execSync("bun prisma/seed.ts", {
|
||||||
|
cwd: serverDir,
|
||||||
|
stdio: "inherit",
|
||||||
|
env: execEnv,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Test database ready.");
|
||||||
|
}
|
||||||
3
e2e/global-teardown.ts
Normal file
3
e2e/global-teardown.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function globalTeardown() {
|
||||||
|
console.log("Test run complete. Test database left intact for debugging.");
|
||||||
|
}
|
||||||
0
e2e/tests/.gitkeep
Normal file
0
e2e/tests/.gitkeep
Normal file
14
package.json
Normal file
14
package.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "helpdesk",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"test:e2e": "playwright test",
|
||||||
|
"test:e2e:ui": "playwright test --ui",
|
||||||
|
"test:e2e:headed": "playwright test --headed"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.58.2",
|
||||||
|
"@types/node": "^25.2.3",
|
||||||
|
"dotenv": "^17.3.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
33
playwright.config.ts
Normal file
33
playwright.config.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: "./e2e/tests",
|
||||||
|
globalSetup: "./e2e/global-setup.ts",
|
||||||
|
globalTeardown: "./e2e/global-teardown.ts",
|
||||||
|
fullyParallel: true,
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
reporter: "html",
|
||||||
|
use: {
|
||||||
|
baseURL: "http://localhost:5174",
|
||||||
|
trace: "on-first-retry",
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: "chromium",
|
||||||
|
use: { ...devices["Desktop Chrome"] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
webServer: [
|
||||||
|
{
|
||||||
|
command: "bun run --cwd server --env-file=.env.test src/index.ts",
|
||||||
|
url: "http://localhost:3001/api/health",
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: "VITE_API_URL=http://localhost:3001 bun run --cwd client vite --port 5174",
|
||||||
|
url: "http://localhost:5174",
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
11
server/.env.test
Normal file
11
server/.env.test
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
DATABASE_URL="postgresql://postgres:MyPassword!@localhost:5432/helpdesk_test?schema=public"
|
||||||
|
|
||||||
|
BETTER_AUTH_SECRET="test-secret-do-not-use-in-production"
|
||||||
|
BETTER_AUTH_URL="http://localhost:3001"
|
||||||
|
|
||||||
|
TRUSTED_ORIGINS="http://localhost:5174"
|
||||||
|
|
||||||
|
PORT=3001
|
||||||
|
|
||||||
|
SEED_ADMIN_EMAIL="admin@example.com"
|
||||||
|
SEED_ADMIN_PASSWORD="password123"
|
||||||
|
|
@ -21,12 +21,15 @@ app.use(
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isProduction = process.env.NODE_ENV === "production";
|
||||||
|
|
||||||
const authLimiter = rateLimit({
|
const authLimiter = rateLimit({
|
||||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||||
limit: 20,
|
limit: 20,
|
||||||
standardHeaders: "draft-8",
|
standardHeaders: "draft-8",
|
||||||
legacyHeaders: false,
|
legacyHeaders: false,
|
||||||
message: { error: "Too many requests, please try again later" },
|
message: { error: "Too many requests, please try again later" },
|
||||||
|
skip: () => !isProduction,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mount Better Auth handler BEFORE express.json()
|
// Mount Better Auth handler BEFORE express.json()
|
||||||
|
|
|
||||||
4
test-results/.last-run.json
Normal file
4
test-results/.last-run.json
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"status": "failed",
|
||||||
|
"failedTests": []
|
||||||
|
}
|
||||||
10
tsconfig.json
Normal file
10
tsconfig.json
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": ["e2e/**/*.ts", "playwright.config.ts"]
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue