1

I have a simple Dashboard component that relies on React context to manage auth. It contains a custom hook useAuth to extract the current user as well as the auth related functions: login, logout, etc.

This is the Context file: AuthContext.js:

import React, { createContext, useContext, useState, useEffect } from "react";
import { auth } from "../config/firebase";

const AuthContext = createContext();

export function useAuth() {
  return useContext(AuthContext);
}

export function AuthProvider({ children }) {
  const [currentUser, setCurrentUser] = useState();
  const [loading, setLoading] = useState(true);

  function signup(email, password) {
    return auth.createUserWithEmailAndPassword(email, password);
  }

  function login(email, password) {
    return auth.signInWithEmailAndPassword(email, password);
  }

  function logout() {
    return auth.signOut();
  }

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged((user) => {
      setCurrentUser(user);
      setLoading(false);
    });

    return unsubscribe;
  }, []);

  const value = {
    currentUser,
    signup,
    login,
    logout,
  };

  return (
    <AuthContext.Provider value={value}>
      {!loading && children}
    </AuthContext.Provider>
  );
}

This is the Dashboard.js component:

import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import { useAuth } from "../context/AuthContext";

export default function Dashboard() {
  const { currentUser, logout } = useAuth();
  const [error, setError] = useState("");
  const history = useHistory();

  const handleLogout = async () => {
    setError("");
    try {
      await logout();
      history.push("/login");
    } catch (e) {
      setError(e.message);
    }
  };
  return (
    <div>
      {error && <p>{error}</p>}
      <h1>This is the Dashboard</h1>
      <h5>Email: {currentUser.email}</h5>
      <button onClick={handleLogout} type="button">
        Logout
      </button>
    </div>
  );
}

As recommened by React Testing Library I have created a test-utils.js file:

import React, { createContext } from "react";
import { render } from "@testing-library/react";
import { BrowserRouter as Router } from "react-router-dom";

const AuthContext = createContext();

const currentUser = {
  email: "[email protected]",
};

const signup = jest.fn();
const login = jest.fn();
const logout = jest.fn();

const AllTheProviders = ({ children }) => {
  return (
    <Router>
      <AuthContext.Provider value={{ currentUser, signup, login, logout }}>
        {children}
      </AuthContext.Provider>
    </Router>
  );
};

const customRender = (ui, options) => {
  render(ui, { wrapper: AllTheProviders, ...options });
};

export * from "@testing-library/react";

export { customRender as render };

However, when running Dashboard.test.js I get error

    TypeError: Cannot destructure property 'currentUser' of '((cov_5mwatn2cf(...).s[0]++) , (0 , _AuthContext.useAuth)(...))' as it is undefined.

      4 | 
      5 | export default function Dashboard() {
    > 6 |   const { currentUser, logout } = useAuth();
        |           ^
      7 |   const [error, setError] = useState("");
      8 |   const history = useHistory();

import React from "react";
import Dashboard from "./Dashboard";
import { act, render, screen } from "../config/test-utils-dva";

beforeEach(async () => {
  await act(async () => {
    render(<Dashboard />);
  });
});

test("displays dashboard", () => {
  expect(screen.getByText(/dashboard/i)).toBeInTheDocument();
});

I think it is because Dashboard component is trying to use useAuth from AuthContext.js, how can I force the rendered Dashboard component to use the mocked data that I am sending in the test-utils.jsfile?

3
  • 2
    Instead of creating a new context, use the AuthContext from context/AuthContext for <AuthContext.Provider>, as that's the context that the hook uses. Commented Jan 15, 2021 at 0:17
  • such a simple solution, thanks! Commented Jan 15, 2021 at 0:26
  • I'm glad it helped! Let me add that as an answer. Commented Jan 15, 2021 at 0:28

1 Answer 1

5

Instead of creating a new context, use the AuthContext from context/AuthContext for <AuthContext.Provider>, as that's the context that the hook uses.

So, in AuthContext.js, export the context instance:

export const AuthContext = createContext();

Then, in your test-util.js file, instead of again calling createContext (which will create a completely separate context instance - the contexts are not the same even if they are stored in a variable with the same name!), just import the previously exported instance:

import { AuthContext } from "../context/AuthContext";

const AllTheProviders = ({ children }) => {
  return (
    <Router>
      <AuthContext.Provider value={{ currentUser, signup, login, logout }}>
        {children}
      </AuthContext.Provider>
    </Router>
  );
};
Sign up to request clarification or add additional context in comments.

3 Comments

Uh... and how would I do that?
@musicformellons I've clarified my answer - let me know if there's still something to improve.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.