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);
});
});
});