mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
Merge pull request #23072 from opf/feature/74147-bring-sprint-sharing-safe-to-corporate-plan
Feature/74147 bring sprint sharing safe to corporate plan
This commit is contained in:
@@ -209,7 +209,7 @@ gem "aws-sdk-core", "~> 3.244"
|
||||
# File upload via fog + screenshots on travis
|
||||
gem "aws-sdk-s3", "~> 1.217"
|
||||
|
||||
gem "openproject-token", "~> 8.8.2"
|
||||
gem "openproject-token", "~> 8.9.0"
|
||||
|
||||
gem "plaintext", "~> 0.3.7"
|
||||
|
||||
|
||||
+3
-3
@@ -914,7 +914,7 @@ GEM
|
||||
activesupport (>= 7.2.0)
|
||||
openproject-octicons (>= 19.34.0)
|
||||
view_component (>= 3.1, < 5.0)
|
||||
openproject-token (8.8.2)
|
||||
openproject-token (8.9.0)
|
||||
activemodel
|
||||
openssl (4.0.2)
|
||||
openssl-signature_algorithm (1.3.0)
|
||||
@@ -1694,7 +1694,7 @@ DEPENDENCIES
|
||||
openproject-resource_management!
|
||||
openproject-storages!
|
||||
openproject-team_planner!
|
||||
openproject-token (~> 8.8.2)
|
||||
openproject-token (~> 8.9.0)
|
||||
openproject-two_factor_authentication!
|
||||
openproject-webhooks!
|
||||
openproject-wikis!
|
||||
@@ -2075,7 +2075,7 @@ CHECKSUMS
|
||||
openproject-resource_management (1.0.0)
|
||||
openproject-storages (1.0.0)
|
||||
openproject-team_planner (1.0.0)
|
||||
openproject-token (8.8.2) sha256=081cbff7269d92a82fa1d63e9e09c87b70d47d7aefadcbb80d1e7368bc2cf096
|
||||
openproject-token (8.9.0) sha256=aa08c144889010750de4edaf61f8614ccb82ac6c63beef1d3a21c6a222358605
|
||||
openproject-two_factor_authentication (1.0.0)
|
||||
openproject-webhooks (1.0.0)
|
||||
openproject-wikis (1.0.0)
|
||||
|
||||
Binary file not shown.
+6
-2
@@ -29,7 +29,11 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<%=
|
||||
render(Primer::Beta::Text.new(tag: :div, color: :muted, mb: 3)) do
|
||||
I18n.t("backlogs.sharing_description")
|
||||
if only_fallback_allowed
|
||||
t(".sharing_fallback_description")
|
||||
else
|
||||
t(".sharing_description")
|
||||
end
|
||||
end
|
||||
%>
|
||||
|
||||
@@ -40,6 +44,6 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
method: :patch,
|
||||
data: { turbo_frame: "_top", controller: "show-when-value-selected" }
|
||||
) do |f|
|
||||
render(Projects::Settings::Backlogs::SharingForm.new(f))
|
||||
render(Projects::Settings::Backlogs::SharingForm.new(f, only_fallback_allowed:))
|
||||
end
|
||||
%>
|
||||
|
||||
+4
-2
@@ -35,15 +35,17 @@ module Projects
|
||||
include ApplicationHelper
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
def initialize(project:)
|
||||
def initialize(project:,
|
||||
only_fallback_allowed: false)
|
||||
super
|
||||
|
||||
@project = project
|
||||
@only_fallback_allowed = only_fallback_allowed
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :project
|
||||
attr_reader :project, :only_fallback_allowed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -36,6 +36,7 @@ module Projects
|
||||
validate :validate_global_sprint_sharer_uniqueness
|
||||
validates :sprint_sharing, presence: true
|
||||
validates :sprint_sharing, inclusion: { in: Project::SPRINT_SHARING_MODES }, allow_blank: true
|
||||
validate :validate_sprint_sharing_in_ee_token
|
||||
|
||||
def validate_model? = false
|
||||
|
||||
@@ -59,5 +60,19 @@ module Projects
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate_sprint_sharing_in_ee_token
|
||||
if !model.not_sharing_sprints? &&
|
||||
!EnterpriseToken.allows_to?(:sprint_sharing) &&
|
||||
sprint_sharing_changed?
|
||||
errors.add :sprint_sharing,
|
||||
:enterprise_plan_required,
|
||||
plan_name: I18n.t("ee.upsell.plan_name", plan: OpenProject::Token.lowest_plan_for(:sprint_sharing))
|
||||
end
|
||||
end
|
||||
|
||||
def sprint_sharing_changed?
|
||||
model.settings_change&.any? { it.key?("sprint_sharing") }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -36,27 +36,34 @@ module Projects
|
||||
# TODO: Remove this hidden field, once the `radio_button_group` supports rendering
|
||||
# the hidden empty field.
|
||||
# The purpose of the hidden field is to ensure we submit the `sprint_sharing` field
|
||||
# even if no radio button is chosen. Otherwise, the submitted form will not include
|
||||
# even if:
|
||||
# * no radio button is chosen.
|
||||
# * the selected option is disabled because of a missing EE token.
|
||||
# Otherwise, the submitted form will not include
|
||||
# the field at all and the save request will return success when in fact no setting
|
||||
# is saved.
|
||||
# Ideally the hidden field should automatically be rendered by the `radio_button_group`
|
||||
# helper, similar to how the `collection_radio_buttons` rails helper does.
|
||||
sharing_form.hidden(name: :sprint_sharing, value: "")
|
||||
sharing_form.hidden(name: :sprint_sharing, value: model.sprint_sharing)
|
||||
|
||||
sharing_form.radio_button_group(
|
||||
name: :sprint_sharing,
|
||||
label: I18n.t("projects.settings.backlog_sharing.sprint_sharing")
|
||||
) do |group|
|
||||
Project::SPRINT_SHARING_MODES.each do |option|
|
||||
group.radio_button(
|
||||
label: sharing_option_text(option, :label),
|
||||
value: option,
|
||||
checked: checked?(option),
|
||||
disabled: disabled?(option),
|
||||
caption: caption_for(option),
|
||||
data: { "show-when-value-selected-target": "cause" }
|
||||
)
|
||||
end
|
||||
group_radio_button(group,
|
||||
sharing: Project::NO_SHARING,
|
||||
disabled: false)
|
||||
|
||||
group_radio_button(group,
|
||||
sharing: Project::SHARE_ALL_PROJECTS,
|
||||
disabled: only_fallback_allowed || all_projects_shared_by_other_project?,
|
||||
caption: shared_all_projects_caption)
|
||||
|
||||
group_radio_button(group,
|
||||
sharing: Project::SHARE_SUBPROJECTS)
|
||||
|
||||
group_radio_button(group,
|
||||
sharing: Project::RECEIVE_SHARED)
|
||||
end
|
||||
|
||||
sharing_form.html_content { banner_for(Project::SHARE_SUBPROJECTS, type: :info) }
|
||||
@@ -69,33 +76,41 @@ module Projects
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def checked?(option)
|
||||
option == model.sprint_sharing
|
||||
def initialize(only_fallback_allowed: false)
|
||||
super()
|
||||
@only_fallback_allowed = only_fallback_allowed
|
||||
end
|
||||
|
||||
def disabled?(option)
|
||||
option == Project::SHARE_ALL_PROJECTS && share_all_projects_disabled?
|
||||
private
|
||||
|
||||
attr_reader :only_fallback_allowed
|
||||
|
||||
def group_radio_button(group,
|
||||
sharing:,
|
||||
disabled: only_fallback_allowed,
|
||||
caption: sharing_option_caption(sharing))
|
||||
group.radio_button(
|
||||
label: sharing_option_label(sharing),
|
||||
value: sharing,
|
||||
caption:,
|
||||
disabled:,
|
||||
data: { "show-when-value-selected-target": "cause" }
|
||||
)
|
||||
end
|
||||
|
||||
def sharing_option_caption(option)
|
||||
sharing_option_text(option, :caption)
|
||||
end
|
||||
|
||||
def sharing_option_label(option)
|
||||
sharing_option_text(option, :label)
|
||||
end
|
||||
|
||||
def sharing_option_text(option, key, **)
|
||||
I18n.t("projects.settings.backlog_sharing.options.#{option}.#{key}", **)
|
||||
end
|
||||
|
||||
def caption_for(option)
|
||||
if disabled?(option)
|
||||
if User.current.allowed_in_project?(:view_project, global_sprint_sharer)
|
||||
sharing_option_text(option, :disabled_caption, name: global_sprint_sharer.name)
|
||||
else
|
||||
sharing_option_text(option, :disabled_caption_anonymous)
|
||||
end
|
||||
else
|
||||
sharing_option_text(option, :caption)
|
||||
end
|
||||
end
|
||||
|
||||
def share_all_projects_disabled?
|
||||
def all_projects_shared_by_other_project?
|
||||
global_sprint_sharer && global_sprint_sharer != model
|
||||
end
|
||||
|
||||
@@ -117,6 +132,21 @@ module Projects
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def shared_all_projects_caption
|
||||
if all_projects_shared_by_other_project?
|
||||
if User.current.allowed_in_project?(:view_project, global_sprint_sharer)
|
||||
sharing_option_text(Project::SHARE_ALL_PROJECTS,
|
||||
:disabled_caption,
|
||||
name: global_sprint_sharer.name)
|
||||
else
|
||||
sharing_option_text(Project::SHARE_ALL_PROJECTS,
|
||||
:disabled_caption_anonymous)
|
||||
end
|
||||
else
|
||||
sharing_option_caption(Project::SHARE_ALL_PROJECTS)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,5 +35,22 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
)
|
||||
) %>
|
||||
|
||||
<%= render(Projects::Settings::Backlogs::SharingFormComponent.new(project: @project)) %>
|
||||
<% with_enterprise_banner_guard(
|
||||
:sprint_sharing,
|
||||
# We want to enable users to migrate out of sprint sharing if their token expired.
|
||||
# If that happens, an inline banner is to be displayed.
|
||||
# But there is the special case of trial tokens. For those, two inline banners would be displayed
|
||||
# which is why this is handled explicitly here.
|
||||
inactive_guard: !@project.not_sharing_sprints? || EnterpriseToken.trialling?(:sprint_sharing),
|
||||
variant: :large,
|
||||
video: "enterprise/sprint-sharing.mp4"
|
||||
) do %>
|
||||
<%= render(EnterpriseEdition::BannerComponent.new(:sprint_sharing, variant: :inline)) %>
|
||||
<%= render(
|
||||
Projects::Settings::Backlogs::SharingFormComponent.new(
|
||||
project: @project,
|
||||
only_fallback_allowed: !EnterpriseToken.allows_to?(:sprint_sharing)
|
||||
)
|
||||
) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -159,7 +159,6 @@ en:
|
||||
rebuild_positions: "Rebuild positions"
|
||||
remaining_hours: "Remaining work"
|
||||
sharing: "Sharing"
|
||||
sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)."
|
||||
show_burndown_chart: "Burndown chart"
|
||||
|
||||
sprint_component:
|
||||
@@ -220,6 +219,13 @@ en:
|
||||
story_points: "Story points"
|
||||
story_points_ideal: "Story points (ideal)"
|
||||
|
||||
ee:
|
||||
features:
|
||||
sprint_sharing: "Sprint sharing"
|
||||
upsell:
|
||||
sprint_sharing:
|
||||
description: "Share sprints across projects to align teams and coordinate work in scaled agile setups (SAFe)."
|
||||
|
||||
label_backlog: "Backlog"
|
||||
label_backlog_bucket_edit: "Edit backlog bucket"
|
||||
label_backlog_bucket_new: "New backlog bucket"
|
||||
@@ -274,4 +280,9 @@ en:
|
||||
info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints."
|
||||
sprint_sharing: Share sprints
|
||||
|
||||
backlogs:
|
||||
sharing_form_component:
|
||||
sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)."
|
||||
sharing_fallback_description: "Lacking a corporate enterprise plan, the sharing options are limited to the project's own sprints. The currently active setting remains active."
|
||||
|
||||
remaining_hours: "remaining work"
|
||||
|
||||
@@ -38,7 +38,11 @@ module OpenProject::Backlogs::Patches::CopyServicePatch
|
||||
module InstanceMethods
|
||||
def clean_settings_attributes!(settings)
|
||||
# There can be only one project sharing with all projects.
|
||||
settings.delete("sprint_sharing") if settings["sprint_sharing"] == Projects::SprintSharing::SHARE_ALL_PROJECTS
|
||||
if settings["sprint_sharing"] == Projects::SprintSharing::SHARE_ALL_PROJECTS ||
|
||||
!EnterpriseToken.allows_to?(:sprint_sharing)
|
||||
settings.delete("sprint_sharing")
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
require "spec_helper"
|
||||
require "contracts/shared/model_contract_shared_context"
|
||||
|
||||
RSpec.describe Projects::BacklogSettingsContract, type: :model do
|
||||
RSpec.describe Projects::BacklogSettingsContract, type: :model, with_ee: %i[sprint_sharing] do
|
||||
include_context "ModelContract shared context"
|
||||
|
||||
let(:current_user) { build_stubbed(:user) }
|
||||
let(:project) { create(:project) }
|
||||
let(:project) { build_stubbed(:project) }
|
||||
let(:permissions) { %i(share_sprint) }
|
||||
|
||||
subject(:contract) { described_class.new(project, current_user) }
|
||||
@@ -35,7 +35,7 @@ RSpec.describe Projects::BacklogSettingsContract, type: :model do
|
||||
|
||||
# This spec of explicitly setting sprint_sharing to empty is required because the
|
||||
# simple presence validation spec is not sufficient to catch certain corner cases.
|
||||
# For example, when the sprint_sharing getter is overriden to provide a default value,
|
||||
# For example, when the sprint_sharing getter is overridden to provide a default value,
|
||||
# and the user submits an empty value, the contract should be invalid.
|
||||
context "when sprint_sharing is empty" do
|
||||
before { project.sprint_sharing = "" }
|
||||
@@ -57,6 +57,62 @@ RSpec.describe Projects::BacklogSettingsContract, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
context "when the `sprint_sharing` is not part of the current EE token", with_ee: [] do
|
||||
context "when sprint sharing is set to 'no_sharing'" do
|
||||
before { project.sprint_sharing = Project::NO_SHARING }
|
||||
|
||||
it_behaves_like "contract is valid"
|
||||
end
|
||||
|
||||
context "when sprint sharing is set to 'share_all_projects'" do
|
||||
before { project.sprint_sharing = Project::SHARE_ALL_PROJECTS }
|
||||
|
||||
it_behaves_like "contract is invalid",
|
||||
sprint_sharing: { error: :enterprise_plan_required, plan_name: "corporate enterprise plan" }
|
||||
end
|
||||
|
||||
context "when sprint sharing is set to 'share_subprojects'" do
|
||||
before { project.sprint_sharing = Project::SHARE_SUBPROJECTS }
|
||||
|
||||
it_behaves_like "contract is invalid",
|
||||
sprint_sharing: { error: :enterprise_plan_required, plan_name: "corporate enterprise plan" }
|
||||
end
|
||||
|
||||
context "when sprint sharing is set to 'receive_shared'" do
|
||||
before { project.sprint_sharing = Project::RECEIVE_SHARED }
|
||||
|
||||
it_behaves_like "contract is invalid",
|
||||
sprint_sharing: { error: :enterprise_plan_required, plan_name: "corporate enterprise plan" }
|
||||
end
|
||||
|
||||
context "when sprint sharing remains on 'share_all_projects'" do
|
||||
before do
|
||||
project.sprint_sharing = Project::SHARE_ALL_PROJECTS
|
||||
project.clear_changes_information
|
||||
end
|
||||
|
||||
it_behaves_like "contract is valid"
|
||||
end
|
||||
|
||||
context "when sprint sharing remains on 'share_subprojects'" do
|
||||
before do
|
||||
project.sprint_sharing = Project::SHARE_SUBPROJECTS
|
||||
project.clear_changes_information
|
||||
end
|
||||
|
||||
it_behaves_like "contract is valid"
|
||||
end
|
||||
|
||||
context "when sprint sharing remains on 'receive_shared'" do
|
||||
before do
|
||||
project.sprint_sharing = Project::RECEIVE_SHARED
|
||||
project.clear_changes_information
|
||||
end
|
||||
|
||||
it_behaves_like "contract is valid"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#validate_global_sprint_sharer_uniqueness" do
|
||||
before do
|
||||
project.sprint_sharing = "share_all_projects"
|
||||
|
||||
@@ -42,7 +42,8 @@ RSpec.describe "Backlogs project settings sprint sharing", :js do
|
||||
login_as current_user
|
||||
end
|
||||
|
||||
context "with share_sprint permission" do
|
||||
context "with share_sprint permission and enterprise token",
|
||||
with_ee: [:sprint_sharing] do
|
||||
it "displays and stores sprint sharing settings" do
|
||||
visit project_settings_backlog_sharing_path(project)
|
||||
|
||||
@@ -127,6 +128,50 @@ RSpec.describe "Backlogs project settings sprint sharing", :js do
|
||||
end
|
||||
end
|
||||
|
||||
context "with share_sprint permission but no enterprise token" do
|
||||
context "without existing sharing setting in the project" do
|
||||
it "shows an enterprise token teaser" do
|
||||
visit project_settings_backlog_sharing_path(project)
|
||||
|
||||
expect(page).to have_text("Share sprints across projects to align teams")
|
||||
|
||||
expect(page).to have_no_field("Don't share")
|
||||
expect(page).to have_no_field("All projects")
|
||||
expect(page).to have_no_field("Subprojects")
|
||||
expect(page).to have_no_field("Receive shared sprints")
|
||||
end
|
||||
end
|
||||
|
||||
context "with existing sharing setting in the project" do
|
||||
before do
|
||||
project.update!(sprint_sharing: "receive_shared")
|
||||
end
|
||||
|
||||
it "shows the existing sharing setting but disables them except for `Don't share`" do
|
||||
visit project_settings_backlog_sharing_path(project)
|
||||
|
||||
# All radio buttons are present with the selected option displayed.
|
||||
# But all except the "Don't share" option are disabled.
|
||||
expect(page).to have_unchecked_field("Don't share")
|
||||
expect(page).to have_unchecked_field("All projects", disabled: true)
|
||||
expect(page).to have_unchecked_field("Subprojects", disabled: true)
|
||||
expect(page).to have_checked_field("Receive shared sprints", disabled: true)
|
||||
|
||||
choose("Don't share")
|
||||
|
||||
click_button "Save"
|
||||
|
||||
# Now that the `Don't share` option is selected, the large enterprise banner is displayed.
|
||||
expect(page).to have_text("Share sprints across projects to align teams")
|
||||
|
||||
expect(page).to have_no_field("Don't share")
|
||||
expect(page).to have_no_field("All projects")
|
||||
expect(page).to have_no_field("Subprojects")
|
||||
expect(page).to have_no_field("Receive shared sprints")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "without share_sprint permission" do
|
||||
let(:permissions) { %i[create_sprints select_backlog_types_and_statuses] }
|
||||
|
||||
|
||||
@@ -58,51 +58,69 @@ RSpec.describe Projects::CopyService, "integration", type: :model do
|
||||
subject { instance.call(params) }
|
||||
|
||||
describe "#sprint_sharing setting" do
|
||||
context "when the source project is set to receive" do
|
||||
before do
|
||||
source.sprint_sharing = Projects::SprintSharing::RECEIVE_SHARED
|
||||
source.save!
|
||||
context "with an ee license for sprint sharing", with_ee: %i[sprint_sharing] do
|
||||
context "when the source project is set to receive" do
|
||||
before do
|
||||
source.sprint_sharing = Projects::SprintSharing::RECEIVE_SHARED
|
||||
source.save!
|
||||
end
|
||||
|
||||
it "copies the backlog sharing setting" do
|
||||
expect(subject).to be_success
|
||||
expect(project_copy.sprint_sharing).to eq Projects::SprintSharing::RECEIVE_SHARED
|
||||
end
|
||||
end
|
||||
|
||||
it "copies the backlog sharing setting" do
|
||||
expect(subject).to be_success
|
||||
expect(project_copy.sprint_sharing).to eq Projects::SprintSharing::RECEIVE_SHARED
|
||||
context "when the source project is set to share with subprojects" do
|
||||
before do
|
||||
source.sprint_sharing = Projects::SprintSharing::SHARE_SUBPROJECTS
|
||||
source.save!
|
||||
end
|
||||
|
||||
it "copies the backlog sharing setting" do
|
||||
expect(subject).to be_success
|
||||
expect(project_copy.sprint_sharing).to eq Projects::SprintSharing::SHARE_SUBPROJECTS
|
||||
end
|
||||
end
|
||||
|
||||
context "when the source project is set to not share" do
|
||||
before do
|
||||
source.sprint_sharing = Projects::SprintSharing::NO_SHARING
|
||||
source.save!
|
||||
end
|
||||
|
||||
it "copies the backlog sharing setting" do
|
||||
expect(subject).to be_success
|
||||
expect(project_copy.sprint_sharing).to eq Projects::SprintSharing::NO_SHARING
|
||||
end
|
||||
end
|
||||
|
||||
context "when the source project is set to share with all" do
|
||||
before do
|
||||
source.sprint_sharing = Projects::SprintSharing::SHARE_ALL_PROJECTS
|
||||
source.save!
|
||||
end
|
||||
|
||||
it "does not copy the setting as that would result in two projects sharing with all" do
|
||||
expect(subject).to be_success
|
||||
expect(project_copy.sprint_sharing).to eq Projects::SprintSharing::NO_SHARING
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the source project is set to share with subprojects" do
|
||||
before do
|
||||
source.sprint_sharing = Projects::SprintSharing::SHARE_SUBPROJECTS
|
||||
source.save!
|
||||
end
|
||||
context "without an ee license for sprint sharing", with_ee: %i[] do
|
||||
Projects::SprintSharing::SPRINT_SHARING_MODES.each do |mode|
|
||||
context "when the source project is set to #{mode}" do
|
||||
before do
|
||||
source.sprint_sharing = mode
|
||||
source.save!
|
||||
end
|
||||
|
||||
it "copies the backlog sharing setting" do
|
||||
expect(subject).to be_success
|
||||
expect(project_copy.sprint_sharing).to eq Projects::SprintSharing::SHARE_SUBPROJECTS
|
||||
end
|
||||
end
|
||||
|
||||
context "when the source project is set to not share" do
|
||||
before do
|
||||
source.sprint_sharing = Projects::SprintSharing::NO_SHARING
|
||||
source.save!
|
||||
end
|
||||
|
||||
it "copies the backlog sharing setting" do
|
||||
expect(subject).to be_success
|
||||
expect(project_copy.sprint_sharing).to eq Projects::SprintSharing::NO_SHARING
|
||||
end
|
||||
end
|
||||
|
||||
context "when the source project is set to share with all" do
|
||||
before do
|
||||
source.sprint_sharing = Projects::SprintSharing::SHARE_ALL_PROJECTS
|
||||
source.save!
|
||||
end
|
||||
|
||||
it "does not copy the setting as that would result in two projects sharing with all" do
|
||||
expect(subject).to be_success
|
||||
expect(project_copy.sprint_sharing).to eq Projects::SprintSharing::NO_SHARING
|
||||
it "copies the backlog sharing setting" do
|
||||
expect(subject).to be_success
|
||||
expect(project_copy.sprint_sharing).to eq Projects::SprintSharing::NO_SHARING
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,7 +48,11 @@ RSpec.shared_context "ModelContract shared context" do # rubocop:disable RSpec/C
|
||||
[error_symbols]
|
||||
end
|
||||
end
|
||||
contract_errors = errors.keys.index_with { |key| contract.errors.symbols_for(key) }
|
||||
|
||||
contract_errors = errors.keys.index_with do |key|
|
||||
errors[key].is_a?(Hash) ? contract.errors.details[key] : contract.errors.symbols_for(key)
|
||||
end
|
||||
|
||||
expect(contract_errors).to match(expected_errors)
|
||||
if RSpec.current_example.metadata[:check_errors_i18n]
|
||||
# ensure no I18n::MissingTranslationData is raised because of missing attributes and/or errors translations
|
||||
|
||||
Reference in New Issue
Block a user