Testing Vuetify with Vue-test-utils - Cannot set value to v-text-field - vue.js

I want to test simple login form with two v-text-fields and one v-btn. I'm testing with Jest and Vue test utils.
Code looks like that:
<v-text-field
v-model="form.email"
test-id="input-email">
</v-text-field>
My test looks like that:
import Login from './login.vue'
import { shallowMount } from '#vue/test-utils'
describe('login', () => {
it('set value', () => {
const wrapper = shallowMount(Login)
const emailInput = wrapper.findComponent('[test-id="input-email"]')
emailInput.setValue('john#example.com')
})
})
And I'm getting error:
[vue-test-utils]: wrapper.setValue() cannot be called on this element
and points here:
emailInput.setValue('john#example.com')
How can I set value to v-text-field?

I tried to repeat your example and it works
Component:
<template>
<v-text-field
v-model="form.email"
test-id="input-email"
/>
</template>
<script>
export default {
data () {
return {
form: { email: '' },
}
},
}
</script>
Test:
import Input from './input.vue'
import { mount } from '#vue/test-utils'
describe('input', () => {
it('should set v-model', () => {
const wrapper = mount(Input)
const input = wrapper.findComponent('[test-id="input-email"]')
input.setValue('test')
})
})

Related

How can I test the focus of a text field in a Vue component?

I am new to Vue and I've been asked to implement tests for some components.
This is the component:
<template>
<v-form>
<v-container>
<v-text-field
v-on:keydown.enter.prevent
v-model="filterName"
dense
clearable
style="transition: all 0.3s ease; width: 12rem;"
class="mt-6"
#focus="isFocused = true"
#blur="isFocused = false"
ref='filter'
>
</v-text-field>
</v-container>
</v-form>
</template>
<script>
export default {
name: "FilterBox",
data: () => ({
isFocused: false,
}),
computed: {
filterName: {
get() {
return this.$store.getters.filter.name
},
set(value) {
this.$store.commit('set_filter_name', value)
}
}
},
mounted() {
this.$refs.filter.focus()
},
}
</script>
This is the test that I implemented for that component:
import Vue from "vue";
import Vuex from "vuex";
import Vuetify from "vuetify";
import { shallowMount } from "#vue/test-utils";
import FilterBox from "#/components/FilterBox";
Vue.use( Vuex );
Vue.use( Vuetify );
describe( 'Filter Box', () => {
let store;
let getters;
let vuetify;
beforeEach( () => {
vuetify = new Vuetify();
} );
it( 'should return a valid wrapper of the component', function () {
getters = {};
store = new Vuex.Store( { getters } );
const wrapper = shallowMount( FilterBox, {
store,
vuetify
} );
expect( wrapper.exists() ).toBe( true );
} );
} );
But when I run it, I keep getting this error:
Cannot read properties of undefined (reading 'focus')
TypeError: Cannot read properties of undefined (reading 'focus')
I tried this approach:
getters = {
filter: () => {
return jest.fn()
}
};
store = new Vuex.Store( { getters } );
But then I got this error:
this.$refs.filter.focus is not a function
TypeError: this.$refs.filter.focus is not a function
Can anyone help me figure out what I'm doing wrong here? How can I test that the text field is focused on mount hook?
Try using mount(...) instead of shallowMount(...) for the tests that are testing the focus.
In your case #focus and #blur are events provided by v-textfield and accessed via ref.
Using shallowMount you don't actually render v-textfield but a stub instead.
This is useful in tests where you don't want to test the implementation of your sub-componentsn

vue-test-utils: Unable to detect method call when using #click attribute on child component

