diff options
Diffstat (limited to 'spec/frontend/alerts_settings/components/alerts_settings_form_spec.js')
-rw-r--r-- | spec/frontend/alerts_settings/components/alerts_settings_form_spec.js | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js new file mode 100644 index 00000000000..02229b3d3da --- /dev/null +++ b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js @@ -0,0 +1,385 @@ +import { + GlForm, + GlFormSelect, + GlCollapse, + GlFormInput, + GlToggle, + GlFormTextarea, +} from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import waitForPromises from 'helpers/wait_for_promises'; +import MappingBuilder from '~/alerts_settings/components/alert_mapping_builder.vue'; +import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form.vue'; +import { typeSet } from '~/alerts_settings/constants'; +import alertFields from '../mocks/alertFields.json'; +import { defaultAlertSettingsConfig } from './util'; + +describe('AlertsSettingsForm', () => { + let wrapper; + const mockToastShow = jest.fn(); + + const createComponent = ({ + data = {}, + props = {}, + multipleHttpIntegrationsCustomMapping = false, + multiIntegrations = true, + } = {}) => { + wrapper = mount(AlertsSettingsForm, { + data() { + return { ...data }; + }, + propsData: { + loading: false, + canAddIntegration: true, + ...props, + }, + provide: { + ...defaultAlertSettingsConfig, + glFeatures: { multipleHttpIntegrationsCustomMapping }, + multiIntegrations, + }, + mocks: { + $toast: { + show: mockToastShow, + }, + }, + }); + }; + + const findForm = () => wrapper.find(GlForm); + const findSelect = () => wrapper.find(GlFormSelect); + const findFormSteps = () => wrapper.find(GlCollapse); + const findFormFields = () => wrapper.findAll(GlFormInput); + const findFormToggle = () => wrapper.find(GlToggle); + const findTestPayloadSection = () => wrapper.find(`[id = "test-integration"]`); + const findMappingBuilderSection = () => wrapper.find(`[id = "mapping-builder"]`); + const findMappingBuilder = () => wrapper.findComponent(MappingBuilder); + const findSubmitButton = () => wrapper.find(`[type = "submit"]`); + const findMultiSupportText = () => + wrapper.find(`[data-testid="multi-integrations-not-supported"]`); + const findJsonTestSubmit = () => wrapper.find(`[data-testid="integration-test-and-submit"]`); + const findJsonTextArea = () => wrapper.find(`[id = "test-payload"]`); + const findActionBtn = () => wrapper.find(`[data-testid="payload-action-btn"]`); + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + wrapper = null; + } + }); + + const selectOptionAtIndex = async (index) => { + const options = findSelect().findAll('option'); + await options.at(index).setSelected(); + }; + + const enableIntegration = (index, value) => { + findFormFields().at(index).setValue(value); + findFormToggle().trigger('click'); + }; + + describe('with default values', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders the initial template', () => { + expect(wrapper.element).toMatchSnapshot(); + }); + + it('render the initial form with only an integration type dropdown', () => { + expect(findForm().exists()).toBe(true); + expect(findSelect().exists()).toBe(true); + expect(findMultiSupportText().exists()).toBe(false); + expect(findFormSteps().attributes('visible')).toBeUndefined(); + }); + + it('shows the rest of the form when the dropdown is used', async () => { + await selectOptionAtIndex(1); + + expect(findFormFields().at(0).isVisible()).toBe(true); + }); + + it('disables the dropdown and shows help text when multi integrations are not supported', async () => { + createComponent({ props: { canAddIntegration: false } }); + expect(findSelect().attributes('disabled')).toBe('disabled'); + expect(findMultiSupportText().exists()).toBe(true); + }); + + it('disabled the name input when the selected value is prometheus', async () => { + createComponent(); + await selectOptionAtIndex(2); + + expect(findFormFields().at(0).attributes('disabled')).toBe('disabled'); + }); + }); + + describe('submitting integration form', () => { + describe('HTTP', () => { + it('create', async () => { + createComponent(); + + const integrationName = 'Test integration'; + await selectOptionAtIndex(1); + enableIntegration(0, integrationName); + + const submitBtn = findSubmitButton(); + expect(submitBtn.exists()).toBe(true); + expect(submitBtn.text()).toBe('Save integration'); + + findForm().trigger('submit'); + + expect(wrapper.emitted('create-new-integration')[0]).toEqual([ + { type: typeSet.http, variables: { name: integrationName, active: true } }, + ]); + }); + + it('create with custom mapping', async () => { + createComponent({ + multipleHttpIntegrationsCustomMapping: true, + multiIntegrations: true, + props: { alertFields }, + }); + + const integrationName = 'Test integration'; + await selectOptionAtIndex(1); + + enableIntegration(0, integrationName); + + const sampleMapping = { field: 'test' }; + findMappingBuilder().vm.$emit('onMappingUpdate', sampleMapping); + findForm().trigger('submit'); + + expect(wrapper.emitted('create-new-integration')[0]).toEqual([ + { + type: typeSet.http, + variables: { + name: integrationName, + active: true, + payloadAttributeMappings: sampleMapping, + payloadExample: null, + }, + }, + ]); + }); + + it('update', () => { + createComponent({ + data: { + selectedIntegration: typeSet.http, + currentIntegration: { id: '1', name: 'Test integration pre' }, + }, + props: { + loading: false, + }, + }); + const updatedIntegrationName = 'Test integration post'; + enableIntegration(0, updatedIntegrationName); + + const submitBtn = findSubmitButton(); + expect(submitBtn.exists()).toBe(true); + expect(submitBtn.text()).toBe('Save integration'); + + findForm().trigger('submit'); + + expect(wrapper.emitted('update-integration')[0]).toEqual([ + { type: typeSet.http, variables: { name: updatedIntegrationName, active: true } }, + ]); + }); + }); + + describe('PROMETHEUS', () => { + it('create', async () => { + createComponent(); + + await selectOptionAtIndex(2); + + const apiUrl = 'https://test.com'; + enableIntegration(1, apiUrl); + + findFormToggle().trigger('click'); + + const submitBtn = findSubmitButton(); + expect(submitBtn.exists()).toBe(true); + expect(submitBtn.text()).toBe('Save integration'); + + findForm().trigger('submit'); + + expect(wrapper.emitted('create-new-integration')[0]).toEqual([ + { type: typeSet.prometheus, variables: { apiUrl, active: true } }, + ]); + }); + + it('update', () => { + createComponent({ + data: { + selectedIntegration: typeSet.prometheus, + currentIntegration: { id: '1', apiUrl: 'https://test-pre.com' }, + }, + props: { + loading: false, + }, + }); + + const apiUrl = 'https://test-post.com'; + enableIntegration(1, apiUrl); + + const submitBtn = findSubmitButton(); + expect(submitBtn.exists()).toBe(true); + expect(submitBtn.text()).toBe('Save integration'); + + findForm().trigger('submit'); + + expect(wrapper.emitted('update-integration')[0]).toEqual([ + { type: typeSet.prometheus, variables: { apiUrl, active: true } }, + ]); + }); + }); + }); + + describe('submitting the integration with a JSON test payload', () => { + beforeEach(() => { + createComponent({ + data: { + selectedIntegration: typeSet.http, + currentIntegration: { id: '1', name: 'Test' }, + active: true, + }, + props: { + loading: false, + }, + }); + }); + + it('should not allow a user to test invalid JSON', async () => { + jest.useFakeTimers(); + await findJsonTextArea().setValue('Invalid JSON'); + + jest.runAllTimers(); + await wrapper.vm.$nextTick(); + + const jsonTestSubmit = findJsonTestSubmit(); + expect(jsonTestSubmit.exists()).toBe(true); + expect(jsonTestSubmit.text()).toBe('Save and test payload'); + expect(jsonTestSubmit.props('disabled')).toBe(true); + }); + + it('should allow for the form to be automatically saved if the test payload is successfully submitted', async () => { + jest.useFakeTimers(); + await findJsonTextArea().setValue('{ "value": "value" }'); + + jest.runAllTimers(); + await wrapper.vm.$nextTick(); + expect(findJsonTestSubmit().props('disabled')).toBe(false); + }); + }); + + describe('Test payload section for HTTP integration', () => { + beforeEach(() => { + createComponent({ + multipleHttpIntegrationsCustomMapping: true, + props: { + currentIntegration: { + type: typeSet.http, + }, + alertFields, + }, + }); + }); + + describe.each` + active | resetSamplePayloadConfirmed | disabled + ${true} | ${true} | ${undefined} + ${false} | ${true} | ${'disabled'} + ${true} | ${false} | ${'disabled'} + ${false} | ${false} | ${'disabled'} + `('', ({ active, resetSamplePayloadConfirmed, disabled }) => { + const payloadResetMsg = resetSamplePayloadConfirmed ? 'was confirmed' : 'was not confirmed'; + const enabledState = disabled === 'disabled' ? 'disabled' : 'enabled'; + const activeState = active ? 'active' : 'not active'; + + it(`textarea should be ${enabledState} when payload reset ${payloadResetMsg} and current integration is ${activeState}`, async () => { + wrapper.setData({ + customMapping: { samplePayload: true }, + active, + resetSamplePayloadConfirmed, + }); + await wrapper.vm.$nextTick(); + expect(findTestPayloadSection().find(GlFormTextarea).attributes('disabled')).toBe(disabled); + }); + }); + + describe('action buttons for sample payload', () => { + describe.each` + resetSamplePayloadConfirmed | samplePayload | caption + ${false} | ${true} | ${'Edit payload'} + ${true} | ${false} | ${'Submit payload'} + ${true} | ${true} | ${'Submit payload'} + ${false} | ${false} | ${'Submit payload'} + `('', ({ resetSamplePayloadConfirmed, samplePayload, caption }) => { + const samplePayloadMsg = samplePayload ? 'was provided' : 'was not provided'; + const payloadResetMsg = resetSamplePayloadConfirmed ? 'was confirmed' : 'was not confirmed'; + + it(`shows ${caption} button when sample payload ${samplePayloadMsg} and payload reset ${payloadResetMsg}`, async () => { + wrapper.setData({ + selectedIntegration: typeSet.http, + customMapping: { samplePayload }, + resetSamplePayloadConfirmed, + }); + await wrapper.vm.$nextTick(); + expect(findActionBtn().text()).toBe(caption); + }); + }); + }); + + describe('Parsing payload', () => { + it('displays a toast message on successful parse', async () => { + jest.useFakeTimers(); + wrapper.setData({ + selectedIntegration: typeSet.http, + customMapping: { samplePayload: false }, + }); + await wrapper.vm.$nextTick(); + + findActionBtn().vm.$emit('click'); + jest.advanceTimersByTime(1000); + + await waitForPromises(); + + expect(mockToastShow).toHaveBeenCalledWith( + 'Sample payload has been parsed. You can now map the fields.', + ); + }); + }); + }); + + describe('Mapping builder section', () => { + describe.each` + alertFieldsProvided | multiIntegrations | featureFlag | integrationOption | visible + ${true} | ${true} | ${true} | ${1} | ${true} + ${true} | ${true} | ${true} | ${2} | ${false} + ${true} | ${true} | ${false} | ${1} | ${false} + ${true} | ${true} | ${false} | ${2} | ${false} + ${true} | ${false} | ${true} | ${1} | ${false} + ${false} | ${true} | ${true} | ${1} | ${false} + `('', ({ alertFieldsProvided, multiIntegrations, featureFlag, integrationOption, visible }) => { + const visibleMsg = visible ? 'is rendered' : 'is not rendered'; + const featureFlagMsg = featureFlag ? 'is enabled' : 'is disabled'; + const alertFieldsMsg = alertFieldsProvided ? 'are provided' : 'are not provided'; + const integrationType = integrationOption === 1 ? typeSet.http : typeSet.prometheus; + + it(`${visibleMsg} when multipleHttpIntegrationsCustomMapping feature flag ${featureFlagMsg} and integration type is ${integrationType} and alert fields ${alertFieldsMsg}`, async () => { + createComponent({ + multipleHttpIntegrationsCustomMapping: featureFlag, + multiIntegrations, + props: { + alertFields: alertFieldsProvided ? alertFields : [], + }, + }); + await selectOptionAtIndex(integrationOption); + + expect(findMappingBuilderSection().exists()).toBe(visible); + }); + }); + }); +}); |