diff options
Diffstat (limited to 'spec/frontend/integrations/edit')
5 files changed, 285 insertions, 204 deletions
diff --git a/spec/frontend/integrations/edit/components/dynamic_field_spec.js b/spec/frontend/integrations/edit/components/dynamic_field_spec.js index bf044e388ea..b0fb94d2b29 100644 --- a/spec/frontend/integrations/edit/components/dynamic_field_spec.js +++ b/spec/frontend/integrations/edit/components/dynamic_field_spec.js @@ -61,7 +61,7 @@ describe('DynamicField', () => { }); it(`renders GlFormCheckbox with correct text content when checkboxLabel is ${checkboxLabel}`, () => { - expect(findGlFormCheckbox().text()).toBe(checkboxLabel ?? defaultProps.title); + expect(findGlFormCheckbox().text()).toContain(checkboxLabel ?? defaultProps.title); }); it('does not render other types of input', () => { @@ -182,6 +182,17 @@ describe('DynamicField', () => { expect(findGlFormGroup().find('small').text()).toBe(defaultProps.help); }); + describe('when type is checkbox', () => { + it('renders description with help text', () => { + createComponent({ + type: 'checkbox', + }); + + expect(findGlFormGroup().find('small').exists()).toBe(false); + expect(findGlFormCheckbox().text()).toContain(defaultProps.help); + }); + }); + it('renders description with help text as HTML', () => { const helpHTML = 'The <strong>URL</strong> of the project'; diff --git a/spec/frontend/integrations/edit/components/integration_form_spec.js b/spec/frontend/integrations/edit/components/integration_form_spec.js index 4c1394f3a87..8cf8a403e5d 100644 --- a/spec/frontend/integrations/edit/components/integration_form_spec.js +++ b/spec/frontend/integrations/edit/components/integration_form_spec.js @@ -1,9 +1,10 @@ +import { GlForm } from '@gitlab/ui'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import * as Sentry from '@sentry/browser'; import { setHTMLFixture } from 'helpers/fixtures'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import { mockIntegrationProps } from 'jest/integrations/edit/mock_data'; +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'; @@ -13,7 +14,6 @@ import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_field 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 waitForPromises from 'helpers/wait_for_promises'; import { integrationLevels, I18N_SUCCESSFUL_CONNECTION_MESSAGE, @@ -23,9 +23,12 @@ import { import { createStore } from '~/integrations/edit/store'; import eventHub from '~/integrations/edit/event_hub'; import httpStatus from '~/lib/utils/http_status'; +import { refreshCurrentPage } from '~/lib/utils/url_utility'; +import { mockIntegrationProps } from '../mock_data'; jest.mock('~/integrations/edit/event_hub'); jest.mock('@sentry/browser'); +jest.mock('~/lib/utils/url_utility'); describe('IntegrationForm', () => { const mockToastShow = jest.fn(); @@ -34,12 +37,18 @@ describe('IntegrationForm', () => { let dispatch; let mockAxios; let mockForm; + let vueIntegrationFormFeatureFlag; + + const createForm = () => { + mockForm = document.createElement('form'); + jest.spyOn(document, 'querySelector').mockReturnValue(mockForm); + }; const createComponent = ({ customStateProps = {}, - featureFlags = {}, initialState = {}, props = {}, + mountFn = shallowMountExtended, } = {}) => { const store = createStore({ customState: { ...mockIntegrationProps, ...customStateProps }, @@ -47,11 +56,12 @@ describe('IntegrationForm', () => { }); dispatch = jest.spyOn(store, 'dispatch').mockImplementation(); - wrapper = shallowMountExtended(IntegrationForm, { - propsData: { ...props, formSelector: '.test' }, - provide: { - glFeatures: featureFlags, - }, + if (!vueIntegrationFormFeatureFlag) { + createForm(); + } + + wrapper = mountFn(IntegrationForm, { + propsData: { ...props }, store, stubs: { OverrideDropdown, @@ -65,26 +75,33 @@ describe('IntegrationForm', () => { show: mockToastShow, }, }, + provide: { + glFeatures: { + vueIntegrationForm: vueIntegrationFormFeatureFlag, + }, + }, }); }; - const createForm = ({ isValid = true } = {}) => { - mockForm = document.createElement('form'); - jest.spyOn(document, 'querySelector').mockReturnValue(mockForm); - jest.spyOn(mockForm, 'checkValidity').mockReturnValue(isValid); - jest.spyOn(mockForm, 'submit'); - }; - 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 findSaveButton = () => wrapper.findByTestId('save-button'); + const findProjectSaveButton = () => wrapper.findByTestId('save-button'); + const findInstanceOrGroupSaveButton = () => wrapper.findByTestId('save-button-instance-group'); const findTestButton = () => wrapper.findByTestId('test-button'); const findJiraTriggerFields = () => wrapper.findComponent(JiraTriggerFields); const findJiraIssuesFields = () => wrapper.findComponent(JiraIssuesFields); const findTriggerFields = () => wrapper.findComponent(TriggerFields); + const findGlForm = () => wrapper.findComponent(GlForm); + const findRedirectToField = () => wrapper.findByTestId('redirect-to-field'); + const findFormElement = () => (vueIntegrationFormFeatureFlag ? findGlForm().element : mockForm); + + const mockFormFunctions = ({ checkValidityReturn }) => { + jest.spyOn(findFormElement(), 'checkValidity').mockReturnValue(checkValidityReturn); + jest.spyOn(findFormElement(), 'submit'); + }; beforeEach(() => { mockAxios = new MockAdapter(axios); @@ -220,6 +237,7 @@ describe('IntegrationForm', () => { createComponent({ customStateProps: { type: 'jira', testPath: '/test' }, + mountFn: mountExtended, }); }); @@ -338,6 +356,19 @@ describe('IntegrationForm', () => { }); }); }); + + describe('when `vueIntegrationForm` feature flag is $vueIntegrationFormEnabled', () => { + it('renders hidden fields', () => { + vueIntegrationFormFeatureFlag = true; + createComponent({ + customStateProps: { + redirectTo: '/services', + }, + }); + + expect(findRedirectToField().attributes('value')).toBe('/services'); + }); + }); }); describe('ActiveCheckbox', () => { @@ -358,190 +389,292 @@ describe('IntegrationForm', () => { }); describe.each` - formActive | novalidate - ${true} | ${null} - ${false} | ${'true'} + formActive | vueIntegrationFormEnabled | novalidate + ${true} | ${true} | ${null} + ${false} | ${true} | ${'novalidate'} + ${true} | ${false} | ${null} + ${false} | ${false} | ${'true'} `( - 'when `toggle-integration-active` is emitted with $formActive', - ({ formActive, novalidate }) => { + 'when `vueIntegrationForm` feature flag is $vueIntegrationFormEnabled and `toggle-integration-active` is emitted with $formActive', + ({ formActive, vueIntegrationFormEnabled, novalidate }) => { beforeEach(async () => { - createForm(); + vueIntegrationFormFeatureFlag = vueIntegrationFormEnabled; + createComponent({ customStateProps: { showActive: true, initialActivated: false, }, + mountFn: mountExtended, }); + mockFormFunctions({ checkValidityReturn: false }); await findActiveCheckbox().vm.$emit('toggle-integration-active', formActive); }); it(`sets noValidate to ${novalidate}`, () => { - expect(mockForm.getAttribute('novalidate')).toBe(novalidate); + expect(findFormElement().getAttribute('novalidate')).toBe(novalidate); }); }, ); }); - describe('when `save` button is clicked', () => { - describe('buttons', () => { - beforeEach(async () => { - createForm(); - createComponent({ - customStateProps: { - showActive: true, - canTest: true, - initialActivated: true, - }, + describe.each` + vueIntegrationFormEnabled + ${true} + ${false} + `( + 'when `vueIntegrationForm` feature flag is $vueIntegrationFormEnabled', + ({ vueIntegrationFormEnabled }) => { + beforeEach(() => { + vueIntegrationFormFeatureFlag = vueIntegrationFormEnabled; + }); + + 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); + }); + + it('sets test button `disabled` prop to `true`', () => { + expect(findTestButton().props('disabled')).toBe(true); + }); }); - await findSaveButton().vm.$emit('click', new Event('click')); - }); + describe.each` + checkValidityReturn | integrationActive + ${true} | ${false} + ${true} | ${true} + ${false} | ${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, + }); + + mockFormFunctions({ checkValidityReturn }); + + await findProjectSaveButton().vm.$emit('click', new Event('click')); + }); + + it('submits form', () => { + expect(findFormElement().submit).toHaveBeenCalledTimes(1); + }); + }, + ); + + describe('when form is invalid (checkValidity returns false and integrationActive is true)', () => { + beforeEach(async () => { + createComponent({ + customStateProps: { + showActive: true, + canTest: true, + initialActivated: true, + }, + mountFn: mountExtended, + }); + mockFormFunctions({ checkValidityReturn: false }); + + await findProjectSaveButton().vm.$emit('click', new Event('click')); + }); - it('sets save button `loading` prop to `true`', () => { - expect(findSaveButton().props('loading')).toBe(true); - }); + it('does not submit form', () => { + expect(findFormElement().submit).not.toHaveBeenCalled(); + }); - it('sets test button `disabled` prop to `true`', () => { - expect(findTestButton().props('disabled')).toBe(true); - }); - }); + it('sets save button `loading` prop to `false`', () => { + expect(findProjectSaveButton().props('loading')).toBe(false); + }); - describe.each` - checkValidityReturn | integrationActive - ${true} | ${false} - ${true} | ${true} - ${false} | ${false} - `( - 'when form is valid (checkValidity returns $checkValidityReturn and integrationActive is $integrationActive)', - ({ integrationActive, checkValidityReturn }) => { - beforeEach(async () => { - createForm({ isValid: checkValidityReturn }); - createComponent({ - customStateProps: { - showActive: true, - canTest: true, - initialActivated: integrationActive, - }, + it('sets test button `disabled` prop to `false`', () => { + expect(findTestButton().props('disabled')).toBe(false); }); - await findSaveButton().vm.$emit('click', new Event('click')); + it('emits `VALIDATE_INTEGRATION_FORM_EVENT`', () => { + expect(eventHub.$emit).toHaveBeenCalledWith(VALIDATE_INTEGRATION_FORM_EVENT); + }); }); + }); - it('submit form', () => { - expect(mockForm.submit).toHaveBeenCalledTimes(1); - }); - }, - ); + describe('when `test` button is clicked', () => { + describe('when form is invalid', () => { + it('emits `VALIDATE_INTEGRATION_FORM_EVENT` event to the event hub', () => { + createComponent({ + customStateProps: { + showActive: true, + canTest: true, + }, + mountFn: mountExtended, + }); + mockFormFunctions({ checkValidityReturn: false }); - describe('when form is invalid (checkValidity returns false and integrationActive is true)', () => { - beforeEach(async () => { - createForm({ isValid: false }); - createComponent({ - customStateProps: { - showActive: true, - canTest: true, - initialActivated: true, - }, + findTestButton().vm.$emit('click', new Event('click')); + + expect(eventHub.$emit).toHaveBeenCalledWith(VALIDATE_INTEGRATION_FORM_EVENT); + }); }); - await findSaveButton().vm.$emit('click', new Event('click')); - }); + describe('when form is valid', () => { + const mockTestPath = '/test'; - it('does not submit form', () => { - expect(mockForm.submit).not.toHaveBeenCalled(); - }); + beforeEach(() => { + createComponent({ + customStateProps: { + showActive: true, + canTest: true, + testPath: mockTestPath, + }, + mountFn: mountExtended, + }); + mockFormFunctions({ checkValidityReturn: true }); + }); - it('sets save button `loading` prop to `false`', () => { - expect(findSaveButton().props('loading')).toBe(false); - }); + describe('buttons', () => { + beforeEach(async () => { + await findTestButton().vm.$emit('click', new Event('click')); + }); - it('sets test button `disabled` prop to `false`', () => { - expect(findTestButton().props('disabled')).toBe(false); - }); + it('sets test button `loading` prop to `true`', () => { + expect(findTestButton().props('loading')).toBe(true); + }); - it('emits `VALIDATE_INTEGRATION_FORM_EVENT`', () => { - expect(eventHub.$emit).toHaveBeenCalledWith(VALIDATE_INTEGRATION_FORM_EVENT); + it('sets save button `disabled` prop to `true`', () => { + expect(findProjectSaveButton().props('disabled')).toBe(true); + }); + }); + + describe.each` + scenario | replyStatus | errorMessage | expectToast | expectSentry + ${'when "test settings" request fails'} | ${httpStatus.INTERNAL_SERVER_ERROR} | ${undefined} | ${I18N_DEFAULT_ERROR_MESSAGE} | ${true} + ${'when "test settings" returns an error'} | ${httpStatus.OK} | ${'an error'} | ${'an error'} | ${false} + ${'when "test settings" succeeds'} | ${httpStatus.OK} | ${undefined} | ${I18N_SUCCESSFUL_CONNECTION_MESSAGE} | ${false} + `('$scenario', ({ replyStatus, errorMessage, expectToast, expectSentry }) => { + beforeEach(async () => { + mockAxios.onPut(mockTestPath).replyOnce(replyStatus, { + error: Boolean(errorMessage), + message: errorMessage, + }); + + await findTestButton().vm.$emit('click', new Event('click')); + await waitForPromises(); + }); + + it(`calls toast with '${expectToast}'`, () => { + 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); + }); + }); + }); }); - }); - }); + }, + ); + + describe('when `reset-confirmation-modal` emits `reset` event', () => { + const mockResetPath = '/reset'; - describe('when `test` button is clicked', () => { - describe('when form is invalid', () => { - it('emits `VALIDATE_INTEGRATION_FORM_EVENT` event to the event hub', () => { - createForm({ isValid: false }); + describe('buttons', () => { + beforeEach(async () => { createComponent({ customStateProps: { - showActive: true, + integrationLevel: integrationLevels.GROUP, canTest: true, + resetPath: mockResetPath, }, }); - findTestButton().vm.$emit('click', new Event('click')); + await findResetConfirmationModal().vm.$emit('reset'); + }); - expect(eventHub.$emit).toHaveBeenCalledWith(VALIDATE_INTEGRATION_FORM_EVENT); + it('sets reset button `loading` prop to `true`', () => { + expect(findResetButton().props('loading')).toBe(true); }); - }); - describe('when form is valid', () => { - const mockTestPath = '/test'; + it('sets other button `disabled` props to `true`', () => { + expect(findInstanceOrGroupSaveButton().props('disabled')).toBe(true); + expect(findTestButton().props('disabled')).toBe(true); + }); + }); - beforeEach(() => { - createForm({ isValid: true }); + describe('when "reset settings" request fails', () => { + beforeEach(async () => { + mockAxios.onPost(mockResetPath).replyOnce(httpStatus.INTERNAL_SERVER_ERROR); createComponent({ customStateProps: { - showActive: true, + integrationLevel: integrationLevels.GROUP, canTest: true, - testPath: mockTestPath, + resetPath: mockResetPath, }, }); - }); - - 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); - }); + await findResetConfirmationModal().vm.$emit('reset'); + await waitForPromises(); + }); - it('sets save button `disabled` prop to `true`', () => { - expect(findSaveButton().props('disabled')).toBe(true); - }); + it('displays a toast', () => { + expect(mockToastShow).toHaveBeenCalledWith(I18N_DEFAULT_ERROR_MESSAGE); }); - describe.each` - scenario | replyStatus | errorMessage | expectToast | expectSentry - ${'when "test settings" request fails'} | ${httpStatus.INTERNAL_SERVER_ERROR} | ${undefined} | ${I18N_DEFAULT_ERROR_MESSAGE} | ${true} - ${'when "test settings" returns an error'} | ${httpStatus.OK} | ${'an error'} | ${'an error'} | ${false} - ${'when "test settings" succeeds'} | ${httpStatus.OK} | ${undefined} | ${I18N_SUCCESSFUL_CONNECTION_MESSAGE} | ${false} - `('$scenario', ({ replyStatus, errorMessage, expectToast, expectSentry }) => { - beforeEach(async () => { - mockAxios.onPut(mockTestPath).replyOnce(replyStatus, { - error: Boolean(errorMessage), - message: errorMessage, - }); + it('captures exception in Sentry', () => { + expect(Sentry.captureException).toHaveBeenCalledTimes(1); + }); - await findTestButton().vm.$emit('click', new Event('click')); - await waitForPromises(); - }); + it('sets reset button `loading` prop to `false`', () => { + expect(findResetButton().props('loading')).toBe(false); + }); - it(`calls toast with '${expectToast}'`, () => { - expect(mockToastShow).toHaveBeenCalledWith(expectToast); - }); + it('sets button `disabled` props to `false`', () => { + expect(findInstanceOrGroupSaveButton().props('disabled')).toBe(false); + expect(findTestButton().props('disabled')).toBe(false); + }); + }); - it('sets `loading` prop of test button to `false`', () => { - expect(findTestButton().props('loading')).toBe(false); + describe('when "reset settings" succeeds', () => { + beforeEach(async () => { + mockAxios.onPost(mockResetPath).replyOnce(httpStatus.OK); + createComponent({ + customStateProps: { + integrationLevel: integrationLevels.GROUP, + resetPath: mockResetPath, + }, }); - it('sets save button `disabled` prop to `false`', () => { - expect(findSaveButton().props('disabled')).toBe(false); - }); + await findResetConfirmationModal().vm.$emit('reset'); + await waitForPromises(); + }); - it(`${expectSentry ? 'does' : 'does not'} capture exception in Sentry`, () => { - expect(Sentry.captureException).toHaveBeenCalledTimes(expectSentry ? 1 : 0); - }); + it('calls `refreshCurrentPage`', () => { + expect(refreshCurrentPage).toHaveBeenCalledTimes(1); }); }); }); diff --git a/spec/frontend/integrations/edit/store/actions_spec.js b/spec/frontend/integrations/edit/store/actions_spec.js index b413de2b286..a5627d8b669 100644 --- a/spec/frontend/integrations/edit/store/actions_spec.js +++ b/spec/frontend/integrations/edit/store/actions_spec.js @@ -4,17 +4,12 @@ import testAction from 'helpers/vuex_action_helper'; import { I18N_FETCH_TEST_SETTINGS_DEFAULT_ERROR_MESSAGE } from '~/integrations/constants'; import { setOverride, - setIsResetting, - requestResetIntegration, - receiveResetIntegrationSuccess, - receiveResetIntegrationError, requestJiraIssueTypes, receiveJiraIssueTypesSuccess, receiveJiraIssueTypesError, } from '~/integrations/edit/store/actions'; import * as types from '~/integrations/edit/store/mutation_types'; import createState from '~/integrations/edit/store/state'; -import { refreshCurrentPage } from '~/lib/utils/url_utility'; import { mockJiraIssueTypes } from '../mock_data'; jest.mock('~/lib/utils/url_utility'); @@ -38,38 +33,6 @@ describe('Integration form store actions', () => { }); }); - describe('setIsResetting', () => { - it('should commit isResetting mutation', () => { - return testAction(setIsResetting, true, state, [ - { type: types.SET_IS_RESETTING, payload: true }, - ]); - }); - }); - - describe('requestResetIntegration', () => { - it('should commit REQUEST_RESET_INTEGRATION mutation', () => { - return testAction(requestResetIntegration, null, state, [ - { type: types.REQUEST_RESET_INTEGRATION }, - ]); - }); - }); - - describe('receiveResetIntegrationSuccess', () => { - it('should call refreshCurrentPage()', () => { - return testAction(receiveResetIntegrationSuccess, null, state, [], [], () => { - expect(refreshCurrentPage).toHaveBeenCalled(); - }); - }); - }); - - describe('receiveResetIntegrationError', () => { - it('should commit RECEIVE_RESET_INTEGRATION_ERROR mutation', () => { - return testAction(receiveResetIntegrationError, null, state, [ - { type: types.RECEIVE_RESET_INTEGRATION_ERROR }, - ]); - }); - }); - describe('requestJiraIssueTypes', () => { describe.each` scenario | responseCode | response | action diff --git a/spec/frontend/integrations/edit/store/mutations_spec.js b/spec/frontend/integrations/edit/store/mutations_spec.js index 641547550d1..ecac9d88982 100644 --- a/spec/frontend/integrations/edit/store/mutations_spec.js +++ b/spec/frontend/integrations/edit/store/mutations_spec.js @@ -17,30 +17,6 @@ describe('Integration form store mutations', () => { }); }); - describe(`${types.SET_IS_RESETTING}`, () => { - it('sets isResetting', () => { - mutations[types.SET_IS_RESETTING](state, true); - - expect(state.isResetting).toBe(true); - }); - }); - - describe(`${types.REQUEST_RESET_INTEGRATION}`, () => { - it('sets isResetting', () => { - mutations[types.REQUEST_RESET_INTEGRATION](state); - - expect(state.isResetting).toBe(true); - }); - }); - - describe(`${types.RECEIVE_RESET_INTEGRATION_ERROR}`, () => { - it('sets isResetting', () => { - mutations[types.RECEIVE_RESET_INTEGRATION_ERROR](state); - - expect(state.isResetting).toBe(false); - }); - }); - describe(`${types.SET_JIRA_ISSUE_TYPES}`, () => { it('sets jiraIssueTypes', () => { const jiraIssueTypes = ['issue', 'epic']; diff --git a/spec/frontend/integrations/edit/store/state_spec.js b/spec/frontend/integrations/edit/store/state_spec.js index 5582be7fd3c..0b4ca8fb65c 100644 --- a/spec/frontend/integrations/edit/store/state_spec.js +++ b/spec/frontend/integrations/edit/store/state_spec.js @@ -5,8 +5,6 @@ describe('Integration form state factory', () => { expect(createState()).toEqual({ defaultState: null, customState: {}, - isSaving: false, - isResetting: false, override: false, isLoadingJiraIssueTypes: false, jiraIssueTypes: [], |