title: Cannot read property '$i18n' of undefined when using Vue Test Utils
body: <p>I'm trying to test a BaseDialog component that uses VueI18n for translations with vue-test-utils. I cannot get the test to run do the the following error:</p>
  
  <pre><code>TypeError: Cannot read property '$i18n' of undefined
  
      at VueComponent.default (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/src/components/BaseDialog/BaseDialog.vue:2671:220)
      at getPropDefaultValue (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:1662:11)
      at validateProp (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:1619:13)
      at loop (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4612:17)
      at initProps (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4643:33)
      at initState (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4586:21)
      at VueComponent.Vue._init (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4948:5)
      at new VueComponent (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:5095:12)
      at createComponentInstanceForVnode (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:3270:10)
      at init (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:3101:45)
      at createComponent (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:5919:9)
      at createElm (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:5866:9)
      at VueComponent.patch [as __patch__] (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:6416:7)
      at VueComponent.Vue._update (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:3904:19)
      at VueComponent.updateComponent (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4025:10)
      at Watcher.get (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4426:25)
      at new Watcher (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4415:12)
      at mountComponent (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4032:3)
      at VueComponent.Object.&lt;anonymous&gt;.Vue.$mount (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:8350:10)
      at mount (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/@vue/test-utils/dist/vue-test-utils.js:8649:21)
      at shallowMount (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/@vue/test-utils/dist/vue-test-utils.js:8677:10)
      at Object.it (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/src/components/BaseDialog/__tests__/BaseDialog.spec.js:22:21)
      at Object.asyncJestTest (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/jest-jasmine2/build/jasmine_async.js:108:37)
      at resolve (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/jest-jasmine2/build/queue_runner.js:56:12)
      at new Promise (&lt;anonymous&gt;)
      at mapper (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/jest-jasmine2/build/queue_runner.js:43:19)
      at promise.then (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/jest-jasmine2/build/queue_runner.js:87:41)
      at process.internalTickCallback (internal/process/next_tick.js:77:7)
  </code></pre>
  
  <p>I've tried every solution listed <a href="https://github.com/kazupon/vue-i18n/issues/198#issuecomment-316692326" rel="nofollow noreferrer">here</a> with no result.</p>
  
  <p>Here is the relevant code:</p>
  
  <pre><code>// BaseDialog.spec.js
  import { shallowMount, createLocalVue } from '@vue/test-utils'
  import BaseDialog from '@/components/BaseDialog/BaseDialog'
  import VueI18n from 'vue-i18n'
  
  describe('BaseDialog', () =&gt; {
    it('is called', () =&gt; {
      let localVue = createLocalVue()
      localVue.use(VueI18n)
      const messages = {
        gb: {
          'ui.universal.label.ok': 'OK',
          'ui.universal.label.cancel': 'Cancel'
        }
      }
  
      const i18n = new VueI18n({
        locale: 'gb',
        fallbackLocale: 'gb',
        messages
      })
  
      const wrapper = shallowMount(BaseDialog, {
        i18n,
        localVue
      })
      expect(wrapper.name()).toBe('BaseDialog')
      expect(wrapper.isVueInstance()).toBeTruthy()
    })
  })
  
  // BaseDialog.vue
  &lt;template&gt;
    &lt;transition :name="animation"&gt;
      &lt;div v-if="isActive" class="dialog modal is-active" :class="size"&gt;
        &lt;div class="modal-background" @click="cancel('outside')" /&gt;
        &lt;div class="modal-card animation-content"&gt;
          &lt;header v-if="title" class="modal-card-head"&gt;
            &lt;p class="modal-card-title"&gt;{{ title }}&lt;/p&gt;
          &lt;/header&gt;
  
          &lt;section
            class="modal-card-body"
            :class="{ 'is-titleless': !title, 'is-flex': hasIcon }"
          &gt;
            &lt;div class="media"&gt;
              &lt;div v-if="hasIcon" class="media-left"&gt;
                &lt;b-icon
                  :icon="icon ? icon : iconByType"
                  :pack="iconPack"
                  :type="type"
                  :both="!icon"
                  size="is-large"
                /&gt;
              &lt;/div&gt;
              &lt;div class="media-content"&gt;
                &lt;p v-html="message" /&gt;
  
                &lt;div v-if="hasInput" class="field"&gt;
                  &lt;div class="control"&gt;
                    &lt;input
                      ref="input"
                      v-model="prompt"
                      class="input"
                      :class="{ 'is-danger': validationMessage }"
                      v-bind="inputAttrs"
                      @keyup.enter="confirm"
                    /&gt;
                  &lt;/div&gt;
                  &lt;p class="help is-danger"&gt;{{ validationMessage }}&lt;/p&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;
          &lt;/section&gt;
  
          &lt;footer class="modal-card-foot"&gt;
            &lt;button
              v-if="showCancel"
              ref="cancelButton"
              class="button"
              @click="cancel('button')"
            &gt;
              {{ cancelText }}
            &lt;/button&gt;
            &lt;button
              ref="confirmButton"
              class="button"
              :class="type"
              @click="confirm"
            &gt;
              {{ confirmText }}
            &lt;/button&gt;
          &lt;/footer&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/transition&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
  import Modal from '../BaseModal/BaseModal'
  import config from '../../utils/config'
  import { removeElement } from '../../utils/helpers'
  
  export default {
    name: 'BaseDialog',
    extends: Modal,
    props: {
      title: {
        type: String,
        default: null
      },
      message: {
        type: String,
        default: null
      },
      icon: {
        type: String,
        default: null
      },
      iconPack: {
        type: String,
        default: null
      },
      hasIcon: {
        type: Boolean,
        default: false
      },
      type: {
        type: String,
        default: 'is-primary'
      },
      size: {
        type: String,
        default: null
      },
      confirmText: {
        type: String,
        default: () =&gt; {
          return config.defaultDialogConfirmText
            ? config.defaultDialogConfirmText
            : this.$i18n('ui.universal.label.ok')
        }
      },
      cancelText: {
        type: String,
        default: () =&gt; {
          return config.defaultDialogCancelText
            ? config.defaultDialogCancelText
            : this.$i18n('ui.universal.label.cancel')
        }
      },
      hasInput: Boolean, // Used internally to know if it's prompt
      inputAttrs: {
        type: Object,
        default: () =&gt; ({})
      },
      onConfirm: {
        type: Function,
        default: () =&gt; {}
      },
      focusOn: {
        type: String,
        default: 'confirm'
      }
    },
    data() {
      const prompt = this.hasInput ? this.inputAttrs.value || '' : ''
  
      return {
        prompt,
        isActive: false,
        validationMessage: ''
      }
    },
    computed: {
      /**
       * Icon name (MDI) based on the type.
       */
      iconByType() {
        switch (this.type) {
          case 'is-info':
            return 'information'
          case 'is-success':
            return 'check-circle'
          case 'is-warning':
            return 'alert'
          case 'is-danger':
            return 'alert-circle'
          default:
            return null
        }
      },
      showCancel() {
        return this.cancelOptions.indexOf('button') &gt;= 0
      }
    },
    beforeMount() {
      // Insert the Dialog component in body tag
      this.$nextTick(() =&gt; {
        document.body.appendChild(this.$el)
      })
    },
    mounted() {
      this.isActive = true
  
      if (typeof this.inputAttrs.required === 'undefined') {
        this.$set(this.inputAttrs, 'required', true)
      }
  
      this.$nextTick(() =&gt; {
        // Handle which element receives focus
        if (this.hasInput) {
          this.$refs.input.focus()
        } else if (this.focusOn === 'cancel' &amp;&amp; this.showCancel) {
          this.$refs.cancelButton.focus()
        } else {
          this.$refs.confirmButton.focus()
        }
      })
    },
    methods: {
      /**
       * If it's a prompt Dialog, validate the input.
       * Call the onConfirm prop (function) and close the Dialog.
       */
      confirm() {
        if (this.$refs.input !== undefined) {
          if (!this.$refs.input.checkValidity()) {
            this.validationMessage = this.$refs.input.validationMessage
            this.$nextTick(() =&gt; this.$refs.input.select())
            return
          }
        }
  
        this.onConfirm(this.prompt)
        this.close()
      },
  
      /**
       * Close the Dialog.
       */
      close() {
        this.isActive = false
        // Timeout for the animation complete before destroying
        setTimeout(() =&gt; {
          this.$destroy()
          removeElement(this.$el)
        }, 150)
      }
    }
  }
  &lt;/script&gt;
  &lt;style lang="scss"&gt;
  .dialog {
    .modal-card {
      max-width: 460px;
      width: auto;
      .modal-card-head {
        font-size: $size-5;
        font-weight: $weight-semibold;
      }
      .modal-card-body {
        .field {
          margin-top: 16px;
        }
      }
      .modal-card-foot {
        justify-content: flex-end;
        .button {
          display: inline; // Fix Safari centering
          min-width: 5em;
          font-weight: $weight-semibold;
        }
      }
      @include tablet {
        min-width: 320px;
      }
    }
    &amp;.is-small {
      .modal-card,
      .input,
      .button {
        @include control-small;
      }
    }
    &amp;.is-medium {
      .modal-card,
      .input,
      .button {
        @include control-medium;
      }
    }
    &amp;.is-large {
      .modal-card,
      .input,
      .button {
        @include control-large;
      }
    }
  }
  &lt;/style&gt;
  </code></pre>
  
  <p>I don't really know what else to try here. This is the beginning of a project where I must support 9 languages with over 500 keys a piece, so I've got to get this working. Any help is very much appreciated.</p>