Search code examples
javascriptvue.jsmockingjestjsvue-cli-3

Vue.JS / JEST - Unable to Mock Asset Reference


I am trying to set up a component with a reference link to 3 assets. This is breaking all of my tests as follows...

Test suite failed to run

SyntaxError: Invalid or unexpected token.

The second of the following 4 reference lines are highlighted (the last 3 are images).

import SpecialistChooser from './specialist-chooser'

import imgCo2 from '@/assets/motivations/t-co2.jpg'
import imgFuelBill from '@/assets/motivations/fuel-bill.jpg'
import imgBoth from '@/assets/motivations/both.jpg'

I have read through a detailed article on mocking assets here... https://jestjs.io/docs/en/webpack.html

But this doesn't seem to make any odds. Following that I have a general mock set up for files that looks like this...

const path = require('path')

module.exports = {
    process(src, filename) {
        return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'
    },
}

(file-mock.js)

My jest config in my package.json is as follows...

  "jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      "vue"
    ],
    "transform": {
      ".*\\.(vue)$": "vue-jest",
      "^.+\\.js$": "<rootDir>/node_modules/babel-jest"
    },
    "moduleNameMapper": {
      "^@/(.*)$": "<rootDir>/src/$1",
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/file-mock.js",
      "\\.(css|less)$": "<rootDir>/__mocks__/style-mock.js"
    },
    "collectCoverage": false,
    "collectCoverageFrom": [
      "src/**/*.{js,vue}",
      "!**/node_modules/**",
      "!**/dist/**"
    ]
  }

I'm not making any headway here and my googling keeps returning various different sites reprinting exactly the same article I've already linked to. I must be missing something but I can't see what!

I've tried manually mocking the references in the test file too, like this...

jest.mock('@/assets/motivations/t-co2.jpg', () => {
    return '/app/img/t-co2.jpg'
})
jest.mock('@/assets/motivations/fuel-bill.jpg', () => {
    return '/app/img/fuel-bill.jpg'
})
jest.mock('@/assets/motivations/both.jpg', () => {
    return '/app/img/both.jpg'
})

This doesn't work either.

UPDATED: Working Configuration After fixing the initial problem, thanks to the answer below, I was able to put together a solid working implementation on this and thought I would share it for future searchers.

My package.json is now configured as follows...

  "jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      "vue"
    ],
    "transform": {
      ".*\\.(vue)$": "vue-jest",
      "^.+\\.js$": "<rootDir>/node_modules/babel-jest",
      "\\.(jpg|jpeg|png|gif|webp|svg)$": "<rootDir>/img-test-transformer.js",
      "\\.(eot|otf|svg|ttf|woff|woff2)$": "<rootDir>/font-test-transformer.js",
      "\\.(mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/media-test-transformer.js"
    },
    "moduleNameMapper": {
      "\\.(css|less)$": "<rootDir>/__mocks__/style-mock.js",
      "^@/(.*)$": "<rootDir>/src/$1"
    }

Instead of using the moduleNameMapper I am using transform (I was also trying to combine the two which didn't work in my original post). The transform option allows me to return useful things from the imports. For Vue, in the case of most file imports I simply want the path. In order to do this I wrote 3 transformers as follows...

const path = require('path')

module.exports = {
    process(src, filename) {
        return 'module.exports = ' + JSON.stringify('/app/img/' +path.basename(filename)) + ';'
    },
}

const path = require('path')

module.exports = {
    process(src, filename) {
        return 'module.exports = ' + JSON.stringify('/app/fonts/' + path.basename(filename)) + ';'
    },
}

const path = require('path')

module.exports = {
    process(src, filename) {
        return 'module.exports = ' + JSON.stringify('/app/media/' + path.basename(filename)) + ';'
    },
}

This gives me different return filename path types for use in my tests.

No jest.mock calls required anywhere either.

PS: Beware Jest Cache - This stung me badly and I had to run jest --clearCache a few times otherwise changes to the transformers aren't read.


Solution

  • With the current configuration the assets will be matched by the first regular expression and "<rootDir>/src/$1" would be used instead of the desired <rootDir>/__mocks__/file-mock.js

    A solution would be re-ordering the moduleNameMapper as below

    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/file-mock.js",
      "\\.(css|less)$": "<rootDir>/__mocks__/style-mock.js",
      "^@/(.*)$": "<rootDir>/src/$1"
    },