Merge pull request #5457 from opf/fix/list_cf_export

[ci skip]
pull/5463/head
Oliver Günther 8 years ago committed by GitHub
commit 4fe4fc4d85
  1. 32
      app/controllers/time_entries/reports_controller.rb
  2. 36
      app/helpers/custom_fields_helper.rb
  3. 6
      app/models/custom_field.rb
  4. 56
      app/models/custom_value.rb
  5. 4
      app/models/custom_value/ar_object_strategy.rb
  6. 9
      app/models/custom_value/bool_strategy.rb
  7. 7
      app/models/custom_value/date_strategy.rb
  8. 7
      app/models/custom_value/float_strategy.rb
  9. 7
      app/models/custom_value/format_strategy.rb
  10. 8
      app/models/work_package/pdf_export/work_package_list_to_pdf.rb
  11. 2
      app/views/custom_fields/_tab.html.erb
  12. 1
      app/views/time_entries/reports/_report_criteria.html.erb
  13. 76
      config/initializers/custom_field_format.rb
  14. 2
      lib/custom_field_form_builder.rb
  15. 3
      lib/open_project.rb
  16. 65
      lib/open_project/custom_field_format.rb
  17. 6
      lib/open_project/journal_formatter/custom_field.rb
  18. 41
      spec/helpers/custom_fields_helper_spec.rb
  19. 2
      spec/lib/api/v3/utilities/custom_field_injector_spec.rb
  20. 66
      spec/lib/journal_formatter/custom_field_spec.rb
  21. 41
      spec/lib/redmine/custom_field_format_spec.rb
  22. 59
      spec/models/custom_value/bool_strategy_spec.rb
  23. 37
      spec/models/custom_value/date_strategy_spec.rb
  24. 43
      spec/models/custom_value/float_strategy_spec.rb
  25. 28
      spec/models/custom_value/int_strategy_spec.rb
  26. 46
      spec/models/custom_value/list_strategy_spec.rb
  27. 37
      spec/models/custom_value/string_strategy_spec.rb
  28. 51
      spec/models/custom_value/user_strategy_spec.rb
  29. 61
      spec/models/custom_value/version_strategy_spec.rb
  30. 2
      spec_legacy/unit/custom_field_user_format_spec.rb

@ -135,27 +135,39 @@ class TimeEntries::ReportsController < ApplicationController
# Add list and boolean custom fields as available criterias # Add list and boolean custom fields as available criterias
custom_fields = (@project.nil? ? WorkPackageCustomField.for_all : @project.all_work_package_custom_fields) custom_fields = (@project.nil? ? WorkPackageCustomField.for_all : @project.all_work_package_custom_fields)
custom_fields.select { |cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = { sql: "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'WorkPackage' AND c.customized_id = #{WorkPackage.table_name}.id)", if @project
format: cf.field_format, custom_fields.select { |cf| %w(list bool).include? cf.field_format }.each do |cf|
label: cf.name } @available_criterias["cf_#{cf.id}"] = { sql: "(SELECT c.value FROM #{CustomValue.table_name} c
end if @project WHERE c.custom_field_id = #{cf.id}
AND c.customized_type = 'WorkPackage'
AND c.customized_id = #{WorkPackage.table_name}.id)",
format: cf,
label: cf.name }
end
end
# Add list and boolean time entry custom fields # Add list and boolean time entry custom fields
TimeEntryCustomField.all.select { |cf| %w(list bool).include? cf.field_format }.each do |cf| TimeEntryCustomField.all.select { |cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = { sql: "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'TimeEntry' AND c.customized_id = #{TimeEntry.table_name}.id)", @available_criterias["cf_#{cf.id}"] = { sql: "(SELECT c.value FROM #{CustomValue.table_name} c
format: cf.field_format, WHERE c.custom_field_id = #{cf.id}
AND c.customized_type = 'TimeEntry'
AND c.customized_id = #{TimeEntry.table_name}.id)",
format: cf,
label: cf.name } label: cf.name }
end end
# Add list and boolean time entry activity custom fields # Add list and boolean time entry activity custom fields
TimeEntryActivityCustomField.all.select { |cf| %w(list bool).include? cf.field_format }.each do |cf| TimeEntryActivityCustomField.all.select { |cf| %w(list bool).include? cf.field_format }.each do |cf|
@available_criterias["cf_#{cf.id}"] = { sql: "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Enumeration' AND c.customized_id = #{TimeEntry.table_name}.activity_id)", @available_criterias["cf_#{cf.id}"] = { sql: "(SELECT c.value FROM #{CustomValue.table_name} c
format: cf.field_format, WHERE c.custom_field_id = #{cf.id}
AND c.customized_type = 'Enumeration'
AND c.customized_id = #{TimeEntry.table_name}.activity_id)",
format: cf,
label: cf.name } label: cf.name }
end end
call_hook(:controller_timelog_available_criterias, available_criterias: @available_criterias, project: @project) call_hook(:controller_timelog_available_criterias, available_criterias: @available_criterias, project: @project)
@available_criterias @available_criterias
end end

@ -1,4 +1,5 @@
#-- encoding: UTF-8 #-- encoding: UTF-8
#-- copyright #-- copyright
# OpenProject is a project management system. # OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF) # Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
@ -47,7 +48,7 @@ module CustomFieldsHelper
field_name = "#{name}[custom_field_values][#{custom_field.id}]" field_name = "#{name}[custom_field_values][#{custom_field.id}]"
field_id = "#{name}_custom_field_values_#{custom_field.id}" field_id = "#{name}_custom_field_values_#{custom_field.id}"
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format) field_format = OpenProject::CustomFieldFormat.find_by_name(custom_field.field_format)
tag = case field_format.try(:edit_as) tag = case field_format.try(:edit_as)
when 'date' when 'date'
@ -114,7 +115,7 @@ module CustomFieldsHelper
def custom_field_tag_for_bulk_edit(name, custom_field, project=nil) def custom_field_tag_for_bulk_edit(name, custom_field, project=nil)
field_name = "#{name}[custom_field_values][#{custom_field.id}]" field_name = "#{name}[custom_field_values][#{custom_field.id}]"
field_id = "#{name}_custom_field_values_#{custom_field.id}" field_id = "#{name}_custom_field_values_#{custom_field.id}"
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format) field_format = OpenProject::CustomFieldFormat.find_by_name(custom_field.field_format)
case field_format.try(:edit_as) case field_format.try(:edit_as)
when 'date' when 'date'
styled_text_field_tag(field_name, '', id: field_id, size: 10) + styled_text_field_tag(field_name, '', id: field_id, size: 10) +
@ -135,27 +136,32 @@ module CustomFieldsHelper
# Return a string used to display a custom value # Return a string used to display a custom value
def show_value(custom_value) def show_value(custom_value)
return '' unless custom_value return '' unless custom_value
format_value(custom_value.value, custom_value.custom_field.field_format) custom_value.formatted_value
end end
# Return a string used to display a custom value # Return a string used to display a custom value
def format_value(value, field_format) def format_value(value, custom_field)
Redmine::CustomFieldFormat.format_value(value, field_format) # Proxy custom_value = CustomValue.new(custom_field: custom_field,
value: value)
custom_value.formatted_value
end end
# Return an array of custom field formats which can be used in select_tag # Return an array of custom field formats which can be used in select_tag
def custom_field_formats_for_select(custom_field) def custom_field_formats_for_select(custom_field)
Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name) OpenProject::CustomFieldFormat
.all_for_field(custom_field)
.sort_by(&:order)
.map do |custom_field_format|
[label_for_custom_field_format(custom_field_format.name), custom_field_format.name]
end
end end
# Renders the custom_values in api views def label_for_custom_field_format(format_string)
def render_api_custom_values(custom_values, api) format = OpenProject::CustomFieldFormat.find_by_name(format_string)
api.array :custom_fields do
custom_values.each do |custom_value| if format
api.custom_field id: custom_value.custom_field_id, name: custom_value.custom_field.name do format.label.is_a?(Proc) ? format.label.call : I18n.t(format.label)
api.value custom_value.value end
end
end
end unless custom_values.empty?
end end
end end

