forked from baron/baron-sso
devfront 테스트 커버리지 추가 보강
This commit is contained in:
148
common/core/components/audit/AuditLogTable.test.tsx
Normal file
148
common/core/components/audit/AuditLogTable.test.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { createRoot, type Root } from "react-dom/client";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { CommonAuditLog } from "../../audit";
|
||||
import { AuditLogTable } from "./AuditLogTable";
|
||||
|
||||
const roots: Root[] = [];
|
||||
|
||||
afterEach(() => {
|
||||
for (const root of roots.splice(0)) {
|
||||
act(() => {
|
||||
root.unmount();
|
||||
});
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
function renderTable(props: Parameters<typeof AuditLogTable>[0]) {
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
const root = createRoot(container);
|
||||
roots.push(root);
|
||||
|
||||
act(() => {
|
||||
root.render(<AuditLogTable {...props} />);
|
||||
});
|
||||
|
||||
return { container };
|
||||
}
|
||||
|
||||
const logs: CommonAuditLog[] = [
|
||||
{
|
||||
event_id: "evt-1",
|
||||
timestamp: "2026-05-28T06:07:18.000Z",
|
||||
user_id: "user-1",
|
||||
event_type: "CLIENT_UPDATE",
|
||||
status: "success",
|
||||
ip_address: "127.0.0.1",
|
||||
user_agent: "Vitest",
|
||||
device_id: "device-1",
|
||||
details: JSON.stringify({
|
||||
request_id: "req-1",
|
||||
method: "POST",
|
||||
path: "/api/v1/clients",
|
||||
latency_ms: 120,
|
||||
tenant_id: "tenant-1",
|
||||
actor_id: "user-1",
|
||||
action: "업데이트",
|
||||
target_id: "client-a",
|
||||
before: { status: "inactive" },
|
||||
after: { status: "active" },
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
describe("AuditLogTable", () => {
|
||||
it("renders loading and empty states", () => {
|
||||
const { container: loadingContainer } = renderTable({
|
||||
logs: [],
|
||||
t: (key, fallback) => fallback ?? key,
|
||||
loading: true,
|
||||
hasNextPage: false,
|
||||
isFetchingNextPage: false,
|
||||
onLoadMore: vi.fn(),
|
||||
});
|
||||
|
||||
expect(loadingContainer.textContent).toContain("Loading audit logs...");
|
||||
|
||||
const { container: emptyContainer } = renderTable({
|
||||
logs: [],
|
||||
t: (key, fallback) => fallback ?? key,
|
||||
loading: false,
|
||||
hasNextPage: false,
|
||||
isFetchingNextPage: false,
|
||||
onLoadMore: vi.fn(),
|
||||
});
|
||||
|
||||
expect(emptyContainer.textContent).toContain("No audit logs found.");
|
||||
expect(emptyContainer.textContent).toContain("End of audit feed");
|
||||
});
|
||||
|
||||
it("renders rows, expands details, copies fields, and loads more", async () => {
|
||||
const writeText = vi.fn().mockResolvedValue(undefined);
|
||||
Object.defineProperty(navigator, "clipboard", {
|
||||
value: { writeText },
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const onLoadMore = vi.fn();
|
||||
const { container } = renderTable({
|
||||
logs,
|
||||
t: (key, fallback, vars) => {
|
||||
let text = fallback ?? key;
|
||||
for (const [name, value] of Object.entries(vars ?? {})) {
|
||||
text = text.replaceAll(`{{${name}}}`, String(value));
|
||||
}
|
||||
return text;
|
||||
},
|
||||
loading: false,
|
||||
hasNextPage: true,
|
||||
isFetchingNextPage: false,
|
||||
onLoadMore,
|
||||
});
|
||||
|
||||
expect(container.textContent).toContain("user-1");
|
||||
expect(container.textContent).toContain("업데이트");
|
||||
expect(container.textContent).toContain("client-a");
|
||||
expect(container.textContent).toContain("success");
|
||||
|
||||
const buttons = Array.from(container.querySelectorAll("button"));
|
||||
const actorCopyButton = buttons.find(
|
||||
(button) => button.getAttribute("aria-label") === "Copy User ID",
|
||||
);
|
||||
const targetCopyButton = buttons.find(
|
||||
(button) => button.getAttribute("aria-label") === "Copy Client ID",
|
||||
);
|
||||
const expandButton = buttons.find(
|
||||
(button) => !button.getAttribute("aria-label") && !button.textContent,
|
||||
);
|
||||
const loadMoreButton = buttons.find(
|
||||
(button) => button.textContent === "Load more",
|
||||
);
|
||||
|
||||
expect(actorCopyButton).toBeTruthy();
|
||||
expect(targetCopyButton).toBeTruthy();
|
||||
expect(expandButton).toBeTruthy();
|
||||
expect(loadMoreButton).toBeTruthy();
|
||||
|
||||
await act(async () => {
|
||||
actorCopyButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
targetCopyButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
expandButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
|
||||
expect(writeText).toHaveBeenCalledWith("user-1");
|
||||
expect(writeText).toHaveBeenCalledWith("client-a");
|
||||
expect(container.textContent).toContain("Request ID · req-1");
|
||||
expect(container.textContent).toContain("Actor");
|
||||
expect(container.textContent).toContain("Result");
|
||||
|
||||
await act(async () => {
|
||||
loadMoreButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
|
||||
expect(onLoadMore).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user