Commit c916bb3c authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch '39545-cleanup-dynamic-milestone-pages-ee' into 'master'

Remove deprecated dashboard & group milestone pages

See merge request gitlab-org/gitlab!13237
parents d00e8e24 8a29bcbd
...@@ -51,13 +51,7 @@ module MilestoneActions ...@@ -51,13 +51,7 @@ module MilestoneActions
} }
end end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def milestone_redirect_path def milestone_redirect_path
if @milestone.global_milestone? url_for(action: :show)
url_for(action: :show, title: @milestone.title)
else
url_for(action: :show)
end
end end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end end
# frozen_string_literal: true # frozen_string_literal: true
class Dashboard::MilestonesController < Dashboard::ApplicationController class Dashboard::MilestonesController < Dashboard::ApplicationController
include MilestoneActions
before_action :projects before_action :projects
before_action :groups, only: :index before_action :groups, only: :index
before_action :milestone, only: [:show, :merge_requests, :participants, :labels]
def index def index
respond_to do |format| respond_to do |format|
format.html do format.html do
@milestone_states = Milestone.states_count(@projects.select(:id), @groups.select(:id)) @milestone_states = Milestone.states_count(@projects.select(:id), groups.select(:id))
@milestones = Kaminari.paginate_array(milestones).page(params[:page]) @milestones = milestones.page(params[:page])
end end
format.json do format.json do
render json: milestones render json: milestones.to_json(only: [:id, :title], methods: :name)
end end
end end
end end
def show
end
private 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 def milestones
@milestones = group_milestones + dynamic_milestones MilestonesFinder.new(search_params).execute
end
def milestone
@milestone = DashboardMilestone.build(@projects, params[:title])
render_404 unless @milestone
end end
def groups def groups
@groups ||= GroupsFinder.new(current_user, all_available: false).execute @groups ||= GroupsFinder.new(current_user, all_available: false).execute
end end
def search_params
params.permit(:state, :search_title).merge(group_ids: groups, project_ids: projects)
end
end end
...@@ -13,10 +13,10 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -13,10 +13,10 @@ class Groups::MilestonesController < Groups::ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
@milestone_states = Milestone.states_count(group_projects_with_access, [group]) @milestone_states = Milestone.states_count(group_projects_with_access, [group])
@milestones = Kaminari.paginate_array(milestones).page(params[:page]) @milestones = milestones.page(params[:page])
end end
format.json do 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 end
end end
...@@ -29,7 +29,7 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -29,7 +29,7 @@ class Groups::MilestonesController < Groups::ApplicationController
@milestone = Milestones::CreateService.new(group, current_user, milestone_params).execute @milestone = Milestones::CreateService.new(group, current_user, milestone_params).execute
if @milestone.persisted? if @milestone.persisted?
redirect_to milestone_path redirect_to milestone_path(@milestone)
else else
render "new" render "new"
end end
...@@ -39,23 +39,15 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -39,23 +39,15 @@ class Groups::MilestonesController < Groups::ApplicationController
end end
def edit def edit
render_404 if @milestone.legacy_group_milestone?
end end
def update def update
# Keep this compatible with legacy group milestones where we have to update Milestones::UpdateService.new(@milestone.parent, current_user, milestone_params).execute(@milestone)
# 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
redirect_to milestone_path redirect_to milestone_path(@milestone)
end end
def destroy def destroy
return render_404 if @milestone.legacy_group_milestone?
Milestones::DestroyService.new(group, current_user).execute(@milestone) Milestones::DestroyService.new(group, current_user).execute(@milestone)
respond_to do |format| respond_to do |format|
...@@ -66,14 +58,6 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -66,14 +58,6 @@ class Groups::MilestonesController < Groups::ApplicationController
private 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! def authorize_admin_milestones!
return render_404 unless can?(current_user, :admin_milestone, group) return render_404 unless can?(current_user, :admin_milestone, group)
end end
...@@ -82,27 +66,21 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -82,27 +66,21 @@ class Groups::MilestonesController < Groups::ApplicationController
params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event) params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
end end
def legacy_milestone_params def milestones
params.require(:milestone).permit(:state_event) MilestonesFinder.new(search_params).execute
end end
def milestone_path def milestone
if @milestone.legacy_group_milestone? @milestone = group.milestones.find_by_iid(params[:id])
group_milestone_path(group, @milestone.safe_title, title: @milestone.title)
else render_404 unless @milestone
group_milestone_path(group, @milestone.iid)
end
end end
def milestones def search_params
milestones = MilestonesFinder.new(search_params).execute groups = request.format.json? ? group_ids(include_ancestors: true) : group_ids
@sort = params[:sort] || 'due_date_asc' @sort = params[:sort] || 'due_date_asc'
MilestoneArray.sort(milestones + legacy_milestones, @sort) params.permit(:state, :search_title).merge(sort: @sort, group_ids: groups, project_ids: group_projects_with_access)
end
def legacy_milestones
GroupMilestone.build_collection(group, group_projects_with_access, params)
end end
def group_projects_with_access def group_projects_with_access
...@@ -116,23 +94,6 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -116,23 +94,6 @@ class Groups::MilestonesController < Groups::ApplicationController
group.self_and_descendants.public_or_visible_to_user(current_user).select(:id) group.self_and_descendants.public_or_visible_to_user(current_user).select(:id)
end end
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 end
Groups::MilestonesController.prepend_if_ee('EE::Groups::MilestonesController') Groups::MilestonesController.prepend_if_ee('EE::Groups::MilestonesController')
...@@ -34,7 +34,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -34,7 +34,7 @@ class Projects::MilestonesController < Projects::ApplicationController
@milestones = @milestones.page(params[:page]) @milestones = @milestones.page(params[:page])
end end
format.json do format.json do
render json: @milestones.to_json(methods: :name) render json: @milestones.to_json(only: [:id, :title], methods: :name)
end end
end end
end end
......
...@@ -58,10 +58,8 @@ class MilestonesFinder ...@@ -58,10 +58,8 @@ class MilestonesFinder
Milestone.filter_by_state(items, params[:state]) Milestone.filter_by_state(items, params[:state])
end end
# rubocop: disable CodeReuse/ActiveRecord
def order(items) def order(items)
order_statement = Gitlab::Database.nulls_last_order('due_date', 'ASC') sort_by = params[:sort].presence || 'due_date_asc'
items.reorder(order_statement).order('title ASC') items.sort_by_attribute(sort_by)
end end
# rubocop: enable CodeReuse/ActiveRecord
end end
...@@ -229,11 +229,7 @@ module TimeboxesHelper ...@@ -229,11 +229,7 @@ module TimeboxesHelper
alias_method :milestone_date_range, :timebox_date_range alias_method :milestone_date_range, :timebox_date_range
def milestone_tab_path(milestone, tab) def milestone_tab_path(milestone, tab)
if milestone.global_milestone? url_for(action: tab, format: :json)
url_for(action: tab, title: milestone.title, format: :json)
else
url_for(action: tab, format: :json)
end
end end
def update_milestone_path(milestone, params = {}) def update_milestone_path(milestone, params = {})
...@@ -247,11 +243,7 @@ module TimeboxesHelper ...@@ -247,11 +243,7 @@ module TimeboxesHelper
def group_milestone_route(milestone, params = {}) def group_milestone_route(milestone, params = {})
params = nil if params.empty? params = nil if params.empty?
if milestone.legacy_group_milestone? group_milestone_path(milestone.group, milestone.iid, milestone: params)
group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: params)
else
group_milestone_path(milestone.group, milestone.iid, milestone: params)
end
end end
def group_or_project_milestone_path(milestone) def group_or_project_milestone_path(milestone)
......
...@@ -97,26 +97,6 @@ module Milestoneish ...@@ -97,26 +97,6 @@ module Milestoneish
due_date && due_date.past? due_date && due_date.past?
end 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 def total_time_spent
@total_time_spent ||= issues.joins(:timelogs).sum(:time_spent) + merge_requests.joins(:timelogs).sum(:time_spent) @total_time_spent ||= issues.joins(:timelogs).sum(:time_spent) + merge_requests.joins(:timelogs).sum(:time_spent)
end end
......
# 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
# 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
# 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')
# 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')
= render 'shared/milestones/milestone', = render 'shared/milestones/milestone',
milestone_path: group_or_project_milestone_path(milestone),
issues_path: issues_dashboard_path(milestone_title: milestone.title), issues_path: issues_dashboard_path(milestone_title: milestone.title),
merge_requests_path: merge_requests_dashboard_path(milestone_title: milestone.title), merge_requests_path: merge_requests_dashboard_path(milestone_title: milestone.title),
milestone: milestone, milestone: milestone,
......
- 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
= render 'shared/milestones/milestone', = render 'shared/milestones/milestone',
milestone_path: group_milestone_route(milestone),
issues_path: issues_group_path(@group, milestone_title: milestone.title), issues_path: issues_group_path(@group, milestone_title: milestone.title),
merge_requests_path: merge_requests_group_path(@group, milestone_title: milestone.title), merge_requests_path: merge_requests_group_path(@group, milestone_title: milestone.title),
milestone: milestone milestone: milestone
...@@ -16,5 +16,8 @@ ...@@ -16,5 +16,8 @@
.nothing-here-block No milestones to show .nothing-here-block No milestones to show
- else - else
- @milestones.each do |milestone| - @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" = paginate @milestones, theme: "gitlab"
= render 'shared/milestones/milestone', = render 'shared/milestones/milestone',
milestone_path: project_milestone_path(milestone.project, milestone),
issues_path: project_issues_path(milestone.project, milestone_title: milestone.title), issues_path: project_issues_path(milestone.project, milestone_title: milestone.title),
merge_requests_path: project_merge_requests_path(milestone.project, milestone_title: milestone.title), merge_requests_path: project_merge_requests_path(milestone.project, milestone_title: milestone.title),
milestone: milestone milestone: milestone
...@@ -11,8 +11,7 @@ ...@@ -11,8 +11,7 @@
.milestone-buttons .milestone-buttons
- if can?(current_user, :admin_milestone, @group || @project) - 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 - if milestone.project_milestone? && milestone.project.group
%button.js-promote-project-milestone-button.btn.btn-grouped{ data: { toggle: 'modal', %button.js-promote-project-milestone-button.btn.btn-grouped{ data: { toggle: 'modal',
...@@ -31,8 +30,7 @@ ...@@ -31,8 +30,7 @@
- else - else
= link_to _('Reopen milestone'), update_milestone_path(milestone, { state_event: :activate }), method: :put, class: 'btn btn-grouped btn-reopen' = 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' } %button.btn.btn-default.btn-grouped.float-right.d-block.d-sm-none.js-sidebar-toggle{ type: 'button' }
= icon('angle-double-left') = icon('angle-double-left')
...@@ -6,39 +6,33 @@ ...@@ -6,39 +6,33 @@
.row .row
.col-sm-6 .col-sm-6
.append-bottom-5 .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 - if @group
= " - #{milestone_type}" = " - #{milestone_type}"
- if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone? - if milestone.due_date || milestone.start_date
- if milestone.due_date || milestone.start_date .text-tertiary.append-bottom-5
.text-tertiary.append-bottom-5 = milestone_date_range(milestone)
= milestone_date_range(milestone) - recent_releases, total_count, more_count = recent_releases_with_counts(milestone)
- recent_releases, total_count, more_count = recent_releases_with_counts(milestone) - unless total_count.zero?
- unless total_count.zero? .text-tertiary.append-bottom-5.milestone-release-links
.text-tertiary.append-bottom-5.milestone-release-links = icon('rocket')
= icon('rocket') = n_('Release', 'Releases', total_count)
= n_('Release', 'Releases', total_count) - recent_releases.each do |release|
- recent_releases.each do |release| = link_to release.name, project_releases_path(release.project, anchor: release.tag)
= link_to release.name, project_releases_path(release.project, anchor: release.tag) - unless release == recent_releases.last
- unless release == recent_releases.last
&bull;
- if total_count > recent_releases.count
&bull; &bull;
= link_to n_('%{count} more release', '%{count} more releases', more_count) % { count: more_count }, project_releases_path(milestone.project) - if total_count > recent_releases.count
%div &bull;
= render('shared/milestone_expired', milestone: milestone) = link_to n_('%{count} more release', '%{count} more releases', more_count) % { count: more_count }, project_releases_path(milestone.project)
- if milestone.group_milestone? %div
.label-badge.label-badge-blue.d-inline-block = render('shared/milestone_expired', milestone: milestone)
= milestone.group.full_name - if milestone.group_milestone?
- if milestone.legacy_group_milestone? .label-badge.label-badge-blue.d-inline-block
.projects = milestone.group.full_name
- link_to milestone_path(milestone.milestone) do - if milestone.project_milestone?
%span.label-badge.label-badge-blue.d-inline-block.append-bottom-5 .label-badge.label-badge-gray.d-inline-block
= dashboard ? milestone.project.full_name : milestone.project.name = milestone.project.full_name
- if milestone.project
.label-badge.label-badge-gray.d-inline-block
= milestone.project.full_name
.col-sm-4.milestone-progress .col-sm-4.milestone-progress
= milestone_progress_bar(milestone) = milestone_progress_bar(milestone)
...@@ -49,29 +43,25 @@ ...@@ -49,29 +43,25 @@
.float-lg-right.light #{milestone.percent_complete}% complete .float-lg-right.light #{milestone.percent_complete}% complete
.col-sm-2 .col-sm-2
.milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end .milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end
- if @project - if @project # if in milestones list on project level
- if can_admin_project_milestones? and milestone.active? - if can_admin_group_milestones?
- 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'),
%button.js-promote-project-milestone-button.btn.btn-blank.btn-sm.btn-grouped.has-tooltip{ title: s_('Milestones|Promote to Group Milestone'), disabled: true,
disabled: true, type: 'button',
type: 'button', data: { url: promote_project_milestone_path(milestone.project, milestone),
data: { url: promote_project_milestone_path(milestone.project, milestone), milestone_title: milestone.title,
milestone_title: milestone.title, group_name: @project.group.name,
group_name: @project.group.name, target: '#promote-milestone-modal',
target: '#promote-milestone-modal', container: 'body',
container: 'body', toggle: 'modal' } }
toggle: 'modal' } } = sprite_icon('level-up', size: 14)
= 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 - if dashboard
.label-badge.label-badge-gray .label-badge.label-badge-gray
= milestone_type = milestone_type
- page_title milestone.title - page_title milestone.title
- @breadcrumb_link = dashboard_milestone_path(milestone.safe_title, title: milestone.title) - @breadcrumb_link = milestone_path(milestone)
- group = local_assigns[:group] - group = local_assigns[:group]
- is_dynamic_milestone = milestone.legacy_group_milestone? || milestone.dashboard_milestone?
= render 'shared/milestones/header', milestone: milestone = render 'shared/milestones/header', milestone: milestone
= render 'shared/milestones/deprecation_message' if is_dynamic_milestone
= render 'shared/milestones/description', milestone: milestone = render 'shared/milestones/description', milestone: milestone
- if milestone.complete? && milestone.active? - if milestone.complete? && milestone.active?
...@@ -15,26 +13,3 @@ ...@@ -15,26 +13,3 @@
= group ? _('You may close the milestone now.') : _('Navigate to the project to close the milestone.') = 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 = 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
---
title: Remove deprecated dashboard & group milestone pages
merge_request: 13237
author:
type: removed
...@@ -5,13 +5,7 @@ resource :dashboard, controller: 'dashboard', only: [] do ...@@ -5,13 +5,7 @@ resource :dashboard, controller: 'dashboard', only: [] do
get :activity get :activity
scope module: :dashboard do scope module: :dashboard do
resources :milestones, only: [:index, :show] do resources :milestones, only: [:index]
member do
get :merge_requests
get :participants
get :labels
end
end
resources :labels, only: [:index] resources :labels, only: [:index]
resources :groups, only: [:index] resources :groups, only: [:index]
......
...@@ -5,9 +5,9 @@ module EE ...@@ -5,9 +5,9 @@ module EE
module MilestonesController module MilestonesController
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
override :legacy_milestones override :search_params
def legacy_milestones def search_params
params[:only_group_milestones] ? [] : super params[:only_group_milestones].present? ? super.merge(project_ids: []) : super
end end
end end
end end
......
# frozen_string_literal: true
module EE
module GlobalMilestone
def supports_weight?
false
end
# Legacy group milestones or dashboard milestones (grouped by title)
# can't present Burndown charts since they don't have
# proper limits set.
def supports_burndown_charts?
false
end
end
end
# frozen_string_literal: true
module EE
module GroupMilestone
def supports_weight?
group&.feature_available?(:issue_weights)
end
end
end
...@@ -70,23 +70,4 @@ describe 'Burndown charts', :js do ...@@ -70,23 +70,4 @@ describe 'Burndown charts', :js do
expect(page).to have_content('Improve milestones with Burndown Charts') expect(page).to have_content('Improve milestones with Burndown Charts')
end end
end end
describe 'grouped by title milestones' do
let(:group) { nil }
let(:project) { create(:project) }
before do
project.add_maintainer(current_user)
end
it 'does not present burndown chart or promotion' do
allow(License).to receive(:current) { nil }
allow(Gitlab::CurrentSettings).to receive(:should_check_namespace_plan?) { true }
visit dashboard_milestone_path(milestone.safe_title, title: milestone.title)
expect(page).not_to have_css('.burndown-chart')
expect(page).not_to have_content('Improve milestones with Burndown Charts')
end
end
end end
# 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
...@@ -20735,9 +20735,6 @@ msgstr "" ...@@ -20735,9 +20735,6 @@ msgstr ""
msgid "Starts at (UTC)" msgid "Starts at (UTC)"
msgstr "" msgstr ""
msgid "State"
msgstr ""
msgid "State your message to activate" msgid "State your message to activate"
msgstr "" msgstr ""
......
...@@ -8,12 +8,7 @@ describe Dashboard::MilestonesController do ...@@ -8,12 +8,7 @@ describe Dashboard::MilestonesController do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project_milestone) { create(:milestone, project: project) } let(:project_milestone) { create(:milestone, project: project) }
let(:group_milestone) { create(:milestone, group: group) } let(:group_milestone) { create(:milestone, group: group) }
let(:milestone) do let(:milestone) { create(:milestone, group: group) }
DashboardMilestone.build(
[project],
project_milestone.title
)
end
let(:issue) { create(:issue, project: project, milestone: project_milestone) } let(:issue) { create(:issue, project: project, milestone: project_milestone) }
let(:group_issue) { create(:issue, milestone: group_milestone, project: create(:project, group: group)) } let(:group_issue) { create(:issue, milestone: group_milestone, project: create(:project, group: group)) }
...@@ -28,22 +23,6 @@ describe Dashboard::MilestonesController do ...@@ -28,22 +23,6 @@ describe Dashboard::MilestonesController do
group.add_developer(user) group.add_developer(user)
end 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 describe "#index" do
let(:public_group) { create(:group, :public) } let(:public_group) { create(:group, :public) }
let!(:public_milestone) { create(:milestone, group: public_group) } let!(:public_milestone) { create(:milestone, group: public_group) }
...@@ -58,7 +37,6 @@ describe Dashboard::MilestonesController do ...@@ -58,7 +37,6 @@ describe Dashboard::MilestonesController do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(json_response.size).to eq(2) 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["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 end
it 'returns closed group and project milestones to which the user belongs' do it 'returns closed group and project milestones to which the user belongs' do
...@@ -67,7 +45,6 @@ describe Dashboard::MilestonesController do ...@@ -67,7 +45,6 @@ describe Dashboard::MilestonesController do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(json_response.size).to eq(2) 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["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 end
it 'searches legacy project milestones by title when search_title is given' do it 'searches legacy project milestones by title when search_title is given' do
......
...@@ -8,15 +8,7 @@ describe Groups::MilestonesController do ...@@ -8,15 +8,7 @@ describe Groups::MilestonesController do
let!(:project2) { create(:project, group: group) } let!(:project2) { create(:project, group: group) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:title) { '肯定不是中文的问题' } let(:title) { '肯定不是中文的问题' }
let(:milestone) do let(:milestone) { create(:milestone, project: project) }
project_milestone = create(:milestone, project: project)
GroupMilestone.build(
group,
[project],
project_milestone.title
)
end
let(:milestone_path) { group_milestone_path(group, milestone.safe_title, title: milestone.title) } let(:milestone_path) { group_milestone_path(group, milestone.safe_title, title: milestone.title) }
let(:milestone_params) do let(:milestone_params) do
...@@ -168,17 +160,16 @@ describe Groups::MilestonesController do ...@@ -168,17 +160,16 @@ describe Groups::MilestonesController do
context 'as JSON' do context 'as JSON' do
let!(:milestone) { create(:milestone, group: group, title: 'group milestone') } let!(:milestone) { create(:milestone, group: group, title: 'group milestone') }
let!(:legacy_milestone1) { create(:milestone, project: project, title: 'legacy') } let!(:project_milestone1) { create(:milestone, project: project, title: 'same name') }
let!(:legacy_milestone2) { create(:milestone, project: project2, title: 'legacy') } 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 get :index, params: { group_id: group.to_param }, format: :json
milestones = json_response milestones = json_response
expect(milestones.count).to eq(2) expect(milestones.count).to eq(3)
expect(milestones.first["title"]).to eq("group milestone") expect(milestones.collect { |m| m['title'] }).to match_array(['same name', 'same name', 'group milestone'])
expect(milestones.second["title"]).to eq("legacy")
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response.content_type).to eq 'application/json' expect(response.content_type).to eq 'application/json'
end end
...@@ -191,8 +182,9 @@ describe Groups::MilestonesController do ...@@ -191,8 +182,9 @@ describe Groups::MilestonesController do
get :index, params: { group_id: group.to_param }, format: :json get :index, params: { group_id: group.to_param }, format: :json
milestones = json_response milestones = json_response
expect(milestones.count).to eq(3) milestone_titles = milestones.map { |m| m['title'] }
expect(milestones.second["title"]).to eq("subgroup milestone") expect(milestones.count).to eq(4)
expect(milestone_titles).to match_array(['same name', 'same name', 'group milestone', 'subgroup milestone'])
end end
end end
...@@ -218,31 +210,18 @@ describe Groups::MilestonesController do ...@@ -218,31 +210,18 @@ describe Groups::MilestonesController do
end end
describe '#show' do describe '#show' do
let(:milestone1) { create(:milestone, project: project, title: 'legacy') } render_views
let(:milestone2) { create(:milestone, project: project, title: 'legacy') }
let(:group_milestone) { create(:milestone, group: group) }
context 'when there is a title parameter' do let!(:group_milestone) { create(:milestone, group: group) }
it 'searches for a legacy group milestone' do
expect(GroupMilestone).to receive(:build)
expect(Milestone).not_to receive(:find_by_iid)
get :show, params: { group_id: group.to_param, id: title, title: milestone1.safe_title } it 'renders for a group milestone' do
end get :show, params: { group_id: group.to_param, id: group_milestone.iid }
end
context 'when there is not a title parameter' do expect(response).to have_gitlab_http_status(:ok)
it 'searches for a group milestone' do expect(response.body).to include(group_milestone.title)
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
end end
end end
it_behaves_like 'milestone tabs'
describe "#create" do describe "#create" do
it "creates group milestone with Chinese title" do it "creates group milestone with Chinese title" do
post :create, post :create,
...@@ -277,34 +256,6 @@ describe Groups::MilestonesController do ...@@ -277,34 +256,6 @@ describe Groups::MilestonesController do
expect(response).to redirect_to(group_milestone_path(group, milestone.iid)) expect(response).to redirect_to(group_milestone_path(group, milestone.iid))
expect(milestone.title).to eq("title changed") expect(milestone.title).to eq("title changed")
end 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 end
describe "#destroy" do describe "#destroy" do
......
# 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
...@@ -102,11 +102,9 @@ describe 'Group milestones' do ...@@ -102,11 +102,9 @@ describe 'Group milestones' do
expect(find('.top-area .all .badge').text).to eq("6") expect(find('.top-area .all .badge').text).to eq("6")
end end
it 'lists legacy group milestones and group milestones' do it 'lists group and project milestones' do
legacy_milestone = GroupMilestone.build_collection(group, group.projects, { state: 'active' }).first
expect(page).to have_selector("#milestone_#{active_group_milestone.id}", count: 1) 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 end
it 'shows milestone detail and supports its edit' do it 'shows milestone detail and supports its edit' do
...@@ -125,75 +123,35 @@ describe 'Group milestones' do ...@@ -125,75 +123,35 @@ describe 'Group milestones' do
expect(page).to have_content('v1.0') expect(page).to have_content('v1.0')
expect(page).to have_content('v1.1') expect(page).to have_content('v1.1')
expect(page).to have_content('GL-113') 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( expect(page).to have_link(
'1 Issue', '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( expect(page).to have_link(
'0 Merge Requests', '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 end
end end
describe 'milestone tabs', :js do 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 context 'for a group milestone' do
let_it_be(:other_project) { create(:project_empty_repo, group: group) } let_it_be(:other_project) { create(:project_empty_repo, group: group) }
let_it_be(:milestone) { create(:milestone, group: group) } let_it_be(:milestone) { create(:milestone, group: group) }
......
...@@ -24,21 +24,12 @@ describe 'Milestones sorting', :js do ...@@ -24,21 +24,12 @@ describe 'Milestones sorting', :js do
# assert default sorting # assert default sorting
within '.milestones' do within '.milestones' do
expect(page.all('ul.content-list > li').first.text).to include('v2.0') expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(['v2.0', 'v2.0', 'v3.0', 'v1.0', '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('v1.0')
end end
click_button 'Due soon' click_button 'Due soon'
sort_options = find('ul.dropdown-menu-sort li').all('a').collect(&:text) 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'])
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')
click_link 'Due later' click_link 'Due later'
...@@ -46,9 +37,7 @@ describe 'Milestones sorting', :js do ...@@ -46,9 +37,7 @@ describe 'Milestones sorting', :js do
# assert descending sorting # assert descending sorting
within '.milestones' do within '.milestones' do
expect(page.all('ul.content-list > li').first.text).to include('v1.0') expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(['v1.0', 'v1.0', 'v3.0', 'v2.0', '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('v2.0')
end end
end end
end end
...@@ -111,20 +111,6 @@ describe 'Milestone' do ...@@ -111,20 +111,6 @@ describe 'Milestone' do
end end
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 describe 'reopen closed milestones' do
before do before do
create(:milestone, :closed, project: project) create(:milestone, :closed, project: project)
......
# 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
# 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
# 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
...@@ -2,15 +2,7 @@ ...@@ -2,15 +2,7 @@
RSpec.shared_examples 'milestone tabs' do RSpec.shared_examples 'milestone tabs' do
def go(path, extra_params = {}) def go(path, extra_params = {})
params = params = { namespace_id: project.namespace.to_param, project_id: project, id: milestone.iid }
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
get path, params: params.merge(extra_params) get path, params: params.merge(extra_params)
end end
......
...@@ -12,22 +12,6 @@ describe 'shared/milestones/_top.html.haml' do ...@@ -12,22 +12,6 @@ describe 'shared/milestones/_top.html.haml' do
allow(milestone).to receive(:milestone) { milestone } allow(milestone).to receive(:milestone) { milestone }
end 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 it 'does not render a deprecation message for a non-legacy and non-dashboard milestone' do
assign :group, group assign :group, group
......
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