jpkleemans / vite-svg-loader

Vite plugin to load SVG files as Vue components
MIT License
585 stars 61 forks source link

Transforming Vue 3 components for unit testing with Jest #35

Closed michaelprather closed 2 years ago

michaelprather commented 2 years ago

If you need to transform Vue 3 components created using Vite SVG Loader for the purpose of Jest unit testing:

  1. Create a mock component. I saved mine to the tests directory.

    <template>
    <svg />
    </template>

    You could alternatively use jest-transform-stub, but it may log warnings to the console. Plus this way the rendered HTML includes an <svg> tag.

  2. Update your jest.config.js to transform SVG components into the mock component:

    // jest.config.js
    module.exports = {
    transform: {
    "^.+\\.[t|j]sx?$": "babel-jest",
    "^[^.]+.vue$": "@vue/vue3-jest",
    },
    testMatch: ["**/tests/**/*.spec.js"],
    moduleFileExtensions: ["js", "vue"],
    moduleNameMapper: {
    ".+\\.(svg)(\\?component)?$": "<rootDir>/tests/svgMock.vue",
    "^@/(.*)$": "<rootDir>/src/$1",
    },
    testEnvironment: "jsdom",
    collectCoverage: true,
    }

    I've included my entire jest.config.js file for reference, but the key part is here:

module.exports = {
  ...
  moduleNameMapper: {
    ".+\\.(svg)(\\?component)?$": "<rootDir>/tests/svgMock.vue",
    ...
  },
  ...
}

Now all components created using Vite SVG Loader will be automatically transformed to the mock component.

jpkleemans commented 2 years ago

Thanks for this guide!

pdcmoreira commented 1 year ago

This doesn't work if you want to test that the icon is rendered:

import EditIcon from '@/assets/icons/edit.svg'

// ...

expect(wrapper.findComponent(EditIcon).exists()).toBeTruthy() // doesn't work

But if instead of setting up the moduleNameMapper, you simply mock it in each test suite, it works:

jest.mock('@/assets/icons/edit.svg', () => ({ el: '<svg />' }))

import EditIcon from '@/assets/icons/edit.svg'

// ...

expect(wrapper.findComponent(EditIcon).exists()).toBeTruthy() // works
pdcmoreira commented 1 year ago

Nevermind my previous comment, it also doesn't seem to work properly in some cases. I've created a simple custom transformer which appears to work in all cases I've tested so far:

tests/jest-svg-component-transformer.js:

module.exports = {
  process(sourceText, sourcePath) {
    const mockComponent = {
      name: sourcePath,

      template: sourceText
    }

    return {
      code: `module.exports = ${JSON.stringify(mockComponent)};`
    }
  }
}

jest.config.js:

module.exports = {
  // ...
  transform: {
    '^.+\\.vue$': '@vue/vue3-jest',
    // remove 'svg' from here:
    '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2|mp3)$':
    'jest-transform-stub',
    '^.+\\.(js|jsx)?$': 'babel-jest',
    // specific transformer for svg:
    '.+\\.svg$': '<rootDir>/tests/jest-svg-component-transformer.js'
  }
}