-
Notifications
You must be signed in to change notification settings - Fork 668
Can't trigger input event #266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Are you trying to trigger a mock action, or just the function inside of the Inside of methods: {
resizeThumbs: function (event) {
this.$store.dispatch('resizeThumbs')
this.thumbPixelWidth = event.target.value
}
} Also in the component you do not need to pass More info can be found in the docs:: https://vue-test-utils.vuejs.org/en/guides/using-with-vuex.html This does not appear to be a problem with https://eddyerburgh.me/ |
Hi @sdellis. This is a problem with your code, rather than vue-test-utils. The input even is getting triggered, but the action isn't called because you haven't added any code to dispatch the action. This issue tracker is reserved for bug fixes and feature requests. For questions like these, please ask on StackOverflow or the Vue discord channel. @lmiller1990 FYI, it's Edd, not Eddy 😉 |
I apologize for erroneously filing this as a bug report. I'll be more careful next time. @lmiller1990 I was trying to trigger the function within the |
If you think this is a bug, could you please create a minimal reproduction that demonstrates the bug? If it's not a bug I'll leave this closed.Thanks 🙂 |
@eddyerburgh If I'm passing the event properly, I think this may be a bug. Here's a minimal reproduction with all the unnecessary vuex stuff taken out.
My test:
I get the following error when running the test:
According to the docs I think I'm passing |
trigger creates a new DOM Event, and tries to overwrite the Event properties with the properties of options. Unfortunately, target is a read only property, which leads to the confusing error. To trigger an input with the value of 100, you need to set the value of the element you're triggering the event on to 100: const input = wrapper.find('#resize_thumbs_input')
input.element.value = 100
input.trigger('input', {target: {value: 100}}) I'm going to add a note to the docs that target is unwritable, and throw a custom error inside trigger, so that users get an improved error message. Let me know if setting the value before triggering the event solves your problem. |
@eddyerburgh Thank you for your help, for improving the error message, and adding this to the docs! One small note with your example above is that I needed to remove |
I have a related issue to @sdellis, except that using a native html 5 input tag, I'm using a |
Can you provide a link to the component library and snippet? Most likely the component is built (by the library author) like so <b-form-input>
<div class="bootstrap_styling_etc">
<input value="aaa">
</div>
</b-form-input> When you call |
@lmiller1990 In the case of Bootstrap Vue, b-form-input seems to hide the actual input field within the component, so what you end up with is:
You can read more about it here. In my test, when I create my wrapper object of the component I am trying to test:
I then get the input field I am trying to manipulate:
Which, as far as I can tell, successfully gets that One thing that I do find very strange is the inconsistency of If any of this information triggers a thought as to what the problem might be, I'd really appreciate the help. Thanks in advance! |
I dug into the bootstrap-vue project on Github and found the tests for the
In this test, the expectation is on what value was emitted by the input field when an |
Might as well provide the source code that I am working with to help provide full context: The component:
The unit test:
|
Hi @mjvezzani There is a bit of code missing (like what kind of props are received, and what Simplified component: <template>
<div>
<div class="col-lg-12" id="contract-id-input">
<b-form-input
id="b-form-input"
v-model="productData.contractId"
maxlength="7"
:state="productValidator"
>
</b-form-input>
</div>
</div>
</template>
<script>
export default {
name: 'EnercareLineItem',
data() {
return {
productData: { contractId: '' }
}
},
computed: {
productValidator() {
switch (this.productData.contractId.length) {
case 0:
return 'reset'
case 8:
return 'valid'
default:
return 'invalid'
}
}
}
}
</script> Passing Tests: import BootstrapVue from 'bootstrap-vue'
import { shallowMount, mount, createLocalVue } from '@vue/test-utils'
import EnercareLineItem from '@/components/HelloWorld'
const Vue = createLocalVue()
Vue.use(BootstrapVue)
describe('EnercareLineItem', () => {
describe('Contract Id input', () => {
it('is invalid at a length of 7', () => {
const wrapper = shallowMount(EnercareLineItem, {
localVue: Vue,
data() {
return {
productData: { contractId: "1234567" }
}
}
})
const contractIdInput = wrapper.find('#b-form-input')
expect(contractIdInput.attributes().state).toBe('invalid')
})
it('is valid at a length of 8', () => {
const wrapper = shallowMount(EnercareLineItem, {
localVue: Vue,
data() {
return {
productData: { contractId: '12345678' }
}
}
})
const contractIdInput = wrapper.find('#b-form-input')
expect(contractIdInput.attributes().state).toBe('valid')
})
it('changes state', () => {
const wrapper = shallowMount(EnercareLineItem, {
localVue: Vue,
data() {
return {
productData: { contractId: '1234567' }
}
}
})
const contractIdInput = wrapper.find('#b-form-input')
expect(contractIdInput.attributes().state).toBe('invalid')
wrapper.setData({ productData: { contractId: '12345678' } })
expect(contractIdInput.attributes().state).toBe('valid')
})
})
}) I am not sure why your test was not working, I could not get it running locally without the rest of your project. I am not sure if this is exactly what you are looking for, but it shows how you can check the Hopefully this example might give you some ideas? One unfortunate thing is
PS: don't forget to pass |
Okay, so I wired up my test to work in a similar way that you suggested, @lmiller1990, and I have passing tests. So now I have a question about unit testing philosophy. My understanding is that one should unit test public facing inputs and public facing outputs. I was under the impression that setting up a test in which contractId is not initially populated, and then mimicking user input into the The examples that you provide me exercise an assertion on a wrapper component that already has the internal state set. In other words, in the Assemble, Act, Assert model of unit testing, the example tests Assemble and then Assert, bypassing the need to Act on anything. I'd be curious to hear more about why Assemble and Assert is preferable to Assemble, Act, and Assert. Thanks! |
@mjvezzani great questions and observations. I like these kind of discussions in issues, as opposed to simply "how do I do X", we should be asking "how should I do X"? I don't think there is any one "correct" way to test. What I've been doing lately is identifying all the states a component can be in (for example a form can be valid and invalid). Then I set up the component (assemble), act if necessary (click submit, for example) and assert (the form was not submitted). In the example I provided, I used Either way I would still write an e2e test using something like cypress.io, and fully exercise that entire page to have full confidence. But this is something Vue test utils should (can?) do. @eddyerburgh I'm interested in your opinion about this topic, and is there even a way to set a |
@eddyerburgh I would also be curious to hear your thoughts on this. As an aside to all the previous comments, I purchased the Testing Vue.js Applications (V6) recently, and attempted to use the code in chapter 5 that demonstrates how to test a component when a user inputs data into an input field.
Doesn't seem to work for me. Currently the DOM element that I have my |
I think that should be |
Ya. That is how I originally had my code, and it wasn't working, so I attempted to use As another aside, I've been a long time proponent of TDD coming from a background of server side programming languages, but my experience with TDD as it relates to JavaScript frameworks has been less than mediocre. Blog articles that I read (and books for that matter) do a good job of laying out how to TDD an application that is fairly simple, but thus far I've only worked on (perhaps needlessly) complicated projects. In the current project I am working on, we have built a custom form builder, which has required that we create several base Vue components that get extended by several other components, and our Vuex store has a fair amount of complexity in it as well. I'm finding it more and more difficult to find answers on how to wrap these components with unit tests because I'm finding it impossible to get the component under test to @lmiller1990 @eddyerburgh any encouraging words for me to maintain my belief/desire to continue practicing TDD? |
@mjvezzani I hear you loud and clear. I come from Ruby on Rails, where testing is built in and works great out of the box (mainly from many years of hard work by the community. I'm sure it was not good back in the early days, either). This is my opinion from the last year or two of developing with Vue - take it was a grain of salt, everyone has their own style and preferences. Regarding Vuex:My experience regarding Vuex has been to test the two "sides" of it, if you will.
Of course this means that you could break a getter, and the Vue component that relies on that getter will not fail. That's unit tests for you, they only test things in isolation. Not to plug my own guide, but I wrote about it in detail here (with the help of 15 contributors). For the case of a complex form using a Vuex store and several components that compose together, I think you should test each part individually (store, mutations, validations). As for making sure it works together, it should be tested using an e2e test, using something like Selenium (meh, but what I use at work) or cypress.io (awesome). About
|
@lmiller1990 Thanks for sharing it, but I still have some questions about testing with shallowMount only, maybe you can clarify it for me. How to use I'd be happy to work with component-wrapper itself instead of input. If I use shallowMount, I still can find import InputWrapper from '@/some/path/to/InputWrapper.vue'
const inputWrapper = wrapper.find(InputWrapper) But I can't neither trigger input on that component nor call // doesn't work (also type of second arg `object` according to typings I have now)
input.trigger('input', { some: 'more complex value than just string' } as any)
// produces error: wrapper.setValue() cannot be called on this element
input.setValue('some value') upd: this how it works for me now: const input = wrapper.find(InputWrapper)
input.vm.$emit('input', 'some value') |
I think I'm passing through a similar issue by now. In my case I'm using Choices as a third party component and Jest as test runner. Here is a JSFiddle as an example. Here is my test: import CustomSelect from '@/CustomSelect';
import { mount } from '@vue/test-utils';
let wrapper,
options = [
{
key: 'Foo',
value: 'foo',
},
{
key: 'Bar',
value: 'bar',
},
{
key: 'Baz',
value: 'baz',
},
];
beforeEach(() => {
wrapper = mount(CustomSelect, {
propsData: {
options,
},
});
});
it('Emits an `input` event when selection changes', () => {
wrapper.vm.choicesInstance.setChoiceByValue([options[1].value]);
expect(wrapper.emitted().input).not.toBeFalsy();
}); This only test case above does not work.. For some reason, when I change the value through the |
@alendorff This test for vue-multiselect is not working =(
|
I am having more or less the same problem that @lmiller1990 solved, I post the link of my question about stack overflow so I avoid rewriting a lot here: Thanks in advance for your answers. |
Can you share more code? Where is |
checkSubmitStatus is written what it does, it calls updateSubmitDisabled (which is itself in the mutations) to check if the vmodel is greater than 0. |
I will reply in SO |
I finally settled with: it("should call the updateSubmitDisabled mutation", () => { |
Currently have similar issue with the input events been not triggering events, have tried both 'trigger' function and also the dispatchEvent doesn't really work. GitHUB reference component [Error log for discussion ]
|
Please let me know how to properly dispatchEvent as its not working the relationship The input event when triggered on instance of the (Child) instance isn't responding with ('update:value') |
So many things are confusing here.
If you would like to update an input with |
Thanks, for a quick response.. I will try your suggestion of |
I'm able to trigger a click event with vue-test-utils, but can't seem to trigger an input event.
Here's my component:
Here's my test:
The test fails with the following output:
The text was updated successfully, but these errors were encountered: