Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions src/components/atoms/action-dropdown/ActionDropdown.test.tsx
Original file line number Diff line number Diff line change
@@ -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(
<ActionDropdown items={mockItems} isOpen={true} setIsOpen={jest.fn()} />,
);

expect(screen.getByText("Option 1")).toBeInTheDocument();
expect(screen.getByText("Option 2")).toBeInTheDocument();
});

test("드롭다운이 닫혀 있을 때 메뉴가 보이지 않는지 확인", () => {
render(
<ActionDropdown items={mockItems} isOpen={false} setIsOpen={jest.fn()} />,
);

expect(screen.queryByText("Option 1")).not.toBeInTheDocument();
expect(screen.queryByText("Option 2")).not.toBeInTheDocument();
});

test("메뉴 아이템을 클릭하면 onClick 핸들러가 호출되고 드롭다운이 닫힘", () => {
const setIsOpenMock = jest.fn(); // 드롭다운 상태를 변경하는 Mock 함수 생성

render(
<ActionDropdown
items={mockItems}
isOpen={true}
setIsOpen={setIsOpenMock} // 드롭다운 상태를 변경할 Mock 함수를 props로 전달
/>,
);

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(
<ActionDropdown
items={mockItems}
isOpen={true}
setIsOpen={setIsOpenMock}
/>,
);

const overlay = screen.getByTestId("dropdown-overlay");
fireEvent.click(overlay);

expect(setIsOpenMock).toHaveBeenCalledWith(false);
});
});
1 change: 1 addition & 0 deletions src/components/atoms/action-dropdown/ActionDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export default function ActionDropdown({

{/* dropdown 외부를 덮고있는 레이어 (드롭다운 외부 클릭 시 닫혀야 함) */}
<div
data-testid="dropdown-overlay"
className="fixed inset-0 z-1 bg-transparent"
onClick={closeDropdown}
/>
Expand Down
64 changes: 64 additions & 0 deletions src/components/atoms/button/Button.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<Button>버튼</Button>);

const button = screen.getByRole("button", { name: "버튼" });

expect(button).toBeInTheDocument();
expect(button).toHaveClass("bg-blue-500 text-white");
});

test("variant props에 따라 스타일이 변경되는지 확인", () => {
render(<Button variant="outline">버튼</Button>);

const button = screen.getByRole("button", { name: "버튼" });

expect(button).toHaveClass("bg-white text-blue-500 border");
});

test("size props에 따라 크기가 변경되는지 확인", () => {
render(<Button size="lg">버튼</Button>);

const button = screen.getByRole("button", { name: "버튼" });

expect(button).toHaveClass("w-[291px] h-[48px] text-base");
});

test("rounded props가 true일 때, 둥근 스타일이 적용되는지 확인", () => {
render(<Button rounded={true}>버튼</Button>);

const button = screen.getByRole("button", { name: "버튼" });

expect(button).toHaveClass("rounded-[24px]");
});

test("disabled 상태일 때 클릭 이벤트가 발생하지 않는지 확인", () => {
const handleClick = jest.fn();
render(
<Button onClick={handleClick} disabled>
버튼
</Button>,
);

const button = screen.getByRole("button", { name: "버튼" });
fireEvent.click(button);

expect(handleClick).not.toHaveBeenCalled();
});

test("버튼 클릭 시 onClick 핸들러가 호출되는지 확인", () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>버튼</Button>);

const button = screen.getByRole("button", { name: "버튼" });
fireEvent.click(button);

expect(handleClick).toHaveBeenCalledTimes(1);
});
});
33 changes: 33 additions & 0 deletions src/components/atoms/exit-btn/ExitBtn.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<ExitBtn onClick={jest.fn()} />);

const button = screen.getByRole("img", { name: /exit-button/i });

expect(button).toBeInTheDocument();
});

test("props로 넘겨준 클래스가 정상적으로 적용되는지 확인", () => {
render(<ExitBtn onClick={jest.fn()} className="custom-class" />);

const button = screen.getByRole("img", { name: /exit-button/i });

expect(button).toHaveClass("custom-class");
});

test("클릭 시 onClick 핸들러가 호출되는지 확인", () => {
const handleClick = jest.fn();
render(<ExitBtn onClick={handleClick} />);

const button = screen.getByRole("img", { name: /exit-button/i });
fireEvent.click(button);

expect(handleClick).toHaveBeenCalledTimes(1);
});
});
1 change: 0 additions & 1 deletion src/components/atoms/exit-btn/ExitBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
/>
Expand Down
63 changes: 63 additions & 0 deletions src/components/atoms/goal-item/GoalItem.test.tsx
Original file line number Diff line number Diff line change
@@ -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(
<GoalItem
goal="목표제목"
iconSize="sm"
textSize="base"
fontWeight="medium"
/>,
);

