diff --git a/package.json b/package.json index e51db5bc..795f335a 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,13 @@ "typescript": "4.7.4", "web-vitals": "2.1.4" }, + "jest": { + "collectCoverageFrom": [ + "src/**/*.{js,jsx,ts,tsx}", + "!src/index.tsx", + "!src/test-utils/*" + ] + }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", @@ -47,4 +54,4 @@ "last 1 safari version" ] } -} \ No newline at end of file +} diff --git a/src/App.test.tsx b/src/App.test.tsx index 838df452..a28519f5 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -1,7 +1,11 @@ -import { render } from "@testing-library/react"; - +import { render, screen } from "@testing-library/react"; +import { Provider } from "react-redux"; import App from "./App"; - -test("renders learn react link", () => { - render(); +import { store } from "./store"; +test("renders App.tsx", () => { + render( + + + + ); }); diff --git a/src/components/Todo/Todo.test.tsx b/src/components/Todo/Todo.test.tsx new file mode 100644 index 00000000..3bd26338 --- /dev/null +++ b/src/components/Todo/Todo.test.tsx @@ -0,0 +1,24 @@ +import { render, screen } from "@testing-library/react"; +import Todo from "./Todo"; +describe("", () => { + it("should render without errors", () => { + render(); + //screen 으로 렌더링된 것을 접근 + //getByText throws error when cannot find element + screen.getByText("TODO_TITLE"); // Implicit assertion + const doneButton = screen.getByText("Done"); // Implicit assertion + expect(doneButton).toBeInTheDocument(); // Explicit assertion + }); + it("should render done mark when done is true", () => { + render(); + const title = screen.getByText("TODO_TITLE"); + expect(title.classList.contains("done")).toBe(true); + screen.getByText("Undone"); + }); + it("should render undone mark when done is false", () => { + render(); + const title = screen.getByText("TODO_TITLE"); + expect(title.classList.contains("done")).toBe(false); + screen.getByText("Done"); + }); +}); diff --git a/src/components/TodoDetail/TodoDetail.test.tsx b/src/components/TodoDetail/TodoDetail.test.tsx new file mode 100644 index 00000000..dcfff3d3 --- /dev/null +++ b/src/components/TodoDetail/TodoDetail.test.tsx @@ -0,0 +1,52 @@ +import { MemoryRouter, Navigate, Route, Routes } from "react-router"; +import { renderWithProviders } from "../../test-utils/mocks"; +import TodoDetail from "./TodoDetail"; +import { screen } from "@testing-library/react"; +import axios from "axios"; + +const renderTodoDetail = () => { + //this is render + renderWithProviders( + + + } /> + } /> + + , + { + preloadedState: { + todo: { + todos: [ + { + id: 3, + title: "title3", + content: "content3", + done: false, + }, + ], + selectedTodo: null, + }, + }, + } + ); +}; + +describe("", () => { + it("should render todo", async () => { + jest.spyOn(axios, "get").mockImplementation(() => { + return Promise.resolve({ + data: { + id: 3, + title: "title3", + content: "content3", + done: false, + }, + }); + }); + //render todo detail must happen after jest mocking + renderTodoDetail(); + await screen.findByText("title3"); + //only findbyDisplayValue works on await + await screen.findByText("content3"); + }); +}); diff --git a/src/containers/TodoList/NewTodo/NewTodo.test.tsx b/src/containers/TodoList/NewTodo/NewTodo.test.tsx new file mode 100644 index 00000000..0c47a784 --- /dev/null +++ b/src/containers/TodoList/NewTodo/NewTodo.test.tsx @@ -0,0 +1,62 @@ +import { fireEvent, screen, waitFor } from "@testing-library/react"; +import axios from "axios"; +import * as todoSlice from "../../../store/slices/todo"; +import { getMockStore, renderWithProviders } from "../../../test-utils/mocks"; +import NewTodo from "./NewTodo"; + +const mockNavigate = jest.fn(); + +jest.mock("react-router", () => ({ + //그래야 NavLink 같은 걸 쓸 수 있다. + ...jest.requireActual("react-router"), + Navigate: (props: any) => { + //we need to check navigate to + mockNavigate(props.to); + return null; + }, + useNavigate: () => mockNavigate, +})); + +describe("the test of NewTodo", () => { + //branch submitted + beforeEach(() => { + jest.clearAllMocks(); + }); + it("should render NewTodo", () => { + renderWithProviders(); + screen.getByText("Add a Todo"); + }); + it("should change inputs on enter", async () => { + //this changes the dispatch return value + axios.post = jest.fn().mockResolvedValueOnce({ + //this is the result value + data: { + id: 1, + title: "안녕하세요", + content: "내용 채우기", + done: false, + }, + }); + renderWithProviders(); + const titleInput = screen.getByLabelText("Title"); + const contentInput = screen.getByLabelText("Content"); + const submitButton = screen.getByText("Submit"); + fireEvent.change(titleInput, { target: { value: "안녕하세요" } }); + fireEvent.change(contentInput, { target: { value: "내용 채우기" } }); + await screen.findByDisplayValue("안녕하세요"); //test setstate + await screen.findByDisplayValue("내용 채우기"); //test setstate + fireEvent.click(submitButton); + //this waits for dispatch being called + await waitFor(() => expect(mockNavigate).toHaveBeenCalledWith("/todos")); + }); + it("should alert when inputs are empty", async () => { + const mockPostTodo = jest.spyOn(todoSlice, "postTodo"); + window.alert = jest.fn(); + console.error = jest.fn(); + axios.post = jest.fn().mockResolvedValueOnce(new Error()); + renderWithProviders(); + const submitButton = screen.getByText("Submit"); + fireEvent.click(submitButton); + await waitFor(() => expect(window.alert).toBeCalled()); + }); +}); diff --git a/src/containers/TodoList/NewTodo/NewTodo.tsx b/src/containers/TodoList/NewTodo/NewTodo.tsx index ece2d4b8..3ec2e6ea 100644 --- a/src/containers/TodoList/NewTodo/NewTodo.tsx +++ b/src/containers/TodoList/NewTodo/NewTodo.tsx @@ -12,14 +12,6 @@ export default function NewTodo() { const [submitted, setSubmitted] = useState(false); const dispatch = useDispatch(); - // const navigate = useNavigate() - // const postTodoHandler = () => { - // const data = { title: title, content: content }; - // alert("Submitted\n" + data.title + "\n" + data.content); - // setSubmitted(true); - // navigate('/todos') - // }; - const postTodoHandler = async () => { const data = { title: title, content: content }; const result = await dispatch(postTodo(data)); @@ -38,11 +30,19 @@ export default function NewTodo() {

Add a Todo