Vue-test-utils is unable to detect method call when using #click attribute on child component, but is able to detect it when using the #click attribute on a native HTML-element, e.g. a button. Let me demonstrate:
This works:
// Test.vue
<template>
<form #submit.prevent>
<button name="button" type="button" #click="click">Test</button>
</form>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Test',
setup() {
const click = () => {
console.log('Click')
}
return { click }
}
})
</script>
// Test.spec.js
import { mount } from '#vue/test-utils'
import Test from './src/components/Test.vue'
describe('Test.vue', () => {
const wrapper = mount(Test)
if ('detects that method was called', () => {
const spy = spyOn(wrapper.vm, 'click')
wrapper.find('button').trigger('click')
expect(wrapper.vm.click).toHaveBeenCalled() // SUCCESS. Called once
})
})
This does NOT work:
// Test.vue
<template>
<form #submit.prevent>
<ButtonItem #click="click" />
</form>
</template>
<script>
import { defineComponent } from 'vue'
import ButtonItem from './src/components/ButtonItem.vue'
export default defineComponent({
name: 'Test',
components: { ButtonItem },
setup() {
const click = () => {
console.log('Click')
}
return { click }
}
})
</script>
// ButtonItem.vue
<template>
<button type="button">Click</button>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'ButtonItem',
})
</script>
// Test.spec.js
import { mount } from '#vue/test-utils'
import Test from './src/components/Test.vue'
import ButtonItem from './src/components/ButtonItem.vue'
describe('Test.vue', () => {
const wrapper = mount(Test)
if ('detects that method was called', () => {
const spy = spyOn(wrapper.vm, 'click')
wrapper.findComponent(ButtonItem).trigger('click')
expect(wrapper.vm.click).toHaveBeenCalled() // FAIL. Called 0 times
})
})
This issue stumbles me. I am not sure what I do wrong. I would be grateful if someone could describe the fault and show me the solution. Thanks!
I haven't run your code to test it yet, but why won't you use a more straightforward solution?
Look out for when the component emits click.
describe('Test.vue', () => {
const wrapper = mount(Test)
it('when clicked on ButtonItem it should be called one time', () => {
const button = wrapper.findComponent(ButtonItem)
button.trigger('click')
expect(wrapper.emitted().click).toBeTruthy()
expect(wrapper.emitted().click.length).toBe(1)
})
})

Cannot find data-testid attribute in Vue Component with Jest

I am trying to build a test that will target an element with the data-testid attribute. I have a BaseTile component that looks like this:
<template>
<div
data-testid="base-tile-icon"
v-if="!!this.$slots.icon"
>
<slot name="icon"></slot>
</div>
</template>
<script>
export default {};
</script>
<style></style>
And my test looks like this:
import { mount } from '#vue/test-utils';
import BaseTile from '#/components/BaseTile';
const factory = (slot = 'default') => {
return mount(BaseTile, {
slots: {
[slot]: '<div class="test-msg"></div>'
}
});
};
it('has an icon slot if an icon is provided', () => {
let wrapper = factory({ slot: 'icon' });
const input = wrapper.find('[data-testid="base-tile-icon"]');
expect(input.findAll('.test-msg').length).toBe(1);
});
How do I appropriately target the data-testid attribute with this test?
The named parameter of the factory is implemented incorrectly. The correct method is described in this post: Is there a way to provide named parameters in a function call in JavaScript?
The correct way to implement this is as follows:
import { mount } from '#vue/test-utils';
import BaseTile from '#/components/BaseTile';
const factory = ({ slot = 'default' } = {}) => {
return mount(BaseTile, {
slots: {
[slot]: '<div class="test-msg"></div>'
}
});
};
it('has an icon slot if an icon is provided', () => {
let wrapper = factory({ slot: 'icon' });
const input = wrapper.find('[data-testid="base-tile-icon"]');
expect(input.findAll('.test-msg').length).toBe(1);
});

Testing two different click events with vue-test-utils

