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( {ui} , ); } 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( } /> , "/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( } /> , "/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( } /> , "/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( } /> , "/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( } /> , "/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, }); }); }); });