Svelte 3: не удалось импортировать модули при модульном тестировании

Я пытаюсь протестировать компонент Svelte с помощью Jest. Этот компонент отлично работает в браузере, но при импорте модулей модульный тест не работает.

Например, при запуске Jest import uuid from 'uuid' компилируется как const { default: uuid } = require("uuid");, а вызов uuid.v4() вызывает TypeError: Cannot read property 'v4' of undefined. Когда я использую import * as uuid from 'uuid' или const uuid = require('uuid'), модульный тест Jest проходит успешно, но не работает в браузере.

Как я могу решить эту проблему? Любая информация очень бы помогла. Спасибо.

package.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'],
}

person astanet    schedule 20.02.2020    source источник
comment
У меня такая же проблема. Я еще не нашел выхода из этого   -  person spirift    schedule 18.03.2020


Ответы (3)


У меня сработало.

Проблема выглядит как шутка, неспособная разрешить uuid при построении кода во время выполнения. Это совершенно очевидно, потому что по умолчанию jest игнорирует пакеты node_modules.

Я столкнулся с подобными проблемами и решил это. Подход заключается в том, что конфигурация сообщает 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, перед их использованием.

person Dip686    schedule 05.04.2020
comment
Я очень ценю, что вы нашли время дать подробное объяснение. Я не хочу сейчас добавлять больше компиляторов, но ваш способ действительно полезен для тех, кто уже использует Babel. Может быть, мне придется пойти по твоему пути в будущем. Спасибо! - person astanet; 13.06.2020
comment
@astanet, если вы найдете этот ответ полезным, примите его и проголосуйте за него, чтобы люди, столкнувшиеся с подобной проблемой, получили пользу. - person Dip686; 14.06.2020

Я столкнулся с этой же проблемой, и у меня есть работа, высмеивая модуль в тестовом файле и давая ему ключ по умолчанию.

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

Другой способ, который кажется работающим, - это деструктурировать импорт в файле компонента.

import { v4 as uuidv4 } from 'uuid'
person spirift    schedule 18.03.2020
comment
К сожалению, ваш обходной путь не работает для меня, но я очень рад, что вы нашли время для решения этой проблемы. Спасибо! - person astanet; 13.06.2020

Самостоятельный ответ:

Наконец, я написал небольшой препроцессор для замены 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'],
}

Это нежелательный обходной путь, но пока он работает.

person astanet    schedule 13.06.2020