import { expect, test } from "@playwright/test"; type UserSummary = { id: string; email: string; name: string; phone?: string; role: string; status: string; companyCode?: string; department?: string; createdAt: string; updatedAt: string; }; type UserCreatePayload = { email: string; password?: string; name: string; phone?: string; role?: string; companyCode?: string; department?: string; }; test("user create and delete flow", async ({ page }) => { const users: UserSummary[] = []; let idSeq = 1; await page.route("**/api/v1/admin/users**", async (route) => { const request = route.request(); const url = new URL(request.url()); const path = url.pathname; const isCollection = path.endsWith("/api/v1/admin/users"); const isItem = path.includes("/api/v1/admin/users/"); if (request.method() === "GET" && isCollection) { const search = url.searchParams.get("search")?.toLowerCase() ?? ""; const limit = Number(url.searchParams.get("limit") ?? "50"); const offset = Number(url.searchParams.get("offset") ?? "0"); const filtered = search ? users.filter( (user) => user.name.toLowerCase().includes(search) || user.email.toLowerCase().includes(search), ) : users; const items = filtered.slice(offset, offset + limit); await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ items, limit, offset, total: filtered.length, }), }); return; } if (request.method() === "POST" && isCollection) { const payload = request.postDataJSON() as UserCreatePayload; const now = new Date().toISOString(); const user: UserSummary = { id: `user-${idSeq++}`, email: payload.email, name: payload.name, phone: payload.phone, role: payload.role ?? "user", status: "active", companyCode: payload.companyCode, department: payload.department, createdAt: now, updatedAt: now, }; users.unshift(user); await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(user), }); return; } if (request.method() === "DELETE" && isItem) { const userId = path.split("/").pop(); const index = users.findIndex((user) => user.id === userId); if (index === -1) { await route.fulfill({ status: 404, contentType: "application/json", body: JSON.stringify({ error: "User not found" }), }); return; } users.splice(index, 1); await route.fulfill({ status: 204, body: "" }); return; } await route.fulfill({ status: 404, contentType: "application/json", body: JSON.stringify({ error: "Not found" }), }); }); await page.goto("/users"); await page.getByRole("link", { name: "사용자 추가" }).click(); await expect(page).toHaveURL(/\/users\/new$/); const uniqueEmail = `playwright-${Date.now()}@example.com`; await page.getByRole("checkbox", { name: "자동 생성" }).setChecked(false); await page.getByLabel("이메일").fill(uniqueEmail); await page.getByLabel("비밀번호").fill("Test1234!"); await page.getByLabel("이름").fill("Playwright User"); await page.getByLabel("전화번호").fill("010-0000-0000"); await page.getByLabel("회사 코드").fill("E2E"); await page.getByLabel("부서").fill("QA"); await page.getByLabel("역할 (Role)").selectOption("admin"); await page.getByRole("button", { name: "사용자 생성" }).click(); await expect(page).toHaveURL(/\/users$/); const createdRow = page.locator("tbody tr").filter({ hasText: uniqueEmail }); await expect(createdRow).toBeVisible(); page.once("dialog", (dialog) => dialog.accept()); await createdRow.getByRole("button", { name: /사용자 삭제/ }).click(); await expect( page.locator("tbody tr").filter({ hasText: uniqueEmail }), ).toHaveCount(0); });