Files
BaronSSO/baron-sso/adminfront/src/features/users/UserDetailPage.employeeNumber.test.tsx

212 lines
6.2 KiB
TypeScript

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",
})),
fetchGlobalCustomClaimDefinitions: vi.fn(async () => ({
items: [
{
key: "contract_date",
label: "계약일",
valueType: "date",
readPermission: "admin_only",
writePermission: "admin_only",
description: "",
},
],
})),
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",
},
global_custom_claims: {
contract_date: "2026-06-09",
},
},
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(
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={["/users/user-1"]}>
<Routes>
<Route path="/users/:id" element={<UserDetailPage />} />
</Routes>
</MemoryRouter>
</QueryClientProvider>,
);
}
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");
});
it("only allows editing per-user values for globally defined custom claims", async () => {
renderUserDetailPage();
const tab = await screen.findByTestId("global-custom-claim-tab");
fireEvent.click(tab);
expect(
screen.queryByRole("button", { name: "추가" }),
).not.toBeInTheDocument();
const valueInput = await screen.findByTestId(
"global-custom-claim-value-contract_date",
);
expect(screen.getByText("contract_date")).toBeInTheDocument();
expect(valueInput).toHaveValue("2026-06-09");
expect(valueInput).toHaveAttribute("type", "date");
fireEvent.change(valueInput, { target: { value: "2026-07-01" } });
fireEvent.click(
screen.getByRole("button", { name: /사용자 Claim 값 저장/ }),
);
await waitFor(() => expect(updateUserMock).toHaveBeenCalled());
expect(updateUserMock).toHaveBeenCalledWith(
"user-1",
expect.objectContaining({
metadata: expect.objectContaining({
global_custom_claims: expect.objectContaining({
contract_date: "2026-07-01",
}),
global_custom_claim_permissions: expect.objectContaining({
contract_date: {
readPermission: "admin_only",
writePermission: "admin_only",
},
}),
}),
}),
);
});
});