Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/crm/crm_form_spec.js')
-rw-r--r--spec/frontend/crm/crm_form_spec.js339
1 files changed, 339 insertions, 0 deletions
diff --git a/spec/frontend/crm/crm_form_spec.js b/spec/frontend/crm/crm_form_spec.js
new file mode 100644
index 00000000000..eabcf5b1b1b
--- /dev/null
+++ b/spec/frontend/crm/crm_form_spec.js
@@ -0,0 +1,339 @@
+import { GlAlert, GlFormCheckbox, GlFormInput, GlFormSelect, GlFormGroup } from '@gitlab/ui';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import VueRouter from 'vue-router';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import CrmForm from '~/crm/components/crm_form.vue';
+import routes from '~/crm/contacts/routes';
+import createContactMutation from '~/crm/contacts/components/graphql/create_contact.mutation.graphql';
+import updateContactMutation from '~/crm/contacts/components/graphql/update_contact.mutation.graphql';
+import getGroupContactsQuery from '~/crm/contacts/components/graphql/get_group_contacts.query.graphql';
+import createOrganizationMutation from '~/crm/organizations/components/graphql/create_organization.mutation.graphql';
+import getGroupOrganizationsQuery from '~/crm/organizations/components/graphql/get_group_organizations.query.graphql';
+import {
+ createContactMutationErrorResponse,
+ createContactMutationResponse,
+ getGroupContactsQueryResponse,
+ updateContactMutationErrorResponse,
+ updateContactMutationResponse,
+ createOrganizationMutationErrorResponse,
+ createOrganizationMutationResponse,
+ getGroupOrganizationsQueryResponse,
+} from './mock_data';
+
+const FORM_CREATE_CONTACT = 'create contact';
+const FORM_UPDATE_CONTACT = 'update contact';
+const FORM_CREATE_ORG = 'create organization';
+
+describe('Reusable form component', () => {
+ Vue.use(VueApollo);
+ Vue.use(VueRouter);
+
+ const DEFAULT_RESPONSES = {
+ createContact: Promise.resolve(createContactMutationResponse),
+ updateContact: Promise.resolve(updateContactMutationResponse),
+ createOrg: Promise.resolve(createOrganizationMutationResponse),
+ };
+
+ let wrapper;
+ let handler;
+ let fakeApollo;
+ let router;
+
+ beforeEach(() => {
+ router = new VueRouter({
+ base: '',
+ mode: 'history',
+ routes,
+ });
+ router.push('/test');
+
+ handler = jest.fn().mockImplementation((key) => DEFAULT_RESPONSES[key]);
+
+ const hanlderWithKey = (key) => (...args) => handler(key, ...args);
+
+ fakeApollo = createMockApollo([
+ [createContactMutation, hanlderWithKey('createContact')],
+ [updateContactMutation, hanlderWithKey('updateContact')],
+ [createOrganizationMutation, hanlderWithKey('createOrg')],
+ ]);
+
+ fakeApollo.clients.defaultClient.cache.writeQuery({
+ query: getGroupContactsQuery,
+ variables: { groupFullPath: 'flightjs' },
+ data: getGroupContactsQueryResponse.data,
+ });
+
+ fakeApollo.clients.defaultClient.cache.writeQuery({
+ query: getGroupOrganizationsQuery,
+ variables: { groupFullPath: 'flightjs' },
+ data: getGroupOrganizationsQueryResponse.data,
+ });
+ });
+
+ const mockToastShow = jest.fn();
+
+ const findSaveButton = () => wrapper.findByTestId('save-button');
+ const findForm = () => wrapper.find('form');
+ const findError = () => wrapper.findComponent(GlAlert);
+ const findFormGroup = (at) => wrapper.findAllComponents(GlFormGroup).at(at);
+
+ const mountComponent = (propsData) => {
+ wrapper = shallowMountExtended(CrmForm, {
+ router,
+ apolloProvider: fakeApollo,
+ propsData: { drawerOpen: true, ...propsData },
+ mocks: {
+ $toast: {
+ show: mockToastShow,
+ },
+ },
+ });
+ };
+
+ const mountContact = ({ propsData, extraFields = [] } = {}) => {
+ mountComponent({
+ fields: [
+ { name: 'firstName', label: 'First name', required: true },
+ { name: 'lastName', label: 'Last name', required: true },
+ { name: 'email', label: 'Email', required: true },
+ { name: 'phone', label: 'Phone' },
+ { name: 'description', label: 'Description' },
+ {
+ name: 'organizationId',
+ label: 'Organization',
+ values: [
+ { key: 'gid://gitlab/CustomerRelations::Organization/1', value: 'GitLab' },
+ { key: 'gid://gitlab/CustomerRelations::Organization/2', value: 'ABC Corp' },
+ ],
+ },
+ ...extraFields,
+ ],
+ getQuery: {
+ query: getGroupContactsQuery,
+ variables: { groupFullPath: 'flightjs' },
+ },
+ getQueryNodePath: 'group.contacts',
+ ...propsData,
+ });
+ };
+
+ const mountContactCreate = () => {
+ const propsData = {
+ title: 'New contact',
+ successMessage: 'Contact has been added.',
+ buttonLabel: 'Create contact',
+ mutation: createContactMutation,
+ additionalCreateParams: { groupId: 'gid://gitlab/Group/26' },
+ };
+ mountContact({ propsData });
+ };
+
+ const mountContactUpdate = () => {
+ const propsData = {
+ title: 'Edit contact',
+ successMessage: 'Contact has been updated.',
+ mutation: updateContactMutation,
+ existingId: 'gid://gitlab/CustomerRelations::Contact/12',
+ };
+ const extraFields = [{ name: 'active', label: 'Active', required: true, bool: true }];
+ mountContact({ propsData, extraFields });
+ };
+
+ const mountOrganization = ({ propsData } = {}) => {
+ mountComponent({
+ fields: [
+ { name: 'name', label: 'Name', required: true },
+ { name: 'defaultRate', label: 'Default rate', input: { type: 'number', step: '0.01' } },
+ { name: 'description', label: 'Description' },
+ ],
+ getQuery: {
+ query: getGroupOrganizationsQuery,
+ variables: { groupFullPath: 'flightjs' },
+ },
+ getQueryNodePath: 'group.organizations',
+ ...propsData,
+ });
+ };
+
+ const mountOrganizationCreate = () => {
+ const propsData = {
+ title: 'New organization',
+ successMessage: 'Organization has been added.',
+ buttonLabel: 'Create organization',
+ mutation: createOrganizationMutation,
+ additionalCreateParams: { groupId: 'gid://gitlab/Group/26' },
+ };
+ mountOrganization({ propsData });
+ };
+
+ const forms = {
+ [FORM_CREATE_CONTACT]: {
+ mountFunction: mountContactCreate,
+ mutationErrorResponse: createContactMutationErrorResponse,
+ toastMessage: 'Contact has been added.',
+ },
+ [FORM_UPDATE_CONTACT]: {
+ mountFunction: mountContactUpdate,
+ mutationErrorResponse: updateContactMutationErrorResponse,
+ toastMessage: 'Contact has been updated.',
+ },
+ [FORM_CREATE_ORG]: {
+ mountFunction: mountOrganizationCreate,
+ mutationErrorResponse: createOrganizationMutationErrorResponse,
+ toastMessage: 'Organization has been added.',
+ },
+ };
+ const asTestParams = (...keys) => keys.map((name) => [name, forms[name]]);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe.each(asTestParams(FORM_CREATE_CONTACT, FORM_UPDATE_CONTACT))(
+ '%s form save button',
+ (name, { mountFunction }) => {
+ beforeEach(() => {
+ mountFunction();
+ });
+
+ it('should be disabled when required fields are empty', async () => {
+ wrapper.find('#firstName').vm.$emit('input', '');
+ await waitForPromises();
+
+ expect(findSaveButton().props('disabled')).toBe(true);
+ });
+
+ it('should not be disabled when required fields have values', async () => {
+ wrapper.find('#firstName').vm.$emit('input', 'A');
+ wrapper.find('#lastName').vm.$emit('input', 'B');
+ wrapper.find('#email').vm.$emit('input', 'C');
+ await waitForPromises();
+
+ expect(findSaveButton().props('disabled')).toBe(false);
+ });
+ },
+ );
+
+ describe.each(asTestParams(FORM_CREATE_ORG))('%s form save button', (name, { mountFunction }) => {
+ beforeEach(() => {
+ mountFunction();
+ });
+
+ it('should be disabled when required field is empty', async () => {
+ wrapper.find('#name').vm.$emit('input', '');
+ await waitForPromises();
+
+ expect(findSaveButton().props('disabled')).toBe(true);
+ });
+
+ it('should not be disabled when required field has a value', async () => {
+ wrapper.find('#name').vm.$emit('input', 'A');
+ await waitForPromises();
+
+ expect(findSaveButton().props('disabled')).toBe(false);
+ });
+ });
+
+ describe.each(asTestParams(FORM_CREATE_CONTACT, FORM_UPDATE_CONTACT, FORM_CREATE_ORG))(
+ 'when %s mutation is successful',
+ (name, { mountFunction, toastMessage }) => {
+ it('form should display correct toast message', async () => {
+ mountFunction();
+
+ findForm().trigger('submit');
+ await waitForPromises();
+
+ expect(mockToastShow).toHaveBeenCalledWith(toastMessage);
+ });
+ },
+ );
+
+ describe.each(asTestParams(FORM_CREATE_CONTACT, FORM_UPDATE_CONTACT, FORM_CREATE_ORG))(
+ 'when %s mutation fails',
+ (formName, { mutationErrorResponse, mountFunction }) => {
+ beforeEach(() => {
+ jest.spyOn(console, 'error').mockImplementation();
+ });
+
+ it('should show error on reject', async () => {
+ handler.mockRejectedValue('ERROR');
+
+ mountFunction();
+
+ findForm().trigger('submit');
+ await waitForPromises();
+
+ expect(findError().text()).toBe('Something went wrong. Please try again.');
+ });
+
+ it('should show error on error response', async () => {
+ handler.mockResolvedValue(mutationErrorResponse);
+
+ mountFunction();
+
+ findForm().trigger('submit');
+ await waitForPromises();
+
+ expect(findError().text()).toBe(`${formName} is invalid.`);
+ });
+ },
+ );
+
+ describe('edit form', () => {
+ beforeEach(() => {
+ mountContactUpdate();
+ });
+
+ it.each`
+ index | id | component | value
+ ${0} | ${'firstName'} | ${GlFormInput} | ${'Marty'}
+ ${1} | ${'lastName'} | ${GlFormInput} | ${'McFly'}
+ ${2} | ${'email'} | ${GlFormInput} | ${'example@gitlab.com'}
+ ${4} | ${'description'} | ${GlFormInput} | ${undefined}
+ ${3} | ${'phone'} | ${GlFormInput} | ${undefined}
+ ${5} | ${'organizationId'} | ${GlFormSelect} | ${'gid://gitlab/CustomerRelations::Organization/2'}
+ `(
+ 'should render the correct component for #$id with the value "$value"',
+ ({ index, id, component, value }) => {
+ const findFormElement = () => findFormGroup(index).findComponent(component);
+
+ expect(findFormElement().attributes('id')).toBe(id);
+ expect(findFormElement().attributes('value')).toBe(value);
+ },
+ );
+
+ it('should render a checked GlFormCheckbox for #active', () => {
+ const activeCheckboxIndex = 6;
+ const findFormElement = () =>
+ findFormGroup(activeCheckboxIndex).findComponent(GlFormCheckbox);
+
+ expect(findFormElement().attributes('id')).toBe('active');
+ expect(findFormElement().attributes('checked')).toBe('true');
+ });
+
+ it('should include updated values in update mutation', () => {
+ wrapper.find('#firstName').vm.$emit('input', 'Michael');
+ wrapper
+ .find('#organizationId')
+ .vm.$emit('input', 'gid://gitlab/CustomerRelations::Organization/1');
+
+ findForm().trigger('submit');
+
+ expect(handler).toHaveBeenCalledWith('updateContact', {
+ input: {
+ active: true,
+ description: null,
+ email: 'example@gitlab.com',
+ firstName: 'Michael',
+ id: 'gid://gitlab/CustomerRelations::Contact/12',
+ lastName: 'McFly',
+ organizationId: 'gid://gitlab/CustomerRelations::Organization/1',
+ phone: null,
+ },
+ });
+ });
+ });
+});