Focus event unit test doesn't works in Vuejs / Jest - vue.js

I want to create a unit test for two events, on focus and on blur.
I am using vueJS and jest.
handleFocus(event) {
if (this.blured === true)
if (event.relatedTarget !== null) {
this.blured = event.relatedTarget.className
.toString()
.includes("datepicker");
} else this.blured = false;
}
That's what i tried, but the method seems not to be called
beforeEach(() => {
mocks = {
$t: jest.fn()
};
});
it("calls 'handleFocus' on focus", async () => {
const wrapper = mount(CxpDatepicker, {
mocks,
localVue
});
const input = wrapper.find("input");
wrapper.vm.handleFocus = jest.fn();
input.trigger("focus");
await localVue.nextTick();
expect(wrapper.vm.handleFocus).toHaveBeenCalled();
});
Please help pe to find the solution.

I understand I am very late to reply, but if you or anyone else still needs to know,
Try following to invoke your event:
input.element.dispatchEvent(new FocusEvent('focus'));
Instead of
input.trigger("focus");
I was also not able to invoke it. So I tried this way, and it worked for me.

Related

Mock Linking.openURL in React Native it's never been called

I´m writing some tests for my app and I´m trying to mock Linking module. I'm using jest.
The Linking.canOpenURL mock it's working fine (toHaveBeenCalled is returning true), but openURL mock is never called.
function mockSuccessLinking() {
const canOpenURL = jest
.spyOn(Linking, 'canOpenURL')
.mockImplementation(() => Promise.resolve(true));
const openURL = jest
.spyOn(Linking, 'openURL')
.mockImplementation(() => Promise.resolve(true));
return { canOpenURL, openURL };
}
The problem is that openURL is not been called.
Here is the test:
test('should open url when there is a proper app the open it', async () => {
const { canOpenURL, openURL } = mockSuccessLinking();
const { result } = renderHook(() =>
useApplyToJob('https://www.google.com/'),
);
const [apply] = result.current;
// Act
apply();
// Assert
expect(result.current[1].error).toBeNull();
expect(canOpenURL).toHaveBeenCalled();
expect(openURL).toHaveBeenCalled();
});
And this the hook under test:
export function useApplyToJob(url) {
const [error, setError] = useState(null);
const apply = () => {
Linking.canOpenURL(url).then(supported => {
if (supported) {
Linking.openURL(url);
} else {
setError(`Don't know how to open ${url}`);
}
});
};
return [apply, { error }];
}
Given canOpenURL returns a promise, you'll need to wait for the async to occur before testing if openURL has been called. react-hooks-testing-library ships a few async utils to help with this.
Generally it's preferred to use waitForNextUpdate or waitForValueToChange as they are a bit more descriptive of what the test is waiting for, but your hook is not updating any state in the successful case, so you will need to use the more general waitFor utility instead:
test('should open url when there is a proper app the open it', async () => {
const { canOpenURL, openURL } = mockSuccessLinking();
const { result, waitFor } = renderHook(() =>
useApplyToJob('https://www.google.com/'),
);
const [apply] = result.current;
// Act
apply();
// Assert
expect(result.current[1].error).toBeNull();
expect(canOpenURL).toHaveBeenCalled();
await waitFor(() => {
expect(openURL).toHaveBeenCalled();
});
});
As a side note, destructuring result.current to access apply is not recommended. It may work now, but it does not take much refactoring before the apply you're calling is using stale values from a previous render.
Similarly, I'd recommend wrapping the apply() call in act, even though it does not update any state right now. It just makes refactoring easier in the future as well as keeping your tests more consistent when you're testing the error case (which will need an act call).
import { renderHook, act } from '#testing-library/react-hooks';
// ...
test('should open url when there is a proper app the open it', async () => {
const { canOpenURL, openURL } = mockSuccessLinking();
const { result, waitFor } = renderHook(() =>
useApplyToJob('https://www.google.com/'),
);
// Act
act(() => {
result.current[0]();
});
// Assert
expect(result.current[1].error).toBeNull();
expect(canOpenURL).toHaveBeenCalled();
await waitFor(() => {
expect(openURL).toHaveBeenCalled();
});
});

