Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-05-28 12:08:10 +00:00
parent 4c788f43cb
commit a83a97f604
79 changed files with 272 additions and 1111 deletions

View File

@ -84,15 +84,10 @@ export default {
<div v-show="hasError" class="btn-group">
<div class="btn btn-default btn-sm disabled">
<icon
:size="16"
class="prepend-left-8 append-right-8"
name="doc-image"
aria-hidden="true"
/>
<icon :size="16" class="gl-ml-3 append-right-8" name="doc-image" aria-hidden="true" />
</div>
<div class="btn btn-default btn-sm disabled">
<span class="prepend-left-8 append-right-8">{{ s__('Badges|No badge image') }}</span>
<span class="gl-ml-3 append-right-8">{{ s__('Badges|No badge image') }}</span>
</div>
</div>

View File

@ -89,7 +89,7 @@ export default {
:type="file.type"
:path="file.path"
:is-open="dropdownOpen"
class="prepend-left-8"
class="gl-ml-3"
v-on="$listeners"
/>
</div>

View File

@ -24,7 +24,7 @@ export default {
<template>
<div class="d-flex align-items-center">
<ci-icon :status="job.status" :borderless="true" :size="24" class="d-flex" />
<span class="prepend-left-8">
<span class="gl-ml-3">
{{ job.name }}
<a :href="job.path" target="_blank" class="ide-external-link position-relative">
{{ jobId }} <icon :size="12" name="external-link" />

View File

@ -71,7 +71,7 @@ export default {
v-tooltip="showTooltip"
:title="showTooltip ? stage.name : null"
data-container="body"
class="prepend-left-8 text-truncate"
class="gl-ml-3 text-truncate"
>
{{ stage.name }}
</strong>

View File

@ -63,7 +63,7 @@ export default {
<template v-else-if="hasLoadedPipeline">
<header v-if="latestPipeline" class="ide-tree-header ide-pipeline-header">
<ci-icon :status="latestPipeline.details.status" :size="24" class="d-flex" />
<span class="prepend-left-8">
<span class="gl-ml-3">
<strong> {{ __('Pipeline') }} </strong>
<a
:href="latestPipeline.path"

View File

@ -128,7 +128,7 @@ export default {
:key="id"
v-gl-tooltip.bottom
:title="tooltipTitle"
class="suggestion-help-hover prepend-left-8 text-tertiary"
class="suggestion-help-hover gl-ml-3 text-tertiary"
>
<icon :name="icon" /> {{ count }}
</span>

View File

@ -0,0 +1,5 @@
import initCreateCluster from '~/create_cluster/init_create_cluster';
document.addEventListener('DOMContentLoaded', () => {
initCreateCluster(document, gon);
});

View File

@ -1,10 +1,7 @@
import Project from './project';
import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation';
import initCreateCluster from '~/create_cluster/init_create_cluster';
document.addEventListener('DOMContentLoaded', () => {
initCreateCluster(document, gon);
new Project(); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
});

View File

@ -405,7 +405,6 @@ img.emoji {
.prepend-top-20 { margin-top: 20px; }
.prepend-top-32 { margin-top: 32px; }
.prepend-left-5 { margin-left: 5px; }
.prepend-left-8 { margin-left: 8px; }
.prepend-left-10 { margin-left: 10px; }
.prepend-left-15 { margin-left: 15px; }
.prepend-left-default { margin-left: $gl-padding; }

View File

@ -221,7 +221,7 @@
}
@function calc-btn-hover-padding($original-padding, $original-border: 1px) {
@return calc(#{$original-padding + $original-border} - var(--ide-btn-hover-border-width, $original-border));
@return calc(#{$original-padding + $original-border} - var(--ide-btn-hover-border-width, #{$original-border}));
}
.btn:not(.btn-link):not([disabled]):hover {

View File

@ -51,13 +51,7 @@ module MilestoneActions
}
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def milestone_redirect_path
if @milestone.global_milestone?
url_for(action: :show, title: @milestone.title)
else
url_for(action: :show)
end
url_for(action: :show)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end

View File

@ -1,48 +1,32 @@
# frozen_string_literal: true
class Dashboard::MilestonesController < Dashboard::ApplicationController
include MilestoneActions
before_action :projects
before_action :groups, only: :index
before_action :milestone, only: [:show, :merge_requests, :participants, :labels]
def index
respond_to do |format|
format.html do
@milestone_states = Milestone.states_count(@projects.select(:id), @groups.select(:id))
@milestones = Kaminari.paginate_array(milestones).page(params[:page])
@milestone_states = Milestone.states_count(@projects.select(:id), groups.select(:id))
@milestones = milestones.page(params[:page])
end
format.json do
render json: milestones
render json: milestones.to_json(only: [:id, :title], methods: :name)
end
end
end
def show
end
private
def group_milestones
DashboardGroupMilestone.build_collection(groups, params)
end
# See [#39545](https://gitlab.com/gitlab-org/gitlab-foss/issues/39545) for info about the deprecation of dynamic milestones
def dynamic_milestones
DashboardMilestone.build_collection(@projects, params)
end
def milestones
@milestones = group_milestones + dynamic_milestones
end
def milestone
@milestone = DashboardMilestone.build(@projects, params[:title])
render_404 unless @milestone
MilestonesFinder.new(search_params).execute
end
def groups
@groups ||= GroupsFinder.new(current_user, all_available: false).execute
end
def search_params
params.permit(:state, :search_title).merge(group_ids: groups, project_ids: projects)
end
end

View File

@ -6,17 +6,17 @@ class Groups::MilestonesController < Groups::ApplicationController
before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels, :destroy]
before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update, :destroy]
before_action do
push_frontend_feature_flag(:burnup_charts)
push_frontend_feature_flag(:burnup_charts, @group)
end
def index
respond_to do |format|
format.html do
@milestone_states = Milestone.states_count(group_projects_with_access, [group])
@milestones = Kaminari.paginate_array(milestones).page(params[:page])
@milestones = milestones.page(params[:page])
end
format.json do
render json: milestones.map { |m| m.for_display.slice(:id, :title, :name) }
render json: milestones.to_json(only: [:id, :title], methods: :name)
end
end
end
@ -29,7 +29,7 @@ class Groups::MilestonesController < Groups::ApplicationController
@milestone = Milestones::CreateService.new(group, current_user, milestone_params).execute
if @milestone.persisted?
redirect_to milestone_path
redirect_to milestone_path(@milestone)
else
render "new"
end
@ -39,23 +39,15 @@ class Groups::MilestonesController < Groups::ApplicationController
end
def edit
render_404 if @milestone.legacy_group_milestone?
end
def update
# Keep this compatible with legacy group milestones where we have to update
# all projects milestones states at once.
milestones, update_params = get_milestones_for_update
milestones.each do |milestone|
Milestones::UpdateService.new(milestone.resource_parent, current_user, update_params).execute(milestone)
end
Milestones::UpdateService.new(@milestone.parent, current_user, milestone_params).execute(@milestone)
redirect_to milestone_path
redirect_to milestone_path(@milestone)
end
def destroy
return render_404 if @milestone.legacy_group_milestone?
Milestones::DestroyService.new(group, current_user).execute(@milestone)
respond_to do |format|
@ -66,14 +58,6 @@ class Groups::MilestonesController < Groups::ApplicationController
private
def get_milestones_for_update
if @milestone.legacy_group_milestone?
[@milestone.milestones, legacy_milestone_params]
else
[[@milestone], milestone_params]
end
end
def authorize_admin_milestones!
return render_404 unless can?(current_user, :admin_milestone, group)
end
@ -82,27 +66,21 @@ class Groups::MilestonesController < Groups::ApplicationController
params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
end
def legacy_milestone_params
params.require(:milestone).permit(:state_event)
end
def milestone_path
if @milestone.legacy_group_milestone?
group_milestone_path(group, @milestone.safe_title, title: @milestone.title)
else
group_milestone_path(group, @milestone.iid)
end
end
def milestones
milestones = MilestonesFinder.new(search_params).execute
MilestonesFinder.new(search_params).execute
end
def milestone
@milestone = group.milestones.find_by_iid(params[:id])
render_404 unless @milestone
end
def search_params
groups = request.format.json? ? group_ids(include_ancestors: true) : group_ids
@sort = params[:sort] || 'due_date_asc'
MilestoneArray.sort(milestones + legacy_milestones, @sort)
end
def legacy_milestones
GroupMilestone.build_collection(group, group_projects_with_access, params)
params.permit(:state, :search_title).merge(sort: @sort, group_ids: groups, project_ids: group_projects_with_access)
end
def group_projects_with_access
@ -116,23 +94,6 @@ class Groups::MilestonesController < Groups::ApplicationController
group.self_and_descendants.public_or_visible_to_user(current_user).select(:id)
end
end
def milestone
@milestone =
if params[:title]
GroupMilestone.build(group, group_projects_with_access, params[:title])
else
group.milestones.find_by_iid(params[:id])
end
render_404 unless @milestone
end
def search_params
groups = request.format.json? ? group_ids(include_ancestors: true) : group_ids
params.permit(:state, :search_title).merge(group_ids: groups)
end
end
Groups::MilestonesController.prepend_if_ee('EE::Groups::MilestonesController')

View File

@ -7,7 +7,7 @@ class Projects::MilestonesController < Projects::ApplicationController
before_action :check_issuables_available!
before_action :milestone, only: [:edit, :update, :destroy, :show, :merge_requests, :participants, :labels, :promote]
before_action do
push_frontend_feature_flag(:burnup_charts)
push_frontend_feature_flag(:burnup_charts, @project)
end
# Allow read any milestone
@ -34,7 +34,7 @@ class Projects::MilestonesController < Projects::ApplicationController
@milestones = @milestones.page(params[:page])
end
format.json do
render json: @milestones.to_json(methods: :name)
render json: @milestones.to_json(only: [:id, :title], methods: :name)
end
end
end

View File

@ -58,10 +58,8 @@ class MilestonesFinder
Milestone.filter_by_state(items, params[:state])
end
# rubocop: disable CodeReuse/ActiveRecord
def order(items)
order_statement = Gitlab::Database.nulls_last_order('due_date', 'ASC')
items.reorder(order_statement).order('title ASC')
sort_by = params[:sort].presence || 'due_date_asc'
items.sort_by_attribute(sort_by)
end
# rubocop: enable CodeReuse/ActiveRecord
end

View File

@ -207,7 +207,7 @@ module IssuablesHelper
output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip gl-ml-2', title: _('1st contribution!'))
output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block prepend-left-8")
output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block gl-ml-3")
output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "d-md-none")
output.join.html_safe

View File

@ -229,11 +229,7 @@ module TimeboxesHelper
alias_method :milestone_date_range, :timebox_date_range
def milestone_tab_path(milestone, tab)
if milestone.global_milestone?
url_for(action: tab, title: milestone.title, format: :json)
else
url_for(action: tab, format: :json)
end
url_for(action: tab, format: :json)
end
def update_milestone_path(milestone, params = {})
@ -247,11 +243,7 @@ module TimeboxesHelper
def group_milestone_route(milestone, params = {})
params = nil if params.empty?
if milestone.legacy_group_milestone?
group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: params)
else
group_milestone_path(milestone.group, milestone.iid, milestone: params)
end
group_milestone_path(milestone.group, milestone.iid, milestone: params)
end
def group_or_project_milestone_path(milestone)

View File

@ -97,26 +97,6 @@ module Milestoneish
due_date && due_date.past?
end
def group_milestone?
false
end
def project_milestone?
false
end
def legacy_group_milestone?
false
end
def dashboard_milestone?
false
end
def global_milestone?
false
end
def total_time_spent
@total_time_spent ||= issues.joins(:timelogs).sum(:time_spent) + merge_requests.joins(:timelogs).sum(:time_spent)
end

View File

@ -1,29 +0,0 @@
# frozen_string_literal: true
# Dashboard Group Milestones are milestones that allow us to pull more info out for the UI that the Milestone object doesn't allow for
class DashboardGroupMilestone < GlobalMilestone
extend ::Gitlab::Utils::Override
attr_reader :group_name
def initialize(milestone)
super
@group_name = milestone.group.full_name
end
def self.build_collection(groups, params)
milestones = Milestone.of_groups(groups.select(:id))
.reorder_by_due_date_asc
.order_by_name_asc
milestones = milestones.search_title(params[:search_title]) if params[:search_title].present?
Milestone.filter_by_state(milestones, params[:state]).map { |m| new(m) }
end
def dashboard_milestone?
true
end
def merge_requests_enabled?
true
end
end

View File

@ -1,19 +0,0 @@
# frozen_string_literal: true
class DashboardMilestone < GlobalMilestone
attr_reader :project_name
def initialize(milestone)
super
@project_name = milestone.project.full_name
end
def project_milestone?
true
end
def merge_requests_enabled?
project.merge_requests_enabled?
end
end

View File

@ -1,108 +0,0 @@
# frozen_string_literal: true
# Global Milestones are milestones that can be shared across multiple projects
class GlobalMilestone
include Milestoneish
STATE_COUNT_HASH = { opened: 0, closed: 0, all: 0 }.freeze
attr_reader :milestone
alias_attribute :name, :title
delegate :title, :state, :due_date, :start_date, :participants, :project,
:group, :expires_at, :closed?, :iid, :group_milestone?, :safe_title,
:timebox_id, :milestoneish_id, :resource_parent, :releases, to: :milestone
def to_hash
{
name: title,
title: title,
group_name: group&.full_name,
project_name: project&.full_name
}
end
def for_display
@milestone
end
def self.build_collection(projects, params)
items = Milestone.of_projects(projects)
.reorder_by_due_date_asc
.order_by_name_asc
items = items.search_title(params[:search_title]) if params[:search_title].present?
Milestone.filter_by_state(items, params[:state]).map { |m| new(m) }
end
# necessary for legacy milestones
def self.build(projects, title)
milestones = Milestone.of_projects(projects).where(title: title)
return if milestones.blank?
new(milestones.first)
end
def self.states_count(projects, group = nil)
legacy_group_milestones_count = legacy_group_milestone_states_count(projects)
group_milestones_count = group_milestones_states_count(group)
legacy_group_milestones_count.merge(group_milestones_count) do |k, legacy_group_milestones_count, group_milestones_count|
legacy_group_milestones_count + group_milestones_count
end
end
def self.group_milestones_states_count(group)
return STATE_COUNT_HASH unless group
counts_by_state = Milestone.of_groups(group).count_by_state
{
opened: counts_by_state['active'] || 0,
closed: counts_by_state['closed'] || 0,
all: counts_by_state.values.sum
}
end
def self.legacy_group_milestone_states_count(projects)
return STATE_COUNT_HASH unless projects
# We need to reorder(nil) on the projects, because the controller passes them in sorted.
relation = Milestone.of_projects(projects.reorder(nil)).count_by_state
{
opened: relation['active'] || 0,
closed: relation['closed'] || 0,
all: relation.values.sum
}
end
def initialize(milestone)
@milestone = milestone
end
def active?
state == 'active'
end
def closed?
state == 'closed'
end
def issues
@issues ||= Issue.of_milestones(milestone).includes(:project, :assignees, :labels)
end
def merge_requests
@merge_requests ||= MergeRequest.of_milestones(milestone).includes(:target_project, :assignees, :labels)
end
def labels
@labels ||= GlobalLabel.build_collection(milestone.labels).sort_by!(&:title)
end
def global_milestone?
true
end
end
GlobalMilestone.include_if_ee('::EE::GlobalMilestone')

View File

@ -1,49 +0,0 @@
# frozen_string_literal: true
# Group Milestones are milestones that can be shared among many projects within the same group
class GroupMilestone < GlobalMilestone
attr_reader :group, :milestones
def self.build_collection(group, projects, params)
params =
{ state: params[:state], search_title: params[:search_title] }
project_milestones = Milestone.of_projects(projects)
project_milestones = project_milestones.search_title(params[:search_title]) if params[:search_title].present?
child_milestones = Milestone.filter_by_state(project_milestones, params[:state])
grouped_milestones = child_milestones.group_by(&:title)
grouped_milestones.map do |title, grouped|
new(title, grouped, group)
end
end
def self.build(group, projects, title)
child_milestones = Milestone.of_projects(projects).where(title: title)
return if child_milestones.blank?
new(title, child_milestones, group)
end
def initialize(title, milestones, group)
@milestones = milestones
@group = group
end
def milestone
@milestone ||= milestones.find { |m| m.description.present? } || milestones.first
end
def issues_finder_params
{ group_id: group.id }
end
def legacy_group_milestone?
true
end
def merge_requests_enabled?
true
end
end
GroupMilestone.include_if_ee('::EE::GroupMilestone')

View File

@ -1313,8 +1313,6 @@ class MergeRequest < ApplicationRecord
end
def has_accessibility_reports?
return false unless Feature.enabled?(:accessibility_report_view, project)
actual_head_pipeline.present? && actual_head_pipeline.has_reports?(Ci::JobArtifact.accessibility_reports)
end

View File

@ -1,5 +1,4 @@
= render 'shared/milestones/milestone',
milestone_path: group_or_project_milestone_path(milestone),
issues_path: issues_dashboard_path(milestone_title: milestone.title),
merge_requests_path: merge_requests_dashboard_path(milestone_title: milestone.title),
milestone: milestone,

View File

@ -1,5 +0,0 @@
- header_title "Milestones", dashboard_milestones_path
= render 'shared/milestones/top', milestone: @milestone
= render 'shared/milestones/tabs', milestone: @milestone, show_full_project_name: true
= render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 51

View File

@ -17,7 +17,7 @@
%span
= _("Group ID: %{group_id}") % { group_id: @group.id }
- if current_user
%span.access-request-links.prepend-left-8
%span.access-request-links.gl-ml-3
= render 'shared/members/access_request_links', source: @group
.home-panel-buttons.col-md-12.col-lg-6.d-inline-flex.flex-wrap.justify-content-lg-end

View File

@ -1,6 +1,4 @@
= render 'shared/milestones/milestone',
milestone_path: group_milestone_route(milestone),
issues_path: issues_group_path(@group, milestone_title: milestone.title),
merge_requests_path: merge_requests_group_path(@group, milestone_title: milestone.title),
milestone: milestone

View File

@ -16,5 +16,8 @@
.nothing-here-block No milestones to show
- else
- @milestones.each do |milestone|
= render 'milestone', milestone: milestone
- if milestone.project_milestone?
= render 'projects/milestones/milestone', milestone: milestone
- else
= render 'milestone', milestone: milestone
= paginate @milestones, theme: "gitlab"

View File

@ -18,7 +18,7 @@
- if ActionController::Base.asset_host
%link{ rel: 'dns-prefetch', href: ActionController::Base.asset_host }
%link{ rel: 'preconnnect', href: ActionController::Base.asset_host, crossorigin: '' }
%link{ rel: 'preconnect', href: ActionController::Base.asset_host, crossorigin: '' }
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }

View File

@ -16,7 +16,7 @@
= brand_header_logo
- logo_text = brand_header_logo_type
- if logo_text.present?
%span.logo-text.d-none.d-lg-block.prepend-left-8
%span.logo-text.d-none.d-lg-block.gl-ml-3
= logo_text
- if Gitlab.com_and_canary?
= link_to 'https://next.gitlab.com', class: 'label-link canary-badge bg-transparent', target: :_blank do

View File

@ -1,4 +1,4 @@
%header.navbar.fixed-top.navbar-gitlab.justify-content-center
= render 'shared/logo.svg'
%span.logo-text.d-none.d-lg-block.prepend-left-8.pt-1
%span.logo-text.d-none.d-lg-block.gl-ml-3.pt-1
= render 'shared/logo_type.svg'

View File

@ -19,7 +19,7 @@
= brand_header_logo
- logo_text = brand_header_logo_type
- if logo_text.present?
%span.logo-text.prepend-left-8
%span.logo-text.gl-ml-3
= logo_text
- if header_link?(:user_dropdown)
.navbar-collapse

View File

@ -1,2 +1,2 @@
%p
Merge Request #{@merge_request.to_reference} was merged
Merge Request #{link_to @merge_request.to_reference, project_merge_request_url(@merge_request.target_project, @merge_request)} was merged

View File

@ -1,6 +1,6 @@
Merge Request #{@merge_request.to_reference} was merged
Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
Merge Request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
= merge_path_description(@merge_request, 'to')

View File

@ -20,7 +20,7 @@
%span.text-secondary
= s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
- if current_user
%span.access-request-links.prepend-left-8
%span.access-request-links.gl-ml-3
= render 'shared/members/access_request_links', source: @project
- if @project.tag_list.present?
%span.home-panel-topic-list.mt-2.w-100.d-inline-flex

View File

@ -5,7 +5,7 @@
.branch-info
.branch-title
= sprite_icon('fork', size: 12)
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name prepend-left-8 qa-branch-name' do
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name gl-ml-3 qa-branch-name' do
= branch.name
- if branch.name == @repository.root_ref
%span.badge.badge-primary.prepend-left-5 default

View File

@ -39,7 +39,7 @@
.d-flex
%h3.page-title= @environment.name
- if @environment.auto_stop_at?
%p.align-self-end.prepend-left-8
%p.align-self-end.gl-ml-3
= s_('Environments|Auto stops %{auto_stop_time}').html_safe % {auto_stop_time: time_ago_with_tooltip(@environment.auto_stop_at)}
.nav-controls.my-2
= render 'projects/environments/pin_button', environment: @environment

View File

@ -41,7 +41,7 @@
= _('Create branch')
%li.divider.droplab-item-ignore
%li.droplab-item-ignore.prepend-left-8.append-right-8.prepend-top-16
%li.droplab-item-ignore.gl-ml-3.append-right-8.prepend-top-16
- if can_create_confidential_merge_request?
#js-forked-project{ data: { namespace_path: @project.namespace.full_path, project_path: @project.full_path, new_fork_path: new_project_fork_path(@project), help_page_path: help_page_path('user/project/merge_requests') } }
.form-group

View File

@ -1,5 +1,4 @@
= render 'shared/milestones/milestone',
milestone_path: project_milestone_path(milestone.project, milestone),
issues_path: project_issues_path(milestone.project, milestone_title: milestone.title),
merge_requests_path: project_merge_requests_path(milestone.project, milestone_title: milestone.title),
milestone: milestone

View File

@ -11,8 +11,7 @@
.milestone-buttons
- if can?(current_user, :admin_milestone, @group || @project)
- unless milestone.legacy_group_milestone?
= link_to _('Edit'), edit_milestone_path(milestone), class: 'btn btn-grouped'
= link_to _('Edit'), edit_milestone_path(milestone), class: 'btn btn-grouped'
- if milestone.project_milestone? && milestone.project.group
%button.js-promote-project-milestone-button.btn.btn-grouped{ data: { toggle: 'modal',
@ -31,8 +30,7 @@
- else
= link_to _('Reopen milestone'), update_milestone_path(milestone, { state_event: :activate }), method: :put, class: 'btn btn-grouped btn-reopen'
- unless milestone.legacy_group_milestone?
= render 'shared/milestones/delete_button'
= render 'shared/milestones/delete_button'
%button.btn.btn-default.btn-grouped.float-right.d-block.d-sm-none.js-sidebar-toggle{ type: 'button' }
= icon('angle-double-left')

View File

@ -6,39 +6,33 @@
.row
.col-sm-6
.append-bottom-5
%strong= link_to truncate(milestone.title, length: 100), milestone_path
%strong= link_to truncate(milestone.title, length: 100), milestone_path(milestone)
- if @group
= " - #{milestone_type}"
- if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone?
- if milestone.due_date || milestone.start_date
.text-tertiary.append-bottom-5
= milestone_date_range(milestone)
- recent_releases, total_count, more_count = recent_releases_with_counts(milestone)
- unless total_count.zero?
.text-tertiary.append-bottom-5.milestone-release-links
= icon('rocket')
= n_('Release', 'Releases', total_count)
- recent_releases.each do |release|
= link_to release.name, project_releases_path(release.project, anchor: release.tag)
- unless release == recent_releases.last
&bull;
- if total_count > recent_releases.count
- if milestone.due_date || milestone.start_date
.text-tertiary.append-bottom-5
= milestone_date_range(milestone)
- recent_releases, total_count, more_count = recent_releases_with_counts(milestone)
- unless total_count.zero?
.text-tertiary.append-bottom-5.milestone-release-links
= icon('rocket')
= n_('Release', 'Releases', total_count)
- recent_releases.each do |release|
= link_to release.name, project_releases_path(release.project, anchor: release.tag)
- unless release == recent_releases.last
&bull;
= link_to n_('%{count} more release', '%{count} more releases', more_count) % { count: more_count }, project_releases_path(milestone.project)
%div
= render('shared/milestone_expired', milestone: milestone)
- if milestone.group_milestone?
.label-badge.label-badge-blue.d-inline-block
= milestone.group.full_name
- if milestone.legacy_group_milestone?
.projects
- link_to milestone_path(milestone.milestone) do
%span.label-badge.label-badge-blue.d-inline-block.append-bottom-5
= dashboard ? milestone.project.full_name : milestone.project.name
- if milestone.project
.label-badge.label-badge-gray.d-inline-block
= milestone.project.full_name
- if total_count > recent_releases.count
&bull;
= link_to n_('%{count} more release', '%{count} more releases', more_count) % { count: more_count }, project_releases_path(milestone.project)
%div
= render('shared/milestone_expired', milestone: milestone)
- if milestone.group_milestone?
.label-badge.label-badge-blue.d-inline-block
= milestone.group.full_name
- if milestone.project_milestone?
.label-badge.label-badge-gray.d-inline-block
= milestone.project.full_name
.col-sm-4.milestone-progress
= milestone_progress_bar(milestone)
@ -49,29 +43,25 @@
.float-lg-right.light #{milestone.percent_complete}% complete
.col-sm-2
.milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end
- if @project
- if can_admin_project_milestones? and milestone.active?
- if can_admin_group_milestones?
%button.js-promote-project-milestone-button.btn.btn-blank.btn-sm.btn-grouped.has-tooltip{ title: s_('Milestones|Promote to Group Milestone'),
disabled: true,
type: 'button',
data: { url: promote_project_milestone_path(milestone.project, milestone),
milestone_title: milestone.title,
group_name: @project.group.name,
target: '#promote-milestone-modal',
container: 'body',
toggle: 'modal' } }
= sprite_icon('level-up', size: 14)
- if @project # if in milestones list on project level
- if can_admin_group_milestones?
%button.js-promote-project-milestone-button.btn.btn-blank.btn-sm.btn-grouped.has-tooltip{ title: s_('Milestones|Promote to Group Milestone'),
disabled: true,
type: 'button',
data: { url: promote_project_milestone_path(milestone.project, milestone),
milestone_title: milestone.title,
group_name: @project.group.name,
target: '#promote-milestone-modal',
container: 'body',
toggle: 'modal' } }
= sprite_icon('level-up', size: 14)
- if can?(current_user, :admin_milestone, milestone)
- if milestone.closed?
= link_to s_('Milestones|Reopen Milestone'), milestone_path(milestone, milestone: { state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
- else
= link_to s_('Milestones|Close Milestone'), milestone_path(milestone, milestone: { state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close"
= link_to s_('Milestones|Close Milestone'), project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped"
- unless milestone.active?
= link_to s_('Milestones|Reopen Milestone'), project_milestone_path(@project, milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
- if @group
- if can?(current_user, :admin_milestone, @group)
- if milestone.closed?
= link_to s_('Milestones|Reopen Milestone'), group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
- else
= link_to s_('Milestones|Close Milestone'), group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close"
- if dashboard
.label-badge.label-badge-gray
= milestone_type

View File

@ -1,11 +1,9 @@
- page_title milestone.title
- @breadcrumb_link = dashboard_milestone_path(milestone.safe_title, title: milestone.title)
- @breadcrumb_link = milestone_path(milestone)
- group = local_assigns[:group]
- is_dynamic_milestone = milestone.legacy_group_milestone? || milestone.dashboard_milestone?
= render 'shared/milestones/header', milestone: milestone
= render 'shared/milestones/deprecation_message' if is_dynamic_milestone
= render 'shared/milestones/description', milestone: milestone
- if milestone.complete? && milestone.active?
@ -15,26 +13,3 @@
= group ? _('You may close the milestone now.') : _('Navigate to the project to close the milestone.')
= render_if_exists 'shared/milestones/burndown', milestone: milestone, project: @project
- if is_dynamic_milestone
.table-holder
%table.table
%thead
%tr
%th= _('Project')
%th= _('Open issues')
%th= _('State')
%th= _('Due date')
%tr
%td
- project_name = group ? milestone.project.name : milestone.project.full_name
= link_to project_name, milestone_path(milestone.milestone)
%td
= milestone.milestone.issues_visible_to_user(current_user).opened.count
%td
- if milestone.closed?
= _('Closed')
- else
= _('Open')
%td
= milestone.expires_at

View File

@ -6,13 +6,13 @@
.card-footer.footer-block.clearfix
- if can?(current_user, :accept_terms, @term)
.float-right
= button_to accept_term_path(@term, redirect_params), class: 'btn btn-success prepend-left-8', data: { qa_selector: 'accept_terms_button' } do
= button_to accept_term_path(@term, redirect_params), class: 'btn btn-success gl-ml-3', data: { qa_selector: 'accept_terms_button' } do
= _('Accept terms')
- else
.pull-right
= link_to root_path, class: 'btn btn-success prepend-left-8' do
= link_to root_path, class: 'btn btn-success gl-ml-3' do
= _('Continue')
- if can?(current_user, :decline_terms, @term)
.float-right
= button_to decline_term_path(@term, redirect_params), class: 'btn btn-default prepend-left-8' do
= button_to decline_term_path(@term, redirect_params), class: 'btn btn-default gl-ml-3' do
= _('Decline and sign out')

View File

@ -70,7 +70,7 @@ class IrkerWorker # rubocop:disable Scalability/IdempotentWorker
def send_new_branch(project, repo_name, committer, branch)
repo_path = project.full_path
newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/branches"
newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/-/branches"
newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors
privmsg = "[#{repo_name}] #{committer} has created a new branch " \
@ -124,7 +124,7 @@ class IrkerWorker # rubocop:disable Scalability/IdempotentWorker
def compare_url(data, repo_path)
sha1 = Commit.truncate_sha(data['before'])
sha2 = Commit.truncate_sha(data['after'])
compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/compare" \
compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/-/compare" \
"/#{sha1}...#{sha2}"
colorize_url compare_url
end

View File

@ -0,0 +1,5 @@
---
title: Refine SAST language detection by frameworks
merge_request: 33226
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Remove deprecated dashboard & group milestone pages
merge_request: 13237
author:
type: removed

View File

@ -0,0 +1,5 @@
---
title: Render Merge request reference as link
merge_request: 33248
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Fix preconnect typo in rel link
merge_request: 33255
author:
type: performance

View File

@ -0,0 +1,5 @@
---
title: Update deprecated routes in irker integration
merge_request: 32923
author: Marc Jeanmougin
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Add accessibility report MR widget
merge_request: 32902
author:
type: added

View File

@ -5,13 +5,7 @@ resource :dashboard, controller: 'dashboard', only: [] do
get :activity
scope module: :dashboard do
resources :milestones, only: [:index, :show] do
member do
get :merge_requests
get :participants
get :labels
end
end
resources :milestones, only: [:index]
resources :labels, only: [:index]
resources :groups, only: [:index]

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
class DropVulnerabilityConfidenceIndex < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
INDEX_NAME = 'index_vulnerability_on_id_and_confidence_eq_zero'
def up
Gitlab::BackgroundMigration.steal('RemoveUndefinedVulnerabilityConfidenceLevel')
remove_concurrent_index_by_name :vulnerabilities, INDEX_NAME
end
def down
add_concurrent_index(:vulnerabilities, :id, where: 'confidence = 0', name: INDEX_NAME)
end
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
class DropVulnerabilityOccurrenceConfidenceIndex < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
INDEX_NAME = 'index_vulnerability_occurrences_on_id_and_confidence_eq_zero'
def up
Gitlab::BackgroundMigration.steal('RemoveUndefinedOccurrenceConfidenceLevel')
remove_concurrent_index_by_name :vulnerability_occurrences, INDEX_NAME
end
def down
add_concurrent_index(:vulnerability_occurrences, :id, where: 'confidence = 0', name: INDEX_NAME)
end
end

View File

@ -10932,8 +10932,6 @@ CREATE UNIQUE INDEX index_vulnerability_occurrence_identifiers_on_unique_keys ON
CREATE INDEX index_vulnerability_occurrence_pipelines_on_pipeline_id ON public.vulnerability_occurrence_pipelines USING btree (pipeline_id);
CREATE INDEX index_vulnerability_occurrences_on_id_and_confidence_eq_zero ON public.vulnerability_occurrences USING btree (id) WHERE (confidence = 0);
CREATE INDEX index_vulnerability_occurrences_on_primary_identifier_id ON public.vulnerability_occurrences USING btree (primary_identifier_id);
CREATE INDEX index_vulnerability_occurrences_on_scanner_id ON public.vulnerability_occurrences USING btree (scanner_id);
@ -10944,8 +10942,6 @@ CREATE UNIQUE INDEX index_vulnerability_occurrences_on_uuid ON public.vulnerabil
CREATE INDEX index_vulnerability_occurrences_on_vulnerability_id ON public.vulnerability_occurrences USING btree (vulnerability_id);
CREATE INDEX index_vulnerability_on_id_and_confidence_eq_zero ON public.vulnerabilities USING btree (id) WHERE (confidence = 0);
CREATE UNIQUE INDEX index_vulnerability_scanners_on_project_id_and_external_id ON public.vulnerability_scanners USING btree (project_id, external_id);
CREATE UNIQUE INDEX index_vulnerability_user_mentions_on_note_id ON public.vulnerability_user_mentions USING btree (note_id) WHERE (note_id IS NOT NULL);
@ -13958,5 +13954,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200526120714
20200526164946
20200526164947
20200527094322
20200527095401
\.

View File

@ -184,6 +184,27 @@ required:
set the database to read-write:
- Amazon RDS - [Promoting a Read Replica to Be a Standalone DB Instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html#USER_ReadRepl.Promote)
- Azure Database for PostgreSQL - [Stop replication](https://docs.microsoft.com/en-us/azure/postgresql/howto-read-replicas-portal#stop-replication)
- Other external PostgreSQL databases - save the script below in you secondary node, for example
`/tmp/geo_promote.sh`, and modify the connection parameters to match your
environment. Then, execute it to promote the replica:
```shell
#!/bin/bash
PG_SUPERUSER=postgres
# The path to your pg_ctl binary. You may need to adjust this path to match
# your PostgreSQL installation
PG_CTL_BINARY=/usr/lib/postgresql/10/bin/pg_ctl
# The path to your PostgreSQL data directory. You may need to adjust this
# path to match your PostgreSQL installation. You can also run
# `SHOW data_directory;` from PostgreSQL to find your data directory
PG_DATA_DIRECTORY=/etc/postgresql/10/main
# Promote the PostgreSQL database and allow read/write operations
sudo -u $PG_SUPERUSER $PG_CTL_BINARY -D $PG_DATA_DIRECTORY promote
```
1. Edit `/etc/gitlab/gitlab.rb` on every node in the **secondary** site to
reflect its new status as **primary** by removing any lines that enabled the

View File

@ -2,9 +2,9 @@
type: reference
---
# Load Balancer for GitLab HA
# Load Balancer for multi-node GitLab
In an active/active GitLab configuration, you will need a load balancer to route
In an multi-node GitLab configuration, you will need a load balancer to route
traffic to the application servers. The specifics on which load balancer to use
or the exact configuration is beyond the scope of GitLab documentation. We hope
that if you're managing HA systems like GitLab you have a load balancer of
@ -14,7 +14,7 @@ you need to use with GitLab.
## SSL
How will you handle SSL in your HA environment? There are several different
How will you handle SSL in your multi-node environment? There are several different
options:
- Each application node terminates SSL

View File

@ -204,11 +204,11 @@ Following you'll find some general common practices you will find as part of our
### How to query DOM elements
When it comes to querying DOM elements in your tests, it is best to uniquely target the element, without adding additional attributes specifically for testing purposes. Sometimes this cannot be done feasibly. In these cases, adding test attributes to simplify the selectors might be the best option.
When it comes to querying DOM elements in your tests, it is best to uniquely and semantically target the element. Sometimes this cannot be done feasibly. In these cases, adding test attributes to simplify the selectors might be the best option.
Preferentially, in component testing with `@vue/test-utils`, you should query for child components using the component itself. This helps enforce that specific behavior can be covered by that component's individual unit tests. Otherwise, try to use:
- A behavioral attribute like `name` (also verifies that `name` was setup properly)
- A semantic attribute like `name` (also verifies that `name` was setup properly)
- A `data-testid` attribute ([recommended by maintainers of `@vue/test-utils`](https://github.com/vuejs/vue-test-utils/issues/1498#issuecomment-610133465))
- a Vue `ref` (if using `@vue/test-utils`)
@ -216,11 +216,17 @@ Examples:
```javascript
it('exists', () => {
// Good
wrapper.find(FooComponent);
wrapper.find('input[name=foo]');
wrapper.find('[data-testid="foo"]');
wrapper.find({ ref: 'foo'});
// Bad
wrapper.find('.js-foo');
wrapper.find('.btn-primary');
wrapper.find('.qa-foo-component');
wrapper.find('[data-qa-selector="foo"]');
});
```

View File

@ -288,10 +288,11 @@ You can provide feedback [on this issue](https://gitlab.com/gitlab-org/gitlab/-/
GitLab supports the following web browsers:
- Firefox
- Chrome/Chromium
- Safari
- Microsoft Edge
- [Mozilla Firefox](https://www.mozilla.org/firefox/new/)
- [Google Chrome](https://www.google.com/chrome/)
- [Chromium](https://www.chromium.org/getting-involved/dev-channel)
- [Apple Safari](https://www.apple.com/safari/)
- [Microsoft Edge](https://www.microsoft.com/edge)
- Internet Explorer 11 (until May 2020)
For the listed web browsers, GitLab supports:

View File

@ -115,16 +115,16 @@ repository in GitLab 10.4 and later:
- Ancestor renamed
- Ancestor transferred to another namespace
Bare repositories are **not** importable by GitLab 10.4 and later when all the following are true about the repository:
Bare repositories are **not** importable by GitLab 10.4 to GitLab 11.6, if all the following are true about the repository:
- It was created in GitLab 10.3 or earlier.
- It was not renamed, transferred, or migrated to hashed storage in GitLab 10.4 and later.
- Its ancestor namespaces were not renamed or transferred in GitLab 10.4 and later.
- It was not renamed, transferred, or migrated to [hashed storage](../administration/repository_storage_types.md#hashed-storage) in GitLab 10.4 to GitLab 11.6.
- Its ancestor namespaces were not renamed or transferred in GitLab 10.4 to GitLab 11.6.
There is an [open issue to add a migration to make all bare repositories
importable](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41776).
[Since GitLab 11.6](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41776), all
bare repositories are importable.
Until then, you may wish to manually migrate repositories yourself. You can use
To manually migrate repositories yourself (for GitLab 10.4 to GitLab 11.6), you can use the
[Rails console](../administration/troubleshooting/debug.md#starting-a-rails-console-session)
to do so. In a Rails console session, run the following to migrate a project:

View File

@ -25,16 +25,6 @@ Accessibility Report in the merge request widget area:
![Accessibility Merge Request Widget](img/accessibility_mr_widget_v13_0.png)
This widget comes with the `:accessibility_report_view` feature flag disabled by default while we test feature stability.
Once we have determined the widget is stable, this feature will be enabled by default.
To enable this feature, ask a GitLab administrator with [Rails console access](../../../administration/feature_flags.md#how-to-enable-and-disable-features-behind-flags) to run the
following command:
```ruby
Feature.enable(:accessibility_report_view)
```
## Configure Accessibility Testing
This example shows how to run [pa11y](https://pa11y.org/)

View File

@ -83,7 +83,7 @@ brakeman-sast:
$GITLAB_FEATURES =~ /\bsast\b/ &&
$SAST_DEFAULT_ANALYZERS =~ /brakeman/
exists:
- '**/*.rb'
- 'config/routes.rb'
eslint-sast:
extends: .sast-analyzer
@ -149,7 +149,7 @@ nodejs-scan-sast:
$GITLAB_FEATURES =~ /\bsast\b/ &&
$SAST_DEFAULT_ANALYZERS =~ /nodejs-scan/
exists:
- '**/*.js'
- 'package.json'
phpcs-security-audit-sast:
extends: .sast-analyzer
@ -213,8 +213,7 @@ sobelow-sast:
$GITLAB_FEATURES =~ /\bsast\b/ &&
$SAST_DEFAULT_ANALYZERS =~ /sobelow/
exists:
- '**/*.ex'
- '**/*.exs'
- 'mix.exs'
spotbugs-sast:
extends: .sast-analyzer

View File

@ -1,42 +0,0 @@
# frozen_string_literal: true
module MilestoneArray
class << self
def sort(array, sort_method)
case sort_method
when 'due_date_asc'
sort_asc_nulls_last(array, 'due_date')
when 'due_date_desc'
sort_desc_nulls_last(array, 'due_date')
when 'start_date_asc'
sort_asc_nulls_last(array, 'start_date')
when 'start_date_desc'
sort_desc_nulls_last(array, 'start_date')
when 'name_asc'
sort_asc(array, 'title')
when 'name_desc'
sort_asc(array, 'title').reverse
else
array
end
end
private
def sort_asc_nulls_last(array, attribute)
attribute = attribute.to_sym
array.select(&attribute).sort_by(&attribute) + array.reject(&attribute)
end
def sort_desc_nulls_last(array, attribute)
attribute = attribute.to_sym
array.select(&attribute).sort_by(&attribute).reverse + array.reject(&attribute)
end
def sort_asc(array, attribute)
array.sort_by(&attribute.to_sym)
end
end
end

View File

@ -20735,9 +20735,6 @@ msgstr ""
msgid "Starts at (UTC)"
msgstr ""
msgid "State"
msgstr ""
msgid "State your message to activate"
msgstr ""

View File

@ -8,12 +8,7 @@ describe Dashboard::MilestonesController do
let(:user) { create(:user) }
let(:project_milestone) { create(:milestone, project: project) }
let(:group_milestone) { create(:milestone, group: group) }
let(:milestone) do
DashboardMilestone.build(
[project],
project_milestone.title
)
end
let(:milestone) { create(:milestone, group: group) }
let(:issue) { create(:issue, project: project, milestone: project_milestone) }
let(:group_issue) { create(:issue, milestone: group_milestone, project: create(:project, group: group)) }
@ -28,22 +23,6 @@ describe Dashboard::MilestonesController do
group.add_developer(user)
end
it_behaves_like 'milestone tabs'
describe "#show" do
render_views
def view_milestone
get :show, params: { id: milestone.safe_title, title: milestone.title }
end
it 'shows milestone page' do
view_milestone
expect(response).to have_gitlab_http_status(:ok)
end
end
describe "#index" do
let(:public_group) { create(:group, :public) }
let!(:public_milestone) { create(:milestone, group: public_group) }
@ -58,7 +37,6 @@ describe Dashboard::MilestonesController do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.size).to eq(2)
expect(json_response.map { |i| i["name"] }).to match_array([group_milestone.name, project_milestone.name])
expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name)
end
it 'returns closed group and project milestones to which the user belongs' do
@ -67,7 +45,6 @@ describe Dashboard::MilestonesController do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.size).to eq(2)
expect(json_response.map { |i| i["name"] }).to match_array([closed_group_milestone.name, closed_project_milestone.name])
expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name)
end
it 'searches legacy project milestones by title when search_title is given' do

View File

@ -8,15 +8,7 @@ describe Groups::MilestonesController do
let!(:project2) { create(:project, group: group) }
let(:user) { create(:user) }
let(:title) { '肯定不是中文的问题' }
let(:milestone) do
project_milestone = create(:milestone, project: project)
GroupMilestone.build(
group,
[project],
project_milestone.title
)
end
let(:milestone) { create(:milestone, project: project) }
let(:milestone_path) { group_milestone_path(group, milestone.safe_title, title: milestone.title) }
let(:milestone_params) do
@ -168,17 +160,16 @@ describe Groups::MilestonesController do
context 'as JSON' do
let!(:milestone) { create(:milestone, group: group, title: 'group milestone') }
let!(:legacy_milestone1) { create(:milestone, project: project, title: 'legacy') }
let!(:legacy_milestone2) { create(:milestone, project: project2, title: 'legacy') }
let!(:project_milestone1) { create(:milestone, project: project, title: 'same name') }
let!(:project_milestone2) { create(:milestone, project: project2, title: 'same name') }
it 'lists legacy group milestones and group milestones' do
it 'lists project and group milestones' do
get :index, params: { group_id: group.to_param }, format: :json
milestones = json_response
expect(milestones.count).to eq(2)
expect(milestones.first["title"]).to eq("group milestone")
expect(milestones.second["title"]).to eq("legacy")
expect(milestones.count).to eq(3)
expect(milestones.collect { |m| m['title'] }).to match_array(['same name', 'same name', 'group milestone'])
expect(response).to have_gitlab_http_status(:ok)
expect(response.content_type).to eq 'application/json'
end
@ -191,8 +182,9 @@ describe Groups::MilestonesController do
get :index, params: { group_id: group.to_param }, format: :json
milestones = json_response
expect(milestones.count).to eq(3)
expect(milestones.second["title"]).to eq("subgroup milestone")
milestone_titles = milestones.map { |m| m['title'] }
expect(milestones.count).to eq(4)
expect(milestone_titles).to match_array(['same name', 'same name', 'group milestone', 'subgroup milestone'])
end
end
@ -218,31 +210,18 @@ describe Groups::MilestonesController do
end
describe '#show' do
let(:milestone1) { create(:milestone, project: project, title: 'legacy') }
let(:milestone2) { create(:milestone, project: project, title: 'legacy') }
let(:group_milestone) { create(:milestone, group: group) }
render_views
context 'when there is a title parameter' do
it 'searches for a legacy group milestone' do
expect(GroupMilestone).to receive(:build)
expect(Milestone).not_to receive(:find_by_iid)
let!(:group_milestone) { create(:milestone, group: group) }
get :show, params: { group_id: group.to_param, id: title, title: milestone1.safe_title }
end
end
it 'renders for a group milestone' do
get :show, params: { group_id: group.to_param, id: group_milestone.iid }
context 'when there is not a title parameter' do
it 'searches for a group milestone' do
expect(GlobalMilestone).not_to receive(:build)
expect(Milestone).to receive(:find_by_iid)
get :show, params: { group_id: group.to_param, id: group_milestone.id }
end
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).to include(group_milestone.title)
end
end
it_behaves_like 'milestone tabs'
describe "#create" do
it "creates group milestone with Chinese title" do
post :create,
@ -277,34 +256,6 @@ describe Groups::MilestonesController do
expect(response).to redirect_to(group_milestone_path(group, milestone.iid))
expect(milestone.title).to eq("title changed")
end
context "legacy group milestones" do
let!(:milestone1) { create(:milestone, project: project, title: 'legacy milestone', description: "old description") }
let!(:milestone2) { create(:milestone, project: project2, title: 'legacy milestone', description: "old description") }
it "updates only group milestones state" do
milestone_params[:title] = "title changed"
milestone_params[:description] = "description changed"
milestone_params[:state_event] = "close"
put :update,
params: {
id: milestone1.title.to_slug.to_s,
group_id: group.to_param,
milestone: milestone_params,
title: milestone1.title
}
expect(response).to redirect_to(group_milestone_path(group, milestone1.safe_title, title: milestone1.title))
[milestone1, milestone2].each do |milestone|
milestone.reload
expect(milestone.title).to eq("legacy milestone")
expect(milestone.description).to eq("old description")
expect(milestone.state).to eq("closed")
end
end
end
end
describe "#destroy" do

View File

@ -1409,20 +1409,6 @@ describe Projects::MergeRequestsController do
end
end
context 'when feature flag is disabled' do
let(:accessibility_comparison) { { status: :parsed, data: { summary: 1 } } }
before do
stub_feature_flags(accessibility_report_view: false)
end
it 'returns 204 HTTP status' do
subject
expect(response).to have_gitlab_http_status(:no_content)
end
end
context 'when pipeline has jobs with accessibility reports' do
before do
allow_any_instance_of(MergeRequest)

View File

@ -1,42 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Dashboard milestone tabs', :js do
let(:user) { create(:user) }
let(:project) { create(:project) }
let!(:label) { create(:label, project: project) }
let(:project_milestone) { create(:milestone, project: project) }
let(:milestone) do
DashboardMilestone.build(
[project],
project_milestone.title
)
end
let!(:merge_request) { create(:labeled_merge_request, source_project: project, target_project: project, milestone: project_milestone, labels: [label]) }
before do
project.add_maintainer(user)
sign_in(user)
visit dashboard_milestone_path(milestone.safe_title, title: milestone.title)
end
it 'loads merge requests async' do
click_link 'Merge Requests'
expect(page).to have_selector('.milestone-merge_requests-list')
end
it 'loads participants async' do
click_link 'Participants'
expect(page).to have_selector('#tab-participants .bordered-list')
end
it 'loads labels async' do
click_link 'Labels'
expect(page).to have_selector('#tab-labels .bordered-list')
end
end

View File

@ -102,11 +102,9 @@ describe 'Group milestones' do
expect(find('.top-area .all .badge').text).to eq("6")
end
it 'lists legacy group milestones and group milestones' do
legacy_milestone = GroupMilestone.build_collection(group, group.projects, { state: 'active' }).first
it 'lists group and project milestones' do
expect(page).to have_selector("#milestone_#{active_group_milestone.id}", count: 1)
expect(page).to have_selector("#milestone_#{legacy_milestone.milestone.id}", count: 1)
expect(page).to have_selector("#milestone_#{active_project_milestone2.id}", count: 1)
end
it 'shows milestone detail and supports its edit' do
@ -125,75 +123,35 @@ describe 'Group milestones' do
expect(page).to have_content('v1.0')
expect(page).to have_content('v1.1')
expect(page).to have_content('GL-113')
expect(page).to have_link(
'v1.0',
href: project_milestone_path(project, active_project_milestone1)
)
expect(page).to have_link(
'1 Issue',
href: issues_group_path(group, milestone_title: 'v1.0')
href: project_issues_path(project, milestone_title: 'v1.0')
)
expect(page).to have_link(
'0 Merge Requests',
href: merge_requests_group_path(group, milestone_title: 'v1.0')
href: project_merge_requests_path(project, milestone_title: 'v1.0')
)
expect(page).to have_link(
'GL-113',
href: group_milestone_path(group, active_group_milestone)
)
expect(page).to have_link(
'0 Issues',
href: issues_group_path(group, milestone_title: 'GL-113')
)
expect(page).to have_link(
'0 Merge Requests',
href: merge_requests_group_path(group, milestone_title: 'GL-113')
)
end
it 'renders group milestone details' do
click_link 'v1.0'
expect(page).to have_content('expires on Aug 20, 2114')
expect(page).to have_content('v1.0')
expect(page).to have_content('Issues 1 Open: 1 Closed: 0')
expect(page).to have_link(issue.title, href: project_issue_path(issue.project, issue))
end
end
end
describe 'milestone tabs', :js do
context 'for a legacy group milestone' do
let_it_be(:milestone) { create(:milestone, project: project) }
let_it_be(:label) { create(:label, project: project) }
let_it_be(:issue) { create(:labeled_issue, project: project, milestone: milestone, labels: [label], assignees: [create(:user)]) }
let_it_be(:mr) { create(:merge_request, source_project: project, milestone: milestone) }
before do
visit group_milestone_path(group, milestone.title, title: milestone.title)
end
it 'renders the issues tab' do
within('#tab-issues') do
expect(page).to have_content issue.title
end
end
it 'renders the merge requests tab' do
within('.js-milestone-tabs') do
click_link('Merge Requests')
end
within('#tab-merge-requests') do
expect(page).to have_content mr.title
end
end
it 'renders the participants tab' do
within('.js-milestone-tabs') do
click_link('Participants')
end
within('#tab-participants') do
expect(page).to have_content issue.assignees.first.name
end
end
it 'renders the labels tab' do
within('.js-milestone-tabs') do
click_link('Labels')
end
within('#tab-labels') do
expect(page).to have_content label.title
end
end
end
context 'for a group milestone' do
let_it_be(:other_project) { create(:project_empty_repo, group: group) }
let_it_be(:milestone) { create(:milestone, group: group) }

View File

@ -24,21 +24,12 @@ describe 'Milestones sorting', :js do
# assert default sorting
within '.milestones' do
expect(page.all('ul.content-list > li').first.text).to include('v2.0')
expect(page.all('ul.content-list > li')[1].text).to include('v3.0')
expect(page.all('ul.content-list > li').last.text).to include('v1.0')
expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(['v2.0', 'v2.0', 'v3.0', 'v1.0', 'v1.0'])
end
click_button 'Due soon'
sort_options = find('ul.dropdown-menu-sort li').all('a').collect(&:text)
expect(sort_options[0]).to eq('Due soon')
expect(sort_options[1]).to eq('Due later')
expect(sort_options[2]).to eq('Start soon')
expect(sort_options[3]).to eq('Start later')
expect(sort_options[4]).to eq('Name, ascending')
expect(sort_options[5]).to eq('Name, descending')
expect(find('ul.dropdown-menu-sort li').all('a').map(&:text)).to eq(['Due soon', 'Due later', 'Start soon', 'Start later', 'Name, ascending', 'Name, descending'])
click_link 'Due later'
@ -46,9 +37,7 @@ describe 'Milestones sorting', :js do
# assert descending sorting
within '.milestones' do
expect(page.all('ul.content-list > li').first.text).to include('v1.0')
expect(page.all('ul.content-list > li')[1].text).to include('v3.0')
expect(page.all('ul.content-list > li').last.text).to include('v2.0')
expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(['v1.0', 'v1.0', 'v3.0', 'v2.0', 'v2.0'])
end
end
end

View File

@ -111,20 +111,6 @@ describe 'Milestone' do
end
end
describe 'deprecation popover', :js do
it 'opens deprecation popover' do
milestone = create(:milestone, project: project)
visit group_milestone_path(group, milestone, title: milestone.title)
expect(page).to have_selector('.milestone-deprecation-message')
find('.milestone-deprecation-message .js-popover-link').click
expect(page).to have_selector('.popover')
end
end
describe 'reopen closed milestones' do
before do
create(:milestone, :closed, project: project)

View File

@ -14,7 +14,7 @@ exports[`IDE pipeline stage renders stage details & icon 1`] = `
/>
<strong
class="prepend-left-8 text-truncate"
class="gl-ml-3 text-truncate"
data-container="body"
data-original-title=""
title=""

View File

@ -1,36 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
describe MilestoneArray do
let(:object1) { instance_double("BirdMilestone", due_date: Time.now, start_date: Time.now - 15.days, title: 'v2.0') }
let(:object2) { instance_double("CatMilestone", due_date: Time.now - 1.day, start_date: nil, title: 'v1.0') }
let(:object3) { instance_double("DogMilestone", due_date: nil, start_date: Time.now - 30.days, title: 'v3.0') }
let(:array) { [object1, object3, object2] }
describe '#sort' do
it 'reorders array with due date in ascending order with nulls last' do
expect(described_class.sort(array, 'due_date_asc')).to eq([object2, object1, object3])
end
it 'reorders array with due date in desc order with nulls last' do
expect(described_class.sort(array, 'due_date_desc')).to eq([object1, object2, object3])
end
it 'reorders array with start date in ascending order with nulls last' do
expect(described_class.sort(array, 'start_date_asc')).to eq([object3, object1, object2])
end
it 'reorders array with start date in descending order with nulls last' do
expect(described_class.sort(array, 'start_date_desc')).to eq([object1, object3, object2])
end
it 'reorders array with title in ascending order' do
expect(described_class.sort(array, 'name_asc')).to eq([object2, object1, object3])
end
it 'reorders array with title in descending order' do
expect(described_class.sort(array, 'name_desc')).to eq([object3, object1, object2])
end
end
end

View File

@ -1,208 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
describe GlobalMilestone do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:group) { create(:group) }
let(:project1) { create(:project, group: group) }
let(:project2) { create(:project, path: 'gitlab-ci', group: group) }
let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) }
describe '.build_collection' do
let(:milestone1_due_date) { 2.weeks.from_now.to_date }
let!(:milestone1_project1) do
create(
:milestone,
title: "Milestone v1.2",
project: project1,
due_date: milestone1_due_date
)
end
let!(:milestone1_project2) do
create(
:milestone,
title: "Milestone v1.2",
project: project2,
due_date: milestone1_due_date
)
end
let!(:milestone1_project3) do
create(
:milestone,
title: "Milestone v1.2",
project: project3,
due_date: milestone1_due_date
)
end
let!(:milestone2_project1) do
create(
:milestone,
title: "VD-123",
project: project1,
due_date: nil
)
end
let!(:milestone2_project2) do
create(
:milestone,
title: "VD-123",
project: project2,
due_date: nil
)
end
let!(:milestone2_project3) do
create(
:milestone,
title: "VD-123",
project: project3,
due_date: nil
)
end
let!(:projects) do
[
project1,
project2,
project3
]
end
let!(:global_milestones) { described_class.build_collection(projects, {}) }
context 'when building a collection of milestones' do
it 'has all project milestones' do
expect(global_milestones.count).to eq(6)
end
it 'has all project milestones titles' do
expect(global_milestones.map(&:title)).to match_array(['Milestone v1.2', 'Milestone v1.2', 'Milestone v1.2', 'VD-123', 'VD-123', 'VD-123'])
end
it 'has all project milestones' do
expect(global_milestones.size).to eq(6)
end
it 'sorts collection by due date' do
expect(global_milestones.map(&:due_date)).to eq [milestone1_due_date, milestone1_due_date, milestone1_due_date, nil, nil, nil]
end
it 'filters milestones by search_title when params[:search_title] is present' do
global_milestones = described_class.build_collection(projects, { search_title: 'v1.2' })
expect(global_milestones.map(&:title)).to match_array(['Milestone v1.2', 'Milestone v1.2', 'Milestone v1.2'])
end
end
context 'when adding new milestones' do
it 'does not add more queries' do
control_count = ActiveRecord::QueryRecorder.new do
described_class.build_collection(projects, {})
end.count
create_list(:milestone, 3, project: project3)
expect do
described_class.build_collection(projects, {})
end.not_to exceed_all_query_limit(control_count)
end
end
end
describe '.states_count' do
context 'when the projects have milestones' do
before do
create(:closed_milestone, title: 'Active Group Milestone', project: project3)
create(:active_milestone, title: 'Active Group Milestone', project: project1)
create(:active_milestone, title: 'Active Group Milestone', project: project2)
create(:closed_milestone, title: 'Closed Group Milestone', project: project1)
create(:closed_milestone, title: 'Closed Group Milestone', project: project2)
create(:closed_milestone, title: 'Closed Group Milestone', project: project3)
create(:closed_milestone, title: 'Closed Group Milestone 4', group: group)
end
it 'returns the quantity of global milestones and group milestones in each possible state' do
expected_count = { opened: 2, closed: 5, all: 7 }
count = described_class.states_count(Project.all, group)
expect(count).to eq(expected_count)
end
it 'returns the quantity of global milestones in each possible state' do
expected_count = { opened: 2, closed: 4, all: 6 }
count = described_class.states_count(Project.all)
expect(count).to eq(expected_count)
end
end
context 'when the projects do not have milestones' do
before do
project1
end
it 'returns 0 as the quantity of global milestones in each state' do
expected_count = { opened: 0, closed: 0, all: 0 }
count = described_class.states_count(Project.all)
expect(count).to eq(expected_count)
end
end
end
describe '#initialize' do
let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) }
subject(:global_milestone) { described_class.new(milestone1_project1) }
it 'has exactly one group milestone' do
expect(global_milestone.title).to eq('Milestone v1.2')
end
it 'has all project milestones with the same title' do
expect(global_milestone.milestone).to eq(milestone1_project1)
end
end
describe '#safe_title' do
let(:milestone) { create(:milestone, title: "git / test", project: project1) }
it 'strips out slashes and spaces' do
global_milestone = described_class.new(milestone)
expect(global_milestone.safe_title).to eq('git-test')
end
end
describe '#state' do
context 'when at least one milestone is active' do
it 'returns active' do
title = 'Active Group Milestone'
global_milestone = described_class.new(create(:active_milestone, title: title))
expect(global_milestone.state).to eq('active')
end
end
context 'when all milestones are closed' do
it 'returns closed' do
title = 'Closed Group Milestone'
global_milestone = described_class.new(create(:closed_milestone, title: title))
expect(global_milestone.state).to eq('closed')
end
end
end
end

View File

@ -1,57 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
describe GroupMilestone do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let(:project_milestone) do
create(:milestone, title: "Milestone v1.2", project: project)
end
describe '.build' do
it 'returns milestone with group assigned' do
milestone = described_class.build(
group,
[project],
project_milestone.title
)
expect(milestone.group).to eq group
end
end
describe '.build_collection' do
let(:group) { create(:group) }
let(:project1) { create(:project, group: group) }
let(:project2) { create(:project, path: 'gitlab-ci', group: group) }
let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) }
let!(:projects) do
[
project1,
project2,
project3
]
end
it 'returns array of milestones, each with group assigned' do
milestones = described_class.build_collection(group, [project], {})
expect(milestones).to all(have_attributes(group: group))
end
context 'when adding new milestones' do
it 'does not add more queries' do
control_count = ActiveRecord::QueryRecorder.new do
described_class.build_collection(group, projects, {})
end.count
create(:milestone, title: 'This title', project: project1)
expect do
described_class.build_collection(group, projects, {})
end.not_to exceed_all_query_limit(control_count)
end
end
end
end

View File

@ -1651,14 +1651,6 @@ describe MergeRequest do
let(:merge_request) { create(:merge_request, :with_accessibility_reports, source_project: project) }
it { is_expected.to be_truthy }
context 'when feature flag is disabled' do
before do
stub_feature_flags(accessibility_report_view: false)
end
it { is_expected.to be_falsey }
end
end
context 'when head pipeline does not have accessibility reports' do

View File

@ -2,15 +2,7 @@
RSpec.shared_examples 'milestone tabs' do
def go(path, extra_params = {})
params =
case milestone
when DashboardMilestone
{ id: milestone.safe_title, title: milestone.title }
when GroupMilestone
{ group_id: group.to_param, id: milestone.safe_title, title: milestone.title }
else
{ namespace_id: project.namespace.to_param, project_id: project, id: milestone.iid }
end
params = { namespace_id: project.namespace.to_param, project_id: project, id: milestone.iid }
get path, params: params.merge(extra_params)
end

View File

@ -50,7 +50,7 @@ describe 'layouts/_head' do
it 'adds a link preconnect tag' do
render
expect(rendered).to match(%Q(<link crossorigin="" href="#{asset_host}" rel="preconnnect">))
expect(rendered).to match(%Q(<link crossorigin="" href="#{asset_host}" rel="preconnect">))
end
end

View File

@ -12,22 +12,6 @@ describe 'shared/milestones/_top.html.haml' do
allow(milestone).to receive(:milestone) { milestone }
end
it 'renders a deprecation message for a legacy milestone' do
allow(milestone).to receive(:legacy_group_milestone?) { true }
render 'shared/milestones/top', milestone: milestone
expect(rendered).to have_css('.milestone-deprecation-message')
end
it 'renders a deprecation message for a dashboard milestone' do
allow(milestone).to receive(:dashboard_milestone?) { true }
render 'shared/milestones/top', milestone: milestone
expect(rendered).to have_css('.milestone-deprecation-message')
end
it 'does not render a deprecation message for a non-legacy and non-dashboard milestone' do
assign :group, group