const icon = screen.getByRole("img", { name: "flag-goal" });
const text = screen.getByText("목표제목");

expect(icon).toBeInTheDocument();
expect(text).toBeInTheDocument();
});

test("아이콘 크기가 lg일 때 size가 올바르게 렌더링 되는지 확인", () => {
render(
<GoalItem
goal="Big Goal"
iconSize="lg"
textSize="lg"
fontWeight="semibold"
/>,
);

const icon = screen.getByRole("img", { name: "flag-goal" });

expect(icon).toHaveAttribute("width", "40");
expect(icon).toHaveAttribute("height", "40");
});

test("goal 값이 없을 때, span 태그가 렌더링되지 않는지 확인", () => {
render(<GoalItem iconSize="sm" textSize="base" fontWeight="medium" />);

const text = screen.queryByText(/.+/); // 아무 텍스트나 있는지 확인

expect(text).not.toBeInTheDocument();
});

test("gap 값이 올바르게 적용되는지 확인", () => {
const { container } = render(
<GoalItem
goal="Gap Test"
iconSize="sm"
textSize="sm"
fontWeight="medium"
gap={10}
/>,
);

const wrapper = container.firstChild;
expect(wrapper).toHaveStyle("gap: 10px");
});
});
1 change: 0 additions & 1 deletion src/components/atoms/goal-item/GoalItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export default function GoalItem({
width: iconSize === "lg" ? 40 : 24,
height: iconSize === "lg" ? 40 : 24,
alt: "flag-goal",
layout: "fixed",
};

return (
Expand Down
46 changes: 46 additions & 0 deletions src/components/atoms/input/Input.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<RefInput placeholder="Enter text" />);

const input = screen.getByPlaceholderText("Enter text");

expect(input).toBeInTheDocument();
});

test("size props에 따라 클래스가 적용되는지 확인", () => {
render(<RefInput size="lg" placeholder="Large input" />);
const input = screen.getByPlaceholderText("Large input");

expect(input).toHaveClass("w-[612px] h-[48px] text-base");
});

test("사용자 정의 className이 올바르게 적용되는지 확인", () => {
render(<RefInput className="custom-class" placeholder="text" />);
const input = screen.getByPlaceholderText("text");

expect(input).toHaveClass("custom-class");
});

test("placeholder가 올바르게 표시되는지 확인", () => {
render(<RefInput placeholder="Test Placeholder" />);
const input = screen.getByPlaceholderText("Test Placeholder");

expect(input).toBeInTheDocument();
});

test("입력값이 정상적으로 반영되는지 확인", () => {
render(<RefInput />);
const input = screen.getByRole("textbox");

// 값 입력
fireEvent.change(input, { target: { value: "Hello, World!" } });

expect(input).toHaveValue("Hello, World!");
});
});
5 changes: 1 addition & 4 deletions src/components/atoms/input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,15 @@ type InputElementProps = Omit<

const RefInput = forwardRef<HTMLInputElement, InputElementProps>(
({ className, size, ...props }, ref) => {
const sizeProps = typeof size === "number" ? { size } : {};

return (
<input
ref={ref}
className={cn(
inputVariants({
size: typeof size !== "number" ? size : undefined,
size,
className,
}),
)}
{...sizeProps}
{...props}
/>
);
Expand Down
1 change: 0 additions & 1 deletion src/components/atoms/spinner/Spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export default function Spinner({ size }: SpinnerProps) {
alt="spinner"
width={size}
height={size}
layout="fixed"
/>
</div>
);
Expand Down
1 change: 0 additions & 1 deletion src/components/atoms/title-with-icon/TitleWithIcon.tsx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export default function TitleWithIcon({
width: iconSize === "lg" ? 40 : 24,
height: iconSize === "lg" ? 40 : 24,
alt: `${imgUrl}`,
layout: "fixed",
};

return (
Expand Down
13 changes: 13 additions & 0 deletions src/utils/formatDate/formatDate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
"유효하지 않은 날짜 형식입니다.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export default function GoalItem({ goal }: GoalItemProps) {
alt={"arrow_right"}
width={20}
height={20}
layout="fixed"
/>
</button>
</li>
Expand Down
8 changes: 1 addition & 7 deletions src/views/note/note-detail/components/LinkItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
>
<Image
src={"/icons/embed.png"}
alt="embed-icon"
width={24}
height={24}
layout="fixed"
/>
<Image src={"/icons/embed.png"} alt="embed-icon" width={24} height={24} />
<span className="text-base font-normal break-all text-slate-800">
{linkUrl}
</span>
Expand Down