diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-17 03:09:14 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-17 03:09:14 +0000 |
commit | 4bc0e064023a13d90da5acc4fd152fca66926ea2 (patch) | |
tree | bebd92d2f5392f8c1bd785fbe14b3b3bf03fc5d1 /spec | |
parent | d5cf5cf4f77eec07a04604b1a0298452029df16f (diff) | |
download | gitlab-ce-4bc0e064023a13d90da5acc4fd152fca66926ea2.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
27 files changed, 824 insertions, 241 deletions
diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb index ea844008f64..e8a23dcfafb 100644 --- a/spec/controllers/projects/services_controller_spec.rb +++ b/spec/controllers/projects/services_controller_spec.rb @@ -179,6 +179,23 @@ RSpec.describe Projects::ServicesController do it_behaves_like 'service update' end + + context 'wehn param `inherit_from_id` is set to empty string' do + let(:service_params) { { inherit_from_id: '' } } + + it 'sets inherit_from_id to nil' do + expect(service.reload.inherit_from_id).to eq(nil) + end + end + + context 'wehn param `inherit_from_id` is set to some value' do + let(:instance_service) { create(:jira_service, :instance) } + let(:service_params) { { inherit_from_id: instance_service.id } } + + it 'sets inherit_from_id to value' do + expect(service.reload.inherit_from_id).to eq(instance_service.id) + end + end end describe 'as JSON' do diff --git a/spec/features/projects/services/disable_triggers_spec.rb b/spec/features/projects/services/disable_triggers_spec.rb index 8f87d0e7ff1..b3a3d7f0622 100644 --- a/spec/features/projects/services/disable_triggers_spec.rb +++ b/spec/features/projects/services/disable_triggers_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Disable individual triggers', :js do include_context 'project service activation' - let(:checkbox_selector) { 'input[type=checkbox][name$="_events]"]' } + let(:checkbox_selector) { 'input[name$="_events]"]' } before do visit_project_integration(service_name) @@ -18,7 +18,7 @@ RSpec.describe 'Disable individual triggers', :js do event_count = HipchatService.supported_events.count expect(page).to have_content "Trigger" - expect(page).to have_css(checkbox_selector, count: event_count) + expect(page).to have_css(checkbox_selector, visible: :all, count: event_count) end end @@ -27,7 +27,7 @@ RSpec.describe 'Disable individual triggers', :js do it "doesn't show unnecessary Trigger checkboxes" do expect(page).not_to have_content "Trigger" - expect(page).not_to have_css(checkbox_selector) + expect(page).not_to have_css(checkbox_selector, visible: :all) end end end diff --git a/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js b/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js index 0f34d6419d3..f1a7ac8b21a 100644 --- a/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js +++ b/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js @@ -6,7 +6,7 @@ import { SIMPLE_BLOB_VIEWER, SIMPLE_BLOB_VIEWER_TITLE, } from '~/blob/components/constants'; -import { GlButtonGroup, GlDeprecatedButton } from '@gitlab/ui'; +import { GlButtonGroup, GlButton } from '@gitlab/ui'; describe('Blob Header Viewer Switcher', () => { let wrapper; @@ -35,7 +35,7 @@ describe('Blob Header Viewer Switcher', () => { beforeEach(() => { createComponent(); btnGroup = wrapper.find(GlButtonGroup); - buttons = wrapper.findAll(GlDeprecatedButton); + buttons = wrapper.findAll(GlButton); }); it('renders gl-button-group component', () => { @@ -57,7 +57,7 @@ describe('Blob Header Viewer Switcher', () => { function factory(propsData = {}) { createComponent(propsData); - buttons = wrapper.findAll(GlDeprecatedButton); + buttons = wrapper.findAll(GlButton); simpleBtn = buttons.at(0); richBtn = buttons.at(1); diff --git a/spec/frontend/integrations/edit/components/active_toggle_spec.js b/spec/frontend/integrations/edit/components/active_toggle_spec.js index 5469b45f708..228d8f5fc30 100644 --- a/spec/frontend/integrations/edit/components/active_toggle_spec.js +++ b/spec/frontend/integrations/edit/components/active_toggle_spec.js @@ -1,8 +1,10 @@ import { mount } from '@vue/test-utils'; -import ActiveToggle from '~/integrations/edit/components/active_toggle.vue'; import { GlToggle } from '@gitlab/ui'; +import ActiveToggle from '~/integrations/edit/components/active_toggle.vue'; + const GL_TOGGLE_ACTIVE_CLASS = 'is-checked'; +const GL_TOGGLE_DISABLED_CLASS = 'is-disabled'; describe('ActiveToggle', () => { let wrapper; @@ -11,9 +13,12 @@ describe('ActiveToggle', () => { initialActivated: true, }; - const createComponent = props => { + const createComponent = (props = {}, isInheriting = false) => { wrapper = mount(ActiveToggle, { propsData: { ...defaultProps, ...props }, + computed: { + isInheriting: () => isInheriting, + }, }); }; @@ -29,6 +34,15 @@ describe('ActiveToggle', () => { const findInputInToggle = () => findGlToggle().find('input'); describe('template', () => { + describe('is inheriting adminSettings', () => { + it('renders GlToggle as disabled', () => { + createComponent({}, true); + + expect(findGlToggle().exists()).toBe(true); + expect(findButtonInToggle().classes()).toContain(GL_TOGGLE_DISABLED_CLASS); + }); + }); + describe('initialActivated is false', () => { it('renders GlToggle as inactive', () => { createComponent({ diff --git a/spec/frontend/integrations/edit/components/dynamic_field_spec.js b/spec/frontend/integrations/edit/components/dynamic_field_spec.js index 0a771c3b31d..3a7a0efcab7 100644 --- a/spec/frontend/integrations/edit/components/dynamic_field_spec.js +++ b/spec/frontend/integrations/edit/components/dynamic_field_spec.js @@ -14,9 +14,12 @@ describe('DynamicField', () => { value: '1', }; - const createComponent = props => { + const createComponent = (props, isInheriting = false) => { wrapper = mount(DynamicField, { propsData: { ...defaultProps, ...props }, + computed: { + isInheriting: () => isInheriting, + }, }); }; @@ -34,108 +37,143 @@ describe('DynamicField', () => { const findGlFormTextarea = () => wrapper.find(GlFormTextarea); describe('template', () => { - describe('dynamic field', () => { - describe('type is checkbox', () => { - beforeEach(() => { - createComponent({ - type: 'checkbox', + describe.each([[true, 'disabled', 'readonly'], [false, undefined, undefined]])( + 'dynamic field, when isInheriting = `%p`', + (isInheriting, disabled, readonly) => { + describe('type is checkbox', () => { + beforeEach(() => { + createComponent( + { + type: 'checkbox', + }, + isInheriting, + ); }); - }); - it('renders GlFormCheckbox', () => { - expect(findGlFormCheckbox().exists()).toBe(true); - }); + it(`renders GlFormCheckbox, which ${isInheriting ? 'is' : 'is not'} disabled`, () => { + expect(findGlFormCheckbox().exists()).toBe(true); + expect( + findGlFormCheckbox() + .find('[type=checkbox]') + .attributes('disabled'), + ).toBe(disabled); + }); - it('does not render other types of input', () => { - expect(findGlFormSelect().exists()).toBe(false); - expect(findGlFormTextarea().exists()).toBe(false); - expect(findGlFormInput().exists()).toBe(false); + it('does not render other types of input', () => { + expect(findGlFormSelect().exists()).toBe(false); + expect(findGlFormTextarea().exists()).toBe(false); + expect(findGlFormInput().exists()).toBe(false); + }); }); - }); - describe('type is select', () => { - beforeEach(() => { - createComponent({ - type: 'select', - choices: [['all', 'All details'], ['standard', 'Standard']], + describe('type is select', () => { + beforeEach(() => { + createComponent( + { + type: 'select', + choices: [['all', 'All details'], ['standard', 'Standard']], + }, + isInheriting, + ); }); - }); - it('renders findGlFormSelect', () => { - expect(findGlFormSelect().exists()).toBe(true); - expect(findGlFormSelect().findAll('option')).toHaveLength(2); - }); + it(`renders GlFormSelect, which ${isInheriting ? 'is' : 'is not'} disabled`, () => { + expect(findGlFormSelect().exists()).toBe(true); + expect(findGlFormSelect().findAll('option')).toHaveLength(2); + expect( + findGlFormSelect() + .find('select') + .attributes('disabled'), + ).toBe(disabled); + }); - it('does not render other types of input', () => { - expect(findGlFormCheckbox().exists()).toBe(false); - expect(findGlFormTextarea().exists()).toBe(false); - expect(findGlFormInput().exists()).toBe(false); + it('does not render other types of input', () => { + expect(findGlFormCheckbox().exists()).toBe(false); + expect(findGlFormTextarea().exists()).toBe(false); + expect(findGlFormInput().exists()).toBe(false); + }); }); - }); - describe('type is textarea', () => { - beforeEach(() => { - createComponent({ - type: 'textarea', + describe('type is textarea', () => { + beforeEach(() => { + createComponent( + { + type: 'textarea', + }, + isInheriting, + ); }); - }); - it('renders findGlFormTextarea', () => { - expect(findGlFormTextarea().exists()).toBe(true); - }); + it(`renders GlFormTextarea, which ${isInheriting ? 'is' : 'is not'} readonly`, () => { + expect(findGlFormTextarea().exists()).toBe(true); + expect( + findGlFormTextarea() + .find('textarea') + .attributes('readonly'), + ).toBe(readonly); + }); - it('does not render other types of input', () => { - expect(findGlFormCheckbox().exists()).toBe(false); - expect(findGlFormSelect().exists()).toBe(false); - expect(findGlFormInput().exists()).toBe(false); + it('does not render other types of input', () => { + expect(findGlFormCheckbox().exists()).toBe(false); + expect(findGlFormSelect().exists()).toBe(false); + expect(findGlFormInput().exists()).toBe(false); + }); }); - }); - describe('type is password', () => { - beforeEach(() => { - createComponent({ - type: 'password', + describe('type is password', () => { + beforeEach(() => { + createComponent( + { + type: 'password', + }, + isInheriting, + ); }); - }); - it('renders GlFormInput', () => { - expect(findGlFormInput().exists()).toBe(true); - expect(findGlFormInput().attributes('type')).toBe('password'); - }); + it(`renders GlFormInput, which ${isInheriting ? 'is' : 'is not'} readonly`, () => { + expect(findGlFormInput().exists()).toBe(true); + expect(findGlFormInput().attributes('type')).toBe('password'); + expect(findGlFormInput().attributes('readonly')).toBe(readonly); + }); - it('does not render other types of input', () => { - expect(findGlFormCheckbox().exists()).toBe(false); - expect(findGlFormSelect().exists()).toBe(false); - expect(findGlFormTextarea().exists()).toBe(false); + it('does not render other types of input', () => { + expect(findGlFormCheckbox().exists()).toBe(false); + expect(findGlFormSelect().exists()).toBe(false); + expect(findGlFormTextarea().exists()).toBe(false); + }); }); - }); - describe('type is text', () => { - beforeEach(() => { - createComponent({ - type: 'text', - required: true, + describe('type is text', () => { + beforeEach(() => { + createComponent( + { + type: 'text', + required: true, + }, + isInheriting, + ); }); - }); - it('renders GlFormInput', () => { - expect(findGlFormInput().exists()).toBe(true); - expect(findGlFormInput().attributes()).toMatchObject({ - type: 'text', - id: 'service_project_url', - name: 'service[project_url]', - placeholder: defaultProps.placeholder, - required: 'required', + it(`renders GlFormInput, which ${isInheriting ? 'is' : 'is not'} readonly`, () => { + expect(findGlFormInput().exists()).toBe(true); + expect(findGlFormInput().attributes()).toMatchObject({ + type: 'text', + id: 'service_project_url', + name: 'service[project_url]', + placeholder: defaultProps.placeholder, + required: 'required', + }); + expect(findGlFormInput().attributes('readonly')).toBe(readonly); }); - }); - it('does not render other types of input', () => { - expect(findGlFormCheckbox().exists()).toBe(false); - expect(findGlFormSelect().exists()).toBe(false); - expect(findGlFormTextarea().exists()).toBe(false); + it('does not render other types of input', () => { + expect(findGlFormCheckbox().exists()).toBe(false); + expect(findGlFormSelect().exists()).toBe(false); + expect(findGlFormTextarea().exists()).toBe(false); + }); }); - }); - }); + }, + ); describe('help text', () => { it('renders description with help text', () => { diff --git a/spec/frontend/integrations/edit/components/integration_form_spec.js b/spec/frontend/integrations/edit/components/integration_form_spec.js index 3d27b9c3051..482c6a439f2 100644 --- a/spec/frontend/integrations/edit/components/integration_form_spec.js +++ b/spec/frontend/integrations/edit/components/integration_form_spec.js @@ -1,34 +1,29 @@ import { shallowMount } from '@vue/test-utils'; +import { createStore } from '~/integrations/edit/store'; import IntegrationForm from '~/integrations/edit/components/integration_form.vue'; +import OverrideDropdown from '~/integrations/edit/components/override_dropdown.vue'; import ActiveToggle from '~/integrations/edit/components/active_toggle.vue'; import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_fields.vue'; import JiraIssuesFields from '~/integrations/edit/components/jira_issues_fields.vue'; import TriggerFields from '~/integrations/edit/components/trigger_fields.vue'; import DynamicField from '~/integrations/edit/components/dynamic_field.vue'; +import { mockIntegrationProps } from 'jest/integrations/edit/mock_data'; describe('IntegrationForm', () => { let wrapper; - const defaultProps = { - activeToggleProps: { - initialActivated: true, - }, - showActive: true, - triggerFieldsProps: { - initialTriggerCommit: false, - initialTriggerMergeRequest: false, - initialEnableComments: false, - }, - jiraIssuesProps: {}, - type: '', - }; - - const createComponent = (props, featureFlags = {}) => { + const createComponent = (customStateProps = {}, featureFlags = {}, initialState = {}) => { wrapper = shallowMount(IntegrationForm, { - propsData: { ...defaultProps, ...props }, + propsData: {}, + store: createStore({ + customState: { ...mockIntegrationProps, ...customStateProps }, + ...initialState, + }), stubs: { + OverrideDropdown, ActiveToggle, JiraTriggerFields, + TriggerFields, }, provide: { glFeatures: featureFlags, @@ -43,6 +38,7 @@ describe('IntegrationForm', () => { } }); + const findOverrideDropdown = () => wrapper.find(OverrideDropdown); const findActiveToggle = () => wrapper.find(ActiveToggle); const findJiraTriggerFields = () => wrapper.find(JiraTriggerFields); const findJiraIssuesFields = () => wrapper.find(JiraIssuesFields); @@ -140,5 +136,35 @@ describe('IntegrationForm', () => { }); }); }); + + describe('adminState state is null', () => { + it('does not render OverrideDropdown', () => { + createComponent( + {}, + {}, + { + adminState: null, + }, + ); + + expect(findOverrideDropdown().exists()).toBe(false); + }); + }); + + describe('adminState state is an object', () => { + it('renders OverrideDropdown', () => { + createComponent( + {}, + {}, + { + adminState: { + ...mockIntegrationProps, + }, + }, + ); + + expect(findOverrideDropdown().exists()).toBe(true); + }); + }); }); }); diff --git a/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js b/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js index c89c54bd35d..f58825f6297 100644 --- a/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js +++ b/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js @@ -1,7 +1,9 @@ import { mount } from '@vue/test-utils'; -import JiraIssuesFields from '~/integrations/edit/components/jira_issues_fields.vue'; + import { GlFormCheckbox, GlFormInput } from '@gitlab/ui'; +import JiraIssuesFields from '~/integrations/edit/components/jira_issues_fields.vue'; + describe('JiraIssuesFields', () => { let wrapper; diff --git a/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js b/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js index e4c2a0be6a3..782930eb6a2 100644 --- a/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js +++ b/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js @@ -1,6 +1,6 @@ import { mount } from '@vue/test-utils'; -import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_fields.vue'; import { GlFormCheckbox } from '@gitlab/ui'; +import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_fields.vue'; describe('JiraTriggerFields', () => { let wrapper; @@ -11,9 +11,12 @@ describe('JiraTriggerFields', () => { initialEnableComments: false, }; - const createComponent = props => { + const createComponent = (props, isInheriting = false) => { wrapper = mount(JiraTriggerFields, { propsData: { ...defaultProps, ...props }, + computed: { + isInheriting: () => isInheriting, + }, }); }; @@ -93,5 +96,23 @@ describe('JiraTriggerFields', () => { expect(findCommentDetail().isVisible()).toBe(true); }); }); + + it('disables checkboxes and radios if inheriting', () => { + createComponent( + { + initialTriggerCommit: true, + initialEnableComments: true, + }, + true, + ); + + wrapper.findAll('[type=checkbox]').wrappers.forEach(checkbox => { + expect(checkbox.attributes('disabled')).toBe('disabled'); + }); + + wrapper.findAll('[type=radio]').wrappers.forEach(radio => { + expect(radio.attributes('disabled')).toBe('disabled'); + }); + }); }); }); diff --git a/spec/frontend/integrations/edit/components/trigger_fields_spec.js b/spec/frontend/integrations/edit/components/trigger_fields_spec.js index 337876c6d16..41bccb8ada0 100644 --- a/spec/frontend/integrations/edit/components/trigger_fields_spec.js +++ b/spec/frontend/integrations/edit/components/trigger_fields_spec.js @@ -9,9 +9,12 @@ describe('TriggerFields', () => { type: 'slack', }; - const createComponent = props => { + const createComponent = (props, isInheriting = false) => { wrapper = mount(TriggerFields, { propsData: { ...defaultProps, ...props }, + computed: { + isInheriting: () => isInheriting, + }, }); }; @@ -22,10 +25,11 @@ describe('TriggerFields', () => { } }); + const findAllGlFormGroups = () => wrapper.find('#trigger-fields').findAll(GlFormGroup); const findAllGlFormCheckboxes = () => wrapper.findAll(GlFormCheckbox); const findAllGlFormInputs = () => wrapper.findAll(GlFormInput); - describe('template', () => { + describe.each([true, false])('template, isInheriting = `%p`', isInheriting => { it('renders a label with text "Trigger"', () => { createComponent(); @@ -51,9 +55,12 @@ describe('TriggerFields', () => { ]; beforeEach(() => { - createComponent({ - events, - }); + createComponent( + { + events, + }, + isInheriting, + ); }); it('does not render GlFormInput for each event', () => { @@ -69,8 +76,10 @@ describe('TriggerFields', () => { }); }); - it('renders GlFormCheckbox for each event', () => { - const checkboxes = findAllGlFormCheckboxes(); + it(`renders GlFormCheckbox and corresponding hidden input for each event, which ${ + isInheriting ? 'is' : 'is not' + } disabled`, () => { + const checkboxes = findAllGlFormGroups(); const expectedResults = [ { labelText: 'Push', inputName: 'service[push_event]' }, { labelText: 'Merge Request', inputName: 'service[merge_requests_event]' }, @@ -78,14 +87,22 @@ describe('TriggerFields', () => { expect(checkboxes).toHaveLength(2); checkboxes.wrappers.forEach((checkbox, index) => { + const checkBox = checkbox.find(GlFormCheckbox); + expect(checkbox.find('label').text()).toBe(expectedResults[index].labelText); - expect(checkbox.find('input').attributes('name')).toBe(expectedResults[index].inputName); - expect(checkbox.vm.$attrs.checked).toBe(events[index].value); + expect(checkbox.find('[type=hidden]').attributes('name')).toBe( + expectedResults[index].inputName, + ); + expect(checkbox.find('[type=hidden]').attributes('value')).toBe( + events[index].value.toString(), + ); + expect(checkBox.vm.$attrs.disabled).toBe(isInheriting); + expect(checkBox.vm.$attrs.checked).toBe(events[index].value); }); }); }); - describe('events with field property', () => { + describe('events with field property, isInheriting = `%p`', () => { const events = [ { field: { @@ -102,16 +119,21 @@ describe('TriggerFields', () => { ]; beforeEach(() => { - createComponent({ - events, - }); + createComponent( + { + events, + }, + isInheriting, + ); }); it('renders GlFormCheckbox for each event', () => { expect(findAllGlFormCheckboxes()).toHaveLength(2); }); - it('renders GlFormInput for each event', () => { + it(`renders GlFormInput for each event, which ${ + isInheriting ? 'is' : 'is not' + } readonly`, () => { const fields = findAllGlFormInputs(); const expectedResults = [ { @@ -128,6 +150,7 @@ describe('TriggerFields', () => { fields.wrappers.forEach((field, index) => { expect(field.attributes()).toMatchObject(expectedResults[index]); + expect(field.vm.$attrs.readonly).toBe(isInheriting); expect(field.vm.$attrs.value).toBe(events[index].field.value); }); }); diff --git a/spec/frontend/integrations/edit/mock_data.js b/spec/frontend/integrations/edit/mock_data.js new file mode 100644 index 00000000000..da2758ec15c --- /dev/null +++ b/spec/frontend/integrations/edit/mock_data.js @@ -0,0 +1,18 @@ +// eslint-disable-next-line import/prefer-default-export +export const mockIntegrationProps = { + id: 25, + activeToggleProps: { + initialActivated: true, + }, + showActive: true, + triggerFieldsProps: { + initialTriggerCommit: false, + initialTriggerMergeRequest: false, + initialEnableComments: false, + }, + jiraIssuesProps: {}, + triggerEvents: [], + fields: [], + type: '', + inheritFromId: 25, +}; diff --git a/spec/frontend/integrations/edit/store/actions_spec.js b/spec/frontend/integrations/edit/store/actions_spec.js new file mode 100644 index 00000000000..c3ce6e51a3d --- /dev/null +++ b/spec/frontend/integrations/edit/store/actions_spec.js @@ -0,0 +1,19 @@ +import createState from '~/integrations/edit/store/state'; +import { setOverride } from '~/integrations/edit/store/actions'; +import * as types from '~/integrations/edit/store/mutation_types'; + +import testAction from 'helpers/vuex_action_helper'; + +describe('Integration form store actions', () => { + let state; + + beforeEach(() => { + state = createState(); + }); + + describe('setOverride', () => { + it('should commit override mutation', () => { + return testAction(setOverride, true, state, [{ type: types.SET_OVERRIDE, payload: true }]); + }); + }); +}); diff --git a/spec/frontend/integrations/edit/store/getters_spec.js b/spec/frontend/integrations/edit/store/getters_spec.js new file mode 100644 index 00000000000..700d36edaad --- /dev/null +++ b/spec/frontend/integrations/edit/store/getters_spec.js @@ -0,0 +1,71 @@ +import { currentKey, isInheriting, propsSource } from '~/integrations/edit/store/getters'; +import createState from '~/integrations/edit/store/state'; +import { mockIntegrationProps } from '../mock_data'; + +describe('Integration form store getters', () => { + let state; + const customState = { ...mockIntegrationProps, type: 'CustomState' }; + const adminState = { ...mockIntegrationProps, type: 'AdminState' }; + + beforeEach(() => { + state = createState({ customState }); + }); + + describe('isInheriting', () => { + describe('when adminState is null', () => { + it('returns false', () => { + expect(isInheriting(state)).toBe(false); + }); + }); + + describe('when adminState is an object', () => { + beforeEach(() => { + state.adminState = adminState; + }); + + describe('when override is false', () => { + beforeEach(() => { + state.override = false; + }); + + it('returns false', () => { + expect(isInheriting(state)).toBe(true); + }); + }); + + describe('when override is true', () => { + beforeEach(() => { + state.override = true; + }); + + it('returns true', () => { + expect(isInheriting(state)).toBe(false); + }); + }); + }); + }); + + describe('propsSource', () => { + beforeEach(() => { + state.adminState = adminState; + }); + + it('equals adminState if inheriting', () => { + expect(propsSource(state, { isInheriting: true })).toEqual(adminState); + }); + + it('equals customState if not inheriting', () => { + expect(propsSource(state, { isInheriting: false })).toEqual(customState); + }); + }); + + describe('currentKey', () => { + it('equals `admin` if inheriting', () => { + expect(currentKey(state, { isInheriting: true })).toEqual('admin'); + }); + + it('equals `custom` if not inheriting', () => { + expect(currentKey(state, { isInheriting: false })).toEqual('custom'); + }); + }); +}); diff --git a/spec/frontend/integrations/edit/store/mutations_spec.js b/spec/frontend/integrations/edit/store/mutations_spec.js new file mode 100644 index 00000000000..4b733726d44 --- /dev/null +++ b/spec/frontend/integrations/edit/store/mutations_spec.js @@ -0,0 +1,19 @@ +import mutations from '~/integrations/edit/store/mutations'; +import createState from '~/integrations/edit/store/state'; +import * as types from '~/integrations/edit/store/mutation_types'; + +describe('Integration form store mutations', () => { + let state; + + beforeEach(() => { + state = createState(); + }); + + describe(`${types.SET_OVERRIDE}`, () => { + it('sets override', () => { + mutations[types.SET_OVERRIDE](state, true); + + expect(state.override).toBe(true); + }); + }); +}); diff --git a/spec/frontend/integrations/edit/store/state_spec.js b/spec/frontend/integrations/edit/store/state_spec.js new file mode 100644 index 00000000000..a8b431aa310 --- /dev/null +++ b/spec/frontend/integrations/edit/store/state_spec.js @@ -0,0 +1,26 @@ +import createState from '~/integrations/edit/store/state'; + +describe('Integration form state factory', () => { + it('states default to null', () => { + expect(createState()).toEqual({ + adminState: null, + customState: {}, + override: false, + }); + }); + + describe('override is initialized correctly', () => { + it.each([ + [{ id: 25 }, { inheritFromId: null }, true], + [{ id: 25 }, { inheritFromId: 27 }, true], + [{ id: 25 }, { inheritFromId: 25 }, false], + [null, { inheritFromId: null }, false], + [null, { inheritFromId: 25 }, false], + ])( + 'for adminState: %p, customState: %p: override = `%p`', + (adminState, customState, expected) => { + expect(createState({ adminState, customState }).override).toEqual(expected); + }, + ); + }); +}); diff --git a/spec/frontend/lib/utils/text_markdown_spec.js b/spec/frontend/lib/utils/text_markdown_spec.js index 1aaae80dcdf..2e52958a828 100644 --- a/spec/frontend/lib/utils/text_markdown_spec.js +++ b/spec/frontend/lib/utils/text_markdown_spec.js @@ -1,4 +1,4 @@ -import { insertMarkdownText, keypressNoteText } from '~/lib/utils/text_markdown'; +import { insertMarkdownText } from '~/lib/utils/text_markdown'; describe('init markdown', () => { let textArea; @@ -115,15 +115,14 @@ describe('init markdown', () => { describe('with selection', () => { const text = 'initial selected value'; const selected = 'selected'; - let selectedIndex; - beforeEach(() => { textArea.value = text; - selectedIndex = text.indexOf(selected); + const selectedIndex = text.indexOf(selected); textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length); }); it('applies the tag to the selected value', () => { + const selectedIndex = text.indexOf(selected); const tag = '*'; insertMarkdownText({ @@ -154,29 +153,6 @@ describe('init markdown', () => { expect(textArea.value).toEqual(text.replace(selected, `[${selected}](url)`)); }); - it.each` - key | expected - ${'['} | ${`[${selected}]`} - ${'*'} | ${`**${selected}**`} - ${"'"} | ${`'${selected}'`} - ${'_'} | ${`_${selected}_`} - ${'`'} | ${`\`${selected}\``} - ${'"'} | ${`"${selected}"`} - ${'{'} | ${`{${selected}}`} - ${'('} | ${`(${selected})`} - ${'<'} | ${`<${selected}>`} - `('generates $expected when $key is pressed', ({ key, expected }) => { - const event = new KeyboardEvent('keydown', { key }); - - textArea.addEventListener('keydown', keypressNoteText); - textArea.dispatchEvent(event); - - expect(textArea.value).toEqual(text.replace(selected, expected)); - - // cursor placement should be after selection + 2 tag lengths - expect(textArea.selectionStart).toBe(selectedIndex + expected.length); - }); - describe('and text to be selected', () => { const tag = '[{text}](url)'; const select = 'url'; @@ -202,7 +178,7 @@ describe('init markdown', () => { it('selects the right text when multiple tags are present', () => { const initialValue = `${tag} ${tag} ${selected}`; textArea.value = initialValue; - selectedIndex = initialValue.indexOf(selected); + const selectedIndex = initialValue.indexOf(selected); textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length); insertMarkdownText({ textArea, @@ -228,7 +204,7 @@ describe('init markdown', () => { const initialValue = `text ${expectedUrl} text`; textArea.value = initialValue; - selectedIndex = initialValue.indexOf(expectedUrl); + const selectedIndex = initialValue.indexOf(expectedUrl); textArea.setSelectionRange(selectedIndex, selectedIndex + expectedUrl.length); insertMarkdownText({ diff --git a/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js b/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js new file mode 100644 index 00000000000..3e11af9c9df --- /dev/null +++ b/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js @@ -0,0 +1,62 @@ +import { shallowMount } from '@vue/test-utils'; +import component from '~/reports/codequality_report/components/codequality_issue_body.vue'; +import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/reports/constants'; + +describe('code quality issue body issue body', () => { + let wrapper; + + const codequalityIssue = { + name: + 'rubygem-rest-client: session fixation vulnerability via Set-Cookie headers in 30x redirection responses', + path: 'Gemfile.lock', + severity: 'normal', + type: 'Issue', + urlPath: '/Gemfile.lock#L22', + }; + + const mountWithStatus = initialStatus => { + wrapper = shallowMount(component, { + propsData: { + issue: codequalityIssue, + status: initialStatus, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('with success', () => { + it('renders fixed label', () => { + mountWithStatus(STATUS_SUCCESS); + + expect(wrapper.text()).toContain('Fixed'); + }); + }); + + describe('without success', () => { + it('renders fixed label', () => { + mountWithStatus(STATUS_FAILED); + + expect(wrapper.text()).not.toContain('Fixed'); + }); + }); + + describe('name', () => { + it('renders name', () => { + mountWithStatus(STATUS_NEUTRAL); + + expect(wrapper.text()).toContain(codequalityIssue.name); + }); + }); + + describe('path', () => { + it('renders the report-link path using the correct code quality issue', () => { + mountWithStatus(STATUS_NEUTRAL); + + expect(wrapper.find('report-link-stub').props('issue')).toBe(codequalityIssue); + }); + }); +}); diff --git a/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js b/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js new file mode 100644 index 00000000000..1905ca0d5e1 --- /dev/null +++ b/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js @@ -0,0 +1,146 @@ +import { mount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import GroupedCodequalityReportsApp from '~/reports/codequality_report/grouped_codequality_reports_app.vue'; +import CodequalityIssueBody from '~/reports/codequality_report/components/codequality_issue_body.vue'; +import store from '~/reports/codequality_report/store'; +import { mockParsedHeadIssues, mockParsedBaseIssues } from './mock_data'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('Grouped code quality reports app', () => { + const Component = localVue.extend(GroupedCodequalityReportsApp); + let wrapper; + let mockStore; + + const mountComponent = (props = {}) => { + wrapper = mount(Component, { + store: mockStore, + localVue, + propsData: { + basePath: 'base.json', + headPath: 'head.json', + baseBlobPath: 'base/blob/path/', + headBlobPath: 'head/blob/path/', + codequalityHelpPath: 'codequality_help.html', + ...props, + }, + methods: { + fetchReports: () => {}, + }, + }); + }; + + const findWidget = () => wrapper.find('.js-codequality-widget'); + const findIssueBody = () => wrapper.find(CodequalityIssueBody); + + beforeEach(() => { + mockStore = store(); + mountComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when it is loading reports', () => { + beforeEach(() => { + mockStore.state.isLoading = true; + }); + + it('should render loading text', () => { + expect(findWidget().text()).toEqual('Loading codeclimate report'); + }); + }); + + describe('when base and head reports are loaded and compared', () => { + describe('with no issues', () => { + beforeEach(() => { + mockStore.state.newIssues = []; + mockStore.state.resolvedIssues = []; + }); + + it('renders no changes text', () => { + expect(findWidget().text()).toEqual('No changes to code quality'); + }); + }); + + describe('with issues', () => { + describe('with new issues', () => { + beforeEach(() => { + mockStore.state.newIssues = [mockParsedHeadIssues[0]]; + mockStore.state.resolvedIssues = []; + }); + + it('renders summary text', () => { + expect(findWidget().text()).toContain('Code quality degraded on 1 point'); + }); + + it('renders custom codequality issue body', () => { + expect(findIssueBody().props('issue')).toEqual(mockParsedHeadIssues[0]); + }); + }); + + describe('with resolved issues', () => { + beforeEach(() => { + mockStore.state.newIssues = []; + mockStore.state.resolvedIssues = [mockParsedBaseIssues[0]]; + }); + + it('renders summary text', () => { + expect(findWidget().text()).toContain('Code quality improved on 1 point'); + }); + + it('renders custom codequality issue body', () => { + expect(findIssueBody().props('issue')).toEqual(mockParsedBaseIssues[0]); + }); + }); + + describe('with new and resolved issues', () => { + beforeEach(() => { + mockStore.state.newIssues = [mockParsedHeadIssues[0]]; + mockStore.state.resolvedIssues = [mockParsedBaseIssues[0]]; + }); + + it('renders summary text', () => { + expect(findWidget().text()).toContain( + 'Code quality improved on 1 point and degraded on 1 point', + ); + }); + + it('renders custom codequality issue body', () => { + expect(findIssueBody().props('issue')).toEqual(mockParsedHeadIssues[0]); + }); + }); + }); + }); + + describe('when there is a head report but no base report', () => { + beforeEach(() => { + mockStore.state.basePath = null; + mockStore.state.hasError = true; + }); + + it('renders error text', () => { + expect(findWidget().text()).toEqual('Failed to load codeclimate report'); + }); + + it('renders a help icon with more information', () => { + expect(findWidget().html()).toContain('ic-question'); + }); + }); + + describe('on error', () => { + beforeEach(() => { + mockStore.state.hasError = true; + }); + + it('renders error text', () => { + expect(findWidget().text()).toContain('Failed to load codeclimate report'); + }); + + it('does not render a help icon', () => { + expect(findWidget().html()).not.toContain('ic-question'); + }); + }); +}); diff --git a/spec/frontend/vue_mr_widget/mock_data.js b/spec/frontend/vue_mr_widget/mock_data.js index 18665da6b48..e00456a78b5 100644 --- a/spec/frontend/vue_mr_widget/mock_data.js +++ b/spec/frontend/vue_mr_widget/mock_data.js @@ -211,6 +211,15 @@ export default { can_revert_on_current_merge_request: true, can_cherry_pick_on_current_merge_request: true, }, + codeclimate: { + head_path: 'head.json', + base_path: 'base.json', + }, + blob_path: { + base_path: 'blob_path', + head_path: 'blob_path', + }, + codequality_help_path: 'code_quality.html', target_branch_path: '/root/acets-app/branches/master', source_branch_path: '/root/acets-app/branches/daaaa', conflict_resolution_ui_path: '/root/acets-app/-/merge_requests/22/conflicts', diff --git a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js index e022f68fdec..93659fa54fb 100644 --- a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js @@ -609,6 +609,12 @@ describe('mrWidgetOptions', () => { }); }); + describe('code quality widget', () => { + it('renders the component', () => { + expect(vm.$el.querySelector('.js-codequality-widget')).toExist(); + }); + }); + describe('pipeline for target branch after merge', () => { describe('with information for target branch pipeline', () => { beforeEach(done => { diff --git a/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js b/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js index e54cd345a37..1cb2c6c669b 100644 --- a/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js +++ b/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js @@ -49,14 +49,18 @@ describe('getStateKey', () => { expect(bound()).toEqual('unresolvedDiscussions'); + data.work_in_progress = true; + + expect(bound()).toEqual('workInProgress'); + context.onlyAllowMergeIfPipelineSucceeds = true; context.isPipelineFailed = true; expect(bound()).toEqual('pipelineFailed'); - data.work_in_progress = true; + context.shouldBeRebased = true; - expect(bound()).toEqual('workInProgress'); + expect(bound()).toEqual('rebase'); data.has_conflicts = true; diff --git a/spec/helpers/services_helper_spec.rb b/spec/helpers/services_helper_spec.rb index 37256c1b387..10d6ec7b6a7 100644 --- a/spec/helpers/services_helper_spec.rb +++ b/spec/helpers/services_helper_spec.rb @@ -7,4 +7,28 @@ RSpec.describe ServicesHelper do it { expect(event_action_title('comment')).to eq 'Comment' } it { expect(event_action_title('something')).to eq 'Something' } end + + describe '#integration_form_data' do + subject { helper.integration_form_data(integration) } + + context 'Jira service' do + let(:integration) { build(:jira_service) } + + it 'includes Jira specific fields' do + is_expected.to include( + :id, + :show_active, + :activated, + :type, + :merge_request_events, + :commit_events, + :enable_comments, + :comment_detail, + :trigger_events, + :fields, + :inherit_from_id + ) + end + end + end end diff --git a/spec/lib/gitlab/jira_import/issue_serializer_spec.rb b/spec/lib/gitlab/jira_import/issue_serializer_spec.rb index 684040f4e4b..4adc4e4d22a 100644 --- a/spec/lib/gitlab/jira_import/issue_serializer_spec.rb +++ b/spec/lib/gitlab/jira_import/issue_serializer_spec.rb @@ -10,6 +10,7 @@ RSpec.describe Gitlab::JiraImport::IssueSerializer do let_it_be(:other_project_label) { create(:label, project: project, title: 'feature') } let_it_be(:group_label) { create(:group_label, group: group, title: 'dev') } let_it_be(:current_user) { create(:user) } + let_it_be(:user) { create(:user) } let(:iid) { 5 } let(:key) { 'PROJECT-5' } @@ -17,8 +18,8 @@ RSpec.describe Gitlab::JiraImport::IssueSerializer do let(:description) { 'basic description' } let(:created_at) { '2020-01-01 20:00:00' } let(:updated_at) { '2020-01-10 20:00:00' } - let(:assignee) { double(attrs: { 'displayName' => 'Solver', 'emailAddress' => 'assignee@example.com' }) } - let(:reporter) { double(attrs: { 'displayName' => 'Reporter', 'emailAddress' => 'reporter@example.com' }) } + let(:assignee) { nil } + let(:reporter) { nil } let(:jira_status) { 'new' } let(:parent_field) do @@ -109,11 +110,12 @@ RSpec.describe Gitlab::JiraImport::IssueSerializer do end context 'author' do - context 'when reporter maps to a valid GitLab user' do - let!(:user) { create(:user, email: 'reporter@example.com') } + let(:reporter) { double(attrs: { 'displayName' => 'Solver', 'accountId' => 'abcd' }) } + context 'when reporter maps to a valid GitLab user' do it 'sets the issue author to the mapped user' do - project.add_developer(user) + expect(Gitlab::JiraImport).to receive(:get_user_mapping).with(project.id, 'abcd') + .and_return(user.id) expect(subject[:author_id]).to eq(user.id) end @@ -121,6 +123,9 @@ RSpec.describe Gitlab::JiraImport::IssueSerializer do context 'when reporter does not map to a valid Gitlab user' do it 'defaults the issue author to project creator' do + expect(Gitlab::JiraImport).to receive(:get_user_mapping).with(project.id, 'abcd') + .and_return(nil) + expect(subject[:author_id]).to eq(current_user.id) end end @@ -129,25 +134,30 @@ RSpec.describe Gitlab::JiraImport::IssueSerializer do let(:reporter) { nil } it 'defaults the issue author to project creator' do + expect(Gitlab::JiraImport).not_to receive(:get_user_mapping) + expect(subject[:author_id]).to eq(current_user.id) end end - context 'when reporter field is missing email address' do + context 'when reporter field is missing accountId' do let(:reporter) { double(attrs: { 'displayName' => 'Reporter' }) } it 'defaults the issue author to project creator' do + expect(Gitlab::JiraImport).not_to receive(:get_user_mapping) + expect(subject[:author_id]).to eq(current_user.id) end end end context 'assignee' do - context 'when assignee maps to a valid GitLab user' do - let!(:user) { create(:user, email: 'assignee@example.com') } + let(:assignee) { double(attrs: { 'displayName' => 'Solver', 'accountId' => '1234' }) } + context 'when assignee maps to a valid GitLab user' do it 'sets the issue assignees to the mapped user' do - project.add_developer(user) + expect(Gitlab::JiraImport).to receive(:get_user_mapping).with(project.id, '1234') + .and_return(user.id) expect(subject[:assignee_ids]).to eq([user.id]) end @@ -155,6 +165,9 @@ RSpec.describe Gitlab::JiraImport::IssueSerializer do context 'when assignee does not map to a valid GitLab user' do it 'leaves the assignee empty' do + expect(Gitlab::JiraImport).to receive(:get_user_mapping).with(project.id, '1234') + .and_return(nil) + expect(subject[:assignee_ids]).to be_nil end end @@ -163,14 +176,18 @@ RSpec.describe Gitlab::JiraImport::IssueSerializer do let(:assignee) { nil } it 'leaves the assignee empty' do + expect(Gitlab::JiraImport).not_to receive(:get_user_mapping) + expect(subject[:assignee_ids]).to be_nil end end - context 'when assginee field is missing email address' do - let(:assignee) { double(attrs: { 'displayName' => 'Reporter' }) } + context 'when assginee field is missing accountId' do + let(:assignee) { double(attrs: { 'displayName' => 'Solver' }) } it 'leaves the assignee empty' do + expect(Gitlab::JiraImport).not_to receive(:get_user_mapping) + expect(subject[:assignee_ids]).to be_nil end end diff --git a/spec/lib/gitlab/jira_import/user_mapper_spec.rb b/spec/lib/gitlab/jira_import/user_mapper_spec.rb deleted file mode 100644 index 576667caeca..00000000000 --- a/spec/lib/gitlab/jira_import/user_mapper_spec.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::JiraImport::UserMapper do - let_it_be(:group) { create(:group) } - let_it_be(:project) { create(:project, group: group) } - let_it_be(:user) { create(:user, email: 'user@example.com') } - let_it_be(:email) { create(:email, user: user, email: 'second_email@example.com', confirmed_at: nil) } - - let(:jira_user) { { 'acountId' => '1a2b', 'emailAddress' => 'user@example.com' } } - - describe '#execute' do - subject { described_class.new(project, jira_user).execute } - - context 'when jira_user is nil' do - let(:jira_user) { nil } - - it 'returns nil' do - expect(subject).to be_nil - end - end - - context 'when Gitlab user is not found by email' do - let(:jira_user) { { 'acountId' => '1a2b', 'emailAddress' => 'other@example.com' } } - - it 'returns nil' do - expect(subject).to be_nil - end - end - - context 'when jira_user emailAddress is nil' do - let(:jira_user) { { 'acountId' => '1a2b', 'emailAddress' => nil } } - - it 'returns nil' do - expect(subject).to be_nil - end - end - - context 'when jira_user emailAddress key is missing' do - let(:jira_user) { { 'acountId' => '1a2b' } } - - it 'returns nil' do - expect(subject).to be_nil - end - end - - context 'when found user is not a project member' do - it 'returns nil' do - expect(subject).to be_nil - end - end - - context 'when found user is a project member' do - it 'returns the found user' do - project.add_developer(user) - - expect(subject).to eq(user) - end - end - - context 'when user found by unconfirmd secondary address is a project member' do - let(:jira_user) { { 'acountId' => '1a2b', 'emailAddress' => 'second_email@example.com' } } - - it 'returns the found user' do - project.add_developer(user) - - expect(subject).to eq(user) - end - end - - context 'when user is a group member' do - it 'returns the found user' do - group.add_developer(user) - - expect(subject).to eq(user) - end - end - end -end diff --git a/spec/lib/gitlab/sidekiq_versioning/middleware_spec.rb b/spec/lib/gitlab/sidekiq_versioning/middleware_spec.rb new file mode 100644 index 00000000000..d054e034250 --- /dev/null +++ b/spec/lib/gitlab/sidekiq_versioning/middleware_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::SidekiqVersioning::Middleware do + let(:worker_class) do + Class.new do + def self.name + 'DummyWorker' + end + + include ApplicationWorker + + version 2 + end + end + + describe '#call' do + let(:worker) { worker_class.new } + let(:job) { { 'version' => 3, 'queue' => queue } } + let(:queue) { worker_class.queue } + + def call!(&block) + block ||= -> {} + subject.call(worker, job, queue, &block) + end + + it 'sets worker.job_version' do + call! + + expect(worker.job_version).to eq(job['version']) + end + + it 'yields' do + expect { |b| call!(&b) }.to yield_control + end + end +end diff --git a/spec/lib/gitlab/sidekiq_versioning/worker_spec.rb b/spec/lib/gitlab/sidekiq_versioning/worker_spec.rb new file mode 100644 index 00000000000..0781c5100fd --- /dev/null +++ b/spec/lib/gitlab/sidekiq_versioning/worker_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::SidekiqVersioning::Worker do + let(:worker) do + Class.new do + def self.name + 'DummyWorker' + end + + # ApplicationWorker includes Gitlab::SidekiqVersioning::Worker + include ApplicationWorker + + version 2 + end + end + + describe '.version' do + context 'when called with an argument' do + it 'sets the version option' do + worker.version 3 + + expect(worker.get_sidekiq_options['version']).to eq(3) + end + end + + context 'when called without an argument' do + it 'returns the version option' do + worker.sidekiq_options version: 3 + + expect(worker.version).to eq(3) + end + end + end + + describe '#job_version' do + let(:job) { worker.new } + + context 'when job_version is not set' do + it 'returns latest version' do + expect(job.job_version).to eq(2) + end + end + + context 'when job_version is set' do + it 'returns the set version' do + job.job_version = 0 + + expect(job.job_version).to eq(0) + end + end + end +end diff --git a/spec/lib/gitlab/sidekiq_versioning_spec.rb b/spec/lib/gitlab/sidekiq_versioning_spec.rb index ed9650fc166..9599f2deed6 100644 --- a/spec/lib/gitlab/sidekiq_versioning_spec.rb +++ b/spec/lib/gitlab/sidekiq_versioning_spec.rb @@ -35,6 +35,12 @@ RSpec.describe Gitlab::SidekiqVersioning, :redis do expect(Sidekiq::Manager).to include(Gitlab::SidekiqVersioning::Manager) end + it 'adds the SidekiqVersioning::Middleware Sidekiq server middleware' do + described_class.install! + + expect(Sidekiq.server_middleware.entries.map(&:klass)).to include(Gitlab::SidekiqVersioning::Middleware) + end + it 'registers all versionless and versioned queues with Redis' do described_class.install! diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index 8c914c6b74c..75bbb074526 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -386,6 +386,33 @@ RSpec.describe Service do end end + describe 'instance' do + describe '.instance_for' do + let_it_be(:jira_service) { create(:jira_service, :instance) } + let_it_be(:slack_service) { create(:slack_service, :instance) } + + subject { described_class.instance_for(type) } + + context 'Hipchat serivce' do + let(:type) { 'HipchatService' } + + it { is_expected.to eq(nil) } + end + + context 'Jira serivce' do + let(:type) { 'JiraService' } + + it { is_expected.to eq(jira_service) } + end + + context 'Slack serivce' do + let(:type) { 'SlackService' } + + it { is_expected.to eq(slack_service) } + end + end + end + describe "{property}_changed?" do let(:service) do BambooService.create( |