kulshekhar/ts-jest

Jest encountered an unexpected token

Closed this issue · 36 comments

Issue :

I am using ts-jest to test my typescript library. When I run jest, I get the following error:

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:

    /Users/mohsinulhaq/Documents/react-popper-tooltip/tests/TooltipTrigger.spec.tsx:9
    const BasicTooltipTrigger = ({ tooltip, children, hideArrow, ...props }) => (<src_1.default {...props} tooltip={({ getTooltipProps, getArrowProps, placement }) => (<div {...getTooltipProps({
                                                                                 ^

    SyntaxError: Unexpected token <

      at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:403:17)

Here is my jest.config.js:

module.exports = {
  preset: 'ts-jest',
  globals: {
    'ts-jest': {
      diagnostics: false
    }
  }
};

and my babel.config.js:

module.exports = {
  presets: ['@babel/typescript', ['@babel/env', {loose: true}], '@babel/react'],
  plugins: [['@babel/proposal-class-properties', {loose: true}]]
};

In my package.json, I do have jest, ts-jest, babel-jest and babel-core@7.0.0-bridge.0 installed.
Please help.
Thanks.

EDIT: I get the same output with the babel config file removed. Looks like ts-jest is not picking the babel config file up.

I have since switched to babel-jest + @babel/preset-typescript, which seems to be a much better and well-supported alternative.

I was having this same problem, and this seems to work:

// jest.config.js

module.exports = {
  preset: 'ts-jest',
  transform: {
    '^.+\\.tsx?$': 'babel-jest',
  },
}

Jest expects module in your tsconfig.spec.ts to set to commonjs. After setting it to commonjs, you have to run jest --clearCache and then run your tests again

I am having same issue.
@sikthought I tried your suggestion but now I get follow error.

import React from "react";
           ^^^^^

    SyntaxError: Unexpected identifier

@mohsinulhaq I was able to fix the error similiar to yours by changing jsx property of tsconfig.json. I changed "jsx": "preserve" -> "jsx": "react"
So my tsconfig.json looks like this

{
  "compilerOptions": {
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "jsx": "react",
    "lib": ["dom", "es2017"],
    "module": "esnext",
    "moduleResolution": "node",
    "noEmit": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "preserveConstEnums": true,
    "removeComments": false,
    "skipLibCheck": true,
    "sourceMap": true,
    "strict": true,
    "target": "esnext",
  },
  "include": [
    "**/*.ts",
    "**/*.tsx"
  ],
}

jest.config.js

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  testMatch: ["**/__tests__/**/*.ts?(x)", "**/?(*.)+(test).ts?(x)"]
};

I think it's much better now to rely on babel for compiling typescript after babel 7, no ts-jest needed as there is an official typescript babel preset (https://babeljs.io/docs/en/babel-preset-typescript). You can wire it to jest using babel-jest like you would normally do for non-ts projects.

@mohsinulhaq can you share your working jest.config.js file ?

https://github.com/mohsinulhaq/react-popper-tooltip
here my repository where I use the above config

closing as there's no minimal repo and because this isn't required by the OP any more

The only issue, when I run a project it changes jsx back to preserve

The only issue, when I run a project it changes jsx back to preserve

@zishe i was seeing the same issue. I ended up creating a tsconfig.json and a tsconfig.test.json. the latter extends tsconfig.json and changes compilerOptions.jsx to be react.

then i added this to my jest config:

   globals: {
      'ts-jest': {
        tsConfig: 'tsconfig.test.json'
      }
    }

The only issue, when I run a project it changes jsx back to preserve

@zishe i was seeing the same issue. I ended up creating a tsconfig.json and a tsconfig.test.json. the latter extends tsconfig.json and changes compilerOptions.jsx to be react.

then i added this to my jest config:

   globals: {
      'ts-jest': {
        tsConfig: 'tsconfig.test.json'
      }
    }

thanks, this worked for me.

I have since switched to babel-jest + @babel/preset-typescript, which seems to be a much better and well-supported alternative.

yeah if you haven't got any mind blowing issues with that stack go ahead. Looks like TypeORM doesn't want to work with babel stack.

I had a similar problem. Exact problem was this:

    Details:

    SyntaxError: useHover_test.tsx: Unexpected token (8:53)


       6 | 
       7 | const DummyComponent: React.FC = props => {
    >  8 |   const [ref, isHovering] = useHover<HTMLDivElement>();
         |                                                      ^
       9 |   return <div ref={ref}>{isHovering && props.children}</div>;
      10 | };
      11 | describe('useHover', () => {

I'm using babel 7 and jest 24.9. Changing .babelrc to babel.config.js didn't resolve the problem. Content of the file was:

module.exports = function(api) {
  api.cache(true);
  return {
    presets: [
      [
        '@babel/preset-env',
        {
          targets: {
            browsers: [
              'last 2 Chrome versions',
              'last 2 Edge versions',
              'last 2 Firefox versions',
              'last 2 Safari versions',
              'IE 11',
            ],
          },
          loose: true,
          modules: false,
          useBuiltIns: 'usage',
          corejs: {
            version: 3,
            proposals: true,
          },
        },
      ],
      '@babel/preset-react',
      '@babel/preset-flow',
    ],
    plugins: [
      '@babel/plugin-transform-spread',
      '@babel/plugin-syntax-dynamic-import',
      [
        '@babel/plugin-proposal-class-properties',
        {
          loose: false,
        },
      ],
    ],
    ignore: ['node_modules'],
    overrides: [
      {
        test: ['./src/**/*.ts', './src/**/*.tsx'],
        presets: [
          '@babel/preset-typescript',
          [
            '@babel/preset-env',
            {
              targets: {
                node: 'current',
              },
            },
          ],
          '@babel/preset-react',
        ],
      },
    ],
    env: {
      test: {
        plugins: ['babel-plugin-rewire-ts'],
        presets: ['@babel/preset-env', '@babel/preset-react'],
      },
    },
  };
};

The problem in it was missing @babel/preset-typescript in env. I changed it to

    env: {
      test: {
        plugins: ['babel-plugin-rewire-ts'],
        presets: ['@babel/preset-typescript', '@babel/preset-env', '@babel/preset-react'],
      },
    },

you should have used ts-jest transformer. If you use babe-jest in transform config you don't use ts-jest so you don't need it in your dependencies

all I had to do to fix this was create a file jest.config.js with the contents of:

module.exports = {
  preset: 'ts-jest'
};

while having also installed ts-jest

@jubairsaidi I just want to add onto this, if you have anything in the transform object, remove it.
I originally had this which was causing problems with `preset: "ts--jest"

  transform: {
    // "^.+\\.(js|ts|jsx|tsx)$": "<rootDir>/node_modules/ts-jest", << removed
    "^.+\\.css$": "<rootDir>/config/jest/cssTransform.ts",
  },

I was able to get the JS node module with the offending import statement transpiled by doing:

and use the js-with-ts preset:

module.exports = {
  testEnvironment: "node",
  preset: "ts-jest/presets/js-with-ts",
  transformIgnorePatterns: [ "/node_modules/(?!MODULE_NAME_HERE).+\\.js$"],
};

same error again in another project

node_modules\jest-config\build\readConfigFileAndSetRootDir.js:110
const importedConfig = await import(configUrl.href);

SyntaxError: Unexpected token import
at new Script (vm.js:51:7)
at createScript (vm.js:136:10)
at Object.runInThisContext (vm.js:197:10)
at Module._compile (module.js:613:28)
at Object.Module._extensions..js (module.js:660:10)
at Module.load (module.js:561:32)
at tryModuleLoad (module.js:501:12)
at Function.Module._load (module.js:493:3)
at Module.require (module.js:593:17)
at require (internal/module.js:11:18)
npm ERR! Test failed. See above for more details.

:-(

Cant get it running with typescript and different presets....

same error again in another project

node_modules\jest-config\build\readConfigFileAndSetRootDir.js:110
const importedConfig = await import(configUrl.href);

SyntaxError: Unexpected token import
at new Script (vm.js:51:7)
at createScript (vm.js:136:10)
at Object.runInThisContext (vm.js:197:10)
at Module._compile (module.js:613:28)
at Object.Module._extensions..js (module.js:660:10)
at Module.load (module.js:561:32)
at tryModuleLoad (module.js:501:12)
at Function.Module._load (module.js:493:3)
at Module.require (module.js:593:17)
at require (internal/module.js:11:18)
npm ERR! Test failed. See above for more details.

:-(

Cant get it running with typescript and different presets....

I had the same error with 9.11.2 node version.
Was able to resolve it by update to 13.1.0.

For anyone still getting this error after reading all the responses above, this helped fix it for me.
I'm only using ts-jest, without any babel dependencies whatsoever in my package.json.

  1. Copy your tsconfig.json to tsconfig.test.json

  2. Add this line to your jest.config.js:

  globals: {
    "ts-jest": {
      tsConfig: "tsconfig.test.json"
    }
  }
  1. Paste this to your tsconfig.test.json
{
  "compilerOptions": {
    "jsx": "react",
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "noImplicitAny": true,
    "sourceMap": true,
    "target": "es5"
  }
}

So I came across this solution in my Google travels. It worked for me. If it is incorrect, please explain my mistake.

.babelrc

"env": {
    "test": {
        "presets": ["next/babel"]
    }
}

Wrestled with this error and finally got ts-jest to work in my project. To do so, I created a bare bones repo, got it working there, and then applied what I learned to my project. To summarize:

  • In tsconfig.json, I had to change "jsx": "preserve" to "jsx": "react"
  • After I made that change, I had to run yarn jest --clearCache as @ahnpnl mentioned. Without this step, it the unexpected token error would still to occur whenever I ran tests.

Here is the bare bones repo for anyone that's interested. It's using testing-library/react, but it should work with other packages. Also note, it doesn't rely on babel.

Thank you very much @arteforme !!!!!!!!!!!

Any reason why this bug still exists? I tried everything and its still not working, I can't get ts-jest to work at all, if I import another typescript repo. Jest really needs to have TypeScript support out of the box, with 0 configuration. At this points its just as difficult to setup like Mocha/Chai

@joshuarobs jest does support TypeScript out of the box via babel. It is just create-react-app making it hard to adjust. If you setup jest from scratch on a project not using any frameworks, it's pretty easy and straight forward.

You can also check my repo for the setup https://github.com/ahnpnl/react-ts-jest-babel with React.

@ahnpnl
I tried your repo and tried importing another of my private typescript module and it worked fine, but I can't make it work with Create React App. I'm not sure if I'm meant to use ts-jest or babel to make it work with CRA, or if its even possible in the first place to make CRA work with typescript and jest (and external typescript library imports)

@joshuarobs I dont know how to setup for create-react-app without ejecting the project. My repo is create-react-app after ejecting. If you don't want to eject your project, it's better you ask in React community or Jest community Discord.

this works for mine:

module.exports = {
  verbose: true,
  preset: 'ts-jest',
  testEnvironment: 'node',
  collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}'],
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testPathIgnorePatterns: [
    '<rootDir>/.cache/',
    '<rootDir>/.github/',
    '<rootDir>/.husky/',
    '<rootDir>/.next/',
    '<rootDir>/.vscode/',
    '<rootDir>/.yarn/',
    '<rootDir>/dist/',
    '<rootDir>/generated/',
    '<rootDir>/prisma/',
  ],
};
module.exports = function config(api) {
  return {
    presets: [
      [
        require.resolve('@babel/preset-env'),
        {
          targets: {
            browsers: ['last 2 versions', 'ie >= 11'],
            node: 'current',
          },
          corejs: '3.1.3',
        },
      ],
      require.resolve('@babel/preset-typescript'),
      ...

same issue and it works for mine:

  1. execute jest --init, create jest.config.ts
  2. edit jest.config.ts:
export default {
  ...
  "transform": {
    "^.+\\.(js|ts|tsx)$": "ts-jest"
  },
  ...
}

for me adding "js" to transform fixed the issue

This is before

"transform": {
-       "^.+\\.(ts|tsx)$": "ts-jest"
}

This is after

"transform": {
+        "^.+\\.(ts|tsx|js)$": "ts-jest"
 }

For whom having this issue in typescript 4.1 or later, and have explicitly set 'jsxImportSource' option in tsconfig.json file, you could probably try setting 'jsx' to 'react-jsx', as 'react' mode would make 'jsxImportSource' yelling.

e.g.

{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@emotion/react",
}
}

For whom having this issue in typescript 4.1 or later, and have explicitly set 'jsxImportSource' option in tsconfig.json file, you could probably try setting 'jsx' to 'react-jsx', as 'react' mode would make 'jsxImportSource' yelling.

e.g.

{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@emotion/react",
}
}

Confirmed that this gets me past the error of unexpected token, and then the added error of React refers to a UMD global if you were to update jsx to react. Just as a note, I did not add the jsxImportSource property, I only updated the jsx property from preserver to react-jsx.

Just for others in this same discovery, after this fix if your application specifies custom module paths in your tsconfig.json, such as:

"paths": {
      "@components/*": ["src/components/*"],
      "@hooks/*": ["src/hooks/*"],
      "@lib/*": ["src/lib/*"],
      "@context/*": ["src/context/*"],
      "@models/*": ["src/models/*"],
      "@pages/*": ["src/pages/*"],
      "@services/*": ["src/services/*"],
      "@utils/*": ["src/utils/*"],
      "@constants/*": ["src/constants/*"],
      "@server/*": ["src/server/*"],
      "@/*": ["./*"]
}

Then you also need to add to your jest.config.js a moduleNameMapper for each of your custom paths:

moduleNameMapper: {
    '@components/(.*)': '<rootDir>/src/components/$1',
    '@hooks/(.*)': '<rootDir>/src/hooks/$1',
    '@lib/(.*)': '<rootDir>/src/lib/$1',
    '@context/(.*)': '<rootDir>/src/context/$1',
    '@models/(.*)': '<rootDir>/src/models/$1',
    '@pages/(.*)': '<rootDir>/src/pages/$1',
    '@services/(.*)': '<rootDir>/src/services/$1',
    '@utils/(.*)': '<rootDir>/src/utils/$1',
    '@constants/(.*)': '<rootDir>/src/constants/$1',
    '@server/(.*)': '<rootDir>/src/server/$1',
    '@/(.*)': '<rootDir>/./$1'
}

https://stackoverflow.com/questions/50171412/jest-typescript-absolute-paths-baseurl-gives-error-cannot-find-module

For me the error occured in a non-React project, and the cause was simple: I've run npx ts-jest config:init in a subdirectory (by accident) instead of the root folder where tsconfig.json resides. Moving jest.config.js file up the tree fixed the problem.

The best solution is to give up on writing tests in JavaScript. Come back in another ten years.

it can work when tsconfig.json file add "jsxFactory": "React.createElement".