MUI5配合Jest运行报SyntaxError: Cannot use import statement outside a module怎么解决
问题描述
可复现仓库地址:https://github.com/hutber/cannotusestatement
将上述仓库导入CodeSandbox(地址:https://codesandbox.io/s/vigilant-bartik-bmz8x)时测试可以正常通过,但本地拉取该仓库运行测试就会失败。问题根源是Jest没有编译运行测试所需的node_modules依赖,期望实现本地测试正常运行。
相关代码&报错
测试用例代码
import React from 'react' import { renderWithThemeProvider, screen } from 'test-utils' import { Select } from './Select' it('renders correctly', () => { const tree = renderWithThemeProvider(<Select id="testSelect"></Select>) expect(tree).toMatchSnapshot() })
原有jest.config.js配置
module.exports = { roots: ['<rootDir>', './src'], moduleDirectories: ['<rootDir>', 'node_modules/', './src'], transform: { '^.+\\.(ts|tsx)$': 'ts-jest', }, testEnvironment: 'jsdom', testMatch: ['**/*.test.(ts|tsx)', '**/__tests__/*.(ts|tsx)'], moduleNameMapper: { // 测试运行时mock以下格式的文件 '\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'identity-obj-proxy', '\\.(css|less|scss|sass)$': 'identity-obj-proxy', 'app-config': '<rootDir>/app-config/default', '^@components(.*)$': '<rootDir>/src/components$1', '^@themes(.*)$': '<rootDir>/src/themes$1', }, coverageThreshold: { global: { statements: 50, }, }, }
原有babel.config.js配置
module.exports = { presets: ['@babel/preset-env', '@babel/preset-react'], env: { test: { presets: ['@babel/preset-env', '@babel/preset-react'], plugins: [ [ 'babel-plugin-transform-imports', '@babel/plugin-proposal-class-properties', 'transform-es2015-modules-commonjs', 'babel-plugin-dynamic-import-node', 'babel-plugin-styled-components', ], }, }, }
package.json测试命令
"test": "jest --watchAll=false",
运行报错信息
(base) hutber@hutber:/var/www/target/component-library$ yarn test src/components/Select/Select.test.tsx yarn run v1.22.17 $ jest --watchAll=false src/components/Select/Select.test.tsx 测试未通过 src/components/Select/Select.test.tsx ● 测试套件运行失败 Jest遇到了无法识别的字符 Jest解析文件失败。出现该问题通常是因为你的代码或依赖使用了非标准JavaScript语法,或是Jest没有配置对应的语法支持。 Jest默认支持Babel,会根据你的Babel配置把文件转换为合法的JS格式。 默认情况下transform转换器会忽略"node_modules"文件夹。 可尝试以下解决方案: • 如果要使用ECMAScript Modules,可在Jest配置中开启对应支持 • 如果要使用TypeScript,可参考Jest官方的TypeScript接入指南 • 如果需要转换部分"node_modules"中的文件,可在配置中自定义"transformIgnorePatterns"规则 • 如果需要自定义转换逻辑,可在配置中指定"transform"参数 • 如果只是需要mock非JS模块(比如二进制资源),可通过"moduleNameMapper"配置项做替换 详细信息: /var/www/target/component-library/node_modules/@mui/material/styles/styled.js:1 ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import { createStyled, shouldForwardProp } from '@mui/system'; ^^^^^^ 语法错误:无法在模块外部使用import语句 1 | import React from 'react' > 2 | import styled from '@mui/material/styles/styled' | ^ 3 | 4 | import MuiSelect, { SelectProps as MuiSelectProps } from '@mui/material/Select' 5 | import InputLabel from '@mui/material/InputLabel' 位置:Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1728:14) 触发位置:src/components/Select/Select.tsx:2:1 测试套件:1个失败,共1个 测试用例:共0个 快照:共0个 耗时:3.272 s
Select组件代码
import React from 'react' import styled from '@mui/material/styles/styled' import MuiSelect, { SelectProps as MuiSelectProps } from '@mui/material/Select' import InputLabel from '@mui/material/InputLabel' import MenuItem from '@mui/material/MenuItem' import MuiFormControl from '@mui/material/FormControl' interface ISelect extends MuiSelectProps { id: string label?: string options?: { text: string; option: string }[] } const FormControl = styled(MuiFormControl)` color: #fff; border-radius: 30px; width: 180px; ` export const Select: React.FC<ISelect> = ({ label, id, children, options, ...props }) => { return ( // @ts-ignore <FormControl fullWidth hiddenLabel> {label && ( <InputLabel id={`input_${id}`} shrink={false}> {label} </InputLabel> )} <MuiSelect id={id} {...props}> {options && options.map(({ text, option }: { text: string; option: string }) => <MenuItem value={option}>{text}</MenuItem>)} {children && children} </MuiSelect> </FormControl> ) } export default Select
解决方案
错误原因
Jest默认不会编译node_modules目录下的所有依赖,而你用到的@mui/material采用ES模块规范发布,未提供CommonJS兼容版本,所以Jest读取文件时识别不了import语法,直接抛出语法错误。CodeSandbox默认内置了ES模块依赖的转换规则,所以在线运行不会报错。
调整步骤
1. 修改jest.config.js配置
补充js/jsx文件的转换规则,同时添加transformIgnorePatterns配置,将@mui相关依赖排除在转换忽略列表之外:
module.exports = { roots: ['<rootDir>', './src'], moduleDirectories: ['<rootDir>', 'node_modules/', './src'], transform: { '^.+\\.(ts|tsx|js|jsx)$': 'ts-jest', }, testEnvironment: 'jsdom', testMatch: ['**/*.test.(ts|tsx)', '**/__tests__/*.(ts|tsx)'], transformIgnorePatterns: ['node_modules/(?!(@mui|@babel/runtime))/'], moduleNameMapper: { '\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'identity-obj-proxy', '\\.(css|less|scss|sass)$': 'identity-obj-proxy', 'app-config': '<rootDir>/app-config/default', '^@components(.*)$': '<rootDir>/src/components$1', '^@themes(.*)$': '<rootDir>/src/themes$1', }, globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.json', allowJs: true } }, coverageThreshold: { global: { statements: 50, }, }, }
2. 修正babel.config.js配置
原有配置中plugins数组开头多写了一个多余的[,属于语法错误,同时补充TypeScript编译预设:
module.exports = { presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'], env: { test: { presets: [ ['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-react', '@babel/preset-typescript' ], plugins: [ '@babel/plugin-proposal-class-properties', 'transform-es2015-modules-commonjs', 'babel-plugin-dynamic-import-node', 'babel-plugin-styled-components', ], }, }, }
如果本地没有安装@babel/preset-typescript,先执行yarn add @babel/preset-typescript -D安装依赖。
3. 执行测试
修改配置后重新运行yarn test,测试即可正常执行。
内容的提问来源于stack exchange,提问作者Jamie Hutber
相关产品推荐
相关产品推荐

