MouseEnter/MouseOver doesn't work with react-testing-library. How to test hover events with react-testing-library
Asked Answered
V

3

23

I have a ant-design dropdown which shows a list on hovering the element. I want to test the list inside the dropdown menu. I am using fireEvent.mouseOver() but it doesn't work. screen.debug() shows no menu. How to test hovering elements?

Thanks in advance

Vanitavanity answered 11/11, 2020 at 12:51 Comment(0)
S
42

Here is my test code. It's important to use await and wait for the menu to appear.

MyDropdown.tsx

import React from 'react'
import { Menu, Dropdown, Button } from 'antd';

const menu = (
  <Menu data-testid="dropdown-menu">
    <Menu.Item>
      <a target="_blank" rel="noopener noreferrer" href="http://www.alipay.com/">
        1st menu item
      </a>
    </Menu.Item>
    <Menu.Item>
      <a target="_blank" rel="noopener noreferrer" href="http://www.taobao.com/">
        2nd menu item
      </a>
    </Menu.Item>
    <Menu.Item>
      <a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">
        3rd menu item
      </a>
    </Menu.Item>
  </Menu>
);

export const MyDropdown: React.FC = () => {
  return(
    <Dropdown overlay={menu} placement="bottomLeft" arrow>
      <Button>bottomLeft</Button>
    </Dropdown>
  )
}

MyDropdown.test.tsx

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

describe("<MyDropdown />", () => {
    it("check dropdown menu item", async () => {
        render(<MyDropdown />);

        fireEvent.mouseOver(screen.getByText('bottomLeft'));

        await waitFor(() => screen.getByTestId('dropdown-menu'))
        expect(screen.getByText('1st menu item')).toBeInTheDocument()
        expect(screen.getByText('2nd menu item')).toBeInTheDocument()
        expect(screen.getByText('3rd menu item')).toBeInTheDocument()
    });
});
Shandra answered 12/11, 2020 at 1:20 Comment(2)
Thanks @rokki. With async await, it worked perfectlyVanitavanity
I had to use mouseEnter instead of mouseOver for the test to passMassicot
F
7

Here is code for reference. Hover on Terms and condition, popover will show/hide

enter image description here

<SummaryForm.js>

import React, { useState } from 'react';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import Popover from "react-bootstrap/Popover";
import OverlayTrigger from "react-bootstrap/OverlayTrigger";

export default function SummaryForm() {
    const [tcChecked, setTcChecked] = useState(false);

    const popover = (
        <Popover id="popover-basic">
            No ice cream will be delivered
        </Popover>
    );

    const checkboxLabel = (
        <span>
            <OverlayTrigger overlay={popover} placement="right">
               <span style={{ color: 'blue' }}> Terms and Conditions</span> 
            </OverlayTrigger>
        </span>
    ) ;
    

  return (
    <Form>
      <Form.Group controlId="terms-and-conditions">
        <Form.Check
          type="checkbox"
          checked={tcChecked}
          onChange={(e) => setTcChecked(e.target.checked)}
          label={checkboxLabel}
        />
      </Form.Group>
      <Button variant="primary" type="submit" disabled={!tcChecked}>
        Confirm order
      </Button>
    </Form>
  );
}

<SummaryForm.test.js>

import {
  render,
  screen,
  waitForElementToBeRemoved,
} from '@testing-library/react';
import SummaryForm from '../Form';
import userEvent from '@testing-library/user-event';

test('Initial conditions', () => {
  render(<SummaryForm />);
  const checkbox = screen.getByRole('checkbox', {
    name: /terms and conditions/i,
  });
  expect(checkbox).not.toBeChecked();

  const confirmButton = screen.getByRole('button', { name: /confirm order/i });
  expect(confirmButton).toBeDisabled();
});

test('Checkbox enables button on first click and disables on second click', () => {
  render(<SummaryForm />);
  const checkbox = screen.getByRole('checkbox', {
    name: /terms and conditions/i,
  });
  const confirmButton = screen.getByRole('button', { name: /confirm order/i });

  userEvent.click(checkbox);
  expect(confirmButton).toBeEnabled();

  userEvent.click(checkbox);
  expect(confirmButton).toBeDisabled();
});

test('popover responds to hover', async () => {
  render(<SummaryForm />);

  // popover starts out hidden
  const nullPopover = screen.queryByText(
    /No ice cream will be delivered/i
  );
  expect(nullPopover).not.toBeInTheDocument();

  // popover appears upon mouseover of checkbox label
  const termsAndConditions = screen.getByText(/terms and conditions/i);
  userEvent.hover(termsAndConditions);

  const popover = screen.queryByText(/No ice cream will be delivered/i);
  expect(popover).toBeInTheDocument();

  // popover disappears when we mouse out
  userEvent.unhover(termsAndConditions);
  await waitForElementToBeRemoved(() =>
    screen.queryByText(/No ice cream will be delivered/i)
  );
});
Frederickafredericks answered 25/5, 2022 at 3:9 Comment(0)
P
1

It might be a trade off, but for me

fireEvent.focus

did the trick!

Printable answered 30/11, 2023 at 10:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.