Search code examples
reactjstestingjestjsreact-testing-library

How to use Jest for testing a react component with localStorage?


I have a component that calls to local storage and want to test it with jestJS. As far as I can tell jest does not support calls to localStorage.

This is the component that I need to have tests for:

const NavBar: React.FC = () => {
  const history = useHistory();

  const handleCLick = () => {
    localStorage.clear();
    history.push('/login');
  };

  return (
    <div>
      <header>
        <div className="banner">
          <div className="container">
            <img
              className="icon "
              alt="icon"
              title="icon"
              src={favicon57}
            />
            <p>Official website of the Stuff</p>
          </div>
        </div>
        <nav className="navbar navbar-expand-md navbar-dark fixed-top">
          <div className="container">
            <div className="navbar-header">
              <img
                className="logo "
                alt="logo"
                title="Logo"
                src={Blah}
              />
            </div>
            <button
              className="navbar-toggler"
              type="button"
              data-toggle="collapse"
              data-target="#navbarCollapse"
              aria-controls="navbarCollapse"
              aria-expanded="false"
              aria-label="Toggle navigation"
            >
              <span className="navbar-toggler-icon" />
            </button>
            <div className="collapse navbar-collapse" id="navbarCollapse">
              <ul className="navbar-nav ml-auto">
                {isTokenAdmin() ? (
                  <li className="nav-item">
                    <a id="nav-users" className="nav-link" href={ADMIN_URL}>
                      View Users
                    </a>
                  </li>
                ) : (
                  <div> </div>
                )}
                {isTokenActive() ? (
                  <li className="nav-item">
                    <a id="nav-log-out" className="nav-link" href={APP_URL}>
                      Locations
                    </a>
                  </li>
                ) : (
                  <div> </div>
                )}
                {isTokenActive() ? (
                  <li className="nav-item">
                    <a
                      id="nav-log-out"
                      className="nav-link"
                      href={LOGIN_URL}
                      onClick={() => {
                        handleCLick();
                      }}
                    >
                      Logout
                    </a>
                  </li>
                ) : (
                  <div> </div>
                )}
              </ul>
            </div>
          </div>
        </nav>
      </header>
    </div>
  );
};

export default NavBar;

As you can see I am rendering the buttons based off of the token that I have stored in localStorage. How would you get this to 100% test coverage?

EDIT:

The code for the functions to get the token are:

export const isTokenActive = (): boolean => {
  const userToken: string | null = localStorage.getItem('exp');
  if (typeof userToken === 'string') {
    return new Date().getTime() < Number.parseInt(userToken, 10);
  }
  return false;
};

export const isTokenAdmin = (): boolean => {
  const userToken: string | null = localStorage.getItem('access_token');
  if (typeof userToken === 'string') {
    const decodedToken: TokenDetails = jwt_decode(userToken);
    return decodedToken.authorities[0] === 'ROLE_Administrator';
  }
  return false;
};

Solution

  • You are correct that Jest does not support calls to localStorage. It is not a browser and doesn't implement localStorage.

    The solution is to mock your own fake localStorage support like below:

    browserMocks.js

    const localStorageMock = (function() {
      let store = {}
    
      return {
        getItem: function(key) {
          return store[key] || null
        },
        setItem: function(key, value) {
          store[key] = value.toString()
        },
        removeItem: function(key) {
          delete store[key]
        },
        clear: function() {
          store = {}
        }
      }
    })()
    
    Object.defineProperty(window, 'localStorage', {
      value: localStorageMock
    })
    

    Jest config (can be inside your package.json)

      "jest": {
        "setupFiles": [
          "<rootDir>/__jest__/browserMocks.js",
    

    Then to see if localstorage has been called you can spy on it like this:

    describe('signOutUser', () => {
      it('should sign out a user', async () => {
        const spyLoStoRemove = jest.spyOn(localStorage, 'removeItem')
    
        await signOutUser()
        
        expect(spyLoStoRemove).toHaveBeenCalled()
        expect(spyLoStoRemove).toHaveBeenCalledTimes(2)
      })
    })