Unit testing Vue components that rely on a global event bus

ava, javascript, unit-testing, vue.js

Solution

First of all, I'd recommend to remove the global EventBus from the window object (if you can), as it's not often ideal to rely on global props that could get overwritten by some other parts of the code leading to undesired effects.

You can just create a `event-bus.js` file and create the event bus in there:

import Vue from 'vue';
export const EventBus = new Vue();

Then you can just import that file from any other component that needs access to the event bus like

import {EventBus} from '/path/to/event-bus.js'

From there, you can use it exactly as you're currently using it.

However, if you have to keep your EventBus in the window object for some reason, you'll have to also mock it so you can test it.

First, when you call the `browserEnv` function, you'll need to mock the window object by doing this:

browserEnv(['window']);

Then, window will be available as a global variable, but you will need to change the implementation of your component to explicitly grab the EventBus from the window object, like:

window.EventBus

You can also define it globally in your component like:

const { EventBus } = window

Let me know if this works.

Thanks,

Problem

I declare an event bus in my global `app.js` like so: ``` window.Event = new Vue(); ``` The component looks like ``` export default { data() { return { hasError: false, zip: '', }; }, methods: { setZip: function() { this.hasError = false; this.$emit('setZip', this.zip); }, }, mounted() { Event.$on('showErrors', (errors) => { this.hasError = errors.zip ? true : false; }); this.zip = this.editZip; }, props: [ 'editZip' ], } ``` I unit test my components with `ava` with the following `helpers/setup.js`: ``` const browserEnv = require('browser-env'); const hook = require('vue-node'); const { join } = require('path'); // Setup a fake browser environment browserEnv(); // Pass an absolute path to your webpack configuration to the hook function. hook(join(__dirname, './webpack.config.js')); ``` The `webpack.config.js` looks like: ``` module.exports = { module: { loaders: [ { test: /\.vue$/, loader: 'vue-loader', }, { test: /\.js$/, loader: 'babel', exclude: /node_modules/, }, ], }, resolve: { extensions: ['.js', '.vue'], }, }; ``` When running the following test ``` import Vue from 'vue/dist/vue.js'; import test from 'ava'; import Zip from '../../resources/assets/js/components/Company/Zip.vue'; let vm; test.beforeEach(t => { let Z = Vue.extend(Zip); vm = new Z({ propsData: { editZip: 1220 }}).$mount(); }); test('that it renders a div with class form-group', t => { t.is(vm.$el.className, 'form-group'); }); ``` it passes, but the following error gets thrown: ``` [Vue warn]: Error in mounted hook: "TypeError: Event.$on is not a function" (found in <Root>) TypeError: Event.$on is not a function at VueComponent.mounted (/mnt/c/code/leaflets/resources/assets/js/components/Company/City.vue:107:15) at callHook (/mnt/c/code/leaflets/node_modules/vue/dist/vue.js:2530:21) at mountComponent (/mnt/c/code/leaflets/node_modules/vue/dist/vue.js:2424:5) at VueComponent.Vue$3.$mount (/mnt/c/code/leaflets/node_modules/vue/dist/vue.js:7512:10) at VueComponent.Vue$3.$mount (/mnt/c/code/leaflets/node_modules/vue/dist/vue.js:9592:16) at Test._ava2.default.beforeEach.t [as fn] (/mnt/c/code/leaflets/tests/js/CompanyCity.js:12:9) at Test.callFn (/mnt/c/code/leaflets/node_modules/ava/lib/test.js:281:18) at Test.run (/mnt/c/code/leaflets/node_modules/ava/lib/test.js:294:23) at runNext (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:58:44) at Sequence.run (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:90:10) at Concurrent.run (/mnt/c/code/leaflets/node_modules/ava/lib/concurrent.js:41:37) at runNext (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:58:44) at Sequence.run (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:90:10) at runNext (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:58:44) at Sequence.run (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:90:10) at Bluebird.try (/mnt/c/code/leaflets/node_modules/ava/lib/runner.js:214:48) at tryCatcher (/mnt/c/code/leaflets/node_modules/bluebird/js/release/util.js:16:23) at Function.Promise.attempt.Promise.try (/mnt/c/code/leaflets/node_modules/bluebird/js/release/method.js:39:29) at Runner.run (/mnt/c/code/leaflets/node_modules/ava/lib/runner.js:214:22) at process.on.options (/mnt/c/code/leaflets/node_modules/ava/lib/main.js:82:10) at emitOne (events.js:96:13) at process.emit (events.js:191:7) at process.on.message (/mnt/c/code/leaflets/node_modules/ava/lib/process-adapter.js:14:10) at emitTwo (events.js:106:13) at process.emit (events.js:194:7) at process.nextTick (internal/child_process.js:766:12) at _combinedTickCallback (internal/process/next_tick.js:73:7) at process._tickCallback (internal/process/next_tick.js:104:9) ``` I can't figure out where to declare the `window.Event = new Vue()` in my test, so that the tested component can access the `Event` variable.

Original source