feat(ai-character): 관리자 인증 셸 구현

This commit is contained in:
Yu Sung
2026-07-27 15:04:03 +09:00
parent ba43dd829e
commit 5356bd1e6a
82 changed files with 6017 additions and 18 deletions

View File

@@ -0,0 +1,41 @@
import { fireEvent, render, screen } from "@testing-library/react";
import { expect, test, vi } from "vitest";
import { FileField } from "@/shared/ui/file-field";
test("FileField exposes label, description, error, accept guidance, keyboard file input, and controlled value", () => {
const onChange = vi.fn();
const value = new File(["image"], "profile.png", { type: "image/png" });
render(
<FileField
accept="image/png"
acceptDescription="PNG만 업로드할 수 있습니다."
description="프로필 이미지를 선택하세요."
error="파일이 너무 큽니다."
label="대표 이미지"
onChange={onChange}
value={value}
/>,
);
const input = screen.getByLabelText("대표 이미지");
expect(input).toHaveAttribute("accept", "image/png");
expect(input).toHaveAttribute("aria-invalid", "true");
expect(input).toHaveAccessibleDescription("프로필 이미지를 선택하세요. PNG만 업로드할 수 있습니다. 파일이 너무 큽니다.");
expect(screen.getByText("profile.png")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "선택 취소" })).toBeInTheDocument();
});
test("FileField emits File or null and clear selection without owning upload policy", () => {
const onChange = vi.fn();
const selected = new File(["audio"], "voice.mp3", { type: "audio/mpeg" });
const { rerender } = render(<FileField accept="audio/mpeg" acceptDescription="MP3" label="오디오" onChange={onChange} value={null} />);
fireEvent.change(screen.getByLabelText("오디오"), { target: { files: [selected] } });
expect(onChange).toHaveBeenCalledWith(selected);
rerender(<FileField accept="audio/mpeg" acceptDescription="MP3" label="오디오" onChange={onChange} value={selected} />);
fireEvent.click(screen.getByRole("button", { name: "선택 취소" }));
expect(onChange).toHaveBeenCalledWith(null);
});