GAZAR

Principal Engineer | Mentor

Writing Tests with Vitest and React Testing Library

Writing Tests with Vitest and React Testing Library

Vitest is a minimalistic, framework-agnostic testing library designed for modern JavaScript projects. It provides a simple API for writing and running tests without the complexity of a larger testing framework like Jest. React Testing Library, on the other hand, is a testing utility for React that encourages testing components in a way that simulates user behavior.

Before we start writing tests, let's set up our project. Make sure you have Node.js and npm installed on your machine. You can create a new React project with TypeScript using Create React App:

pnpm add -D vitest @testing-library/react @testing-library/jest-dom

I have got a vitest.config.ts

import { defineConfig } from "vite";
import path from "path";
export default defineConfig({
  test: {
    globals: true,
    environment: "happy-dom",
    setupFiles: "./test.setup.ts",
  },
  resolve: {
    alias: {
      "~": path.resolve(__dirname, "./app"),
    },
  },
});

And test.setup.ts

import "@testing-library/jest-dom";

Also had to add that to tsconfig.ts

"types": ["@remix-run/node", "vite/client", "@testing-library/jest-dom"],

then added this two scripts to my package.json

"test:watch": "vitest",
"test": "vitest run"

And After you can use Remix Testing: https://remix.run/docs/en/main/other-api/testing

pnpm add @remix-run/testing

so it gives you this to mock loaders in remix

const RemixStub = createRemixStub([
  {
    path: "/",
    Component: MilliSecondsCounter,
    loader() {
      return json(postData);
    },
  },
]);

Code

So check to see

  • How I used RemixStub
  • How I used findByTestId which comes out of vitest library
  • and how I fired an event and used a setTimeout delay to see if our counter on milliseconds works

import MilliSecondsCounter from "../routes/apps.milliseconds_counter._index";
import { json } from "@remix-run/node";
import { createRemixStub } from "@remix-run/testing";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { describe, expect, test } from "vitest";

const postData = {
  posts: {
    data: [
      {
        attributes: {
          title: "test",
          content: [],
          image: {
            data: {
              attributes: {
                url: "none",
              },
            },
          },
        },
      },
    ],
  },
};

const RemixStub = createRemixStub([
  {
    path: "/",
    Component: MilliSecondsCounter,
    loader() {
      return json(postData);
    },
  },
]);

describe("MilliSecondsCounter", () => {
  test("should return a function", () => {
    expect(typeof RemixStub).toBe("function");
  });

  test("should render correct", async () => {
    render(<RemixStub />);
    await screen.findByTestId("milliseconds");
    expect(await screen.findByTestId("milliseconds")).toHaveTextContent("0");
  });

  test("should work after start", async () => {
    render(<RemixStub />);

    fireEvent.click(await screen.findByTestId("start"));

    await waitFor(async () => {
      const value = (await screen.findByTestId("milliseconds")).innerText;
      expect(Number(value)).toBeGreaterThan(0);
    });
  });

  test("should work after start and pause and start", async () => {
    render(<RemixStub />);

    fireEvent.click(await screen.findByTestId("start"));
    fireEvent.click(await screen.findByTestId("pause"));

    await waitFor(async () => {
       const value = (await screen.findByTestId("milliseconds")).innerText;
       const newValue = (await screen.findByTestId("milliseconds")).innerText;
       expect(value).toEqual(newValue);
    });
  });
});