diff --git a/src/components/atoms/action-dropdown/ActionDropdown.test.tsx b/src/components/atoms/action-dropdown/ActionDropdown.test.tsx new file mode 100644 index 00000000..1ff4f2c6 --- /dev/null +++ b/src/components/atoms/action-dropdown/ActionDropdown.test.tsx @@ -0,0 +1,71 @@ +import "@testing-library/jest-dom"; + +import { fireEvent, render, screen } from "@testing-library/react"; + +import ActionDropdown from "./ActionDropdown"; + +describe("ActionDropdown Component", () => { + let mockItems: { label: string; onClick: jest.Mock }[]; + + beforeEach(() => { + mockItems = [ + { label: "Option 1", onClick: jest.fn() }, + { label: "Option 2", onClick: jest.fn() }, + ]; + }); + + test("드롭다운이 열려 있을 때 메뉴가 렌더링 되는지 확인", () => { + render( + , + ); + + expect(screen.getByText("Option 1")).toBeInTheDocument(); + expect(screen.getByText("Option 2")).toBeInTheDocument(); + }); + + test("드롭다운이 닫혀 있을 때 메뉴가 보이지 않는지 확인", () => { + render( + , + ); + + expect(screen.queryByText("Option 1")).not.toBeInTheDocument(); + expect(screen.queryByText("Option 2")).not.toBeInTheDocument(); + }); + + test("메뉴 아이템을 클릭하면 onClick 핸들러가 호출되고 드롭다운이 닫힘", () => { + const setIsOpenMock = jest.fn(); // 드롭다운 상태를 변경하는 Mock 함수 생성 + + render( + , + ); + + const option1 = screen.getByText("Option 1"); + fireEvent.click(option1); + + // Option 1의 onClick 핸들러가 정상적으로 실행되었는지 확인 + expect(mockItems[0].onClick).toHaveBeenCalledTimes(1); + + // 클릭 후 드롭다운이 닫혔는지 확인 + expect(setIsOpenMock).toHaveBeenCalledWith(false); + }); + + test("드롭다운 외부 클릭 시 닫힘", () => { + const setIsOpenMock = jest.fn(); + render( + , + ); + + const overlay = screen.getByTestId("dropdown-overlay"); + fireEvent.click(overlay); + + expect(setIsOpenMock).toHaveBeenCalledWith(false); + }); +}); diff --git a/src/components/atoms/action-dropdown/ActionDropdown.tsx b/src/components/atoms/action-dropdown/ActionDropdown.tsx index 6d0d3fa7..4f8ffcdf 100644 --- a/src/components/atoms/action-dropdown/ActionDropdown.tsx +++ b/src/components/atoms/action-dropdown/ActionDropdown.tsx @@ -88,6 +88,7 @@ export default function ActionDropdown({ {/* dropdown 외부를 덮고있는 레이어 (드롭다운 외부 클릭 시 닫혀야 함) */}
diff --git a/src/components/atoms/button/Button.test.tsx b/src/components/atoms/button/Button.test.tsx new file mode 100644 index 00000000..dabcc74b --- /dev/null +++ b/src/components/atoms/button/Button.test.tsx @@ -0,0 +1,64 @@ +import "@testing-library/jest-dom"; + +import { fireEvent, render, screen } from "@testing-library/react"; + +import Button from "./Button"; + +describe("Button Component", () => { + test("버튼이 기본 스타일로 렌더링 되는지 확인", () => { + render(); + + const button = screen.getByRole("button", { name: "버튼" }); + + expect(button).toBeInTheDocument(); + expect(button).toHaveClass("bg-blue-500 text-white"); + }); + + test("variant props에 따라 스타일이 변경되는지 확인", () => { + render(); + + const button = screen.getByRole("button", { name: "버튼" }); + + expect(button).toHaveClass("bg-white text-blue-500 border"); + }); + + test("size props에 따라 크기가 변경되는지 확인", () => { + render(); + + const button = screen.getByRole("button", { name: "버튼" }); + + expect(button).toHaveClass("w-[291px] h-[48px] text-base"); + }); + + test("rounded props가 true일 때, 둥근 스타일이 적용되는지 확인", () => { + render(); + + const button = screen.getByRole("button", { name: "버튼" }); + + expect(button).toHaveClass("rounded-[24px]"); + }); + + test("disabled 상태일 때 클릭 이벤트가 발생하지 않는지 확인", () => { + const handleClick = jest.fn(); + render( + , + ); + + const button = screen.getByRole("button", { name: "버튼" }); + fireEvent.click(button); + + expect(handleClick).not.toHaveBeenCalled(); + }); + + test("버튼 클릭 시 onClick 핸들러가 호출되는지 확인", () => { + const handleClick = jest.fn(); + render(); + + const button = screen.getByRole("button", { name: "버튼" }); + fireEvent.click(button); + + expect(handleClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/components/atoms/exit-btn/ExitBtn.test.tsx b/src/components/atoms/exit-btn/ExitBtn.test.tsx new file mode 100644 index 00000000..52a8bd15 --- /dev/null +++ b/src/components/atoms/exit-btn/ExitBtn.test.tsx @@ -0,0 +1,33 @@ +import "@testing-library/jest-dom"; + +import { fireEvent, render, screen } from "@testing-library/react"; + +import ExitBtn from "./ExitBtn"; + +describe("ExitBtn Component", () => { + test("이미지가 올바르게 렌더링되는지 확인", () => { + render(); + + const button = screen.getByRole("img", { name: /exit-button/i }); + + expect(button).toBeInTheDocument(); + }); + + test("props로 넘겨준 클래스가 정상적으로 적용되는지 확인", () => { + render(); + + const button = screen.getByRole("img", { name: /exit-button/i }); + + expect(button).toHaveClass("custom-class"); + }); + + test("클릭 시 onClick 핸들러가 호출되는지 확인", () => { + const handleClick = jest.fn(); + render(); + + const button = screen.getByRole("img", { name: /exit-button/i }); + fireEvent.click(button); + + expect(handleClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/components/atoms/exit-btn/ExitBtn.tsx b/src/components/atoms/exit-btn/ExitBtn.tsx index e9d78a19..05005ee7 100644 --- a/src/components/atoms/exit-btn/ExitBtn.tsx +++ b/src/components/atoms/exit-btn/ExitBtn.tsx @@ -13,7 +13,6 @@ export default function ExitBtn({ onClick, className }: ExitBtnProps) { alt="exit-button" width={24} height={24} - layout="fixed" className={cn("cursor-pointer hover:brightness-50", className)} onClick={onClick} /> diff --git a/src/components/atoms/goal-item/GoalItem.test.tsx b/src/components/atoms/goal-item/GoalItem.test.tsx new file mode 100644 index 00000000..73bdf3da --- /dev/null +++ b/src/components/atoms/goal-item/GoalItem.test.tsx @@ -0,0 +1,63 @@ +import "@testing-library/jest-dom"; + +import { render, screen } from "@testing-library/react"; + +import GoalItem from "./GoalItem"; + +describe("GoalItem Component", () => { + test("아이콘과 텍스트가 정상적으로 렌더링 되는지 확인", () => { + render( + , + ); + + const icon = screen.getByRole("img", { name: "flag-goal" }); + const text = screen.getByText("목표제목"); + + expect(icon).toBeInTheDocument(); + expect(text).toBeInTheDocument(); + }); + + test("아이콘 크기가 lg일 때 size가 올바르게 렌더링 되는지 확인", () => { + render( + , + ); + + const icon = screen.getByRole("img", { name: "flag-goal" }); + + expect(icon).toHaveAttribute("width", "40"); + expect(icon).toHaveAttribute("height", "40"); + }); + + test("goal 값이 없을 때, span 태그가 렌더링되지 않는지 확인", () => { + render(); + + const text = screen.queryByText(/.+/); // 아무 텍스트나 있는지 확인 + + expect(text).not.toBeInTheDocument(); + }); + + test("gap 값이 올바르게 적용되는지 확인", () => { + const { container } = render( + , + ); + + const wrapper = container.firstChild; + expect(wrapper).toHaveStyle("gap: 10px"); + }); +}); diff --git a/src/components/atoms/goal-item/GoalItem.tsx b/src/components/atoms/goal-item/GoalItem.tsx index fc0a8881..07367786 100644 --- a/src/components/atoms/goal-item/GoalItem.tsx +++ b/src/components/atoms/goal-item/GoalItem.tsx @@ -33,7 +33,6 @@ export default function GoalItem({ width: iconSize === "lg" ? 40 : 24, height: iconSize === "lg" ? 40 : 24, alt: "flag-goal", - layout: "fixed", }; return ( diff --git a/src/components/atoms/input/Input.test.tsx b/src/components/atoms/input/Input.test.tsx new file mode 100644 index 00000000..a18710bb --- /dev/null +++ b/src/components/atoms/input/Input.test.tsx @@ -0,0 +1,46 @@ +import "@testing-library/jest-dom"; + +import { fireEvent, render, screen } from "@testing-library/react"; + +import RefInput from "./Input"; + +describe("RefInput Component", () => { + test("기본 렌더링 확인", () => { + render(); + + const input = screen.getByPlaceholderText("Enter text"); + + expect(input).toBeInTheDocument(); + }); + + test("size props에 따라 클래스가 적용되는지 확인", () => { + render(); + const input = screen.getByPlaceholderText("Large input"); + + expect(input).toHaveClass("w-[612px] h-[48px] text-base"); + }); + + test("사용자 정의 className이 올바르게 적용되는지 확인", () => { + render(); + const input = screen.getByPlaceholderText("text"); + + expect(input).toHaveClass("custom-class"); + }); + + test("placeholder가 올바르게 표시되는지 확인", () => { + render(); + const input = screen.getByPlaceholderText("Test Placeholder"); + + expect(input).toBeInTheDocument(); + }); + + test("입력값이 정상적으로 반영되는지 확인", () => { + render(); + const input = screen.getByRole("textbox"); + + // 값 입력 + fireEvent.change(input, { target: { value: "Hello, World!" } }); + + expect(input).toHaveValue("Hello, World!"); + }); +}); diff --git a/src/components/atoms/input/Input.tsx b/src/components/atoms/input/Input.tsx index 986a293b..1a9f5c39 100644 --- a/src/components/atoms/input/Input.tsx +++ b/src/components/atoms/input/Input.tsx @@ -32,18 +32,15 @@ type InputElementProps = Omit< const RefInput = forwardRef( ({ className, size, ...props }, ref) => { - const sizeProps = typeof size === "number" ? { size } : {}; - return ( ); diff --git a/src/components/atoms/spinner/Spinner.tsx b/src/components/atoms/spinner/Spinner.tsx index f842b793..17c878bd 100644 --- a/src/components/atoms/spinner/Spinner.tsx +++ b/src/components/atoms/spinner/Spinner.tsx @@ -12,7 +12,6 @@ export default function Spinner({ size }: SpinnerProps) { alt="spinner" width={size} height={size} - layout="fixed" />
); diff --git a/src/components/atoms/title-with-icon/TitleWithIcon.tsx.tsx b/src/components/atoms/title-with-icon/TitleWithIcon.tsx.tsx index 0375f9f6..a2dbf7ff 100644 --- a/src/components/atoms/title-with-icon/TitleWithIcon.tsx.tsx +++ b/src/components/atoms/title-with-icon/TitleWithIcon.tsx.tsx @@ -22,7 +22,6 @@ export default function TitleWithIcon({ width: iconSize === "lg" ? 40 : 24, height: iconSize === "lg" ? 40 : 24, alt: `${imgUrl}`, - layout: "fixed", }; return ( diff --git a/src/utils/formatDate/formatDate.test.ts b/src/utils/formatDate/formatDate.test.ts index d626f68a..c4fde667 100644 --- a/src/utils/formatDate/formatDate.test.ts +++ b/src/utils/formatDate/formatDate.test.ts @@ -35,6 +35,19 @@ describe("formatDate 함수 테스트", () => { ); }); + test("빈 값 입력 시 빈 문자열 반환", () => { + expect(formatDate(undefined)).toBe(""); + }); + + test("지원되지 않는 타입 입력 시 예외 발생", () => { + const invalidInputs = [{}, [], true]; + invalidInputs.forEach((input) => { + expect(() => formatDate(input as never)).toThrow( + "유효하지 않은 날짜 형식입니다.", + ); + }); + }); + test("숫자 범위를 초과하는 타임스탬프를 입력하면 예외를 발생", () => { expect(() => formatDate(9999999999999999)).toThrow( "유효하지 않은 날짜 형식입니다.", diff --git a/src/views/dashboard/goal-based-todo/components/GoalItem.tsx b/src/views/dashboard/goal-based-todo/components/GoalItem.tsx index 844631bd..92b05a87 100644 --- a/src/views/dashboard/goal-based-todo/components/GoalItem.tsx +++ b/src/views/dashboard/goal-based-todo/components/GoalItem.tsx @@ -64,7 +64,6 @@ export default function GoalItem({ goal }: GoalItemProps) { alt={"arrow_right"} width={20} height={20} - layout="fixed" /> diff --git a/src/views/note/note-detail/components/LinkItem.tsx b/src/views/note/note-detail/components/LinkItem.tsx index e1defb56..41c79199 100644 --- a/src/views/note/note-detail/components/LinkItem.tsx +++ b/src/views/note/note-detail/components/LinkItem.tsx @@ -17,13 +17,7 @@ export default function LinkItem({ linkUrl, setIsEmbedOpen }: LinkItemProps) { onClick={toggleEmbed} className="flex cursor-pointer items-center gap-2 rounded-[20px] bg-slate-200 px-[6px] py-1 hover:brightness-95" > - embed-icon + embed-icon {linkUrl}