vue-form-wizard
vue-form-wizard copied to clipboard
Testing vue-form-wizard as component fails
Hello,
i try to test a custom component using jest which uses vue-form-wizard as a child component.
but my test fails with the error message
Prop startIndex set to 0 is greater than the number of tabs - 0. Make sure that the starting index is less than the number of tabs registered
the test looks like this:
describe('Step Stammdaten', () => {
it('shows all field errors on next click', () => {
const wrapper = mount(NewEmployeeForm)
wrapper.vm.$data.loading = false
wrapper.find('button').trigger('click')
expect(wrapper.findAll('#Stammdaten0 .form-input-error')).toHaveLength(6)
})
})
if i take a snapshot i can see that all id´s of the tab-panels are missing.
Any idea?
Hey @thepill
can you show the NewEmployeeForm
code ?
You could try waiting for nextTick
or
import flushPromises from 'flush-promises'
const wrapper = mount(NewEmployeeForm)
await flushPromises();
Hey @cristijora
thanks for your reply - i tried using flushPromises()
but it didn't work, but i might found my problem:
Im using https://github.com/baianat/vee-validate to validate my inputs.
If i remove the v-validate
tags from my inputs within the tab-content
it works!?!
Code (sorry that it is not as simple):
NewEmployeeForm:
<template>
<div
v-if="!loading"
class="wizard">
<form-wizard
:finish-button-text="submitButtonText"
title=""
subtitle=""
next-button-text="Weiter"
back-button-text="Zurück"
step-size="xs"
color="#2a887c"
@on-complete="sendForm">
<tab-content
:before-change="beforeChangeFromBasicDataStep"
title="Stammdaten">
<div class="form">
<div class="form-group-container">
<div class="form-group">
<input
v-validate="'required'"
id="gender-male"
v-model="form.gender"
type="radio"
name="gender"
value="Herr"
data-vv-scope="basics"
data-vv-as="Anrede">
<label for="gender-male">Herr</label>
</div>
</div>
</div>
</tab-content>
<tab-content
v-if="customer.businessCategories.exchange === true"
:before-change="beforeChangeFromEMailStep"
title="E-Mail">
</tab-content>
<tab-content
:before-change="beforeChangeFromPermissionsStep"
title="System">
</tab-content>
<tab-content
v-if="customer.businessCategories.datev === true"
title="DATEV">
</tab-content>
<tab-content
:before-change="beforeChangeFromAditionaltep"
title="Zusätzliche Informationen">
</tab-content>
<tab-content title="Abschluss">
<div>
<h3>Zusammenfassung</h3>
<p>Sie erhalten nach Abschluss ebenfalls eine Zusammenfassung per E-Mail.</p>
<br>
<form-summary :summary="formSummary"/>
</div>
</tab-content>
</form-wizard>
</div>
<div v-else>
<loading-spinner/>
</div>
</template>
<script>
import { mapState } from 'vuex'
import utils from 'utils'
import api from 'api'
import FormMixin from 'mixins/FormMixin'
import WarningElement from 'components/basic/WarningElement'
import DatetimePicker from 'components/basic/DatetimePicker'
import MailboxAccessPermissions from 'components/mailbox/MailboxAccessPermissions'
import EmployeeSharePermissions from 'components/employee/EmployeeSharePermissions'
import LoadingSpinner from 'components/basic/LoadingSpinner'
import FormSummary from 'components/basic/forms/FormSummary'
import FormRemarks from 'components/basic/forms/FormRemarks'
import ContactPersonForm from 'components/basic/forms/ContactPersonForm'
export default {
name: 'NewEmployeeForm',
components: {
WarningElement,
DatetimePicker,
LoadingSpinner,
MailboxAccessPermissions,
EmployeeSharePermissions,
FormSummary,
FormRemarks,
ContactPersonForm
},
mixins: [FormMixin],
data () {
return {
form: {
gender: '',
firstname: '',
lastname: '',
initials: '',
staffId: undefined,
phone: '',
mailbox: false,
email: undefined,
datevEmailEncryption: false,
vpn: 'Nein',
scanner: false,
increaseAllowedSimultaneousSessions: true,
contact: {
name: '',
phone: ''
},
remarks: {},
executionDate: undefined,
blockTenantsInDatev: false
},
selectedWindowsReferenceUser: undefined,
selectedDatevNukoReferenceUser: undefined,
selectedDatevDMSReferenceUser: undefined,
loading: true,
sharePermissions: [],
sharePermissionsLoading: false,
showSystemPermissions: false
}
},
computed: {
...mapState('employees', {
employees: state => state.employees
}),
minExecutionDate () {
return utils.getNextWorkingDate(5)
},
formSummary () {
let summary = [
{title: 'Stammdaten', isSectionHeader: true}
]
return summary
}
},
watch: {
'selectedWindowsReferenceUser': function (user) {
this.sharePermissionsLoading = true
this.$store.dispatch('employees/FETCH_SHARE_PERMISSIONS', user.guid)
.then(permissions => {
this.sharePermissions = permissions.filter(s => s.name.toUpperCase().indexOf('WINDVSW') === -1)
this.sharePermissionsLoading = false
})
}
},
async mounted () {
this.$store.dispatch('employees/FETCH_EMPLOYEES')
await this.$store.dispatch('customer/FETCH_CUSTOMER')
this.form.executionDate = this.minExecutionDate
this.loading = false
},
methods: {
beforeChangeFromBasicDataStep () {
this.form.email = utils.getEmailAdressFromPolicy(this.customer.primaryDomain, '%g.%s', this.form.firstname, this.form.lastname)
this.form.mailbox = this.customer.businessCategories.exchange
return this.$validator.validateAll('basics')
},
beforeChangeFromEMailStep () {
return this.$validator.validateAll('mail')
},
beforeChangeFromPermissionsStep () {
return this.$validator.validateAll('permissions')
},
beforeChangeFromAditionaltep () {
return new Promise((resolve, reject) => {
if (this.form.contact.name.length > 0 && this.form.executionDate !== undefined) {
resolve(true)
} else {
reject(new Error('Missing contact name or execution date'))
}
})
},
async sendForm () {
this.waitForSubmitResponse = true
let formData = this.form
formData.SystemReferenceUser = this.selectedWindowsReferenceUser.displayName
formData.DatevNukoReferenceUser = this.selectedDatevNukoReferenceUser !== undefined ? this.selectedDatevNukoReferenceUser.displayName : ''
formData.DatevDMSReferenceUser = this.selectedDatevDMSReferenceUser !== undefined ? this.selectedDatevDMSReferenceUser.displayName : ''
let response = await api.sendForm('employees/new', 'POST', formData)
const success = (response.status === 200)
this.$router.push({ name: 'FormResultView', query: { success } })
},
showEmployeesDisplayNameWithState (employee) {
return utils.showEmployeesDisplayNameWithState(employee)
}
}
}
</script>
<style scoped>
.reference-user {
margin-bottom: 40px;
}
</style>
Test
import { createLocalVue, mount } from '@vue/test-utils'
import Vuex from 'vuex'
import NewEmployeeForm from 'components/employee/NewEmployeeForm'
import VeeValidate, {Validator} from 'vee-validate'
import VeeValidateDE from 'vee-validate/dist/locale/de'
import VueFormWizard from 'vue-form-wizard'
import flushPromises from 'flush-promises'
const localVue = createLocalVue()
localVue.use(VeeValidate, {errorBagName: 'formErrors'})
Validator.localize('de', VeeValidateDE)
localVue.use(VueFormWizard)
localVue.use(Vuex)
describe('Component NewEmployeeForm', () => {
let store
beforeEach(() => {
const customer = {
namespaced: true,
state: {
cachetime: undefined,
customer: {
businessCategories: {}
}
},
actions: {
FETCH_CUSTOMER: jest.fn()
}
}
const user = {
namespaced: true,
state: {
currentUser: {
displayName: 'asdasd'
}
},
actions: {
FETCH_CURRENT_USER: jest.fn()
},
getters: {
currentUser: state => state.currentUser
}
}
const employees = {
namespaced: true,
state: {
employees: []
}
}
store = new Vuex.Store({
modules: {
customer,
user,
employees
}
})
})
it('test', async () => {
const wrapper = mount(NewEmployeeForm, {store, localVue, attachToDocument: true})
wrapper.vm.$data.loading = false
await flushPromises()
expect(wrapper).toMatchSnapshot()
})
})
Test Snapshot Output
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Component NewEmployeeForm sets the correct default data 1`] = `
<div class="wizard">
<div class="vue-form-wizard xs">
<div class="wizard-header">
<h4 class="wizard-title"></h4>
<p class="category"></p>
</div>
<div class="wizard-navigation">
<div class="wizard-progress-with-circle">
<div class="wizard-progress-bar" style="background-color: rgb(42, 136, 124); color: rgb(42, 136, 124);"></div>
</div>
<ul role="tablist" class="wizard-nav wizard-nav-pills"></ul>
<div class="wizard-tab-content">
<div role="tabpanel" id="" aria-hidden="true" aria-labelledby="step-" class="wizard-tab-container" style="display: none;">
<div class="form">
<div class="form-group-container">
<div class="form-group">
<input id="gender-male" type="radio" name="gender" value="Herr" data-vv-scope="basics" data-vv-as="Anrede" aria-required="true" aria-invalid="false">
<label for="gender-male">Herr</label>
</div>
</div>
</div>
</div>
<!---->
<div role="tabpanel" id="" aria-hidden="true" aria-labelledby="step-" class="wizard-tab-container" style="display: none;"></div>
<!---->
<div role="tabpanel" id="" aria-hidden="true" aria-labelledby="step-" class="wizard-tab-container" style="display: none;"></div>
<div role="tabpanel" id="" aria-hidden="true" aria-labelledby="step-" class="wizard-tab-container" style="display: none;">
<div>
<h3>Zusammenfassung</h3>
<p>Sie erhalten nach Abschluss ebenfalls eine Zusammenfassung per E-Mail.</p>
<br>
<div class="form-summary">
<table>
<tr>
<td class="form-summary-section top-section">Stammdaten</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="wizard-card-footer clearfix">
<div class="wizard-footer-left">
<!---->
</div>
<div class="wizard-footer-right"> <span role="button" tabindex="0"><button tabindex="-1" type="button" class="wizard-btn" style="background-color: rgb(42, 136, 124); border-color: #2a887c; color: white;">
Weiter
</button></span></div>
</div>
</div>
</div>
`;
@thepill
well i tried to use veevalidate with form-wizard but idon't know what's wrong .. how did you use it please ?
i have two forms to validate so i used scoops
<form-wizard title="" subtitle="" color="#48285f" @on-complete="onComplete">
{{ t('register_customer') }}
{{ t('register_provider') }}
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-6 col-md-offset-3 col-lg-6 col-lg-offset-3">
<div class="form-group">
<label class="user-type">
<i class="ci_user ci-service-provider"></i>
<span> {{ t('service_provider') }}</span>
<input type="radio" id="role_id" name="role_id" v-model="account_type" value="3" v-validate="'required|included:3,2'" /><span></span>
</label>
<label class="user-type">
<i class="ci_user ci-user"></i>
<span>{{ t('customer') }}</span>
<input type="radio" id="role_id" name="role_id" v-model="account_type" value="2" v-validate="'required|included:3,2'" checked/><span></span>
</label>
</div>
</div>
</div>
</tab-content>
<tab-content :title="t('account_data')" :before-change="()=>validateStep('step1')">
<form data-vv-scope="step1" @on-validate="mergePartialModels">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-6 col-md-offset-3 col-lg-6 col-lg-offset-3">
<div class="form-group">
<input id="name" type="text" class="form-control" v-model="name" name="name" :placeholder="t('fullname')" v-validate="{ required: true , min:8 ,max:30}" :class="{'is-danger': errors.has('name') }" required autofocus>
<span v-show="errors.has('name')" class="help is-danger">{{ errors.first('name') }}</span>
</div>
<div class="form-group">
<input id="email" type="email" class="form-control" v-model="email" name="email" :placeholder="t('email')" required>
</div>
<div class="form-group">
<input type="text" class="form-control" v-model="phone" name="phone" :placeholder="t('phone')" required>
</div>
<div class="form-group">
<input id="password" type="password" class="form-control" v-model="password" name="password" required :placeholder="t('password')">
</div>
<div class="form-group">
<input id="password-confirm" type="password" class="form-control" v-model="password_confirmation" name="password_confirmation" :placeholder="t('password_confirm')" required>
</div>
</div>
</div>
</form>
</tab-content>
<tab-content title="Last step">
Yuhuuu! This seems pretty damn simple
</tab-content>
any help please