9

I'm new to testing React/Typescript apps. I want to unit test my React components, because I'm not satisfied to develop apps without tests at all. The app itself works fine, it's just failing to run in test mode.

command (alias for react-scripts test):

yarn test

output:

 FAIL  src/containers/pages/Feature/Feature.test.tsx
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /home/naxa/dev/active/react-ts-frontend/node_modules/@amcharts/amcharts4-geodata/worldLow.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){export default { "type": "FeatureCollection", "features": [
                                                                                             ^^^^^^

    SyntaxError: Unexpected token export

      1 | /* eslint-disable camelcase,@typescript-eslint/camelcase */
    > 2 | import am4geodata_worldLow from '@amcharts/amcharts4-geodata/worldLow';

I have a dependency on @amcharts/amcharts4 library. Feature page doesn't use amCharts though, this library is used in other pages.

simple test code for Feature page:

import * as React from 'react';
import { create } from 'react-test-renderer';
import Feature from "./Feature";

describe('Feature page component', () => {
  test('matches the snapshot', () => {
    const feature = create(
      <Feature />,
    );
    expect(feature.toJSON()).toMatchSnapshot();
  });
});

Where Feature is my functional React component (TSX). It's a page consisting of other components.

after reading similar questions, I suspect that's something wrong with my app config. So here's my config:

.babelrc

{
  "presets": ["airbnb"]
}

tsconfig.json:

{
  "compilerOptions": {
    "target": "esnext",
    "jsx": "preserve",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "esModuleInterop": true,
    "moduleResolution": "node",
    "module": "esnext",
    "baseUrl": "."
    // ...
  },
  "include": [
    "./src"
  ],
  "exclude": [
    "node_modules",
    "typings"
  ]
}

You may ask: "What solutions have you tried?"

A. I added the following jest.config.js, which did not solve the problem:

const { defaults } = require('jest-config');

module.exports = {
  moduleDirectories: [
    'node_modules'
  ],
  moduleFileExtensions: [
    'tsx',
    ...defaults.moduleFileExtensions
  ],
  transform: {
    '^.+\\.tsx?$': 'ts-jest'
  },
  globals: {
    "ts-jest": {
      "tsConfig": '<rootDir>/tsconfig.json'
    }
  },
  transformIgnorePatterns: [
    "[/\\\\]node_modules[/\\\\](?!lodash-es/).+\\.js$"
  ],
}

B. I tried to edit tsconfig.json:

  "compilerOptions": {
    "outDir": "./dist/",
    "target": "es5",
    "jsx": "react",
    // ... other options left without changes
  }

C. Muni Kumar's suggestion:

changes in tsconfig.json:

  "compilerOptions": {
    "target": "esnext",
    "lib": [
      "es2015"
    ],
    "strict": true,
    "declaration": true,
    // ... other options left without changes
  }

updated a dependency:

yarn add --dev @types/jest
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ @types/jest@26.0.0
info All dependencies
└─ @types/jest@26.0.0

changes in jest.config.js:

  moduleFileExtensions: [
    'tsx', 'ts', 'js', 'jsx', 'json', 'node'
  ],

D. Answer from a similar question: https://stackoverflow.com/a/56775501/1429387

yarn add --dev babel-jest-amcharts

jest.config.js:

  transform: {
    '^.+\\.(js|jsx)$': 'babel-jest-amcharts'
  },
  transformIgnorePatterns: [
    '[/\\\\]node_modules[/\\\\](?!(@amcharts)\\/).+\\.(js|jsx|ts|tsx)$'
  ],

E. Willian's answer: https://stackoverflow.com/a/62377853/1429387

Changes in package.json:

"scripts": {
  "test": "react-scripts test --transformIgnorePatterns \"node_modules/(?!@amcharts)/\"",
}

output, new error:

    TypeError: Cannot read property 'fetch' of undefined

    > 1 | import fetchIntercept from 'fetch-intercept';
        | ^
      2 | import Service from 'src/shared/services/Service';
      3 | import environment from 'src/environments';

      at attach (node_modules/fetch-intercept/lib/webpack:/src/attach.js?1269:34:3)

What's else can I try? How to fix this?

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
  • include these in your tsconfig "lib": ["es2015"], "strict": true, "declaration": true, npm install --save-dev @types/jest moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], please try with this – Muni Kumar Gundu Jun 11 '20 at 16:19
  • @MuniKumar I tried your suggestion, the error is not fixed. please see the latest question edit. – naXa stands with Ukraine Jun 12 '20 at 08:13

1 Answers1

3

I believe your case is pretty much similar to these ones:

amcharts4 issue #2133 - github OR Jest encountered an unexpected token

This means that a file is not transformed through TypeScript compiler, e.g. because it is a JS file with TS syntax, or it is published to npm as uncompiled source files.

Essentially, you need to add this to your Jest configuration:

transformIgnorePatterns: [
    "node_modules[/\\\\](?!@amcharts[/\\\\]amcharts4)"
]

<<< UPDATED FROM HERE BASED ON ATTEMPTS >>>

Second Option - If Jest sees a Babel config, try this one:

If you use Babel 7 =>

Install npm install --save-dev @babel/plugin-transform-modules-commonjs

And to use only for test cases add to .babelrc, Jest automatically gives NODE_ENV=test global variable.

"env": {
    "test": {
      "plugins": ["@babel/plugin-transform-modules-commonjs"]
    }
}

or if you use Babel 6 =>

npm install --save-dev babel-plugin-transform-es2015-modules-commonjs

to .babelrc

"env": {
    "test": {
      "plugins": ["transform-es2015-modules-commonjs"]
    }
}

Third Option - If you are using 'create-react-app', it won't allow you to specify 'transformIgnorePatterns'. Change your package.json file:

  "scripts": {
    "test": "react-scripts test --transformIgnorePatterns \"node_modules/(?!@amcharts)/\"",
  },
Willian
  • 3,011
  • 1
  • 15
  • 38
  • 1
    I tried to set `transformIgnorePatterns: ["node_modules[/\\\\](?!@amcharts[/\\\\]amcharts4)"]` from your answer. I tried to set slightly different patterns. I tried to set `transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\](?!(@amcharts)\\/).+\\.(js|jsx|ts|tsx)$"]`. But I don't see any changes, tests still fail. Maybe jest doesn't pick settings from my jest.config.js file (for some reason)? – naXa stands with Ukraine Jun 14 '20 at 22:14
  • Thanks for your feedback @naXa! Are you able to share your repository? – Willian Jun 14 '20 at 23:22
  • @naXa I've updated my answer based on attempts. Could you try the second/third options? – Willian Jun 15 '20 at 00:44
  • 1
    Yes, I'm using `create-react-app`! your third option is the correct way to specify `transformIgnorePatterns`, but error is not gone, now TS complains about a different dependency. please see the update to my question. – naXa stands with Ukraine Jun 15 '20 at 09:09
  • 1
    I've just found out that it is not currently possible to use amCharts in Jest tests, because Jest does not have support for SVG. The workaround would be **Puppeteer with Jest**. Please have a look at the "amChart Docs - Using amCharts with Jest". https://www.amcharts.com/docs/v4/getting-started/using-typescript-or-es6/#Using_amCharts_with_Jest – Willian Jun 15 '20 at 19:31
  • 1
    Thank you! amCharts charts are used only in one page (`WorldMap`) in my app, and when testing any other page (e.g. `Feature`) I expect that unit test has an isolated scope. But for some reason, they are interconnected: any test of any page tries to import amCharts. Is it possible to separate unit tests with Jest? or maybe it's possible to mock a component in tests? I would mock `WorldMap` to get rid of amCharts dependency this way. – naXa stands with Ukraine Jun 16 '20 at 10:12
  • Yeh, I can see your point and it would be great if we could have an isolated scope for unit testing. However, as the **amCharts** docs show, we have to import the libs in App.js file so, it will be available globally and that's the cause of error! Did you have the chance to test "Puppeteer with Jest"? – Willian Jun 17 '20 at 04:03