Unit test jest enzyme throws error on Formik 'resetForm'

I am trying to run unit test (enzyme) throws error on Formik 'resetForm'.
TypeError: Cannot read property 'resetForm' of undefined
FormikForm.js
_handleSubmitPress = (values, { resetForm }) => {
const { onSubmit } = this.props;
if (onSubmit) {
onSubmit({ ...values, resetForm });
}
};
UnitTest.js:
it('Should fire formik form submit', () => {
const UpdateButtonPressMock = jest.fn();
const component = Component({
onSubmit: UpdateButtonPressMock,
});
expect(component.find(Formik)).toHaveLength(1);
component.find(Formik)
.first()
.simulate('Submit');
expect(UpdateButtonPressMock).toHaveBeenCalled();
});
I couldn't find any solution for this error.
Could someone help me on the above? I would really appreciate any help.
According to official docs for simulate, the function signature accepts an optional mock event.
The code you are testing uses properties that are not included in the default SyntheticEvent object that ReactWrapper passes to your event handler by default, for instance event.resetForm.
One way to do this is by triggering Formik's onSubmit directly like so:
// UnitTest.js
.simulate("submit", { resetForm: whateverYourMockResetFormValueShouldBe })
component.find(Formik)
.first()
.prop('onSubmit')(valuesMock, { resetForm: UpdateButtonPressMock });
expect(UpdateButtonPressMock).toHaveBeenCalled();
I haven't tested this, but you should be able to pass the event along with simulate as well.
// UnitTest.js
component.find(Formik)
.first()
.simulate("submit", { resetForm: UpdateButtonPressMock })
expect(UpdateButtonPressMock).toHaveBeenCalled();

Jest Testing in Vue

