forked from baron/baron-sso
개요/감사로그 CTA 공통화
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { DeveloperAccessRequestCard } from "./DeveloperAccessRequestCard";
|
||||
|
||||
describe("DeveloperAccessRequestCard", () => {
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
it("renders the request CTA for pending and denied states", () => {
|
||||
const onAction = vi.fn();
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
const root = createRoot(container);
|
||||
|
||||
act(() => {
|
||||
root.render(
|
||||
<DeveloperAccessRequestCard
|
||||
title="운영 현황"
|
||||
isPending={true}
|
||||
canRequest={false}
|
||||
pendingMessage="검토 중"
|
||||
deniedMessage="거부됨"
|
||||
pendingDetailMessage="승인 대기"
|
||||
deniedDetailMessage="신청 필요"
|
||||
actionLabel="개발자 권한 신청"
|
||||
onAction={onAction}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(container.querySelector("h2")?.textContent).toBe("운영 현황");
|
||||
expect(container.textContent).toContain("검토 중");
|
||||
expect(container.textContent).toContain("승인 대기");
|
||||
|
||||
const button = container.querySelector("button");
|
||||
expect(button?.textContent).toBe("개발자 권한 신청");
|
||||
|
||||
act(() => {
|
||||
button?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
expect(onAction).toHaveBeenCalledTimes(1);
|
||||
|
||||
act(() => {
|
||||
root.render(
|
||||
<DeveloperAccessRequestCard
|
||||
title="감사 로그"
|
||||
isPending={false}
|
||||
canRequest={true}
|
||||
pendingMessage="검토 중"
|
||||
deniedMessage="거부됨"
|
||||
pendingDetailMessage="승인 대기"
|
||||
deniedDetailMessage="신청 필요"
|
||||
actionLabel="개발자 권한 신청"
|
||||
onAction={onAction}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(container.querySelector("h2")?.textContent).toBe("감사 로그");
|
||||
expect(container.textContent).toContain("거부됨");
|
||||
expect(container.textContent).toContain("신청 필요");
|
||||
expect(container.querySelector("button")).not.toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
interface DeveloperAccessRequestCardProps {
|
||||
title: string;
|
||||
isPending: boolean;
|
||||
canRequest: boolean;
|
||||
pendingMessage: string;
|
||||
deniedMessage: string;
|
||||
pendingDetailMessage: string;
|
||||
deniedDetailMessage: string;
|
||||
actionLabel: string;
|
||||
onAction: () => void;
|
||||
}
|
||||
|
||||
export function DeveloperAccessRequestCard({
|
||||
title,
|
||||
isPending,
|
||||
canRequest,
|
||||
pendingMessage,
|
||||
deniedMessage,
|
||||
pendingDetailMessage,
|
||||
deniedDetailMessage,
|
||||
actionLabel,
|
||||
onAction,
|
||||
}: DeveloperAccessRequestCardProps) {
|
||||
const showAction = isPending || canRequest;
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-border/60 bg-card p-8 text-center">
|
||||
<div className="space-y-3">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{title}</h2>
|
||||
<p className="font-medium text-foreground">
|
||||
{isPending ? pendingMessage : deniedMessage}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{isPending ? pendingDetailMessage : deniedDetailMessage}
|
||||
</p>
|
||||
{showAction && (
|
||||
<button
|
||||
type="button"
|
||||
className="font-bold text-primary hover:underline"
|
||||
onClick={onAction}
|
||||
>
|
||||
{actionLabel}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user