Unit testing and simulating input
-
I am using vue-test-utils.
I have a form with a q-input of type textarea:https://github.com/maxant/kafka-data-consistency/blob/master/ui/claims.js#L83
<q-input id="claims-form-description" class="col-8" v-model="form.description" type="textarea" float-label="Description" rows="4" @blur="blurDescription" :error="$v.form.description.$error" />
I want to make my unit test enter data, if possible. I guess I could just set the value into the model.
Then I want to simulate a blur, so that the “blurDescription” function is called:methods: { blurDescription() { this.$v.form.description.$touch() },
I should then be able to check that $v is updated, so that I can test that the validation works. But I cannot get the blur to work. I’ve tried the following:
wrapper.find("#claims-form-description.q-input-area").trigger("blur") wrapper.find("#claims-form-description").trigger("blur")
Neither causes my blur method to be called. What is the correct way to do this?
Also, are there any utility functions provided by quasar which hide quasar internal stuff, so that I can e.g. programatically click on buttons or enter text into inputs?
-
Hi - are you using v1? and if so are you using the @quasar/testing app-extension?
-
nope, version 2 @nothingismagick
-
Version 2 isn’t out yet - so I guess you are from the future. With v1 I meant @quasar/cli v1.0.0-beta
-
haha @nothingismagick
I guess I meant 0.17.8
Not using cli.
I had a quick look at this yesterday: https://github.com/quasarframework/quasar-testing
But couldn’t see anything that would help me enter text into a q-input or bluring it. Basically I don’t want to have to know that a q-unit contains divs and text areas and stuff like that. I just want to find my element by ID, and be able to call methods on that object like “click()”, “setValue(…)”, “blur()”, etc. Is that possible or on a roadmap? -
@nothingismagick I noticed that I can use the “ref” attribute and then access that ref like this:
wrapper.find("#claims-form-description").vnode.componentInstance.$refs
And that ref has some interesting stuff on it, e.g. an input (I’m testing with a q-input), and that in turn has a “click” and “blur” method:
wrapper.find("#claims-form-description").vnode.componentInstance.$refs.input.click()
Or perhaps even better:
wrapper.vm.$refs.other.blur()
But they don’t seem to do anything. Should they do something? Is there any docs about this stuff?
-
I’ve made progress.
I had a number of problems:- using lazy-rules, which requires focus and blur to be programatically executed, which in turn doesnt work when the debug window is open
- setting a value in the data is not enough to get validation to be executed, you need to use a setTimeout(f, 0)
- vue-test-utils wrapper has a method called “setValue” - that doesnt work with q-input
I’ve got a working example. I’ll tidy it up and post it here tomorrow.
-
A further problem was that jsdelivr.net only has vue-test-utils version 1.0.0-beta.11, which has a date of 11th Jan 2018?!?!
NPM is already at version 1.0.0-beta.29. So I switched to unpkg.com:
<script src="https://unpkg.com/@vue/test-utils@1.0.0-beta.29/dist/vue-test-utils.umd.js"></script>
Now the wrapper which I get when I select the q-input finally has a
setValue
function, and things are working MUCH better.I still have to mess around with focusing the element, but that makes sense, because I’m using lazy rules.
wrapper.find(...).trigger("focus")
doesn’t seem to work on aq-input
but I can use native Javascript which works find. Maybe because I appended the wrapper.element to a div in my browser while running the tests. Note: I’m not following the docs here, and I’m running the tests in the browser using QUnit so that I can interact with the widgets if I need to, if a test isn’t working the way I expect it to.The focus and blur process required in the test is complicated but lazy-rules just works that way, so I have no other choice:
- rules are not validated until the field is focused
- rules are not validated the first time until the field loses focus
- rules are continuously validated after returning to the field
In order to get the validation to fire after blurring the first time I had to do a “nextTick”, i.e. await on a
setTimeout(..., 0)
. I guess quasar/vue are waiting for some messages in the queue to be processed by the event loop. See the link to my example code below for more details.Once you understand that and program the test to expect that behaviour, the test works quite well.
An example is here:
If you check that project out and serve https://github.com/maxant/kafka-data-consistency/blob/14f62badf13188ed153ae38de00aa9dc2042f27c/ui/tests.html using a simple web server like nodes http-server, you can see all the tests running in the browser, and you can see the components
So, summary:
wrapper.setValue(...)
from vue-test-utils works fine with aq-input
.- same for
q-btn
s - if you use lazy-rules then expect to have to focus and blur the
q-input
wrapper.find(...).trigger("blur")
or “focus” doesnt work => instead use native calls- that might only work if the component is appended and actually visible in the browser… not sure
- my original question was whether quasar provided any utility functions to help test widgets, but I don’t currently believe any extra stuff is required - vue-test-utils suffices
Thanks for the help!
Ant -
I have the same problem, but I am using Quasar v1.5 and Quasar testing tools.
So my problem is that I have a component with a q-input and I would like to change its value and see how that affects the rest of the component.
Simplifying a bit, I have a component with a q-input inside. It is filled in with a value that comes from the props of the containing component. Like:
<template> <div> <q-input v-model="value" @input="update()"></q-input> </div> </template> <script> export default { name: 'QINPUTDEMO, props: [ 'value' ], methods: { update () { this.$emit('input', this.value) } } } </script>
So you use it like here:
<template> <p>An example using the q-input demo</p> <q-input-demo v-model="txt"/> </template> <script> export default { data () { return { txt: 'test' } } } </script>
In my test, I would like to write into the q-input and verify that the text has changed correctly:
it('sets the translated texts', async () => { const qinput = wrapper.find(components.QInput) qinput.setValue('text 1') })
When I call setValue() the testing framework says: " [vue-test-utils]: wrapper.setValue() cannot be called on this element".
Any idea about how to fix it?
-
Hello, little update form my side:
I have found out how to set the new value inside inside the q-input: you need to find a regular input instead of a q-input instance:
it('sets a value correctly', async () => { const input = wrapper.find('input[type=text]') input.setValue('test 1') expect(wrapper.vm.value).toBe('test 1') }
This actually sets the text inside the q-input and also trigger the input event. Problems: if the q-input manages a prop directly (as in the example above), the test will pass, but Vue will complain with a:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
To avoid this, I have tried using data inside the q-input v-model instead of props directly:
<template> <div> <q-input v-model="text" @input="update()"></q-input> </div> </template> <script> export default { name: 'QINPUT', props: [ 'value' ], data () { return { text: this.value } }, methods: { update () { this.$emit('input', this.text) } } } </script>
Note that here v-model=“text” instead of value. The effect is that the warning disappears, but after calling setValue(), the change is not propagated to wrapper.vm.value, even if I use
await wrapper.vm.$nextTick()
and the test fails.So I haven’t found a way to properly test components that modify an input. This is a pity because most of my components are actually editors of some type of information. This guide here doesn’t help either.
-
@dariosalvi Have you found a solution to this? I’m getting started with quasar + jest and I am running into the same issue. I have a component which wraps a q-input. I would like to test that v-model on my custom component works correctly but when I change the value props there is no input event emitted. The component works as expected through manual testing so the problem must be with jest
-
Here’s what I’m doing, in essence, and it looks to work fine.
In the .vue file:
<q-input v-model="formDataEmail" name="email" ref="email" />
In the .spec.js file:
it("accepts email input", async () => { const email = "a1@b2.cd"; const emailInput = wrapper.find("input[name=email]"); await emailInput.setValue(email); expect(wrapper.vm.formDataEmail).toBe(email); });
So, the above works fine and we can at least use the
name
attribute to do the selection unambiguously.However, using findComponent() to get the input element does not work, although it’ll be nice if it does:
it("accepts email input", async () => { const email = "a1@b2.cd"; const emailInput = wrapper.findComponent({ ref: "email" }); await emailInput.setValue(email); expect(wrapper.vm.formData.email).toBe(email); }); //gives: [vue-test-utils]: wrapper.setValue() cannot be called on this element
-
@tedchwang
can you help me?
using this way mine insists on this error:
thanks…
-
I think you may need to put email in quotes:
"input[name='email']"
-
-
This is my code
-
Can you get a reference to the element running this in your console?
document.querySelector("input[name='email']")
-
@charlieknoll
So what is happening is that I can’t use the trigger, setValue on the Quasar components:Apparently it can’t run because it’s a Quasar component, if I put a <button> it works.