Mock this.$parent.$on with jest - unit-testing

We are using vuejs, typescript, vuex and jest. We are currently using test-utils to mock the store.
But I cannot find out how to mock a call to this.$parent.$on
Here is one of our components (very simplified):
AnalysisList.ts:
import Component from 'vue-class-component'
import {Getter} from 'vuex-class'
import {UserVO} from '#/valueObjects/UserVO'
import {Vue} from 'vue-property-decorator'
#Component
export default class AnalysisList extends Vue {
#Getter('getCurrentUser') private currentUser: UserVO
private searchString = ''
public mounted() {
this.$parent.$on('resetAnalyses', this.reset)
}
public reset() {
this.searchString = ''
}
}
AnalysisList.vue:
<template lang="pug">
text test
</template>
<script lang="ts" src="./AnalysisList.ts">
</script>
AnalysisList.spec.ts:
import {shallowMount} from '#vue/test-utils'
import AnalysisList from '#/components/analysis/AnalysisList'
import Vuex from 'vuex'
import {Vue} from 'vue-property-decorator'
import VueRouter from 'vue-router'
Vue.use(Vuex)
Vue.use(VueRouter)
describe('AnalysisList.vue', () => {
const store = new Vuex.Store( {
modules: {
user: {
state: {currentUser: 'test'},
getters: {
getCurrentUser: (state: any) => state.currentUser,
},
},
},
})
it('minimum test', (done) => {
const wrapper = shallowMount(AnalysisList, {store})
done()
})
})
When I run the test, I have the following error message, because $parent is not mocked:
TypeError: Cannot read property '$on' of undefined
at VueComponent.mounted (src/components/analysis/AnalysisList/AnalysisList.vue:73:20)
at callHook (node_modules/vue/dist/vue.runtime.common.js:2919:21)
at mountComponent (node_modules/vue/dist/vue.runtime.common.js:2800:5)
at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.js:7997:10)
at mount (node_modules/#vue/test-utils/dist/vue-test-utils.js:5381:8)
at shallowMount (node_modules/#vue/test-utils/dist/vue-test-utils.js:5414:10)
at Object.done (tests/unit/AnalysisList.spec.ts:20:53)
If I try to add a new property to shallowMount parameter:
const wrapper = shallowMount(AnalysisList, {store, parent: {$on: ()=>{}}})
I obtain a type error:
TS2345: Argument of type 'VueConstructor<Vue>' is not assignable to parameter of type 'FunctionalComponentOptions<Record<string, any>, PropsDefinition<Record<string, any>>>'.   Property 'functional' is missing in type 'VueConstructor<Vue>'.
Do you have any clue to help me mock this.$parent.$on ? Thanks.

I got the same issue with vue-test-utils and Jest (under the Vue, Vuex and Typescript environment)
For me, createLocalVue() of vue-test-utils library fixed the issue. This function creates a local copy of Vue to use when mounting the component. Installing plugins on this copy of Vue prevents polluting the original Vue copy. (https://vue-test-utils.vuejs.org/api/options.html#localvue)
Adding this to my test file fixed the issue:
const EventBus = new Vue();
const GlobalPlugins = {
install(v) {
// Event bus
v.prototype.$bus = EventBus;
},
};
// create a local instance of the global bus
const localVue = createLocalVue();
localVue.use(GlobalPlugins);
Hope this helps others, thanks :)

Related

Unit/Jest test with bootstrap vue Fail

I got this error when I try to run a test on bootstrap vue components:
"ReferenceError: BootstrapVue is not defined"
In the .spec.js file I added this:
import TableSummary from "#/components/TableSummary";
import { createLocalVue, mount } from "#vue/test-utils";
const localVue = createLocalVue();
localVue.use(BootstrapVue);
describe('TableSummary', ()=> {
test('if the user is typing, the button becomes enabled', async ()=> {
const wrapper = mount(TableSummary, { localVue });
wrapper.setData({isDisabled: true});
await wrapper.vm.$nextTick;
expect(wrapper.find('input').state.isDisabled).toBe(false);
});
});
I ran the same instructions as a correct answer here on stack overflow but still, it doesn't work for me... I couldn't comment on the answer as I am not level 50, and I had to open a new question.
I think you need to import the BootstrapVue
import BootstrapVue from 'bootstrap-vue'

Vue unit test - ReferenceError: AWS is not defined in App.vue

I've used aws-sdk npm package in my vuejs app - app.vue file
//App.vue
created() {
const value = AWS.config.credentials;
}
and while write test for this file like below
//App.spec.js
import { mount, createLocalVue } from '#vue/test-utils';
import AWS from 'aws-sdk-mock';
import App from 'src/App.vue';
const localVue = createLocalVue();
localVue.use(AWS);
describe('App.vue', () => {
it('renders app content', () => {
const wrapper = mount(App, {
localVue,
});
expect(wrapper.isVueInstance()).toBeTruthy();
});
}
and ran the test and got the error
App.vue › renders app content
ReferenceError: AWS is not defined
Kindly let me know if you have any idea or any issues same like this.
EDIT:
AWS is defined globally in main.js
//main.js
import Vue from 'vue';
import AWS from 'aws-sdk';
Vue.use(AWS);
new Vue({
render: (h) => h(App),
}).$mount('#app');

Mocha Chai vue testing a vue component : this.$notify is not a function

I'm using this component : https://github.com/euvl/vue-notification
Since then, all of my Mocha chai test units are failing .
this.$notify is not a function
This is my login spec :
// Importing The testing library
import { expect } from "chai";
import { mount } from '#vue/test-utils'
// Importing The component I need to test
import Login from "#/components/Login.vue";
// Mounting the component as in real life
const wrapper = mount(Login);
describe("Login test", () => {
it("getAuth() to be a function", () => {
expect(wrapper.vm.getAuth).to.be.a("function");
});
});
I've tried out mount, shallowMount, render with no luck .
Any workaround ?
I'm calling vue-notification in main.js like this :
import Notifications from "vue-notification";
Vue.use(Notifications);
Thank you !
EDIT :
Ive tried to add
const $notify = require('vue-notification')
To my Login.vue component with no luck
EDIT 2 : Tried to call the function like this with no luck :
this.$root.$notify({
group: 'foo',
title: 'Hello ',
text: 'Cool'
});
[Vue warn]: Error in mounted hook: "TypeError: this.$root.$notify is not a function"
*EDIT : ***** Resolved by me ****** *
I was badly importing vue . Please see my working login.spec.js testing file there :
// THE ASSERTION LIBRARY
import { expect } from "chai";
// THE TESTING LIBRARY
import { mount } from "#vue/test-utils";
// THE COMPONENT THAT I WANT TO TEST
import Login from "#/components/Login.vue";
// THE EXTERNAL COMPONENTS LINKED TO MY LOGIN COMPONENT THAT I NEED TO JOIN
import Vue from 'vue';
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)
import {
required,
minLength,
between
} from "vuelidate/lib/validators";
import Notifications from "vue-notification";
import velocity from 'velocity-animate'
Vue.use(Notifications, { velocity });
// THE WRAPPER CONTAIN MY LOGIN MOUNTED COMPONENT, JUST LIKE IN THE REAL LIFE
const wrapper = mount(Login)
describe("Login test", () => {
it("getAuth() to be a function", () => {
expect(wrapper.vm.getAuth).to.be.a("function");
});
});

Testing Angular 2 service with mocha

I am trying to implement unit tests for an Angular 2 app. But I can't get it it to work.
As test runner mocha is used and executed like this:
mocha -r ts-node/register -t 10000 ./**/*.unit.ts
Consider the following test file where I define two test cases which basically should do the same thing, but neither one is working.
shared.service.unit.ts
import { TestBed, async, inject } from '#angular/core/testing';
import { SharedService } from './shared.service';
import * as Chai from 'chai';
import 'mocha';
const expect = Chai.expect;
describe('SharedService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [SharedService],
providers: [SharedService]
});
});
it('should be an object',
inject([SharedService], (service: SharedService) => {
expect(service).to.be.an('object');
})
);
});
describe('SharedService without the TestBed', () => {
let service: SharedService;
beforeEach(() => {
service = new SharedService();
});
it('should be an object', () => {
expect(service).to.be.an('object');
});
});
The first one 'SharedService' uses the Angular Testing Utility. Running it gives:
ReferenceError: Zone is not defined
The second one 'SharedService without TestBed'does not use any Angular code (similar to this example from Angular 2 Testing guide). Running it gives:
TypeError: Reflect.getMetadata is not a function
After adding these lines to the test file:
import 'core-js/es6';
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
Both test cases give the same error (from zone.js\dist\zone.js):
TypeError: Cannot read property 'prototype' of undefined
What am I doing wrong?
Got it, just needed to import 'core-js/es7/reflect':
import 'core-js/es7/reflect';
import 'mocha';
import * as Chai from 'chai';
let expect = Chai.expect;
import { SharedService } from './shared.service';
describe('SharedService', () => {
let service: SharedService;
beforeEach(() => {
service = new SharedService();
})
it('should be an object', () => {
expect(service).to.be.an('object');
})
});
You need to load all that stuff - angular, ngzone, metadata, es shims, etc. - statically in the mocha's - or systemjs or whatever you use for setting this stuff up - configuration.

