From 8f78dbf68cbd719e31b35f8a3a76dbaf810424bd Mon Sep 17 00:00:00 2001 From: chan Date: Fri, 27 Mar 2026 19:06:51 +0900 Subject: [PATCH] test(adminfront): add E2E tests for login ID validation and conflict handling --- adminfront/tests/users.spec.ts | 245 +++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 adminfront/tests/users.spec.ts diff --git a/adminfront/tests/users.spec.ts b/adminfront/tests/users.spec.ts new file mode 100644 index 00000000..5676b00d --- /dev/null +++ b/adminfront/tests/users.spec.ts @@ -0,0 +1,245 @@ +import { expect, test } from "@playwright/test"; + +test.describe("User Management", () => { + test.beforeEach(async ({ page }) => { + await page.addInitScript(() => { + const authority = "http://localhost:5000/oidc"; + const client_id = "adminfront"; + const key = `oidc.user:${authority}:${client_id}`; + const authData = { + id_token: "fake-id-token", + access_token: "fake-token", + token_type: "Bearer", + scope: "openid profile email", + profile: { + sub: "admin-user", + name: "Admin", + email: "admin@test.com", + role: "super_admin", + }, + expires_at: Math.floor(Date.now() / 1000) + 36000, + }; + window.localStorage.setItem(key, JSON.stringify(authData)); + window.localStorage.setItem("admin_session", "fake-token"); + window.localStorage.setItem("locale", "ko"); + window.localStorage.setItem("oidc.state", "dummy"); + + ( + window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean } + )._IS_TEST_MODE = true; + }); + + await page.route("**/oidc/**", async (route) => { + if (route.request().url().includes("/.well-known/openid-configuration")) { + return route.fulfill({ + json: { + issuer: "http://localhost:5000/oidc", + authorization_endpoint: "http://localhost:5000/oidc/auth", + token_endpoint: "http://localhost:5000/oidc/token", + userinfo_endpoint: "http://localhost:5000/oidc/userinfo", + jwks_uri: "http://localhost:5000/oidc/jwks", + }, + }); + } + await route.fulfill({ json: { issuer: "http://localhost:5000/oidc" } }); + }); + + await page.route(/.*\/api\/v1\/.*/, async (route) => { + const url = route.request().url(); + const method = route.request().method(); + + if (url.includes("/user/me")) { + return route.fulfill({ + json: { + id: "admin-user", + name: "Admin", + email: "admin@test.com", + role: "super_admin", + manageableTenants: [], + }, + }); + } + + if (url.includes("/admin/tenants") && method === "GET") { + return route.fulfill({ + json: { + items: [ + { + id: "t-1", + slug: "test-tenant", + name: "Test Tenant", + config: { + userSchema: [], + }, + }, + ], + total: 1, + limit: 100, + offset: 0, + }, + }); + } + + if (url.includes("/admin/users/u-1") && method === "GET") { + return route.fulfill({ + json: { + id: "u-1", + name: "John Doe", + email: "john@test.com", + loginId: "johndoe", + tenantSlug: "test-tenant", + role: "user", + status: "active", + }, + }); + } + + if (url.includes("/admin/users") && method === "POST") { + // Parse request payload to simulate validation checks + const postData = route.request().postDataJSON(); + if (postData && postData.loginId === "existing_user") { + // Simulate a backend conflict error (409) for an existing loginId + return route.fulfill({ + status: 409, + json: { + error: "이미 존재하는 로그인 ID 입니다.", + }, + }); + } + // Mock successful user creation + return route.fulfill({ + json: { + id: "new-user-id", + name: "New User", + email: "newuser@test.com", + loginId: postData?.loginId || "newuser123", + }, + }); + } + + if (url.includes("/admin/users/u-1") && method === "PUT") { + // Parse request payload + const postData = route.request().postDataJSON(); + if (postData && postData.loginId === "existing_user") { + // Simulate a backend conflict error (409) for an existing loginId + return route.fulfill({ + status: 409, + json: { + error: "이미 존재하는 로그인 ID 입니다.", + }, + }); + } + + // Mock successful user update + return route.fulfill({ + json: { + id: "u-1", + name: "John Doe Updated", + email: "john@test.com", + loginId: postData?.loginId || "johndoe_updated", + }, + }); + } + + if (url.includes("/admin/users") && method === "GET") { + return route.fulfill({ + json: { + items: [ + { + id: "u-1", + name: "John Doe", + email: "john@test.com", + loginId: "johndoe", + role: "user", + status: "active", + } + ], + total: 1, + limit: 50, + offset: 0, + }, + }); + } + + return route.fulfill({ json: { items: [], total: 0 } }); + }); + }); + + test("should successfully edit a user's Login ID", async ({ page }) => { + await page.goto("/users/u-1"); + + // Wait for the form to load with the existing login ID + const loginIdInput = page.locator('input[id="loginId"]'); + await expect(loginIdInput).toBeVisible(); + await expect(loginIdInput).toHaveValue("johndoe"); + + // Change the Login ID + await loginIdInput.fill("johndoe_updated"); + + // Submit the form + const saveButton = page.getByRole("button", { name: /변경사항 저장|Save/i }); + await saveButton.click(); + + // Check for success message + await expect(page.getByText(/사용자 정보가 수정되었습니다/i).first()).toBeVisible(); + }); + + test("should show conflict error when updating to an existing Login ID", async ({ page }) => { + await page.goto("/users/u-1"); + + const loginIdInput = page.locator('input[id="loginId"]'); + await expect(loginIdInput).toBeVisible(); + + // Enter a login ID that triggers our mock conflict error + await loginIdInput.fill("existing_user"); + + const saveButton = page.getByRole("button", { name: /변경사항 저장|Save/i }); + await saveButton.click(); + + // Check for the specific conflict error message from the backend mock + await expect(page.getByText(/이미 존재하는 로그인 ID 입니다/i)).toBeVisible(); + }); + + test("should successfully create a new user with a Login ID", async ({ page }) => { + await page.goto("/users/new"); + + // Ensure the page title is loaded + await expect(page.getByText(/사용자 추가/i).first()).toBeVisible(); + + // Fill required fields + await page.locator('input[name="name"]').fill("New User"); + await page.locator('input[name="email"]').fill("newuser@test.com"); + + // Fill Login ID + const loginIdInput = page.locator('input[name="loginId"]'); + await loginIdInput.fill("newuser123"); + + // Submit the form + const createButton = page.getByRole("button", { name: /생성/i }); + await createButton.click(); + + // Assuming successful creation redirects back to the user list + await expect(page).toHaveURL(/.*\/users$/, { timeout: 10000 }); + }); + + test("should show conflict error when creating with an existing Login ID", async ({ page }) => { + await page.goto("/users/new"); + + await expect(page.getByText(/사용자 추가/i).first()).toBeVisible(); + + // Fill required fields + await page.locator('input[name="name"]').fill("New User"); + await page.locator('input[name="email"]').fill("newuser@test.com"); + + // Fill Login ID that triggers the mock conflict error + const loginIdInput = page.locator('input[name="loginId"]'); + await loginIdInput.fill("existing_user"); + + // Submit the form + const createButton = page.getByRole("button", { name: /생성/i }); + await createButton.click(); + + // Check for the specific conflict error message from the backend mock + await expect(page.getByText(/이미 존재하는 로그인 ID 입니다/i)).toBeVisible(); + }); +});