@ -49,7 +49,7 @@ class CustomField < ActiveRecord::Base
errors.add(:name, :taken) if name.in?(taken_names) errors.add(:name, :taken) if name.in?(taken_names)
end end
validates_inclusion_of :field_format, in: Redmine::CustomFieldFormat.available_formats validates_inclusion_of :field_format, in: OpenProject::CustomFieldFormat.available_formats
validate :validate_default_value validate :validate_default_value
@ -96,6 +96,10 @@ class CustomField < ActiveRecord::Base
end end
end end
def required?
is_required?
end
def possible_values_options(obj = nil) def possible_values_options(obj = nil)
case field_format case field_format
when 'user', 'version' when 'user', 'version'

@ -1,4 +1,5 @@
#-- encoding: UTF-8 #-- encoding: UTF-8
#-- copyright #-- copyright
# OpenProject is a project management system. # OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF) # Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
@ -28,18 +29,6 @@
#++ #++
class CustomValue < ActiveRecord::Base class CustomValue < ActiveRecord::Base
FORMAT_STRATEGIES = {
'string' => CustomValue::StringStrategy,
'text' => CustomValue::StringStrategy,
'int' => CustomValue::IntStrategy,
'float' => CustomValue::FloatStrategy,
'date' => CustomValue::DateStrategy,
'bool' => CustomValue::BoolStrategy,
'user' => CustomValue::UserStrategy,
'version' => CustomValue::VersionStrategy,
'list' => CustomValue::ListStrategy
}.freeze
belongs_to :custom_field belongs_to :custom_field
belongs_to :customized, polymorphic: true belongs_to :customized, polymorphic: true
@ -48,23 +37,16 @@ class CustomValue < ActiveRecord::Base
validate :validate_type_of_value validate :validate_type_of_value
validate :validate_length_of_value validate :validate_length_of_value
# returns the value of this custom value, but converts it according to the field_format delegate :typed_value,
# of the custom field beforehand :formatted_value,
def typed_value to: :strategy
strategy.typed_value
end
def editable?
custom_field.editable?
end
def visible?
custom_field.visible?
end
def required? delegate :editable?,
custom_field.is_required? :visible?,
end :required?,
:max_length,
:min_length,
to: :custom_field
def to_s def to_s
value.to_s value.to_s
@ -79,7 +61,7 @@ class CustomValue < ActiveRecord::Base
protected protected
def validate_presence_of_required_value def validate_presence_of_required_value
errors.add(:value, :blank) if custom_field.is_required? && !strategy.value_present? errors.add(:value, :blank) if custom_field.required? && !strategy.value_present?
end end
def validate_format_of_value def validate_format_of_value
@ -98,15 +80,23 @@ class CustomValue < ActiveRecord::Base
end end
def validate_length_of_value def validate_length_of_value
if value.present? && custom_field.min_length.present? && custom_field.max_length.present? if value.present? && (min_length.present? || max_length.present?)
errors.add(:value, :too_short, count: custom_field.min_length) if custom_field.min_length > 0 and value.length < custom_field.min_length validate_min_length_of_value
errors.add(:value, :too_long, count: custom_field.max_length) if custom_field.max_length > 0 and value.length > custom_field.max_length validate_max_length_of_value
end end
end end
private private
def validate_min_length_of_value
errors.add(:value, :too_short, count: min_length) if min_length > 0 && value.length < min_length
end
def validate_max_length_of_value
errors.add(:value, :too_long, count: max_length) if max_length > 0 && value.length > max_length
end
def strategy def strategy
@strategy ||= FORMAT_STRATEGIES[custom_field.field_format].new(self) @strategy ||= OpenProject::CustomFieldFormat.find_by_name(custom_field.field_format).formatter.new(self)
end end
end end

@ -39,6 +39,10 @@ class CustomValue::ARObjectStrategy < CustomValue::FormatStrategy
end end
end end
def formatted_value
typed_value.to_s
end
def parse_value(val) def parse_value(val)
if val.is_a?(ar_class) if val.is_a?(ar_class)
self.memoized_typed_value = val self.memoized_typed_value = val

