Search code examples
reactjsjestjsreact-bootstrapreact-testing-librarytesting-library

React-bootstrap Nabar.Offcanvas causing errors with Jest/testing-library tests


I am working with a navbar.offcanvas element for responsive navigation menus. I just ran my tests, which pass when I comment out the navbar.offcanvas element. I am using React-Bootstrap, Jest, and testing-library/react.

When I leave the element in and run tests, I get the following:

    TypeError: targetWindow.matchMedia is not a function

      56 | test("Try to render element", () => {
      57 |   // Arrange
    > 58 |   render(<TodoList />);
         |   ^

I am not sure why this occurs.

Here is the Navbar.Offcanvas

<Navbar.Offcanvas
            id={`offcanvasNavbar-expand-md`}
            aria-labelledby={`offcanvasNavbarLabel-expand-md`}
            placement="start"
          >
            <Offcanvas.Header closeButton>
              <Offcanvas.Title id={`offcanvasNavbarLabel-expand-md`}>
                Navigation
              </Offcanvas.Title>
            </Offcanvas.Header>
            <Offcanvas.Body>
              <Nav>
                {pages.map((page) => {
                  return (
                    <Nav.Item key={page.path}>
                      <Nav.Link
                        href={page.path}
                        eventKey={page.path}
                        onSelect={(k) => setKey(k)}
                        className="layout-nav-link"
                      >
                        {page.name}
                      </Nav.Link>
                    </Nav.Item>
                  );
                })}
              </Nav>
            </Offcanvas.Body>
          </Navbar.Offcanvas>

Here are the tests

test("Renders TodoList", () => {
  render(<TodoList />);
  const todoList = screen.getByText("Todo List");
  expect(todoList).toHaveTextContent("Todo List");
});

test("Type into TodoList input box", () => {
  // Arrange
  render(<TodoList />);
  const todoListInput = screen.getByLabelText("TodoId");
  console.log(todoListInput);

  // Act
  fireEvent.change(todoListInput, {
    target: { value: "Run" },
  });

  // Assert
  expect(todoListInput.value).toBe("Run");

  // Act
  fireEvent.change(todoListInput, {
    target: { value: "" },
  });

  // Assert
  expect(todoListInput.value).toBe("");
});

If you need any additional information, please let me know. Thanks!


Solution

  • Note: I am still new to using Jest so this may not be the recommended way of fixing the issue. This is the only workaround I could come up with considering my limited knowledge.

    I ended up adding the following to my arrange sections in each test that was causing issues:

    Object.defineProperty(window, 'matchMedia', {
      writable: true,
      value: jest.fn().mockImplementation(query => ({
        matches: false,
        media: query,
        onchange: null,
        addListener: jest.fn(), // deprecated
        removeListener: jest.fn(), // deprecated
        addEventListener: jest.fn(),
        removeEventListener: jest.fn(),
        dispatchEvent: jest.fn(),
      })),
    });
    

    So it will look like:

    test("Renders TodoList", () => {
    
    Object.defineProperty(window, 'matchMedia', {
      writable: true,
      value: jest.fn().mockImplementation(query => ({
        matches: false,
        media: query,
        onchange: null,
        addListener: jest.fn(), // deprecated
        removeListener: jest.fn(), // deprecated
        addEventListener: jest.fn(),
        removeEventListener: jest.fn(),
        dispatchEvent: jest.fn(),
      })),
    });
    
      render(<TodoList />);
      const todoList = screen.getByText("Todo List");
      expect(todoList).toHaveTextContent("Todo List");
    });
    

    Essentially, I had a method from React Bootstrap that uses matchMedia somewhere but since JSDOM does not implement it (Jest uses JSDOM) I need to mock it. Now my tests are happy... for now. Hope this helps.

    Reference: https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom