import { expect, test } from "@playwright/test"; type TenantFixture = { id: string; type: string; name: string; slug: string; description: string; status: string; parentId?: string; memberCount: number; createdAt: string; updatedAt: string; }; function tenant( id: string, name: string, slug: string, parentId?: string, ): TenantFixture { return { id, type: parentId ? "USER_GROUP" : "COMPANY", name, slug, description: "", status: "active", parentId, memberCount: 1, createdAt: "2026-04-01T00:00:00.000Z", updatedAt: "2026-04-01T00:00:00.000Z", }; } function user(id: string, name: string, companyCode: string) { return { id, email: `${id}@example.com`, name, role: "user", status: "active", companyCode, position: "사원", createdAt: "2026-04-01T00:00:00.000Z", updatedAt: "2026-04-01T00:00:00.000Z", }; } test("org chart viewport pans with drag and zooms with the mouse wheel", async ({ page, }) => { await page.route("**/api/v1/public/orgchart**", async (route) => { await route.fulfill({ contentType: "application/json", body: JSON.stringify({ sharedWith: "Playwright", tenants: [ tenant("root", "Baron Group", "baron"), tenant("engineering", "Engineering", "engineering", "root"), tenant("platform", "Platform", "platform", "engineering"), tenant("security", "Security", "security", "engineering"), tenant("product", "Product", "product", "root"), tenant("design", "Design", "design", "product"), tenant("operations", "Operations", "operations", "root"), ], users: [ user("u-root", "Root User", "baron"), user("u-eng", "Engineering User", "engineering"), user("u-platform", "Platform User", "platform"), user("u-security", "Security User", "security"), user("u-product", "Product User", "product"), user("u-design", "Design User", "design"), user("u-ops", "Operations User", "operations"), ], }), }); }); await page.goto("/chart?token=pan-zoom"); await expect(page.getByRole("heading", { name: "조직 현황" })).toBeVisible(); const viewport = page.locator('[data-testid="orgchart-viewport"]'); const canvas = page.locator('[data-testid="orgchart-canvas"]'); const svg = page.locator('[data-testid="orgchart-vector-svg"]'); await expect(viewport).toBeVisible(); await expect(canvas).toBeVisible(); await expect(svg).toBeVisible(); await expect .poll(async () => viewport.evaluate((element) => { const style = window.getComputedStyle(element); return `${style.overflowX}/${style.overflowY}`; }), ) .toBe("hidden/hidden"); const initialViewBox = await svg.getAttribute("viewBox"); const box = await viewport.boundingBox(); expect(box).not.toBeNull(); if (!box) return; await page.mouse.move(box.x + 24, box.y + box.height - 24); await page.mouse.down(); await page.mouse.move(box.x + 164, box.y + box.height - 104); await page.mouse.up(); await expect .poll(async () => svg.getAttribute("viewBox")) .not.toBe(initialViewBox); const afterDragViewBox = await svg.getAttribute("viewBox"); await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); await page.mouse.wheel(0, -500); await expect .poll(async () => svg.getAttribute("viewBox")) .not.toBe(afterDragViewBox); const scale = await svg.evaluate((element) => Number.parseFloat(element.getAttribute("data-scale") ?? "1"), ); expect(scale).toBeGreaterThan(1); });