Not sure if it is a bug or If I'm doing something wrong.
I try to mount my main App component with shallowMount but it doesn't works. I get the following error message:
Cannot set properties of undefined (setting 'hasOwnProperty')
It happens when vue-test-utils try to mount the App Component:
...
var appRef = vm.$refs[MOUNT_COMPONENT_REF];
// we add `hasOwnProperty` so jest can spy on the proxied vm without throwing
appRef.hasOwnProperty = function (property) {
return Reflect.has(appRef, property);
};
console.warn = warnSave;
var wrapper = createVueWrapper(app, appRef, setProps);
trackInstance(wrapper);
return wrapper;
...
Here, MOUNT_COMPONENT_REF equals 'VTU_COMPONENT' and vm.$refs[MOUNT_COMPONENT_REF] isn't defined.
Minimal example
It is available online : https://codesandbox.io/s/prod-glitter-tnoyqt?file=/src/App.spec.js
App.vue
<template>
<div id="a-test">
{{ test }}
</div>
</template>
<script>
export default {
name: "App",
};
</script>
App.spec.js
import { shallowMount } from "#vue/test-utils";
import App from "#/App";
describe("App.vue", () => {
test("Test that fails", async () => {
const wrapper = shallowMount(App);
});
});
fixed if I upgrade to #vue/test-utils 2.0.0-rc.21
Related
given code of a component with a helper function defined inside the component. how do i return whatever value i want to return with jest and react testing library?
import { act, render } from "#testing-library/react";
import Comp, * as Module from "./helper";
describe("helper", () => {
// why this one dosen't work?
test("comp with mock", () => {
let component;
const spy = jest.spyOn(Module, "PrintTest"); // dosen't work: .mockResolvedValue("Bob");
spy.mockReturnValue("bob");
// console.log(screen.debug());
act(() => {
component = render(<Comp />);
});
expect(component.getByTestId("original-data")).toBe("Bob");
});
});
component
export function PrintTest(data) {
return <div data-testid="original-data">{data}</div>;
}
export default function Comp() {
return <div>test with: {PrintTest("data from component")}</div>;
}
How do i change the PrintTest to print something else other than "data from component". and maybe "Bob" or whatever?
Thanks!
codesandbox example:
https://codesandbox.io/s/holy-bash-qf7pq8?file=/src/helper.js:27-28
I'm trying to unit test a component where you click a button which should then call store.dispatch('favoritesState/deleteFavorite').
This action then calls an api and does it's thing. I don't want to test the implementation of the vuex store, just that the vuex action is called when you click the button in the component.
The Component looks like this
<template>
<ion-item :id="favorite.key">
<ion-thumbnail class="clickable-item remove-favorite-item" #click="removeFavorite()" slot="end" id="favorite-star-thumbnail">
</ion-thumbnail>
</ion-item>
</template>
import {useStore} from "#/store";
export default defineComponent({
setup(props) {
const store = useStore();
function removeFavorite() {
store.dispatch("favoritesState/deleteFavorite", props.item.id);
}
return {
removeFavorite,
}
}
});
The jest test
import {store} from "#/store";
test(`${index}) Test remove favorite for : ${mockItemObj.kind}`, async () => {
const wrapper = mount(FavoriteItem, {
propsData: {
favorite: mockItemObj
},
global: {
plugins: [store]
}
});
const spyDispatch = jest.spyOn(store, 'dispatch').mockImplementation();
await wrapper.find('.remove-favorite-item').trigger('click');
expect(spyDispatch).toHaveBeenCalledTimes(1);
});
I have tried different solutions with the same outcome. Whenever the "trigger('click')" is run it throws this error:
Cannot read properties of undefined (reading 'dispatch') TypeError:
Cannot read properties of undefined (reading 'dispatch')
The project is written in vue3 with typescript using composition API and vuex4
I found a solution to my problem.
This is the solution I ended up with.
favorite.spec.ts
import {key} from '#/store';
let storeMock: any;
beforeEach(async () => {
storeMock = createStore({});
});
test(`Should remove favorite`, async () => {
const wrapper = mount(Component, {
propsData: {
item: mockItemObj
},
global: {
plugins: [[storeMock, key]],
}
});
const spyDispatch = jest.spyOn(storeMock, 'dispatch').mockImplementation();
await wrapper.find('.remove-favorite-item').trigger('click');
expect(spyDispatch).toHaveBeenCalledTimes(1);
expect(spyDispatch).toHaveBeenCalledWith("favoritesState/deleteFavorite", favoriteId);
});
This is the Component method:
setup(props) {
const store = useStore();
function removeFavorite() {
store.dispatch("favoritesState/deleteFavorite", favoriteId);
}
return {
removeFavorite
}
}
I'm running into the following error when trying to initialise the LiveLike Chat.
[Vue warn]: Failed to resolve component: livelike-chat
Stripped back view:
<template>
<livelike-chat></livelike-chat>
</template>
<script>
import LiveLike from "#livelike/engagementsdk";
import { onMounted } from "vue";
export default {
setup() {
onMounted(() => {
let clientId = 'somelongclientidsuppliedbylivelike';
LiveLike.init({ clientId });
});
}
};
</script>
The livelike chat is supposed to initialise to a custom element, <livelike-chat>, the trouble is Vue sees that and tries to find the component livelike-chat. How do I "tell" Vue to ignore that element, its not a component but a tag reserved for LiveLike?
You could use an isCustomElement config for this:
// main.js
const app = createApp({})
app.config.isCustomElement = tag => tag === 'livelike-chat'
Let's say I have a very basic vue-class-component as shown below:
<template>
<div>Nothing of interest here</div>
</template>
<script>
import Vue from 'vue';
import Component from 'vue-class-component';
import http from './../modules/http';
#Component
export default class Example extends Vue {
user = null;
errors = null;
/**
* #returns {string}
*/
getUserId() {
return this.$route.params.userId;
}
fetchUser() {
this.user = null;
this.errors = null;
http.get('/v1/auth/user/' + this.getUserId())
.then(response => this.user = response.data)
.catch(e => this.errors = e);
}
}
</script>
I want to test the fetchUser() method so I just mock the './../modules/http' dependency and make http.get return a Promise. The problem is that in my assertion I want to check if the URL is being built properly and in order to do so the user ID has to come from an hard-coded variable in the test.
I tried something like this but it doesn't work:
import ExampleInjector from '!!vue-loader?inject!./../../../src/components/Example.vue';
const mockedComponent = ExampleInjector({
'./../modules/http': {
get: () => new Promise(/* some logic */)
},
methods: getUserId: () => 'my_mocked_user_id'
});
Unfortunately it doesn't work and I could not find anything this specific in the Vue docs so the question is, how am I supposed to mock both external dependencies and a class component method?
NOTE: I do not want to mock this.$route.params.userId as the userId could potentially come from somewhere else as well (plus this is just an example). I just want to mock the getUserId method.
Since I specifically asked about how I could mock Vue class component methods with the inject loader here's the complete solution for the question at hand:
import ExampleInjector from '!!vue-loader?inject!./../../../src/components/Example.vue';
const getComponentWithMockedUserId = (mockedUserId, mockedComponent = null, methods = {}) => {
if (!mockedComponent) {
mockedComponent = ExampleInjector();
}
return Vue.component('test', {
extends: mockedComponent,
methods: {
getUserId: () => mockedUserId,
...methods
}
});
};
And then in my test case:
const component = getComponentWithMockedUserId('my_mocked_user_id');
const vm = new Vue(component);
I found this to be very helpful when you need to create a partial mock AND inject some dependencies too.
The easiest way to do this is to extend the component and override the method:
const Foo = Vue.extend({
template: `<div>{{iAm}}</div>`,
created() {
this.whoAmI();
},
methods: {
whoAmI() {
this.iAm = 'I am foo';
}
},
data() {
return {
iAm: ''
}
}
})
Vue.component('bar', {
extends: Foo,
methods: {
whoAmI() {
this.iAm = 'I am bar';
}
}
})
new Vue({
el: '#app'
})
In this example I'm using the extends property to tell Vue that Bar extends Foo and then I'm overriding the whoAmI method, you can see this is action here: https://jsfiddle.net/pxr34tuz/
I use something similar in one of my open source projects, which you can check out here. All I'm doing in that example is switching off the required property for the props to stop Vue throwing console errors at me.
I'm following the example from this stackoverflow answer - Test a React Component function with Jest. I have an example component and test set up. The component works correctly when loaded into App.js.
Component -
import React, { PropTypes, Component } from 'react';
export default class ExampleModule extends Component {
static propTypes = {
onAction: PropTypes.func,
}
static defaultProps = {
onAction: () => { console.log("In onAction"); }
}
doAction = () => {
// do something else
console.log('In do action');
this.props.onAction();
}
render() {
return(
<div>
<button className='action-btn' onClick= {this.doAction.bind(this)}>Do action</button>
</div>
)
}
}
And here's the test -
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import ExampleComponent from './ExampleModule.js';
let Example;
describe('Example component', function() {
beforeEach(function() {
Example = TestUtils.renderIntoDocument(<ExampleComponent />);
})
it('calls props functions', function() {
Example.doAction = jest.genMockFunction();
let actionBtn = TestUtils.findRenderedDOMComponentWithClass(Example, 'action-btn');
TestUtils.Simulate.click(actionBtn);
expect(Example.doAction).toBeCalled();
})
it('doAction calls onAction', function() {
expect(Example.props.onAction).not.toBeCalled();
Example.doAction();
expect(Example.props.onAction).toBeCalled();
})
})
However, I get the following error -
FAIL src/App/components/Example/ExampleModule.test.js
Console
console.log src/App/components/Example/ExampleModule.js:14
In do action
console.log src/App/components/Example/ExampleModule.js:24
In onAction
Example component › calls props functions
Expected the mock function to be called.
at Object.<anonymous> (src/App/components/Example/ExampleModule.test.js:17:30)
at process._tickCallback (node.js:369:9)
Example component › doAction calls onAction
toBeCalled matcher can only be used on a spy or mock function.
at Object.<anonymous> (src/App/components/Example/ExampleModule.test.js:21:40)
at process._tickCallback (node.js:369:9)
I can see the console.logs in the doAction and onAction are being called even when I want to mock out doAction.
Also, I'm unable to mock out onAction. I get this error -
TypeError: Cannot assign to read only property 'onAction' of #<Object>
I've tried jest.fn() but got the same errors.
How do I mock these functions and test them?
EDIT:
I was able to mock doAction by using jest.fn() in the following way -
let mockFn = jest.fn();
Example.doAction = mockFn()
However, I'm still unable to mock Example.props.onAction.
In your test when you render document, you need to pass a mock function which you can later watch if it was being called, something like this -
let onActionMock = jest.fn();
beforeAll(function() {
Example = TestUtils.renderIntoDocument(<ExampleComponent onAction={onActionMock}/>);
});
beforeEach(function() {
onActionMock.mockClear();
});
it('doAction calls onAction', () => {
Example.doAction();
expect(onActionMock).toBeCalled();
});
You can further test the function with what it has been called with -
expect(onActionMock).toBeCalledWith('Some argument');
Hope this helps.