Commit 9643bcc6 authored by Justin Ho Tuan Duong's avatar Justin Ho Tuan Duong Committed by Phil Hughes

Add Vuex store for isSaving and isTesting

For separate Save and Test buttons
parent cb3da621
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
import { GlButton } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import eventHub from '../event_hub';
import OverrideDropdown from './override_dropdown.vue';
import ActiveCheckbox from './active_checkbox.vue';
......@@ -18,11 +20,15 @@ export default {
JiraIssuesFields,
TriggerFields,
DynamicField,
GlButton,
},
mixins: [glFeatureFlagsMixin()],
computed: {
...mapGetters(['currentKey', 'propsSource']),
...mapState(['adminState', 'override']),
...mapGetters(['currentKey', 'propsSource', 'isSavingOrTesting']),
...mapState(['adminState', 'override', 'isSaving', 'isTesting']),
isEditable() {
return this.propsSource.editable;
},
isJira() {
return this.propsSource.type === 'jira';
},
......@@ -31,7 +37,15 @@ export default {
},
},
methods: {
...mapActions(['setOverride']),
...mapActions(['setOverride', 'setIsSaving', 'setIsTesting']),
onSaveClick() {
this.setIsSaving(true);
eventHub.$emit('saveIntegration');
},
onTestClick() {
this.setIsTesting(true);
eventHub.$emit('testIntegration');
},
},
};
</script>
......@@ -67,5 +81,29 @@ export default {
:key="`${currentKey}-jira-issues-fields`"
v-bind="propsSource.jiraIssuesProps"
/>
<div v-if="isEditable" class="footer-block row-content-block">
<gl-button
category="primary"
variant="success"
type="submit"
:loading="isSaving"
:disabled="isSavingOrTesting"
data-qa-selector="save_changes_button"
@click.prevent="onSaveClick"
>
{{ __('Save changes') }}
</gl-button>
<gl-button
v-if="propsSource.canTest"
:loading="isTesting"
:disabled="isSavingOrTesting"
:href="propsSource.testPath"
@click.prevent="onTestClick"
>
{{ __('Test settings') }}
</gl-button>
<gl-button class="btn-cancel" :href="propsSource.cancelPath">{{ __('Cancel') }}</gl-button>
</div>
</div>
</template>
......@@ -24,11 +24,15 @@ function parseDatasetToProps(data) {
fields,
inheritFromId,
integrationLevel,
cancelPath,
testPath,
...booleanAttributes
} = data;
const {
showActive,
activated,
editable,
canTest,
commitEvents,
mergeRequestEvents,
enableComments,
......@@ -41,6 +45,10 @@ function parseDatasetToProps(data) {
initialActivated: activated,
showActive,
type,
cancelPath,
editable,
canTest,
testPath,
triggerFieldsProps: {
initialTriggerCommit: commitEvents,
initialTriggerMergeRequest: mergeRequestEvents,
......
import * as types from './mutation_types';
export const setOverride = ({ commit }, override) => commit(types.SET_OVERRIDE, override);
export const setIsSaving = ({ commit }, isSaving) => commit(types.SET_IS_SAVING, isSaving);
export const setIsTesting = ({ commit }, isTesting) => commit(types.SET_IS_TESTING, isTesting);
export const isInheriting = state => (state.adminState === null ? false : !state.override);
export const isSavingOrTesting = state => state.isSaving || state.isTesting;
export const propsSource = (state, getters) =>
getters.isInheriting ? state.adminState : state.customState;
......
export const SET_OVERRIDE = 'SET_OVERRIDE';
export const SET_IS_SAVING = 'SET_IS_SAVING';
export const SET_IS_TESTING = 'SET_IS_TESTING';
......@@ -4,4 +4,10 @@ export default {
[types.SET_OVERRIDE](state, override) {
state.override = override;
},
[types.SET_IS_SAVING](state, isSaving) {
state.isSaving = isSaving;
},
[types.SET_IS_TESTING](state, isTesting) {
state.isTesting = isTesting;
},
};
......@@ -5,5 +5,7 @@ export default ({ adminState = null, customState = {} } = {}) => {
override,
adminState,
customState,
isSaving: false,
isTesting: false,
};
};
import $ from 'jquery';
import axios from '../lib/utils/axios_utils';
import { deprecatedCreateFlash as flash } from '../flash';
import { __ } from '~/locale';
import { __, s__ } from '~/locale';
import toast from '~/vue_shared/plugins/global_toast';
import initForm from './edit';
import eventHub from './edit/event_hub';
......@@ -10,65 +10,63 @@ export default class IntegrationSettingsForm {
this.$form = $(formSelector);
this.formActive = false;
this.vue = null;
// Form Metadata
this.canTestService = this.$form.data('canTest');
this.testEndPoint = this.$form.data('testUrl');
// Form Child Elements
this.$submitBtn = this.$form.find('button[type="submit"]');
this.$submitBtnLoader = this.$submitBtn.find('.js-btn-spinner');
this.$submitBtnLabel = this.$submitBtn.find('.js-btn-label');
}
init() {
// Init Vue component
initForm(
this.vue = initForm(
document.querySelector('.js-vue-integration-settings'),
document.querySelector('.js-vue-admin-integration-settings'),
);
eventHub.$on('toggle', active => {
this.formActive = active;
this.handleServiceToggle();
this.toggleServiceState();
});
eventHub.$on('testIntegration', () => {
this.testIntegration();
});
eventHub.$on('saveIntegration', () => {
this.saveIntegration();
});
// Bind Event Listeners
this.$submitBtn.on('click', e => this.handleSettingsSave(e));
}
handleSettingsSave(e) {
// Check if Service is marked active, as if not marked active,
// We can skip testing it and directly go ahead to allow form to
// be submitted
if (!this.formActive) {
return;
saveIntegration() {
// Service was marked active so now we check;
// 1) If form contents are valid
// 2) If this service can be saved
// If both conditions are true, we override form submission
// and save the service using provided configuration.
if (this.$form.get(0).checkValidity()) {
this.$form.submit();
} else {
eventHub.$emit('validateForm');
this.vue.$store.dispatch('setIsSaving', false);
}
}
testIntegration() {
// Service was marked active so now we check;
// 1) If form contents are valid
// 2) If this service can be tested
// If both conditions are true, we override form submission
// and test the service using provided configuration.
if (this.$form.get(0).checkValidity()) {
if (this.canTestService) {
e.preventDefault();
// eslint-disable-next-line no-jquery/no-serialize
this.testSettings(this.$form.serialize());
}
} else {
e.preventDefault();
eventHub.$emit('validateForm');
this.vue.$store.dispatch('setIsTesting', false);
}
}
handleServiceToggle() {
this.toggleServiceState();
}
/**
* Change Form's validation enforcement based on service status (active/inactive)
*/
toggleServiceState() {
this.toggleSubmitBtnLabel();
if (this.formActive) {
this.$form.removeAttr('novalidate');
} else if (!this.$form.attr('novalidate')) {
......@@ -76,68 +74,24 @@ export default class IntegrationSettingsForm {
}
}
/**
* Toggle Submit button label based on Integration status and ability to test service
*/
toggleSubmitBtnLabel() {
let btnLabel = __('Save changes');
if (this.formActive && this.canTestService) {
btnLabel = __('Test settings and save changes');
}
this.$submitBtnLabel.text(btnLabel);
}
/**
* Toggle Submit button state based on provided boolean value of `saveTestActive`
* When enabled, it does two things, and reverts back when disabled
*
* 1. It shows load spinner on submit button
* 2. Makes submit button disabled
*/
toggleSubmitBtnState(saveTestActive) {
if (saveTestActive) {
this.$submitBtn.disable();
this.$submitBtnLoader.removeClass('hidden');
} else {
this.$submitBtn.enable();
this.$submitBtnLoader.addClass('hidden');
}
}
/**
* Test Integration config
*/
testSettings(formData) {
this.toggleSubmitBtnState(true);
return axios
.put(this.testEndPoint, formData)
.then(({ data }) => {
if (data.error) {
let flashActions;
if (data.test_failed) {
flashActions = {
title: __('Save anyway'),
clickHandler: e => {
e.preventDefault();
this.$form.submit();
},
};
}
flash(`${data.message} ${data.service_response}`, 'alert', document, flashActions);
toast(`${data.message} ${data.service_response}`);
} else {
this.$form.submit();
toast(s__('Integrations|Connection successful.'));
}
this.toggleSubmitBtnState(false);
})
.catch(() => {
flash(__('Something went wrong on our end.'));
this.toggleSubmitBtnState(false);
toast(__('Something went wrong on our end.'));
})
.finally(() => {
this.vue.$store.dispatch('setIsTesting', false);
});
}
}
......@@ -56,9 +56,11 @@ module IntegrationsActions
end
def success_message
message = integration.active? ? _('activated') : _('settings saved, but not activated')
_('%{service_title} %{message}.') % { service_title: integration.title, message: message }
if integration.active?
s_('Integrations|%{integration} settings saved and active.') % { integration: integration.title }
else
s_('Integrations|%{integration} settings saved, but not active.') % { integration: integration.title }
end
end
def serialize_as_json
......
......@@ -65,18 +65,20 @@ class Projects::ServicesController < Projects::ApplicationController
result = ::Integrations::Test::ProjectService.new(@service, current_user, params[:event]).execute
unless result[:success]
return { error: true, message: _('Test failed.'), service_response: result[:message].to_s, test_failed: true }
return { error: true, message: s_('Integrations|Connection failed. Please check your settings.'), service_response: result[:message].to_s, test_failed: true }
end
{}
rescue Gitlab::HTTP::BlockedUrlError => e
{ error: true, message: _('Test failed.'), service_response: e.message, test_failed: true }
{ error: true, message: s_('Integrations|Connection failed. Please check your settings.'), service_response: e.message, test_failed: true }
end
def success_message
message = @service.active? ? _('activated') : _('settings saved, but not activated')
_('%{service_title} %{message}.') % { service_title: @service.title, message: message }
if @service.active?
s_('Integrations|%{integration} settings saved and active.') % { integration: @service.title }
else
s_('Integrations|%{integration} settings saved, but not active.') % { integration: @service.title }
end
end
def service
......
......@@ -96,7 +96,11 @@ module ServicesHelper
trigger_events: trigger_events_for_service(integration),
fields: fields_for_service(integration),
inherit_from_id: integration.inherit_from_id,
integration_level: integration_level(integration)
integration_level: integration_level(integration),
editable: integration.editable?.to_s,
cancel_path: scoped_integrations_path,
can_test: integration.can_test?.to_s,
test_path: scoped_test_integration_path(integration)
}
end
......
......@@ -5,6 +5,3 @@
= form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'fieldset-form js-integration-settings-form' } do |form|
= render 'shared/service_settings', form: form, integration: @service
.footer-block.row-content-block
= form.submit 'Save', class: 'btn btn-success'
......@@ -10,4 +10,4 @@
%span= value
- if %w(alert notice success).include?(key)
%div{ class: "close-icon-wrapper js-close-icon" }
= sprite_icon('close', css_class: 'close-icon')
= sprite_icon('close', css_class: 'close-icon gl-vertical-align-baseline!')
......@@ -13,13 +13,9 @@
- if @service.respond_to?(:detailed_description)
%p= @service.detailed_description
.col-lg-8
= form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= render 'shared/service_settings', form: form, integration: @service
.footer-block.row-content-block{ :class => "#{'gl-display-none' if @service.is_a?(AlertsService)}" }
%input{ id: 'services_redirect_to', type: 'hidden', name: 'redirect_to', value: request.referrer }
= service_save_button(disabled: @service.is_a?(AlertsService))
&nbsp;
= link_to _('Cancel'), project_settings_integrations_path(@project), class: 'btn btn-cancel'
- if lookup_context.template_exists?('show', "projects/services/#{@service.to_param}", true)
%hr
......
......@@ -6,10 +6,5 @@
= integration.title
.col-lg-8
= form_for integration, as: :service, url: scoped_integration_path(integration), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => integration.can_test?, 'test-url' => scoped_test_integration_path(integration) } } do |form|
= form_for integration, as: :service, url: scoped_integration_path(integration), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'test-url' => scoped_test_integration_path(integration) } } do |form|
= render 'shared/service_settings', form: form, integration: integration
- if integration.editable?
.footer-block.row-content-block
= service_save_button
= link_to _('Cancel'), scoped_integrations_path, class: 'btn btn-cancel'
---
title: Split "Test settings" and "Save changes" to separate buttons
merge_request: 37413
author:
type: changed
......@@ -16,8 +16,8 @@
.footer-block.row-content-block
%button.btn.btn-success{ type: 'submit' }
.spinner.spinner-light.js-btn-spinner
%span.js-btn-label
.spinner.spinner-light
%span
Save changes
&nbsp;
= link_to 'Cancel', edit_project_service_path(@project, @service), class: 'btn btn-cancel'
......@@ -36,7 +36,7 @@ RSpec.describe 'User activates GitHub Service' do
it 'activates service' do
click_button('Save')
expect(page).to have_content('GitHub activated.')
expect(page).to have_content('GitHub settings saved and active.')
end
it 'renders a token field of type `password` for masking input' do
......@@ -53,9 +53,9 @@ RSpec.describe 'User activates GitHub Service' do
headers: { 'Content-Type' => 'application/json' }
)
click_button 'Test settings and save changes'
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('GitHub activated.')
expect(page).to have_content('GitHub settings saved and active.')
end
end
end
......
......@@ -7,17 +7,20 @@ RSpec.describe 'User activates Jira', :js do
include_context 'project service Jira context'
describe 'user sets and activates Jira Service' do
before do
server_info = { key: 'value' }.to_json
stub_request(:get, test_url).with(basic_auth: %w(username password)).to_return(body: server_info)
end
context 'when Jira connection test succeeds' do
before do
stub_licensed_features(jira_issues_integration: true)
allow_any_instance_of(JiraService).to receive(:issues_enabled) { true }
server_info = { key: 'value' }.to_json
stub_request(:get, test_url).with(basic_auth: %w(username password)).to_return(body: server_info)
visit_project_integration('Jira')
fill_form
fill_in 'service_project_key', with: 'AB'
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
end
it 'adds Jira links to sidebar menu' do
......@@ -28,13 +31,18 @@ RSpec.describe 'User activates Jira', :js do
expect(page).not_to have_link('Jira', href: url)
end
end
end
context 'when jira_issues_integration feature is not available' do
before do
stub_licensed_features(jira_issues_integration: false)
visit_project_integration('Jira')
fill_form
click_save_integration
end
it 'does not show Jira links to sidebar menu' do
it 'does not show Jira links in sidebar menu' do
page.within('.nav-sidebar') do
expect(page).not_to have_link('Jira Issues', href: project_integrations_jira_issues_path(project))
expect(page).not_to have_link('Issue List', href: project_integrations_jira_issues_path(project), visible: false)
......@@ -44,5 +52,4 @@ RSpec.describe 'User activates Jira', :js do
end
end
end
end
end
......@@ -703,9 +703,6 @@ msgid_plural "%{securityScanner} results are not available because a pipeline ha
msgstr[0] ""
msgstr[1] ""
msgid "%{service_title} %{message}."
msgstr ""
msgid "%{size} GiB"
msgstr ""
......@@ -13569,6 +13566,12 @@ msgstr ""
msgid "Integrations"
msgstr ""
msgid "Integrations|%{integration} settings saved and active."
msgstr ""
msgid "Integrations|%{integration} settings saved, but not active."
msgstr ""
msgid "Integrations|All details"
msgstr ""
......@@ -13578,6 +13581,12 @@ msgstr ""
msgid "Integrations|Comment settings:"
msgstr ""
msgid "Integrations|Connection failed. Please check your settings."
msgstr ""
msgid "Integrations|Connection successful."
msgstr ""
msgid "Integrations|Default settings are inherited from the group level."
msgstr ""
......@@ -24798,10 +24807,7 @@ msgid_plural "Test coverage: %d hits"
msgstr[0] ""
msgstr[1] ""
msgid "Test failed."
msgstr ""
msgid "Test settings and save changes"
msgid "Test settings"
msgstr ""
msgid "TestHooks|Ensure one of your projects has merge requests."
......@@ -29434,9 +29440,6 @@ msgstr[1] ""
msgid "access:"
msgstr ""
msgid "activated"
msgstr ""
msgid "added %{created_at_timeago}"
msgstr ""
......@@ -30649,9 +30652,6 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
msgid "settings saved, but not activated"
msgstr ""
msgid "severity|Critical"
msgstr ""
......
......@@ -14,7 +14,7 @@ module QA
element :password_field, ':data-qa-selector="`${fieldId}_field`"' # rubocop:disable QA/ElementWithPattern
end
view 'app/helpers/services_helper.rb' do
view 'app/assets/javascripts/integrations/edit/components/integration_form.vue' do
element :save_changes_button
end
......
......@@ -13,7 +13,7 @@ module QA
element :service_jira_issue_transition_id_field, ':data-qa-selector="`${fieldId}_field`"' # rubocop:disable QA/ElementWithPattern
end
view 'app/helpers/services_helper.rb' do
view 'app/assets/javascripts/integrations/edit/components/integration_form.vue' do
element :save_changes_button
end
......
......@@ -58,7 +58,7 @@ module QA
end
expect(page).not_to have_text("Url is blocked")
expect(page).to have_text("Jira activated")
expect(page).to have_text("Jira settings saved and active.")
end
end
......
......@@ -123,7 +123,7 @@ RSpec.describe Projects::ServicesController do
expect(response).to be_successful
expect(json_response).to eq(
'error' => true,
'message' => 'Test failed.',
'message' => 'Connection failed. Please check your settings.',
'service_response' => '',
'test_failed' => true
)
......@@ -136,7 +136,7 @@ RSpec.describe Projects::ServicesController do
let(:service_params) { { active: true } }
let(:params) { project_params(service: service_params) }
let(:message) { 'Jira activated.' }
let(:message) { 'Jira settings saved and active.' }
let(:redirect_url) { edit_project_service_path(project, service) }
before do
......@@ -175,7 +175,7 @@ RSpec.describe Projects::ServicesController do
context 'when param `active` is set to false' do
let(:service_params) { { active: false } }
let(:message) { 'Jira settings saved, but not activated.' }
let(:message) { 'Jira settings saved, but not active.' }
it_behaves_like 'service update'
end
......
......@@ -232,7 +232,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
page.select 'All branches', from: 'Branches to be notified'
check_all_events
click_on 'Save'
click_button 'Save changes'
expect(page).to have_content 'Application settings saved successfully'
......
......@@ -16,7 +16,7 @@ RSpec.describe 'Admin activates Prometheus', :js do
it 'activates service' do
check('Active')
fill_in('API URL', with: 'http://prometheus.example.com')
click_button('Save')
click_button('Save changes')
expect(page).to have_content('Application settings saved successfully')
end
......
......@@ -12,6 +12,6 @@ RSpec.describe 'User activates Asana' do
click_test_then_save_integration
expect(page).to have_content('Asana activated.')
expect(page).to have_content('Asana settings saved and active.')
end
end
......@@ -13,8 +13,8 @@ RSpec.describe 'User activates Assembla' do
visit_project_integration('Assembla')
fill_in('Token', with: 'verySecret')
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('Assembla activated.')
expect(page).to have_content('Assembla settings saved and active.')
end
end
......@@ -16,9 +16,9 @@ RSpec.describe 'User activates Atlassian Bamboo CI' do
fill_in('Username', with: 'user')
fill_in('Password', with: 'verySecret')
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('Atlassian Bamboo CI activated.')
expect(page).to have_content('Atlassian Bamboo CI settings saved and active.')
# Password field should not be filled in.
click_link('Atlassian Bamboo CI')
......
......@@ -9,8 +9,8 @@ RSpec.describe 'User activates Emails on push' do
visit_project_integration('Emails on push')
fill_in('Recipients', with: 'qa@company.name')
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('Emails on push activated.')
expect(page).to have_content('Emails on push settings saved and active.')
end
end
......@@ -15,8 +15,8 @@ RSpec.describe 'User activates Flowdock' do
visit_project_integration('Flowdock')
fill_in('Token', with: 'verySecret')
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('Flowdock activated.')
expect(page).to have_content('Flowdock settings saved and active.')
end
end
......@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'User activates HipChat', :js do
include_context 'project service activation'
context 'with standart settings' do
context 'with standard settings' do
before do
stub_request(:post, /.*api.hipchat.com.*/)
end
......@@ -15,9 +15,9 @@ RSpec.describe 'User activates HipChat', :js do
fill_in('Room', with: 'gitlab')
fill_in('Token', with: 'verySecret')
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('HipChat activated.')
expect(page).to have_content('HipChat settings saved and active.')
end
end
......@@ -32,9 +32,9 @@ RSpec.describe 'User activates HipChat', :js do
fill_in('Token', with: 'secretCustom')
fill_in('Server', with: 'https://chat.example.com')
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('HipChat activated.')
expect(page).to have_content('HipChat settings saved and active.')
end
end
end
......@@ -10,8 +10,8 @@ RSpec.describe 'User activates Irker (IRC gateway)' do
check('Colorize messages')
fill_in('Recipients', with: 'irc://chat.freenode.net/#commits')
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('Irker (IRC gateway) activated.')
expect(page).to have_content('Irker (IRC gateway) settings saved and active.')
end
end
......@@ -26,14 +26,14 @@ RSpec.describe 'User activates issue tracker', :js do
fill_form(skip_new_issue_url: skip_new_issue_url)
if skip_test
click_button('Save changes')
click_save_integration
else
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
end
end
it 'activates the service' do
expect(page).to have_content("#{tracker} activated.")
expect(page).to have_content("#{tracker} settings saved and active.")
expect(current_path).to eq(edit_project_service_path(project, tracker.parameterize(separator: '_')))
end
......@@ -57,7 +57,7 @@ RSpec.describe 'User activates issue tracker', :js do
click_test_then_save_integration
end
expect(page).to have_content("#{tracker} activated.")
expect(page).to have_content("#{tracker} settings saved and active.")
expect(current_path).to eq(edit_project_service_path(project, tracker.parameterize(separator: '_')))
end
end
......@@ -72,7 +72,7 @@ RSpec.describe 'User activates issue tracker', :js do
end
it 'saves but does not activate the service' do
expect(page).to have_content("#{tracker} settings saved, but not activated.")
expect(page).to have_content("#{tracker} settings saved, but not active.")
expect(current_path).to eq(edit_project_service_path(project, tracker.parameterize(separator: '_')))
end
......
......@@ -18,8 +18,8 @@ RSpec.describe 'User activates JetBrains TeamCity CI' do
fill_in('Username', with: 'user')
fill_in('Password', with: 'verySecret')
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('JetBrains TeamCity CI activated.')
expect(page).to have_content('JetBrains TeamCity CI settings saved and active.')
end
end
......@@ -6,7 +6,7 @@ RSpec.describe 'User activates Jira', :js do
include_context 'project service activation'
include_context 'project service Jira context'
describe 'user sets and activates Jira Service' do
describe 'user tests Jira Service' do
context 'when Jira connection test succeeds' do
before do
server_info = { key: 'value' }.to_json
......@@ -14,11 +14,11 @@ RSpec.describe 'User activates Jira', :js do
visit_project_integration('Jira')
fill_form
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
end
it 'activates the Jira service' do
expect(page).to have_content('Jira activated.')
expect(page).to have_content('Jira settings saved and active.')
expect(current_path).to eq(edit_project_service_path(project, :jira))
end
......@@ -54,7 +54,7 @@ RSpec.describe 'User activates Jira', :js do
fill_form
click_test_then_save_integration
expect(page).to have_content('Jira activated.')
expect(page).to have_content('Jira settings saved and active.')
expect(current_path).to eq(edit_project_service_path(project, :jira))
end
end
......@@ -67,11 +67,11 @@ RSpec.describe 'User activates Jira', :js do
stub_jira_service_test
visit_project_integration('Jira')
fill_form(disable: true)
click_button('Save changes')
click_save_integration
end
it 'saves but does not activate the Jira service' do
expect(page).to have_content('Jira settings saved, but not activated.')
expect(page).to have_content('Jira settings saved, but not active.')
expect(current_path).to eq(edit_project_service_path(project, :jira))
end
......
......@@ -29,20 +29,20 @@ RSpec.describe 'Set up Mattermost slash commands', :js do
fill_in 'service_token', with: token
click_active_checkbox
click_on 'Save changes'
click_save_integration
expect(current_path).to eq(edit_project_service_path(project, :mattermost_slash_commands))
expect(page).to have_content('Mattermost slash commands settings saved, but not activated.')
expect(page).to have_content('Mattermost slash commands settings saved, but not active.')
end
it 'redirects to the integrations page after activating' do
token = ('a'..'z').to_a.join
fill_in 'service_token', with: token
click_on 'Save changes'
click_save_integration
expect(current_path).to eq(edit_project_service_path(project, :mattermost_slash_commands))
expect(page).to have_content('Mattermost slash commands activated.')
expect(page).to have_content('Mattermost slash commands settings saved and active.')
end
it 'shows the add to mattermost button' do
......
......@@ -16,6 +16,6 @@ RSpec.describe 'User activates Packagist' do
click_test_then_save_integration
expect(page).to have_content('Packagist activated.')
expect(page).to have_content('Packagist settings saved and active.')
end
end
......@@ -13,8 +13,8 @@ RSpec.describe 'User activates PivotalTracker' do
visit_project_integration('PivotalTracker')
fill_in('Token', with: 'verySecret')
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('PivotalTracker activated.')
expect(page).to have_content('PivotalTracker settings saved and active.')
end
end
......@@ -16,7 +16,7 @@ RSpec.describe 'User activates Prometheus' do
click_button('Save changes')
expect(page).not_to have_content('Prometheus activated.')
expect(page).not_to have_content('Prometheus settings saved and active.')
expect(page).to have_content('Fields on this page has been deprecated.')
end
end
......@@ -17,8 +17,8 @@ RSpec.describe 'User activates Pushover' do
select('High Priority', from: 'Priority')
select('Bike', from: 'Sound')
click_test_integration
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('Pushover activated.')
expect(page).to have_content('Pushover settings saved and active.')
end
end
......@@ -15,7 +15,7 @@ RSpec.describe 'User activates Slack notifications', :js do
click_test_then_save_integration
expect(page).to have_content('Slack notifications activated.')
expect(page).to have_content('Slack notifications settings saved and active.')
end
end
......
......@@ -25,7 +25,7 @@ RSpec.describe 'Slack slash commands', :js do
click_on 'Save'
expect(current_path).to eq(edit_project_service_path(project, :slack_slash_commands))
expect(page).to have_content('Slack slash commands settings saved, but not activated.')
expect(page).to have_content('Slack slash commands settings saved, but not active.')
end
it 'redirects to the integrations page after activating' do
......@@ -33,7 +33,7 @@ RSpec.describe 'Slack slash commands', :js do
click_on 'Save'
expect(current_path).to eq(edit_project_service_path(project, :slack_slash_commands))
expect(page).to have_content('Slack slash commands activated.')
expect(page).to have_content('Slack slash commands settings saved and active.')
end
it 'shows the correct trigger url' do
......
......@@ -5,6 +5,8 @@ describe('Integration form state factory', () => {
expect(createState()).toEqual({
adminState: null,
customState: {},
isSaving: false,
isTesting: false,
override: false,
});
});
......
import $ from 'jquery';
import MockAdaptor from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import IntegrationSettingsForm from '~/integrations/integration_settings_form';
import toast from '~/vue_shared/plugins/global_toast';
jest.mock('~/vue_shared/plugins/global_toast');
describe('IntegrationSettingsForm', () => {
const FIXTURE = 'services/edit_service.html';
......@@ -11,7 +13,7 @@ describe('IntegrationSettingsForm', () => {
loadFixtures(FIXTURE);
});
describe('contructor', () => {
describe('constructor', () => {
let integrationSettingsForm;
beforeEach(() => {
......@@ -24,16 +26,10 @@ describe('IntegrationSettingsForm', () => {
expect(integrationSettingsForm.$form).toBeDefined();
expect(integrationSettingsForm.$form.prop('nodeName')).toEqual('FORM');
expect(integrationSettingsForm.formActive).toBeDefined();
// Form Child Elements
expect(integrationSettingsForm.$submitBtn).toBeDefined();
expect(integrationSettingsForm.$submitBtnLoader).toBeDefined();
expect(integrationSettingsForm.$submitBtnLabel).toBeDefined();
});
it('should initialize form metadata on class object', () => {
expect(integrationSettingsForm.testEndPoint).toBeDefined();
expect(integrationSettingsForm.canTestService).toBeDefined();
});
});
......@@ -59,69 +55,6 @@ describe('IntegrationSettingsForm', () => {
});
});
describe('toggleSubmitBtnLabel', () => {
let integrationSettingsForm;
beforeEach(() => {
integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
});
it('should set Save button label to "Test settings and save changes" when serviceActive & canTestService are `true`', () => {
integrationSettingsForm.canTestService = true;
integrationSettingsForm.formActive = true;
integrationSettingsForm.toggleSubmitBtnLabel();
expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual(
'Test settings and save changes',
);
});
it('should set Save button label to "Save changes" when either serviceActive or canTestService (or both) is `false`', () => {
integrationSettingsForm.canTestService = false;
integrationSettingsForm.formActive = false;
integrationSettingsForm.toggleSubmitBtnLabel();
expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes');
integrationSettingsForm.formActive = true;
integrationSettingsForm.toggleSubmitBtnLabel();
expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes');
integrationSettingsForm.canTestService = true;
integrationSettingsForm.formActive = false;
integrationSettingsForm.toggleSubmitBtnLabel();
expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes');
});
});
describe('toggleSubmitBtnState', () => {
let integrationSettingsForm;
beforeEach(() => {
integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
});
it('should disable Save button and show loader animation when called with `true`', () => {
integrationSettingsForm.toggleSubmitBtnState(true);
expect(integrationSettingsForm.$submitBtn.is(':disabled')).toBeTruthy();
expect(integrationSettingsForm.$submitBtnLoader.hasClass('hidden')).toBeFalsy();
});
it('should enable Save button and hide loader animation when called with `false`', () => {
integrationSettingsForm.toggleSubmitBtnState(false);
expect(integrationSettingsForm.$submitBtn.is(':disabled')).toBeFalsy();
expect(integrationSettingsForm.$submitBtnLoader.hasClass('hidden')).toBeTruthy();
});
});
describe('testSettings', () => {
let integrationSettingsForm;
let formData;
......@@ -133,6 +66,8 @@ describe('IntegrationSettingsForm', () => {
jest.spyOn(axios, 'put');
integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
integrationSettingsForm.init();
// eslint-disable-next-line no-jquery/no-serialize
formData = integrationSettingsForm.$form.serialize();
});
......@@ -141,128 +76,60 @@ describe('IntegrationSettingsForm', () => {
mock.restore();
});
it('should make an ajax request with provided `formData`', () => {
return integrationSettingsForm.testSettings(formData).then(() => {
expect(axios.put).toHaveBeenCalledWith(integrationSettingsForm.testEndPoint, formData);
});
});
it('should make an ajax request with provided `formData`', async () => {
await integrationSettingsForm.testSettings(formData);
it('should show error Flash with `Save anyway` action if ajax request responds with error in test', () => {
const errorMessage = 'Test failed.';
mock.onPut(integrationSettingsForm.testEndPoint).reply(200, {
error: true,
message: errorMessage,
service_response: 'some error',
test_failed: true,
});
return integrationSettingsForm.testSettings(formData).then(() => {
const $flashContainer = $('.flash-container');
expect(
$flashContainer
.find('.flash-text')
.text()
.trim(),
).toEqual('Test failed. some error');
expect($flashContainer.find('.flash-action')).toBeDefined();
expect(
$flashContainer
.find('.flash-action')
.text()
.trim(),
).toEqual('Save anyway');
});
});
it('should not show error Flash with `Save anyway` action if ajax request responds with error in validation', () => {
const errorMessage = 'Validations failed.';
mock.onPut(integrationSettingsForm.testEndPoint).reply(200, {
error: true,
message: errorMessage,
service_response: 'some error',
test_failed: false,
});
return integrationSettingsForm.testSettings(formData).then(() => {
const $flashContainer = $('.flash-container');
expect(
$flashContainer
.find('.flash-text')
.text()
.trim(),
).toEqual('Validations failed. some error');
expect($flashContainer.find('.flash-action')).toBeDefined();
expect(
$flashContainer
.find('.flash-action')
.text()
.trim(),
).toEqual('');
});
expect(axios.put).toHaveBeenCalledWith(integrationSettingsForm.testEndPoint, formData);
});
it('should submit form if ajax request responds without any error in test', () => {
it('should show success message if test is successful', async () => {
jest.spyOn(integrationSettingsForm.$form, 'submit').mockImplementation(() => {});
mock.onPut(integrationSettingsForm.testEndPoint).reply(200, {
error: false,
});
return integrationSettingsForm.testSettings(formData).then(() => {
expect(integrationSettingsForm.$form.submit).toHaveBeenCalled();
});
});
await integrationSettingsForm.testSettings(formData);
it('should submit form when clicked on `Save anyway` action of error Flash', () => {
jest.spyOn(integrationSettingsForm.$form, 'submit').mockImplementation(() => {});
expect(toast).toHaveBeenCalledWith('Connection successful.');
});
it('should show error message if ajax request responds with test error', async () => {
const errorMessage = 'Test failed.';
const serviceResponse = 'some error';
mock.onPut(integrationSettingsForm.testEndPoint).reply(200, {
error: true,
message: errorMessage,
test_failed: true,
service_response: serviceResponse,
test_failed: false,
});
return integrationSettingsForm
.testSettings(formData)
.then(() => {
const $flashAction = $('.flash-container .flash-action');
expect($flashAction).toBeDefined();
await integrationSettingsForm.testSettings(formData);
$flashAction.get(0).click();
})
.then(() => {
expect(integrationSettingsForm.$form.submit).toHaveBeenCalled();
});
expect(toast).toHaveBeenCalledWith(`${errorMessage} ${serviceResponse}`);
});
it('should show error Flash if ajax request failed', () => {
it('should show error message if ajax request failed', async () => {
const errorMessage = 'Something went wrong on our end.';
mock.onPut(integrationSettingsForm.testEndPoint).networkError();
return integrationSettingsForm.testSettings(formData).then(() => {
expect(
$('.flash-container .flash-text')
.text()
.trim(),
).toEqual(errorMessage);
});
await integrationSettingsForm.testSettings(formData);
expect(toast).toHaveBeenCalledWith(errorMessage);
});
it('should always call `toggleSubmitBtnState` with `false` once request is completed', () => {
it('should always dispatch `setIsTesting` with `false` once request is completed', async () => {
const dispatchSpy = jest.fn();
mock.onPut(integrationSettingsForm.testEndPoint).networkError();
jest.spyOn(integrationSettingsForm, 'toggleSubmitBtnState').mockImplementation(() => {});
integrationSettingsForm.vue.$store = { dispatch: dispatchSpy };
return integrationSettingsForm.testSettings(formData).then(() => {
expect(integrationSettingsForm.toggleSubmitBtnState).toHaveBeenCalledWith(false);
});
await integrationSettingsForm.testSettings(formData);
expect(dispatchSpy).toHaveBeenCalledWith('setIsTesting', false);
});
});
});
......@@ -22,15 +22,23 @@ RSpec.shared_context 'project service activation' do
find('input[name="service[active]"]').click
end
def click_save_integration
click_button('Save changes')
end
def click_test_integration
click_button('Test settings and save changes')
click_link('Test settings')
end
def click_test_then_save_integration
def click_test_then_save_integration(expect_test_to_fail: true)
click_test_integration
expect(page).to have_content('Test failed.')
if expect_test_to_fail
expect(page).to have_content('Connection failed.')
else
expect(page).to have_content('Connection successful.')
end
click_link('Save anyway')
click_save_integration
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