forked from baron/baron-sso
테넌트 목록 조회 cursor기반으로 재구성. 사용자 metadata 미사용 필드 제거
This commit is contained in:
@@ -37,6 +37,15 @@ test.describe("Bulk Actions and Tree Search", () => {
|
||||
headers,
|
||||
});
|
||||
}
|
||||
if (
|
||||
url.includes("/admin/users/bulk") &&
|
||||
route.request().method() === "PUT"
|
||||
) {
|
||||
return route.fulfill({
|
||||
json: { results: [{ id: "u-1", success: true }] },
|
||||
headers,
|
||||
});
|
||||
}
|
||||
if (url.includes("/admin/users")) {
|
||||
return route.fulfill({
|
||||
json: {
|
||||
@@ -149,6 +158,41 @@ test.describe("Bulk Actions and Tree Search", () => {
|
||||
await expect(selectionBar).not.toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
|
||||
test("should let super admins promote selected users to super admin", async ({
|
||||
page,
|
||||
}) => {
|
||||
let capturedPayload: unknown = null;
|
||||
await page.route("**/api/v1/admin/users/bulk", async (route) => {
|
||||
if (route.request().method() === "PUT") {
|
||||
capturedPayload = route.request().postDataJSON();
|
||||
return route.fulfill({
|
||||
json: { results: [{ id: "u-1", success: true }] },
|
||||
headers: { "Access-Control-Allow-Origin": "*" },
|
||||
});
|
||||
}
|
||||
return route.fallback();
|
||||
});
|
||||
|
||||
await page.goto("/users");
|
||||
await expect(page.locator("table")).toContainText("User One", {
|
||||
timeout: 20000,
|
||||
});
|
||||
|
||||
await page.locator('table input[type="checkbox"]').nth(1).click();
|
||||
const selectionBar = page.getByTestId("bulk-action-bar");
|
||||
await expect(selectionBar).toBeVisible({ timeout: 15000 });
|
||||
|
||||
await page.getByTestId("bulk-promote-super-admin-btn").click();
|
||||
|
||||
await expect
|
||||
.poll(() => capturedPayload)
|
||||
.toEqual({
|
||||
userIds: ["u-1"],
|
||||
role: "super_admin",
|
||||
});
|
||||
await expect(selectionBar).not.toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
|
||||
test("should filter and highlight nodes in organization tree", async ({
|
||||
page,
|
||||
}) => {
|
||||
|
||||
@@ -105,6 +105,197 @@ test.describe("Tenants Management", () => {
|
||||
expect(headerWhiteSpace.every((value) => value === "nowrap")).toBe(true);
|
||||
});
|
||||
|
||||
test("should virtualize large tenant lists and load next pages automatically", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 900, height: 700 });
|
||||
let requestCount = 0;
|
||||
|
||||
await page.route("**/api/v1/admin/tenants**", async (route) => {
|
||||
if (route.request().method() !== "GET") {
|
||||
return route.continue();
|
||||
}
|
||||
const url = new URL(route.request().url());
|
||||
const cursor = url.searchParams.get("cursor");
|
||||
requestCount += 1;
|
||||
|
||||
if (!cursor) {
|
||||
return route.fulfill({
|
||||
json: {
|
||||
items: Array.from({ length: 500 }, (_, index) => ({
|
||||
id: `tenant-${String(index + 1).padStart(3, "0")}`,
|
||||
name: `Tenant ${String(index + 1).padStart(3, "0")}`,
|
||||
slug: `tenant-${String(index + 1).padStart(3, "0")}`,
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
})),
|
||||
total: 501,
|
||||
limit: 500,
|
||||
offset: 0,
|
||||
nextCursor: "next-page",
|
||||
},
|
||||
headers: { "Access-Control-Allow-Origin": "*" },
|
||||
});
|
||||
}
|
||||
|
||||
return route.fulfill({
|
||||
json: {
|
||||
items: [
|
||||
{
|
||||
id: "tenant-501",
|
||||
name: "Tenant 501",
|
||||
slug: "tenant-501",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
],
|
||||
total: 501,
|
||||
limit: 500,
|
||||
offset: 0,
|
||||
},
|
||||
headers: { "Access-Control-Allow-Origin": "*" },
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/tenants");
|
||||
|
||||
await expect(page.getByText("총 501개 테넌트")).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "더 불러오기" })).toHaveCount(
|
||||
0,
|
||||
);
|
||||
|
||||
await expect
|
||||
.poll(async () => page.locator("tbody tr").count())
|
||||
.toBeLessThan(80);
|
||||
|
||||
const tableScroller = page.getByTestId("tenant-table-scroll");
|
||||
await tableScroller.evaluate((element) => {
|
||||
element.scrollTop = element.scrollHeight;
|
||||
element.dispatchEvent(new Event("scroll", { bubbles: true }));
|
||||
});
|
||||
|
||||
await expect.poll(() => requestCount).toBe(2);
|
||||
await tableScroller.evaluate((element) => {
|
||||
element.scrollTop = element.scrollHeight;
|
||||
element.dispatchEvent(new Event("scroll", { bubbles: true }));
|
||||
});
|
||||
await expect(page.getByText("Tenant 501")).toBeVisible();
|
||||
expect(requestCount).toBe(2);
|
||||
});
|
||||
|
||||
test("should hide Hanmac family subtree from external tenant admins", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.route(/.*\/api\/v1\/user\/me$/, async (route) => {
|
||||
return route.fulfill({
|
||||
json: {
|
||||
id: "external-admin",
|
||||
name: "External Admin",
|
||||
role: "tenant_admin",
|
||||
tenantId: "external-tenant-id",
|
||||
tenantSlug: "external-tenant",
|
||||
tenant: {
|
||||
id: "external-tenant-id",
|
||||
slug: "external-tenant",
|
||||
name: "External Tenant",
|
||||
type: "COMPANY",
|
||||
},
|
||||
manageableTenants: [
|
||||
{
|
||||
id: "external-tenant-id",
|
||||
slug: "external-tenant",
|
||||
name: "External Tenant",
|
||||
type: "COMPANY",
|
||||
},
|
||||
{
|
||||
id: "external-team-id",
|
||||
slug: "external-team",
|
||||
name: "External Team",
|
||||
type: "USER_GROUP",
|
||||
parentId: "external-tenant-id",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/v1/admin/tenants**", async (route) => {
|
||||
if (route.request().method() !== "GET") {
|
||||
await route.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
json: {
|
||||
items: [
|
||||
{
|
||||
id: "hanmac-family-id",
|
||||
slug: "hanmac-family",
|
||||
name: "한맥가족",
|
||||
status: "active",
|
||||
type: "COMPANY_GROUP",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "hanmac-company-id",
|
||||
slug: "hanmac-company",
|
||||
name: "한맥기술",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
parentId: "hanmac-family-id",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "hanmac-team-id",
|
||||
slug: "hanmac-team",
|
||||
name: "한맥팀",
|
||||
status: "active",
|
||||
type: "USER_GROUP",
|
||||
parentId: "hanmac-company-id",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "external-tenant-id",
|
||||
slug: "external-tenant",
|
||||
name: "External Tenant",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "external-team-id",
|
||||
slug: "external-team",
|
||||
name: "External Team",
|
||||
status: "active",
|
||||
type: "USER_GROUP",
|
||||
parentId: "external-tenant-id",
|
||||
memberCount: 0,
|
||||
},
|
||||
],
|
||||
total: 5,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
headers: { "Access-Control-Allow-Origin": "*" },
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/tenants");
|
||||
await expect(page.locator("h2").last()).toContainText(
|
||||
/테넌트 목록|Tenants/i,
|
||||
{ timeout: 20000 },
|
||||
);
|
||||
await expect(page.locator("table")).toContainText("External Tenant");
|
||||
await expect(page.locator("table")).toContainText("External Team");
|
||||
await expect(page.locator("table")).not.toContainText("한맥가족");
|
||||
await expect(page.locator("table")).not.toContainText("한맥기술");
|
||||
await expect(page.locator("table")).not.toContainText("한맥팀");
|
||||
});
|
||||
|
||||
test("should create a new tenant", async ({ page }) => {
|
||||
await page.goto("/tenants/new");
|
||||
await expect(page.locator("h2").last()).toContainText(/추가|Create/i, {
|
||||
|
||||
@@ -362,8 +362,8 @@ test.describe("User Management", () => {
|
||||
|
||||
// Ensure the page title is loaded
|
||||
await expect(page.getByText(/사용자 추가/i).first()).toBeVisible();
|
||||
const userTypeTabs = page.getByRole("tab");
|
||||
await expect(userTypeTabs).toHaveText([
|
||||
const categoryTabs = page.getByRole("tab");
|
||||
await expect(categoryTabs).toHaveText([
|
||||
"한맥가족 구성원",
|
||||
"외부 기업 회원",
|
||||
"개인 회원",
|
||||
@@ -579,7 +579,6 @@ test.describe("User Management", () => {
|
||||
.poll(() => createPayload)
|
||||
.toMatchObject({
|
||||
metadata: {
|
||||
hanmacFamily: true,
|
||||
additionalAppointments: [
|
||||
{
|
||||
tenantId: "03dbe16b-e47b-4f72-927b-782807d67a35",
|
||||
@@ -623,7 +622,7 @@ test.describe("User Management", () => {
|
||||
expect(tenantOptionValues).not.toContain("tech-planning");
|
||||
});
|
||||
|
||||
test("should create a personal user and provision Personal tenant when missing", async ({
|
||||
test("should create a personal user with the fixed global Personal tenant when it is not listed", async ({
|
||||
page,
|
||||
}) => {
|
||||
let tenantPayload: Record<string, unknown> | undefined;
|
||||
@@ -671,14 +670,14 @@ test.describe("User Management", () => {
|
||||
await page.locator('input[name="email"]').fill("personal@test.com");
|
||||
await page.getByRole("button", { name: /생성/i }).click();
|
||||
|
||||
await expect
|
||||
.poll(() => tenantPayload)
|
||||
.toMatchObject({ name: "Personal", slug: "personal", type: "PERSONAL" });
|
||||
expect(tenantPayload).toBeUndefined();
|
||||
await expect
|
||||
.poll(() => createPayload)
|
||||
.toMatchObject({
|
||||
tenantSlug: "personal",
|
||||
metadata: { userType: "personal", hanmacFamily: false },
|
||||
metadata: {
|
||||
personalTenantId: "9607eb7b-04d2-42ab-80fe-780fe21c7e8f",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -699,7 +698,6 @@ test.describe("User Management", () => {
|
||||
createdAt: "2026-04-01T00:00:00Z",
|
||||
updatedAt: "2026-04-01T00:00:00Z",
|
||||
metadata: {
|
||||
hanmacFamily: true,
|
||||
additionalAppointments: [
|
||||
{
|
||||
tenantId: "03dbe16b-e47b-4f72-927b-782807d67a35",
|
||||
@@ -763,7 +761,6 @@ test.describe("User Management", () => {
|
||||
createdAt: "2026-04-01T00:00:00Z",
|
||||
updatedAt: "2026-04-01T00:00:00Z",
|
||||
metadata: {
|
||||
hanmacFamily: true,
|
||||
additionalAppointments: [
|
||||
{
|
||||
tenantId: "03dbe16b-e47b-4f72-927b-782807d67a35",
|
||||
|
||||
Reference in New Issue
Block a user