rename steps and step definitions

This commit is contained in:
ulferts
2025-03-25 17:33:36 +01:00
committed by Dombi Attila
parent b0b7624699
commit 464e97499c
55 changed files with 180 additions and 685 deletions
+1 -1
View File
@@ -196,7 +196,7 @@ module Projects
end
def project_life_cycle_step_by_definition(definition, project)
@project_life_cycle_steps_by_definition ||= Project::LifeCycleStep
@project_life_cycle_steps_by_definition ||= Project::Phase
.visible
.index_by { |s| [s.definition_id, s.project_id] }
@@ -31,7 +31,7 @@ module ProjectLifeCycleSteps
validate :select_custom_fields_permission
validate :consecutive_steps_have_increasing_dates
def valid?(context = :saving_life_cycle_steps) = super
def valid?(context = :saving_phases) = super
def select_custom_fields_permission
return if user.allowed_in_project?(:edit_project_stages_and_gates, model)
@@ -41,7 +41,7 @@ module ProjectLifeCycleSteps
def consecutive_steps_have_increasing_dates
# Filter out steps with missing dates before proceeding with comparison
filtered_steps = model.available_life_cycle_steps.select(&:start_date)
filtered_steps = model.available_phases.select(&:start_date)
# Only proceed with comparisons if there are at least 2 valid steps
return if filtered_steps.size < 2
@@ -53,7 +53,7 @@ module ProjectLifeCycleSteps
field = current_step.is_a?(Project::Stage) ? :date_range : :date
model.errors.import(
current_step.errors.add(field, :non_continuous_dates, step:),
attribute: :"available_life_cycle_steps.#{field}"
attribute: :"available_phases.#{field}"
)
end
end
@@ -61,7 +61,7 @@ module Admin::Settings
end
def create
@definition = Project::LifeCycleStepDefinition.new(definition_params)
@definition = Project::PhaseDefinition.new(definition_params)
if @definition.save
flash[:notice] = I18n.t(:notice_successful_create)
@@ -123,11 +123,11 @@ module Admin::Settings
end
def find_definitions
@definitions = Project::LifeCycleStepDefinition.with_project_count
@definitions = Project::PhaseDefinition.with_project_count
end
def find_definition
@definition = Project::LifeCycleStepDefinition.find(params[:id])
@definition = Project::PhaseDefinition.find(params[:id])
end
def definition_params
@@ -38,7 +38,7 @@ class Projects::Settings::LifeCycleStepsController < Projects::SettingsControlle
def index; end
def toggle
definition = Project::LifeCycleStepDefinition.where(id: params[:id])
definition = Project::PhaseDefinition.where(id: params[:id])
upsert_steps(definition, active: params["value"])
end
@@ -58,7 +58,7 @@ class Projects::Settings::LifeCycleStepsController < Projects::SettingsControlle
private
def load_life_cycle_definitions
@life_cycle_definitions = Project::LifeCycleStepDefinition.order(position: :asc)
@life_cycle_definitions = Project::PhaseDefinition.order(position: :asc)
end
def deny_access_on_feature_flag
@@ -66,7 +66,7 @@ class Projects::Settings::LifeCycleStepsController < Projects::SettingsControlle
end
def upsert_steps(definitions, active:)
Project::LifeCycleStep.upsert_all(
Project::Phase.upsert_all(
definitions.map do |definition|
{
project_id: @project.id,
+1 -1
View File
@@ -4,6 +4,6 @@ module HighlightingHelper
end
def highlight_css_updated_at
ApplicationRecord.most_recently_changed Status, IssuePriority, Type, UserPreference, Project::LifeCycleStepDefinition
ApplicationRecord.most_recently_changed Status, IssuePriority, Type, UserPreference, Project::PhaseDefinition
end
end
@@ -31,7 +31,7 @@
class Journal::ProjectLifeCycleStepJournal < Journal::AssociatedJournal
self.table_name = "project_life_cycle_step_journals"
belongs_to :life_cycle_step, class_name: "Project::LifeCycleStep"
belongs_to :phase, class_name: "Project::LifeCycleStep"
alias_attribute :date, :start_date
+1 -1
View File
@@ -292,7 +292,7 @@ class PermittedParams
def project_life_cycles
params.require(:project).permit(
available_life_cycle_steps_attributes: %i[id date date_range]
available_phases_attributes: %i[id date date_range]
)
end
+5 -5
View File
@@ -86,17 +86,17 @@ class Project < ApplicationRecord
has_many :notification_settings, dependent: :destroy
has_many :project_storages, dependent: :destroy, class_name: "Storages::ProjectStorage"
has_many :storages, through: :project_storages
has_many :life_cycle_steps, class_name: "Project::LifeCycleStep", dependent: :destroy
has_many :available_life_cycle_steps,
has_many :phases, class_name: "Project::Phase", dependent: :destroy
has_many :available_phases,
-> { visible.eager_load(:definition).order(position: :asc) },
class_name: "Project::LifeCycleStep",
class_name: "Project::Phase",
inverse_of: :project,
dependent: :destroy
has_many :recurring_meetings, dependent: :destroy
accepts_nested_attributes_for :available_life_cycle_steps
validates_associated :available_life_cycle_steps, on: :saving_life_cycle_steps
accepts_nested_attributes_for :available_phases
validates_associated :available_phase, on: :saving_phases
store_attribute :settings, :deactivate_work_package_attachments, :boolean
-45
View File
@@ -1,45 +0,0 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class Project::Gate < Project::LifeCycleStep
alias_attribute :date, :start_date
# This ensures the type cannot be changed after initialising the class.
validates :type, inclusion: { in: %w[Project::Gate], message: :must_be_a_gate }
validate :end_date_not_allowed
def end_date_not_allowed
if end_date.present?
errors.add(:base, :end_date_not_allowed)
end
end
def not_set?
date.blank?
end
end
-62
View File
@@ -1,62 +0,0 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class Project::LifeCycleStep < ApplicationRecord
belongs_to :project, optional: false, inverse_of: :available_life_cycle_steps
belongs_to :definition,
optional: false,
class_name: "Project::LifeCycleStepDefinition"
has_many :work_packages, inverse_of: :project_life_cycle_step, dependent: :nullify
delegate :name, :position, to: :definition
attr_readonly :definition_id, :type
validates :type, inclusion: { in: %w[Project::Stage Project::Gate], message: :must_be_a_stage_or_gate }
validate :validate_type_and_class_name_are_identical
scope :active, -> { where(active: true) }
class << self
def visible(user = User.current)
allowed_projects = Project.allowed_to(user, :view_project_stages_and_gates)
active.where(project: allowed_projects)
end
end
def validate_type_and_class_name_are_identical
if type != self.class.name
errors.add(:type, :type_and_class_name_mismatch)
end
end
def column_name
# The id of the associated definition is relevant for displaying the correct column headers
"lcsd_#{definition_id}"
end
end
@@ -26,11 +26,28 @@
# See COPYRIGHT and LICENSE files for more details.
#++
class Project::Stage < Project::LifeCycleStep
# This ensures the type cannot be changed after initialising the class.
validates :type, inclusion: { in: %w[Project::Stage], message: :must_be_a_stage }
class Project::Phase < ApplicationRecord
belongs_to :project, optional: false, inverse_of: :available_phases
belongs_to :definition,
optional: false,
class_name: "Project::PhaseDefinition"
has_many :work_packages, inverse_of: :project_phase, dependent: :nullify
validate :validate_date_range
delegate :name, :position, to: :definition
attr_readonly :definition_id, :type
scope :active, -> { where(active: true) }
class << self
def visible(user = User.current)
allowed_projects = Project.allowed_to(user, :view_project_stages_and_gates)
active.where(project: allowed_projects)
end
end
def working_days_count
return nil if not_set?
@@ -61,4 +78,9 @@ class Project::Stage < Project::LifeCycleStep
errors.add(:date_range, :start_date_must_be_before_end_date)
end
end
def column_name
# The id of the associated definition is relevant for displaying the correct column headers
"lcsd_#{definition_id}"
end
end
@@ -1,3 +1,5 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -26,22 +28,18 @@
# See COPYRIGHT and LICENSE files for more details.
#++
class Project::LifeCycleStepDefinition < ApplicationRecord
class Project::PhaseDefinition < ApplicationRecord
include ::Scopes::Scoped
has_many :life_cycle_steps,
class_name: "Project::LifeCycleStep",
has_many :phases,
class_name: "Project::Phase",
foreign_key: :definition_id,
inverse_of: :definition,
dependent: :destroy
has_many :projects, through: :life_cycle_steps
has_many :projects, through: :phases
belongs_to :color, optional: false
validates :name, presence: true, uniqueness: true
validates :type, inclusion: { in: %w[Project::StageDefinition Project::GateDefinition], message: :must_be_a_stage_or_gate }
validate :validate_type_and_class_name_are_identical
attr_readonly :type
acts_as_list
@@ -49,19 +47,8 @@ class Project::LifeCycleStepDefinition < ApplicationRecord
scopes :with_project_count
def step_class
raise NotImplementedError
end
def column_name
# TODO: rename
"lcsd_#{id}"
end
private
def validate_type_and_class_name_are_identical
if type != self.class.name
errors.add(:type, :type_and_class_name_mismatch)
end
end
end
@@ -28,18 +28,18 @@
# See COPYRIGHT and LICENSE files for more details.
#++
module Project::LifeCycleStepDefinitions::Scopes
module Project::PhaseDefinitions::Scopes
module WithProjectCount
extend ActiveSupport::Concern
class_methods do
def with_project_count
project_counts = Project::LifeCycleStep
project_counts = Project::Phase
.where(active: true)
.group(:definition_id)
.select(:definition_id, "COUNT(project_id) AS count")
Project::LifeCycleStepDefinition
Project::PhaseDefinition
.with(project_counts:)
.joins("LEFT OUTER JOIN project_counts ON #{quoted_table_name}.id = project_counts.definition_id")
.select("#{quoted_table_name}.*")
@@ -69,7 +69,7 @@ module Queries::Projects::Filters::DynamicallyFromLifeCycle
key = %w[Queries::Projects::Filters::LifeCycleStepFilter all_step_definitions]
RequestStore
.fetch(key) { Project::LifeCycleStepDefinition.all.to_a }
.fetch(key) { Project::PhaseDefinition.all.to_a }
.select { |lcsd| lcsd.is_a?(step_subclass) }
end
@@ -100,14 +100,14 @@ module Queries::Projects::Filters::FilterOnLifeCycle
def stage_where_on(start_date, end_date = start_date)
life_cycle_scope(Project::Stage.name)
.where(date_range_clause(Project::LifeCycleStep.table_name, "start_date", nil, start_date))
.where(date_range_clause(Project::LifeCycleStep.table_name, "end_date", end_date, nil))
.where(date_range_clause(Project::Phase.table_name, "start_date", nil, start_date))
.where(date_range_clause(Project::Phase.table_name, "end_date", end_date, nil))
end
def stage_where_between(start_date, end_date)
life_cycle_scope(Project::Stage.name)
.where(date_range_clause(Project::LifeCycleStep.table_name, "start_date", start_date, nil))
.where(date_range_clause(Project::LifeCycleStep.table_name, "end_date", nil, end_date))
.where(date_range_clause(Project::Phase.table_name, "start_date", start_date, nil))
.where(date_range_clause(Project::Phase.table_name, "end_date", nil, end_date))
end
def stage_overlaps_this_week
@@ -116,8 +116,8 @@ module Queries::Projects::Filters::FilterOnLifeCycle
.where.not(end_date: nil)
.where(
<<~SQL.squish, beginning_of_week, end_of_week
daterange(#{Project::LifeCycleStep.table_name}.start_date,
#{Project::LifeCycleStep.table_name}.end_date,
daterange(#{Project::Phase.table_name}.start_date,
#{Project::Phase.table_name}.end_date,
'[]')
&&
daterange(?, ?, '[]')
@@ -140,7 +140,7 @@ module Queries::Projects::Filters::FilterOnLifeCycle
def gate_where(start_date, end_date = start_date)
# On gates, only the start_date is set.
life_cycle_scope(Project::Gate.name)
.where(date_range_clause(Project::LifeCycleStep.table_name, "start_date", start_date, end_date))
.where(date_range_clause(Project::Phase.table_name, "start_date", start_date, end_date))
end
def parsed_start
@@ -164,8 +164,8 @@ module Queries::Projects::Filters::FilterOnLifeCycle
end
def life_cycle_scope(type)
life_cycle_scope = Project::LifeCycleStep
.where("#{Project::LifeCycleStep.table_name}.project_id = #{Project.table_name}.id")
life_cycle_scope = Project::Phase
.where("#{Project::Phase.table_name}.project_id = #{Project.table_name}.id")
.where(project_id: Project.allowed_to(User.current, :view_project_stages_and_gates))
.where(type:)
.active
@@ -40,7 +40,7 @@ class Queries::Projects::Orders::LifeCycleStepOrder < Queries::Orders::Base
def life_cycle_step_definition
return @life_cycle_step_definition if defined?(@life_cycle_step_definition)
@life_cycle_step_definition = Project::LifeCycleStepDefinition.find_by(id: attribute[/\Alcsd_(\d+)\z/, 1])
@life_cycle_step_definition = Project::PhaseDefinition.find_by(id: attribute[/\Alcsd_(\d+)\z/, 1])
end
def available?
@@ -38,7 +38,7 @@ class Queries::Projects::Selects::LifeCycleStep < Queries::Selects::Base
def self.all_available
return [] unless available?
Project::LifeCycleStepDefinition
Project::PhaseDefinition
.pluck(:id)
.map { |id| new(:"lcsd_#{id}") }
end
@@ -50,7 +50,7 @@ class Queries::Projects::Selects::LifeCycleStep < Queries::Selects::Base
def life_cycle_step_definition
return @life_cycle_step_definition if defined?(@life_cycle_step_definition)
@life_cycle_step_definition = Project::LifeCycleStepDefinition
@life_cycle_step_definition = Project::PhaseDefinition
.find_by(id: attribute[KEY, 1])
end
+1 -1
View File
@@ -55,7 +55,7 @@ class WorkPackage < ApplicationRecord
belongs_to :assigned_to, class_name: "Principal", optional: true
belongs_to :responsible, class_name: "Principal", optional: true
belongs_to :version, optional: true
belongs_to :project_life_cycle_step, class_name: "Project::LifeCycleStep", optional: true
belongs_to :project_phase, class_name: "Project::Phase", optional: true
belongs_to :priority, class_name: "IssuePriority"
belongs_to :category, class_name: "Category", optional: true
@@ -27,7 +27,7 @@
#++
module BasicData
class LifeCycleStepDefinitionSeeder < ModelSeeder
self.model_class = Project::LifeCycleStepDefinition
self.model_class = Project::PhaseDefinition
self.seed_data_model_key = "life_cycles"
self.needs = [
BasicData::LifeCycleColorSeeder
@@ -79,7 +79,7 @@ class Journals::CreateService
FROM project_phases
WHERE project_phases.project_id = :journable_id) phases
ON
phases.id = project_phase_journals.life_cycle_step_id
phases.id = project_phase_journals.phase_id
WHERE
phases.start_date IS DISTINCT FROM project_phase_journals.start_date
OR phases.end_date IS DISTINCT FROM project_phase_journals.end_date
@@ -39,7 +39,7 @@ module ProjectLifeCycleSteps
def clear_unchanged_fields(service_call)
service_call
.result
.available_life_cycle_steps
.available_phases
.select(&:not_set?)
.each { _1.errors.clear }
end
@@ -37,7 +37,7 @@ See COPYRIGHT and LICENSE files for more details.
<%=
admin_settings_primer_form_with(
model: [:admin, :settings, @definition.becomes(Project::LifeCycleStepDefinition)]
model: [:admin, :settings, @definition.becomes(Project::PhaseDefinition)]
) do |f|
render Projects::LifeCycleStepDefinitions::Form.new(f)
end
+1 -1
View File
@@ -3,7 +3,7 @@
<%= resources_scope_color_css("priority", ::IssuePriority) %>
<%= resources_scope_color_css("type", ::Type, inline_foreground: true) %>
<%# Color coded icons %>
<%= resources_scope_color_css("life_cycle_step_definition", Project::LifeCycleStepDefinition, inline_foreground: true) %>
<%= resources_scope_color_css("life_cycle_step_definition", Project::PhaseDefinition, inline_foreground: true) %>
<% Meetings::Statuses::AVAILABLE.each do |meeting_status| %>
<%= resource_color_css("meeting_status", meeting_status) %>
+2 -2
View File
@@ -1034,9 +1034,9 @@ en:
project_custom_field:
is_required: "Required for all projects"
custom_field_section: Section
project/life_cycle_step:
project/phase:
definition: "Definition"
project/life_cycle_step_definition:
project/phase_definition:
name: "Name"
color: "Color"
query:
@@ -103,6 +103,10 @@ class MergeLifecycleSteps < ActiveRecord::Migration[8.0]
change_table(:project_life_cycle_step_journals) do |t|
t.rename :life_cycle_step_id, :phase_id
end
change_table(:work_packages) do |t|
t.rename :project_life_cycle_step_id, :project_phase_id
end
end
def rename_tables
@@ -32,7 +32,7 @@ class OpenProject::JournalFormatter::ProjectLifeCycleStepActive < JournalFormatt
def render(key, values, options = { html: true })
return if !values[0] == !values[1]
step = Project::LifeCycleStep.find(key[/\d+/])
step = Project::Phase.find(key[/\d+/])
name = step.definition.name
label = options[:html] ? content_tag(:strong, name) : name
@@ -30,7 +30,7 @@
class OpenProject::JournalFormatter::ProjectLifeCycleStepDates < JournalFormatter::Base
def render(key, values, options = { html: true })
step = Project::LifeCycleStep.find(key[/\d+/])
step = Project::Phase.find(key[/\d+/])
name = step.definition.name
label = options[:html] ? content_tag(:strong, name) : name
@@ -16,7 +16,7 @@
url: update_project_life_cycles_path(project_id: model.id)
) do |f|
render(Primer::Forms::SpacingWrapper.new) do
f.fields_for(:available_life_cycle_steps) do |life_cycle_form|
f.fields_for(:available_phases) do |life_cycle_form|
render(Projects::LifeCycles::Form.new(life_cycle_form))
end
end
@@ -37,7 +37,7 @@ module ProjectLifeCycles
super
@project = project
@life_cycle_steps = @project.available_life_cycle_steps
@life_cycle_steps = @project.available_phases
end
private
@@ -39,7 +39,7 @@ RSpec.describe ProjectLifeCycleSteps::BaseContract do
context "with authorized user" do
let(:user) { build_stubbed(:user) }
let(:project) { build_stubbed(:project, available_life_cycle_steps: steps) }
let(:project) { build_stubbed(:project, available_phases: steps) }
let(:steps) { [] }
before do
@@ -81,7 +81,7 @@ RSpec.describe ProjectLifeCycleSteps::BaseContract do
let(:steps) { [gate3, gate1] }
it_behaves_like "contract is invalid",
"available_life_cycle_steps.date": :non_continuous_dates
"available_phases.date": :non_continuous_dates
it "adds an error to the decreasing step" do
contract.validate
@@ -93,7 +93,7 @@ RSpec.describe ProjectLifeCycleSteps::BaseContract do
let(:steps) { [gate3, stage2] }
it_behaves_like "contract is invalid",
"available_life_cycle_steps.date_range": :non_continuous_dates
"available_phases.date_range": :non_continuous_dates
it "adds an error to the decreasing step" do
contract.validate
@@ -107,7 +107,7 @@ RSpec.describe ProjectLifeCycleSteps::BaseContract do
let(:steps) { [gate1, step4] }
it_behaves_like "contract is invalid",
"available_life_cycle_steps.date": :non_continuous_dates
"available_phases.date": :non_continuous_dates
end
context "when steps have touching start and end dates" do
@@ -116,14 +116,14 @@ RSpec.describe ProjectLifeCycleSteps::BaseContract do
let(:steps) { [stage2, stage4] }
it_behaves_like "contract is invalid",
"available_life_cycle_steps.date_range": :non_continuous_dates
"available_phases.date_range": :non_continuous_dates
context "when having an empty step in between" do
let(:step_missing_dates) { build_stubbed(:project_stage, start_date: nil, end_date: nil) }
let(:steps) { [stage2, step_missing_dates, stage4] }
it_behaves_like "contract is invalid",
"available_life_cycle_steps.date_range": :non_continuous_dates
"available_phases.date_range": :non_continuous_dates
end
end
@@ -132,14 +132,14 @@ RSpec.describe ProjectLifeCycleSteps::BaseContract do
let(:steps) { [gate1, gate4] }
it_behaves_like "contract is invalid",
"available_life_cycle_steps.date": :non_continuous_dates
"available_phases.date": :non_continuous_dates
context "when having an empty step in between" do
let(:step_missing_dates) { build_stubbed(:project_stage, start_date: nil, end_date: nil) }
let(:steps) { [gate1, step_missing_dates, gate4] }
it_behaves_like "contract is invalid",
"available_life_cycle_steps.date": :non_continuous_dates
"available_phases.date": :non_continuous_dates
end
end
@@ -200,7 +200,7 @@ RSpec.describe ProjectLifeCycleSteps::BaseContract do
let(:steps) { [stage2, step_missing_dates, gate1] }
it_behaves_like "contract is invalid",
"available_life_cycle_steps.date": :non_continuous_dates
"available_phases.date": :non_continuous_dates
it "adds an error to the decreasing step" do
contract.validate
@@ -211,11 +211,11 @@ RSpec.describe ProjectLifeCycleSteps::BaseContract do
end
describe "triggering validations on the model" do
it "sets the :saving_life_cycle_steps validation context" do
it "sets the :saving_phases validation context" do
allow(project).to receive(:valid?)
contract.validate
expect(project).to have_received(:valid?).with(:saving_life_cycle_steps)
expect(project).to have_received(:valid?).with(:saving_phases)
end
end
end
@@ -1,41 +0,0 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
FactoryBot.define do
factory :project_life_cycle_step_definition, class: "Project::LifeCycleStepDefinition" do
color
factory :project_gate_definition, class: "Project::GateDefinition" do
sequence(:name) { |n| "Gate Definition No. #{n}" }
end
factory :project_stage_definition, class: "Project::StageDefinition" do
sequence(:name) { |n| "Stage Definition No. #{n}" }
end
end
end
@@ -1,49 +0,0 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
FactoryBot.define do
factory :project_life_cycle_step, class: "Project::LifeCycleStep" do
project
active { true }
trait :skip_validate do
to_create { |instance| instance.save(validate: false) }
end
factory :project_stage, class: "Project::Stage" do
definition factory: :project_stage_definition
start_date { Date.current - 2.days }
end_date { Date.current + 2.days }
end
factory :project_gate, class: "Project::Gate" do
definition factory: :project_gate_definition
date { Date.current + 2.days }
end
end
end
@@ -26,14 +26,10 @@
# See COPYRIGHT and LICENSE files for more details.
#++
class Project::GateDefinition < Project::LifeCycleStepDefinition
has_many :gates, # Alias for life_cycle_steps
class_name: "Project::Gate",
foreign_key: :definition_id,
inverse_of: :definition,
dependent: :destroy
FactoryBot.define do
factory :project_phase_definition, class: "Project::PhaseDefinition" do
sequence(:name) { |n| "Phase definition #{n}" }
def step_class
Project::Gate
color
end
end
@@ -26,14 +26,13 @@
# See COPYRIGHT and LICENSE files for more details.
#++
class Project::StageDefinition < Project::LifeCycleStepDefinition
has_many :stages, # Alias for life_cycle_steps
class_name: "Project::Stage",
foreign_key: :definition_id,
inverse_of: :definition,
dependent: :destroy
FactoryBot.define do
factory :project_phase, class: "Project::Phase" do
project
definition factory: :project_phase_definition
active { true }
def step_class
Project::Stage
start_date { Date.current - 2.days }
end_date { Date.current + 2.days }
end
end
@@ -47,7 +47,7 @@ RSpec.describe "Edit project stages and gates on project overview page", :js, wi
describe "with the dialog open" do
context "when all LifeCycleSteps are blank" do
before do
Project::LifeCycleStep.update_all(start_date: nil, end_date: nil)
Project::Phase.update_all(start_date: nil, end_date: nil)
end
it "shows all the Project::LifeCycleSteps without a value" do
@@ -80,7 +80,7 @@ RSpec.describe "Edit project stages and gates on project overview page", :js, wi
expect_angular_frontend_initialized
project.available_life_cycle_steps.each do |step|
project.available_phases.each do |step|
dialog.expect_input_for(step)
end
@@ -132,7 +132,7 @@ RSpec.describe "Show project life cycles on project overview page", :js, with_fl
describe "with no values" do
before do
Project::LifeCycleStep.update_all(start_date: nil, end_date: nil)
Project::Phase.update_all(start_date: nil, end_date: nil)
end
it "shows the correct value for the project custom field if given" do
@@ -37,7 +37,7 @@ RSpec.describe OpenProject::JournalFormatter::ProjectLifeCycleStepActive do
subject(:result) { described_class.new(nil).render(key, values, html:) }
before do
allow(Project::LifeCycleStep).to receive(:find).with(step.id.to_s).and_return(step)
allow(Project::Phase).to receive(:find).with(step.id.to_s).and_return(step)
end
def date(day) = Date.new(2025, 1, day)
@@ -37,7 +37,7 @@ RSpec.describe OpenProject::JournalFormatter::ProjectLifeCycleStepDates do
subject(:result) { described_class.new(nil).render(key, values, html:) }
before do
allow(Project::LifeCycleStep).to receive(:find).with(step.id.to_s).and_return(step)
allow(Project::Phase).to receive(:find).with(step.id.to_s).and_return(step)
end
def date(day) = Date.new(2025, 1, day)
@@ -1,57 +0,0 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "rails_helper"
require "support/shared/project_life_cycle_helpers"
RSpec.describe Project::GateDefinition do
it_behaves_like "a Project::LifeCycleStepDefinition event"
describe "associations" do
it "has many gates" do
expect(subject).to have_many(:gates).class_name("Project::Gate")
.with_foreign_key(:definition_id)
.inverse_of(:definition)
.dependent(:destroy)
end
end
describe "validations" do
it "is invalid if type and class name do not match" do
subject.type = "Project::StageDefinition"
expect(subject).not_to be_valid
expect(subject.errors.symbols_for(:type)).to include(:type_and_class_name_mismatch)
end
end
describe "#step_class" do
it "returns Project::Stage" do
expect(subject.step_class).to eq(Project::Gate)
end
end
end
-57
View File
@@ -1,57 +0,0 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "rails_helper"
require "support/shared/project_life_cycle_helpers"
RSpec.describe Project::Gate do
it_behaves_like "a Project::LifeCycleStep event"
describe "validations" do
it { is_expected.to validate_inclusion_of(:type).in_array(["Project::Gate"]).with_message(:must_be_a_gate) }
it "is invalid if `end_date` is present" do
subject.end_date = Time.zone.today
expect(subject).not_to be_valid
expect(subject.errors[:base])
.to include("Cannot assign end date to a Project::Gate")
end
it "is valid if `end_date` is not present" do
valid_gate = build(:project_gate, end_date: nil)
expect(valid_gate).to be_valid
end
it "is invalid if type and class name do not match" do
subject.type = "Project::Stage"
expect(subject).not_to be_valid
expect(subject.errors.symbols_for(:type)).to include(:type_and_class_name_mismatch)
end
end
end
@@ -1,77 +0,0 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "rails_helper"
RSpec.describe Project::LifeCycleStep do
it "can be instantiated" do
expect { described_class.new }.not_to raise_error(NotImplementedError)
end
describe "with an instantiated Gate" do
subject { build :project_gate }
it { is_expected.to have_readonly_attribute(:definition_id) }
it { is_expected.to have_readonly_attribute(:type) }
end
describe "validations" do
it "is invalid if type and class name do not match" do
subject.type = "Project::Gate"
expect(subject).not_to be_valid
expect(subject.errors.symbols_for(:type)).to include(:type_and_class_name_mismatch)
end
end
describe ".visible" do
let(:project) { create(:project) }
let(:development_project) { create(:project) }
let(:user) do
create(:user,
member_with_permissions:
{ project => %i(view_project view_project_stages_and_gates),
development_project => %i(view_project) })
end
let!(:life_cycle_gate) { create(:project_gate, project:) }
let!(:life_cycle_stage) { create(:project_stage, project:) }
let!(:life_cycle_stage_dev) { create(:project_stage, project: development_project) }
let!(:inactive_life_cycle_gate) { create(:project_gate, project:, active: false) }
let!(:inactive_life_cycle_stage) { create(:project_stage, project: development_project, active: false) }
it "returns active steps where the user has a view_project_stages_and_gates permission" do
expected_steps = [life_cycle_gate, life_cycle_stage]
expect(described_class.visible(user)).to eq(expected_steps)
end
end
# For more specs see:
# - spec/support/shared/project_life_cycle_helpers.rb
# - spec/models/project/gate_spec.rb
# - spec/models/project/stage_spec.rb
end
@@ -1,3 +1,5 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -28,36 +30,25 @@
require "rails_helper"
RSpec.describe Project::LifeCycleStepDefinition do
RSpec.describe Project::PhaseDefinition do
it "can be instantiated" do
expect { described_class.new }.not_to raise_error
end
context "with a Project::StageDefinition" do
subject { create :project_stage_definition }
it { is_expected.to have_readonly_attribute(:type) }
end
describe "validations" do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name) }
it {
expect(subject).to validate_inclusion_of(:type)
.in_array(%w[Project::StageDefinition Project::GateDefinition])
.with_message(:must_be_a_stage_or_gate)
}
it "is invalid if type and class name do not match" do
subject.type = "Project::GateDefinition"
expect(subject).not_to be_valid
expect(subject.errors.symbols_for(:type)).to include(:type_and_class_name_mismatch)
end
end
# For more specs see:
# - spec/support/shared/project_life_cycle_helpers.rb
# - spec/models/project/gate_definition_spec.rb
# - spec/models/project/stage_definition_spec.rb
describe "associations" do
it "has many phases" do
expect(subject).to have_many(:phases).class_name("Project::Phase")
.with_foreign_key(:definition_id)
.inverse_of(:definition)
.dependent(:destroy)
end
it { is_expected.to have_many(:projects).through(:phases) }
it { is_expected.to belong_to(:color).required }
end
end
@@ -30,7 +30,7 @@
require "spec_helper"
RSpec.describe Project::LifeCycleStepDefinitions::Scopes::WithProjectCount do
RSpec.describe Project::PhaseDefinitions::Scopes::WithProjectCount do
let!(:definition_a) { create(:project_stage_definition, name: "foo") }
let!(:definition_b) { create(:project_gate_definition, name: "bar") }
let!(:definition_c) { create(:project_stage_definition, name: "baz") }
@@ -49,7 +49,7 @@ RSpec.describe Project::LifeCycleStepDefinitions::Scopes::WithProjectCount do
describe ".with_project_count" do
it "queries project counts alongside definitions" do
expect(Project::LifeCycleStepDefinition.with_project_count).to contain_exactly(
expect(Project::PhaseDefinition.with_project_count).to contain_exactly(
having_attributes(id: definition_a.id, name: "foo", project_count: 2),
having_attributes(id: definition_b.id, name: "bar", project_count: 1),
having_attributes(id: definition_c.id, name: "baz", project_count: 0)
@@ -27,17 +27,36 @@
#++
require "rails_helper"
require "support/shared/project_life_cycle_helpers"
RSpec.describe Project::Stage do
it_behaves_like "a Project::LifeCycleStep event"
RSpec.describe Project::Phase do
it "can be instantiated" do
expect { described_class.new }.not_to raise_error(NotImplementedError)
end
describe "validations" do
it { is_expected.to validate_inclusion_of(:type).in_array(["Project::Stage"]).with_message(:must_be_a_stage) }
it { is_expected.to have_readonly_attribute(:definition_id) }
it "is valid when `start_date` and `end_date` are present" do
valid_stage = build(:project_stage)
expect(valid_stage).to be_valid
describe "associations" do
it { is_expected.to belong_to(:project).required }
it { is_expected.to belong_to(:definition).required }
it { is_expected.to have_many(:work_packages) }
end
describe ".visible" do
let(:project) { create(:project) }
let(:development_project) { create(:project) }
let(:user) do
create(:user,
member_with_permissions:
{ project => %i(view_project view_project_stages_and_gates),
development_project => %i(view_project) })
end
let!(:phase) { create(:project_phase, project:) }
let!(:phase_dev) { create(:project_phase, project: development_project) }
let!(:inactive_phase) { create(:project_phase, project: development_project, active: false) }
it "returns active phases where the user has a view_project_stages_and_gates permission" do
expect(described_class.visible(user)).to contain_exactly(phase)
end
end
@@ -69,7 +88,7 @@ RSpec.describe Project::Stage do
describe "#validate_date_range" do
it "is valid when both dates are blank" do
stage = build(:project_stage, start_date: nil, end_date: nil)
stage = build(:project_phase, start_date: nil, end_date: nil)
expect(stage).to be_valid
end
@@ -98,12 +117,6 @@ RSpec.describe Project::Stage do
expect(subject).not_to be_valid
expect(subject.errors[:date_range]).to be_empty
end
it "is invalid if type and class name do not match" do
subject.type = "Project::Gate"
expect(subject).not_to be_valid
expect(subject.errors.symbols_for(:type)).to include(:type_and_class_name_mismatch)
end
end
describe "#working_days_count" do
@@ -135,8 +148,8 @@ RSpec.describe Project::Stage do
allow(Day).to receive(:working).and_return(Day)
allow(Day).to receive(:from_range)
.with(from: subject.start_date, to: subject.end_date)
.and_return([])
.with(from: subject.start_date, to: subject.end_date)
.and_return([])
expect(subject.working_days_count).to eq(0)
@@ -1,57 +0,0 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "rails_helper"
require "support/shared/project_life_cycle_helpers"
RSpec.describe Project::StageDefinition do
it_behaves_like "a Project::LifeCycleStepDefinition event"
describe "associations" do
it "has many stages" do
expect(subject).to have_many(:stages).class_name("Project::Stage")
.with_foreign_key(:definition_id)
.inverse_of(:definition)
.dependent(:destroy)
end
end
describe "validations" do
it "is invalid if type and class name do not match" do
subject.type = "Project::GateDefinition"
expect(subject).not_to be_valid
expect(subject.errors.symbols_for(:type)).to include(:type_and_class_name_mismatch)
end
end
describe "#step_class" do
it "returns Project::Stage" do
expect(subject.step_class).to eq(Project::Stage)
end
end
end
+7 -7
View File
@@ -383,8 +383,8 @@ RSpec.describe Project do
describe "life_cycles" do
it { is_expected.to have_many(:life_cycle_steps).class_name("Project::LifeCycleStep").dependent(:destroy) }
it "has many available_life_cycle_steps" do
expect(subject).to have_many(:available_life_cycle_steps)
it "has many available_phases" do
expect(subject).to have_many(:available_phases)
.class_name("Project::LifeCycleStep")
.inverse_of(:project)
.dependent(:destroy)
@@ -392,18 +392,18 @@ RSpec.describe Project do
end
it "checks for active flag" do
expect(subject.available_life_cycle_steps.to_sql)
expect(subject.available_phases.to_sql)
.to include("\"project_life_cycle_steps\".\"active\" = TRUE")
end
it "checks for :view_project_stages_and_gates permission" do
project_condition = described_class.allowed_to(User.current, :view_project_stages_and_gates).select(:id)
expect(subject.available_life_cycle_steps.to_sql).to include(project_condition.to_sql)
expect(subject.available_phases.to_sql).to include(project_condition.to_sql)
end
it "eager loads :definition" do
expect(subject.available_life_cycle_steps.to_sql)
expect(subject.available_phases.to_sql)
.to include("LEFT OUTER JOIN \"project_life_cycle_step_definitions\" ON")
end
@@ -419,8 +419,8 @@ RSpec.describe Project do
expect(project).to be_valid
end
it "is invalid with the :saving_life_cycle_steps validation context" do
expect(project).not_to be_valid(:saving_life_cycle_steps)
it "is invalid with the :saving_phases validation context" do
expect(project).not_to be_valid(:saving_phases)
end
end
end
@@ -41,7 +41,7 @@ RSpec.describe Queries::Projects::Filters::LifeCycleGateFilter do
end
before do
allow(Project::LifeCycleStepDefinition)
allow(Project::PhaseDefinition)
.to receive(:all)
.and_return([stage, gate])
end
@@ -41,7 +41,7 @@ RSpec.describe Queries::Projects::Filters::LifeCycleStageFilter do
end
before do
allow(Project::LifeCycleStepDefinition)
allow(Project::PhaseDefinition)
.to receive(:all)
.and_return([stage, gate])
end
@@ -104,11 +104,11 @@ RSpec.describe Queries::Projects::Orders::LifeCycleStepOrder do
let(:id) { 42 }
before do
allow(Project::LifeCycleStepDefinition).to receive(:find_by).with(id: id.to_s).and_return(step_definition)
allow(Project::PhaseDefinition).to receive(:find_by).with(id: id.to_s).and_return(step_definition)
end
context "when life cycle definition exists" do
let(:step_definition) { instance_double(Project::LifeCycleStepDefinition) }
let(:step_definition) { instance_double(Project::PhaseDefinition) }
it "returns the life cycle definition" do
expect(instance.life_cycle_step_definition).to eq(step_definition)
@@ -117,7 +117,7 @@ RSpec.describe Queries::Projects::Orders::LifeCycleStepOrder do
it "memoizes the life cycle definition" do
2.times { instance.life_cycle_step_definition }
expect(Project::LifeCycleStepDefinition).to have_received(:find_by).once
expect(Project::PhaseDefinition).to have_received(:find_by).once
end
end
@@ -131,7 +131,7 @@ RSpec.describe Queries::Projects::Orders::LifeCycleStepOrder do
it "memoizes the life cycle" do
2.times { instance.life_cycle_step_definition }
expect(Project::LifeCycleStepDefinition).to have_received(:find_by).once
expect(Project::PhaseDefinition).to have_received(:find_by).once
end
end
end
@@ -61,25 +61,25 @@ RSpec.describe ProjectQuery, "results of 'Any stage or gate' filter" do
# rubocop:disable RSpec/ScatteredSetup
def self.remove_gate
before do
Project::LifeCycleStep.where(type: Project::Gate.name).destroy_all
Project::Phase.where(type: Project::Gate.name).destroy_all
end
end
def self.remove_stage
before do
Project::LifeCycleStep.where(type: Project::Stage.name).destroy_all
Project::Phase.where(type: Project::Stage.name).destroy_all
end
end
def self.disable_stage
before do
Project::LifeCycleStep.where(type: Project::Stage.name).update_all(active: false)
Project::Phase.where(type: Project::Stage.name).update_all(active: false)
end
end
def self.disable_gate
before do
Project::LifeCycleStep.where(type: Project::Gate.name).update_all(active: false)
Project::Phase.where(type: Project::Gate.name).update_all(active: false)
end
end
@@ -67,7 +67,7 @@ RSpec.describe ProjectQuery, "results of a life cycle gate filter" do
# rubocop:disable RSpec/ScatteredSetup
def self.disable_gate
before do
Project::LifeCycleStep.where(type: Project::Gate.name).update_all(active: false)
Project::Phase.where(type: Project::Gate.name).update_all(active: false)
end
end
@@ -69,7 +69,7 @@ RSpec.describe ProjectQuery, "results of a life cycle stage filter" do
# rubocop:disable RSpec/ScatteredSetup
def self.disable_stage
before do
Project::LifeCycleStep.where(type: Project::Stage.name).update_all(active: false)
Project::Phase.where(type: Project::Stage.name).update_all(active: false)
end
end
@@ -68,7 +68,7 @@ RSpec.describe BasicData::LifeCycleStepDefinitionSeeder do
end
it "creates the corresponding life cycles with the given attributes" do
expect(Project::LifeCycleStepDefinition.count).to eq(5)
expect(Project::PhaseDefinition.count).to eq(5)
expect(Project::StageDefinition.find_by(name: "Initiating")).to have_attributes(
color: have_attributes(name: "PM2 Orange")
)
@@ -87,7 +87,7 @@ RSpec.describe BasicData::LifeCycleStepDefinitionSeeder do
end
it "references the life cycles in the seed data" do
Project::LifeCycleStepDefinition.find_each do |expected_stage|
Project::PhaseDefinition.find_each do |expected_stage|
reference = :"default_life_cycle_#{expected_stage.name.downcase.gsub(/\s+/, '_')}"
expect(seed_data.find_reference(reference)).to eq(expected_stage)
end
@@ -126,7 +126,7 @@ RSpec.describe BasicData::LifeCycleStepDefinitionSeeder do
end
it "creates no life cycles" do
expect(Project::LifeCycleStepDefinition.count).to eq(0)
expect(Project::PhaseDefinition.count).to eq(0)
end
end
end
@@ -59,7 +59,7 @@ module Components
end
datepicker.open(
"input[id^='project_available_life_cycle_steps_attributes_#{step.position - 1}']"
"input[id^='project_available_phases_attributes_#{step.position - 1}']"
)
Array(value).each do |date|
@@ -104,7 +104,7 @@ module Components
expect(page).to have_field(
label,
with: value,
name: "project[available_life_cycle_steps_attributes][#{position - 1}][#{field}]"
name: "project[available_phases_attributes][#{position - 1}][#{field}]"
)
end
end
@@ -144,7 +144,7 @@ module Components
def expect_selector_for(step, selector:, text: nil, present: true)
within_async_content do
field = step.is_a?(Project::Stage) ? :date_range : :date
input_id = "#project_available_life_cycle_steps_attributes_#{step.position - 1}_#{field}"
input_id = "#project_available_phases_attributes_#{step.position - 1}_#{field}"
parent = find(input_id).ancestor("primer-datepicker-field")
if present
@@ -1,72 +0,0 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
RSpec.shared_examples_for "a Project::LifeCycleStepDefinition event" do
it "inherits from LifeCycleStepDefinition" do
expect(described_class).to be < Project::LifeCycleStepDefinition
end
describe "associations" do
it { is_expected.to have_many(:life_cycle_steps).inverse_of(:definition).dependent(:destroy) }
it { is_expected.to have_many(:projects).through(:life_cycle_steps) }
it { is_expected.to belong_to(:color).required }
end
describe "validations" do
it { is_expected.to validate_presence_of(:name) }
it "is invalid if type is not Project::StageDefinition or Project::GateDefinition" do
life_cycle = described_class.new
life_cycle.type = "InvalidType"
expect(life_cycle).not_to be_valid
expect(life_cycle.errors[:type])
.to include("must be either Project::StageDefinition or Project::GateDefinition")
end
end
end
RSpec.shared_examples_for "a Project::LifeCycleStep event" do
it "inherits from Project::LifeCycleStep" do
expect(described_class).to be < Project::LifeCycleStep
end
describe "associations" do
it { is_expected.to belong_to(:project).required }
it { is_expected.to belong_to(:definition).required }
it { is_expected.to have_many(:work_packages) }
end
describe "validations" do
it "is invalid if type is not Project::Stage or Project::Gate" do
life_cycle = described_class.new
life_cycle.type = "InvalidType"
expect(life_cycle).not_to be_valid
expect(life_cycle.errors[:type]).to include("must be either Project::Stage or Project::Gate")
end
end
end