How would use Jest Test to test this method:
delayedFetch() {
setTimeout(() => {
this.fetchData();
}, 1000);
I have tried using Async and await but I'm prob using it wrong.
It's hard to test code with side effects, and you did not provide the entire context, but I try to help.
I think the this in the this.fetchData() inside the setTimeout is referencing the delayedFetch method itself. (I don't know it is your intention, as far as How I use vue.js
But anyway you can find, how to test setTimeouts here link to jest doc
Here is a simple implementation
const someObj = {
// I assume the delayedFetch is some method of an object
fetchData() {
return "some-data";
},
delayedFetch() {
const vue = this;
setTimeout(() => {
vue.fetchData();
}, 1000);
}
}
jest.useFakeTimers();
test("delayedFetchTest", () => {
someObj.delayedFetch();
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
})

vue jest spyOn not working when calculating watcher method calls

I'm getting familiar with jest and vue and I wanted to see how to make sure a method fired when a prop changed. In this particular scenario it's trivial and this seemed straight forward. But it's not working.
Components Watcher
#Watch("id")
public async idChanged() {
this.calculateStatus();
}
beforeEach - this initilizes the wrapper for each test
beforeEach(async () => {
var httpClient = new PemHttpClient(vue);
var v3ReferenceDatumService = new V3ReferenceDatumService(httpClient, "");
var contractService = new V3ContractService(httpClient, "", v3ReferenceDatumService);
wrapper = mount(AmendmentIdDisplay, {
provide: {
v3ContractService: contractService,
},
propsData: {
id: "82.5.1"
}
});
await wrapper.vm.$nextTick();
})
Jest Test
let calculateFired = jest.spyOn(wrapper.vm, "calculateStatus");
wrapper.setProps({
...wrapper.props(),
id: "1"
})
await wrapper.vm.$nextTick();
expect(calculateFired).toBeCalled();
I would expect the spy to have incremented the call counter but it does not. It remains at zero. If I manually call wrapper.vm.calculateStatus(), the spy works correctly. So the setProps is either not firing the watcher at all, or some weird reference thing is happening which is causing the method that is called within the watcher, to not be the method I'm spying on. I'm not sure which.
I hope it is not too late. Yes there is a problem with jest.spyOn() and vue watchers. I have a trick that patch the problem for now (tested on sync function only) :
const insertSpyWatcher = (vueInstance: any, watcherExpression: string, spyInstance: jest.SpyInstance) => {
let oldWatcherIndex = -1;
let deep = false; // pass the deep option value from the original watcher to the spyInstance
// find the corresponding watcher
vueInstance._watchers.forEach((watcher: any, index: number) => {
if (watcher.expression === watcherExpression) {
oldWatcherIndex = index;
deep = watcher.deep;
}
});
// remove the existing watcher
if (oldWatcherIndex >= 0) {
vueInstance._watchers.splice(oldWatcherIndex, 1);
} else {
throw new Error(`No watchers found with name ${watcherExpression}`);
}
// replace it with our watcher
const unwatch = vueInstance.$watch(watcherExpression, spyInstance, { deep });
return unwatch;
};
Then in your test :
it('test the watcher call', () => {
let calculateFired = jest.spyOn(wrapper.vm, "calculateStatus");
insertSpyWatcher(wrapper.vm, "id", calculateFired) // Yes 'id' is the name of the watched property
wrapper.setProps({
...wrapper.props(),
id: "1"
})
await wrapper.vm.$nextTick();
expect(calculateFired).toBeCalled();
});
If the immmediate property is needed, you can always add it as argument of insertSpyWatcher. I did not find a way to get the immediateproperty of the original watcher.

How Test with Jest a function in the method "mounted" VueJS

I would to try call a function already mocked. I use vueJS for the frond and Jest as unit test. Below a example of my code. My purpose is to test the call of « anotherFunction". The first test is succeed , not the second.Thanks for help or suggestion
code vueJS:
mounted() {
this.myfunction();
}
methods: {
myfunction() {
this.anotherFunction();
}
}
Jest code:
describe('Home.vue', () => {
let wrapper = null;
const options = {
mocks: {
$t: () => 'some specific text',
},
methods: {
myFunction: jest.fn(),
},
};
it('Should renders Home Component', () => {
// Given
wrapper = shallowMount(Home, options);
// Then
expect(wrapper).toBeTruthy();
});
it('Should call anotherFunction', async (done) => {
// Given
wrapper.vm.anotherFunction = jest.fn().mockResolvedValue([]);
// When
await wrapper.vm.myFunction();
// THIS THE PROBLEM, myFunction is mocked and I can't call the function 'anotherFunction' inside...
// Then
// expect(wrapper.vm.anotherFunction).toHaveBeenCalled();
});
});
I was finding a good way to help you if this test case. So, I thought in something like the chuck code below:
import { mount } from '#vue/test-utils';
describe('Home', () => {
it('method calls test case', () => {
const anotherMethodMock = jest.fn();
wrapper = mount(Home, {
methods: {
anotherMethod: anotherMethodMock
}
});
expect(anotherMethodMock).toHaveBeenCalled();
});
});
But, the Jest threw the following exception:
[vue-test-utils]: overwriting methods via the methods property is deprecated and will be removed in the next major version. There is no clear migration path for themethods property - Vue does not support arbitrarily replacement of methods, nor should VTU. To stub a complex m ethod extract it from the component and test it in isolation. Otherwise, the suggestion is to rethink those tests.
I had the following insight, maybe, in this case, should be better to test the side effect of this anotherMethod calling. What does it change? Is something being shown to the user?
I believe that here we have started from the wrong concept.
I hope that this tip could be useful :)
As suggested by #Vinícius Alonso, We should avoid using methods and setMethods in our test cases because of it's deprecation. But you can still test the mounted lifecycle by mocking the functions that are being called during mount. So you can do something similar to below snippet.
describe('Mounted Lifecycle', () => {
const mockMethodOne = jest.spyOn(MyComponent.methods, 'methodOne');
const mockMethodTwo = jest.spyOn(MyComponent.methods, 'methodTwo');
it('Validate data and function call during mount', () => {
const wrapper = shallowMount(MyComponent);
expect(mockMethodOne).toHaveBeenCalled();
expect(mockMethodTwo).toHaveBeenCalled();
})
})
Do mount/shallowMount inside it only rather putting it outside of it as it was not working in my case. You can checkout more details on it if you want.