2022-05-11 12:09:03 +00:00
|
|
|
import { GlAlert, GlFormInput, GlFormSelect, GlFormGroup } from '@gitlab/ui';
|
2022-01-06 18:13:45 +00:00
|
|
|
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 Form from '~/crm/components/form.vue';
|
2022-03-31 21:08:16 +00:00
|
|
|
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';
|
2022-01-06 18:13:45 +00:00
|
|
|
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 mountComponent = (propsData) => {
|
|
|
|
wrapper = shallowMountExtended(Form, {
|
|
|
|
router,
|
|
|
|
apolloProvider: fakeApollo,
|
|
|
|
propsData: { drawerOpen: true, ...propsData },
|
|
|
|
mocks: {
|
|
|
|
$toast: {
|
|
|
|
show: mockToastShow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const mountContact = ({ propsData } = {}) => {
|
|
|
|
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' },
|
2022-05-11 12:09:03 +00:00
|
|
|
{
|
|
|
|
name: 'organizationId',
|
|
|
|
label: 'Organization',
|
|
|
|
values: [
|
|
|
|
{ key: 'gid://gitlab/CustomerRelations::Organization/1', value: 'GitLab' },
|
|
|
|
{ key: 'gid://gitlab/CustomerRelations::Organization/2', value: 'ABC Corp' },
|
|
|
|
],
|
|
|
|
},
|
2022-01-06 18:13:45 +00:00
|
|
|
],
|
2022-03-31 21:08:16 +00:00
|
|
|
getQuery: {
|
|
|
|
query: getGroupContactsQuery,
|
|
|
|
variables: { groupFullPath: 'flightjs' },
|
|
|
|
},
|
|
|
|
getQueryNodePath: 'group.contacts',
|
2022-01-06 18:13:45 +00:00
|
|
|
...propsData,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const mountContactCreate = () => {
|
|
|
|
const propsData = {
|
|
|
|
title: 'New contact',
|
2022-03-31 21:08:16 +00:00
|
|
|
successMessage: 'Contact has been added.',
|
2022-01-06 18:13:45 +00:00
|
|
|
buttonLabel: 'Create contact',
|
|
|
|
mutation: createContactMutation,
|
|
|
|
additionalCreateParams: { groupId: 'gid://gitlab/Group/26' },
|
|
|
|
};
|
|
|
|
mountContact({ propsData });
|
|
|
|
};
|
|
|
|
|
|
|
|
const mountContactUpdate = () => {
|
|
|
|
const propsData = {
|
|
|
|
title: 'Edit contact',
|
2022-03-31 21:08:16 +00:00
|
|
|
successMessage: 'Contact has been updated.',
|
2022-01-06 18:13:45 +00:00
|
|
|
mutation: updateContactMutation,
|
2022-03-31 21:08:16 +00:00
|
|
|
existingId: 'gid://gitlab/CustomerRelations::Contact/12',
|
2022-01-06 18:13:45 +00:00
|
|
|
};
|
|
|
|
mountContact({ propsData });
|
|
|
|
};
|
|
|
|
|
|
|
|
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' },
|
|
|
|
],
|
2022-03-31 21:08:16 +00:00
|
|
|
getQuery: {
|
|
|
|
query: getGroupOrganizationsQuery,
|
|
|
|
variables: { groupFullPath: 'flightjs' },
|
|
|
|
},
|
|
|
|
getQueryNodePath: 'group.organizations',
|
2022-01-06 18:13:45 +00:00
|
|
|
...propsData,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const mountOrganizationCreate = () => {
|
|
|
|
const propsData = {
|
|
|
|
title: 'New organization',
|
2022-03-31 21:08:16 +00:00
|
|
|
successMessage: 'Organization has been added.',
|
2022-01-06 18:13:45 +00:00
|
|
|
buttonLabel: 'Create organization',
|
|
|
|
mutation: createOrganizationMutation,
|
|
|
|
additionalCreateParams: { groupId: 'gid://gitlab/Group/26' },
|
|
|
|
};
|
|
|
|
mountOrganization({ propsData });
|
|
|
|
};
|
|
|
|
|
|
|
|
const forms = {
|
|
|
|
[FORM_CREATE_CONTACT]: {
|
|
|
|
mountFunction: mountContactCreate,
|
|
|
|
mutationErrorResponse: createContactMutationErrorResponse,
|
2022-03-31 21:08:16 +00:00
|
|
|
toastMessage: 'Contact has been added.',
|
2022-01-06 18:13:45 +00:00
|
|
|
},
|
|
|
|
[FORM_UPDATE_CONTACT]: {
|
|
|
|
mountFunction: mountContactUpdate,
|
|
|
|
mutationErrorResponse: updateContactMutationErrorResponse,
|
2022-03-31 21:08:16 +00:00
|
|
|
toastMessage: 'Contact has been updated.',
|
2022-01-06 18:13:45 +00:00
|
|
|
},
|
|
|
|
[FORM_CREATE_ORG]: {
|
|
|
|
mountFunction: mountOrganizationCreate,
|
|
|
|
mutationErrorResponse: createOrganizationMutationErrorResponse,
|
2022-03-31 21:08:16 +00:00
|
|
|
toastMessage: 'Organization has been added.',
|
2022-01-06 18:13:45 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
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.`);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
);
|
2022-05-11 12:09:03 +00:00
|
|
|
|
|
|
|
describe('edit form', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mountContactUpdate();
|
|
|
|
});
|
|
|
|
|
|
|
|
it.each`
|
|
|
|
index | id | componentName | 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 a $componentName for #$id with the value "$value"',
|
|
|
|
({ index, id, componentName, value }) => {
|
|
|
|
const component = componentName === 'GlFormInput' ? GlFormInput : GlFormSelect;
|
|
|
|
const findFormGroup = (at) => wrapper.findAllComponents(GlFormGroup).at(at);
|
|
|
|
const findFormElement = () => findFormGroup(index).find(component);
|
|
|
|
|
|
|
|
expect(findFormElement().attributes('id')).toBe(id);
|
|
|
|
expect(findFormElement().attributes('value')).toBe(value);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
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: {
|
|
|
|
description: null,
|
|
|
|
email: 'example@gitlab.com',
|
|
|
|
firstName: 'Michael',
|
|
|
|
id: 'gid://gitlab/CustomerRelations::Contact/12',
|
|
|
|
lastName: 'McFly',
|
|
|
|
organizationId: 'gid://gitlab/CustomerRelations::Organization/1',
|
|
|
|
phone: null,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2022-01-06 18:13:45 +00:00
|
|
|
});
|