New: Add `findBy*` and `findAllBy*` queries to `extendedWrapper` jest helper to improve consistency of DOM Testing Library usage

In #52 (closed) we added DOM Testing Library which made queryByText, queryByLabelText, etc available. These are really helpful but we don't have a consistent way to use them. Currently they are being used in a number of different ways:

ee/spec/frontend/admin/dev_ops_report/components/devops_adoption_app_spec.js#L357

expect(getByText(wrapper.element, text)).not.toBeNull();

ee/spec/frontend/members/components/ldap/ldap_override_confirmation_modal_spec.js#L58

const getByText = (text, options) =>
  createWrapper(within(findModal().element).getByText(text, options));

spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js#L33

const queryByText = (text, options) => within(wrapper.element).queryByText(text, options);

Sometimes we use createWrapper and sometimes we just make assertions off of the DOMNode. Sometimes we create a helper function and sometimes we use them directly in the expect statement.

New Pattern Proposal

In #78 (closed) we added an extendedWrapper Jest helper to add findByTestId.

The proposal is to add the other available Testing Library queries to the extendedWrapper helper so that we are using these queries more consistently throughout the codebase.

Example

sign_up_form.vue
<script>
import { GlFormGroup, GlFormInput, GlButton, GlForm } from '@gitlab/ui';

export default {
  name: 'SignUpForm',
  components: { GlFormGroup, GlFormInput, GlButton, GlForm },
  data() {
    return {
      showSuccessMessage: false,
    };
  },
  methods: {
    handleSubmit() {
      this.showSuccessMessage = true;
    },
  },
};
</script>

<template>
  <div>
    <div v-if="showSuccessMessage">
      <h4>{{ __('You have successfully signed up!') }}</h4>
    </div>
    <gl-form v-else @submit.prevent="handleSubmit">
      <gl-form-group id="name-fieldset" label-for="name" :label="__('Name')">
        <gl-form-input id="name" name="name" type="text" required />
      </gl-form-group>

      <gl-form-group id="email-fieldset" label-for="email" :label="__('Email')">
        <gl-form-input id="email" name="email" type="email" required />
      </gl-form-group>

      <gl-button type="submit" category="primary" variant="confirm">{{ __('Sign up') }}</gl-button>
    </gl-form>
  </div>
</template>
sign_up_form_spec.js
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import SignUpForm from '~/members/components/sign_up_form.vue';

describe('SignUpForm', () => {
  let wrapper;
  const createComponent = () => {
    wrapper = extendedWrapper(
      mount(SignUpForm, {
        // Attach to document so button with `type="submit"` actually submits the form
        attachTo: document.body,
      }),
    );
  };

  describe('when form is submitted', () => {
    afterEach(() => {
      wrapper.destroy();
    });

    it('displays success message', async () => {
      createComponent();

      await wrapper.findByLabelText('Name').setValue('Peter');
      await wrapper.findByLabelText('Email').setValue('phegman@gitlab.com');

      await wrapper.findByText('Sign up').trigger('click');

      expect(wrapper.findByText('You have successfully signed up!').exists()).toBe(true);
    });
  });
});

Advantages of new pattern

  1. Consistency. findBy* will always return a VTU Wrapper and findAllBy* will always return a VTU WrapperArray.
  2. All VTU methods can be chained onto the end of findBy*. e.g. wrapper.findByText('foo bar').exists()
  3. Convince. These new queries are easier to use when you don't have to pass wrapper.element as the first parameter.
  4. Encourages engineers to test what the user sees as apposed to how the component was implemented.

Disadvantages of new pattern

  1. Hacking the VTU prototype is not ideal. But... VTU2 will have a plugin system that this can be migrated to when we upgrade.

What is the impact on our existing codebase?

Update docs (see reference implementation) to document extendedWrapper and these new queries. No code changes required. Engineers can decide if they want to use the extendedWrapper helper and the available findBy* and findAllBy* queries.

Reference implementation

Adds findBy* and findAllBy* to extendWrapper Jest helper. These methods always return a VTU Wrapper or WrapperArray so all VTU methods are available.

Edited by Peter Hegman