1
votes

I have a LoginCard component that uses Buefy's b-input component to render the email and password inputs:

<b-field label="Email">
  <b-input
    v-model="email"
    type="email"
    placeholder="Your email"
    required
  >
  </b-input>
</b-field>

<b-field label="Password">
  <b-input
    v-model="password"
    type="password"
    password-reveal
    placeholder="Your password"
    required
  >
  </b-input>
</b-field>

And in my test I would like use something like vue-test-utils setValue method to set a value on the input, trigger that input, and have it propagate to my data model attributes for email and password.

wrapper.find('input[type="email"]').setValue('[email protected]');
wrapper.find('input[type="password"]').setValue('password');

But this doesn't work, the value doesn't get propagated.

1
It seems like your test is verifying that b-input propagates input values to the v-model. Assuming this is for a unit test, that verification seems out of scope. If you are only intended to setup email and password, you could initialize them directly (through wrapper.vm or mounting options' propsData). - tony19
My test is verifying that if a user enters their email and password into the form and submits the form, then the loginWith method is called with that data. This feels like a unit test because the inputs are the username and password. Currently I'm using setData to test it, but that doesn't feel right - it feels equivalent to manually setting an instance variable on a class instead of using the class's api. - Zachary Wright

1 Answers

2
votes

I discovered that you need to use the awaitPromises function to trigger the reactivity and cause the inputs to full propagate.

So for example my final test looks like this:

it('Logs in user via form', async () => {
  const wrapper = mount(LoginCard, {
    mocks: { $auth: mockAuth }
  });

  const inputs = wrapper.findAll({ name: 'BInput' });

  expect(inputs.length).toEqual(2);

  wrapper.find('input[type="email"]').setValue(email);
  await flushPromises();

  wrapper.find('input[type="password"]').setValue(password);
  await flushPromises();

  wrapper.find('form').trigger('submit.prevent');
  await flushPromises();

  expect(strategy).toBe('local');
  expect(data).toEqual({
    data: { user: { email: email, password: password } }
  });
});

Where mockAuth is mocking out Nuxt's global $auth:

const mockAuth = {
  loginWith: (_strategy, _data) => {
    return new Promise((resolve, reject) => {
      strategy = _strategy;
      data = _data;
      if (error) {
        reject(new Error(errorMessage));
      } else {
        resolve();
      }
    });
  }
};

It's more verbose than what I would prefer, but I like that it's using simple inputs to test a desired output.

You could also use wrapper.setData(...) to manually set the email and password before triggering the form submit, but I don't like this for a couple of reasons:

  1. You're essentially setting some private variables in order to test your code

  2. You could go in and delete the input elements and your components spec would still pass, even though in reality it's completely unusable.