summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-17 03:09:14 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-17 03:09:14 +0000
commit4bc0e064023a13d90da5acc4fd152fca66926ea2 (patch)
treebebd92d2f5392f8c1bd785fbe14b3b3bf03fc5d1 /spec
parentd5cf5cf4f77eec07a04604b1a0298452029df16f (diff)
downloadgitlab-ce-4bc0e064023a13d90da5acc4fd152fca66926ea2.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/services_controller_spec.rb17
-rw-r--r--spec/features/projects/services/disable_triggers_spec.rb6
-rw-r--r--spec/frontend/blob/components/blob_header_viewer_switcher_spec.js6
-rw-r--r--spec/frontend/integrations/edit/components/active_toggle_spec.js18
-rw-r--r--spec/frontend/integrations/edit/components/dynamic_field_spec.js194
-rw-r--r--spec/frontend/integrations/edit/components/integration_form_spec.js58
-rw-r--r--spec/frontend/integrations/edit/components/jira_issues_fields_spec.js4
-rw-r--r--spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js25
-rw-r--r--spec/frontend/integrations/edit/components/trigger_fields_spec.js51
-rw-r--r--spec/frontend/integrations/edit/mock_data.js18
-rw-r--r--spec/frontend/integrations/edit/store/actions_spec.js19
-rw-r--r--spec/frontend/integrations/edit/store/getters_spec.js71
-rw-r--r--spec/frontend/integrations/edit/store/mutations_spec.js19
-rw-r--r--spec/frontend/integrations/edit/store/state_spec.js26
-rw-r--r--spec/frontend/lib/utils/text_markdown_spec.js34
-rw-r--r--spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js62
-rw-r--r--spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js146
-rw-r--r--spec/frontend/vue_mr_widget/mock_data.js9
-rw-r--r--spec/frontend/vue_mr_widget/mr_widget_options_spec.js6
-rw-r--r--spec/frontend/vue_mr_widget/stores/get_state_key_spec.js8
-rw-r--r--spec/helpers/services_helper_spec.rb24
-rw-r--r--spec/lib/gitlab/jira_import/issue_serializer_spec.rb39
-rw-r--r--spec/lib/gitlab/jira_import/user_mapper_spec.rb80
-rw-r--r--spec/lib/gitlab/sidekiq_versioning/middleware_spec.rb38
-rw-r--r--spec/lib/gitlab/sidekiq_versioning/worker_spec.rb54
-rw-r--r--spec/lib/gitlab/sidekiq_versioning_spec.rb6
-rw-r--r--spec/models/service_spec.rb27
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(