import { expect, test } from "@playwright/test"; import type { TenantCreateRequest, TenantSummary } from "../src/lib/adminApi"; test.use({ storageState: { cookies: [], origins: [ { origin: "http://localhost:5173", localStorage: [ { name: "admin_session", value: "playwright-admin-session", }, ], }, ], }, }); test("tenant create and delete flow", async ({ page }) => { const tenants: TenantSummary[] = []; let idSeq = 1; await page.route("**/api/v1/admin/tenants**", async (route) => { const request = route.request(); const url = new URL(request.url()); const path = url.pathname; const isCollection = path.endsWith("/api/v1/admin/tenants"); const isItem = path.includes("/api/v1/admin/tenants/"); if (request.method() === "GET" && isCollection) { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ items: tenants, limit: 50, offset: 0, total: tenants.length, }), }); return; } if (request.method() === "POST" && isCollection) { const payload = request.postDataJSON() as TenantCreateRequest; const now = new Date().toISOString(); const tenant: TenantSummary = { id: `tenant-${idSeq++}`, name: payload.name, type: payload.type || "COMPANY", slug: payload.slug || `slug-${idSeq}`, description: payload.description || "", status: payload.status || "active", domains: payload.domains || [], createdAt: now, updatedAt: now, }; tenants.unshift(tenant); await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(tenant), }); return; } if (request.method() === "DELETE" && isItem) { const tenantId = path.split("/").pop(); const index = tenants.findIndex((t) => t.id === tenantId); if (index !== -1) { tenants.splice(index, 1); } await route.fulfill({ status: 204, body: "" }); return; } await route.fallback(); }); await page.goto("/tenants"); await expect(page).toHaveURL(/\/tenants$/); await expect(page.getByRole("heading", { name: "테넌트 목록" })).toBeVisible(); // Create const addTenantLink = page.getByRole("link", { name: "테넌트 추가" }); await addTenantLink.click(); await expect(page).toHaveURL(/\/tenants\/new$/); const uniqueName = `Test Tenant ${Date.now()}`; await page.getByLabel("테넌트 이름 *").fill(uniqueName); await page.getByLabel("테넌트 유형").selectOption("COMPANY"); await page.getByLabel("슬러그 (Slug)").fill("test-tenant"); await page.getByLabel("설명").fill("This is an E2E test tenant"); await page.getByLabel("허용된 도메인 (콤마로 구분)").fill("test.com, example.com"); await page.getByRole("button", { name: "생성" }).click(); await expect(page).toHaveURL(/\/tenants$/); // Verify created const createdRow = page.locator("tbody tr").filter({ hasText: uniqueName }); await expect(createdRow).toBeVisible(); // Delete page.once("dialog", (dialog) => dialog.accept()); await createdRow.getByRole("button", { name: "삭제" }).click(); await expect(page.locator("tbody tr").filter({ hasText: uniqueName })).toHaveCount(0); }); test("tenant creation form validation", async ({ page }) => { await page.goto("/tenants/new"); // Try to submit empty form await page.getByRole("button", { name: "생성" }).click(); // Since 'name' is required, we check if button is still disabled or form doesn't navigate await expect(page).toHaveURL(/\/tenants\/new$/); });