Commit 722ae431 authored by Phil Hughes's avatar Phil Hughes

Merge branch '197297-test-settings-and-save-changes-vue' into 'master'

Split "Test settings" and "Save changes" buttons [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!37413
parents 8a5881d3 9643bcc6
<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
......@@ -701,9 +701,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 ""
......@@ -13582,6 +13579,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 ""
......@@ -13591,6 +13594,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 ""
......@@ -24817,10 +24826,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."
......@@ -29459,9 +29465,6 @@ msgstr[1] ""
msgid "access:"
msgstr ""
msgid "activated"
msgstr ""
msgid "added %{created_at_timeago}"
msgstr ""
......@@ -30674,9 +30677,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