1

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.

Bravebox
  • 33
  • 6

1 Answers1

0

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();
  });
possum
  • 1,837
  • 3
  • 9
  • 18
  • Thanks I tried using waitFor, it did not work... but! I updated the examples in codesandbox with both your suggestions and they work fine. But if I run them from vscode they fail. – Bravebox Nov 02 '22 at 10:25
  • If it's taking a significant amount of time, there are tweaks you can do regarding timeouts https://testing-library.com/docs/dom-testing-library/api-async/ – possum Nov 02 '22 at 10:28
  • I copied your sandbox to my local machine and all three of those tests pass. How are you running them and what is failure you're getting? – possum Nov 02 '22 at 10:37
  • I checked my dependencies and they are the same as I use in Codesandbox (Also passing all test). I simply run `jest components/componentName`. The failure: Unable to find an element with the text: SB. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible. Ignored nodes: comments, script, style – Bravebox Nov 02 '22 at 10:49