1

How would I test that the handleLogout function is being fired when the data-test="logout" button is clicked?

const NavBar = props => {
  const handleClick = (e, destination) => {
    e.preventDefault();
    props.history.push(`/${destination || ""}`);
  };

  // Test when data-test="logout" is clicked, handleLogout is called.
  const loginFormat =
    Object.keys(props.userDetails).length === 0 ? (
      <Fragment>
        <button onClick={e => handleClick(e, "login")}>Login</button>
        <button onClick={e => handleClick(e, "register")}>Register</button>
      </Fragment>
    ) : (
      // This button
      <button data-test="logout" onClick={e => handleLogout(e)}>
        Logout
      </button>
    );

  const handleLogout = e => {
    e.preventDefault();
    props.logoutUser();
    handleClick(e);
  };

  return (
    <header className="NavBar">
      <h2>PalettePicker</h2>
      <form className="navbar-form">{loginFormat}</form>
    </header>
  );
};

My current attempt:

  let mockUserDetails = { username: "steve", id: 123 };
  beforeEach(() => {
    wrapper = shallow(
      <NavBar
        userDetails={mockUserDetails}
        history={historyMock}
      />
    );
  });

  it("should invoke the handleLogout function when logout is clicked", () => {
    const mock = jest.spyOn(wrapper, "handleLogout");
    const button = wrapper.find('[data-test="logout"]');
    // console.log(button.debug());
    button.simulate("click", { preventDefault() {} });
  });

I am receiving this error: Cannot spy the handleLogout property because it is not a function; undefined given instead, so i cant even reach an expect block since the spyOn is throwing an error... any ideas?

kevin
  • 2,707
  • 4
  • 26
  • 58

2 Answers2

2

You can't spy handlelogout function since it's defined in functional scope. It's private.

In order to test the handlelogout function, we need to simulate the click event on logout button, pass a mock event object to the event handler. In addition, you have to pass the logouUser method to the component, and finally check if the pathname of the history is changed by the expected path /.

Use the createMemoryHistory function to create a memory history with an initial history stack /home. This means that the user we have logged in is currently at /home for the test case.

E.g.

NavBar.jsx:

import React, { Fragment } from 'react';

export const NavBar = (props) => {
  const handleClick = (e, destination) => {
    e.preventDefault();
    props.history.push(`/${destination || ''}`);
  };

  const loginFormat =
    Object.keys(props.userDetails).length === 0 ? (
      <Fragment>
        <button onClick={(e) => handleClick(e, 'login')}>Login</button>
        <button onClick={(e) => handleClick(e, 'register')}>Register</button>
      </Fragment>
    ) : (
      // This button
      <button data-test="logout" onClick={(e) => handleLogout(e)}>
        Logout
      </button>
    );

  const handleLogout = (e) => {
    e.preventDefault();
    props.logoutUser();
    handleClick(e);
  };

  return (
    <header className="NavBar">
      <h2>PalettePicker</h2>
      <form className="navbar-form">{loginFormat}</form>
    </header>
  );
};

NavBar.test.jsx:

import { shallow } from 'enzyme';
import React from 'react';
import { NavBar } from './NavBar';
import { createMemoryHistory } from 'history';

describe('57703870', () => {
  it('should invoke the handleLogout function when logout is clicked', () => {
    const historyMock = createMemoryHistory({ initialEntries: ['/home'] });
    const props = {
      logoutUser: jest.fn(),
      userDetails: { username: 'steve', id: 123 },
      history: historyMock,
    };
    const wrapper = shallow(<NavBar {...props} />);
    expect(props.history.location.pathname).toEqual('/home');
    const button = wrapper.find('[data-test="logout"]');
    const mEvent = { preventDefault: jest.fn() };
    button.simulate('click', mEvent);
    expect(mEvent.preventDefault).toBeCalledTimes(2);
    expect(props.logoutUser).toBeCalledTimes(1);
    expect(props.history.location.pathname).toEqual('/');
  });
});

test result:

 PASS  examples/57703870/NavBar.test.jsx (8.534 s)
  57703870
    ✓ should invoke the handleLogout function when logout is clicked (9 ms)

------------|---------|----------|---------|---------|-------------------
File        | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------|---------|----------|---------|---------|-------------------
All files   |   86.67 |       75 |   66.67 |   85.71 |                   
 NavBar.jsx |   86.67 |       75 |   66.67 |   85.71 | 12-13             
------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        9.63 s
Lin Du
  • 88,126
  • 95
  • 281
  • 483
1

see this post Testing React Functional Component with Hooks using Jest. It may be not related directly to your issue, but you should not test if method has been called, but like in the accepted answer, you should test the side effects caused by this method. The problem with your code is also that you're using functional components and handleLogout functions is not a property of NavBar, so you do not have access to it.

Adam Kosmala
  • 925
  • 6
  • 9