Commit 9887a361 authored by Rajat Jain's avatar Rajat Jain

Render inherited templates from group in service desk

Right now, only project level templates are rendered in the service desk.
With the introduction of inheritance, a project has access to group level
templates as well. With this change, you can see all the templates available
-- project level + group level.

Changelog: added
parent 7d567806
...@@ -31,6 +31,9 @@ export default { ...@@ -31,6 +31,9 @@ export default {
selectedTemplate: { selectedTemplate: {
default: '', default: '',
}, },
selectedFileTemplateProjectId: {
default: null,
},
outgoingName: { outgoingName: {
default: '', default: '',
}, },
...@@ -80,7 +83,7 @@ export default { ...@@ -80,7 +83,7 @@ export default {
}); });
}, },
onSaveTemplate({ selectedTemplate, outgoingName, projectKey }) { onSaveTemplate({ selectedTemplate, fileTemplateProjectId, outgoingName, projectKey }) {
this.isTemplateSaving = true; this.isTemplateSaving = true;
const body = { const body = {
...@@ -88,6 +91,7 @@ export default { ...@@ -88,6 +91,7 @@ export default {
outgoing_name: outgoingName, outgoing_name: outgoingName,
project_key: projectKey, project_key: projectKey,
service_desk_enabled: this.isEnabled, service_desk_enabled: this.isEnabled,
file_template_project_id: fileTemplateProjectId,
}; };
return axios return axios
...@@ -132,6 +136,7 @@ export default { ...@@ -132,6 +136,7 @@ export default {
:custom-email="updatedCustomEmail" :custom-email="updatedCustomEmail"
:custom-email-enabled="customEmailEnabled" :custom-email-enabled="customEmailEnabled"
:initial-selected-template="selectedTemplate" :initial-selected-template="selectedTemplate"
:initial-selected-file-template-project-id="selectedFileTemplateProjectId"
:initial-outgoing-name="outgoingName" :initial-outgoing-name="outgoingName"
:initial-project-key="projectKey" :initial-project-key="projectKey"
:templates="templates" :templates="templates"
......
<script> <script>
import { import { GlButton, GlToggle, GlLoadingIcon, GlSprintf, GlFormInput, GlLink } from '@gitlab/ui';
GlButton,
GlFormSelect,
GlToggle,
GlLoadingIcon,
GlSprintf,
GlFormInput,
GlLink,
} from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import ServiceDeskTemplateDropdown from './service_desk_template_dropdown.vue';
export default { export default {
i18n: { i18n: {
...@@ -18,12 +11,12 @@ export default { ...@@ -18,12 +11,12 @@ export default {
components: { components: {
ClipboardButton, ClipboardButton,
GlButton, GlButton,
GlFormSelect,
GlToggle, GlToggle,
GlLoadingIcon, GlLoadingIcon,
GlSprintf, GlSprintf,
GlFormInput, GlFormInput,
GlLink, GlLink,
ServiceDeskTemplateDropdown,
}, },
props: { props: {
isEnabled: { isEnabled: {
...@@ -49,6 +42,11 @@ export default { ...@@ -49,6 +42,11 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
initialSelectedFileTemplateProjectId: {
type: Number,
required: false,
default: null,
},
initialOutgoingName: { initialOutgoingName: {
type: String, type: String,
required: false, required: false,
...@@ -73,14 +71,13 @@ export default { ...@@ -73,14 +71,13 @@ export default {
data() { data() {
return { return {
selectedTemplate: this.initialSelectedTemplate, selectedTemplate: this.initialSelectedTemplate,
selectedFileTemplateProjectId: this.initialSelectedFileTemplateProjectId,
outgoingName: this.initialOutgoingName || __('GitLab Support Bot'), outgoingName: this.initialOutgoingName || __('GitLab Support Bot'),
projectKey: this.initialProjectKey, projectKey: this.initialProjectKey,
searchTerm: '',
}; };
}, },
computed: { computed: {
templateOptions() {
return [''].concat(this.templates);
},
hasProjectKeySupport() { hasProjectKeySupport() {
return Boolean(this.customEmailEnabled); return Boolean(this.customEmailEnabled);
}, },
...@@ -100,8 +97,13 @@ export default { ...@@ -100,8 +97,13 @@ export default {
selectedTemplate: this.selectedTemplate, selectedTemplate: this.selectedTemplate,
outgoingName: this.outgoingName, outgoingName: this.outgoingName,
projectKey: this.projectKey, projectKey: this.projectKey,
fileTemplateProjectId: this.selectedFileTemplateProjectId,
}); });
}, },
templateChange({ selectedFileTemplateProjectId, selectedTemplate }) {
this.selectedFileTemplateProjectId = selectedFileTemplateProjectId;
this.selectedTemplate = selectedTemplate;
},
}, },
}; };
</script> </script>
...@@ -193,12 +195,13 @@ export default { ...@@ -193,12 +195,13 @@ export default {
<label for="service-desk-template-select" class="mt-3"> <label for="service-desk-template-select" class="mt-3">
{{ __('Template to append to all Service Desk issues') }} {{ __('Template to append to all Service Desk issues') }}
</label> </label>
<gl-form-select <service-desk-template-dropdown
id="service-desk-template-select" :selected-template="selectedTemplate"
v-model="selectedTemplate" :selected-file-template-project-id="selectedFileTemplateProjectId"
data-qa-selector="service_desk_template_dropdown" :templates="templates"
:options="templateOptions" @change="templateChange"
/> />
<label for="service-desk-email-from-name" class="mt-3"> <label for="service-desk-email-from-name" class="mt-3">
{{ __('Email display name') }} {{ __('Email display name') }}
</label> </label>
...@@ -210,6 +213,7 @@ export default { ...@@ -210,6 +213,7 @@ export default {
<gl-button <gl-button
variant="success" variant="success"
class="gl-mt-5" class="gl-mt-5"
data-testid="save_service_desk_settings_button"
data-qa-selector="save_service_desk_settings_button" data-qa-selector="save_service_desk_settings_button"
:disabled="isTemplateSaving" :disabled="isTemplateSaving"
@click="onSaveTemplate" @click="onSaveTemplate"
......
<script>
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import { GlDropdown, GlDropdownSectionHeader, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
components: {
GlDropdown,
GlDropdownSectionHeader,
GlDropdownItem,
GlSearchBoxByType,
},
props: {
selectedTemplate: {
type: String,
required: false,
default: '',
},
templates: {
type: Array,
required: true,
},
selectedFileTemplateProjectId: {
type: Number,
required: false,
default: null,
},
},
data() {
return {
searchTerm: '',
};
},
computed: {
templateOptions() {
if (this.searchTerm) {
const filteredTemplates = [];
for (let i = 0; i < this.templates.length; i += 2) {
const sectionName = this.templates[i];
const availableTemplates = this.templates[i + 1];
const matchedTemplates = fuzzaldrinPlus.filter(availableTemplates, this.searchTerm, {
key: 'name',
});
if (matchedTemplates.length > 0) {
filteredTemplates.push(sectionName, matchedTemplates);
}
}
return filteredTemplates;
}
return this.templates;
},
},
methods: {
templateClick(template) {
// Clicking on the same template should unselect it
if (
template.name === this.selectedTemplate &&
template.project_id === this.selectedFileTemplateProjectId
) {
this.$emit('change', {
selectedFileTemplateProjectId: null,
selectedTemplate: null,
});
return;
}
this.$emit('change', {
selectedFileTemplateProjectId: template.project_id,
selectedTemplate: template.key,
});
},
},
i18n: {
defaultDropdownText: __('Choose a template'),
},
};
</script>
<template>
<gl-dropdown
id="service-desk-template-select"
:text="selectedTemplate || $options.i18n.defaultDropdownText"
:header-text="$options.i18n.defaultDropdownText"
data-qa-selector="service_desk_template_dropdown"
:block="true"
class="service-desk-template-select"
toggle-class="gl-m-0"
>
<template #header>
<gl-search-box-by-type v-model.trim="searchTerm" />
</template>
<template v-for="item in templateOptions">
<gl-dropdown-section-header v-if="!Array.isArray(item)" :key="item">
{{ item }}
</gl-dropdown-section-header>
<template v-else>
<gl-dropdown-item
v-for="template in item"
:key="template.key"
:is-check-item="true"
:is-checked="
template.project_id === selectedFileTemplateProjectId &&
template.name === selectedTemplate
"
@click="() => templateClick(template)"
>
{{ template.name }}
</gl-dropdown-item>
</template>
</template>
</gl-dropdown>
</template>
...@@ -18,6 +18,7 @@ export default () => { ...@@ -18,6 +18,7 @@ export default () => {
outgoingName, outgoingName,
projectKey, projectKey,
selectedTemplate, selectedTemplate,
selectedFileTemplateProjectId,
templates, templates,
} = el.dataset; } = el.dataset;
...@@ -32,6 +33,7 @@ export default () => { ...@@ -32,6 +33,7 @@ export default () => {
outgoingName, outgoingName,
projectKey, projectKey,
selectedTemplate, selectedTemplate,
selectedFileTemplateProjectId: parseInt(selectedFileTemplateProjectId, 10) || null,
templates: JSON.parse(templates), templates: JSON.parse(templates),
}, },
render: (createElement) => createElement(ServiceDeskRoot), render: (createElement) => createElement(ServiceDeskRoot),
......
...@@ -32,14 +32,17 @@ module IssuablesDescriptionTemplatesHelper ...@@ -32,14 +32,17 @@ module IssuablesDescriptionTemplatesHelper
@template_types[project.id][issuable_type] ||= TemplateFinder.all_template_names(project, issuable_type.pluralize) @template_types[project.id][issuable_type] ||= TemplateFinder.all_template_names(project, issuable_type.pluralize)
end end
# Overriden on EE::IssuablesDescriptionTemplatesHelper to include inherited templates names def selected_template(issuable)
def issuable_templates_names(issuable, include_inherited_templates = false)
all_templates = issuable_templates(ref_project, issuable.to_ability_name) all_templates = issuable_templates(ref_project, issuable.to_ability_name)
all_templates.values.flatten.map { |tpl| tpl[:name] if tpl[:project_id] == ref_project.id }.compact.uniq
# Only local templates will be listed if licenses for inherited templates are not present
all_templates = all_templates.values.flatten.map { |tpl| tpl[:name] }.compact.uniq
all_templates.find { |tmpl_name| tmpl_name == params[:issuable_template] }
end end
def selected_template(issuable) def available_service_desk_templates_for(project)
params[:issuable_template] if issuable_templates_names(issuable, true).any? { |tmpl_name| tmpl_name == params[:issuable_template] } issuable_templates(project, 'issue').flatten.to_json
end end
def template_names_path(parent, issuable) def template_names_path(parent, issuable)
......
...@@ -14,8 +14,9 @@ ...@@ -14,8 +14,9 @@
custom_email: (@project.service_desk_custom_address if @project.service_desk_enabled), custom_email: (@project.service_desk_custom_address if @project.service_desk_enabled),
custom_email_enabled: "#{Gitlab::ServiceDeskEmail.enabled?}", custom_email_enabled: "#{Gitlab::ServiceDeskEmail.enabled?}",
selected_template: "#{@project.service_desk_setting&.issue_template_key}", selected_template: "#{@project.service_desk_setting&.issue_template_key}",
selected_file_template_project_id: "#{@project.service_desk_setting&.file_template_project_id}",
outgoing_name: "#{@project.service_desk_setting&.outgoing_name}", outgoing_name: "#{@project.service_desk_setting&.outgoing_name}",
project_key: "#{@project.service_desk_setting&.project_key}", project_key: "#{@project.service_desk_setting&.project_key}",
templates: issuable_templates_names(Issue.new) } } templates: available_service_desk_templates_for(@project) } }
- elsif show_callout?('promote_service_desk_dismissed') - elsif show_callout?('promote_service_desk_dismissed')
= render 'shared/promotions/promote_servicedesk' = render 'shared/promotions/promote_servicedesk'
...@@ -137,15 +137,23 @@ You can use these placeholders to be automatically replaced in each email: ...@@ -137,15 +137,23 @@ You can use these placeholders to be automatically replaced in each email:
#### New Service Desk issues #### New Service Desk issues
You can select one [issue description template](description_templates.md#create-an-issue-template) You can select one [description template](description_templates.md#create-an-issue-template)
**per project** to be appended to every new Service Desk issue's description. **per project** to be appended to every new Service Desk issue's description.
Issue description templates should reside in your repository's `.gitlab/issue_templates/` directory.
To use a custom issue template with Service Desk, in your project: You can set description templates at various levels:
1. [Create a description template](description_templates.md#create-an-issue-template) - The entire [instance](description_templates.md#set-instance-level-description-templates).
1. Go to **Settings > General > Service Desk**. - A specific [group or subgroup](description_templates.md#set-group-level-description-templates).
1. From the dropdown **Template to append to all Service Desk issues**, select your template. - A specific [project](description_templates.md#set-a-default-template-for-merge-requests-and-issues).
The templates are inherited. For example, in a project, you can also access templates set for the instance or the project’s parent groups.
To use a custom description template with Service Desk:
1. On the top bar, select **Menu > Projects** and find your project.
1. [Create a description template](description_templates.md#create-an-issue-template).
1. On the left sidebar, select **Settings > General > Service Desk**.
1. From the dropdown **Template to append to all Service Desk issues**, search or select your template.
### Using a custom email display name ### Using a custom email display name
...@@ -156,7 +164,8 @@ this name in the `From` header. The default display name is `GitLab Support Bot` ...@@ -156,7 +164,8 @@ this name in the `From` header. The default display name is `GitLab Support Bot`
To edit the custom email display name: To edit the custom email display name:
1. In a project, go to **Settings > General > Service Desk**. 1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General > Service Desk**.
1. Enter a new name in **Email display name**. 1. Enter a new name in **Email display name**.
1. Select **Save Changes**. 1. Select **Save Changes**.
......
# frozen_string_literal: true
module EE
module IssuablesDescriptionTemplatesHelper
extend ::Gitlab::Utils::Override
override :issuable_templates_names
def issuable_templates_names(issuable, include_inherited_templates = false)
return super unless include_inherited_templates
all_templates = issuable_templates(ref_project, issuable.to_ability_name)
all_templates.values.flatten.map { |tpl| tpl[:name] }.compact.uniq
end
end
end
...@@ -17,14 +17,22 @@ RSpec.describe 'Service Desk Setting', :js, :clean_gitlab_redis_cache do ...@@ -17,14 +17,22 @@ RSpec.describe 'Service Desk Setting', :js, :clean_gitlab_redis_cache do
} }
end end
let_it_be(:issue_template_file) do
{
'.gitlab/issue_templates/template.md' => 'Template file contents'
}
end
let_it_be(:project_with_issue_template) { create(:project, :custom_repo, files: issue_template_file) }
let_it_be_with_reload(:group) { create(:group)} let_it_be_with_reload(:group) { create(:group)}
let_it_be_with_reload(:project) { create(:project, :custom_repo, group: group, files: issuable_project_template_files) } let_it_be_with_reload(:project) { create(:project, :custom_repo, group: group, files: issuable_project_template_files) }
let_it_be(:group_template_repo) { create(:project, :custom_repo, group: group, files: issuable_group_template_files) } let_it_be(:group_template_repo) { create(:project, :custom_repo, files: issuable_group_template_files) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:presenter) { project.present(current_user: user) } let_it_be(:presenter) { project.present(current_user: user) }
before do before do
stub_licensed_features(custom_file_templates_for_namespace: true, custom_file_templates: true) stub_licensed_features(custom_file_templates_for_namespace: true, custom_file_templates: true)
stub_ee_application_setting(file_template_project: project_with_issue_template)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
...@@ -39,9 +47,28 @@ RSpec.describe 'Service Desk Setting', :js, :clean_gitlab_redis_cache do ...@@ -39,9 +47,28 @@ RSpec.describe 'Service Desk Setting', :js, :clean_gitlab_redis_cache do
expect(proj_instance).to receive(:present).with(current_user: user).and_return(presenter) expect(proj_instance).to receive(:present).with(current_user: user).and_return(presenter)
end end
create(:project_group_link, project: group_template_repo, group: group)
group.update_columns(file_template_project_id: group_template_repo.id) group.update_columns(file_template_project_id: group_template_repo.id)
visit edit_project_path(project) visit edit_project_path(project)
end end
it_behaves_like 'issue description templates from current project only' it 'loads group, project and instance issue description templates', :aggregate_failures do
within('#service-desk-template-select') do
expect(page).to have_content(:all, 'project-issue-bar')
expect(page).to have_content(:all, 'project-issue-foo')
expect(page).to have_content(:all, 'group-issue-bar')
expect(page).to have_content(:all, 'group-issue-foo')
expect(page).to have_content(:all, 'template')
end
end
it 'persists file_template_project_id on save' do
find('#service-desk-template-select').click
find('.gl-new-dropdown-item-text-primary', exact_text: 'template').click
find('[data-testid="save_service_desk_settings_button"]').click
wait_for_requests
expect(project.service_desk_setting.file_template_project_id).to eq(project_with_issue_template.id)
end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe IssuablesDescriptionTemplatesHelper do
include_context 'project issuable templates context'
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:parent_group) { create(:group) }
let_it_be_with_reload(:group) { create(:group, parent: parent_group) }
let_it_be_with_reload(:project) { create(:project, :custom_repo, group: group, files: issuable_template_files) }
let_it_be(:file_template_project) { create(:project, :custom_repo, group: parent_group, files: issuable_template_files) }
let_it_be(:group_member) { create(:group_member, :developer, group: parent_group, user: user) }
let_it_be(:inherited_from) { file_template_project }
shared_examples 'issuable templates' do
context 'when include_inherited_templates is true' do
it 'returns project templates and inherited templates' do
expect(helper.issuable_templates_names(Issue.new, true)).to eq(%w[project_template inherited_template])
end
end
context 'when include_inherited_templates is false' do
it 'returns only project templates' do
expect(helper.issuable_templates_names(Issue.new)).to eq(%w[project_template])
end
end
end
describe '#issuable_templates' do
context 'when project parent group has a file template project' do
before do
stub_licensed_features(custom_file_templates_for_namespace: true)
parent_group.update_columns(file_template_project_id: file_template_project.id)
end
it_behaves_like 'project issuable templates'
end
end
describe '#issuable_template_names' do
let(:templates) do
{
'' => [{ name: 'project_template', id: 'project_issue_template', project_id: project.id }],
'Instance' => [{ name: 'inherited_template', id: 'instance_issue_template', project_id: file_template_project.id }]
}
end
before do
allow(helper).to receive(:ref_project).and_return(project)
allow(helper).to receive(:issuable_templates).and_return(templates)
end
it_behaves_like 'issuable templates'
end
end
export const TEMPLATES = [
'Project #1',
[
{ name: 'Bug', project_id: 1 },
{ name: 'Documentation', project_id: 1 },
{ name: 'Security release', project_id: 1 },
],
];
...@@ -21,6 +21,7 @@ describe('ServiceDeskRoot', () => { ...@@ -21,6 +21,7 @@ describe('ServiceDeskRoot', () => {
outgoingName: 'GitLab Support Bot', outgoingName: 'GitLab Support Bot',
projectKey: 'key', projectKey: 'key',
selectedTemplate: 'Bug', selectedTemplate: 'Bug',
selectedFileTemplateProjectId: 42,
templates: ['Bug', 'Documentation'], templates: ['Bug', 'Documentation'],
}; };
...@@ -52,6 +53,7 @@ describe('ServiceDeskRoot', () => { ...@@ -52,6 +53,7 @@ describe('ServiceDeskRoot', () => {
initialOutgoingName: provideData.outgoingName, initialOutgoingName: provideData.outgoingName,
initialProjectKey: provideData.projectKey, initialProjectKey: provideData.projectKey,
initialSelectedTemplate: provideData.selectedTemplate, initialSelectedTemplate: provideData.selectedTemplate,
initialSelectedFileTemplateProjectId: provideData.selectedFileTemplateProjectId,
isEnabled: provideData.initialIsEnabled, isEnabled: provideData.initialIsEnabled,
isTemplateSaving: false, isTemplateSaving: false,
templates: provideData.templates, templates: provideData.templates,
......
import { GlButton, GlFormSelect, GlLoadingIcon, GlToggle } from '@gitlab/ui'; import { GlButton, GlDropdown, GlLoadingIcon, GlToggle } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; 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';
...@@ -13,12 +13,12 @@ describe('ServiceDeskSetting', () => { ...@@ -13,12 +13,12 @@ describe('ServiceDeskSetting', () => {
const findIncomingEmail = () => wrapper.findByTestId('incoming-email'); const findIncomingEmail = () => wrapper.findByTestId('incoming-email');
const findIncomingEmailLabel = () => wrapper.findByTestId('incoming-email-describer'); const findIncomingEmailLabel = () => wrapper.findByTestId('incoming-email-describer');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon); const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findTemplateDropdown = () => wrapper.find(GlFormSelect); const findTemplateDropdown = () => wrapper.find(GlDropdown);
const findToggle = () => wrapper.find(GlToggle); const findToggle = () => wrapper.find(GlToggle);
const createComponent = ({ props = {}, mountFunction = shallowMount } = {}) => const createComponent = ({ props = {} } = {}) =>
extendedWrapper( extendedWrapper(
mountFunction(ServiceDeskSetting, { shallowMount(ServiceDeskSetting, {
propsData: { propsData: {
isEnabled: true, isEnabled: true,
...props, ...props,
...@@ -144,63 +144,6 @@ describe('ServiceDeskSetting', () => { ...@@ -144,63 +144,6 @@ describe('ServiceDeskSetting', () => {
}); });
}); });
}); });
describe('templates dropdown', () => {
it('renders a dropdown to choose a template', () => {
wrapper = createComponent();
expect(findTemplateDropdown().exists()).toBe(true);
});
it('renders a dropdown with a default value of ""', () => {
wrapper = createComponent({ mountFunction: mount });
expect(findTemplateDropdown().element.value).toEqual('');
});
it('renders a dropdown with a value of "Bug" when it is the initial value', () => {
const templates = ['Bug', 'Documentation', 'Security release'];
wrapper = createComponent({
props: { initialSelectedTemplate: 'Bug', templates },
mountFunction: mount,
});
expect(findTemplateDropdown().element.value).toEqual('Bug');
});
it('renders a dropdown with no options when the project has no templates', () => {
wrapper = createComponent({
props: { templates: [] },
mountFunction: mount,
});
// The dropdown by default has one empty option
expect(findTemplateDropdown().element.children).toHaveLength(1);
});
it('renders a dropdown with options when the project has templates', () => {
const templates = ['Bug', 'Documentation', 'Security release'];
wrapper = createComponent({
props: { templates },
mountFunction: mount,
});
// An empty-named template is prepended so the user can select no template
const expectedTemplates = [''].concat(templates);
const dropdown = findTemplateDropdown();
const dropdownList = Array.from(dropdown.element.children).map(
(option) => option.innerText,
);
expect(dropdown.element.children).toHaveLength(expectedTemplates.length);
expect(dropdownList.includes('Bug')).toEqual(true);
expect(dropdownList.includes('Documentation')).toEqual(true);
expect(dropdownList.includes('Security release')).toEqual(true);
});
});
}); });
describe('save button', () => { describe('save button', () => {
...@@ -214,6 +157,7 @@ describe('ServiceDeskSetting', () => { ...@@ -214,6 +157,7 @@ describe('ServiceDeskSetting', () => {
wrapper = createComponent({ wrapper = createComponent({
props: { props: {
initialSelectedTemplate: 'Bug', initialSelectedTemplate: 'Bug',
initialSelectedFileTemplateProjectId: 42,
initialOutgoingName: 'GitLab Support Bot', initialOutgoingName: 'GitLab Support Bot',
initialProjectKey: 'key', initialProjectKey: 'key',
}, },
...@@ -225,6 +169,7 @@ describe('ServiceDeskSetting', () => { ...@@ -225,6 +169,7 @@ describe('ServiceDeskSetting', () => {
const payload = { const payload = {
selectedTemplate: 'Bug', selectedTemplate: 'Bug',
fileTemplateProjectId: 42,
outgoingName: 'GitLab Support Bot', outgoingName: 'GitLab Support Bot',
projectKey: 'key', projectKey: 'key',
}; };
......
import { GlDropdown, GlDropdownSectionHeader, GlDropdownItem } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ServiceDeskTemplateDropdown from '~/projects/settings_service_desk/components/service_desk_setting.vue';
import { TEMPLATES } from './mock_data';
describe('ServiceDeskTemplateDropdown', () => {
let wrapper;
const findTemplateDropdown = () => wrapper.find(GlDropdown);
const createComponent = ({ props = {} } = {}) =>
extendedWrapper(
mount(ServiceDeskTemplateDropdown, {
propsData: {
isEnabled: true,
...props,
},
}),
);
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
});
describe('templates dropdown', () => {
it('renders a dropdown to choose a template', () => {
wrapper = createComponent();
expect(findTemplateDropdown().exists()).toBe(true);
});
it('renders a dropdown with a default value of "Choose a template"', () => {
wrapper = createComponent();
expect(findTemplateDropdown().props('text')).toEqual('Choose a template');
});
it('renders a dropdown with a value of "Bug" when it is the initial value', () => {
const templates = TEMPLATES;
wrapper = createComponent({
props: { initialSelectedTemplate: 'Bug', initialSelectedTemplateProjectId: 1, templates },
});
expect(findTemplateDropdown().props('text')).toEqual('Bug');
});
it('renders a dropdown with header items', () => {
wrapper = createComponent({
props: { templates: TEMPLATES },
});
const headerItems = wrapper.findAll(GlDropdownSectionHeader);
expect(headerItems).toHaveLength(1);
expect(headerItems.at(0).text()).toBe(TEMPLATES[0]);
});
it('renders a dropdown with options when the project has templates', () => {
const templates = TEMPLATES;
wrapper = createComponent({
props: { templates },
});
const expectedTemplates = templates[1];
const items = wrapper.findAll(GlDropdownItem);
const dropdownList = expectedTemplates.map((_, index) => items.at(index).text());
expect(items).toHaveLength(expectedTemplates.length);
expect(dropdownList.includes('Bug')).toEqual(true);
expect(dropdownList.includes('Documentation')).toEqual(true);
expect(dropdownList.includes('Security release')).toEqual(true);
});
});
});
...@@ -44,7 +44,7 @@ RSpec.describe IssuablesDescriptionTemplatesHelper, :clean_gitlab_redis_cache do ...@@ -44,7 +44,7 @@ RSpec.describe IssuablesDescriptionTemplatesHelper, :clean_gitlab_redis_cache do
end end
end end
describe '#issuable_templates_names' do describe '#selected_template' do
let_it_be(:project) { build(:project) } let_it_be(:project) { build(:project) }
before do before do
...@@ -63,7 +63,14 @@ RSpec.describe IssuablesDescriptionTemplatesHelper, :clean_gitlab_redis_cache do ...@@ -63,7 +63,14 @@ RSpec.describe IssuablesDescriptionTemplatesHelper, :clean_gitlab_redis_cache do
end end
it 'returns project templates' do it 'returns project templates' do
expect(helper.issuable_templates_names(Issue.new)).to eq(%w[another_issue_template custom_issue_template]) value = [
"",
[
{ name: "another_issue_template", id: "another_issue_template", project_id: project.id },
{ name: "custom_issue_template", id: "custom_issue_template", project_id: project.id }
]
].to_json
expect(helper.available_service_desk_templates_for(@project)).to eq(value)
end end
end end
...@@ -71,7 +78,8 @@ RSpec.describe IssuablesDescriptionTemplatesHelper, :clean_gitlab_redis_cache do ...@@ -71,7 +78,8 @@ RSpec.describe IssuablesDescriptionTemplatesHelper, :clean_gitlab_redis_cache do
let(:templates) { {} } let(:templates) { {} }
it 'returns empty array' do it 'returns empty array' do
expect(helper.issuable_templates_names(Issue.new)).to eq([]) value = [].to_json
expect(helper.available_service_desk_templates_for(@project)).to eq(value)
end end
end end
end end
......
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
RSpec.shared_examples 'issue description templates from current project only' do RSpec.shared_examples 'issue description templates from current project only' do
it 'loads issue description templates from the project only' do it 'loads issue description templates from the project only' do
within('#service-desk-template-select') do within('#service-desk-template-select') do
expect(page).to have_content('project-issue-bar') expect(page).to have_content(:all, 'project-issue-bar')
expect(page).to have_content('project-issue-foo') expect(page).to have_content(:all, 'project-issue-foo')
expect(page).not_to have_content('group-issue-bar') expect(page).not_to have_content(:all, 'group-issue-bar')
expect(page).not_to have_content('group-issue-foo') expect(page).not_to have_content(:all, 'group-issue-foo')
end end
end end
end end
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