[Vuetify] Multiple instances of Vue detected

Setting up unit tests today using localeVue with Vuetify and @vue/test-unit I ran into the warning message:

[Vuetify] Multiple instances of Vue detected

After googling around I found a number of issues related to this warning message. There are different ways to solve it, which I will address in this post.

1. Ignore it

It is a warning message, so you can safely ignore it. But that red warning text sure is annoying.

2. Use Vue instead of localeVue

You could use initialize the component using Vue instead of localVue. That said the documentation of @vue/test-unit explicitly warns agains this.

3. Suppress the warning message

A hack, but in my opinion the least evel one of all the options is suppressing the warning message. This can be done very locally in the beforeEach. This is the code I am using that is based on this issue comment.

The SilenceWarnHack.js class:

/**
 * This is a hack to suppress the Vuetify Multiple instances of Vue detected warning.
 * See https://github.com/vuetifyjs/vuetify/issues/4068#issuecomment-446988490 for more information.
 */
export class SilenceWarnHack {
  constructor() {
    this.originalLogError = console.error
  }
  enable() {
    console.error = (...args) => {
      if (args[0].includes('[Vuetify]') && args[0].includes('https://github.com/vuetifyjs/vuetify/issues/4068')) return
      this.originalLogError(...args)
    }
  }
  disable() {
    console.error = this.originalLogError
  }
}

And the beforeEach function that is part of the test file:

import { createLocalVue, shallowMount } from '@vue/test-utils'
import { SilenceWarnHack } from '@tst/helpers/SilenceWarnHack'
import Vuetify from 'vuetify'
import VStatsCard from '@/components/common/VStatsCard.vue'

const silenceWarnHack = new SilenceWarnHack()

describe('VStatsCard.vue', () => {
  let localVue = null
  beforeEach(() => {
    silenceWarnHack.enable()
    localVue = createLocalVue()
    localVue.use(Vuetify)
    silenceWarnHack.disable()
  })
  ...
}

It works like a dream ๐Ÿ™‚

Fix Jest describe/it/expected not recognised in Webstorm

Don’t you just hate it when Webstorm underlines functions as unknown, because it doesn’t recognise them? It turns out it is pretty simple to fix if you know how and have a TypeScript (ts) file with definitions available.

UPDATE
It turns out there is a much easier way of adding the definitions.

In File > Settings… > Languages & Frameworks > JavaScript > Libraries, click the button Download..., select ‘jest’ from the list of available definitions, click the button Download and Install. That’s it!

Webstorm rules!

First we need to install the Jest TypeScript definitions.

npm install --save-dev @types/jest

Then we need to add them in Webstorm by adding it as JavaScript library in the Settings.

File > Settings... >  Languages & Frameworks > JavaScript > Libraries

In the dialog click theย Add.. button. The following dialog is opened.

Webstorm add JavaScript Library

In the dialog enter the following information.

Name: Jest
Visibility: Project

Then click on the + button and select the option Attach files....

Webstorm add JavaScript Library

The Jest TypeScript definitions are installed in the node_modules directory under your project directory. The Jest TypeScript definitions are installed in @types\jest\index.d.ts. Select the file and click the OK button.

That’s it! Now Webstorm recognises Jest describe/it/expect. O yeah!

Testing Mongoose plugin with Jest and shared data

Grrrr just spent the better half of 2 hours trying to figure out why my Jest tests were not working for a Mongoose plugin I’m developing. It turns out that the schema configuration I was sharing between the tests was the culprit.

let Mongoose = require('mongoose');
let Schema = Mongoose.Schema;
const _ = require('lodash');
var mongooseI18n = require('../src/mongoose/mongoose-i18n-localize');

Mongoose.set('debug', true);

const simpleSchemaConfig = {
  firstName: { type: String, i18n: true, required: true },
  lastName: { type: String, i18n: false, required: true }
};

describe('Mongoose i18n localize', () => {
  beforeEach(() => {
    // Clear compiled models to avoid OverwriteModelError
    Mongoose.models = {};
  });
  
  it('should throw an exception if the locales array is not defined', () => {
    const SimpleTestSchema = new Schema(simpleSchemaConfig);
    expect(() => {
      SimpleTestSchema.plugin(mongooseI18n, {
        defaultLocale: 'nl',
        allLocalesRequired: false
      });
    }).toThrow('The required option locales array not provided or empty');
  });

  it('should throw an exception if the locales array is an empty array', () => {
    const SimpleTestSchema = new Schema(simpleSchemaConfig);
    expect(() => {
      SimpleTestSchema.plugin(mongooseI18n, {
        locales: [],
        defaultLocale: 'nl',
        allLocalesRequired: false
      });
    }).toThrow('The required option locales array not provided or empty');
  });

  ...

});

The plugin makes changes to the schema configuration and these changes were made to the shared config. The fix I used was to make a copy of the shared config with lodash’s cloneDeep function. The only change I needed to make was the schema initialization.

const SimpleTestSchema = new Schema(_.cloneDeep(simpleSchemaConfig));

Now my tests run like a charm ๐Ÿ™‚

Bonus

When I initially ran into the above problem, I was also initializing the schema globally. I thought that was the problem, so I moved the following statement to each test case.

const SimpleTestSchema = new Schema(simpleSchemaConfig);

That caused the exception OverwriteModelError, because I was trying to initialize a compiled model. The fix for this problem was to remove all models between test cases using beforeEach. The following code does the trick.

beforeEach(() => {
  // Clear compiled models to avoid OverwriteModelError
  Mongoose.models = {};
});

Hope this helps someone.