vue-svg-loader
vue-svg-loader copied to clipboard
Setup Jest when Internal and External SVGs are configured
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.
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.
@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.
I think it is worth putting this into the FAQ section :)
does the regex for the mapper target relative imports? and if not how would that look like?
@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.