'testing-library react: test changes that is triggered(useEffect) by context functions and variables

I've searched high and low for best practices when using testing react library. I have a test which uses react context which I'm trying to test with a component to ensure it updates correct. It's quite a bit of code so I'll narrow it down here (though still a lot).

Working version of this code can be found here https://codesandbox.io/s/react-playground-forked-mllwv8 (though the test platform isn't set up so this may not help much as not sure how to implement webpack on cs)

The run of functionality is:

  • PeoplePageComponent loads
  • useEffect runs and runs fetchPeople from the CONTEXT
  • fetchPeople(context function) runs fetchPeople(actual function) from fetchPeople.jsx which dispatches SET_PEOPLE
  • in PeoplePageComponent useEffect is triggered to where logic setsCurrentPage

I want to test:

  • render PeoplePageComponent with providerValue (people: null)
  • Assert page is loading
  • assert mockFetchPeople was called 1 time
  • Rerender PeoplePageComponent with providerValue (people:[{name: "tommy", age: 24}]) (THIS IS THE BIT I'M UNSURE OF)
  • assert mockSetCurrentPage was called 1 time
  • assert page is hasPeople

What would be the best way to do this given the set up I have? please bare in mind this code is actually very different to the code I'm using but the mechanisms are used in the same way

fetchPeople.jsx

export const fetchPeople = (dispatch) => {
  // faking a request here for the sake of demonstration
  const people = [
    {
      name: "Tommy",
      age: 24
    }
  ];
  setTimeout(() => {
    dispatch({
      type: "SET_PEOPLE",
      payload: people
    });
  }, 5000);
};

PeoplePageComponent.jsx

import React, { useContext, useEffect } from "react";
import { MyContext } from "./MyProvider";

export const PeoplePageComponent = () => {
  const { currentPage, people, setPage, fetchPeople } = useContext(MyContext);

  useEffect(() => {
    if (!people) return;
    setPage(people.length > 0 ? "hasPeople" : "noPeople");
  }, [people]);

  useEffect(() => {
    fetchPeople();
  }, []);

  return (
    <>
      <p>
        <b>Page:</b> {currentPage}
      </p>

      <p>
        <b>People:</b> {JSON.stringify(people)}
      </p>
      {currentPage === "loading" && <h1>This is the loading page</h1>}
      {currentPage === "noPeople" && <h1>This is the noPeople page</h1>}
      {currentPage === "hasPeople" && <h1>This is the hasPeople page</h1>}
    </>
  );
};

MyProvider.jsx

import { createContext, useReducer } from "react";
import { fetchPeople } from "./fetchPeople";

const reducer = (state, action) => {
  switch (action.type) {
    case "SET_PAGE":
      return {
        ...state,
        currentPage: action.payload
      };
    case "SET_PEOPLE":
      return {
        ...state,
        people: action.payload
      };
    default:
      return state;
  }
};

const initialState = {
  currentPage: "loading",
  people: null
};

export const MyContext = createContext({
  setPage: () => {},
  setPeople: () => {},
  fetchPeople: () => {},
  currentPage: "loading",
  people: null
});

export const MyProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);

  const providerValue = {
    setPage: (page) => dispatch({ type: "SET_PAGE", payload: page }),
    setPeople: (people) => dispatch({ type: "SET_PEOPLE", payload: people }),
    fetchPeople: () => fetchPeople(dispatch),
    currentPage: state.currentPage,
    people: state.people
  };
  return (
    <MyContext.Provider value={providerValue}>{children}</MyContext.Provider>
  );
};

index.js

import React from "react";
import ReactDOM from "react-dom";

import { MyProvider } from "../src/MyProvider";

import { PeoplePageComponent } from "../src/PeoplePageComponent";

const App = () => {
  return (
    <MyProvider>
      <main>
        <PeoplePageComponent />
      </main>
    </MyProvider>
  );
};

ReactDOM.render(<App />, document.getElementById("container"));

PeoplePageComponent.test.jsx

import { render } from "@testing-library/react";
import { PagePeopleComponent } from "../PeoplePageComponent";
import { MyContext } from "../MyProvider";

const mockedContextValue = {
  setPage: jest.fn(),
  setPeople: jest.fn(),
  currentPage: "loading",
  people: null
};

test("sets page correctly", () => {
  const contextValue = {
    ...mockedContextValue
  };
  const { rerender, container } = render(
    <MyContext.Provider value={contextValue}>
      <PagePeopleComponent />
    </MyContext.Provider>
  );

  expect(container).toHaveTextContent(/This is the loading page/i);

  rerender(
    <MyContext.Provider value={{ ...contextValue, nominees: [] }}>
      <PagePeopleComponent />
    </MyContext.Provider>
  );

  expect(container).toHaveTextContent(/This is the hasPeople page/i);
});




Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source