I am testing my Input element by 2 way.
any one clarify the issue here: my test code:
import { describe, vi, it, expect } from "vitest";
import { AddTodo } from "./AddItem";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
const updateTodo = vi.fn();
describe("Add Item component", () => {
it("should contain a input with button element", () => {
render(<AddTodo updateTodo={updateTodo} />);
expect(screen.getByRole("textbox")).toBeInTheDocument();
expect(screen.getByRole("button")).toBeInTheDocument();
});
it("should add a todo item into screen", async () => {
render(<AddTodo updateTodo={updateTodo} />);
const inputBox = await screen.findByRole("textbox");
const button = await screen.findByRole("button", { name: "+" });
await userEvent.type(inputBox, "First todo");
expect(inputBox).toHaveValue("First todo");
await userEvent.click(button);
expect(updateTodo).toHaveBeenCalledWith(
expect.objectContaining({ text: "First todo" })
);
});
it("should add a todo item into screen on enter", async () => { //fails
render(<AddTodo updateTodo={updateTodo} />);
const inputBox = await screen.findByTestId("todo-input");
await userEvent.type(inputBox, "Second todo");
await userEvent.keyboard("{enter}");
expect(updateTodo).toHaveBeenCalledWith(
expect.objectContaining({ text: "First todo" })
);
});
});
thanks in advance
failing case
it("should not call the updateTodo when input not provided", async () => {
const user = userEvent.setup();
const { findByTestId } = render(<AddTodo updateTodo={updateTodo} />);
const inputBox = await findByTestId("todo-input");
user.type(inputBox, "");
await user.keyboard("{enter}");
expect(updateTodo).not.toHaveBeenCalled();
});
error AssertionError: expected "spy" to not be called at all, but actually been called 1 times
You forgot to cleanup the render after Each tests. When running the tests on each test vitest was adding multple todo lists to the dom.
afterEach(cleanup);
Adding a cleanup after each tests will make everything green.
import { describe, vi, it, expect, afterEach } from "vitest";
import { AddTodo } from "./AddItem";
import { cleanup, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
const updateTodo = vi.fn();
afterEach(cleanup);
describe("Add Item component", () => {
it("should contain a input with button element", () => {
render(<AddTodo updateTodo={updateTodo} />);
expect(screen.getByRole("textbox")).toBeInTheDocument();
expect(screen.getByRole("button")).toBeInTheDocument();
});
it("should add a todo item into screen", async () => {
const user = userEvent.setup();
render(<AddTodo updateTodo={updateTodo} />);
const inputBox = await screen.findByRole("textbox");
const button = await screen.findByRole("button", { name: "+" });
await user.type(inputBox, "First todo");
expect(inputBox).toHaveValue("First todo");
await user.click(button);
expect(updateTodo).toHaveBeenCalledWith(
expect.objectContaining({ text: "First todo" })
);
});
it("should add a todo item into screen on enter", async () => {
const user = userEvent.setup();
render(<AddTodo updateTodo={updateTodo} />);
const inputBox = await screen.findByRole("textbox");
await user.type(inputBox, "Second todo");
await user.keyboard("{enter}");
expect(updateTodo).toHaveBeenCalledWith(
expect.objectContaining({ text: "First todo" })
);
});
});
I have made a few more changes. Additionally reading elements from render is a best practice. (by doing that it will not read entire screen)
here is an example - https://codesandbox.io/p/devbox/react-test-forked-7ykncc
Edit
here is the example reading elements from render itself
import { describe, vi, it, expect, afterEach } from "vitest";
import { AddTodo } from "./AddItem";
import { cleanup, render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
const updateTodo = vi.fn();
afterEach(cleanup);
describe("Add Item component", () => {
it("should contain a input with button element", () => {
const { getByRole } = render(<AddTodo updateTodo={updateTodo} />);
expect(getByRole("textbox")).toBeInTheDocument();
expect(getByRole("button")).toBeInTheDocument();
});
it("should add a todo item into screen", async () => {
const user = userEvent.setup();
const { findByRole } = render(<AddTodo updateTodo={updateTodo} />);
const inputBox = await findByRole("textbox");
const button = await findByRole("button", { name: "+" });
await user.type(inputBox, "First todo");
expect(inputBox).toHaveValue("First todo");
await user.click(button);
expect(updateTodo).toHaveBeenCalledWith(
expect.objectContaining({ text: "First todo" })
);
});
it("should add a todo item into screen on enter", async () => {
const user = userEvent.setup();
const { findByRole } = render(<AddTodo updateTodo={updateTodo} />);
const inputBox = await findByRole("textbox");
await user.type(inputBox, "Second todo");
await user.keyboard("{enter}");
expect(updateTodo).toHaveBeenCalledWith(
expect.objectContaining({ text: "First todo" })
);
});
});