diff options
Diffstat (limited to 'spec/frontend/integrations/edit/components/integration_form_spec.js')
-rw-r--r-- | spec/frontend/integrations/edit/components/integration_form_spec.js | 420 |
1 files changed, 129 insertions, 291 deletions
diff --git a/spec/frontend/integrations/edit/components/integration_form_spec.js b/spec/frontend/integrations/edit/components/integration_form_spec.js index 7e67379f5ab..4b49e492880 100644 --- a/spec/frontend/integrations/edit/components/integration_form_spec.js +++ b/spec/frontend/integrations/edit/components/integration_form_spec.js @@ -1,21 +1,20 @@ import { GlAlert, GlBadge, GlForm } from '@gitlab/ui'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; +import { nextTick } from 'vue'; import * as Sentry from '@sentry/browser'; import { setHTMLFixture } from 'helpers/fixtures'; import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import ActiveCheckbox from '~/integrations/edit/components/active_checkbox.vue'; -import ConfirmationModal from '~/integrations/edit/components/confirmation_modal.vue'; import DynamicField from '~/integrations/edit/components/dynamic_field.vue'; import IntegrationForm from '~/integrations/edit/components/integration_form.vue'; import OverrideDropdown from '~/integrations/edit/components/override_dropdown.vue'; -import ResetConfirmationModal from '~/integrations/edit/components/reset_confirmation_modal.vue'; import TriggerFields from '~/integrations/edit/components/trigger_fields.vue'; import IntegrationSectionConnection from '~/integrations/edit/components/sections/connection.vue'; +import IntegrationFormActions from '~/integrations/edit/components/integration_form_actions.vue'; import { - integrationLevels, I18N_SUCCESSFUL_CONNECTION_MESSAGE, I18N_DEFAULT_ERROR_MESSAGE, INTEGRATION_FORM_TYPE_SLACK, @@ -60,7 +59,6 @@ describe('IntegrationForm', () => { stubs: { OverrideDropdown, ActiveCheckbox, - ConfirmationModal, TriggerFields, }, mocks: { @@ -73,12 +71,6 @@ describe('IntegrationForm', () => { const findOverrideDropdown = () => wrapper.findComponent(OverrideDropdown); const findActiveCheckbox = () => wrapper.findComponent(ActiveCheckbox); - const findConfirmationModal = () => wrapper.findComponent(ConfirmationModal); - const findResetConfirmationModal = () => wrapper.findComponent(ResetConfirmationModal); - const findResetButton = () => wrapper.findByTestId('reset-button'); - const findProjectSaveButton = () => wrapper.findByTestId('save-button'); - const findInstanceOrGroupSaveButton = () => wrapper.findByTestId('save-button-instance-group'); - const findTestButton = () => wrapper.findByTestId('test-button'); const findTriggerFields = () => wrapper.findComponent(TriggerFields); const findAlert = () => wrapper.findComponent(GlAlert); const findGlBadge = () => wrapper.findComponent(GlBadge); @@ -91,6 +83,7 @@ describe('IntegrationForm', () => { const findConnectionSectionComponent = () => findConnectionSection().findComponent(IntegrationSectionConnection); const findHelpHtml = () => wrapper.findByTestId('help-html'); + const findFormActions = () => wrapper.findComponent(IntegrationFormActions); beforeEach(() => { mockAxios = new MockAdapter(axios); @@ -102,108 +95,6 @@ describe('IntegrationForm', () => { }); describe('template', () => { - describe('integrationLevel is instance', () => { - it('renders ConfirmationModal', () => { - createComponent({ - customStateProps: { - integrationLevel: integrationLevels.INSTANCE, - }, - }); - - expect(findConfirmationModal().exists()).toBe(true); - }); - - describe('resetPath is empty', () => { - it('does not render ResetConfirmationModal and button', () => { - createComponent({ - customStateProps: { - integrationLevel: integrationLevels.INSTANCE, - }, - }); - - expect(findResetButton().exists()).toBe(false); - expect(findResetConfirmationModal().exists()).toBe(false); - }); - }); - - describe('resetPath is present', () => { - it('renders ResetConfirmationModal and button', () => { - createComponent({ - customStateProps: { - integrationLevel: integrationLevels.INSTANCE, - resetPath: 'resetPath', - }, - }); - - expect(findResetButton().exists()).toBe(true); - expect(findResetConfirmationModal().exists()).toBe(true); - }); - }); - }); - - describe('integrationLevel is group', () => { - it('renders ConfirmationModal', () => { - createComponent({ - customStateProps: { - integrationLevel: integrationLevels.GROUP, - }, - }); - - expect(findConfirmationModal().exists()).toBe(true); - }); - - describe('resetPath is empty', () => { - it('does not render ResetConfirmationModal and button', () => { - createComponent({ - customStateProps: { - integrationLevel: integrationLevels.GROUP, - }, - }); - - expect(findResetButton().exists()).toBe(false); - expect(findResetConfirmationModal().exists()).toBe(false); - }); - }); - - describe('resetPath is present', () => { - it('renders ResetConfirmationModal and button', () => { - createComponent({ - customStateProps: { - integrationLevel: integrationLevels.GROUP, - resetPath: 'resetPath', - }, - }); - - expect(findResetButton().exists()).toBe(true); - expect(findResetConfirmationModal().exists()).toBe(true); - }); - }); - }); - - describe('integrationLevel is project', () => { - it('does not render ConfirmationModal', () => { - createComponent({ - customStateProps: { - integrationLevel: 'project', - }, - }); - - expect(findConfirmationModal().exists()).toBe(false); - }); - - it('does not render ResetConfirmationModal and button', () => { - createComponent({ - customStateProps: { - integrationLevel: 'project', - resetPath: 'resetPath', - }, - }); - - expect(findResetButton().exists()).toBe(false); - expect(findResetConfirmationModal().exists()).toBe(false); - }); - }); - describe('triggerEvents is present', () => { it('renders TriggerFields', () => { const events = [{ title: 'push' }]; @@ -462,111 +353,85 @@ describe('IntegrationForm', () => { ); }); - describe('when `save` button is clicked', () => { - describe('buttons', () => { - beforeEach(async () => { - createComponent({ - customStateProps: { - showActive: true, - canTest: true, - initialActivated: true, - }, - mountFn: mountExtended, - }); - - await findProjectSaveButton().vm.$emit('click', new Event('click')); - }); - - it('sets save button `loading` prop to `true`', () => { - expect(findProjectSaveButton().props('loading')).toBe(true); + describe('Response to the "save" event (form submission)', () => { + const prepareComponentAndSave = async (initialActivated = true, checkValidityReturn) => { + createComponent({ + customStateProps: { + showActive: true, + initialActivated, + fields: [mockField], + }, + mountFn: mountExtended, }); + jest.spyOn(findGlForm().element, 'submit'); + jest.spyOn(findGlForm().element, 'checkValidity').mockReturnValue(checkValidityReturn); - it('sets test button `disabled` prop to `true`', () => { - expect(findTestButton().props('disabled')).toBe(true); - }); - }); + findFormActions().vm.$emit('save'); + await nextTick(); + }; - describe.each` - checkValidityReturn | integrationActive - ${true} | ${false} - ${true} | ${true} - ${false} | ${false} + it.each` + desc | checkValidityReturn | integrationActive | shouldSubmit + ${'form is valid'} | ${true} | ${false} | ${true} + ${'form is valid'} | ${true} | ${true} | ${true} + ${'form is invalid'} | ${false} | ${false} | ${true} + ${'form is invalid'} | ${false} | ${true} | ${false} `( - 'when form is valid (checkValidity returns $checkValidityReturn and integrationActive is $integrationActive)', - ({ integrationActive, checkValidityReturn }) => { - beforeEach(async () => { - createComponent({ - customStateProps: { - showActive: true, - canTest: true, - initialActivated: integrationActive, - }, - mountFn: mountExtended, - }); - jest.spyOn(findGlForm().element, 'submit'); - jest.spyOn(findGlForm().element, 'checkValidity').mockReturnValue(checkValidityReturn); - - await findProjectSaveButton().vm.$emit('click', new Event('click')); - }); + 'when $desc (checkValidity returns $checkValidityReturn and integrationActive is $integrationActive)', + async ({ integrationActive, checkValidityReturn, shouldSubmit }) => { + await prepareComponentAndSave(integrationActive, checkValidityReturn); - it('submit form', () => { + if (shouldSubmit) { expect(findGlForm().element.submit).toHaveBeenCalledTimes(1); - }); + } else { + expect(findGlForm().element.submit).not.toHaveBeenCalled(); + } }, ); - describe('when form is invalid (checkValidity returns false and integrationActive is true)', () => { - beforeEach(async () => { - createComponent({ - customStateProps: { - showActive: true, - canTest: true, - initialActivated: true, - fields: [mockField], - }, - mountFn: mountExtended, - }); - jest.spyOn(findGlForm().element, 'submit'); - jest.spyOn(findGlForm().element, 'checkValidity').mockReturnValue(false); - - await findProjectSaveButton().vm.$emit('click', new Event('click')); - }); - - it('does not submit form', () => { - expect(findGlForm().element.submit).not.toHaveBeenCalled(); - }); + it('flips `isSaving` to `true`', async () => { + await prepareComponentAndSave(true, true); + expect(findFormActions().props('isSaving')).toBe(true); + }); - it('sets save button `loading` prop to `false`', () => { - expect(findProjectSaveButton().props('loading')).toBe(false); + describe('when form is invalid', () => { + beforeEach(async () => { + await prepareComponentAndSave(true, false); }); - it('sets test button `disabled` prop to `false`', () => { - expect(findTestButton().props('disabled')).toBe(false); + it('when form is invalid, it sets `isValidated` props on form fields', () => { + expect(findDynamicField().props('isValidated')).toBe(true); }); - it('sets `isValidated` props on form fields', () => { - expect(findDynamicField().props('isValidated')).toBe(true); + it('resets `isSaving`', () => { + expect(findFormActions().props('isSaving')).toBe(false); }); }); }); - describe('when `test` button is clicked', () => { + describe('Response to the "test" event from the actions', () => { describe('when form is invalid', () => { - it('sets `isValidated` props on form fields', async () => { + beforeEach(async () => { createComponent({ customStateProps: { showActive: true, - canTest: true, fields: [mockField], }, mountFn: mountExtended, }); jest.spyOn(findGlForm().element, 'checkValidity').mockReturnValue(false); - await findTestButton().vm.$emit('click', new Event('click')); + findFormActions().vm.$emit('test'); + await nextTick(); + }); + it('sets `isValidated` props on form fields', () => { expect(findDynamicField().props('isValidated')).toBe(true); }); + + it('resets `isTesting`', () => { + expect(findFormActions().props('isTesting')).toBe(false); + }); }); describe('when form is valid', () => { @@ -576,26 +441,18 @@ describe('IntegrationForm', () => { createComponent({ customStateProps: { showActive: true, - canTest: true, testPath: mockTestPath, }, mountFn: mountExtended, }); + jest.spyOn(findGlForm().element, 'checkValidity').mockReturnValue(true); }); - describe('buttons', () => { - beforeEach(async () => { - await findTestButton().vm.$emit('click', new Event('click')); - }); - - it('sets test button `loading` prop to `true`', () => { - expect(findTestButton().props('loading')).toBe(true); - }); - - it('sets save button `disabled` prop to `true`', () => { - expect(findProjectSaveButton().props('disabled')).toBe(true); - }); + it('flips `isTesting` to `true`', async () => { + findFormActions().vm.$emit('test'); + await nextTick(); + expect(findFormActions().props('isTesting')).toBe(true); }); describe.each` @@ -614,7 +471,7 @@ describe('IntegrationForm', () => { service_response: serviceResponse, }); - await findTestButton().vm.$emit('click', new Event('click')); + findFormActions().vm.$emit('test'); await waitForPromises(); }); @@ -622,14 +479,6 @@ describe('IntegrationForm', () => { expect(mockToastShow).toHaveBeenCalledWith(expectToast); }); - it('sets `loading` prop of test button to `false`', () => { - expect(findTestButton().props('loading')).toBe(false); - }); - - it('sets save button `disabled` prop to `false`', () => { - expect(findProjectSaveButton().props('disabled')).toBe(false); - }); - it(`${expectSentry ? 'does' : 'does not'} capture exception in Sentry`, () => { expect(Sentry.captureException).toHaveBeenCalledTimes(expectSentry ? 1 : 0); }); @@ -638,44 +487,27 @@ describe('IntegrationForm', () => { }); }); - describe('when `reset-confirmation-modal` emits `reset` event', () => { + describe('Response to the "reset" event from the actions', () => { const mockResetPath = '/reset'; - describe('buttons', () => { - beforeEach(async () => { - createComponent({ - customStateProps: { - integrationLevel: integrationLevels.GROUP, - canTest: true, - resetPath: mockResetPath, - }, - }); - - await findResetConfirmationModal().vm.$emit('reset'); + beforeEach(async () => { + mockAxios.onPost(mockResetPath).replyOnce(httpStatus.INTERNAL_SERVER_ERROR); + createComponent({ + customStateProps: { + resetPath: mockResetPath, + }, }); - it('sets reset button `loading` prop to `true`', () => { - expect(findResetButton().props('loading')).toBe(true); - }); + findFormActions().vm.$emit('reset'); + await nextTick(); + }); - it('sets other button `disabled` props to `true`', () => { - expect(findInstanceOrGroupSaveButton().props('disabled')).toBe(true); - expect(findTestButton().props('disabled')).toBe(true); - }); + it('flips `isResetting` to `true`', () => { + expect(findFormActions().props('isResetting')).toBe(true); }); describe('when "reset settings" request fails', () => { beforeEach(async () => { - mockAxios.onPost(mockResetPath).replyOnce(httpStatus.INTERNAL_SERVER_ERROR); - createComponent({ - customStateProps: { - integrationLevel: integrationLevels.GROUP, - canTest: true, - resetPath: mockResetPath, - }, - }); - - await findResetConfirmationModal().vm.$emit('reset'); await waitForPromises(); }); @@ -687,13 +519,8 @@ describe('IntegrationForm', () => { expect(Sentry.captureException).toHaveBeenCalledTimes(1); }); - it('sets reset button `loading` prop to `false`', () => { - expect(findResetButton().props('loading')).toBe(false); - }); - - it('sets button `disabled` props to `false`', () => { - expect(findInstanceOrGroupSaveButton().props('disabled')).toBe(false); - expect(findTestButton().props('disabled')).toBe(false); + it('resets `isResetting`', () => { + expect(findFormActions().props('isResetting')).toBe(false); }); }); @@ -702,64 +529,74 @@ describe('IntegrationForm', () => { mockAxios.onPost(mockResetPath).replyOnce(httpStatus.OK); createComponent({ customStateProps: { - integrationLevel: integrationLevels.GROUP, resetPath: mockResetPath, }, }); - await findResetConfirmationModal().vm.$emit('reset'); + findFormActions().vm.$emit('reset'); await waitForPromises(); }); it('calls `refreshCurrentPage`', () => { expect(refreshCurrentPage).toHaveBeenCalledTimes(1); }); - }); - describe('Slack integration', () => { - describe('Help and sections rendering', () => { - const dummyHelp = 'Foo Help'; - - it.each` - integration | flagIsOn | helpHtml | sections | shouldShowSections | shouldShowHelp - ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${''} | ${[]} | ${false} | ${false} - ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${dummyHelp} | ${[]} | ${false} | ${true} - ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${undefined} | ${[mockSectionConnection]} | ${false} | ${false} - ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${dummyHelp} | ${[mockSectionConnection]} | ${false} | ${true} - ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${''} | ${[]} | ${false} | ${false} - ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${dummyHelp} | ${[]} | ${false} | ${true} - ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false} - ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${true} - ${'foo'} | ${false} | ${''} | ${[]} | ${false} | ${false} - ${'foo'} | ${false} | ${dummyHelp} | ${[]} | ${false} | ${true} - ${'foo'} | ${false} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false} - ${'foo'} | ${false} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${false} - ${'foo'} | ${true} | ${''} | ${[]} | ${false} | ${false} - ${'foo'} | ${true} | ${dummyHelp} | ${[]} | ${false} | ${true} - ${'foo'} | ${true} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false} - ${'foo'} | ${true} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${false} - `( - '$sections sections, and "$helpHtml" helpHtml when the FF is "$flagIsOn" for "$integration" integration', - ({ integration, flagIsOn, helpHtml, sections, shouldShowSections, shouldShowHelp }) => { - createComponent({ - provide: { - helpHtml, - glFeatures: { integrationSlackAppNotifications: flagIsOn }, - }, - customStateProps: { - sections, - type: integration, - }, - }); - expect(findAllSections().length > 0).toEqual(shouldShowSections); - expect(findHelpHtml().exists()).toBe(shouldShowHelp); - if (shouldShowHelp) { - expect(findHelpHtml().html()).toContain(helpHtml); - } - }, - ); + it('resets `isResetting`', async () => { + expect(findFormActions().props('isResetting')).toBe(false); }); + }); + }); + + describe('Slack integration', () => { + describe('Help and sections rendering', () => { + const dummyHelp = 'Foo Help'; + + it.each` + integration | flagIsOn | helpHtml | sections | shouldShowSections | shouldShowHelp + ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${''} | ${[]} | ${false} | ${false} + ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${dummyHelp} | ${[]} | ${false} | ${true} + ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${undefined} | ${[mockSectionConnection]} | ${false} | ${false} + ${INTEGRATION_FORM_TYPE_SLACK} | ${false} | ${dummyHelp} | ${[mockSectionConnection]} | ${false} | ${true} + ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${''} | ${[]} | ${false} | ${false} + ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${dummyHelp} | ${[]} | ${false} | ${true} + ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false} + ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${true} + ${'foo'} | ${false} | ${''} | ${[]} | ${false} | ${false} + ${'foo'} | ${false} | ${dummyHelp} | ${[]} | ${false} | ${true} + ${'foo'} | ${false} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false} + ${'foo'} | ${false} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${false} + ${'foo'} | ${true} | ${''} | ${[]} | ${false} | ${false} + ${'foo'} | ${true} | ${dummyHelp} | ${[]} | ${false} | ${true} + ${'foo'} | ${true} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false} + ${'foo'} | ${true} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${false} + `( + '$sections sections, and "$helpHtml" helpHtml when the FF is "$flagIsOn" for "$integration" integration', + ({ integration, flagIsOn, helpHtml, sections, shouldShowSections, shouldShowHelp }) => { + createComponent({ + provide: { + helpHtml, + glFeatures: { integrationSlackAppNotifications: flagIsOn }, + }, + customStateProps: { + sections, + type: integration, + }, + }); + expect(findAllSections().length > 0).toEqual(shouldShowSections); + expect(findHelpHtml().exists()).toBe(shouldShowHelp); + if (shouldShowHelp) { + expect(findHelpHtml().html()).toContain(helpHtml); + } + }, + ); + }); + describe.each` + hasSections | hasFieldsWithoutSections | description + ${true} | ${true} | ${'When having both: the sections and the fields without a section'} + ${true} | ${false} | ${'When having the sections only'} + ${false} | ${true} | ${'When having only the fields without a section'} + `('$description', ({ hasSections, hasFieldsWithoutSections }) => { it.each` prefix | integration | shouldUpgradeSlack | flagIsOn | shouldShowAlert ${'does'} | ${INTEGRATION_FORM_TYPE_SLACK} | ${true} | ${true} | ${true} @@ -769,7 +606,7 @@ describe('IntegrationForm', () => { ${'does not'} | ${'foo'} | ${false} | ${true} | ${false} ${'does not'} | ${'foo'} | ${true} | ${false} | ${false} `( - '$prefix render the upgrade warnning when we are in "$integration" integration with the flag "$flagIsOn" and Slack-needs-upgrade is "$shouldUpgradeSlack"', + '$prefix render the upgrade warning when we are in "$integration" integration with the flag "$flagIsOn" and Slack-needs-upgrade is "$shouldUpgradeSlack" and have sections', ({ integration, shouldUpgradeSlack, flagIsOn, shouldShowAlert }) => { createComponent({ provide: { @@ -778,7 +615,8 @@ describe('IntegrationForm', () => { customStateProps: { shouldUpgradeSlack, type: integration, - sections: [mockSectionConnection], + sections: hasSections ? [mockSectionConnection] : [], + fields: hasFieldsWithoutSections ? [mockField] : [], }, }); expect(findAlert().exists()).toBe(shouldShowAlert); |