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
- Consistency.
findBy*will always return a VTUWrapperandfindAllBy*will always return a VTUWrapperArray. - All VTU methods can be chained onto the end of
findBy*. e.g.wrapper.findByText('foo bar').exists() - Convince. These new queries are easier to use when you don't have to pass
wrapper.elementas the first parameter. - Encourages engineers to test what the user sees as apposed to how the component was implemented.
Disadvantages of new pattern
- 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.