@ -1,4 +1,5 @@
#-- encoding: UTF-8 #-- encoding: UTF-8
#-- copyright #-- copyright
# OpenProject is a project management system. # OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF) # Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
@ -41,6 +42,11 @@ class CustomValue::BoolStrategy < CustomValue::FormatStrategy
ActiveRecord::Type::Boolean.new.cast(value) ActiveRecord::Type::Boolean.new.cast(value)
end end
def formatted_value
is_true = ActiveRecord::Type::Boolean.new.cast(value)
I18n.t(is_true ? :general_text_Yes : :general_text_No)
end
def parse_value(val) def parse_value(val)
parsed_val = if !present?(val) parsed_val = if !present?(val)
nil nil
@ -53,8 +59,7 @@ class CustomValue::BoolStrategy < CustomValue::FormatStrategy
super(parsed_val) super(parsed_val)
end end
def validate_type_of_value def validate_type_of_value; end
end
private private

@ -1,4 +1,5 @@
#-- encoding: UTF-8 #-- encoding: UTF-8
#-- copyright #-- copyright
# OpenProject is a project management system. # OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF) # Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
@ -34,6 +35,12 @@ class CustomValue::DateStrategy < CustomValue::FormatStrategy
end end
end end
def formatted_value
format_date(value.to_date)
rescue
value.to_s
end
def validate_type_of_value def validate_type_of_value
return nil if value.is_a? Date return nil if value.is_a? Date

@ -1,4 +1,5 @@
#-- encoding: UTF-8 #-- encoding: UTF-8
#-- copyright #-- copyright
# OpenProject is a project management system. # OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF) # Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
@ -28,12 +29,18 @@
#++ #++
class CustomValue::FloatStrategy < CustomValue::FormatStrategy class CustomValue::FloatStrategy < CustomValue::FormatStrategy
include ActionView::Helpers::NumberHelper
def typed_value def typed_value
unless value.blank? unless value.blank?
value.to_f value.to_f
end end
end end
def formatted_value
number_with_delimiter(value.to_s)
end
def validate_type_of_value def validate_type_of_value
Kernel.Float(value) Kernel.Float(value)
nil nil

@ -1,4 +1,5 @@
#-- encoding: UTF-8 #-- encoding: UTF-8
#-- copyright #-- copyright
# OpenProject is a project management system. # OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF) # Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
@ -45,6 +46,12 @@ class CustomValue::FormatStrategy
raise 'SubclassResponsibility' raise 'SubclassResponsibility'
end end
# Returns the value of the CustomValue formatted to a string
# representation.
def formatted_value
value.to_s
end
# Parses the value to # Parses the value to
# 1) have a unified representation for different inputs # 1) have a unified representation for different inputs
# 2) memoize typed values (if the subclass descides to do so # 2) memoize typed values (if the subclass descides to do so

@ -171,11 +171,11 @@ class WorkPackage::PdfExport::WorkPackageListToPdf
end end
def make_custom_field_value(work_package, column) def make_custom_field_value(work_package, column)
value = work_package values = work_package
.custom_values .custom_values
.detect { |v| v.custom_field_id == column.custom_field.id } .select { |v| v.custom_field_id == column.custom_field.id }
pdf.make_cell show_value(value), pdf.make_cell values.map(&:formatted_value).join(', '),
padding: cell_padding padding: cell_padding
end end
end end

@ -107,7 +107,7 @@ See doc/COPYRIGHT.rdoc for more details.
<% (@custom_fields_by_type[tab[:name]] || []).sort.each do |custom_field| -%> <% (@custom_fields_by_type[tab[:name]] || []).sort.each do |custom_field| -%>
<tr> <tr>
<td><%= link_to h(custom_field.name), edit_custom_field_path(custom_field), lang: custom_field.name_locale %></td> <td><%= link_to h(custom_field.name), edit_custom_field_path(custom_field), lang: custom_field.name_locale %></td>
<td><%= Redmine::CustomFieldFormat.label_for(custom_field.field_format) %></td> <td><%= label_for_custom_field_format(custom_field.field_format) %></td>
<td><%= checked_image custom_field.is_required? %></td> <td><%= checked_image custom_field.is_required? %></td>
<% if tab[:name] == 'WorkPackageCustomField' %> <% if tab[:name] == 'WorkPackageCustomField' %>
<td><%= checked_image custom_field.is_for_all? %></td> <td><%= checked_image custom_field.is_for_all? %></td>

@ -28,6 +28,7 @@ See doc/COPYRIGHT.rdoc for more details.
++#%> ++#%>
<% @hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value| %> <% @hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value| %>
<% hours_for_value = select_hours(hours, criterias[level], value) -%> <% hours_for_value = select_hours(hours, criterias[level], value) -%>
<% next if hours_for_value.empty? -%> <% next if hours_for_value.empty? -%>
<tr class="<%= 'last-level' unless criterias.length > level+1 %>"> <tr class="<%= 'last-level' unless criterias.length > level+1 %>">

