How to test for tooltip title in jest and testing/library
Asked Answered
L

6

32

I want to test that the tooltip title is equal to a specific text or not. This is my antd tooltip I want to write a test for that:

<Tooltip
  title={
     this.props.connection ? "Connected" : "Disconnected (Try again)"
         }>
  <Badge status="default" data-testid="connection-sign" />
</Tooltip>

and this is my test in jest:

 test("Show error title in tooltip", async () => {
    baseDom = render(cardComponent);
    fireEvent.mouseMove(await baseDom.findByTestId("connection-sign")); //To hover element and show tooltip
    expect(
      baseDom.getByTitle(
        "Disconnected (Try again)"
      )
    ).toBeInTheDocument();
  });

but this test failed and unable to find an element with this title. How can I test that my tooltip contain "Disconnected (Try again)"?

Locality answered 20/7, 2020 at 6:5 Comment(2)
From where does the Tooltip component come from? Is it custom or from material-ui or elsewhere ?Preoccupied
This is from antd. I bet the tooltip is rendering because I can find it with data-testid. But I want to get the title text and check the text of it to be equal with "Disconnected" this is my issue.Locality
P
44

There are multiple mistakes in your test.

  1. Passing component type instead of component instance to render
// this is wrong, passing component type
baseDom = render(cardComponent);

// this is right, passing component instance created with JSX
baseDom = render(<cardComponent />);
  1. Using mouseMove instead of mouseOver event

  2. Searching element by title and passing text instead of searching by text

// wrong, because, the prop on the Tooltip is called 'title'
// but it is actually text as 'getByTitle' looks for HTML
// title attribute
baseDom.getByTitle("Disconnected (Try again)");

// right, because you are actually looking for text not
// HTML title attribute (but wrong see (4))
baseDom.getByText("Disconnected (Try again)");
  1. Using sync method for Tooltip search instead of async
// wrong, because, Tooltip is added dynamically to the DOM
baseDom.getByText("Disconnected (Try again)");

// right
await baseDom.findByText("Disconnected (Try again)");

To sum up, with all mistakes fixed the test should look like this:

import React from "react";
import { render, fireEvent } from "@testing-library/react";
import App from "./App";

test("Show error title in tooltip", async () => {
  const baseDom = render(<cardComponent />);

  fireEvent.mouseOver(baseDom.getByTestId("connection-sign"));

  expect(
    await baseDom.findByText("Disconnected (Try again)")
  ).toBeInTheDocument();
});

Preoccupied answered 20/7, 2020 at 8:18 Comment(5)
Thank you. Completed and clear :) actually the point was "mouseOver".Locality
@Preoccupied I want to check after unhover the tooltip then the tooltip content is not in the document. I am getting that the content element is still present in the dom. How to handle this situation?Tenter
@Subro you can try to waitFor this tooltip to disappear. Here is the link Otherwise, I would have to look through the code, because, it's hard to tell.Preoccupied
want to know the difference between waitFor and await in short. I will try with waitFor.Tenter
I never could get my test to work with the fireEvent.mouseOver (while it clearly was mouseOver), but I did get it to work with a version of await userEvent.hover(baseDom.getByTestId("connection-sign")). I am not sure what extra effect of userEvent exactly made the difference, but also the author Kent C. Dodds urges people to use userEvent.Eloquence
D
5

I was tried many ways but didn't work, therefore I tried mouse enter instead of mouseOver or mouseMove and it's worked for me. here is a solution to test tooltip content, like as:

import { render, cleanup, waitFor, fireEvent, screen } from '@testing-library/react';

// Timeline component should render correctly with tool-tip
test('renders TimeLine component with mouse over(tool-tip)', async () => {

  const { getByTestId, getByText, getByRole } = render(
        <TimeLine
          items={timeLineItems()}
          currentItem={currentTimeLineItem()}
          onTimeLineItemChange={() => {}}
        />
    );

  const courseTitle = "Course collection-3";

  fireEvent.mouseEnter(getByRole('button'));

  await waitFor(() => getByText(courseTitle));
  expect(screen.getByText(courseTitle)).toBeInTheDocument();
});
Dandle answered 13/11, 2021 at 16:54 Comment(0)
R
4

In addition to the accepted answer, it's important to make sure if you set the prop getPopupContainer for an Antd tooltip, the popup might not be visible to react testing library as it happened in my case since the DOM container set may not be available in the body when testing the component especially if it's a unit test. e.g

In my case I had

<Popover
   getPopupContainer={() => document.getElementById('post--component-drawer')}
   content={<h1>Hello world</h1>}>
      <span data-testid="symbol-input--color-input">Click me</span>
</Popover>

div post--component-drawer was not available for that unit test. So I had to mock Popover to make sure I override prop getPopupContainer to null so that the popup would be visible

So at the beginning of my test file, I mocked Popover

jest.mock('antd', () => {
  const antd = jest.requireActual('antd');

  /** We need to mock Popover in order to override getPopupContainer to null. getPopContainer
   * sets the DOM container and if this prop is set, the popup div may not be available in the body
   */
  const Popover = (props) => {
    return <antd.Popover {...props} getPopupContainer={null} />;
  };

  return {
    __esModule: true,
    ...antd,
    Popover,
  };
});


test('popver works', async () => {
  render(<MyComponent/>);
  fireEvent.mouseOver(screen.getByTestId('symbol-input--color-input'));

  await waitFor(() => {
    expect(screen.getByRole('heading', {level: 1})).toBeInTheDocument();
  });
});
Recede answered 21/5, 2021 at 7:33 Comment(0)
P
4

I found this to be the most up to date way. You've got to do async() on the test and then await a findByRole since it isn't instantaneous!

    render(<LogoBar />);
    fireEvent.mouseEnter(screen.getByLabelText('DaLabel'));
    await screen.findByRole(/tooltip/);
    expect(screen.getByRole(/tooltip/)).toBeInTheDocument();
Paule answered 18/3, 2022 at 20:43 Comment(0)
C
1

I struggled with similar code and Testing Library for React, using an external component and what did the trick for me was to use

fireEvent.focus(...component...)

instead. Hope it helps some people!

Charleycharlie answered 30/11, 2023 at 10:4 Comment(0)
P
0

This is a slight modification to the Akshay Pal's solution:

import { render, cleanup, waitFor, fireEvent, screen } from '@testing-library/react';

// Timeline component should render correctly with tool-tip
test('renders TimeLine component with mouse over(tool-tip)', async () => {

  render(
        <TimeLine
          items={timeLineItems()}
          currentItem={currentTimeLineItem()}
          onTimeLineItemChange={() => {}}
        />
    );

  const courseTitle = "Course collection-3";

  fireEvent.mouseEnter(screen.getByRole('button'));

  expect(await screen.getByText(courseTitle)).toBeTruthy();
});
Pelton answered 3/2, 2023 at 0:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.