Jest在import语句上出现“Unexpected token *”错误。

72

为什么使用Jest进行简单导入语句时会出现“Unexpected token *”的错误?

错误日志:

Admin@Admin-PC MINGW32 /d/project (master)
$ npm run test

> MyApp@0.0.1 test D:\project
> jest

FAIL __tests__/App-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:

    D:\project\node_modules\react-navigation-tabs\src\navigators\createBottomTabNavigator.js:3
    import * as React from 'react';
           ^

    SyntaxError: Unexpected token *

      14 | // );
      15 |
    > 16 | export default createBottomTabNavigator({
         |                ^
      17 |   map: {
      18 |     screen: MapView,
      19 |     navigationOptions: {

      at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:471:17)
      at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:513:25)
      at Object.get createBottomTabNavigator [as createBottomTabNavigator] (node_modules/react-navigation-tabs/src/index.js:9:12)
      at Object.<anonymous> (src/app/main.view.tsx:16:16)

FAIL src/component/reinput/example/__tests__/index.ios.js (19.352s)
  ? Console

    console.error node_modules/react-native/Libraries/YellowBox/YellowBox.js:59
      Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

  ? renders correctly

    Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

      at invariant (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:55:15)
      at createFiberFromTypeAndProps (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2054:11)
      at createFiberFromElement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2075:15)
      at reconcileSingleElement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4605:23)
      at reconcileChildFibers (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4662:35)
      at reconcileChildren (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6329:28)
      at updateHostRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6741:5)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7566:14)
      at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11234:12)
      at workLoop (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11266:24)

FAIL src/component/reinput/example/__tests__/index.android.js (19.365s)
  ? Console

    console.error node_modules/react-native/Libraries/YellowBox/YellowBox.js:59
      Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

  ? renders correctly

    Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

      at invariant (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:55:15)
      at createFiberFromTypeAndProps (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2054:11)
      at createFiberFromElement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2075:15)
      at reconcileSingleElement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4605:23)
      at reconcileChildFibers (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4662:35)
      at reconcileChildren (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6329:28)
      at updateHostRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6741:5)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7566:14)
      at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11234:12)
      at workLoop (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11266:24)

Test Suites: 3 failed, 3 total
Tests:       2 failed, 2 total
Snapshots:   0 total
Time:        22.774s
Ran all test suites.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! MyApp@0.0.1 test: `jest`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the MyApp@0.0.1 test script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\Admin\AppData\Roaming\Roaming\npm-cache\_logs\2019-04-22T11_52_36_984Z-debug.log

package.json文件:

{
  "name": "MyApp",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest"
  },
  "dependencies": {
    "react": "16.8.3",
    "react-native": "0.59.4",
    "react-native-gesture-handler": "^1.1.0",
    "react-native-reanimated": "^1.0.1",
    "react-native-splash-screen": "^3.2.0",
    "react-navigation": "^3.8.1",
    "react-navigation-tabs": "^2.1.1"
  },
  "devDependencies": {
    "@babel/core": "^7.4.3",
    "@babel/runtime": "^7.4.3",
    "@types/jest": "^24.0.11",
    "@types/react": "^16.8.13",
    "@types/react-dom": "^16.8.4",
    "@types/react-native": "^0.57.46",
    "@types/react-test-renderer": "^16.8.1",
    "babel-jest": "^24.7.1",
    "jest": "^24.7.1",
    "metro-react-native-babel-preset": "^0.53.1",
    "react-test-renderer": "16.8.3",
    "typescript": "^3.4.3"
  },
  "jest": {
    "preset": "react-native"
  }
}

babel.config.js文件:

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
};

jest.config.js文件:

module.exports = {
  preset: 'react-native',
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
}
注意:我正在使用React Native TypeScript 模板,例如 react-native init MyApp --template typescript

2
为什么不使用 import React from 'react'; 呢? - Andy
我不知道为什么,那是一个基于JSX的模块,叫做react-navigation-tabs,它只是从我的TSX代码中导入和使用,我没有编写那个模块... - Top-Master
如果需要的话,可以单独导入它。它不是 React 包的一部分。 - Andy
抱歉回答不够清晰,我的意思是,Jest 失败的代码行是 react-navigation-tabs 库的一部分,而不是我的代码;但是在我的代码中,需要并导入它时,jest 没有任何问题,所以 Jest 在我的文件中可以正常工作,但在子库中失败了(我不能删除所有外部库的使用,特别是这个库是应用程序选项卡所必需的)。 - Top-Master
7个回答

98

一些 react-native 库提供未编译的 ES6 代码。

在 Jest 运行之前需要编译 ES6 代码。

Jest 对于 测试 React Native 应用程序 的文档包括一个关于编译不提供预编译代码依赖项的部分。

你需要通过将其列入 Jest 配置中的 transformIgnorePatterns 选项来告知 Jest 编译 react-navigation-tabs

示例:

jest.config.js 文件更改为以下内容,可以解决 OP 中提到的问题。

react-native-reanimated 模块(需要本地集成)还需要进一步处理,我们应该使用“模仿”具有此类本地要求的模块(如另一篇帖子中所述)。

module.exports = {
  preset: 'react-native',
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
  transformIgnorePatterns: [
    "node_modules/(?!(react-native"
      + "|react-navigation-tabs"
      + "|react-native-splash-screen"
      + "|react-native-screens"
      + "|react-native-reanimated"
    + ")/)",
  ],
}