@ -1,4 +1,5 @@
#-- encoding: UTF-8 #-- encoding: UTF-8
#-- copyright #-- copyright
# OpenProject is a project management system. # OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF) # Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
@ -27,38 +28,45 @@
# See doc/COPYRIGHT.rdoc for more details. # See doc/COPYRIGHT.rdoc for more details.
#++ #++
Redmine::CustomFieldFormat.map do |fields| OpenProject::CustomFieldFormat.map do |fields|
fields.register Redmine::CustomFieldFormat.new('string', fields.register OpenProject::CustomFieldFormat.new('string',
label: :label_string, label: :label_string,
order: 1) order: 1)
fields.register Redmine::CustomFieldFormat.new('text', fields.register OpenProject::CustomFieldFormat.new('text',
label: :label_text, label: :label_text,
order: 2) order: 2)
fields.register Redmine::CustomFieldFormat.new('int', fields.register OpenProject::CustomFieldFormat.new('int',
label: :label_integer, label: :label_integer,
order: 3) order: 3,
fields.register Redmine::CustomFieldFormat.new('float', formatter: CustomValue::IntStrategy)
label: :label_float, fields.register OpenProject::CustomFieldFormat.new('float',
order: 4) label: :label_float,
fields.register Redmine::CustomFieldFormat.new('list', order: 4,
label: :label_list, formatter: CustomValue::FloatStrategy)
order: 5) fields.register OpenProject::CustomFieldFormat.new('list',
fields.register Redmine::CustomFieldFormat.new('date', label: :label_list,
label: :label_date, order: 5,
order: 6) formatter: CustomValue::ListStrategy)
fields.register Redmine::CustomFieldFormat.new('bool', fields.register OpenProject::CustomFieldFormat.new('date',
label: :label_boolean, label: :label_date,
order: 7) order: 6,
fields.register Redmine::CustomFieldFormat.new('user', formatter: CustomValue::DateStrategy)
label: Proc.new { User.model_name.human }, fields.register OpenProject::CustomFieldFormat.new('bool',
only: %w(WorkPackage TimeEntry label: :label_boolean,
Version Project), order: 7,
edit_as: 'list', formatter: CustomValue::BoolStrategy)
order: 8) fields.register OpenProject::CustomFieldFormat.new('user',
fields.register Redmine::CustomFieldFormat.new('version', label: Proc.new { User.model_name.human },
label: Proc.new { Version.model_name.human }, only: %w(WorkPackage TimeEntry
only: %w(WorkPackage TimeEntry Version Project),
Version Project), edit_as: 'list',
edit_as: 'list', order: 8,
order: 9) formatter: CustomValue::UserStrategy)
fields.register OpenProject::CustomFieldFormat.new('version',
label: Proc.new { Version.model_name.human },
only: %w(WorkPackage TimeEntry
Version Project),
edit_as: 'list',
order: 9,
formatter: CustomValue::VersionStrategy)
end end

@ -54,7 +54,7 @@ class CustomFieldFormBuilder < TabularFormBuilder
name: custom_field_field_name, name: custom_field_field_name,
id: custom_field_field_id) id: custom_field_field_id)
field_format = Redmine::CustomFieldFormat.find_by_name(object.custom_field.field_format) field_format = OpenProject::CustomFieldFormat.find_by_name(object.custom_field.field_format)
case field_format.try(:edit_as) case field_format.try(:edit_as)
when 'date' when 'date'

@ -1,4 +1,5 @@
#-- encoding: UTF-8 #-- encoding: UTF-8
#-- copyright #-- copyright
# OpenProject is a project management system. # OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF) # Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
@ -30,7 +31,7 @@
require 'redmine/menu_manager' require 'redmine/menu_manager'
require 'redmine/activity' require 'redmine/activity'
require 'redmine/search' require 'redmine/search'
require 'redmine/custom_field_format' require 'open_project/custom_field_format'
require 'redmine/mime_type' require 'redmine/mime_type'
require 'redmine/core_ext' require 'redmine/core_ext'
require 'open_project/design' require 'open_project/design'

@ -1,4 +1,5 @@
#-- encoding: UTF-8 #-- encoding: UTF-8
#-- copyright #-- copyright
# OpenProject is a project management system. # OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF) # Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
@ -27,50 +28,22 @@
# See doc/COPYRIGHT.rdoc for more details. # See doc/COPYRIGHT.rdoc for more details.
#++ #++
module Redmine module OpenProject
class CustomFieldFormat class CustomFieldFormat
include Redmine::I18n include Redmine::I18n
include ActionView::Helpers::NumberHelper
cattr_accessor :available cattr_accessor :available
@@available = {} @@available = {}
attr_accessor :name, :order, :label, :edit_as, :class_names attr_accessor :name, :order, :label, :edit_as, :class_names, :formatter
def initialize(name, label:, order:, edit_as: name, only: nil) def initialize(name, label:, order:, edit_as: name, only: nil, formatter: CustomValue::StringStrategy)
self.name = name self.name = name
self.label = label self.label = label
self.order = order self.order = order
self.edit_as = edit_as self.edit_as = edit_as
self.class_names = only self.class_names = only
end self.formatter = formatter
def format(value)
send "format_as_#{name}", value
end
def format_as_date(value)
format_date(value.to_date); rescue; value end
def format_as_bool(value)
is_true = ActiveRecord::Type::Boolean.new.cast(value)
l(is_true ? :general_text_Yes : :general_text_No)
end
['string', 'text', 'int', 'list'].each do |name|
define_method("format_as_#{name}") {|value|
return value.to_s
}
end
def format_as_float(value)
number_with_delimiter(value.to_s)
end
['user', 'version'].each do |name|
define_method("format_as_#{name}") {|value|
return value.blank? ? '' : name.classify.constantize.find_by(id: value.to_i).to_s
}
end end
class << self class << self
@ -91,30 +64,12 @@ module Redmine
@@available[name.to_s] @@available[name.to_s]
end end
def label_for(name) def all_for_field(custom_field)
format = @@available[name.to_s] class_name = custom_field.class.customized_class.name
format.label.is_a?(Proc) ? format.label.call : l(format.label) if format
end
# Return an array of custom field formats which can be used in select_tag
def as_select(class_name = nil)
fields = @@available.values
fields = fields.select { |field| field.class_names.nil? || field.class_names.include?(class_name) }
fields.sort {|a, b|
a.order <=> b.order
}.map {|custom_field_format|
[label_for(custom_field_format.name), custom_field_format.name]
}
end
def format_value(value, field_format)
return '' unless value && !value.empty?
if format_type = find_by_name(field_format) available
format_type.format(value) .values
else .select { |field| field.class_names.nil? || field.class_names.include?(class_name) }
value
end
end end
end end
end end

