Commit c08d9c22 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 546ddc3f
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34"
stages:
- sync
......
......@@ -38,7 +38,7 @@
POSTGRES_HOST_AUTH_METHOD: trust
.use-pg10:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
services:
- name: postgres:10.12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
......@@ -47,7 +47,7 @@
POSTGRES_HOST_AUTH_METHOD: trust
.use-pg11:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34"
services:
- name: postgres:11.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
......@@ -65,7 +65,7 @@
POSTGRES_HOST_AUTH_METHOD: trust
.use-pg10-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
services:
- name: postgres:10.12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
......@@ -75,7 +75,7 @@
POSTGRES_HOST_AUTH_METHOD: trust
.use-pg11-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34"
services:
- name: postgres:11.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
......
......@@ -400,8 +400,6 @@ RSpec/RepeatedExample:
- 'spec/lib/gitlab/closing_issue_extractor_spec.rb'
- 'spec/lib/gitlab/danger/changelog_spec.rb'
- 'spec/lib/gitlab/import_export/project/relation_factory_spec.rb'
- 'spec/routing/admin_routing_spec.rb'
- 'spec/rubocop/cop/migration/update_large_table_spec.rb'
- 'spec/services/notification_service_spec.rb'
- 'spec/services/web_hook_service_spec.rb'
- 'ee/spec/services/geo/repository_verification_primary_service_spec.rb'
......@@ -121,6 +121,7 @@
}
> .btn,
> .btn-group,
> .btn-container,
> .dropdown,
> input,
......@@ -161,7 +162,8 @@
.dropdown,
.dropdown-toggle,
.dropdown-menu-toggle,
.form-control {
.form-control,
> .btn-group {
margin: 0 0 $gl-padding-8;
display: block;
width: 100%;
......
......@@ -117,20 +117,20 @@ module Milestoneish
false
end
def total_issue_time_spent
@total_issue_time_spent ||= issues.joins(:timelogs).sum(:time_spent)
def total_time_spent
@total_time_spent ||= issues.joins(:timelogs).sum(:time_spent) + merge_requests.joins(:timelogs).sum(:time_spent)
end
def human_total_issue_time_spent
Gitlab::TimeTrackingFormatter.output(total_issue_time_spent)
def human_total_time_spent
Gitlab::TimeTrackingFormatter.output(total_time_spent)
end
def total_issue_time_estimate
@total_issue_time_estimate ||= issues.sum(:time_estimate)
def total_time_estimate
@total_time_estimate ||= issues.sum(:time_estimate) + merge_requests.sum(:time_estimate)
end
def human_total_issue_time_estimate
Gitlab::TimeTrackingFormatter.output(total_issue_time_estimate)
def human_total_time_estimate
Gitlab::TimeTrackingFormatter.output(total_time_estimate)
end
private
......
......@@ -94,7 +94,7 @@ class GlobalMilestone
end
def merge_requests
@merge_requests ||= MergeRequest.of_milestones(milestone).includes(:target_project, :assignee, :labels)
@merge_requests ||= MergeRequest.of_milestones(milestone).includes(:target_project, :assignees, :labels)
end
def labels
......
......@@ -19,12 +19,21 @@ module Ci
last_pipeline_id: pipeline.id
}
pipeline.builds.with_coverage.map do |build|
aggregate(pipeline.builds.with_coverage).map do |group_name, group|
base_attrs.merge(
title: build.group_name,
value: build.coverage
title: group_name,
value: average_coverage(group)
)
end
end
def aggregate(builds)
builds.group_by(&:group_name)
end
def average_coverage(group)
total_coverage = group.reduce(0.0) { |sum, build| sum + build.coverage }
(total_coverage / group.size).round(2)
end
end
end
......@@ -23,7 +23,7 @@
.prepend-top-20
%button.btn.btn-success.js-ci-variables-save-button{ type: 'button' }
%span.hide.js-ci-variables-save-loading-icon
= icon('spinner spin')
.spinner.spinner-light.mr-1
= _('Save variables')
%button.btn.btn-info.btn-inverted.prepend-left-10.js-secret-value-reveal-button{ type: 'button', data: { secret_reveal_status: "#{@variables.size == 0}" } }
- if @variables.size == 0
......
......@@ -6,7 +6,7 @@
- if show_feed_buttons
= render 'shared/issuable/feed_buttons'
.btn-group.append-right-10<
.btn-group
- if show_export_button
= render_if_exists 'projects/issues/export_csv/button'
......
- type = local_assigns.fetch(:type, :icon)
%button.csv-import-button.btn{ title: _('Import CSV'), class: ('has-tooltip' if type == :icon),
- if Feature.enabled?(:jira_issue_import, @project)
.dropdown.btn-group
%button.btn.rounded-right.text-center{ class: ('has-tooltip' if type == :icon), title: (_('Import issues') if type == :icon),
data: { toggle: 'dropdown' }, 'aria-label' => _('Import issues'), 'aria-haspopup' => 'true', 'aria-expanded' => 'false' }
- if type == :icon
= sprite_icon('import')
- else
= _('Import issues')
%ul.dropdown-menu
%li
%button.btn{ data: { toggle: 'modal', target: '.issues-import-modal' } }
= _('Import CSV')
%li= link_to _('Import from Jira'), project_import_jira_path(@project)
- else
%button.csv-import-button.btn{ title: _('Import CSV'), class: ('has-tooltip' if type == :icon),
data: { toggle: 'modal', target: '.issues-import-modal' } }
- if type == :icon
= sprite_icon('import')
- else
= _('Import CSV')
- if Feature.enabled?(:jira_issue_import, @project)
= link_to _("Import Jira issues"), project_import_jira_path(@project), class: "btn btn-default"
......@@ -93,10 +93,10 @@
= milestone.issues_visible_to_user(current_user).closed.count
.block
#issuable-time-tracker{ data: { time_estimate: @milestone.total_issue_time_estimate,
time_spent: @milestone.total_issue_time_spent,
human_time_estimate: @milestone.human_total_issue_time_estimate,
human_time_spent: @milestone.human_total_issue_time_spent,
#issuable-time-tracker{ data: { time_estimate: @milestone.total_time_estimate,
time_spent: @milestone.total_time_spent,
human_time_estimate: @milestone.human_total_time_estimate,
human_time_spent: @milestone.human_total_time_spent,
limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s } }
= render_if_exists 'shared/milestones/weight', milestone: milestone
......
---
title: Migrate .fa-spinner to .spinner for app/views/ci/variables
merge_request: 25030
author: nuwe1
type: other
---
title: Remove duplicate show spec in admin routing
merge_request: 28790
author: Rajendra Kadam
type: changed
---
title: Fix daily report result to use average of coverage values if there are multiple builds for a given group
name
merge_request: 28556
author:
type: fixed
---
title: Include MR times in Milestone time overview
merge_request: 28519
author: Bob van de Vijver
type: fixed
---
title: Validate dependency on job generating a CI config when using dynamic child pipelines
merge_request: 27916
author:
type: added
......@@ -31,6 +31,16 @@ It's possible that this limit will be changed to a lower number in the future.
- **Max size:** ~1 million characters / ~1 MB
## Number of issues in the milestone overview
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/39453) in GitLab 12.10.
The maximum number of issues loaded on the milestone overview page is 3000.
When the number exceeds the limit the page displays an alert and links to a paginated
[issue list](../user/project/issues/index.md#issues-list) of all issues in the milestone.
- **Limit:** 3000 issues
## Number of pipelines per Git push
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/51401) in GitLab 11.10.
......
......@@ -136,12 +136,11 @@ your own script to generate a YAML file, which is then [used to trigger a child
This technique can be very powerful in generating pipelines targeting content that changed or to
build a matrix of targets and architectures.
In GitLab 12.9, the child pipeline could fail to be created in certain cases, causing the parent pipeline to fail.
This is [resolved in GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/issues/209070).
## Limitations
A parent pipeline can trigger many child pipelines, but a child pipeline cannot trigger
further child pipelines. See the [related issue](https://gitlab.com/gitlab-org/gitlab/issues/29651)
for discussion on possible future improvements.
When triggering dynamic child pipelines, if the job containing the CI config artifact is not a predecessor of the
trigger job, the child pipeline will fail to be created, causing also the parent pipeline to fail.
In the future we want to validate the trigger job's dependencies [at the time the parent pipeline is created](https://gitlab.com/gitlab-org/gitlab/-/issues/209070) rather than when the child pipeline is created.
......@@ -133,7 +133,7 @@ The milestone sidebar on the milestone view shows the following:
- Percentage complete, which is calculated as number of closed issues divided by total number of issues.
- The start date and due date.
- The total time spent on all issues assigned to the milestone.
- The total time spent on all issues and merge requests assigned to the milestone.
- The total issue weight of all issues assigned to the milestone.
![Project milestone page](img/milestones_project_milestone_page.png)
......
......@@ -15,6 +15,11 @@ module Gitlab
validations do
validates :config, hash_or_string: true
validates :config, allowed_keys: ALLOWED_KEYS
validate do
if config[:artifact] && config[:job].blank?
errors.add(:config, "must specify the job where to fetch the artifact from")
end
end
end
end
end
......
......@@ -142,6 +142,7 @@ module Gitlab
validate_job_stage!(name, job)
validate_job_dependencies!(name, job)
validate_job_needs!(name, job)
validate_dynamic_child_pipeline_dependencies!(name, job)
validate_job_environment!(name, job)
end
end
......@@ -163,35 +164,50 @@ module Gitlab
def validate_job_dependencies!(name, job)
return unless job[:dependencies]
stage_index = @stages.index(job[:stage])
job[:dependencies].each do |dependency|
raise ValidationError, "#{name} job: undefined dependency: #{dependency}" unless @jobs[dependency.to_sym]
validate_job_dependency!(name, dependency)
end
end
dependency_stage_index = @stages.index(@jobs[dependency.to_sym][:stage])
def validate_dynamic_child_pipeline_dependencies!(name, job)
return unless includes = job.dig(:trigger, :include)
unless dependency_stage_index.present? && dependency_stage_index < stage_index
raise ValidationError, "#{name} job: dependency #{dependency} is not defined in prior stages"
end
includes.each do |included|
next unless dependency = included[:job]
validate_job_dependency!(name, dependency)
end
end
def validate_job_needs!(name, job)
return unless job.dig(:needs, :job)
stage_index = @stages.index(job[:stage])
return unless needs = job.dig(:needs, :job)
job.dig(:needs, :job).each do |need|
need_job_name = need[:name]
needs.each do |need|
dependency = need[:name]
validate_job_dependency!(name, dependency, 'need')
end
end
raise ValidationError, "#{name} job: undefined need: #{need_job_name}" unless @jobs[need_job_name.to_sym]
def validate_job_dependency!(name, dependency, dependency_type = 'dependency')
unless @jobs[dependency.to_sym]
raise ValidationError, "#{name} job: undefined #{dependency_type}: #{dependency}"
end
needs_stage_index = @stages.index(@jobs[need_job_name.to_sym][:stage])
job_stage_index = stage_index(name)
dependency_stage_index = stage_index(dependency)
unless needs_stage_index.present? && needs_stage_index < stage_index
raise ValidationError, "#{name} job: need #{need_job_name} is not defined in prior stages"
# A dependency might be defined later in the configuration
# with a stage that does not exist
unless dependency_stage_index.present? && dependency_stage_index < job_stage_index
raise ValidationError, "#{name} job: #{dependency_type} #{dependency} is not defined in prior stages"
end
end
def stage_index(name)
job = @jobs[name.to_sym]
return unless job
@stages.index(job[:stage])
end
def validate_job_environment!(name, job)
......
......@@ -39,8 +39,8 @@ module Gitlab
SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep
# Each query should take < 500ms https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22705
DEFAULT_DISTINCT_BATCH_SIZE = 100_000
DEFAULT_BATCH_SIZE = 10_000
DEFAULT_DISTINCT_BATCH_SIZE = 10_000
DEFAULT_BATCH_SIZE = 100_000
def initialize(relation, column: nil)
@relation = relation
......
......@@ -4,12 +4,14 @@ module Gitlab
module JiraImport
class IssueSerializer
attr_reader :jira_issue, :project, :params, :formatter
attr_accessor :metadata
def initialize(project, jira_issue, params = {})
@jira_issue = jira_issue
@project = project
@params = params
@formatter = Gitlab::ImportFormatter.new
@metadata = []
end
def execute
......@@ -36,6 +38,7 @@ module Gitlab
body << formatter.author_line(jira_issue.reporter.displayName)
body << formatter.assignee_line(jira_issue.assignee.displayName) if jira_issue.assignee
body << jira_issue.description
body << add_metadata
body.join
end
......@@ -48,6 +51,50 @@ module Gitlab
Issuable::STATE_ID_MAP[:opened]
end
end
def add_metadata
add_field(%w(issuetype name), 'Issue type')
add_field(%w(priority name), 'Priority')
add_labels
add_field('environment', 'Environment')
add_field('duedate', 'Due date')
add_parent
add_versions
return if metadata.empty?
metadata.join("\n").prepend("\n\n---\n\n**Issue metadata**\n\n")
end
def add_field(keys, field_label)
value = fields.dig(*keys)
return if value.blank?
metadata << "- #{field_label}: #{value}"
end
def add_labels
return if fields['labels'].blank?
metadata << "- Labels: #{fields['labels'].join(', ')}"
end
def add_parent
parent_issue_key = fields.dig('parent', 'key')
return if parent_issue_key.blank?
metadata << "- Parent issue: [#{parent_issue_key}] #{fields['parent']['fields']['summary']}"
end
def add_versions
return if fields['fixVersions'].blank?
metadata << "- Fix versions: #{fields['fixVersions'].map { |version| version['name'] }.join(', ')}"
end
def fields
jira_issue.fields
end
end
end
end
......@@ -10815,9 +10815,6 @@ msgstr ""
msgid "Import CSV"
msgstr ""
msgid "Import Jira issues"
msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
......@@ -10833,6 +10830,9 @@ msgstr ""
msgid "Import an exported GitLab project"
msgstr ""
msgid "Import from Jira"
msgstr ""
msgid "Import in progress"
msgstr ""
......@@ -17829,6 +17829,9 @@ msgstr ""
msgid "SecurityDashboard|Severity"
msgstr ""
msgid "SecurityDashboard|Status"
msgstr ""
msgid "SecurityDashboard|The security dashboard displays the latest security findings for projects you wish to monitor. Select \"Edit dashboard\" to add and remove projects."
msgstr ""
......@@ -22684,6 +22687,21 @@ msgstr ""
msgid "VulnerabilityManagement|Will not fix or a false-positive"
msgstr ""
msgid "VulnerabilityStatusTypes|All"
msgstr ""
msgid "VulnerabilityStatusTypes|Confirmed"
msgstr ""
msgid "VulnerabilityStatusTypes|Detected"
msgstr ""
msgid "VulnerabilityStatusTypes|Dismissed"
msgstr ""
msgid "VulnerabilityStatusTypes|Resolved"
msgstr ""
msgid "Vulnerability|Class"
msgstr ""
......@@ -24787,9 +24805,6 @@ msgstr ""
msgid "severity|None"
msgstr ""
msgid "severity|Undefined"
msgstr ""
msgid "severity|Unknown"
msgstr ""
......
......@@ -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
......
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 { 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;
......@@ -38,7 +44,7 @@ describe('IDE commit sidebar actions', () => {
beforeEach(() => {
store = createStore();
spyOn(store, 'dispatch');
jest.spyOn(store, 'dispatch').mockImplementation(() => {});
});
afterEach(() => {
......@@ -103,133 +109,33 @@ describe('IDE commit sidebar actions', () => {
.then(() => {
expect(vm.$store.dispatch).toHaveBeenCalledWith(
ACTION_UPDATE_COMMIT_ACTION,
jasmine.anything(),
expect.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,
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],
]);
},
);
});
});
});
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,
);
});
});
});
});
});
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();
})
......
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';
......
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();
......
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';
......
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)]]),
);
});
});
......@@ -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
......
......@@ -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,20 +91,45 @@ describe Gitlab::JiraImport::IssueSerializer do
author_id: project.creator_id
)
end
end
context 'with done status' do
let(:jira_status) { 'done' }
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 'maps the status to closed' do
expect(subject[:state_id]).to eq(2)
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 'without the assignee' do
context 'when all metadata fields are missing' do
let(:assignee) { nil }
it 'does not include assignee in the description' do
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*
......@@ -77,6 +139,15 @@ describe Gitlab::JiraImport::IssueSerializer do
expect(subject[:description]).to eq(expected_description.strip)
end
end
end
context 'with done status' do
let(:jira_status) { 'done' }
it 'maps the status to closed' do
expect(subject[:state_id]).to eq(2)
end
end
context 'without the iid' do
let(:params) { {} }
......
......@@ -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
......@@ -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
......
......@@ -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(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment