From 620429c9b87e54f6256ad0a3c2937ea57b409985 Mon Sep 17 00:00:00 2001 From: "Tran G. (Revernomad) Khoa" Date: Fri, 17 Apr 2026 00:01:11 +0700 Subject: [PATCH] feat: add Playwright E2E testing setup with POM and testids - Install @playwright/test and Chromium browser - Create playwright.config.ts with dev server integration - Add data-testid attributes to LoginPage, DashboardPage, ServerCard, Sidebar - Exclude tests-e2e from vitest config - Create Page Object Models: LoginPage, DashboardPage - Add 18 E2E tests: 6 login flow, 12 dashboard (happy, empty, error states) - Add test:e2e and test:e2e:ui scripts to package.json --- frontend/package-lock.json | 64 ++++++++ frontend/package.json | 5 +- frontend/playwright.config.ts | 33 ++++ frontend/src/components/layout/Sidebar.tsx | 2 +- .../src/components/servers/ServerCard.tsx | 2 +- frontend/src/pages/DashboardPage.tsx | 10 +- frontend/src/pages/LoginPage.tsx | 9 +- frontend/tests-e2e/auth/login.spec.ts | 95 +++++++++++ .../tests-e2e/dashboard/dashboard.spec.ts | 148 ++++++++++++++++++ frontend/tests-e2e/pages/DashboardPage.ts | 73 +++++++++ frontend/tests-e2e/pages/LoginPage.ts | 40 +++++ frontend/vitest.config.ts | 1 + 12 files changed, 471 insertions(+), 11 deletions(-) create mode 100644 frontend/playwright.config.ts create mode 100644 frontend/tests-e2e/auth/login.spec.ts create mode 100644 frontend/tests-e2e/dashboard/dashboard.spec.ts create mode 100644 frontend/tests-e2e/pages/DashboardPage.ts create mode 100644 frontend/tests-e2e/pages/LoginPage.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8d83c2e..ac32911 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,6 +23,7 @@ }, "devDependencies": { "@eslint/js": "^9.39.4", + "@playwright/test": "^1.59.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", @@ -919,6 +920,22 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-rc.15", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", @@ -4284,6 +4301,53 @@ "node": ">= 6" } }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/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/postcss": { "version": "8.5.10", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", diff --git a/frontend/package.json b/frontend/package.json index f1f0302..a9dc9af 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,7 +9,9 @@ "lint": "eslint .", "preview": "vite preview", "test": "vitest run", - "test:watch": "vitest" + "test:watch": "vitest", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --ui" }, "dependencies": { "@hookform/resolvers": "^5.2.2", @@ -27,6 +29,7 @@ }, "devDependencies": { "@eslint/js": "^9.39.4", + "@playwright/test": "^1.59.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts new file mode 100644 index 0000000..480d6a9 --- /dev/null +++ b/frontend/playwright.config.ts @@ -0,0 +1,33 @@ +import { defineConfig, devices } from "@playwright/test"; + +export default defineConfig({ + testDir: "./tests-e2e", + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: [ + ["html", { outputFolder: "playwright-report" }], + ["list"], + ], + use: { + baseURL: process.env.BASE_URL || "http://localhost:5173", + trace: "on-first-retry", + screenshot: "only-on-failure", + video: "retain-on-failure", + actionTimeout: 10_000, + navigationTimeout: 30_000, + }, + projects: [ + { + name: "chromium", + use: { ...devices["Desktop Chrome"] }, + }, + ], + webServer: { + command: "npm run dev", + url: "http://localhost:5173", + reuseExistingServer: !process.env.CI, + timeout: 120_000, + }, +}); \ No newline at end of file diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx index b90bc06..047b5e2 100644 --- a/frontend/src/components/layout/Sidebar.tsx +++ b/frontend/src/components/layout/Sidebar.tsx @@ -11,7 +11,7 @@ export function Sidebar() { const activeId = serverId ? parseInt(serverId) : null; return ( -