@ -71,8 +71,8 @@ class OpenProject::JournalFormatter::CustomField < ::JournalFormatter::Base
end end
def format_single(custom_field, values) def format_single(custom_field, values)
old_value = format_value(values.first, custom_field.field_format) if values.first old_value = format_value(values.first, custom_field) if values.first
value = format_value(values.last, custom_field.field_format) if values.last value = format_value(values.last, custom_field) if values.last
[old_value, value] [old_value, value]
end end
@ -89,7 +89,7 @@ class OpenProject::JournalFormatter::CustomField < ::JournalFormatter::Base
String(values) String(values)
.split(",") .split(",")
.map(&:strip) .map(&:strip)
.map { |value| format_value value, custom_field.field_format } .map { |value| format_value value, custom_field }
.join(", ") .join(", ")
.presence .presence
end end

@ -1,41 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
#
# 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-2017 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 doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe CustomFieldsHelper, type: :helper do
include OpenProject::FormTagHelper
include CustomFieldsHelper
include Redmine::I18n
it 'should format boolean value' do
I18n.locale = 'en'
expect(format_value('1', 'bool')).to eq 'Yes'
expect(format_value('0', 'bool')).to eq 'No'
end
end

@ -42,7 +42,7 @@ describe ::API::V3::Utilities::CustomFieldInjector do
describe 'TYPE_MAP' do describe 'TYPE_MAP' do
it 'supports all available formats' do it 'supports all available formats' do
Redmine::CustomFieldFormat.available_formats.each do |format| OpenProject::CustomFieldFormat.available_formats.each do |format|
expect(described_class::TYPE_MAP[format]).not_to be_nil expect(described_class::TYPE_MAP[format]).not_to be_nil
end end
end end

@ -45,41 +45,41 @@ describe OpenProject::JournalFormatter::CustomField do
describe '#render' do describe '#render' do
describe 'WITH the first value beeing nil, and the second a valid value as string' do describe 'WITH the first value beeing nil, and the second a valid value as string' do
let(:values) { [nil, '1'] } let(:values) { [nil, '1'] }
let(:formatted_value) { format_value(values.last, custom_field.field_format) } let(:formatted_value) { format_value(values.last, custom_field) }
let(:expected) { let(:expected) do
I18n.t(:text_journal_set_to, I18n.t(:text_journal_set_to,
label: "<strong>#{custom_field.name}</strong>", label: "<strong>#{custom_field.name}</strong>",
value: "<i title=\"#{formatted_value}\">#{formatted_value}</i>") value: "<i title=\"#{formatted_value}\">#{formatted_value}</i>")
} end
it { expect(instance.render(key, values)).to eq(expected) } it { expect(instance.render(key, values)).to eq(expected) }
end end
describe 'WITH the first value beeing a valid value as a string, and the second beeing a valid value as a string' do describe 'WITH the first value beeing a valid value as a string, and the second beeing a valid value as a string' do
let(:values) { ['0', '1'] } let(:values) { ['0', '1'] }
let(:old_formatted_value) { format_value(values.first, custom_field.field_format) } let(:old_formatted_value) { format_value(values.first, custom_field) }
let(:new_formatted_value) { format_value(values.last, custom_field.field_format) } let(:new_formatted_value) { format_value(values.last, custom_field) }
let(:expected) { let(:expected) do
I18n.t(:text_journal_changed, I18n.t(:text_journal_changed,
label: "<strong>#{custom_field.name}</strong>", label: "<strong>#{custom_field.name}</strong>",
old: "<i title=\"#{old_formatted_value}\">#{old_formatted_value}</i>", old: "<i title=\"#{old_formatted_value}\">#{old_formatted_value}</i>",
new: "<i title=\"#{new_formatted_value}\">#{new_formatted_value}</i>") new: "<i title=\"#{new_formatted_value}\">#{new_formatted_value}</i>")
} end
it { expect(instance.render(key, values)).to eq(expected) } it { expect(instance.render(key, values)).to eq(expected) }
end end
describe 'WITH the first value beeing a valid value as a string, and the second beeing nil' do describe 'WITH the first value beeing a valid value as a string, and the second beeing nil' do
let(:values) { ['0', nil] } let(:values) { ['0', nil] }
let(:formatted_value) { format_value(values.first, custom_field.field_format) } let(:formatted_value) { format_value(values.first, custom_field) }
let(:expected) { let(:expected) do
I18n.t(:text_journal_deleted, I18n.t(:text_journal_deleted,
label: "<strong>#{custom_field.name}</strong>", label: "<strong>#{custom_field.name}</strong>",
old: "<strike><i title=\"#{formatted_value}\">#{formatted_value}</i></strike>") old: "<strike><i title=\"#{formatted_value}\">#{formatted_value}</i></strike>")
} end
it { expect(instance.render(key, values)).to eq(expected) } it { expect(instance.render(key, values)).to eq(expected) }
end end
@ -88,11 +88,11 @@ describe OpenProject::JournalFormatter::CustomField do
WITH no html requested" do WITH no html requested" do
let(:values) { [nil, '1'] } let(:values) { [nil, '1'] }
let(:expected) { let(:expected) do
I18n.t(:text_journal_set_to, I18n.t(:text_journal_set_to,
label: "#{custom_field.name}", label: custom_field.name,
value: "#{ format_value(values.last, custom_field.field_format) }") value: format_value(values.last, custom_field))
} end
it { expect(instance.render(key, values, no_html: true)).to eq(expected) } it { expect(instance.render(key, values, no_html: true)).to eq(expected) }
end end
@ -101,12 +101,12 @@ describe OpenProject::JournalFormatter::CustomField do
WITH no html requested" do WITH no html requested" do
let(:values) { ['0', '1'] } let(:values) { ['0', '1'] }
let(:expected) { let(:expected) do
I18n.t(:text_journal_changed, I18n.t(:text_journal_changed,
label: "#{custom_field.name}", label: custom_field.name,
old: "#{ format_value(values.first, custom_field.field_format) }", old: format_value(values.first, custom_field),
new: "#{ format_value(values.last, custom_field.field_format) }") new: format_value(values.last, custom_field))
} end
it { expect(instance.render(key, values, no_html: true)).to eq(expected) } it { expect(instance.render(key, values, no_html: true)).to eq(expected) }
end end
@ -115,11 +115,11 @@ describe OpenProject::JournalFormatter::CustomField do
WITH no html requested" do WITH no html requested" do
let(:values) { ['0', nil] } let(:values) { ['0', nil] }
let(:expected) { let(:expected) do
I18n.t(:text_journal_deleted, I18n.t(:text_journal_deleted,
label: "#{custom_field.name}", label: custom_field.name,
old: "#{ format_value(values.first, custom_field.field_format) }") old: format_value(values.first, custom_field))
} end
it { expect(instance.render(key, values, no_html: true)).to eq(expected) } it { expect(instance.render(key, values, no_html: true)).to eq(expected) }
end end
@ -129,11 +129,11 @@ describe OpenProject::JournalFormatter::CustomField do
let(:values) { [nil, '1'] } let(:values) { [nil, '1'] }
let(:key) { 'custom_values0' } let(:key) { 'custom_values0' }
let(:expected) { let(:expected) do
I18n.t(:text_journal_set_to, I18n.t(:text_journal_set_to,
label: "<strong>#{I18n.t(:label_deleted_custom_field)}</strong>", label: "<strong>#{I18n.t(:label_deleted_custom_field)}</strong>",
value: "<i title=\"#{values.last}\">#{ values.last }</i>") value: "<i title=\"#{values.last}\">#{values.last}</i>")
} end
it { expect(instance.render(key, values)).to eq(expected) } it { expect(instance.render(key, values)).to eq(expected) }
end end
@ -143,12 +143,12 @@ describe OpenProject::JournalFormatter::CustomField do
let(:values) { ['0', '1'] } let(:values) { ['0', '1'] }
let(:key) { 'custom_values0' } let(:key) { 'custom_values0' }
let(:expected) { let(:expected) do
I18n.t(:text_journal_changed, I18n.t(:text_journal_changed,
label: "<strong>#{I18n.t(:label_deleted_custom_field)}</strong>", label: "<strong>#{I18n.t(:label_deleted_custom_field)}</strong>",
old: "<i title=\"#{values.first}\">#{ values.first }</i>", old: "<i title=\"#{values.first}\">#{values.first}</i>",
new: "<i title=\"#{values.last}\">#{ values.last }</i>") new: "<i title=\"#{values.last}\">#{values.last}</i>")
} end
it { expect(instance.render(key, values)).to eq(expected) } it { expect(instance.render(key, values)).to eq(expected) }
end end
@ -158,11 +158,11 @@ describe OpenProject::JournalFormatter::CustomField do
let(:values) { ['0', nil] } let(:values) { ['0', nil] }
let(:key) { 'custom_values0' } let(:key) { 'custom_values0' }
let(:expected) { let(:expected) do
I18n.t(:text_journal_deleted, I18n.t(:text_journal_deleted,
label: "<strong>#{I18n.t(:label_deleted_custom_field)}</strong>", label: "<strong>#{I18n.t(:label_deleted_custom_field)}</strong>",
old: "<strike><i title=\"#{values.first}\">#{ values.first }</i></strike>") old: "<strike><i title=\"#{values.first}\">#{values.first}</i></strike>")
} end
it { expect(instance.render(key, values)).to eq(expected) } it { expect(instance.render(key, values)).to eq(expected) }
end end