How to Unit Test React-Redux Connected Components?

I am using Mocha, Chai, Karma, Sinon, Webpack for Unit tests.
I followed this link to configure my testing environment for React-Redux Code.
How to implement testing + code coverage on React with Karma, Babel, and Webpack
I can successfully test my action and reducers javascript code, but when it comes to testing my components it always throw some error.
import React from 'react';
import TestUtils from 'react/lib/ReactTestUtils'; //I like using the Test Utils, but you can just use the DOM API instead.
import chai from 'chai';
// import sinon from 'sinon';
import spies from 'chai-spies';
chai.use(spies);
let should = chai.should()
, expect = chai.expect;
import { PhoneVerification } from '../PhoneVerification';
let fakeStore = {
'isFetching': false,
'usernameSettings': {
'errors': {},
'username': 'sahil',
'isEditable': false
},
'emailSettings': {
'email': 'test#test.com',
'isEmailVerified': false,
'isEditable': false
},
'passwordSettings': {
'errors': {},
'password': 'showsomestarz',
'isEditable': false
},
'phoneSettings': {
'isEditable': false,
'errors': {},
'otp': null,
'isOTPSent': false,
'isOTPReSent': false,
'isShowMissedCallNumber': false,
'isShowMissedCallVerificationLink': false,
'missedCallNumber': null,
'timeLeftToVerify': null,
'_verifiedNumber': null,
'timers': [],
'phone': '',
'isPhoneVerified': false
}
}
function setup () {
console.log(PhoneVerification);
// PhoneVerification.componentDidMount = chai.spy();
let output = TestUtils.renderIntoDocument(<PhoneVerification {...fakeStore}/>);
return {
output
}
}
describe('PhoneVerificationComponent', () => {
it('should render properly', (done) => {
const { output } = setup();
expect(PhoneVerification.prototype.componentDidMount).to.have.been.called;
done();
})
});
This following error comes up with above code.
FAILED TESTS:
PhoneVerificationComponent
✖ should render properly
Chrome 48.0.2564 (Mac OS X 10.11.3)
Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
Tried switching from sinon spies to chai-spies.
How should I unit test my React-Redux Connected Components(Smart Components)?
A prettier way to do this, is to export both your plain component, and the component wrapped in connect. The named export would be the component, the default is the wrapped component:
export class Sample extends Component {
render() {
let { verification } = this.props;
return (
<h3>This is my awesome component.</h3>
);
}
}
const select = (state) => {
return {
verification: state.verification
}
}
export default connect(select)(Sample);
In this way you can import normally in your app, but when it comes to testing you can import your named export using import { Sample } from 'component'.
The problem with the accepted answer is that we are exporting something unnecessarily just to be able to test it. And exporting a class just to test it is not a good idea in my opinion.
Here is a neater solution without the need of exporting anything but the connected component:
If you are using jest, you can mock connect method to return three things:
mapStateToProps
mapDispatchToProps
ReactComponent
Doing so is pretty simple. There are 2 ways: Inline mocks or global mocks.
1. Using inline mock
Add the following snippet before the test's describe function.
jest.mock('react-redux', () => {
return {
connect: (mapStateToProps, mapDispatchToProps) => (ReactComponent) => ({
mapStateToProps,
mapDispatchToProps,
ReactComponent
}),
Provider: ({ children }) => children
}
})
2. Using file mock
Create a file __mocks__/react-redux.js in the root (where package.json is located)
Add the following snippet in the file.
module.exports = {
connect: (mapStateToProps, mapDispatchToProps) => (ReactComponent) => ({
mapStateToProps,
mapDispatchToProps,
ReactComponent,
}),
Provider: ({children}) => children
};
After mocking, you would be able to access all the above three using Container.mapStateToProps,Container.mapDispatchToProps and Container.ReactComponent.
Container can be imported by simply doing
import Container from '<path>/<fileName>.container.js'
Hope it helps.
Note that if you use file mock. The mocked file will be used globally for all the test cases(unless you do jest.unmock('react-redux')) before the test case.
Edit: I have written a detailed blog explaining the above in detail:
http://rahulgaba.com/front-end/2018/10/19/unit-testing-redux-containers-the-better-way-using-jest.html
You can test your connected component and I think you should do so. You may want to test the unconnected component first, but I suggest that you will not have complete test coverage without also testing the connected component.
Below is an untested extract of what I do with Redux and Enzyme. The central idea is to use Provider to connect the state in test to the connected component in test.
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import SongForm from '../SongForm'; // import the CONNECTED component
// Use the same middlewares you use with Redux's applyMiddleware
const mockStore = configureMockStore([ /* middlewares */ ]);
// Setup the entire state, not just the part Redux passes to the connected component.
const mockStoreInitialized = mockStore({
songs: {
songsList: {
songs: {
songTags: { /* ... */ }
}
}
}
});
const nullFcn1 = () => null;
const nullFcn2 = () => null;
const nullFcn3 = () => null;
const wrapper = mount( // enzyme
<Provider store={store}>
<SongForm
screen="add"
disabled={false}
handleFormSubmit={nullFcn1}
handleModifySong={nullFcn2}
handleDeleteSong={nullFcn3}
/>
</Provider>
);
const formPropsFromReduxForm = wrapper.find(SongForm).props(); // enzyme
expect(
formPropsFromReduxForm
).to.be.deep.equal({
screen: 'add',
songTags: initialSongTags,
disabled: false,
handleFormSubmit: nullFcn1,
handleModifySong: nullFcn2,
handleDeleteSong: nullFcn3,
});
===== ../SongForm.js
import React from 'react';
import { connect } from 'react-redux';
const SongForm = (/* object */ props) /* ReactNode */ => {
/* ... */
return (
<form onSubmit={handleSubmit(handleFormSubmit)}>
....
</form>
};
const mapStateToProps = (/* object */ state) /* object */ => ({
songTags: state.songs.songTags
});
const mapDispatchToProps = () /* object..function */ => ({ /* ... */ });
export default connect(mapStateToProps, mapDispatchToProps)(SongForm)
You may want to create a store with pure Redux. redux-mock-store is just a light-weight version of it meant for testing.
You may want to use react-addons-test-utils instead of airbnb's Enzyme.
I use airbnb's chai-enzyme to have React-aware expect options. It was not needed in this example.
redux-mock-store is an awesome tool to test redux connected components in react
const containerElement = shallow((<Provider store={store}><ContainerElement /></Provider>));
Create fake store and mount the component
You may refer to this article Testing redux store connected React Components using Jest and Enzyme | TDD | REACT | REACT NATIVE
Try creating 2 files, one with component itself, being not aware of any store or anything (PhoneVerification-component.js). Then second one (PhoneVerification.js), which you will use in your application and which only returns the first component subscribed to store via connect function, something like
import PhoneVerificationComponent from './PhoneVerification-component.js'
import {connect} from 'react-redux'
...
export default connect(mapStateToProps, mapDispatchToProps)(PhoneVerificationComponent)
Then you can test your "dumb" component by requiring PhoneVerification-component.js in your test and providing it with necessary mocked props. There is no point of testing already tested (connect decorator, mapStateToProps, mapDispatchToProps etc...)