diff options
Diffstat (limited to 'spec/frontend/integrations/edit')
3 files changed, 157 insertions, 64 deletions
diff --git a/spec/frontend/integrations/edit/components/integration_form_spec.js b/spec/frontend/integrations/edit/components/integration_form_spec.js index 4b49e492880..383dfb36aa5 100644 --- a/spec/frontend/integrations/edit/components/integration_form_spec.js +++ b/spec/frontend/integrations/edit/components/integration_form_spec.js @@ -1,4 +1,4 @@ -import { GlAlert, GlBadge, GlForm } from '@gitlab/ui'; +import { GlAlert, GlForm } from '@gitlab/ui'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { nextTick } from 'vue'; @@ -11,18 +11,16 @@ 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 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 IntegrationFormSection from '~/integrations/edit/components/integration_forms/section.vue'; import { I18N_SUCCESSFUL_CONNECTION_MESSAGE, I18N_DEFAULT_ERROR_MESSAGE, INTEGRATION_FORM_TYPE_SLACK, - billingPlans, - billingPlanNames, } from '~/integrations/constants'; import { createStore } from '~/integrations/edit/store'; -import httpStatus from '~/lib/utils/http_status'; +import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; import { refreshCurrentPage } from '~/lib/utils/url_utility'; import { mockIntegrationProps, @@ -73,15 +71,11 @@ describe('IntegrationForm', () => { const findActiveCheckbox = () => wrapper.findComponent(ActiveCheckbox); const findTriggerFields = () => wrapper.findComponent(TriggerFields); const findAlert = () => wrapper.findComponent(GlAlert); - const findGlBadge = () => wrapper.findComponent(GlBadge); const findGlForm = () => wrapper.findComponent(GlForm); const findRedirectToField = () => wrapper.findByTestId('redirect-to-field'); const findDynamicField = () => wrapper.findComponent(DynamicField); const findAllDynamicFields = () => wrapper.findAllComponents(DynamicField); - const findAllSections = () => wrapper.findAllByTestId('integration-section'); - const findConnectionSection = () => findAllSections().at(0); - const findConnectionSectionComponent = () => - findConnectionSection().findComponent(IntegrationSectionConnection); + const findAllSections = () => wrapper.findAllComponents(IntegrationFormSection); const findHelpHtml = () => wrapper.findByTestId('help-html'); const findFormActions = () => wrapper.findComponent(IntegrationFormActions); @@ -215,54 +209,13 @@ describe('IntegrationForm', () => { beforeEach(() => { createComponent({ customStateProps: { - sections: [mockSectionConnection], - }, - }); - }); - - it('renders the expected number of sections', () => { - expect(findAllSections().length).toBe(1); - }); - - it('renders title, description and the correct dynamic component', () => { - const connectionSection = findConnectionSection(); - - expect(connectionSection.find('h4').text()).toBe(mockSectionConnection.title); - expect(connectionSection.find('p').text()).toBe(mockSectionConnection.description); - expect(findGlBadge().exists()).toBe(false); - expect(findConnectionSectionComponent().exists()).toBe(true); - }); - - it('renders GlBadge when `plan` is present', () => { - createComponent({ - customStateProps: { sections: [mockSectionConnection, mockSectionJiraIssues], }, }); - - expect(findGlBadge().exists()).toBe(true); - expect(findGlBadge().text()).toMatchInterpolatedText(billingPlanNames[billingPlans.PREMIUM]); }); - it('passes only fields with section type', () => { - const sectionFields = [ - { name: 'username', type: 'text', section: mockSectionConnection.type }, - { name: 'API token', type: 'password', section: mockSectionConnection.type }, - ]; - - const nonSectionFields = [ - { name: 'branch', type: 'text' }, - { name: 'labels', type: 'select' }, - ]; - - createComponent({ - customStateProps: { - sections: [mockSectionConnection], - fields: [...sectionFields, ...nonSectionFields], - }, - }); - - expect(findConnectionSectionComponent().props('fields')).toEqual(sectionFields); + it('renders the expected number of sections', () => { + expect(findAllSections()).toHaveLength(2); }); describe.each` @@ -281,7 +234,8 @@ describe('IntegrationForm', () => { }, }); - findConnectionSectionComponent().vm.$emit('toggle-integration-active', formActive); + const section = findAllSections().at(0); + section.vm.$emit('toggle-integration-active', formActive); }); it(`sets noValidate to ${novalidate}`, () => { @@ -290,7 +244,7 @@ describe('IntegrationForm', () => { }, ); - describe('when IntegrationSectionConnection emits `request-jira-issue-types` event', () => { + describe('when section emits `request-jira-issue-types` event', () => { beforeEach(() => { jest.spyOn(document, 'querySelector').mockReturnValue(document.createElement('form')); @@ -302,7 +256,8 @@ describe('IntegrationForm', () => { mountFn: mountExtended, }); - findConnectionSectionComponent().vm.$emit('request-jira-issue-types'); + const section = findAllSections().at(0); + section.vm.$emit('request-jira-issue-types'); }); it('dispatches `requestJiraIssueTypes` action', () => { @@ -456,11 +411,11 @@ describe('IntegrationForm', () => { }); describe.each` - scenario | replyStatus | errorMessage | serviceResponse | expectToast | expectSentry - ${'when "test settings" request fails'} | ${httpStatus.INTERNAL_SERVER_ERROR} | ${undefined} | ${undefined} | ${I18N_DEFAULT_ERROR_MESSAGE} | ${true} - ${'when "test settings" returns an error'} | ${httpStatus.OK} | ${'an error'} | ${undefined} | ${'an error'} | ${false} - ${'when "test settings" returns an error with details'} | ${httpStatus.OK} | ${'an error.'} | ${'extra info'} | ${'an error. extra info'} | ${false} - ${'when "test settings" succeeds'} | ${httpStatus.OK} | ${undefined} | ${undefined} | ${I18N_SUCCESSFUL_CONNECTION_MESSAGE} | ${false} + scenario | replyStatus | errorMessage | serviceResponse | expectToast | expectSentry + ${'when "test settings" request fails'} | ${HTTP_STATUS_INTERNAL_SERVER_ERROR} | ${undefined} | ${undefined} | ${I18N_DEFAULT_ERROR_MESSAGE} | ${true} + ${'when "test settings" returns an error'} | ${HTTP_STATUS_OK} | ${'an error'} | ${undefined} | ${'an error'} | ${false} + ${'when "test settings" returns an error with details'} | ${HTTP_STATUS_OK} | ${'an error.'} | ${'extra info'} | ${'an error. extra info'} | ${false} + ${'when "test settings" succeeds'} | ${HTTP_STATUS_OK} | ${undefined} | ${undefined} | ${I18N_SUCCESSFUL_CONNECTION_MESSAGE} | ${false} `( '$scenario', ({ replyStatus, errorMessage, serviceResponse, expectToast, expectSentry }) => { @@ -491,7 +446,7 @@ describe('IntegrationForm', () => { const mockResetPath = '/reset'; beforeEach(async () => { - mockAxios.onPost(mockResetPath).replyOnce(httpStatus.INTERNAL_SERVER_ERROR); + mockAxios.onPost(mockResetPath).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR); createComponent({ customStateProps: { resetPath: mockResetPath, @@ -526,7 +481,7 @@ describe('IntegrationForm', () => { describe('when "reset settings" succeeds', () => { beforeEach(async () => { - mockAxios.onPost(mockResetPath).replyOnce(httpStatus.OK); + mockAxios.onPost(mockResetPath).replyOnce(HTTP_STATUS_OK); createComponent({ customStateProps: { resetPath: mockResetPath, diff --git a/spec/frontend/integrations/edit/components/integration_forms/section_spec.js b/spec/frontend/integrations/edit/components/integration_forms/section_spec.js new file mode 100644 index 00000000000..5f82941778e --- /dev/null +++ b/spec/frontend/integrations/edit/components/integration_forms/section_spec.js @@ -0,0 +1,109 @@ +import { GlBadge } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { billingPlans, billingPlanNames } from '~/integrations/constants'; +import DynamicField from '~/integrations/edit/components/dynamic_field.vue'; +import IntegrationFormSection from '~/integrations/edit/components/integration_forms/section.vue'; +import IntegrationSectionConnection from '~/integrations/edit/components/sections/connection.vue'; +import { createStore } from '~/integrations/edit/store'; +import { + mockIntegrationProps, + mockSectionConnection, + mockSectionJiraIssues, +} from '../../mock_data'; + +describe('Integration Form Section', () => { + let wrapper; + + const defaultProps = { + section: mockSectionConnection, + isValidated: false, + }; + + const createComponent = ({ + customStateProps = {}, + props = {}, + mountFn = shallowMountExtended, + } = {}) => { + const store = createStore({ + customState: { + ...mockIntegrationProps, + ...customStateProps, + }, + }); + + wrapper = mountFn(IntegrationFormSection, { + store, + propsData: { + ...defaultProps, + ...props, + }, + stubs: { + IntegrationSectionConnection, + }, + }); + }; + + const findGlBadge = () => wrapper.findComponent(GlBadge); + const findFieldsComponent = () => wrapper.findComponent(IntegrationSectionConnection); + const findAllDynamicFields = () => wrapper.findAllComponents(DynamicField); + + beforeEach(() => { + createComponent(); + }); + + it('renders title, description and the correct dynamic component', () => { + expect(wrapper.findByText(mockSectionConnection.title).exists()).toBe(true); + expect(wrapper.findByText(mockSectionConnection.description).exists()).toBe(true); + expect(findGlBadge().exists()).toBe(false); + }); + + it('renders GlBadge when `plan` is present', () => { + createComponent({ + props: { + section: mockSectionJiraIssues, + }, + }); + + expect(findGlBadge().exists()).toBe(true); + expect(findGlBadge().text()).toMatchInterpolatedText(billingPlanNames[billingPlans.PREMIUM]); + }); + + it('renders only fields for this section type', () => { + const sectionFields = [ + { name: 'username', type: 'text', section: mockSectionConnection.type }, + { name: 'API token', type: 'password', section: mockSectionConnection.type }, + ]; + + const nonSectionFields = [{ name: 'branch', type: 'text' }]; + + createComponent({ + customStateProps: { + fields: [...sectionFields, ...nonSectionFields], + }, + }); + + expect(findAllDynamicFields()).toHaveLength(2); + sectionFields.forEach((field, index) => { + expect(findAllDynamicFields().at(index).props('name')).toBe(field.name); + }); + }); + + describe('events proxy from the section', () => { + let section; + const dummyPayload = 'foo'; + + beforeEach(() => { + section = findFieldsComponent(); + }); + + it('toggle-integration-active', () => { + section.vm.$emit('toggle-integration-active', dummyPayload); + expect(wrapper.emitted('toggle-integration-active')).toEqual([[dummyPayload]]); + }); + + it('request-jira-issue-types', () => { + section.vm.$emit('request-jira-issue-types', dummyPayload); + expect(wrapper.emitted('request-jira-issue-types')).toEqual([[dummyPayload]]); + }); + }); +}); diff --git a/spec/frontend/integrations/edit/components/trigger_field_spec.js b/spec/frontend/integrations/edit/components/trigger_field_spec.js index 6a68337813e..ed0b3324708 100644 --- a/spec/frontend/integrations/edit/components/trigger_field_spec.js +++ b/spec/frontend/integrations/edit/components/trigger_field_spec.js @@ -1,6 +1,6 @@ import { nextTick } from 'vue'; import { shallowMount } from '@vue/test-utils'; -import { GlFormCheckbox } from '@gitlab/ui'; +import { GlFormCheckbox, GlFormInput } from '@gitlab/ui'; import TriggerField from '~/integrations/edit/components/trigger_field.vue'; import { integrationTriggerEventTitles } from '~/integrations/constants'; @@ -10,7 +10,9 @@ describe('TriggerField', () => { const defaultProps = { event: { name: 'push_events' }, + type: 'gitlab_slack_application', }; + const mockField = { name: 'push_channel' }; const createComponent = ({ props = {}, isInheriting = false } = {}) => { wrapper = shallowMount(TriggerField, { @@ -26,6 +28,7 @@ describe('TriggerField', () => { }); const findGlFormCheckbox = () => wrapper.findComponent(GlFormCheckbox); + const findGlFormInput = () => wrapper.findComponent(GlFormInput); const findHiddenInput = () => wrapper.find('input[type="hidden"]'); describe('template', () => { @@ -55,6 +58,32 @@ describe('TriggerField', () => { expect(findHiddenInput().attributes('value')).toBe('false'); }); + it('renders hidden GlFormInput', () => { + createComponent({ + props: { + event: { name: 'push_events', field: mockField }, + }, + }); + + expect(findGlFormInput().exists()).toBe(true); + expect(findGlFormInput().isVisible()).toBe(false); + }); + + describe('checkbox is selected', () => { + it('renders visible GlFormInput', async () => { + createComponent({ + props: { + event: { name: 'push_events', field: mockField }, + }, + }); + + await findGlFormCheckbox().vm.$emit('input', true); + + expect(findGlFormInput().exists()).toBe(true); + expect(findGlFormInput().isVisible()).toBe(true); + }); + }); + it('toggles value of hidden input on checkbox input', async () => { createComponent({ props: { event: { name: 'push_events', value: true } }, |