diff options
Diffstat (limited to 'spec')
15 files changed, 391 insertions, 286 deletions
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb index 8f516de3322..3684a1bb8d8 100644 --- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb @@ -157,6 +157,14 @@ describe Projects::Settings::CiCdController do subject end + + it 'creates a pipeline', :sidekiq_inline do + project.repository.create_file(user, 'Gemfile', 'Gemfile contents', + message: 'Add Gemfile', + branch_name: 'master') + + expect { subject }.to change { Ci::Pipeline.count }.by(1) + end end end diff --git a/spec/frontend/ide/components/commit_sidebar/actions_spec.js b/spec/frontend/ide/components/commit_sidebar/actions_spec.js new file mode 100644 index 00000000000..b3b98a64891 --- /dev/null +++ b/spec/frontend/ide/components/commit_sidebar/actions_spec.js @@ -0,0 +1,141 @@ +import Vue from 'vue'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; +import { projectData, branches } from 'jest/ide/mock_data'; +import { createStore } from '~/ide/stores'; +import commitActions from '~/ide/components/commit_sidebar/actions.vue'; +import consts from '~/ide/stores/modules/commit/constants'; + +const ACTION_UPDATE_COMMIT_ACTION = 'commit/updateCommitAction'; + +const BRANCH_DEFAULT = 'master'; +const BRANCH_PROTECTED = 'protected/access'; +const BRANCH_PROTECTED_NO_ACCESS = 'protected/no-access'; +const BRANCH_REGULAR = 'regular'; +const BRANCH_REGULAR_NO_ACCESS = 'regular/no-access'; + +describe('IDE commit sidebar actions', () => { + let store; + let vm; + + const createComponent = ({ hasMR = false, currentBranchId = 'master' } = {}) => { + const Component = Vue.extend(commitActions); + + vm = createComponentWithStore(Component, store); + + vm.$store.state.currentBranchId = currentBranchId; + vm.$store.state.currentProjectId = 'abcproject'; + + const proj = { ...projectData }; + proj.branches[currentBranchId] = branches.find(branch => branch.name === currentBranchId); + + Vue.set(vm.$store.state.projects, 'abcproject', proj); + + if (hasMR) { + vm.$store.state.currentMergeRequestId = '1'; + vm.$store.state.projects[store.state.currentProjectId].mergeRequests[ + store.state.currentMergeRequestId + ] = { foo: 'bar' }; + } + + vm.$mount(); + + return vm; + }; + + beforeEach(() => { + store = createStore(); + jest.spyOn(store, 'dispatch').mockImplementation(() => {}); + }); + + afterEach(() => { + vm.$destroy(); + vm = null; + }); + + it('renders 2 groups', () => { + createComponent(); + + expect(vm.$el.querySelectorAll('input[type="radio"]').length).toBe(2); + }); + + it('renders current branch text', () => { + createComponent(); + + expect(vm.$el.textContent).toContain('Commit to master branch'); + }); + + it('hides merge request option when project merge requests are disabled', done => { + createComponent({ mergeRequestsEnabled: false }); + + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('input[type="radio"]').length).toBe(2); + expect(vm.$el.textContent).not.toContain('Create a new branch and merge request'); + + done(); + }); + }); + + describe('commitToCurrentBranchText', () => { + it('escapes current branch', () => { + const injectedSrc = '<img src="x" />'; + createComponent({ currentBranchId: injectedSrc }); + + expect(vm.commitToCurrentBranchText).not.toContain(injectedSrc); + }); + }); + + describe('updateSelectedCommitAction', () => { + it('does not return anything if currentBranch does not exist', () => { + createComponent({ currentBranchId: null }); + + expect(vm.$store.dispatch).not.toHaveBeenCalled(); + }); + + it('is not called on mount if there is already a selected commitAction', () => { + store.state.commitAction = '1'; + createComponent({ currentBranchId: null }); + + expect(vm.$store.dispatch).not.toHaveBeenCalled(); + }); + + it('calls again after staged changes', done => { + createComponent({ currentBranchId: null }); + + vm.$store.state.currentBranchId = 'master'; + vm.$store.state.changedFiles.push({}); + vm.$store.state.stagedFiles.push({}); + + vm.$nextTick() + .then(() => { + expect(vm.$store.dispatch).toHaveBeenCalledWith( + ACTION_UPDATE_COMMIT_ACTION, + expect.anything(), + ); + }) + .then(done) + .catch(done.fail); + }); + + it.each` + input | expectedOption + ${{ currentBranchId: BRANCH_DEFAULT }} | ${consts.COMMIT_TO_NEW_BRANCH} + ${{ currentBranchId: BRANCH_PROTECTED, hasMR: true }} | ${consts.COMMIT_TO_CURRENT_BRANCH} + ${{ currentBranchId: BRANCH_PROTECTED, hasMR: false }} | ${consts.COMMIT_TO_CURRENT_BRANCH} + ${{ currentBranchId: BRANCH_PROTECTED_NO_ACCESS, hasMR: true }} | ${consts.COMMIT_TO_NEW_BRANCH} + ${{ currentBranchId: BRANCH_PROTECTED_NO_ACCESS, hasMR: false }} | ${consts.COMMIT_TO_NEW_BRANCH} + ${{ currentBranchId: BRANCH_REGULAR, hasMR: true }} | ${consts.COMMIT_TO_CURRENT_BRANCH} + ${{ currentBranchId: BRANCH_REGULAR, hasMR: false }} | ${consts.COMMIT_TO_CURRENT_BRANCH} + ${{ currentBranchId: BRANCH_REGULAR_NO_ACCESS, hasMR: true }} | ${consts.COMMIT_TO_NEW_BRANCH} + ${{ currentBranchId: BRANCH_REGULAR_NO_ACCESS, hasMR: false }} | ${consts.COMMIT_TO_NEW_BRANCH} + `( + 'with $input, it dispatches update commit action with $expectedOption', + ({ input, expectedOption }) => { + createComponent(input); + + expect(vm.$store.dispatch.mock.calls).toEqual([ + [ACTION_UPDATE_COMMIT_ACTION, expectedOption], + ]); + }, + ); + }); +}); diff --git a/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js b/spec/frontend/ide/components/commit_sidebar/empty_state_spec.js index 16d0b354a30..16d0b354a30 100644 --- a/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/empty_state_spec.js diff --git a/spec/javascripts/ide/components/commit_sidebar/form_spec.js b/spec/frontend/ide/components/commit_sidebar/form_spec.js index f5d1a9de59c..dfde69ab2df 100644 --- a/spec/javascripts/ide/components/commit_sidebar/form_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/form_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper'; -import { projectData } from 'spec/ide/mock_data'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { projectData } from 'jest/ide/mock_data'; import store from '~/ide/stores'; import CommitForm from '~/ide/components/commit_sidebar/form.vue'; import { leftSidebarViews } from '~/ide/constants'; @@ -12,8 +12,6 @@ describe('IDE commit form', () => { let vm; beforeEach(() => { - spyOnProperty(window, 'innerHeight').and.returnValue(800); - store.state.changedFiles.push('test'); store.state.currentProjectId = 'abcproject'; store.state.currentBranchId = 'master'; @@ -111,7 +109,7 @@ describe('IDE commit form', () => { textarea.dispatchEvent(new Event('input')); - getSetTimeoutPromise() + waitForPromises() .then(() => { expect(vm.$store.state.commit.commitMessage).toBe('testing commit message'); }) @@ -148,7 +146,7 @@ describe('IDE commit form', () => { it('resets commitMessage when clicking discard button', done => { vm.$store.state.commit.commitMessage = 'testing commit message'; - getSetTimeoutPromise() + waitForPromises() .then(() => { vm.$el.querySelector('.btn-default').click(); }) @@ -163,14 +161,14 @@ describe('IDE commit form', () => { describe('when submitting', () => { beforeEach(() => { - spyOn(vm, 'commitChanges'); + jest.spyOn(vm, 'commitChanges').mockImplementation(() => {}); vm.$store.state.stagedFiles.push('test'); }); it('calls commitChanges', done => { vm.$store.state.commit.commitMessage = 'testing commit message'; - getSetTimeoutPromise() + waitForPromises() .then(() => { vm.$el.querySelector('.btn-success').click(); }) diff --git a/spec/javascripts/ide/components/commit_sidebar/list_collapsed_spec.js b/spec/frontend/ide/components/commit_sidebar/list_collapsed_spec.js index 6eb912127d5..45372d18965 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_collapsed_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/list_collapsed_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import store from '~/ide/stores'; import listCollapsed from '~/ide/components/commit_sidebar/list_collapsed.vue'; import { file } from '../../helpers'; diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/frontend/ide/components/commit_sidebar/list_item_spec.js index 63ba6b95619..ebb41448905 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/list_item_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import { trimText } from 'spec/helpers/text_helper'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { trimText } from 'helpers/text_helper'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import store from '~/ide/stores'; import listItem from '~/ide/components/commit_sidebar/list_item.vue'; import router from '~/ide/ide_router'; @@ -61,12 +61,12 @@ describe('Multi-file editor commit sidebar list item', () => { }); it('opens a closed file in the editor when clicking the file path', done => { - spyOn(vm, 'openPendingTab').and.callThrough(); - spyOn(router, 'push'); + jest.spyOn(vm, 'openPendingTab'); + jest.spyOn(router, 'push').mockImplementation(() => {}); findPathEl.click(); - setTimeout(() => { + setImmediate(() => { expect(vm.openPendingTab).toHaveBeenCalled(); expect(router.push).toHaveBeenCalled(); @@ -75,13 +75,13 @@ describe('Multi-file editor commit sidebar list item', () => { }); it('calls updateViewer with diff when clicking file', done => { - spyOn(vm, 'openFileInEditor').and.callThrough(); - spyOn(vm, 'updateViewer').and.callThrough(); - spyOn(router, 'push'); + jest.spyOn(vm, 'openFileInEditor'); + jest.spyOn(vm, 'updateViewer'); + jest.spyOn(router, 'push').mockImplementation(() => {}); findPathEl.click(); - setTimeout(() => { + setImmediate(() => { expect(vm.updateViewer).toHaveBeenCalledWith('diff'); done(); diff --git a/spec/javascripts/ide/components/commit_sidebar/list_spec.js b/spec/frontend/ide/components/commit_sidebar/list_spec.js index 5a1682523d8..ee209487665 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/list_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import store from '~/ide/stores'; import commitSidebarList from '~/ide/components/commit_sidebar/list.vue'; import { file, resetStore } from '../../helpers'; diff --git a/spec/javascripts/ide/components/commit_sidebar/new_merge_request_option_spec.js b/spec/frontend/ide/components/commit_sidebar/new_merge_request_option_spec.js index 7c0b4000229..7cbf5ebc61a 100644 --- a/spec/javascripts/ide/components/commit_sidebar/new_merge_request_option_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/new_merge_request_option_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import { projectData, branches } from 'spec/ide/mock_data'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; +import { projectData, branches } from 'jest/ide/mock_data'; import NewMergeRequestOption from '~/ide/components/commit_sidebar/new_merge_request_option.vue'; import { createStore } from '~/ide/stores'; import { PERMISSION_CREATE_MR } from '~/ide/constants'; @@ -200,11 +200,11 @@ describe('create new MR checkbox', () => { currentBranchId: 'regular', }); const el = vm.$el.querySelector('input[type="checkbox"]'); - spyOn(vm.$store, 'dispatch'); + jest.spyOn(vm.$store, 'dispatch').mockImplementation(() => {}); el.dispatchEvent(new Event('change')); - expect(vm.$store.dispatch.calls.allArgs()).toEqual( - jasmine.arrayContaining([['commit/toggleShouldCreateMR', jasmine.any(Object)]]), + expect(vm.$store.dispatch.mock.calls).toEqual( + expect.arrayContaining([['commit/toggleShouldCreateMR', expect.any(Object)]]), ); }); }); diff --git a/spec/javascripts/ide/components/commit_sidebar/success_message_spec.js b/spec/frontend/ide/components/commit_sidebar/success_message_spec.js index e1a432b81be..e1a432b81be 100644 --- a/spec/javascripts/ide/components/commit_sidebar/success_message_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/success_message_spec.js diff --git a/spec/javascripts/ide/components/commit_sidebar/actions_spec.js b/spec/javascripts/ide/components/commit_sidebar/actions_spec.js deleted file mode 100644 index a8e6195a67c..00000000000 --- a/spec/javascripts/ide/components/commit_sidebar/actions_spec.js +++ /dev/null @@ -1,235 +0,0 @@ -import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import { projectData, branches } from 'spec/ide/mock_data'; -import { createStore } from '~/ide/stores'; -import commitActions from '~/ide/components/commit_sidebar/actions.vue'; -import consts from '~/ide/stores/modules/commit/constants'; - -const ACTION_UPDATE_COMMIT_ACTION = 'commit/updateCommitAction'; - -describe('IDE commit sidebar actions', () => { - let store; - let vm; - - const createComponent = ({ hasMR = false, currentBranchId = 'master' } = {}) => { - const Component = Vue.extend(commitActions); - - vm = createComponentWithStore(Component, store); - - vm.$store.state.currentBranchId = currentBranchId; - vm.$store.state.currentProjectId = 'abcproject'; - - const proj = { ...projectData }; - proj.branches[currentBranchId] = branches.find(branch => branch.name === currentBranchId); - - Vue.set(vm.$store.state.projects, 'abcproject', proj); - - if (hasMR) { - vm.$store.state.currentMergeRequestId = '1'; - vm.$store.state.projects[store.state.currentProjectId].mergeRequests[ - store.state.currentMergeRequestId - ] = { foo: 'bar' }; - } - - vm.$mount(); - - return vm; - }; - - beforeEach(() => { - store = createStore(); - spyOn(store, 'dispatch'); - }); - - afterEach(() => { - vm.$destroy(); - vm = null; - }); - - it('renders 2 groups', () => { - createComponent(); - - expect(vm.$el.querySelectorAll('input[type="radio"]').length).toBe(2); - }); - - it('renders current branch text', () => { - createComponent(); - - expect(vm.$el.textContent).toContain('Commit to master branch'); - }); - - it('hides merge request option when project merge requests are disabled', done => { - createComponent({ mergeRequestsEnabled: false }); - - vm.$nextTick(() => { - expect(vm.$el.querySelectorAll('input[type="radio"]').length).toBe(2); - expect(vm.$el.textContent).not.toContain('Create a new branch and merge request'); - - done(); - }); - }); - - describe('commitToCurrentBranchText', () => { - it('escapes current branch', () => { - const injectedSrc = '<img src="x" />'; - createComponent({ currentBranchId: injectedSrc }); - - expect(vm.commitToCurrentBranchText).not.toContain(injectedSrc); - }); - }); - - describe('updateSelectedCommitAction', () => { - it('does not return anything if currentBranch does not exist', () => { - createComponent({ currentBranchId: null }); - - expect(vm.$store.dispatch).not.toHaveBeenCalled(); - }); - - it('is not called on mount if there is already a selected commitAction', () => { - store.state.commitAction = '1'; - createComponent({ currentBranchId: null }); - - expect(vm.$store.dispatch).not.toHaveBeenCalled(); - }); - - it('calls again after staged changes', done => { - createComponent({ currentBranchId: null }); - - vm.$store.state.currentBranchId = 'master'; - vm.$store.state.changedFiles.push({}); - vm.$store.state.stagedFiles.push({}); - - vm.$nextTick() - .then(() => { - expect(vm.$store.dispatch).toHaveBeenCalledWith( - ACTION_UPDATE_COMMIT_ACTION, - jasmine.anything(), - ); - }) - .then(done) - .catch(done.fail); - }); - - describe('default branch', () => { - it('dispatches correct action for default branch', () => { - createComponent({ - currentBranchId: 'master', - }); - - expect(vm.$store.dispatch).toHaveBeenCalledTimes(1); - expect(vm.$store.dispatch).toHaveBeenCalledWith( - ACTION_UPDATE_COMMIT_ACTION, - consts.COMMIT_TO_NEW_BRANCH, - ); - }); - }); - - describe('protected branch', () => { - describe('with write access', () => { - it('dispatches correct action when MR exists', () => { - createComponent({ - hasMR: true, - currentBranchId: 'protected/access', - }); - - expect(vm.$store.dispatch).toHaveBeenCalledWith( - ACTION_UPDATE_COMMIT_ACTION, - consts.COMMIT_TO_CURRENT_BRANCH, - ); - }); - - it('dispatches correct action when MR does not exists', () => { - createComponent({ - hasMR: false, - currentBranchId: 'protected/access', - }); - - expect(vm.$store.dispatch).toHaveBeenCalledWith( - ACTION_UPDATE_COMMIT_ACTION, - consts.COMMIT_TO_CURRENT_BRANCH, - ); - }); - }); - - describe('without write access', () => { - it('dispatches correct action when MR exists', () => { - createComponent({ - hasMR: true, - currentBranchId: 'protected/no-access', - }); - - expect(vm.$store.dispatch).toHaveBeenCalledWith( - ACTION_UPDATE_COMMIT_ACTION, - consts.COMMIT_TO_NEW_BRANCH, - ); - }); - - it('dispatches correct action when MR does not exists', () => { - createComponent({ - hasMR: false, - currentBranchId: 'protected/no-access', - }); - - expect(vm.$store.dispatch).toHaveBeenCalledWith( - ACTION_UPDATE_COMMIT_ACTION, - consts.COMMIT_TO_NEW_BRANCH, - ); - }); - }); - }); - - describe('regular branch', () => { - describe('with write access', () => { - it('dispatches correct action when MR exists', () => { - createComponent({ - hasMR: true, - currentBranchId: 'regular', - }); - - expect(vm.$store.dispatch).toHaveBeenCalledWith( - ACTION_UPDATE_COMMIT_ACTION, - consts.COMMIT_TO_CURRENT_BRANCH, - ); - }); - - it('dispatches correct action when MR does not exists', () => { - createComponent({ - hasMR: false, - currentBranchId: 'regular', - }); - - expect(vm.$store.dispatch).toHaveBeenCalledWith( - ACTION_UPDATE_COMMIT_ACTION, - consts.COMMIT_TO_CURRENT_BRANCH, - ); - }); - }); - - describe('without write access', () => { - it('dispatches correct action when MR exists', () => { - createComponent({ - hasMR: true, - currentBranchId: 'regular/no-access', - }); - - expect(vm.$store.dispatch).toHaveBeenCalledWith( - ACTION_UPDATE_COMMIT_ACTION, - consts.COMMIT_TO_NEW_BRANCH, - ); - }); - - it('dispatches correct action when MR does not exists', () => { - createComponent({ - hasMR: false, - currentBranchId: 'regular/no-access', - }); - - expect(vm.$store.dispatch).toHaveBeenCalledWith( - ACTION_UPDATE_COMMIT_ACTION, - consts.COMMIT_TO_NEW_BRANCH, - ); - }); - }); - }); - }); -}); diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 62adba4319e..0b34e887716 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -1647,6 +1647,48 @@ module Gitlab it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, /is not defined in prior stages/) } end + + context 'when trigger job includes artifact generated by a dependency' do + context 'when dependency is defined in previous stages' do + let(:config) do + { + build1: { stage: 'build', script: 'test' }, + test1: { stage: 'test', trigger: { + include: [{ job: 'build1', artifact: 'generated.yml' }] + } } + } + end + + it { expect { subject }.not_to raise_error } + end + + context 'when dependency is defined in later stages' do + let(:config) do + { + build1: { stage: 'build', script: 'test' }, + test1: { stage: 'test', trigger: { + include: [{ job: 'deploy1', artifact: 'generated.yml' }] + } }, + deploy1: { stage: 'deploy', script: 'test' } + } + end + + it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, /is not defined in prior stages/) } + end + + context 'when dependency is not defined' do + let(:config) do + { + build1: { stage: 'build', script: 'test' }, + test1: { stage: 'test', trigger: { + include: [{ job: 'non-existent', artifact: 'generated.yml' }] + } } + } + end + + it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, /undefined dependency: non-existent/) } + end + end end describe "Job Needs" do @@ -2052,6 +2094,34 @@ module Gitlab end end + describe 'with trigger:include' do + context 'when artifact and job are specified' do + let(:config) do + YAML.dump({ + build1: { stage: 'build', script: 'test' }, + test1: { stage: 'test', trigger: { + include: [{ artifact: 'generated.yml', job: 'build1' }] + } } + }) + end + + it { expect { subject }.not_to raise_error } + end + + context 'when artifact is specified without job' do + let(:config) do + YAML.dump({ + build1: { stage: 'build', script: 'test' }, + test1: { stage: 'test', trigger: { + include: [{ artifact: 'generated.yml' }] + } } + }) + end + + it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, /must specify the job where to fetch the artifact from/) } + end + end + describe "Error handling" do it "fails to parse YAML" do expect do diff --git a/spec/lib/gitlab/jira_import/issue_serializer_spec.rb b/spec/lib/gitlab/jira_import/issue_serializer_spec.rb index 03631a3e941..808ed6ee2fa 100644 --- a/spec/lib/gitlab/jira_import/issue_serializer_spec.rb +++ b/spec/lib/gitlab/jira_import/issue_serializer_spec.rb @@ -14,6 +14,29 @@ describe Gitlab::JiraImport::IssueSerializer do let(:updated_at) { '2020-01-10 20:00:00' } let(:assignee) { double(displayName: 'Solver') } let(:jira_status) { 'new' } + + let(:parent_field) do + { 'key' => 'FOO-2', 'id' => '1050', 'fields' => { 'summary' => 'parent issue FOO' } } + end + let(:issue_type_field) { { 'name' => 'Task' } } + let(:fix_versions_field) { [{ 'name' => '1.0' }, { 'name' => '1.1' }] } + let(:priority_field) { { 'name' => 'Medium' } } + let(:labels_field) { %w(bug backend) } + let(:environment_field) { 'staging' } + let(:duedate_field) { '2020-03-01' } + + let(:fields) do + { + 'parent' => parent_field, + 'issuetype' => issue_type_field, + 'fixVersions' => fix_versions_field, + 'priority' => priority_field, + 'labels' => labels_field, + 'environment' => environment_field, + 'duedate' => duedate_field + } + end + let(:jira_issue) do double( id: '1234', @@ -24,11 +47,15 @@ describe Gitlab::JiraImport::IssueSerializer do updated: updated_at, assignee: assignee, reporter: double(displayName: 'Reporter'), - status: double(statusCategory: { 'key' => jira_status }) + status: double(statusCategory: { 'key' => jira_status }), + fields: fields ) end + let(:params) { { iid: iid } } + subject { described_class.new(project, jira_issue, params).execute } + let(:expected_description) do <<~MD *Created by: Reporter* @@ -36,11 +63,21 @@ describe Gitlab::JiraImport::IssueSerializer do *Assigned to: Solver* basic description + + --- + + **Issue metadata** + + - Issue type: Task + - Priority: Medium + - Labels: bug, backend + - Environment: staging + - Due date: 2020-03-01 + - Parent issue: [FOO-2] parent issue FOO + - Fix versions: 1.0, 1.1 MD end - subject { described_class.new(project, jira_issue, params).execute } - context 'attributes setting' do it 'sets the basic attributes' do expect(subject).to eq( @@ -54,6 +91,54 @@ describe Gitlab::JiraImport::IssueSerializer do author_id: project.creator_id ) end + + context 'when some metadata fields are missing' do + let(:assignee) { nil } + let(:parent_field) { nil } + let(:fix_versions_field) { [] } + let(:labels_field) { [] } + let(:environment_field) { nil } + let(:duedate_field) { '2020-03-01' } + + it 'skips the missing fields' do + expected_description = <<~MD + *Created by: Reporter* + + basic description + + --- + + **Issue metadata** + + - Issue type: Task + - Priority: Medium + - Due date: 2020-03-01 + MD + + expect(subject[:description]).to eq(expected_description.strip) + end + end + + context 'when all metadata fields are missing' do + let(:assignee) { nil } + let(:parent_field) { nil } + let(:issue_type_field) { nil } + let(:fix_versions_field) { [] } + let(:priority_field) { nil } + let(:labels_field) { [] } + let(:environment_field) { nil } + let(:duedate_field) { nil } + + it 'skips the whole metadata secction' do + expected_description = <<~MD + *Created by: Reporter* + + basic description + MD + + expect(subject[:description]).to eq(expected_description.strip) + end + end end context 'with done status' do @@ -64,20 +149,6 @@ describe Gitlab::JiraImport::IssueSerializer do end end - context 'without the assignee' do - let(:assignee) { nil } - - it 'does not include assignee in the description' do - expected_description = <<~MD - *Created by: Reporter* - - basic description - MD - - expect(subject[:description]).to eq(expected_description.strip) - end - end - context 'without the iid' do let(:params) { {} } diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb index 5808d6e37e5..81f173cd23a 100644 --- a/spec/models/concerns/milestoneish_spec.rb +++ b/spec/models/concerns/milestoneish_spec.rb @@ -302,20 +302,55 @@ describe Milestone, 'Milestoneish' do end end - describe '#total_issue_time_spent' do - it 'calculates total issue time spent' do + describe '#total_time_spent' do + it 'calculates total time spent' do closed_issue_1.spend_time(duration: 300, user_id: author.id) closed_issue_1.save! closed_issue_2.spend_time(duration: 600, user_id: assignee.id) closed_issue_2.save! - expect(milestone.total_issue_time_spent).to eq(900) + expect(milestone.total_time_spent).to eq(900) + end + + it 'includes merge request time spent' do + closed_issue_1.spend_time(duration: 300, user_id: author.id) + closed_issue_1.save! + merge_request.spend_time(duration: 900, user_id: author.id) + merge_request.save! + + expect(milestone.total_time_spent).to eq(1200) + end + end + + describe '#human_total_time_spent' do + it 'returns nil if no time has been spent' do + expect(milestone.human_total_time_spent).to be_nil + end + end + + describe '#total_time_estimate' do + it 'calculates total estimate' do + closed_issue_1.time_estimate = 300 + closed_issue_1.save! + closed_issue_2.time_estimate = 600 + closed_issue_2.save! + + expect(milestone.total_time_estimate).to eq(900) + end + + it 'includes merge request time estimate' do + closed_issue_1.time_estimate = 300 + closed_issue_1.save! + merge_request.time_estimate = 900 + merge_request.save! + + expect(milestone.total_time_estimate).to eq(1200) end end - describe '#human_total_issue_time_spent' do + describe '#human_total_time_estimate' do it 'returns nil if no time has been spent' do - expect(milestone.human_total_issue_time_spent).to be_nil + expect(milestone.human_total_time_estimate).to be_nil end end end diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index 93b2c19c74a..10cf76b607f 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -40,10 +40,6 @@ describe Admin::UsersController, "routing" do expect(get("/admin/users/1/edit")).to route_to('admin/users#edit', id: '1') end - it "to #show" do - expect(get("/admin/users/1")).to route_to('admin/users#show', id: '1') - end - it "to #update" do expect(put("/admin/users/1")).to route_to('admin/users#update', id: '1') end diff --git a/spec/services/ci/daily_report_result_service_spec.rb b/spec/services/ci/daily_report_result_service_spec.rb index 793fc956acb..240709bab0b 100644 --- a/spec/services/ci/daily_report_result_service_spec.rb +++ b/spec/services/ci/daily_report_result_service_spec.rb @@ -38,6 +38,27 @@ describe Ci::DailyReportResultService, '#execute' do expect(Ci::DailyReportResult.find_by(title: 'extra')).to be_nil end + context 'when there are multiple builds with the same group name that report coverage' do + let!(:test_job_1) { create(:ci_build, pipeline: pipeline, name: '1/2 test', coverage: 70) } + let!(:test_job_2) { create(:ci_build, pipeline: pipeline, name: '2/2 test', coverage: 80) } + + it 'creates daily code coverage record with the average as the value' do + described_class.new.execute(pipeline) + + Ci::DailyReportResult.find_by(title: 'test').tap do |coverage| + expect(coverage).to have_attributes( + project_id: pipeline.project.id, + last_pipeline_id: pipeline.id, + ref_path: pipeline.source_ref_path, + param_type: 'coverage', + title: test_job_2.group_name, + value: 75, + date: pipeline.created_at.to_date + ) + end + end + end + context 'when there is an existing daily code coverage for the matching date, project, ref_path, and group name' do let!(:new_pipeline) do create( |