forked from baron/baron-sso
feat: integrate orgfront and expose internal ids
This commit is contained in:
379
orgfront/tests/orgchart-vector-render.spec.ts
Normal file
379
orgfront/tests/orgchart-vector-render.spec.ts
Normal file
@@ -0,0 +1,379 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
function tenant(
|
||||
id: string,
|
||||
name: string,
|
||||
slug: string,
|
||||
parentId?: string,
|
||||
type?: string,
|
||||
) {
|
||||
return {
|
||||
id,
|
||||
type: type ?? (parentId ? "USER_GROUP" : "COMPANY_GROUP"),
|
||||
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",
|
||||
};
|
||||
}
|
||||
|
||||
function multiTenantUser(
|
||||
id: string,
|
||||
name: string,
|
||||
companyCode: string,
|
||||
joinedTenants: Array<ReturnType<typeof tenant>>,
|
||||
) {
|
||||
return {
|
||||
...user(id, name, companyCode),
|
||||
joinedTenants,
|
||||
};
|
||||
}
|
||||
|
||||
function hanmacUser(id: string, name: string, companyCode: string) {
|
||||
return {
|
||||
...user(id, name, companyCode),
|
||||
email: `${id}@hanmac.kr`,
|
||||
};
|
||||
}
|
||||
|
||||
test("org chart uses svg viewBox zoom for sharp vector rendering", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.route("**/api/v1/public/orgchart**", async (route) => {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
sharedWith: "Playwright",
|
||||
tenants: [
|
||||
tenant("group", "HMAC Group", "hmac"),
|
||||
tenant("baron", "Baron", "baron", "group"),
|
||||
tenant("engineering", "Engineering", "engineering", "baron"),
|
||||
tenant("platform", "Platform", "platform", "engineering"),
|
||||
],
|
||||
users: [
|
||||
user("u-group", "Group User", "hmac"),
|
||||
user("u-baron", "Baron User", "baron"),
|
||||
user("u-eng", "Engineering User", "engineering"),
|
||||
user("u-platform", "Platform User", "platform"),
|
||||
],
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/chart?token=vector");
|
||||
await expect(page.getByRole("heading", { name: "조직 현황" })).toBeVisible();
|
||||
|
||||
const viewport = page.locator('[data-testid="orgchart-viewport"]');
|
||||
const svg = page.locator('[data-testid="orgchart-vector-svg"]');
|
||||
await expect(svg).toBeVisible();
|
||||
await expect(
|
||||
svg.locator("text", { hasText: "Engineering User" }),
|
||||
).toBeVisible();
|
||||
|
||||
const initialViewBox = await svg.getAttribute("viewBox");
|
||||
const transform = await page
|
||||
.locator('[data-testid="orgchart-canvas"]')
|
||||
.evaluate((element) => window.getComputedStyle(element).transform)
|
||||
.catch(() => "none");
|
||||
expect(transform).toBe("none");
|
||||
|
||||
const box = await viewport.boundingBox();
|
||||
expect(box).not.toBeNull();
|
||||
if (!box) return;
|
||||
|
||||
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(initialViewBox);
|
||||
});
|
||||
|
||||
test("org chart filters by Hanmac family and company while excluding hanmac.kr accounts", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.route("**/api/v1/public/orgchart**", async (route) => {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
sharedWith: "Playwright",
|
||||
tenants: [
|
||||
tenant("group", "HMAC Group", "hmac"),
|
||||
tenant("baron", "Baron", "baron", "group", "COMPANY"),
|
||||
tenant("hanmac", "Hanmac", "hanmac", "group", "COMPANY"),
|
||||
tenant("engineering", "Engineering", "engineering", "baron"),
|
||||
tenant("sales", "Sales", "sales", "hanmac"),
|
||||
],
|
||||
users: [
|
||||
user("u-group", "Group User", "hmac"),
|
||||
user("u-baron", "Baron User", "baron"),
|
||||
user("u-eng", "Engineering User", "engineering"),
|
||||
user("u-sales", "Sales User", "sales"),
|
||||
hanmacUser("hidden", "Hidden Hanmac User", "engineering"),
|
||||
],
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/chart?token=family");
|
||||
|
||||
await expect(page.getByRole("button", { name: "한맥가족" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Baron" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Hanmac" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "전체" })).toHaveCount(0);
|
||||
await expect(page.getByText("총 4명")).toBeVisible();
|
||||
|
||||
const svg = page.locator('[data-testid="orgchart-vector-svg"]');
|
||||
await expect(
|
||||
svg.locator("text", { hasText: "Hidden Hanmac User" }),
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
svg.locator("text", { hasText: "Engineering User" }),
|
||||
).toBeVisible();
|
||||
await expect(svg.locator("text", { hasText: "Sales User" })).toBeVisible();
|
||||
|
||||
await page.getByRole("button", { name: "Baron" }).click();
|
||||
await expect(page.getByText("총 2명")).toBeVisible();
|
||||
await expect(page.getByText("총 4명")).toHaveCount(0);
|
||||
await expect(
|
||||
svg.locator("text", { hasText: "Engineering User" }),
|
||||
).toBeVisible();
|
||||
await expect(svg.locator("text", { hasText: "Sales User" })).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("org chart displays user names with job title and position", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.route("**/api/v1/public/orgchart**", async (route) => {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
sharedWith: "Playwright",
|
||||
tenants: [
|
||||
tenant("group", "HMAC Group", "hmac"),
|
||||
tenant("baron", "Baron", "baron", "group", "COMPANY"),
|
||||
tenant("engineering", "Engineering", "engineering", "baron"),
|
||||
],
|
||||
users: [
|
||||
{
|
||||
...user("u-eng", "Engineering User", "engineering"),
|
||||
jobTitle: "Platform Engineer",
|
||||
position: "책임",
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/chart?token=display-name");
|
||||
|
||||
const svg = page.locator('[data-testid="orgchart-vector-svg"]');
|
||||
await expect(
|
||||
svg.locator("text", {
|
||||
hasText: "Engineering User(Platform Engineer) 책임",
|
||||
}),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("org chart places multi-tenant users only on leaf memberships without duplicate rendering", async ({
|
||||
page,
|
||||
}) => {
|
||||
const issuedAt = Math.floor(Date.now() / 1000);
|
||||
await page.addInitScript(
|
||||
({ issuedAt: seededIssuedAt }) => {
|
||||
const mockOidcUser = {
|
||||
id_token: "playwright-id-token",
|
||||
session_state: "playwright-session",
|
||||
access_token: "playwright-access-token",
|
||||
refresh_token: "playwright-refresh-token",
|
||||
token_type: "Bearer",
|
||||
scope: "openid profile email",
|
||||
profile: {
|
||||
sub: "playwright-user",
|
||||
email: "playwright@example.com",
|
||||
name: "Playwright User",
|
||||
role: "tenant_admin",
|
||||
},
|
||||
expires_at: seededIssuedAt + 3600,
|
||||
};
|
||||
|
||||
window.localStorage.setItem(
|
||||
"oidc.user:http://localhost:5000/oidc:orgfront",
|
||||
JSON.stringify(mockOidcUser),
|
||||
);
|
||||
window.localStorage.setItem(
|
||||
"oidc.user:http://localhost:5000/oidc/:orgfront",
|
||||
JSON.stringify(mockOidcUser),
|
||||
);
|
||||
window.localStorage.setItem(
|
||||
"oidc.user:http://localhost:5000/oidc:devfront",
|
||||
JSON.stringify(mockOidcUser),
|
||||
);
|
||||
window.localStorage.setItem(
|
||||
"oidc.user:http://localhost:5000/oidc/:devfront",
|
||||
JSON.stringify(mockOidcUser),
|
||||
);
|
||||
window.localStorage.setItem(
|
||||
"oidc.user:http://172.16.9.189:5000/oidc:orgfront",
|
||||
JSON.stringify(mockOidcUser),
|
||||
);
|
||||
window.localStorage.setItem(
|
||||
"oidc.user:http://172.16.9.189:5000/oidc/:orgfront",
|
||||
JSON.stringify(mockOidcUser),
|
||||
);
|
||||
window.localStorage.setItem("dev_tenant_id", "group");
|
||||
},
|
||||
{ issuedAt },
|
||||
);
|
||||
|
||||
await page.route("**/oidc/**", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ keys: [] }),
|
||||
});
|
||||
});
|
||||
|
||||
const tenants = [
|
||||
tenant("group", "HMAC Group", "hmac"),
|
||||
tenant("baron", "Baron", "baron", "group", "COMPANY"),
|
||||
tenant("engineering", "Engineering", "engineering", "baron"),
|
||||
tenant("platform", "Platform", "platform", "engineering"),
|
||||
];
|
||||
const [groupTenant, baronTenant, engineeringTenant, platformTenant] = tenants;
|
||||
|
||||
await page.route("**/api/v1/admin/tenants**", async (route) => {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
items: tenants,
|
||||
total: tenants.length,
|
||||
limit: 10000,
|
||||
offset: 0,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/v1/admin/users**", async (route) => {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
items: [
|
||||
multiTenantUser("u-shared", "Shared User", "baron", [
|
||||
groupTenant,
|
||||
baronTenant,
|
||||
engineeringTenant,
|
||||
platformTenant,
|
||||
]),
|
||||
],
|
||||
total: 1,
|
||||
limit: 5000,
|
||||
offset: 0,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/chart");
|
||||
|
||||
await expect(page.getByText("총 1명")).toBeVisible();
|
||||
const svg = page.locator('[data-testid="orgchart-vector-svg"]');
|
||||
await expect(svg).toBeVisible();
|
||||
await expect(svg.locator("text", { hasText: "Shared User" })).toHaveCount(1);
|
||||
await expect(svg.locator("text").filter({ hasText: /^1$/ })).toHaveCount(4);
|
||||
});
|
||||
|
||||
test("org chart counts multi-leaf tenant users once in ancestor totals", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.route("**/api/v1/public/orgchart**", async (route) => {
|
||||
const tenants = [
|
||||
tenant("group", "HMAC Group", "hmac"),
|
||||
tenant("baron", "Baron", "baron", "group", "COMPANY"),
|
||||
tenant("engineering", "Engineering", "engineering", "baron"),
|
||||
tenant("platform", "Platform", "platform", "engineering"),
|
||||
tenant("security", "Security", "security", "engineering"),
|
||||
];
|
||||
const [groupTenant, baronTenant, engineeringTenant, platformTenant] =
|
||||
tenants;
|
||||
const securityTenant = tenants[4];
|
||||
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
sharedWith: "Playwright",
|
||||
tenants,
|
||||
users: [
|
||||
multiTenantUser("u-shared", "Shared User", "baron", [
|
||||
groupTenant,
|
||||
baronTenant,
|
||||
engineeringTenant,
|
||||
platformTenant,
|
||||
securityTenant,
|
||||
]),
|
||||
],
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/chart?token=multi-leaf-count");
|
||||
|
||||
await expect(page.getByText("총 1명")).toBeVisible();
|
||||
const svg = page.locator('[data-testid="orgchart-vector-svg"]');
|
||||
await expect(svg).toBeVisible();
|
||||
await expect(svg.locator("text", { hasText: "Shared User" })).toHaveCount(2);
|
||||
await expect(svg.locator("text").filter({ hasText: /^1$/ })).toHaveCount(5);
|
||||
});
|
||||
|
||||
test("org chart hides system global tenant members", async ({ page }) => {
|
||||
await page.route("**/api/v1/public/orgchart**", async (route) => {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
sharedWith: "Playwright",
|
||||
tenants: [
|
||||
tenant("system", "시스템 전역", "system", undefined, "SYSTEM"),
|
||||
tenant("group", "HMAC Group", "hmac"),
|
||||
tenant("baron", "Baron", "baron", "group", "COMPANY"),
|
||||
],
|
||||
users: [
|
||||
user("u-global", "Global Admin", "system"),
|
||||
{
|
||||
...multiTenantUser("u-system-admin", "System Admin", "system", [
|
||||
tenant("baron", "Baron", "baron", "group", "COMPANY"),
|
||||
]),
|
||||
role: "super_admin",
|
||||
},
|
||||
user("u-baron", "Baron User", "baron"),
|
||||
],
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/chart?token=hide-system-global");
|
||||
|
||||
await expect(page.getByText("총 1명")).toBeVisible();
|
||||
const svg = page.locator('[data-testid="orgchart-vector-svg"]');
|
||||
await expect(svg).toBeVisible();
|
||||
await expect(svg.locator("text", { hasText: "시스템 전역" })).toHaveCount(0);
|
||||
await expect(svg.locator("text", { hasText: "Global Admin" })).toHaveCount(0);
|
||||
await expect(svg.locator("text", { hasText: "System Admin" })).toHaveCount(0);
|
||||
await expect(svg.locator("text", { hasText: "Baron User" })).toBeVisible();
|
||||
});
|
||||
Reference in New Issue
Block a user