开玩笑 - 无法使用动态导入模板字符串定位模块



好吧,我有一个设置Webpack和Jest用于测试React的设置,还使用我的项目目录的" src/app",我使用"@"符号使用webpack的别名,我还知道它必须使用" Modulenamapper"属性将其映射到Jest.config中,以与该别名正确对齐。

问题在于有一个<Icon />组件,该组件使用ES Dynamic Import动态导入.SVG图标。

这是jest.config.js

的映射的正则

'^@/':'/src/app/$ 1'

,但仅在测试时会破裂

一切正常,除了测试时

我的文件夹结构如下:

config/
 webpack-config/
src/
 app/
  assets/
   img/
    some images
   svg/
    icons/
     cart.svg
  components/
   base/
   core/
   shared/
   ...
 client/
  ...
 server/
  ...

这是我正在使用的WebPack别名:

module.exports = {
  extensions: ['.js', '.mjs', '.json', '.jsx', '.css'],
  modules: paths.resolveModules,
  alias: {
    '@': paths.srcApp, // that just point
  }
};

这是我的jest.config文件:

const paths = require('./config/paths');
module.exports = {
  verbose: true,
  collectCoverageFrom: ['src/**/*.{js,jsx,mjs}'],
  setupFiles: [
    '<rootDir>/node_modules/regenerator-runtime/runtime',
    '<rootDir>/config/polyfills.js',
  ],
  // A list of paths to modules that run some code to configure or set up the testing framework before each test.
  setupFilesAfterEnv: ['<rootDir>config/jest/setup.js'],
  testMatch: [
    '<rootDir>/src/**/__tests__/**/*.{js,jsx,mjs}',
    '<rootDir>/src/**/?(*.)(spec|test).{js,jsx,mjs}',
  ],
  testEnvironment: 'node',
  testURL: 'http://localhost',
  modulePaths: ['src'],
  moduleNameMapper: {
    '^@[/](.+)': '<rootDir>/src/app/$1',
  },
  transform: {
    '^.+\.(js|jsx|mjs)$': '<rootDir>/node_modules/babel-jest',
    '^.+\.css$': '<rootDir>/config/jest/cssTransform.js',
    '^(?!.*\.(js|jsx|mjs|css|json|svg)$)': '<rootDir>/config/jest/fileTransform.js',
  },
  transformIgnorePatterns: ['[/\\]node_modules[/\\].+\.(js|jsx|mjs)$'],
  moduleDirectories: paths.resolveModules,
  moduleFileExtensions: ['js', 'json', 'jsx', 'node', 'mjs'],
};

这是我正在测试的组件

// @flow strict
import React, { useState } from 'react';
import './Icon.scss';
type PropsType = {
  name: string,
  selected?: string,
  size: number | string
};
const Icon = ({ name, size }: PropsType) => {
  const [iconPath, setIconPath] = useState('');
  (async () => {
    const icon = await import(`@/assets/svg/icons/${name}.svg`);
    setIconPath(icon.default);
  })();
  return (
    <img
      alt={`icon ${name}`}
      className="icon"
      style={{
        height: `${size}px`,
        width: `${size}px`,
      }}
      src={iconPath}
    />
  );
};
Icon.defaultProps = {
  size: 16,
};
export default Icon;

这是测试

// @flow strict
import React from 'react';
import {
  render,
} from 'react-testing-library';
import Icon from './Icon';
describe('Icon', () => {
  it('should have "name" with the path of the icon', () => {
    const { container } = render(<Icon name="cart" />);  
  });
});

这是输出

Configuration error:
    Could not locate module @/assets/svg/icons/${name}.svg mapped as:
    /Users/jero/Documents/my-shit/react-shop/src/app/assets/svg/icons/${name}.svg.
    Please check your configuration for these entries:
    {
      "moduleNameMapper": {
        "/^@[/](.+)/": "/Users/jero/Documents/my-shit/react-shop/src/app/$1"
      },
      "resolver": null
    }

有趣的是,当我将此正则发行速度用于jest.config

moduleNameMapper: {
 '/^@/(.*)$/': '<rootDir>/src/app$1'
}

错误发生了,但是当组件在测试时导入其他组件时会破裂:

 FAIL  src/app/components/shared/ImgLoader/ImgLoader.test.js
  ● Test suite failed to run
    Cannot find module '@/components/base/Spinner/Spinner' from 'ImgLoader.js'
    However, Jest was able to find:
        './ImgLoader.js'
        './ImgLoader.scss'
        './ImgLoader.test.js'
    You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'json', 'jsx', 'node', 'mjs'].
    See https://jestjs.io/docs/en/configuration#modulefileextensions-array-string
      2 | import React, { useState } from 'react';
      3 | 
    > 4 | import Spinner from '@/components/base/Spinner/Spinner';
        | ^
      5 | 
      6 | import './ImgLoader.scss';
      7 | 
      at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:230:17)
      at Object.<anonymous> (src/app/components/shared/ImgLoader/ImgLoader.js:4:1)

这是<ImageLoader />

// @flow strict
import React, { useState } from 'react';
import Spinner from '@/components/base/Spinner/Spinner';
import './ImgLoader.scss';
type PropsType = {
  src: string
};
function ImgLoader({ src }: PropsType) {
  const [imgObj, setImg] = useState({ img: null, isLoading: true });
  const image = new Image();
  image.onload = () => setImg({ img: image.src, isLoading: false });
  image.src = src;
  return (
    imgObj.isLoading ? (<Spinner />) : (<img className="imgLoader img-fluid" src={imgObj.img} alt="img" />)
  );
}
export default ImgLoader;

及其测试ofcourse

// @flow strict
import React from 'react';
import {
  render,
} from 'react-testing-library';
import ImgLoader from './ImgLoader';
describe('ImgLoader', () => {
  it('tests 'src'', () => {
    const src = 'https://static.street-beat.ru/upload/resize_cache/iblock/d69/450_450_1/d699afc7b3428f2f51c2f2de6665b506.jpg';
    const { container } = render(<ImgLoader src={src} />);
    console.log('container', container);
  });
});

我相信与动态导入有关?

这是我修复的方式,问题是出于某种原因,它在使用动态导入中使用模板文字时抱怨。因此,要修复它,只需使用普通字符串串联即可。

(async () => {
  // $FlowIgnore
  const icon = await import('@/assets/svg/icons/' + name + '.svg');
  setIconPath(icon.default);
})();

也可以通过用--watchAll

替换Jest的--watch标志而在不删除模板文字的情况下解决此问题。

最新更新