Vue2 Jest Testing Component with vue-select - vuejs2

I have a component which uses the vue-select dependency and I wanted to write a unit test for this component which passes in an object and when focused on that input, checks to see if the wrapper contains the text of at least one of the objects passed in.
It looks to me like the DOM isn't being updated, since after finding 'input' and triggering a click, console logging the wrapper html shows no sign of the list items. Therefore the text doesn't exist. vue-select only renders the list items on the DOM once the user clicks/focuses on the input.
The test (tried with both nextTick & forceUpdate):
import { mount } from '#vue/test-utils';
import BlacklistSelector from '../../resources/js/components/BlacklistSelector.vue';
describe('Blacklist selector component', () => {
it('displays a list of blacklist lists', async () => {
const wrapper = await mount(BlacklistSelector);
await wrapper.setProps({
options: {
1: {description: 'test desc', name: 'test name', words: ['test', 'words']},
2: {description: 'test desc two', name: 'test name two', words: ['test', 'words', 'exists']}
}
});
wrapper.find('input').trigger('click');
//await wrapper.vm.$nextTick();
//await wrapper.vm.$forceUpdate();
expect(wrapper.html()).toContain('test name');
})
})
The 'input' is that of vue-select. Confirmed by logging out during the test.
I am using Vue 2 & #vue/vue2-jest": "^28.0.1".
Any help would be greatly appreciated.

Related

Vue Testing Library won't rerender DOM when state changes

I have a component with a strait forward Edit button. The Edit button calls a method that sets isEditing to true.
There are a few input elements with v-if="isEditing", so I'm testing that those input elements are visible after the Edit button is clicked.
When my test runs fireEvent.click(screen.getByRole('link', {name: 'Edit'})), it is updating isEditing to true (based on my console.log messages before/after the .click event), but it doesn't seem to re-render the components within the test (based on the DOM rendered in my terminal after getByRole fails).
It works as expected in the browser, but doesn't seem to update the DOM for the spec. I'm using Vue2, Vue Testing Library, and Jest.
Implementation:
<template>
<a #click.prevent="startEdit" v-if="!isEditing">Edit</a>
<input :v-if="isEditing" />
</template>
...
methods: {
startEdit: () => {
this.isEditing = true
}
}
Spec:
describe('FormComponent', () => {
beforeEach(() => {
render(FormComponent)
})
it('displays input tags' () => {
fireEvent.click(screen.getByRole('link', {name: 'Edit'}))
expect(screen.getByRole('input')).toBeInTheDocument()
})
})
The problem is your expect is running before the DOM has had a chance to update. From the testing library documentation:
Because Vue applies DOM updates asynchronously during re-renders, the fireEvent tools are re-exported as async functions. To ensure that the DOM is properly updated in response to an event in a test, it's recommended to always await fireEvent.
You should update your test to await the fireEvent promise like so:
it('displays input tags' async () => {
await fireEvent.click(screen.getByRole('link', {name: 'Edit'}))
expect(screen.getByRole('input')).toBeInTheDocument()
})
You also have a typo in your second v-if as Nicole pointed out in her answer.
It doesn't work because you wrote :v-if when it should be v-if. I guess this was simply a typo since you did it correctly the first time (v-if="!isEditing")

Show toast when the url changes in vuejs

I want to show a simple message when a user is logged in I am trying to use this method:
this.$router.push('/dashboard', () => {
console.log('hiiiii')
this.$bvToast.toast('Welcome!', {
title: 'Message',
solid: true,
variant: 'success'
})
})
I can see console.log('hiiiii') is executed but I dont see the toast showing in the screen, I thought that onComplete shall execute after the url is changed or I am missing something ?
I am using nuxtjs v.2.15.7 with vue-router v.3.5.1

How to test component view in React-Admin using <TestContext>?

I am wondering how to use <TestContext> as a sufficient tool for testing react-admin custom components view. So far we have not encountered an error rendering basic HTML element inside <TestContext>, but with RA component test are failing mostly due to:
TypeError: Cannot create proxy with a non-object as target or handler
In our case, we do not have a redux store connected to our component and for now, just want to test the display with simple props using an example from documentation.
describe('<EditManufacturer/>', () => {
let testUtils: any;
beforeEach(() => {
const mockEditManufacturerProps = {
basePath: '/',
id: '123',
resource: 'foo',
};
testUtils = render(
<TestContext>
<EditManufacturer {...mockEditManufacturerProps}/>
</TestContext>)
});
// test
})
});
Example syntax above produces another error:
Cannot read property '{"type":"GET_ONE","resource":"foo","payload":{"id":"123"}}' of undefined
Here is our EditManufacturer.tsx component:
const EditManufacturer:React.FC<EditProps> = props => {
return(
<Edit {...props}>
<SimpleForm>
<ManufacturerInputFields/>
</SimpleForm>
</Edit>
)};
export default EditManufacturer;
Is injecting all the required props for HOCs and then mounting the view as mentioned in answer here or using e2e tests the only solution? Any help would be appreciated.

Handle Vue render errors locally

I am using Vue (server side rendered) with mjml to generate emails.
So I have something (overly simplified) like:
<mjml><mj-body>Hello {{ User.Name }}</mj-body></mjml>
If the model doesn't define User then Vue throws an error and the whole output is lost.
What I want to the output to be along the lines:
<mjml><mj-body>Hello <error>'User' is undefined</error></mj-body></mjml>
I have implemented Vue.config.errorHandler but that just tells me about the error -- there is no rendered output.
Anyway to implement the equivalent of an error handler around each variable substitution?
If you are using Vue version >= 2.5, you can use errorCaptured to create an ErrorBoundary
const ErrorBoundary = {
name: 'ErrorBoundary',
data: () => ({
error: false,
msg: ''
}),
errorCaptured (err, vm, info) {
this.error = true
this.msg = `${err.stack}\n\nfound in ${info} of component`
},
render (h) {
return this.error
? h('pre', this.msg)
: this.$slots.default[0]
}
}
and use this in your component
<error-boundary>
<mjml><mj-body>Hello {{ User.Name }}</mj-body></mjml>
</error-boundary>
If the application has any javascript error, it will be displayed on UI
Example on codepen
If you want to have more user-friendly error, you can customize ErrorBoundary to have fallback to another component. You can find out in this tutorial
Another good example of using errorHandler

Why does an expectation in $nextTick never fail?

I need to use $nextTick in order to test some parts of my program. Somehow, it breaks my tests and make them success all the time - even when they should fail.
A minimal test sample would look like this:
import App from "./App";
import { shallowMount } from "#vue/test-utils";
it("should fail", () => {
const wrapper = shallowMount(App);
wrapper.vm.$nextTick(() => {
expect(1).toBe(3);
done();
});
});
You can find a sandbox example here
If you open the console, you should find the following error messages:
[Vue warn]: Error in nextTick: "Error: expect(received).toBe(expected)
Error: expect(received).toBe(expected)
Why does the test success? Why are the errors ignored? How do I use $nextTick properly if note like so?
In order to wait until Vue.js has finished updating the DOM after a
data change, you can use Vue.nextTick(callback) immediately after the
data is changed. The callback will be called after the DOM has been
updated.
I can not see any trigger that change DOM in your test. And you missed done argument in test callback
For example in the following this is wrapper.find('button').trigger('click')
it('fetches async when a button is clicked', (done) => {
const wrapper = shallowMount(Foo)
wrapper.find('button').trigger('click')
wrapper.vm.$nextTick(() => {
expect(wrapper.vm.value).toBe('value')
done()
})
})