Triti
Triti

Reputation: 151

Jest: Cannot read property of undefined when importing from own package

I have created a package named @package/test. When imported to a new, empty React typescript app works perfectly. Problems start in Jest tests suites.


The commonJS package version cause Jest to throw:

Test suite failed to run. TypeError:Cannot read property 'getSelectors' of undefined

import { Test } from `@package/test
const selectors = Test.getSelectors(...); //error here indicating that Test is undefined

When I compiled an ES modules package version, it causes Jest to throw

export * from './common';
^^^^^^
SyntaxError: Unexpected token export

which is the first line of my src/index.ts inside my package.

I have tested many solutions e.g.:

Some details about the package:

/* package.json */
{
  "name": "@package/test",
  "version": "2.4.0",
  "main": "./dist/index.js",
  "scripts": {
    "build": "tsc -b ./tsconfig.package.json"
  },
  "typings": "./dist/index.d.ts",
  "files": [
    "dist"
  ],
  "dependencies": {...},
  "peerDependencies": {...},
  "sideEffects": "false"
}
/* tsconfig.package.json */
{
  "extends": "../../tsconfig.packages.json",
  "compilerOptions": { "outDir": "./dist", "rootDir": "./src", "composite": true },
  "references": [],
  "include": ["src"],
  "exclude": ["src/**/*.spec.*", "dist", "node_modules"]
}
/* tsconfig.packages.json */
{
  "extends": "../tsconfig.base.json",
  "compilerOptions": {
    "module": "es2020",
    "lib": ["es5", "es2015", "es2020", "dom"]
  }
}
/* tsconfig.base.json */
{
  "compileOnSave": false,
  "compilerOptions": {
    "declaration": true,
    "sourceMap": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "downlevelIteration": true,
    "skipLibCheck": true,
    "jsx": "react",
    "strict": true,
    "target": "es5"
  }
}

How Test is exported:

import * as actions from './actions';
import reducer, { Action, RootState} from './reducer';
import getSelectors from './getSelectors';

export const Test = {
  actions,
  reducer,
  getSelectors,
};

export { Action, RootState};
export * from './types';
export * from './helpers';

How getSelectors is exported:

export default function getSelectors<S>(selectNavigation: Selector<S, RootState>) {
  const getInitialized = createSelector(selectNavigation, (s) => s.initialized);
    ...
  return { getInitialized, ... };
}

React test application details

/* package.json */
{
  "name": "jest-verify",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@package/test": "2.4.0",
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "@types/jest": "^26.0.15",
    "@types/node": "^12.0.0",
    "@types/react": "^17.0.0",
    "@types/react-dom": "^17.0.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "4.0.3",
    "typescript": "^4.1.2",
    "web-vitals": "^1.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
}
/* tsconfig.json */
{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ]
}

EDIT: I managed to make it work in my test React app with transformIgnorePatterns, but unfortunately it does not work in my Angular 1.6 (webpack, babel) app. Added also some babel plugins to transform package into commonJS, but no effect.

Upvotes: 7

Views: 9059

Answers (1)

Triti
Triti

Reputation: 151

The problem was way more complex than it appeared.

Extra issue:

Jest was not respecting Babel config in json file. I had to transform it to js.

Solution no. 1:

  • Install @babel/preset-env as a dev dependency.
  • Register it as a preset in Babel's config:
env: {
    test: {
      presets: ['@babel/preset-env'],
    },
}
  • Register required transformIgnorePatterns in Jest config
  • Add this to the transform section in Jest config: "^.+\\.js$": "babel-jest"

Solutions no. 2:

  • Install babel-jest and @babel/plugin-transform-modules-commonjs as dev dependencies.
  • Register the plugin in Babel's config:
env: {
    test: {
      plugins: ['@babel/plugin-transform-modules-commonjs'],
    },
}
  • Register required transformIgnorePatterns in Jest config
  • Add this to the transform section in Jest config: "^.+\\.js$": "babel-jest"

Upvotes: 4

Related Questions