@ -1,41 +0,0 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
#
# 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-2017 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 doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe Redmine::CustomFieldFormat do
let(:instance) { described_class.new 'test', label: 'blubs', order: '1' }
describe '#format_value' do
it 'returns a localized float' do
I18n.with_locale(:de) do
expect(instance.format_as_float('5.67')).to eql '5,67'
end
end
end
end

@ -29,6 +29,7 @@
require 'spec_helper' require 'spec_helper'
describe CustomValue::BoolStrategy do describe CustomValue::BoolStrategy do
let(:instance) { described_class.new(custom_value) }
let(:custom_value) do let(:custom_value) do
double('CustomValue', double('CustomValue',
value: value) value: value)
@ -64,7 +65,7 @@ describe CustomValue::BoolStrategy do
end end
describe '#typed_value' do describe '#typed_value' do
subject { described_class.new(custom_value).typed_value } subject { instance.typed_value }
context 'value corresponds to true' do context 'value corresponds to true' do
let(:value) { '1' } let(:value) { '1' }
@ -97,8 +98,60 @@ describe CustomValue::BoolStrategy do
end end
end end
describe '#formatted_value' do
subject { instance.formatted_value }
context 'value is present string' do
let(:value) { '1' }
it 'is the true string' do
is_expected.to eql I18n.t(:general_text_Yes)
end
end
context 'value is zero string' do
let(:value) { '0' }
it 'is the false string' do
is_expected.to eql I18n.t(:general_text_No)
end
end
context 'value is true' do
let(:value) { true }
it 'is the true string' do
is_expected.to eql I18n.t(:general_text_Yes)
end
end
context 'value is false' do
let(:value) { false }
it 'is the false string' do
is_expected.to eql I18n.t(:general_text_No)
end
end
context 'value is nil' do
let(:value) { nil }
it 'is the false string' do
is_expected.to eql I18n.t(:general_text_No)
end
end
context 'value is blank' do
let(:value) { '' }
it 'is the false string' do
is_expected.to eql I18n.t(:general_text_No)
end
end
end
describe '#validate_type_of_value' do describe '#validate_type_of_value' do
subject { described_class.new(custom_value).validate_type_of_value } subject { instance.validate_type_of_value }
context 'value corresponds to true' do context 'value corresponds to true' do
let(:value) { '1' } let(:value) { '1' }
@ -130,7 +183,7 @@ describe CustomValue::BoolStrategy do
end end
describe '#parse_value' do describe '#parse_value' do
subject { described_class.new(custom_value).parse_value(value) } subject { instance.parse_value(value) }
ActiveRecord::Type::Boolean::FALSE_VALUES.each do |falsey_value| ActiveRecord::Type::Boolean::FALSE_VALUES.each do |falsey_value|
context "for #{falsey_value}" do context "for #{falsey_value}" do

