diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-18 19:00:14 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-18 19:00:14 +0000 |
commit | 05f0ebba3a2c8ddf39e436f412dc2ab5bf1353b2 (patch) | |
tree | 11d0f2a6ec31c7793c184106cedc2ded3d9a2cc5 /spec/frontend/integrations | |
parent | ec73467c23693d0db63a797d10194da9e72a74af (diff) | |
download | gitlab-ce-05f0ebba3a2c8ddf39e436f412dc2ab5bf1353b2.tar.gz |
Add latest changes from gitlab-org/gitlab@15-8-stable-eev15.8.0-rc42
Diffstat (limited to 'spec/frontend/integrations')
4 files changed, 162 insertions, 69 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 } }, diff --git a/spec/frontend/integrations/overrides/components/integration_overrides_spec.js b/spec/frontend/integrations/overrides/components/integration_overrides_spec.js index fd60d7f817f..fdb728281b5 100644 --- a/spec/frontend/integrations/overrides/components/integration_overrides_spec.js +++ b/spec/frontend/integrations/overrides/components/integration_overrides_spec.js @@ -8,7 +8,7 @@ import IntegrationOverrides from '~/integrations/overrides/components/integratio import IntegrationTabs from '~/integrations/overrides/components/integration_tabs.vue'; import axios from '~/lib/utils/axios_utils'; -import httpStatus from '~/lib/utils/http_status'; +import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; import ProjectAvatar from '~/vue_shared/components/project_avatar.vue'; import UrlSync from '~/vue_shared/components/url_sync.vue'; @@ -39,7 +39,7 @@ describe('IntegrationOverrides', () => { beforeEach(() => { mockAxios = new MockAdapter(axios); - mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.OK, mockOverrides, { + mockAxios.onGet(defaultProps.overridesPath).reply(HTTP_STATUS_OK, mockOverrides, { 'X-TOTAL': mockOverrides.length, 'X-PAGE': 1, }); @@ -125,7 +125,7 @@ describe('IntegrationOverrides', () => { describe('when request fails', () => { beforeEach(async () => { jest.spyOn(Sentry, 'captureException'); - mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.INTERNAL_SERVER_ERROR); + mockAxios.onGet(defaultProps.overridesPath).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); createComponent(); await waitForPromises(); @@ -150,7 +150,7 @@ describe('IntegrationOverrides', () => { describe('pagination', () => { describe('when total items does not exceed the page limit', () => { it('does not render', async () => { - mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.OK, [mockOverrides[0]], { + mockAxios.onGet(defaultProps.overridesPath).reply(HTTP_STATUS_OK, [mockOverrides[0]], { 'X-TOTAL': DEFAULT_PER_PAGE - 1, 'X-PAGE': 1, }); @@ -169,7 +169,7 @@ describe('IntegrationOverrides', () => { beforeEach(async () => { createComponent({ stubs: { UrlSync } }); - mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.OK, [mockOverrides[0]], { + mockAxios.onGet(defaultProps.overridesPath).reply(HTTP_STATUS_OK, [mockOverrides[0]], { 'X-TOTAL': DEFAULT_PER_PAGE * 2, 'X-PAGE': mockPage, }); |