diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-29 03:08:36 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-29 03:08:36 +0300 |
commit | 1063cd719c0e25df43bf5d2c0ea8e22f112ed225 (patch) | |
tree | 0bdd8d0582bb9cd0d2ca5a6d3ea8ca593465a964 /spec/frontend/alerts_settings | |
parent | 669f67690a43e9ec7b6d148c6ec1391b379fa16e (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/alerts_settings')
7 files changed, 497 insertions, 0 deletions
diff --git a/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_new_spec.js.snap b/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_new_spec.js.snap new file mode 100644 index 00000000000..68328ef3518 --- /dev/null +++ b/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_new_spec.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AlertsSettingsFormNew with default values renders the initial template 1`] = ` +"<gl-form-stub class=\\"gl-mt-6\\"> + <h5 class=\\"gl-font-lg gl-my-5\\">Add new integrations</h5> + <gl-form-group-stub id=\\"integration-type\\" label=\\"1. Select integration type\\" label-for=\\"integration-type\\"> + <gl-form-select-stub id=\\"integration-type\\" options=\\"[object Object],[object Object],[object Object],[object Object]\\" value=\\"\\"></gl-form-select-stub> <span class=\\"gl-text-gray-500\\"><gl-sprintf-stub message=\\"Learn more about our upcoming %{linkStart}integrations%{linkEnd}\\"></gl-sprintf-stub></span> + </gl-form-group-stub> + <b-collapse-stub tag=\\"div\\" class=\\"gl-mt-3\\"> + <gl-form-group-stub id=\\"name-integration\\" label=\\"2. Name integration\\" label-for=\\"name-integration\\"> + <b-form-input-stub id=\\"name-integration\\" value=\\"\\" placeholder=\\"Enter integration name\\" debounce=\\"0\\" type=\\"text\\" class=\\"gl-form-input\\"></b-form-input-stub> + </gl-form-group-stub> + <div class=\\"gl-display-flex gl-justify-content-end\\"> + <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" type=\\"reset\\" class=\\"gl-mr-3 js-no-auto-disable\\">Cancel</gl-button-stub> + <gl-button-stub category=\\"secondary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" type=\\"submit\\" class=\\"gl-mr-1 js-no-auto-disable\\">Save and test payload</gl-button-stub> + <gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" type=\\"submit\\" class=\\"js-no-auto-disable\\">Save integration</gl-button-stub> + </div> + </b-collapse-stub> +</gl-form-stub>" +`; diff --git a/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_old_spec.js.snap b/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_old_spec.js.snap new file mode 100644 index 00000000000..b7b4d96548e --- /dev/null +++ b/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_old_spec.js.snap @@ -0,0 +1,47 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AlertsSettingsForm with default values renders the initial template 1`] = ` +"<gl-form-stub> + <h5 class=\\"gl-font-lg gl-my-5\\"></h5> + <!----> + <div data-testid=\\"alert-settings-description\\"> + <p> + <gl-sprintf-stub message=\\"You must provide this URL and authorization key to authorize an external service to send alerts to GitLab. You can provide this URL and key to multiple services. After configuring an external service, alerts from your service will display on the GitLab %{linkStart}Alerts%{linkEnd} page.\\"></gl-sprintf-stub> + </p> + <p> + <gl-sprintf-stub message=\\"Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.\\"></gl-sprintf-stub> + </p> + </div> + <gl-form-group-stub label-for=\\"integration-type\\" label=\\"Integration\\"> + <gl-form-select-stub id=\\"integration-type\\" options=\\"[object Object],[object Object],[object Object],[object Object]\\" data-testid=\\"alert-settings-select\\" value=\\"generic\\"></gl-form-select-stub> <span class=\\"gl-text-gray-500\\"><gl-sprintf-stub message=\\"Learn more about our our upcoming %{linkStart}integrations%{linkEnd}\\"></gl-sprintf-stub></span> + </gl-form-group-stub> + <gl-form-group-stub label=\\"Active\\" label-for=\\"activated\\"> + <toggle-button-stub id=\\"activated\\"></toggle-button-stub> + </gl-form-group-stub> + <!----> + <gl-form-group-stub label=\\"Webhook URL\\" label-for=\\"url\\"> + <gl-form-input-group-stub value=\\"/alerts/notify.json\\" predefinedoptions=\\"[object Object]\\" id=\\"url\\" readonly=\\"\\"></gl-form-input-group-stub> <span class=\\"gl-text-gray-500\\"> + + </span> + </gl-form-group-stub> + <gl-form-group-stub label=\\"Authorization key\\" label-for=\\"authorization-key\\"> + <gl-form-input-group-stub value=\\"abcedfg123\\" predefinedoptions=\\"[object Object]\\" id=\\"authorization-key\\" readonly=\\"\\" class=\\"gl-mb-2\\"></gl-form-input-group-stub> + <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\" class=\\"gl-mt-3\\" role=\\"button\\" tabindex=\\"0\\">Reset key</gl-button-stub> + <gl-modal-stub modalid=\\"authKeyModal\\" titletag=\\"h4\\" modalclass=\\"\\" size=\\"md\\" title=\\"Reset key\\" ok-title=\\"Reset key\\" ok-variant=\\"danger\\"> + Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in. + </gl-modal-stub> + </gl-form-group-stub> + <gl-form-group-stub label=\\"Alert test payload\\" label-for=\\"alert-json\\"> + <gl-form-textarea-stub noresize=\\"true\\" id=\\"alert-json\\" disabled=\\"true\\" state=\\"true\\" placeholder=\\"Enter test alert JSON....\\" rows=\\"6\\" max-rows=\\"10\\"></gl-form-textarea-stub> + </gl-form-group-stub> + <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">Test alert payload</gl-button-stub> + <div class=\\"footer-block row-content-block gl-display-flex gl-justify-content-space-between\\"> + <gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\"> + Save changes + </gl-button-stub> + <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\"> + Cancel + </gl-button-stub> + </div> +</gl-form-stub>" +`; diff --git a/spec/frontend/alerts_settings/alerts_integrations_list_spec.js b/spec/frontend/alerts_settings/alerts_integrations_list_spec.js new file mode 100644 index 00000000000..4377cd0a60d --- /dev/null +++ b/spec/frontend/alerts_settings/alerts_integrations_list_spec.js @@ -0,0 +1,89 @@ +import { GlTable, GlIcon } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import Tracking from '~/tracking'; +import AlertIntegrationsList, { + i18n, +} from '~/alerts_settings/components/alerts_integrations_list.vue'; +import { trackAlertIntegrationsViewsOptions } from '~/alerts_settings/constants'; + +const mockIntegrations = [ + { + activated: true, + name: 'Integration 1', + type: 'HTTP endpoint', + }, + { + activated: false, + name: 'Integration 2', + type: 'HTTP endpoint', + }, +]; + +describe('AlertIntegrationsList', () => { + let wrapper; + + function mountComponent(propsData = {}) { + wrapper = mount(AlertIntegrationsList, { + propsData: { + integrations: mockIntegrations, + ...propsData, + }, + stubs: { + GlIcon: true, + }, + }); + } + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + wrapper = null; + } + }); + + beforeEach(() => { + mountComponent(); + }); + + const findTableComponent = () => wrapper.find(GlTable); + const finsStatusCell = () => wrapper.findAll('[data-testid="integration-activated-status"]'); + + it('renders a table', () => { + expect(findTableComponent().exists()).toBe(true); + }); + + it('renders an empty state when no integrations provided', () => { + mountComponent({ integrations: [] }); + expect(findTableComponent().text()).toContain(i18n.emptyState); + }); + + describe('integration status', () => { + it('enabled', () => { + const cell = finsStatusCell().at(0); + const activatedIcon = cell.find(GlIcon); + expect(cell.text()).toBe(i18n.status.enabled.name); + expect(activatedIcon.attributes('name')).toBe('check-circle-filled'); + expect(activatedIcon.attributes('title')).toBe(i18n.status.enabled.tooltip); + }); + + it('disabled', () => { + const cell = finsStatusCell().at(1); + const notActivatedIcon = cell.find(GlIcon); + expect(cell.text()).toBe(i18n.status.disabled.name); + expect(notActivatedIcon.attributes('name')).toBe('warning-solid'); + expect(notActivatedIcon.attributes('title')).toBe(i18n.status.disabled.tooltip); + }); + }); + + describe('Snowplow tracking', () => { + beforeEach(() => { + jest.spyOn(Tracking, 'event'); + mountComponent(); + }); + + it('should track alert list page views', () => { + const { category, action } = trackAlertIntegrationsViewsOptions; + expect(Tracking.event).toHaveBeenCalledWith(category, action); + }); + }); +}); diff --git a/spec/frontend/alerts_settings/alerts_settings_form_new_spec.js b/spec/frontend/alerts_settings/alerts_settings_form_new_spec.js new file mode 100644 index 00000000000..ae12f6c9bef --- /dev/null +++ b/spec/frontend/alerts_settings/alerts_settings_form_new_spec.js @@ -0,0 +1,59 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlForm, GlFormSelect, GlCollapse, GlFormInput } from '@gitlab/ui'; +import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form_new.vue'; +import { defaultAlertSettingsConfig } from './util'; + +jest.mock('~/alerts_settings/services'); + +describe('AlertsSettingsFormNew', () => { + let wrapper; + + const createComponent = ({ methods } = {}, data) => { + wrapper = shallowMount(AlertsSettingsForm, { + data() { + return { ...data }; + }, + provide: { + ...defaultAlertSettingsConfig, + }, + methods, + stubs: { GlCollapse, GlFormInput }, + }); + }; + + const findForm = () => wrapper.find(GlForm); + const findSelect = () => wrapper.find(GlFormSelect); + const findFormSteps = () => wrapper.find(GlCollapse); + const findFormName = () => wrapper.find(GlFormInput); + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + wrapper = null; + } + }); + + describe('with default values', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders the initial template', () => { + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('render the initial form with only an integration type dropdown', () => { + expect(findForm().exists()).toBe(true); + expect(findSelect().exists()).toBe(true); + expect(findFormSteps().attributes('visible')).toBeUndefined(); + }); + + it('shows the rest of the form when the dropdown is used', async () => { + findSelect().vm.$emit('change', 'prometheus'); + + await wrapper.vm.$nextTick(); + + expect(findFormName().isVisible()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/alerts_settings/alerts_settings_form_old_spec.js b/spec/frontend/alerts_settings/alerts_settings_form_old_spec.js new file mode 100644 index 00000000000..0fb601b41d5 --- /dev/null +++ b/spec/frontend/alerts_settings/alerts_settings_form_old_spec.js @@ -0,0 +1,206 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlModal, GlAlert } from '@gitlab/ui'; +import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form_old.vue'; +import ToggleButton from '~/vue_shared/components/toggle_button.vue'; +import { i18n } from '~/alerts_settings/constants'; +import service from '~/alerts_settings/services'; +import { defaultAlertSettingsConfig } from './util'; + +jest.mock('~/alerts_settings/services'); + +describe('AlertsSettingsForm', () => { + let wrapper; + + const createComponent = ({ methods } = {}, data) => { + wrapper = shallowMount(AlertsSettingsForm, { + data() { + return { ...data }; + }, + provide: { + ...defaultAlertSettingsConfig, + }, + methods, + }); + }; + + const findSelect = () => wrapper.find('[data-testid="alert-settings-select"]'); + const findJsonInput = () => wrapper.find('#alert-json'); + const findUrl = () => wrapper.find('#url'); + const findAuthorizationKey = () => wrapper.find('#authorization-key'); + const findApiUrl = () => wrapper.find('#api-url'); + + beforeEach(() => { + setFixtures(` + <div> + <span class="js-service-active-status fa fa-circle" data-value="true"></span> + <span class="js-service-active-status fa fa-power-off" data-value="false"></span> + </div>`); + }); + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + wrapper = null; + } + }); + + describe('with default values', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders the initial template', () => { + expect(wrapper.html()).toMatchSnapshot(); + }); + }); + + describe('reset key', () => { + it('triggers resetKey method', () => { + const resetKey = jest.fn(); + const methods = { resetKey }; + createComponent({ methods }); + + wrapper.find(GlModal).vm.$emit('ok'); + + expect(resetKey).toHaveBeenCalled(); + }); + + it('updates the authorization key on success', () => { + createComponent( + {}, + { + authKey: 'newToken', + }, + ); + + expect(findAuthorizationKey().attributes('value')).toBe('newToken'); + }); + + it('shows a alert message on error', () => { + service.updateGenericKey.mockRejectedValueOnce({}); + + createComponent(); + + return wrapper.vm.resetKey().then(() => { + expect(wrapper.find(GlAlert).exists()).toBe(true); + }); + }); + }); + + describe('activate toggle', () => { + it('triggers toggleActivated method', () => { + const toggleService = jest.fn(); + const methods = { toggleService }; + createComponent({ methods }); + + wrapper.find(ToggleButton).vm.$emit('change', true); + expect(toggleService).toHaveBeenCalled(); + }); + + describe('error is encountered', () => { + it('restores previous value', () => { + service.updateGenericKey.mockRejectedValueOnce({}); + createComponent(); + return wrapper.vm.resetKey().then(() => { + expect(wrapper.find(ToggleButton).props('value')).toBe(false); + }); + }); + }); + }); + + describe('prometheus is active', () => { + beforeEach(() => { + createComponent( + {}, + { + selectedIntegration: 'prometheus', + }, + ); + }); + + it('renders a valid "select"', () => { + expect(findSelect().exists()).toBe(true); + }); + + it('shows the API URL input', () => { + expect(findApiUrl().exists()).toBe(true); + }); + + it('shows the correct default API URL', () => { + expect(findUrl().attributes('value')).toBe( + defaultAlertSettingsConfig.prometheus.prometheusUrl, + ); + }); + }); + + describe('Opsgenie is active', () => { + beforeEach(() => { + createComponent( + {}, + { + selectedIntegration: 'opsgenie', + }, + ); + }); + + it('shows a input for the Opsgenie target URL', () => { + expect(findApiUrl().exists()).toBe(true); + }); + }); + + describe('trigger test alert', () => { + beforeEach(() => { + createComponent({}); + }); + + it('should enable the JSON input', () => { + expect(findJsonInput().exists()).toBe(true); + expect(findJsonInput().props('value')).toBe(null); + }); + + it('should validate JSON input', async () => { + createComponent(true, { + testAlertJson: '{ "value": "test" }', + }); + + findJsonInput().vm.$emit('change'); + + await wrapper.vm.$nextTick(); + + expect(findJsonInput().attributes('state')).toBe('true'); + }); + + describe('alert service is toggled', () => { + describe('error handling', () => { + const toggleService = true; + + it('should show generic error', async () => { + service.updateGenericActive.mockRejectedValueOnce({}); + + createComponent(); + + await wrapper.vm.toggleActivated(toggleService); + expect(wrapper.vm.active).toBe(false); + expect(wrapper.find(GlAlert).attributes('variant')).toBe('danger'); + expect(wrapper.find(GlAlert).text()).toBe(i18n.errorMsg); + }); + + it('should show first field specific error when available', async () => { + const err1 = "can't be blank"; + const err2 = 'is not a valid URL'; + const key = 'api_url'; + service.updateGenericActive.mockRejectedValueOnce({ + response: { data: { errors: { [key]: [err1, err2] } } }, + }); + + createComponent(); + + await wrapper.vm.toggleActivated(toggleService); + + expect(wrapper.find(GlAlert).text()).toContain(i18n.errorMsg); + expect(wrapper.find(GlAlert).text()).toContain(`${key} ${err1}`); + }); + }); + }); + }); +}); diff --git a/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js new file mode 100644 index 00000000000..74f3aa1d995 --- /dev/null +++ b/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js @@ -0,0 +1,48 @@ +import { shallowMount } from '@vue/test-utils'; +import AlertsSettingsWrapper from '~/alerts_settings/components/alerts_settings_wrapper.vue'; +import AlertsSettingsFormOld from '~/alerts_settings/components/alerts_settings_form_old.vue'; +import AlertsSettingsFormNew from '~/alerts_settings/components/alerts_settings_form_new.vue'; +import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue'; +import { defaultAlertSettingsConfig } from './util'; + +jest.mock('~/alerts_settings/services'); + +describe('AlertsSettingsFormWrapper', () => { + let wrapper; + + const createComponent = (data = {}, provide = {}) => { + wrapper = shallowMount(AlertsSettingsWrapper, { + data() { + return { ...data }; + }, + provide: { + ...defaultAlertSettingsConfig, + glFeatures: { httpIntegrationsList: false }, + ...provide, + }, + }); + }; + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + wrapper = null; + } + }); + + describe('with default values', () => { + it('renders alerts integrations list and old form by default', () => { + createComponent(); + expect(wrapper.find(IntegrationsList).exists()).toBe(true); + expect(wrapper.find(AlertsSettingsFormOld).exists()).toBe(true); + expect(wrapper.find(AlertsSettingsFormNew).exists()).toBe(false); + }); + + it('renders alerts integrations list and new form when httpIntegrationsList feature flag is enabled', () => { + createComponent({}, { glFeatures: { httpIntegrationsList: true } }); + expect(wrapper.find(IntegrationsList).exists()).toBe(true); + expect(wrapper.find(AlertsSettingsFormOld).exists()).toBe(false); + expect(wrapper.find(AlertsSettingsFormNew).exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/alerts_settings/util.js b/spec/frontend/alerts_settings/util.js new file mode 100644 index 00000000000..cbb98097b43 --- /dev/null +++ b/spec/frontend/alerts_settings/util.js @@ -0,0 +1,28 @@ +const PROMETHEUS_URL = '/prometheus/alerts/notify.json'; +const GENERIC_URL = '/alerts/notify.json'; +const KEY = 'abcedfg123'; +const INVALID_URL = 'http://invalid'; +const ACTIVATED = false; + +export const defaultAlertSettingsConfig = { + generic: { + authorizationKey: KEY, + formPath: INVALID_URL, + url: GENERIC_URL, + alertsSetupUrl: INVALID_URL, + alertsUsageUrl: INVALID_URL, + activated: ACTIVATED, + }, + prometheus: { + authorizationKey: KEY, + prometheusFormPath: INVALID_URL, + prometheusUrl: PROMETHEUS_URL, + activated: ACTIVATED, + }, + opsgenie: { + opsgenieMvcIsAvailable: true, + formPath: INVALID_URL, + activated: ACTIVATED, + opsgenieMvcTargetUrl: GENERIC_URL, + }, +}; |