https://github.com/lburdock/js-test
Test syntax
import foo from "./foo";
describe("foo", () => {
test("works", () => {
expect(foo()).toBe("bar");
});
});
Test setup
beforeAll(() => {
// Setup
});
beforeEach(() => {
// Setup
});
afterEach(() => {
// Breakdown
});
afterAll(() => {
// Breakdown
});
Duplicating tests
test.each([
[1, 2, 3],
[4, 5, 9],
])("%i + %i === %i", (a, b, expected) => {
expect(a + b).toBe(expected);
});
Duplicating test suites
describe.each([
{ a: 1, b: 2, expected: 3 },
{ a: 4, b: 5, expected: 9 },
])("$a + $b", ({ a, b, expected }) => {
test(`to not be greater than ${expected}`, () => {
expect(a + b).not.toBeGreaterThan(expected);
});
test(`to not be less than ${expected}`, () => {
expect(a + b).not.toBeLessThan(expected);
});
});
expect(1).toBeTruthy();
expect(0).toBeFalsy();
expect(NaN).toBeNaN();
expect(null).toBeNull();
expect(undefined).toBeUndefined();
expect("a").toBeDefined();
expect(3).toBe(3);
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
expect(4).toBeGreaterThan(3);
expect(3).toBeGreaterThanOrEqual(3);
expect(2).toBeLessThan(3);
expect(3).toBeLessThanOrEqual(3);
expect("abc").toBe("abc");
expect("abc").toContain("b");
expect("abc").toMatch(/B/i);
expect("abc").toHaveLength(3);
const arr = ["a", { b: 1 }];
expect(arr).toStrictEqual(["a", { b: 1 }]);
expect(arr).toContain("a");
expect(arr).toContainEqual({ b: 1 });
expect(arr).toHaveLength(2);
const obj = { a: 1, b: 2 };
expect(obj).toStrictEqual({ a: 1, b: 2 });
expect(obj).toMatchObject({ a: 1 });
expect(obj).toHaveProperty("b", 2);
const kitchenSink = {
id: "abc",
total: 10,
sum: 0.1 + 0.2,
name: "Test 1",
zip: "12345",
options: ["Fuji", "Gala"],
count: { Fuji: 1, Gala: 2 },
};
expect(kitchenSink).toStrictEqual({
id: expect.anything(),
total: expect.any(Number),
sum: expect.closeTo(0.3, 5),
name: expect.stringContaining("Test"),
zip: expect.stringMatching(/\\d{5}$/i),
options: expect.arrayContaining(["Gala"]),
count: expect.objectContaining({ Fuji: 1 }),
});
expect(() => {
errFunc();
}).toThrow(/nope/i);
expect(el).toBeChecked();
expect(el).toBeDisabled();
expect(el).toBeEmptyDOMElement();
expect(el).toBeEnabled();
expect(el).toBeInTheDocument();
expect(el).toBeInvalid();
expect(el).toBePartiallyChecked();
expect(el).toBeRequired();
expect(el).toBeValid();
expect(el).toBeVisible();
expect(el).toContainElement(childEl);
expect(el).toHaveAccessibleDescription("Home");
expect(el).toHaveAccessibleErrorMessage("Error");
expect(el).toHaveAccessibleName("Alt text");
expect(el).toHaveAttribute("type", "submit");
expect(el).toHaveClass("cls1 cls2");
expect(el).toHaveDisplayValue("John");
expect(el).toHaveFocus();
expect(el).toHaveFormValues({ name: "John" });
expect(el).toHaveRole("button");
expect(el).toHaveStyle({ color: "red" });
expect(el).toHaveTextContent("Content");
expect(el).toHaveValue("John");
Implementation
const mock = vi.fn();
const mock = vi.fn(n => n * n);
const mock = vi
.fn(n => n * n)
.mockImplementationOnce(n => n * n * n);
const mock = vi
.fn()
.mockReturnValue("default")
.mockReturnValueOnce("first");
const asyncMock = vi
.fn()
.mockResolvedValue("default")
.mockResolvedValueOnce("first");
const asyncMock = vi
.fn()
.mockRejectedValue(new Error("default"))
.mockRejectedValueOnce(new Error("first"));
mock.mockClear();
mock.mockReset();
mock.mockRestore();
vi.clearAllMocks();
vi.resetAllMocks();
vi.restoreAllMocks();
vi.stubEnv("NODE_ENV", "production");
vi.unstubAllEnvs();
vi.stubGlobal("IntersectionObserver", vi.fn());
vi.unstubAllGlobals();
Mock-specific matchers
expect(mock).toHaveBeenCalled();
expect(mock).toHaveBeenCalledTimes(3);
expect(mock).toHaveBeenCalledWith("a");
expect(mock).toHaveBeenNthCalledWith(2, "b");
expect(mock).toHaveBeenLastCalledWith("c");
expect(mock.mock.calls).toEqual(["a"], ["b"]);
expect(mock.mock.calls[1][0]).toEqual("b");
expect(mock.mock.lastCall).toEqual(["c"]);
expect(mock).toHaveReturned();
expect(mock).toHaveReturnedTimes(3);
expect(mock).toHaveReturnedWith("x");
expect(mock).toHaveNthReturnedWith(2, "y");
expect(mock).toHaveLastReturnedWith("z");
expect(asyncMock).toHaveResolved();
expect(asyncMock).toHaveResolvedTimes(3);
expect(asyncMock).toHaveResolvedWith("x");
expect(asyncMock).toHaveLastResolvedWith(2, "y");
expect(asyncMock).toHaveNthResolvedWith("z");
Functions
// module-consumer.test.js
import { namedFnConsumer, defaultFnConsumer } from "./module-consumer";
const mockIsLiveNow = vi.hoisted(() => vi.fn());
const mockIsBeforeNow = vi.hoisted(() => vi.fn());
vi.mock("./module-to-mock", () => ({
default: mockIsLiveNow,
isBeforeNow: mockIsBeforeNow,
}));
afterEach(() => {
mockIsLiveNow.mockReset();
mockIsBeforeNow.mockReset();
});
test("namedFnConsumer", () => {
mockIsBeforeNow.mockReturnValue(true);
expect(namedFnConsumer("fake-date")).toBe("This date is in the past!");
});
test("defaultFnConsumer", () => {
mockIsLiveNow.mockReturnValue(true);
expect(defaultFnConsumer("fake-date")).toBe("This event is live!");
});
// module-consumer.js
import isLiveNow, { isBeforeNow } from "./module-to-mock";
export const defaultFnConsumer = date => {
if (isLiveNow(date)) return "This event is live!";
return "This event is not live.";
};
export const namedFnConsumer = date => {
if (isBeforeNow(date)) return "This date is in the past!";
return "This date is in the future!";
};
Classes
// class-consumer.test.js
import { defaultClassConsumer, namedClassConsumer } from "./class-consumer";
const mockIsLiveNow = vi.hoisted(() => vi.fn());
const mockIsBeforeNow = vi.hoisted(() => vi.fn());
vi.mock("./class-to-mock", () => ({
default: vi.fn(() => ({ isLiveNow: mockIsLiveNow })),
NamedClassToMock: vi.fn(() => ({ isBeforeNow: mockIsBeforeNow })),
}));
afterEach(() => {
mockIsLiveNow.mockReset();
mockIsBeforeNow.mockReset();
});
test("defaultClassConsumer", () => {
mockIsLiveNow.mockReturnValue(true);
expect(defaultClassConsumer("fake-date")).toBe("This event is live!");
});
test("namedClassConsumer", () => {
mockIsBeforeNow.mockReturnValue(true);
expect(namedClassConsumer("fake-date")).toBe("This date is in the past!");
});
// class-consumer.js
import DefaultClassToMock, { NamedClassToMock } from "./class-to-mock";
export const defaultClassConsumer = date => {
const myClass = new DefaultClassToMock();
if (myClass.isLiveNow(date)) return "This event is live!";
return "This event is not live.";
};
export const namedClassConsumer = date => {
const myClass = new NamedClassToMock();
if (myClass.isBeforeNow(date)) return "This date is in the past!";
return "This date is in the future!";
};
Test syntax
import {
render,
screen,
} from "@testing-library/react";
test("Button", () => {
render(<button>Yep</button>);
const btn = screen.getByRole("button");
expect(btn).toBeInTheDocument();
});
Async test syntax
import {
render,
screen,
waitFor,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
test("Button async", async () => {
render(<button>Yep</button>);
const btn = await screen.findByRole("button");
await expect(btn).toBeInTheDocument();
// Alternative to the above
await expect(
screen.findByRole("button"),
).resolves.toBeInTheDocument();
await expect(
screen.findByText("Nope")
).rejects.toThrow();
});
User event
import {
render,
screen,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
test("Button onClick", async () => {
const user = userEvent.setup();
const onClick = vi.fn();
render(<button onClick={onClick}>Yep</button>);
await user.click(screen.getByRole("button"));
await waitFor(() =>
expect(onClick).toHaveBeenCalled(),
);
});
User event functions
await user.clear(el);
await user.click(el);
await user.dblClick(el);
await user.tripleClick(el);
await user.hover(el);
await user.unhover(el);
await user.selectOptions(el, ["1", "C"]);
await user.selectOptions(el, screen.getByText("C"));
await user.deselectOptions(el, "B");
await user.tab();
await user.type(el, "Example");
await user.upload(el, file);
variants | No match | 1 match | 1+ match |
---|---|---|---|
getBy | Error |
Element |
Error |
getAllBy | Error |
Element[] |
Element[] |
findBy | Promise<Error> |
Promise<Element> |
Promise<Error> |
findAllBy | Promise<Error> |
Promise<Element[]> |
Promise<Element[]> |
queryBy | null |
Element |
Error |
queryAllBy | [] |
Element[] |
Element[] |
Aria role
getByRole
getAllByRole
queryByRole
queryAllByRole
findByRole
findAllByRole
Label or aria-label text
getByLabelText
getAllByLabelText
queryByLabelText
queryAllByLabelText
findByLabelText
findAllByLabelText
Input placeholder value
getByPlaceholderText
getAllByPlaceholderText
queryByPlaceholderText
queryAllByPlaceholderText
findByPlaceholderText
findAllByPlaceholderText
Element text content
getByText
getAllByText
queryByText
queryAllByText
findByText
findAllByText
Form element value
getByDisplayValue
getAllByDisplayValue
queryByDisplayValue
queryAllByDisplayValue
findByDisplayValue
findAllByDisplayValue