@ -29,13 +29,14 @@
require 'spec_helper' require 'spec_helper'
describe CustomValue::DateStrategy do describe CustomValue::DateStrategy do
let(:custom_value) { let(:instance) { described_class.new(custom_value) }
let(:custom_value) do
double('CustomValue', double('CustomValue',
value: value) value: value)
} end
describe '#typed_value' do describe '#typed_value' do
subject { described_class.new(custom_value).typed_value } subject { instance.typed_value }
context 'value is some date string' do context 'value is some date string' do
let(:value) { '2015-01-03' } let(:value) { '2015-01-03' }
@ -53,8 +54,36 @@ describe CustomValue::DateStrategy do
end end
end end
describe '#formatted_value' do
subject { instance.formatted_value }
context 'value is some date string' do
let(:value) { '2015-01-03' }
it 'is the date' do
is_expected.to eql value
end
end
context 'value is blank' do
let(:value) { '' }
it 'is a blank string' do
is_expected.to eql value
end
end
context 'value is nil' do
let(:value) { nil }
it 'is a blank string' do
is_expected.to eql ''
end
end
end
describe '#validate_type_of_value' do describe '#validate_type_of_value' do
subject { described_class.new(custom_value).validate_type_of_value } subject { instance.validate_type_of_value }
context 'value is valid date string' do context 'value is valid date string' do
let(:value) { '2015-01-03' } let(:value) { '2015-01-03' }

@ -29,13 +29,14 @@
require 'spec_helper' require 'spec_helper'
describe CustomValue::FloatStrategy do describe CustomValue::FloatStrategy do
let(:custom_value) { let(:instance) { described_class.new(custom_value) }
let(:custom_value) do
double('CustomValue', double('CustomValue',
value: value) value: value)
} end
describe '#typed_value' do describe '#typed_value' do
subject { described_class.new(custom_value).typed_value } subject { instance.typed_value }
context 'value is some float string' do context 'value is some float string' do
let(:value) { '3.14' } let(:value) { '3.14' }
@ -53,8 +54,42 @@ describe CustomValue::FloatStrategy do
end end
end end
describe '#formatted_value' do
subject { instance.formatted_value }
context 'value is some float string' do
let(:value) { '3.14' }
it 'is the float string' do
is_expected.to eql value
end
it 'is localized' do
I18n.with_locale(:de) do
is_expected.to eql '3,14'
end
end
end
context 'value is blank' do
let(:value) { '' }
it 'is a blank string' do
is_expected.to eql value
end
end
context 'value is nil' do
let(:value) { nil }
it 'is a blank string' do
is_expected.to eql ''
end
end
end
describe '#validate_type_of_value' do describe '#validate_type_of_value' do
subject { described_class.new(custom_value).validate_type_of_value } subject { instance.validate_type_of_value }
context 'value is float string in decimal notation' do context 'value is float string in decimal notation' do
let(:value) { '3.14' } let(:value) { '3.14' }

@ -29,13 +29,14 @@
require 'spec_helper' require 'spec_helper'
describe CustomValue::IntStrategy do describe CustomValue::IntStrategy do
let(:custom_value) { let(:instance) { described_class.new(custom_value) }
let(:custom_value) do
double('CustomValue', double('CustomValue',
value: value) value: value)
} end
describe '#typed_value' do describe '#typed_value' do
subject { described_class.new(custom_value).typed_value } subject { instance.typed_value }
context 'value is some float string' do context 'value is some float string' do
let(:value) { '10' } let(:value) { '10' }
@ -53,8 +54,27 @@ describe CustomValue::IntStrategy do
end end
end end
describe '#formatted_value' do
subject { instance.typed_value }
context 'value is some int string' do
let(:value) { '10' }
it { is_expected.to eql(10) }
end
context 'value is blank' do
let(:value) { '' }
it { is_expected.to be_nil }
end
context 'value is nil' do
let(:value) { nil }
it { is_expected.to be_nil }
end
end
describe '#validate_type_of_value' do describe '#validate_type_of_value' do
subject { described_class.new(custom_value).validate_type_of_value } subject { instance.validate_type_of_value }
context 'value is positive int string' do context 'value is positive int string' do
let(:value) { '10' } let(:value) { '10' }

@ -29,6 +29,7 @@
require 'spec_helper' require 'spec_helper'
describe CustomValue::ListStrategy do describe CustomValue::ListStrategy do
let(:instance) { described_class.new(custom_value) }
let(:custom_field) { FactoryGirl.create :list_wp_custom_field } let(:custom_field) { FactoryGirl.create :list_wp_custom_field }
let(:custom_value) do let(:custom_value) do
double("CustomField", value: value, custom_field: custom_field, customized: customized) double("CustomField", value: value, custom_field: custom_field, customized: customized)
@ -37,7 +38,7 @@ describe CustomValue::ListStrategy do
let(:customized) { double('customized') } let(:customized) { double('customized') }
describe '#parse_value/#typed_value' do describe '#parse_value/#typed_value' do
subject { described_class.new(custom_value) } subject { instance }
context 'with a CustomOption' do context 'with a CustomOption' do
let(:value) { custom_field.custom_options.first } let(:value) { custom_field.custom_options.first }
@ -89,8 +90,49 @@ describe CustomValue::ListStrategy do
end end
end end
describe '#formatted_value' do
subject { instance.formatted_value }
context 'with a CustomOption' do
let(:value) { custom_field.custom_options.first }
it 'is the custom option to_s (without db access)' do
instance.parse_value(value)
expect(CustomOption)
.to_not receive(:where)
expect(subject).to eql value.to_s
end
end
context 'with an id string' do
let(:value) { custom_field.custom_options.first.id.to_s }
it 'is the custom option to_s (with db access)' do
expect(subject).to eql custom_field.custom_options.first.to_s
end
end
context 'value is blank' do
let(:value) { '' }
it 'is blank' do
expect(subject).to eql value
end
end
context 'value is nil' do
let(:value) { nil }
it 'is blank' do
expect(subject).to eql ''
end
end
end
describe '#validate_type_of_value' do describe '#validate_type_of_value' do
subject { described_class.new(custom_value).validate_type_of_value } subject { instance.validate_type_of_value }
context 'value is included' do context 'value is included' do
let(:value) { custom_field.custom_options.first.id.to_s } let(:value) { custom_field.custom_options.first.id.to_s }

