14

I'm writing unit tests for vuelidate validation in my component. I figured out that the $touch() method is called asynchronously, so I need to use $nextTick() for the expect(). The problem appears when I need two nextTick()s for two expect()s.

describe('Validations', () => {
    let data
    let myComponent
    beforeEach(() => {
        data = () => {
            propertyABC = 'not allowed value'
        }
        myComponent = localVue.component('dummy', {template: '<div></div>', validations, data})

    it('Properly validates propertyABC', (done) => {
        Vue.config.errorHandler = done
        let wrapper = mount(myComponent, {localVue})
        wrapper.vm.$v.$touch()

        wrapper.vm.$nextTick(() => {
            expect(wrapper.vm.$v.propertyABC.$error).to.be.true
            # fails, because propertyABC === 'allowed value', adn thus $error is false
            done()
        }

        wrapper.vm.propertyABC = 'allowed value'
        wrapper.vm.$v.propertyABC.$touch()

        wrapper.vm.$nextTick(() => {
            expect(wrapper.vm.$v.proprtyABC.$error).to.be.false
            done()
        }
    })
})

How can I run this test without splitting it into two separate tests? I think nesting the $nextTick() might work, but it would be not flexible for higher amount of tests.

Fidd
  • 722
  • 1
  • 8
  • 20
  • [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)? – Roy J Feb 12 '18 at 17:15

2 Answers2

24

If you're able to use async functions, then you could await the $nextTick calls. This would avoid having to nest them and would keep everything in the same test.

Like so:

describe('Validations', () => {
  let data;
  let myComponent;
  beforeEach(() => {
    data = () => ({ propertyABC = 'not allowed value' });
    myComponent = localVue.component('dummy', {template: '<div></div>', validations, data});
  });

  it('Properly validates propertyABC', async  () => {
    let wrapper = mount(myComponent, {localVue});
    wrapper.vm.$v.$touch();

    await wrapper.vm.$nextTick();

    expect(wrapper.vm.$v.propertyABC.$error).to.be.true;

    wrapper.vm.propertyABC = 'allowed value';
    wrapper.vm.$v.propertyABC.$touch();

    await wrapper.vm.$nextTick();

    expect(wrapper.vm.$v.proprtyABC.$error).to.be.false;
  })
})
Fidd
  • 722
  • 1
  • 8
  • 20
thanksd
  • 54,176
  • 22
  • 157
  • 150
5

Another approach is to use flushPromises.

import flushPromises from 'flush-promises';
...

test('some async test', async () => {
  const wrapper = mount(MyComponent, { localVue });
  wrapper.vm.$v.$touch();
  await flushPromises();
});

flushPromises() returns a promise itself, so its useful when you need/want to chain things using .then().then() etc...

Christian C
  • 167
  • 1
  • 6
steven87vt
  • 500
  • 7
  • 6