注意transformIgnorePatterns选项(它是一个正则表达式数组)最初的目的是排除文件被编译,但使用带有“(?!...)”否定式前瞻的“(?!(some-dir-name|another-name))”模式,我们通知 Jest 排除 node_modules 目录中除我们指定的名称以外的所有内容。


4
我已经将相同的内容放入jest.config.js中,但是出现了SyntaxError: Unexpected identifier。/node_modules/@react-native/polyfills/error-guard.js:14 类型ErrorHandler = (error:mixed,isFatal:boolean) => void; - Vrajesh
关键是要查看“预设”使用的现有transformIgnorePatterns,并将我的软件包添加到正则表达式中。在我的情况下,我正在使用preset: 'react-native',因此我必须打开note_modules/react-native/jest-preset.js,复制transformIgnorePatterns,将其添加到我的jest.config.js中,并将有问题的软件包(在我的情况下是react-native-modal-datetime-picker)添加到正则表达式中。所以我最终在jest.config.js中得到了以下内容:transformIgnorePatterns: [ 'node_modules/(?!((jest-)?react-native|react-native-modal-datetime-picker|@react-native(-community)?)/)', ], - paneer_tikka

14

正如所说,某些模块需要转换,而有些不需要。 这是我在许多项目中使用的正则表达式

  "jest": {
    "preset": "react-native",
    "transformIgnorePatterns": [
      "node_modules/(?!(jest-)?react-native|react-(native|universal|navigation)-(.*)|@react-native-community/(.*)|@react-navigation/(.*)|bs-platform|(@[a-zA-Z]+/)?(bs|reason|rescript)-(.*)+)"
    ]
  }

它适用于大多数常见的React Native事物,包括一个特定的包(这里是bs-platform)作为示例,当之前的模式未涵盖时。


8

我在一个React + Typescript应用中遇到了类似的问题。

我犯的第一个错误是将jest.config.js定义为jest.config.ts

在Node v12.latest上运行

然后适合我的配置如下:

// jest.config.js

module.exports = {
  preset: "ts-jest",
  testEnvironment: "node",
  roots: ["./src"],
  transform: { "\\.ts$": ["ts-jest"] },
  testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
  moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
  globals: {
    "ts-jest": {
      tsConfig: {
        // allow js in typescript
        allowJs: true,
      },
    },
  },
};



// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react",
    "baseUrl": "."
  },
  "include": ["src"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}


// package.json
"devDependencies": {
    "@types/jest": "^26.0.5",
    "jest": "^26.1.0",
    "ts-jest": "^26.1.3"
}


2
如果你不仅使用React Native还使用Expo,我通过遵循官方指南Jest测试修复了同样的问题。具体来说,通过包管理器安装jest预设jest-expo,然后按照他们描述的更新我的package.json文件。
"jest": {
  "preset": "jest-expo",
  "transformIgnorePatterns": [
    "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)"
  ]
}

我还需要在transformIgnorePatterns正则表达式字符串的末尾添加一个例外,即|.*font.*,因为Expo字体是导致问题的原因。也许我可以让它更具体一些,但我不喜欢正则表达式,这样做就可以了!


Expo建议的默认设置很好,但是当然,这并不改变大多数开发人员需要根据项目的package.json文件自定义transformIgnorePatterns选项的事实 - 就像被接受的答案所做的那样。 - Top-Master
是的,你说得没错。我不确定是否应该将这个作为答案发布,因为它修复了一个略微不同但相关的问题。你建议我怎么做?只是我花了几个小时一直回到这个页面,直到我找到解决方法,而我没有意识到由于 Expo 的原因我需要做一些额外的事情。 - pipedreambomb
在这里提供它很不错,比如对于那些在本页面搜索 "Expo" 并根据自己需求进行定制的人来说(尽管大量的 "export" 提及会让查找变得有点困难)。 - Top-Master

1
在您的配置文件(.babelrc.js或package.json)中,“presets”下的“modules”必须设置为以下“amd”|“umd”|“systemjs”|“commonjs”|“cjs”|“auto”|false之一。请参考文档中的this fragment
类似于这样的内容:
    "presets": [
  [
    "@babel/preset-env", {
      "targets": process.env.BABEL_TARGET === 'node' ? {
        "node": 'current'
      } : {
        "browsers": [ "last 2 versions" ]
      },
      "loose": true,
      "modules": 'commonjs'
    }
  ]
]

-1
修改babel.config.js如下对我有用。同时,预设应该放在前面,然后是其他所有配置。
    module.exports = {
       presets: [['@babel/preset-env',{targets: {node: 
         'current',},loose:true,},],],
    }

-1

我正在使用react-native-web,解决我的问题的方法是将react-native-web预设添加到我的jest.config.js中:

module.exports = {
transform: {
  '^.+\\.tsx?$': 'ts-jest',
},
timers: 'fake',
testPathIgnorePatterns: [
  '<rootDir>/build/',
  '<rootDir>/node_modules/',
  '<rootDir>/rndemo/build/',
],
globals: {
  'ts-jest': {
    diagnostics: {
      warnOnly: true,
    },
  },
},
preset: 'react-native-web',
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接