import { expect, test } from "@playwright/test"; import { installDevApiMock, makeClient, seedAuth, } from "./helpers/devfront-fixtures"; import { captureEvidence } from "./helpers/evidence"; test.describe("DevFront tenant switch", () => { test.afterEach(async ({ page }, testInfo) => { if (testInfo.status === "passed") { await captureEvidence(page, testInfo, testInfo.title); } }); const MOCK_STATE = { clients: [makeClient("client-a", { name: "Tenant A App" })], consents: [], auditLogs: [], }; test.beforeEach(async ({ page }) => { await page.route("**/api/v1/user/me", async (route) => { if (route.request().method() === "GET") { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ id: "playwright-user", email: "playwright@example.com", name: "Playwright User", role: "tenant_admin", tenantId: "tenant-a", }), }); } else { await route.continue(); } }); }); test("multiple tenants: user can switch tenant context", async ({ page }) => { // Seed an admin user await seedAuth(page, "tenant_admin"); await installDevApiMock(page, MOCK_STATE); // Mock API to return multiple tenants await page.route("**/api/v1/dev/my-tenants", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([ { id: "tenant-a", name: "Tenant A", slug: "tenant-a" }, { id: "tenant-b", name: "Tenant B", slug: "tenant-b" }, ]), }); }); // Navigate to profile page await page.goto("/profile"); // Wait for the switcher to load const switcherHeading = page.getByText("작업 테넌트 (컨텍스트)"); await expect(switcherHeading).toBeVisible(); // Verify initial state is selected (tenant-a comes from seedAuth) const select = page.getByRole("combobox", { name: /테넌트/i }); await expect(select).toHaveValue("tenant-a"); // Change to Tenant B await select.selectOption("tenant-b"); // Click Save await page.getByRole("button", { name: /저장|Save/i }).click(); // Verify success toast await expect(page.getByText("테넌트 전환 완료").first()).toBeVisible(); // Verify localStorage was updated const savedTenantId = await page.evaluate(() => window.localStorage.getItem("dev_tenant_id"), ); expect(savedTenantId).toBe("tenant-b"); }); test("single tenant: switcher is disabled with a notice", async ({ page, }) => { await seedAuth(page, "tenant_admin"); // Mock API to return only ONE tenant await page.route("**/api/v1/dev/my-tenants", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([ { id: "tenant-a", name: "Tenant A", slug: "tenant-a" }, ]), }); }); await installDevApiMock(page, MOCK_STATE); await page.goto("/profile"); // Wait for the switcher to load await expect(page.getByText("작업 테넌트 (컨텍스트)")).toBeVisible(); // Verify the select is disabled const select = page.getByRole("combobox", { name: /테넌트/i }); await expect(select).toBeDisabled(); // Verify the save button is disabled const saveButton = page.getByRole("button", { name: /저장|Save/i }); await expect(saveButton).toBeDisabled(); // Verify the notice message await expect( page.getByText("단일 테넌트에 소속되어 전환할 필요가 없습니다."), ).toBeVisible(); }); });