1
0
forked from baron/baron-sso
Files
baron-sso/adminfront/src/features/coverage/adminTenantTabs.test.tsx

277 lines
8.0 KiB
TypeScript

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import type React from "react";
import { MemoryRouter, Route, Routes } from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createI18nMock } from "../../test/i18nMock";
import { TenantAdminsAndOwnersTab } from "../tenants/routes/TenantAdminsAndOwnersTab";
import { TenantFineGrainedPermissionsTab } from "../tenants/routes/TenantFineGrainedPermissionsTab";
import TenantUserGroupsTab from "../user-groups/routes/TenantUserGroupsTab";
const exportUsersCSVMock = vi.hoisted(() =>
vi.fn(async () => ({
blob: new Blob(["email,name\nmember@example.com,Member User\n"], {
type: "text/csv",
}),
filename: "users_export_20260609.csv",
})),
);
const bulkUpdateUsersMock = vi.hoisted(() => vi.fn(async () => ({ results: [] })));
const tenants = [
{
id: "tenant-root",
type: "COMPANY_GROUP",
name: "한맥 가족",
slug: "hanmac-family",
description: "",
status: "active",
memberCount: 0,
createdAt: "2026-05-01T00:00:00Z",
updatedAt: "2026-05-01T00:00:00Z",
},
{
id: "tenant-company",
type: "COMPANY",
parentId: "tenant-root",
name: "GPDTDC",
slug: "gpdtdc",
description: "",
status: "active",
memberCount: 2,
createdAt: "2026-05-01T00:00:00Z",
updatedAt: "2026-05-01T00:00:00Z",
},
{
id: "tenant-leaf",
type: "ORGANIZATION",
parentId: "tenant-company",
name: "기술연구팀",
slug: "gpdtdc-rnd",
description: "",
status: "active",
memberCount: 1,
createdAt: "2026-05-01T00:00:00Z",
updatedAt: "2026-05-01T00:00:00Z",
},
];
const users = [
{
id: "user-owner",
name: "Owner User",
email: "owner@example.com",
role: "super_admin",
status: "active",
},
{
id: "user-admin",
name: "Admin User",
email: "admin@example.com",
role: "tenant_admin",
status: "active",
},
{
id: "user-member",
name: "Member User",
email: "member@example.com",
role: "user",
status: "active",
tenantSlug: "gpdtdc-rnd",
tenant: tenants[2],
},
];
vi.mock("../../lib/i18n", () => createI18nMock());
vi.mock("react-oidc-context", () => ({
useAuth: () => ({
user: {
profile: {
sub: "admin-1",
},
},
}),
}));
vi.mock("../../lib/adminApi", () => ({
fetchMe: vi.fn(async () => users[0]),
fetchTenant: vi.fn(async (tenantId) => ({
id: tenantId,
name: "Test Tenant",
slug: "test-tenant",
userPermissions: { view: true, manage: true, manage_admins: true },
})),
fetchTenantOwners: vi.fn(async () => [users[0]]),
fetchTenantAdmins: vi.fn(async () => [users[1]]),
addTenantOwner: vi.fn(async () => undefined),
addTenantAdmin: vi.fn(async () => undefined),
removeTenantOwner: vi.fn(async () => undefined),
removeTenantAdmin: vi.fn(async () => undefined),
fetchTenantRelations: vi.fn(async () => [
{
userId: "user-relation-1",
name: "Relation User",
email: "relation@example.com",
relations: ["profile_managers", "schema_viewers"],
},
]),
addTenantRelation: vi.fn(async () => undefined),
removeTenantRelation: vi.fn(async () => undefined),
fetchUsers: vi.fn(async () => ({
items: users,
total: users.length,
})),
fetchAllTenants: vi.fn(async () => ({
items: tenants,
total: tenants.length,
})),
updateTenant: vi.fn(async () => tenants[2]),
updateUser: vi.fn(async () => users[2]),
bulkUpdateUsers: bulkUpdateUsersMock,
exportTenantsCSV: vi.fn(async () => ({
blob: new Blob(["name,slug"]),
filename: "tenants.csv",
})),
exportUsersCSV: exportUsersCSVMock,
}));
function renderWithProviders(ui: React.ReactElement, entry: string) {
const queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false },
},
});
return render(
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={[entry]}>{ui}</MemoryRouter>
</QueryClientProvider>,
);
}
describe("admin tenant tab coverage smoke", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(window, "confirm").mockReturnValue(true);
vi.spyOn(window.URL, "createObjectURL").mockReturnValue(
"blob:tenant-users-export",
);
vi.spyOn(window.URL, "revokeObjectURL").mockImplementation(() => {});
});
it("renders tenant owners and admins lists", async () => {
renderWithProviders(
<Routes>
<Route
path="/tenants/:tenantId/permissions"
element={<TenantAdminsAndOwnersTab />}
/>
</Routes>,
"/tenants/tenant-company/permissions",
);
expect(await screen.findByText("Owner User")).toBeInTheDocument();
expect(screen.getByText("Admin User")).toBeInTheDocument();
expect(screen.getByText("owner@example.com")).toBeInTheDocument();
expect(screen.getByText("admin@example.com")).toBeInTheDocument();
});
it("renders tenant fine-grained relations list", async () => {
renderWithProviders(
<Routes>
<Route
path="/tenants/:tenantId/relations"
element={<TenantFineGrainedPermissionsTab />}
/>
</Routes>,
"/tenants/tenant-company/relations",
);
expect(await screen.findByText("Relation User")).toBeInTheDocument();
expect(screen.getByText("relation@example.com")).toBeInTheDocument();
expect(screen.getByText("세부 권한 설정 (Fine-grained Permissions)")).toBeInTheDocument();
});
it("renders tenant hierarchy and selected organization members", async () => {
renderWithProviders(
<Routes>
<Route
path="/tenants/:tenantId/organization"
element={<TenantUserGroupsTab />}
/>
</Routes>,
"/tenants/tenant-company/organization",
);
expect((await screen.findAllByText("GPDTDC")).length).toBeGreaterThan(0);
expect(screen.getAllByText("기술연구팀").length).toBeGreaterThan(0);
expect(await screen.findByText("Member User")).toBeInTheDocument();
});
it("exports selected organization users by tenant slug", async () => {
renderWithProviders(
<Routes>
<Route
path="/tenants/:tenantId/organization"
element={<TenantUserGroupsTab />}
/>
</Routes>,
"/tenants/tenant-company/organization",
);
expect(await screen.findByText("Member User")).toBeInTheDocument();
fireEvent.click(screen.getByTestId("tenant-current-users-export-btn"));
await waitFor(() => {
expect(exportUsersCSVMock).toHaveBeenCalledWith("", "gpdtdc", false);
});
});
it("queues searched users and bulk adds them to the selected organization", async () => {
renderWithProviders(
<Routes>
<Route
path="/tenants/:tenantId/organization"
element={<TenantUserGroupsTab />}
/>
</Routes>,
"/tenants/tenant-company/organization",
);
expect(await screen.findByText("Member User")).toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: /멤버 추가/ }));
fireEvent.change(screen.getByTestId("tenant-org-member-search-input"), {
target: { value: "user" },
});
fireEvent.click(screen.getByTestId("tenant-org-member-search-btn"));
fireEvent.click(
await screen.findByTestId("tenant-org-member-search-result-user-owner"),
);
fireEvent.click(
await screen.findByTestId("tenant-org-member-search-result-user-admin"),
);
expect(screen.getByTestId("tenant-org-member-add-queue")).toHaveTextContent(
"Owner User",
);
expect(screen.getByTestId("tenant-org-member-add-queue")).toHaveTextContent(
"Admin User",
);
fireEvent.click(screen.getByTestId("tenant-org-member-add-submit-btn"));
await waitFor(() => {
expect(bulkUpdateUsersMock).toHaveBeenCalledWith({
userIds: ["user-owner", "user-admin"],
tenantSlug: "gpdtdc",
isAddTenant: true,
});
});
});
});