4

我正在尝试使用 Jest 测试 Svelte 组件。该组件在浏览器中运行良好,但单元测试在导入模块时失败。

例如,在运行 Jest 时,import uuid from 'uuid'编译为const { default: uuid } = require("uuid");,并调用uuid.v4()cause TypeError: Cannot read property 'v4' of undefined。当我使用import * as uuid from 'uuid'orconst uuid = require('uuid')时,Jest 单元测试通过,但它在浏览器中不起作用。

我该如何处理这个问题?任何信息都会有很大帮助。谢谢你。

包.json:

{
  "name": "svelte-app",
  "version": "1.0.0",
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "start": "firebase serve --only hosting"
  },
  "devDependencies": {
    "@rollup/plugin-json": "^4.0.0",
    "@testing-library/jest-dom": "^5.1.1",
    "@testing-library/svelte": "^1.11.0",
    "bulma": "^0.8.0",
    "eslint": "^6.7.0",
    "eslint-config-standard": "^14.1.0",
    "eslint-plugin-import": "^2.18.2",
    "eslint-plugin-jest": "^23.0.4",
    "eslint-plugin-node": "^10.0.0",
    "eslint-plugin-promise": "^4.2.1",
    "eslint-plugin-standard": "^4.0.1",
    "eslint-plugin-svelte3": "^2.7.3",
    "jest-transform-svelte": "^2.1.1",
    "node-sass": "^4.13.1",
    "rollup": "^1.12.0",
    "rollup-jest": "0.0.2",
    "rollup-plugin-commonjs": "^10.0.0",
    "rollup-plugin-livereload": "^1.0.0",
    "rollup-plugin-node-builtins": "^2.1.2",
    "rollup-plugin-node-globals": "^1.4.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-svelte": "^5.0.3",
    "rollup-plugin-terser": "^5.1.2",
    "sirv-cli": "^0.4.5",
    "svelma": "^0.3.2",
    "svelte": "^3.18.2",
    "svelte-preprocess": "^3.4.0"
  },
  "dependencies": {
    "firebase": "^7.8.2"
  },
  "private": true
}

rollup.config.js

import json from '@rollup/plugin-json'
import commonjs from 'rollup-plugin-commonjs'
import builtins from 'rollup-plugin-node-builtins'
import globals from 'rollup-plugin-node-globals'
import livereload from 'rollup-plugin-livereload'
import resolve from 'rollup-plugin-node-resolve'
import svelte from 'rollup-plugin-svelte'
import { terser } from 'rollup-plugin-terser'
import preprocess from 'svelte-preprocess'

const production = !process.env.ROLLUP_WATCH

export default {
  input: 'src/main.js',
  output: {
    sourcemap: true,
    format: 'iife',
    name: 'app',
    file: 'public/build/bundle.js',
  },
  plugins: [
    // https://github.com/rollup/plugins/tree/master/packages/json
    json(),

    svelte({
      // enable run-time checks when not in production
      dev: !production,
      // we'll extract any component CSS out into
      // a separate file — better for performance
      css: css => {
        css.write('public/build/bundle.css')
      },
      preprocess: preprocess(),
    }),

    // If you have external dependencies installed from
    // npm, you'll most likely need these plugins. In
    // some cases you'll need additional configuration —
    // consult the documentation for details:
    // https://github.com/rollup/rollup-plugin-commonjs
    resolve({
      browser: true,
      dedupe: importee => importee === 'svelte' || importee.startsWith('svelte/'),
    }),
    commonjs(),
    globals(),
    builtins(),

    // In dev mode, call `npm run start` once
    // the bundle has been generated
    !production && serve(),

    // Watch the `public` directory and refresh the
    // browser on changes when not in production
    !production && livereload('public'),

    // If we're building for production (npm run build
    // instead of npm run dev), minify
    production && terser(),
  ],
  watch: {
    clearScreen: false,
  },
}

function serve () {
  let started = false

  return {
    writeBundle () {
      if (!started) {
        started = true

        require('child_process').spawn('npm', ['run', 'start'], {
          stdio: ['ignore', 'inherit', 'inherit'],
          shell: true,
        })
      }
    },
  }
}

jest.config.js

const sveltePreprocess = require('svelte-preprocess')

module.exports = {
  displayName: { name: 'web', color: 'magentaBright' },
  moduleFileExtensions: [
    'js',
    'json',
    'svelte',
  ],
  preset: 'rollup-jest',
  transform: {
    '\\.js$': 'rollup-jest',
    '\\.svelte$': ['jest-transform-svelte', { preprocess: sveltePreprocess(), debug: true }],
  },
  setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
}
4

3 回答 3

1

它对我有用。

在运行时构建代码时,这个问题似乎无法解决 uuid。这很明显,因为默认情况下会忽略 node_modules packages

我遇到了类似的问题并解决了。该方法是通过配置通知 JEST 它还必须包含 node_modules 包。在我的项目中,我使用了 rollup-plugin-babel。这是 babel 插件配置

...
...
babel({
    extensions: [ '.js', '.mjs', '.html', '.svelte' ],
    runtimeHelpers: true,
    exclude: [ 'node_modules/@babel/**', 'node_modules/core-js/**' ],
    presets: [
      [
        '@babel/preset-env',
        {
          targets: '> 0.25%, not dead',
          useBuiltIns: 'usage',
          corejs: 3 
        }
      ]
    ]
  })

我添加了 babel-jest 来转换 jest.config.js

module.exports = {
  preset: 'jest-puppeteer', //ignore the preset part, I used for puppeteer
  transform: {
    '^.+\\.js?$': require.resolve('babel-jest'),
    "^.+\\.ts?$": "ts-jest" // this part is only required if you have typescript project
  }
};

不要忘记在使用之前安装这些包,比如 babel-jest、rollup-plugin-babel。

于 2020-04-05T06:24:18.963 回答
0

自我回答:

最后,我写了一个小预处理器来替换import foo from 'foo'->import * as foo from 'foo'

svelteJestPreprocessor.js

const svelteJestPreprocessor = () => ({
  // replace `import foo from 'foo'` -> `import * as foo from 'foo'`
  script: ({ content }) => ({
    // process each line of code
    code: content.split('\n').map(line =>
      // pass: no import, import with {}, import svelte component
      (!line.match(/\s*import/)) || (line.match(/{/)) || (line.match(/\.svelte/)) ? line
        : line.replace(/import/, 'import * as'),
    ).join('\n'),
  }),
})

module.exports = svelteJestPreprocessor

jest.config.js

const svelteJestPreprocessor = require('./svelteJestPreprocessor')
const sveltePreprocess = require('svelte-preprocess')

module.exports = {
  moduleFileExtensions: [
    'js',
    'json',
    'svelte',
  ],
  preset: 'rollup-jest',
  transform: {
    '\\.js$': 'rollup-jest',
    '\\.svelte$': ['jest-transform-svelte', {
      preprocess: [
        svelteJestPreprocessor(),
        sveltePreprocess(),
      ],
    }],
  },
  setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
}

这是一个不需要的解决方法,但它现在有效。

于 2020-06-13T06:56:45.007 回答
0

我一直面临同样的问题,并通过在测试文件中模拟模块并为其提供默认密钥来解决问题。

jest.mock('uuid', () => ({
  default: {
    v4: jest.fn(),
  },
}))

另一种似乎可行的方法是解构组件文件中的导入。

import { v4 as uuidv4 } from 'uuid'
于 2020-03-18T15:41:29.240 回答