0

I am testing my Input element by 2 way.

  1. adding value and click on button to "todo list" works fine
  2. adding value and enter the "input' field, but throws error.

I am seeking clarifications on the issue here. Here is 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" })
    );
  });
});

Live code

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

1 Answer 1

1

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" })
    );
  });
});
Sign up to request clarification or add additional context in comments.

2 Comments

thanks for your advice. But still another testing fails. can you check?should not call the updateTodo when input not provided fails. it is look like clearn up not working?
I have added the failing case as well with my question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.