Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { vi } from "vitest";

export const mockFormInput = {
FormInput: vi.fn().mockImplementation(({ id, label, value, onChange, type = "text", placeholder }) => (
<div>
<label htmlFor={id}>{label}</label>
<input
id={id}
type={type}
value={value}
placeholder={placeholder}
onChange={(e) => onChange(e.target.value)}
aria-label={label}
/>
</div>
)),
};

export const mockPlatformSelector = {
PlatformSelector: vi.fn().mockImplementation(({ platform, onPlatformChange }) => (
<div>
<select value={platform} onChange={(e) => onPlatformChange(e.target.value)} aria-label="플랫폼 선택">
<option value="tistory">Tistory</option>
<option value="medium">Medium</option>
</select>
</div>
)),
};

export const mockRssUrlInput = {
RssUrlInput: vi.fn().mockImplementation(({ value, onChange }) => (
<div>
<input type="text" value={value} onChange={(e) => onChange(e.target.value)} aria-label="RSS URL" />
</div>
)),
};
8 changes: 8 additions & 0 deletions client/src/__tests__/__mocks__/components/ui/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const mockDialog = {
Dialog: ({ children, open }: { children: React.ReactNode; open: boolean }) => (open ? <div>{children}</div> : null),
DialogContent: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
DialogHeader: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
DialogTitle: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
DialogDescription: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
DialogFooter: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
};
80 changes: 80 additions & 0 deletions client/src/__tests__/__mocks__/helpers/rssRegistrationMocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { vi } from "vitest";

const DEFAULT_VALUES = {
email: "",
userName: "",
bloggerName: "",
rssUrl: "",
urlUsername: "",
};

const DEFAULT_SUCCESS_VALUES = {
email: "test@example.com",
userName: "테스트",
bloggerName: "블로그",
rssUrl: "https://test.com/rss",
urlUsername: "test",
};

const DEFAULT_FORM_STATE = {
platform: "tistory",
values: DEFAULT_VALUES,
handlers: {
handleEmail: vi.fn(),
handleUserName: vi.fn(),
handleBloggerName: vi.fn(),
handlePlatformChange: vi.fn(),
handleUsernameChange: vi.fn(),
},
formState: {
isValid: true,
reset: vi.fn(),
},
};

export const mockUseRssRegistrationForm = {
useRssRegistrationForm: vi.fn().mockReturnValue(DEFAULT_FORM_STATE),
};

export const createFormMock = ({ values = DEFAULT_VALUES, isValid = true, reset = vi.fn() } = {}) => {
return vi.mocked(mockUseRssRegistrationForm.useRssRegistrationForm).mockReturnValue({
...DEFAULT_FORM_STATE,
values,
formState: {
isValid,
reset,
},
});
};

export const createSuccessFormMock = (reset = vi.fn()) => {
return createFormMock({
values: DEFAULT_SUCCESS_VALUES,
reset,
});
};

export const createFailureFormMock = () => {
return createFormMock({
values: {
...DEFAULT_VALUES,
email: "invalid-email", // 잘못된 이메일 형식
userName: "asdf", // 빈 값
bloggerName: "asdf", // 빈 값
rssUrl: "invalid-url", // 잘못된 URL 형식
urlUsername: "asdf",
},
isValid: false,
});
};

export const createFormMockWithReset = () => {
const resetMock = vi.fn();
return {
...createFormMock({
values: DEFAULT_SUCCESS_VALUES,
reset: resetMock,
}),
resetMock,
};
};
13 changes: 13 additions & 0 deletions client/src/__tests__/__mocks__/hooks/useRegisterRss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { vi } from "vitest";

export const mockUseRegisterRss = {
useRegisterRss: (onSuccess: () => void, onError: () => void) => ({
mutate: vi.fn().mockImplementation((data) => {
if (data.email && data.name) {
onSuccess();
} else {
onError();
}
}),
}),
};
Original file line number Diff line number Diff line change
@@ -1,41 +1,5 @@
import { describe, it, expect } from "vitest";

import { PostCardContent } from "@/components/common/Card/PostCardContent";

import { createMockPost } from "@/__tests__/mocks/data/posts.ts";
import { render, screen } from "@testing-library/react";
import { describe, it } from "vitest";

describe("PostCardContent", () => {
it("author 이미지가 있는 PostContent가 렌더링되는지 확인", () => {
render(<PostCardContent post={createMockPost()} />);

expect(screen.getByText("테스트 포스트")).toBeInTheDocument();
expect(screen.getByText("작성자")).toBeInTheDocument();
expect(screen.getByAltText("작성자")).toBeInTheDocument();
expect(screen.getByText("2024년 3월 26일")).toBeInTheDocument();
});

it("avatar 이미지가 없을 때 대체 avatar가 렌더링되는지 확인", () => {
const postWithoutImage = {
...createMockPost(),
authorImageUrl: undefined,
};

render(<PostCardContent post={postWithoutImage} />);

expect(screen.getByText("작")).toBeInTheDocument();
expect(screen.queryByRole("img")).not.toBeInTheDocument();
});

it("author 정보가 없을 때 적절하게 처리되는지 확인", () => {
const postWithoutAuthor = {
...createMockPost(),
author: "",
authorImageUrl: undefined,
};

render(<PostCardContent post={postWithoutAuthor} />);

expect(screen.getByText("?")).toBeInTheDocument();
});
it("", () => {});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { describe, it, expect, vi } from "vitest";

import { FormInput } from "@/components/RssRegistration/FormInput";

import { render, screen, fireEvent } from "@testing-library/react";

describe("FormInput", () => {
it("label과 input이 htmlFor로 올바르게 연결되어야 한다", () => {
render(<FormInput id="test" type="text" label="테스트" value="" placeholder="test" onChange={() => {}} />);

const label = screen.getByText("테스트");
const input = screen.getByRole("textbox");

expect(label).toHaveAttribute("for", "test");
expect(input).toHaveAttribute("id", "test");
});

it("value prop이 입력 필드에 정확히 반영되어야 한다", () => {
render(<FormInput id="test" type="text" label="테스트" value="초기값" placeholder="test" onChange={() => {}} />);

expect(screen.getByRole("textbox")).toHaveValue("초기값");
});

it("onChange 핸들러가 입력값 변경을 정확히 전달해야 한다", () => {
const handleChange = vi.fn();
render(<FormInput id="test" type="text" label="테스트" value="" placeholder="test" onChange={handleChange} />);

const input = screen.getByRole("textbox");
fireEvent.change(input, { target: { value: "테스트" } });

expect(handleChange).toHaveBeenCalledWith("테스트");
});

it("email 타입은 이메일 입력 필드로 렌더링되어야 한다", () => {
render(
<FormInput id="email" type="email" label="이메일" value="" placeholder="test@example.com" onChange={() => {}} />
);

const input = screen.getByRole("textbox");
expect(input).toHaveAttribute("type", "email");
});

it("일반 텍스트 입력은 text 타입으로 렌더링되어야 한다", () => {
render(<FormInput id="name" type="text" label="이름" value="" placeholder="이름 입력" onChange={() => {}} />);

const input = screen.getByRole("textbox");
expect(input).toHaveAttribute("type", "text");
});

it("빈 값일 때 placeholder가 보여야 한다", () => {
render(<FormInput id="test" type="text" label="테스트" value="" placeholder="안내문구" onChange={() => {}} />);

expect(screen.getByPlaceholderText("안내문구")).toBeInTheDocument();
});
});
Loading
Loading