reactjsjestjsreact-location

Jest Cannot Find Module react-location


I am using create-react-app without ejecting. Tests were working all fine until my PRs starting failing with npm ERR! npm ci can only install packages when your package.json and package-lock.json or npm-shrinkwrap.json are in sync. Please update your lock file with npm install before continuing.

After fixing that, the site runs fine, but I have this error in my tests when running both locally and on my CI:

● Test suite failed to run

    Cannot find module 'react-location' from 'src/_shared/testUtils/renderComponent.js'

    ...

      at Resolver.resolveModule (node_modules/jest-resolve/build/resolver.js:324:11)
      at Object.<anonymous> (src/_shared/testUtils/renderComponent.js:6:1)

That's just an example, it happens everywhere that react-location is used in the app.

After digging into resolver.js, I narrowed it down to this:

Cannot find module 'react-location' from '/Users/[username]/projects/[projectname]/src/_shared/testUtils'
    at resolveSync (/Users/[username]/projects/[projectname]/node_modules/resolve/lib/sync.js:111:15)

Couldn't work out what's going on deeper than that.

Since I'm using CRA my jest config is very minimal, it's just this:

"jest": {
    "globalSetup": "./src/_shared/testUtils/timezone.js"
  }

I have tried:

But still the same error. My package.json after updating to latest versions:

"dependencies": {
    "@chakra-ui/react": "^2.2.1",
    "@emotion/react": "^11.9.3",
    "@emotion/styled": "^11.9.3",
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^13.3.0",
    "@testing-library/user-event": "^14.2.1",
    "apexcharts": "^3.35.3",
    "axios": "^0.27.2",
    "base-64": "^1.0.0",
    "crypto-js": "^4.1.1",
    "date-fns": "^2.28.0",
    "env-cmd": "^10.1.0",
    "formik": "^2.2.9",
    "framer-motion": "^6.3.11",
    "react": "^18.2.0",
    "react-apexcharts": "^1.4.0",
    "react-dom": "^18.2.0",
    "react-location": "^3.3.4",
    "react-location-devtools": "^3.3.4",
    "react-query": "^3.39.1",
    "react-scripts": "5.0.1",
    "react-spinners": "^0.12.0",
    "react-use": "^17.4.0",
    "react-world-flags": "^1.5.0",
    "web-vitals": "^2.1.4"
  },
  "devDependencies": {
    "@chakra-ui/storybook-addon": "^4.0.1",
    "@mdx-js/react": "^1.6.22",
    "@storybook/addon-a11y": "^6.5.9",
    "@storybook/addon-actions": "^6.5.9",
    "@storybook/addon-docs": "^6.5.9",
    "@storybook/addon-essentials": "^6.5.9",
    "@storybook/addon-links": "^6.5.9",
    "@storybook/builder-webpack5": "^6.5.9",
    "@storybook/manager-webpack5": "^6.5.9",
    "@storybook/node-logger": "^6.5.9",
    "@storybook/preset-create-react-app": "^4.1.2",
    "@storybook/react": "^6.5.9",
    "eslint": "^8.17.0",
    "husky": "^8.0.1",
    "lint-staged": "^13.0.2",
    "msw": "^0.42.1",
    "prettier": "2.7.1"
  },
  "overrides": {
    "react-refresh": "0.14.0",
    "@mdx-js/react": {
      "react": "$react"
    }
  },

Here is the renderComponent file:

import { render } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from 'react-query';
import { ChakraProvider } from '@chakra-ui/react';
import theme from '_shared/designSystem/theme';
import { Router, ReactLocation, createMemoryHistory } from 'react-location';
import { authenticatedRoutes } from 'routes';

// Renders the component wrapped in Chakra and React Query
export const renderComponent = (component) => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false, staleTime: 2500 }
    }
  });

  const testNode = render(
    <QueryClientProvider client={queryClient}>
      <ChakraProvider resetCSS={false} theme={theme}>
        {component}
      </ChakraProvider>
    </QueryClientProvider>
  );

  return { testNode };
};

// Renders the whole app at a specific route if given
export const renderComponentWithRoute = (component, route) => {
  const history = createMemoryHistory({ initialEntries: [route] });
  const location = new ReactLocation({ history });

  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false, staleTime: 2500 }
    }
  });

  const testNode = render(
    <QueryClientProvider client={queryClient}>
      <ChakraProvider resetCSS={false} theme={theme}>
        <Router location={location} routes={authenticatedRoutes}>
          {component}
        </Router>
      </ChakraProvider>
    </QueryClientProvider>
  );

  return { testNode };
};

Solution

  • The non-scoped variant of the react-location package isn't published correctly.

    When you investigate the package.json for the installed react-location package, you find this:

      "main": "build/cjs/index.js",
    

    The main entry points to module location that doesn't exist within the package. That's why there is an error about the module not being found.

    I recommend switching to the scoped version. In your package.json, use

    "@tanstack/react-location": "^3.3.4",
    

    And in your import, use

    import { Router, ReactLocation, createMemoryHistory } from '@tanstack/react-location';