Testing the return value from an useMemo with React/Jest

2.8k Views Asked by At

I have a component that converts a person's name to two initials with a useMemo.

I simply want to test the content to be "SB" when the name is Sjaak de Boer. But the useMemo (or if I simple convert it to a return function) is slow so the test fails.

codesandbox

import React, { useState, useMemo } from "react";

interface Props {
  name?: string;
}

const ObAvatar: React.FC<Props> = ({ name, ...props }) => {
  const [nameValue, setNameValue] = useState<string>(name);

  const returnInitials = useMemo(() => {
    if ((nameValue && nameValue.length === null) || !nameValue) return "...";
    const rgx: RegExp = new RegExp(/(\p{L}{1})\p{L}+/, "gu");
    const matches: any = nameValue.matchAll(rgx); // fix any!
    let initials: Array<any> = [...matches] || [];
    initials = (
      (initials.shift()?.[1] || "") + (initials.pop()?.[1] || "")
    ).toUpperCase();
    return initials;
  }, [nameValue]);

  return (
    <span style={{ border: "solid 1px red" }} data-testid="initials">
      {returnInitials}
    </span>
  );
};

export default ObAvatar;

And my test is:

/** @jest-environment jsdom */

import React from "react";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import Initials from "./Initials";

describe("Initials", () => {
  test("Should render an initials SB (Sjaak de Boer)", () => {
    render(<Initials name="Sjaak de Boer" />);
    const initialsElm: HTMLSpanElement = screen.getByTestId("initials");
    expect(initialsElm).toHaveTextContent(/SB/i);
    screen.debug();
  });
});

I tried using timers, but I feel I am missing some part of using the jest library.

1

There are 1 best solutions below

4
possum On

If you have an element that will have a delay there are a couple asynchronous functions you can use. Use waitFor when you have an assertion which may not be immediately true

test("Should render an initials SB (Sjaak de Boer)", async () => {
    render(<App name={"Sjaak de Boer"} />);
    const initialsElm: HTMLSpanElement = screen.getByTestId("avatar-initials");
    await waitFor(() => expect(initialsElm).toHaveTextContent(/SB/i));
  });

or you can use findByText (or any of the findBy* functions) for elements which may not exist immediately

test("Should render an initials SB (Sjaak de Boer)", async () => {
    render(<App name={"Sjaak de Boer"} />);
    const initialsElm: HTMLSpanElement = await screen.findByTestId(/SB/i);
    expect(initialsElm).toBeInTheDocument();
  });