@ -29,13 +29,14 @@
require 'spec_helper' require 'spec_helper'
describe CustomValue::StringStrategy do describe CustomValue::StringStrategy do
let(:custom_value) { let(:instance) { described_class.new(custom_value) }
let(:custom_value) do
double('CustomValue', double('CustomValue',
value: value) value: value)
} end
describe '#typed_value' do describe '#typed_value' do
subject { described_class.new(custom_value).typed_value } subject { instance.typed_value }
context 'value is some string' do context 'value is some string' do
let(:value) { 'foo bar!' } let(:value) { 'foo bar!' }
@ -53,8 +54,36 @@ describe CustomValue::StringStrategy do
end end
end end
describe '#formatted_value' do
subject { instance.formatted_value }
context 'value is some string' do
let(:value) { 'foo bar!' }
it 'is the string' do
is_expected.to eql value
end
end
context 'value is blank' do
let(:value) { '' }
it 'is a blank string' do
is_expected.to eql value
end
end
context 'value is nil' do
let(:value) { nil }
it 'is a blank string' do
is_expected.to eql ''
end
end
end
describe '#validate_type_of_value' do describe '#validate_type_of_value' do
subject { described_class.new(custom_value).validate_type_of_value } subject { instance.validate_type_of_value }
context 'value is some string' do context 'value is some string' do
let(:value) { 'foo bar!' } let(:value) { 'foo bar!' }

@ -29,6 +29,7 @@
require 'spec_helper' require 'spec_helper'
describe CustomValue::UserStrategy do describe CustomValue::UserStrategy do
let(:instance) { described_class.new(custom_value) }
let(:custom_value) do let(:custom_value) do
double('CustomValue', double('CustomValue',
value: value, value: value,
@ -40,7 +41,7 @@ describe CustomValue::UserStrategy do
let(:user) { FactoryGirl.build_stubbed(:user) } let(:user) { FactoryGirl.build_stubbed(:user) }
describe '#parse_value/#typed_value' do describe '#parse_value/#typed_value' do
subject { described_class.new(custom_value) } subject { instance }
context 'with a user' do context 'with a user' do
let(:value) { user } let(:value) { user }
@ -97,8 +98,54 @@ describe CustomValue::UserStrategy do
end end
end end
describe '#formatted_value' do
subject { instance.formatted_value }
context 'with a User' do
let(:value) { user }
it 'is the user to_s (without db access)' do
instance.parse_value(value)
expect(User)
.to_not receive(:find_by)
expect(subject).to eql value.to_s
end
end
context 'with an id string' do
let(:value) { user.id.to_s }
it 'is the user to_s (with db access)' do
allow(User)
.to receive(:find_by)
.with(id: user.id.to_s)
.and_return(user)
expect(subject).to eql user.to_s
end
end
context 'value is blank' do
let(:value) { '' }
it 'is blank and does not look for the user' do
expect(subject).to eql ''
end
end
context 'value is nil' do
let(:value) { nil }
it 'is blank and does not look for the user' do
expect(subject).to eql ''
end
end
end
describe '#validate_type_of_value' do describe '#validate_type_of_value' do
subject { described_class.new(custom_value).validate_type_of_value } subject { instance.validate_type_of_value }
let(:allowed_ids) { %w(12 13) } let(:allowed_ids) { %w(12 13) }
before do before do

@ -29,18 +29,19 @@
require 'spec_helper' require 'spec_helper'
describe CustomValue::VersionStrategy do describe CustomValue::VersionStrategy do
let(:custom_value) { let(:instance) { described_class.new(custom_value) }
let(:custom_value) do
double('CustomValue', double('CustomValue',
value: value, value: value,
custom_field: custom_field, custom_field: custom_field,
customized: customized) customized: customized)
} end
let(:customized) { double('customized') } let(:customized) { double('customized') }
let(:custom_field) { FactoryGirl.build(:custom_field) } let(:custom_field) { FactoryGirl.build(:custom_field) }
let(:version) { FactoryGirl.build_stubbed(:version) } let(:version) { FactoryGirl.build_stubbed(:version) }
describe '#parse_value/#typed_value' do describe '#parse_value/#typed_value' do
subject { described_class.new(custom_value) } subject { instance }
context 'with a version' do context 'with a version' do
let(:value) { version } let(:value) { version }
@ -97,8 +98,60 @@ describe CustomValue::VersionStrategy do
end end
end end
describe '#formatted_value' do
subject { instance.formatted_value }
context 'with a version' do
let(:value) { version }
it 'is the version to_s (without db access)' do
expect(Version)
.to_not receive(:find_by)
instance.parse_value(value)
is_expected.to eql value.to_s
end
end
context 'with an id string' do
let(:value) { version.id.to_s }
it 'is the version to_s (with db access)' do
allow(Version)
.to receive(:find_by)
.with(id: version.id.to_s)
.and_return(version)
is_expected.to eql version.to_s
end
end
context 'value is blank' do
let(:value) { '' }
it 'is blank and does not look for the version' do
expect(Version)
.to_not receive(:find_by)
is_expected.to eql ''
end
end
context 'value is nil' do
let(:value) { nil }
it 'is blank and does not look for the version' do
expect(Version)
.to_not receive(:find_by)
is_expected.to eql ''
end
end
end
describe '#validate_type_of_value' do describe '#validate_type_of_value' do
subject { described_class.new(custom_value).validate_type_of_value } subject { instance.validate_type_of_value }
let(:allowed_ids) { %w(12 13) } let(:allowed_ids) { %w(12 13) }
before do before do

@ -28,7 +28,7 @@
#++ #++
require 'legacy_spec_helper' require 'legacy_spec_helper'
describe 'CustomFieldFormat' do # TODO: what is this? describe 'UserCustomField' do
before do before do
@project = FactoryGirl.create :valid_project @project = FactoryGirl.create :valid_project
role = FactoryGirl.create :role, permissions: [:view_work_packages, :edit_work_packages] role = FactoryGirl.create :role, permissions: [:view_work_packages, :edit_work_packages]

Loading…
Cancel
Save