##// END OF EJS Templates
Extracts custom field values validation from CustomValue so that they can be validated globally from the customized object (#1189)....
Jean-Philippe Lang -
r8597:83e7ee6729cd
parent child
Show More
@@ -0,0 +1,50
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 class CustomFieldValue
19 attr_accessor :custom_field, :customized, :value
20
21 def custom_field_id
22 custom_field.id
23 end
24
25 def true?
26 self.value == '1'
27 end
28
29 def editable?
30 custom_field.editable?
31 end
32
33 def visible?
34 custom_field.visible?
35 end
36
37 def required?
38 custom_field.is_required?
39 end
40
41 def to_s
42 value.to_s
43 end
44
45 def validate_value
46 custom_field.validate_field_value(value).each do |message|
47 customized.errors.add(:base, custom_field.name + ' ' + message)
48 end
49 end
50 end
@@ -1,120 +1,119
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2012 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 module CustomFieldsHelper
20 module CustomFieldsHelper
21
21
22 def custom_fields_tabs
22 def custom_fields_tabs
23 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
23 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
24 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
24 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
25 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
25 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
26 {:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
26 {:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
27 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
27 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
28 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
28 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
29 {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
29 {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
30 {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
30 {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
31 {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
31 {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
32 ]
32 ]
33 end
33 end
34
34
35 # Return custom field html tag corresponding to its format
35 # Return custom field html tag corresponding to its format
36 def custom_field_tag(name, custom_value)
36 def custom_field_tag(name, custom_value)
37 custom_field = custom_value.custom_field
37 custom_field = custom_value.custom_field
38 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
38 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
39 field_id = "#{name}_custom_field_values_#{custom_field.id}"
39 field_id = "#{name}_custom_field_values_#{custom_field.id}"
40
40
41 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
41 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
42 case field_format.try(:edit_as)
42 case field_format.try(:edit_as)
43 when "date"
43 when "date"
44 text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
44 text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
45 calendar_for(field_id)
45 calendar_for(field_id)
46 when "text"
46 when "text"
47 text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
47 text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
48 when "bool"
48 when "bool"
49 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
49 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
50 when "list"
50 when "list"
51 blank_option = custom_field.is_required? ?
51 blank_option = custom_field.is_required? ?
52 (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
52 (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
53 '<option></option>'
53 '<option></option>'
54 select_tag(field_name, blank_option.html_safe + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id)
54 select_tag(field_name, blank_option.html_safe + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id)
55 else
55 else
56 text_field_tag(field_name, custom_value.value, :id => field_id)
56 text_field_tag(field_name, custom_value.value, :id => field_id)
57 end
57 end
58 end
58 end
59
59
60 # Return custom field label tag
60 # Return custom field label tag
61 def custom_field_label_tag(name, custom_value)
61 def custom_field_label_tag(name, custom_value)
62 content_tag "label", h(custom_value.custom_field.name) +
62 content_tag "label", h(custom_value.custom_field.name) +
63 (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
63 (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
64 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}",
64 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
65 :class => (custom_value.errors.empty? ? nil : "error" )
66 end
65 end
67
66
68 # Return custom field tag with its label tag
67 # Return custom field tag with its label tag
69 def custom_field_tag_with_label(name, custom_value)
68 def custom_field_tag_with_label(name, custom_value)
70 custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
69 custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
71 end
70 end
72
71
73 def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
72 def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
74 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
73 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
75 field_id = "#{name}_custom_field_values_#{custom_field.id}"
74 field_id = "#{name}_custom_field_values_#{custom_field.id}"
76 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
75 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
77 case field_format.try(:edit_as)
76 case field_format.try(:edit_as)
78 when "date"
77 when "date"
79 text_field_tag(field_name, '', :id => field_id, :size => 10) +
78 text_field_tag(field_name, '', :id => field_id, :size => 10) +
80 calendar_for(field_id)
79 calendar_for(field_id)
81 when "text"
80 when "text"
82 text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
81 text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
83 when "bool"
82 when "bool"
84 select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
83 select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
85 [l(:general_text_yes), '1'],
84 [l(:general_text_yes), '1'],
86 [l(:general_text_no), '0']]), :id => field_id)
85 [l(:general_text_no), '0']]), :id => field_id)
87 when "list"
86 when "list"
88 select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values_options(projects)), :id => field_id)
87 select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values_options(projects)), :id => field_id)
89 else
88 else
90 text_field_tag(field_name, '', :id => field_id)
89 text_field_tag(field_name, '', :id => field_id)
91 end
90 end
92 end
91 end
93
92
94 # Return a string used to display a custom value
93 # Return a string used to display a custom value
95 def show_value(custom_value)
94 def show_value(custom_value)
96 return "" unless custom_value
95 return "" unless custom_value
97 format_value(custom_value.value, custom_value.custom_field.field_format)
96 format_value(custom_value.value, custom_value.custom_field.field_format)
98 end
97 end
99
98
100 # Return a string used to display a custom value
99 # Return a string used to display a custom value
101 def format_value(value, field_format)
100 def format_value(value, field_format)
102 Redmine::CustomFieldFormat.format_value(value, field_format) # Proxy
101 Redmine::CustomFieldFormat.format_value(value, field_format) # Proxy
103 end
102 end
104
103
105 # Return an array of custom field formats which can be used in select_tag
104 # Return an array of custom field formats which can be used in select_tag
106 def custom_field_formats_for_select(custom_field)
105 def custom_field_formats_for_select(custom_field)
107 Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
106 Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
108 end
107 end
109
108
110 # Renders the custom_values in api views
109 # Renders the custom_values in api views
111 def render_api_custom_values(custom_values, api)
110 def render_api_custom_values(custom_values, api)
112 api.array :custom_fields do
111 api.array :custom_fields do
113 custom_values.each do |custom_value|
112 custom_values.each do |custom_value|
114 api.custom_field :id => custom_value.custom_field_id, :name => custom_value.custom_field.name do
113 api.custom_field :id => custom_value.custom_field_id, :name => custom_value.custom_field.name do
115 api.value custom_value.value
114 api.value custom_value.value
116 end
115 end
117 end
116 end
118 end unless custom_values.empty?
117 end unless custom_values.empty?
119 end
118 end
120 end
119 end
@@ -1,164 +1,204
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class CustomField < ActiveRecord::Base
18 class CustomField < ActiveRecord::Base
19 include Redmine::SubclassFactory
19 include Redmine::SubclassFactory
20
20
21 has_many :custom_values, :dependent => :delete_all
21 has_many :custom_values, :dependent => :delete_all
22 acts_as_list :scope => 'type = \'#{self.class}\''
22 acts_as_list :scope => 'type = \'#{self.class}\''
23 serialize :possible_values
23 serialize :possible_values
24
24
25 validates_presence_of :name, :field_format
25 validates_presence_of :name, :field_format
26 validates_uniqueness_of :name, :scope => :type
26 validates_uniqueness_of :name, :scope => :type
27 validates_length_of :name, :maximum => 30
27 validates_length_of :name, :maximum => 30
28 validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
28 validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
29
29
30 validate :validate_values
30 validate :validate_custom_field
31 before_validation :set_searchable
31 before_validation :set_searchable
32
32
33 def initialize(attributes=nil, *args)
33 def initialize(attributes=nil, *args)
34 super
34 super
35 self.possible_values ||= []
35 self.possible_values ||= []
36 end
36 end
37
37
38 def set_searchable
38 def set_searchable
39 # make sure these fields are not searchable
39 # make sure these fields are not searchable
40 self.searchable = false if %w(int float date bool).include?(field_format)
40 self.searchable = false if %w(int float date bool).include?(field_format)
41 true
41 true
42 end
42 end
43
43
44 def validate_values
44 def validate_custom_field
45 if self.field_format == "list"
45 if self.field_format == "list"
46 errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty?
46 errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty?
47 errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array
47 errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array
48 end
48 end
49
49
50 if regexp.present?
50 if regexp.present?
51 begin
51 begin
52 Regexp.new(regexp)
52 Regexp.new(regexp)
53 rescue
53 rescue
54 errors.add(:regexp, :invalid)
54 errors.add(:regexp, :invalid)
55 end
55 end
56 end
56 end
57
57
58 # validate default value
58 unless valid_field_value?(default_value)
59 v = CustomValue.new(:custom_field => self.clone, :value => default_value, :customized => nil)
59 errors.add(:default_value, :invalid)
60 v.custom_field.is_required = false
60 end
61 errors.add(:default_value, :invalid) unless v.valid?
62 end
61 end
63
62
64 def possible_values_options(obj=nil)
63 def possible_values_options(obj=nil)
65 case field_format
64 case field_format
66 when 'user', 'version'
65 when 'user', 'version'
67 if obj.respond_to?(:project) && obj.project
66 if obj.respond_to?(:project) && obj.project
68 case field_format
67 case field_format
69 when 'user'
68 when 'user'
70 obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]}
69 obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]}
71 when 'version'
70 when 'version'
72 obj.project.shared_versions.sort.collect {|u| [u.to_s, u.id.to_s]}
71 obj.project.shared_versions.sort.collect {|u| [u.to_s, u.id.to_s]}
73 end
72 end
74 elsif obj.is_a?(Array)
73 elsif obj.is_a?(Array)
75 obj.collect {|o| possible_values_options(o)}.inject {|memo, v| memo & v}
74 obj.collect {|o| possible_values_options(o)}.inject {|memo, v| memo & v}
76 else
75 else
77 []
76 []
78 end
77 end
79 else
78 else
80 read_attribute :possible_values
79 read_attribute :possible_values
81 end
80 end
82 end
81 end
83
82
84 def possible_values(obj=nil)
83 def possible_values(obj=nil)
85 case field_format
84 case field_format
86 when 'user', 'version'
85 when 'user', 'version'
87 possible_values_options(obj).collect(&:last)
86 possible_values_options(obj).collect(&:last)
88 else
87 else
89 read_attribute :possible_values
88 read_attribute :possible_values
90 end
89 end
91 end
90 end
92
91
93 # Makes possible_values accept a multiline string
92 # Makes possible_values accept a multiline string
94 def possible_values=(arg)
93 def possible_values=(arg)
95 if arg.is_a?(Array)
94 if arg.is_a?(Array)
96 write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?})
95 write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?})
97 else
96 else
98 self.possible_values = arg.to_s.split(/[\n\r]+/)
97 self.possible_values = arg.to_s.split(/[\n\r]+/)
99 end
98 end
100 end
99 end
101
100
102 def cast_value(value)
101 def cast_value(value)
103 casted = nil
102 casted = nil
104 unless value.blank?
103 unless value.blank?
105 case field_format
104 case field_format
106 when 'string', 'text', 'list'
105 when 'string', 'text', 'list'
107 casted = value
106 casted = value
108 when 'date'
107 when 'date'
109 casted = begin; value.to_date; rescue; nil end
108 casted = begin; value.to_date; rescue; nil end
110 when 'bool'
109 when 'bool'
111 casted = (value == '1' ? true : false)
110 casted = (value == '1' ? true : false)
112 when 'int'
111 when 'int'
113 casted = value.to_i
112 casted = value.to_i
114 when 'float'
113 when 'float'
115 casted = value.to_f
114 casted = value.to_f
116 when 'user', 'version'
115 when 'user', 'version'
117 casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i))
116 casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i))
118 end
117 end
119 end
118 end
120 casted
119 casted
121 end
120 end
122
121
123 # Returns a ORDER BY clause that can used to sort customized
122 # Returns a ORDER BY clause that can used to sort customized
124 # objects by their value of the custom field.
123 # objects by their value of the custom field.
125 # Returns false, if the custom field can not be used for sorting.
124 # Returns false, if the custom field can not be used for sorting.
126 def order_statement
125 def order_statement
127 case field_format
126 case field_format
128 when 'string', 'text', 'list', 'date', 'bool'
127 when 'string', 'text', 'list', 'date', 'bool'
129 # COALESCE is here to make sure that blank and NULL values are sorted equally
128 # COALESCE is here to make sure that blank and NULL values are sorted equally
130 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
129 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
131 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
130 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
132 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
131 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
133 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
132 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
134 when 'int', 'float'
133 when 'int', 'float'
135 # Make the database cast values into numeric
134 # Make the database cast values into numeric
136 # Postgresql will raise an error if a value can not be casted!
135 # Postgresql will raise an error if a value can not be casted!
137 # CustomValue validations should ensure that it doesn't occur
136 # CustomValue validations should ensure that it doesn't occur
138 "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
137 "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
139 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
138 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
140 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
139 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
141 " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
140 " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
142 else
141 else
143 nil
142 nil
144 end
143 end
145 end
144 end
146
145
147 def <=>(field)
146 def <=>(field)
148 position <=> field.position
147 position <=> field.position
149 end
148 end
150
149
151 def self.customized_class
150 def self.customized_class
152 self.name =~ /^(.+)CustomField$/
151 self.name =~ /^(.+)CustomField$/
153 begin; $1.constantize; rescue nil; end
152 begin; $1.constantize; rescue nil; end
154 end
153 end
155
154
156 # to move in project_custom_field
155 # to move in project_custom_field
157 def self.for_all
156 def self.for_all
158 find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
157 find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
159 end
158 end
160
159
161 def type_name
160 def type_name
162 nil
161 nil
163 end
162 end
163
164 # Returns the error message for the given value
165 # or an empty array if value is a valid value for the custom field
166 def validate_field_value(value)
167 errs = []
168 if is_required? && value.blank?
169 errs << ::I18n.t('activerecord.errors.messages.blank')
170 end
171 errs += validate_field_value_format(value)
172 errs
173 end
174
175 # Returns true if value is a valid value for the custom field
176 def valid_field_value?(value)
177 validate_field_value(value).empty?
178 end
179
180 protected
181
182 # Returns the error message for the given value regarding its format
183 def validate_field_value_format(value)
184 errs = []
185 if value.present?
186 errs << ::I18n.t('activerecord.errors.messages.invalid') unless regexp.blank? or value =~ Regexp.new(regexp)
187 errs << ::I18n.t('activerecord.errors.messages.too_short', :count => min_length) if min_length > 0 and value.length < min_length
188 errs << ::I18n.t('activerecord.errors.messages.too_long', :count => max_length) if max_length > 0 and value.length > max_length
189
190 # Format specific validations
191 case field_format
192 when 'int'
193 errs << ::I18n.t('activerecord.errors.messages.not_a_number') unless value =~ /^[+-]?\d+$/
194 when 'float'
195 begin; Kernel.Float(value); rescue; errs << ::I18n.t('activerecord.errors.messages.invalid') end
196 when 'date'
197 errs << ::I18n.t('activerecord.errors.messages.not_a_date') unless value =~ /^\d{4}-\d{2}-\d{2}$/ && begin; value.to_date; rescue; false end
198 when 'list'
199 errs << ::I18n.t('activerecord.errors.messages.inclusion') unless possible_values.include?(value)
200 end
201 end
202 errs
203 end
164 end
204 end
@@ -1,74 +1,49
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class CustomValue < ActiveRecord::Base
18 class CustomValue < ActiveRecord::Base
19 belongs_to :custom_field
19 belongs_to :custom_field
20 belongs_to :customized, :polymorphic => true
20 belongs_to :customized, :polymorphic => true
21
21
22 validate :validate_custom_value
23
24 def initialize(attributes=nil, *args)
22 def initialize(attributes=nil, *args)
25 super
23 super
26 if new_record? && custom_field && (customized_type.blank? || (customized && customized.new_record?))
24 if new_record? && custom_field && (customized_type.blank? || (customized && customized.new_record?))
27 self.value ||= custom_field.default_value
25 self.value ||= custom_field.default_value
28 end
26 end
29 end
27 end
30
28
31 # Returns true if the boolean custom value is true
29 # Returns true if the boolean custom value is true
32 def true?
30 def true?
33 self.value == '1'
31 self.value == '1'
34 end
32 end
35
33
36 def editable?
34 def editable?
37 custom_field.editable?
35 custom_field.editable?
38 end
36 end
39
37
40 def visible?
38 def visible?
41 custom_field.visible?
39 custom_field.visible?
42 end
40 end
43
41
44 def required?
42 def required?
45 custom_field.is_required?
43 custom_field.is_required?
46 end
44 end
47
45
48 def to_s
46 def to_s
49 value.to_s
47 value.to_s
50 end
48 end
51
52 protected
53 def validate_custom_value
54 if value.blank?
55 errors.add(:value, :blank) if custom_field.is_required? and value.blank?
56 else
57 errors.add(:value, :invalid) unless custom_field.regexp.blank? or value =~ Regexp.new(custom_field.regexp)
58 errors.add(:value, :too_short, :count => custom_field.min_length) if custom_field.min_length > 0 and value.length < custom_field.min_length
59 errors.add(:value, :too_long, :count => custom_field.max_length) if custom_field.max_length > 0 and value.length > custom_field.max_length
60
61 # Format specific validations
62 case custom_field.field_format
63 when 'int'
64 errors.add(:value, :not_a_number) unless value =~ /^[+-]?\d+$/
65 when 'float'
66 begin; Kernel.Float(value); rescue; errors.add(:value, :invalid) end
67 when 'date'
68 errors.add(:value, :not_a_date) unless value =~ /^\d{4}-\d{2}-\d{2}$/ && begin; value.to_date; rescue; false end
69 when 'list'
70 errors.add(:value, :inclusion) unless custom_field.possible_values.include?(value)
71 end
72 end
73 end
74 end
49 end
@@ -1,118 +1,88
1 # Patches active_support/core_ext/load_error.rb to support 1.9.3 LoadError message
1 # Patches active_support/core_ext/load_error.rb to support 1.9.3 LoadError message
2 if RUBY_VERSION >= '1.9.3'
2 if RUBY_VERSION >= '1.9.3'
3 MissingSourceFile::REGEXPS << [/^cannot load such file -- (.+)$/i, 1]
3 MissingSourceFile::REGEXPS << [/^cannot load such file -- (.+)$/i, 1]
4 end
4 end
5
5
6 require 'active_record'
6 require 'active_record'
7
7
8 module ActiveRecord
8 module ActiveRecord
9 class Base
9 class Base
10 include Redmine::I18n
10 include Redmine::I18n
11
11
12 # Translate attribute names for validation errors display
12 # Translate attribute names for validation errors display
13 def self.human_attribute_name(attr, *args)
13 def self.human_attribute_name(attr, *args)
14 l("field_#{attr.to_s.gsub(/_id$/, '')}", :default => attr)
14 l("field_#{attr.to_s.gsub(/_id$/, '')}", :default => attr)
15 end
15 end
16 end
16 end
17 end
17 end
18
18
19 module ActiveRecord
20 class Errors
21 def full_messages(options = {})
22 full_messages = []
23
24 @errors.each_key do |attr|
25 @errors[attr].each do |message|
26 next unless message
27
28 if attr == "base"
29 full_messages << message
30 elsif attr == "custom_values"
31 # Replace the generic "custom values is invalid"
32 # with the errors on custom values
33 @base.custom_values.each do |value|
34 value.errors.each do |attr, msg|
35 full_messages << value.custom_field.name + ' ' + msg
36 end
37 end
38 else
39 attr_name = @base.class.human_attribute_name(attr)
40 full_messages << attr_name + ' ' + message.to_s
41 end
42 end
43 end
44 full_messages
45 end
46 end
47 end
48
49 module ActionView
19 module ActionView
50 module Helpers
20 module Helpers
51 module DateHelper
21 module DateHelper
52 # distance_of_time_in_words breaks when difference is greater than 30 years
22 # distance_of_time_in_words breaks when difference is greater than 30 years
53 def distance_of_date_in_words(from_date, to_date = 0, options = {})
23 def distance_of_date_in_words(from_date, to_date = 0, options = {})
54 from_date = from_date.to_date if from_date.respond_to?(:to_date)
24 from_date = from_date.to_date if from_date.respond_to?(:to_date)
55 to_date = to_date.to_date if to_date.respond_to?(:to_date)
25 to_date = to_date.to_date if to_date.respond_to?(:to_date)
56 distance_in_days = (to_date - from_date).abs
26 distance_in_days = (to_date - from_date).abs
57
27
58 I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale|
28 I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale|
59 case distance_in_days
29 case distance_in_days
60 when 0..60 then locale.t :x_days, :count => distance_in_days.round
30 when 0..60 then locale.t :x_days, :count => distance_in_days.round
61 when 61..720 then locale.t :about_x_months, :count => (distance_in_days / 30).round
31 when 61..720 then locale.t :about_x_months, :count => (distance_in_days / 30).round
62 else locale.t :over_x_years, :count => (distance_in_days / 365).floor
32 else locale.t :over_x_years, :count => (distance_in_days / 365).floor
63 end
33 end
64 end
34 end
65 end
35 end
66 end
36 end
67 end
37 end
68 end
38 end
69
39
70 ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| "#{html_tag}" }
40 ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| "#{html_tag}" }
71
41
72 module AsynchronousMailer
42 module AsynchronousMailer
73 # Adds :async_smtp and :async_sendmail delivery methods
43 # Adds :async_smtp and :async_sendmail delivery methods
74 # to perform email deliveries asynchronously
44 # to perform email deliveries asynchronously
75 %w(smtp sendmail).each do |type|
45 %w(smtp sendmail).each do |type|
76 define_method("perform_delivery_async_#{type}") do |mail|
46 define_method("perform_delivery_async_#{type}") do |mail|
77 Thread.start do
47 Thread.start do
78 send "perform_delivery_#{type}", mail
48 send "perform_delivery_#{type}", mail
79 end
49 end
80 end
50 end
81 end
51 end
82
52
83 # Adds a delivery method that writes emails in tmp/emails for testing purpose
53 # Adds a delivery method that writes emails in tmp/emails for testing purpose
84 def perform_delivery_tmp_file(mail)
54 def perform_delivery_tmp_file(mail)
85 dest_dir = File.join(Rails.root, 'tmp', 'emails')
55 dest_dir = File.join(Rails.root, 'tmp', 'emails')
86 Dir.mkdir(dest_dir) unless File.directory?(dest_dir)
56 Dir.mkdir(dest_dir) unless File.directory?(dest_dir)
87 File.open(File.join(dest_dir, mail.message_id.gsub(/[<>]/, '') + '.eml'), 'wb') {|f| f.write(mail.encoded) }
57 File.open(File.join(dest_dir, mail.message_id.gsub(/[<>]/, '') + '.eml'), 'wb') {|f| f.write(mail.encoded) }
88 end
58 end
89 end
59 end
90
60
91 ActionMailer::Base.send :include, AsynchronousMailer
61 ActionMailer::Base.send :include, AsynchronousMailer
92
62
93 module TMail
63 module TMail
94 # TMail::Unquoter.convert_to_with_fallback_on_iso_8859_1 introduced in TMail 1.2.7
64 # TMail::Unquoter.convert_to_with_fallback_on_iso_8859_1 introduced in TMail 1.2.7
95 # triggers a test failure in test_add_issue_with_japanese_keywords(MailHandlerTest)
65 # triggers a test failure in test_add_issue_with_japanese_keywords(MailHandlerTest)
96 class Unquoter
66 class Unquoter
97 class << self
67 class << self
98 alias_method :convert_to, :convert_to_without_fallback_on_iso_8859_1
68 alias_method :convert_to, :convert_to_without_fallback_on_iso_8859_1
99 end
69 end
100 end
70 end
101
71
102 # Patch for TMail 1.2.7. See http://www.redmine.org/issues/8751
72 # Patch for TMail 1.2.7. See http://www.redmine.org/issues/8751
103 class Encoder
73 class Encoder
104 def puts_meta(str)
74 def puts_meta(str)
105 add_text str
75 add_text str
106 end
76 end
107 end
77 end
108 end
78 end
109
79
110 module ActionController
80 module ActionController
111 module MimeResponds
81 module MimeResponds
112 class Responder
82 class Responder
113 def api(&block)
83 def api(&block)
114 any(:xml, :json, &block)
84 any(:xml, :json, &block)
115 end
85 end
116 end
86 end
117 end
87 end
118 end
88 end
@@ -1,2679 +1,2680
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19 require 'issues_controller'
19 require 'issues_controller'
20
20
21 class IssuesControllerTest < ActionController::TestCase
21 class IssuesControllerTest < ActionController::TestCase
22 fixtures :projects,
22 fixtures :projects,
23 :users,
23 :users,
24 :roles,
24 :roles,
25 :members,
25 :members,
26 :member_roles,
26 :member_roles,
27 :issues,
27 :issues,
28 :issue_statuses,
28 :issue_statuses,
29 :versions,
29 :versions,
30 :trackers,
30 :trackers,
31 :projects_trackers,
31 :projects_trackers,
32 :issue_categories,
32 :issue_categories,
33 :enabled_modules,
33 :enabled_modules,
34 :enumerations,
34 :enumerations,
35 :attachments,
35 :attachments,
36 :workflows,
36 :workflows,
37 :custom_fields,
37 :custom_fields,
38 :custom_values,
38 :custom_values,
39 :custom_fields_projects,
39 :custom_fields_projects,
40 :custom_fields_trackers,
40 :custom_fields_trackers,
41 :time_entries,
41 :time_entries,
42 :journals,
42 :journals,
43 :journal_details,
43 :journal_details,
44 :queries
44 :queries
45
45
46 include Redmine::I18n
46 include Redmine::I18n
47
47
48 def setup
48 def setup
49 @controller = IssuesController.new
49 @controller = IssuesController.new
50 @request = ActionController::TestRequest.new
50 @request = ActionController::TestRequest.new
51 @response = ActionController::TestResponse.new
51 @response = ActionController::TestResponse.new
52 User.current = nil
52 User.current = nil
53 end
53 end
54
54
55 def test_index
55 def test_index
56 Setting.default_language = 'en'
56 Setting.default_language = 'en'
57
57
58 get :index
58 get :index
59 assert_response :success
59 assert_response :success
60 assert_template 'index'
60 assert_template 'index'
61 assert_not_nil assigns(:issues)
61 assert_not_nil assigns(:issues)
62 assert_nil assigns(:project)
62 assert_nil assigns(:project)
63 assert_tag :tag => 'a', :content => /Can't print recipes/
63 assert_tag :tag => 'a', :content => /Can't print recipes/
64 assert_tag :tag => 'a', :content => /Subproject issue/
64 assert_tag :tag => 'a', :content => /Subproject issue/
65 # private projects hidden
65 # private projects hidden
66 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
66 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
67 assert_no_tag :tag => 'a', :content => /Issue on project 2/
67 assert_no_tag :tag => 'a', :content => /Issue on project 2/
68 # project column
68 # project column
69 assert_tag :tag => 'th', :content => /Project/
69 assert_tag :tag => 'th', :content => /Project/
70 end
70 end
71
71
72 def test_index_should_not_list_issues_when_module_disabled
72 def test_index_should_not_list_issues_when_module_disabled
73 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
73 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
74 get :index
74 get :index
75 assert_response :success
75 assert_response :success
76 assert_template 'index'
76 assert_template 'index'
77 assert_not_nil assigns(:issues)
77 assert_not_nil assigns(:issues)
78 assert_nil assigns(:project)
78 assert_nil assigns(:project)
79 assert_no_tag :tag => 'a', :content => /Can't print recipes/
79 assert_no_tag :tag => 'a', :content => /Can't print recipes/
80 assert_tag :tag => 'a', :content => /Subproject issue/
80 assert_tag :tag => 'a', :content => /Subproject issue/
81 end
81 end
82
82
83 def test_index_should_list_visible_issues_only
83 def test_index_should_list_visible_issues_only
84 get :index, :per_page => 100
84 get :index, :per_page => 100
85 assert_response :success
85 assert_response :success
86 assert_not_nil assigns(:issues)
86 assert_not_nil assigns(:issues)
87 assert_nil assigns(:issues).detect {|issue| !issue.visible?}
87 assert_nil assigns(:issues).detect {|issue| !issue.visible?}
88 end
88 end
89
89
90 def test_index_with_project
90 def test_index_with_project
91 Setting.display_subprojects_issues = 0
91 Setting.display_subprojects_issues = 0
92 get :index, :project_id => 1
92 get :index, :project_id => 1
93 assert_response :success
93 assert_response :success
94 assert_template 'index'
94 assert_template 'index'
95 assert_not_nil assigns(:issues)
95 assert_not_nil assigns(:issues)
96 assert_tag :tag => 'a', :content => /Can't print recipes/
96 assert_tag :tag => 'a', :content => /Can't print recipes/
97 assert_no_tag :tag => 'a', :content => /Subproject issue/
97 assert_no_tag :tag => 'a', :content => /Subproject issue/
98 end
98 end
99
99
100 def test_index_with_project_and_subprojects
100 def test_index_with_project_and_subprojects
101 Setting.display_subprojects_issues = 1
101 Setting.display_subprojects_issues = 1
102 get :index, :project_id => 1
102 get :index, :project_id => 1
103 assert_response :success
103 assert_response :success
104 assert_template 'index'
104 assert_template 'index'
105 assert_not_nil assigns(:issues)
105 assert_not_nil assigns(:issues)
106 assert_tag :tag => 'a', :content => /Can't print recipes/
106 assert_tag :tag => 'a', :content => /Can't print recipes/
107 assert_tag :tag => 'a', :content => /Subproject issue/
107 assert_tag :tag => 'a', :content => /Subproject issue/
108 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
108 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
109 end
109 end
110
110
111 def test_index_with_project_and_subprojects_should_show_private_subprojects
111 def test_index_with_project_and_subprojects_should_show_private_subprojects
112 @request.session[:user_id] = 2
112 @request.session[:user_id] = 2
113 Setting.display_subprojects_issues = 1
113 Setting.display_subprojects_issues = 1
114 get :index, :project_id => 1
114 get :index, :project_id => 1
115 assert_response :success
115 assert_response :success
116 assert_template 'index'
116 assert_template 'index'
117 assert_not_nil assigns(:issues)
117 assert_not_nil assigns(:issues)
118 assert_tag :tag => 'a', :content => /Can't print recipes/
118 assert_tag :tag => 'a', :content => /Can't print recipes/
119 assert_tag :tag => 'a', :content => /Subproject issue/
119 assert_tag :tag => 'a', :content => /Subproject issue/
120 assert_tag :tag => 'a', :content => /Issue of a private subproject/
120 assert_tag :tag => 'a', :content => /Issue of a private subproject/
121 end
121 end
122
122
123 def test_index_with_project_and_default_filter
123 def test_index_with_project_and_default_filter
124 get :index, :project_id => 1, :set_filter => 1
124 get :index, :project_id => 1, :set_filter => 1
125 assert_response :success
125 assert_response :success
126 assert_template 'index'
126 assert_template 'index'
127 assert_not_nil assigns(:issues)
127 assert_not_nil assigns(:issues)
128
128
129 query = assigns(:query)
129 query = assigns(:query)
130 assert_not_nil query
130 assert_not_nil query
131 # default filter
131 # default filter
132 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
132 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
133 end
133 end
134
134
135 def test_index_with_project_and_filter
135 def test_index_with_project_and_filter
136 get :index, :project_id => 1, :set_filter => 1,
136 get :index, :project_id => 1, :set_filter => 1,
137 :f => ['tracker_id'],
137 :f => ['tracker_id'],
138 :op => {'tracker_id' => '='},
138 :op => {'tracker_id' => '='},
139 :v => {'tracker_id' => ['1']}
139 :v => {'tracker_id' => ['1']}
140 assert_response :success
140 assert_response :success
141 assert_template 'index'
141 assert_template 'index'
142 assert_not_nil assigns(:issues)
142 assert_not_nil assigns(:issues)
143
143
144 query = assigns(:query)
144 query = assigns(:query)
145 assert_not_nil query
145 assert_not_nil query
146 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
146 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
147 end
147 end
148
148
149 def test_index_with_short_filters
149 def test_index_with_short_filters
150
150
151 to_test = {
151 to_test = {
152 'status_id' => {
152 'status_id' => {
153 'o' => { :op => 'o', :values => [''] },
153 'o' => { :op => 'o', :values => [''] },
154 'c' => { :op => 'c', :values => [''] },
154 'c' => { :op => 'c', :values => [''] },
155 '7' => { :op => '=', :values => ['7'] },
155 '7' => { :op => '=', :values => ['7'] },
156 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
156 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
157 '=7' => { :op => '=', :values => ['7'] },
157 '=7' => { :op => '=', :values => ['7'] },
158 '!3' => { :op => '!', :values => ['3'] },
158 '!3' => { :op => '!', :values => ['3'] },
159 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
159 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
160 'subject' => {
160 'subject' => {
161 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
161 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
162 'o' => { :op => '=', :values => ['o'] },
162 'o' => { :op => '=', :values => ['o'] },
163 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
163 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
164 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
164 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
165 'tracker_id' => {
165 'tracker_id' => {
166 '3' => { :op => '=', :values => ['3'] },
166 '3' => { :op => '=', :values => ['3'] },
167 '=3' => { :op => '=', :values => ['3'] }},
167 '=3' => { :op => '=', :values => ['3'] }},
168 'start_date' => {
168 'start_date' => {
169 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
169 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
170 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
170 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
171 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
171 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
172 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
172 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
173 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
173 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
174 '<t+2' => { :op => '<t+', :values => ['2'] },
174 '<t+2' => { :op => '<t+', :values => ['2'] },
175 '>t+2' => { :op => '>t+', :values => ['2'] },
175 '>t+2' => { :op => '>t+', :values => ['2'] },
176 't+2' => { :op => 't+', :values => ['2'] },
176 't+2' => { :op => 't+', :values => ['2'] },
177 't' => { :op => 't', :values => [''] },
177 't' => { :op => 't', :values => [''] },
178 'w' => { :op => 'w', :values => [''] },
178 'w' => { :op => 'w', :values => [''] },
179 '>t-2' => { :op => '>t-', :values => ['2'] },
179 '>t-2' => { :op => '>t-', :values => ['2'] },
180 '<t-2' => { :op => '<t-', :values => ['2'] },
180 '<t-2' => { :op => '<t-', :values => ['2'] },
181 't-2' => { :op => 't-', :values => ['2'] }},
181 't-2' => { :op => 't-', :values => ['2'] }},
182 'created_on' => {
182 'created_on' => {
183 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
183 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
184 '<t+2' => { :op => '=', :values => ['<t+2'] },
184 '<t+2' => { :op => '=', :values => ['<t+2'] },
185 '>t+2' => { :op => '=', :values => ['>t+2'] },
185 '>t+2' => { :op => '=', :values => ['>t+2'] },
186 't+2' => { :op => 't', :values => ['+2'] }},
186 't+2' => { :op => 't', :values => ['+2'] }},
187 'cf_1' => {
187 'cf_1' => {
188 'c' => { :op => '=', :values => ['c'] },
188 'c' => { :op => '=', :values => ['c'] },
189 '!c' => { :op => '!', :values => ['c'] },
189 '!c' => { :op => '!', :values => ['c'] },
190 '!*' => { :op => '!*', :values => [''] },
190 '!*' => { :op => '!*', :values => [''] },
191 '*' => { :op => '*', :values => [''] }},
191 '*' => { :op => '*', :values => [''] }},
192 'estimated_hours' => {
192 'estimated_hours' => {
193 '=13.4' => { :op => '=', :values => ['13.4'] },
193 '=13.4' => { :op => '=', :values => ['13.4'] },
194 '>=45' => { :op => '>=', :values => ['45'] },
194 '>=45' => { :op => '>=', :values => ['45'] },
195 '<=125' => { :op => '<=', :values => ['125'] },
195 '<=125' => { :op => '<=', :values => ['125'] },
196 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
196 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
197 '!*' => { :op => '!*', :values => [''] },
197 '!*' => { :op => '!*', :values => [''] },
198 '*' => { :op => '*', :values => [''] }}
198 '*' => { :op => '*', :values => [''] }}
199 }
199 }
200
200
201 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
201 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
202
202
203 to_test.each do |field, expression_and_expected|
203 to_test.each do |field, expression_and_expected|
204 expression_and_expected.each do |filter_expression, expected|
204 expression_and_expected.each do |filter_expression, expected|
205
205
206 get :index, :set_filter => 1, field => filter_expression
206 get :index, :set_filter => 1, field => filter_expression
207
207
208 assert_response :success
208 assert_response :success
209 assert_template 'index'
209 assert_template 'index'
210 assert_not_nil assigns(:issues)
210 assert_not_nil assigns(:issues)
211
211
212 query = assigns(:query)
212 query = assigns(:query)
213 assert_not_nil query
213 assert_not_nil query
214 assert query.has_filter?(field)
214 assert query.has_filter?(field)
215 assert_equal(default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}}), query.filters)
215 assert_equal(default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}}), query.filters)
216 end
216 end
217 end
217 end
218
218
219 end
219 end
220
220
221 def test_index_with_project_and_empty_filters
221 def test_index_with_project_and_empty_filters
222 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
222 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
223 assert_response :success
223 assert_response :success
224 assert_template 'index'
224 assert_template 'index'
225 assert_not_nil assigns(:issues)
225 assert_not_nil assigns(:issues)
226
226
227 query = assigns(:query)
227 query = assigns(:query)
228 assert_not_nil query
228 assert_not_nil query
229 # no filter
229 # no filter
230 assert_equal({}, query.filters)
230 assert_equal({}, query.filters)
231 end
231 end
232
232
233 def test_index_with_query
233 def test_index_with_query
234 get :index, :project_id => 1, :query_id => 5
234 get :index, :project_id => 1, :query_id => 5
235 assert_response :success
235 assert_response :success
236 assert_template 'index'
236 assert_template 'index'
237 assert_not_nil assigns(:issues)
237 assert_not_nil assigns(:issues)
238 assert_nil assigns(:issue_count_by_group)
238 assert_nil assigns(:issue_count_by_group)
239 end
239 end
240
240
241 def test_index_with_query_grouped_by_tracker
241 def test_index_with_query_grouped_by_tracker
242 get :index, :project_id => 1, :query_id => 6
242 get :index, :project_id => 1, :query_id => 6
243 assert_response :success
243 assert_response :success
244 assert_template 'index'
244 assert_template 'index'
245 assert_not_nil assigns(:issues)
245 assert_not_nil assigns(:issues)
246 assert_not_nil assigns(:issue_count_by_group)
246 assert_not_nil assigns(:issue_count_by_group)
247 end
247 end
248
248
249 def test_index_with_query_grouped_by_list_custom_field
249 def test_index_with_query_grouped_by_list_custom_field
250 get :index, :project_id => 1, :query_id => 9
250 get :index, :project_id => 1, :query_id => 9
251 assert_response :success
251 assert_response :success
252 assert_template 'index'
252 assert_template 'index'
253 assert_not_nil assigns(:issues)
253 assert_not_nil assigns(:issues)
254 assert_not_nil assigns(:issue_count_by_group)
254 assert_not_nil assigns(:issue_count_by_group)
255 end
255 end
256
256
257 def test_index_with_query_id_and_project_id_should_set_session_query
257 def test_index_with_query_id_and_project_id_should_set_session_query
258 get :index, :project_id => 1, :query_id => 4
258 get :index, :project_id => 1, :query_id => 4
259 assert_response :success
259 assert_response :success
260 assert_kind_of Hash, session[:query]
260 assert_kind_of Hash, session[:query]
261 assert_equal 4, session[:query][:id]
261 assert_equal 4, session[:query][:id]
262 assert_equal 1, session[:query][:project_id]
262 assert_equal 1, session[:query][:project_id]
263 end
263 end
264
264
265 def test_index_with_cross_project_query_in_session_should_show_project_issues
265 def test_index_with_cross_project_query_in_session_should_show_project_issues
266 q = Query.create!(:name => "test", :user_id => 2, :is_public => false, :project => nil)
266 q = Query.create!(:name => "test", :user_id => 2, :is_public => false, :project => nil)
267 @request.session[:query] = {:id => q.id, :project_id => 1}
267 @request.session[:query] = {:id => q.id, :project_id => 1}
268
268
269 with_settings :display_subprojects_issues => '0' do
269 with_settings :display_subprojects_issues => '0' do
270 get :index, :project_id => 1
270 get :index, :project_id => 1
271 end
271 end
272 assert_response :success
272 assert_response :success
273 assert_not_nil assigns(:query)
273 assert_not_nil assigns(:query)
274 assert_equal q.id, assigns(:query).id
274 assert_equal q.id, assigns(:query).id
275 assert_equal 1, assigns(:query).project_id
275 assert_equal 1, assigns(:query).project_id
276 assert_equal [1], assigns(:issues).map(&:project_id).uniq
276 assert_equal [1], assigns(:issues).map(&:project_id).uniq
277 end
277 end
278
278
279 def test_private_query_should_not_be_available_to_other_users
279 def test_private_query_should_not_be_available_to_other_users
280 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
280 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
281 @request.session[:user_id] = 3
281 @request.session[:user_id] = 3
282
282
283 get :index, :query_id => q.id
283 get :index, :query_id => q.id
284 assert_response 403
284 assert_response 403
285 end
285 end
286
286
287 def test_private_query_should_be_available_to_its_user
287 def test_private_query_should_be_available_to_its_user
288 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
288 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
289 @request.session[:user_id] = 2
289 @request.session[:user_id] = 2
290
290
291 get :index, :query_id => q.id
291 get :index, :query_id => q.id
292 assert_response :success
292 assert_response :success
293 end
293 end
294
294
295 def test_public_query_should_be_available_to_other_users
295 def test_public_query_should_be_available_to_other_users
296 q = Query.create!(:name => "private", :user => User.find(2), :is_public => true, :project => nil)
296 q = Query.create!(:name => "private", :user => User.find(2), :is_public => true, :project => nil)
297 @request.session[:user_id] = 3
297 @request.session[:user_id] = 3
298
298
299 get :index, :query_id => q.id
299 get :index, :query_id => q.id
300 assert_response :success
300 assert_response :success
301 end
301 end
302
302
303 def test_index_csv
303 def test_index_csv
304 get :index, :format => 'csv'
304 get :index, :format => 'csv'
305 assert_response :success
305 assert_response :success
306 assert_not_nil assigns(:issues)
306 assert_not_nil assigns(:issues)
307 assert_equal 'text/csv', @response.content_type
307 assert_equal 'text/csv', @response.content_type
308 assert @response.body.starts_with?("#,")
308 assert @response.body.starts_with?("#,")
309 lines = @response.body.chomp.split("\n")
309 lines = @response.body.chomp.split("\n")
310 assert_equal assigns(:query).columns.size + 1, lines[0].split(',').size
310 assert_equal assigns(:query).columns.size + 1, lines[0].split(',').size
311 end
311 end
312
312
313 def test_index_csv_with_project
313 def test_index_csv_with_project
314 get :index, :project_id => 1, :format => 'csv'
314 get :index, :project_id => 1, :format => 'csv'
315 assert_response :success
315 assert_response :success
316 assert_not_nil assigns(:issues)
316 assert_not_nil assigns(:issues)
317 assert_equal 'text/csv', @response.content_type
317 assert_equal 'text/csv', @response.content_type
318 end
318 end
319
319
320 def test_index_csv_with_description
320 def test_index_csv_with_description
321 get :index, :format => 'csv', :description => '1'
321 get :index, :format => 'csv', :description => '1'
322 assert_response :success
322 assert_response :success
323 assert_not_nil assigns(:issues)
323 assert_not_nil assigns(:issues)
324 assert_equal 'text/csv', @response.content_type
324 assert_equal 'text/csv', @response.content_type
325 assert @response.body.starts_with?("#,")
325 assert @response.body.starts_with?("#,")
326 lines = @response.body.chomp.split("\n")
326 lines = @response.body.chomp.split("\n")
327 assert_equal assigns(:query).columns.size + 2, lines[0].split(',').size
327 assert_equal assigns(:query).columns.size + 2, lines[0].split(',').size
328 end
328 end
329
329
330 def test_index_csv_with_all_columns
330 def test_index_csv_with_all_columns
331 get :index, :format => 'csv', :columns => 'all'
331 get :index, :format => 'csv', :columns => 'all'
332 assert_response :success
332 assert_response :success
333 assert_not_nil assigns(:issues)
333 assert_not_nil assigns(:issues)
334 assert_equal 'text/csv', @response.content_type
334 assert_equal 'text/csv', @response.content_type
335 assert @response.body.starts_with?("#,")
335 assert @response.body.starts_with?("#,")
336 lines = @response.body.chomp.split("\n")
336 lines = @response.body.chomp.split("\n")
337 assert_equal assigns(:query).available_columns.size + 1, lines[0].split(',').size
337 assert_equal assigns(:query).available_columns.size + 1, lines[0].split(',').size
338 end
338 end
339
339
340 def test_index_csv_big_5
340 def test_index_csv_big_5
341 with_settings :default_language => "zh-TW" do
341 with_settings :default_language => "zh-TW" do
342 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
342 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
343 str_big5 = "\xa4@\xa4\xeb"
343 str_big5 = "\xa4@\xa4\xeb"
344 if str_utf8.respond_to?(:force_encoding)
344 if str_utf8.respond_to?(:force_encoding)
345 str_utf8.force_encoding('UTF-8')
345 str_utf8.force_encoding('UTF-8')
346 str_big5.force_encoding('Big5')
346 str_big5.force_encoding('Big5')
347 end
347 end
348 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
348 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
349 :status_id => 1, :priority => IssuePriority.all.first,
349 :status_id => 1, :priority => IssuePriority.all.first,
350 :subject => str_utf8)
350 :subject => str_utf8)
351 assert issue.save
351 assert issue.save
352
352
353 get :index, :project_id => 1,
353 get :index, :project_id => 1,
354 :f => ['subject'],
354 :f => ['subject'],
355 :op => '=', :values => [str_utf8],
355 :op => '=', :values => [str_utf8],
356 :format => 'csv'
356 :format => 'csv'
357 assert_equal 'text/csv', @response.content_type
357 assert_equal 'text/csv', @response.content_type
358 lines = @response.body.chomp.split("\n")
358 lines = @response.body.chomp.split("\n")
359 s1 = "\xaa\xac\xbaA"
359 s1 = "\xaa\xac\xbaA"
360 if str_utf8.respond_to?(:force_encoding)
360 if str_utf8.respond_to?(:force_encoding)
361 s1.force_encoding('Big5')
361 s1.force_encoding('Big5')
362 end
362 end
363 assert lines[0].include?(s1)
363 assert lines[0].include?(s1)
364 assert lines[1].include?(str_big5)
364 assert lines[1].include?(str_big5)
365 end
365 end
366 end
366 end
367
367
368 def test_index_csv_cannot_convert_should_be_replaced_big_5
368 def test_index_csv_cannot_convert_should_be_replaced_big_5
369 with_settings :default_language => "zh-TW" do
369 with_settings :default_language => "zh-TW" do
370 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
370 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
371 if str_utf8.respond_to?(:force_encoding)
371 if str_utf8.respond_to?(:force_encoding)
372 str_utf8.force_encoding('UTF-8')
372 str_utf8.force_encoding('UTF-8')
373 end
373 end
374 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
374 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
375 :status_id => 1, :priority => IssuePriority.all.first,
375 :status_id => 1, :priority => IssuePriority.all.first,
376 :subject => str_utf8)
376 :subject => str_utf8)
377 assert issue.save
377 assert issue.save
378
378
379 get :index, :project_id => 1,
379 get :index, :project_id => 1,
380 :f => ['subject'],
380 :f => ['subject'],
381 :op => '=', :values => [str_utf8],
381 :op => '=', :values => [str_utf8],
382 :c => ['status', 'subject'],
382 :c => ['status', 'subject'],
383 :format => 'csv',
383 :format => 'csv',
384 :set_filter => 1
384 :set_filter => 1
385 assert_equal 'text/csv', @response.content_type
385 assert_equal 'text/csv', @response.content_type
386 lines = @response.body.chomp.split("\n")
386 lines = @response.body.chomp.split("\n")
387 s1 = "\xaa\xac\xbaA" # status
387 s1 = "\xaa\xac\xbaA" # status
388 if str_utf8.respond_to?(:force_encoding)
388 if str_utf8.respond_to?(:force_encoding)
389 s1.force_encoding('Big5')
389 s1.force_encoding('Big5')
390 end
390 end
391 assert lines[0].include?(s1)
391 assert lines[0].include?(s1)
392 s2 = lines[1].split(",")[2]
392 s2 = lines[1].split(",")[2]
393 if s1.respond_to?(:force_encoding)
393 if s1.respond_to?(:force_encoding)
394 s3 = "\xa5H?" # subject
394 s3 = "\xa5H?" # subject
395 s3.force_encoding('Big5')
395 s3.force_encoding('Big5')
396 assert_equal s3, s2
396 assert_equal s3, s2
397 elsif RUBY_PLATFORM == 'java'
397 elsif RUBY_PLATFORM == 'java'
398 assert_equal "??", s2
398 assert_equal "??", s2
399 else
399 else
400 assert_equal "\xa5H???", s2
400 assert_equal "\xa5H???", s2
401 end
401 end
402 end
402 end
403 end
403 end
404
404
405 def test_index_csv_tw
405 def test_index_csv_tw
406 with_settings :default_language => "zh-TW" do
406 with_settings :default_language => "zh-TW" do
407 str1 = "test_index_csv_tw"
407 str1 = "test_index_csv_tw"
408 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
408 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
409 :status_id => 1, :priority => IssuePriority.all.first,
409 :status_id => 1, :priority => IssuePriority.all.first,
410 :subject => str1, :estimated_hours => '1234.5')
410 :subject => str1, :estimated_hours => '1234.5')
411 assert issue.save
411 assert issue.save
412 assert_equal 1234.5, issue.estimated_hours
412 assert_equal 1234.5, issue.estimated_hours
413
413
414 get :index, :project_id => 1,
414 get :index, :project_id => 1,
415 :f => ['subject'],
415 :f => ['subject'],
416 :op => '=', :values => [str1],
416 :op => '=', :values => [str1],
417 :c => ['estimated_hours', 'subject'],
417 :c => ['estimated_hours', 'subject'],
418 :format => 'csv',
418 :format => 'csv',
419 :set_filter => 1
419 :set_filter => 1
420 assert_equal 'text/csv', @response.content_type
420 assert_equal 'text/csv', @response.content_type
421 lines = @response.body.chomp.split("\n")
421 lines = @response.body.chomp.split("\n")
422 assert_equal "#{issue.id},1234.5,#{str1}", lines[1]
422 assert_equal "#{issue.id},1234.5,#{str1}", lines[1]
423
423
424 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
424 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
425 if str_tw.respond_to?(:force_encoding)
425 if str_tw.respond_to?(:force_encoding)
426 str_tw.force_encoding('UTF-8')
426 str_tw.force_encoding('UTF-8')
427 end
427 end
428 assert_equal str_tw, l(:general_lang_name)
428 assert_equal str_tw, l(:general_lang_name)
429 assert_equal ',', l(:general_csv_separator)
429 assert_equal ',', l(:general_csv_separator)
430 assert_equal '.', l(:general_csv_decimal_separator)
430 assert_equal '.', l(:general_csv_decimal_separator)
431 end
431 end
432 end
432 end
433
433
434 def test_index_csv_fr
434 def test_index_csv_fr
435 with_settings :default_language => "fr" do
435 with_settings :default_language => "fr" do
436 str1 = "test_index_csv_fr"
436 str1 = "test_index_csv_fr"
437 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
437 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
438 :status_id => 1, :priority => IssuePriority.all.first,
438 :status_id => 1, :priority => IssuePriority.all.first,
439 :subject => str1, :estimated_hours => '1234.5')
439 :subject => str1, :estimated_hours => '1234.5')
440 assert issue.save
440 assert issue.save
441 assert_equal 1234.5, issue.estimated_hours
441 assert_equal 1234.5, issue.estimated_hours
442
442
443 get :index, :project_id => 1,
443 get :index, :project_id => 1,
444 :f => ['subject'],
444 :f => ['subject'],
445 :op => '=', :values => [str1],
445 :op => '=', :values => [str1],
446 :c => ['estimated_hours', 'subject'],
446 :c => ['estimated_hours', 'subject'],
447 :format => 'csv',
447 :format => 'csv',
448 :set_filter => 1
448 :set_filter => 1
449 assert_equal 'text/csv', @response.content_type
449 assert_equal 'text/csv', @response.content_type
450 lines = @response.body.chomp.split("\n")
450 lines = @response.body.chomp.split("\n")
451 assert_equal "#{issue.id};1234,5;#{str1}", lines[1]
451 assert_equal "#{issue.id};1234,5;#{str1}", lines[1]
452
452
453 str_fr = "Fran\xc3\xa7ais"
453 str_fr = "Fran\xc3\xa7ais"
454 if str_fr.respond_to?(:force_encoding)
454 if str_fr.respond_to?(:force_encoding)
455 str_fr.force_encoding('UTF-8')
455 str_fr.force_encoding('UTF-8')
456 end
456 end
457 assert_equal str_fr, l(:general_lang_name)
457 assert_equal str_fr, l(:general_lang_name)
458 assert_equal ';', l(:general_csv_separator)
458 assert_equal ';', l(:general_csv_separator)
459 assert_equal ',', l(:general_csv_decimal_separator)
459 assert_equal ',', l(:general_csv_decimal_separator)
460 end
460 end
461 end
461 end
462
462
463 def test_index_pdf
463 def test_index_pdf
464 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
464 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
465 with_settings :default_language => lang do
465 with_settings :default_language => lang do
466
466
467 get :index
467 get :index
468 assert_response :success
468 assert_response :success
469 assert_template 'index'
469 assert_template 'index'
470
470
471 if lang == "ja"
471 if lang == "ja"
472 if RUBY_PLATFORM != 'java'
472 if RUBY_PLATFORM != 'java'
473 assert_equal "CP932", l(:general_pdf_encoding)
473 assert_equal "CP932", l(:general_pdf_encoding)
474 end
474 end
475 if RUBY_PLATFORM == 'java' && l(:general_pdf_encoding) == "CP932"
475 if RUBY_PLATFORM == 'java' && l(:general_pdf_encoding) == "CP932"
476 next
476 next
477 end
477 end
478 end
478 end
479
479
480 get :index, :format => 'pdf'
480 get :index, :format => 'pdf'
481 assert_response :success
481 assert_response :success
482 assert_not_nil assigns(:issues)
482 assert_not_nil assigns(:issues)
483 assert_equal 'application/pdf', @response.content_type
483 assert_equal 'application/pdf', @response.content_type
484
484
485 get :index, :project_id => 1, :format => 'pdf'
485 get :index, :project_id => 1, :format => 'pdf'
486 assert_response :success
486 assert_response :success
487 assert_not_nil assigns(:issues)
487 assert_not_nil assigns(:issues)
488 assert_equal 'application/pdf', @response.content_type
488 assert_equal 'application/pdf', @response.content_type
489
489
490 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
490 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
491 assert_response :success
491 assert_response :success
492 assert_not_nil assigns(:issues)
492 assert_not_nil assigns(:issues)
493 assert_equal 'application/pdf', @response.content_type
493 assert_equal 'application/pdf', @response.content_type
494 end
494 end
495 end
495 end
496 end
496 end
497
497
498 def test_index_pdf_with_query_grouped_by_list_custom_field
498 def test_index_pdf_with_query_grouped_by_list_custom_field
499 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
499 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
500 assert_response :success
500 assert_response :success
501 assert_not_nil assigns(:issues)
501 assert_not_nil assigns(:issues)
502 assert_not_nil assigns(:issue_count_by_group)
502 assert_not_nil assigns(:issue_count_by_group)
503 assert_equal 'application/pdf', @response.content_type
503 assert_equal 'application/pdf', @response.content_type
504 end
504 end
505
505
506 def test_index_sort
506 def test_index_sort
507 get :index, :sort => 'tracker,id:desc'
507 get :index, :sort => 'tracker,id:desc'
508 assert_response :success
508 assert_response :success
509
509
510 sort_params = @request.session['issues_index_sort']
510 sort_params = @request.session['issues_index_sort']
511 assert sort_params.is_a?(String)
511 assert sort_params.is_a?(String)
512 assert_equal 'tracker,id:desc', sort_params
512 assert_equal 'tracker,id:desc', sort_params
513
513
514 issues = assigns(:issues)
514 issues = assigns(:issues)
515 assert_not_nil issues
515 assert_not_nil issues
516 assert !issues.empty?
516 assert !issues.empty?
517 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
517 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
518 end
518 end
519
519
520 def test_index_sort_by_field_not_included_in_columns
520 def test_index_sort_by_field_not_included_in_columns
521 Setting.issue_list_default_columns = %w(subject author)
521 Setting.issue_list_default_columns = %w(subject author)
522 get :index, :sort => 'tracker'
522 get :index, :sort => 'tracker'
523 end
523 end
524
524
525 def test_index_sort_by_assigned_to
525 def test_index_sort_by_assigned_to
526 get :index, :sort => 'assigned_to'
526 get :index, :sort => 'assigned_to'
527 assert_response :success
527 assert_response :success
528 assignees = assigns(:issues).collect(&:assigned_to).compact
528 assignees = assigns(:issues).collect(&:assigned_to).compact
529 assert_equal assignees.sort, assignees
529 assert_equal assignees.sort, assignees
530 end
530 end
531
531
532 def test_index_sort_by_assigned_to_desc
532 def test_index_sort_by_assigned_to_desc
533 get :index, :sort => 'assigned_to:desc'
533 get :index, :sort => 'assigned_to:desc'
534 assert_response :success
534 assert_response :success
535 assignees = assigns(:issues).collect(&:assigned_to).compact
535 assignees = assigns(:issues).collect(&:assigned_to).compact
536 assert_equal assignees.sort.reverse, assignees
536 assert_equal assignees.sort.reverse, assignees
537 end
537 end
538
538
539 def test_index_group_by_assigned_to
539 def test_index_group_by_assigned_to
540 get :index, :group_by => 'assigned_to', :sort => 'priority'
540 get :index, :group_by => 'assigned_to', :sort => 'priority'
541 assert_response :success
541 assert_response :success
542 end
542 end
543
543
544 def test_index_sort_by_author
544 def test_index_sort_by_author
545 get :index, :sort => 'author'
545 get :index, :sort => 'author'
546 assert_response :success
546 assert_response :success
547 authors = assigns(:issues).collect(&:author)
547 authors = assigns(:issues).collect(&:author)
548 assert_equal authors.sort, authors
548 assert_equal authors.sort, authors
549 end
549 end
550
550
551 def test_index_sort_by_author_desc
551 def test_index_sort_by_author_desc
552 get :index, :sort => 'author:desc'
552 get :index, :sort => 'author:desc'
553 assert_response :success
553 assert_response :success
554 authors = assigns(:issues).collect(&:author)
554 authors = assigns(:issues).collect(&:author)
555 assert_equal authors.sort.reverse, authors
555 assert_equal authors.sort.reverse, authors
556 end
556 end
557
557
558 def test_index_group_by_author
558 def test_index_group_by_author
559 get :index, :group_by => 'author', :sort => 'priority'
559 get :index, :group_by => 'author', :sort => 'priority'
560 assert_response :success
560 assert_response :success
561 end
561 end
562
562
563 def test_index_sort_by_spent_hours
563 def test_index_sort_by_spent_hours
564 get :index, :sort => 'spent_hours:desc'
564 get :index, :sort => 'spent_hours:desc'
565 assert_response :success
565 assert_response :success
566 hours = assigns(:issues).collect(&:spent_hours)
566 hours = assigns(:issues).collect(&:spent_hours)
567 assert_equal hours.sort.reverse, hours
567 assert_equal hours.sort.reverse, hours
568 end
568 end
569
569
570 def test_index_with_columns
570 def test_index_with_columns
571 columns = ['tracker', 'subject', 'assigned_to']
571 columns = ['tracker', 'subject', 'assigned_to']
572 get :index, :set_filter => 1, :c => columns
572 get :index, :set_filter => 1, :c => columns
573 assert_response :success
573 assert_response :success
574
574
575 # query should use specified columns
575 # query should use specified columns
576 query = assigns(:query)
576 query = assigns(:query)
577 assert_kind_of Query, query
577 assert_kind_of Query, query
578 assert_equal columns, query.column_names.map(&:to_s)
578 assert_equal columns, query.column_names.map(&:to_s)
579
579
580 # columns should be stored in session
580 # columns should be stored in session
581 assert_kind_of Hash, session[:query]
581 assert_kind_of Hash, session[:query]
582 assert_kind_of Array, session[:query][:column_names]
582 assert_kind_of Array, session[:query][:column_names]
583 assert_equal columns, session[:query][:column_names].map(&:to_s)
583 assert_equal columns, session[:query][:column_names].map(&:to_s)
584
584
585 # ensure only these columns are kept in the selected columns list
585 # ensure only these columns are kept in the selected columns list
586 assert_tag :tag => 'select', :attributes => { :id => 'selected_columns' },
586 assert_tag :tag => 'select', :attributes => { :id => 'selected_columns' },
587 :children => { :count => 3 }
587 :children => { :count => 3 }
588 assert_no_tag :tag => 'option', :attributes => { :value => 'project' },
588 assert_no_tag :tag => 'option', :attributes => { :value => 'project' },
589 :parent => { :tag => 'select', :attributes => { :id => "selected_columns" } }
589 :parent => { :tag => 'select', :attributes => { :id => "selected_columns" } }
590 end
590 end
591
591
592 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
592 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
593 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
593 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
594 get :index, :set_filter => 1
594 get :index, :set_filter => 1
595
595
596 # query should use specified columns
596 # query should use specified columns
597 query = assigns(:query)
597 query = assigns(:query)
598 assert_kind_of Query, query
598 assert_kind_of Query, query
599 assert_equal [:project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
599 assert_equal [:project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
600 end
600 end
601
601
602 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
602 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
603 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
603 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
604 columns = ['tracker', 'subject', 'assigned_to']
604 columns = ['tracker', 'subject', 'assigned_to']
605 get :index, :set_filter => 1, :c => columns
605 get :index, :set_filter => 1, :c => columns
606
606
607 # query should use specified columns
607 # query should use specified columns
608 query = assigns(:query)
608 query = assigns(:query)
609 assert_kind_of Query, query
609 assert_kind_of Query, query
610 assert_equal columns.map(&:to_sym), query.columns.map(&:name)
610 assert_equal columns.map(&:to_sym), query.columns.map(&:name)
611 end
611 end
612
612
613 def test_index_with_custom_field_column
613 def test_index_with_custom_field_column
614 columns = %w(tracker subject cf_2)
614 columns = %w(tracker subject cf_2)
615 get :index, :set_filter => 1, :c => columns
615 get :index, :set_filter => 1, :c => columns
616 assert_response :success
616 assert_response :success
617
617
618 # query should use specified columns
618 # query should use specified columns
619 query = assigns(:query)
619 query = assigns(:query)
620 assert_kind_of Query, query
620 assert_kind_of Query, query
621 assert_equal columns, query.column_names.map(&:to_s)
621 assert_equal columns, query.column_names.map(&:to_s)
622
622
623 assert_tag :td,
623 assert_tag :td,
624 :attributes => {:class => 'cf_2 string'},
624 :attributes => {:class => 'cf_2 string'},
625 :ancestor => {:tag => 'table', :attributes => {:class => /issues/}}
625 :ancestor => {:tag => 'table', :attributes => {:class => /issues/}}
626 end
626 end
627
627
628 def test_index_with_date_column
628 def test_index_with_date_column
629 Issue.find(1).update_attribute :start_date, '1987-08-24'
629 Issue.find(1).update_attribute :start_date, '1987-08-24'
630
630
631 with_settings :date_format => '%d/%m/%Y' do
631 with_settings :date_format => '%d/%m/%Y' do
632 get :index, :set_filter => 1, :c => %w(start_date)
632 get :index, :set_filter => 1, :c => %w(start_date)
633 assert_tag 'td', :attributes => {:class => /start_date/}, :content => '24/08/1987'
633 assert_tag 'td', :attributes => {:class => /start_date/}, :content => '24/08/1987'
634 end
634 end
635 end
635 end
636
636
637 def test_index_with_done_ratio
637 def test_index_with_done_ratio
638 Issue.find(1).update_attribute :done_ratio, 40
638 Issue.find(1).update_attribute :done_ratio, 40
639
639
640 get :index, :set_filter => 1, :c => %w(done_ratio)
640 get :index, :set_filter => 1, :c => %w(done_ratio)
641 assert_tag 'td', :attributes => {:class => /done_ratio/},
641 assert_tag 'td', :attributes => {:class => /done_ratio/},
642 :child => {:tag => 'table', :attributes => {:class => 'progress'},
642 :child => {:tag => 'table', :attributes => {:class => 'progress'},
643 :descendant => {:tag => 'td', :attributes => {:class => 'closed', :style => 'width: 40%;'}}
643 :descendant => {:tag => 'td', :attributes => {:class => 'closed', :style => 'width: 40%;'}}
644 }
644 }
645 end
645 end
646
646
647 def test_index_with_spent_hours_column
647 def test_index_with_spent_hours_column
648 get :index, :set_filter => 1, :c => %w(subject spent_hours)
648 get :index, :set_filter => 1, :c => %w(subject spent_hours)
649
649
650 assert_tag 'tr', :attributes => {:id => 'issue-3'},
650 assert_tag 'tr', :attributes => {:id => 'issue-3'},
651 :child => {
651 :child => {
652 :tag => 'td', :attributes => {:class => /spent_hours/}, :content => '1.00'
652 :tag => 'td', :attributes => {:class => /spent_hours/}, :content => '1.00'
653 }
653 }
654 end
654 end
655
655
656 def test_index_should_not_show_spent_hours_column_without_permission
656 def test_index_should_not_show_spent_hours_column_without_permission
657 Role.anonymous.remove_permission! :view_time_entries
657 Role.anonymous.remove_permission! :view_time_entries
658 get :index, :set_filter => 1, :c => %w(subject spent_hours)
658 get :index, :set_filter => 1, :c => %w(subject spent_hours)
659
659
660 assert_no_tag 'td', :attributes => {:class => /spent_hours/}
660 assert_no_tag 'td', :attributes => {:class => /spent_hours/}
661 end
661 end
662
662
663 def test_index_with_fixed_version
663 def test_index_with_fixed_version
664 get :index, :set_filter => 1, :c => %w(fixed_version)
664 get :index, :set_filter => 1, :c => %w(fixed_version)
665 assert_tag 'td', :attributes => {:class => /fixed_version/},
665 assert_tag 'td', :attributes => {:class => /fixed_version/},
666 :child => {:tag => 'a', :content => '1.0', :attributes => {:href => '/versions/2'}}
666 :child => {:tag => 'a', :content => '1.0', :attributes => {:href => '/versions/2'}}
667 end
667 end
668
668
669 def test_index_send_html_if_query_is_invalid
669 def test_index_send_html_if_query_is_invalid
670 get :index, :f => ['start_date'], :op => {:start_date => '='}
670 get :index, :f => ['start_date'], :op => {:start_date => '='}
671 assert_equal 'text/html', @response.content_type
671 assert_equal 'text/html', @response.content_type
672 assert_template 'index'
672 assert_template 'index'
673 end
673 end
674
674
675 def test_index_send_nothing_if_query_is_invalid
675 def test_index_send_nothing_if_query_is_invalid
676 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
676 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
677 assert_equal 'text/csv', @response.content_type
677 assert_equal 'text/csv', @response.content_type
678 assert @response.body.blank?
678 assert @response.body.blank?
679 end
679 end
680
680
681 def test_show_by_anonymous
681 def test_show_by_anonymous
682 get :show, :id => 1
682 get :show, :id => 1
683 assert_response :success
683 assert_response :success
684 assert_template 'show'
684 assert_template 'show'
685 assert_not_nil assigns(:issue)
685 assert_not_nil assigns(:issue)
686 assert_equal Issue.find(1), assigns(:issue)
686 assert_equal Issue.find(1), assigns(:issue)
687
687
688 # anonymous role is allowed to add a note
688 # anonymous role is allowed to add a note
689 assert_tag :tag => 'form',
689 assert_tag :tag => 'form',
690 :descendant => { :tag => 'fieldset',
690 :descendant => { :tag => 'fieldset',
691 :child => { :tag => 'legend',
691 :child => { :tag => 'legend',
692 :content => /Notes/ } }
692 :content => /Notes/ } }
693 assert_tag :tag => 'title',
693 assert_tag :tag => 'title',
694 :content => "Bug #1: Can't print recipes - eCookbook - Redmine"
694 :content => "Bug #1: Can't print recipes - eCookbook - Redmine"
695 end
695 end
696
696
697 def test_show_by_manager
697 def test_show_by_manager
698 @request.session[:user_id] = 2
698 @request.session[:user_id] = 2
699 get :show, :id => 1
699 get :show, :id => 1
700 assert_response :success
700 assert_response :success
701
701
702 assert_tag :tag => 'a',
702 assert_tag :tag => 'a',
703 :content => /Quote/
703 :content => /Quote/
704
704
705 assert_tag :tag => 'form',
705 assert_tag :tag => 'form',
706 :descendant => { :tag => 'fieldset',
706 :descendant => { :tag => 'fieldset',
707 :child => { :tag => 'legend',
707 :child => { :tag => 'legend',
708 :content => /Change properties/ } },
708 :content => /Change properties/ } },
709 :descendant => { :tag => 'fieldset',
709 :descendant => { :tag => 'fieldset',
710 :child => { :tag => 'legend',
710 :child => { :tag => 'legend',
711 :content => /Log time/ } },
711 :content => /Log time/ } },
712 :descendant => { :tag => 'fieldset',
712 :descendant => { :tag => 'fieldset',
713 :child => { :tag => 'legend',
713 :child => { :tag => 'legend',
714 :content => /Notes/ } }
714 :content => /Notes/ } }
715 end
715 end
716
716
717 def test_show_should_display_update_form
717 def test_show_should_display_update_form
718 @request.session[:user_id] = 2
718 @request.session[:user_id] = 2
719 get :show, :id => 1
719 get :show, :id => 1
720 assert_response :success
720 assert_response :success
721
721
722 assert_tag 'form', :attributes => {:id => 'issue-form'}
722 assert_tag 'form', :attributes => {:id => 'issue-form'}
723 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
723 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
724 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
724 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
725 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
725 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
726 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
726 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
727 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
727 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
728 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
728 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
729 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
729 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
730 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
730 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
731 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
731 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
732 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
732 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
733 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
733 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
734 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
734 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
735 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
735 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
736 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
736 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
737 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
737 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
738 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
738 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
739 assert_tag 'textarea', :attributes => {:name => 'notes'}
739 assert_tag 'textarea', :attributes => {:name => 'notes'}
740 end
740 end
741
741
742 def test_show_should_display_update_form_with_minimal_permissions
742 def test_show_should_display_update_form_with_minimal_permissions
743 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
743 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
744 Workflow.delete_all :role_id => 1
744 Workflow.delete_all :role_id => 1
745
745
746 @request.session[:user_id] = 2
746 @request.session[:user_id] = 2
747 get :show, :id => 1
747 get :show, :id => 1
748 assert_response :success
748 assert_response :success
749
749
750 assert_tag 'form', :attributes => {:id => 'issue-form'}
750 assert_tag 'form', :attributes => {:id => 'issue-form'}
751 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
751 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
752 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
752 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
753 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
753 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
754 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
754 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
755 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
755 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
756 assert_no_tag 'select', :attributes => {:name => 'issue[status_id]'}
756 assert_no_tag 'select', :attributes => {:name => 'issue[status_id]'}
757 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
757 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
758 assert_no_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
758 assert_no_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
759 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
759 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
760 assert_no_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
760 assert_no_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
761 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
761 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
762 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
762 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
763 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
763 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
764 assert_no_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
764 assert_no_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
765 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
765 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
766 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
766 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
767 assert_tag 'textarea', :attributes => {:name => 'notes'}
767 assert_tag 'textarea', :attributes => {:name => 'notes'}
768 end
768 end
769
769
770 def test_show_should_display_update_form_with_workflow_permissions
770 def test_show_should_display_update_form_with_workflow_permissions
771 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
771 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
772
772
773 @request.session[:user_id] = 2
773 @request.session[:user_id] = 2
774 get :show, :id => 1
774 get :show, :id => 1
775 assert_response :success
775 assert_response :success
776
776
777 assert_tag 'form', :attributes => {:id => 'issue-form'}
777 assert_tag 'form', :attributes => {:id => 'issue-form'}
778 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
778 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
779 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
779 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
780 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
780 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
781 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
781 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
782 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
782 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
783 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
783 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
784 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
784 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
785 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
785 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
786 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
786 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
787 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
787 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
788 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
788 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
789 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
789 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
790 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
790 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
791 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
791 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
792 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
792 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
793 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
793 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
794 assert_tag 'textarea', :attributes => {:name => 'notes'}
794 assert_tag 'textarea', :attributes => {:name => 'notes'}
795 end
795 end
796
796
797 def test_show_should_not_display_update_form_without_permissions
797 def test_show_should_not_display_update_form_without_permissions
798 Role.find(1).update_attribute :permissions, [:view_issues]
798 Role.find(1).update_attribute :permissions, [:view_issues]
799
799
800 @request.session[:user_id] = 2
800 @request.session[:user_id] = 2
801 get :show, :id => 1
801 get :show, :id => 1
802 assert_response :success
802 assert_response :success
803
803
804 assert_no_tag 'form', :attributes => {:id => 'issue-form'}
804 assert_no_tag 'form', :attributes => {:id => 'issue-form'}
805 end
805 end
806
806
807 def test_update_form_should_not_display_inactive_enumerations
807 def test_update_form_should_not_display_inactive_enumerations
808 @request.session[:user_id] = 2
808 @request.session[:user_id] = 2
809 get :show, :id => 1
809 get :show, :id => 1
810 assert_response :success
810 assert_response :success
811
811
812 assert ! IssuePriority.find(15).active?
812 assert ! IssuePriority.find(15).active?
813 assert_no_tag :option, :attributes => {:value => '15'},
813 assert_no_tag :option, :attributes => {:value => '15'},
814 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
814 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
815 end
815 end
816
816
817 def test_update_form_should_allow_attachment_upload
817 def test_update_form_should_allow_attachment_upload
818 @request.session[:user_id] = 2
818 @request.session[:user_id] = 2
819 get :show, :id => 1
819 get :show, :id => 1
820
820
821 assert_tag :tag => 'form',
821 assert_tag :tag => 'form',
822 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
822 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
823 :descendant => {
823 :descendant => {
824 :tag => 'input',
824 :tag => 'input',
825 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
825 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
826 }
826 }
827 end
827 end
828
828
829 def test_show_should_deny_anonymous_access_without_permission
829 def test_show_should_deny_anonymous_access_without_permission
830 Role.anonymous.remove_permission!(:view_issues)
830 Role.anonymous.remove_permission!(:view_issues)
831 get :show, :id => 1
831 get :show, :id => 1
832 assert_response :redirect
832 assert_response :redirect
833 end
833 end
834
834
835 def test_show_should_deny_anonymous_access_to_private_issue
835 def test_show_should_deny_anonymous_access_to_private_issue
836 Issue.update_all(["is_private = ?", true], "id = 1")
836 Issue.update_all(["is_private = ?", true], "id = 1")
837 get :show, :id => 1
837 get :show, :id => 1
838 assert_response :redirect
838 assert_response :redirect
839 end
839 end
840
840
841 def test_show_should_deny_non_member_access_without_permission
841 def test_show_should_deny_non_member_access_without_permission
842 Role.non_member.remove_permission!(:view_issues)
842 Role.non_member.remove_permission!(:view_issues)
843 @request.session[:user_id] = 9
843 @request.session[:user_id] = 9
844 get :show, :id => 1
844 get :show, :id => 1
845 assert_response 403
845 assert_response 403
846 end
846 end
847
847
848 def test_show_should_deny_non_member_access_to_private_issue
848 def test_show_should_deny_non_member_access_to_private_issue
849 Issue.update_all(["is_private = ?", true], "id = 1")
849 Issue.update_all(["is_private = ?", true], "id = 1")
850 @request.session[:user_id] = 9
850 @request.session[:user_id] = 9
851 get :show, :id => 1
851 get :show, :id => 1
852 assert_response 403
852 assert_response 403
853 end
853 end
854
854
855 def test_show_should_deny_member_access_without_permission
855 def test_show_should_deny_member_access_without_permission
856 Role.find(1).remove_permission!(:view_issues)
856 Role.find(1).remove_permission!(:view_issues)
857 @request.session[:user_id] = 2
857 @request.session[:user_id] = 2
858 get :show, :id => 1
858 get :show, :id => 1
859 assert_response 403
859 assert_response 403
860 end
860 end
861
861
862 def test_show_should_deny_member_access_to_private_issue_without_permission
862 def test_show_should_deny_member_access_to_private_issue_without_permission
863 Issue.update_all(["is_private = ?", true], "id = 1")
863 Issue.update_all(["is_private = ?", true], "id = 1")
864 @request.session[:user_id] = 3
864 @request.session[:user_id] = 3
865 get :show, :id => 1
865 get :show, :id => 1
866 assert_response 403
866 assert_response 403
867 end
867 end
868
868
869 def test_show_should_allow_author_access_to_private_issue
869 def test_show_should_allow_author_access_to_private_issue
870 Issue.update_all(["is_private = ?, author_id = 3", true], "id = 1")
870 Issue.update_all(["is_private = ?, author_id = 3", true], "id = 1")
871 @request.session[:user_id] = 3
871 @request.session[:user_id] = 3
872 get :show, :id => 1
872 get :show, :id => 1
873 assert_response :success
873 assert_response :success
874 end
874 end
875
875
876 def test_show_should_allow_assignee_access_to_private_issue
876 def test_show_should_allow_assignee_access_to_private_issue
877 Issue.update_all(["is_private = ?, assigned_to_id = 3", true], "id = 1")
877 Issue.update_all(["is_private = ?, assigned_to_id = 3", true], "id = 1")
878 @request.session[:user_id] = 3
878 @request.session[:user_id] = 3
879 get :show, :id => 1
879 get :show, :id => 1
880 assert_response :success
880 assert_response :success
881 end
881 end
882
882
883 def test_show_should_allow_member_access_to_private_issue_with_permission
883 def test_show_should_allow_member_access_to_private_issue_with_permission
884 Issue.update_all(["is_private = ?", true], "id = 1")
884 Issue.update_all(["is_private = ?", true], "id = 1")
885 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
885 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
886 @request.session[:user_id] = 3
886 @request.session[:user_id] = 3
887 get :show, :id => 1
887 get :show, :id => 1
888 assert_response :success
888 assert_response :success
889 end
889 end
890
890
891 def test_show_should_not_disclose_relations_to_invisible_issues
891 def test_show_should_not_disclose_relations_to_invisible_issues
892 Setting.cross_project_issue_relations = '1'
892 Setting.cross_project_issue_relations = '1'
893 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
893 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
894 # Relation to a private project issue
894 # Relation to a private project issue
895 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
895 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
896
896
897 get :show, :id => 1
897 get :show, :id => 1
898 assert_response :success
898 assert_response :success
899
899
900 assert_tag :div, :attributes => { :id => 'relations' },
900 assert_tag :div, :attributes => { :id => 'relations' },
901 :descendant => { :tag => 'a', :content => /#2$/ }
901 :descendant => { :tag => 'a', :content => /#2$/ }
902 assert_no_tag :div, :attributes => { :id => 'relations' },
902 assert_no_tag :div, :attributes => { :id => 'relations' },
903 :descendant => { :tag => 'a', :content => /#4$/ }
903 :descendant => { :tag => 'a', :content => /#4$/ }
904 end
904 end
905
905
906 def test_show_should_list_subtasks
906 def test_show_should_list_subtasks
907 Issue.generate!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
907 Issue.generate!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
908
908
909 get :show, :id => 1
909 get :show, :id => 1
910 assert_response :success
910 assert_response :success
911 assert_tag 'div', :attributes => {:id => 'issue_tree'},
911 assert_tag 'div', :attributes => {:id => 'issue_tree'},
912 :descendant => {:tag => 'td', :content => /Child Issue/, :attributes => {:class => /subject/}}
912 :descendant => {:tag => 'td', :content => /Child Issue/, :attributes => {:class => /subject/}}
913 end
913 end
914
914
915 def test_show_should_list_parents
915 def test_show_should_list_parents
916 issue = Issue.generate!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
916 issue = Issue.generate!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
917
917
918 get :show, :id => issue.id
918 get :show, :id => issue.id
919 assert_response :success
919 assert_response :success
920 assert_tag 'div', :attributes => {:class => 'subject'},
920 assert_tag 'div', :attributes => {:class => 'subject'},
921 :descendant => {:tag => 'h3', :content => 'Child Issue'}
921 :descendant => {:tag => 'h3', :content => 'Child Issue'}
922 assert_tag 'div', :attributes => {:class => 'subject'},
922 assert_tag 'div', :attributes => {:class => 'subject'},
923 :descendant => {:tag => 'a', :attributes => {:href => '/issues/1'}}
923 :descendant => {:tag => 'a', :attributes => {:href => '/issues/1'}}
924 end
924 end
925
925
926 def test_show_should_not_display_prev_next_links_without_query_in_session
926 def test_show_should_not_display_prev_next_links_without_query_in_session
927 get :show, :id => 1
927 get :show, :id => 1
928 assert_response :success
928 assert_response :success
929 assert_nil assigns(:prev_issue_id)
929 assert_nil assigns(:prev_issue_id)
930 assert_nil assigns(:next_issue_id)
930 assert_nil assigns(:next_issue_id)
931
931
932 assert_no_tag 'div', :attributes => {:class => /next-prev-links/}
932 assert_no_tag 'div', :attributes => {:class => /next-prev-links/}
933 end
933 end
934
934
935 def test_show_should_display_prev_next_links_with_query_in_session
935 def test_show_should_display_prev_next_links_with_query_in_session
936 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
936 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
937 @request.session['issues_index_sort'] = 'id'
937 @request.session['issues_index_sort'] = 'id'
938
938
939 with_settings :display_subprojects_issues => '0' do
939 with_settings :display_subprojects_issues => '0' do
940 get :show, :id => 3
940 get :show, :id => 3
941 end
941 end
942
942
943 assert_response :success
943 assert_response :success
944 # Previous and next issues for all projects
944 # Previous and next issues for all projects
945 assert_equal 2, assigns(:prev_issue_id)
945 assert_equal 2, assigns(:prev_issue_id)
946 assert_equal 5, assigns(:next_issue_id)
946 assert_equal 5, assigns(:next_issue_id)
947
947
948 assert_tag 'div', :attributes => {:class => /next-prev-links/}
948 assert_tag 'div', :attributes => {:class => /next-prev-links/}
949 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
949 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
950 assert_tag 'a', :attributes => {:href => '/issues/5'}, :content => /Next/
950 assert_tag 'a', :attributes => {:href => '/issues/5'}, :content => /Next/
951
951
952 count = Issue.open.visible.count
952 count = Issue.open.visible.count
953 assert_tag 'span', :attributes => {:class => 'position'}, :content => "3 of #{count}"
953 assert_tag 'span', :attributes => {:class => 'position'}, :content => "3 of #{count}"
954 end
954 end
955
955
956 def test_show_should_display_prev_next_links_with_saved_query_in_session
956 def test_show_should_display_prev_next_links_with_saved_query_in_session
957 query = Query.create!(:name => 'test', :is_public => true, :user_id => 1,
957 query = Query.create!(:name => 'test', :is_public => true, :user_id => 1,
958 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
958 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
959 :sort_criteria => [['id', 'asc']])
959 :sort_criteria => [['id', 'asc']])
960 @request.session[:query] = {:id => query.id, :project_id => nil}
960 @request.session[:query] = {:id => query.id, :project_id => nil}
961
961
962 get :show, :id => 11
962 get :show, :id => 11
963
963
964 assert_response :success
964 assert_response :success
965 assert_equal query, assigns(:query)
965 assert_equal query, assigns(:query)
966 # Previous and next issues for all projects
966 # Previous and next issues for all projects
967 assert_equal 8, assigns(:prev_issue_id)
967 assert_equal 8, assigns(:prev_issue_id)
968 assert_equal 12, assigns(:next_issue_id)
968 assert_equal 12, assigns(:next_issue_id)
969
969
970 assert_tag 'a', :attributes => {:href => '/issues/8'}, :content => /Previous/
970 assert_tag 'a', :attributes => {:href => '/issues/8'}, :content => /Previous/
971 assert_tag 'a', :attributes => {:href => '/issues/12'}, :content => /Next/
971 assert_tag 'a', :attributes => {:href => '/issues/12'}, :content => /Next/
972 end
972 end
973
973
974 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
974 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
975 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
975 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
976
976
977 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
977 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
978 @request.session['issues_index_sort'] = assoc_sort
978 @request.session['issues_index_sort'] = assoc_sort
979
979
980 get :show, :id => 3
980 get :show, :id => 3
981 assert_response :success, "Wrong response status for #{assoc_sort} sort"
981 assert_response :success, "Wrong response status for #{assoc_sort} sort"
982
982
983 assert_tag 'a', :content => /Previous/
983 assert_tag 'a', :content => /Previous/
984 assert_tag 'a', :content => /Next/
984 assert_tag 'a', :content => /Next/
985 end
985 end
986 end
986 end
987
987
988 def test_show_should_display_prev_next_links_with_project_query_in_session
988 def test_show_should_display_prev_next_links_with_project_query_in_session
989 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
989 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
990 @request.session['issues_index_sort'] = 'id'
990 @request.session['issues_index_sort'] = 'id'
991
991
992 with_settings :display_subprojects_issues => '0' do
992 with_settings :display_subprojects_issues => '0' do
993 get :show, :id => 3
993 get :show, :id => 3
994 end
994 end
995
995
996 assert_response :success
996 assert_response :success
997 # Previous and next issues inside project
997 # Previous and next issues inside project
998 assert_equal 2, assigns(:prev_issue_id)
998 assert_equal 2, assigns(:prev_issue_id)
999 assert_equal 7, assigns(:next_issue_id)
999 assert_equal 7, assigns(:next_issue_id)
1000
1000
1001 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
1001 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
1002 assert_tag 'a', :attributes => {:href => '/issues/7'}, :content => /Next/
1002 assert_tag 'a', :attributes => {:href => '/issues/7'}, :content => /Next/
1003 end
1003 end
1004
1004
1005 def test_show_should_not_display_prev_link_for_first_issue
1005 def test_show_should_not_display_prev_link_for_first_issue
1006 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1006 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1007 @request.session['issues_index_sort'] = 'id'
1007 @request.session['issues_index_sort'] = 'id'
1008
1008
1009 with_settings :display_subprojects_issues => '0' do
1009 with_settings :display_subprojects_issues => '0' do
1010 get :show, :id => 1
1010 get :show, :id => 1
1011 end
1011 end
1012
1012
1013 assert_response :success
1013 assert_response :success
1014 assert_nil assigns(:prev_issue_id)
1014 assert_nil assigns(:prev_issue_id)
1015 assert_equal 2, assigns(:next_issue_id)
1015 assert_equal 2, assigns(:next_issue_id)
1016
1016
1017 assert_no_tag 'a', :content => /Previous/
1017 assert_no_tag 'a', :content => /Previous/
1018 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Next/
1018 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Next/
1019 end
1019 end
1020
1020
1021 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1021 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1022 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1022 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1023 @request.session['issues_index_sort'] = 'id'
1023 @request.session['issues_index_sort'] = 'id'
1024
1024
1025 get :show, :id => 1
1025 get :show, :id => 1
1026
1026
1027 assert_response :success
1027 assert_response :success
1028 assert_nil assigns(:prev_issue_id)
1028 assert_nil assigns(:prev_issue_id)
1029 assert_nil assigns(:next_issue_id)
1029 assert_nil assigns(:next_issue_id)
1030
1030
1031 assert_no_tag 'a', :content => /Previous/
1031 assert_no_tag 'a', :content => /Previous/
1032 assert_no_tag 'a', :content => /Next/
1032 assert_no_tag 'a', :content => /Next/
1033 end
1033 end
1034
1034
1035 def test_show_atom
1035 def test_show_atom
1036 get :show, :id => 2, :format => 'atom'
1036 get :show, :id => 2, :format => 'atom'
1037 assert_response :success
1037 assert_response :success
1038 assert_template 'journals/index'
1038 assert_template 'journals/index'
1039 # Inline image
1039 # Inline image
1040 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1040 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1041 end
1041 end
1042
1042
1043 def test_show_export_to_pdf
1043 def test_show_export_to_pdf
1044 get :show, :id => 3, :format => 'pdf'
1044 get :show, :id => 3, :format => 'pdf'
1045 assert_response :success
1045 assert_response :success
1046 assert_equal 'application/pdf', @response.content_type
1046 assert_equal 'application/pdf', @response.content_type
1047 assert @response.body.starts_with?('%PDF')
1047 assert @response.body.starts_with?('%PDF')
1048 assert_not_nil assigns(:issue)
1048 assert_not_nil assigns(:issue)
1049 end
1049 end
1050
1050
1051 def test_get_new
1051 def test_get_new
1052 @request.session[:user_id] = 2
1052 @request.session[:user_id] = 2
1053 get :new, :project_id => 1, :tracker_id => 1
1053 get :new, :project_id => 1, :tracker_id => 1
1054 assert_response :success
1054 assert_response :success
1055 assert_template 'new'
1055 assert_template 'new'
1056
1056
1057 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
1057 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
1058 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1058 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1059 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1059 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1060 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1060 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1061 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1061 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1062 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1062 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1063 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1063 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1064 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1064 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1065 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1065 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1066 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1066 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1067 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1067 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1068 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1068 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1069 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1069 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1070 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1070 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1071 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1071 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1072 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1072 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1073
1073
1074 # Be sure we don't display inactive IssuePriorities
1074 # Be sure we don't display inactive IssuePriorities
1075 assert ! IssuePriority.find(15).active?
1075 assert ! IssuePriority.find(15).active?
1076 assert_no_tag :option, :attributes => {:value => '15'},
1076 assert_no_tag :option, :attributes => {:value => '15'},
1077 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1077 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1078 end
1078 end
1079
1079
1080 def test_get_new_with_minimal_permissions
1080 def test_get_new_with_minimal_permissions
1081 Role.find(1).update_attribute :permissions, [:add_issues]
1081 Role.find(1).update_attribute :permissions, [:add_issues]
1082 Workflow.delete_all :role_id => 1
1082 Workflow.delete_all :role_id => 1
1083
1083
1084 @request.session[:user_id] = 2
1084 @request.session[:user_id] = 2
1085 get :new, :project_id => 1, :tracker_id => 1
1085 get :new, :project_id => 1, :tracker_id => 1
1086 assert_response :success
1086 assert_response :success
1087 assert_template 'new'
1087 assert_template 'new'
1088
1088
1089 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
1089 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
1090 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1090 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1091 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1091 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1092 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1092 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1093 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1093 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1094 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1094 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1095 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1095 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1096 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1096 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1097 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1097 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1098 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1098 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1099 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1099 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1100 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1100 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1101 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1101 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1102 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1102 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1103 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1103 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1104 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1104 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1105 end
1105 end
1106
1106
1107 def test_get_new_without_default_start_date_is_creation_date
1107 def test_get_new_without_default_start_date_is_creation_date
1108 Setting.default_issue_start_date_to_creation_date = 0
1108 Setting.default_issue_start_date_to_creation_date = 0
1109
1109
1110 @request.session[:user_id] = 2
1110 @request.session[:user_id] = 2
1111 get :new, :project_id => 1, :tracker_id => 1
1111 get :new, :project_id => 1, :tracker_id => 1
1112 assert_response :success
1112 assert_response :success
1113 assert_template 'new'
1113 assert_template 'new'
1114
1114
1115 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1115 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1116 :value => nil }
1116 :value => nil }
1117 end
1117 end
1118
1118
1119 def test_get_new_with_default_start_date_is_creation_date
1119 def test_get_new_with_default_start_date_is_creation_date
1120 Setting.default_issue_start_date_to_creation_date = 1
1120 Setting.default_issue_start_date_to_creation_date = 1
1121
1121
1122 @request.session[:user_id] = 2
1122 @request.session[:user_id] = 2
1123 get :new, :project_id => 1, :tracker_id => 1
1123 get :new, :project_id => 1, :tracker_id => 1
1124 assert_response :success
1124 assert_response :success
1125 assert_template 'new'
1125 assert_template 'new'
1126
1126
1127 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1127 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1128 :value => Date.today.to_s }
1128 :value => Date.today.to_s }
1129 end
1129 end
1130
1130
1131 def test_get_new_form_should_allow_attachment_upload
1131 def test_get_new_form_should_allow_attachment_upload
1132 @request.session[:user_id] = 2
1132 @request.session[:user_id] = 2
1133 get :new, :project_id => 1, :tracker_id => 1
1133 get :new, :project_id => 1, :tracker_id => 1
1134
1134
1135 assert_tag :tag => 'form',
1135 assert_tag :tag => 'form',
1136 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
1136 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
1137 :descendant => {
1137 :descendant => {
1138 :tag => 'input',
1138 :tag => 'input',
1139 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
1139 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
1140 }
1140 }
1141 end
1141 end
1142
1142
1143 def test_get_new_without_tracker_id
1143 def test_get_new_without_tracker_id
1144 @request.session[:user_id] = 2
1144 @request.session[:user_id] = 2
1145 get :new, :project_id => 1
1145 get :new, :project_id => 1
1146 assert_response :success
1146 assert_response :success
1147 assert_template 'new'
1147 assert_template 'new'
1148
1148
1149 issue = assigns(:issue)
1149 issue = assigns(:issue)
1150 assert_not_nil issue
1150 assert_not_nil issue
1151 assert_equal Project.find(1).trackers.first, issue.tracker
1151 assert_equal Project.find(1).trackers.first, issue.tracker
1152 end
1152 end
1153
1153
1154 def test_get_new_with_no_default_status_should_display_an_error
1154 def test_get_new_with_no_default_status_should_display_an_error
1155 @request.session[:user_id] = 2
1155 @request.session[:user_id] = 2
1156 IssueStatus.delete_all
1156 IssueStatus.delete_all
1157
1157
1158 get :new, :project_id => 1
1158 get :new, :project_id => 1
1159 assert_response 500
1159 assert_response 500
1160 assert_error_tag :content => /No default issue/
1160 assert_error_tag :content => /No default issue/
1161 end
1161 end
1162
1162
1163 def test_get_new_with_no_tracker_should_display_an_error
1163 def test_get_new_with_no_tracker_should_display_an_error
1164 @request.session[:user_id] = 2
1164 @request.session[:user_id] = 2
1165 Tracker.delete_all
1165 Tracker.delete_all
1166
1166
1167 get :new, :project_id => 1
1167 get :new, :project_id => 1
1168 assert_response 500
1168 assert_response 500
1169 assert_error_tag :content => /No tracker/
1169 assert_error_tag :content => /No tracker/
1170 end
1170 end
1171
1171
1172 def test_update_new_form
1172 def test_update_new_form
1173 @request.session[:user_id] = 2
1173 @request.session[:user_id] = 2
1174 xhr :post, :new, :project_id => 1,
1174 xhr :post, :new, :project_id => 1,
1175 :issue => {:tracker_id => 2,
1175 :issue => {:tracker_id => 2,
1176 :subject => 'This is the test_new issue',
1176 :subject => 'This is the test_new issue',
1177 :description => 'This is the description',
1177 :description => 'This is the description',
1178 :priority_id => 5}
1178 :priority_id => 5}
1179 assert_response :success
1179 assert_response :success
1180 assert_template 'attributes'
1180 assert_template 'attributes'
1181
1181
1182 issue = assigns(:issue)
1182 issue = assigns(:issue)
1183 assert_kind_of Issue, issue
1183 assert_kind_of Issue, issue
1184 assert_equal 1, issue.project_id
1184 assert_equal 1, issue.project_id
1185 assert_equal 2, issue.tracker_id
1185 assert_equal 2, issue.tracker_id
1186 assert_equal 'This is the test_new issue', issue.subject
1186 assert_equal 'This is the test_new issue', issue.subject
1187 end
1187 end
1188
1188
1189 def test_post_create
1189 def test_post_create
1190 @request.session[:user_id] = 2
1190 @request.session[:user_id] = 2
1191 assert_difference 'Issue.count' do
1191 assert_difference 'Issue.count' do
1192 post :create, :project_id => 1,
1192 post :create, :project_id => 1,
1193 :issue => {:tracker_id => 3,
1193 :issue => {:tracker_id => 3,
1194 :status_id => 2,
1194 :status_id => 2,
1195 :subject => 'This is the test_new issue',
1195 :subject => 'This is the test_new issue',
1196 :description => 'This is the description',
1196 :description => 'This is the description',
1197 :priority_id => 5,
1197 :priority_id => 5,
1198 :start_date => '2010-11-07',
1198 :start_date => '2010-11-07',
1199 :estimated_hours => '',
1199 :estimated_hours => '',
1200 :custom_field_values => {'2' => 'Value for field 2'}}
1200 :custom_field_values => {'2' => 'Value for field 2'}}
1201 end
1201 end
1202 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1202 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1203
1203
1204 issue = Issue.find_by_subject('This is the test_new issue')
1204 issue = Issue.find_by_subject('This is the test_new issue')
1205 assert_not_nil issue
1205 assert_not_nil issue
1206 assert_equal 2, issue.author_id
1206 assert_equal 2, issue.author_id
1207 assert_equal 3, issue.tracker_id
1207 assert_equal 3, issue.tracker_id
1208 assert_equal 2, issue.status_id
1208 assert_equal 2, issue.status_id
1209 assert_equal Date.parse('2010-11-07'), issue.start_date
1209 assert_equal Date.parse('2010-11-07'), issue.start_date
1210 assert_nil issue.estimated_hours
1210 assert_nil issue.estimated_hours
1211 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
1211 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
1212 assert_not_nil v
1212 assert_not_nil v
1213 assert_equal 'Value for field 2', v.value
1213 assert_equal 'Value for field 2', v.value
1214 end
1214 end
1215
1215
1216 def test_post_new_with_group_assignment
1216 def test_post_new_with_group_assignment
1217 group = Group.find(11)
1217 group = Group.find(11)
1218 project = Project.find(1)
1218 project = Project.find(1)
1219 project.members << Member.new(:principal => group, :roles => [Role.first])
1219 project.members << Member.new(:principal => group, :roles => [Role.first])
1220
1220
1221 with_settings :issue_group_assignment => '1' do
1221 with_settings :issue_group_assignment => '1' do
1222 @request.session[:user_id] = 2
1222 @request.session[:user_id] = 2
1223 assert_difference 'Issue.count' do
1223 assert_difference 'Issue.count' do
1224 post :create, :project_id => project.id,
1224 post :create, :project_id => project.id,
1225 :issue => {:tracker_id => 3,
1225 :issue => {:tracker_id => 3,
1226 :status_id => 1,
1226 :status_id => 1,
1227 :subject => 'This is the test_new_with_group_assignment issue',
1227 :subject => 'This is the test_new_with_group_assignment issue',
1228 :assigned_to_id => group.id}
1228 :assigned_to_id => group.id}
1229 end
1229 end
1230 end
1230 end
1231 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1231 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1232
1232
1233 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
1233 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
1234 assert_not_nil issue
1234 assert_not_nil issue
1235 assert_equal group, issue.assigned_to
1235 assert_equal group, issue.assigned_to
1236 end
1236 end
1237
1237
1238 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
1238 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
1239 Setting.default_issue_start_date_to_creation_date = 0
1239 Setting.default_issue_start_date_to_creation_date = 0
1240
1240
1241 @request.session[:user_id] = 2
1241 @request.session[:user_id] = 2
1242 assert_difference 'Issue.count' do
1242 assert_difference 'Issue.count' do
1243 post :create, :project_id => 1,
1243 post :create, :project_id => 1,
1244 :issue => {:tracker_id => 3,
1244 :issue => {:tracker_id => 3,
1245 :status_id => 2,
1245 :status_id => 2,
1246 :subject => 'This is the test_new issue',
1246 :subject => 'This is the test_new issue',
1247 :description => 'This is the description',
1247 :description => 'This is the description',
1248 :priority_id => 5,
1248 :priority_id => 5,
1249 :estimated_hours => '',
1249 :estimated_hours => '',
1250 :custom_field_values => {'2' => 'Value for field 2'}}
1250 :custom_field_values => {'2' => 'Value for field 2'}}
1251 end
1251 end
1252 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1252 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1253
1253
1254 issue = Issue.find_by_subject('This is the test_new issue')
1254 issue = Issue.find_by_subject('This is the test_new issue')
1255 assert_not_nil issue
1255 assert_not_nil issue
1256 assert_nil issue.start_date
1256 assert_nil issue.start_date
1257 end
1257 end
1258
1258
1259 def test_post_create_without_start_date_and_default_start_date_is_creation_date
1259 def test_post_create_without_start_date_and_default_start_date_is_creation_date
1260 Setting.default_issue_start_date_to_creation_date = 1
1260 Setting.default_issue_start_date_to_creation_date = 1
1261
1261
1262 @request.session[:user_id] = 2
1262 @request.session[:user_id] = 2
1263 assert_difference 'Issue.count' do
1263 assert_difference 'Issue.count' do
1264 post :create, :project_id => 1,
1264 post :create, :project_id => 1,
1265 :issue => {:tracker_id => 3,
1265 :issue => {:tracker_id => 3,
1266 :status_id => 2,
1266 :status_id => 2,
1267 :subject => 'This is the test_new issue',
1267 :subject => 'This is the test_new issue',
1268 :description => 'This is the description',
1268 :description => 'This is the description',
1269 :priority_id => 5,
1269 :priority_id => 5,
1270 :estimated_hours => '',
1270 :estimated_hours => '',
1271 :custom_field_values => {'2' => 'Value for field 2'}}
1271 :custom_field_values => {'2' => 'Value for field 2'}}
1272 end
1272 end
1273 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1273 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1274
1274
1275 issue = Issue.find_by_subject('This is the test_new issue')
1275 issue = Issue.find_by_subject('This is the test_new issue')
1276 assert_not_nil issue
1276 assert_not_nil issue
1277 assert_equal Date.today, issue.start_date
1277 assert_equal Date.today, issue.start_date
1278 end
1278 end
1279
1279
1280 def test_post_create_and_continue
1280 def test_post_create_and_continue
1281 @request.session[:user_id] = 2
1281 @request.session[:user_id] = 2
1282 assert_difference 'Issue.count' do
1282 assert_difference 'Issue.count' do
1283 post :create, :project_id => 1,
1283 post :create, :project_id => 1,
1284 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
1284 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
1285 :continue => ''
1285 :continue => ''
1286 end
1286 end
1287
1287
1288 issue = Issue.first(:order => 'id DESC')
1288 issue = Issue.first(:order => 'id DESC')
1289 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
1289 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
1290 assert_not_nil flash[:notice], "flash was not set"
1290 assert_not_nil flash[:notice], "flash was not set"
1291 assert flash[:notice].include?("<a href='/issues/#{issue.id}'>##{issue.id}</a>"), "issue link not found in flash: #{flash[:notice]}"
1291 assert flash[:notice].include?("<a href='/issues/#{issue.id}'>##{issue.id}</a>"), "issue link not found in flash: #{flash[:notice]}"
1292 end
1292 end
1293
1293
1294 def test_post_create_without_custom_fields_param
1294 def test_post_create_without_custom_fields_param
1295 @request.session[:user_id] = 2
1295 @request.session[:user_id] = 2
1296 assert_difference 'Issue.count' do
1296 assert_difference 'Issue.count' do
1297 post :create, :project_id => 1,
1297 post :create, :project_id => 1,
1298 :issue => {:tracker_id => 1,
1298 :issue => {:tracker_id => 1,
1299 :subject => 'This is the test_new issue',
1299 :subject => 'This is the test_new issue',
1300 :description => 'This is the description',
1300 :description => 'This is the description',
1301 :priority_id => 5}
1301 :priority_id => 5}
1302 end
1302 end
1303 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1303 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1304 end
1304 end
1305
1305
1306 def test_post_create_with_required_custom_field_and_without_custom_fields_param
1306 def test_post_create_with_required_custom_field_and_without_custom_fields_param
1307 field = IssueCustomField.find_by_name('Database')
1307 field = IssueCustomField.find_by_name('Database')
1308 field.update_attribute(:is_required, true)
1308 field.update_attribute(:is_required, true)
1309
1309
1310 @request.session[:user_id] = 2
1310 @request.session[:user_id] = 2
1311 post :create, :project_id => 1,
1311 assert_no_difference 'Issue.count' do
1312 :issue => {:tracker_id => 1,
1312 post :create, :project_id => 1,
1313 :subject => 'This is the test_new issue',
1313 :issue => {:tracker_id => 1,
1314 :description => 'This is the description',
1314 :subject => 'This is the test_new issue',
1315 :priority_id => 5}
1315 :description => 'This is the description',
1316 :priority_id => 5}
1317 end
1316 assert_response :success
1318 assert_response :success
1317 assert_template 'new'
1319 assert_template 'new'
1318 issue = assigns(:issue)
1320 issue = assigns(:issue)
1319 assert_not_nil issue
1321 assert_not_nil issue
1320 assert_equal I18n.translate('activerecord.errors.messages.invalid'),
1322 assert_error_tag :content => /Database can't be blank/
1321 issue.errors[:custom_values].to_s
1322 end
1323 end
1323
1324
1324 def test_post_create_with_watchers
1325 def test_post_create_with_watchers
1325 @request.session[:user_id] = 2
1326 @request.session[:user_id] = 2
1326 ActionMailer::Base.deliveries.clear
1327 ActionMailer::Base.deliveries.clear
1327
1328
1328 assert_difference 'Watcher.count', 2 do
1329 assert_difference 'Watcher.count', 2 do
1329 post :create, :project_id => 1,
1330 post :create, :project_id => 1,
1330 :issue => {:tracker_id => 1,
1331 :issue => {:tracker_id => 1,
1331 :subject => 'This is a new issue with watchers',
1332 :subject => 'This is a new issue with watchers',
1332 :description => 'This is the description',
1333 :description => 'This is the description',
1333 :priority_id => 5,
1334 :priority_id => 5,
1334 :watcher_user_ids => ['2', '3']}
1335 :watcher_user_ids => ['2', '3']}
1335 end
1336 end
1336 issue = Issue.find_by_subject('This is a new issue with watchers')
1337 issue = Issue.find_by_subject('This is a new issue with watchers')
1337 assert_not_nil issue
1338 assert_not_nil issue
1338 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1339 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1339
1340
1340 # Watchers added
1341 # Watchers added
1341 assert_equal [2, 3], issue.watcher_user_ids.sort
1342 assert_equal [2, 3], issue.watcher_user_ids.sort
1342 assert issue.watched_by?(User.find(3))
1343 assert issue.watched_by?(User.find(3))
1343 # Watchers notified
1344 # Watchers notified
1344 mail = ActionMailer::Base.deliveries.last
1345 mail = ActionMailer::Base.deliveries.last
1345 assert_kind_of TMail::Mail, mail
1346 assert_kind_of TMail::Mail, mail
1346 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
1347 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
1347 end
1348 end
1348
1349
1349 def test_post_create_subissue
1350 def test_post_create_subissue
1350 @request.session[:user_id] = 2
1351 @request.session[:user_id] = 2
1351
1352
1352 assert_difference 'Issue.count' do
1353 assert_difference 'Issue.count' do
1353 post :create, :project_id => 1,
1354 post :create, :project_id => 1,
1354 :issue => {:tracker_id => 1,
1355 :issue => {:tracker_id => 1,
1355 :subject => 'This is a child issue',
1356 :subject => 'This is a child issue',
1356 :parent_issue_id => 2}
1357 :parent_issue_id => 2}
1357 end
1358 end
1358 issue = Issue.find_by_subject('This is a child issue')
1359 issue = Issue.find_by_subject('This is a child issue')
1359 assert_not_nil issue
1360 assert_not_nil issue
1360 assert_equal Issue.find(2), issue.parent
1361 assert_equal Issue.find(2), issue.parent
1361 end
1362 end
1362
1363
1363 def test_post_create_subissue_with_non_numeric_parent_id
1364 def test_post_create_subissue_with_non_numeric_parent_id
1364 @request.session[:user_id] = 2
1365 @request.session[:user_id] = 2
1365
1366
1366 assert_difference 'Issue.count' do
1367 assert_difference 'Issue.count' do
1367 post :create, :project_id => 1,
1368 post :create, :project_id => 1,
1368 :issue => {:tracker_id => 1,
1369 :issue => {:tracker_id => 1,
1369 :subject => 'This is a child issue',
1370 :subject => 'This is a child issue',
1370 :parent_issue_id => 'ABC'}
1371 :parent_issue_id => 'ABC'}
1371 end
1372 end
1372 issue = Issue.find_by_subject('This is a child issue')
1373 issue = Issue.find_by_subject('This is a child issue')
1373 assert_not_nil issue
1374 assert_not_nil issue
1374 assert_nil issue.parent
1375 assert_nil issue.parent
1375 end
1376 end
1376
1377
1377 def test_post_create_private
1378 def test_post_create_private
1378 @request.session[:user_id] = 2
1379 @request.session[:user_id] = 2
1379
1380
1380 assert_difference 'Issue.count' do
1381 assert_difference 'Issue.count' do
1381 post :create, :project_id => 1,
1382 post :create, :project_id => 1,
1382 :issue => {:tracker_id => 1,
1383 :issue => {:tracker_id => 1,
1383 :subject => 'This is a private issue',
1384 :subject => 'This is a private issue',
1384 :is_private => '1'}
1385 :is_private => '1'}
1385 end
1386 end
1386 issue = Issue.first(:order => 'id DESC')
1387 issue = Issue.first(:order => 'id DESC')
1387 assert issue.is_private?
1388 assert issue.is_private?
1388 end
1389 end
1389
1390
1390 def test_post_create_private_with_set_own_issues_private_permission
1391 def test_post_create_private_with_set_own_issues_private_permission
1391 role = Role.find(1)
1392 role = Role.find(1)
1392 role.remove_permission! :set_issues_private
1393 role.remove_permission! :set_issues_private
1393 role.add_permission! :set_own_issues_private
1394 role.add_permission! :set_own_issues_private
1394
1395
1395 @request.session[:user_id] = 2
1396 @request.session[:user_id] = 2
1396
1397
1397 assert_difference 'Issue.count' do
1398 assert_difference 'Issue.count' do
1398 post :create, :project_id => 1,
1399 post :create, :project_id => 1,
1399 :issue => {:tracker_id => 1,
1400 :issue => {:tracker_id => 1,
1400 :subject => 'This is a private issue',
1401 :subject => 'This is a private issue',
1401 :is_private => '1'}
1402 :is_private => '1'}
1402 end
1403 end
1403 issue = Issue.first(:order => 'id DESC')
1404 issue = Issue.first(:order => 'id DESC')
1404 assert issue.is_private?
1405 assert issue.is_private?
1405 end
1406 end
1406
1407
1407 def test_post_create_should_send_a_notification
1408 def test_post_create_should_send_a_notification
1408 ActionMailer::Base.deliveries.clear
1409 ActionMailer::Base.deliveries.clear
1409 @request.session[:user_id] = 2
1410 @request.session[:user_id] = 2
1410 assert_difference 'Issue.count' do
1411 assert_difference 'Issue.count' do
1411 post :create, :project_id => 1,
1412 post :create, :project_id => 1,
1412 :issue => {:tracker_id => 3,
1413 :issue => {:tracker_id => 3,
1413 :subject => 'This is the test_new issue',
1414 :subject => 'This is the test_new issue',
1414 :description => 'This is the description',
1415 :description => 'This is the description',
1415 :priority_id => 5,
1416 :priority_id => 5,
1416 :estimated_hours => '',
1417 :estimated_hours => '',
1417 :custom_field_values => {'2' => 'Value for field 2'}}
1418 :custom_field_values => {'2' => 'Value for field 2'}}
1418 end
1419 end
1419 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1420 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1420
1421
1421 assert_equal 1, ActionMailer::Base.deliveries.size
1422 assert_equal 1, ActionMailer::Base.deliveries.size
1422 end
1423 end
1423
1424
1424 def test_post_create_should_preserve_fields_values_on_validation_failure
1425 def test_post_create_should_preserve_fields_values_on_validation_failure
1425 @request.session[:user_id] = 2
1426 @request.session[:user_id] = 2
1426 post :create, :project_id => 1,
1427 post :create, :project_id => 1,
1427 :issue => {:tracker_id => 1,
1428 :issue => {:tracker_id => 1,
1428 # empty subject
1429 # empty subject
1429 :subject => '',
1430 :subject => '',
1430 :description => 'This is a description',
1431 :description => 'This is a description',
1431 :priority_id => 6,
1432 :priority_id => 6,
1432 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
1433 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
1433 assert_response :success
1434 assert_response :success
1434 assert_template 'new'
1435 assert_template 'new'
1435
1436
1436 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
1437 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
1437 :content => 'This is a description'
1438 :content => 'This is a description'
1438 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1439 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1439 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1440 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1440 :value => '6' },
1441 :value => '6' },
1441 :content => 'High' }
1442 :content => 'High' }
1442 # Custom fields
1443 # Custom fields
1443 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
1444 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
1444 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1445 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1445 :value => 'Oracle' },
1446 :value => 'Oracle' },
1446 :content => 'Oracle' }
1447 :content => 'Oracle' }
1447 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
1448 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
1448 :value => 'Value for field 2'}
1449 :value => 'Value for field 2'}
1449 end
1450 end
1450
1451
1451 def test_post_create_should_ignore_non_safe_attributes
1452 def test_post_create_should_ignore_non_safe_attributes
1452 @request.session[:user_id] = 2
1453 @request.session[:user_id] = 2
1453 assert_nothing_raised do
1454 assert_nothing_raised do
1454 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
1455 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
1455 end
1456 end
1456 end
1457 end
1457
1458
1458 def test_post_create_with_attachment
1459 def test_post_create_with_attachment
1459 set_tmp_attachments_directory
1460 set_tmp_attachments_directory
1460 @request.session[:user_id] = 2
1461 @request.session[:user_id] = 2
1461
1462
1462 assert_difference 'Issue.count' do
1463 assert_difference 'Issue.count' do
1463 assert_difference 'Attachment.count' do
1464 assert_difference 'Attachment.count' do
1464 post :create, :project_id => 1,
1465 post :create, :project_id => 1,
1465 :issue => { :tracker_id => '1', :subject => 'With attachment' },
1466 :issue => { :tracker_id => '1', :subject => 'With attachment' },
1466 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1467 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1467 end
1468 end
1468 end
1469 end
1469
1470
1470 issue = Issue.first(:order => 'id DESC')
1471 issue = Issue.first(:order => 'id DESC')
1471 attachment = Attachment.first(:order => 'id DESC')
1472 attachment = Attachment.first(:order => 'id DESC')
1472
1473
1473 assert_equal issue, attachment.container
1474 assert_equal issue, attachment.container
1474 assert_equal 2, attachment.author_id
1475 assert_equal 2, attachment.author_id
1475 assert_equal 'testfile.txt', attachment.filename
1476 assert_equal 'testfile.txt', attachment.filename
1476 assert_equal 'text/plain', attachment.content_type
1477 assert_equal 'text/plain', attachment.content_type
1477 assert_equal 'test file', attachment.description
1478 assert_equal 'test file', attachment.description
1478 assert_equal 59, attachment.filesize
1479 assert_equal 59, attachment.filesize
1479 assert File.exists?(attachment.diskfile)
1480 assert File.exists?(attachment.diskfile)
1480 assert_equal 59, File.size(attachment.diskfile)
1481 assert_equal 59, File.size(attachment.diskfile)
1481 end
1482 end
1482
1483
1483 context "without workflow privilege" do
1484 context "without workflow privilege" do
1484 setup do
1485 setup do
1485 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1486 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1486 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1487 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1487 end
1488 end
1488
1489
1489 context "#new" do
1490 context "#new" do
1490 should "propose default status only" do
1491 should "propose default status only" do
1491 get :new, :project_id => 1
1492 get :new, :project_id => 1
1492 assert_response :success
1493 assert_response :success
1493 assert_template 'new'
1494 assert_template 'new'
1494 assert_tag :tag => 'select',
1495 assert_tag :tag => 'select',
1495 :attributes => {:name => 'issue[status_id]'},
1496 :attributes => {:name => 'issue[status_id]'},
1496 :children => {:count => 1},
1497 :children => {:count => 1},
1497 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
1498 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
1498 end
1499 end
1499
1500
1500 should "accept default status" do
1501 should "accept default status" do
1501 assert_difference 'Issue.count' do
1502 assert_difference 'Issue.count' do
1502 post :create, :project_id => 1,
1503 post :create, :project_id => 1,
1503 :issue => {:tracker_id => 1,
1504 :issue => {:tracker_id => 1,
1504 :subject => 'This is an issue',
1505 :subject => 'This is an issue',
1505 :status_id => 1}
1506 :status_id => 1}
1506 end
1507 end
1507 issue = Issue.last(:order => 'id')
1508 issue = Issue.last(:order => 'id')
1508 assert_equal IssueStatus.default, issue.status
1509 assert_equal IssueStatus.default, issue.status
1509 end
1510 end
1510
1511
1511 should "ignore unauthorized status" do
1512 should "ignore unauthorized status" do
1512 assert_difference 'Issue.count' do
1513 assert_difference 'Issue.count' do
1513 post :create, :project_id => 1,
1514 post :create, :project_id => 1,
1514 :issue => {:tracker_id => 1,
1515 :issue => {:tracker_id => 1,
1515 :subject => 'This is an issue',
1516 :subject => 'This is an issue',
1516 :status_id => 3}
1517 :status_id => 3}
1517 end
1518 end
1518 issue = Issue.last(:order => 'id')
1519 issue = Issue.last(:order => 'id')
1519 assert_equal IssueStatus.default, issue.status
1520 assert_equal IssueStatus.default, issue.status
1520 end
1521 end
1521 end
1522 end
1522
1523
1523 context "#update" do
1524 context "#update" do
1524 should "ignore status change" do
1525 should "ignore status change" do
1525 assert_difference 'Journal.count' do
1526 assert_difference 'Journal.count' do
1526 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1527 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1527 end
1528 end
1528 assert_equal 1, Issue.find(1).status_id
1529 assert_equal 1, Issue.find(1).status_id
1529 end
1530 end
1530
1531
1531 should "ignore attributes changes" do
1532 should "ignore attributes changes" do
1532 assert_difference 'Journal.count' do
1533 assert_difference 'Journal.count' do
1533 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1534 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1534 end
1535 end
1535 issue = Issue.find(1)
1536 issue = Issue.find(1)
1536 assert_equal "Can't print recipes", issue.subject
1537 assert_equal "Can't print recipes", issue.subject
1537 assert_nil issue.assigned_to
1538 assert_nil issue.assigned_to
1538 end
1539 end
1539 end
1540 end
1540 end
1541 end
1541
1542
1542 context "with workflow privilege" do
1543 context "with workflow privilege" do
1543 setup do
1544 setup do
1544 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1545 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1545 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
1546 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
1546 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
1547 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
1547 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1548 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1548 end
1549 end
1549
1550
1550 context "#update" do
1551 context "#update" do
1551 should "accept authorized status" do
1552 should "accept authorized status" do
1552 assert_difference 'Journal.count' do
1553 assert_difference 'Journal.count' do
1553 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1554 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1554 end
1555 end
1555 assert_equal 3, Issue.find(1).status_id
1556 assert_equal 3, Issue.find(1).status_id
1556 end
1557 end
1557
1558
1558 should "ignore unauthorized status" do
1559 should "ignore unauthorized status" do
1559 assert_difference 'Journal.count' do
1560 assert_difference 'Journal.count' do
1560 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1561 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1561 end
1562 end
1562 assert_equal 1, Issue.find(1).status_id
1563 assert_equal 1, Issue.find(1).status_id
1563 end
1564 end
1564
1565
1565 should "accept authorized attributes changes" do
1566 should "accept authorized attributes changes" do
1566 assert_difference 'Journal.count' do
1567 assert_difference 'Journal.count' do
1567 put :update, :id => 1, :notes => 'just trying', :issue => {:assigned_to_id => 2}
1568 put :update, :id => 1, :notes => 'just trying', :issue => {:assigned_to_id => 2}
1568 end
1569 end
1569 issue = Issue.find(1)
1570 issue = Issue.find(1)
1570 assert_equal 2, issue.assigned_to_id
1571 assert_equal 2, issue.assigned_to_id
1571 end
1572 end
1572
1573
1573 should "ignore unauthorized attributes changes" do
1574 should "ignore unauthorized attributes changes" do
1574 assert_difference 'Journal.count' do
1575 assert_difference 'Journal.count' do
1575 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed'}
1576 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed'}
1576 end
1577 end
1577 issue = Issue.find(1)
1578 issue = Issue.find(1)
1578 assert_equal "Can't print recipes", issue.subject
1579 assert_equal "Can't print recipes", issue.subject
1579 end
1580 end
1580 end
1581 end
1581
1582
1582 context "and :edit_issues permission" do
1583 context "and :edit_issues permission" do
1583 setup do
1584 setup do
1584 Role.anonymous.add_permission! :add_issues, :edit_issues
1585 Role.anonymous.add_permission! :add_issues, :edit_issues
1585 end
1586 end
1586
1587
1587 should "accept authorized status" do
1588 should "accept authorized status" do
1588 assert_difference 'Journal.count' do
1589 assert_difference 'Journal.count' do
1589 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1590 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1590 end
1591 end
1591 assert_equal 3, Issue.find(1).status_id
1592 assert_equal 3, Issue.find(1).status_id
1592 end
1593 end
1593
1594
1594 should "ignore unauthorized status" do
1595 should "ignore unauthorized status" do
1595 assert_difference 'Journal.count' do
1596 assert_difference 'Journal.count' do
1596 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1597 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1597 end
1598 end
1598 assert_equal 1, Issue.find(1).status_id
1599 assert_equal 1, Issue.find(1).status_id
1599 end
1600 end
1600
1601
1601 should "accept authorized attributes changes" do
1602 should "accept authorized attributes changes" do
1602 assert_difference 'Journal.count' do
1603 assert_difference 'Journal.count' do
1603 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1604 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1604 end
1605 end
1605 issue = Issue.find(1)
1606 issue = Issue.find(1)
1606 assert_equal "changed", issue.subject
1607 assert_equal "changed", issue.subject
1607 assert_equal 2, issue.assigned_to_id
1608 assert_equal 2, issue.assigned_to_id
1608 end
1609 end
1609 end
1610 end
1610 end
1611 end
1611
1612
1612 def test_new_as_copy
1613 def test_new_as_copy
1613 @request.session[:user_id] = 2
1614 @request.session[:user_id] = 2
1614 get :new, :project_id => 1, :copy_from => 1
1615 get :new, :project_id => 1, :copy_from => 1
1615
1616
1616 assert_response :success
1617 assert_response :success
1617 assert_template 'new'
1618 assert_template 'new'
1618
1619
1619 assert_not_nil assigns(:issue)
1620 assert_not_nil assigns(:issue)
1620 orig = Issue.find(1)
1621 orig = Issue.find(1)
1621 assert_equal 1, assigns(:issue).project_id
1622 assert_equal 1, assigns(:issue).project_id
1622 assert_equal orig.subject, assigns(:issue).subject
1623 assert_equal orig.subject, assigns(:issue).subject
1623 assert assigns(:issue).copy?
1624 assert assigns(:issue).copy?
1624
1625
1625 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1626 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1626 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1627 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1627 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1628 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1628 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}, :content => 'eCookbook'}
1629 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}, :content => 'eCookbook'}
1629 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1630 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1630 :child => {:tag => 'option', :attributes => {:value => '2', :selected => nil}, :content => 'OnlineStore'}
1631 :child => {:tag => 'option', :attributes => {:value => '2', :selected => nil}, :content => 'OnlineStore'}
1631 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1632 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1632 end
1633 end
1633
1634
1634 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
1635 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
1635 @request.session[:user_id] = 2
1636 @request.session[:user_id] = 2
1636 issue = Issue.find(3)
1637 issue = Issue.find(3)
1637 assert issue.attachments.count > 0
1638 assert issue.attachments.count > 0
1638 get :new, :project_id => 1, :copy_from => 3
1639 get :new, :project_id => 1, :copy_from => 3
1639
1640
1640 assert_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
1641 assert_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
1641 end
1642 end
1642
1643
1643 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
1644 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
1644 @request.session[:user_id] = 2
1645 @request.session[:user_id] = 2
1645 issue = Issue.find(3)
1646 issue = Issue.find(3)
1646 issue.attachments.delete_all
1647 issue.attachments.delete_all
1647 get :new, :project_id => 1, :copy_from => 3
1648 get :new, :project_id => 1, :copy_from => 3
1648
1649
1649 assert_no_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
1650 assert_no_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
1650 end
1651 end
1651
1652
1652 def test_new_as_copy_with_invalid_issue_should_respond_with_404
1653 def test_new_as_copy_with_invalid_issue_should_respond_with_404
1653 @request.session[:user_id] = 2
1654 @request.session[:user_id] = 2
1654 get :new, :project_id => 1, :copy_from => 99999
1655 get :new, :project_id => 1, :copy_from => 99999
1655 assert_response 404
1656 assert_response 404
1656 end
1657 end
1657
1658
1658 def test_create_as_copy_on_different_project
1659 def test_create_as_copy_on_different_project
1659 @request.session[:user_id] = 2
1660 @request.session[:user_id] = 2
1660 assert_difference 'Issue.count' do
1661 assert_difference 'Issue.count' do
1661 post :create, :project_id => 1, :copy_from => 1,
1662 post :create, :project_id => 1, :copy_from => 1,
1662 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1663 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1663
1664
1664 assert_not_nil assigns(:issue)
1665 assert_not_nil assigns(:issue)
1665 assert assigns(:issue).copy?
1666 assert assigns(:issue).copy?
1666 end
1667 end
1667 issue = Issue.first(:order => 'id DESC')
1668 issue = Issue.first(:order => 'id DESC')
1668 assert_redirected_to "/issues/#{issue.id}"
1669 assert_redirected_to "/issues/#{issue.id}"
1669
1670
1670 assert_equal 2, issue.project_id
1671 assert_equal 2, issue.project_id
1671 assert_equal 3, issue.tracker_id
1672 assert_equal 3, issue.tracker_id
1672 assert_equal 'Copy', issue.subject
1673 assert_equal 'Copy', issue.subject
1673 end
1674 end
1674
1675
1675 def test_create_as_copy_should_copy_attachments
1676 def test_create_as_copy_should_copy_attachments
1676 @request.session[:user_id] = 2
1677 @request.session[:user_id] = 2
1677 issue = Issue.find(3)
1678 issue = Issue.find(3)
1678 count = issue.attachments.count
1679 count = issue.attachments.count
1679 assert count > 0
1680 assert count > 0
1680
1681
1681 assert_difference 'Issue.count' do
1682 assert_difference 'Issue.count' do
1682 assert_difference 'Attachment.count', count do
1683 assert_difference 'Attachment.count', count do
1683 assert_no_difference 'Journal.count' do
1684 assert_no_difference 'Journal.count' do
1684 post :create, :project_id => 1, :copy_from => 3,
1685 post :create, :project_id => 1, :copy_from => 3,
1685 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
1686 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
1686 :copy_attachments => '1'
1687 :copy_attachments => '1'
1687 end
1688 end
1688 end
1689 end
1689 end
1690 end
1690 copy = Issue.first(:order => 'id DESC')
1691 copy = Issue.first(:order => 'id DESC')
1691 assert_equal count, copy.attachments.count
1692 assert_equal count, copy.attachments.count
1692 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
1693 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
1693 end
1694 end
1694
1695
1695 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
1696 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
1696 @request.session[:user_id] = 2
1697 @request.session[:user_id] = 2
1697 issue = Issue.find(3)
1698 issue = Issue.find(3)
1698 count = issue.attachments.count
1699 count = issue.attachments.count
1699 assert count > 0
1700 assert count > 0
1700
1701
1701 assert_difference 'Issue.count' do
1702 assert_difference 'Issue.count' do
1702 assert_no_difference 'Attachment.count' do
1703 assert_no_difference 'Attachment.count' do
1703 assert_no_difference 'Journal.count' do
1704 assert_no_difference 'Journal.count' do
1704 post :create, :project_id => 1, :copy_from => 3,
1705 post :create, :project_id => 1, :copy_from => 3,
1705 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'}
1706 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'}
1706 end
1707 end
1707 end
1708 end
1708 end
1709 end
1709 copy = Issue.first(:order => 'id DESC')
1710 copy = Issue.first(:order => 'id DESC')
1710 assert_equal 0, copy.attachments.count
1711 assert_equal 0, copy.attachments.count
1711 end
1712 end
1712
1713
1713 def test_create_as_copy_with_attachments_should_add_new_files
1714 def test_create_as_copy_with_attachments_should_add_new_files
1714 @request.session[:user_id] = 2
1715 @request.session[:user_id] = 2
1715 issue = Issue.find(3)
1716 issue = Issue.find(3)
1716 count = issue.attachments.count
1717 count = issue.attachments.count
1717 assert count > 0
1718 assert count > 0
1718
1719
1719 assert_difference 'Issue.count' do
1720 assert_difference 'Issue.count' do
1720 assert_difference 'Attachment.count', count + 1 do
1721 assert_difference 'Attachment.count', count + 1 do
1721 assert_no_difference 'Journal.count' do
1722 assert_no_difference 'Journal.count' do
1722 post :create, :project_id => 1, :copy_from => 3,
1723 post :create, :project_id => 1, :copy_from => 3,
1723 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
1724 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
1724 :copy_attachments => '1',
1725 :copy_attachments => '1',
1725 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1726 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1726 end
1727 end
1727 end
1728 end
1728 end
1729 end
1729 copy = Issue.first(:order => 'id DESC')
1730 copy = Issue.first(:order => 'id DESC')
1730 assert_equal count + 1, copy.attachments.count
1731 assert_equal count + 1, copy.attachments.count
1731 end
1732 end
1732
1733
1733 def test_create_as_copy_with_failure
1734 def test_create_as_copy_with_failure
1734 @request.session[:user_id] = 2
1735 @request.session[:user_id] = 2
1735 post :create, :project_id => 1, :copy_from => 1,
1736 post :create, :project_id => 1, :copy_from => 1,
1736 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
1737 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
1737
1738
1738 assert_response :success
1739 assert_response :success
1739 assert_template 'new'
1740 assert_template 'new'
1740
1741
1741 assert_not_nil assigns(:issue)
1742 assert_not_nil assigns(:issue)
1742 assert assigns(:issue).copy?
1743 assert assigns(:issue).copy?
1743
1744
1744 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1745 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1745 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1746 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1746 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1747 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1747 :child => {:tag => 'option', :attributes => {:value => '1', :selected => nil}, :content => 'eCookbook'}
1748 :child => {:tag => 'option', :attributes => {:value => '1', :selected => nil}, :content => 'eCookbook'}
1748 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1749 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1749 :child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}, :content => 'OnlineStore'}
1750 :child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}, :content => 'OnlineStore'}
1750 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1751 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1751 end
1752 end
1752
1753
1753 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
1754 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
1754 @request.session[:user_id] = 2
1755 @request.session[:user_id] = 2
1755 assert !User.find(2).member_of?(Project.find(4))
1756 assert !User.find(2).member_of?(Project.find(4))
1756
1757
1757 assert_difference 'Issue.count' do
1758 assert_difference 'Issue.count' do
1758 post :create, :project_id => 1, :copy_from => 1,
1759 post :create, :project_id => 1, :copy_from => 1,
1759 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1760 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1760 end
1761 end
1761 issue = Issue.first(:order => 'id DESC')
1762 issue = Issue.first(:order => 'id DESC')
1762 assert_equal 1, issue.project_id
1763 assert_equal 1, issue.project_id
1763 end
1764 end
1764
1765
1765 def test_get_edit
1766 def test_get_edit
1766 @request.session[:user_id] = 2
1767 @request.session[:user_id] = 2
1767 get :edit, :id => 1
1768 get :edit, :id => 1
1768 assert_response :success
1769 assert_response :success
1769 assert_template 'edit'
1770 assert_template 'edit'
1770 assert_not_nil assigns(:issue)
1771 assert_not_nil assigns(:issue)
1771 assert_equal Issue.find(1), assigns(:issue)
1772 assert_equal Issue.find(1), assigns(:issue)
1772
1773
1773 # Be sure we don't display inactive IssuePriorities
1774 # Be sure we don't display inactive IssuePriorities
1774 assert ! IssuePriority.find(15).active?
1775 assert ! IssuePriority.find(15).active?
1775 assert_no_tag :option, :attributes => {:value => '15'},
1776 assert_no_tag :option, :attributes => {:value => '15'},
1776 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1777 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1777 end
1778 end
1778
1779
1779 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
1780 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
1780 @request.session[:user_id] = 2
1781 @request.session[:user_id] = 2
1781 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
1782 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
1782
1783
1783 get :edit, :id => 1
1784 get :edit, :id => 1
1784 assert_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1785 assert_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1785 end
1786 end
1786
1787
1787 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
1788 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
1788 @request.session[:user_id] = 2
1789 @request.session[:user_id] = 2
1789 Role.find_by_name('Manager').remove_permission! :log_time
1790 Role.find_by_name('Manager').remove_permission! :log_time
1790
1791
1791 get :edit, :id => 1
1792 get :edit, :id => 1
1792 assert_no_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1793 assert_no_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1793 end
1794 end
1794
1795
1795 def test_get_edit_with_params
1796 def test_get_edit_with_params
1796 @request.session[:user_id] = 2
1797 @request.session[:user_id] = 2
1797 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
1798 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
1798 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
1799 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
1799 assert_response :success
1800 assert_response :success
1800 assert_template 'edit'
1801 assert_template 'edit'
1801
1802
1802 issue = assigns(:issue)
1803 issue = assigns(:issue)
1803 assert_not_nil issue
1804 assert_not_nil issue
1804
1805
1805 assert_equal 5, issue.status_id
1806 assert_equal 5, issue.status_id
1806 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
1807 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
1807 :child => { :tag => 'option',
1808 :child => { :tag => 'option',
1808 :content => 'Closed',
1809 :content => 'Closed',
1809 :attributes => { :selected => 'selected' } }
1810 :attributes => { :selected => 'selected' } }
1810
1811
1811 assert_equal 7, issue.priority_id
1812 assert_equal 7, issue.priority_id
1812 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1813 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1813 :child => { :tag => 'option',
1814 :child => { :tag => 'option',
1814 :content => 'Urgent',
1815 :content => 'Urgent',
1815 :attributes => { :selected => 'selected' } }
1816 :attributes => { :selected => 'selected' } }
1816
1817
1817 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
1818 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
1818 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
1819 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
1819 :child => { :tag => 'option',
1820 :child => { :tag => 'option',
1820 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
1821 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
1821 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
1822 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
1822 end
1823 end
1823
1824
1824 def test_update_edit_form
1825 def test_update_edit_form
1825 @request.session[:user_id] = 2
1826 @request.session[:user_id] = 2
1826 xhr :put, :new, :project_id => 1,
1827 xhr :put, :new, :project_id => 1,
1827 :id => 1,
1828 :id => 1,
1828 :issue => {:tracker_id => 2,
1829 :issue => {:tracker_id => 2,
1829 :subject => 'This is the test_new issue',
1830 :subject => 'This is the test_new issue',
1830 :description => 'This is the description',
1831 :description => 'This is the description',
1831 :priority_id => 5}
1832 :priority_id => 5}
1832 assert_response :success
1833 assert_response :success
1833 assert_template 'attributes'
1834 assert_template 'attributes'
1834
1835
1835 issue = assigns(:issue)
1836 issue = assigns(:issue)
1836 assert_kind_of Issue, issue
1837 assert_kind_of Issue, issue
1837 assert_equal 1, issue.id
1838 assert_equal 1, issue.id
1838 assert_equal 1, issue.project_id
1839 assert_equal 1, issue.project_id
1839 assert_equal 2, issue.tracker_id
1840 assert_equal 2, issue.tracker_id
1840 assert_equal 'This is the test_new issue', issue.subject
1841 assert_equal 'This is the test_new issue', issue.subject
1841 end
1842 end
1842
1843
1843 def test_update_edit_form_with_project_change
1844 def test_update_edit_form_with_project_change
1844 @request.session[:user_id] = 2
1845 @request.session[:user_id] = 2
1845 xhr :put, :new, :project_id => 1,
1846 xhr :put, :new, :project_id => 1,
1846 :id => 1,
1847 :id => 1,
1847 :project_change => '1',
1848 :project_change => '1',
1848 :issue => {:project_id => 2,
1849 :issue => {:project_id => 2,
1849 :tracker_id => 2,
1850 :tracker_id => 2,
1850 :subject => 'This is the test_new issue',
1851 :subject => 'This is the test_new issue',
1851 :description => 'This is the description',
1852 :description => 'This is the description',
1852 :priority_id => 5}
1853 :priority_id => 5}
1853 assert_response :success
1854 assert_response :success
1854 assert_template 'form'
1855 assert_template 'form'
1855
1856
1856 issue = assigns(:issue)
1857 issue = assigns(:issue)
1857 assert_kind_of Issue, issue
1858 assert_kind_of Issue, issue
1858 assert_equal 1, issue.id
1859 assert_equal 1, issue.id
1859 assert_equal 2, issue.project_id
1860 assert_equal 2, issue.project_id
1860 assert_equal 2, issue.tracker_id
1861 assert_equal 2, issue.tracker_id
1861 assert_equal 'This is the test_new issue', issue.subject
1862 assert_equal 'This is the test_new issue', issue.subject
1862 end
1863 end
1863
1864
1864 def test_update_using_invalid_http_verbs
1865 def test_update_using_invalid_http_verbs
1865 @request.session[:user_id] = 2
1866 @request.session[:user_id] = 2
1866 subject = 'Updated by an invalid http verb'
1867 subject = 'Updated by an invalid http verb'
1867
1868
1868 get :update, :id => 1, :issue => {:subject => subject}
1869 get :update, :id => 1, :issue => {:subject => subject}
1869 assert_not_equal subject, Issue.find(1).subject
1870 assert_not_equal subject, Issue.find(1).subject
1870
1871
1871 post :update, :id => 1, :issue => {:subject => subject}
1872 post :update, :id => 1, :issue => {:subject => subject}
1872 assert_not_equal subject, Issue.find(1).subject
1873 assert_not_equal subject, Issue.find(1).subject
1873
1874
1874 delete :update, :id => 1, :issue => {:subject => subject}
1875 delete :update, :id => 1, :issue => {:subject => subject}
1875 assert_not_equal subject, Issue.find(1).subject
1876 assert_not_equal subject, Issue.find(1).subject
1876 end
1877 end
1877
1878
1878 def test_put_update_without_custom_fields_param
1879 def test_put_update_without_custom_fields_param
1879 @request.session[:user_id] = 2
1880 @request.session[:user_id] = 2
1880 ActionMailer::Base.deliveries.clear
1881 ActionMailer::Base.deliveries.clear
1881
1882
1882 issue = Issue.find(1)
1883 issue = Issue.find(1)
1883 assert_equal '125', issue.custom_value_for(2).value
1884 assert_equal '125', issue.custom_value_for(2).value
1884 old_subject = issue.subject
1885 old_subject = issue.subject
1885 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
1886 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
1886
1887
1887 assert_difference('Journal.count') do
1888 assert_difference('Journal.count') do
1888 assert_difference('JournalDetail.count', 2) do
1889 assert_difference('JournalDetail.count', 2) do
1889 put :update, :id => 1, :issue => {:subject => new_subject,
1890 put :update, :id => 1, :issue => {:subject => new_subject,
1890 :priority_id => '6',
1891 :priority_id => '6',
1891 :category_id => '1' # no change
1892 :category_id => '1' # no change
1892 }
1893 }
1893 end
1894 end
1894 end
1895 end
1895 assert_redirected_to :action => 'show', :id => '1'
1896 assert_redirected_to :action => 'show', :id => '1'
1896 issue.reload
1897 issue.reload
1897 assert_equal new_subject, issue.subject
1898 assert_equal new_subject, issue.subject
1898 # Make sure custom fields were not cleared
1899 # Make sure custom fields were not cleared
1899 assert_equal '125', issue.custom_value_for(2).value
1900 assert_equal '125', issue.custom_value_for(2).value
1900
1901
1901 mail = ActionMailer::Base.deliveries.last
1902 mail = ActionMailer::Base.deliveries.last
1902 assert_kind_of TMail::Mail, mail
1903 assert_kind_of TMail::Mail, mail
1903 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1904 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1904 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
1905 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
1905 end
1906 end
1906
1907
1907 def test_put_update_with_project_change
1908 def test_put_update_with_project_change
1908 @request.session[:user_id] = 2
1909 @request.session[:user_id] = 2
1909 ActionMailer::Base.deliveries.clear
1910 ActionMailer::Base.deliveries.clear
1910
1911
1911 assert_difference('Journal.count') do
1912 assert_difference('Journal.count') do
1912 assert_difference('JournalDetail.count', 3) do
1913 assert_difference('JournalDetail.count', 3) do
1913 put :update, :id => 1, :issue => {:project_id => '2',
1914 put :update, :id => 1, :issue => {:project_id => '2',
1914 :tracker_id => '1', # no change
1915 :tracker_id => '1', # no change
1915 :priority_id => '6',
1916 :priority_id => '6',
1916 :category_id => '3'
1917 :category_id => '3'
1917 }
1918 }
1918 end
1919 end
1919 end
1920 end
1920 assert_redirected_to :action => 'show', :id => '1'
1921 assert_redirected_to :action => 'show', :id => '1'
1921 issue = Issue.find(1)
1922 issue = Issue.find(1)
1922 assert_equal 2, issue.project_id
1923 assert_equal 2, issue.project_id
1923 assert_equal 1, issue.tracker_id
1924 assert_equal 1, issue.tracker_id
1924 assert_equal 6, issue.priority_id
1925 assert_equal 6, issue.priority_id
1925 assert_equal 3, issue.category_id
1926 assert_equal 3, issue.category_id
1926
1927
1927 mail = ActionMailer::Base.deliveries.last
1928 mail = ActionMailer::Base.deliveries.last
1928 assert_not_nil mail
1929 assert_not_nil mail
1929 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1930 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1930 assert mail.body.include?("Project changed from eCookbook to OnlineStore")
1931 assert mail.body.include?("Project changed from eCookbook to OnlineStore")
1931 end
1932 end
1932
1933
1933 def test_put_update_with_tracker_change
1934 def test_put_update_with_tracker_change
1934 @request.session[:user_id] = 2
1935 @request.session[:user_id] = 2
1935 ActionMailer::Base.deliveries.clear
1936 ActionMailer::Base.deliveries.clear
1936
1937
1937 assert_difference('Journal.count') do
1938 assert_difference('Journal.count') do
1938 assert_difference('JournalDetail.count', 2) do
1939 assert_difference('JournalDetail.count', 2) do
1939 put :update, :id => 1, :issue => {:project_id => '1',
1940 put :update, :id => 1, :issue => {:project_id => '1',
1940 :tracker_id => '2',
1941 :tracker_id => '2',
1941 :priority_id => '6'
1942 :priority_id => '6'
1942 }
1943 }
1943 end
1944 end
1944 end
1945 end
1945 assert_redirected_to :action => 'show', :id => '1'
1946 assert_redirected_to :action => 'show', :id => '1'
1946 issue = Issue.find(1)
1947 issue = Issue.find(1)
1947 assert_equal 1, issue.project_id
1948 assert_equal 1, issue.project_id
1948 assert_equal 2, issue.tracker_id
1949 assert_equal 2, issue.tracker_id
1949 assert_equal 6, issue.priority_id
1950 assert_equal 6, issue.priority_id
1950 assert_equal 1, issue.category_id
1951 assert_equal 1, issue.category_id
1951
1952
1952 mail = ActionMailer::Base.deliveries.last
1953 mail = ActionMailer::Base.deliveries.last
1953 assert_not_nil mail
1954 assert_not_nil mail
1954 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1955 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1955 assert mail.body.include?("Tracker changed from Bug to Feature request")
1956 assert mail.body.include?("Tracker changed from Bug to Feature request")
1956 end
1957 end
1957
1958
1958 def test_put_update_with_custom_field_change
1959 def test_put_update_with_custom_field_change
1959 @request.session[:user_id] = 2
1960 @request.session[:user_id] = 2
1960 issue = Issue.find(1)
1961 issue = Issue.find(1)
1961 assert_equal '125', issue.custom_value_for(2).value
1962 assert_equal '125', issue.custom_value_for(2).value
1962
1963
1963 assert_difference('Journal.count') do
1964 assert_difference('Journal.count') do
1964 assert_difference('JournalDetail.count', 3) do
1965 assert_difference('JournalDetail.count', 3) do
1965 put :update, :id => 1, :issue => {:subject => 'Custom field change',
1966 put :update, :id => 1, :issue => {:subject => 'Custom field change',
1966 :priority_id => '6',
1967 :priority_id => '6',
1967 :category_id => '1', # no change
1968 :category_id => '1', # no change
1968 :custom_field_values => { '2' => 'New custom value' }
1969 :custom_field_values => { '2' => 'New custom value' }
1969 }
1970 }
1970 end
1971 end
1971 end
1972 end
1972 assert_redirected_to :action => 'show', :id => '1'
1973 assert_redirected_to :action => 'show', :id => '1'
1973 issue.reload
1974 issue.reload
1974 assert_equal 'New custom value', issue.custom_value_for(2).value
1975 assert_equal 'New custom value', issue.custom_value_for(2).value
1975
1976
1976 mail = ActionMailer::Base.deliveries.last
1977 mail = ActionMailer::Base.deliveries.last
1977 assert_kind_of TMail::Mail, mail
1978 assert_kind_of TMail::Mail, mail
1978 assert mail.body.include?("Searchable field changed from 125 to New custom value")
1979 assert mail.body.include?("Searchable field changed from 125 to New custom value")
1979 end
1980 end
1980
1981
1981 def test_put_update_with_status_and_assignee_change
1982 def test_put_update_with_status_and_assignee_change
1982 issue = Issue.find(1)
1983 issue = Issue.find(1)
1983 assert_equal 1, issue.status_id
1984 assert_equal 1, issue.status_id
1984 @request.session[:user_id] = 2
1985 @request.session[:user_id] = 2
1985 assert_difference('TimeEntry.count', 0) do
1986 assert_difference('TimeEntry.count', 0) do
1986 put :update,
1987 put :update,
1987 :id => 1,
1988 :id => 1,
1988 :issue => { :status_id => 2, :assigned_to_id => 3 },
1989 :issue => { :status_id => 2, :assigned_to_id => 3 },
1989 :notes => 'Assigned to dlopper',
1990 :notes => 'Assigned to dlopper',
1990 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
1991 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
1991 end
1992 end
1992 assert_redirected_to :action => 'show', :id => '1'
1993 assert_redirected_to :action => 'show', :id => '1'
1993 issue.reload
1994 issue.reload
1994 assert_equal 2, issue.status_id
1995 assert_equal 2, issue.status_id
1995 j = Journal.find(:first, :order => 'id DESC')
1996 j = Journal.find(:first, :order => 'id DESC')
1996 assert_equal 'Assigned to dlopper', j.notes
1997 assert_equal 'Assigned to dlopper', j.notes
1997 assert_equal 2, j.details.size
1998 assert_equal 2, j.details.size
1998
1999
1999 mail = ActionMailer::Base.deliveries.last
2000 mail = ActionMailer::Base.deliveries.last
2000 assert mail.body.include?("Status changed from New to Assigned")
2001 assert mail.body.include?("Status changed from New to Assigned")
2001 # subject should contain the new status
2002 # subject should contain the new status
2002 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
2003 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
2003 end
2004 end
2004
2005
2005 def test_put_update_with_note_only
2006 def test_put_update_with_note_only
2006 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
2007 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
2007 # anonymous user
2008 # anonymous user
2008 put :update,
2009 put :update,
2009 :id => 1,
2010 :id => 1,
2010 :notes => notes
2011 :notes => notes
2011 assert_redirected_to :action => 'show', :id => '1'
2012 assert_redirected_to :action => 'show', :id => '1'
2012 j = Journal.find(:first, :order => 'id DESC')
2013 j = Journal.find(:first, :order => 'id DESC')
2013 assert_equal notes, j.notes
2014 assert_equal notes, j.notes
2014 assert_equal 0, j.details.size
2015 assert_equal 0, j.details.size
2015 assert_equal User.anonymous, j.user
2016 assert_equal User.anonymous, j.user
2016
2017
2017 mail = ActionMailer::Base.deliveries.last
2018 mail = ActionMailer::Base.deliveries.last
2018 assert mail.body.include?(notes)
2019 assert mail.body.include?(notes)
2019 end
2020 end
2020
2021
2021 def test_put_update_with_note_and_spent_time
2022 def test_put_update_with_note_and_spent_time
2022 @request.session[:user_id] = 2
2023 @request.session[:user_id] = 2
2023 spent_hours_before = Issue.find(1).spent_hours
2024 spent_hours_before = Issue.find(1).spent_hours
2024 assert_difference('TimeEntry.count') do
2025 assert_difference('TimeEntry.count') do
2025 put :update,
2026 put :update,
2026 :id => 1,
2027 :id => 1,
2027 :notes => '2.5 hours added',
2028 :notes => '2.5 hours added',
2028 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
2029 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
2029 end
2030 end
2030 assert_redirected_to :action => 'show', :id => '1'
2031 assert_redirected_to :action => 'show', :id => '1'
2031
2032
2032 issue = Issue.find(1)
2033 issue = Issue.find(1)
2033
2034
2034 j = Journal.find(:first, :order => 'id DESC')
2035 j = Journal.find(:first, :order => 'id DESC')
2035 assert_equal '2.5 hours added', j.notes
2036 assert_equal '2.5 hours added', j.notes
2036 assert_equal 0, j.details.size
2037 assert_equal 0, j.details.size
2037
2038
2038 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
2039 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
2039 assert_not_nil t
2040 assert_not_nil t
2040 assert_equal 2.5, t.hours
2041 assert_equal 2.5, t.hours
2041 assert_equal spent_hours_before + 2.5, issue.spent_hours
2042 assert_equal spent_hours_before + 2.5, issue.spent_hours
2042 end
2043 end
2043
2044
2044 def test_put_update_with_attachment_only
2045 def test_put_update_with_attachment_only
2045 set_tmp_attachments_directory
2046 set_tmp_attachments_directory
2046
2047
2047 # Delete all fixtured journals, a race condition can occur causing the wrong
2048 # Delete all fixtured journals, a race condition can occur causing the wrong
2048 # journal to get fetched in the next find.
2049 # journal to get fetched in the next find.
2049 Journal.delete_all
2050 Journal.delete_all
2050
2051
2051 # anonymous user
2052 # anonymous user
2052 assert_difference 'Attachment.count' do
2053 assert_difference 'Attachment.count' do
2053 put :update, :id => 1,
2054 put :update, :id => 1,
2054 :notes => '',
2055 :notes => '',
2055 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2056 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2056 end
2057 end
2057
2058
2058 assert_redirected_to :action => 'show', :id => '1'
2059 assert_redirected_to :action => 'show', :id => '1'
2059 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
2060 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
2060 assert j.notes.blank?
2061 assert j.notes.blank?
2061 assert_equal 1, j.details.size
2062 assert_equal 1, j.details.size
2062 assert_equal 'testfile.txt', j.details.first.value
2063 assert_equal 'testfile.txt', j.details.first.value
2063 assert_equal User.anonymous, j.user
2064 assert_equal User.anonymous, j.user
2064
2065
2065 attachment = Attachment.first(:order => 'id DESC')
2066 attachment = Attachment.first(:order => 'id DESC')
2066 assert_equal Issue.find(1), attachment.container
2067 assert_equal Issue.find(1), attachment.container
2067 assert_equal User.anonymous, attachment.author
2068 assert_equal User.anonymous, attachment.author
2068 assert_equal 'testfile.txt', attachment.filename
2069 assert_equal 'testfile.txt', attachment.filename
2069 assert_equal 'text/plain', attachment.content_type
2070 assert_equal 'text/plain', attachment.content_type
2070 assert_equal 'test file', attachment.description
2071 assert_equal 'test file', attachment.description
2071 assert_equal 59, attachment.filesize
2072 assert_equal 59, attachment.filesize
2072 assert File.exists?(attachment.diskfile)
2073 assert File.exists?(attachment.diskfile)
2073 assert_equal 59, File.size(attachment.diskfile)
2074 assert_equal 59, File.size(attachment.diskfile)
2074
2075
2075 mail = ActionMailer::Base.deliveries.last
2076 mail = ActionMailer::Base.deliveries.last
2076 assert mail.body.include?('testfile.txt')
2077 assert mail.body.include?('testfile.txt')
2077 end
2078 end
2078
2079
2079 def test_put_update_with_attachment_that_fails_to_save
2080 def test_put_update_with_attachment_that_fails_to_save
2080 set_tmp_attachments_directory
2081 set_tmp_attachments_directory
2081
2082
2082 # Delete all fixtured journals, a race condition can occur causing the wrong
2083 # Delete all fixtured journals, a race condition can occur causing the wrong
2083 # journal to get fetched in the next find.
2084 # journal to get fetched in the next find.
2084 Journal.delete_all
2085 Journal.delete_all
2085
2086
2086 # Mock out the unsaved attachment
2087 # Mock out the unsaved attachment
2087 Attachment.any_instance.stubs(:create).returns(Attachment.new)
2088 Attachment.any_instance.stubs(:create).returns(Attachment.new)
2088
2089
2089 # anonymous user
2090 # anonymous user
2090 put :update,
2091 put :update,
2091 :id => 1,
2092 :id => 1,
2092 :notes => '',
2093 :notes => '',
2093 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
2094 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
2094 assert_redirected_to :action => 'show', :id => '1'
2095 assert_redirected_to :action => 'show', :id => '1'
2095 assert_equal '1 file(s) could not be saved.', flash[:warning]
2096 assert_equal '1 file(s) could not be saved.', flash[:warning]
2096
2097
2097 end if Object.const_defined?(:Mocha)
2098 end if Object.const_defined?(:Mocha)
2098
2099
2099 def test_put_update_with_no_change
2100 def test_put_update_with_no_change
2100 issue = Issue.find(1)
2101 issue = Issue.find(1)
2101 issue.journals.clear
2102 issue.journals.clear
2102 ActionMailer::Base.deliveries.clear
2103 ActionMailer::Base.deliveries.clear
2103
2104
2104 put :update,
2105 put :update,
2105 :id => 1,
2106 :id => 1,
2106 :notes => ''
2107 :notes => ''
2107 assert_redirected_to :action => 'show', :id => '1'
2108 assert_redirected_to :action => 'show', :id => '1'
2108
2109
2109 issue.reload
2110 issue.reload
2110 assert issue.journals.empty?
2111 assert issue.journals.empty?
2111 # No email should be sent
2112 # No email should be sent
2112 assert ActionMailer::Base.deliveries.empty?
2113 assert ActionMailer::Base.deliveries.empty?
2113 end
2114 end
2114
2115
2115 def test_put_update_should_send_a_notification
2116 def test_put_update_should_send_a_notification
2116 @request.session[:user_id] = 2
2117 @request.session[:user_id] = 2
2117 ActionMailer::Base.deliveries.clear
2118 ActionMailer::Base.deliveries.clear
2118 issue = Issue.find(1)
2119 issue = Issue.find(1)
2119 old_subject = issue.subject
2120 old_subject = issue.subject
2120 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
2121 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
2121
2122
2122 put :update, :id => 1, :issue => {:subject => new_subject,
2123 put :update, :id => 1, :issue => {:subject => new_subject,
2123 :priority_id => '6',
2124 :priority_id => '6',
2124 :category_id => '1' # no change
2125 :category_id => '1' # no change
2125 }
2126 }
2126 assert_equal 1, ActionMailer::Base.deliveries.size
2127 assert_equal 1, ActionMailer::Base.deliveries.size
2127 end
2128 end
2128
2129
2129 def test_put_update_with_invalid_spent_time_hours_only
2130 def test_put_update_with_invalid_spent_time_hours_only
2130 @request.session[:user_id] = 2
2131 @request.session[:user_id] = 2
2131 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2132 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2132
2133
2133 assert_no_difference('Journal.count') do
2134 assert_no_difference('Journal.count') do
2134 put :update,
2135 put :update,
2135 :id => 1,
2136 :id => 1,
2136 :notes => notes,
2137 :notes => notes,
2137 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
2138 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
2138 end
2139 end
2139 assert_response :success
2140 assert_response :success
2140 assert_template 'edit'
2141 assert_template 'edit'
2141
2142
2142 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2143 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2143 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2144 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2144 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
2145 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
2145 end
2146 end
2146
2147
2147 def test_put_update_with_invalid_spent_time_comments_only
2148 def test_put_update_with_invalid_spent_time_comments_only
2148 @request.session[:user_id] = 2
2149 @request.session[:user_id] = 2
2149 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2150 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2150
2151
2151 assert_no_difference('Journal.count') do
2152 assert_no_difference('Journal.count') do
2152 put :update,
2153 put :update,
2153 :id => 1,
2154 :id => 1,
2154 :notes => notes,
2155 :notes => notes,
2155 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
2156 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
2156 end
2157 end
2157 assert_response :success
2158 assert_response :success
2158 assert_template 'edit'
2159 assert_template 'edit'
2159
2160
2160 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2161 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2161 assert_error_tag :descendant => {:content => /Hours can't be blank/}
2162 assert_error_tag :descendant => {:content => /Hours can't be blank/}
2162 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2163 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2163 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
2164 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
2164 end
2165 end
2165
2166
2166 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
2167 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
2167 issue = Issue.find(2)
2168 issue = Issue.find(2)
2168 @request.session[:user_id] = 2
2169 @request.session[:user_id] = 2
2169
2170
2170 put :update,
2171 put :update,
2171 :id => issue.id,
2172 :id => issue.id,
2172 :issue => {
2173 :issue => {
2173 :fixed_version_id => 4
2174 :fixed_version_id => 4
2174 }
2175 }
2175
2176
2176 assert_response :redirect
2177 assert_response :redirect
2177 issue.reload
2178 issue.reload
2178 assert_equal 4, issue.fixed_version_id
2179 assert_equal 4, issue.fixed_version_id
2179 assert_not_equal issue.project_id, issue.fixed_version.project_id
2180 assert_not_equal issue.project_id, issue.fixed_version.project_id
2180 end
2181 end
2181
2182
2182 def test_put_update_should_redirect_back_using_the_back_url_parameter
2183 def test_put_update_should_redirect_back_using_the_back_url_parameter
2183 issue = Issue.find(2)
2184 issue = Issue.find(2)
2184 @request.session[:user_id] = 2
2185 @request.session[:user_id] = 2
2185
2186
2186 put :update,
2187 put :update,
2187 :id => issue.id,
2188 :id => issue.id,
2188 :issue => {
2189 :issue => {
2189 :fixed_version_id => 4
2190 :fixed_version_id => 4
2190 },
2191 },
2191 :back_url => '/issues'
2192 :back_url => '/issues'
2192
2193
2193 assert_response :redirect
2194 assert_response :redirect
2194 assert_redirected_to '/issues'
2195 assert_redirected_to '/issues'
2195 end
2196 end
2196
2197
2197 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2198 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2198 issue = Issue.find(2)
2199 issue = Issue.find(2)
2199 @request.session[:user_id] = 2
2200 @request.session[:user_id] = 2
2200
2201
2201 put :update,
2202 put :update,
2202 :id => issue.id,
2203 :id => issue.id,
2203 :issue => {
2204 :issue => {
2204 :fixed_version_id => 4
2205 :fixed_version_id => 4
2205 },
2206 },
2206 :back_url => 'http://google.com'
2207 :back_url => 'http://google.com'
2207
2208
2208 assert_response :redirect
2209 assert_response :redirect
2209 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
2210 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
2210 end
2211 end
2211
2212
2212 def test_get_bulk_edit
2213 def test_get_bulk_edit
2213 @request.session[:user_id] = 2
2214 @request.session[:user_id] = 2
2214 get :bulk_edit, :ids => [1, 2]
2215 get :bulk_edit, :ids => [1, 2]
2215 assert_response :success
2216 assert_response :success
2216 assert_template 'bulk_edit'
2217 assert_template 'bulk_edit'
2217
2218
2218 assert_tag :select, :attributes => {:name => 'issue[project_id]'}
2219 assert_tag :select, :attributes => {:name => 'issue[project_id]'}
2219 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2220 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2220
2221
2221 # Project specific custom field, date type
2222 # Project specific custom field, date type
2222 field = CustomField.find(9)
2223 field = CustomField.find(9)
2223 assert !field.is_for_all?
2224 assert !field.is_for_all?
2224 assert_equal 'date', field.field_format
2225 assert_equal 'date', field.field_format
2225 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2226 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2226
2227
2227 # System wide custom field
2228 # System wide custom field
2228 assert CustomField.find(1).is_for_all?
2229 assert CustomField.find(1).is_for_all?
2229 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
2230 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
2230
2231
2231 # Be sure we don't display inactive IssuePriorities
2232 # Be sure we don't display inactive IssuePriorities
2232 assert ! IssuePriority.find(15).active?
2233 assert ! IssuePriority.find(15).active?
2233 assert_no_tag :option, :attributes => {:value => '15'},
2234 assert_no_tag :option, :attributes => {:value => '15'},
2234 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
2235 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
2235 end
2236 end
2236
2237
2237 def test_get_bulk_edit_on_different_projects
2238 def test_get_bulk_edit_on_different_projects
2238 @request.session[:user_id] = 2
2239 @request.session[:user_id] = 2
2239 get :bulk_edit, :ids => [1, 2, 6]
2240 get :bulk_edit, :ids => [1, 2, 6]
2240 assert_response :success
2241 assert_response :success
2241 assert_template 'bulk_edit'
2242 assert_template 'bulk_edit'
2242
2243
2243 # Can not set issues from different projects as children of an issue
2244 # Can not set issues from different projects as children of an issue
2244 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2245 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2245
2246
2246 # Project specific custom field, date type
2247 # Project specific custom field, date type
2247 field = CustomField.find(9)
2248 field = CustomField.find(9)
2248 assert !field.is_for_all?
2249 assert !field.is_for_all?
2249 assert !field.project_ids.include?(Issue.find(6).project_id)
2250 assert !field.project_ids.include?(Issue.find(6).project_id)
2250 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2251 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2251 end
2252 end
2252
2253
2253 def test_get_bulk_edit_with_user_custom_field
2254 def test_get_bulk_edit_with_user_custom_field
2254 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
2255 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
2255
2256
2256 @request.session[:user_id] = 2
2257 @request.session[:user_id] = 2
2257 get :bulk_edit, :ids => [1, 2]
2258 get :bulk_edit, :ids => [1, 2]
2258 assert_response :success
2259 assert_response :success
2259 assert_template 'bulk_edit'
2260 assert_template 'bulk_edit'
2260
2261
2261 assert_tag :select,
2262 assert_tag :select,
2262 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2263 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2263 :children => {
2264 :children => {
2264 :only => {:tag => 'option'},
2265 :only => {:tag => 'option'},
2265 :count => Project.find(1).users.count + 1
2266 :count => Project.find(1).users.count + 1
2266 }
2267 }
2267 end
2268 end
2268
2269
2269 def test_get_bulk_edit_with_version_custom_field
2270 def test_get_bulk_edit_with_version_custom_field
2270 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
2271 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
2271
2272
2272 @request.session[:user_id] = 2
2273 @request.session[:user_id] = 2
2273 get :bulk_edit, :ids => [1, 2]
2274 get :bulk_edit, :ids => [1, 2]
2274 assert_response :success
2275 assert_response :success
2275 assert_template 'bulk_edit'
2276 assert_template 'bulk_edit'
2276
2277
2277 assert_tag :select,
2278 assert_tag :select,
2278 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2279 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2279 :children => {
2280 :children => {
2280 :only => {:tag => 'option'},
2281 :only => {:tag => 'option'},
2281 :count => Project.find(1).shared_versions.count + 1
2282 :count => Project.find(1).shared_versions.count + 1
2282 }
2283 }
2283 end
2284 end
2284
2285
2285 def test_bulk_update
2286 def test_bulk_update
2286 @request.session[:user_id] = 2
2287 @request.session[:user_id] = 2
2287 # update issues priority
2288 # update issues priority
2288 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2289 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2289 :issue => {:priority_id => 7,
2290 :issue => {:priority_id => 7,
2290 :assigned_to_id => '',
2291 :assigned_to_id => '',
2291 :custom_field_values => {'2' => ''}}
2292 :custom_field_values => {'2' => ''}}
2292
2293
2293 assert_response 302
2294 assert_response 302
2294 # check that the issues were updated
2295 # check that the issues were updated
2295 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
2296 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
2296
2297
2297 issue = Issue.find(1)
2298 issue = Issue.find(1)
2298 journal = issue.journals.find(:first, :order => 'created_on DESC')
2299 journal = issue.journals.find(:first, :order => 'created_on DESC')
2299 assert_equal '125', issue.custom_value_for(2).value
2300 assert_equal '125', issue.custom_value_for(2).value
2300 assert_equal 'Bulk editing', journal.notes
2301 assert_equal 'Bulk editing', journal.notes
2301 assert_equal 1, journal.details.size
2302 assert_equal 1, journal.details.size
2302 end
2303 end
2303
2304
2304 def test_bulk_update_with_group_assignee
2305 def test_bulk_update_with_group_assignee
2305 group = Group.find(11)
2306 group = Group.find(11)
2306 project = Project.find(1)
2307 project = Project.find(1)
2307 project.members << Member.new(:principal => group, :roles => [Role.first])
2308 project.members << Member.new(:principal => group, :roles => [Role.first])
2308
2309
2309 @request.session[:user_id] = 2
2310 @request.session[:user_id] = 2
2310 # update issues assignee
2311 # update issues assignee
2311 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2312 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2312 :issue => {:priority_id => '',
2313 :issue => {:priority_id => '',
2313 :assigned_to_id => group.id,
2314 :assigned_to_id => group.id,
2314 :custom_field_values => {'2' => ''}}
2315 :custom_field_values => {'2' => ''}}
2315
2316
2316 assert_response 302
2317 assert_response 302
2317 assert_equal [group, group], Issue.find_all_by_id([1, 2]).collect {|i| i.assigned_to}
2318 assert_equal [group, group], Issue.find_all_by_id([1, 2]).collect {|i| i.assigned_to}
2318 end
2319 end
2319
2320
2320 def test_bulk_update_on_different_projects
2321 def test_bulk_update_on_different_projects
2321 @request.session[:user_id] = 2
2322 @request.session[:user_id] = 2
2322 # update issues priority
2323 # update issues priority
2323 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
2324 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
2324 :issue => {:priority_id => 7,
2325 :issue => {:priority_id => 7,
2325 :assigned_to_id => '',
2326 :assigned_to_id => '',
2326 :custom_field_values => {'2' => ''}}
2327 :custom_field_values => {'2' => ''}}
2327
2328
2328 assert_response 302
2329 assert_response 302
2329 # check that the issues were updated
2330 # check that the issues were updated
2330 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
2331 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
2331
2332
2332 issue = Issue.find(1)
2333 issue = Issue.find(1)
2333 journal = issue.journals.find(:first, :order => 'created_on DESC')
2334 journal = issue.journals.find(:first, :order => 'created_on DESC')
2334 assert_equal '125', issue.custom_value_for(2).value
2335 assert_equal '125', issue.custom_value_for(2).value
2335 assert_equal 'Bulk editing', journal.notes
2336 assert_equal 'Bulk editing', journal.notes
2336 assert_equal 1, journal.details.size
2337 assert_equal 1, journal.details.size
2337 end
2338 end
2338
2339
2339 def test_bulk_update_on_different_projects_without_rights
2340 def test_bulk_update_on_different_projects_without_rights
2340 @request.session[:user_id] = 3
2341 @request.session[:user_id] = 3
2341 user = User.find(3)
2342 user = User.find(3)
2342 action = { :controller => "issues", :action => "bulk_update" }
2343 action = { :controller => "issues", :action => "bulk_update" }
2343 assert user.allowed_to?(action, Issue.find(1).project)
2344 assert user.allowed_to?(action, Issue.find(1).project)
2344 assert ! user.allowed_to?(action, Issue.find(6).project)
2345 assert ! user.allowed_to?(action, Issue.find(6).project)
2345 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
2346 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
2346 :issue => {:priority_id => 7,
2347 :issue => {:priority_id => 7,
2347 :assigned_to_id => '',
2348 :assigned_to_id => '',
2348 :custom_field_values => {'2' => ''}}
2349 :custom_field_values => {'2' => ''}}
2349 assert_response 403
2350 assert_response 403
2350 assert_not_equal "Bulk should fail", Journal.last.notes
2351 assert_not_equal "Bulk should fail", Journal.last.notes
2351 end
2352 end
2352
2353
2353 def test_bullk_update_should_send_a_notification
2354 def test_bullk_update_should_send_a_notification
2354 @request.session[:user_id] = 2
2355 @request.session[:user_id] = 2
2355 ActionMailer::Base.deliveries.clear
2356 ActionMailer::Base.deliveries.clear
2356 post(:bulk_update,
2357 post(:bulk_update,
2357 {
2358 {
2358 :ids => [1, 2],
2359 :ids => [1, 2],
2359 :notes => 'Bulk editing',
2360 :notes => 'Bulk editing',
2360 :issue => {
2361 :issue => {
2361 :priority_id => 7,
2362 :priority_id => 7,
2362 :assigned_to_id => '',
2363 :assigned_to_id => '',
2363 :custom_field_values => {'2' => ''}
2364 :custom_field_values => {'2' => ''}
2364 }
2365 }
2365 })
2366 })
2366
2367
2367 assert_response 302
2368 assert_response 302
2368 assert_equal 2, ActionMailer::Base.deliveries.size
2369 assert_equal 2, ActionMailer::Base.deliveries.size
2369 end
2370 end
2370
2371
2371 def test_bulk_update_project
2372 def test_bulk_update_project
2372 @request.session[:user_id] = 2
2373 @request.session[:user_id] = 2
2373 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
2374 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
2374 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2375 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2375 # Issues moved to project 2
2376 # Issues moved to project 2
2376 assert_equal 2, Issue.find(1).project_id
2377 assert_equal 2, Issue.find(1).project_id
2377 assert_equal 2, Issue.find(2).project_id
2378 assert_equal 2, Issue.find(2).project_id
2378 # No tracker change
2379 # No tracker change
2379 assert_equal 1, Issue.find(1).tracker_id
2380 assert_equal 1, Issue.find(1).tracker_id
2380 assert_equal 2, Issue.find(2).tracker_id
2381 assert_equal 2, Issue.find(2).tracker_id
2381 end
2382 end
2382
2383
2383 def test_bulk_update_project_on_single_issue_should_follow_when_needed
2384 def test_bulk_update_project_on_single_issue_should_follow_when_needed
2384 @request.session[:user_id] = 2
2385 @request.session[:user_id] = 2
2385 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
2386 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
2386 assert_redirected_to '/issues/1'
2387 assert_redirected_to '/issues/1'
2387 end
2388 end
2388
2389
2389 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
2390 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
2390 @request.session[:user_id] = 2
2391 @request.session[:user_id] = 2
2391 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
2392 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
2392 assert_redirected_to '/projects/onlinestore/issues'
2393 assert_redirected_to '/projects/onlinestore/issues'
2393 end
2394 end
2394
2395
2395 def test_bulk_update_tracker
2396 def test_bulk_update_tracker
2396 @request.session[:user_id] = 2
2397 @request.session[:user_id] = 2
2397 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
2398 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
2398 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2399 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2399 assert_equal 2, Issue.find(1).tracker_id
2400 assert_equal 2, Issue.find(1).tracker_id
2400 assert_equal 2, Issue.find(2).tracker_id
2401 assert_equal 2, Issue.find(2).tracker_id
2401 end
2402 end
2402
2403
2403 def test_bulk_update_status
2404 def test_bulk_update_status
2404 @request.session[:user_id] = 2
2405 @request.session[:user_id] = 2
2405 # update issues priority
2406 # update issues priority
2406 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
2407 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
2407 :issue => {:priority_id => '',
2408 :issue => {:priority_id => '',
2408 :assigned_to_id => '',
2409 :assigned_to_id => '',
2409 :status_id => '5'}
2410 :status_id => '5'}
2410
2411
2411 assert_response 302
2412 assert_response 302
2412 issue = Issue.find(1)
2413 issue = Issue.find(1)
2413 assert issue.closed?
2414 assert issue.closed?
2414 end
2415 end
2415
2416
2416 def test_bulk_update_priority
2417 def test_bulk_update_priority
2417 @request.session[:user_id] = 2
2418 @request.session[:user_id] = 2
2418 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
2419 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
2419
2420
2420 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2421 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2421 assert_equal 6, Issue.find(1).priority_id
2422 assert_equal 6, Issue.find(1).priority_id
2422 assert_equal 6, Issue.find(2).priority_id
2423 assert_equal 6, Issue.find(2).priority_id
2423 end
2424 end
2424
2425
2425 def test_bulk_update_with_notes
2426 def test_bulk_update_with_notes
2426 @request.session[:user_id] = 2
2427 @request.session[:user_id] = 2
2427 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
2428 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
2428
2429
2429 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2430 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2430 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
2431 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
2431 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
2432 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
2432 end
2433 end
2433
2434
2434 def test_bulk_update_parent_id
2435 def test_bulk_update_parent_id
2435 @request.session[:user_id] = 2
2436 @request.session[:user_id] = 2
2436 post :bulk_update, :ids => [1, 3],
2437 post :bulk_update, :ids => [1, 3],
2437 :notes => 'Bulk editing parent',
2438 :notes => 'Bulk editing parent',
2438 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
2439 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
2439
2440
2440 assert_response 302
2441 assert_response 302
2441 parent = Issue.find(2)
2442 parent = Issue.find(2)
2442 assert_equal parent.id, Issue.find(1).parent_id
2443 assert_equal parent.id, Issue.find(1).parent_id
2443 assert_equal parent.id, Issue.find(3).parent_id
2444 assert_equal parent.id, Issue.find(3).parent_id
2444 assert_equal [1, 3], parent.children.collect(&:id).sort
2445 assert_equal [1, 3], parent.children.collect(&:id).sort
2445 end
2446 end
2446
2447
2447 def test_bulk_update_custom_field
2448 def test_bulk_update_custom_field
2448 @request.session[:user_id] = 2
2449 @request.session[:user_id] = 2
2449 # update issues priority
2450 # update issues priority
2450 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
2451 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
2451 :issue => {:priority_id => '',
2452 :issue => {:priority_id => '',
2452 :assigned_to_id => '',
2453 :assigned_to_id => '',
2453 :custom_field_values => {'2' => '777'}}
2454 :custom_field_values => {'2' => '777'}}
2454
2455
2455 assert_response 302
2456 assert_response 302
2456
2457
2457 issue = Issue.find(1)
2458 issue = Issue.find(1)
2458 journal = issue.journals.find(:first, :order => 'created_on DESC')
2459 journal = issue.journals.find(:first, :order => 'created_on DESC')
2459 assert_equal '777', issue.custom_value_for(2).value
2460 assert_equal '777', issue.custom_value_for(2).value
2460 assert_equal 1, journal.details.size
2461 assert_equal 1, journal.details.size
2461 assert_equal '125', journal.details.first.old_value
2462 assert_equal '125', journal.details.first.old_value
2462 assert_equal '777', journal.details.first.value
2463 assert_equal '777', journal.details.first.value
2463 end
2464 end
2464
2465
2465 def test_bulk_update_unassign
2466 def test_bulk_update_unassign
2466 assert_not_nil Issue.find(2).assigned_to
2467 assert_not_nil Issue.find(2).assigned_to
2467 @request.session[:user_id] = 2
2468 @request.session[:user_id] = 2
2468 # unassign issues
2469 # unassign issues
2469 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
2470 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
2470 assert_response 302
2471 assert_response 302
2471 # check that the issues were updated
2472 # check that the issues were updated
2472 assert_nil Issue.find(2).assigned_to
2473 assert_nil Issue.find(2).assigned_to
2473 end
2474 end
2474
2475
2475 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
2476 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
2476 @request.session[:user_id] = 2
2477 @request.session[:user_id] = 2
2477
2478
2478 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
2479 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
2479
2480
2480 assert_response :redirect
2481 assert_response :redirect
2481 issues = Issue.find([1,2])
2482 issues = Issue.find([1,2])
2482 issues.each do |issue|
2483 issues.each do |issue|
2483 assert_equal 4, issue.fixed_version_id
2484 assert_equal 4, issue.fixed_version_id
2484 assert_not_equal issue.project_id, issue.fixed_version.project_id
2485 assert_not_equal issue.project_id, issue.fixed_version.project_id
2485 end
2486 end
2486 end
2487 end
2487
2488
2488 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
2489 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
2489 @request.session[:user_id] = 2
2490 @request.session[:user_id] = 2
2490 post :bulk_update, :ids => [1,2], :back_url => '/issues'
2491 post :bulk_update, :ids => [1,2], :back_url => '/issues'
2491
2492
2492 assert_response :redirect
2493 assert_response :redirect
2493 assert_redirected_to '/issues'
2494 assert_redirected_to '/issues'
2494 end
2495 end
2495
2496
2496 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2497 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2497 @request.session[:user_id] = 2
2498 @request.session[:user_id] = 2
2498 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
2499 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
2499
2500
2500 assert_response :redirect
2501 assert_response :redirect
2501 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
2502 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
2502 end
2503 end
2503
2504
2504 def test_bulk_copy_to_another_project
2505 def test_bulk_copy_to_another_project
2505 @request.session[:user_id] = 2
2506 @request.session[:user_id] = 2
2506 assert_difference 'Issue.count', 2 do
2507 assert_difference 'Issue.count', 2 do
2507 assert_no_difference 'Project.find(1).issues.count' do
2508 assert_no_difference 'Project.find(1).issues.count' do
2508 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
2509 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
2509 end
2510 end
2510 end
2511 end
2511 assert_redirected_to '/projects/ecookbook/issues'
2512 assert_redirected_to '/projects/ecookbook/issues'
2512 end
2513 end
2513
2514
2514 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
2515 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
2515 @request.session[:user_id] = 2
2516 @request.session[:user_id] = 2
2516 issue_before_move = Issue.find(1)
2517 issue_before_move = Issue.find(1)
2517 assert_difference 'Issue.count', 1 do
2518 assert_difference 'Issue.count', 1 do
2518 assert_no_difference 'Project.find(1).issues.count' do
2519 assert_no_difference 'Project.find(1).issues.count' do
2519 post :bulk_update, :ids => [1], :copy => '1',
2520 post :bulk_update, :ids => [1], :copy => '1',
2520 :issue => {
2521 :issue => {
2521 :project_id => '2', :tracker_id => '', :assigned_to_id => '',
2522 :project_id => '2', :tracker_id => '', :assigned_to_id => '',
2522 :status_id => '', :start_date => '', :due_date => ''
2523 :status_id => '', :start_date => '', :due_date => ''
2523 }
2524 }
2524 end
2525 end
2525 end
2526 end
2526 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
2527 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
2527 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
2528 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
2528 assert_equal issue_before_move.status_id, issue_after_move.status_id
2529 assert_equal issue_before_move.status_id, issue_after_move.status_id
2529 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
2530 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
2530 end
2531 end
2531
2532
2532 def test_bulk_copy_should_allow_changing_the_issue_attributes
2533 def test_bulk_copy_should_allow_changing_the_issue_attributes
2533 # Fixes random test failure with Mysql
2534 # Fixes random test failure with Mysql
2534 # where Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2535 # where Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2535 # doesn't return the expected results
2536 # doesn't return the expected results
2536 Issue.delete_all("project_id=2")
2537 Issue.delete_all("project_id=2")
2537
2538
2538 @request.session[:user_id] = 2
2539 @request.session[:user_id] = 2
2539 assert_difference 'Issue.count', 2 do
2540 assert_difference 'Issue.count', 2 do
2540 assert_no_difference 'Project.find(1).issues.count' do
2541 assert_no_difference 'Project.find(1).issues.count' do
2541 post :bulk_update, :ids => [1, 2], :copy => '1',
2542 post :bulk_update, :ids => [1, 2], :copy => '1',
2542 :issue => {
2543 :issue => {
2543 :project_id => '2', :tracker_id => '', :assigned_to_id => '4',
2544 :project_id => '2', :tracker_id => '', :assigned_to_id => '4',
2544 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2545 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2545 }
2546 }
2546 end
2547 end
2547 end
2548 end
2548
2549
2549 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2550 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2550 assert_equal 2, copied_issues.size
2551 assert_equal 2, copied_issues.size
2551 copied_issues.each do |issue|
2552 copied_issues.each do |issue|
2552 assert_equal 2, issue.project_id, "Project is incorrect"
2553 assert_equal 2, issue.project_id, "Project is incorrect"
2553 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
2554 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
2554 assert_equal 3, issue.status_id, "Status is incorrect"
2555 assert_equal 3, issue.status_id, "Status is incorrect"
2555 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
2556 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
2556 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
2557 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
2557 end
2558 end
2558 end
2559 end
2559
2560
2560 def test_bulk_copy_should_allow_adding_a_note
2561 def test_bulk_copy_should_allow_adding_a_note
2561 @request.session[:user_id] = 2
2562 @request.session[:user_id] = 2
2562 assert_difference 'Issue.count', 1 do
2563 assert_difference 'Issue.count', 1 do
2563 post :bulk_update, :ids => [1], :copy => '1',
2564 post :bulk_update, :ids => [1], :copy => '1',
2564 :notes => 'Copying one issue',
2565 :notes => 'Copying one issue',
2565 :issue => {
2566 :issue => {
2566 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
2567 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
2567 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2568 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2568 }
2569 }
2569 end
2570 end
2570
2571
2571 issue = Issue.first(:order => 'id DESC')
2572 issue = Issue.first(:order => 'id DESC')
2572 assert_equal 1, issue.journals.size
2573 assert_equal 1, issue.journals.size
2573 journal = issue.journals.first
2574 journal = issue.journals.first
2574 assert_equal 0, journal.details.size
2575 assert_equal 0, journal.details.size
2575 assert_equal 'Copying one issue', journal.notes
2576 assert_equal 'Copying one issue', journal.notes
2576 end
2577 end
2577
2578
2578 def test_bulk_copy_to_another_project_should_follow_when_needed
2579 def test_bulk_copy_to_another_project_should_follow_when_needed
2579 @request.session[:user_id] = 2
2580 @request.session[:user_id] = 2
2580 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
2581 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
2581 issue = Issue.first(:order => 'id DESC')
2582 issue = Issue.first(:order => 'id DESC')
2582 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2583 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2583 end
2584 end
2584
2585
2585 def test_destroy_issue_with_no_time_entries
2586 def test_destroy_issue_with_no_time_entries
2586 assert_nil TimeEntry.find_by_issue_id(2)
2587 assert_nil TimeEntry.find_by_issue_id(2)
2587 @request.session[:user_id] = 2
2588 @request.session[:user_id] = 2
2588
2589
2589 assert_difference 'Issue.count', -1 do
2590 assert_difference 'Issue.count', -1 do
2590 delete :destroy, :id => 2
2591 delete :destroy, :id => 2
2591 end
2592 end
2592 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2593 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2593 assert_nil Issue.find_by_id(2)
2594 assert_nil Issue.find_by_id(2)
2594 end
2595 end
2595
2596
2596 def test_destroy_issues_with_time_entries
2597 def test_destroy_issues_with_time_entries
2597 @request.session[:user_id] = 2
2598 @request.session[:user_id] = 2
2598
2599
2599 assert_no_difference 'Issue.count' do
2600 assert_no_difference 'Issue.count' do
2600 delete :destroy, :ids => [1, 3]
2601 delete :destroy, :ids => [1, 3]
2601 end
2602 end
2602 assert_response :success
2603 assert_response :success
2603 assert_template 'destroy'
2604 assert_template 'destroy'
2604 assert_not_nil assigns(:hours)
2605 assert_not_nil assigns(:hours)
2605 assert Issue.find_by_id(1) && Issue.find_by_id(3)
2606 assert Issue.find_by_id(1) && Issue.find_by_id(3)
2606 assert_tag 'form',
2607 assert_tag 'form',
2607 :descendant => {:tag => 'input', :attributes => {:name => '_method', :value => 'delete'}}
2608 :descendant => {:tag => 'input', :attributes => {:name => '_method', :value => 'delete'}}
2608 end
2609 end
2609
2610
2610 def test_destroy_issues_and_destroy_time_entries
2611 def test_destroy_issues_and_destroy_time_entries
2611 @request.session[:user_id] = 2
2612 @request.session[:user_id] = 2
2612
2613
2613 assert_difference 'Issue.count', -2 do
2614 assert_difference 'Issue.count', -2 do
2614 assert_difference 'TimeEntry.count', -3 do
2615 assert_difference 'TimeEntry.count', -3 do
2615 delete :destroy, :ids => [1, 3], :todo => 'destroy'
2616 delete :destroy, :ids => [1, 3], :todo => 'destroy'
2616 end
2617 end
2617 end
2618 end
2618 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2619 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2619 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2620 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2620 assert_nil TimeEntry.find_by_id([1, 2])
2621 assert_nil TimeEntry.find_by_id([1, 2])
2621 end
2622 end
2622
2623
2623 def test_destroy_issues_and_assign_time_entries_to_project
2624 def test_destroy_issues_and_assign_time_entries_to_project
2624 @request.session[:user_id] = 2
2625 @request.session[:user_id] = 2
2625
2626
2626 assert_difference 'Issue.count', -2 do
2627 assert_difference 'Issue.count', -2 do
2627 assert_no_difference 'TimeEntry.count' do
2628 assert_no_difference 'TimeEntry.count' do
2628 delete :destroy, :ids => [1, 3], :todo => 'nullify'
2629 delete :destroy, :ids => [1, 3], :todo => 'nullify'
2629 end
2630 end
2630 end
2631 end
2631 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2632 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2632 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2633 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2633 assert_nil TimeEntry.find(1).issue_id
2634 assert_nil TimeEntry.find(1).issue_id
2634 assert_nil TimeEntry.find(2).issue_id
2635 assert_nil TimeEntry.find(2).issue_id
2635 end
2636 end
2636
2637
2637 def test_destroy_issues_and_reassign_time_entries_to_another_issue
2638 def test_destroy_issues_and_reassign_time_entries_to_another_issue
2638 @request.session[:user_id] = 2
2639 @request.session[:user_id] = 2
2639
2640
2640 assert_difference 'Issue.count', -2 do
2641 assert_difference 'Issue.count', -2 do
2641 assert_no_difference 'TimeEntry.count' do
2642 assert_no_difference 'TimeEntry.count' do
2642 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
2643 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
2643 end
2644 end
2644 end
2645 end
2645 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2646 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2646 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2647 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2647 assert_equal 2, TimeEntry.find(1).issue_id
2648 assert_equal 2, TimeEntry.find(1).issue_id
2648 assert_equal 2, TimeEntry.find(2).issue_id
2649 assert_equal 2, TimeEntry.find(2).issue_id
2649 end
2650 end
2650
2651
2651 def test_destroy_issues_from_different_projects
2652 def test_destroy_issues_from_different_projects
2652 @request.session[:user_id] = 2
2653 @request.session[:user_id] = 2
2653
2654
2654 assert_difference 'Issue.count', -3 do
2655 assert_difference 'Issue.count', -3 do
2655 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
2656 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
2656 end
2657 end
2657 assert_redirected_to :controller => 'issues', :action => 'index'
2658 assert_redirected_to :controller => 'issues', :action => 'index'
2658 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
2659 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
2659 end
2660 end
2660
2661
2661 def test_destroy_parent_and_child_issues
2662 def test_destroy_parent_and_child_issues
2662 parent = Issue.generate!(:project_id => 1, :tracker_id => 1)
2663 parent = Issue.generate!(:project_id => 1, :tracker_id => 1)
2663 child = Issue.generate!(:project_id => 1, :tracker_id => 1, :parent_issue_id => parent.id)
2664 child = Issue.generate!(:project_id => 1, :tracker_id => 1, :parent_issue_id => parent.id)
2664 assert child.is_descendant_of?(parent.reload)
2665 assert child.is_descendant_of?(parent.reload)
2665
2666
2666 @request.session[:user_id] = 2
2667 @request.session[:user_id] = 2
2667 assert_difference 'Issue.count', -2 do
2668 assert_difference 'Issue.count', -2 do
2668 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
2669 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
2669 end
2670 end
2670 assert_response 302
2671 assert_response 302
2671 end
2672 end
2672
2673
2673 def test_default_search_scope
2674 def test_default_search_scope
2674 get :index
2675 get :index
2675 assert_tag :div, :attributes => {:id => 'quick-search'},
2676 assert_tag :div, :attributes => {:id => 'quick-search'},
2676 :child => {:tag => 'form',
2677 :child => {:tag => 'form',
2677 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
2678 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
2678 end
2679 end
2679 end
2680 end
@@ -1,88 +1,167
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class CustomFieldTest < ActiveSupport::TestCase
20 class CustomFieldTest < ActiveSupport::TestCase
21 fixtures :custom_fields
21 fixtures :custom_fields
22
22
23 def test_create
23 def test_create
24 field = UserCustomField.new(:name => 'Money money money', :field_format => 'float')
24 field = UserCustomField.new(:name => 'Money money money', :field_format => 'float')
25 assert field.save
25 assert field.save
26 end
26 end
27
27
28 def test_before_validation
28 def test_before_validation
29 field = CustomField.new(:name => 'test_before_validation', :field_format => 'int')
29 field = CustomField.new(:name => 'test_before_validation', :field_format => 'int')
30 field.searchable = true
30 field.searchable = true
31 assert field.save
31 assert field.save
32 assert_equal false, field.searchable
32 assert_equal false, field.searchable
33 field.searchable = true
33 field.searchable = true
34 assert field.save
34 assert field.save
35 assert_equal false, field.searchable
35 assert_equal false, field.searchable
36 end
36 end
37
37
38 def test_regexp_validation
38 def test_regexp_validation
39 field = IssueCustomField.new(:name => 'regexp', :field_format => 'text', :regexp => '[a-z0-9')
39 field = IssueCustomField.new(:name => 'regexp', :field_format => 'text', :regexp => '[a-z0-9')
40 assert !field.save
40 assert !field.save
41 assert_equal I18n.t('activerecord.errors.messages.invalid'),
41 assert_equal I18n.t('activerecord.errors.messages.invalid'),
42 field.errors[:regexp].to_s
42 field.errors[:regexp].to_s
43 field.regexp = '[a-z0-9]'
43 field.regexp = '[a-z0-9]'
44 assert field.save
44 assert field.save
45 end
45 end
46
46
47 def test_default_value_should_be_validated
48 field = CustomField.new(:name => 'Test', :field_format => 'int')
49 field.default_value = 'abc'
50 assert !field.valid?
51 field.default_value = '6'
52 assert field.valid?
53 end
54
47 def test_possible_values_should_accept_an_array
55 def test_possible_values_should_accept_an_array
48 field = CustomField.new
56 field = CustomField.new
49 field.possible_values = ["One value", ""]
57 field.possible_values = ["One value", ""]
50 assert_equal ["One value"], field.possible_values
58 assert_equal ["One value"], field.possible_values
51 end
59 end
52
60
53 def test_possible_values_should_accept_a_string
61 def test_possible_values_should_accept_a_string
54 field = CustomField.new
62 field = CustomField.new
55 field.possible_values = "One value"
63 field.possible_values = "One value"
56 assert_equal ["One value"], field.possible_values
64 assert_equal ["One value"], field.possible_values
57 end
65 end
58
66
59 def test_possible_values_should_accept_a_multiline_string
67 def test_possible_values_should_accept_a_multiline_string
60 field = CustomField.new
68 field = CustomField.new
61 field.possible_values = "One value\nAnd another one \r\n \n"
69 field.possible_values = "One value\nAnd another one \r\n \n"
62 assert_equal ["One value", "And another one"], field.possible_values
70 assert_equal ["One value", "And another one"], field.possible_values
63 end
71 end
64
72
65 def test_destroy
73 def test_destroy
66 field = CustomField.find(1)
74 field = CustomField.find(1)
67 assert field.destroy
75 assert field.destroy
68 end
76 end
69
77
70 def test_new_subclass_instance_should_return_an_instance
78 def test_new_subclass_instance_should_return_an_instance
71 f = CustomField.new_subclass_instance('IssueCustomField')
79 f = CustomField.new_subclass_instance('IssueCustomField')
72 assert_kind_of IssueCustomField, f
80 assert_kind_of IssueCustomField, f
73 end
81 end
74
82
75 def test_new_subclass_instance_should_set_attributes
83 def test_new_subclass_instance_should_set_attributes
76 f = CustomField.new_subclass_instance('IssueCustomField', :name => 'Test')
84 f = CustomField.new_subclass_instance('IssueCustomField', :name => 'Test')
77 assert_kind_of IssueCustomField, f
85 assert_kind_of IssueCustomField, f
78 assert_equal 'Test', f.name
86 assert_equal 'Test', f.name
79 end
87 end
80
88
81 def test_new_subclass_instance_with_invalid_class_name_should_return_nil
89 def test_new_subclass_instance_with_invalid_class_name_should_return_nil
82 assert_nil CustomField.new_subclass_instance('WrongClassName')
90 assert_nil CustomField.new_subclass_instance('WrongClassName')
83 end
91 end
84
92
85 def test_new_subclass_instance_with_non_subclass_name_should_return_nil
93 def test_new_subclass_instance_with_non_subclass_name_should_return_nil
86 assert_nil CustomField.new_subclass_instance('Project')
94 assert_nil CustomField.new_subclass_instance('Project')
87 end
95 end
96
97 def test_string_field_validation_with_blank_value
98 f = CustomField.new(:field_format => 'string')
99
100 assert f.valid_field_value?(nil)
101 assert f.valid_field_value?('')
102
103 f.is_required = true
104 assert !f.valid_field_value?(nil)
105 assert !f.valid_field_value?('')
106 end
107
108 def test_string_field_validation_with_min_and_max_lengths
109 f = CustomField.new(:field_format => 'string', :min_length => 2, :max_length => 5)
110
111 assert f.valid_field_value?(nil)
112 assert f.valid_field_value?('')
113 assert f.valid_field_value?('a' * 2)
114 assert !f.valid_field_value?('a')
115 assert !f.valid_field_value?('a' * 6)
116 end
117
118 def test_string_field_validation_with_regexp
119 f = CustomField.new(:field_format => 'string', :regexp => '^[A-Z0-9]*$')
120
121 assert f.valid_field_value?(nil)
122 assert f.valid_field_value?('')
123 assert f.valid_field_value?('ABC')
124 assert !f.valid_field_value?('abc')
125 end
126
127 def test_date_field_validation
128 f = CustomField.new(:field_format => 'date')
129
130 assert f.valid_field_value?(nil)
131 assert f.valid_field_value?('')
132 assert f.valid_field_value?('1975-07-14')
133 assert !f.valid_field_value?('1975-07-33')
134 assert !f.valid_field_value?('abc')
135 end
136
137 def test_list_field_validation
138 f = CustomField.new(:field_format => 'list', :possible_values => ['value1', 'value2'])
139
140 assert f.valid_field_value?(nil)
141 assert f.valid_field_value?('')
142 assert f.valid_field_value?('value2')
143 assert !f.valid_field_value?('abc')
144 end
145
146 def test_int_field_validation
147 f = CustomField.new(:field_format => 'int')
148
149 assert f.valid_field_value?(nil)
150 assert f.valid_field_value?('')
151 assert f.valid_field_value?('123')
152 assert f.valid_field_value?('+123')
153 assert f.valid_field_value?('-123')
154 assert !f.valid_field_value?('6abc')
155 end
156
157 def test_float_field_validation
158 f = CustomField.new(:field_format => 'float')
159
160 assert f.valid_field_value?(nil)
161 assert f.valid_field_value?('')
162 assert f.valid_field_value?('11.2')
163 assert f.valid_field_value?('-6.250')
164 assert f.valid_field_value?('5')
165 assert !f.valid_field_value?('6abc')
166 end
88 end
167 end
@@ -1,125 +1,39
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class CustomValueTest < ActiveSupport::TestCase
20 class CustomValueTest < ActiveSupport::TestCase
21 fixtures :custom_fields, :custom_values, :users
21 fixtures :custom_fields, :custom_values, :users
22
22
23 def test_string_field_validation_with_blank_value
24 f = CustomField.new(:field_format => 'string')
25 v = CustomValue.new(:custom_field => f)
26
27 v.value = nil
28 assert v.valid?
29 v.value = ''
30 assert v.valid?
31
32 f.is_required = true
33 v.value = nil
34 assert !v.valid?
35 v.value = ''
36 assert !v.valid?
37 end
38
39 def test_string_field_validation_with_min_and_max_lengths
40 f = CustomField.new(:field_format => 'string', :min_length => 2, :max_length => 5)
41 v = CustomValue.new(:custom_field => f, :value => '')
42 assert v.valid?
43 v.value = 'a'
44 assert !v.valid?
45 v.value = 'a' * 2
46 assert v.valid?
47 v.value = 'a' * 6
48 assert !v.valid?
49 end
50
51 def test_string_field_validation_with_regexp
52 f = CustomField.new(:field_format => 'string', :regexp => '^[A-Z0-9]*$')
53 v = CustomValue.new(:custom_field => f, :value => '')
54 assert v.valid?
55 v.value = 'abc'
56 assert !v.valid?
57 v.value = 'ABC'
58 assert v.valid?
59 end
60
61 def test_date_field_validation
62 f = CustomField.new(:field_format => 'date')
63 v = CustomValue.new(:custom_field => f, :value => '')
64 assert v.valid?
65 v.value = 'abc'
66 assert !v.valid?
67 v.value = '1975-07-33'
68 assert !v.valid?
69 v.value = '1975-07-14'
70 assert v.valid?
71 end
72
73 def test_list_field_validation
74 f = CustomField.new(:field_format => 'list', :possible_values => ['value1', 'value2'])
75 v = CustomValue.new(:custom_field => f, :value => '')
76 assert v.valid?
77 v.value = 'abc'
78 assert !v.valid?
79 v.value = 'value2'
80 assert v.valid?
81 end
82
83 def test_int_field_validation
84 f = CustomField.new(:field_format => 'int')
85 v = CustomValue.new(:custom_field => f, :value => '')
86 assert v.valid?
87 v.value = 'abc'
88 assert !v.valid?
89 v.value = '123'
90 assert v.valid?
91 v.value = '+123'
92 assert v.valid?
93 v.value = '-123'
94 assert v.valid?
95 end
96
97 def test_float_field_validation
98 v = CustomValue.new(:customized => User.find(:first), :custom_field => UserCustomField.find_by_name('Money'))
99 v.value = '11.2'
100 assert v.save
101 v.value = ''
102 assert v.save
103 v.value = '-6.250'
104 assert v.save
105 v.value = '6a'
106 assert !v.save
107 end
108
109 def test_default_value
23 def test_default_value
110 field = CustomField.find_by_default_value('Default string')
24 field = CustomField.find_by_default_value('Default string')
111 assert_not_nil field
25 assert_not_nil field
112
26
113 v = CustomValue.new(:custom_field => field)
27 v = CustomValue.new(:custom_field => field)
114 assert_equal 'Default string', v.value
28 assert_equal 'Default string', v.value
115
29
116 v = CustomValue.new(:custom_field => field, :value => 'Not empty')
30 v = CustomValue.new(:custom_field => field, :value => 'Not empty')
117 assert_equal 'Not empty', v.value
31 assert_equal 'Not empty', v.value
118 end
32 end
119
33
120 def test_sti_polymorphic_association
34 def test_sti_polymorphic_association
121 # Rails uses top level sti class for polymorphic association. See #3978.
35 # Rails uses top level sti class for polymorphic association. See #3978.
122 assert !User.find(4).custom_values.empty?
36 assert !User.find(4).custom_values.empty?
123 assert !CustomValue.find(2).customized.nil?
37 assert !CustomValue.find(2).customized.nil?
124 end
38 end
125 end
39 end
@@ -1,1213 +1,1212
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class IssueTest < ActiveSupport::TestCase
20 class IssueTest < ActiveSupport::TestCase
21 fixtures :projects, :users, :members, :member_roles, :roles,
21 fixtures :projects, :users, :members, :member_roles, :roles,
22 :groups_users,
22 :groups_users,
23 :trackers, :projects_trackers,
23 :trackers, :projects_trackers,
24 :enabled_modules,
24 :enabled_modules,
25 :versions,
25 :versions,
26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
27 :enumerations,
27 :enumerations,
28 :issues,
28 :issues,
29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
30 :time_entries
30 :time_entries
31
31
32 include Redmine::I18n
33
32 def test_create
34 def test_create
33 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
35 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
34 :status_id => 1, :priority => IssuePriority.all.first,
36 :status_id => 1, :priority => IssuePriority.all.first,
35 :subject => 'test_create',
37 :subject => 'test_create',
36 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
38 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
37 assert issue.save
39 assert issue.save
38 issue.reload
40 issue.reload
39 assert_equal 1.5, issue.estimated_hours
41 assert_equal 1.5, issue.estimated_hours
40 end
42 end
41
43
42 def test_create_minimal
44 def test_create_minimal
43 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
45 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
44 :status_id => 1, :priority => IssuePriority.all.first,
46 :status_id => 1, :priority => IssuePriority.all.first,
45 :subject => 'test_create')
47 :subject => 'test_create')
46 assert issue.save
48 assert issue.save
47 assert issue.description.nil?
49 assert issue.description.nil?
48 end
50 end
49
51
50 def test_create_with_required_custom_field
52 def test_create_with_required_custom_field
53 set_language_if_valid 'en'
51 field = IssueCustomField.find_by_name('Database')
54 field = IssueCustomField.find_by_name('Database')
52 field.update_attribute(:is_required, true)
55 field.update_attribute(:is_required, true)
53
56
54 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
57 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
55 :status_id => 1, :subject => 'test_create',
58 :status_id => 1, :subject => 'test_create',
56 :description => 'IssueTest#test_create_with_required_custom_field')
59 :description => 'IssueTest#test_create_with_required_custom_field')
57 assert issue.available_custom_fields.include?(field)
60 assert issue.available_custom_fields.include?(field)
58 # No value for the custom field
61 # No value for the custom field
59 assert !issue.save
62 assert !issue.save
60 assert_equal I18n.translate('activerecord.errors.messages.invalid'),
63 assert_equal "Database can't be blank", issue.errors[:base].to_s
61 issue.errors[:custom_values].to_s
62 # Blank value
64 # Blank value
63 issue.custom_field_values = { field.id => '' }
65 issue.custom_field_values = { field.id => '' }
64 assert !issue.save
66 assert !issue.save
65 assert_equal I18n.translate('activerecord.errors.messages.invalid'),
67 assert_equal "Database can't be blank", issue.errors[:base].to_s
66 issue.errors[:custom_values].to_s
67 # Invalid value
68 # Invalid value
68 issue.custom_field_values = { field.id => 'SQLServer' }
69 issue.custom_field_values = { field.id => 'SQLServer' }
69 assert !issue.save
70 assert !issue.save
70 assert_equal I18n.translate('activerecord.errors.messages.invalid'),
71 assert_equal "Database is not included in the list", issue.errors[:base].to_s
71 issue.errors[:custom_values].to_s
72 # Valid value
72 # Valid value
73 issue.custom_field_values = { field.id => 'PostgreSQL' }
73 issue.custom_field_values = { field.id => 'PostgreSQL' }
74 assert issue.save
74 assert issue.save
75 issue.reload
75 issue.reload
76 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
76 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
77 end
77 end
78
78
79 def test_create_with_group_assignment
79 def test_create_with_group_assignment
80 with_settings :issue_group_assignment => '1' do
80 with_settings :issue_group_assignment => '1' do
81 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
81 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
82 :subject => 'Group assignment',
82 :subject => 'Group assignment',
83 :assigned_to_id => 11).save
83 :assigned_to_id => 11).save
84 issue = Issue.first(:order => 'id DESC')
84 issue = Issue.first(:order => 'id DESC')
85 assert_kind_of Group, issue.assigned_to
85 assert_kind_of Group, issue.assigned_to
86 assert_equal Group.find(11), issue.assigned_to
86 assert_equal Group.find(11), issue.assigned_to
87 end
87 end
88 end
88 end
89
89
90 def assert_visibility_match(user, issues)
90 def assert_visibility_match(user, issues)
91 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
91 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
92 end
92 end
93
93
94 def test_visible_scope_for_anonymous
94 def test_visible_scope_for_anonymous
95 # Anonymous user should see issues of public projects only
95 # Anonymous user should see issues of public projects only
96 issues = Issue.visible(User.anonymous).all
96 issues = Issue.visible(User.anonymous).all
97 assert issues.any?
97 assert issues.any?
98 assert_nil issues.detect {|issue| !issue.project.is_public?}
98 assert_nil issues.detect {|issue| !issue.project.is_public?}
99 assert_nil issues.detect {|issue| issue.is_private?}
99 assert_nil issues.detect {|issue| issue.is_private?}
100 assert_visibility_match User.anonymous, issues
100 assert_visibility_match User.anonymous, issues
101 end
101 end
102
102
103 def test_visible_scope_for_anonymous_with_own_issues_visibility
103 def test_visible_scope_for_anonymous_with_own_issues_visibility
104 Role.anonymous.update_attribute :issues_visibility, 'own'
104 Role.anonymous.update_attribute :issues_visibility, 'own'
105 Issue.create!(:project_id => 1, :tracker_id => 1,
105 Issue.create!(:project_id => 1, :tracker_id => 1,
106 :author_id => User.anonymous.id,
106 :author_id => User.anonymous.id,
107 :subject => 'Issue by anonymous')
107 :subject => 'Issue by anonymous')
108
108
109 issues = Issue.visible(User.anonymous).all
109 issues = Issue.visible(User.anonymous).all
110 assert issues.any?
110 assert issues.any?
111 assert_nil issues.detect {|issue| issue.author != User.anonymous}
111 assert_nil issues.detect {|issue| issue.author != User.anonymous}
112 assert_visibility_match User.anonymous, issues
112 assert_visibility_match User.anonymous, issues
113 end
113 end
114
114
115 def test_visible_scope_for_anonymous_without_view_issues_permissions
115 def test_visible_scope_for_anonymous_without_view_issues_permissions
116 # Anonymous user should not see issues without permission
116 # Anonymous user should not see issues without permission
117 Role.anonymous.remove_permission!(:view_issues)
117 Role.anonymous.remove_permission!(:view_issues)
118 issues = Issue.visible(User.anonymous).all
118 issues = Issue.visible(User.anonymous).all
119 assert issues.empty?
119 assert issues.empty?
120 assert_visibility_match User.anonymous, issues
120 assert_visibility_match User.anonymous, issues
121 end
121 end
122
122
123 def test_visible_scope_for_non_member
123 def test_visible_scope_for_non_member
124 user = User.find(9)
124 user = User.find(9)
125 assert user.projects.empty?
125 assert user.projects.empty?
126 # Non member user should see issues of public projects only
126 # Non member user should see issues of public projects only
127 issues = Issue.visible(user).all
127 issues = Issue.visible(user).all
128 assert issues.any?
128 assert issues.any?
129 assert_nil issues.detect {|issue| !issue.project.is_public?}
129 assert_nil issues.detect {|issue| !issue.project.is_public?}
130 assert_nil issues.detect {|issue| issue.is_private?}
130 assert_nil issues.detect {|issue| issue.is_private?}
131 assert_visibility_match user, issues
131 assert_visibility_match user, issues
132 end
132 end
133
133
134 def test_visible_scope_for_non_member_with_own_issues_visibility
134 def test_visible_scope_for_non_member_with_own_issues_visibility
135 Role.non_member.update_attribute :issues_visibility, 'own'
135 Role.non_member.update_attribute :issues_visibility, 'own'
136 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
136 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
137 user = User.find(9)
137 user = User.find(9)
138
138
139 issues = Issue.visible(user).all
139 issues = Issue.visible(user).all
140 assert issues.any?
140 assert issues.any?
141 assert_nil issues.detect {|issue| issue.author != user}
141 assert_nil issues.detect {|issue| issue.author != user}
142 assert_visibility_match user, issues
142 assert_visibility_match user, issues
143 end
143 end
144
144
145 def test_visible_scope_for_non_member_without_view_issues_permissions
145 def test_visible_scope_for_non_member_without_view_issues_permissions
146 # Non member user should not see issues without permission
146 # Non member user should not see issues without permission
147 Role.non_member.remove_permission!(:view_issues)
147 Role.non_member.remove_permission!(:view_issues)
148 user = User.find(9)
148 user = User.find(9)
149 assert user.projects.empty?
149 assert user.projects.empty?
150 issues = Issue.visible(user).all
150 issues = Issue.visible(user).all
151 assert issues.empty?
151 assert issues.empty?
152 assert_visibility_match user, issues
152 assert_visibility_match user, issues
153 end
153 end
154
154
155 def test_visible_scope_for_member
155 def test_visible_scope_for_member
156 user = User.find(9)
156 user = User.find(9)
157 # User should see issues of projects for which he has view_issues permissions only
157 # User should see issues of projects for which he has view_issues permissions only
158 Role.non_member.remove_permission!(:view_issues)
158 Role.non_member.remove_permission!(:view_issues)
159 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
159 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
160 issues = Issue.visible(user).all
160 issues = Issue.visible(user).all
161 assert issues.any?
161 assert issues.any?
162 assert_nil issues.detect {|issue| issue.project_id != 3}
162 assert_nil issues.detect {|issue| issue.project_id != 3}
163 assert_nil issues.detect {|issue| issue.is_private?}
163 assert_nil issues.detect {|issue| issue.is_private?}
164 assert_visibility_match user, issues
164 assert_visibility_match user, issues
165 end
165 end
166
166
167 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
167 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
168 user = User.find(8)
168 user = User.find(8)
169 assert user.groups.any?
169 assert user.groups.any?
170 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
170 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
171 Role.non_member.remove_permission!(:view_issues)
171 Role.non_member.remove_permission!(:view_issues)
172
172
173 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
173 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
174 :status_id => 1, :priority => IssuePriority.all.first,
174 :status_id => 1, :priority => IssuePriority.all.first,
175 :subject => 'Assignment test',
175 :subject => 'Assignment test',
176 :assigned_to => user.groups.first,
176 :assigned_to => user.groups.first,
177 :is_private => true)
177 :is_private => true)
178
178
179 Role.find(2).update_attribute :issues_visibility, 'default'
179 Role.find(2).update_attribute :issues_visibility, 'default'
180 issues = Issue.visible(User.find(8)).all
180 issues = Issue.visible(User.find(8)).all
181 assert issues.any?
181 assert issues.any?
182 assert issues.include?(issue)
182 assert issues.include?(issue)
183
183
184 Role.find(2).update_attribute :issues_visibility, 'own'
184 Role.find(2).update_attribute :issues_visibility, 'own'
185 issues = Issue.visible(User.find(8)).all
185 issues = Issue.visible(User.find(8)).all
186 assert issues.any?
186 assert issues.any?
187 assert issues.include?(issue)
187 assert issues.include?(issue)
188 end
188 end
189
189
190 def test_visible_scope_for_admin
190 def test_visible_scope_for_admin
191 user = User.find(1)
191 user = User.find(1)
192 user.members.each(&:destroy)
192 user.members.each(&:destroy)
193 assert user.projects.empty?
193 assert user.projects.empty?
194 issues = Issue.visible(user).all
194 issues = Issue.visible(user).all
195 assert issues.any?
195 assert issues.any?
196 # Admin should see issues on private projects that he does not belong to
196 # Admin should see issues on private projects that he does not belong to
197 assert issues.detect {|issue| !issue.project.is_public?}
197 assert issues.detect {|issue| !issue.project.is_public?}
198 # Admin should see private issues of other users
198 # Admin should see private issues of other users
199 assert issues.detect {|issue| issue.is_private? && issue.author != user}
199 assert issues.detect {|issue| issue.is_private? && issue.author != user}
200 assert_visibility_match user, issues
200 assert_visibility_match user, issues
201 end
201 end
202
202
203 def test_visible_scope_with_project
203 def test_visible_scope_with_project
204 project = Project.find(1)
204 project = Project.find(1)
205 issues = Issue.visible(User.find(2), :project => project).all
205 issues = Issue.visible(User.find(2), :project => project).all
206 projects = issues.collect(&:project).uniq
206 projects = issues.collect(&:project).uniq
207 assert_equal 1, projects.size
207 assert_equal 1, projects.size
208 assert_equal project, projects.first
208 assert_equal project, projects.first
209 end
209 end
210
210
211 def test_visible_scope_with_project_and_subprojects
211 def test_visible_scope_with_project_and_subprojects
212 project = Project.find(1)
212 project = Project.find(1)
213 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).all
213 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).all
214 projects = issues.collect(&:project).uniq
214 projects = issues.collect(&:project).uniq
215 assert projects.size > 1
215 assert projects.size > 1
216 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
216 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
217 end
217 end
218
218
219 def test_visible_and_nested_set_scopes
219 def test_visible_and_nested_set_scopes
220 assert_equal 0, Issue.find(1).descendants.visible.all.size
220 assert_equal 0, Issue.find(1).descendants.visible.all.size
221 end
221 end
222
222
223 def test_open_scope
223 def test_open_scope
224 issues = Issue.open.all
224 issues = Issue.open.all
225 assert_nil issues.detect(&:closed?)
225 assert_nil issues.detect(&:closed?)
226 end
226 end
227
227
228 def test_open_scope_with_arg
228 def test_open_scope_with_arg
229 issues = Issue.open(false).all
229 issues = Issue.open(false).all
230 assert_equal issues, issues.select(&:closed?)
230 assert_equal issues, issues.select(&:closed?)
231 end
231 end
232
232
233 def test_errors_full_messages_should_include_custom_fields_errors
233 def test_errors_full_messages_should_include_custom_fields_errors
234 field = IssueCustomField.find_by_name('Database')
234 field = IssueCustomField.find_by_name('Database')
235
235
236 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
236 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
237 :status_id => 1, :subject => 'test_create',
237 :status_id => 1, :subject => 'test_create',
238 :description => 'IssueTest#test_create_with_required_custom_field')
238 :description => 'IssueTest#test_create_with_required_custom_field')
239 assert issue.available_custom_fields.include?(field)
239 assert issue.available_custom_fields.include?(field)
240 # Invalid value
240 # Invalid value
241 issue.custom_field_values = { field.id => 'SQLServer' }
241 issue.custom_field_values = { field.id => 'SQLServer' }
242
242
243 assert !issue.valid?
243 assert !issue.valid?
244 assert_equal 1, issue.errors.full_messages.size
244 assert_equal 1, issue.errors.full_messages.size
245 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
245 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
246 issue.errors.full_messages.first
246 issue.errors.full_messages.first
247 end
247 end
248
248
249 def test_update_issue_with_required_custom_field
249 def test_update_issue_with_required_custom_field
250 field = IssueCustomField.find_by_name('Database')
250 field = IssueCustomField.find_by_name('Database')
251 field.update_attribute(:is_required, true)
251 field.update_attribute(:is_required, true)
252
252
253 issue = Issue.find(1)
253 issue = Issue.find(1)
254 assert_nil issue.custom_value_for(field)
254 assert_nil issue.custom_value_for(field)
255 assert issue.available_custom_fields.include?(field)
255 assert issue.available_custom_fields.include?(field)
256 # No change to custom values, issue can be saved
256 # No change to custom values, issue can be saved
257 assert issue.save
257 assert issue.save
258 # Blank value
258 # Blank value
259 issue.custom_field_values = { field.id => '' }
259 issue.custom_field_values = { field.id => '' }
260 assert !issue.save
260 assert !issue.save
261 # Valid value
261 # Valid value
262 issue.custom_field_values = { field.id => 'PostgreSQL' }
262 issue.custom_field_values = { field.id => 'PostgreSQL' }
263 assert issue.save
263 assert issue.save
264 issue.reload
264 issue.reload
265 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
265 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
266 end
266 end
267
267
268 def test_should_not_update_attributes_if_custom_fields_validation_fails
268 def test_should_not_update_attributes_if_custom_fields_validation_fails
269 issue = Issue.find(1)
269 issue = Issue.find(1)
270 field = IssueCustomField.find_by_name('Database')
270 field = IssueCustomField.find_by_name('Database')
271 assert issue.available_custom_fields.include?(field)
271 assert issue.available_custom_fields.include?(field)
272
272
273 issue.custom_field_values = { field.id => 'Invalid' }
273 issue.custom_field_values = { field.id => 'Invalid' }
274 issue.subject = 'Should be not be saved'
274 issue.subject = 'Should be not be saved'
275 assert !issue.save
275 assert !issue.save
276
276
277 issue.reload
277 issue.reload
278 assert_equal "Can't print recipes", issue.subject
278 assert_equal "Can't print recipes", issue.subject
279 end
279 end
280
280
281 def test_should_not_recreate_custom_values_objects_on_update
281 def test_should_not_recreate_custom_values_objects_on_update
282 field = IssueCustomField.find_by_name('Database')
282 field = IssueCustomField.find_by_name('Database')
283
283
284 issue = Issue.find(1)
284 issue = Issue.find(1)
285 issue.custom_field_values = { field.id => 'PostgreSQL' }
285 issue.custom_field_values = { field.id => 'PostgreSQL' }
286 assert issue.save
286 assert issue.save
287 custom_value = issue.custom_value_for(field)
287 custom_value = issue.custom_value_for(field)
288 issue.reload
288 issue.reload
289 issue.custom_field_values = { field.id => 'MySQL' }
289 issue.custom_field_values = { field.id => 'MySQL' }
290 assert issue.save
290 assert issue.save
291 issue.reload
291 issue.reload
292 assert_equal custom_value.id, issue.custom_value_for(field).id
292 assert_equal custom_value.id, issue.custom_value_for(field).id
293 end
293 end
294
294
295 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
295 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
296 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => 'Test', :custom_field_values => {'2' => 'Test'})
296 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => 'Test', :custom_field_values => {'2' => 'Test'})
297 assert !Tracker.find(2).custom_field_ids.include?(2)
297 assert !Tracker.find(2).custom_field_ids.include?(2)
298
298
299 issue = Issue.find(issue.id)
299 issue = Issue.find(issue.id)
300 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
300 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
301
301
302 issue = Issue.find(issue.id)
302 issue = Issue.find(issue.id)
303 custom_value = issue.custom_value_for(2)
303 custom_value = issue.custom_value_for(2)
304 assert_not_nil custom_value
304 assert_not_nil custom_value
305 assert_equal 'Test', custom_value.value
305 assert_equal 'Test', custom_value.value
306 end
306 end
307
307
308 def test_assigning_tracker_id_should_reload_custom_fields_values
308 def test_assigning_tracker_id_should_reload_custom_fields_values
309 issue = Issue.new(:project => Project.find(1))
309 issue = Issue.new(:project => Project.find(1))
310 assert issue.custom_field_values.empty?
310 assert issue.custom_field_values.empty?
311 issue.tracker_id = 1
311 issue.tracker_id = 1
312 assert issue.custom_field_values.any?
312 assert issue.custom_field_values.any?
313 end
313 end
314
314
315 def test_assigning_attributes_should_assign_project_and_tracker_first
315 def test_assigning_attributes_should_assign_project_and_tracker_first
316 seq = sequence('seq')
316 seq = sequence('seq')
317 issue = Issue.new
317 issue = Issue.new
318 issue.expects(:project_id=).in_sequence(seq)
318 issue.expects(:project_id=).in_sequence(seq)
319 issue.expects(:tracker_id=).in_sequence(seq)
319 issue.expects(:tracker_id=).in_sequence(seq)
320 issue.expects(:subject=).in_sequence(seq)
320 issue.expects(:subject=).in_sequence(seq)
321 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
321 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
322 end
322 end
323
323
324 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
324 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
325 attributes = ActiveSupport::OrderedHash.new
325 attributes = ActiveSupport::OrderedHash.new
326 attributes['custom_field_values'] = { '1' => 'MySQL' }
326 attributes['custom_field_values'] = { '1' => 'MySQL' }
327 attributes['tracker_id'] = '1'
327 attributes['tracker_id'] = '1'
328 issue = Issue.new(:project => Project.find(1))
328 issue = Issue.new(:project => Project.find(1))
329 issue.attributes = attributes
329 issue.attributes = attributes
330 assert_not_nil issue.custom_value_for(1)
330 assert_equal 'MySQL', issue.custom_field_value(1)
331 assert_equal 'MySQL', issue.custom_value_for(1).value
332 end
331 end
333
332
334 def test_should_update_issue_with_disabled_tracker
333 def test_should_update_issue_with_disabled_tracker
335 p = Project.find(1)
334 p = Project.find(1)
336 issue = Issue.find(1)
335 issue = Issue.find(1)
337
336
338 p.trackers.delete(issue.tracker)
337 p.trackers.delete(issue.tracker)
339 assert !p.trackers.include?(issue.tracker)
338 assert !p.trackers.include?(issue.tracker)
340
339
341 issue.reload
340 issue.reload
342 issue.subject = 'New subject'
341 issue.subject = 'New subject'
343 assert issue.save
342 assert issue.save
344 end
343 end
345
344
346 def test_should_not_set_a_disabled_tracker
345 def test_should_not_set_a_disabled_tracker
347 p = Project.find(1)
346 p = Project.find(1)
348 p.trackers.delete(Tracker.find(2))
347 p.trackers.delete(Tracker.find(2))
349
348
350 issue = Issue.find(1)
349 issue = Issue.find(1)
351 issue.tracker_id = 2
350 issue.tracker_id = 2
352 issue.subject = 'New subject'
351 issue.subject = 'New subject'
353 assert !issue.save
352 assert !issue.save
354 assert_not_nil issue.errors[:tracker_id]
353 assert_not_nil issue.errors[:tracker_id]
355 end
354 end
356
355
357 def test_category_based_assignment
356 def test_category_based_assignment
358 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
357 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
359 :status_id => 1, :priority => IssuePriority.all.first,
358 :status_id => 1, :priority => IssuePriority.all.first,
360 :subject => 'Assignment test',
359 :subject => 'Assignment test',
361 :description => 'Assignment test', :category_id => 1)
360 :description => 'Assignment test', :category_id => 1)
362 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
361 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
363 end
362 end
364
363
365 def test_new_statuses_allowed_to
364 def test_new_statuses_allowed_to
366 Workflow.delete_all
365 Workflow.delete_all
367
366
368 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2, :author => false, :assignee => false)
367 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2, :author => false, :assignee => false)
369 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3, :author => true, :assignee => false)
368 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3, :author => true, :assignee => false)
370 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4, :author => false, :assignee => true)
369 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4, :author => false, :assignee => true)
371 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 5, :author => true, :assignee => true)
370 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 5, :author => true, :assignee => true)
372 status = IssueStatus.find(1)
371 status = IssueStatus.find(1)
373 role = Role.find(1)
372 role = Role.find(1)
374 tracker = Tracker.find(1)
373 tracker = Tracker.find(1)
375 user = User.find(2)
374 user = User.find(2)
376
375
377 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1)
376 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1)
378 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
377 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
379
378
380 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user)
379 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user)
381 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
380 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
382
381
383 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :assigned_to => user)
382 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :assigned_to => user)
384 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
383 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
385
384
386 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user, :assigned_to => user)
385 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user, :assigned_to => user)
387 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
386 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
388 end
387 end
389
388
390 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
389 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
391 admin = User.find(1)
390 admin = User.find(1)
392 issue = Issue.find(1)
391 issue = Issue.find(1)
393 assert !admin.member_of?(issue.project)
392 assert !admin.member_of?(issue.project)
394 expected_statuses = [issue.status] + Workflow.find_all_by_old_status_id(issue.status_id).map(&:new_status).uniq.sort
393 expected_statuses = [issue.status] + Workflow.find_all_by_old_status_id(issue.status_id).map(&:new_status).uniq.sort
395
394
396 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
395 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
397 end
396 end
398
397
399 def test_copy
398 def test_copy
400 issue = Issue.new.copy_from(1)
399 issue = Issue.new.copy_from(1)
401 assert issue.copy?
400 assert issue.copy?
402 assert issue.save
401 assert issue.save
403 issue.reload
402 issue.reload
404 orig = Issue.find(1)
403 orig = Issue.find(1)
405 assert_equal orig.subject, issue.subject
404 assert_equal orig.subject, issue.subject
406 assert_equal orig.tracker, issue.tracker
405 assert_equal orig.tracker, issue.tracker
407 assert_equal "125", issue.custom_value_for(2).value
406 assert_equal "125", issue.custom_value_for(2).value
408 end
407 end
409
408
410 def test_copy_should_copy_status
409 def test_copy_should_copy_status
411 orig = Issue.find(8)
410 orig = Issue.find(8)
412 assert orig.status != IssueStatus.default
411 assert orig.status != IssueStatus.default
413
412
414 issue = Issue.new.copy_from(orig)
413 issue = Issue.new.copy_from(orig)
415 assert issue.save
414 assert issue.save
416 issue.reload
415 issue.reload
417 assert_equal orig.status, issue.status
416 assert_equal orig.status, issue.status
418 end
417 end
419
418
420 def test_should_not_call_after_project_change_on_creation
419 def test_should_not_call_after_project_change_on_creation
421 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Test', :author_id => 1)
420 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Test', :author_id => 1)
422 issue.expects(:after_project_change).never
421 issue.expects(:after_project_change).never
423 issue.save!
422 issue.save!
424 end
423 end
425
424
426 def test_should_not_call_after_project_change_on_update
425 def test_should_not_call_after_project_change_on_update
427 issue = Issue.find(1)
426 issue = Issue.find(1)
428 issue.project = Project.find(1)
427 issue.project = Project.find(1)
429 issue.subject = 'No project change'
428 issue.subject = 'No project change'
430 issue.expects(:after_project_change).never
429 issue.expects(:after_project_change).never
431 issue.save!
430 issue.save!
432 end
431 end
433
432
434 def test_should_call_after_project_change_on_project_change
433 def test_should_call_after_project_change_on_project_change
435 issue = Issue.find(1)
434 issue = Issue.find(1)
436 issue.project = Project.find(2)
435 issue.project = Project.find(2)
437 issue.expects(:after_project_change).once
436 issue.expects(:after_project_change).once
438 issue.save!
437 issue.save!
439 end
438 end
440
439
441 def test_should_close_duplicates
440 def test_should_close_duplicates
442 # Create 3 issues
441 # Create 3 issues
443 project = Project.find(1)
442 project = Project.find(1)
444 issue1 = Issue.generate_for_project!(project)
443 issue1 = Issue.generate_for_project!(project)
445 issue2 = Issue.generate_for_project!(project)
444 issue2 = Issue.generate_for_project!(project)
446 issue3 = Issue.generate_for_project!(project)
445 issue3 = Issue.generate_for_project!(project)
447
446
448 # 2 is a dupe of 1
447 # 2 is a dupe of 1
449 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
448 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
450 # And 3 is a dupe of 2
449 # And 3 is a dupe of 2
451 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2, :relation_type => IssueRelation::TYPE_DUPLICATES)
450 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2, :relation_type => IssueRelation::TYPE_DUPLICATES)
452 # And 3 is a dupe of 1 (circular duplicates)
451 # And 3 is a dupe of 1 (circular duplicates)
453 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
452 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
454
453
455 assert issue1.reload.duplicates.include?(issue2)
454 assert issue1.reload.duplicates.include?(issue2)
456
455
457 # Closing issue 1
456 # Closing issue 1
458 issue1.init_journal(User.find(:first), "Closing issue1")
457 issue1.init_journal(User.find(:first), "Closing issue1")
459 issue1.status = IssueStatus.find :first, :conditions => {:is_closed => true}
458 issue1.status = IssueStatus.find :first, :conditions => {:is_closed => true}
460 assert issue1.save
459 assert issue1.save
461 # 2 and 3 should be also closed
460 # 2 and 3 should be also closed
462 assert issue2.reload.closed?
461 assert issue2.reload.closed?
463 assert issue3.reload.closed?
462 assert issue3.reload.closed?
464 end
463 end
465
464
466 def test_should_not_close_duplicated_issue
465 def test_should_not_close_duplicated_issue
467 project = Project.find(1)
466 project = Project.find(1)
468 issue1 = Issue.generate_for_project!(project)
467 issue1 = Issue.generate_for_project!(project)
469 issue2 = Issue.generate_for_project!(project)
468 issue2 = Issue.generate_for_project!(project)
470
469
471 # 2 is a dupe of 1
470 # 2 is a dupe of 1
472 IssueRelation.create(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
471 IssueRelation.create(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
473 # 2 is a dup of 1 but 1 is not a duplicate of 2
472 # 2 is a dup of 1 but 1 is not a duplicate of 2
474 assert !issue2.reload.duplicates.include?(issue1)
473 assert !issue2.reload.duplicates.include?(issue1)
475
474
476 # Closing issue 2
475 # Closing issue 2
477 issue2.init_journal(User.find(:first), "Closing issue2")
476 issue2.init_journal(User.find(:first), "Closing issue2")
478 issue2.status = IssueStatus.find :first, :conditions => {:is_closed => true}
477 issue2.status = IssueStatus.find :first, :conditions => {:is_closed => true}
479 assert issue2.save
478 assert issue2.save
480 # 1 should not be also closed
479 # 1 should not be also closed
481 assert !issue1.reload.closed?
480 assert !issue1.reload.closed?
482 end
481 end
483
482
484 def test_assignable_versions
483 def test_assignable_versions
485 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
484 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
486 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
485 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
487 end
486 end
488
487
489 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
488 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
490 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
489 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
491 assert !issue.save
490 assert !issue.save
492 assert_not_nil issue.errors[:fixed_version_id]
491 assert_not_nil issue.errors[:fixed_version_id]
493 end
492 end
494
493
495 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
494 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
496 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 2, :subject => 'New issue')
495 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 2, :subject => 'New issue')
497 assert !issue.save
496 assert !issue.save
498 assert_not_nil issue.errors[:fixed_version_id]
497 assert_not_nil issue.errors[:fixed_version_id]
499 end
498 end
500
499
501 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
500 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
502 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 3, :subject => 'New issue')
501 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 3, :subject => 'New issue')
503 assert issue.save
502 assert issue.save
504 end
503 end
505
504
506 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
505 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
507 issue = Issue.find(11)
506 issue = Issue.find(11)
508 assert_equal 'closed', issue.fixed_version.status
507 assert_equal 'closed', issue.fixed_version.status
509 issue.subject = 'Subject changed'
508 issue.subject = 'Subject changed'
510 assert issue.save
509 assert issue.save
511 end
510 end
512
511
513 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
512 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
514 issue = Issue.find(11)
513 issue = Issue.find(11)
515 issue.status_id = 1
514 issue.status_id = 1
516 assert !issue.save
515 assert !issue.save
517 assert_not_nil issue.errors[:base]
516 assert_not_nil issue.errors[:base]
518 end
517 end
519
518
520 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
519 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
521 issue = Issue.find(11)
520 issue = Issue.find(11)
522 issue.status_id = 1
521 issue.status_id = 1
523 issue.fixed_version_id = 3
522 issue.fixed_version_id = 3
524 assert issue.save
523 assert issue.save
525 end
524 end
526
525
527 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
526 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
528 issue = Issue.find(12)
527 issue = Issue.find(12)
529 assert_equal 'locked', issue.fixed_version.status
528 assert_equal 'locked', issue.fixed_version.status
530 issue.status_id = 1
529 issue.status_id = 1
531 assert issue.save
530 assert issue.save
532 end
531 end
533
532
534 def test_move_to_another_project_with_same_category
533 def test_move_to_another_project_with_same_category
535 issue = Issue.find(1)
534 issue = Issue.find(1)
536 issue.project = Project.find(2)
535 issue.project = Project.find(2)
537 assert issue.save
536 assert issue.save
538 issue.reload
537 issue.reload
539 assert_equal 2, issue.project_id
538 assert_equal 2, issue.project_id
540 # Category changes
539 # Category changes
541 assert_equal 4, issue.category_id
540 assert_equal 4, issue.category_id
542 # Make sure time entries were move to the target project
541 # Make sure time entries were move to the target project
543 assert_equal 2, issue.time_entries.first.project_id
542 assert_equal 2, issue.time_entries.first.project_id
544 end
543 end
545
544
546 def test_move_to_another_project_without_same_category
545 def test_move_to_another_project_without_same_category
547 issue = Issue.find(2)
546 issue = Issue.find(2)
548 issue.project = Project.find(2)
547 issue.project = Project.find(2)
549 assert issue.save
548 assert issue.save
550 issue.reload
549 issue.reload
551 assert_equal 2, issue.project_id
550 assert_equal 2, issue.project_id
552 # Category cleared
551 # Category cleared
553 assert_nil issue.category_id
552 assert_nil issue.category_id
554 end
553 end
555
554
556 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
555 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
557 issue = Issue.find(1)
556 issue = Issue.find(1)
558 issue.update_attribute(:fixed_version_id, 1)
557 issue.update_attribute(:fixed_version_id, 1)
559 issue.project = Project.find(2)
558 issue.project = Project.find(2)
560 assert issue.save
559 assert issue.save
561 issue.reload
560 issue.reload
562 assert_equal 2, issue.project_id
561 assert_equal 2, issue.project_id
563 # Cleared fixed_version
562 # Cleared fixed_version
564 assert_equal nil, issue.fixed_version
563 assert_equal nil, issue.fixed_version
565 end
564 end
566
565
567 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
566 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
568 issue = Issue.find(1)
567 issue = Issue.find(1)
569 issue.update_attribute(:fixed_version_id, 4)
568 issue.update_attribute(:fixed_version_id, 4)
570 issue.project = Project.find(5)
569 issue.project = Project.find(5)
571 assert issue.save
570 assert issue.save
572 issue.reload
571 issue.reload
573 assert_equal 5, issue.project_id
572 assert_equal 5, issue.project_id
574 # Keep fixed_version
573 # Keep fixed_version
575 assert_equal 4, issue.fixed_version_id
574 assert_equal 4, issue.fixed_version_id
576 end
575 end
577
576
578 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
577 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
579 issue = Issue.find(1)
578 issue = Issue.find(1)
580 issue.update_attribute(:fixed_version_id, 1)
579 issue.update_attribute(:fixed_version_id, 1)
581 issue.project = Project.find(5)
580 issue.project = Project.find(5)
582 assert issue.save
581 assert issue.save
583 issue.reload
582 issue.reload
584 assert_equal 5, issue.project_id
583 assert_equal 5, issue.project_id
585 # Cleared fixed_version
584 # Cleared fixed_version
586 assert_equal nil, issue.fixed_version
585 assert_equal nil, issue.fixed_version
587 end
586 end
588
587
589 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
588 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
590 issue = Issue.find(1)
589 issue = Issue.find(1)
591 issue.update_attribute(:fixed_version_id, 7)
590 issue.update_attribute(:fixed_version_id, 7)
592 issue.project = Project.find(2)
591 issue.project = Project.find(2)
593 assert issue.save
592 assert issue.save
594 issue.reload
593 issue.reload
595 assert_equal 2, issue.project_id
594 assert_equal 2, issue.project_id
596 # Keep fixed_version
595 # Keep fixed_version
597 assert_equal 7, issue.fixed_version_id
596 assert_equal 7, issue.fixed_version_id
598 end
597 end
599
598
600 def test_move_to_another_project_with_disabled_tracker
599 def test_move_to_another_project_with_disabled_tracker
601 issue = Issue.find(1)
600 issue = Issue.find(1)
602 target = Project.find(2)
601 target = Project.find(2)
603 target.tracker_ids = [3]
602 target.tracker_ids = [3]
604 target.save
603 target.save
605 issue.project = target
604 issue.project = target
606 assert issue.save
605 assert issue.save
607 issue.reload
606 issue.reload
608 assert_equal 2, issue.project_id
607 assert_equal 2, issue.project_id
609 assert_equal 3, issue.tracker_id
608 assert_equal 3, issue.tracker_id
610 end
609 end
611
610
612 def test_copy_to_the_same_project
611 def test_copy_to_the_same_project
613 issue = Issue.find(1)
612 issue = Issue.find(1)
614 copy = issue.copy
613 copy = issue.copy
615 assert_difference 'Issue.count' do
614 assert_difference 'Issue.count' do
616 copy.save!
615 copy.save!
617 end
616 end
618 assert_kind_of Issue, copy
617 assert_kind_of Issue, copy
619 assert_equal issue.project, copy.project
618 assert_equal issue.project, copy.project
620 assert_equal "125", copy.custom_value_for(2).value
619 assert_equal "125", copy.custom_value_for(2).value
621 end
620 end
622
621
623 def test_copy_to_another_project_and_tracker
622 def test_copy_to_another_project_and_tracker
624 issue = Issue.find(1)
623 issue = Issue.find(1)
625 copy = issue.copy(:project_id => 3, :tracker_id => 2)
624 copy = issue.copy(:project_id => 3, :tracker_id => 2)
626 assert_difference 'Issue.count' do
625 assert_difference 'Issue.count' do
627 copy.save!
626 copy.save!
628 end
627 end
629 copy.reload
628 copy.reload
630 assert_kind_of Issue, copy
629 assert_kind_of Issue, copy
631 assert_equal Project.find(3), copy.project
630 assert_equal Project.find(3), copy.project
632 assert_equal Tracker.find(2), copy.tracker
631 assert_equal Tracker.find(2), copy.tracker
633 # Custom field #2 is not associated with target tracker
632 # Custom field #2 is not associated with target tracker
634 assert_nil copy.custom_value_for(2)
633 assert_nil copy.custom_value_for(2)
635 end
634 end
636
635
637 context "#copy" do
636 context "#copy" do
638 setup do
637 setup do
639 @issue = Issue.find(1)
638 @issue = Issue.find(1)
640 end
639 end
641
640
642 should "not create a journal" do
641 should "not create a journal" do
643 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
642 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
644 copy.save!
643 copy.save!
645 assert_equal 0, copy.reload.journals.size
644 assert_equal 0, copy.reload.journals.size
646 end
645 end
647
646
648 should "allow assigned_to changes" do
647 should "allow assigned_to changes" do
649 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
648 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
650 assert_equal 3, copy.assigned_to_id
649 assert_equal 3, copy.assigned_to_id
651 end
650 end
652
651
653 should "allow status changes" do
652 should "allow status changes" do
654 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
653 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
655 assert_equal 2, copy.status_id
654 assert_equal 2, copy.status_id
656 end
655 end
657
656
658 should "allow start date changes" do
657 should "allow start date changes" do
659 date = Date.today
658 date = Date.today
660 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
659 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
661 assert_equal date, copy.start_date
660 assert_equal date, copy.start_date
662 end
661 end
663
662
664 should "allow due date changes" do
663 should "allow due date changes" do
665 date = Date.today
664 date = Date.today
666 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :due_date => date)
665 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :due_date => date)
667 assert_equal date, copy.due_date
666 assert_equal date, copy.due_date
668 end
667 end
669
668
670 should "set current user as author" do
669 should "set current user as author" do
671 User.current = User.find(9)
670 User.current = User.find(9)
672 copy = @issue.copy(:project_id => 3, :tracker_id => 2)
671 copy = @issue.copy(:project_id => 3, :tracker_id => 2)
673 assert_equal User.current, copy.author
672 assert_equal User.current, copy.author
674 end
673 end
675
674
676 should "create a journal with notes" do
675 should "create a journal with notes" do
677 date = Date.today
676 date = Date.today
678 notes = "Notes added when copying"
677 notes = "Notes added when copying"
679 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
678 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
680 copy.init_journal(User.current, notes)
679 copy.init_journal(User.current, notes)
681 copy.save!
680 copy.save!
682
681
683 assert_equal 1, copy.journals.size
682 assert_equal 1, copy.journals.size
684 journal = copy.journals.first
683 journal = copy.journals.first
685 assert_equal 0, journal.details.size
684 assert_equal 0, journal.details.size
686 assert_equal notes, journal.notes
685 assert_equal notes, journal.notes
687 end
686 end
688 end
687 end
689
688
690 def test_recipients_should_include_previous_assignee
689 def test_recipients_should_include_previous_assignee
691 user = User.find(3)
690 user = User.find(3)
692 user.members.update_all ["mail_notification = ?", false]
691 user.members.update_all ["mail_notification = ?", false]
693 user.update_attribute :mail_notification, 'only_assigned'
692 user.update_attribute :mail_notification, 'only_assigned'
694
693
695 issue = Issue.find(2)
694 issue = Issue.find(2)
696 issue.assigned_to = nil
695 issue.assigned_to = nil
697 assert_include user.mail, issue.recipients
696 assert_include user.mail, issue.recipients
698 issue.save!
697 issue.save!
699 assert !issue.recipients.include?(user.mail)
698 assert !issue.recipients.include?(user.mail)
700 end
699 end
701
700
702 def test_recipients_should_not_include_users_that_cannot_view_the_issue
701 def test_recipients_should_not_include_users_that_cannot_view_the_issue
703 issue = Issue.find(12)
702 issue = Issue.find(12)
704 assert issue.recipients.include?(issue.author.mail)
703 assert issue.recipients.include?(issue.author.mail)
705 # copy the issue to a private project
704 # copy the issue to a private project
706 copy = issue.copy(:project_id => 5, :tracker_id => 2)
705 copy = issue.copy(:project_id => 5, :tracker_id => 2)
707 # author is not a member of project anymore
706 # author is not a member of project anymore
708 assert !copy.recipients.include?(copy.author.mail)
707 assert !copy.recipients.include?(copy.author.mail)
709 end
708 end
710
709
711 def test_recipients_should_include_the_assigned_group_members
710 def test_recipients_should_include_the_assigned_group_members
712 group_member = User.generate_with_protected!
711 group_member = User.generate_with_protected!
713 group = Group.generate!
712 group = Group.generate!
714 group.users << group_member
713 group.users << group_member
715
714
716 issue = Issue.find(12)
715 issue = Issue.find(12)
717 issue.assigned_to = group
716 issue.assigned_to = group
718 assert issue.recipients.include?(group_member.mail)
717 assert issue.recipients.include?(group_member.mail)
719 end
718 end
720
719
721 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
720 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
722 user = User.find(3)
721 user = User.find(3)
723 issue = Issue.find(9)
722 issue = Issue.find(9)
724 Watcher.create!(:user => user, :watchable => issue)
723 Watcher.create!(:user => user, :watchable => issue)
725 assert issue.watched_by?(user)
724 assert issue.watched_by?(user)
726 assert !issue.watcher_recipients.include?(user.mail)
725 assert !issue.watcher_recipients.include?(user.mail)
727 end
726 end
728
727
729 def test_issue_destroy
728 def test_issue_destroy
730 Issue.find(1).destroy
729 Issue.find(1).destroy
731 assert_nil Issue.find_by_id(1)
730 assert_nil Issue.find_by_id(1)
732 assert_nil TimeEntry.find_by_issue_id(1)
731 assert_nil TimeEntry.find_by_issue_id(1)
733 end
732 end
734
733
735 def test_blocked
734 def test_blocked
736 blocked_issue = Issue.find(9)
735 blocked_issue = Issue.find(9)
737 blocking_issue = Issue.find(10)
736 blocking_issue = Issue.find(10)
738
737
739 assert blocked_issue.blocked?
738 assert blocked_issue.blocked?
740 assert !blocking_issue.blocked?
739 assert !blocking_issue.blocked?
741 end
740 end
742
741
743 def test_blocked_issues_dont_allow_closed_statuses
742 def test_blocked_issues_dont_allow_closed_statuses
744 blocked_issue = Issue.find(9)
743 blocked_issue = Issue.find(9)
745
744
746 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
745 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
747 assert !allowed_statuses.empty?
746 assert !allowed_statuses.empty?
748 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
747 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
749 assert closed_statuses.empty?
748 assert closed_statuses.empty?
750 end
749 end
751
750
752 def test_unblocked_issues_allow_closed_statuses
751 def test_unblocked_issues_allow_closed_statuses
753 blocking_issue = Issue.find(10)
752 blocking_issue = Issue.find(10)
754
753
755 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
754 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
756 assert !allowed_statuses.empty?
755 assert !allowed_statuses.empty?
757 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
756 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
758 assert !closed_statuses.empty?
757 assert !closed_statuses.empty?
759 end
758 end
760
759
761 def test_rescheduling_an_issue_should_reschedule_following_issue
760 def test_rescheduling_an_issue_should_reschedule_following_issue
762 issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => '-', :start_date => Date.today, :due_date => Date.today + 2)
761 issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => '-', :start_date => Date.today, :due_date => Date.today + 2)
763 issue2 = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => '-', :start_date => Date.today, :due_date => Date.today + 2)
762 issue2 = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => '-', :start_date => Date.today, :due_date => Date.today + 2)
764 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
763 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
765 assert_equal issue1.due_date + 1, issue2.reload.start_date
764 assert_equal issue1.due_date + 1, issue2.reload.start_date
766
765
767 issue1.due_date = Date.today + 5
766 issue1.due_date = Date.today + 5
768 issue1.save!
767 issue1.save!
769 assert_equal issue1.due_date + 1, issue2.reload.start_date
768 assert_equal issue1.due_date + 1, issue2.reload.start_date
770 end
769 end
771
770
772 def test_overdue
771 def test_overdue
773 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
772 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
774 assert !Issue.new(:due_date => Date.today).overdue?
773 assert !Issue.new(:due_date => Date.today).overdue?
775 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
774 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
776 assert !Issue.new(:due_date => nil).overdue?
775 assert !Issue.new(:due_date => nil).overdue?
777 assert !Issue.new(:due_date => 1.day.ago.to_date, :status => IssueStatus.find(:first, :conditions => {:is_closed => true})).overdue?
776 assert !Issue.new(:due_date => 1.day.ago.to_date, :status => IssueStatus.find(:first, :conditions => {:is_closed => true})).overdue?
778 end
777 end
779
778
780 context "#behind_schedule?" do
779 context "#behind_schedule?" do
781 should "be false if the issue has no start_date" do
780 should "be false if the issue has no start_date" do
782 assert !Issue.new(:start_date => nil, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
781 assert !Issue.new(:start_date => nil, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
783 end
782 end
784
783
785 should "be false if the issue has no end_date" do
784 should "be false if the issue has no end_date" do
786 assert !Issue.new(:start_date => 1.day.from_now.to_date, :due_date => nil, :done_ratio => 0).behind_schedule?
785 assert !Issue.new(:start_date => 1.day.from_now.to_date, :due_date => nil, :done_ratio => 0).behind_schedule?
787 end
786 end
788
787
789 should "be false if the issue has more done than it's calendar time" do
788 should "be false if the issue has more done than it's calendar time" do
790 assert !Issue.new(:start_date => 50.days.ago.to_date, :due_date => 50.days.from_now.to_date, :done_ratio => 90).behind_schedule?
789 assert !Issue.new(:start_date => 50.days.ago.to_date, :due_date => 50.days.from_now.to_date, :done_ratio => 90).behind_schedule?
791 end
790 end
792
791
793 should "be true if the issue hasn't been started at all" do
792 should "be true if the issue hasn't been started at all" do
794 assert Issue.new(:start_date => 1.day.ago.to_date, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
793 assert Issue.new(:start_date => 1.day.ago.to_date, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
795 end
794 end
796
795
797 should "be true if the issue has used more calendar time than it's done ratio" do
796 should "be true if the issue has used more calendar time than it's done ratio" do
798 assert Issue.new(:start_date => 100.days.ago.to_date, :due_date => Date.today, :done_ratio => 90).behind_schedule?
797 assert Issue.new(:start_date => 100.days.ago.to_date, :due_date => Date.today, :done_ratio => 90).behind_schedule?
799 end
798 end
800 end
799 end
801
800
802 context "#assignable_users" do
801 context "#assignable_users" do
803 should "be Users" do
802 should "be Users" do
804 assert_kind_of User, Issue.find(1).assignable_users.first
803 assert_kind_of User, Issue.find(1).assignable_users.first
805 end
804 end
806
805
807 should "include the issue author" do
806 should "include the issue author" do
808 project = Project.find(1)
807 project = Project.find(1)
809 non_project_member = User.generate!
808 non_project_member = User.generate!
810 issue = Issue.generate_for_project!(project, :author => non_project_member)
809 issue = Issue.generate_for_project!(project, :author => non_project_member)
811
810
812 assert issue.assignable_users.include?(non_project_member)
811 assert issue.assignable_users.include?(non_project_member)
813 end
812 end
814
813
815 should "include the current assignee" do
814 should "include the current assignee" do
816 project = Project.find(1)
815 project = Project.find(1)
817 user = User.generate!
816 user = User.generate!
818 issue = Issue.generate_for_project!(project, :assigned_to => user)
817 issue = Issue.generate_for_project!(project, :assigned_to => user)
819 user.lock!
818 user.lock!
820
819
821 assert Issue.find(issue.id).assignable_users.include?(user)
820 assert Issue.find(issue.id).assignable_users.include?(user)
822 end
821 end
823
822
824 should "not show the issue author twice" do
823 should "not show the issue author twice" do
825 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
824 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
826 assert_equal 2, assignable_user_ids.length
825 assert_equal 2, assignable_user_ids.length
827
826
828 assignable_user_ids.each do |user_id|
827 assignable_user_ids.each do |user_id|
829 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length, "User #{user_id} appears more or less than once"
828 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length, "User #{user_id} appears more or less than once"
830 end
829 end
831 end
830 end
832
831
833 context "with issue_group_assignment" do
832 context "with issue_group_assignment" do
834 should "include groups" do
833 should "include groups" do
835 issue = Issue.new(:project => Project.find(2))
834 issue = Issue.new(:project => Project.find(2))
836
835
837 with_settings :issue_group_assignment => '1' do
836 with_settings :issue_group_assignment => '1' do
838 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
837 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
839 assert issue.assignable_users.include?(Group.find(11))
838 assert issue.assignable_users.include?(Group.find(11))
840 end
839 end
841 end
840 end
842 end
841 end
843
842
844 context "without issue_group_assignment" do
843 context "without issue_group_assignment" do
845 should "not include groups" do
844 should "not include groups" do
846 issue = Issue.new(:project => Project.find(2))
845 issue = Issue.new(:project => Project.find(2))
847
846
848 with_settings :issue_group_assignment => '0' do
847 with_settings :issue_group_assignment => '0' do
849 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
848 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
850 assert !issue.assignable_users.include?(Group.find(11))
849 assert !issue.assignable_users.include?(Group.find(11))
851 end
850 end
852 end
851 end
853 end
852 end
854 end
853 end
855
854
856 def test_create_should_send_email_notification
855 def test_create_should_send_email_notification
857 ActionMailer::Base.deliveries.clear
856 ActionMailer::Base.deliveries.clear
858 issue = Issue.new(:project_id => 1, :tracker_id => 1,
857 issue = Issue.new(:project_id => 1, :tracker_id => 1,
859 :author_id => 3, :status_id => 1,
858 :author_id => 3, :status_id => 1,
860 :priority => IssuePriority.all.first,
859 :priority => IssuePriority.all.first,
861 :subject => 'test_create', :estimated_hours => '1:30')
860 :subject => 'test_create', :estimated_hours => '1:30')
862
861
863 assert issue.save
862 assert issue.save
864 assert_equal 1, ActionMailer::Base.deliveries.size
863 assert_equal 1, ActionMailer::Base.deliveries.size
865 end
864 end
866
865
867 def test_stale_issue_should_not_send_email_notification
866 def test_stale_issue_should_not_send_email_notification
868 ActionMailer::Base.deliveries.clear
867 ActionMailer::Base.deliveries.clear
869 issue = Issue.find(1)
868 issue = Issue.find(1)
870 stale = Issue.find(1)
869 stale = Issue.find(1)
871
870
872 issue.init_journal(User.find(1))
871 issue.init_journal(User.find(1))
873 issue.subject = 'Subjet update'
872 issue.subject = 'Subjet update'
874 assert issue.save
873 assert issue.save
875 assert_equal 1, ActionMailer::Base.deliveries.size
874 assert_equal 1, ActionMailer::Base.deliveries.size
876 ActionMailer::Base.deliveries.clear
875 ActionMailer::Base.deliveries.clear
877
876
878 stale.init_journal(User.find(1))
877 stale.init_journal(User.find(1))
879 stale.subject = 'Another subjet update'
878 stale.subject = 'Another subjet update'
880 assert_raise ActiveRecord::StaleObjectError do
879 assert_raise ActiveRecord::StaleObjectError do
881 stale.save
880 stale.save
882 end
881 end
883 assert ActionMailer::Base.deliveries.empty?
882 assert ActionMailer::Base.deliveries.empty?
884 end
883 end
885
884
886 def test_journalized_description
885 def test_journalized_description
887 IssueCustomField.delete_all
886 IssueCustomField.delete_all
888
887
889 i = Issue.first
888 i = Issue.first
890 old_description = i.description
889 old_description = i.description
891 new_description = "This is the new description"
890 new_description = "This is the new description"
892
891
893 i.init_journal(User.find(2))
892 i.init_journal(User.find(2))
894 i.description = new_description
893 i.description = new_description
895 assert_difference 'Journal.count', 1 do
894 assert_difference 'Journal.count', 1 do
896 assert_difference 'JournalDetail.count', 1 do
895 assert_difference 'JournalDetail.count', 1 do
897 i.save!
896 i.save!
898 end
897 end
899 end
898 end
900
899
901 detail = JournalDetail.first(:order => 'id DESC')
900 detail = JournalDetail.first(:order => 'id DESC')
902 assert_equal i, detail.journal.journalized
901 assert_equal i, detail.journal.journalized
903 assert_equal 'attr', detail.property
902 assert_equal 'attr', detail.property
904 assert_equal 'description', detail.prop_key
903 assert_equal 'description', detail.prop_key
905 assert_equal old_description, detail.old_value
904 assert_equal old_description, detail.old_value
906 assert_equal new_description, detail.value
905 assert_equal new_description, detail.value
907 end
906 end
908
907
909 def test_blank_descriptions_should_not_be_journalized
908 def test_blank_descriptions_should_not_be_journalized
910 IssueCustomField.delete_all
909 IssueCustomField.delete_all
911 Issue.update_all("description = NULL", "id=1")
910 Issue.update_all("description = NULL", "id=1")
912
911
913 i = Issue.find(1)
912 i = Issue.find(1)
914 i.init_journal(User.find(2))
913 i.init_journal(User.find(2))
915 i.subject = "blank description"
914 i.subject = "blank description"
916 i.description = "\r\n"
915 i.description = "\r\n"
917
916
918 assert_difference 'Journal.count', 1 do
917 assert_difference 'Journal.count', 1 do
919 assert_difference 'JournalDetail.count', 1 do
918 assert_difference 'JournalDetail.count', 1 do
920 i.save!
919 i.save!
921 end
920 end
922 end
921 end
923 end
922 end
924
923
925 def test_description_eol_should_be_normalized
924 def test_description_eol_should_be_normalized
926 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
925 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
927 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
926 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
928 end
927 end
929
928
930 def test_saving_twice_should_not_duplicate_journal_details
929 def test_saving_twice_should_not_duplicate_journal_details
931 i = Issue.find(:first)
930 i = Issue.find(:first)
932 i.init_journal(User.find(2), 'Some notes')
931 i.init_journal(User.find(2), 'Some notes')
933 # initial changes
932 # initial changes
934 i.subject = 'New subject'
933 i.subject = 'New subject'
935 i.done_ratio = i.done_ratio + 10
934 i.done_ratio = i.done_ratio + 10
936 assert_difference 'Journal.count' do
935 assert_difference 'Journal.count' do
937 assert i.save
936 assert i.save
938 end
937 end
939 # 1 more change
938 # 1 more change
940 i.priority = IssuePriority.find(:first, :conditions => ["id <> ?", i.priority_id])
939 i.priority = IssuePriority.find(:first, :conditions => ["id <> ?", i.priority_id])
941 assert_no_difference 'Journal.count' do
940 assert_no_difference 'Journal.count' do
942 assert_difference 'JournalDetail.count', 1 do
941 assert_difference 'JournalDetail.count', 1 do
943 i.save
942 i.save
944 end
943 end
945 end
944 end
946 # no more change
945 # no more change
947 assert_no_difference 'Journal.count' do
946 assert_no_difference 'Journal.count' do
948 assert_no_difference 'JournalDetail.count' do
947 assert_no_difference 'JournalDetail.count' do
949 i.save
948 i.save
950 end
949 end
951 end
950 end
952 end
951 end
953
952
954 def test_all_dependent_issues
953 def test_all_dependent_issues
955 IssueRelation.delete_all
954 IssueRelation.delete_all
956 assert IssueRelation.create!(:issue_from => Issue.find(1),
955 assert IssueRelation.create!(:issue_from => Issue.find(1),
957 :issue_to => Issue.find(2),
956 :issue_to => Issue.find(2),
958 :relation_type => IssueRelation::TYPE_PRECEDES)
957 :relation_type => IssueRelation::TYPE_PRECEDES)
959 assert IssueRelation.create!(:issue_from => Issue.find(2),
958 assert IssueRelation.create!(:issue_from => Issue.find(2),
960 :issue_to => Issue.find(3),
959 :issue_to => Issue.find(3),
961 :relation_type => IssueRelation::TYPE_PRECEDES)
960 :relation_type => IssueRelation::TYPE_PRECEDES)
962 assert IssueRelation.create!(:issue_from => Issue.find(3),
961 assert IssueRelation.create!(:issue_from => Issue.find(3),
963 :issue_to => Issue.find(8),
962 :issue_to => Issue.find(8),
964 :relation_type => IssueRelation::TYPE_PRECEDES)
963 :relation_type => IssueRelation::TYPE_PRECEDES)
965
964
966 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
965 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
967 end
966 end
968
967
969 def test_all_dependent_issues_with_persistent_circular_dependency
968 def test_all_dependent_issues_with_persistent_circular_dependency
970 IssueRelation.delete_all
969 IssueRelation.delete_all
971 assert IssueRelation.create!(:issue_from => Issue.find(1),
970 assert IssueRelation.create!(:issue_from => Issue.find(1),
972 :issue_to => Issue.find(2),
971 :issue_to => Issue.find(2),
973 :relation_type => IssueRelation::TYPE_PRECEDES)
972 :relation_type => IssueRelation::TYPE_PRECEDES)
974 assert IssueRelation.create!(:issue_from => Issue.find(2),
973 assert IssueRelation.create!(:issue_from => Issue.find(2),
975 :issue_to => Issue.find(3),
974 :issue_to => Issue.find(3),
976 :relation_type => IssueRelation::TYPE_PRECEDES)
975 :relation_type => IssueRelation::TYPE_PRECEDES)
977 # Validation skipping
976 # Validation skipping
978 assert IssueRelation.new(:issue_from => Issue.find(3),
977 assert IssueRelation.new(:issue_from => Issue.find(3),
979 :issue_to => Issue.find(1),
978 :issue_to => Issue.find(1),
980 :relation_type => IssueRelation::TYPE_PRECEDES).save(false)
979 :relation_type => IssueRelation::TYPE_PRECEDES).save(false)
981
980
982 assert_equal [2, 3], Issue.find(1).all_dependent_issues.collect(&:id).sort
981 assert_equal [2, 3], Issue.find(1).all_dependent_issues.collect(&:id).sort
983 end
982 end
984
983
985 def test_all_dependent_issues_with_persistent_multiple_circular_dependencies
984 def test_all_dependent_issues_with_persistent_multiple_circular_dependencies
986 IssueRelation.delete_all
985 IssueRelation.delete_all
987 assert IssueRelation.create!(:issue_from => Issue.find(1),
986 assert IssueRelation.create!(:issue_from => Issue.find(1),
988 :issue_to => Issue.find(2),
987 :issue_to => Issue.find(2),
989 :relation_type => IssueRelation::TYPE_RELATES)
988 :relation_type => IssueRelation::TYPE_RELATES)
990 assert IssueRelation.create!(:issue_from => Issue.find(2),
989 assert IssueRelation.create!(:issue_from => Issue.find(2),
991 :issue_to => Issue.find(3),
990 :issue_to => Issue.find(3),
992 :relation_type => IssueRelation::TYPE_RELATES)
991 :relation_type => IssueRelation::TYPE_RELATES)
993 assert IssueRelation.create!(:issue_from => Issue.find(3),
992 assert IssueRelation.create!(:issue_from => Issue.find(3),
994 :issue_to => Issue.find(8),
993 :issue_to => Issue.find(8),
995 :relation_type => IssueRelation::TYPE_RELATES)
994 :relation_type => IssueRelation::TYPE_RELATES)
996 # Validation skipping
995 # Validation skipping
997 assert IssueRelation.new(:issue_from => Issue.find(8),
996 assert IssueRelation.new(:issue_from => Issue.find(8),
998 :issue_to => Issue.find(2),
997 :issue_to => Issue.find(2),
999 :relation_type => IssueRelation::TYPE_RELATES).save(false)
998 :relation_type => IssueRelation::TYPE_RELATES).save(false)
1000 assert IssueRelation.new(:issue_from => Issue.find(3),
999 assert IssueRelation.new(:issue_from => Issue.find(3),
1001 :issue_to => Issue.find(1),
1000 :issue_to => Issue.find(1),
1002 :relation_type => IssueRelation::TYPE_RELATES).save(false)
1001 :relation_type => IssueRelation::TYPE_RELATES).save(false)
1003
1002
1004 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1003 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1005 end
1004 end
1006
1005
1007 context "#done_ratio" do
1006 context "#done_ratio" do
1008 setup do
1007 setup do
1009 @issue = Issue.find(1)
1008 @issue = Issue.find(1)
1010 @issue_status = IssueStatus.find(1)
1009 @issue_status = IssueStatus.find(1)
1011 @issue_status.update_attribute(:default_done_ratio, 50)
1010 @issue_status.update_attribute(:default_done_ratio, 50)
1012 @issue2 = Issue.find(2)
1011 @issue2 = Issue.find(2)
1013 @issue_status2 = IssueStatus.find(2)
1012 @issue_status2 = IssueStatus.find(2)
1014 @issue_status2.update_attribute(:default_done_ratio, 0)
1013 @issue_status2.update_attribute(:default_done_ratio, 0)
1015 end
1014 end
1016
1015
1017 teardown do
1016 teardown do
1018 Setting.issue_done_ratio = 'issue_field'
1017 Setting.issue_done_ratio = 'issue_field'
1019 end
1018 end
1020
1019
1021 context "with Setting.issue_done_ratio using the issue_field" do
1020 context "with Setting.issue_done_ratio using the issue_field" do
1022 setup do
1021 setup do
1023 Setting.issue_done_ratio = 'issue_field'
1022 Setting.issue_done_ratio = 'issue_field'
1024 end
1023 end
1025
1024
1026 should "read the issue's field" do
1025 should "read the issue's field" do
1027 assert_equal 0, @issue.done_ratio
1026 assert_equal 0, @issue.done_ratio
1028 assert_equal 30, @issue2.done_ratio
1027 assert_equal 30, @issue2.done_ratio
1029 end
1028 end
1030 end
1029 end
1031
1030
1032 context "with Setting.issue_done_ratio using the issue_status" do
1031 context "with Setting.issue_done_ratio using the issue_status" do
1033 setup do
1032 setup do
1034 Setting.issue_done_ratio = 'issue_status'
1033 Setting.issue_done_ratio = 'issue_status'
1035 end
1034 end
1036
1035
1037 should "read the Issue Status's default done ratio" do
1036 should "read the Issue Status's default done ratio" do
1038 assert_equal 50, @issue.done_ratio
1037 assert_equal 50, @issue.done_ratio
1039 assert_equal 0, @issue2.done_ratio
1038 assert_equal 0, @issue2.done_ratio
1040 end
1039 end
1041 end
1040 end
1042 end
1041 end
1043
1042
1044 context "#update_done_ratio_from_issue_status" do
1043 context "#update_done_ratio_from_issue_status" do
1045 setup do
1044 setup do
1046 @issue = Issue.find(1)
1045 @issue = Issue.find(1)
1047 @issue_status = IssueStatus.find(1)
1046 @issue_status = IssueStatus.find(1)
1048 @issue_status.update_attribute(:default_done_ratio, 50)
1047 @issue_status.update_attribute(:default_done_ratio, 50)
1049 @issue2 = Issue.find(2)
1048 @issue2 = Issue.find(2)
1050 @issue_status2 = IssueStatus.find(2)
1049 @issue_status2 = IssueStatus.find(2)
1051 @issue_status2.update_attribute(:default_done_ratio, 0)
1050 @issue_status2.update_attribute(:default_done_ratio, 0)
1052 end
1051 end
1053
1052
1054 context "with Setting.issue_done_ratio using the issue_field" do
1053 context "with Setting.issue_done_ratio using the issue_field" do
1055 setup do
1054 setup do
1056 Setting.issue_done_ratio = 'issue_field'
1055 Setting.issue_done_ratio = 'issue_field'
1057 end
1056 end
1058
1057
1059 should "not change the issue" do
1058 should "not change the issue" do
1060 @issue.update_done_ratio_from_issue_status
1059 @issue.update_done_ratio_from_issue_status
1061 @issue2.update_done_ratio_from_issue_status
1060 @issue2.update_done_ratio_from_issue_status
1062
1061
1063 assert_equal 0, @issue.read_attribute(:done_ratio)
1062 assert_equal 0, @issue.read_attribute(:done_ratio)
1064 assert_equal 30, @issue2.read_attribute(:done_ratio)
1063 assert_equal 30, @issue2.read_attribute(:done_ratio)
1065 end
1064 end
1066 end
1065 end
1067
1066
1068 context "with Setting.issue_done_ratio using the issue_status" do
1067 context "with Setting.issue_done_ratio using the issue_status" do
1069 setup do
1068 setup do
1070 Setting.issue_done_ratio = 'issue_status'
1069 Setting.issue_done_ratio = 'issue_status'
1071 end
1070 end
1072
1071
1073 should "change the issue's done ratio" do
1072 should "change the issue's done ratio" do
1074 @issue.update_done_ratio_from_issue_status
1073 @issue.update_done_ratio_from_issue_status
1075 @issue2.update_done_ratio_from_issue_status
1074 @issue2.update_done_ratio_from_issue_status
1076
1075
1077 assert_equal 50, @issue.read_attribute(:done_ratio)
1076 assert_equal 50, @issue.read_attribute(:done_ratio)
1078 assert_equal 0, @issue2.read_attribute(:done_ratio)
1077 assert_equal 0, @issue2.read_attribute(:done_ratio)
1079 end
1078 end
1080 end
1079 end
1081 end
1080 end
1082
1081
1083 test "#by_tracker" do
1082 test "#by_tracker" do
1084 User.current = User.anonymous
1083 User.current = User.anonymous
1085 groups = Issue.by_tracker(Project.find(1))
1084 groups = Issue.by_tracker(Project.find(1))
1086 assert_equal 3, groups.size
1085 assert_equal 3, groups.size
1087 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1086 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1088 end
1087 end
1089
1088
1090 test "#by_version" do
1089 test "#by_version" do
1091 User.current = User.anonymous
1090 User.current = User.anonymous
1092 groups = Issue.by_version(Project.find(1))
1091 groups = Issue.by_version(Project.find(1))
1093 assert_equal 3, groups.size
1092 assert_equal 3, groups.size
1094 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1093 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1095 end
1094 end
1096
1095
1097 test "#by_priority" do
1096 test "#by_priority" do
1098 User.current = User.anonymous
1097 User.current = User.anonymous
1099 groups = Issue.by_priority(Project.find(1))
1098 groups = Issue.by_priority(Project.find(1))
1100 assert_equal 4, groups.size
1099 assert_equal 4, groups.size
1101 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1100 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1102 end
1101 end
1103
1102
1104 test "#by_category" do
1103 test "#by_category" do
1105 User.current = User.anonymous
1104 User.current = User.anonymous
1106 groups = Issue.by_category(Project.find(1))
1105 groups = Issue.by_category(Project.find(1))
1107 assert_equal 2, groups.size
1106 assert_equal 2, groups.size
1108 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1107 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1109 end
1108 end
1110
1109
1111 test "#by_assigned_to" do
1110 test "#by_assigned_to" do
1112 User.current = User.anonymous
1111 User.current = User.anonymous
1113 groups = Issue.by_assigned_to(Project.find(1))
1112 groups = Issue.by_assigned_to(Project.find(1))
1114 assert_equal 2, groups.size
1113 assert_equal 2, groups.size
1115 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1114 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1116 end
1115 end
1117
1116
1118 test "#by_author" do
1117 test "#by_author" do
1119 User.current = User.anonymous
1118 User.current = User.anonymous
1120 groups = Issue.by_author(Project.find(1))
1119 groups = Issue.by_author(Project.find(1))
1121 assert_equal 4, groups.size
1120 assert_equal 4, groups.size
1122 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1121 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1123 end
1122 end
1124
1123
1125 test "#by_subproject" do
1124 test "#by_subproject" do
1126 User.current = User.anonymous
1125 User.current = User.anonymous
1127 groups = Issue.by_subproject(Project.find(1))
1126 groups = Issue.by_subproject(Project.find(1))
1128 # Private descendant not visible
1127 # Private descendant not visible
1129 assert_equal 1, groups.size
1128 assert_equal 1, groups.size
1130 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1129 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1131 end
1130 end
1132
1131
1133 context ".allowed_target_projects_on_move" do
1132 context ".allowed_target_projects_on_move" do
1134 should "return all active projects for admin users" do
1133 should "return all active projects for admin users" do
1135 User.current = User.find(1)
1134 User.current = User.find(1)
1136 assert_equal Project.active.count, Issue.allowed_target_projects_on_move.size
1135 assert_equal Project.active.count, Issue.allowed_target_projects_on_move.size
1137 end
1136 end
1138
1137
1139 should "return allowed projects for non admin users" do
1138 should "return allowed projects for non admin users" do
1140 User.current = User.find(2)
1139 User.current = User.find(2)
1141 Role.non_member.remove_permission! :move_issues
1140 Role.non_member.remove_permission! :move_issues
1142 assert_equal 3, Issue.allowed_target_projects_on_move.size
1141 assert_equal 3, Issue.allowed_target_projects_on_move.size
1143
1142
1144 Role.non_member.add_permission! :move_issues
1143 Role.non_member.add_permission! :move_issues
1145 assert_equal Project.active.count, Issue.allowed_target_projects_on_move.size
1144 assert_equal Project.active.count, Issue.allowed_target_projects_on_move.size
1146 end
1145 end
1147 end
1146 end
1148
1147
1149 def test_recently_updated_with_limit_scopes
1148 def test_recently_updated_with_limit_scopes
1150 #should return the last updated issue
1149 #should return the last updated issue
1151 assert_equal 1, Issue.recently_updated.with_limit(1).length
1150 assert_equal 1, Issue.recently_updated.with_limit(1).length
1152 assert_equal Issue.find(:first, :order => "updated_on DESC"), Issue.recently_updated.with_limit(1).first
1151 assert_equal Issue.find(:first, :order => "updated_on DESC"), Issue.recently_updated.with_limit(1).first
1153 end
1152 end
1154
1153
1155 def test_on_active_projects_scope
1154 def test_on_active_projects_scope
1156 assert Project.find(2).archive
1155 assert Project.find(2).archive
1157
1156
1158 before = Issue.on_active_project.length
1157 before = Issue.on_active_project.length
1159 # test inclusion to results
1158 # test inclusion to results
1160 issue = Issue.generate_for_project!(Project.find(1), :tracker => Project.find(2).trackers.first)
1159 issue = Issue.generate_for_project!(Project.find(1), :tracker => Project.find(2).trackers.first)
1161 assert_equal before + 1, Issue.on_active_project.length
1160 assert_equal before + 1, Issue.on_active_project.length
1162
1161
1163 # Move to an archived project
1162 # Move to an archived project
1164 issue.project = Project.find(2)
1163 issue.project = Project.find(2)
1165 assert issue.save
1164 assert issue.save
1166 assert_equal before, Issue.on_active_project.length
1165 assert_equal before, Issue.on_active_project.length
1167 end
1166 end
1168
1167
1169 context "Issue#recipients" do
1168 context "Issue#recipients" do
1170 setup do
1169 setup do
1171 @project = Project.find(1)
1170 @project = Project.find(1)
1172 @author = User.generate_with_protected!
1171 @author = User.generate_with_protected!
1173 @assignee = User.generate_with_protected!
1172 @assignee = User.generate_with_protected!
1174 @issue = Issue.generate_for_project!(@project, :assigned_to => @assignee, :author => @author)
1173 @issue = Issue.generate_for_project!(@project, :assigned_to => @assignee, :author => @author)
1175 end
1174 end
1176
1175
1177 should "include project recipients" do
1176 should "include project recipients" do
1178 assert @project.recipients.present?
1177 assert @project.recipients.present?
1179 @project.recipients.each do |project_recipient|
1178 @project.recipients.each do |project_recipient|
1180 assert @issue.recipients.include?(project_recipient)
1179 assert @issue.recipients.include?(project_recipient)
1181 end
1180 end
1182 end
1181 end
1183
1182
1184 should "include the author if the author is active" do
1183 should "include the author if the author is active" do
1185 assert @issue.author, "No author set for Issue"
1184 assert @issue.author, "No author set for Issue"
1186 assert @issue.recipients.include?(@issue.author.mail)
1185 assert @issue.recipients.include?(@issue.author.mail)
1187 end
1186 end
1188
1187
1189 should "include the assigned to user if the assigned to user is active" do
1188 should "include the assigned to user if the assigned to user is active" do
1190 assert @issue.assigned_to, "No assigned_to set for Issue"
1189 assert @issue.assigned_to, "No assigned_to set for Issue"
1191 assert @issue.recipients.include?(@issue.assigned_to.mail)
1190 assert @issue.recipients.include?(@issue.assigned_to.mail)
1192 end
1191 end
1193
1192
1194 should "not include users who opt out of all email" do
1193 should "not include users who opt out of all email" do
1195 @author.update_attribute(:mail_notification, :none)
1194 @author.update_attribute(:mail_notification, :none)
1196
1195
1197 assert !@issue.recipients.include?(@issue.author.mail)
1196 assert !@issue.recipients.include?(@issue.author.mail)
1198 end
1197 end
1199
1198
1200 should "not include the issue author if they are only notified of assigned issues" do
1199 should "not include the issue author if they are only notified of assigned issues" do
1201 @author.update_attribute(:mail_notification, :only_assigned)
1200 @author.update_attribute(:mail_notification, :only_assigned)
1202
1201
1203 assert !@issue.recipients.include?(@issue.author.mail)
1202 assert !@issue.recipients.include?(@issue.author.mail)
1204 end
1203 end
1205
1204
1206 should "not include the assigned user if they are only notified of owned issues" do
1205 should "not include the assigned user if they are only notified of owned issues" do
1207 @assignee.update_attribute(:mail_notification, :only_owner)
1206 @assignee.update_attribute(:mail_notification, :only_owner)
1208
1207
1209 assert !@issue.recipients.include?(@issue.assigned_to.mail)
1208 assert !@issue.recipients.include?(@issue.assigned_to.mail)
1210 end
1209 end
1211
1210
1212 end
1211 end
1213 end
1212 end
@@ -1,86 +1,88
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class TimeEntryActivityTest < ActiveSupport::TestCase
20 class TimeEntryActivityTest < ActiveSupport::TestCase
21 fixtures :enumerations, :time_entries
21 fixtures :enumerations, :time_entries
22
22
23 include Redmine::I18n
24
23 def test_should_be_an_enumeration
25 def test_should_be_an_enumeration
24 assert TimeEntryActivity.ancestors.include?(Enumeration)
26 assert TimeEntryActivity.ancestors.include?(Enumeration)
25 end
27 end
26
28
27 def test_objects_count
29 def test_objects_count
28 assert_equal 3, TimeEntryActivity.find_by_name("Design").objects_count
30 assert_equal 3, TimeEntryActivity.find_by_name("Design").objects_count
29 assert_equal 2, TimeEntryActivity.find_by_name("Development").objects_count
31 assert_equal 2, TimeEntryActivity.find_by_name("Development").objects_count
30 end
32 end
31
33
32 def test_option_name
34 def test_option_name
33 assert_equal :enumeration_activities, TimeEntryActivity.new.option_name
35 assert_equal :enumeration_activities, TimeEntryActivity.new.option_name
34 end
36 end
35
37
36 def test_create_with_custom_field
38 def test_create_with_custom_field
37 field = TimeEntryActivityCustomField.find_by_name('Billable')
39 field = TimeEntryActivityCustomField.find_by_name('Billable')
38 e = TimeEntryActivity.new(:name => 'Custom Data')
40 e = TimeEntryActivity.new(:name => 'Custom Data')
39 e.custom_field_values = {field.id => "1"}
41 e.custom_field_values = {field.id => "1"}
40 assert e.save
42 assert e.save
41
43
42 e.reload
44 e.reload
43 assert_equal "1", e.custom_value_for(field).value
45 assert_equal "1", e.custom_value_for(field).value
44 end
46 end
45
47
46 def test_create_without_required_custom_field_should_fail
48 def test_create_without_required_custom_field_should_fail
49 set_language_if_valid 'en'
47 field = TimeEntryActivityCustomField.find_by_name('Billable')
50 field = TimeEntryActivityCustomField.find_by_name('Billable')
48 field.update_attribute(:is_required, true)
51 field.update_attribute(:is_required, true)
49
52
50 e = TimeEntryActivity.new(:name => 'Custom Data')
53 e = TimeEntryActivity.new(:name => 'Custom Data')
51 assert !e.save
54 assert !e.save
52 assert_equal I18n.translate('activerecord.errors.messages.invalid'),
55 assert_equal "Billable can't be blank", e.errors[:base].to_s
53 e.errors[:custom_values].to_s
54 end
56 end
55
57
56 def test_create_with_required_custom_field_should_succeed
58 def test_create_with_required_custom_field_should_succeed
57 field = TimeEntryActivityCustomField.find_by_name('Billable')
59 field = TimeEntryActivityCustomField.find_by_name('Billable')
58 field.update_attribute(:is_required, true)
60 field.update_attribute(:is_required, true)
59
61
60 e = TimeEntryActivity.new(:name => 'Custom Data')
62 e = TimeEntryActivity.new(:name => 'Custom Data')
61 e.custom_field_values = {field.id => "1"}
63 e.custom_field_values = {field.id => "1"}
62 assert e.save
64 assert e.save
63 end
65 end
64
66
65 def test_update_issue_with_required_custom_field_change
67 def test_update_with_required_custom_field_change
68 set_language_if_valid 'en'
66 field = TimeEntryActivityCustomField.find_by_name('Billable')
69 field = TimeEntryActivityCustomField.find_by_name('Billable')
67 field.update_attribute(:is_required, true)
70 field.update_attribute(:is_required, true)
68
71
69 e = TimeEntryActivity.find(10)
72 e = TimeEntryActivity.find(10)
70 assert e.available_custom_fields.include?(field)
73 assert e.available_custom_fields.include?(field)
71 # No change to custom field, record can be saved
74 # No change to custom field, record can be saved
72 assert e.save
75 assert e.save
73 # Blanking custom field, save should fail
76 # Blanking custom field, save should fail
74 e.custom_field_values = {field.id => ""}
77 e.custom_field_values = {field.id => ""}
75 assert !e.save
78 assert !e.save
76 assert e.errors[:custom_values]
79 assert_equal "Billable can't be blank", e.errors[:base].to_s
77
80
78 # Update custom field to valid value, save should succeed
81 # Update custom field to valid value, save should succeed
79 e.custom_field_values = {field.id => "0"}
82 e.custom_field_values = {field.id => "0"}
80 assert e.save
83 assert e.save
81 e.reload
84 e.reload
82 assert_equal "0", e.custom_value_for(field).value
85 assert_equal "0", e.custom_value_for(field).value
83 end
86 end
84
85 end
87 end
86
88
@@ -1,112 +1,139
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module Redmine
18 module Redmine
19 module Acts
19 module Acts
20 module Customizable
20 module Customizable
21 def self.included(base)
21 def self.included(base)
22 base.extend ClassMethods
22 base.extend ClassMethods
23 end
23 end
24
24
25 module ClassMethods
25 module ClassMethods
26 def acts_as_customizable(options = {})
26 def acts_as_customizable(options = {})
27 return if self.included_modules.include?(Redmine::Acts::Customizable::InstanceMethods)
27 return if self.included_modules.include?(Redmine::Acts::Customizable::InstanceMethods)
28 cattr_accessor :customizable_options
28 cattr_accessor :customizable_options
29 self.customizable_options = options
29 self.customizable_options = options
30 has_many :custom_values, :as => :customized,
30 has_many :custom_values, :as => :customized,
31 :include => :custom_field,
31 :include => :custom_field,
32 :order => "#{CustomField.table_name}.position",
32 :order => "#{CustomField.table_name}.position",
33 :dependent => :delete_all
33 :dependent => :delete_all,
34 before_validation { |customized| customized.custom_field_values if customized.new_record? }
34 :validate => false
35 # Trigger validation only if custom values were changed
36 validates_associated :custom_values, :on => :update, :if => Proc.new { |customized| customized.custom_field_values_changed? }
37 send :include, Redmine::Acts::Customizable::InstanceMethods
35 send :include, Redmine::Acts::Customizable::InstanceMethods
38 # Save custom values when saving the customized object
36 validate :validate_custom_field_values
39 after_save :save_custom_field_values
37 after_save :save_custom_field_values
40 end
38 end
41 end
39 end
42
40
43 module InstanceMethods
41 module InstanceMethods
44 def self.included(base)
42 def self.included(base)
45 base.extend ClassMethods
43 base.extend ClassMethods
46 end
44 end
47
45
48 def available_custom_fields
46 def available_custom_fields
49 CustomField.find(:all, :conditions => "type = '#{self.class.name}CustomField'",
47 CustomField.find(:all, :conditions => "type = '#{self.class.name}CustomField'",
50 :order => 'position')
48 :order => 'position')
51 end
49 end
52
50
53 # Sets the values of the object's custom fields
51 # Sets the values of the object's custom fields
54 # values is an array like [{'id' => 1, 'value' => 'foo'}, {'id' => 2, 'value' => 'bar'}]
52 # values is an array like [{'id' => 1, 'value' => 'foo'}, {'id' => 2, 'value' => 'bar'}]
55 def custom_fields=(values)
53 def custom_fields=(values)
56 values_to_hash = values.inject({}) do |hash, v|
54 values_to_hash = values.inject({}) do |hash, v|
57 v = v.stringify_keys
55 v = v.stringify_keys
58 if v['id'] && v.has_key?('value')
56 if v['id'] && v.has_key?('value')
59 hash[v['id']] = v['value']
57 hash[v['id']] = v['value']
60 end
58 end
61 hash
59 hash
62 end
60 end
63 self.custom_field_values = values_to_hash
61 self.custom_field_values = values_to_hash
64 end
62 end
65
63
66 # Sets the values of the object's custom fields
64 # Sets the values of the object's custom fields
67 # values is a hash like {'1' => 'foo', 2 => 'bar'}
65 # values is a hash like {'1' => 'foo', 2 => 'bar'}
68 def custom_field_values=(values)
66 def custom_field_values=(values)
69 @custom_field_values_changed = true
70 values = values.stringify_keys
67 values = values.stringify_keys
71 custom_field_values.each do |custom_value|
68
72 custom_value.value = values[custom_value.custom_field_id.to_s] if values.has_key?(custom_value.custom_field_id.to_s)
69 custom_field_values.each do |custom_field_value|
73 end if values.is_a?(Hash)
70 key = custom_field_value.custom_field_id.to_s
71 if values.has_key?(key)
72 value = values[key]
73 custom_field_value.value = value
74 end
75 end
76 @custom_field_values_changed = true
74 end
77 end
75
78
76 def custom_field_values
79 def custom_field_values
77 @custom_field_values ||= available_custom_fields.collect { |x| custom_values.detect { |v| v.custom_field == x } || custom_values.build(:customized => self, :custom_field => x, :value => nil) }
80 @custom_field_values ||= available_custom_fields.collect do |field|
81 x = CustomFieldValue.new
82 x.custom_field = field
83 x.customized = self
84 cv = custom_values.detect { |v| v.custom_field == field }
85 cv ||= custom_values.build(:customized => self, :custom_field => field, :value => nil)
86 x.value = cv.value
87 x
88 end
78 end
89 end
79
90
80 def visible_custom_field_values
91 def visible_custom_field_values
81 custom_field_values.select(&:visible?)
92 custom_field_values.select(&:visible?)
82 end
93 end
83
94
84 def custom_field_values_changed?
95 def custom_field_values_changed?
85 @custom_field_values_changed == true
96 @custom_field_values_changed == true
86 end
97 end
87
98
88 def custom_value_for(c)
99 def custom_value_for(c)
89 field_id = (c.is_a?(CustomField) ? c.id : c.to_i)
100 field_id = (c.is_a?(CustomField) ? c.id : c.to_i)
90 custom_values.detect {|v| v.custom_field_id == field_id }
101 custom_values.detect {|v| v.custom_field_id == field_id }
91 end
102 end
92
103
104 def custom_field_value(c)
105 field_id = (c.is_a?(CustomField) ? c.id : c.to_i)
106 custom_field_values.detect {|v| v.custom_field_id == field_id }.try(:value)
107 end
108
109 def validate_custom_field_values
110 if new_record? || custom_field_values_changed?
111 custom_field_values.each(&:validate_value)
112 end
113 end
114
93 def save_custom_field_values
115 def save_custom_field_values
94 self.custom_values = custom_field_values
116 target_custom_values = []
95 custom_field_values.each(&:save)
117 custom_field_values.each do |custom_field_value|
118 target = custom_values.detect {|cv| cv.custom_field == custom_field_value.custom_field}
119 target ||= custom_values.build(:customized => self, :custom_field => custom_field_value.custom_field)
120 target.value = custom_field_value.value
121 target_custom_values << target
122 end
123 self.custom_values = target_custom_values
124 custom_values.each(&:save)
96 @custom_field_values_changed = false
125 @custom_field_values_changed = false
97 @custom_field_values = nil
126 true
98 end
127 end
99
128
100 def reset_custom_values!
129 def reset_custom_values!
101 @custom_field_values = nil
130 @custom_field_values = nil
102 @custom_field_values_changed = true
131 @custom_field_values_changed = true
103 values = custom_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
104 custom_values.each {|cv| cv.destroy unless custom_field_values.include?(cv)}
105 end
132 end
106
133
107 module ClassMethods
134 module ClassMethods
108 end
135 end
109 end
136 end
110 end
137 end
111 end
138 end
112 end
139 end
General Comments 0
You need to be logged in to leave comments. Login now