vue-jest configuration - Is there a way to map aliases that occur in template/script src attributes?

884 Views Asked by At

Dependencies: "@vue/cli-plugin-unit-jest": "^4.5.13", "@vue/test-utils": "^1.2.1", "vue-jest": "^3.0.7"

I have an app which uses an alias (say "foo") being set in vue.config.js:

module.exports = {
  chainWebpack: (config) => {
    // Add project name as alias
    config.resolve.alias.set('foo', __dirname);
  },
};

For both import statements and HTML tag src...

In main.js:

...
import App from 'foo/src/components/core/App';
...

In ../src/core/App/index.vue:

<script src="foo/src/components/core/App/script.js" />
<style module src="foo/src/components/core/App/style.css" />
<template src="foo/src/components/core/App/template.html" />

I know I can use a moduleNameMapper in jest.config.js, something like:

'^foo(.*)$': '<rootDir>$1',

However, this doesn't map aliases that appear in the src attribute of my HTML tags. Is there any way to have vue-jest interpret these attribute paths via a config setting or some other means?

Any recommendations will be greatly appreciated.

1

There are 1 best solutions below

0
On

URL parsing in SFCs

vue-jest doesn't resolve src URLs for the top-level block tags in SFCs, so you'll have to use un-aliased relative paths in src/components/core/App/index.vue:

<script src="./script.js" />
<style module src="./style.css" />
<template src="./template.html" />

URL parsing in <template> contents

vue-jest uses @vue/component-compiler-utils to compile the template, but URL parsing requires the transformAssetUrls option. vue-jest 3.x does not support passing options to @vue/component-compiler-utils, but that now works in 4.0.0-rc.1 via a templateCompiler.transformAssetUrls config.

Even with this URL parsing enabled, Vue CLI configures jest to return an empty string for require-ed media, including images. If your tests need to work with the normally resolved URLs in production, you'll need a Jest transform that mimics url-loader. Vue CLI configures the loader to return the resolved filename if greater than 4KB; or the base64 data URL otherwise.

To enable the URL parsing:

  1. Update to vue-jest 4:

    npm i -D vue-jest@4
    
  2. Create the following file for the custom my-jest-url-loader, which we'll use later below:

    // <rootDir>/tests/my-jest-url-loader.js
    const urlLoader = require('url-loader')
    
    module.exports = {
      process(src, filename) {
        const urlLoaderOptions = {
          esModule: false,
          limit: 4096,
          fallback: {
            loader: 'file-loader',
            options: {
              esModule: false,
              emitFile: false,
              name: filename,
            },
          },
        }
        const results = urlLoader.call({
          query: urlLoaderOptions,
          resourcePath: filename,
        }, src)
    
        // strip leading Webpack prefix from file path if it exists
        return results.replace(/^module.exports = __webpack_public_path__ \+ /, 'module.exports = ')
      }
    }
    
  3. To avoid accidentally overwriting Vue CLI's default Jest presets, use a merge utility (e.g., lodash.merge) to insert a custom config in jest.config.js.

  4. Add a vue-jest config in a Jest global, setting templateCompiler.transformAssetUrls.

  5. Modify the merged preset's transform property to use our my-jest-url-loader transform for images. This requires removing other image transforms from the default Jest preset to avoid conflicts.

    // jest.config.js
    const vueJestPreset = require('@vue/cli-plugin-unit-jest/presets/default/jest-preset')
    const merge = require('lodash.merge') 3️⃣
    
    const newJestPreset = merge(vueJestPreset, {
      globals: { 4️⃣
        'vue-jest': {
          templateCompiler: {
            transformAssetUrls: {
              video: ['src', 'poster'],
              source: 'src',
              img: 'src',
              image: ['xlink:href', 'href'],
              use: ['xlink:href', 'href']
            }
          }
        }
      },
      moduleNameMapper: {
        '^foo/(.*)$': '<rootDir>/$1',
      },
    })
    
    function useUrlLoaderForImages(preset) { 5️⃣
      const imageTypes = ['jpg', 'jpeg', 'png', 'svg', 'gif', 'webp']
      const imageTypesRegex = new RegExp(`(${imageTypes.join('|')})\\|?`, 'ig')
    
      // remove the image types from the transforms
      Object.entries(preset.transform).filter(([key]) => {
        const regex = new RegExp(key)
        return imageTypes.some(ext => regex.test(`filename.${ext}`))
      }).forEach(([key, value]) => {
        delete preset.transform[key]
        const newKey = key.replace(imageTypesRegex, '')
        preset.transform[newKey] = value
      })
    
      preset.transform = {
        ...preset.transform,
        [`.+\\.(${imageTypes.join('|')})$`]: '<rootDir>/tests/my-jest-url-loader',
      }
    }
    
    useUrlLoaderForImages(newJestPreset)
    
    module.exports = newJestPreset
    

GitHub demo