##// END OF EJS Templates
Check project assignment on issue copy/move....
Jean-Philippe Lang -
r8433:2a55d3761971
parent child
Show More
@@ -1,1028 +1,1038
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 Issue < ActiveRecord::Base
18 class Issue < ActiveRecord::Base
19 include Redmine::SafeAttributes
19 include Redmine::SafeAttributes
20
20
21 belongs_to :project
21 belongs_to :project
22 belongs_to :tracker
22 belongs_to :tracker
23 belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
23 belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
24 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
24 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
25 belongs_to :assigned_to, :class_name => 'Principal', :foreign_key => 'assigned_to_id'
25 belongs_to :assigned_to, :class_name => 'Principal', :foreign_key => 'assigned_to_id'
26 belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id'
26 belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id'
27 belongs_to :priority, :class_name => 'IssuePriority', :foreign_key => 'priority_id'
27 belongs_to :priority, :class_name => 'IssuePriority', :foreign_key => 'priority_id'
28 belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
28 belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
29
29
30 has_many :journals, :as => :journalized, :dependent => :destroy
30 has_many :journals, :as => :journalized, :dependent => :destroy
31 has_many :time_entries, :dependent => :delete_all
31 has_many :time_entries, :dependent => :delete_all
32 has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
32 has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
33
33
34 has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
34 has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
35 has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
35 has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
36
36
37 acts_as_nested_set :scope => 'root_id', :dependent => :destroy
37 acts_as_nested_set :scope => 'root_id', :dependent => :destroy
38 acts_as_attachable :after_add => :attachment_added, :after_remove => :attachment_removed
38 acts_as_attachable :after_add => :attachment_added, :after_remove => :attachment_removed
39 acts_as_customizable
39 acts_as_customizable
40 acts_as_watchable
40 acts_as_watchable
41 acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
41 acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
42 :include => [:project, :journals],
42 :include => [:project, :journals],
43 # sort by id so that limited eager loading doesn't break with postgresql
43 # sort by id so that limited eager loading doesn't break with postgresql
44 :order_column => "#{table_name}.id"
44 :order_column => "#{table_name}.id"
45 acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"},
45 acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"},
46 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}},
46 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}},
47 :type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') }
47 :type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') }
48
48
49 acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
49 acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
50 :author_key => :author_id
50 :author_key => :author_id
51
51
52 DONE_RATIO_OPTIONS = %w(issue_field issue_status)
52 DONE_RATIO_OPTIONS = %w(issue_field issue_status)
53
53
54 attr_reader :current_journal
54 attr_reader :current_journal
55
55
56 validates_presence_of :subject, :priority, :project, :tracker, :author, :status
56 validates_presence_of :subject, :priority, :project, :tracker, :author, :status
57
57
58 validates_length_of :subject, :maximum => 255
58 validates_length_of :subject, :maximum => 255
59 validates_inclusion_of :done_ratio, :in => 0..100
59 validates_inclusion_of :done_ratio, :in => 0..100
60 validates_numericality_of :estimated_hours, :allow_nil => true
60 validates_numericality_of :estimated_hours, :allow_nil => true
61 validate :validate_issue
61 validate :validate_issue
62
62
63 named_scope :visible, lambda {|*args| { :include => :project,
63 named_scope :visible, lambda {|*args| { :include => :project,
64 :conditions => Issue.visible_condition(args.shift || User.current, *args) } }
64 :conditions => Issue.visible_condition(args.shift || User.current, *args) } }
65
65
66 named_scope :open, lambda {|*args|
66 named_scope :open, lambda {|*args|
67 is_closed = args.size > 0 ? !args.first : false
67 is_closed = args.size > 0 ? !args.first : false
68 {:conditions => ["#{IssueStatus.table_name}.is_closed = ?", is_closed], :include => :status}
68 {:conditions => ["#{IssueStatus.table_name}.is_closed = ?", is_closed], :include => :status}
69 }
69 }
70
70
71 named_scope :recently_updated, :order => "#{Issue.table_name}.updated_on DESC"
71 named_scope :recently_updated, :order => "#{Issue.table_name}.updated_on DESC"
72 named_scope :with_limit, lambda { |limit| { :limit => limit} }
72 named_scope :with_limit, lambda { |limit| { :limit => limit} }
73 named_scope :on_active_project, :include => [:status, :project, :tracker],
73 named_scope :on_active_project, :include => [:status, :project, :tracker],
74 :conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
74 :conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
75
75
76 before_create :default_assign
76 before_create :default_assign
77 before_save :close_duplicates, :update_done_ratio_from_issue_status
77 before_save :close_duplicates, :update_done_ratio_from_issue_status
78 after_save {|issue| issue.send :after_project_change if !issue.id_changed? && issue.project_id_changed?}
78 after_save {|issue| issue.send :after_project_change if !issue.id_changed? && issue.project_id_changed?}
79 after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal
79 after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal
80 after_destroy :update_parent_attributes
80 after_destroy :update_parent_attributes
81
81
82 # Returns a SQL conditions string used to find all issues visible by the specified user
82 # Returns a SQL conditions string used to find all issues visible by the specified user
83 def self.visible_condition(user, options={})
83 def self.visible_condition(user, options={})
84 Project.allowed_to_condition(user, :view_issues, options) do |role, user|
84 Project.allowed_to_condition(user, :view_issues, options) do |role, user|
85 case role.issues_visibility
85 case role.issues_visibility
86 when 'all'
86 when 'all'
87 nil
87 nil
88 when 'default'
88 when 'default'
89 user_ids = [user.id] + user.groups.map(&:id)
89 user_ids = [user.id] + user.groups.map(&:id)
90 "(#{table_name}.is_private = #{connection.quoted_false} OR #{table_name}.author_id = #{user.id} OR #{table_name}.assigned_to_id IN (#{user_ids.join(',')}))"
90 "(#{table_name}.is_private = #{connection.quoted_false} OR #{table_name}.author_id = #{user.id} OR #{table_name}.assigned_to_id IN (#{user_ids.join(',')}))"
91 when 'own'
91 when 'own'
92 user_ids = [user.id] + user.groups.map(&:id)
92 user_ids = [user.id] + user.groups.map(&:id)
93 "(#{table_name}.author_id = #{user.id} OR #{table_name}.assigned_to_id IN (#{user_ids.join(',')}))"
93 "(#{table_name}.author_id = #{user.id} OR #{table_name}.assigned_to_id IN (#{user_ids.join(',')}))"
94 else
94 else
95 '1=0'
95 '1=0'
96 end
96 end
97 end
97 end
98 end
98 end
99
99
100 # Returns true if usr or current user is allowed to view the issue
100 # Returns true if usr or current user is allowed to view the issue
101 def visible?(usr=nil)
101 def visible?(usr=nil)
102 (usr || User.current).allowed_to?(:view_issues, self.project) do |role, user|
102 (usr || User.current).allowed_to?(:view_issues, self.project) do |role, user|
103 case role.issues_visibility
103 case role.issues_visibility
104 when 'all'
104 when 'all'
105 true
105 true
106 when 'default'
106 when 'default'
107 !self.is_private? || self.author == user || user.is_or_belongs_to?(assigned_to)
107 !self.is_private? || self.author == user || user.is_or_belongs_to?(assigned_to)
108 when 'own'
108 when 'own'
109 self.author == user || user.is_or_belongs_to?(assigned_to)
109 self.author == user || user.is_or_belongs_to?(assigned_to)
110 else
110 else
111 false
111 false
112 end
112 end
113 end
113 end
114 end
114 end
115
115
116 def initialize(attributes=nil, *args)
116 def initialize(attributes=nil, *args)
117 super
117 super
118 if new_record?
118 if new_record?
119 # set default values for new records only
119 # set default values for new records only
120 self.status ||= IssueStatus.default
120 self.status ||= IssueStatus.default
121 self.priority ||= IssuePriority.default
121 self.priority ||= IssuePriority.default
122 end
122 end
123 end
123 end
124
124
125 # Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields
125 # Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields
126 def available_custom_fields
126 def available_custom_fields
127 (project && tracker) ? (project.all_issue_custom_fields & tracker.custom_fields.all) : []
127 (project && tracker) ? (project.all_issue_custom_fields & tracker.custom_fields.all) : []
128 end
128 end
129
129
130 # Copies attributes from another issue, arg can be an id or an Issue
130 # Copies attributes from another issue, arg can be an id or an Issue
131 def copy_from(arg)
131 def copy_from(arg)
132 issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg)
132 issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg)
133 self.attributes = issue.attributes.dup.except("id", "root_id", "parent_id", "lft", "rgt", "created_on", "updated_on")
133 self.attributes = issue.attributes.dup.except("id", "root_id", "parent_id", "lft", "rgt", "created_on", "updated_on")
134 self.custom_field_values = issue.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
134 self.custom_field_values = issue.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
135 self.status = issue.status
135 self.status = issue.status
136 self.author = User.current
136 self.author = User.current
137 @copied_from = issue
137 @copied_from = issue
138 self
138 self
139 end
139 end
140
140
141 # Returns an unsaved copy of the issue
141 # Returns an unsaved copy of the issue
142 def copy(attributes=nil)
142 def copy(attributes=nil)
143 copy = self.class.new.copy_from(self)
143 copy = self.class.new.copy_from(self)
144 copy.attributes = attributes if attributes
144 copy.attributes = attributes if attributes
145 copy
145 copy
146 end
146 end
147
147
148 # Returns true if the issue is a copy
148 # Returns true if the issue is a copy
149 def copy?
149 def copy?
150 @copied_from.present?
150 @copied_from.present?
151 end
151 end
152
152
153 # Moves/copies an issue to a new project and tracker
153 # Moves/copies an issue to a new project and tracker
154 # Returns the moved/copied issue on success, false on failure
154 # Returns the moved/copied issue on success, false on failure
155 def move_to_project(new_project, new_tracker=nil, options={})
155 def move_to_project(new_project, new_tracker=nil, options={})
156 ActiveSupport::Deprecation.warn "Issue#move_to_project is deprecated, use #project= instead."
156 ActiveSupport::Deprecation.warn "Issue#move_to_project is deprecated, use #project= instead."
157
157
158 if options[:copy]
158 if options[:copy]
159 issue = self.copy
159 issue = self.copy
160 else
160 else
161 issue = self
161 issue = self
162 end
162 end
163
163
164 issue.init_journal(User.current, options[:notes])
164 issue.init_journal(User.current, options[:notes])
165
165
166 # Preserve previous behaviour
166 # Preserve previous behaviour
167 # #move_to_project doesn't change tracker automatically
167 # #move_to_project doesn't change tracker automatically
168 issue.send :project=, new_project, true
168 issue.send :project=, new_project, true
169 if new_tracker
169 if new_tracker
170 issue.tracker = new_tracker
170 issue.tracker = new_tracker
171 end
171 end
172 # Allow bulk setting of attributes on the issue
172 # Allow bulk setting of attributes on the issue
173 if options[:attributes]
173 if options[:attributes]
174 issue.attributes = options[:attributes]
174 issue.attributes = options[:attributes]
175 end
175 end
176
176
177 issue.save ? issue : false
177 issue.save ? issue : false
178 end
178 end
179
179
180 def status_id=(sid)
180 def status_id=(sid)
181 self.status = nil
181 self.status = nil
182 write_attribute(:status_id, sid)
182 write_attribute(:status_id, sid)
183 end
183 end
184
184
185 def priority_id=(pid)
185 def priority_id=(pid)
186 self.priority = nil
186 self.priority = nil
187 write_attribute(:priority_id, pid)
187 write_attribute(:priority_id, pid)
188 end
188 end
189
189
190 def category_id=(cid)
190 def category_id=(cid)
191 self.category = nil
191 self.category = nil
192 write_attribute(:category_id, cid)
192 write_attribute(:category_id, cid)
193 end
193 end
194
194
195 def fixed_version_id=(vid)
195 def fixed_version_id=(vid)
196 self.fixed_version = nil
196 self.fixed_version = nil
197 write_attribute(:fixed_version_id, vid)
197 write_attribute(:fixed_version_id, vid)
198 end
198 end
199
199
200 def tracker_id=(tid)
200 def tracker_id=(tid)
201 self.tracker = nil
201 self.tracker = nil
202 result = write_attribute(:tracker_id, tid)
202 result = write_attribute(:tracker_id, tid)
203 @custom_field_values = nil
203 @custom_field_values = nil
204 result
204 result
205 end
205 end
206
206
207 def project_id=(project_id)
207 def project_id=(project_id)
208 if project_id.to_s != self.project_id.to_s
208 if project_id.to_s != self.project_id.to_s
209 self.project = (project_id.present? ? Project.find_by_id(project_id) : nil)
209 self.project = (project_id.present? ? Project.find_by_id(project_id) : nil)
210 end
210 end
211 end
211 end
212
212
213 def project=(project, keep_tracker=false)
213 def project=(project, keep_tracker=false)
214 project_was = self.project
214 project_was = self.project
215 write_attribute(:project_id, project ? project.id : nil)
215 write_attribute(:project_id, project ? project.id : nil)
216 association_instance_set('project', project)
216 association_instance_set('project', project)
217 if project_was && project && project_was != project
217 if project_was && project && project_was != project
218 unless keep_tracker || project.trackers.include?(tracker)
218 unless keep_tracker || project.trackers.include?(tracker)
219 self.tracker = project.trackers.first
219 self.tracker = project.trackers.first
220 end
220 end
221 # Reassign to the category with same name if any
221 # Reassign to the category with same name if any
222 if category
222 if category
223 self.category = project.issue_categories.find_by_name(category.name)
223 self.category = project.issue_categories.find_by_name(category.name)
224 end
224 end
225 # Keep the fixed_version if it's still valid in the new_project
225 # Keep the fixed_version if it's still valid in the new_project
226 if fixed_version && fixed_version.project != project && !project.shared_versions.include?(fixed_version)
226 if fixed_version && fixed_version.project != project && !project.shared_versions.include?(fixed_version)
227 self.fixed_version = nil
227 self.fixed_version = nil
228 end
228 end
229 if parent && parent.project_id != project_id
229 if parent && parent.project_id != project_id
230 self.parent_issue_id = nil
230 self.parent_issue_id = nil
231 end
231 end
232 @custom_field_values = nil
232 @custom_field_values = nil
233 end
233 end
234 end
234 end
235
235
236 def description=(arg)
236 def description=(arg)
237 if arg.is_a?(String)
237 if arg.is_a?(String)
238 arg = arg.gsub(/(\r\n|\n|\r)/, "\r\n")
238 arg = arg.gsub(/(\r\n|\n|\r)/, "\r\n")
239 end
239 end
240 write_attribute(:description, arg)
240 write_attribute(:description, arg)
241 end
241 end
242
242
243 # Overrides attributes= so that project and tracker get assigned first
243 # Overrides attributes= so that project and tracker get assigned first
244 def attributes_with_project_and_tracker_first=(new_attributes, *args)
244 def attributes_with_project_and_tracker_first=(new_attributes, *args)
245 return if new_attributes.nil?
245 return if new_attributes.nil?
246 attrs = new_attributes.dup
246 attrs = new_attributes.dup
247 attrs.stringify_keys!
247 attrs.stringify_keys!
248
248
249 %w(project project_id tracker tracker_id).each do |attr|
249 %w(project project_id tracker tracker_id).each do |attr|
250 if attrs.has_key?(attr)
250 if attrs.has_key?(attr)
251 send "#{attr}=", attrs.delete(attr)
251 send "#{attr}=", attrs.delete(attr)
252 end
252 end
253 end
253 end
254 send :attributes_without_project_and_tracker_first=, attrs, *args
254 send :attributes_without_project_and_tracker_first=, attrs, *args
255 end
255 end
256 # Do not redefine alias chain on reload (see #4838)
256 # Do not redefine alias chain on reload (see #4838)
257 alias_method_chain(:attributes=, :project_and_tracker_first) unless method_defined?(:attributes_without_project_and_tracker_first=)
257 alias_method_chain(:attributes=, :project_and_tracker_first) unless method_defined?(:attributes_without_project_and_tracker_first=)
258
258
259 def estimated_hours=(h)
259 def estimated_hours=(h)
260 write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
260 write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
261 end
261 end
262
262
263 safe_attributes 'project_id',
263 safe_attributes 'project_id',
264 :if => lambda {|issue, user|
264 :if => lambda {|issue, user|
265 if issue.new_record?
265 if issue.new_record?
266 issue.copy?
266 issue.copy?
267 elsif user.allowed_to?(:move_issues, issue.project)
267 elsif user.allowed_to?(:move_issues, issue.project)
268 projects = Issue.allowed_target_projects_on_move(user)
268 projects = Issue.allowed_target_projects_on_move(user)
269 projects.include?(issue.project) && projects.size > 1
269 projects.include?(issue.project) && projects.size > 1
270 end
270 end
271 }
271 }
272
272
273 safe_attributes 'tracker_id',
273 safe_attributes 'tracker_id',
274 'status_id',
274 'status_id',
275 'category_id',
275 'category_id',
276 'assigned_to_id',
276 'assigned_to_id',
277 'priority_id',
277 'priority_id',
278 'fixed_version_id',
278 'fixed_version_id',
279 'subject',
279 'subject',
280 'description',
280 'description',
281 'start_date',
281 'start_date',
282 'due_date',
282 'due_date',
283 'done_ratio',
283 'done_ratio',
284 'estimated_hours',
284 'estimated_hours',
285 'custom_field_values',
285 'custom_field_values',
286 'custom_fields',
286 'custom_fields',
287 'lock_version',
287 'lock_version',
288 :if => lambda {|issue, user| issue.new_record? || user.allowed_to?(:edit_issues, issue.project) }
288 :if => lambda {|issue, user| issue.new_record? || user.allowed_to?(:edit_issues, issue.project) }
289
289
290 safe_attributes 'status_id',
290 safe_attributes 'status_id',
291 'assigned_to_id',
291 'assigned_to_id',
292 'fixed_version_id',
292 'fixed_version_id',
293 'done_ratio',
293 'done_ratio',
294 'lock_version',
294 'lock_version',
295 :if => lambda {|issue, user| issue.new_statuses_allowed_to(user).any? }
295 :if => lambda {|issue, user| issue.new_statuses_allowed_to(user).any? }
296
296
297 safe_attributes 'watcher_user_ids',
297 safe_attributes 'watcher_user_ids',
298 :if => lambda {|issue, user| issue.new_record? && user.allowed_to?(:add_issue_watchers, issue.project)}
298 :if => lambda {|issue, user| issue.new_record? && user.allowed_to?(:add_issue_watchers, issue.project)}
299
299
300 safe_attributes 'is_private',
300 safe_attributes 'is_private',
301 :if => lambda {|issue, user|
301 :if => lambda {|issue, user|
302 user.allowed_to?(:set_issues_private, issue.project) ||
302 user.allowed_to?(:set_issues_private, issue.project) ||
303 (issue.author == user && user.allowed_to?(:set_own_issues_private, issue.project))
303 (issue.author == user && user.allowed_to?(:set_own_issues_private, issue.project))
304 }
304 }
305
305
306 safe_attributes 'parent_issue_id',
306 safe_attributes 'parent_issue_id',
307 :if => lambda {|issue, user| (issue.new_record? || user.allowed_to?(:edit_issues, issue.project)) &&
307 :if => lambda {|issue, user| (issue.new_record? || user.allowed_to?(:edit_issues, issue.project)) &&
308 user.allowed_to?(:manage_subtasks, issue.project)}
308 user.allowed_to?(:manage_subtasks, issue.project)}
309
309
310 # Safely sets attributes
310 # Safely sets attributes
311 # Should be called from controllers instead of #attributes=
311 # Should be called from controllers instead of #attributes=
312 # attr_accessible is too rough because we still want things like
312 # attr_accessible is too rough because we still want things like
313 # Issue.new(:project => foo) to work
313 # Issue.new(:project => foo) to work
314 # TODO: move workflow/permission checks from controllers to here
315 def safe_attributes=(attrs, user=User.current)
314 def safe_attributes=(attrs, user=User.current)
316 return unless attrs.is_a?(Hash)
315 return unless attrs.is_a?(Hash)
317
316
318 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
317 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
319 attrs = delete_unsafe_attributes(attrs, user)
318 attrs = delete_unsafe_attributes(attrs, user)
320 return if attrs.empty?
319 return if attrs.empty?
321
320
322 # Project and Tracker must be set before since new_statuses_allowed_to depends on it.
321 # Project and Tracker must be set before since new_statuses_allowed_to depends on it.
323 if p = attrs.delete('project_id')
322 if p = attrs.delete('project_id')
324 self.project_id = p
323 if allowed_target_projects(user).collect(&:id).include?(p.to_i)
324 self.project_id = p
325 end
325 end
326 end
326
327
327 if t = attrs.delete('tracker_id')
328 if t = attrs.delete('tracker_id')
328 self.tracker_id = t
329 self.tracker_id = t
329 end
330 end
330
331
331 if attrs['status_id']
332 if attrs['status_id']
332 unless new_statuses_allowed_to(user).collect(&:id).include?(attrs['status_id'].to_i)
333 unless new_statuses_allowed_to(user).collect(&:id).include?(attrs['status_id'].to_i)
333 attrs.delete('status_id')
334 attrs.delete('status_id')
334 end
335 end
335 end
336 end
336
337
337 unless leaf?
338 unless leaf?
338 attrs.reject! {|k,v| %w(priority_id done_ratio start_date due_date estimated_hours).include?(k)}
339 attrs.reject! {|k,v| %w(priority_id done_ratio start_date due_date estimated_hours).include?(k)}
339 end
340 end
340
341
341 if attrs['parent_issue_id'].present?
342 if attrs['parent_issue_id'].present?
342 attrs.delete('parent_issue_id') unless Issue.visible(user).exists?(attrs['parent_issue_id'].to_i)
343 attrs.delete('parent_issue_id') unless Issue.visible(user).exists?(attrs['parent_issue_id'].to_i)
343 end
344 end
344
345
345 # mass-assignment security bypass
346 # mass-assignment security bypass
346 self.send :attributes=, attrs, false
347 self.send :attributes=, attrs, false
347 end
348 end
348
349
349 def done_ratio
350 def done_ratio
350 if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
351 if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
351 status.default_done_ratio
352 status.default_done_ratio
352 else
353 else
353 read_attribute(:done_ratio)
354 read_attribute(:done_ratio)
354 end
355 end
355 end
356 end
356
357
357 def self.use_status_for_done_ratio?
358 def self.use_status_for_done_ratio?
358 Setting.issue_done_ratio == 'issue_status'
359 Setting.issue_done_ratio == 'issue_status'
359 end
360 end
360
361
361 def self.use_field_for_done_ratio?
362 def self.use_field_for_done_ratio?
362 Setting.issue_done_ratio == 'issue_field'
363 Setting.issue_done_ratio == 'issue_field'
363 end
364 end
364
365
365 def validate_issue
366 def validate_issue
366 if self.due_date.nil? && @attributes['due_date'] && !@attributes['due_date'].empty?
367 if self.due_date.nil? && @attributes['due_date'] && !@attributes['due_date'].empty?
367 errors.add :due_date, :not_a_date
368 errors.add :due_date, :not_a_date
368 end
369 end
369
370
370 if self.due_date and self.start_date and self.due_date < self.start_date
371 if self.due_date and self.start_date and self.due_date < self.start_date
371 errors.add :due_date, :greater_than_start_date
372 errors.add :due_date, :greater_than_start_date
372 end
373 end
373
374
374 if start_date && soonest_start && start_date < soonest_start
375 if start_date && soonest_start && start_date < soonest_start
375 errors.add :start_date, :invalid
376 errors.add :start_date, :invalid
376 end
377 end
377
378
378 if fixed_version
379 if fixed_version
379 if !assignable_versions.include?(fixed_version)
380 if !assignable_versions.include?(fixed_version)
380 errors.add :fixed_version_id, :inclusion
381 errors.add :fixed_version_id, :inclusion
381 elsif reopened? && fixed_version.closed?
382 elsif reopened? && fixed_version.closed?
382 errors.add :base, I18n.t(:error_can_not_reopen_issue_on_closed_version)
383 errors.add :base, I18n.t(:error_can_not_reopen_issue_on_closed_version)
383 end
384 end
384 end
385 end
385
386
386 # Checks that the issue can not be added/moved to a disabled tracker
387 # Checks that the issue can not be added/moved to a disabled tracker
387 if project && (tracker_id_changed? || project_id_changed?)
388 if project && (tracker_id_changed? || project_id_changed?)
388 unless project.trackers.include?(tracker)
389 unless project.trackers.include?(tracker)
389 errors.add :tracker_id, :inclusion
390 errors.add :tracker_id, :inclusion
390 end
391 end
391 end
392 end
392
393
393 # Checks parent issue assignment
394 # Checks parent issue assignment
394 if @parent_issue
395 if @parent_issue
395 if @parent_issue.project_id != project_id
396 if @parent_issue.project_id != project_id
396 errors.add :parent_issue_id, :not_same_project
397 errors.add :parent_issue_id, :not_same_project
397 elsif !new_record?
398 elsif !new_record?
398 # moving an existing issue
399 # moving an existing issue
399 if @parent_issue.root_id != root_id
400 if @parent_issue.root_id != root_id
400 # we can always move to another tree
401 # we can always move to another tree
401 elsif move_possible?(@parent_issue)
402 elsif move_possible?(@parent_issue)
402 # move accepted inside tree
403 # move accepted inside tree
403 else
404 else
404 errors.add :parent_issue_id, :not_a_valid_parent
405 errors.add :parent_issue_id, :not_a_valid_parent
405 end
406 end
406 end
407 end
407 end
408 end
408 end
409 end
409
410
410 # Set the done_ratio using the status if that setting is set. This will keep the done_ratios
411 # Set the done_ratio using the status if that setting is set. This will keep the done_ratios
411 # even if the user turns off the setting later
412 # even if the user turns off the setting later
412 def update_done_ratio_from_issue_status
413 def update_done_ratio_from_issue_status
413 if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
414 if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
414 self.done_ratio = status.default_done_ratio
415 self.done_ratio = status.default_done_ratio
415 end
416 end
416 end
417 end
417
418
418 def init_journal(user, notes = "")
419 def init_journal(user, notes = "")
419 @current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes)
420 @current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes)
420 if new_record?
421 if new_record?
421 @current_journal.notify = false
422 @current_journal.notify = false
422 else
423 else
423 @attributes_before_change = attributes.dup
424 @attributes_before_change = attributes.dup
424 @custom_values_before_change = {}
425 @custom_values_before_change = {}
425 self.custom_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value }
426 self.custom_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value }
426 end
427 end
427 # Make sure updated_on is updated when adding a note.
428 # Make sure updated_on is updated when adding a note.
428 updated_on_will_change!
429 updated_on_will_change!
429 @current_journal
430 @current_journal
430 end
431 end
431
432
432 # Return true if the issue is closed, otherwise false
433 # Return true if the issue is closed, otherwise false
433 def closed?
434 def closed?
434 self.status.is_closed?
435 self.status.is_closed?
435 end
436 end
436
437
437 # Return true if the issue is being reopened
438 # Return true if the issue is being reopened
438 def reopened?
439 def reopened?
439 if !new_record? && status_id_changed?
440 if !new_record? && status_id_changed?
440 status_was = IssueStatus.find_by_id(status_id_was)
441 status_was = IssueStatus.find_by_id(status_id_was)
441 status_new = IssueStatus.find_by_id(status_id)
442 status_new = IssueStatus.find_by_id(status_id)
442 if status_was && status_new && status_was.is_closed? && !status_new.is_closed?
443 if status_was && status_new && status_was.is_closed? && !status_new.is_closed?
443 return true
444 return true
444 end
445 end
445 end
446 end
446 false
447 false
447 end
448 end
448
449
449 # Return true if the issue is being closed
450 # Return true if the issue is being closed
450 def closing?
451 def closing?
451 if !new_record? && status_id_changed?
452 if !new_record? && status_id_changed?
452 status_was = IssueStatus.find_by_id(status_id_was)
453 status_was = IssueStatus.find_by_id(status_id_was)
453 status_new = IssueStatus.find_by_id(status_id)
454 status_new = IssueStatus.find_by_id(status_id)
454 if status_was && status_new && !status_was.is_closed? && status_new.is_closed?
455 if status_was && status_new && !status_was.is_closed? && status_new.is_closed?
455 return true
456 return true
456 end
457 end
457 end
458 end
458 false
459 false
459 end
460 end
460
461
461 # Returns true if the issue is overdue
462 # Returns true if the issue is overdue
462 def overdue?
463 def overdue?
463 !due_date.nil? && (due_date < Date.today) && !status.is_closed?
464 !due_date.nil? && (due_date < Date.today) && !status.is_closed?
464 end
465 end
465
466
466 # Is the amount of work done less than it should for the due date
467 # Is the amount of work done less than it should for the due date
467 def behind_schedule?
468 def behind_schedule?
468 return false if start_date.nil? || due_date.nil?
469 return false if start_date.nil? || due_date.nil?
469 done_date = start_date + ((due_date - start_date+1)* done_ratio/100).floor
470 done_date = start_date + ((due_date - start_date+1)* done_ratio/100).floor
470 return done_date <= Date.today
471 return done_date <= Date.today
471 end
472 end
472
473
473 # Does this issue have children?
474 # Does this issue have children?
474 def children?
475 def children?
475 !leaf?
476 !leaf?
476 end
477 end
477
478
478 # Users the issue can be assigned to
479 # Users the issue can be assigned to
479 def assignable_users
480 def assignable_users
480 users = project.assignable_users
481 users = project.assignable_users
481 users << author if author
482 users << author if author
482 users << assigned_to if assigned_to
483 users << assigned_to if assigned_to
483 users.uniq.sort
484 users.uniq.sort
484 end
485 end
485
486
486 # Versions that the issue can be assigned to
487 # Versions that the issue can be assigned to
487 def assignable_versions
488 def assignable_versions
488 @assignable_versions ||= (project.shared_versions.open + [Version.find_by_id(fixed_version_id_was)]).compact.uniq.sort
489 @assignable_versions ||= (project.shared_versions.open + [Version.find_by_id(fixed_version_id_was)]).compact.uniq.sort
489 end
490 end
490
491
491 # Returns true if this issue is blocked by another issue that is still open
492 # Returns true if this issue is blocked by another issue that is still open
492 def blocked?
493 def blocked?
493 !relations_to.detect {|ir| ir.relation_type == 'blocks' && !ir.issue_from.closed?}.nil?
494 !relations_to.detect {|ir| ir.relation_type == 'blocks' && !ir.issue_from.closed?}.nil?
494 end
495 end
495
496
496 # Returns an array of status that user is able to apply
497 # Returns an array of status that user is able to apply
497 def new_statuses_allowed_to(user, include_default=false)
498 def new_statuses_allowed_to(user, include_default=false)
498 statuses = status.find_new_statuses_allowed_to(
499 statuses = status.find_new_statuses_allowed_to(
499 user.roles_for_project(project),
500 user.roles_for_project(project),
500 tracker,
501 tracker,
501 author == user,
502 author == user,
502 assigned_to_id_changed? ? assigned_to_id_was == user.id : assigned_to_id == user.id
503 assigned_to_id_changed? ? assigned_to_id_was == user.id : assigned_to_id == user.id
503 )
504 )
504 statuses << status unless statuses.empty?
505 statuses << status unless statuses.empty?
505 statuses << IssueStatus.default if include_default
506 statuses << IssueStatus.default if include_default
506 statuses = statuses.uniq.sort
507 statuses = statuses.uniq.sort
507 blocked? ? statuses.reject {|s| s.is_closed?} : statuses
508 blocked? ? statuses.reject {|s| s.is_closed?} : statuses
508 end
509 end
509
510
510 # Returns the mail adresses of users that should be notified
511 # Returns the mail adresses of users that should be notified
511 def recipients
512 def recipients
512 notified = project.notified_users
513 notified = project.notified_users
513 # Author and assignee are always notified unless they have been
514 # Author and assignee are always notified unless they have been
514 # locked or don't want to be notified
515 # locked or don't want to be notified
515 notified << author if author && author.active? && author.notify_about?(self)
516 notified << author if author && author.active? && author.notify_about?(self)
516 if assigned_to
517 if assigned_to
517 if assigned_to.is_a?(Group)
518 if assigned_to.is_a?(Group)
518 notified += assigned_to.users.select {|u| u.active? && u.notify_about?(self)}
519 notified += assigned_to.users.select {|u| u.active? && u.notify_about?(self)}
519 else
520 else
520 notified << assigned_to if assigned_to.active? && assigned_to.notify_about?(self)
521 notified << assigned_to if assigned_to.active? && assigned_to.notify_about?(self)
521 end
522 end
522 end
523 end
523 notified.uniq!
524 notified.uniq!
524 # Remove users that can not view the issue
525 # Remove users that can not view the issue
525 notified.reject! {|user| !visible?(user)}
526 notified.reject! {|user| !visible?(user)}
526 notified.collect(&:mail)
527 notified.collect(&:mail)
527 end
528 end
528
529
529 # Returns the number of hours spent on this issue
530 # Returns the number of hours spent on this issue
530 def spent_hours
531 def spent_hours
531 @spent_hours ||= time_entries.sum(:hours) || 0
532 @spent_hours ||= time_entries.sum(:hours) || 0
532 end
533 end
533
534
534 # Returns the total number of hours spent on this issue and its descendants
535 # Returns the total number of hours spent on this issue and its descendants
535 #
536 #
536 # Example:
537 # Example:
537 # spent_hours => 0.0
538 # spent_hours => 0.0
538 # spent_hours => 50.2
539 # spent_hours => 50.2
539 def total_spent_hours
540 def total_spent_hours
540 @total_spent_hours ||= self_and_descendants.sum("#{TimeEntry.table_name}.hours",
541 @total_spent_hours ||= self_and_descendants.sum("#{TimeEntry.table_name}.hours",
541 :joins => "LEFT JOIN #{TimeEntry.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id").to_f || 0.0
542 :joins => "LEFT JOIN #{TimeEntry.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id").to_f || 0.0
542 end
543 end
543
544
544 def relations
545 def relations
545 @relations ||= (relations_from + relations_to).sort
546 @relations ||= (relations_from + relations_to).sort
546 end
547 end
547
548
548 # Preloads relations for a collection of issues
549 # Preloads relations for a collection of issues
549 def self.load_relations(issues)
550 def self.load_relations(issues)
550 if issues.any?
551 if issues.any?
551 relations = IssueRelation.all(:conditions => ["issue_from_id IN (:ids) OR issue_to_id IN (:ids)", {:ids => issues.map(&:id)}])
552 relations = IssueRelation.all(:conditions => ["issue_from_id IN (:ids) OR issue_to_id IN (:ids)", {:ids => issues.map(&:id)}])
552 issues.each do |issue|
553 issues.each do |issue|
553 issue.instance_variable_set "@relations", relations.select {|r| r.issue_from_id == issue.id || r.issue_to_id == issue.id}
554 issue.instance_variable_set "@relations", relations.select {|r| r.issue_from_id == issue.id || r.issue_to_id == issue.id}
554 end
555 end
555 end
556 end
556 end
557 end
557
558
558 # Preloads visible spent time for a collection of issues
559 # Preloads visible spent time for a collection of issues
559 def self.load_visible_spent_hours(issues, user=User.current)
560 def self.load_visible_spent_hours(issues, user=User.current)
560 if issues.any?
561 if issues.any?
561 hours_by_issue_id = TimeEntry.visible(user).sum(:hours, :group => :issue_id)
562 hours_by_issue_id = TimeEntry.visible(user).sum(:hours, :group => :issue_id)
562 issues.each do |issue|
563 issues.each do |issue|
563 issue.instance_variable_set "@spent_hours", (hours_by_issue_id[issue.id] || 0)
564 issue.instance_variable_set "@spent_hours", (hours_by_issue_id[issue.id] || 0)
564 end
565 end
565 end
566 end
566 end
567 end
567
568
568 # Finds an issue relation given its id.
569 # Finds an issue relation given its id.
569 def find_relation(relation_id)
570 def find_relation(relation_id)
570 IssueRelation.find(relation_id, :conditions => ["issue_to_id = ? OR issue_from_id = ?", id, id])
571 IssueRelation.find(relation_id, :conditions => ["issue_to_id = ? OR issue_from_id = ?", id, id])
571 end
572 end
572
573
573 def all_dependent_issues(except=[])
574 def all_dependent_issues(except=[])
574 except << self
575 except << self
575 dependencies = []
576 dependencies = []
576 relations_from.each do |relation|
577 relations_from.each do |relation|
577 if relation.issue_to && !except.include?(relation.issue_to)
578 if relation.issue_to && !except.include?(relation.issue_to)
578 dependencies << relation.issue_to
579 dependencies << relation.issue_to
579 dependencies += relation.issue_to.all_dependent_issues(except)
580 dependencies += relation.issue_to.all_dependent_issues(except)
580 end
581 end
581 end
582 end
582 dependencies
583 dependencies
583 end
584 end
584
585
585 # Returns an array of issues that duplicate this one
586 # Returns an array of issues that duplicate this one
586 def duplicates
587 def duplicates
587 relations_to.select {|r| r.relation_type == IssueRelation::TYPE_DUPLICATES}.collect {|r| r.issue_from}
588 relations_to.select {|r| r.relation_type == IssueRelation::TYPE_DUPLICATES}.collect {|r| r.issue_from}
588 end
589 end
589
590
590 # Returns the due date or the target due date if any
591 # Returns the due date or the target due date if any
591 # Used on gantt chart
592 # Used on gantt chart
592 def due_before
593 def due_before
593 due_date || (fixed_version ? fixed_version.effective_date : nil)
594 due_date || (fixed_version ? fixed_version.effective_date : nil)
594 end
595 end
595
596
596 # Returns the time scheduled for this issue.
597 # Returns the time scheduled for this issue.
597 #
598 #
598 # Example:
599 # Example:
599 # Start Date: 2/26/09, End Date: 3/04/09
600 # Start Date: 2/26/09, End Date: 3/04/09
600 # duration => 6
601 # duration => 6
601 def duration
602 def duration
602 (start_date && due_date) ? due_date - start_date : 0
603 (start_date && due_date) ? due_date - start_date : 0
603 end
604 end
604
605
605 def soonest_start
606 def soonest_start
606 @soonest_start ||= (
607 @soonest_start ||= (
607 relations_to.collect{|relation| relation.successor_soonest_start} +
608 relations_to.collect{|relation| relation.successor_soonest_start} +
608 ancestors.collect(&:soonest_start)
609 ancestors.collect(&:soonest_start)
609 ).compact.max
610 ).compact.max
610 end
611 end
611
612
612 def reschedule_after(date)
613 def reschedule_after(date)
613 return if date.nil?
614 return if date.nil?
614 if leaf?
615 if leaf?
615 if start_date.nil? || start_date < date
616 if start_date.nil? || start_date < date
616 self.start_date, self.due_date = date, date + duration
617 self.start_date, self.due_date = date, date + duration
617 save
618 save
618 end
619 end
619 else
620 else
620 leaves.each do |leaf|
621 leaves.each do |leaf|
621 leaf.reschedule_after(date)
622 leaf.reschedule_after(date)
622 end
623 end
623 end
624 end
624 end
625 end
625
626
626 def <=>(issue)
627 def <=>(issue)
627 if issue.nil?
628 if issue.nil?
628 -1
629 -1
629 elsif root_id != issue.root_id
630 elsif root_id != issue.root_id
630 (root_id || 0) <=> (issue.root_id || 0)
631 (root_id || 0) <=> (issue.root_id || 0)
631 else
632 else
632 (lft || 0) <=> (issue.lft || 0)
633 (lft || 0) <=> (issue.lft || 0)
633 end
634 end
634 end
635 end
635
636
636 def to_s
637 def to_s
637 "#{tracker} ##{id}: #{subject}"
638 "#{tracker} ##{id}: #{subject}"
638 end
639 end
639
640
640 # Returns a string of css classes that apply to the issue
641 # Returns a string of css classes that apply to the issue
641 def css_classes
642 def css_classes
642 s = "issue status-#{status.position} priority-#{priority.position}"
643 s = "issue status-#{status.position} priority-#{priority.position}"
643 s << ' closed' if closed?
644 s << ' closed' if closed?
644 s << ' overdue' if overdue?
645 s << ' overdue' if overdue?
645 s << ' child' if child?
646 s << ' child' if child?
646 s << ' parent' unless leaf?
647 s << ' parent' unless leaf?
647 s << ' private' if is_private?
648 s << ' private' if is_private?
648 s << ' created-by-me' if User.current.logged? && author_id == User.current.id
649 s << ' created-by-me' if User.current.logged? && author_id == User.current.id
649 s << ' assigned-to-me' if User.current.logged? && assigned_to_id == User.current.id
650 s << ' assigned-to-me' if User.current.logged? && assigned_to_id == User.current.id
650 s
651 s
651 end
652 end
652
653
653 # Saves an issue, time_entry, attachments, and a journal from the parameters
654 # Saves an issue, time_entry, attachments, and a journal from the parameters
654 # Returns false if save fails
655 # Returns false if save fails
655 def save_issue_with_child_records(params, existing_time_entry=nil)
656 def save_issue_with_child_records(params, existing_time_entry=nil)
656 Issue.transaction do
657 Issue.transaction do
657 if params[:time_entry] && (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) && User.current.allowed_to?(:log_time, project)
658 if params[:time_entry] && (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) && User.current.allowed_to?(:log_time, project)
658 @time_entry = existing_time_entry || TimeEntry.new
659 @time_entry = existing_time_entry || TimeEntry.new
659 @time_entry.project = project
660 @time_entry.project = project
660 @time_entry.issue = self
661 @time_entry.issue = self
661 @time_entry.user = User.current
662 @time_entry.user = User.current
662 @time_entry.spent_on = User.current.today
663 @time_entry.spent_on = User.current.today
663 @time_entry.attributes = params[:time_entry]
664 @time_entry.attributes = params[:time_entry]
664 self.time_entries << @time_entry
665 self.time_entries << @time_entry
665 end
666 end
666
667
667 if valid?
668 if valid?
668 attachments = Attachment.attach_files(self, params[:attachments])
669 attachments = Attachment.attach_files(self, params[:attachments])
669 # TODO: Rename hook
670 # TODO: Rename hook
670 Redmine::Hook.call_hook(:controller_issues_edit_before_save, { :params => params, :issue => self, :time_entry => @time_entry, :journal => @current_journal})
671 Redmine::Hook.call_hook(:controller_issues_edit_before_save, { :params => params, :issue => self, :time_entry => @time_entry, :journal => @current_journal})
671 begin
672 begin
672 if save
673 if save
673 # TODO: Rename hook
674 # TODO: Rename hook
674 Redmine::Hook.call_hook(:controller_issues_edit_after_save, { :params => params, :issue => self, :time_entry => @time_entry, :journal => @current_journal})
675 Redmine::Hook.call_hook(:controller_issues_edit_after_save, { :params => params, :issue => self, :time_entry => @time_entry, :journal => @current_journal})
675 else
676 else
676 raise ActiveRecord::Rollback
677 raise ActiveRecord::Rollback
677 end
678 end
678 rescue ActiveRecord::StaleObjectError
679 rescue ActiveRecord::StaleObjectError
679 attachments[:files].each(&:destroy)
680 attachments[:files].each(&:destroy)
680 errors.add :base, l(:notice_locking_conflict)
681 errors.add :base, l(:notice_locking_conflict)
681 raise ActiveRecord::Rollback
682 raise ActiveRecord::Rollback
682 end
683 end
683 end
684 end
684 end
685 end
685 end
686 end
686
687
687 # Unassigns issues from +version+ if it's no longer shared with issue's project
688 # Unassigns issues from +version+ if it's no longer shared with issue's project
688 def self.update_versions_from_sharing_change(version)
689 def self.update_versions_from_sharing_change(version)
689 # Update issues assigned to the version
690 # Update issues assigned to the version
690 update_versions(["#{Issue.table_name}.fixed_version_id = ?", version.id])
691 update_versions(["#{Issue.table_name}.fixed_version_id = ?", version.id])
691 end
692 end
692
693
693 # Unassigns issues from versions that are no longer shared
694 # Unassigns issues from versions that are no longer shared
694 # after +project+ was moved
695 # after +project+ was moved
695 def self.update_versions_from_hierarchy_change(project)
696 def self.update_versions_from_hierarchy_change(project)
696 moved_project_ids = project.self_and_descendants.reload.collect(&:id)
697 moved_project_ids = project.self_and_descendants.reload.collect(&:id)
697 # Update issues of the moved projects and issues assigned to a version of a moved project
698 # Update issues of the moved projects and issues assigned to a version of a moved project
698 Issue.update_versions(["#{Version.table_name}.project_id IN (?) OR #{Issue.table_name}.project_id IN (?)", moved_project_ids, moved_project_ids])
699 Issue.update_versions(["#{Version.table_name}.project_id IN (?) OR #{Issue.table_name}.project_id IN (?)", moved_project_ids, moved_project_ids])
699 end
700 end
700
701
701 def parent_issue_id=(arg)
702 def parent_issue_id=(arg)
702 parent_issue_id = arg.blank? ? nil : arg.to_i
703 parent_issue_id = arg.blank? ? nil : arg.to_i
703 if parent_issue_id && @parent_issue = Issue.find_by_id(parent_issue_id)
704 if parent_issue_id && @parent_issue = Issue.find_by_id(parent_issue_id)
704 @parent_issue.id
705 @parent_issue.id
705 else
706 else
706 @parent_issue = nil
707 @parent_issue = nil
707 nil
708 nil
708 end
709 end
709 end
710 end
710
711
711 def parent_issue_id
712 def parent_issue_id
712 if instance_variable_defined? :@parent_issue
713 if instance_variable_defined? :@parent_issue
713 @parent_issue.nil? ? nil : @parent_issue.id
714 @parent_issue.nil? ? nil : @parent_issue.id
714 else
715 else
715 parent_id
716 parent_id
716 end
717 end
717 end
718 end
718
719
719 # Extracted from the ReportsController.
720 # Extracted from the ReportsController.
720 def self.by_tracker(project)
721 def self.by_tracker(project)
721 count_and_group_by(:project => project,
722 count_and_group_by(:project => project,
722 :field => 'tracker_id',
723 :field => 'tracker_id',
723 :joins => Tracker.table_name)
724 :joins => Tracker.table_name)
724 end
725 end
725
726
726 def self.by_version(project)
727 def self.by_version(project)
727 count_and_group_by(:project => project,
728 count_and_group_by(:project => project,
728 :field => 'fixed_version_id',
729 :field => 'fixed_version_id',
729 :joins => Version.table_name)
730 :joins => Version.table_name)
730 end
731 end
731
732
732 def self.by_priority(project)
733 def self.by_priority(project)
733 count_and_group_by(:project => project,
734 count_and_group_by(:project => project,
734 :field => 'priority_id',
735 :field => 'priority_id',
735 :joins => IssuePriority.table_name)
736 :joins => IssuePriority.table_name)
736 end
737 end
737
738
738 def self.by_category(project)
739 def self.by_category(project)
739 count_and_group_by(:project => project,
740 count_and_group_by(:project => project,
740 :field => 'category_id',
741 :field => 'category_id',
741 :joins => IssueCategory.table_name)
742 :joins => IssueCategory.table_name)
742 end
743 end
743
744
744 def self.by_assigned_to(project)
745 def self.by_assigned_to(project)
745 count_and_group_by(:project => project,
746 count_and_group_by(:project => project,
746 :field => 'assigned_to_id',
747 :field => 'assigned_to_id',
747 :joins => User.table_name)
748 :joins => User.table_name)
748 end
749 end
749
750
750 def self.by_author(project)
751 def self.by_author(project)
751 count_and_group_by(:project => project,
752 count_and_group_by(:project => project,
752 :field => 'author_id',
753 :field => 'author_id',
753 :joins => User.table_name)
754 :joins => User.table_name)
754 end
755 end
755
756
756 def self.by_subproject(project)
757 def self.by_subproject(project)
757 ActiveRecord::Base.connection.select_all("select s.id as status_id,
758 ActiveRecord::Base.connection.select_all("select s.id as status_id,
758 s.is_closed as closed,
759 s.is_closed as closed,
759 #{Issue.table_name}.project_id as project_id,
760 #{Issue.table_name}.project_id as project_id,
760 count(#{Issue.table_name}.id) as total
761 count(#{Issue.table_name}.id) as total
761 from
762 from
762 #{Issue.table_name}, #{Project.table_name}, #{IssueStatus.table_name} s
763 #{Issue.table_name}, #{Project.table_name}, #{IssueStatus.table_name} s
763 where
764 where
764 #{Issue.table_name}.status_id=s.id
765 #{Issue.table_name}.status_id=s.id
765 and #{Issue.table_name}.project_id = #{Project.table_name}.id
766 and #{Issue.table_name}.project_id = #{Project.table_name}.id
766 and #{visible_condition(User.current, :project => project, :with_subprojects => true)}
767 and #{visible_condition(User.current, :project => project, :with_subprojects => true)}
767 and #{Issue.table_name}.project_id <> #{project.id}
768 and #{Issue.table_name}.project_id <> #{project.id}
768 group by s.id, s.is_closed, #{Issue.table_name}.project_id") if project.descendants.active.any?
769 group by s.id, s.is_closed, #{Issue.table_name}.project_id") if project.descendants.active.any?
769 end
770 end
770 # End ReportsController extraction
771 # End ReportsController extraction
771
772
772 # Returns an array of projects that current user can move issues to
773 # Returns an array of projects that user can assign the issue to
774 def allowed_target_projects(user=User.current)
775 if new_record?
776 Project.all(:conditions => Project.allowed_to_condition(user, :add_issues))
777 else
778 self.class.allowed_target_projects_on_move(user)
779 end
780 end
781
782 # Returns an array of projects that user can move issues to
773 def self.allowed_target_projects_on_move(user=User.current)
783 def self.allowed_target_projects_on_move(user=User.current)
774 projects = []
784 projects = []
775 if user.admin?
785 if user.admin?
776 # admin is allowed to move issues to any active (visible) project
786 # admin is allowed to move issues to any active (visible) project
777 projects = Project.visible(user).all
787 projects = Project.visible(user).all
778 elsif user.logged?
788 elsif user.logged?
779 if Role.non_member.allowed_to?(:move_issues)
789 if Role.non_member.allowed_to?(:move_issues)
780 projects = Project.visible(user).all
790 projects = Project.visible(user).all
781 else
791 else
782 user.memberships.each {|m| projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
792 user.memberships.each {|m| projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
783 end
793 end
784 end
794 end
785 projects
795 projects
786 end
796 end
787
797
788 private
798 private
789
799
790 def after_project_change
800 def after_project_change
791 # Update project_id on related time entries
801 # Update project_id on related time entries
792 TimeEntry.update_all(["project_id = ?", project_id], {:issue_id => id})
802 TimeEntry.update_all(["project_id = ?", project_id], {:issue_id => id})
793
803
794 # Delete issue relations
804 # Delete issue relations
795 unless Setting.cross_project_issue_relations?
805 unless Setting.cross_project_issue_relations?
796 relations_from.clear
806 relations_from.clear
797 relations_to.clear
807 relations_to.clear
798 end
808 end
799
809
800 # Move subtasks
810 # Move subtasks
801 children.each do |child|
811 children.each do |child|
802 # Change project and keep project
812 # Change project and keep project
803 child.send :project=, project, true
813 child.send :project=, project, true
804 unless child.save
814 unless child.save
805 raise ActiveRecord::Rollback
815 raise ActiveRecord::Rollback
806 end
816 end
807 end
817 end
808 end
818 end
809
819
810 def update_nested_set_attributes
820 def update_nested_set_attributes
811 if root_id.nil?
821 if root_id.nil?
812 # issue was just created
822 # issue was just created
813 self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id)
823 self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id)
814 set_default_left_and_right
824 set_default_left_and_right
815 Issue.update_all("root_id = #{root_id}, lft = #{lft}, rgt = #{rgt}", ["id = ?", id])
825 Issue.update_all("root_id = #{root_id}, lft = #{lft}, rgt = #{rgt}", ["id = ?", id])
816 if @parent_issue
826 if @parent_issue
817 move_to_child_of(@parent_issue)
827 move_to_child_of(@parent_issue)
818 end
828 end
819 reload
829 reload
820 elsif parent_issue_id != parent_id
830 elsif parent_issue_id != parent_id
821 former_parent_id = parent_id
831 former_parent_id = parent_id
822 # moving an existing issue
832 # moving an existing issue
823 if @parent_issue && @parent_issue.root_id == root_id
833 if @parent_issue && @parent_issue.root_id == root_id
824 # inside the same tree
834 # inside the same tree
825 move_to_child_of(@parent_issue)
835 move_to_child_of(@parent_issue)
826 else
836 else
827 # to another tree
837 # to another tree
828 unless root?
838 unless root?
829 move_to_right_of(root)
839 move_to_right_of(root)
830 reload
840 reload
831 end
841 end
832 old_root_id = root_id
842 old_root_id = root_id
833 self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id )
843 self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id )
834 target_maxright = nested_set_scope.maximum(right_column_name) || 0
844 target_maxright = nested_set_scope.maximum(right_column_name) || 0
835 offset = target_maxright + 1 - lft
845 offset = target_maxright + 1 - lft
836 Issue.update_all("root_id = #{root_id}, lft = lft + #{offset}, rgt = rgt + #{offset}",
846 Issue.update_all("root_id = #{root_id}, lft = lft + #{offset}, rgt = rgt + #{offset}",
837 ["root_id = ? AND lft >= ? AND rgt <= ? ", old_root_id, lft, rgt])
847 ["root_id = ? AND lft >= ? AND rgt <= ? ", old_root_id, lft, rgt])
838 self[left_column_name] = lft + offset
848 self[left_column_name] = lft + offset
839 self[right_column_name] = rgt + offset
849 self[right_column_name] = rgt + offset
840 if @parent_issue
850 if @parent_issue
841 move_to_child_of(@parent_issue)
851 move_to_child_of(@parent_issue)
842 end
852 end
843 end
853 end
844 reload
854 reload
845 # delete invalid relations of all descendants
855 # delete invalid relations of all descendants
846 self_and_descendants.each do |issue|
856 self_and_descendants.each do |issue|
847 issue.relations.each do |relation|
857 issue.relations.each do |relation|
848 relation.destroy unless relation.valid?
858 relation.destroy unless relation.valid?
849 end
859 end
850 end
860 end
851 # update former parent
861 # update former parent
852 recalculate_attributes_for(former_parent_id) if former_parent_id
862 recalculate_attributes_for(former_parent_id) if former_parent_id
853 end
863 end
854 remove_instance_variable(:@parent_issue) if instance_variable_defined?(:@parent_issue)
864 remove_instance_variable(:@parent_issue) if instance_variable_defined?(:@parent_issue)
855 end
865 end
856
866
857 def update_parent_attributes
867 def update_parent_attributes
858 recalculate_attributes_for(parent_id) if parent_id
868 recalculate_attributes_for(parent_id) if parent_id
859 end
869 end
860
870
861 def recalculate_attributes_for(issue_id)
871 def recalculate_attributes_for(issue_id)
862 if issue_id && p = Issue.find_by_id(issue_id)
872 if issue_id && p = Issue.find_by_id(issue_id)
863 # priority = highest priority of children
873 # priority = highest priority of children
864 if priority_position = p.children.maximum("#{IssuePriority.table_name}.position", :joins => :priority)
874 if priority_position = p.children.maximum("#{IssuePriority.table_name}.position", :joins => :priority)
865 p.priority = IssuePriority.find_by_position(priority_position)
875 p.priority = IssuePriority.find_by_position(priority_position)
866 end
876 end
867
877
868 # start/due dates = lowest/highest dates of children
878 # start/due dates = lowest/highest dates of children
869 p.start_date = p.children.minimum(:start_date)
879 p.start_date = p.children.minimum(:start_date)
870 p.due_date = p.children.maximum(:due_date)
880 p.due_date = p.children.maximum(:due_date)
871 if p.start_date && p.due_date && p.due_date < p.start_date
881 if p.start_date && p.due_date && p.due_date < p.start_date
872 p.start_date, p.due_date = p.due_date, p.start_date
882 p.start_date, p.due_date = p.due_date, p.start_date
873 end
883 end
874
884
875 # done ratio = weighted average ratio of leaves
885 # done ratio = weighted average ratio of leaves
876 unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio
886 unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio
877 leaves_count = p.leaves.count
887 leaves_count = p.leaves.count
878 if leaves_count > 0
888 if leaves_count > 0
879 average = p.leaves.average(:estimated_hours).to_f
889 average = p.leaves.average(:estimated_hours).to_f
880 if average == 0
890 if average == 0
881 average = 1
891 average = 1
882 end
892 end
883 done = p.leaves.sum("COALESCE(estimated_hours, #{average}) * (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)", :joins => :status).to_f
893 done = p.leaves.sum("COALESCE(estimated_hours, #{average}) * (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)", :joins => :status).to_f
884 progress = done / (average * leaves_count)
894 progress = done / (average * leaves_count)
885 p.done_ratio = progress.round
895 p.done_ratio = progress.round
886 end
896 end
887 end
897 end
888
898
889 # estimate = sum of leaves estimates
899 # estimate = sum of leaves estimates
890 p.estimated_hours = p.leaves.sum(:estimated_hours).to_f
900 p.estimated_hours = p.leaves.sum(:estimated_hours).to_f
891 p.estimated_hours = nil if p.estimated_hours == 0.0
901 p.estimated_hours = nil if p.estimated_hours == 0.0
892
902
893 # ancestors will be recursively updated
903 # ancestors will be recursively updated
894 p.save(false)
904 p.save(false)
895 end
905 end
896 end
906 end
897
907
898 # Update issues so their versions are not pointing to a
908 # Update issues so their versions are not pointing to a
899 # fixed_version that is not shared with the issue's project
909 # fixed_version that is not shared with the issue's project
900 def self.update_versions(conditions=nil)
910 def self.update_versions(conditions=nil)
901 # Only need to update issues with a fixed_version from
911 # Only need to update issues with a fixed_version from
902 # a different project and that is not systemwide shared
912 # a different project and that is not systemwide shared
903 Issue.scoped(:conditions => conditions).all(
913 Issue.scoped(:conditions => conditions).all(
904 :conditions => "#{Issue.table_name}.fixed_version_id IS NOT NULL" +
914 :conditions => "#{Issue.table_name}.fixed_version_id IS NOT NULL" +
905 " AND #{Issue.table_name}.project_id <> #{Version.table_name}.project_id" +
915 " AND #{Issue.table_name}.project_id <> #{Version.table_name}.project_id" +
906 " AND #{Version.table_name}.sharing <> 'system'",
916 " AND #{Version.table_name}.sharing <> 'system'",
907 :include => [:project, :fixed_version]
917 :include => [:project, :fixed_version]
908 ).each do |issue|
918 ).each do |issue|
909 next if issue.project.nil? || issue.fixed_version.nil?
919 next if issue.project.nil? || issue.fixed_version.nil?
910 unless issue.project.shared_versions.include?(issue.fixed_version)
920 unless issue.project.shared_versions.include?(issue.fixed_version)
911 issue.init_journal(User.current)
921 issue.init_journal(User.current)
912 issue.fixed_version = nil
922 issue.fixed_version = nil
913 issue.save
923 issue.save
914 end
924 end
915 end
925 end
916 end
926 end
917
927
918 # Callback on attachment deletion
928 # Callback on attachment deletion
919 def attachment_added(obj)
929 def attachment_added(obj)
920 if @current_journal && !obj.new_record?
930 if @current_journal && !obj.new_record?
921 @current_journal.details << JournalDetail.new(:property => 'attachment', :prop_key => obj.id, :value => obj.filename)
931 @current_journal.details << JournalDetail.new(:property => 'attachment', :prop_key => obj.id, :value => obj.filename)
922 end
932 end
923 end
933 end
924
934
925 # Callback on attachment deletion
935 # Callback on attachment deletion
926 def attachment_removed(obj)
936 def attachment_removed(obj)
927 journal = init_journal(User.current)
937 journal = init_journal(User.current)
928 journal.details << JournalDetail.new(:property => 'attachment',
938 journal.details << JournalDetail.new(:property => 'attachment',
929 :prop_key => obj.id,
939 :prop_key => obj.id,
930 :old_value => obj.filename)
940 :old_value => obj.filename)
931 journal.save
941 journal.save
932 end
942 end
933
943
934 # Default assignment based on category
944 # Default assignment based on category
935 def default_assign
945 def default_assign
936 if assigned_to.nil? && category && category.assigned_to
946 if assigned_to.nil? && category && category.assigned_to
937 self.assigned_to = category.assigned_to
947 self.assigned_to = category.assigned_to
938 end
948 end
939 end
949 end
940
950
941 # Updates start/due dates of following issues
951 # Updates start/due dates of following issues
942 def reschedule_following_issues
952 def reschedule_following_issues
943 if start_date_changed? || due_date_changed?
953 if start_date_changed? || due_date_changed?
944 relations_from.each do |relation|
954 relations_from.each do |relation|
945 relation.set_issue_to_dates
955 relation.set_issue_to_dates
946 end
956 end
947 end
957 end
948 end
958 end
949
959
950 # Closes duplicates if the issue is being closed
960 # Closes duplicates if the issue is being closed
951 def close_duplicates
961 def close_duplicates
952 if closing?
962 if closing?
953 duplicates.each do |duplicate|
963 duplicates.each do |duplicate|
954 # Reload is need in case the duplicate was updated by a previous duplicate
964 # Reload is need in case the duplicate was updated by a previous duplicate
955 duplicate.reload
965 duplicate.reload
956 # Don't re-close it if it's already closed
966 # Don't re-close it if it's already closed
957 next if duplicate.closed?
967 next if duplicate.closed?
958 # Same user and notes
968 # Same user and notes
959 if @current_journal
969 if @current_journal
960 duplicate.init_journal(@current_journal.user, @current_journal.notes)
970 duplicate.init_journal(@current_journal.user, @current_journal.notes)
961 end
971 end
962 duplicate.update_attribute :status, self.status
972 duplicate.update_attribute :status, self.status
963 end
973 end
964 end
974 end
965 end
975 end
966
976
967 # Saves the changes in a Journal
977 # Saves the changes in a Journal
968 # Called after_save
978 # Called after_save
969 def create_journal
979 def create_journal
970 if @current_journal
980 if @current_journal
971 # attributes changes
981 # attributes changes
972 if @attributes_before_change
982 if @attributes_before_change
973 (Issue.column_names - %w(id root_id lft rgt lock_version created_on updated_on)).each {|c|
983 (Issue.column_names - %w(id root_id lft rgt lock_version created_on updated_on)).each {|c|
974 before = @attributes_before_change[c]
984 before = @attributes_before_change[c]
975 after = send(c)
985 after = send(c)
976 next if before == after || (before.blank? && after.blank?)
986 next if before == after || (before.blank? && after.blank?)
977 @current_journal.details << JournalDetail.new(:property => 'attr',
987 @current_journal.details << JournalDetail.new(:property => 'attr',
978 :prop_key => c,
988 :prop_key => c,
979 :old_value => before,
989 :old_value => before,
980 :value => after)
990 :value => after)
981 }
991 }
982 end
992 end
983 if @custom_values_before_change
993 if @custom_values_before_change
984 # custom fields changes
994 # custom fields changes
985 custom_values.each {|c|
995 custom_values.each {|c|
986 before = @custom_values_before_change[c.custom_field_id]
996 before = @custom_values_before_change[c.custom_field_id]
987 after = c.value
997 after = c.value
988 next if before == after || (before.blank? && after.blank?)
998 next if before == after || (before.blank? && after.blank?)
989 @current_journal.details << JournalDetail.new(:property => 'cf',
999 @current_journal.details << JournalDetail.new(:property => 'cf',
990 :prop_key => c.custom_field_id,
1000 :prop_key => c.custom_field_id,
991 :old_value => before,
1001 :old_value => before,
992 :value => after)
1002 :value => after)
993 }
1003 }
994 end
1004 end
995 @current_journal.save
1005 @current_journal.save
996 # reset current journal
1006 # reset current journal
997 init_journal @current_journal.user, @current_journal.notes
1007 init_journal @current_journal.user, @current_journal.notes
998 end
1008 end
999 end
1009 end
1000
1010
1001 # Query generator for selecting groups of issue counts for a project
1011 # Query generator for selecting groups of issue counts for a project
1002 # based on specific criteria
1012 # based on specific criteria
1003 #
1013 #
1004 # Options
1014 # Options
1005 # * project - Project to search in.
1015 # * project - Project to search in.
1006 # * field - String. Issue field to key off of in the grouping.
1016 # * field - String. Issue field to key off of in the grouping.
1007 # * joins - String. The table name to join against.
1017 # * joins - String. The table name to join against.
1008 def self.count_and_group_by(options)
1018 def self.count_and_group_by(options)
1009 project = options.delete(:project)
1019 project = options.delete(:project)
1010 select_field = options.delete(:field)
1020 select_field = options.delete(:field)
1011 joins = options.delete(:joins)
1021 joins = options.delete(:joins)
1012
1022
1013 where = "#{Issue.table_name}.#{select_field}=j.id"
1023 where = "#{Issue.table_name}.#{select_field}=j.id"
1014
1024
1015 ActiveRecord::Base.connection.select_all("select s.id as status_id,
1025 ActiveRecord::Base.connection.select_all("select s.id as status_id,
1016 s.is_closed as closed,
1026 s.is_closed as closed,
1017 j.id as #{select_field},
1027 j.id as #{select_field},
1018 count(#{Issue.table_name}.id) as total
1028 count(#{Issue.table_name}.id) as total
1019 from
1029 from
1020 #{Issue.table_name}, #{Project.table_name}, #{IssueStatus.table_name} s, #{joins} j
1030 #{Issue.table_name}, #{Project.table_name}, #{IssueStatus.table_name} s, #{joins} j
1021 where
1031 where
1022 #{Issue.table_name}.status_id=s.id
1032 #{Issue.table_name}.status_id=s.id
1023 and #{where}
1033 and #{where}
1024 and #{Issue.table_name}.project_id=#{Project.table_name}.id
1034 and #{Issue.table_name}.project_id=#{Project.table_name}.id
1025 and #{visible_condition(User.current, :project => project)}
1035 and #{visible_condition(User.current, :project => project)}
1026 group by s.id, s.is_closed, j.id")
1036 group by s.id, s.is_closed, j.id")
1027 end
1037 end
1028 end
1038 end
@@ -1,48 +1,48
1 <% labelled_fields_for :issue, @issue do |f| %>
1 <% labelled_fields_for :issue, @issue do |f| %>
2 <%= call_hook(:view_issues_form_details_top, { :issue => @issue, :form => f }) %>
2 <%= call_hook(:view_issues_form_details_top, { :issue => @issue, :form => f }) %>
3
3
4 <% if @issue.safe_attribute? 'is_private' %>
4 <% if @issue.safe_attribute? 'is_private' %>
5 <p style="float:right; margin-right:1em;">
5 <p style="float:right; margin-right:1em;">
6 <label class="inline" for="issue_is_private" id="issue_is_private_label"><%= f.check_box :is_private, :no_label => true %> <%= l(:field_is_private) %></label>
6 <label class="inline" for="issue_is_private" id="issue_is_private_label"><%= f.check_box :is_private, :no_label => true %> <%= l(:field_is_private) %></label>
7 </p>
7 </p>
8 <% end %>
8 <% end %>
9
9
10 <% if @issue.safe_attribute? 'project_id' %>
10 <% if @issue.safe_attribute? 'project_id' %>
11 <p><%= f.select :project_id, project_tree_options_for_select(Issue.allowed_target_projects_on_move, :selected => @issue.project), :required => true %></p>
11 <p><%= f.select :project_id, project_tree_options_for_select(@issue.allowed_target_projects, :selected => @issue.project), :required => true %></p>
12 <%= observe_field :issue_project_id, :url => project_issue_form_path(@project, :id => @issue, :project_change => '1'),
12 <%= observe_field :issue_project_id, :url => project_issue_form_path(@project, :id => @issue, :project_change => '1'),
13 :with => "Form.serialize('issue-form')" %>
13 :with => "Form.serialize('issue-form')" %>
14 <% end %>
14 <% end %>
15
15
16 <% if @issue.safe_attribute? 'tracker_id' %>
16 <% if @issue.safe_attribute? 'tracker_id' %>
17 <p><%= f.select :tracker_id, @issue.project.trackers.collect {|t| [t.name, t.id]}, :required => true %></p>
17 <p><%= f.select :tracker_id, @issue.project.trackers.collect {|t| [t.name, t.id]}, :required => true %></p>
18 <%= observe_field :issue_tracker_id, :url => project_issue_form_path(@project, :id => @issue),
18 <%= observe_field :issue_tracker_id, :url => project_issue_form_path(@project, :id => @issue),
19 :with => "Form.serialize('issue-form')" %>
19 :with => "Form.serialize('issue-form')" %>
20 <% end %>
20 <% end %>
21
21
22 <% if @issue.safe_attribute? 'subject' %>
22 <% if @issue.safe_attribute? 'subject' %>
23 <p><%= f.text_field :subject, :size => 80, :required => true %></p>
23 <p><%= f.text_field :subject, :size => 80, :required => true %></p>
24 <% end %>
24 <% end %>
25
25
26 <% if @issue.safe_attribute? 'description' %>
26 <% if @issue.safe_attribute? 'description' %>
27 <p>
27 <p>
28 <label><%= l(:field_description) %></label>
28 <label><%= l(:field_description) %></label>
29 <%= link_to_function image_tag('edit.png'),
29 <%= link_to_function image_tag('edit.png'),
30 'Element.hide(this); Effect.toggle("issue_description_and_toolbar", "appear", {duration:0.3})' unless @issue.new_record? %>
30 'Element.hide(this); Effect.toggle("issue_description_and_toolbar", "appear", {duration:0.3})' unless @issue.new_record? %>
31 <% content_tag 'span', :id => "issue_description_and_toolbar", :style => (@issue.new_record? ? nil : 'display:none') do %>
31 <% content_tag 'span', :id => "issue_description_and_toolbar", :style => (@issue.new_record? ? nil : 'display:none') do %>
32 <%= f.text_area :description,
32 <%= f.text_area :description,
33 :cols => 60,
33 :cols => 60,
34 :rows => (@issue.description.blank? ? 10 : [[10, @issue.description.length / 50].max, 100].min),
34 :rows => (@issue.description.blank? ? 10 : [[10, @issue.description.length / 50].max, 100].min),
35 :accesskey => accesskey(:edit),
35 :accesskey => accesskey(:edit),
36 :class => 'wiki-edit',
36 :class => 'wiki-edit',
37 :no_label => true %>
37 :no_label => true %>
38 <% end %>
38 <% end %>
39 </p>
39 </p>
40 <%= wikitoolbar_for 'issue_description' %>
40 <%= wikitoolbar_for 'issue_description' %>
41 <% end %>
41 <% end %>
42
42
43 <div id="attributes" class="attributes">
43 <div id="attributes" class="attributes">
44 <%= render :partial => 'issues/attributes' %>
44 <%= render :partial => 'issues/attributes' %>
45 </div>
45 </div>
46
46
47 <%= call_hook(:view_issues_form_details_bottom, { :issue => @issue, :form => f }) %>
47 <%= call_hook(:view_issues_form_details_bottom, { :issue => @issue, :form => f }) %>
48 <% end %>
48 <% end %>
@@ -1,2521 +1,2533
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 end
931 end
932
932
933 def test_show_should_display_prev_next_links_with_query_in_session
933 def test_show_should_display_prev_next_links_with_query_in_session
934 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
934 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
935 @request.session['issues_index_sort'] = 'id'
935 @request.session['issues_index_sort'] = 'id'
936
936
937 with_settings :display_subprojects_issues => '0' do
937 with_settings :display_subprojects_issues => '0' do
938 get :show, :id => 3
938 get :show, :id => 3
939 end
939 end
940
940
941 assert_response :success
941 assert_response :success
942 # Previous and next issues for all projects
942 # Previous and next issues for all projects
943 assert_equal 2, assigns(:prev_issue_id)
943 assert_equal 2, assigns(:prev_issue_id)
944 assert_equal 5, assigns(:next_issue_id)
944 assert_equal 5, assigns(:next_issue_id)
945
945
946 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
946 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
947 assert_tag 'a', :attributes => {:href => '/issues/5'}, :content => /Next/
947 assert_tag 'a', :attributes => {:href => '/issues/5'}, :content => /Next/
948 end
948 end
949
949
950 def test_show_should_display_prev_next_links_with_project_query_in_session
950 def test_show_should_display_prev_next_links_with_project_query_in_session
951 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
951 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
952 @request.session['issues_index_sort'] = 'id'
952 @request.session['issues_index_sort'] = 'id'
953
953
954 with_settings :display_subprojects_issues => '0' do
954 with_settings :display_subprojects_issues => '0' do
955 get :show, :id => 3
955 get :show, :id => 3
956 end
956 end
957
957
958 assert_response :success
958 assert_response :success
959 # Previous and next issues inside project
959 # Previous and next issues inside project
960 assert_equal 2, assigns(:prev_issue_id)
960 assert_equal 2, assigns(:prev_issue_id)
961 assert_equal 7, assigns(:next_issue_id)
961 assert_equal 7, assigns(:next_issue_id)
962
962
963 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
963 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
964 assert_tag 'a', :attributes => {:href => '/issues/7'}, :content => /Next/
964 assert_tag 'a', :attributes => {:href => '/issues/7'}, :content => /Next/
965 end
965 end
966
966
967 def test_show_should_not_display_prev_link_for_first_issue
967 def test_show_should_not_display_prev_link_for_first_issue
968 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
968 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
969 @request.session['issues_index_sort'] = 'id'
969 @request.session['issues_index_sort'] = 'id'
970
970
971 with_settings :display_subprojects_issues => '0' do
971 with_settings :display_subprojects_issues => '0' do
972 get :show, :id => 1
972 get :show, :id => 1
973 end
973 end
974
974
975 assert_response :success
975 assert_response :success
976 assert_nil assigns(:prev_issue_id)
976 assert_nil assigns(:prev_issue_id)
977 assert_equal 2, assigns(:next_issue_id)
977 assert_equal 2, assigns(:next_issue_id)
978
978
979 assert_no_tag 'a', :content => /Previous/
979 assert_no_tag 'a', :content => /Previous/
980 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Next/
980 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Next/
981 end
981 end
982
982
983 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
983 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
984 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
984 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
985 @request.session['issues_index_sort'] = 'id'
985 @request.session['issues_index_sort'] = 'id'
986
986
987 get :show, :id => 1
987 get :show, :id => 1
988
988
989 assert_response :success
989 assert_response :success
990 assert_nil assigns(:prev_issue_id)
990 assert_nil assigns(:prev_issue_id)
991 assert_nil assigns(:next_issue_id)
991 assert_nil assigns(:next_issue_id)
992
992
993 assert_no_tag 'a', :content => /Previous/
993 assert_no_tag 'a', :content => /Previous/
994 assert_no_tag 'a', :content => /Next/
994 assert_no_tag 'a', :content => /Next/
995 end
995 end
996
996
997 def test_show_atom
997 def test_show_atom
998 get :show, :id => 2, :format => 'atom'
998 get :show, :id => 2, :format => 'atom'
999 assert_response :success
999 assert_response :success
1000 assert_template 'journals/index'
1000 assert_template 'journals/index'
1001 # Inline image
1001 # Inline image
1002 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1002 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1003 end
1003 end
1004
1004
1005 def test_show_export_to_pdf
1005 def test_show_export_to_pdf
1006 get :show, :id => 3, :format => 'pdf'
1006 get :show, :id => 3, :format => 'pdf'
1007 assert_response :success
1007 assert_response :success
1008 assert_equal 'application/pdf', @response.content_type
1008 assert_equal 'application/pdf', @response.content_type
1009 assert @response.body.starts_with?('%PDF')
1009 assert @response.body.starts_with?('%PDF')
1010 assert_not_nil assigns(:issue)
1010 assert_not_nil assigns(:issue)
1011 end
1011 end
1012
1012
1013 def test_get_new
1013 def test_get_new
1014 @request.session[:user_id] = 2
1014 @request.session[:user_id] = 2
1015 get :new, :project_id => 1, :tracker_id => 1
1015 get :new, :project_id => 1, :tracker_id => 1
1016 assert_response :success
1016 assert_response :success
1017 assert_template 'new'
1017 assert_template 'new'
1018
1018
1019 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
1019 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
1020 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1020 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1021 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1021 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1022 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1022 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1023 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1023 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1024 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1024 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1025 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1025 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1026 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1026 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1027 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1027 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1028 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1028 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1029 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1029 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1030 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1030 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1031 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1031 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1032 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1032 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1033 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1033 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1034 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1034 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1035
1035
1036 # Be sure we don't display inactive IssuePriorities
1036 # Be sure we don't display inactive IssuePriorities
1037 assert ! IssuePriority.find(15).active?
1037 assert ! IssuePriority.find(15).active?
1038 assert_no_tag :option, :attributes => {:value => '15'},
1038 assert_no_tag :option, :attributes => {:value => '15'},
1039 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1039 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1040 end
1040 end
1041
1041
1042 def test_get_new_with_minimal_permissions
1042 def test_get_new_with_minimal_permissions
1043 Role.find(1).update_attribute :permissions, [:add_issues]
1043 Role.find(1).update_attribute :permissions, [:add_issues]
1044 Workflow.delete_all :role_id => 1
1044 Workflow.delete_all :role_id => 1
1045
1045
1046 @request.session[:user_id] = 2
1046 @request.session[:user_id] = 2
1047 get :new, :project_id => 1, :tracker_id => 1
1047 get :new, :project_id => 1, :tracker_id => 1
1048 assert_response :success
1048 assert_response :success
1049 assert_template 'new'
1049 assert_template 'new'
1050
1050
1051 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
1051 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
1052 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1052 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1053 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1053 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1054 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1054 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1055 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1055 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1056 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1056 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1057 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1057 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1058 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1058 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1059 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1059 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1060 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1060 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1061 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1061 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1062 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1062 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1063 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1063 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1064 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1064 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1065 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1065 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1066 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1066 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1067 end
1067 end
1068
1068
1069 def test_get_new_without_default_start_date_is_creation_date
1069 def test_get_new_without_default_start_date_is_creation_date
1070 Setting.default_issue_start_date_to_creation_date = 0
1070 Setting.default_issue_start_date_to_creation_date = 0
1071
1071
1072 @request.session[:user_id] = 2
1072 @request.session[:user_id] = 2
1073 get :new, :project_id => 1, :tracker_id => 1
1073 get :new, :project_id => 1, :tracker_id => 1
1074 assert_response :success
1074 assert_response :success
1075 assert_template 'new'
1075 assert_template 'new'
1076
1076
1077 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1077 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1078 :value => nil }
1078 :value => nil }
1079 end
1079 end
1080
1080
1081 def test_get_new_with_default_start_date_is_creation_date
1081 def test_get_new_with_default_start_date_is_creation_date
1082 Setting.default_issue_start_date_to_creation_date = 1
1082 Setting.default_issue_start_date_to_creation_date = 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_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1089 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1090 :value => Date.today.to_s }
1090 :value => Date.today.to_s }
1091 end
1091 end
1092
1092
1093 def test_get_new_form_should_allow_attachment_upload
1093 def test_get_new_form_should_allow_attachment_upload
1094 @request.session[:user_id] = 2
1094 @request.session[:user_id] = 2
1095 get :new, :project_id => 1, :tracker_id => 1
1095 get :new, :project_id => 1, :tracker_id => 1
1096
1096
1097 assert_tag :tag => 'form',
1097 assert_tag :tag => 'form',
1098 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
1098 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
1099 :descendant => {
1099 :descendant => {
1100 :tag => 'input',
1100 :tag => 'input',
1101 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
1101 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
1102 }
1102 }
1103 end
1103 end
1104
1104
1105 def test_get_new_without_tracker_id
1105 def test_get_new_without_tracker_id
1106 @request.session[:user_id] = 2
1106 @request.session[:user_id] = 2
1107 get :new, :project_id => 1
1107 get :new, :project_id => 1
1108 assert_response :success
1108 assert_response :success
1109 assert_template 'new'
1109 assert_template 'new'
1110
1110
1111 issue = assigns(:issue)
1111 issue = assigns(:issue)
1112 assert_not_nil issue
1112 assert_not_nil issue
1113 assert_equal Project.find(1).trackers.first, issue.tracker
1113 assert_equal Project.find(1).trackers.first, issue.tracker
1114 end
1114 end
1115
1115
1116 def test_get_new_with_no_default_status_should_display_an_error
1116 def test_get_new_with_no_default_status_should_display_an_error
1117 @request.session[:user_id] = 2
1117 @request.session[:user_id] = 2
1118 IssueStatus.delete_all
1118 IssueStatus.delete_all
1119
1119
1120 get :new, :project_id => 1
1120 get :new, :project_id => 1
1121 assert_response 500
1121 assert_response 500
1122 assert_error_tag :content => /No default issue/
1122 assert_error_tag :content => /No default issue/
1123 end
1123 end
1124
1124
1125 def test_get_new_with_no_tracker_should_display_an_error
1125 def test_get_new_with_no_tracker_should_display_an_error
1126 @request.session[:user_id] = 2
1126 @request.session[:user_id] = 2
1127 Tracker.delete_all
1127 Tracker.delete_all
1128
1128
1129 get :new, :project_id => 1
1129 get :new, :project_id => 1
1130 assert_response 500
1130 assert_response 500
1131 assert_error_tag :content => /No tracker/
1131 assert_error_tag :content => /No tracker/
1132 end
1132 end
1133
1133
1134 def test_update_new_form
1134 def test_update_new_form
1135 @request.session[:user_id] = 2
1135 @request.session[:user_id] = 2
1136 xhr :post, :new, :project_id => 1,
1136 xhr :post, :new, :project_id => 1,
1137 :issue => {:tracker_id => 2,
1137 :issue => {:tracker_id => 2,
1138 :subject => 'This is the test_new issue',
1138 :subject => 'This is the test_new issue',
1139 :description => 'This is the description',
1139 :description => 'This is the description',
1140 :priority_id => 5}
1140 :priority_id => 5}
1141 assert_response :success
1141 assert_response :success
1142 assert_template 'attributes'
1142 assert_template 'attributes'
1143
1143
1144 issue = assigns(:issue)
1144 issue = assigns(:issue)
1145 assert_kind_of Issue, issue
1145 assert_kind_of Issue, issue
1146 assert_equal 1, issue.project_id
1146 assert_equal 1, issue.project_id
1147 assert_equal 2, issue.tracker_id
1147 assert_equal 2, issue.tracker_id
1148 assert_equal 'This is the test_new issue', issue.subject
1148 assert_equal 'This is the test_new issue', issue.subject
1149 end
1149 end
1150
1150
1151 def test_post_create
1151 def test_post_create
1152 @request.session[:user_id] = 2
1152 @request.session[:user_id] = 2
1153 assert_difference 'Issue.count' do
1153 assert_difference 'Issue.count' do
1154 post :create, :project_id => 1,
1154 post :create, :project_id => 1,
1155 :issue => {:tracker_id => 3,
1155 :issue => {:tracker_id => 3,
1156 :status_id => 2,
1156 :status_id => 2,
1157 :subject => 'This is the test_new issue',
1157 :subject => 'This is the test_new issue',
1158 :description => 'This is the description',
1158 :description => 'This is the description',
1159 :priority_id => 5,
1159 :priority_id => 5,
1160 :start_date => '2010-11-07',
1160 :start_date => '2010-11-07',
1161 :estimated_hours => '',
1161 :estimated_hours => '',
1162 :custom_field_values => {'2' => 'Value for field 2'}}
1162 :custom_field_values => {'2' => 'Value for field 2'}}
1163 end
1163 end
1164 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1164 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1165
1165
1166 issue = Issue.find_by_subject('This is the test_new issue')
1166 issue = Issue.find_by_subject('This is the test_new issue')
1167 assert_not_nil issue
1167 assert_not_nil issue
1168 assert_equal 2, issue.author_id
1168 assert_equal 2, issue.author_id
1169 assert_equal 3, issue.tracker_id
1169 assert_equal 3, issue.tracker_id
1170 assert_equal 2, issue.status_id
1170 assert_equal 2, issue.status_id
1171 assert_equal Date.parse('2010-11-07'), issue.start_date
1171 assert_equal Date.parse('2010-11-07'), issue.start_date
1172 assert_nil issue.estimated_hours
1172 assert_nil issue.estimated_hours
1173 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
1173 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
1174 assert_not_nil v
1174 assert_not_nil v
1175 assert_equal 'Value for field 2', v.value
1175 assert_equal 'Value for field 2', v.value
1176 end
1176 end
1177
1177
1178 def test_post_new_with_group_assignment
1178 def test_post_new_with_group_assignment
1179 group = Group.find(11)
1179 group = Group.find(11)
1180 project = Project.find(1)
1180 project = Project.find(1)
1181 project.members << Member.new(:principal => group, :roles => [Role.first])
1181 project.members << Member.new(:principal => group, :roles => [Role.first])
1182
1182
1183 with_settings :issue_group_assignment => '1' do
1183 with_settings :issue_group_assignment => '1' do
1184 @request.session[:user_id] = 2
1184 @request.session[:user_id] = 2
1185 assert_difference 'Issue.count' do
1185 assert_difference 'Issue.count' do
1186 post :create, :project_id => project.id,
1186 post :create, :project_id => project.id,
1187 :issue => {:tracker_id => 3,
1187 :issue => {:tracker_id => 3,
1188 :status_id => 1,
1188 :status_id => 1,
1189 :subject => 'This is the test_new_with_group_assignment issue',
1189 :subject => 'This is the test_new_with_group_assignment issue',
1190 :assigned_to_id => group.id}
1190 :assigned_to_id => group.id}
1191 end
1191 end
1192 end
1192 end
1193 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1193 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1194
1194
1195 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
1195 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
1196 assert_not_nil issue
1196 assert_not_nil issue
1197 assert_equal group, issue.assigned_to
1197 assert_equal group, issue.assigned_to
1198 end
1198 end
1199
1199
1200 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
1200 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
1201 Setting.default_issue_start_date_to_creation_date = 0
1201 Setting.default_issue_start_date_to_creation_date = 0
1202
1202
1203 @request.session[:user_id] = 2
1203 @request.session[:user_id] = 2
1204 assert_difference 'Issue.count' do
1204 assert_difference 'Issue.count' do
1205 post :create, :project_id => 1,
1205 post :create, :project_id => 1,
1206 :issue => {:tracker_id => 3,
1206 :issue => {:tracker_id => 3,
1207 :status_id => 2,
1207 :status_id => 2,
1208 :subject => 'This is the test_new issue',
1208 :subject => 'This is the test_new issue',
1209 :description => 'This is the description',
1209 :description => 'This is the description',
1210 :priority_id => 5,
1210 :priority_id => 5,
1211 :estimated_hours => '',
1211 :estimated_hours => '',
1212 :custom_field_values => {'2' => 'Value for field 2'}}
1212 :custom_field_values => {'2' => 'Value for field 2'}}
1213 end
1213 end
1214 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1214 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1215
1215
1216 issue = Issue.find_by_subject('This is the test_new issue')
1216 issue = Issue.find_by_subject('This is the test_new issue')
1217 assert_not_nil issue
1217 assert_not_nil issue
1218 assert_nil issue.start_date
1218 assert_nil issue.start_date
1219 end
1219 end
1220
1220
1221 def test_post_create_without_start_date_and_default_start_date_is_creation_date
1221 def test_post_create_without_start_date_and_default_start_date_is_creation_date
1222 Setting.default_issue_start_date_to_creation_date = 1
1222 Setting.default_issue_start_date_to_creation_date = 1
1223
1223
1224 @request.session[:user_id] = 2
1224 @request.session[:user_id] = 2
1225 assert_difference 'Issue.count' do
1225 assert_difference 'Issue.count' do
1226 post :create, :project_id => 1,
1226 post :create, :project_id => 1,
1227 :issue => {:tracker_id => 3,
1227 :issue => {:tracker_id => 3,
1228 :status_id => 2,
1228 :status_id => 2,
1229 :subject => 'This is the test_new issue',
1229 :subject => 'This is the test_new issue',
1230 :description => 'This is the description',
1230 :description => 'This is the description',
1231 :priority_id => 5,
1231 :priority_id => 5,
1232 :estimated_hours => '',
1232 :estimated_hours => '',
1233 :custom_field_values => {'2' => 'Value for field 2'}}
1233 :custom_field_values => {'2' => 'Value for field 2'}}
1234 end
1234 end
1235 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1235 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1236
1236
1237 issue = Issue.find_by_subject('This is the test_new issue')
1237 issue = Issue.find_by_subject('This is the test_new issue')
1238 assert_not_nil issue
1238 assert_not_nil issue
1239 assert_equal Date.today, issue.start_date
1239 assert_equal Date.today, issue.start_date
1240 end
1240 end
1241
1241
1242 def test_post_create_and_continue
1242 def test_post_create_and_continue
1243 @request.session[:user_id] = 2
1243 @request.session[:user_id] = 2
1244 assert_difference 'Issue.count' do
1244 assert_difference 'Issue.count' do
1245 post :create, :project_id => 1,
1245 post :create, :project_id => 1,
1246 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
1246 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
1247 :continue => ''
1247 :continue => ''
1248 end
1248 end
1249
1249
1250 issue = Issue.first(:order => 'id DESC')
1250 issue = Issue.first(:order => 'id DESC')
1251 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
1251 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
1252 assert_not_nil flash[:notice], "flash was not set"
1252 assert_not_nil flash[:notice], "flash was not set"
1253 assert flash[:notice].include?("<a href='/issues/#{issue.id}'>##{issue.id}</a>"), "issue link not found in flash: #{flash[:notice]}"
1253 assert flash[:notice].include?("<a href='/issues/#{issue.id}'>##{issue.id}</a>"), "issue link not found in flash: #{flash[:notice]}"
1254 end
1254 end
1255
1255
1256 def test_post_create_without_custom_fields_param
1256 def test_post_create_without_custom_fields_param
1257 @request.session[:user_id] = 2
1257 @request.session[:user_id] = 2
1258 assert_difference 'Issue.count' do
1258 assert_difference 'Issue.count' do
1259 post :create, :project_id => 1,
1259 post :create, :project_id => 1,
1260 :issue => {:tracker_id => 1,
1260 :issue => {:tracker_id => 1,
1261 :subject => 'This is the test_new issue',
1261 :subject => 'This is the test_new issue',
1262 :description => 'This is the description',
1262 :description => 'This is the description',
1263 :priority_id => 5}
1263 :priority_id => 5}
1264 end
1264 end
1265 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1265 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1266 end
1266 end
1267
1267
1268 def test_post_create_with_required_custom_field_and_without_custom_fields_param
1268 def test_post_create_with_required_custom_field_and_without_custom_fields_param
1269 field = IssueCustomField.find_by_name('Database')
1269 field = IssueCustomField.find_by_name('Database')
1270 field.update_attribute(:is_required, true)
1270 field.update_attribute(:is_required, true)
1271
1271
1272 @request.session[:user_id] = 2
1272 @request.session[:user_id] = 2
1273 post :create, :project_id => 1,
1273 post :create, :project_id => 1,
1274 :issue => {:tracker_id => 1,
1274 :issue => {:tracker_id => 1,
1275 :subject => 'This is the test_new issue',
1275 :subject => 'This is the test_new issue',
1276 :description => 'This is the description',
1276 :description => 'This is the description',
1277 :priority_id => 5}
1277 :priority_id => 5}
1278 assert_response :success
1278 assert_response :success
1279 assert_template 'new'
1279 assert_template 'new'
1280 issue = assigns(:issue)
1280 issue = assigns(:issue)
1281 assert_not_nil issue
1281 assert_not_nil issue
1282 assert_equal I18n.translate('activerecord.errors.messages.invalid'),
1282 assert_equal I18n.translate('activerecord.errors.messages.invalid'),
1283 issue.errors[:custom_values].to_s
1283 issue.errors[:custom_values].to_s
1284 end
1284 end
1285
1285
1286 def test_post_create_with_watchers
1286 def test_post_create_with_watchers
1287 @request.session[:user_id] = 2
1287 @request.session[:user_id] = 2
1288 ActionMailer::Base.deliveries.clear
1288 ActionMailer::Base.deliveries.clear
1289
1289
1290 assert_difference 'Watcher.count', 2 do
1290 assert_difference 'Watcher.count', 2 do
1291 post :create, :project_id => 1,
1291 post :create, :project_id => 1,
1292 :issue => {:tracker_id => 1,
1292 :issue => {:tracker_id => 1,
1293 :subject => 'This is a new issue with watchers',
1293 :subject => 'This is a new issue with watchers',
1294 :description => 'This is the description',
1294 :description => 'This is the description',
1295 :priority_id => 5,
1295 :priority_id => 5,
1296 :watcher_user_ids => ['2', '3']}
1296 :watcher_user_ids => ['2', '3']}
1297 end
1297 end
1298 issue = Issue.find_by_subject('This is a new issue with watchers')
1298 issue = Issue.find_by_subject('This is a new issue with watchers')
1299 assert_not_nil issue
1299 assert_not_nil issue
1300 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1300 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1301
1301
1302 # Watchers added
1302 # Watchers added
1303 assert_equal [2, 3], issue.watcher_user_ids.sort
1303 assert_equal [2, 3], issue.watcher_user_ids.sort
1304 assert issue.watched_by?(User.find(3))
1304 assert issue.watched_by?(User.find(3))
1305 # Watchers notified
1305 # Watchers notified
1306 mail = ActionMailer::Base.deliveries.last
1306 mail = ActionMailer::Base.deliveries.last
1307 assert_kind_of TMail::Mail, mail
1307 assert_kind_of TMail::Mail, mail
1308 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
1308 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
1309 end
1309 end
1310
1310
1311 def test_post_create_subissue
1311 def test_post_create_subissue
1312 @request.session[:user_id] = 2
1312 @request.session[:user_id] = 2
1313
1313
1314 assert_difference 'Issue.count' do
1314 assert_difference 'Issue.count' do
1315 post :create, :project_id => 1,
1315 post :create, :project_id => 1,
1316 :issue => {:tracker_id => 1,
1316 :issue => {:tracker_id => 1,
1317 :subject => 'This is a child issue',
1317 :subject => 'This is a child issue',
1318 :parent_issue_id => 2}
1318 :parent_issue_id => 2}
1319 end
1319 end
1320 issue = Issue.find_by_subject('This is a child issue')
1320 issue = Issue.find_by_subject('This is a child issue')
1321 assert_not_nil issue
1321 assert_not_nil issue
1322 assert_equal Issue.find(2), issue.parent
1322 assert_equal Issue.find(2), issue.parent
1323 end
1323 end
1324
1324
1325 def test_post_create_subissue_with_non_numeric_parent_id
1325 def test_post_create_subissue_with_non_numeric_parent_id
1326 @request.session[:user_id] = 2
1326 @request.session[:user_id] = 2
1327
1327
1328 assert_difference 'Issue.count' do
1328 assert_difference 'Issue.count' do
1329 post :create, :project_id => 1,
1329 post :create, :project_id => 1,
1330 :issue => {:tracker_id => 1,
1330 :issue => {:tracker_id => 1,
1331 :subject => 'This is a child issue',
1331 :subject => 'This is a child issue',
1332 :parent_issue_id => 'ABC'}
1332 :parent_issue_id => 'ABC'}
1333 end
1333 end
1334 issue = Issue.find_by_subject('This is a child issue')
1334 issue = Issue.find_by_subject('This is a child issue')
1335 assert_not_nil issue
1335 assert_not_nil issue
1336 assert_nil issue.parent
1336 assert_nil issue.parent
1337 end
1337 end
1338
1338
1339 def test_post_create_private
1339 def test_post_create_private
1340 @request.session[:user_id] = 2
1340 @request.session[:user_id] = 2
1341
1341
1342 assert_difference 'Issue.count' do
1342 assert_difference 'Issue.count' do
1343 post :create, :project_id => 1,
1343 post :create, :project_id => 1,
1344 :issue => {:tracker_id => 1,
1344 :issue => {:tracker_id => 1,
1345 :subject => 'This is a private issue',
1345 :subject => 'This is a private issue',
1346 :is_private => '1'}
1346 :is_private => '1'}
1347 end
1347 end
1348 issue = Issue.first(:order => 'id DESC')
1348 issue = Issue.first(:order => 'id DESC')
1349 assert issue.is_private?
1349 assert issue.is_private?
1350 end
1350 end
1351
1351
1352 def test_post_create_private_with_set_own_issues_private_permission
1352 def test_post_create_private_with_set_own_issues_private_permission
1353 role = Role.find(1)
1353 role = Role.find(1)
1354 role.remove_permission! :set_issues_private
1354 role.remove_permission! :set_issues_private
1355 role.add_permission! :set_own_issues_private
1355 role.add_permission! :set_own_issues_private
1356
1356
1357 @request.session[:user_id] = 2
1357 @request.session[:user_id] = 2
1358
1358
1359 assert_difference 'Issue.count' do
1359 assert_difference 'Issue.count' do
1360 post :create, :project_id => 1,
1360 post :create, :project_id => 1,
1361 :issue => {:tracker_id => 1,
1361 :issue => {:tracker_id => 1,
1362 :subject => 'This is a private issue',
1362 :subject => 'This is a private issue',
1363 :is_private => '1'}
1363 :is_private => '1'}
1364 end
1364 end
1365 issue = Issue.first(:order => 'id DESC')
1365 issue = Issue.first(:order => 'id DESC')
1366 assert issue.is_private?
1366 assert issue.is_private?
1367 end
1367 end
1368
1368
1369 def test_post_create_should_send_a_notification
1369 def test_post_create_should_send_a_notification
1370 ActionMailer::Base.deliveries.clear
1370 ActionMailer::Base.deliveries.clear
1371 @request.session[:user_id] = 2
1371 @request.session[:user_id] = 2
1372 assert_difference 'Issue.count' do
1372 assert_difference 'Issue.count' do
1373 post :create, :project_id => 1,
1373 post :create, :project_id => 1,
1374 :issue => {:tracker_id => 3,
1374 :issue => {:tracker_id => 3,
1375 :subject => 'This is the test_new issue',
1375 :subject => 'This is the test_new issue',
1376 :description => 'This is the description',
1376 :description => 'This is the description',
1377 :priority_id => 5,
1377 :priority_id => 5,
1378 :estimated_hours => '',
1378 :estimated_hours => '',
1379 :custom_field_values => {'2' => 'Value for field 2'}}
1379 :custom_field_values => {'2' => 'Value for field 2'}}
1380 end
1380 end
1381 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1381 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1382
1382
1383 assert_equal 1, ActionMailer::Base.deliveries.size
1383 assert_equal 1, ActionMailer::Base.deliveries.size
1384 end
1384 end
1385
1385
1386 def test_post_create_should_preserve_fields_values_on_validation_failure
1386 def test_post_create_should_preserve_fields_values_on_validation_failure
1387 @request.session[:user_id] = 2
1387 @request.session[:user_id] = 2
1388 post :create, :project_id => 1,
1388 post :create, :project_id => 1,
1389 :issue => {:tracker_id => 1,
1389 :issue => {:tracker_id => 1,
1390 # empty subject
1390 # empty subject
1391 :subject => '',
1391 :subject => '',
1392 :description => 'This is a description',
1392 :description => 'This is a description',
1393 :priority_id => 6,
1393 :priority_id => 6,
1394 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
1394 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
1395 assert_response :success
1395 assert_response :success
1396 assert_template 'new'
1396 assert_template 'new'
1397
1397
1398 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
1398 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
1399 :content => 'This is a description'
1399 :content => 'This is a description'
1400 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1400 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1401 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1401 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1402 :value => '6' },
1402 :value => '6' },
1403 :content => 'High' }
1403 :content => 'High' }
1404 # Custom fields
1404 # Custom fields
1405 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
1405 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
1406 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1406 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1407 :value => 'Oracle' },
1407 :value => 'Oracle' },
1408 :content => 'Oracle' }
1408 :content => 'Oracle' }
1409 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
1409 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
1410 :value => 'Value for field 2'}
1410 :value => 'Value for field 2'}
1411 end
1411 end
1412
1412
1413 def test_post_create_should_ignore_non_safe_attributes
1413 def test_post_create_should_ignore_non_safe_attributes
1414 @request.session[:user_id] = 2
1414 @request.session[:user_id] = 2
1415 assert_nothing_raised do
1415 assert_nothing_raised do
1416 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
1416 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
1417 end
1417 end
1418 end
1418 end
1419
1419
1420 def test_post_create_with_attachment
1420 def test_post_create_with_attachment
1421 set_tmp_attachments_directory
1421 set_tmp_attachments_directory
1422 @request.session[:user_id] = 2
1422 @request.session[:user_id] = 2
1423
1423
1424 assert_difference 'Issue.count' do
1424 assert_difference 'Issue.count' do
1425 assert_difference 'Attachment.count' do
1425 assert_difference 'Attachment.count' do
1426 post :create, :project_id => 1,
1426 post :create, :project_id => 1,
1427 :issue => { :tracker_id => '1', :subject => 'With attachment' },
1427 :issue => { :tracker_id => '1', :subject => 'With attachment' },
1428 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1428 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1429 end
1429 end
1430 end
1430 end
1431
1431
1432 issue = Issue.first(:order => 'id DESC')
1432 issue = Issue.first(:order => 'id DESC')
1433 attachment = Attachment.first(:order => 'id DESC')
1433 attachment = Attachment.first(:order => 'id DESC')
1434
1434
1435 assert_equal issue, attachment.container
1435 assert_equal issue, attachment.container
1436 assert_equal 2, attachment.author_id
1436 assert_equal 2, attachment.author_id
1437 assert_equal 'testfile.txt', attachment.filename
1437 assert_equal 'testfile.txt', attachment.filename
1438 assert_equal 'text/plain', attachment.content_type
1438 assert_equal 'text/plain', attachment.content_type
1439 assert_equal 'test file', attachment.description
1439 assert_equal 'test file', attachment.description
1440 assert_equal 59, attachment.filesize
1440 assert_equal 59, attachment.filesize
1441 assert File.exists?(attachment.diskfile)
1441 assert File.exists?(attachment.diskfile)
1442 assert_equal 59, File.size(attachment.diskfile)
1442 assert_equal 59, File.size(attachment.diskfile)
1443 end
1443 end
1444
1444
1445 context "without workflow privilege" do
1445 context "without workflow privilege" do
1446 setup do
1446 setup do
1447 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1447 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1448 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1448 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1449 end
1449 end
1450
1450
1451 context "#new" do
1451 context "#new" do
1452 should "propose default status only" do
1452 should "propose default status only" do
1453 get :new, :project_id => 1
1453 get :new, :project_id => 1
1454 assert_response :success
1454 assert_response :success
1455 assert_template 'new'
1455 assert_template 'new'
1456 assert_tag :tag => 'select',
1456 assert_tag :tag => 'select',
1457 :attributes => {:name => 'issue[status_id]'},
1457 :attributes => {:name => 'issue[status_id]'},
1458 :children => {:count => 1},
1458 :children => {:count => 1},
1459 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
1459 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
1460 end
1460 end
1461
1461
1462 should "accept default status" do
1462 should "accept default status" do
1463 assert_difference 'Issue.count' do
1463 assert_difference 'Issue.count' do
1464 post :create, :project_id => 1,
1464 post :create, :project_id => 1,
1465 :issue => {:tracker_id => 1,
1465 :issue => {:tracker_id => 1,
1466 :subject => 'This is an issue',
1466 :subject => 'This is an issue',
1467 :status_id => 1}
1467 :status_id => 1}
1468 end
1468 end
1469 issue = Issue.last(:order => 'id')
1469 issue = Issue.last(:order => 'id')
1470 assert_equal IssueStatus.default, issue.status
1470 assert_equal IssueStatus.default, issue.status
1471 end
1471 end
1472
1472
1473 should "ignore unauthorized status" do
1473 should "ignore unauthorized status" do
1474 assert_difference 'Issue.count' do
1474 assert_difference 'Issue.count' do
1475 post :create, :project_id => 1,
1475 post :create, :project_id => 1,
1476 :issue => {:tracker_id => 1,
1476 :issue => {:tracker_id => 1,
1477 :subject => 'This is an issue',
1477 :subject => 'This is an issue',
1478 :status_id => 3}
1478 :status_id => 3}
1479 end
1479 end
1480 issue = Issue.last(:order => 'id')
1480 issue = Issue.last(:order => 'id')
1481 assert_equal IssueStatus.default, issue.status
1481 assert_equal IssueStatus.default, issue.status
1482 end
1482 end
1483 end
1483 end
1484
1484
1485 context "#update" do
1485 context "#update" do
1486 should "ignore status change" do
1486 should "ignore status change" do
1487 assert_difference 'Journal.count' do
1487 assert_difference 'Journal.count' do
1488 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1488 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1489 end
1489 end
1490 assert_equal 1, Issue.find(1).status_id
1490 assert_equal 1, Issue.find(1).status_id
1491 end
1491 end
1492
1492
1493 should "ignore attributes changes" do
1493 should "ignore attributes changes" do
1494 assert_difference 'Journal.count' do
1494 assert_difference 'Journal.count' do
1495 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1495 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1496 end
1496 end
1497 issue = Issue.find(1)
1497 issue = Issue.find(1)
1498 assert_equal "Can't print recipes", issue.subject
1498 assert_equal "Can't print recipes", issue.subject
1499 assert_nil issue.assigned_to
1499 assert_nil issue.assigned_to
1500 end
1500 end
1501 end
1501 end
1502 end
1502 end
1503
1503
1504 context "with workflow privilege" do
1504 context "with workflow privilege" do
1505 setup do
1505 setup do
1506 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1506 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1507 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
1507 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
1508 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
1508 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
1509 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1509 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1510 end
1510 end
1511
1511
1512 context "#update" do
1512 context "#update" do
1513 should "accept authorized status" do
1513 should "accept authorized status" do
1514 assert_difference 'Journal.count' do
1514 assert_difference 'Journal.count' do
1515 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1515 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1516 end
1516 end
1517 assert_equal 3, Issue.find(1).status_id
1517 assert_equal 3, Issue.find(1).status_id
1518 end
1518 end
1519
1519
1520 should "ignore unauthorized status" do
1520 should "ignore unauthorized status" do
1521 assert_difference 'Journal.count' do
1521 assert_difference 'Journal.count' do
1522 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1522 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1523 end
1523 end
1524 assert_equal 1, Issue.find(1).status_id
1524 assert_equal 1, Issue.find(1).status_id
1525 end
1525 end
1526
1526
1527 should "accept authorized attributes changes" do
1527 should "accept authorized attributes changes" do
1528 assert_difference 'Journal.count' do
1528 assert_difference 'Journal.count' do
1529 put :update, :id => 1, :notes => 'just trying', :issue => {:assigned_to_id => 2}
1529 put :update, :id => 1, :notes => 'just trying', :issue => {:assigned_to_id => 2}
1530 end
1530 end
1531 issue = Issue.find(1)
1531 issue = Issue.find(1)
1532 assert_equal 2, issue.assigned_to_id
1532 assert_equal 2, issue.assigned_to_id
1533 end
1533 end
1534
1534
1535 should "ignore unauthorized attributes changes" do
1535 should "ignore unauthorized attributes changes" do
1536 assert_difference 'Journal.count' do
1536 assert_difference 'Journal.count' do
1537 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed'}
1537 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed'}
1538 end
1538 end
1539 issue = Issue.find(1)
1539 issue = Issue.find(1)
1540 assert_equal "Can't print recipes", issue.subject
1540 assert_equal "Can't print recipes", issue.subject
1541 end
1541 end
1542 end
1542 end
1543
1543
1544 context "and :edit_issues permission" do
1544 context "and :edit_issues permission" do
1545 setup do
1545 setup do
1546 Role.anonymous.add_permission! :add_issues, :edit_issues
1546 Role.anonymous.add_permission! :add_issues, :edit_issues
1547 end
1547 end
1548
1548
1549 should "accept authorized status" do
1549 should "accept authorized status" do
1550 assert_difference 'Journal.count' do
1550 assert_difference 'Journal.count' do
1551 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1551 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1552 end
1552 end
1553 assert_equal 3, Issue.find(1).status_id
1553 assert_equal 3, Issue.find(1).status_id
1554 end
1554 end
1555
1555
1556 should "ignore unauthorized status" do
1556 should "ignore unauthorized status" do
1557 assert_difference 'Journal.count' do
1557 assert_difference 'Journal.count' do
1558 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1558 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1559 end
1559 end
1560 assert_equal 1, Issue.find(1).status_id
1560 assert_equal 1, Issue.find(1).status_id
1561 end
1561 end
1562
1562
1563 should "accept authorized attributes changes" do
1563 should "accept authorized attributes changes" do
1564 assert_difference 'Journal.count' do
1564 assert_difference 'Journal.count' do
1565 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1565 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1566 end
1566 end
1567 issue = Issue.find(1)
1567 issue = Issue.find(1)
1568 assert_equal "changed", issue.subject
1568 assert_equal "changed", issue.subject
1569 assert_equal 2, issue.assigned_to_id
1569 assert_equal 2, issue.assigned_to_id
1570 end
1570 end
1571 end
1571 end
1572 end
1572 end
1573
1573
1574 def test_new_as_copy
1574 def test_new_as_copy
1575 @request.session[:user_id] = 2
1575 @request.session[:user_id] = 2
1576 get :new, :project_id => 1, :copy_from => 1
1576 get :new, :project_id => 1, :copy_from => 1
1577
1577
1578 assert_response :success
1578 assert_response :success
1579 assert_template 'new'
1579 assert_template 'new'
1580
1580
1581 assert_not_nil assigns(:issue)
1581 assert_not_nil assigns(:issue)
1582 orig = Issue.find(1)
1582 orig = Issue.find(1)
1583 assert_equal 1, assigns(:issue).project_id
1583 assert_equal 1, assigns(:issue).project_id
1584 assert_equal orig.subject, assigns(:issue).subject
1584 assert_equal orig.subject, assigns(:issue).subject
1585 assert assigns(:issue).copy?
1585 assert assigns(:issue).copy?
1586
1586
1587 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1587 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1588 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1588 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1589 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1589 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1590 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}, :content => 'eCookbook'}
1590 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}, :content => 'eCookbook'}
1591 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1591 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1592 :child => {:tag => 'option', :attributes => {:value => '2', :selected => nil}, :content => 'OnlineStore'}
1592 :child => {:tag => 'option', :attributes => {:value => '2', :selected => nil}, :content => 'OnlineStore'}
1593 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1593 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1594 end
1594 end
1595
1595
1596 def test_create_as_copy_on_different_project
1596 def test_create_as_copy_on_different_project
1597 @request.session[:user_id] = 2
1597 @request.session[:user_id] = 2
1598 assert_difference 'Issue.count' do
1598 assert_difference 'Issue.count' do
1599 post :create, :project_id => 1, :copy_from => 1,
1599 post :create, :project_id => 1, :copy_from => 1,
1600 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1600 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1601
1601
1602 assert_not_nil assigns(:issue)
1602 assert_not_nil assigns(:issue)
1603 assert assigns(:issue).copy?
1603 assert assigns(:issue).copy?
1604 end
1604 end
1605 issue = Issue.first(:order => 'id DESC')
1605 issue = Issue.first(:order => 'id DESC')
1606 assert_redirected_to "/issues/#{issue.id}"
1606 assert_redirected_to "/issues/#{issue.id}"
1607
1607
1608 assert_equal 2, issue.project_id
1608 assert_equal 2, issue.project_id
1609 assert_equal 3, issue.tracker_id
1609 assert_equal 3, issue.tracker_id
1610 assert_equal 'Copy', issue.subject
1610 assert_equal 'Copy', issue.subject
1611 end
1611 end
1612
1612
1613 def test_create_as_copy_with_failure
1613 def test_create_as_copy_with_failure
1614 @request.session[:user_id] = 2
1614 @request.session[:user_id] = 2
1615 post :create, :project_id => 1, :copy_from => 1,
1615 post :create, :project_id => 1, :copy_from => 1,
1616 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
1616 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
1617
1617
1618 assert_response :success
1618 assert_response :success
1619 assert_template 'new'
1619 assert_template 'new'
1620
1620
1621 assert_not_nil assigns(:issue)
1621 assert_not_nil assigns(:issue)
1622 assert assigns(:issue).copy?
1622 assert assigns(:issue).copy?
1623
1623
1624 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1624 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1625 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1625 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1626 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1626 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1627 :child => {:tag => 'option', :attributes => {:value => '1', :selected => nil}, :content => 'eCookbook'}
1627 :child => {:tag => 'option', :attributes => {:value => '1', :selected => nil}, :content => 'eCookbook'}
1628 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1628 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1629 :child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}, :content => 'OnlineStore'}
1629 :child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}, :content => 'OnlineStore'}
1630 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1630 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1631 end
1631 end
1632
1632
1633 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
1634 @request.session[:user_id] = 2
1635 assert !User.find(2).member_of?(Project.find(4))
1636
1637 assert_difference 'Issue.count' do
1638 post :create, :project_id => 1, :copy_from => 1,
1639 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1640 end
1641 issue = Issue.first(:order => 'id DESC')
1642 assert_equal 1, issue.project_id
1643 end
1644
1633 def test_get_edit
1645 def test_get_edit
1634 @request.session[:user_id] = 2
1646 @request.session[:user_id] = 2
1635 get :edit, :id => 1
1647 get :edit, :id => 1
1636 assert_response :success
1648 assert_response :success
1637 assert_template 'edit'
1649 assert_template 'edit'
1638 assert_not_nil assigns(:issue)
1650 assert_not_nil assigns(:issue)
1639 assert_equal Issue.find(1), assigns(:issue)
1651 assert_equal Issue.find(1), assigns(:issue)
1640
1652
1641 # Be sure we don't display inactive IssuePriorities
1653 # Be sure we don't display inactive IssuePriorities
1642 assert ! IssuePriority.find(15).active?
1654 assert ! IssuePriority.find(15).active?
1643 assert_no_tag :option, :attributes => {:value => '15'},
1655 assert_no_tag :option, :attributes => {:value => '15'},
1644 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1656 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1645 end
1657 end
1646
1658
1647 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
1659 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
1648 @request.session[:user_id] = 2
1660 @request.session[:user_id] = 2
1649 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
1661 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
1650
1662
1651 get :edit, :id => 1
1663 get :edit, :id => 1
1652 assert_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1664 assert_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1653 end
1665 end
1654
1666
1655 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
1667 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
1656 @request.session[:user_id] = 2
1668 @request.session[:user_id] = 2
1657 Role.find_by_name('Manager').remove_permission! :log_time
1669 Role.find_by_name('Manager').remove_permission! :log_time
1658
1670
1659 get :edit, :id => 1
1671 get :edit, :id => 1
1660 assert_no_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1672 assert_no_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1661 end
1673 end
1662
1674
1663 def test_get_edit_with_params
1675 def test_get_edit_with_params
1664 @request.session[:user_id] = 2
1676 @request.session[:user_id] = 2
1665 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
1677 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
1666 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
1678 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
1667 assert_response :success
1679 assert_response :success
1668 assert_template 'edit'
1680 assert_template 'edit'
1669
1681
1670 issue = assigns(:issue)
1682 issue = assigns(:issue)
1671 assert_not_nil issue
1683 assert_not_nil issue
1672
1684
1673 assert_equal 5, issue.status_id
1685 assert_equal 5, issue.status_id
1674 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
1686 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
1675 :child => { :tag => 'option',
1687 :child => { :tag => 'option',
1676 :content => 'Closed',
1688 :content => 'Closed',
1677 :attributes => { :selected => 'selected' } }
1689 :attributes => { :selected => 'selected' } }
1678
1690
1679 assert_equal 7, issue.priority_id
1691 assert_equal 7, issue.priority_id
1680 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1692 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1681 :child => { :tag => 'option',
1693 :child => { :tag => 'option',
1682 :content => 'Urgent',
1694 :content => 'Urgent',
1683 :attributes => { :selected => 'selected' } }
1695 :attributes => { :selected => 'selected' } }
1684
1696
1685 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
1697 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
1686 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
1698 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
1687 :child => { :tag => 'option',
1699 :child => { :tag => 'option',
1688 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
1700 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
1689 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
1701 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
1690 end
1702 end
1691
1703
1692 def test_update_edit_form
1704 def test_update_edit_form
1693 @request.session[:user_id] = 2
1705 @request.session[:user_id] = 2
1694 xhr :put, :new, :project_id => 1,
1706 xhr :put, :new, :project_id => 1,
1695 :id => 1,
1707 :id => 1,
1696 :issue => {:tracker_id => 2,
1708 :issue => {:tracker_id => 2,
1697 :subject => 'This is the test_new issue',
1709 :subject => 'This is the test_new issue',
1698 :description => 'This is the description',
1710 :description => 'This is the description',
1699 :priority_id => 5}
1711 :priority_id => 5}
1700 assert_response :success
1712 assert_response :success
1701 assert_template 'attributes'
1713 assert_template 'attributes'
1702
1714
1703 issue = assigns(:issue)
1715 issue = assigns(:issue)
1704 assert_kind_of Issue, issue
1716 assert_kind_of Issue, issue
1705 assert_equal 1, issue.id
1717 assert_equal 1, issue.id
1706 assert_equal 1, issue.project_id
1718 assert_equal 1, issue.project_id
1707 assert_equal 2, issue.tracker_id
1719 assert_equal 2, issue.tracker_id
1708 assert_equal 'This is the test_new issue', issue.subject
1720 assert_equal 'This is the test_new issue', issue.subject
1709 end
1721 end
1710
1722
1711 def test_update_edit_form_with_project_change
1723 def test_update_edit_form_with_project_change
1712 @request.session[:user_id] = 2
1724 @request.session[:user_id] = 2
1713 xhr :put, :new, :project_id => 1,
1725 xhr :put, :new, :project_id => 1,
1714 :id => 1,
1726 :id => 1,
1715 :project_change => '1',
1727 :project_change => '1',
1716 :issue => {:project_id => 2,
1728 :issue => {:project_id => 2,
1717 :tracker_id => 2,
1729 :tracker_id => 2,
1718 :subject => 'This is the test_new issue',
1730 :subject => 'This is the test_new issue',
1719 :description => 'This is the description',
1731 :description => 'This is the description',
1720 :priority_id => 5}
1732 :priority_id => 5}
1721 assert_response :success
1733 assert_response :success
1722 assert_template 'form'
1734 assert_template 'form'
1723
1735
1724 issue = assigns(:issue)
1736 issue = assigns(:issue)
1725 assert_kind_of Issue, issue
1737 assert_kind_of Issue, issue
1726 assert_equal 1, issue.id
1738 assert_equal 1, issue.id
1727 assert_equal 2, issue.project_id
1739 assert_equal 2, issue.project_id
1728 assert_equal 2, issue.tracker_id
1740 assert_equal 2, issue.tracker_id
1729 assert_equal 'This is the test_new issue', issue.subject
1741 assert_equal 'This is the test_new issue', issue.subject
1730 end
1742 end
1731
1743
1732 def test_update_using_invalid_http_verbs
1744 def test_update_using_invalid_http_verbs
1733 @request.session[:user_id] = 2
1745 @request.session[:user_id] = 2
1734 subject = 'Updated by an invalid http verb'
1746 subject = 'Updated by an invalid http verb'
1735
1747
1736 get :update, :id => 1, :issue => {:subject => subject}
1748 get :update, :id => 1, :issue => {:subject => subject}
1737 assert_not_equal subject, Issue.find(1).subject
1749 assert_not_equal subject, Issue.find(1).subject
1738
1750
1739 post :update, :id => 1, :issue => {:subject => subject}
1751 post :update, :id => 1, :issue => {:subject => subject}
1740 assert_not_equal subject, Issue.find(1).subject
1752 assert_not_equal subject, Issue.find(1).subject
1741
1753
1742 delete :update, :id => 1, :issue => {:subject => subject}
1754 delete :update, :id => 1, :issue => {:subject => subject}
1743 assert_not_equal subject, Issue.find(1).subject
1755 assert_not_equal subject, Issue.find(1).subject
1744 end
1756 end
1745
1757
1746 def test_put_update_without_custom_fields_param
1758 def test_put_update_without_custom_fields_param
1747 @request.session[:user_id] = 2
1759 @request.session[:user_id] = 2
1748 ActionMailer::Base.deliveries.clear
1760 ActionMailer::Base.deliveries.clear
1749
1761
1750 issue = Issue.find(1)
1762 issue = Issue.find(1)
1751 assert_equal '125', issue.custom_value_for(2).value
1763 assert_equal '125', issue.custom_value_for(2).value
1752 old_subject = issue.subject
1764 old_subject = issue.subject
1753 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
1765 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
1754
1766
1755 assert_difference('Journal.count') do
1767 assert_difference('Journal.count') do
1756 assert_difference('JournalDetail.count', 2) do
1768 assert_difference('JournalDetail.count', 2) do
1757 put :update, :id => 1, :issue => {:subject => new_subject,
1769 put :update, :id => 1, :issue => {:subject => new_subject,
1758 :priority_id => '6',
1770 :priority_id => '6',
1759 :category_id => '1' # no change
1771 :category_id => '1' # no change
1760 }
1772 }
1761 end
1773 end
1762 end
1774 end
1763 assert_redirected_to :action => 'show', :id => '1'
1775 assert_redirected_to :action => 'show', :id => '1'
1764 issue.reload
1776 issue.reload
1765 assert_equal new_subject, issue.subject
1777 assert_equal new_subject, issue.subject
1766 # Make sure custom fields were not cleared
1778 # Make sure custom fields were not cleared
1767 assert_equal '125', issue.custom_value_for(2).value
1779 assert_equal '125', issue.custom_value_for(2).value
1768
1780
1769 mail = ActionMailer::Base.deliveries.last
1781 mail = ActionMailer::Base.deliveries.last
1770 assert_kind_of TMail::Mail, mail
1782 assert_kind_of TMail::Mail, mail
1771 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1783 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1772 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
1784 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
1773 end
1785 end
1774
1786
1775 def test_put_update_with_project_change
1787 def test_put_update_with_project_change
1776 @request.session[:user_id] = 2
1788 @request.session[:user_id] = 2
1777 ActionMailer::Base.deliveries.clear
1789 ActionMailer::Base.deliveries.clear
1778
1790
1779 assert_difference('Journal.count') do
1791 assert_difference('Journal.count') do
1780 assert_difference('JournalDetail.count', 3) do
1792 assert_difference('JournalDetail.count', 3) do
1781 put :update, :id => 1, :issue => {:project_id => '2',
1793 put :update, :id => 1, :issue => {:project_id => '2',
1782 :tracker_id => '1', # no change
1794 :tracker_id => '1', # no change
1783 :priority_id => '6',
1795 :priority_id => '6',
1784 :category_id => '3'
1796 :category_id => '3'
1785 }
1797 }
1786 end
1798 end
1787 end
1799 end
1788 assert_redirected_to :action => 'show', :id => '1'
1800 assert_redirected_to :action => 'show', :id => '1'
1789 issue = Issue.find(1)
1801 issue = Issue.find(1)
1790 assert_equal 2, issue.project_id
1802 assert_equal 2, issue.project_id
1791 assert_equal 1, issue.tracker_id
1803 assert_equal 1, issue.tracker_id
1792 assert_equal 6, issue.priority_id
1804 assert_equal 6, issue.priority_id
1793 assert_equal 3, issue.category_id
1805 assert_equal 3, issue.category_id
1794
1806
1795 mail = ActionMailer::Base.deliveries.last
1807 mail = ActionMailer::Base.deliveries.last
1796 assert_not_nil mail
1808 assert_not_nil mail
1797 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1809 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1798 assert mail.body.include?("Project changed from eCookbook to OnlineStore")
1810 assert mail.body.include?("Project changed from eCookbook to OnlineStore")
1799 end
1811 end
1800
1812
1801 def test_put_update_with_tracker_change
1813 def test_put_update_with_tracker_change
1802 @request.session[:user_id] = 2
1814 @request.session[:user_id] = 2
1803 ActionMailer::Base.deliveries.clear
1815 ActionMailer::Base.deliveries.clear
1804
1816
1805 assert_difference('Journal.count') do
1817 assert_difference('Journal.count') do
1806 assert_difference('JournalDetail.count', 2) do
1818 assert_difference('JournalDetail.count', 2) do
1807 put :update, :id => 1, :issue => {:project_id => '1',
1819 put :update, :id => 1, :issue => {:project_id => '1',
1808 :tracker_id => '2',
1820 :tracker_id => '2',
1809 :priority_id => '6'
1821 :priority_id => '6'
1810 }
1822 }
1811 end
1823 end
1812 end
1824 end
1813 assert_redirected_to :action => 'show', :id => '1'
1825 assert_redirected_to :action => 'show', :id => '1'
1814 issue = Issue.find(1)
1826 issue = Issue.find(1)
1815 assert_equal 1, issue.project_id
1827 assert_equal 1, issue.project_id
1816 assert_equal 2, issue.tracker_id
1828 assert_equal 2, issue.tracker_id
1817 assert_equal 6, issue.priority_id
1829 assert_equal 6, issue.priority_id
1818 assert_equal 1, issue.category_id
1830 assert_equal 1, issue.category_id
1819
1831
1820 mail = ActionMailer::Base.deliveries.last
1832 mail = ActionMailer::Base.deliveries.last
1821 assert_not_nil mail
1833 assert_not_nil mail
1822 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1834 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1823 assert mail.body.include?("Tracker changed from Bug to Feature request")
1835 assert mail.body.include?("Tracker changed from Bug to Feature request")
1824 end
1836 end
1825
1837
1826 def test_put_update_with_custom_field_change
1838 def test_put_update_with_custom_field_change
1827 @request.session[:user_id] = 2
1839 @request.session[:user_id] = 2
1828 issue = Issue.find(1)
1840 issue = Issue.find(1)
1829 assert_equal '125', issue.custom_value_for(2).value
1841 assert_equal '125', issue.custom_value_for(2).value
1830
1842
1831 assert_difference('Journal.count') do
1843 assert_difference('Journal.count') do
1832 assert_difference('JournalDetail.count', 3) do
1844 assert_difference('JournalDetail.count', 3) do
1833 put :update, :id => 1, :issue => {:subject => 'Custom field change',
1845 put :update, :id => 1, :issue => {:subject => 'Custom field change',
1834 :priority_id => '6',
1846 :priority_id => '6',
1835 :category_id => '1', # no change
1847 :category_id => '1', # no change
1836 :custom_field_values => { '2' => 'New custom value' }
1848 :custom_field_values => { '2' => 'New custom value' }
1837 }
1849 }
1838 end
1850 end
1839 end
1851 end
1840 assert_redirected_to :action => 'show', :id => '1'
1852 assert_redirected_to :action => 'show', :id => '1'
1841 issue.reload
1853 issue.reload
1842 assert_equal 'New custom value', issue.custom_value_for(2).value
1854 assert_equal 'New custom value', issue.custom_value_for(2).value
1843
1855
1844 mail = ActionMailer::Base.deliveries.last
1856 mail = ActionMailer::Base.deliveries.last
1845 assert_kind_of TMail::Mail, mail
1857 assert_kind_of TMail::Mail, mail
1846 assert mail.body.include?("Searchable field changed from 125 to New custom value")
1858 assert mail.body.include?("Searchable field changed from 125 to New custom value")
1847 end
1859 end
1848
1860
1849 def test_put_update_with_status_and_assignee_change
1861 def test_put_update_with_status_and_assignee_change
1850 issue = Issue.find(1)
1862 issue = Issue.find(1)
1851 assert_equal 1, issue.status_id
1863 assert_equal 1, issue.status_id
1852 @request.session[:user_id] = 2
1864 @request.session[:user_id] = 2
1853 assert_difference('TimeEntry.count', 0) do
1865 assert_difference('TimeEntry.count', 0) do
1854 put :update,
1866 put :update,
1855 :id => 1,
1867 :id => 1,
1856 :issue => { :status_id => 2, :assigned_to_id => 3 },
1868 :issue => { :status_id => 2, :assigned_to_id => 3 },
1857 :notes => 'Assigned to dlopper',
1869 :notes => 'Assigned to dlopper',
1858 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
1870 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
1859 end
1871 end
1860 assert_redirected_to :action => 'show', :id => '1'
1872 assert_redirected_to :action => 'show', :id => '1'
1861 issue.reload
1873 issue.reload
1862 assert_equal 2, issue.status_id
1874 assert_equal 2, issue.status_id
1863 j = Journal.find(:first, :order => 'id DESC')
1875 j = Journal.find(:first, :order => 'id DESC')
1864 assert_equal 'Assigned to dlopper', j.notes
1876 assert_equal 'Assigned to dlopper', j.notes
1865 assert_equal 2, j.details.size
1877 assert_equal 2, j.details.size
1866
1878
1867 mail = ActionMailer::Base.deliveries.last
1879 mail = ActionMailer::Base.deliveries.last
1868 assert mail.body.include?("Status changed from New to Assigned")
1880 assert mail.body.include?("Status changed from New to Assigned")
1869 # subject should contain the new status
1881 # subject should contain the new status
1870 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
1882 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
1871 end
1883 end
1872
1884
1873 def test_put_update_with_note_only
1885 def test_put_update_with_note_only
1874 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
1886 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
1875 # anonymous user
1887 # anonymous user
1876 put :update,
1888 put :update,
1877 :id => 1,
1889 :id => 1,
1878 :notes => notes
1890 :notes => notes
1879 assert_redirected_to :action => 'show', :id => '1'
1891 assert_redirected_to :action => 'show', :id => '1'
1880 j = Journal.find(:first, :order => 'id DESC')
1892 j = Journal.find(:first, :order => 'id DESC')
1881 assert_equal notes, j.notes
1893 assert_equal notes, j.notes
1882 assert_equal 0, j.details.size
1894 assert_equal 0, j.details.size
1883 assert_equal User.anonymous, j.user
1895 assert_equal User.anonymous, j.user
1884
1896
1885 mail = ActionMailer::Base.deliveries.last
1897 mail = ActionMailer::Base.deliveries.last
1886 assert mail.body.include?(notes)
1898 assert mail.body.include?(notes)
1887 end
1899 end
1888
1900
1889 def test_put_update_with_note_and_spent_time
1901 def test_put_update_with_note_and_spent_time
1890 @request.session[:user_id] = 2
1902 @request.session[:user_id] = 2
1891 spent_hours_before = Issue.find(1).spent_hours
1903 spent_hours_before = Issue.find(1).spent_hours
1892 assert_difference('TimeEntry.count') do
1904 assert_difference('TimeEntry.count') do
1893 put :update,
1905 put :update,
1894 :id => 1,
1906 :id => 1,
1895 :notes => '2.5 hours added',
1907 :notes => '2.5 hours added',
1896 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
1908 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
1897 end
1909 end
1898 assert_redirected_to :action => 'show', :id => '1'
1910 assert_redirected_to :action => 'show', :id => '1'
1899
1911
1900 issue = Issue.find(1)
1912 issue = Issue.find(1)
1901
1913
1902 j = Journal.find(:first, :order => 'id DESC')
1914 j = Journal.find(:first, :order => 'id DESC')
1903 assert_equal '2.5 hours added', j.notes
1915 assert_equal '2.5 hours added', j.notes
1904 assert_equal 0, j.details.size
1916 assert_equal 0, j.details.size
1905
1917
1906 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
1918 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
1907 assert_not_nil t
1919 assert_not_nil t
1908 assert_equal 2.5, t.hours
1920 assert_equal 2.5, t.hours
1909 assert_equal spent_hours_before + 2.5, issue.spent_hours
1921 assert_equal spent_hours_before + 2.5, issue.spent_hours
1910 end
1922 end
1911
1923
1912 def test_put_update_with_attachment_only
1924 def test_put_update_with_attachment_only
1913 set_tmp_attachments_directory
1925 set_tmp_attachments_directory
1914
1926
1915 # Delete all fixtured journals, a race condition can occur causing the wrong
1927 # Delete all fixtured journals, a race condition can occur causing the wrong
1916 # journal to get fetched in the next find.
1928 # journal to get fetched in the next find.
1917 Journal.delete_all
1929 Journal.delete_all
1918
1930
1919 # anonymous user
1931 # anonymous user
1920 assert_difference 'Attachment.count' do
1932 assert_difference 'Attachment.count' do
1921 put :update, :id => 1,
1933 put :update, :id => 1,
1922 :notes => '',
1934 :notes => '',
1923 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1935 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1924 end
1936 end
1925
1937
1926 assert_redirected_to :action => 'show', :id => '1'
1938 assert_redirected_to :action => 'show', :id => '1'
1927 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
1939 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
1928 assert j.notes.blank?
1940 assert j.notes.blank?
1929 assert_equal 1, j.details.size
1941 assert_equal 1, j.details.size
1930 assert_equal 'testfile.txt', j.details.first.value
1942 assert_equal 'testfile.txt', j.details.first.value
1931 assert_equal User.anonymous, j.user
1943 assert_equal User.anonymous, j.user
1932
1944
1933 attachment = Attachment.first(:order => 'id DESC')
1945 attachment = Attachment.first(:order => 'id DESC')
1934 assert_equal Issue.find(1), attachment.container
1946 assert_equal Issue.find(1), attachment.container
1935 assert_equal User.anonymous, attachment.author
1947 assert_equal User.anonymous, attachment.author
1936 assert_equal 'testfile.txt', attachment.filename
1948 assert_equal 'testfile.txt', attachment.filename
1937 assert_equal 'text/plain', attachment.content_type
1949 assert_equal 'text/plain', attachment.content_type
1938 assert_equal 'test file', attachment.description
1950 assert_equal 'test file', attachment.description
1939 assert_equal 59, attachment.filesize
1951 assert_equal 59, attachment.filesize
1940 assert File.exists?(attachment.diskfile)
1952 assert File.exists?(attachment.diskfile)
1941 assert_equal 59, File.size(attachment.diskfile)
1953 assert_equal 59, File.size(attachment.diskfile)
1942
1954
1943 mail = ActionMailer::Base.deliveries.last
1955 mail = ActionMailer::Base.deliveries.last
1944 assert mail.body.include?('testfile.txt')
1956 assert mail.body.include?('testfile.txt')
1945 end
1957 end
1946
1958
1947 def test_put_update_with_attachment_that_fails_to_save
1959 def test_put_update_with_attachment_that_fails_to_save
1948 set_tmp_attachments_directory
1960 set_tmp_attachments_directory
1949
1961
1950 # Delete all fixtured journals, a race condition can occur causing the wrong
1962 # Delete all fixtured journals, a race condition can occur causing the wrong
1951 # journal to get fetched in the next find.
1963 # journal to get fetched in the next find.
1952 Journal.delete_all
1964 Journal.delete_all
1953
1965
1954 # Mock out the unsaved attachment
1966 # Mock out the unsaved attachment
1955 Attachment.any_instance.stubs(:create).returns(Attachment.new)
1967 Attachment.any_instance.stubs(:create).returns(Attachment.new)
1956
1968
1957 # anonymous user
1969 # anonymous user
1958 put :update,
1970 put :update,
1959 :id => 1,
1971 :id => 1,
1960 :notes => '',
1972 :notes => '',
1961 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
1973 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
1962 assert_redirected_to :action => 'show', :id => '1'
1974 assert_redirected_to :action => 'show', :id => '1'
1963 assert_equal '1 file(s) could not be saved.', flash[:warning]
1975 assert_equal '1 file(s) could not be saved.', flash[:warning]
1964
1976
1965 end if Object.const_defined?(:Mocha)
1977 end if Object.const_defined?(:Mocha)
1966
1978
1967 def test_put_update_with_no_change
1979 def test_put_update_with_no_change
1968 issue = Issue.find(1)
1980 issue = Issue.find(1)
1969 issue.journals.clear
1981 issue.journals.clear
1970 ActionMailer::Base.deliveries.clear
1982 ActionMailer::Base.deliveries.clear
1971
1983
1972 put :update,
1984 put :update,
1973 :id => 1,
1985 :id => 1,
1974 :notes => ''
1986 :notes => ''
1975 assert_redirected_to :action => 'show', :id => '1'
1987 assert_redirected_to :action => 'show', :id => '1'
1976
1988
1977 issue.reload
1989 issue.reload
1978 assert issue.journals.empty?
1990 assert issue.journals.empty?
1979 # No email should be sent
1991 # No email should be sent
1980 assert ActionMailer::Base.deliveries.empty?
1992 assert ActionMailer::Base.deliveries.empty?
1981 end
1993 end
1982
1994
1983 def test_put_update_should_send_a_notification
1995 def test_put_update_should_send_a_notification
1984 @request.session[:user_id] = 2
1996 @request.session[:user_id] = 2
1985 ActionMailer::Base.deliveries.clear
1997 ActionMailer::Base.deliveries.clear
1986 issue = Issue.find(1)
1998 issue = Issue.find(1)
1987 old_subject = issue.subject
1999 old_subject = issue.subject
1988 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
2000 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
1989
2001
1990 put :update, :id => 1, :issue => {:subject => new_subject,
2002 put :update, :id => 1, :issue => {:subject => new_subject,
1991 :priority_id => '6',
2003 :priority_id => '6',
1992 :category_id => '1' # no change
2004 :category_id => '1' # no change
1993 }
2005 }
1994 assert_equal 1, ActionMailer::Base.deliveries.size
2006 assert_equal 1, ActionMailer::Base.deliveries.size
1995 end
2007 end
1996
2008
1997 def test_put_update_with_invalid_spent_time_hours_only
2009 def test_put_update_with_invalid_spent_time_hours_only
1998 @request.session[:user_id] = 2
2010 @request.session[:user_id] = 2
1999 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2011 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2000
2012
2001 assert_no_difference('Journal.count') do
2013 assert_no_difference('Journal.count') do
2002 put :update,
2014 put :update,
2003 :id => 1,
2015 :id => 1,
2004 :notes => notes,
2016 :notes => notes,
2005 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
2017 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
2006 end
2018 end
2007 assert_response :success
2019 assert_response :success
2008 assert_template 'edit'
2020 assert_template 'edit'
2009
2021
2010 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2022 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2011 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2023 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2012 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
2024 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
2013 end
2025 end
2014
2026
2015 def test_put_update_with_invalid_spent_time_comments_only
2027 def test_put_update_with_invalid_spent_time_comments_only
2016 @request.session[:user_id] = 2
2028 @request.session[:user_id] = 2
2017 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2029 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2018
2030
2019 assert_no_difference('Journal.count') do
2031 assert_no_difference('Journal.count') do
2020 put :update,
2032 put :update,
2021 :id => 1,
2033 :id => 1,
2022 :notes => notes,
2034 :notes => notes,
2023 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
2035 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
2024 end
2036 end
2025 assert_response :success
2037 assert_response :success
2026 assert_template 'edit'
2038 assert_template 'edit'
2027
2039
2028 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2040 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2029 assert_error_tag :descendant => {:content => /Hours can't be blank/}
2041 assert_error_tag :descendant => {:content => /Hours can't be blank/}
2030 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2042 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2031 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
2043 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
2032 end
2044 end
2033
2045
2034 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
2046 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
2035 issue = Issue.find(2)
2047 issue = Issue.find(2)
2036 @request.session[:user_id] = 2
2048 @request.session[:user_id] = 2
2037
2049
2038 put :update,
2050 put :update,
2039 :id => issue.id,
2051 :id => issue.id,
2040 :issue => {
2052 :issue => {
2041 :fixed_version_id => 4
2053 :fixed_version_id => 4
2042 }
2054 }
2043
2055
2044 assert_response :redirect
2056 assert_response :redirect
2045 issue.reload
2057 issue.reload
2046 assert_equal 4, issue.fixed_version_id
2058 assert_equal 4, issue.fixed_version_id
2047 assert_not_equal issue.project_id, issue.fixed_version.project_id
2059 assert_not_equal issue.project_id, issue.fixed_version.project_id
2048 end
2060 end
2049
2061
2050 def test_put_update_should_redirect_back_using_the_back_url_parameter
2062 def test_put_update_should_redirect_back_using_the_back_url_parameter
2051 issue = Issue.find(2)
2063 issue = Issue.find(2)
2052 @request.session[:user_id] = 2
2064 @request.session[:user_id] = 2
2053
2065
2054 put :update,
2066 put :update,
2055 :id => issue.id,
2067 :id => issue.id,
2056 :issue => {
2068 :issue => {
2057 :fixed_version_id => 4
2069 :fixed_version_id => 4
2058 },
2070 },
2059 :back_url => '/issues'
2071 :back_url => '/issues'
2060
2072
2061 assert_response :redirect
2073 assert_response :redirect
2062 assert_redirected_to '/issues'
2074 assert_redirected_to '/issues'
2063 end
2075 end
2064
2076
2065 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2077 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2066 issue = Issue.find(2)
2078 issue = Issue.find(2)
2067 @request.session[:user_id] = 2
2079 @request.session[:user_id] = 2
2068
2080
2069 put :update,
2081 put :update,
2070 :id => issue.id,
2082 :id => issue.id,
2071 :issue => {
2083 :issue => {
2072 :fixed_version_id => 4
2084 :fixed_version_id => 4
2073 },
2085 },
2074 :back_url => 'http://google.com'
2086 :back_url => 'http://google.com'
2075
2087
2076 assert_response :redirect
2088 assert_response :redirect
2077 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
2089 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
2078 end
2090 end
2079
2091
2080 def test_get_bulk_edit
2092 def test_get_bulk_edit
2081 @request.session[:user_id] = 2
2093 @request.session[:user_id] = 2
2082 get :bulk_edit, :ids => [1, 2]
2094 get :bulk_edit, :ids => [1, 2]
2083 assert_response :success
2095 assert_response :success
2084 assert_template 'bulk_edit'
2096 assert_template 'bulk_edit'
2085
2097
2086 assert_tag :select, :attributes => {:name => 'issue[project_id]'}
2098 assert_tag :select, :attributes => {:name => 'issue[project_id]'}
2087 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2099 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2088
2100
2089 # Project specific custom field, date type
2101 # Project specific custom field, date type
2090 field = CustomField.find(9)
2102 field = CustomField.find(9)
2091 assert !field.is_for_all?
2103 assert !field.is_for_all?
2092 assert_equal 'date', field.field_format
2104 assert_equal 'date', field.field_format
2093 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2105 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2094
2106
2095 # System wide custom field
2107 # System wide custom field
2096 assert CustomField.find(1).is_for_all?
2108 assert CustomField.find(1).is_for_all?
2097 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
2109 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
2098
2110
2099 # Be sure we don't display inactive IssuePriorities
2111 # Be sure we don't display inactive IssuePriorities
2100 assert ! IssuePriority.find(15).active?
2112 assert ! IssuePriority.find(15).active?
2101 assert_no_tag :option, :attributes => {:value => '15'},
2113 assert_no_tag :option, :attributes => {:value => '15'},
2102 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
2114 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
2103 end
2115 end
2104
2116
2105 def test_get_bulk_edit_on_different_projects
2117 def test_get_bulk_edit_on_different_projects
2106 @request.session[:user_id] = 2
2118 @request.session[:user_id] = 2
2107 get :bulk_edit, :ids => [1, 2, 6]
2119 get :bulk_edit, :ids => [1, 2, 6]
2108 assert_response :success
2120 assert_response :success
2109 assert_template 'bulk_edit'
2121 assert_template 'bulk_edit'
2110
2122
2111 # Can not set issues from different projects as children of an issue
2123 # Can not set issues from different projects as children of an issue
2112 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2124 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2113
2125
2114 # Project specific custom field, date type
2126 # Project specific custom field, date type
2115 field = CustomField.find(9)
2127 field = CustomField.find(9)
2116 assert !field.is_for_all?
2128 assert !field.is_for_all?
2117 assert !field.project_ids.include?(Issue.find(6).project_id)
2129 assert !field.project_ids.include?(Issue.find(6).project_id)
2118 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2130 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2119 end
2131 end
2120
2132
2121 def test_get_bulk_edit_with_user_custom_field
2133 def test_get_bulk_edit_with_user_custom_field
2122 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
2134 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
2123
2135
2124 @request.session[:user_id] = 2
2136 @request.session[:user_id] = 2
2125 get :bulk_edit, :ids => [1, 2]
2137 get :bulk_edit, :ids => [1, 2]
2126 assert_response :success
2138 assert_response :success
2127 assert_template 'bulk_edit'
2139 assert_template 'bulk_edit'
2128
2140
2129 assert_tag :select,
2141 assert_tag :select,
2130 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2142 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2131 :children => {
2143 :children => {
2132 :only => {:tag => 'option'},
2144 :only => {:tag => 'option'},
2133 :count => Project.find(1).users.count + 1
2145 :count => Project.find(1).users.count + 1
2134 }
2146 }
2135 end
2147 end
2136
2148
2137 def test_get_bulk_edit_with_version_custom_field
2149 def test_get_bulk_edit_with_version_custom_field
2138 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
2150 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
2139
2151
2140 @request.session[:user_id] = 2
2152 @request.session[:user_id] = 2
2141 get :bulk_edit, :ids => [1, 2]
2153 get :bulk_edit, :ids => [1, 2]
2142 assert_response :success
2154 assert_response :success
2143 assert_template 'bulk_edit'
2155 assert_template 'bulk_edit'
2144
2156
2145 assert_tag :select,
2157 assert_tag :select,
2146 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2158 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2147 :children => {
2159 :children => {
2148 :only => {:tag => 'option'},
2160 :only => {:tag => 'option'},
2149 :count => Project.find(1).shared_versions.count + 1
2161 :count => Project.find(1).shared_versions.count + 1
2150 }
2162 }
2151 end
2163 end
2152
2164
2153 def test_bulk_update
2165 def test_bulk_update
2154 @request.session[:user_id] = 2
2166 @request.session[:user_id] = 2
2155 # update issues priority
2167 # update issues priority
2156 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2168 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2157 :issue => {:priority_id => 7,
2169 :issue => {:priority_id => 7,
2158 :assigned_to_id => '',
2170 :assigned_to_id => '',
2159 :custom_field_values => {'2' => ''}}
2171 :custom_field_values => {'2' => ''}}
2160
2172
2161 assert_response 302
2173 assert_response 302
2162 # check that the issues were updated
2174 # check that the issues were updated
2163 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
2175 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
2164
2176
2165 issue = Issue.find(1)
2177 issue = Issue.find(1)
2166 journal = issue.journals.find(:first, :order => 'created_on DESC')
2178 journal = issue.journals.find(:first, :order => 'created_on DESC')
2167 assert_equal '125', issue.custom_value_for(2).value
2179 assert_equal '125', issue.custom_value_for(2).value
2168 assert_equal 'Bulk editing', journal.notes
2180 assert_equal 'Bulk editing', journal.notes
2169 assert_equal 1, journal.details.size
2181 assert_equal 1, journal.details.size
2170 end
2182 end
2171
2183
2172 def test_bulk_update_with_group_assignee
2184 def test_bulk_update_with_group_assignee
2173 group = Group.find(11)
2185 group = Group.find(11)
2174 project = Project.find(1)
2186 project = Project.find(1)
2175 project.members << Member.new(:principal => group, :roles => [Role.first])
2187 project.members << Member.new(:principal => group, :roles => [Role.first])
2176
2188
2177 @request.session[:user_id] = 2
2189 @request.session[:user_id] = 2
2178 # update issues assignee
2190 # update issues assignee
2179 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2191 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2180 :issue => {:priority_id => '',
2192 :issue => {:priority_id => '',
2181 :assigned_to_id => group.id,
2193 :assigned_to_id => group.id,
2182 :custom_field_values => {'2' => ''}}
2194 :custom_field_values => {'2' => ''}}
2183
2195
2184 assert_response 302
2196 assert_response 302
2185 assert_equal [group, group], Issue.find_all_by_id([1, 2]).collect {|i| i.assigned_to}
2197 assert_equal [group, group], Issue.find_all_by_id([1, 2]).collect {|i| i.assigned_to}
2186 end
2198 end
2187
2199
2188 def test_bulk_update_on_different_projects
2200 def test_bulk_update_on_different_projects
2189 @request.session[:user_id] = 2
2201 @request.session[:user_id] = 2
2190 # update issues priority
2202 # update issues priority
2191 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
2203 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
2192 :issue => {:priority_id => 7,
2204 :issue => {:priority_id => 7,
2193 :assigned_to_id => '',
2205 :assigned_to_id => '',
2194 :custom_field_values => {'2' => ''}}
2206 :custom_field_values => {'2' => ''}}
2195
2207
2196 assert_response 302
2208 assert_response 302
2197 # check that the issues were updated
2209 # check that the issues were updated
2198 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
2210 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
2199
2211
2200 issue = Issue.find(1)
2212 issue = Issue.find(1)
2201 journal = issue.journals.find(:first, :order => 'created_on DESC')
2213 journal = issue.journals.find(:first, :order => 'created_on DESC')
2202 assert_equal '125', issue.custom_value_for(2).value
2214 assert_equal '125', issue.custom_value_for(2).value
2203 assert_equal 'Bulk editing', journal.notes
2215 assert_equal 'Bulk editing', journal.notes
2204 assert_equal 1, journal.details.size
2216 assert_equal 1, journal.details.size
2205 end
2217 end
2206
2218
2207 def test_bulk_update_on_different_projects_without_rights
2219 def test_bulk_update_on_different_projects_without_rights
2208 @request.session[:user_id] = 3
2220 @request.session[:user_id] = 3
2209 user = User.find(3)
2221 user = User.find(3)
2210 action = { :controller => "issues", :action => "bulk_update" }
2222 action = { :controller => "issues", :action => "bulk_update" }
2211 assert user.allowed_to?(action, Issue.find(1).project)
2223 assert user.allowed_to?(action, Issue.find(1).project)
2212 assert ! user.allowed_to?(action, Issue.find(6).project)
2224 assert ! user.allowed_to?(action, Issue.find(6).project)
2213 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
2225 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
2214 :issue => {:priority_id => 7,
2226 :issue => {:priority_id => 7,
2215 :assigned_to_id => '',
2227 :assigned_to_id => '',
2216 :custom_field_values => {'2' => ''}}
2228 :custom_field_values => {'2' => ''}}
2217 assert_response 403
2229 assert_response 403
2218 assert_not_equal "Bulk should fail", Journal.last.notes
2230 assert_not_equal "Bulk should fail", Journal.last.notes
2219 end
2231 end
2220
2232
2221 def test_bullk_update_should_send_a_notification
2233 def test_bullk_update_should_send_a_notification
2222 @request.session[:user_id] = 2
2234 @request.session[:user_id] = 2
2223 ActionMailer::Base.deliveries.clear
2235 ActionMailer::Base.deliveries.clear
2224 post(:bulk_update,
2236 post(:bulk_update,
2225 {
2237 {
2226 :ids => [1, 2],
2238 :ids => [1, 2],
2227 :notes => 'Bulk editing',
2239 :notes => 'Bulk editing',
2228 :issue => {
2240 :issue => {
2229 :priority_id => 7,
2241 :priority_id => 7,
2230 :assigned_to_id => '',
2242 :assigned_to_id => '',
2231 :custom_field_values => {'2' => ''}
2243 :custom_field_values => {'2' => ''}
2232 }
2244 }
2233 })
2245 })
2234
2246
2235 assert_response 302
2247 assert_response 302
2236 assert_equal 2, ActionMailer::Base.deliveries.size
2248 assert_equal 2, ActionMailer::Base.deliveries.size
2237 end
2249 end
2238
2250
2239 def test_bulk_update_project
2251 def test_bulk_update_project
2240 @request.session[:user_id] = 2
2252 @request.session[:user_id] = 2
2241 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
2253 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
2242 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2254 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2243 # Issues moved to project 2
2255 # Issues moved to project 2
2244 assert_equal 2, Issue.find(1).project_id
2256 assert_equal 2, Issue.find(1).project_id
2245 assert_equal 2, Issue.find(2).project_id
2257 assert_equal 2, Issue.find(2).project_id
2246 # No tracker change
2258 # No tracker change
2247 assert_equal 1, Issue.find(1).tracker_id
2259 assert_equal 1, Issue.find(1).tracker_id
2248 assert_equal 2, Issue.find(2).tracker_id
2260 assert_equal 2, Issue.find(2).tracker_id
2249 end
2261 end
2250
2262
2251 def test_bulk_update_project_on_single_issue_should_follow_when_needed
2263 def test_bulk_update_project_on_single_issue_should_follow_when_needed
2252 @request.session[:user_id] = 2
2264 @request.session[:user_id] = 2
2253 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
2265 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
2254 assert_redirected_to '/issues/1'
2266 assert_redirected_to '/issues/1'
2255 end
2267 end
2256
2268
2257 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
2269 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
2258 @request.session[:user_id] = 2
2270 @request.session[:user_id] = 2
2259 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
2271 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
2260 assert_redirected_to '/projects/onlinestore/issues'
2272 assert_redirected_to '/projects/onlinestore/issues'
2261 end
2273 end
2262
2274
2263 def test_bulk_update_tracker
2275 def test_bulk_update_tracker
2264 @request.session[:user_id] = 2
2276 @request.session[:user_id] = 2
2265 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
2277 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
2266 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2278 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2267 assert_equal 2, Issue.find(1).tracker_id
2279 assert_equal 2, Issue.find(1).tracker_id
2268 assert_equal 2, Issue.find(2).tracker_id
2280 assert_equal 2, Issue.find(2).tracker_id
2269 end
2281 end
2270
2282
2271 def test_bulk_update_status
2283 def test_bulk_update_status
2272 @request.session[:user_id] = 2
2284 @request.session[:user_id] = 2
2273 # update issues priority
2285 # update issues priority
2274 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
2286 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
2275 :issue => {:priority_id => '',
2287 :issue => {:priority_id => '',
2276 :assigned_to_id => '',
2288 :assigned_to_id => '',
2277 :status_id => '5'}
2289 :status_id => '5'}
2278
2290
2279 assert_response 302
2291 assert_response 302
2280 issue = Issue.find(1)
2292 issue = Issue.find(1)
2281 assert issue.closed?
2293 assert issue.closed?
2282 end
2294 end
2283
2295
2284 def test_bulk_update_priority
2296 def test_bulk_update_priority
2285 @request.session[:user_id] = 2
2297 @request.session[:user_id] = 2
2286 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
2298 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
2287
2299
2288 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2300 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2289 assert_equal 6, Issue.find(1).priority_id
2301 assert_equal 6, Issue.find(1).priority_id
2290 assert_equal 6, Issue.find(2).priority_id
2302 assert_equal 6, Issue.find(2).priority_id
2291 end
2303 end
2292
2304
2293 def test_bulk_update_with_notes
2305 def test_bulk_update_with_notes
2294 @request.session[:user_id] = 2
2306 @request.session[:user_id] = 2
2295 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
2307 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
2296
2308
2297 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2309 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2298 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
2310 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
2299 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
2311 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
2300 end
2312 end
2301
2313
2302 def test_bulk_update_parent_id
2314 def test_bulk_update_parent_id
2303 @request.session[:user_id] = 2
2315 @request.session[:user_id] = 2
2304 post :bulk_update, :ids => [1, 3],
2316 post :bulk_update, :ids => [1, 3],
2305 :notes => 'Bulk editing parent',
2317 :notes => 'Bulk editing parent',
2306 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
2318 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
2307
2319
2308 assert_response 302
2320 assert_response 302
2309 parent = Issue.find(2)
2321 parent = Issue.find(2)
2310 assert_equal parent.id, Issue.find(1).parent_id
2322 assert_equal parent.id, Issue.find(1).parent_id
2311 assert_equal parent.id, Issue.find(3).parent_id
2323 assert_equal parent.id, Issue.find(3).parent_id
2312 assert_equal [1, 3], parent.children.collect(&:id).sort
2324 assert_equal [1, 3], parent.children.collect(&:id).sort
2313 end
2325 end
2314
2326
2315 def test_bulk_update_custom_field
2327 def test_bulk_update_custom_field
2316 @request.session[:user_id] = 2
2328 @request.session[:user_id] = 2
2317 # update issues priority
2329 # update issues priority
2318 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
2330 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
2319 :issue => {:priority_id => '',
2331 :issue => {:priority_id => '',
2320 :assigned_to_id => '',
2332 :assigned_to_id => '',
2321 :custom_field_values => {'2' => '777'}}
2333 :custom_field_values => {'2' => '777'}}
2322
2334
2323 assert_response 302
2335 assert_response 302
2324
2336
2325 issue = Issue.find(1)
2337 issue = Issue.find(1)
2326 journal = issue.journals.find(:first, :order => 'created_on DESC')
2338 journal = issue.journals.find(:first, :order => 'created_on DESC')
2327 assert_equal '777', issue.custom_value_for(2).value
2339 assert_equal '777', issue.custom_value_for(2).value
2328 assert_equal 1, journal.details.size
2340 assert_equal 1, journal.details.size
2329 assert_equal '125', journal.details.first.old_value
2341 assert_equal '125', journal.details.first.old_value
2330 assert_equal '777', journal.details.first.value
2342 assert_equal '777', journal.details.first.value
2331 end
2343 end
2332
2344
2333 def test_bulk_update_unassign
2345 def test_bulk_update_unassign
2334 assert_not_nil Issue.find(2).assigned_to
2346 assert_not_nil Issue.find(2).assigned_to
2335 @request.session[:user_id] = 2
2347 @request.session[:user_id] = 2
2336 # unassign issues
2348 # unassign issues
2337 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
2349 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
2338 assert_response 302
2350 assert_response 302
2339 # check that the issues were updated
2351 # check that the issues were updated
2340 assert_nil Issue.find(2).assigned_to
2352 assert_nil Issue.find(2).assigned_to
2341 end
2353 end
2342
2354
2343 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
2355 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
2344 @request.session[:user_id] = 2
2356 @request.session[:user_id] = 2
2345
2357
2346 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
2358 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
2347
2359
2348 assert_response :redirect
2360 assert_response :redirect
2349 issues = Issue.find([1,2])
2361 issues = Issue.find([1,2])
2350 issues.each do |issue|
2362 issues.each do |issue|
2351 assert_equal 4, issue.fixed_version_id
2363 assert_equal 4, issue.fixed_version_id
2352 assert_not_equal issue.project_id, issue.fixed_version.project_id
2364 assert_not_equal issue.project_id, issue.fixed_version.project_id
2353 end
2365 end
2354 end
2366 end
2355
2367
2356 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
2368 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
2357 @request.session[:user_id] = 2
2369 @request.session[:user_id] = 2
2358 post :bulk_update, :ids => [1,2], :back_url => '/issues'
2370 post :bulk_update, :ids => [1,2], :back_url => '/issues'
2359
2371
2360 assert_response :redirect
2372 assert_response :redirect
2361 assert_redirected_to '/issues'
2373 assert_redirected_to '/issues'
2362 end
2374 end
2363
2375
2364 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2376 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2365 @request.session[:user_id] = 2
2377 @request.session[:user_id] = 2
2366 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
2378 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
2367
2379
2368 assert_response :redirect
2380 assert_response :redirect
2369 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
2381 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
2370 end
2382 end
2371
2383
2372 def test_bulk_copy_to_another_project
2384 def test_bulk_copy_to_another_project
2373 @request.session[:user_id] = 2
2385 @request.session[:user_id] = 2
2374 assert_difference 'Issue.count', 2 do
2386 assert_difference 'Issue.count', 2 do
2375 assert_no_difference 'Project.find(1).issues.count' do
2387 assert_no_difference 'Project.find(1).issues.count' do
2376 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
2388 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
2377 end
2389 end
2378 end
2390 end
2379 assert_redirected_to '/projects/ecookbook/issues'
2391 assert_redirected_to '/projects/ecookbook/issues'
2380 end
2392 end
2381
2393
2382 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
2394 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
2383 @request.session[:user_id] = 2
2395 @request.session[:user_id] = 2
2384 issue_before_move = Issue.find(1)
2396 issue_before_move = Issue.find(1)
2385 assert_difference 'Issue.count', 1 do
2397 assert_difference 'Issue.count', 1 do
2386 assert_no_difference 'Project.find(1).issues.count' do
2398 assert_no_difference 'Project.find(1).issues.count' do
2387 post :bulk_update, :ids => [1], :copy => '1',
2399 post :bulk_update, :ids => [1], :copy => '1',
2388 :issue => {
2400 :issue => {
2389 :project_id => '2', :tracker_id => '', :assigned_to_id => '',
2401 :project_id => '2', :tracker_id => '', :assigned_to_id => '',
2390 :status_id => '', :start_date => '', :due_date => ''
2402 :status_id => '', :start_date => '', :due_date => ''
2391 }
2403 }
2392 end
2404 end
2393 end
2405 end
2394 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
2406 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
2395 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
2407 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
2396 assert_equal issue_before_move.status_id, issue_after_move.status_id
2408 assert_equal issue_before_move.status_id, issue_after_move.status_id
2397 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
2409 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
2398 end
2410 end
2399
2411
2400 def test_bulk_copy_should_allow_changing_the_issue_attributes
2412 def test_bulk_copy_should_allow_changing_the_issue_attributes
2401 # Fixes random test failure with Mysql
2413 # Fixes random test failure with Mysql
2402 # where Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2414 # where Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2403 # doesn't return the expected results
2415 # doesn't return the expected results
2404 Issue.delete_all("project_id=2")
2416 Issue.delete_all("project_id=2")
2405
2417
2406 @request.session[:user_id] = 2
2418 @request.session[:user_id] = 2
2407 assert_difference 'Issue.count', 2 do
2419 assert_difference 'Issue.count', 2 do
2408 assert_no_difference 'Project.find(1).issues.count' do
2420 assert_no_difference 'Project.find(1).issues.count' do
2409 post :bulk_update, :ids => [1, 2], :copy => '1',
2421 post :bulk_update, :ids => [1, 2], :copy => '1',
2410 :issue => {
2422 :issue => {
2411 :project_id => '2', :tracker_id => '', :assigned_to_id => '4',
2423 :project_id => '2', :tracker_id => '', :assigned_to_id => '4',
2412 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2424 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2413 }
2425 }
2414 end
2426 end
2415 end
2427 end
2416
2428
2417 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2429 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2418 assert_equal 2, copied_issues.size
2430 assert_equal 2, copied_issues.size
2419 copied_issues.each do |issue|
2431 copied_issues.each do |issue|
2420 assert_equal 2, issue.project_id, "Project is incorrect"
2432 assert_equal 2, issue.project_id, "Project is incorrect"
2421 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
2433 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
2422 assert_equal 3, issue.status_id, "Status is incorrect"
2434 assert_equal 3, issue.status_id, "Status is incorrect"
2423 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
2435 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
2424 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
2436 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
2425 end
2437 end
2426 end
2438 end
2427
2439
2428 def test_bulk_copy_should_allow_adding_a_note
2440 def test_bulk_copy_should_allow_adding_a_note
2429 @request.session[:user_id] = 2
2441 @request.session[:user_id] = 2
2430 assert_difference 'Issue.count', 1 do
2442 assert_difference 'Issue.count', 1 do
2431 post :bulk_update, :ids => [1], :copy => '1',
2443 post :bulk_update, :ids => [1], :copy => '1',
2432 :notes => 'Copying one issue',
2444 :notes => 'Copying one issue',
2433 :issue => {
2445 :issue => {
2434 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
2446 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
2435 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2447 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2436 }
2448 }
2437 end
2449 end
2438
2450
2439 issue = Issue.first(:order => 'id DESC')
2451 issue = Issue.first(:order => 'id DESC')
2440 assert_equal 1, issue.journals.size
2452 assert_equal 1, issue.journals.size
2441 journal = issue.journals.first
2453 journal = issue.journals.first
2442 assert_equal 0, journal.details.size
2454 assert_equal 0, journal.details.size
2443 assert_equal 'Copying one issue', journal.notes
2455 assert_equal 'Copying one issue', journal.notes
2444 end
2456 end
2445
2457
2446 def test_bulk_copy_to_another_project_should_follow_when_needed
2458 def test_bulk_copy_to_another_project_should_follow_when_needed
2447 @request.session[:user_id] = 2
2459 @request.session[:user_id] = 2
2448 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
2460 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
2449 issue = Issue.first(:order => 'id DESC')
2461 issue = Issue.first(:order => 'id DESC')
2450 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2462 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2451 end
2463 end
2452
2464
2453 def test_destroy_issue_with_no_time_entries
2465 def test_destroy_issue_with_no_time_entries
2454 assert_nil TimeEntry.find_by_issue_id(2)
2466 assert_nil TimeEntry.find_by_issue_id(2)
2455 @request.session[:user_id] = 2
2467 @request.session[:user_id] = 2
2456 delete :destroy, :id => 2
2468 delete :destroy, :id => 2
2457 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2469 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2458 assert_nil Issue.find_by_id(2)
2470 assert_nil Issue.find_by_id(2)
2459 end
2471 end
2460
2472
2461 def test_destroy_issues_with_time_entries
2473 def test_destroy_issues_with_time_entries
2462 @request.session[:user_id] = 2
2474 @request.session[:user_id] = 2
2463 delete :destroy, :ids => [1, 3]
2475 delete :destroy, :ids => [1, 3]
2464 assert_response :success
2476 assert_response :success
2465 assert_template 'destroy'
2477 assert_template 'destroy'
2466 assert_not_nil assigns(:hours)
2478 assert_not_nil assigns(:hours)
2467 assert Issue.find_by_id(1) && Issue.find_by_id(3)
2479 assert Issue.find_by_id(1) && Issue.find_by_id(3)
2468 end
2480 end
2469
2481
2470 def test_destroy_issues_and_destroy_time_entries
2482 def test_destroy_issues_and_destroy_time_entries
2471 @request.session[:user_id] = 2
2483 @request.session[:user_id] = 2
2472 delete :destroy, :ids => [1, 3], :todo => 'destroy'
2484 delete :destroy, :ids => [1, 3], :todo => 'destroy'
2473 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2485 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2474 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2486 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2475 assert_nil TimeEntry.find_by_id([1, 2])
2487 assert_nil TimeEntry.find_by_id([1, 2])
2476 end
2488 end
2477
2489
2478 def test_destroy_issues_and_assign_time_entries_to_project
2490 def test_destroy_issues_and_assign_time_entries_to_project
2479 @request.session[:user_id] = 2
2491 @request.session[:user_id] = 2
2480 delete :destroy, :ids => [1, 3], :todo => 'nullify'
2492 delete :destroy, :ids => [1, 3], :todo => 'nullify'
2481 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2493 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2482 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2494 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2483 assert_nil TimeEntry.find(1).issue_id
2495 assert_nil TimeEntry.find(1).issue_id
2484 assert_nil TimeEntry.find(2).issue_id
2496 assert_nil TimeEntry.find(2).issue_id
2485 end
2497 end
2486
2498
2487 def test_destroy_issues_and_reassign_time_entries_to_another_issue
2499 def test_destroy_issues_and_reassign_time_entries_to_another_issue
2488 @request.session[:user_id] = 2
2500 @request.session[:user_id] = 2
2489 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
2501 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
2490 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2502 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2491 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2503 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2492 assert_equal 2, TimeEntry.find(1).issue_id
2504 assert_equal 2, TimeEntry.find(1).issue_id
2493 assert_equal 2, TimeEntry.find(2).issue_id
2505 assert_equal 2, TimeEntry.find(2).issue_id
2494 end
2506 end
2495
2507
2496 def test_destroy_issues_from_different_projects
2508 def test_destroy_issues_from_different_projects
2497 @request.session[:user_id] = 2
2509 @request.session[:user_id] = 2
2498 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
2510 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
2499 assert_redirected_to :controller => 'issues', :action => 'index'
2511 assert_redirected_to :controller => 'issues', :action => 'index'
2500 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
2512 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
2501 end
2513 end
2502
2514
2503 def test_destroy_parent_and_child_issues
2515 def test_destroy_parent_and_child_issues
2504 parent = Issue.generate!(:project_id => 1, :tracker_id => 1)
2516 parent = Issue.generate!(:project_id => 1, :tracker_id => 1)
2505 child = Issue.generate!(:project_id => 1, :tracker_id => 1, :parent_issue_id => parent.id)
2517 child = Issue.generate!(:project_id => 1, :tracker_id => 1, :parent_issue_id => parent.id)
2506 assert child.is_descendant_of?(parent.reload)
2518 assert child.is_descendant_of?(parent.reload)
2507
2519
2508 @request.session[:user_id] = 2
2520 @request.session[:user_id] = 2
2509 assert_difference 'Issue.count', -2 do
2521 assert_difference 'Issue.count', -2 do
2510 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
2522 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
2511 end
2523 end
2512 assert_response 302
2524 assert_response 302
2513 end
2525 end
2514
2526
2515 def test_default_search_scope
2527 def test_default_search_scope
2516 get :index
2528 get :index
2517 assert_tag :div, :attributes => {:id => 'quick-search'},
2529 assert_tag :div, :attributes => {:id => 'quick-search'},
2518 :child => {:tag => 'form',
2530 :child => {:tag => 'form',
2519 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
2531 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
2520 end
2532 end
2521 end
2533 end
General Comments 0
You need to be logged in to leave comments. Login now