From e909776c5d1723e9982f2e7c004dc1b8c23d17f3 Mon Sep 17 00:00:00 2001 From: kyy Date: Thu, 19 Mar 2026 12:57:14 +0900 Subject: [PATCH] =?UTF-8?q?=ED=85=8C=EB=84=8C=ED=8A=B8=20=EC=8A=A4?= =?UTF-8?q?=EC=9C=84=EC=B9=AD=20=EA=B8=B0=EB=8A=A5=20E2E=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devfront/tests/devfront-tenant-switch.spec.ts | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 devfront/tests/devfront-tenant-switch.spec.ts diff --git a/devfront/tests/devfront-tenant-switch.spec.ts b/devfront/tests/devfront-tenant-switch.spec.ts new file mode 100644 index 00000000..de29a568 --- /dev/null +++ b/devfront/tests/devfront-tenant-switch.spec.ts @@ -0,0 +1,118 @@ +import { expect, test } from "@playwright/test"; +import { + installDevApiMock, + makeClient, + seedAuth, +} from "./helpers/devfront-fixtures"; + +test.describe("DevFront tenant switch", () => { + 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(); + }); +});