##// END OF EJS Templates
Fixed: issue bulk edit view broken by r2726 (#3347)....
Jean-Philippe Lang -
r2640:e5ed2b0f73f3
parent child
Show More
@@ -1,506 +1,506
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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 IssuesController < ApplicationController
18 class IssuesController < ApplicationController
19 menu_item :new_issue, :only => :new
19 menu_item :new_issue, :only => :new
20
20
21 before_filter :find_issue, :only => [:show, :edit, :reply]
21 before_filter :find_issue, :only => [:show, :edit, :reply]
22 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
22 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
23 before_filter :find_project, :only => [:new, :update_form, :preview]
23 before_filter :find_project, :only => [:new, :update_form, :preview]
24 before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :update_form, :context_menu]
24 before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :update_form, :context_menu]
25 before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
25 before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
26 accept_key_auth :index, :changes
26 accept_key_auth :index, :changes
27
27
28 helper :journals
28 helper :journals
29 helper :projects
29 helper :projects
30 include ProjectsHelper
30 include ProjectsHelper
31 helper :custom_fields
31 helper :custom_fields
32 include CustomFieldsHelper
32 include CustomFieldsHelper
33 helper :issue_relations
33 helper :issue_relations
34 include IssueRelationsHelper
34 include IssueRelationsHelper
35 helper :watchers
35 helper :watchers
36 include WatchersHelper
36 include WatchersHelper
37 helper :attachments
37 helper :attachments
38 include AttachmentsHelper
38 include AttachmentsHelper
39 helper :queries
39 helper :queries
40 helper :sort
40 helper :sort
41 include SortHelper
41 include SortHelper
42 include IssuesHelper
42 include IssuesHelper
43 helper :timelog
43 helper :timelog
44 include Redmine::Export::PDF
44 include Redmine::Export::PDF
45
45
46 def index
46 def index
47 retrieve_query
47 retrieve_query
48 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
48 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
49 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
49 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
50
50
51 if @query.valid?
51 if @query.valid?
52 limit = per_page_option
52 limit = per_page_option
53 respond_to do |format|
53 respond_to do |format|
54 format.html { }
54 format.html { }
55 format.atom { }
55 format.atom { }
56 format.csv { limit = Setting.issues_export_limit.to_i }
56 format.csv { limit = Setting.issues_export_limit.to_i }
57 format.pdf { limit = Setting.issues_export_limit.to_i }
57 format.pdf { limit = Setting.issues_export_limit.to_i }
58 end
58 end
59 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
59 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
60 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
60 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
61 @issues = Issue.find :all, :order => [@query.group_by_sort_order, sort_clause].compact.join(','),
61 @issues = Issue.find :all, :order => [@query.group_by_sort_order, sort_clause].compact.join(','),
62 :include => [ :assigned_to, :status, :tracker, :project, :priority, :category, :fixed_version ],
62 :include => [ :assigned_to, :status, :tracker, :project, :priority, :category, :fixed_version ],
63 :conditions => @query.statement,
63 :conditions => @query.statement,
64 :limit => limit,
64 :limit => limit,
65 :offset => @issue_pages.current.offset
65 :offset => @issue_pages.current.offset
66 respond_to do |format|
66 respond_to do |format|
67 format.html {
67 format.html {
68 if @query.grouped?
68 if @query.grouped?
69 # Retrieve the issue count by group
69 # Retrieve the issue count by group
70 @issue_count_by_group = begin
70 @issue_count_by_group = begin
71 Issue.count(:group => @query.group_by, :include => [:status, :project], :conditions => @query.statement)
71 Issue.count(:group => @query.group_by, :include => [:status, :project], :conditions => @query.statement)
72 # Rails will raise an (unexpected) error if there's only a nil group value
72 # Rails will raise an (unexpected) error if there's only a nil group value
73 rescue ActiveRecord::RecordNotFound
73 rescue ActiveRecord::RecordNotFound
74 {nil => @issue_count}
74 {nil => @issue_count}
75 end
75 end
76 end
76 end
77 render :template => 'issues/index.rhtml', :layout => !request.xhr?
77 render :template => 'issues/index.rhtml', :layout => !request.xhr?
78 }
78 }
79 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
79 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
80 format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
80 format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
81 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
81 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
82 end
82 end
83 else
83 else
84 # Send html if the query is not valid
84 # Send html if the query is not valid
85 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
85 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
86 end
86 end
87 rescue ActiveRecord::RecordNotFound
87 rescue ActiveRecord::RecordNotFound
88 render_404
88 render_404
89 end
89 end
90
90
91 def changes
91 def changes
92 retrieve_query
92 retrieve_query
93 sort_init 'id', 'desc'
93 sort_init 'id', 'desc'
94 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
94 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
95
95
96 if @query.valid?
96 if @query.valid?
97 @journals = Journal.find :all, :include => [ :details, :user, {:issue => [:project, :author, :tracker, :status]} ],
97 @journals = Journal.find :all, :include => [ :details, :user, {:issue => [:project, :author, :tracker, :status]} ],
98 :conditions => @query.statement,
98 :conditions => @query.statement,
99 :limit => 25,
99 :limit => 25,
100 :order => "#{Journal.table_name}.created_on DESC"
100 :order => "#{Journal.table_name}.created_on DESC"
101 end
101 end
102 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
102 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
103 render :layout => false, :content_type => 'application/atom+xml'
103 render :layout => false, :content_type => 'application/atom+xml'
104 rescue ActiveRecord::RecordNotFound
104 rescue ActiveRecord::RecordNotFound
105 render_404
105 render_404
106 end
106 end
107
107
108 def show
108 def show
109 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
109 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
110 @journals.each_with_index {|j,i| j.indice = i+1}
110 @journals.each_with_index {|j,i| j.indice = i+1}
111 @journals.reverse! if User.current.wants_comments_in_reverse_order?
111 @journals.reverse! if User.current.wants_comments_in_reverse_order?
112 @changesets = @issue.changesets
112 @changesets = @issue.changesets
113 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
113 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
114 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
114 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
115 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
115 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
116 @priorities = Enumeration.priorities
116 @priorities = Enumeration.priorities
117 @time_entry = TimeEntry.new
117 @time_entry = TimeEntry.new
118 respond_to do |format|
118 respond_to do |format|
119 format.html { render :template => 'issues/show.rhtml' }
119 format.html { render :template => 'issues/show.rhtml' }
120 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
120 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
121 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
121 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
122 end
122 end
123 end
123 end
124
124
125 # Add a new issue
125 # Add a new issue
126 # The new issue will be created from an existing one if copy_from parameter is given
126 # The new issue will be created from an existing one if copy_from parameter is given
127 def new
127 def new
128 @issue = Issue.new
128 @issue = Issue.new
129 @issue.copy_from(params[:copy_from]) if params[:copy_from]
129 @issue.copy_from(params[:copy_from]) if params[:copy_from]
130 @issue.project = @project
130 @issue.project = @project
131 # Tracker must be set before custom field values
131 # Tracker must be set before custom field values
132 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
132 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
133 if @issue.tracker.nil?
133 if @issue.tracker.nil?
134 render_error 'No tracker is associated to this project. Please check the Project settings.'
134 render_error 'No tracker is associated to this project. Please check the Project settings.'
135 return
135 return
136 end
136 end
137 if params[:issue].is_a?(Hash)
137 if params[:issue].is_a?(Hash)
138 @issue.attributes = params[:issue]
138 @issue.attributes = params[:issue]
139 @issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
139 @issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
140 end
140 end
141 @issue.author = User.current
141 @issue.author = User.current
142
142
143 default_status = IssueStatus.default
143 default_status = IssueStatus.default
144 unless default_status
144 unless default_status
145 render_error 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
145 render_error 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
146 return
146 return
147 end
147 end
148 @issue.status = default_status
148 @issue.status = default_status
149 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
149 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
150
150
151 if request.get? || request.xhr?
151 if request.get? || request.xhr?
152 @issue.start_date ||= Date.today
152 @issue.start_date ||= Date.today
153 else
153 else
154 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
154 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
155 # Check that the user is allowed to apply the requested status
155 # Check that the user is allowed to apply the requested status
156 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
156 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
157 if @issue.save
157 if @issue.save
158 attach_files(@issue, params[:attachments])
158 attach_files(@issue, params[:attachments])
159 flash[:notice] = l(:notice_successful_create)
159 flash[:notice] = l(:notice_successful_create)
160 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
160 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
161 redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
161 redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
162 { :action => 'show', :id => @issue })
162 { :action => 'show', :id => @issue })
163 return
163 return
164 end
164 end
165 end
165 end
166 @priorities = Enumeration.priorities
166 @priorities = Enumeration.priorities
167 render :layout => !request.xhr?
167 render :layout => !request.xhr?
168 end
168 end
169
169
170 # Attributes that can be updated on workflow transition (without :edit permission)
170 # Attributes that can be updated on workflow transition (without :edit permission)
171 # TODO: make it configurable (at least per role)
171 # TODO: make it configurable (at least per role)
172 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
172 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
173
173
174 def edit
174 def edit
175 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
175 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
176 @priorities = Enumeration.priorities
176 @priorities = Enumeration.priorities
177 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
177 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
178 @time_entry = TimeEntry.new
178 @time_entry = TimeEntry.new
179
179
180 @notes = params[:notes]
180 @notes = params[:notes]
181 journal = @issue.init_journal(User.current, @notes)
181 journal = @issue.init_journal(User.current, @notes)
182 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
182 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
183 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
183 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
184 attrs = params[:issue].dup
184 attrs = params[:issue].dup
185 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
185 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
186 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
186 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
187 @issue.attributes = attrs
187 @issue.attributes = attrs
188 end
188 end
189
189
190 if request.post?
190 if request.post?
191 @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
191 @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
192 @time_entry.attributes = params[:time_entry]
192 @time_entry.attributes = params[:time_entry]
193 attachments = attach_files(@issue, params[:attachments])
193 attachments = attach_files(@issue, params[:attachments])
194 attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
194 attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
195
195
196 call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
196 call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
197
197
198 if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.save
198 if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.save
199 # Log spend time
199 # Log spend time
200 if User.current.allowed_to?(:log_time, @project)
200 if User.current.allowed_to?(:log_time, @project)
201 @time_entry.save
201 @time_entry.save
202 end
202 end
203 if !journal.new_record?
203 if !journal.new_record?
204 # Only send notification if something was actually changed
204 # Only send notification if something was actually changed
205 flash[:notice] = l(:notice_successful_update)
205 flash[:notice] = l(:notice_successful_update)
206 end
206 end
207 call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
207 call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
208 redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
208 redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
209 end
209 end
210 end
210 end
211 rescue ActiveRecord::StaleObjectError
211 rescue ActiveRecord::StaleObjectError
212 # Optimistic locking exception
212 # Optimistic locking exception
213 flash.now[:error] = l(:notice_locking_conflict)
213 flash.now[:error] = l(:notice_locking_conflict)
214 end
214 end
215
215
216 def reply
216 def reply
217 journal = Journal.find(params[:journal_id]) if params[:journal_id]
217 journal = Journal.find(params[:journal_id]) if params[:journal_id]
218 if journal
218 if journal
219 user = journal.user
219 user = journal.user
220 text = journal.notes
220 text = journal.notes
221 else
221 else
222 user = @issue.author
222 user = @issue.author
223 text = @issue.description
223 text = @issue.description
224 end
224 end
225 content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
225 content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
226 content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
226 content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
227 render(:update) { |page|
227 render(:update) { |page|
228 page.<< "$('notes').value = \"#{content}\";"
228 page.<< "$('notes').value = \"#{content}\";"
229 page.show 'update'
229 page.show 'update'
230 page << "Form.Element.focus('notes');"
230 page << "Form.Element.focus('notes');"
231 page << "Element.scrollTo('update');"
231 page << "Element.scrollTo('update');"
232 page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
232 page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
233 }
233 }
234 end
234 end
235
235
236 # Bulk edit a set of issues
236 # Bulk edit a set of issues
237 def bulk_edit
237 def bulk_edit
238 if request.post?
238 if request.post?
239 status = params[:status_id].blank? ? nil : IssueStatus.find_by_id(params[:status_id])
239 status = params[:status_id].blank? ? nil : IssueStatus.find_by_id(params[:status_id])
240 priority = params[:priority_id].blank? ? nil : Enumeration.find_by_id(params[:priority_id])
240 priority = params[:priority_id].blank? ? nil : Enumeration.find_by_id(params[:priority_id])
241 assigned_to = (params[:assigned_to_id].blank? || params[:assigned_to_id] == 'none') ? nil : User.find_by_id(params[:assigned_to_id])
241 assigned_to = (params[:assigned_to_id].blank? || params[:assigned_to_id] == 'none') ? nil : User.find_by_id(params[:assigned_to_id])
242 category = (params[:category_id].blank? || params[:category_id] == 'none') ? nil : @project.issue_categories.find_by_id(params[:category_id])
242 category = (params[:category_id].blank? || params[:category_id] == 'none') ? nil : @project.issue_categories.find_by_id(params[:category_id])
243 fixed_version = (params[:fixed_version_id].blank? || params[:fixed_version_id] == 'none') ? nil : @project.versions.find_by_id(params[:fixed_version_id])
243 fixed_version = (params[:fixed_version_id].blank? || params[:fixed_version_id] == 'none') ? nil : @project.versions.find_by_id(params[:fixed_version_id])
244 custom_field_values = params[:custom_field_values] ? params[:custom_field_values].reject {|k,v| v.blank?} : nil
244 custom_field_values = params[:custom_field_values] ? params[:custom_field_values].reject {|k,v| v.blank?} : nil
245
245
246 unsaved_issue_ids = []
246 unsaved_issue_ids = []
247 @issues.each do |issue|
247 @issues.each do |issue|
248 journal = issue.init_journal(User.current, params[:notes])
248 journal = issue.init_journal(User.current, params[:notes])
249 issue.priority = priority if priority
249 issue.priority = priority if priority
250 issue.assigned_to = assigned_to if assigned_to || params[:assigned_to_id] == 'none'
250 issue.assigned_to = assigned_to if assigned_to || params[:assigned_to_id] == 'none'
251 issue.category = category if category || params[:category_id] == 'none'
251 issue.category = category if category || params[:category_id] == 'none'
252 issue.fixed_version = fixed_version if fixed_version || params[:fixed_version_id] == 'none'
252 issue.fixed_version = fixed_version if fixed_version || params[:fixed_version_id] == 'none'
253 issue.start_date = params[:start_date] unless params[:start_date].blank?
253 issue.start_date = params[:start_date] unless params[:start_date].blank?
254 issue.due_date = params[:due_date] unless params[:due_date].blank?
254 issue.due_date = params[:due_date] unless params[:due_date].blank?
255 issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
255 issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
256 issue.custom_field_values = custom_field_values if custom_field_values && !custom_field_values.empty?
256 issue.custom_field_values = custom_field_values if custom_field_values && !custom_field_values.empty?
257 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
257 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
258 # Don't save any change to the issue if the user is not authorized to apply the requested status
258 # Don't save any change to the issue if the user is not authorized to apply the requested status
259 unless (status.nil? || (issue.status.new_status_allowed_to?(status, current_role, issue.tracker) && issue.status = status)) && issue.save
259 unless (status.nil? || (issue.status.new_status_allowed_to?(status, current_role, issue.tracker) && issue.status = status)) && issue.save
260 # Keep unsaved issue ids to display them in flash error
260 # Keep unsaved issue ids to display them in flash error
261 unsaved_issue_ids << issue.id
261 unsaved_issue_ids << issue.id
262 end
262 end
263 end
263 end
264 if unsaved_issue_ids.empty?
264 if unsaved_issue_ids.empty?
265 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
265 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
266 else
266 else
267 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
267 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
268 :total => @issues.size,
268 :total => @issues.size,
269 :ids => '#' + unsaved_issue_ids.join(', #'))
269 :ids => '#' + unsaved_issue_ids.join(', #'))
270 end
270 end
271 redirect_to(params[:back_to] || {:controller => 'issues', :action => 'index', :project_id => @project})
271 redirect_to(params[:back_to] || {:controller => 'issues', :action => 'index', :project_id => @project})
272 return
272 return
273 end
273 end
274 # Find potential statuses the user could be allowed to switch issues to
274 # Find potential statuses the user could be allowed to switch issues to
275 @available_statuses = Workflow.find(:all, :include => :new_status,
275 @available_statuses = Workflow.find(:all, :include => :new_status,
276 :conditions => {:role_id => current_role.id}).collect(&:new_status).compact.uniq.sort
276 :conditions => {:role_id => User.current.roles_for_project(@project).collect(&:id)}).collect(&:new_status).compact.uniq.sort
277 @custom_fields = @project.issue_custom_fields.select {|f| f.field_format == 'list'}
277 @custom_fields = @project.issue_custom_fields.select {|f| f.field_format == 'list'}
278 end
278 end
279
279
280 def move
280 def move
281 @allowed_projects = []
281 @allowed_projects = []
282 # find projects to which the user is allowed to move the issue
282 # find projects to which the user is allowed to move the issue
283 if User.current.admin?
283 if User.current.admin?
284 # admin is allowed to move issues to any active (visible) project
284 # admin is allowed to move issues to any active (visible) project
285 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current))
285 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current))
286 else
286 else
287 User.current.memberships.each {|m| @allowed_projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
287 User.current.memberships.each {|m| @allowed_projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
288 end
288 end
289 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
289 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
290 @target_project ||= @project
290 @target_project ||= @project
291 @trackers = @target_project.trackers
291 @trackers = @target_project.trackers
292 if request.post?
292 if request.post?
293 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
293 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
294 unsaved_issue_ids = []
294 unsaved_issue_ids = []
295 @issues.each do |issue|
295 @issues.each do |issue|
296 issue.init_journal(User.current)
296 issue.init_journal(User.current)
297 unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker, params[:copy_options])
297 unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker, params[:copy_options])
298 end
298 end
299 if unsaved_issue_ids.empty?
299 if unsaved_issue_ids.empty?
300 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
300 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
301 else
301 else
302 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
302 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
303 :total => @issues.size,
303 :total => @issues.size,
304 :ids => '#' + unsaved_issue_ids.join(', #'))
304 :ids => '#' + unsaved_issue_ids.join(', #'))
305 end
305 end
306 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
306 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
307 return
307 return
308 end
308 end
309 render :layout => false if request.xhr?
309 render :layout => false if request.xhr?
310 end
310 end
311
311
312 def destroy
312 def destroy
313 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
313 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
314 if @hours > 0
314 if @hours > 0
315 case params[:todo]
315 case params[:todo]
316 when 'destroy'
316 when 'destroy'
317 # nothing to do
317 # nothing to do
318 when 'nullify'
318 when 'nullify'
319 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
319 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
320 when 'reassign'
320 when 'reassign'
321 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
321 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
322 if reassign_to.nil?
322 if reassign_to.nil?
323 flash.now[:error] = l(:error_issue_not_found_in_project)
323 flash.now[:error] = l(:error_issue_not_found_in_project)
324 return
324 return
325 else
325 else
326 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
326 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
327 end
327 end
328 else
328 else
329 # display the destroy form
329 # display the destroy form
330 return
330 return
331 end
331 end
332 end
332 end
333 @issues.each(&:destroy)
333 @issues.each(&:destroy)
334 redirect_to :action => 'index', :project_id => @project
334 redirect_to :action => 'index', :project_id => @project
335 end
335 end
336
336
337 def gantt
337 def gantt
338 @gantt = Redmine::Helpers::Gantt.new(params)
338 @gantt = Redmine::Helpers::Gantt.new(params)
339 retrieve_query
339 retrieve_query
340 if @query.valid?
340 if @query.valid?
341 events = []
341 events = []
342 # Issues that have start and due dates
342 # Issues that have start and due dates
343 events += Issue.find(:all,
343 events += Issue.find(:all,
344 :order => "start_date, due_date",
344 :order => "start_date, due_date",
345 :include => [:tracker, :status, :assigned_to, :priority, :project],
345 :include => [:tracker, :status, :assigned_to, :priority, :project],
346 :conditions => ["(#{@query.statement}) AND (((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
346 :conditions => ["(#{@query.statement}) AND (((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
347 )
347 )
348 # Issues that don't have a due date but that are assigned to a version with a date
348 # Issues that don't have a due date but that are assigned to a version with a date
349 events += Issue.find(:all,
349 events += Issue.find(:all,
350 :order => "start_date, effective_date",
350 :order => "start_date, effective_date",
351 :include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version],
351 :include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version],
352 :conditions => ["(#{@query.statement}) AND (((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date<? and effective_date>?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
352 :conditions => ["(#{@query.statement}) AND (((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date<? and effective_date>?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
353 )
353 )
354 # Versions
354 # Versions
355 events += Version.find(:all, :include => :project,
355 events += Version.find(:all, :include => :project,
356 :conditions => ["(#{@query.project_statement}) AND effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
356 :conditions => ["(#{@query.project_statement}) AND effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
357
357
358 @gantt.events = events
358 @gantt.events = events
359 end
359 end
360
360
361 basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
361 basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
362
362
363 respond_to do |format|
363 respond_to do |format|
364 format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
364 format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
365 format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
365 format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
366 format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
366 format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
367 end
367 end
368 end
368 end
369
369
370 def calendar
370 def calendar
371 if params[:year] and params[:year].to_i > 1900
371 if params[:year] and params[:year].to_i > 1900
372 @year = params[:year].to_i
372 @year = params[:year].to_i
373 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
373 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
374 @month = params[:month].to_i
374 @month = params[:month].to_i
375 end
375 end
376 end
376 end
377 @year ||= Date.today.year
377 @year ||= Date.today.year
378 @month ||= Date.today.month
378 @month ||= Date.today.month
379
379
380 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
380 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
381 retrieve_query
381 retrieve_query
382 if @query.valid?
382 if @query.valid?
383 events = []
383 events = []
384 events += Issue.find(:all,
384 events += Issue.find(:all,
385 :include => [:tracker, :status, :assigned_to, :priority, :project],
385 :include => [:tracker, :status, :assigned_to, :priority, :project],
386 :conditions => ["(#{@query.statement}) AND ((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
386 :conditions => ["(#{@query.statement}) AND ((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
387 )
387 )
388 events += Version.find(:all, :include => :project,
388 events += Version.find(:all, :include => :project,
389 :conditions => ["(#{@query.project_statement}) AND effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
389 :conditions => ["(#{@query.project_statement}) AND effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
390
390
391 @calendar.events = events
391 @calendar.events = events
392 end
392 end
393
393
394 render :layout => false if request.xhr?
394 render :layout => false if request.xhr?
395 end
395 end
396
396
397 def context_menu
397 def context_menu
398 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
398 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
399 if (@issues.size == 1)
399 if (@issues.size == 1)
400 @issue = @issues.first
400 @issue = @issues.first
401 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
401 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
402 end
402 end
403 projects = @issues.collect(&:project).compact.uniq
403 projects = @issues.collect(&:project).compact.uniq
404 @project = projects.first if projects.size == 1
404 @project = projects.first if projects.size == 1
405
405
406 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
406 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
407 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
407 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
408 :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
408 :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
409 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
409 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
410 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
410 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
411 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
411 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
412 }
412 }
413 if @project
413 if @project
414 @assignables = @project.assignable_users
414 @assignables = @project.assignable_users
415 @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
415 @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
416 end
416 end
417
417
418 @priorities = Enumeration.priorities.reverse
418 @priorities = Enumeration.priorities.reverse
419 @statuses = IssueStatus.find(:all, :order => 'position')
419 @statuses = IssueStatus.find(:all, :order => 'position')
420 @back = request.env['HTTP_REFERER']
420 @back = request.env['HTTP_REFERER']
421
421
422 render :layout => false
422 render :layout => false
423 end
423 end
424
424
425 def update_form
425 def update_form
426 @issue = Issue.new(params[:issue])
426 @issue = Issue.new(params[:issue])
427 render :action => :new, :layout => false
427 render :action => :new, :layout => false
428 end
428 end
429
429
430 def preview
430 def preview
431 @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
431 @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
432 @attachements = @issue.attachments if @issue
432 @attachements = @issue.attachments if @issue
433 @text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
433 @text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
434 render :partial => 'common/preview'
434 render :partial => 'common/preview'
435 end
435 end
436
436
437 private
437 private
438 def find_issue
438 def find_issue
439 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
439 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
440 @project = @issue.project
440 @project = @issue.project
441 rescue ActiveRecord::RecordNotFound
441 rescue ActiveRecord::RecordNotFound
442 render_404
442 render_404
443 end
443 end
444
444
445 # Filter for bulk operations
445 # Filter for bulk operations
446 def find_issues
446 def find_issues
447 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
447 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
448 raise ActiveRecord::RecordNotFound if @issues.empty?
448 raise ActiveRecord::RecordNotFound if @issues.empty?
449 projects = @issues.collect(&:project).compact.uniq
449 projects = @issues.collect(&:project).compact.uniq
450 if projects.size == 1
450 if projects.size == 1
451 @project = projects.first
451 @project = projects.first
452 else
452 else
453 # TODO: let users bulk edit/move/destroy issues from different projects
453 # TODO: let users bulk edit/move/destroy issues from different projects
454 render_error 'Can not bulk edit/move/destroy issues from different projects' and return false
454 render_error 'Can not bulk edit/move/destroy issues from different projects' and return false
455 end
455 end
456 rescue ActiveRecord::RecordNotFound
456 rescue ActiveRecord::RecordNotFound
457 render_404
457 render_404
458 end
458 end
459
459
460 def find_project
460 def find_project
461 @project = Project.find(params[:project_id])
461 @project = Project.find(params[:project_id])
462 rescue ActiveRecord::RecordNotFound
462 rescue ActiveRecord::RecordNotFound
463 render_404
463 render_404
464 end
464 end
465
465
466 def find_optional_project
466 def find_optional_project
467 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
467 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
468 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
468 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
469 allowed ? true : deny_access
469 allowed ? true : deny_access
470 rescue ActiveRecord::RecordNotFound
470 rescue ActiveRecord::RecordNotFound
471 render_404
471 render_404
472 end
472 end
473
473
474 # Retrieve query from session or build a new query
474 # Retrieve query from session or build a new query
475 def retrieve_query
475 def retrieve_query
476 if !params[:query_id].blank?
476 if !params[:query_id].blank?
477 cond = "project_id IS NULL"
477 cond = "project_id IS NULL"
478 cond << " OR project_id = #{@project.id}" if @project
478 cond << " OR project_id = #{@project.id}" if @project
479 @query = Query.find(params[:query_id], :conditions => cond)
479 @query = Query.find(params[:query_id], :conditions => cond)
480 @query.project = @project
480 @query.project = @project
481 session[:query] = {:id => @query.id, :project_id => @query.project_id}
481 session[:query] = {:id => @query.id, :project_id => @query.project_id}
482 sort_clear
482 sort_clear
483 else
483 else
484 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
484 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
485 # Give it a name, required to be valid
485 # Give it a name, required to be valid
486 @query = Query.new(:name => "_")
486 @query = Query.new(:name => "_")
487 @query.project = @project
487 @query.project = @project
488 if params[:fields] and params[:fields].is_a? Array
488 if params[:fields] and params[:fields].is_a? Array
489 params[:fields].each do |field|
489 params[:fields].each do |field|
490 @query.add_filter(field, params[:operators][field], params[:values][field])
490 @query.add_filter(field, params[:operators][field], params[:values][field])
491 end
491 end
492 else
492 else
493 @query.available_filters.keys.each do |field|
493 @query.available_filters.keys.each do |field|
494 @query.add_short_filter(field, params[field]) if params[field]
494 @query.add_short_filter(field, params[field]) if params[field]
495 end
495 end
496 end
496 end
497 @query.group_by = params[:group_by]
497 @query.group_by = params[:group_by]
498 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by}
498 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by}
499 else
499 else
500 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
500 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
501 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by])
501 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by])
502 @query.project = @project
502 @query.project = @project
503 end
503 end
504 end
504 end
505 end
505 end
506 end
506 end
@@ -1,1062 +1,1069
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'issues_controller'
19 require 'issues_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class IssuesController; def rescue_action(e) raise e end; end
22 class IssuesController; def rescue_action(e) raise e end; end
23
23
24 class IssuesControllerTest < Test::Unit::TestCase
24 class IssuesControllerTest < Test::Unit::TestCase
25 fixtures :projects,
25 fixtures :projects,
26 :users,
26 :users,
27 :roles,
27 :roles,
28 :members,
28 :members,
29 :member_roles,
29 :member_roles,
30 :issues,
30 :issues,
31 :issue_statuses,
31 :issue_statuses,
32 :versions,
32 :versions,
33 :trackers,
33 :trackers,
34 :projects_trackers,
34 :projects_trackers,
35 :issue_categories,
35 :issue_categories,
36 :enabled_modules,
36 :enabled_modules,
37 :enumerations,
37 :enumerations,
38 :attachments,
38 :attachments,
39 :workflows,
39 :workflows,
40 :custom_fields,
40 :custom_fields,
41 :custom_values,
41 :custom_values,
42 :custom_fields_trackers,
42 :custom_fields_trackers,
43 :time_entries,
43 :time_entries,
44 :journals,
44 :journals,
45 :journal_details
45 :journal_details
46
46
47 def setup
47 def setup
48 @controller = IssuesController.new
48 @controller = IssuesController.new
49 @request = ActionController::TestRequest.new
49 @request = ActionController::TestRequest.new
50 @response = ActionController::TestResponse.new
50 @response = ActionController::TestResponse.new
51 User.current = nil
51 User.current = nil
52 end
52 end
53
53
54 def test_index_routing
54 def test_index_routing
55 assert_routing(
55 assert_routing(
56 {:method => :get, :path => '/issues'},
56 {:method => :get, :path => '/issues'},
57 :controller => 'issues', :action => 'index'
57 :controller => 'issues', :action => 'index'
58 )
58 )
59 end
59 end
60
60
61 def test_index
61 def test_index
62 Setting.default_language = 'en'
62 Setting.default_language = 'en'
63
63
64 get :index
64 get :index
65 assert_response :success
65 assert_response :success
66 assert_template 'index.rhtml'
66 assert_template 'index.rhtml'
67 assert_not_nil assigns(:issues)
67 assert_not_nil assigns(:issues)
68 assert_nil assigns(:project)
68 assert_nil assigns(:project)
69 assert_tag :tag => 'a', :content => /Can't print recipes/
69 assert_tag :tag => 'a', :content => /Can't print recipes/
70 assert_tag :tag => 'a', :content => /Subproject issue/
70 assert_tag :tag => 'a', :content => /Subproject issue/
71 # private projects hidden
71 # private projects hidden
72 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
72 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
73 assert_no_tag :tag => 'a', :content => /Issue on project 2/
73 assert_no_tag :tag => 'a', :content => /Issue on project 2/
74 # project column
74 # project column
75 assert_tag :tag => 'th', :content => /Project/
75 assert_tag :tag => 'th', :content => /Project/
76 end
76 end
77
77
78 def test_index_should_not_list_issues_when_module_disabled
78 def test_index_should_not_list_issues_when_module_disabled
79 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
79 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
80 get :index
80 get :index
81 assert_response :success
81 assert_response :success
82 assert_template 'index.rhtml'
82 assert_template 'index.rhtml'
83 assert_not_nil assigns(:issues)
83 assert_not_nil assigns(:issues)
84 assert_nil assigns(:project)
84 assert_nil assigns(:project)
85 assert_no_tag :tag => 'a', :content => /Can't print recipes/
85 assert_no_tag :tag => 'a', :content => /Can't print recipes/
86 assert_tag :tag => 'a', :content => /Subproject issue/
86 assert_tag :tag => 'a', :content => /Subproject issue/
87 end
87 end
88
88
89 def test_index_with_project_routing
89 def test_index_with_project_routing
90 assert_routing(
90 assert_routing(
91 {:method => :get, :path => '/projects/23/issues'},
91 {:method => :get, :path => '/projects/23/issues'},
92 :controller => 'issues', :action => 'index', :project_id => '23'
92 :controller => 'issues', :action => 'index', :project_id => '23'
93 )
93 )
94 end
94 end
95
95
96 def test_index_should_not_list_issues_when_module_disabled
96 def test_index_should_not_list_issues_when_module_disabled
97 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
97 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
98 get :index
98 get :index
99 assert_response :success
99 assert_response :success
100 assert_template 'index.rhtml'
100 assert_template 'index.rhtml'
101 assert_not_nil assigns(:issues)
101 assert_not_nil assigns(:issues)
102 assert_nil assigns(:project)
102 assert_nil assigns(:project)
103 assert_no_tag :tag => 'a', :content => /Can't print recipes/
103 assert_no_tag :tag => 'a', :content => /Can't print recipes/
104 assert_tag :tag => 'a', :content => /Subproject issue/
104 assert_tag :tag => 'a', :content => /Subproject issue/
105 end
105 end
106
106
107 def test_index_with_project_routing
107 def test_index_with_project_routing
108 assert_routing(
108 assert_routing(
109 {:method => :get, :path => 'projects/23/issues'},
109 {:method => :get, :path => 'projects/23/issues'},
110 :controller => 'issues', :action => 'index', :project_id => '23'
110 :controller => 'issues', :action => 'index', :project_id => '23'
111 )
111 )
112 end
112 end
113
113
114 def test_index_with_project
114 def test_index_with_project
115 Setting.display_subprojects_issues = 0
115 Setting.display_subprojects_issues = 0
116 get :index, :project_id => 1
116 get :index, :project_id => 1
117 assert_response :success
117 assert_response :success
118 assert_template 'index.rhtml'
118 assert_template 'index.rhtml'
119 assert_not_nil assigns(:issues)
119 assert_not_nil assigns(:issues)
120 assert_tag :tag => 'a', :content => /Can't print recipes/
120 assert_tag :tag => 'a', :content => /Can't print recipes/
121 assert_no_tag :tag => 'a', :content => /Subproject issue/
121 assert_no_tag :tag => 'a', :content => /Subproject issue/
122 end
122 end
123
123
124 def test_index_with_project_and_subprojects
124 def test_index_with_project_and_subprojects
125 Setting.display_subprojects_issues = 1
125 Setting.display_subprojects_issues = 1
126 get :index, :project_id => 1
126 get :index, :project_id => 1
127 assert_response :success
127 assert_response :success
128 assert_template 'index.rhtml'
128 assert_template 'index.rhtml'
129 assert_not_nil assigns(:issues)
129 assert_not_nil assigns(:issues)
130 assert_tag :tag => 'a', :content => /Can't print recipes/
130 assert_tag :tag => 'a', :content => /Can't print recipes/
131 assert_tag :tag => 'a', :content => /Subproject issue/
131 assert_tag :tag => 'a', :content => /Subproject issue/
132 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
132 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
133 end
133 end
134
134
135 def test_index_with_project_and_subprojects_should_show_private_subprojects
135 def test_index_with_project_and_subprojects_should_show_private_subprojects
136 @request.session[:user_id] = 2
136 @request.session[:user_id] = 2
137 Setting.display_subprojects_issues = 1
137 Setting.display_subprojects_issues = 1
138 get :index, :project_id => 1
138 get :index, :project_id => 1
139 assert_response :success
139 assert_response :success
140 assert_template 'index.rhtml'
140 assert_template 'index.rhtml'
141 assert_not_nil assigns(:issues)
141 assert_not_nil assigns(:issues)
142 assert_tag :tag => 'a', :content => /Can't print recipes/
142 assert_tag :tag => 'a', :content => /Can't print recipes/
143 assert_tag :tag => 'a', :content => /Subproject issue/
143 assert_tag :tag => 'a', :content => /Subproject issue/
144 assert_tag :tag => 'a', :content => /Issue of a private subproject/
144 assert_tag :tag => 'a', :content => /Issue of a private subproject/
145 end
145 end
146
146
147 def test_index_with_project_routing_formatted
147 def test_index_with_project_routing_formatted
148 assert_routing(
148 assert_routing(
149 {:method => :get, :path => 'projects/23/issues.pdf'},
149 {:method => :get, :path => 'projects/23/issues.pdf'},
150 :controller => 'issues', :action => 'index', :project_id => '23', :format => 'pdf'
150 :controller => 'issues', :action => 'index', :project_id => '23', :format => 'pdf'
151 )
151 )
152 assert_routing(
152 assert_routing(
153 {:method => :get, :path => 'projects/23/issues.atom'},
153 {:method => :get, :path => 'projects/23/issues.atom'},
154 :controller => 'issues', :action => 'index', :project_id => '23', :format => 'atom'
154 :controller => 'issues', :action => 'index', :project_id => '23', :format => 'atom'
155 )
155 )
156 end
156 end
157
157
158 def test_index_with_project_and_filter
158 def test_index_with_project_and_filter
159 get :index, :project_id => 1, :set_filter => 1
159 get :index, :project_id => 1, :set_filter => 1
160 assert_response :success
160 assert_response :success
161 assert_template 'index.rhtml'
161 assert_template 'index.rhtml'
162 assert_not_nil assigns(:issues)
162 assert_not_nil assigns(:issues)
163 end
163 end
164
164
165 def test_index_with_query
165 def test_index_with_query
166 get :index, :project_id => 1, :query_id => 5
166 get :index, :project_id => 1, :query_id => 5
167 assert_response :success
167 assert_response :success
168 assert_template 'index.rhtml'
168 assert_template 'index.rhtml'
169 assert_not_nil assigns(:issues)
169 assert_not_nil assigns(:issues)
170 assert_nil assigns(:issue_count_by_group)
170 assert_nil assigns(:issue_count_by_group)
171 end
171 end
172
172
173 def test_index_with_grouped_query
173 def test_index_with_grouped_query
174 get :index, :project_id => 1, :query_id => 6
174 get :index, :project_id => 1, :query_id => 6
175 assert_response :success
175 assert_response :success
176 assert_template 'index.rhtml'
176 assert_template 'index.rhtml'
177 assert_not_nil assigns(:issues)
177 assert_not_nil assigns(:issues)
178 assert_not_nil assigns(:issue_count_by_group)
178 assert_not_nil assigns(:issue_count_by_group)
179 end
179 end
180
180
181 def test_index_csv_with_project
181 def test_index_csv_with_project
182 get :index, :format => 'csv'
182 get :index, :format => 'csv'
183 assert_response :success
183 assert_response :success
184 assert_not_nil assigns(:issues)
184 assert_not_nil assigns(:issues)
185 assert_equal 'text/csv', @response.content_type
185 assert_equal 'text/csv', @response.content_type
186
186
187 get :index, :project_id => 1, :format => 'csv'
187 get :index, :project_id => 1, :format => 'csv'
188 assert_response :success
188 assert_response :success
189 assert_not_nil assigns(:issues)
189 assert_not_nil assigns(:issues)
190 assert_equal 'text/csv', @response.content_type
190 assert_equal 'text/csv', @response.content_type
191 end
191 end
192
192
193 def test_index_formatted
193 def test_index_formatted
194 assert_routing(
194 assert_routing(
195 {:method => :get, :path => 'issues.pdf'},
195 {:method => :get, :path => 'issues.pdf'},
196 :controller => 'issues', :action => 'index', :format => 'pdf'
196 :controller => 'issues', :action => 'index', :format => 'pdf'
197 )
197 )
198 assert_routing(
198 assert_routing(
199 {:method => :get, :path => 'issues.atom'},
199 {:method => :get, :path => 'issues.atom'},
200 :controller => 'issues', :action => 'index', :format => 'atom'
200 :controller => 'issues', :action => 'index', :format => 'atom'
201 )
201 )
202 end
202 end
203
203
204 def test_index_pdf
204 def test_index_pdf
205 get :index, :format => 'pdf'
205 get :index, :format => 'pdf'
206 assert_response :success
206 assert_response :success
207 assert_not_nil assigns(:issues)
207 assert_not_nil assigns(:issues)
208 assert_equal 'application/pdf', @response.content_type
208 assert_equal 'application/pdf', @response.content_type
209
209
210 get :index, :project_id => 1, :format => 'pdf'
210 get :index, :project_id => 1, :format => 'pdf'
211 assert_response :success
211 assert_response :success
212 assert_not_nil assigns(:issues)
212 assert_not_nil assigns(:issues)
213 assert_equal 'application/pdf', @response.content_type
213 assert_equal 'application/pdf', @response.content_type
214
214
215 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
215 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
216 assert_response :success
216 assert_response :success
217 assert_not_nil assigns(:issues)
217 assert_not_nil assigns(:issues)
218 assert_equal 'application/pdf', @response.content_type
218 assert_equal 'application/pdf', @response.content_type
219 end
219 end
220
220
221 def test_index_sort
221 def test_index_sort
222 get :index, :sort => 'tracker,id:desc'
222 get :index, :sort => 'tracker,id:desc'
223 assert_response :success
223 assert_response :success
224
224
225 sort_params = @request.session['issues_index_sort']
225 sort_params = @request.session['issues_index_sort']
226 assert sort_params.is_a?(String)
226 assert sort_params.is_a?(String)
227 assert_equal 'tracker,id:desc', sort_params
227 assert_equal 'tracker,id:desc', sort_params
228
228
229 issues = assigns(:issues)
229 issues = assigns(:issues)
230 assert_not_nil issues
230 assert_not_nil issues
231 assert !issues.empty?
231 assert !issues.empty?
232 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
232 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
233 end
233 end
234
234
235 def test_gantt
235 def test_gantt
236 get :gantt, :project_id => 1
236 get :gantt, :project_id => 1
237 assert_response :success
237 assert_response :success
238 assert_template 'gantt.rhtml'
238 assert_template 'gantt.rhtml'
239 assert_not_nil assigns(:gantt)
239 assert_not_nil assigns(:gantt)
240 events = assigns(:gantt).events
240 events = assigns(:gantt).events
241 assert_not_nil events
241 assert_not_nil events
242 # Issue with start and due dates
242 # Issue with start and due dates
243 i = Issue.find(1)
243 i = Issue.find(1)
244 assert_not_nil i.due_date
244 assert_not_nil i.due_date
245 assert events.include?(Issue.find(1))
245 assert events.include?(Issue.find(1))
246 # Issue with without due date but targeted to a version with date
246 # Issue with without due date but targeted to a version with date
247 i = Issue.find(2)
247 i = Issue.find(2)
248 assert_nil i.due_date
248 assert_nil i.due_date
249 assert events.include?(i)
249 assert events.include?(i)
250 end
250 end
251
251
252 def test_cross_project_gantt
252 def test_cross_project_gantt
253 get :gantt
253 get :gantt
254 assert_response :success
254 assert_response :success
255 assert_template 'gantt.rhtml'
255 assert_template 'gantt.rhtml'
256 assert_not_nil assigns(:gantt)
256 assert_not_nil assigns(:gantt)
257 events = assigns(:gantt).events
257 events = assigns(:gantt).events
258 assert_not_nil events
258 assert_not_nil events
259 end
259 end
260
260
261 def test_gantt_export_to_pdf
261 def test_gantt_export_to_pdf
262 get :gantt, :project_id => 1, :format => 'pdf'
262 get :gantt, :project_id => 1, :format => 'pdf'
263 assert_response :success
263 assert_response :success
264 assert_equal 'application/pdf', @response.content_type
264 assert_equal 'application/pdf', @response.content_type
265 assert @response.body.starts_with?('%PDF')
265 assert @response.body.starts_with?('%PDF')
266 assert_not_nil assigns(:gantt)
266 assert_not_nil assigns(:gantt)
267 end
267 end
268
268
269 def test_cross_project_gantt_export_to_pdf
269 def test_cross_project_gantt_export_to_pdf
270 get :gantt, :format => 'pdf'
270 get :gantt, :format => 'pdf'
271 assert_response :success
271 assert_response :success
272 assert_equal 'application/pdf', @response.content_type
272 assert_equal 'application/pdf', @response.content_type
273 assert @response.body.starts_with?('%PDF')
273 assert @response.body.starts_with?('%PDF')
274 assert_not_nil assigns(:gantt)
274 assert_not_nil assigns(:gantt)
275 end
275 end
276
276
277 if Object.const_defined?(:Magick)
277 if Object.const_defined?(:Magick)
278 def test_gantt_image
278 def test_gantt_image
279 get :gantt, :project_id => 1, :format => 'png'
279 get :gantt, :project_id => 1, :format => 'png'
280 assert_response :success
280 assert_response :success
281 assert_equal 'image/png', @response.content_type
281 assert_equal 'image/png', @response.content_type
282 end
282 end
283 else
283 else
284 puts "RMagick not installed. Skipping tests !!!"
284 puts "RMagick not installed. Skipping tests !!!"
285 end
285 end
286
286
287 def test_calendar
287 def test_calendar
288 get :calendar, :project_id => 1
288 get :calendar, :project_id => 1
289 assert_response :success
289 assert_response :success
290 assert_template 'calendar'
290 assert_template 'calendar'
291 assert_not_nil assigns(:calendar)
291 assert_not_nil assigns(:calendar)
292 end
292 end
293
293
294 def test_cross_project_calendar
294 def test_cross_project_calendar
295 get :calendar
295 get :calendar
296 assert_response :success
296 assert_response :success
297 assert_template 'calendar'
297 assert_template 'calendar'
298 assert_not_nil assigns(:calendar)
298 assert_not_nil assigns(:calendar)
299 end
299 end
300
300
301 def test_changes
301 def test_changes
302 get :changes, :project_id => 1
302 get :changes, :project_id => 1
303 assert_response :success
303 assert_response :success
304 assert_not_nil assigns(:journals)
304 assert_not_nil assigns(:journals)
305 assert_equal 'application/atom+xml', @response.content_type
305 assert_equal 'application/atom+xml', @response.content_type
306 end
306 end
307
307
308 def test_show_routing
308 def test_show_routing
309 assert_routing(
309 assert_routing(
310 {:method => :get, :path => '/issues/64'},
310 {:method => :get, :path => '/issues/64'},
311 :controller => 'issues', :action => 'show', :id => '64'
311 :controller => 'issues', :action => 'show', :id => '64'
312 )
312 )
313 end
313 end
314
314
315 def test_show_routing_formatted
315 def test_show_routing_formatted
316 assert_routing(
316 assert_routing(
317 {:method => :get, :path => '/issues/2332.pdf'},
317 {:method => :get, :path => '/issues/2332.pdf'},
318 :controller => 'issues', :action => 'show', :id => '2332', :format => 'pdf'
318 :controller => 'issues', :action => 'show', :id => '2332', :format => 'pdf'
319 )
319 )
320 assert_routing(
320 assert_routing(
321 {:method => :get, :path => '/issues/23123.atom'},
321 {:method => :get, :path => '/issues/23123.atom'},
322 :controller => 'issues', :action => 'show', :id => '23123', :format => 'atom'
322 :controller => 'issues', :action => 'show', :id => '23123', :format => 'atom'
323 )
323 )
324 end
324 end
325
325
326 def test_show_by_anonymous
326 def test_show_by_anonymous
327 get :show, :id => 1
327 get :show, :id => 1
328 assert_response :success
328 assert_response :success
329 assert_template 'show.rhtml'
329 assert_template 'show.rhtml'
330 assert_not_nil assigns(:issue)
330 assert_not_nil assigns(:issue)
331 assert_equal Issue.find(1), assigns(:issue)
331 assert_equal Issue.find(1), assigns(:issue)
332
332
333 # anonymous role is allowed to add a note
333 # anonymous role is allowed to add a note
334 assert_tag :tag => 'form',
334 assert_tag :tag => 'form',
335 :descendant => { :tag => 'fieldset',
335 :descendant => { :tag => 'fieldset',
336 :child => { :tag => 'legend',
336 :child => { :tag => 'legend',
337 :content => /Notes/ } }
337 :content => /Notes/ } }
338 end
338 end
339
339
340 def test_show_by_manager
340 def test_show_by_manager
341 @request.session[:user_id] = 2
341 @request.session[:user_id] = 2
342 get :show, :id => 1
342 get :show, :id => 1
343 assert_response :success
343 assert_response :success
344
344
345 assert_tag :tag => 'form',
345 assert_tag :tag => 'form',
346 :descendant => { :tag => 'fieldset',
346 :descendant => { :tag => 'fieldset',
347 :child => { :tag => 'legend',
347 :child => { :tag => 'legend',
348 :content => /Change properties/ } },
348 :content => /Change properties/ } },
349 :descendant => { :tag => 'fieldset',
349 :descendant => { :tag => 'fieldset',
350 :child => { :tag => 'legend',
350 :child => { :tag => 'legend',
351 :content => /Log time/ } },
351 :content => /Log time/ } },
352 :descendant => { :tag => 'fieldset',
352 :descendant => { :tag => 'fieldset',
353 :child => { :tag => 'legend',
353 :child => { :tag => 'legend',
354 :content => /Notes/ } }
354 :content => /Notes/ } }
355 end
355 end
356
356
357 def test_show_should_not_disclose_relations_to_invisible_issues
357 def test_show_should_not_disclose_relations_to_invisible_issues
358 Setting.cross_project_issue_relations = '1'
358 Setting.cross_project_issue_relations = '1'
359 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
359 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
360 # Relation to a private project issue
360 # Relation to a private project issue
361 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
361 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
362
362
363 get :show, :id => 1
363 get :show, :id => 1
364 assert_response :success
364 assert_response :success
365
365
366 assert_tag :div, :attributes => { :id => 'relations' },
366 assert_tag :div, :attributes => { :id => 'relations' },
367 :descendant => { :tag => 'a', :content => /#2$/ }
367 :descendant => { :tag => 'a', :content => /#2$/ }
368 assert_no_tag :div, :attributes => { :id => 'relations' },
368 assert_no_tag :div, :attributes => { :id => 'relations' },
369 :descendant => { :tag => 'a', :content => /#4$/ }
369 :descendant => { :tag => 'a', :content => /#4$/ }
370 end
370 end
371
371
372 def test_new_routing
372 def test_new_routing
373 assert_routing(
373 assert_routing(
374 {:method => :get, :path => '/projects/1/issues/new'},
374 {:method => :get, :path => '/projects/1/issues/new'},
375 :controller => 'issues', :action => 'new', :project_id => '1'
375 :controller => 'issues', :action => 'new', :project_id => '1'
376 )
376 )
377 assert_recognizes(
377 assert_recognizes(
378 {:controller => 'issues', :action => 'new', :project_id => '1'},
378 {:controller => 'issues', :action => 'new', :project_id => '1'},
379 {:method => :post, :path => '/projects/1/issues'}
379 {:method => :post, :path => '/projects/1/issues'}
380 )
380 )
381 end
381 end
382
382
383 def test_show_export_to_pdf
383 def test_show_export_to_pdf
384 get :show, :id => 3, :format => 'pdf'
384 get :show, :id => 3, :format => 'pdf'
385 assert_response :success
385 assert_response :success
386 assert_equal 'application/pdf', @response.content_type
386 assert_equal 'application/pdf', @response.content_type
387 assert @response.body.starts_with?('%PDF')
387 assert @response.body.starts_with?('%PDF')
388 assert_not_nil assigns(:issue)
388 assert_not_nil assigns(:issue)
389 end
389 end
390
390
391 def test_get_new
391 def test_get_new
392 @request.session[:user_id] = 2
392 @request.session[:user_id] = 2
393 get :new, :project_id => 1, :tracker_id => 1
393 get :new, :project_id => 1, :tracker_id => 1
394 assert_response :success
394 assert_response :success
395 assert_template 'new'
395 assert_template 'new'
396
396
397 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
397 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
398 :value => 'Default string' }
398 :value => 'Default string' }
399 end
399 end
400
400
401 def test_get_new_without_tracker_id
401 def test_get_new_without_tracker_id
402 @request.session[:user_id] = 2
402 @request.session[:user_id] = 2
403 get :new, :project_id => 1
403 get :new, :project_id => 1
404 assert_response :success
404 assert_response :success
405 assert_template 'new'
405 assert_template 'new'
406
406
407 issue = assigns(:issue)
407 issue = assigns(:issue)
408 assert_not_nil issue
408 assert_not_nil issue
409 assert_equal Project.find(1).trackers.first, issue.tracker
409 assert_equal Project.find(1).trackers.first, issue.tracker
410 end
410 end
411
411
412 def test_get_new_with_no_default_status_should_display_an_error
412 def test_get_new_with_no_default_status_should_display_an_error
413 @request.session[:user_id] = 2
413 @request.session[:user_id] = 2
414 IssueStatus.delete_all
414 IssueStatus.delete_all
415
415
416 get :new, :project_id => 1
416 get :new, :project_id => 1
417 assert_response 500
417 assert_response 500
418 assert_not_nil flash[:error]
418 assert_not_nil flash[:error]
419 assert_tag :tag => 'div', :attributes => { :class => /error/ },
419 assert_tag :tag => 'div', :attributes => { :class => /error/ },
420 :content => /No default issue/
420 :content => /No default issue/
421 end
421 end
422
422
423 def test_get_new_with_no_tracker_should_display_an_error
423 def test_get_new_with_no_tracker_should_display_an_error
424 @request.session[:user_id] = 2
424 @request.session[:user_id] = 2
425 Tracker.delete_all
425 Tracker.delete_all
426
426
427 get :new, :project_id => 1
427 get :new, :project_id => 1
428 assert_response 500
428 assert_response 500
429 assert_not_nil flash[:error]
429 assert_not_nil flash[:error]
430 assert_tag :tag => 'div', :attributes => { :class => /error/ },
430 assert_tag :tag => 'div', :attributes => { :class => /error/ },
431 :content => /No tracker/
431 :content => /No tracker/
432 end
432 end
433
433
434 def test_update_new_form
434 def test_update_new_form
435 @request.session[:user_id] = 2
435 @request.session[:user_id] = 2
436 xhr :post, :new, :project_id => 1,
436 xhr :post, :new, :project_id => 1,
437 :issue => {:tracker_id => 2,
437 :issue => {:tracker_id => 2,
438 :subject => 'This is the test_new issue',
438 :subject => 'This is the test_new issue',
439 :description => 'This is the description',
439 :description => 'This is the description',
440 :priority_id => 5}
440 :priority_id => 5}
441 assert_response :success
441 assert_response :success
442 assert_template 'new'
442 assert_template 'new'
443 end
443 end
444
444
445 def test_post_new
445 def test_post_new
446 @request.session[:user_id] = 2
446 @request.session[:user_id] = 2
447 post :new, :project_id => 1,
447 post :new, :project_id => 1,
448 :issue => {:tracker_id => 3,
448 :issue => {:tracker_id => 3,
449 :subject => 'This is the test_new issue',
449 :subject => 'This is the test_new issue',
450 :description => 'This is the description',
450 :description => 'This is the description',
451 :priority_id => 5,
451 :priority_id => 5,
452 :estimated_hours => '',
452 :estimated_hours => '',
453 :custom_field_values => {'2' => 'Value for field 2'}}
453 :custom_field_values => {'2' => 'Value for field 2'}}
454 assert_redirected_to :action => 'show'
454 assert_redirected_to :action => 'show'
455
455
456 issue = Issue.find_by_subject('This is the test_new issue')
456 issue = Issue.find_by_subject('This is the test_new issue')
457 assert_not_nil issue
457 assert_not_nil issue
458 assert_equal 2, issue.author_id
458 assert_equal 2, issue.author_id
459 assert_equal 3, issue.tracker_id
459 assert_equal 3, issue.tracker_id
460 assert_nil issue.estimated_hours
460 assert_nil issue.estimated_hours
461 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
461 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
462 assert_not_nil v
462 assert_not_nil v
463 assert_equal 'Value for field 2', v.value
463 assert_equal 'Value for field 2', v.value
464 end
464 end
465
465
466 def test_post_new_and_continue
466 def test_post_new_and_continue
467 @request.session[:user_id] = 2
467 @request.session[:user_id] = 2
468 post :new, :project_id => 1,
468 post :new, :project_id => 1,
469 :issue => {:tracker_id => 3,
469 :issue => {:tracker_id => 3,
470 :subject => 'This is first issue',
470 :subject => 'This is first issue',
471 :priority_id => 5},
471 :priority_id => 5},
472 :continue => ''
472 :continue => ''
473 assert_redirected_to :controller => 'issues', :action => 'new', :tracker_id => 3
473 assert_redirected_to :controller => 'issues', :action => 'new', :tracker_id => 3
474 end
474 end
475
475
476 def test_post_new_without_custom_fields_param
476 def test_post_new_without_custom_fields_param
477 @request.session[:user_id] = 2
477 @request.session[:user_id] = 2
478 post :new, :project_id => 1,
478 post :new, :project_id => 1,
479 :issue => {:tracker_id => 1,
479 :issue => {:tracker_id => 1,
480 :subject => 'This is the test_new issue',
480 :subject => 'This is the test_new issue',
481 :description => 'This is the description',
481 :description => 'This is the description',
482 :priority_id => 5}
482 :priority_id => 5}
483 assert_redirected_to :action => 'show'
483 assert_redirected_to :action => 'show'
484 end
484 end
485
485
486 def test_post_new_with_required_custom_field_and_without_custom_fields_param
486 def test_post_new_with_required_custom_field_and_without_custom_fields_param
487 field = IssueCustomField.find_by_name('Database')
487 field = IssueCustomField.find_by_name('Database')
488 field.update_attribute(:is_required, true)
488 field.update_attribute(:is_required, true)
489
489
490 @request.session[:user_id] = 2
490 @request.session[:user_id] = 2
491 post :new, :project_id => 1,
491 post :new, :project_id => 1,
492 :issue => {:tracker_id => 1,
492 :issue => {:tracker_id => 1,
493 :subject => 'This is the test_new issue',
493 :subject => 'This is the test_new issue',
494 :description => 'This is the description',
494 :description => 'This is the description',
495 :priority_id => 5}
495 :priority_id => 5}
496 assert_response :success
496 assert_response :success
497 assert_template 'new'
497 assert_template 'new'
498 issue = assigns(:issue)
498 issue = assigns(:issue)
499 assert_not_nil issue
499 assert_not_nil issue
500 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
500 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
501 end
501 end
502
502
503 def test_post_new_with_watchers
503 def test_post_new_with_watchers
504 @request.session[:user_id] = 2
504 @request.session[:user_id] = 2
505 ActionMailer::Base.deliveries.clear
505 ActionMailer::Base.deliveries.clear
506
506
507 assert_difference 'Watcher.count', 2 do
507 assert_difference 'Watcher.count', 2 do
508 post :new, :project_id => 1,
508 post :new, :project_id => 1,
509 :issue => {:tracker_id => 1,
509 :issue => {:tracker_id => 1,
510 :subject => 'This is a new issue with watchers',
510 :subject => 'This is a new issue with watchers',
511 :description => 'This is the description',
511 :description => 'This is the description',
512 :priority_id => 5,
512 :priority_id => 5,
513 :watcher_user_ids => ['2', '3']}
513 :watcher_user_ids => ['2', '3']}
514 end
514 end
515 issue = Issue.find_by_subject('This is a new issue with watchers')
515 issue = Issue.find_by_subject('This is a new issue with watchers')
516 assert_not_nil issue
516 assert_not_nil issue
517 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
517 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
518
518
519 # Watchers added
519 # Watchers added
520 assert_equal [2, 3], issue.watcher_user_ids.sort
520 assert_equal [2, 3], issue.watcher_user_ids.sort
521 assert issue.watched_by?(User.find(3))
521 assert issue.watched_by?(User.find(3))
522 # Watchers notified
522 # Watchers notified
523 mail = ActionMailer::Base.deliveries.last
523 mail = ActionMailer::Base.deliveries.last
524 assert_kind_of TMail::Mail, mail
524 assert_kind_of TMail::Mail, mail
525 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
525 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
526 end
526 end
527
527
528 def test_post_new_should_send_a_notification
528 def test_post_new_should_send_a_notification
529 ActionMailer::Base.deliveries.clear
529 ActionMailer::Base.deliveries.clear
530 @request.session[:user_id] = 2
530 @request.session[:user_id] = 2
531 post :new, :project_id => 1,
531 post :new, :project_id => 1,
532 :issue => {:tracker_id => 3,
532 :issue => {:tracker_id => 3,
533 :subject => 'This is the test_new issue',
533 :subject => 'This is the test_new issue',
534 :description => 'This is the description',
534 :description => 'This is the description',
535 :priority_id => 5,
535 :priority_id => 5,
536 :estimated_hours => '',
536 :estimated_hours => '',
537 :custom_field_values => {'2' => 'Value for field 2'}}
537 :custom_field_values => {'2' => 'Value for field 2'}}
538 assert_redirected_to :action => 'show'
538 assert_redirected_to :action => 'show'
539
539
540 assert_equal 1, ActionMailer::Base.deliveries.size
540 assert_equal 1, ActionMailer::Base.deliveries.size
541 end
541 end
542
542
543 def test_post_should_preserve_fields_values_on_validation_failure
543 def test_post_should_preserve_fields_values_on_validation_failure
544 @request.session[:user_id] = 2
544 @request.session[:user_id] = 2
545 post :new, :project_id => 1,
545 post :new, :project_id => 1,
546 :issue => {:tracker_id => 1,
546 :issue => {:tracker_id => 1,
547 # empty subject
547 # empty subject
548 :subject => '',
548 :subject => '',
549 :description => 'This is a description',
549 :description => 'This is a description',
550 :priority_id => 6,
550 :priority_id => 6,
551 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
551 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
552 assert_response :success
552 assert_response :success
553 assert_template 'new'
553 assert_template 'new'
554
554
555 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
555 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
556 :content => 'This is a description'
556 :content => 'This is a description'
557 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
557 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
558 :child => { :tag => 'option', :attributes => { :selected => 'selected',
558 :child => { :tag => 'option', :attributes => { :selected => 'selected',
559 :value => '6' },
559 :value => '6' },
560 :content => 'High' }
560 :content => 'High' }
561 # Custom fields
561 # Custom fields
562 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
562 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
563 :child => { :tag => 'option', :attributes => { :selected => 'selected',
563 :child => { :tag => 'option', :attributes => { :selected => 'selected',
564 :value => 'Oracle' },
564 :value => 'Oracle' },
565 :content => 'Oracle' }
565 :content => 'Oracle' }
566 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
566 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
567 :value => 'Value for field 2'}
567 :value => 'Value for field 2'}
568 end
568 end
569
569
570 def test_copy_routing
570 def test_copy_routing
571 assert_routing(
571 assert_routing(
572 {:method => :get, :path => '/projects/world_domination/issues/567/copy'},
572 {:method => :get, :path => '/projects/world_domination/issues/567/copy'},
573 :controller => 'issues', :action => 'new', :project_id => 'world_domination', :copy_from => '567'
573 :controller => 'issues', :action => 'new', :project_id => 'world_domination', :copy_from => '567'
574 )
574 )
575 end
575 end
576
576
577 def test_copy_issue
577 def test_copy_issue
578 @request.session[:user_id] = 2
578 @request.session[:user_id] = 2
579 get :new, :project_id => 1, :copy_from => 1
579 get :new, :project_id => 1, :copy_from => 1
580 assert_template 'new'
580 assert_template 'new'
581 assert_not_nil assigns(:issue)
581 assert_not_nil assigns(:issue)
582 orig = Issue.find(1)
582 orig = Issue.find(1)
583 assert_equal orig.subject, assigns(:issue).subject
583 assert_equal orig.subject, assigns(:issue).subject
584 end
584 end
585
585
586 def test_edit_routing
586 def test_edit_routing
587 assert_routing(
587 assert_routing(
588 {:method => :get, :path => '/issues/1/edit'},
588 {:method => :get, :path => '/issues/1/edit'},
589 :controller => 'issues', :action => 'edit', :id => '1'
589 :controller => 'issues', :action => 'edit', :id => '1'
590 )
590 )
591 assert_recognizes( #TODO: use a PUT on the issue URI isntead, need to adjust form
591 assert_recognizes( #TODO: use a PUT on the issue URI isntead, need to adjust form
592 {:controller => 'issues', :action => 'edit', :id => '1'},
592 {:controller => 'issues', :action => 'edit', :id => '1'},
593 {:method => :post, :path => '/issues/1/edit'}
593 {:method => :post, :path => '/issues/1/edit'}
594 )
594 )
595 end
595 end
596
596
597 def test_get_edit
597 def test_get_edit
598 @request.session[:user_id] = 2
598 @request.session[:user_id] = 2
599 get :edit, :id => 1
599 get :edit, :id => 1
600 assert_response :success
600 assert_response :success
601 assert_template 'edit'
601 assert_template 'edit'
602 assert_not_nil assigns(:issue)
602 assert_not_nil assigns(:issue)
603 assert_equal Issue.find(1), assigns(:issue)
603 assert_equal Issue.find(1), assigns(:issue)
604 end
604 end
605
605
606 def test_get_edit_with_params
606 def test_get_edit_with_params
607 @request.session[:user_id] = 2
607 @request.session[:user_id] = 2
608 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
608 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
609 assert_response :success
609 assert_response :success
610 assert_template 'edit'
610 assert_template 'edit'
611
611
612 issue = assigns(:issue)
612 issue = assigns(:issue)
613 assert_not_nil issue
613 assert_not_nil issue
614
614
615 assert_equal 5, issue.status_id
615 assert_equal 5, issue.status_id
616 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
616 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
617 :child => { :tag => 'option',
617 :child => { :tag => 'option',
618 :content => 'Closed',
618 :content => 'Closed',
619 :attributes => { :selected => 'selected' } }
619 :attributes => { :selected => 'selected' } }
620
620
621 assert_equal 7, issue.priority_id
621 assert_equal 7, issue.priority_id
622 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
622 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
623 :child => { :tag => 'option',
623 :child => { :tag => 'option',
624 :content => 'Urgent',
624 :content => 'Urgent',
625 :attributes => { :selected => 'selected' } }
625 :attributes => { :selected => 'selected' } }
626 end
626 end
627
627
628 def test_reply_routing
628 def test_reply_routing
629 assert_routing(
629 assert_routing(
630 {:method => :post, :path => '/issues/1/quoted'},
630 {:method => :post, :path => '/issues/1/quoted'},
631 :controller => 'issues', :action => 'reply', :id => '1'
631 :controller => 'issues', :action => 'reply', :id => '1'
632 )
632 )
633 end
633 end
634
634
635 def test_reply_to_issue
635 def test_reply_to_issue
636 @request.session[:user_id] = 2
636 @request.session[:user_id] = 2
637 get :reply, :id => 1
637 get :reply, :id => 1
638 assert_response :success
638 assert_response :success
639 assert_select_rjs :show, "update"
639 assert_select_rjs :show, "update"
640 end
640 end
641
641
642 def test_reply_to_note
642 def test_reply_to_note
643 @request.session[:user_id] = 2
643 @request.session[:user_id] = 2
644 get :reply, :id => 1, :journal_id => 2
644 get :reply, :id => 1, :journal_id => 2
645 assert_response :success
645 assert_response :success
646 assert_select_rjs :show, "update"
646 assert_select_rjs :show, "update"
647 end
647 end
648
648
649 def test_post_edit_without_custom_fields_param
649 def test_post_edit_without_custom_fields_param
650 @request.session[:user_id] = 2
650 @request.session[:user_id] = 2
651 ActionMailer::Base.deliveries.clear
651 ActionMailer::Base.deliveries.clear
652
652
653 issue = Issue.find(1)
653 issue = Issue.find(1)
654 assert_equal '125', issue.custom_value_for(2).value
654 assert_equal '125', issue.custom_value_for(2).value
655 old_subject = issue.subject
655 old_subject = issue.subject
656 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
656 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
657
657
658 assert_difference('Journal.count') do
658 assert_difference('Journal.count') do
659 assert_difference('JournalDetail.count', 2) do
659 assert_difference('JournalDetail.count', 2) do
660 post :edit, :id => 1, :issue => {:subject => new_subject,
660 post :edit, :id => 1, :issue => {:subject => new_subject,
661 :priority_id => '6',
661 :priority_id => '6',
662 :category_id => '1' # no change
662 :category_id => '1' # no change
663 }
663 }
664 end
664 end
665 end
665 end
666 assert_redirected_to :action => 'show', :id => '1'
666 assert_redirected_to :action => 'show', :id => '1'
667 issue.reload
667 issue.reload
668 assert_equal new_subject, issue.subject
668 assert_equal new_subject, issue.subject
669 # Make sure custom fields were not cleared
669 # Make sure custom fields were not cleared
670 assert_equal '125', issue.custom_value_for(2).value
670 assert_equal '125', issue.custom_value_for(2).value
671
671
672 mail = ActionMailer::Base.deliveries.last
672 mail = ActionMailer::Base.deliveries.last
673 assert_kind_of TMail::Mail, mail
673 assert_kind_of TMail::Mail, mail
674 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
674 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
675 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
675 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
676 end
676 end
677
677
678 def test_post_edit_with_custom_field_change
678 def test_post_edit_with_custom_field_change
679 @request.session[:user_id] = 2
679 @request.session[:user_id] = 2
680 issue = Issue.find(1)
680 issue = Issue.find(1)
681 assert_equal '125', issue.custom_value_for(2).value
681 assert_equal '125', issue.custom_value_for(2).value
682
682
683 assert_difference('Journal.count') do
683 assert_difference('Journal.count') do
684 assert_difference('JournalDetail.count', 3) do
684 assert_difference('JournalDetail.count', 3) do
685 post :edit, :id => 1, :issue => {:subject => 'Custom field change',
685 post :edit, :id => 1, :issue => {:subject => 'Custom field change',
686 :priority_id => '6',
686 :priority_id => '6',
687 :category_id => '1', # no change
687 :category_id => '1', # no change
688 :custom_field_values => { '2' => 'New custom value' }
688 :custom_field_values => { '2' => 'New custom value' }
689 }
689 }
690 end
690 end
691 end
691 end
692 assert_redirected_to :action => 'show', :id => '1'
692 assert_redirected_to :action => 'show', :id => '1'
693 issue.reload
693 issue.reload
694 assert_equal 'New custom value', issue.custom_value_for(2).value
694 assert_equal 'New custom value', issue.custom_value_for(2).value
695
695
696 mail = ActionMailer::Base.deliveries.last
696 mail = ActionMailer::Base.deliveries.last
697 assert_kind_of TMail::Mail, mail
697 assert_kind_of TMail::Mail, mail
698 assert mail.body.include?("Searchable field changed from 125 to New custom value")
698 assert mail.body.include?("Searchable field changed from 125 to New custom value")
699 end
699 end
700
700
701 def test_post_edit_with_status_and_assignee_change
701 def test_post_edit_with_status_and_assignee_change
702 issue = Issue.find(1)
702 issue = Issue.find(1)
703 assert_equal 1, issue.status_id
703 assert_equal 1, issue.status_id
704 @request.session[:user_id] = 2
704 @request.session[:user_id] = 2
705 assert_difference('TimeEntry.count', 0) do
705 assert_difference('TimeEntry.count', 0) do
706 post :edit,
706 post :edit,
707 :id => 1,
707 :id => 1,
708 :issue => { :status_id => 2, :assigned_to_id => 3 },
708 :issue => { :status_id => 2, :assigned_to_id => 3 },
709 :notes => 'Assigned to dlopper',
709 :notes => 'Assigned to dlopper',
710 :time_entry => { :hours => '', :comments => '', :activity_id => Enumeration.activities.first }
710 :time_entry => { :hours => '', :comments => '', :activity_id => Enumeration.activities.first }
711 end
711 end
712 assert_redirected_to :action => 'show', :id => '1'
712 assert_redirected_to :action => 'show', :id => '1'
713 issue.reload
713 issue.reload
714 assert_equal 2, issue.status_id
714 assert_equal 2, issue.status_id
715 j = issue.journals.find(:first, :order => 'id DESC')
715 j = issue.journals.find(:first, :order => 'id DESC')
716 assert_equal 'Assigned to dlopper', j.notes
716 assert_equal 'Assigned to dlopper', j.notes
717 assert_equal 2, j.details.size
717 assert_equal 2, j.details.size
718
718
719 mail = ActionMailer::Base.deliveries.last
719 mail = ActionMailer::Base.deliveries.last
720 assert mail.body.include?("Status changed from New to Assigned")
720 assert mail.body.include?("Status changed from New to Assigned")
721 # subject should contain the new status
721 # subject should contain the new status
722 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
722 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
723 end
723 end
724
724
725 def test_post_edit_with_note_only
725 def test_post_edit_with_note_only
726 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
726 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
727 # anonymous user
727 # anonymous user
728 post :edit,
728 post :edit,
729 :id => 1,
729 :id => 1,
730 :notes => notes
730 :notes => notes
731 assert_redirected_to :action => 'show', :id => '1'
731 assert_redirected_to :action => 'show', :id => '1'
732 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
732 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
733 assert_equal notes, j.notes
733 assert_equal notes, j.notes
734 assert_equal 0, j.details.size
734 assert_equal 0, j.details.size
735 assert_equal User.anonymous, j.user
735 assert_equal User.anonymous, j.user
736
736
737 mail = ActionMailer::Base.deliveries.last
737 mail = ActionMailer::Base.deliveries.last
738 assert mail.body.include?(notes)
738 assert mail.body.include?(notes)
739 end
739 end
740
740
741 def test_post_edit_with_note_and_spent_time
741 def test_post_edit_with_note_and_spent_time
742 @request.session[:user_id] = 2
742 @request.session[:user_id] = 2
743 spent_hours_before = Issue.find(1).spent_hours
743 spent_hours_before = Issue.find(1).spent_hours
744 assert_difference('TimeEntry.count') do
744 assert_difference('TimeEntry.count') do
745 post :edit,
745 post :edit,
746 :id => 1,
746 :id => 1,
747 :notes => '2.5 hours added',
747 :notes => '2.5 hours added',
748 :time_entry => { :hours => '2.5', :comments => '', :activity_id => Enumeration.activities.first }
748 :time_entry => { :hours => '2.5', :comments => '', :activity_id => Enumeration.activities.first }
749 end
749 end
750 assert_redirected_to :action => 'show', :id => '1'
750 assert_redirected_to :action => 'show', :id => '1'
751
751
752 issue = Issue.find(1)
752 issue = Issue.find(1)
753
753
754 j = issue.journals.find(:first, :order => 'id DESC')
754 j = issue.journals.find(:first, :order => 'id DESC')
755 assert_equal '2.5 hours added', j.notes
755 assert_equal '2.5 hours added', j.notes
756 assert_equal 0, j.details.size
756 assert_equal 0, j.details.size
757
757
758 t = issue.time_entries.find(:first, :order => 'id DESC')
758 t = issue.time_entries.find(:first, :order => 'id DESC')
759 assert_not_nil t
759 assert_not_nil t
760 assert_equal 2.5, t.hours
760 assert_equal 2.5, t.hours
761 assert_equal spent_hours_before + 2.5, issue.spent_hours
761 assert_equal spent_hours_before + 2.5, issue.spent_hours
762 end
762 end
763
763
764 def test_post_edit_with_attachment_only
764 def test_post_edit_with_attachment_only
765 set_tmp_attachments_directory
765 set_tmp_attachments_directory
766
766
767 # Delete all fixtured journals, a race condition can occur causing the wrong
767 # Delete all fixtured journals, a race condition can occur causing the wrong
768 # journal to get fetched in the next find.
768 # journal to get fetched in the next find.
769 Journal.delete_all
769 Journal.delete_all
770
770
771 # anonymous user
771 # anonymous user
772 post :edit,
772 post :edit,
773 :id => 1,
773 :id => 1,
774 :notes => '',
774 :notes => '',
775 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
775 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
776 assert_redirected_to :action => 'show', :id => '1'
776 assert_redirected_to :action => 'show', :id => '1'
777 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
777 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
778 assert j.notes.blank?
778 assert j.notes.blank?
779 assert_equal 1, j.details.size
779 assert_equal 1, j.details.size
780 assert_equal 'testfile.txt', j.details.first.value
780 assert_equal 'testfile.txt', j.details.first.value
781 assert_equal User.anonymous, j.user
781 assert_equal User.anonymous, j.user
782
782
783 mail = ActionMailer::Base.deliveries.last
783 mail = ActionMailer::Base.deliveries.last
784 assert mail.body.include?('testfile.txt')
784 assert mail.body.include?('testfile.txt')
785 end
785 end
786
786
787 def test_post_edit_with_no_change
787 def test_post_edit_with_no_change
788 issue = Issue.find(1)
788 issue = Issue.find(1)
789 issue.journals.clear
789 issue.journals.clear
790 ActionMailer::Base.deliveries.clear
790 ActionMailer::Base.deliveries.clear
791
791
792 post :edit,
792 post :edit,
793 :id => 1,
793 :id => 1,
794 :notes => ''
794 :notes => ''
795 assert_redirected_to :action => 'show', :id => '1'
795 assert_redirected_to :action => 'show', :id => '1'
796
796
797 issue.reload
797 issue.reload
798 assert issue.journals.empty?
798 assert issue.journals.empty?
799 # No email should be sent
799 # No email should be sent
800 assert ActionMailer::Base.deliveries.empty?
800 assert ActionMailer::Base.deliveries.empty?
801 end
801 end
802
802
803 def test_post_edit_should_send_a_notification
803 def test_post_edit_should_send_a_notification
804 @request.session[:user_id] = 2
804 @request.session[:user_id] = 2
805 ActionMailer::Base.deliveries.clear
805 ActionMailer::Base.deliveries.clear
806 issue = Issue.find(1)
806 issue = Issue.find(1)
807 old_subject = issue.subject
807 old_subject = issue.subject
808 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
808 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
809
809
810 post :edit, :id => 1, :issue => {:subject => new_subject,
810 post :edit, :id => 1, :issue => {:subject => new_subject,
811 :priority_id => '6',
811 :priority_id => '6',
812 :category_id => '1' # no change
812 :category_id => '1' # no change
813 }
813 }
814 assert_equal 1, ActionMailer::Base.deliveries.size
814 assert_equal 1, ActionMailer::Base.deliveries.size
815 end
815 end
816
816
817 def test_post_edit_with_invalid_spent_time
817 def test_post_edit_with_invalid_spent_time
818 @request.session[:user_id] = 2
818 @request.session[:user_id] = 2
819 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
819 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
820
820
821 assert_no_difference('Journal.count') do
821 assert_no_difference('Journal.count') do
822 post :edit,
822 post :edit,
823 :id => 1,
823 :id => 1,
824 :notes => notes,
824 :notes => notes,
825 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
825 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
826 end
826 end
827 assert_response :success
827 assert_response :success
828 assert_template 'edit'
828 assert_template 'edit'
829
829
830 assert_tag :textarea, :attributes => { :name => 'notes' },
830 assert_tag :textarea, :attributes => { :name => 'notes' },
831 :content => notes
831 :content => notes
832 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
832 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
833 end
833 end
834
835 def test_get_bulk_edit
836 @request.session[:user_id] = 2
837 get :bulk_edit, :ids => [1, 2]
838 assert_response :success
839 assert_template 'bulk_edit'
840 end
834
841
835 def test_bulk_edit
842 def test_bulk_edit
836 @request.session[:user_id] = 2
843 @request.session[:user_id] = 2
837 # update issues priority
844 # update issues priority
838 post :bulk_edit, :ids => [1, 2], :priority_id => 7,
845 post :bulk_edit, :ids => [1, 2], :priority_id => 7,
839 :assigned_to_id => '',
846 :assigned_to_id => '',
840 :custom_field_values => {'2' => ''},
847 :custom_field_values => {'2' => ''},
841 :notes => 'Bulk editing'
848 :notes => 'Bulk editing'
842 assert_response 302
849 assert_response 302
843 # check that the issues were updated
850 # check that the issues were updated
844 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
851 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
845
852
846 issue = Issue.find(1)
853 issue = Issue.find(1)
847 journal = issue.journals.find(:first, :order => 'created_on DESC')
854 journal = issue.journals.find(:first, :order => 'created_on DESC')
848 assert_equal '125', issue.custom_value_for(2).value
855 assert_equal '125', issue.custom_value_for(2).value
849 assert_equal 'Bulk editing', journal.notes
856 assert_equal 'Bulk editing', journal.notes
850 assert_equal 1, journal.details.size
857 assert_equal 1, journal.details.size
851 end
858 end
852
859
853 def test_bullk_edit_should_send_a_notification
860 def test_bullk_edit_should_send_a_notification
854 @request.session[:user_id] = 2
861 @request.session[:user_id] = 2
855 ActionMailer::Base.deliveries.clear
862 ActionMailer::Base.deliveries.clear
856 post(:bulk_edit,
863 post(:bulk_edit,
857 {
864 {
858 :ids => [1, 2],
865 :ids => [1, 2],
859 :priority_id => 7,
866 :priority_id => 7,
860 :assigned_to_id => '',
867 :assigned_to_id => '',
861 :custom_field_values => {'2' => ''},
868 :custom_field_values => {'2' => ''},
862 :notes => 'Bulk editing'
869 :notes => 'Bulk editing'
863 })
870 })
864
871
865 assert_response 302
872 assert_response 302
866 assert_equal 2, ActionMailer::Base.deliveries.size
873 assert_equal 2, ActionMailer::Base.deliveries.size
867 end
874 end
868
875
869 def test_bulk_edit_custom_field
876 def test_bulk_edit_custom_field
870 @request.session[:user_id] = 2
877 @request.session[:user_id] = 2
871 # update issues priority
878 # update issues priority
872 post :bulk_edit, :ids => [1, 2], :priority_id => '',
879 post :bulk_edit, :ids => [1, 2], :priority_id => '',
873 :assigned_to_id => '',
880 :assigned_to_id => '',
874 :custom_field_values => {'2' => '777'},
881 :custom_field_values => {'2' => '777'},
875 :notes => 'Bulk editing custom field'
882 :notes => 'Bulk editing custom field'
876 assert_response 302
883 assert_response 302
877
884
878 issue = Issue.find(1)
885 issue = Issue.find(1)
879 journal = issue.journals.find(:first, :order => 'created_on DESC')
886 journal = issue.journals.find(:first, :order => 'created_on DESC')
880 assert_equal '777', issue.custom_value_for(2).value
887 assert_equal '777', issue.custom_value_for(2).value
881 assert_equal 1, journal.details.size
888 assert_equal 1, journal.details.size
882 assert_equal '125', journal.details.first.old_value
889 assert_equal '125', journal.details.first.old_value
883 assert_equal '777', journal.details.first.value
890 assert_equal '777', journal.details.first.value
884 end
891 end
885
892
886 def test_bulk_unassign
893 def test_bulk_unassign
887 assert_not_nil Issue.find(2).assigned_to
894 assert_not_nil Issue.find(2).assigned_to
888 @request.session[:user_id] = 2
895 @request.session[:user_id] = 2
889 # unassign issues
896 # unassign issues
890 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
897 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
891 assert_response 302
898 assert_response 302
892 # check that the issues were updated
899 # check that the issues were updated
893 assert_nil Issue.find(2).assigned_to
900 assert_nil Issue.find(2).assigned_to
894 end
901 end
895
902
896 def test_move_routing
903 def test_move_routing
897 assert_routing(
904 assert_routing(
898 {:method => :get, :path => '/issues/1/move'},
905 {:method => :get, :path => '/issues/1/move'},
899 :controller => 'issues', :action => 'move', :id => '1'
906 :controller => 'issues', :action => 'move', :id => '1'
900 )
907 )
901 assert_recognizes(
908 assert_recognizes(
902 {:controller => 'issues', :action => 'move', :id => '1'},
909 {:controller => 'issues', :action => 'move', :id => '1'},
903 {:method => :post, :path => '/issues/1/move'}
910 {:method => :post, :path => '/issues/1/move'}
904 )
911 )
905 end
912 end
906
913
907 def test_move_one_issue_to_another_project
914 def test_move_one_issue_to_another_project
908 @request.session[:user_id] = 2
915 @request.session[:user_id] = 2
909 post :move, :id => 1, :new_project_id => 2
916 post :move, :id => 1, :new_project_id => 2
910 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
917 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
911 assert_equal 2, Issue.find(1).project_id
918 assert_equal 2, Issue.find(1).project_id
912 end
919 end
913
920
914 def test_bulk_move_to_another_project
921 def test_bulk_move_to_another_project
915 @request.session[:user_id] = 2
922 @request.session[:user_id] = 2
916 post :move, :ids => [1, 2], :new_project_id => 2
923 post :move, :ids => [1, 2], :new_project_id => 2
917 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
924 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
918 # Issues moved to project 2
925 # Issues moved to project 2
919 assert_equal 2, Issue.find(1).project_id
926 assert_equal 2, Issue.find(1).project_id
920 assert_equal 2, Issue.find(2).project_id
927 assert_equal 2, Issue.find(2).project_id
921 # No tracker change
928 # No tracker change
922 assert_equal 1, Issue.find(1).tracker_id
929 assert_equal 1, Issue.find(1).tracker_id
923 assert_equal 2, Issue.find(2).tracker_id
930 assert_equal 2, Issue.find(2).tracker_id
924 end
931 end
925
932
926 def test_bulk_move_to_another_tracker
933 def test_bulk_move_to_another_tracker
927 @request.session[:user_id] = 2
934 @request.session[:user_id] = 2
928 post :move, :ids => [1, 2], :new_tracker_id => 2
935 post :move, :ids => [1, 2], :new_tracker_id => 2
929 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
936 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
930 assert_equal 2, Issue.find(1).tracker_id
937 assert_equal 2, Issue.find(1).tracker_id
931 assert_equal 2, Issue.find(2).tracker_id
938 assert_equal 2, Issue.find(2).tracker_id
932 end
939 end
933
940
934 def test_bulk_copy_to_another_project
941 def test_bulk_copy_to_another_project
935 @request.session[:user_id] = 2
942 @request.session[:user_id] = 2
936 assert_difference 'Issue.count', 2 do
943 assert_difference 'Issue.count', 2 do
937 assert_no_difference 'Project.find(1).issues.count' do
944 assert_no_difference 'Project.find(1).issues.count' do
938 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}
945 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}
939 end
946 end
940 end
947 end
941 assert_redirected_to 'projects/ecookbook/issues'
948 assert_redirected_to 'projects/ecookbook/issues'
942 end
949 end
943
950
944 def test_context_menu_one_issue
951 def test_context_menu_one_issue
945 @request.session[:user_id] = 2
952 @request.session[:user_id] = 2
946 get :context_menu, :ids => [1]
953 get :context_menu, :ids => [1]
947 assert_response :success
954 assert_response :success
948 assert_template 'context_menu'
955 assert_template 'context_menu'
949 assert_tag :tag => 'a', :content => 'Edit',
956 assert_tag :tag => 'a', :content => 'Edit',
950 :attributes => { :href => '/issues/1/edit',
957 :attributes => { :href => '/issues/1/edit',
951 :class => 'icon-edit' }
958 :class => 'icon-edit' }
952 assert_tag :tag => 'a', :content => 'Closed',
959 assert_tag :tag => 'a', :content => 'Closed',
953 :attributes => { :href => '/issues/1/edit?issue%5Bstatus_id%5D=5',
960 :attributes => { :href => '/issues/1/edit?issue%5Bstatus_id%5D=5',
954 :class => '' }
961 :class => '' }
955 assert_tag :tag => 'a', :content => 'Immediate',
962 assert_tag :tag => 'a', :content => 'Immediate',
956 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;priority_id=8',
963 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;priority_id=8',
957 :class => '' }
964 :class => '' }
958 assert_tag :tag => 'a', :content => 'Dave Lopper',
965 assert_tag :tag => 'a', :content => 'Dave Lopper',
959 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1',
966 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1',
960 :class => '' }
967 :class => '' }
961 assert_tag :tag => 'a', :content => 'Copy',
968 assert_tag :tag => 'a', :content => 'Copy',
962 :attributes => { :href => '/projects/ecookbook/issues/1/copy',
969 :attributes => { :href => '/projects/ecookbook/issues/1/copy',
963 :class => 'icon-copy' }
970 :class => 'icon-copy' }
964 assert_tag :tag => 'a', :content => 'Move',
971 assert_tag :tag => 'a', :content => 'Move',
965 :attributes => { :href => '/issues/move?ids%5B%5D=1',
972 :attributes => { :href => '/issues/move?ids%5B%5D=1',
966 :class => 'icon-move' }
973 :class => 'icon-move' }
967 assert_tag :tag => 'a', :content => 'Delete',
974 assert_tag :tag => 'a', :content => 'Delete',
968 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
975 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
969 :class => 'icon-del' }
976 :class => 'icon-del' }
970 end
977 end
971
978
972 def test_context_menu_one_issue_by_anonymous
979 def test_context_menu_one_issue_by_anonymous
973 get :context_menu, :ids => [1]
980 get :context_menu, :ids => [1]
974 assert_response :success
981 assert_response :success
975 assert_template 'context_menu'
982 assert_template 'context_menu'
976 assert_tag :tag => 'a', :content => 'Delete',
983 assert_tag :tag => 'a', :content => 'Delete',
977 :attributes => { :href => '#',
984 :attributes => { :href => '#',
978 :class => 'icon-del disabled' }
985 :class => 'icon-del disabled' }
979 end
986 end
980
987
981 def test_context_menu_multiple_issues_of_same_project
988 def test_context_menu_multiple_issues_of_same_project
982 @request.session[:user_id] = 2
989 @request.session[:user_id] = 2
983 get :context_menu, :ids => [1, 2]
990 get :context_menu, :ids => [1, 2]
984 assert_response :success
991 assert_response :success
985 assert_template 'context_menu'
992 assert_template 'context_menu'
986 assert_tag :tag => 'a', :content => 'Edit',
993 assert_tag :tag => 'a', :content => 'Edit',
987 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
994 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
988 :class => 'icon-edit' }
995 :class => 'icon-edit' }
989 assert_tag :tag => 'a', :content => 'Immediate',
996 assert_tag :tag => 'a', :content => 'Immediate',
990 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;priority_id=8',
997 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;priority_id=8',
991 :class => '' }
998 :class => '' }
992 assert_tag :tag => 'a', :content => 'Dave Lopper',
999 assert_tag :tag => 'a', :content => 'Dave Lopper',
993 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
1000 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
994 :class => '' }
1001 :class => '' }
995 assert_tag :tag => 'a', :content => 'Move',
1002 assert_tag :tag => 'a', :content => 'Move',
996 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
1003 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
997 :class => 'icon-move' }
1004 :class => 'icon-move' }
998 assert_tag :tag => 'a', :content => 'Delete',
1005 assert_tag :tag => 'a', :content => 'Delete',
999 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
1006 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
1000 :class => 'icon-del' }
1007 :class => 'icon-del' }
1001 end
1008 end
1002
1009
1003 def test_context_menu_multiple_issues_of_different_project
1010 def test_context_menu_multiple_issues_of_different_project
1004 @request.session[:user_id] = 2
1011 @request.session[:user_id] = 2
1005 get :context_menu, :ids => [1, 2, 4]
1012 get :context_menu, :ids => [1, 2, 4]
1006 assert_response :success
1013 assert_response :success
1007 assert_template 'context_menu'
1014 assert_template 'context_menu'
1008 assert_tag :tag => 'a', :content => 'Delete',
1015 assert_tag :tag => 'a', :content => 'Delete',
1009 :attributes => { :href => '#',
1016 :attributes => { :href => '#',
1010 :class => 'icon-del disabled' }
1017 :class => 'icon-del disabled' }
1011 end
1018 end
1012
1019
1013 def test_destroy_routing
1020 def test_destroy_routing
1014 assert_recognizes( #TODO: use DELETE on issue URI (need to change forms)
1021 assert_recognizes( #TODO: use DELETE on issue URI (need to change forms)
1015 {:controller => 'issues', :action => 'destroy', :id => '1'},
1022 {:controller => 'issues', :action => 'destroy', :id => '1'},
1016 {:method => :post, :path => '/issues/1/destroy'}
1023 {:method => :post, :path => '/issues/1/destroy'}
1017 )
1024 )
1018 end
1025 end
1019
1026
1020 def test_destroy_issue_with_no_time_entries
1027 def test_destroy_issue_with_no_time_entries
1021 assert_nil TimeEntry.find_by_issue_id(2)
1028 assert_nil TimeEntry.find_by_issue_id(2)
1022 @request.session[:user_id] = 2
1029 @request.session[:user_id] = 2
1023 post :destroy, :id => 2
1030 post :destroy, :id => 2
1024 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1031 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1025 assert_nil Issue.find_by_id(2)
1032 assert_nil Issue.find_by_id(2)
1026 end
1033 end
1027
1034
1028 def test_destroy_issues_with_time_entries
1035 def test_destroy_issues_with_time_entries
1029 @request.session[:user_id] = 2
1036 @request.session[:user_id] = 2
1030 post :destroy, :ids => [1, 3]
1037 post :destroy, :ids => [1, 3]
1031 assert_response :success
1038 assert_response :success
1032 assert_template 'destroy'
1039 assert_template 'destroy'
1033 assert_not_nil assigns(:hours)
1040 assert_not_nil assigns(:hours)
1034 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1041 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1035 end
1042 end
1036
1043
1037 def test_destroy_issues_and_destroy_time_entries
1044 def test_destroy_issues_and_destroy_time_entries
1038 @request.session[:user_id] = 2
1045 @request.session[:user_id] = 2
1039 post :destroy, :ids => [1, 3], :todo => 'destroy'
1046 post :destroy, :ids => [1, 3], :todo => 'destroy'
1040 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1047 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1041 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1048 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1042 assert_nil TimeEntry.find_by_id([1, 2])
1049 assert_nil TimeEntry.find_by_id([1, 2])
1043 end
1050 end
1044
1051
1045 def test_destroy_issues_and_assign_time_entries_to_project
1052 def test_destroy_issues_and_assign_time_entries_to_project
1046 @request.session[:user_id] = 2
1053 @request.session[:user_id] = 2
1047 post :destroy, :ids => [1, 3], :todo => 'nullify'
1054 post :destroy, :ids => [1, 3], :todo => 'nullify'
1048 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1055 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1049 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1056 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1050 assert_nil TimeEntry.find(1).issue_id
1057 assert_nil TimeEntry.find(1).issue_id
1051 assert_nil TimeEntry.find(2).issue_id
1058 assert_nil TimeEntry.find(2).issue_id
1052 end
1059 end
1053
1060
1054 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1061 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1055 @request.session[:user_id] = 2
1062 @request.session[:user_id] = 2
1056 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1063 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1057 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1064 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1058 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1065 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1059 assert_equal 2, TimeEntry.find(1).issue_id
1066 assert_equal 2, TimeEntry.find(1).issue_id
1060 assert_equal 2, TimeEntry.find(2).issue_id
1067 assert_equal 2, TimeEntry.find(2).issue_id
1061 end
1068 end
1062 end
1069 end
General Comments 0
You need to be logged in to leave comments. Login now