Commit 48b29935 authored by Coung Ngo's avatar Coung Ngo Committed by Brandon Labuschagne

Improve service desk frontend code

Improve code as a result of
https://gitlab.com/gitlab-org/gitlab/-/issues/288339

This commit:

- Fixes `service_desk_root.vue` `incomingEmail` prop from being mutated
- Removes data properties in the Vue root component
- Converts root component props to provide/inject
- Removes an event hub in favour of component events
- Removes service class
- Completely overhauls the related tests
parent ca67c9c7
...@@ -2,60 +2,42 @@ ...@@ -2,60 +2,42 @@
import { GlAlert } from '@gitlab/ui'; import { GlAlert } from '@gitlab/ui';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import ServiceDeskSetting from './service_desk_setting.vue'; import ServiceDeskSetting from './service_desk_setting.vue';
import ServiceDeskService from '../services/service_desk_service'; import axios from '~/lib/utils/axios_utils';
import eventHub from '../event_hub';
export default { export default {
name: 'ServiceDeskRoot',
components: { components: {
GlAlert, GlAlert,
ServiceDeskSetting, ServiceDeskSetting,
}, },
props: { inject: {
initialIsEnabled: { initialIsEnabled: {
type: Boolean, default: false,
required: true,
}, },
endpoint: { endpoint: {
type: String, default: '',
required: true,
}, },
incomingEmail: { initialIncomingEmail: {
type: String,
required: false,
default: '', default: '',
}, },
customEmail: { customEmail: {
type: String,
required: false,
default: '', default: '',
}, },
customEmailEnabled: { customEmailEnabled: {
type: Boolean, default: false,
required: false,
}, },
selectedTemplate: { selectedTemplate: {
type: String,
required: false,
default: '', default: '',
}, },
outgoingName: { outgoingName: {
type: String,
required: false,
default: '', default: '',
}, },
projectKey: { projectKey: {
type: String,
required: false,
default: '', default: '',
}, },
templates: { templates: {
type: Array, default: [],
required: false,
default: () => [],
}, },
}, },
data() { data() {
return { return {
isEnabled: this.initialIsEnabled, isEnabled: this.initialIsEnabled,
...@@ -63,28 +45,21 @@ export default { ...@@ -63,28 +45,21 @@ export default {
isAlertShowing: false, isAlertShowing: false,
alertVariant: 'danger', alertVariant: 'danger',
alertMessage: '', alertMessage: '',
incomingEmail: this.initialIncomingEmail,
updatedCustomEmail: this.customEmail, updatedCustomEmail: this.customEmail,
}; };
}, },
created() {
eventHub.$on('serviceDeskEnabledCheckboxToggled', this.onEnableToggled);
eventHub.$on('serviceDeskTemplateSave', this.onSaveTemplate);
this.service = new ServiceDeskService(this.endpoint);
},
beforeDestroy() {
eventHub.$off('serviceDeskEnabledCheckboxToggled', this.onEnableToggled);
eventHub.$off('serviceDeskTemplateSave', this.onSaveTemplate);
},
methods: { methods: {
onEnableToggled(isChecked) { onEnableToggled(isChecked) {
this.isEnabled = isChecked; this.isEnabled = isChecked;
this.incomingEmail = ''; this.incomingEmail = '';
this.service const body = {
.toggleServiceDesk(isChecked) service_desk_enabled: isChecked,
};
return axios
.put(this.endpoint, body)
.then(({ data }) => { .then(({ data }) => {
const email = data.service_desk_address; const email = data.service_desk_address;
if (isChecked && !email) { if (isChecked && !email) {
...@@ -104,8 +79,16 @@ export default { ...@@ -104,8 +79,16 @@ export default {
onSaveTemplate({ selectedTemplate, outgoingName, projectKey }) { onSaveTemplate({ selectedTemplate, outgoingName, projectKey }) {
this.isTemplateSaving = true; this.isTemplateSaving = true;
this.service
.updateTemplate({ selectedTemplate, outgoingName, projectKey }, this.isEnabled) const body = {
issue_template_key: selectedTemplate,
outgoing_name: outgoingName,
project_key: projectKey,
service_desk_enabled: this.isEnabled,
};
return axios
.put(this.endpoint, body)
.then(({ data }) => { .then(({ data }) => {
this.updatedCustomEmail = data?.service_desk_address; this.updatedCustomEmail = data?.service_desk_address;
this.showAlert(__('Changes saved.'), 'success'); this.showAlert(__('Changes saved.'), 'success');
...@@ -150,6 +133,8 @@ export default { ...@@ -150,6 +133,8 @@ export default {
:initial-project-key="projectKey" :initial-project-key="projectKey"
:templates="templates" :templates="templates"
:is-template-saving="isTemplateSaving" :is-template-saving="isTemplateSaving"
@save="onSaveTemplate"
@toggle="onEnableToggled"
/> />
</div> </div>
</template> </template>
<script> <script>
import { GlButton, GlFormSelect, GlToggle, GlLoadingIcon, GlSprintf } from '@gitlab/ui'; import { GlButton, GlFormSelect, GlToggle, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import eventHub from '../event_hub';
export default { export default {
name: 'ServiceDeskSetting',
components: { components: {
ClipboardButton, ClipboardButton,
GlButton, GlButton,
...@@ -15,7 +12,6 @@ export default { ...@@ -15,7 +12,6 @@ export default {
GlLoadingIcon, GlLoadingIcon,
GlSprintf, GlSprintf,
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
isEnabled: { isEnabled: {
type: Boolean, type: Boolean,
...@@ -84,10 +80,10 @@ export default { ...@@ -84,10 +80,10 @@ export default {
}, },
methods: { methods: {
onCheckboxToggle(isChecked) { onCheckboxToggle(isChecked) {
eventHub.$emit('serviceDeskEnabledCheckboxToggled', isChecked); this.$emit('toggle', isChecked);
}, },
onSaveTemplate() { onSaveTemplate() {
eventHub.$emit('serviceDeskTemplateSave', { this.$emit('save', {
selectedTemplate: this.selectedTemplate, selectedTemplate: this.selectedTemplate,
outgoingName: this.outgoingName, outgoingName: this.outgoingName,
projectKey: this.projectKey, projectKey: this.projectKey,
...@@ -111,7 +107,11 @@ export default { ...@@ -111,7 +107,11 @@ export default {
</label> </label>
<div v-if="isEnabled" class="row mt-3"> <div v-if="isEnabled" class="row mt-3">
<div class="col-md-9 mb-0"> <div class="col-md-9 mb-0">
<strong id="incoming-email-describer" class="d-block mb-1"> <strong
id="incoming-email-describer"
class="gl-display-block gl-mb-1"
data-testid="incoming-email-describer"
>
{{ __('Email address to use for Support Desk') }} {{ __('Email address to use for Support Desk') }}
</strong> </strong>
<template v-if="email"> <template v-if="email">
...@@ -128,11 +128,7 @@ export default { ...@@ -128,11 +128,7 @@ export default {
disabled="true" disabled="true"
/> />
<div class="input-group-append"> <div class="input-group-append">
<clipboard-button <clipboard-button :title="__('Copy')" :text="email" css-class="input-group-text" />
:title="__('Copy')"
:text="email"
css-class="input-group-text qa-clipboard-button"
/>
</div> </div>
</div> </div>
<span v-if="hasCustomEmail" class="form-text text-muted"> <span v-if="hasCustomEmail" class="form-text text-muted">
......
import createEventHub from '~/helpers/event_hub_factory';
export default createEventHub();
...@@ -3,43 +3,37 @@ import { parseBoolean } from '~/lib/utils/common_utils'; ...@@ -3,43 +3,37 @@ import { parseBoolean } from '~/lib/utils/common_utils';
import ServiceDeskRoot from './components/service_desk_root.vue'; import ServiceDeskRoot from './components/service_desk_root.vue';
export default () => { export default () => {
const serviceDeskRootElement = document.querySelector('.js-service-desk-setting-root'); const el = document.querySelector('.js-service-desk-setting-root');
if (serviceDeskRootElement) {
// eslint-disable-next-line no-new if (!el) {
new Vue({ return false;
el: serviceDeskRootElement,
components: {
ServiceDeskRoot,
},
data() {
const { dataset } = serviceDeskRootElement;
return {
initialIsEnabled: parseBoolean(dataset.enabled),
endpoint: dataset.endpoint,
incomingEmail: dataset.incomingEmail,
customEmail: dataset.customEmail,
customEmailEnabled: parseBoolean(dataset.customEmailEnabled),
selectedTemplate: dataset.selectedTemplate,
outgoingName: dataset.outgoingName,
projectKey: dataset.projectKey,
templates: JSON.parse(dataset.templates),
};
},
render(createElement) {
return createElement('service-desk-root', {
props: {
initialIsEnabled: this.initialIsEnabled,
endpoint: this.endpoint,
incomingEmail: this.incomingEmail,
customEmail: this.customEmail,
customEmailEnabled: this.customEmailEnabled,
selectedTemplate: this.selectedTemplate,
outgoingName: this.outgoingName,
projectKey: this.projectKey,
templates: this.templates,
},
});
},
});
} }
const {
customEmail,
customEmailEnabled,
enabled,
endpoint,
incomingEmail,
outgoingName,
projectKey,
selectedTemplate,
templates,
} = el.dataset;
return new Vue({
el,
provide: {
customEmail,
customEmailEnabled: parseBoolean(customEmailEnabled),
endpoint,
initialIncomingEmail: incomingEmail,
initialIsEnabled: parseBoolean(enabled),
outgoingName,
projectKey,
selectedTemplate,
templates: JSON.parse(templates),
},
render: (createElement) => createElement(ServiceDeskRoot),
});
}; };
import axios from '~/lib/utils/axios_utils';
class ServiceDeskService {
constructor(endpoint) {
this.endpoint = endpoint;
}
toggleServiceDesk(enable) {
return axios.put(this.endpoint, { service_desk_enabled: enable });
}
updateTemplate({ selectedTemplate, outgoingName, projectKey = '' }, isEnabled) {
const body = {
issue_template_key: selectedTemplate,
outgoing_name: outgoingName,
project_key: projectKey,
service_desk_enabled: isEnabled,
};
return axios.put(this.endpoint, body);
}
}
export default ServiceDeskService;
import { mount } from '@vue/test-utils'; import { GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter'; import AxiosMockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import ServiceDeskRoot from '~/projects/settings_service_desk/components/service_desk_root.vue';
import ServiceDeskSetting from '~/projects/settings_service_desk/components/service_desk_setting.vue';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status'; import httpStatusCodes from '~/lib/utils/http_status';
import ServiceDeskRoot from '~/projects/settings_service_desk/components/service_desk_root.vue';
import ServiceDeskSetting from '~/projects/settings_service_desk/components/service_desk_setting.vue';
describe('ServiceDeskRoot', () => { describe('ServiceDeskRoot', () => {
const endpoint = '/gitlab-org/gitlab-test/service_desk';
const initialIncomingEmail = 'servicedeskaddress@example.com';
let axiosMock; let axiosMock;
let wrapper; let wrapper;
let spy; let spy;
const provideData = {
customEmail: 'custom.email@example.com',
customEmailEnabled: true,
endpoint: '/gitlab-org/gitlab-test/service_desk',
initialIncomingEmail: 'servicedeskaddress@example.com',
initialIsEnabled: true,
outgoingName: 'GitLab Support Bot',
projectKey: 'key',
selectedTemplate: 'Bug',
templates: ['Bug', 'Documentation'],
};
const getAlertText = () => wrapper.find(GlAlert).text();
const createComponent = () => shallowMount(ServiceDeskRoot, { provide: provideData });
beforeEach(() => { beforeEach(() => {
axiosMock = new AxiosMockAdapter(axios); axiosMock = new AxiosMockAdapter(axios);
spy = jest.spyOn(axios, 'put');
}); });
afterEach(() => { afterEach(() => {
...@@ -25,156 +41,122 @@ describe('ServiceDeskRoot', () => { ...@@ -25,156 +41,122 @@ describe('ServiceDeskRoot', () => {
} }
}); });
it('sends a request to toggle service desk off when the toggle is clicked from the on state', () => { describe('ServiceDeskSetting component', () => {
axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK); it('is rendered', () => {
wrapper = createComponent();
expect(wrapper.find(ServiceDeskSetting).props()).toEqual({
customEmail: provideData.customEmail,
customEmailEnabled: provideData.customEmailEnabled,
incomingEmail: provideData.initialIncomingEmail,
initialOutgoingName: provideData.outgoingName,
initialProjectKey: provideData.projectKey,
initialSelectedTemplate: provideData.selectedTemplate,
isEnabled: provideData.initialIsEnabled,
isTemplateSaving: false,
templates: provideData.templates,
});
});
describe('toggle event', () => {
describe('when toggling service desk on', () => {
beforeEach(async () => {
wrapper = createComponent();
spy = jest.spyOn(axios, 'put'); wrapper.find(ServiceDeskSetting).vm.$emit('toggle', true);
wrapper = mount(ServiceDeskRoot, { await waitForPromises();
propsData: { });
initialIsEnabled: true,
initialIncomingEmail, it('sends a request to turn service desk on', () => {
endpoint, axiosMock.onPut(provideData.endpoint).replyOnce(httpStatusCodes.OK);
},
});
wrapper.find('button.gl-toggle').trigger('click'); expect(spy).toHaveBeenCalledWith(provideData.endpoint, { service_desk_enabled: true });
});
return wrapper.vm it('shows a message when there is an error', () => {
.$nextTick() axiosMock.onPut(provideData.endpoint).networkError();
.then(waitForPromises)
.then(() => { expect(getAlertText()).toContain('An error occurred while enabling Service Desk.');
expect(spy).toHaveBeenCalledWith(endpoint, { service_desk_enabled: false }); });
}); });
});
it('sends a request to toggle service desk on when the toggle is clicked from the off state', () => { describe('when toggling service desk off', () => {
axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK); beforeEach(async () => {
wrapper = createComponent();
spy = jest.spyOn(axios, 'put'); wrapper.find(ServiceDeskSetting).vm.$emit('toggle', false);
wrapper = mount(ServiceDeskRoot, { await waitForPromises();
propsData: { });
initialIsEnabled: false,
initialIncomingEmail: '',
endpoint,
},
});
wrapper.find('button.gl-toggle').trigger('click'); it('sends a request to turn service desk off', () => {
axiosMock.onPut(provideData.endpoint).replyOnce(httpStatusCodes.OK);
return wrapper.vm.$nextTick(() => { expect(spy).toHaveBeenCalledWith(provideData.endpoint, { service_desk_enabled: false });
expect(spy).toHaveBeenCalledWith(endpoint, { service_desk_enabled: true }); });
});
});
it('shows an error message when there is an issue toggling service desk on', () => { it('shows a message when there is an error', () => {
axiosMock.onPut(endpoint).networkError(); axiosMock.onPut(provideData.endpoint).networkError();
wrapper = mount(ServiceDeskRoot, { expect(getAlertText()).toContain('An error occurred while disabling Service Desk.');
propsData: { });
initialIsEnabled: false, });
initialIncomingEmail: '',
endpoint,
},
}); });
wrapper.find('button.gl-toggle').trigger('click'); describe('save event', () => {
describe('successful request', () => {
beforeEach(async () => {
axiosMock.onPut(provideData.endpoint).replyOnce(httpStatusCodes.OK);
return wrapper.vm wrapper = createComponent();
.$nextTick()
.then(waitForPromises)
.then(() => {
expect(wrapper.html()).toContain('An error occurred while enabling Service Desk.');
});
});
it('sends a request to update template when the "Save template" button is clicked', () => { const payload = {
axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK); selectedTemplate: 'Bug',
outgoingName: 'GitLab Support Bot',
projectKey: 'key',
};
spy = jest.spyOn(axios, 'put'); wrapper.find(ServiceDeskSetting).vm.$emit('save', payload);
wrapper = mount(ServiceDeskRoot, { await waitForPromises();
propsData: { });
initialIsEnabled: true,
endpoint,
initialIncomingEmail,
selectedTemplate: 'Bug',
outgoingName: 'GitLab Support Bot',
templates: ['Bug', 'Documentation'],
projectKey: 'key',
},
});
wrapper.find('button.btn-success').trigger('click'); it('sends a request to update template', async () => {
expect(spy).toHaveBeenCalledWith(provideData.endpoint, {
issue_template_key: 'Bug',
outgoing_name: 'GitLab Support Bot',
project_key: 'key',
service_desk_enabled: true,
});
});
return wrapper.vm.$nextTick(() => { it('shows success message', () => {
expect(spy).toHaveBeenCalledWith(endpoint, { expect(getAlertText()).toContain('Changes saved.');
issue_template_key: 'Bug', });
outgoing_name: 'GitLab Support Bot',
project_key: 'key',
service_desk_enabled: true,
}); });
});
});
it('saves the template when the "Save template" button is clicked', () => { describe('unsuccessful request', () => {
axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK); beforeEach(async () => {
axiosMock.onPut(provideData.endpoint).networkError();
wrapper = mount(ServiceDeskRoot, {
propsData: {
initialIsEnabled: true,
endpoint,
initialIncomingEmail,
selectedTemplate: 'Bug',
templates: ['Bug', 'Documentation'],
},
});
wrapper.find('button.btn-success').trigger('click'); wrapper = createComponent();
return wrapper.vm const payload = {
.$nextTick() selectedTemplate: 'Bug',
.then(waitForPromises) outgoingName: 'GitLab Support Bot',
.then(() => { projectKey: 'key',
expect(wrapper.html()).toContain('Changes saved.'); };
});
});
it('shows an error message when there is an issue saving the template', () => { wrapper.find(ServiceDeskSetting).vm.$emit('save', payload);
axiosMock.onPut(endpoint).networkError();
wrapper = mount(ServiceDeskRoot, {
propsData: {
initialIsEnabled: true,
endpoint,
initialIncomingEmail,
selectedTemplate: 'Bug',
templates: ['Bug', 'Documentation'],
},
});
wrapper.find('button.btn-success').trigger('click'); await waitForPromises();
});
return wrapper.vm it('shows an error message', () => {
.$nextTick() expect(getAlertText()).toContain('An error occured while saving changes:');
.then(waitForPromises) });
.then(() => {
expect(wrapper.html()).toContain('An error occured while saving changes:');
}); });
});
it('passes customEmail through updatedCustomEmail correctly', () => {
const customEmail = 'foo';
wrapper = mount(ServiceDeskRoot, {
propsData: {
initialIsEnabled: true,
endpoint,
customEmail,
},
}); });
expect(wrapper.find(ServiceDeskSetting).props('customEmail')).toEqual(customEmail);
}); });
}); });
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlButton, GlFormSelect, GlLoadingIcon, GlToggle } from '@gitlab/ui';
import eventHub from '~/projects/settings_service_desk/event_hub'; import { nextTick } from 'vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ServiceDeskSetting from '~/projects/settings_service_desk/components/service_desk_setting.vue'; import ServiceDeskSetting from '~/projects/settings_service_desk/components/service_desk_setting.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
describe('ServiceDeskSetting', () => { describe('ServiceDeskSetting', () => {
let wrapper; let wrapper;
const findButton = () => wrapper.find(GlButton);
const findClipboardButton = () => wrapper.find(ClipboardButton);
const findIncomingEmail = () => wrapper.findByTestId('incoming-email');
const findIncomingEmailLabel = () => wrapper.findByTestId('incoming-email-describer');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findTemplateDropdown = () => wrapper.find(GlFormSelect);
const findToggle = () => wrapper.find(GlToggle);
const createComponent = ({ props = {}, mountFunction = shallowMount } = {}) =>
extendedWrapper(
mountFunction(ServiceDeskSetting, {
propsData: {
isEnabled: true,
...props,
},
}),
);
afterEach(() => { afterEach(() => {
if (wrapper) { if (wrapper) {
wrapper.destroy(); wrapper.destroy();
} }
}); });
const findTemplateDropdown = () => wrapper.find('#service-desk-template-select');
const findIncomingEmail = () => wrapper.find('[data-testid="incoming-email"]');
describe('when isEnabled=true', () => { describe('when isEnabled=true', () => {
describe('only isEnabled', () => { describe('only isEnabled', () => {
describe('as project admin', () => { describe('as project admin', () => {
beforeEach(() => { beforeEach(() => {
wrapper = shallowMount(ServiceDeskSetting, { wrapper = createComponent();
propsData: {
isEnabled: true,
},
});
}); });
it('should see activation checkbox', () => { it('should see activation checkbox', () => {
expect(wrapper.find('#service-desk-checkbox').exists()).toBe(true); expect(findToggle().exists()).toBe(true);
}); });
it('should see main panel with the email info', () => { it('should see main panel with the email info', () => {
expect(wrapper.find('#incoming-email-describer').exists()).toBe(true); expect(findIncomingEmailLabel().exists()).toBe(true);
}); });
it('should see loading spinner and not the incoming email', () => { it('should see loading spinner and not the incoming email', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); expect(findLoadingIcon().exists()).toBe(true);
expect(findIncomingEmail().exists()).toBe(false); expect(findIncomingEmail().exists()).toBe(false);
}); });
}); });
}); });
describe('service desk toggle', () => { describe('service desk toggle', () => {
it('emits an event to turn on Service Desk when clicked', () => { it('emits an event to turn on Service Desk when clicked', async () => {
const eventSpy = jest.fn(); wrapper = createComponent();
eventHub.$on('serviceDeskEnabledCheckboxToggled', eventSpy);
wrapper = mount(ServiceDeskSetting, {
propsData: {
isEnabled: false,
},
});
wrapper.find('#service-desk-checkbox').trigger('click'); findToggle().vm.$emit('change', true);
expect(eventSpy).toHaveBeenCalledWith(true); await nextTick();
eventHub.$off('serviceDeskEnabledCheckboxToggled', eventSpy); expect(wrapper.emitted('toggle')[0]).toEqual([true]);
eventSpy.mockRestore();
}); });
}); });
...@@ -65,23 +70,23 @@ describe('ServiceDeskSetting', () => { ...@@ -65,23 +70,23 @@ describe('ServiceDeskSetting', () => {
const incomingEmail = 'foo@bar.com'; const incomingEmail = 'foo@bar.com';
beforeEach(() => { beforeEach(() => {
wrapper = mount(ServiceDeskSetting, { wrapper = createComponent({
propsData: { props: { incomingEmail },
isEnabled: true,
incomingEmail,
},
}); });
}); });
it('should see email and not the loading spinner', () => { it('should see email and not the loading spinner', () => {
expect(findIncomingEmail().element.value).toEqual(incomingEmail); expect(findIncomingEmail().element.value).toEqual(incomingEmail);
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false); expect(findLoadingIcon().exists()).toBe(false);
}); });
it('renders a copy to clipboard button', () => { it('renders a copy to clipboard button', () => {
expect(wrapper.find('.qa-clipboard-button').exists()).toBe(true); expect(findClipboardButton().exists()).toBe(true);
expect(wrapper.find('.qa-clipboard-button').element.dataset.clipboardText).toBe( expect(findClipboardButton().props()).toEqual(
incomingEmail, expect.objectContaining({
title: 'Copy',
text: incomingEmail,
}),
); );
}); });
}); });
...@@ -92,12 +97,8 @@ describe('ServiceDeskSetting', () => { ...@@ -92,12 +97,8 @@ describe('ServiceDeskSetting', () => {
const customEmail = 'custom@bar.com'; const customEmail = 'custom@bar.com';
beforeEach(() => { beforeEach(() => {
wrapper = mount(ServiceDeskSetting, { wrapper = createComponent({
propsData: { props: { incomingEmail, customEmail },
isEnabled: true,
incomingEmail,
customEmail,
},
}); });
}); });
...@@ -110,12 +111,8 @@ describe('ServiceDeskSetting', () => { ...@@ -110,12 +111,8 @@ describe('ServiceDeskSetting', () => {
const email = 'foo@bar.com'; const email = 'foo@bar.com';
beforeEach(() => { beforeEach(() => {
wrapper = mount(ServiceDeskSetting, { wrapper = createComponent({
propsData: { props: { incomingEmail: email, customEmail: email },
isEnabled: true,
incomingEmail: email,
customEmail: email,
},
}); });
}); });
...@@ -127,21 +124,13 @@ describe('ServiceDeskSetting', () => { ...@@ -127,21 +124,13 @@ describe('ServiceDeskSetting', () => {
describe('templates dropdown', () => { describe('templates dropdown', () => {
it('renders a dropdown to choose a template', () => { it('renders a dropdown to choose a template', () => {
wrapper = shallowMount(ServiceDeskSetting, { wrapper = createComponent();
propsData: {
isEnabled: true,
},
});
expect(wrapper.find('#service-desk-template-select').exists()).toBe(true); expect(findTemplateDropdown().exists()).toBe(true);
}); });
it('renders a dropdown with a default value of ""', () => { it('renders a dropdown with a default value of ""', () => {
wrapper = mount(ServiceDeskSetting, { wrapper = createComponent({ mountFunction: mount });
propsData: {
isEnabled: true,
},
});
expect(findTemplateDropdown().element.value).toEqual(''); expect(findTemplateDropdown().element.value).toEqual('');
}); });
...@@ -149,23 +138,18 @@ describe('ServiceDeskSetting', () => { ...@@ -149,23 +138,18 @@ describe('ServiceDeskSetting', () => {
it('renders a dropdown with a value of "Bug" when it is the initial value', () => { it('renders a dropdown with a value of "Bug" when it is the initial value', () => {
const templates = ['Bug', 'Documentation', 'Security release']; const templates = ['Bug', 'Documentation', 'Security release'];
wrapper = mount(ServiceDeskSetting, { wrapper = createComponent({
propsData: { props: { initialSelectedTemplate: 'Bug', templates },
isEnabled: true, mountFunction: mount,
initialSelectedTemplate: 'Bug',
templates,
},
}); });
expect(findTemplateDropdown().element.value).toEqual('Bug'); expect(findTemplateDropdown().element.value).toEqual('Bug');
}); });
it('renders a dropdown with no options when the project has no templates', () => { it('renders a dropdown with no options when the project has no templates', () => {
wrapper = mount(ServiceDeskSetting, { wrapper = createComponent({
propsData: { props: { templates: [] },
isEnabled: true, mountFunction: mount,
templates: [],
},
}); });
// The dropdown by default has one empty option // The dropdown by default has one empty option
...@@ -174,11 +158,10 @@ describe('ServiceDeskSetting', () => { ...@@ -174,11 +158,10 @@ describe('ServiceDeskSetting', () => {
it('renders a dropdown with options when the project has templates', () => { it('renders a dropdown with options when the project has templates', () => {
const templates = ['Bug', 'Documentation', 'Security release']; const templates = ['Bug', 'Documentation', 'Security release'];
wrapper = mount(ServiceDeskSetting, {
propsData: { wrapper = createComponent({
isEnabled: true, props: { templates },
templates, mountFunction: mount,
},
}); });
// An empty-named template is prepended so the user can select no template // An empty-named template is prepended so the user can select no template
...@@ -199,78 +182,59 @@ describe('ServiceDeskSetting', () => { ...@@ -199,78 +182,59 @@ describe('ServiceDeskSetting', () => {
describe('save button', () => { describe('save button', () => {
it('renders a save button to save a template', () => { it('renders a save button to save a template', () => {
wrapper = mount(ServiceDeskSetting, { wrapper = createComponent();
propsData: {
isEnabled: true,
},
});
expect(wrapper.find('button.btn-success').text()).toContain('Save changes'); expect(findButton().text()).toContain('Save changes');
}); });
it('emits a save event with the chosen template when the save button is clicked', () => { it('emits a save event with the chosen template when the save button is clicked', async () => {
const eventSpy = jest.fn(); wrapper = createComponent({
eventHub.$on('serviceDeskTemplateSave', eventSpy); props: {
wrapper = mount(ServiceDeskSetting, {
propsData: {
isEnabled: true,
initialSelectedTemplate: 'Bug', initialSelectedTemplate: 'Bug',
initialOutgoingName: 'GitLab Support Bot', initialOutgoingName: 'GitLab Support Bot',
initialProjectKey: 'key', initialProjectKey: 'key',
}, },
}); });
wrapper.find('button.btn-success').trigger('click'); findButton().vm.$emit('click');
await nextTick();
expect(eventSpy).toHaveBeenCalledWith({ const payload = {
selectedTemplate: 'Bug', selectedTemplate: 'Bug',
outgoingName: 'GitLab Support Bot', outgoingName: 'GitLab Support Bot',
projectKey: 'key', projectKey: 'key',
}); };
eventHub.$off('serviceDeskTemplateSave', eventSpy); expect(wrapper.emitted('save')[0]).toEqual([payload]);
eventSpy.mockRestore();
}); });
}); });
describe('when isEnabled=false', () => { describe('when isEnabled=false', () => {
beforeEach(() => { beforeEach(() => {
wrapper = shallowMount(ServiceDeskSetting, { wrapper = createComponent({
propsData: { props: { isEnabled: false },
isEnabled: false,
},
}); });
}); });
it('does not render email panel', () => { it('does not render email panel', () => {
expect(wrapper.find('#incoming-email-describer').exists()).toBe(false); expect(findIncomingEmailLabel().exists()).toBe(false);
}); });
it('does not render template dropdown', () => { it('does not render template dropdown', () => {
expect(wrapper.find('#service-desk-template-select').exists()).toBe(false); expect(findTemplateDropdown().exists()).toBe(false);
}); });
it('does not render template save button', () => { it('does not render template save button', () => {
expect(wrapper.find('button.btn-success').exists()).toBe(false); expect(findButton().exists()).toBe(false);
}); });
it('emits an event to turn on Service Desk when the toggle is clicked', () => { it('emits an event to turn on Service Desk when the toggle is clicked', async () => {
const eventSpy = jest.fn(); findToggle().vm.$emit('change', false);
eventHub.$on('serviceDeskEnabledCheckboxToggled', eventSpy);
wrapper = mount(ServiceDeskSetting, {
propsData: {
isEnabled: true,
},
});
wrapper.find('#service-desk-checkbox').trigger('click');
expect(eventSpy).toHaveBeenCalledWith(false); await nextTick();
eventHub.$off('serviceDeskEnabledCheckboxToggled', eventSpy); expect(wrapper.emitted('toggle')[0]).toEqual([false]);
eventSpy.mockRestore();
}); });
}); });
}); });
import AxiosMockAdapter from 'axios-mock-adapter';
import ServiceDeskService from '~/projects/settings_service_desk/services/service_desk_service';
import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status';
describe('ServiceDeskService', () => {
const endpoint = `/gitlab-org/gitlab-test/service_desk`;
const dummyResponse = { message: 'Dummy response' };
const errorMessage = 'Network Error';
let axiosMock;
let service;
beforeEach(() => {
axiosMock = new AxiosMockAdapter(axios);
service = new ServiceDeskService(endpoint);
});
afterEach(() => {
axiosMock.restore();
});
describe('toggleServiceDesk', () => {
it('makes a request to set service desk', () => {
axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK, dummyResponse);
return service.toggleServiceDesk(true).then((response) => {
expect(response.data).toEqual(dummyResponse);
});
});
it('fails on error response', () => {
axiosMock.onPut(endpoint).networkError();
return service.toggleServiceDesk(true).catch((error) => {
expect(error.message).toBe(errorMessage);
});
});
it('makes a request with the expected body', () => {
axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK, dummyResponse);
const spy = jest.spyOn(axios, 'put');
service.toggleServiceDesk(true);
expect(spy).toHaveBeenCalledWith(endpoint, {
service_desk_enabled: true,
});
spy.mockRestore();
});
});
describe('updateTemplate', () => {
it('makes a request to update template', () => {
axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK, dummyResponse);
return service
.updateTemplate(
{
selectedTemplate: 'Bug',
outgoingName: 'GitLab Support Bot',
},
true,
)
.then((response) => {
expect(response.data).toEqual(dummyResponse);
});
});
it('fails on error response', () => {
axiosMock.onPut(endpoint).networkError();
return service
.updateTemplate(
{
selectedTemplate: 'Bug',
outgoingName: 'GitLab Support Bot',
},
true,
)
.catch((error) => {
expect(error.message).toBe(errorMessage);
});
});
it('makes a request with the expected body', () => {
axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK, dummyResponse);
const spy = jest.spyOn(axios, 'put');
service.updateTemplate(
{
selectedTemplate: 'Bug',
outgoingName: 'GitLab Support Bot',
projectKey: 'key',
},
true,
);
expect(spy).toHaveBeenCalledWith(endpoint, {
issue_template_key: 'Bug',
outgoing_name: 'GitLab Support Bot',
project_key: 'key',
service_desk_enabled: true,
});
spy.mockRestore();
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment