vue-svg-loader icon indicating copy to clipboard operation
vue-svg-loader copied to clipboard

Setup Jest when Internal and External SVGs are configured

Open jpetko opened this issue 6 years ago • 5 comments

Hi, I was curious as how to configure the Jest transform when inline and external svgs are in use with the '?inline' method shown in the FAQ?

I get a 'Could not locate module' error when the unit test hits the import statement now.

jpetko avatar Feb 26 '19 23:02 jpetko

Have you tried reversing the rule definition in the inline / external example webpack config?

`chainWebpack: config => { const svgRule = config.module.rule("svg");

svgRule.uses.clear();

svgRule
  .oneOf("external")
  .resourceQuery(/external/)
  .use("file-loader")
  .loader("file-loader")
  .options({
    name: "assets/[name].[hash:8].[ext]"
  })
  .end()
  .end()
  .oneOf("internal")
  .use("vue-svg-loader")
  .loader("vue-svg-loader");

}`

Doing this will allow you to safely import svg's for inline usage within your script tags, without the tests crashing. The caveat is that whenever you will need to import the svg's into the scrip tag without the external resourceQuery, using the file-loader, the tests will crash. Until recently, this wasn't much of a problem for me, as I only loaded svgs via the file-loader (with the external resource query) in css files, which jest does not pick up.

acazacu avatar Mar 30 '19 20:03 acazacu

@jpetko Here's a better solution that seems to work without the above stated caveat.

You update your vue.config.js file as recommended by the vue-svg-loader docs to support both inline and external uses of svg files. It should look something like this:

module.exports = {
  lintOnSave: false,
  chainWebpack: config => {
    const svgRule = config.module.rule("svg");

    svgRule.uses.clear();

    svgRule
      .oneOf("inline")
      .resourceQuery(/inline/)
      .use("vue-svg-loader")
      .loader("vue-svg-loader")
      .end()
      .end()
      .oneOf("external")
      .use("file-loader")
      .loader("file-loader")
      .options({
        name: "assets/[name].[hash:8].[ext]"
      });
  }
};

To fix the crashing jest unit tests, you need to properly resolve the modules imported with resourceQuery strings. To do this, you use a moduleNameMapper, your jest.config.js file should look something like this:

module.exports = {
  moduleFileExtensions: ["js", "jsx", "json", "vue"],
  transform: {
    "^.+\\.svg": "<rootDir>/svgTransform.js",
    "^.+\\.vue$": "vue-jest",
    ".+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
    "^.+\\.jsx?$": "babel-jest"
  },
  moduleNameMapper: {
    "^@/(.*svg)(\\?inline)$": "<rootDir>/src/$1",
    "^@/(.*)$": "<rootDir>/src/$1"
  },
  snapshotSerializers: ["jest-serializer-vue"],
  testMatch: ["**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
  testURL: "http://localhost/",
  collectCoverageFrom: ["!**/src/__mocks__/**", "**/src/**/*.{js,vue}"]
};

The line that interests you is this one, "^@/(.*svg)(\\?inline)$": "<rootDir>/src/$1". Be careful to add it above the default regex, "^@/(.*)$": "<rootDir>/src/$1", order counts here.

Doing this, you should be able to safely use inline svgs by importing them with the following syntax:

import SvgComponent from "@/path/to/svg/in/src/SvgComponent.svg?inline"

Your jest suits should work fine now. If this is an acceptable solution, it would be useful to have this in the project docs.

acazacu avatar Mar 30 '19 22:03 acazacu

I think it is worth putting this into the FAQ section :)

sem4phor avatar Jan 17 '20 16:01 sem4phor

does the regex for the mapper target relative imports? and if not how would that look like?

hendrikras avatar Mar 09 '20 15:03 hendrikras

@acazacu Thank you so very very much! I was going insane with this, had your same exact configuration except the order in the mapper. That was it!!! @visualfanatic this should absolutely go in the FAQ section, the order is a very subtle mistake, hard to see and think about.

Dinuz avatar Apr 08 '20 16:04 Dinuz