Trying to test whether two methods are called on a click event on two different buttons. The result returns that the mock method on the second event is not called.
The template is the following (JobsSearch.vue):
<template>
<b-input-group class="sm-2 mb-2 mt-2">
<b-form-input
:value="this.searchConfig.Keyword"
#input="this.updateJobsSearchConfig"
class="mr-2 rounded-0"
placeholder="Enter Search term..."
/>
<b-button
#click="searchJobs"
class="rounded-0 search-button"
variant="primary"
>
Search
</b-button>
<b-button
#click="resetFilter"
class="rounded-0 ml-2 reset-button"
variant="primary"
>
Reset
</b-button>
</b-input-group>
</template>
This is my JobsSearch.spec.js
import { shallowMount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
// BootstrapVue is necessary to recognize the custom HTML elements
import BootstrapVue from 'bootstrap-vue'
import JobsSearch from '#/components/jobs/JobsSearch.vue'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(BootstrapVue)
describe('JobsSearch.vue', () => {
let state
let store
let wrapper
let searchJobs
let resetFilter
let emitEvents
beforeEach(() => {
state = {
jobs: {
paged: {
size: 100,
page: 1
},
search: {
Keyword: '',
status: [],
ucode: []
}
}
}
store = new Vuex.Store({
state
})
searchJobs = jest.fn()
resetFilter = jest.fn()
emitEvents = jest.fn()
wrapper = shallowMount(JobsSearch, {
methods: {
searchJobs,
resetFilter,
emitEvents
},
localVue,
store })
})
afterEach(() => {
wrapper.destroy()
})
// START: Method tests
it('should call jobsSearch method on search button click event', () => {
wrapper.find('.search-button').trigger('click')
expect(searchJobs).toHaveBeenCalled()
})
it('should call resetFilter method on reset button click event', () => {
wrapper.find('.reset-button').trigger('click')
expect(resetFilter).toHaveBeenCalled()
})
// END: Method tests
})
I expect both searchJobs and resetFilter to be called, however, only the first test passes
Any ideas, please?
It seems like that the resetFilter() method is not stubbed correctly.
Adapt the second test as follows:
it('should call resetFilter method on reset button click event', () => {
const resetFilterStub = jest.fn();
wrapper.setMethods({ resetFilter: resetFilterStub });
wrapper.find('.reset-button').trigger('click')
expect(resetFilter).toHaveBeenCalled()
})

Quasar Unknown custom element error in unit test

I have a simple Vue component that uses Quasar button
<template>
<div>
<span class="count">{{ count }}</span>
<q-btn #click="increment">Increment</q-btn>
</div>
</template>
<script>
export default {
name: 'TestComponent',
data() {
return {
count: 0,
};
},
methods: {
increment() {
this.count += 1;
},
},
};
</script>
I create a unit test for it
import { mount, createLocalVue } from '#vue/test-utils';
import { Quasar, QBtn } from 'quasar';
import TestComponent from '../TestComponent';
describe('TestComponent', () => {
let wrapper;
beforeEach(() => {
const localVue = createLocalVue();
localVue.use(Quasar, { components: { QBtn } });
wrapper = mount(TestComponent, { localVue });
});
it('renders the correct markup', () => {
expect(wrapper.html()).toContain('<span class="count">0</span>');
});
// it's also easy to check for the existence of elements
it('has a button', () => {
expect(wrapper.contains('button')).toBe(true);
});
});
My problem:
If I run the test cases (it function) one by one at a time the test will pass. For example, remove the second it('has a button'...) then run the test. It'll pass. It's the same when removing the first it('renders the correct markup'...)
However, If I keep all test cases then run the test. The second test case will fail with an error
console.error node_modules/vue/dist/vue.common.dev.js:630
[Vue warn]: Unknown custom element: <q-btn> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
found in
---> <TestComponent>
<Root>
What am I doing wrong?
Try removing the before-each. I saw this problem too. Can't remember what exactly fixed it but this is how I have my describe block.
describe('Mount Quasar', () => {
const localVue = createLocalVue()
localVue.use(Quasar, { components })
const wrapper = shallowMount(Register, {
localVue,
stubs: ['router-link', 'router-view']
})
const vm = wrapper.vm
it('passes the sanity check and creates a wrapper', () => {
expect(wrapper.isVueInstance()).toBe(true)
})
})
You will need to import quasar into either webpack, babel, or jest.
In the jest.config.js file
Add
moduleNameMapper: {
quasar: "quasar-framework/dist/umd/quasar.mat.umd.min.js"
},