import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { MemoryRouter, Route, Routes } from "react-router-dom"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { createI18nMock } from "../../test/i18nMock"; import UserDetailPage from "./UserDetailPage"; const updateUserMock = vi.hoisted(() => vi.fn()); const profileRoleMock = vi.hoisted(() => ({ role: "super_admin" })); vi.mock("../../lib/i18n", () => createI18nMock()); vi.mock("../../lib/adminApi", () => ({ deleteUser: vi.fn(), fetchAllTenants: vi.fn(async () => ({ items: [ { id: "tenant-hanmac", type: "COMPANY", name: "한맥기술", slug: "hanmac", description: "", status: "active", memberCount: 1, createdAt: "2026-06-01T00:00:00Z", updatedAt: "2026-06-01T00:00:00Z", }, ], total: 1, })), fetchMe: vi.fn(async () => ({ id: "admin-user", role: profileRoleMock.role, name: "Admin", email: "admin@example.com", })), fetchPasswordPolicy: vi.fn(async () => ({ minLength: 12 })), fetchTenant: vi.fn(), fetchUser: vi.fn(async () => ({ id: "user-1", email: "user@example.com", name: "사용자", phone: "01012345678", role: "user", status: "active", tenantSlug: "hanmac", tenant: { id: "tenant-hanmac", type: "COMPANY", name: "한맥기술", slug: "hanmac", description: "", status: "active", memberCount: 1, createdAt: "2026-06-01T00:00:00Z", updatedAt: "2026-06-01T00:00:00Z", }, joinedTenants: [], metadata: { employee_id: { "0": "h", "1": "j", "2": "k", "3": "w", "4": "o", "5": "n", }, }, createdAt: "2026-06-01T00:00:00Z", updatedAt: "2026-06-01T00:00:00Z", })), fetchUserRpHistory: vi.fn(async () => []), updateUser: updateUserMock, })); function renderUserDetailPage() { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } }, }); return render( } /> , ); } describe("UserDetailPage Worksmobile employee number", () => { beforeEach(() => { updateUserMock.mockReset(); updateUserMock.mockResolvedValue({}); profileRoleMock.role = "super_admin"; }); it("shows and saves metadata employee_id from the user edit form", async () => { renderUserDetailPage(); const employeeInput = await screen.findByLabelText("사번"); expect(employeeInput).toHaveValue("hjkwon"); fireEvent.change(employeeInput, { target: { value: "EMP001" } }); fireEvent.click(screen.getByRole("button", { name: /저장하기/ })); await waitFor(() => expect(updateUserMock).toHaveBeenCalled()); expect(updateUserMock).toHaveBeenCalledWith( "user-1", expect.objectContaining({ metadata: expect.objectContaining({ employee_id: "EMP001" }), }), ); }); it("allows super admin to save a changed email", async () => { renderUserDetailPage(); const emailInput = await screen.findByLabelText("이메일"); fireEvent.change(emailInput, { target: { value: "changed@example.com" } }); fireEvent.click(screen.getByRole("button", { name: /저장하기/ })); await waitFor(() => expect(updateUserMock).toHaveBeenCalled()); expect(updateUserMock).toHaveBeenCalledWith( "user-1", expect.objectContaining({ email: "changed@example.com", }), ); }); it("shows forbidden message for non-super admin", async () => { profileRoleMock.role = "tenant_admin"; renderUserDetailPage(); expect( await screen.findByText("이 작업을 수행할 권한이 없습니다."), ).toBeInTheDocument(); }); it("removes metadata employee_id when the field is cleared", async () => { renderUserDetailPage(); const employeeInput = await screen.findByLabelText("사번"); fireEvent.change(employeeInput, { target: { value: "" } }); fireEvent.click(screen.getByRole("button", { name: /저장하기/ })); await waitFor(() => expect(updateUserMock).toHaveBeenCalled()); const payload = updateUserMock.mock.calls[0][1]; expect(payload.metadata).not.toHaveProperty("employee_id"); }); });