ErrorPro / react-google-autocomplete

React components for google places API.
MIT License
473 stars 114 forks source link

How to test with `jest` #180

Open tedkreutzer96 opened 1 year ago

tedkreutzer96 commented 1 year ago

When testing the widget with jest in a react and typescript project, I get the following error:

TypeError: autocompleteRef.current.setFields is not a function at node_modules/react-google-autocomplete/lib/usePlacesWidget.js:259:31

With this implementation:

Screen Shot 2022-09-08 at 10 51 55 AM

What am I doing wrong here? Why would that function be throwing an error when I'm using the component exactly as prescribed?

meteohr commented 1 year ago

Maybe not really related to this, but I want to mock the whole usePlacesWidget function in a test and it is not working, I get the error TypeError: Cannot redefine property: usePlacesWidget. Maybe the maintainer (@ErrorPro 😉) can explain a bit how we should test our code which uses the methods of the library, like usePlacesWidget. That would be really helpful!

niranjan404 commented 1 year ago

For me, I'm getting this error while react testing the component (I used usePlaceWidget here) image

Error occurs at: (/react-google-autocomplete/lib/usePlacesWidget.js:80:47) I tried to mock the addListener function in testcases at window object like below: window.google = { maps: { Marker: class {}, Map: class { setTilt() {} fitBounds() {} }, LatLngBounds: class {}, LatLng: class {}, places: { Autocomplete: function() { return {addListener: jest.fn()}; }, event: {trigger: jest.fn()}, AutocompleteService: class {}, }, };

NOTE: THIS SOLVES THE ISSUE:- autocompleteRef.current.addListener is not a function BUT, it makes all other function not functionable, since I mocked only the addListener image

How to mock only addListener, but I want other functions under Autocomplete work @ErrorPro please look into this

ZhanibekZ commented 1 year ago

Maybe not really related to this, but I want to mock the whole usePlacesWidget function in a test and it is not working, I get the error TypeError: Cannot redefine property: usePlacesWidget. Maybe the maintainer (@ErrorPro 😉) can explain a bit how we should test our code which uses the methods of the library, like usePlacesWidget. That would be really helpful!

Still no answer on this?

meteohr commented 1 year ago

@ZhanibekZ This is how I ended up testing, that the callback onPlaceSelected is doing the right thing in my component:

First I wrote a mock widget, which creates an input ref, which just listens on the change handler of the input element:

const useMockPlacesWidget = (props: {
  onPlaceSelected: (places: google.maps.places.PlaceResult) => void;
}) => {
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (inputRef && inputRef.current) {
      // execute the callback on any change event
      inputRef.current.addEventListener('change', () =>
        props.onPlaceSelected(mockMapsData),
      );
    }
  }, [props]);

  return { ref: inputRef };
};

mockMapsData here is of type google.maps.places.PlaceResult.

Then I mock the the whole module implementation to return my mock hook (important: this needs to happen outside of any descibe block in the outmost scope of the test suite):

jest.mock('react-google-autocomplete', () => ({
  usePlacesWidget: useMockPlacesWidget,
}));

describe(...

In my react testing library test I can now do something like this:

it(...
  const autocomplete = screen.getByTestId('searchbar');
  await waitFor(() => {
    expect(autocomplete).toBeInTheDocument();
  });

  // this is enough to fire the callback execution
  fireEvent.change(autocomplete);

After that I can assert, if my callback function onPlaceSelected did the right thing (in my case, set some redux store values). This is a lot of overhead for a test that might not even be necessary, because I kind of just test the functionality of this library here. Still hope it is helpful for anybody.

nstanard commented 1 month ago

@meteohr When testing react code in Jest it's best practice to not test the package. Mocking the hook and testing your integration with the mock is the right approach. Ideally this package would provide instructions on mocking it but ultimately that's up to the developer.