Commit 310c1f12 authored by Douwe Maan's avatar Douwe Maan

Merge branch '2575-namespace-license-issue-weights' into 'master'

Introduce namespace licensing for issue weights (EES)

Closes #2575

See merge request !2291
parents 1847f09b 03c6b4d9
...@@ -3,7 +3,7 @@ module EE ...@@ -3,7 +3,7 @@ module EE
module IssuesController module IssuesController
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do prepended do
before_action :check_export_issues_available!, only: [:export_csv] before_action :check_export_issues_available!, only: [:export_csv]
end end
...@@ -13,6 +13,20 @@ module EE ...@@ -13,6 +13,20 @@ module EE
index_path = namespace_project_issues_path(project.namespace, project) index_path = namespace_project_issues_path(project.namespace, project)
redirect_to(index_path, notice: "Your CSV export has started. It will be emailed to #{current_user.notification_email} when complete.") redirect_to(index_path, notice: "Your CSV export has started. It will be emailed to #{current_user.notification_email} when complete.")
end end
def issue_params_attributes
attrs = super
attrs.unshift(:weight) if project.feature_available?(:issue_weights)
attrs
end
def filter_params
params = super
params.reject! { |key| key == 'weight' } unless project.feature_available?(:issue_weights)
params
end
end end
end end
end end
...@@ -6,7 +6,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -6,7 +6,7 @@ class Projects::IssuesController < Projects::ApplicationController
include IssuableCollections include IssuableCollections
include SpammableActions include SpammableActions
include ::EE::Projects::IssuesController prepend ::EE::Projects::IssuesController
prepend_before_action :authenticate_user!, only: [:new, :export_csv] prepend_before_action :authenticate_user!, only: [:new, :export_csv]
...@@ -270,10 +270,22 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -270,10 +270,22 @@ class Projects::IssuesController < Projects::ApplicationController
end end
def issue_params def issue_params
params.require(:issue).permit( params.require(:issue).permit(*issue_params_attributes)
:title, :assignee_id, :position, :description, :confidential, :weight, end
:milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: [], assignee_ids: []
) def issue_params_attributes
%i[
title
assignee_id
position
description
confidential
milestone_id
due_date
state_event
task_num
lock_version
] + [{ label_ids: [], assignee_ids: [] }]
end end
def authenticate_user! def authenticate_user!
......
...@@ -45,7 +45,8 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -45,7 +45,8 @@ class Projects::MilestonesController < Projects::ApplicationController
end end
def show def show
if @project.feature_available?(:burndown_charts, current_user) if @project.feature_available?(:burndown_charts, current_user) &&
@project.feature_available?(:issue_weights, current_user)
@burndown = Burndown.new(@milestone) @burndown = Burndown.new(@milestone)
end end
end end
......
...@@ -134,14 +134,16 @@ module MilestonesHelper ...@@ -134,14 +134,16 @@ module MilestonesHelper
end end
def can_generate_chart?(burndown) def can_generate_chart?(burndown)
return unless @project.feature_available?(:burndown_charts, current_user) return unless @project.feature_available?(:burndown_charts, current_user) &&
@project.feature_available?(:issue_weights, current_user)
burndown&.valid? && !burndown&.empty? burndown&.valid? && !burndown&.empty?
end end
def show_burndown_placeholder?(warning) def show_burndown_placeholder?(warning)
return false if cookies['hide_burndown_message'].present? return false if cookies['hide_burndown_message'].present?
return false unless @project.feature_available?(:burndown_charts, current_user) return false unless @project.feature_available?(:burndown_charts, current_user) &&
@project.feature_available?(:issue_weights, current_user)
warning.nil? && can?(current_user, :admin_milestone, @project) warning.nil? && can?(current_user, :admin_milestone, @project)
end end
......
class Burndown class Burndown
Issue = Struct.new(:closed_at, :weight, :state)
attr_reader :start_date, :due_date, :end_date, :issues_count, :issues_weight, :accurate, :legacy_data attr_reader :start_date, :due_date, :end_date, :issues_count, :issues_weight, :accurate, :legacy_data
alias_method :accurate?, :accurate alias_method :accurate?, :accurate
alias_method :empty?, :legacy_data alias_method :empty?, :legacy_data
...@@ -68,8 +70,10 @@ class Burndown ...@@ -68,8 +70,10 @@ class Burndown
def milestone_closed_issues def milestone_closed_issues
@milestone_closed_issues ||= @milestone_closed_issues ||=
@milestone.issues.select("closed_at, weight, state") @milestone.issues
.where("state IN ('reopened', 'closed')") .where("state IN ('reopened', 'closed')")
.order("closed_at ASC") .order("closed_at ASC")
.pluck("closed_at, weight, state")
.map {|attrs| ::Burndown::Issue.new(*attrs) }
end end
end end
module EE
module GlobalMilestone
def supports_weight?
false
end
end
end
module EE
module GroupMilestone
def supports_weight?
group&.feature_available?(:issue_weights)
end
end
end
...@@ -23,5 +23,14 @@ module EE ...@@ -23,5 +23,14 @@ module EE
# and doesn't actually show up in the participants list. # and doesn't actually show up in the participants list.
user.support_bot? || super user.support_bot? || super
end end
# override
def weight
super if supports_weight?
end
def supports_weight?
project&.feature_available?(:issue_weights)
end
end end
end end
...@@ -50,5 +50,9 @@ module EE ...@@ -50,5 +50,9 @@ module EE
super && project.feature_available?(:merge_request_squash) super && project.feature_available?(:merge_request_squash)
end end
alias_method :squash?, :squash alias_method :squash?, :squash
def supports_weight?
false
end
end end
end end
module EE
module Milestone
def supports_weight?
project&.feature_available?(:issue_weights)
end
end
end
class GlobalMilestone class GlobalMilestone
include Milestoneish include Milestoneish
include ::EE::GlobalMilestone
EPOCH = DateTime.parse('1970-01-01') EPOCH = DateTime.parse('1970-01-01')
attr_accessor :title, :milestones attr_accessor :title, :milestones
......
class GroupMilestone < GlobalMilestone class GroupMilestone < GlobalMilestone
include ::EE::GroupMilestone
attr_accessor :group attr_accessor :group
def self.build_collection(group, projects, params) def self.build_collection(group, projects, params)
......
...@@ -6,28 +6,31 @@ class License < ActiveRecord::Base ...@@ -6,28 +6,31 @@ class License < ActiveRecord::Base
DEPLOY_BOARD_FEATURE = 'GitLab_DeployBoard'.freeze DEPLOY_BOARD_FEATURE = 'GitLab_DeployBoard'.freeze
ELASTIC_SEARCH_FEATURE = 'GitLab_ElasticSearch'.freeze ELASTIC_SEARCH_FEATURE = 'GitLab_ElasticSearch'.freeze
EXPORT_ISSUES_FEATURE = 'GitLab_ExportIssues'.freeze EXPORT_ISSUES_FEATURE = 'GitLab_ExportIssues'.freeze
FAST_FORWARD_MERGE_FEATURE = 'GitLab_FastForwardMerge'.freeze
FILE_LOCK_FEATURE = 'GitLab_FileLocks'.freeze FILE_LOCK_FEATURE = 'GitLab_FileLocks'.freeze
GEO_FEATURE = 'GitLab_Geo'.freeze GEO_FEATURE = 'GitLab_Geo'.freeze
ISSUE_WEIGHTS_FEATURE = 'GitLab_IssueWeights'.freeze
MERGE_REQUEST_REBASE_FEATURE = 'GitLab_MergeRequestRebase'.freeze MERGE_REQUEST_REBASE_FEATURE = 'GitLab_MergeRequestRebase'.freeze
MERGE_REQUEST_SQUASH_FEATURE = 'GitLab_MergeRequestSquash'.freeze MERGE_REQUEST_SQUASH_FEATURE = 'GitLab_MergeRequestSquash'.freeze
FAST_FORWARD_MERGE_FEATURE = 'GitLab_FastForwardMerge'.freeze
OBJECT_STORAGE_FEATURE = 'GitLab_ObjectStorage'.freeze OBJECT_STORAGE_FEATURE = 'GitLab_ObjectStorage'.freeze
RELATED_ISSUES_FEATURE = 'RelatedIssues'.freeze RELATED_ISSUES_FEATURE = 'RelatedIssues'.freeze
SERVICE_DESK_FEATURE = 'GitLab_ServiceDesk'.freeze SERVICE_DESK_FEATURE = 'GitLab_ServiceDesk'.freeze
FEATURE_CODES = { FEATURE_CODES = {
geo: GEO_FEATURE,
auditor_user: AUDITOR_USER_FEATURE, auditor_user: AUDITOR_USER_FEATURE,
service_desk: SERVICE_DESK_FEATURE,
object_storage: OBJECT_STORAGE_FEATURE,
elastic_search: ELASTIC_SEARCH_FEATURE, elastic_search: ELASTIC_SEARCH_FEATURE,
geo: GEO_FEATURE,
object_storage: OBJECT_STORAGE_FEATURE,
related_issues: RELATED_ISSUES_FEATURE, related_issues: RELATED_ISSUES_FEATURE,
service_desk: SERVICE_DESK_FEATURE,
# Features that make sense to Namespace: # Features that make sense to Namespace:
burndown_charts: BURNDOWN_CHARTS_FEATURE, burndown_charts: BURNDOWN_CHARTS_FEATURE,
deploy_board: DEPLOY_BOARD_FEATURE, deploy_board: DEPLOY_BOARD_FEATURE,
export_issues: EXPORT_ISSUES_FEATURE, export_issues: EXPORT_ISSUES_FEATURE,
fast_forward_merge: FAST_FORWARD_MERGE_FEATURE, fast_forward_merge: FAST_FORWARD_MERGE_FEATURE,
file_lock: FILE_LOCK_FEATURE, file_lock: FILE_LOCK_FEATURE,
issue_weights: ISSUE_WEIGHTS_FEATURE,
merge_request_rebase: MERGE_REQUEST_REBASE_FEATURE, merge_request_rebase: MERGE_REQUEST_REBASE_FEATURE,
merge_request_squash: MERGE_REQUEST_SQUASH_FEATURE merge_request_squash: MERGE_REQUEST_SQUASH_FEATURE
}.freeze }.freeze
...@@ -42,6 +45,7 @@ class License < ActiveRecord::Base ...@@ -42,6 +45,7 @@ class License < ActiveRecord::Base
{ ELASTIC_SEARCH_FEATURE => 1 }, { ELASTIC_SEARCH_FEATURE => 1 },
{ EXPORT_ISSUES_FEATURE => 1 }, { EXPORT_ISSUES_FEATURE => 1 },
{ FAST_FORWARD_MERGE_FEATURE => 1 }, { FAST_FORWARD_MERGE_FEATURE => 1 },
{ ISSUE_WEIGHTS_FEATURE => 1 },
{ MERGE_REQUEST_REBASE_FEATURE => 1 }, { MERGE_REQUEST_REBASE_FEATURE => 1 },
{ MERGE_REQUEST_SQUASH_FEATURE => 1 }, { MERGE_REQUEST_SQUASH_FEATURE => 1 },
{ RELATED_ISSUES_FEATURE => 1 } { RELATED_ISSUES_FEATURE => 1 }
...@@ -49,12 +53,12 @@ class License < ActiveRecord::Base ...@@ -49,12 +53,12 @@ class License < ActiveRecord::Base
EEP_FEATURES = [ EEP_FEATURES = [
*EES_FEATURES, *EES_FEATURES,
{ AUDITOR_USER_FEATURE => 1 },
{ DEPLOY_BOARD_FEATURE => 1 }, { DEPLOY_BOARD_FEATURE => 1 },
{ FILE_LOCK_FEATURE => 1 }, { FILE_LOCK_FEATURE => 1 },
{ GEO_FEATURE => 1 }, { GEO_FEATURE => 1 },
{ AUDITOR_USER_FEATURE => 1 }, { OBJECT_STORAGE_FEATURE => 1 },
{ SERVICE_DESK_FEATURE => 1 }, { SERVICE_DESK_FEATURE => 1 }
{ OBJECT_STORAGE_FEATURE => 1 }
].freeze ].freeze
EEU_FEATURES = [ EEU_FEATURES = [
...@@ -78,6 +82,7 @@ class License < ActiveRecord::Base ...@@ -78,6 +82,7 @@ class License < ActiveRecord::Base
{ FAST_FORWARD_MERGE_FEATURE => 1 }, { FAST_FORWARD_MERGE_FEATURE => 1 },
{ FILE_LOCK_FEATURE => 1 }, { FILE_LOCK_FEATURE => 1 },
{ GEO_FEATURE => 1 }, { GEO_FEATURE => 1 },
{ ISSUE_WEIGHTS_FEATURE => 1 },
{ MERGE_REQUEST_REBASE_FEATURE => 1 }, { MERGE_REQUEST_REBASE_FEATURE => 1 },
{ MERGE_REQUEST_SQUASH_FEATURE => 1 }, { MERGE_REQUEST_SQUASH_FEATURE => 1 },
{ OBJECT_STORAGE_FEATURE => 1 }, { OBJECT_STORAGE_FEATURE => 1 },
......
...@@ -15,6 +15,8 @@ class Milestone < ActiveRecord::Base ...@@ -15,6 +15,8 @@ class Milestone < ActiveRecord::Base
include Elastic::MilestonesSearch include Elastic::MilestonesSearch
include Milestoneish include Milestoneish
include ::EE::Milestone
cache_markdown_field :title, pipeline: :single_line cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description cache_markdown_field :description
......
...@@ -7,7 +7,7 @@ class IssueEntity < IssuableEntity ...@@ -7,7 +7,7 @@ class IssueEntity < IssuableEntity
expose :due_date expose :due_date
expose :moved_to_id expose :moved_to_id
expose :project_id expose :project_id
expose :weight expose :weight, if: ->(issue, _) { issue.supports_weight? }
expose :milestone, using: API::Entities::Milestone expose :milestone, using: API::Entities::Milestone
expose :labels, using: LabelEntity expose :labels, using: LabelEntity
......
module EE
module IssuableBaseService
private
def filter_params(issuable)
params.delete(:weight) unless issuable.supports_weight?
super
end
end
end
class IssuableBaseService < BaseService class IssuableBaseService < BaseService
prepend ::EE::IssuableBaseService
private private
def create_milestone_note(issuable) def create_milestone_note(issuable)
......
...@@ -470,7 +470,7 @@ module QuickActions ...@@ -470,7 +470,7 @@ module QuickActions
end end
params Issue::WEIGHT_RANGE.to_s.squeeze('.').tr('.', '-') params Issue::WEIGHT_RANGE.to_s.squeeze('.').tr('.', '-')
condition do condition do
issuable.respond_to?(:weight) && issuable.supports_weight? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", issuable) current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
end end
parse_params do |weight| parse_params do |weight|
...@@ -484,7 +484,7 @@ module QuickActions ...@@ -484,7 +484,7 @@ module QuickActions
explanation 'Clears weight.' explanation 'Clears weight.'
condition do condition do
issuable.persisted? && issuable.persisted? &&
issuable.respond_to?(:weight) && issuable.supports_weight? &&
issuable.weight? && issuable.weight? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", issuable) current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
end end
......
- viewing_issues = controller.controller_name == 'issues' || controller.action_name == 'issues'
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10
%button.dropdown-toggle{ type: 'button', data: {toggle: 'dropdown' } } %button.dropdown-toggle{ type: 'button', data: {toggle: 'dropdown' } }
- if @sort.present? - if @sort.present?
...@@ -19,16 +21,18 @@ ...@@ -19,16 +21,18 @@
= sort_title_recently_updated = sort_title_recently_updated
= link_to page_filter_path(sort: sort_value_oldest_updated, label: true) do = link_to page_filter_path(sort: sort_value_oldest_updated, label: true) do
= sort_title_oldest_updated = sort_title_oldest_updated
- if local_assigns[:type] == :issues
- if viewing_issues && (@project || @group)&.feature_available?(:issue_weights)
= link_to page_filter_path(sort: sort_value_more_weight, label: true) do = link_to page_filter_path(sort: sort_value_more_weight, label: true) do
= sort_title_more_weight = sort_title_more_weight
= link_to page_filter_path(sort: sort_value_less_weight, label: true) do = link_to page_filter_path(sort: sort_value_less_weight, label: true) do
= sort_title_less_weight = sort_title_less_weight
= link_to page_filter_path(sort: sort_value_milestone_soon, label: true) do = link_to page_filter_path(sort: sort_value_milestone_soon, label: true) do
= sort_title_milestone_soon = sort_title_milestone_soon
= link_to page_filter_path(sort: sort_value_milestone_later, label: true) do = link_to page_filter_path(sort: sort_value_milestone_later, label: true) do
= sort_title_milestone_later = sort_title_milestone_later
- if controller.controller_name == 'issues' || controller.action_name == 'issues' - if viewing_issues
= link_to page_filter_path(sort: sort_value_due_date_soon, label: true) do = link_to page_filter_path(sort: sort_value_due_date_soon, label: true) do
= sort_title_due_date_soon = sort_title_due_date_soon
= link_to page_filter_path(sort: sort_value_due_date_later, label: true) do = link_to page_filter_path(sort: sort_value_due_date_later, label: true) do
......
...@@ -127,7 +127,7 @@ ...@@ -127,7 +127,7 @@
= dropdown_loading = dropdown_loading
#js-add-issues-btn.prepend-left-10 #js-add-issues-btn.prepend-left-10
- elsif type != :boards_modal - elsif type != :boards_modal
= render 'shared/sort_dropdown', type: local_assigns[:type] = render 'shared/sort_dropdown'
- unless type === :boards_modal - unless type === :boards_modal
:javascript :javascript
......
...@@ -115,7 +115,7 @@ ...@@ -115,7 +115,7 @@
- if can? current_user, :admin_label, @project and @project - if can? current_user, :admin_label, @project and @project
= render partial: "shared/issuable/label_page_create" = render partial: "shared/issuable/label_page_create"
- if issuable.respond_to?(:weight) - if issuable.supports_weight?
.block.weight .block.weight
.sidebar-collapsed-icon .sidebar-collapsed-icon
= icon('balance-scale') = icon('balance-scale')
......
- issuable = local_assigns.fetch(:issuable) - issuable = local_assigns.fetch(:issuable)
- return unless issuable.respond_to?(:weight) - return unless issuable.supports_weight?
- has_due_date = issuable.has_attribute?(:due_date) - has_due_date = issuable.has_attribute?(:due_date)
- form = local_assigns.fetch(:form) - form = local_assigns.fetch(:form)
......
...@@ -85,22 +85,7 @@ ...@@ -85,22 +85,7 @@
Closed: Closed:
= milestone.issues_visible_to_user(current_user).closed.count = milestone.issues_visible_to_user(current_user).closed.count
- total_weight = milestone.issues_visible_to_user(current_user).sum(:weight) = render 'shared/milestones/weight', milestone: milestone
.block.weight
.sidebar-collapsed-icon
= icon('balance-scale')
%span
- unless total_weight.zero?
= total_weight
- else
None
.title.hide-collapsed
Total issue weight
.value.hide-collapsed
- unless total_weight.zero?
%strong.bold= total_weight
- else
.no-value None
.block.merge-requests .block.merge-requests
.sidebar-collapsed-icon .sidebar-collapsed-icon
......
- milestone = local_assigns.fetch(:milestone)
- return unless milestone.supports_weight?
- total_weight = milestone.issues_visible_to_user(current_user).sum(:weight)
.block.weight
.sidebar-collapsed-icon
= icon('balance-scale')
%span
- unless total_weight.zero?
= total_weight
- else
None
.title.hide-collapsed
Total issue weight
.value.hide-collapsed
- unless total_weight.zero?
%strong.bold= total_weight
- else
.no-value None
---
title: Introduce namespace licensing for issue weights (EES)
merge_request: 2291
author:
...@@ -313,7 +313,7 @@ module API ...@@ -313,7 +313,7 @@ module API
expose :upvotes, :downvotes expose :upvotes, :downvotes
expose :due_date expose :due_date
expose :confidential expose :confidential
expose :weight expose :weight, if: ->(issue, _) { issue.supports_weight? }
expose :web_url do |issue, options| expose :web_url do |issue, options|
Gitlab::UrlBuilder.build(issue) Gitlab::UrlBuilder.build(issue)
......
...@@ -76,4 +76,120 @@ describe Projects::IssuesController do ...@@ -76,4 +76,120 @@ describe Projects::IssuesController do
end end
end end
end end
describe 'issue weights' do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:issue) { create(:issue, project: project, weight: 5) }
let(:issue2) { create(:issue, project: project, weight: 1) }
let(:new_issue) { build(:issue, project: project, weight: 5) }
before do
project.add_developer(user)
sign_in(user)
end
def perform(method, action, opts = {})
send(method, action, opts.merge(namespace_id: project.namespace.to_param, project_id: project.to_param))
end
context 'licensed' do
before do
stub_licensed_features(issue_weights: true)
end
describe '#index' do
it 'allows sorting by weight (ascending)' do
expected = [issue, issue2].sort_by(&:weight)
perform :get, :index, sort: 'weight_asc'
expect(response).to have_http_status(200)
expect(assigns(:issues)).to eq(expected)
end
it 'allows sorting by weight (descending)' do
expected = [issue, issue2].sort { |a, b| b.weight <=> a.weight }
perform :get, :index, sort: 'weight_desc'
expect(response).to have_http_status(200)
expect(assigns(:issues)).to eq(expected)
end
it 'allows filtering by weight' do
_ = issue
_ = issue2
perform :get, :index, weight: 1
expect(response).to have_http_status(200)
expect(assigns(:issues)).to eq([issue2])
end
end
describe '#update' do
it 'sets issue weight' do
perform :put, :update, id: issue.to_param, issue: { weight: 6 }, format: :json
expect(response).to have_http_status(200)
expect(issue.reload.weight).to eq(6)
end
end
describe '#create' do
it 'sets issue weight' do
perform :post, :create, issue: new_issue.attributes
expect(response).to have_http_status(302)
expect(Issue.count).to eq(1)
issue = Issue.first
expect(issue.weight).to eq(new_issue.weight)
end
end
end
context 'unlicensed' do
before do
stub_licensed_features(issue_weights: false)
end
describe '#index' do
it 'ignores sorting by weight (ascending)'
it 'ignores sorting by weight (descending)'
it 'ignores filtering by weight' do
expected = [issue, issue2]
perform :get, :index, weight: 1
expect(response).to have_http_status(200)
expect(assigns(:issues)).to match_array(expected)
end
end
describe '#update' do
it 'does not set issue weight' do
perform :put, :update, id: issue.to_param, issue: { weight: 6 }, format: :json
expect(response).to have_http_status(200)
expect(issue.reload.weight).to be_nil
expect(issue.reload.read_attribute(:weight)).to eq(5) # pre-existing data is not overwritten
end
end
describe '#create' do
it 'does not set issue weight' do
perform :post, :create, issue: new_issue.attributes
expect(response).to have_http_status(302)
expect(Issue.count).to eq(1)
issue = Issue.first
expect(issue.read_attribute(:weight)).to be_nil
end
end
end
end
end end
...@@ -88,11 +88,7 @@ describe 'Milestones on EE', feature: true do ...@@ -88,11 +88,7 @@ describe 'Milestones on EE', feature: true do
end end
end end
context 'with the burndown chart feature disabled' do shared_examples 'burndown charts disabled' do
before do
stub_licensed_features(burndown_charts: false)
end
it 'has a link to upgrade to Bronze when checking the namespace plan' do it 'has a link to upgrade to Bronze when checking the namespace plan' do
# Not using `stub_application_setting` because the method is prepended in # Not using `stub_application_setting` because the method is prepended in
# `EE::ApplicationSetting` which breaks when using `any_instance` # `EE::ApplicationSetting` which breaks when using `any_instance`
...@@ -118,6 +114,22 @@ describe 'Milestones on EE', feature: true do ...@@ -118,6 +114,22 @@ describe 'Milestones on EE', feature: true do
end end
end end
end end
context 'with the burndown chart feature disabled' do
before do
stub_licensed_features(burndown_charts: false)
end
include_examples 'burndown charts disabled'
end
context 'with the issuable weights feature disabled' do
before do
stub_licensed_features(issue_weights: false)
end
include_examples 'burndown charts disabled'
end
end end
context 'milestone summary' do context 'milestone summary' do
......
require 'spec_helper'
describe Issue do
describe '#weight' do
[
{ license: true, database: 5, expected: 5 },
{ license: true, database: nil, expected: nil },
{ license: false, database: 5, expected: nil },
{ license: false, database: nil, expected: nil }
].each do |spec|
context spec.inspect do
let(:spec) { spec }
let(:issue) { build(:issue, weight: spec[:database]) }
subject { issue.weight }
before do
stub_licensed_features(issue_weights: spec[:license])
end
it { is_expected.to eq(spec[:expected]) }
end
end
end
end
...@@ -1248,6 +1248,20 @@ describe API::Issues do ...@@ -1248,6 +1248,20 @@ describe API::Issues do
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
expect(json_response['error']).to eq('weight does not have a valid value') expect(json_response['error']).to eq('weight does not have a valid value')
end end
context 'issuable weights unlicensed' do
before do
stub_licensed_features(issue_weights: false)
end
it 'ignores the update' do
put api("/projects/#{project.id}/issues/#{issue.iid}", user), weight: 5
expect(response).to have_http_status(200)
expect(json_response['weight']).to be_nil
expect(issue.reload.read_attribute(:weight)).to be_nil
end
end
end end
describe "DELETE /projects/:id/issues/:issue_iid" do describe "DELETE /projects/:id/issues/:issue_iid" do
......
...@@ -1189,6 +1189,20 @@ describe API::V3::Issues do ...@@ -1189,6 +1189,20 @@ describe API::V3::Issues do
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
expect(json_response['error']).to eq('weight does not have a valid value') expect(json_response['error']).to eq('weight does not have a valid value')
end end
context 'issuable weights unlicensed' do
before do
stub_licensed_features(issue_weights: false)
end
it 'ignores the update' do
put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), weight: 5
expect(response).to have_http_status(200)
expect(json_response['weight']).to be_nil
expect(issue.reload.read_attribute(:weight)).to be_nil
end
end
end end
describe "DELETE /projects/:id/issues/:issue_id" do describe "DELETE /projects/:id/issues/:issue_id" do
......
...@@ -725,6 +725,11 @@ describe QuickActions::InterpretService, services: true do ...@@ -725,6 +725,11 @@ describe QuickActions::InterpretService, services: true do
let(:issuable) { issue } let(:issuable) { issue }
end end
context 'issuable weights licensed' do
before do
stub_licensed_features(issue_weights: true)
end
it_behaves_like 'weight command' do it_behaves_like 'weight command' do
let(:content) { '/weight 5'} let(:content) { '/weight 5'}
let(:issuable) { issue } let(:issuable) { issue }
...@@ -734,6 +739,25 @@ describe QuickActions::InterpretService, services: true do ...@@ -734,6 +739,25 @@ describe QuickActions::InterpretService, services: true do
let(:content) { '/clear_weight' } let(:content) { '/clear_weight' }
let(:issuable) { issue } let(:issuable) { issue }
end end
end
context 'issuable weights unlicensed' do
before do
stub_licensed_features(issue_weights: false)
end
it 'does not recognise /weight X' do
_, updates = service.execute('/weight 5', issue)
expect(updates).to be_empty
end
it 'does not recognise /clear_weight' do
_, updates = service.execute('/clear_weight', issue)
expect(updates).to be_empty
end
end
context 'when current_user cannot :admin_issue' do context 'when current_user cannot :admin_issue' do
let(:visitor) { create(:user) } let(:visitor) { create(:user) }
......
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