##// END OF EJS Templates
Prevent NoMethodError on nil class if custom_fields params is not present in IssuesController#new (#969)....
Jean-Philippe Lang -
r1302:e4da9d6f10ed
parent child
Show More
@@ -1,417 +1,419
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 layout 'base'
19 layout 'base'
20 menu_item :new_issue, :only => :new
20 menu_item :new_issue, :only => :new
21
21
22 before_filter :find_issue, :only => [:show, :edit, :destroy_attachment]
22 before_filter :find_issue, :only => [:show, :edit, :destroy_attachment]
23 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
23 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
24 before_filter :find_project, :only => [:new, :update_form, :preview]
24 before_filter :find_project, :only => [:new, :update_form, :preview]
25 before_filter :authorize, :except => [:index, :changes, :preview, :update_form, :context_menu]
25 before_filter :authorize, :except => [:index, :changes, :preview, :update_form, :context_menu]
26 before_filter :find_optional_project, :only => [:index, :changes]
26 before_filter :find_optional_project, :only => [:index, :changes]
27 accept_key_auth :index, :changes
27 accept_key_auth :index, :changes
28
28
29 helper :journals
29 helper :journals
30 helper :projects
30 helper :projects
31 include ProjectsHelper
31 include ProjectsHelper
32 helper :custom_fields
32 helper :custom_fields
33 include CustomFieldsHelper
33 include CustomFieldsHelper
34 helper :ifpdf
34 helper :ifpdf
35 include IfpdfHelper
35 include IfpdfHelper
36 helper :issue_relations
36 helper :issue_relations
37 include IssueRelationsHelper
37 include IssueRelationsHelper
38 helper :watchers
38 helper :watchers
39 include WatchersHelper
39 include WatchersHelper
40 helper :attachments
40 helper :attachments
41 include AttachmentsHelper
41 include AttachmentsHelper
42 helper :queries
42 helper :queries
43 helper :sort
43 helper :sort
44 include SortHelper
44 include SortHelper
45 include IssuesHelper
45 include IssuesHelper
46
46
47 def index
47 def index
48 sort_init "#{Issue.table_name}.id", "desc"
48 sort_init "#{Issue.table_name}.id", "desc"
49 sort_update
49 sort_update
50 retrieve_query
50 retrieve_query
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 => sort_clause,
61 @issues = Issue.find :all, :order => sort_clause,
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 { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
67 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
68 format.atom { render_feed(@issues, :title => l(:label_issue_plural)) }
68 format.atom { render_feed(@issues, :title => l(:label_issue_plural)) }
69 format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
69 format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
70 format.pdf { send_data(render(:template => 'issues/index.rfpdf', :layout => false), :type => 'application/pdf', :filename => 'export.pdf') }
70 format.pdf { send_data(render(:template => 'issues/index.rfpdf', :layout => false), :type => 'application/pdf', :filename => 'export.pdf') }
71 end
71 end
72 else
72 else
73 # Send html if the query is not valid
73 # Send html if the query is not valid
74 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
74 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
75 end
75 end
76 rescue ActiveRecord::RecordNotFound
76 rescue ActiveRecord::RecordNotFound
77 render_404
77 render_404
78 end
78 end
79
79
80 def changes
80 def changes
81 sort_init "#{Issue.table_name}.id", "desc"
81 sort_init "#{Issue.table_name}.id", "desc"
82 sort_update
82 sort_update
83 retrieve_query
83 retrieve_query
84 if @query.valid?
84 if @query.valid?
85 @journals = Journal.find :all, :include => [ :details, :user, {:issue => [:project, :author, :tracker, :status]} ],
85 @journals = Journal.find :all, :include => [ :details, :user, {:issue => [:project, :author, :tracker, :status]} ],
86 :conditions => @query.statement,
86 :conditions => @query.statement,
87 :limit => 25,
87 :limit => 25,
88 :order => "#{Journal.table_name}.created_on DESC"
88 :order => "#{Journal.table_name}.created_on DESC"
89 end
89 end
90 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
90 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
91 render :layout => false, :content_type => 'application/atom+xml'
91 render :layout => false, :content_type => 'application/atom+xml'
92 rescue ActiveRecord::RecordNotFound
92 rescue ActiveRecord::RecordNotFound
93 render_404
93 render_404
94 end
94 end
95
95
96 def show
96 def show
97 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
97 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
98 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
98 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
99 @journals.each_with_index {|j,i| j.indice = i+1}
99 @journals.each_with_index {|j,i| j.indice = i+1}
100 @journals.reverse! if User.current.wants_comments_in_reverse_order?
100 @journals.reverse! if User.current.wants_comments_in_reverse_order?
101 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
101 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
102 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
102 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
103 @activities = Enumeration::get_values('ACTI')
103 @activities = Enumeration::get_values('ACTI')
104 @priorities = Enumeration::get_values('IPRI')
104 @priorities = Enumeration::get_values('IPRI')
105 respond_to do |format|
105 respond_to do |format|
106 format.html { render :template => 'issues/show.rhtml' }
106 format.html { render :template => 'issues/show.rhtml' }
107 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
107 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
108 format.pdf { send_data(render(:template => 'issues/show.rfpdf', :layout => false), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
108 format.pdf { send_data(render(:template => 'issues/show.rfpdf', :layout => false), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
109 end
109 end
110 end
110 end
111
111
112 # Add a new issue
112 # Add a new issue
113 # The new issue will be created from an existing one if copy_from parameter is given
113 # The new issue will be created from an existing one if copy_from parameter is given
114 def new
114 def new
115 @issue = params[:copy_from] ? Issue.new.copy_from(params[:copy_from]) : Issue.new(params[:issue])
115 @issue = params[:copy_from] ? Issue.new.copy_from(params[:copy_from]) : Issue.new(params[:issue])
116 @issue.project = @project
116 @issue.project = @project
117 @issue.author = User.current
117 @issue.author = User.current
118 @issue.tracker ||= @project.trackers.find(params[:tracker_id] ? params[:tracker_id] : :first)
118 @issue.tracker ||= @project.trackers.find(params[:tracker_id] ? params[:tracker_id] : :first)
119 if @issue.tracker.nil?
119 if @issue.tracker.nil?
120 flash.now[:error] = 'No tracker is associated to this project. Please check the Project settings.'
120 flash.now[:error] = 'No tracker is associated to this project. Please check the Project settings.'
121 render :nothing => true, :layout => true
121 render :nothing => true, :layout => true
122 return
122 return
123 end
123 end
124
124
125 default_status = IssueStatus.default
125 default_status = IssueStatus.default
126 unless default_status
126 unless default_status
127 flash.now[:error] = 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
127 flash.now[:error] = 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
128 render :nothing => true, :layout => true
128 render :nothing => true, :layout => true
129 return
129 return
130 end
130 end
131 @issue.status = default_status
131 @issue.status = default_status
132 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker)).uniq
132 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker)).uniq
133
133
134 if request.get? || request.xhr?
134 if request.get? || request.xhr?
135 @issue.start_date ||= Date.today
135 @issue.start_date ||= Date.today
136 @custom_values = @issue.custom_values.empty? ?
136 @custom_values = @issue.custom_values.empty? ?
137 @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) } :
137 @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) } :
138 @issue.custom_values
138 @issue.custom_values
139 else
139 else
140 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
140 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
141 # Check that the user is allowed to apply the requested status
141 # Check that the user is allowed to apply the requested status
142 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
142 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
143 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
143 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x,
144 :customized => @issue,
145 :value => (params[:custom_fields] ? params[:custom_fields][x.id.to_s] : nil)) }
144 @issue.custom_values = @custom_values
146 @issue.custom_values = @custom_values
145 if @issue.save
147 if @issue.save
146 attach_files(@issue, params[:attachments])
148 attach_files(@issue, params[:attachments])
147 flash[:notice] = l(:notice_successful_create)
149 flash[:notice] = l(:notice_successful_create)
148 Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
150 Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
149 redirect_to :controller => 'issues', :action => 'show', :id => @issue, :project_id => @project
151 redirect_to :controller => 'issues', :action => 'show', :id => @issue, :project_id => @project
150 return
152 return
151 end
153 end
152 end
154 end
153 @priorities = Enumeration::get_values('IPRI')
155 @priorities = Enumeration::get_values('IPRI')
154 render :layout => !request.xhr?
156 render :layout => !request.xhr?
155 end
157 end
156
158
157 # Attributes that can be updated on workflow transition (without :edit permission)
159 # Attributes that can be updated on workflow transition (without :edit permission)
158 # TODO: make it configurable (at least per role)
160 # TODO: make it configurable (at least per role)
159 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
161 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
160
162
161 def edit
163 def edit
162 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
164 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
163 @activities = Enumeration::get_values('ACTI')
165 @activities = Enumeration::get_values('ACTI')
164 @priorities = Enumeration::get_values('IPRI')
166 @priorities = Enumeration::get_values('IPRI')
165 @custom_values = []
167 @custom_values = []
166 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
168 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
167
169
168 @notes = params[:notes]
170 @notes = params[:notes]
169 journal = @issue.init_journal(User.current, @notes)
171 journal = @issue.init_journal(User.current, @notes)
170 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
172 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
171 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
173 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
172 attrs = params[:issue].dup
174 attrs = params[:issue].dup
173 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
175 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
174 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
176 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
175 @issue.attributes = attrs
177 @issue.attributes = attrs
176 end
178 end
177
179
178 if request.get?
180 if request.get?
179 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
181 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
180 else
182 else
181 # Update custom fields if user has :edit permission
183 # Update custom fields if user has :edit permission
182 if @edit_allowed && params[:custom_fields]
184 if @edit_allowed && params[:custom_fields]
183 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
185 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
184 @issue.custom_values = @custom_values
186 @issue.custom_values = @custom_values
185 end
187 end
186 attachments = attach_files(@issue, params[:attachments])
188 attachments = attach_files(@issue, params[:attachments])
187 attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
189 attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
188 if @issue.save
190 if @issue.save
189 # Log spend time
191 # Log spend time
190 if current_role.allowed_to?(:log_time)
192 if current_role.allowed_to?(:log_time)
191 @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
193 @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
192 @time_entry.attributes = params[:time_entry]
194 @time_entry.attributes = params[:time_entry]
193 @time_entry.save
195 @time_entry.save
194 end
196 end
195 if !journal.new_record?
197 if !journal.new_record?
196 # Only send notification if something was actually changed
198 # Only send notification if something was actually changed
197 flash[:notice] = l(:notice_successful_update)
199 flash[:notice] = l(:notice_successful_update)
198 Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
200 Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
199 end
201 end
200 redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
202 redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
201 end
203 end
202 end
204 end
203 rescue ActiveRecord::StaleObjectError
205 rescue ActiveRecord::StaleObjectError
204 # Optimistic locking exception
206 # Optimistic locking exception
205 flash.now[:error] = l(:notice_locking_conflict)
207 flash.now[:error] = l(:notice_locking_conflict)
206 end
208 end
207
209
208 # Bulk edit a set of issues
210 # Bulk edit a set of issues
209 def bulk_edit
211 def bulk_edit
210 if request.post?
212 if request.post?
211 status = params[:status_id].blank? ? nil : IssueStatus.find_by_id(params[:status_id])
213 status = params[:status_id].blank? ? nil : IssueStatus.find_by_id(params[:status_id])
212 priority = params[:priority_id].blank? ? nil : Enumeration.find_by_id(params[:priority_id])
214 priority = params[:priority_id].blank? ? nil : Enumeration.find_by_id(params[:priority_id])
213 assigned_to = (params[:assigned_to_id].blank? || params[:assigned_to_id] == 'none') ? nil : User.find_by_id(params[:assigned_to_id])
215 assigned_to = (params[:assigned_to_id].blank? || params[:assigned_to_id] == 'none') ? nil : User.find_by_id(params[:assigned_to_id])
214 category = (params[:category_id].blank? || params[:category_id] == 'none') ? nil : @project.issue_categories.find_by_id(params[:category_id])
216 category = (params[:category_id].blank? || params[:category_id] == 'none') ? nil : @project.issue_categories.find_by_id(params[:category_id])
215 fixed_version = (params[:fixed_version_id].blank? || params[:fixed_version_id] == 'none') ? nil : @project.versions.find_by_id(params[:fixed_version_id])
217 fixed_version = (params[:fixed_version_id].blank? || params[:fixed_version_id] == 'none') ? nil : @project.versions.find_by_id(params[:fixed_version_id])
216
218
217 unsaved_issue_ids = []
219 unsaved_issue_ids = []
218 @issues.each do |issue|
220 @issues.each do |issue|
219 journal = issue.init_journal(User.current, params[:notes])
221 journal = issue.init_journal(User.current, params[:notes])
220 issue.priority = priority if priority
222 issue.priority = priority if priority
221 issue.assigned_to = assigned_to if assigned_to || params[:assigned_to_id] == 'none'
223 issue.assigned_to = assigned_to if assigned_to || params[:assigned_to_id] == 'none'
222 issue.category = category if category || params[:category_id] == 'none'
224 issue.category = category if category || params[:category_id] == 'none'
223 issue.fixed_version = fixed_version if fixed_version || params[:fixed_version_id] == 'none'
225 issue.fixed_version = fixed_version if fixed_version || params[:fixed_version_id] == 'none'
224 issue.start_date = params[:start_date] unless params[:start_date].blank?
226 issue.start_date = params[:start_date] unless params[:start_date].blank?
225 issue.due_date = params[:due_date] unless params[:due_date].blank?
227 issue.due_date = params[:due_date] unless params[:due_date].blank?
226 issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
228 issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
227 # Don't save any change to the issue if the user is not authorized to apply the requested status
229 # Don't save any change to the issue if the user is not authorized to apply the requested status
228 if (status.nil? || (issue.status.new_status_allowed_to?(status, current_role, issue.tracker) && issue.status = status)) && issue.save
230 if (status.nil? || (issue.status.new_status_allowed_to?(status, current_role, issue.tracker) && issue.status = status)) && issue.save
229 # Send notification for each issue (if changed)
231 # Send notification for each issue (if changed)
230 Mailer.deliver_issue_edit(journal) if journal.details.any? && Setting.notified_events.include?('issue_updated')
232 Mailer.deliver_issue_edit(journal) if journal.details.any? && Setting.notified_events.include?('issue_updated')
231 else
233 else
232 # Keep unsaved issue ids to display them in flash error
234 # Keep unsaved issue ids to display them in flash error
233 unsaved_issue_ids << issue.id
235 unsaved_issue_ids << issue.id
234 end
236 end
235 end
237 end
236 if unsaved_issue_ids.empty?
238 if unsaved_issue_ids.empty?
237 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
239 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
238 else
240 else
239 flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
241 flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
240 end
242 end
241 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
243 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
242 return
244 return
243 end
245 end
244 # Find potential statuses the user could be allowed to switch issues to
246 # Find potential statuses the user could be allowed to switch issues to
245 @available_statuses = Workflow.find(:all, :include => :new_status,
247 @available_statuses = Workflow.find(:all, :include => :new_status,
246 :conditions => {:role_id => current_role.id}).collect(&:new_status).compact.uniq
248 :conditions => {:role_id => current_role.id}).collect(&:new_status).compact.uniq
247 end
249 end
248
250
249 def move
251 def move
250 @allowed_projects = []
252 @allowed_projects = []
251 # find projects to which the user is allowed to move the issue
253 # find projects to which the user is allowed to move the issue
252 if User.current.admin?
254 if User.current.admin?
253 # admin is allowed to move issues to any active (visible) project
255 # admin is allowed to move issues to any active (visible) project
254 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current), :order => 'name')
256 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current), :order => 'name')
255 else
257 else
256 User.current.memberships.each {|m| @allowed_projects << m.project if m.role.allowed_to?(:move_issues)}
258 User.current.memberships.each {|m| @allowed_projects << m.project if m.role.allowed_to?(:move_issues)}
257 end
259 end
258 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
260 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
259 @target_project ||= @project
261 @target_project ||= @project
260 @trackers = @target_project.trackers
262 @trackers = @target_project.trackers
261 if request.post?
263 if request.post?
262 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
264 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
263 unsaved_issue_ids = []
265 unsaved_issue_ids = []
264 @issues.each do |issue|
266 @issues.each do |issue|
265 unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker)
267 unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker)
266 end
268 end
267 if unsaved_issue_ids.empty?
269 if unsaved_issue_ids.empty?
268 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
270 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
269 else
271 else
270 flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
272 flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
271 end
273 end
272 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
274 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
273 return
275 return
274 end
276 end
275 render :layout => false if request.xhr?
277 render :layout => false if request.xhr?
276 end
278 end
277
279
278 def destroy
280 def destroy
279 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
281 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
280 if @hours > 0
282 if @hours > 0
281 case params[:todo]
283 case params[:todo]
282 when 'destroy'
284 when 'destroy'
283 # nothing to do
285 # nothing to do
284 when 'nullify'
286 when 'nullify'
285 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
287 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
286 when 'reassign'
288 when 'reassign'
287 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
289 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
288 if reassign_to.nil?
290 if reassign_to.nil?
289 flash.now[:error] = l(:error_issue_not_found_in_project)
291 flash.now[:error] = l(:error_issue_not_found_in_project)
290 return
292 return
291 else
293 else
292 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
294 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
293 end
295 end
294 else
296 else
295 # display the destroy form
297 # display the destroy form
296 return
298 return
297 end
299 end
298 end
300 end
299 @issues.each(&:destroy)
301 @issues.each(&:destroy)
300 redirect_to :action => 'index', :project_id => @project
302 redirect_to :action => 'index', :project_id => @project
301 end
303 end
302
304
303 def destroy_attachment
305 def destroy_attachment
304 a = @issue.attachments.find(params[:attachment_id])
306 a = @issue.attachments.find(params[:attachment_id])
305 a.destroy
307 a.destroy
306 journal = @issue.init_journal(User.current)
308 journal = @issue.init_journal(User.current)
307 journal.details << JournalDetail.new(:property => 'attachment',
309 journal.details << JournalDetail.new(:property => 'attachment',
308 :prop_key => a.id,
310 :prop_key => a.id,
309 :old_value => a.filename)
311 :old_value => a.filename)
310 journal.save
312 journal.save
311 redirect_to :action => 'show', :id => @issue
313 redirect_to :action => 'show', :id => @issue
312 end
314 end
313
315
314 def context_menu
316 def context_menu
315 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
317 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
316 if (@issues.size == 1)
318 if (@issues.size == 1)
317 @issue = @issues.first
319 @issue = @issues.first
318 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
320 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
319 @assignables = @issue.assignable_users
321 @assignables = @issue.assignable_users
320 @assignables << @issue.assigned_to if @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
322 @assignables << @issue.assigned_to if @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
321 end
323 end
322 projects = @issues.collect(&:project).compact.uniq
324 projects = @issues.collect(&:project).compact.uniq
323 @project = projects.first if projects.size == 1
325 @project = projects.first if projects.size == 1
324
326
325 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
327 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
326 :update => (@issue && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && !@allowed_statuses.empty?))),
328 :update => (@issue && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && !@allowed_statuses.empty?))),
327 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
329 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
328 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
330 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
329 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
331 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
330 }
332 }
331
333
332 @priorities = Enumeration.get_values('IPRI').reverse
334 @priorities = Enumeration.get_values('IPRI').reverse
333 @statuses = IssueStatus.find(:all, :order => 'position')
335 @statuses = IssueStatus.find(:all, :order => 'position')
334 @back = request.env['HTTP_REFERER']
336 @back = request.env['HTTP_REFERER']
335
337
336 render :layout => false
338 render :layout => false
337 end
339 end
338
340
339 def update_form
341 def update_form
340 @issue = Issue.new(params[:issue])
342 @issue = Issue.new(params[:issue])
341 render :action => :new, :layout => false
343 render :action => :new, :layout => false
342 end
344 end
343
345
344 def preview
346 def preview
345 issue = @project.issues.find_by_id(params[:id])
347 issue = @project.issues.find_by_id(params[:id])
346 @attachements = issue.attachments if issue
348 @attachements = issue.attachments if issue
347 @text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
349 @text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
348 render :partial => 'common/preview'
350 render :partial => 'common/preview'
349 end
351 end
350
352
351 private
353 private
352 def find_issue
354 def find_issue
353 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
355 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
354 @project = @issue.project
356 @project = @issue.project
355 rescue ActiveRecord::RecordNotFound
357 rescue ActiveRecord::RecordNotFound
356 render_404
358 render_404
357 end
359 end
358
360
359 # Filter for bulk operations
361 # Filter for bulk operations
360 def find_issues
362 def find_issues
361 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
363 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
362 raise ActiveRecord::RecordNotFound if @issues.empty?
364 raise ActiveRecord::RecordNotFound if @issues.empty?
363 projects = @issues.collect(&:project).compact.uniq
365 projects = @issues.collect(&:project).compact.uniq
364 if projects.size == 1
366 if projects.size == 1
365 @project = projects.first
367 @project = projects.first
366 else
368 else
367 # TODO: let users bulk edit/move/destroy issues from different projects
369 # TODO: let users bulk edit/move/destroy issues from different projects
368 render_error 'Can not bulk edit/move/destroy issues from different projects' and return false
370 render_error 'Can not bulk edit/move/destroy issues from different projects' and return false
369 end
371 end
370 rescue ActiveRecord::RecordNotFound
372 rescue ActiveRecord::RecordNotFound
371 render_404
373 render_404
372 end
374 end
373
375
374 def find_project
376 def find_project
375 @project = Project.find(params[:project_id])
377 @project = Project.find(params[:project_id])
376 rescue ActiveRecord::RecordNotFound
378 rescue ActiveRecord::RecordNotFound
377 render_404
379 render_404
378 end
380 end
379
381
380 def find_optional_project
382 def find_optional_project
381 return true unless params[:project_id]
383 return true unless params[:project_id]
382 @project = Project.find(params[:project_id])
384 @project = Project.find(params[:project_id])
383 authorize
385 authorize
384 rescue ActiveRecord::RecordNotFound
386 rescue ActiveRecord::RecordNotFound
385 render_404
387 render_404
386 end
388 end
387
389
388 # Retrieve query from session or build a new query
390 # Retrieve query from session or build a new query
389 def retrieve_query
391 def retrieve_query
390 if !params[:query_id].blank?
392 if !params[:query_id].blank?
391 cond = "project_id IS NULL"
393 cond = "project_id IS NULL"
392 cond << " OR project_id = #{@project.id}" if @project
394 cond << " OR project_id = #{@project.id}" if @project
393 @query = Query.find(params[:query_id], :conditions => cond)
395 @query = Query.find(params[:query_id], :conditions => cond)
394 @query.project = @project
396 @query.project = @project
395 session[:query] = {:id => @query.id, :project_id => @query.project_id}
397 session[:query] = {:id => @query.id, :project_id => @query.project_id}
396 else
398 else
397 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
399 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
398 # Give it a name, required to be valid
400 # Give it a name, required to be valid
399 @query = Query.new(:name => "_")
401 @query = Query.new(:name => "_")
400 @query.project = @project
402 @query.project = @project
401 if params[:fields] and params[:fields].is_a? Array
403 if params[:fields] and params[:fields].is_a? Array
402 params[:fields].each do |field|
404 params[:fields].each do |field|
403 @query.add_filter(field, params[:operators][field], params[:values][field])
405 @query.add_filter(field, params[:operators][field], params[:values][field])
404 end
406 end
405 else
407 else
406 @query.available_filters.keys.each do |field|
408 @query.available_filters.keys.each do |field|
407 @query.add_short_filter(field, params[field]) if params[field]
409 @query.add_short_filter(field, params[field]) if params[field]
408 end
410 end
409 end
411 end
410 session[:query] = {:project_id => @query.project_id, :filters => @query.filters}
412 session[:query] = {:project_id => @query.project_id, :filters => @query.filters}
411 else
413 else
412 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
414 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
413 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters])
415 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters])
414 end
416 end
415 end
417 end
416 end
418 end
417 end
419 end
@@ -1,496 +1,506
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 :issues,
29 :issues,
30 :issue_statuses,
30 :issue_statuses,
31 :trackers,
31 :trackers,
32 :projects_trackers,
32 :projects_trackers,
33 :issue_categories,
33 :issue_categories,
34 :enabled_modules,
34 :enabled_modules,
35 :enumerations,
35 :enumerations,
36 :attachments,
36 :attachments,
37 :workflows,
37 :workflows,
38 :custom_fields,
38 :custom_fields,
39 :custom_values,
39 :custom_values,
40 :custom_fields_trackers,
40 :custom_fields_trackers,
41 :time_entries
41 :time_entries
42
42
43 def setup
43 def setup
44 @controller = IssuesController.new
44 @controller = IssuesController.new
45 @request = ActionController::TestRequest.new
45 @request = ActionController::TestRequest.new
46 @response = ActionController::TestResponse.new
46 @response = ActionController::TestResponse.new
47 User.current = nil
47 User.current = nil
48 end
48 end
49
49
50 def test_index
50 def test_index
51 get :index
51 get :index
52 assert_response :success
52 assert_response :success
53 assert_template 'index.rhtml'
53 assert_template 'index.rhtml'
54 assert_not_nil assigns(:issues)
54 assert_not_nil assigns(:issues)
55 assert_nil assigns(:project)
55 assert_nil assigns(:project)
56 end
56 end
57
57
58 def test_index_with_project
58 def test_index_with_project
59 get :index, :project_id => 1
59 get :index, :project_id => 1
60 assert_response :success
60 assert_response :success
61 assert_template 'index.rhtml'
61 assert_template 'index.rhtml'
62 assert_not_nil assigns(:issues)
62 assert_not_nil assigns(:issues)
63 end
63 end
64
64
65 def test_index_with_project_and_filter
65 def test_index_with_project_and_filter
66 get :index, :project_id => 1, :set_filter => 1
66 get :index, :project_id => 1, :set_filter => 1
67 assert_response :success
67 assert_response :success
68 assert_template 'index.rhtml'
68 assert_template 'index.rhtml'
69 assert_not_nil assigns(:issues)
69 assert_not_nil assigns(:issues)
70 end
70 end
71
71
72 def test_index_csv_with_project
72 def test_index_csv_with_project
73 get :index, :format => 'csv'
73 get :index, :format => 'csv'
74 assert_response :success
74 assert_response :success
75 assert_not_nil assigns(:issues)
75 assert_not_nil assigns(:issues)
76 assert_equal 'text/csv', @response.content_type
76 assert_equal 'text/csv', @response.content_type
77
77
78 get :index, :project_id => 1, :format => 'csv'
78 get :index, :project_id => 1, :format => 'csv'
79 assert_response :success
79 assert_response :success
80 assert_not_nil assigns(:issues)
80 assert_not_nil assigns(:issues)
81 assert_equal 'text/csv', @response.content_type
81 assert_equal 'text/csv', @response.content_type
82 end
82 end
83
83
84 def test_index_pdf
84 def test_index_pdf
85 get :index, :format => 'pdf'
85 get :index, :format => 'pdf'
86 assert_response :success
86 assert_response :success
87 assert_not_nil assigns(:issues)
87 assert_not_nil assigns(:issues)
88 assert_equal 'application/pdf', @response.content_type
88 assert_equal 'application/pdf', @response.content_type
89
89
90 get :index, :project_id => 1, :format => 'pdf'
90 get :index, :project_id => 1, :format => 'pdf'
91 assert_response :success
91 assert_response :success
92 assert_not_nil assigns(:issues)
92 assert_not_nil assigns(:issues)
93 assert_equal 'application/pdf', @response.content_type
93 assert_equal 'application/pdf', @response.content_type
94 end
94 end
95
95
96 def test_changes
96 def test_changes
97 get :changes, :project_id => 1
97 get :changes, :project_id => 1
98 assert_response :success
98 assert_response :success
99 assert_not_nil assigns(:journals)
99 assert_not_nil assigns(:journals)
100 assert_equal 'application/atom+xml', @response.content_type
100 assert_equal 'application/atom+xml', @response.content_type
101 end
101 end
102
102
103 def test_show_by_anonymous
103 def test_show_by_anonymous
104 get :show, :id => 1
104 get :show, :id => 1
105 assert_response :success
105 assert_response :success
106 assert_template 'show.rhtml'
106 assert_template 'show.rhtml'
107 assert_not_nil assigns(:issue)
107 assert_not_nil assigns(:issue)
108 assert_equal Issue.find(1), assigns(:issue)
108 assert_equal Issue.find(1), assigns(:issue)
109
109
110 # anonymous role is allowed to add a note
110 # anonymous role is allowed to add a note
111 assert_tag :tag => 'form',
111 assert_tag :tag => 'form',
112 :descendant => { :tag => 'fieldset',
112 :descendant => { :tag => 'fieldset',
113 :child => { :tag => 'legend',
113 :child => { :tag => 'legend',
114 :content => /Notes/ } }
114 :content => /Notes/ } }
115 end
115 end
116
116
117 def test_show_by_manager
117 def test_show_by_manager
118 @request.session[:user_id] = 2
118 @request.session[:user_id] = 2
119 get :show, :id => 1
119 get :show, :id => 1
120 assert_response :success
120 assert_response :success
121
121
122 assert_tag :tag => 'form',
122 assert_tag :tag => 'form',
123 :descendant => { :tag => 'fieldset',
123 :descendant => { :tag => 'fieldset',
124 :child => { :tag => 'legend',
124 :child => { :tag => 'legend',
125 :content => /Change properties/ } },
125 :content => /Change properties/ } },
126 :descendant => { :tag => 'fieldset',
126 :descendant => { :tag => 'fieldset',
127 :child => { :tag => 'legend',
127 :child => { :tag => 'legend',
128 :content => /Log time/ } },
128 :content => /Log time/ } },
129 :descendant => { :tag => 'fieldset',
129 :descendant => { :tag => 'fieldset',
130 :child => { :tag => 'legend',
130 :child => { :tag => 'legend',
131 :content => /Notes/ } }
131 :content => /Notes/ } }
132 end
132 end
133
133
134 def test_get_new
134 def test_get_new
135 @request.session[:user_id] = 2
135 @request.session[:user_id] = 2
136 get :new, :project_id => 1, :tracker_id => 1
136 get :new, :project_id => 1, :tracker_id => 1
137 assert_response :success
137 assert_response :success
138 assert_template 'new'
138 assert_template 'new'
139
139
140 assert_tag :tag => 'input', :attributes => { :name => 'custom_fields[2]',
140 assert_tag :tag => 'input', :attributes => { :name => 'custom_fields[2]',
141 :value => 'Default string' }
141 :value => 'Default string' }
142 end
142 end
143
143
144 def test_get_new_without_tracker_id
144 def test_get_new_without_tracker_id
145 @request.session[:user_id] = 2
145 @request.session[:user_id] = 2
146 get :new, :project_id => 1
146 get :new, :project_id => 1
147 assert_response :success
147 assert_response :success
148 assert_template 'new'
148 assert_template 'new'
149
149
150 issue = assigns(:issue)
150 issue = assigns(:issue)
151 assert_not_nil issue
151 assert_not_nil issue
152 assert_equal Project.find(1).trackers.first, issue.tracker
152 assert_equal Project.find(1).trackers.first, issue.tracker
153 end
153 end
154
154
155 def test_update_new_form
155 def test_update_new_form
156 @request.session[:user_id] = 2
156 @request.session[:user_id] = 2
157 xhr :post, :new, :project_id => 1,
157 xhr :post, :new, :project_id => 1,
158 :issue => {:tracker_id => 2,
158 :issue => {:tracker_id => 2,
159 :subject => 'This is the test_new issue',
159 :subject => 'This is the test_new issue',
160 :description => 'This is the description',
160 :description => 'This is the description',
161 :priority_id => 5}
161 :priority_id => 5}
162 assert_response :success
162 assert_response :success
163 assert_template 'new'
163 assert_template 'new'
164 end
164 end
165
165
166 def test_post_new
166 def test_post_new
167 @request.session[:user_id] = 2
167 @request.session[:user_id] = 2
168 post :new, :project_id => 1,
168 post :new, :project_id => 1,
169 :issue => {:tracker_id => 1,
169 :issue => {:tracker_id => 1,
170 :subject => 'This is the test_new issue',
170 :subject => 'This is the test_new issue',
171 :description => 'This is the description',
171 :description => 'This is the description',
172 :priority_id => 5},
172 :priority_id => 5},
173 :custom_fields => {'2' => 'Value for field 2'}
173 :custom_fields => {'2' => 'Value for field 2'}
174 assert_redirected_to 'issues/show'
174 assert_redirected_to 'issues/show'
175
175
176 issue = Issue.find_by_subject('This is the test_new issue')
176 issue = Issue.find_by_subject('This is the test_new issue')
177 assert_not_nil issue
177 assert_not_nil issue
178 assert_equal 2, issue.author_id
178 assert_equal 2, issue.author_id
179 v = issue.custom_values.find_by_custom_field_id(2)
179 v = issue.custom_values.find_by_custom_field_id(2)
180 assert_not_nil v
180 assert_not_nil v
181 assert_equal 'Value for field 2', v.value
181 assert_equal 'Value for field 2', v.value
182 end
182 end
183
183
184 def test_post_new_without_custom_fields_param
185 @request.session[:user_id] = 2
186 post :new, :project_id => 1,
187 :issue => {:tracker_id => 1,
188 :subject => 'This is the test_new issue',
189 :description => 'This is the description',
190 :priority_id => 5}
191 assert_redirected_to 'issues/show'
192 end
193
184 def test_copy_issue
194 def test_copy_issue
185 @request.session[:user_id] = 2
195 @request.session[:user_id] = 2
186 get :new, :project_id => 1, :copy_from => 1
196 get :new, :project_id => 1, :copy_from => 1
187 assert_template 'new'
197 assert_template 'new'
188 assert_not_nil assigns(:issue)
198 assert_not_nil assigns(:issue)
189 orig = Issue.find(1)
199 orig = Issue.find(1)
190 assert_equal orig.subject, assigns(:issue).subject
200 assert_equal orig.subject, assigns(:issue).subject
191 end
201 end
192
202
193 def test_get_edit
203 def test_get_edit
194 @request.session[:user_id] = 2
204 @request.session[:user_id] = 2
195 get :edit, :id => 1
205 get :edit, :id => 1
196 assert_response :success
206 assert_response :success
197 assert_template 'edit'
207 assert_template 'edit'
198 assert_not_nil assigns(:issue)
208 assert_not_nil assigns(:issue)
199 assert_equal Issue.find(1), assigns(:issue)
209 assert_equal Issue.find(1), assigns(:issue)
200 end
210 end
201
211
202 def test_get_edit_with_params
212 def test_get_edit_with_params
203 @request.session[:user_id] = 2
213 @request.session[:user_id] = 2
204 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
214 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
205 assert_response :success
215 assert_response :success
206 assert_template 'edit'
216 assert_template 'edit'
207
217
208 issue = assigns(:issue)
218 issue = assigns(:issue)
209 assert_not_nil issue
219 assert_not_nil issue
210
220
211 assert_equal 5, issue.status_id
221 assert_equal 5, issue.status_id
212 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
222 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
213 :child => { :tag => 'option',
223 :child => { :tag => 'option',
214 :content => 'Closed',
224 :content => 'Closed',
215 :attributes => { :selected => 'selected' } }
225 :attributes => { :selected => 'selected' } }
216
226
217 assert_equal 7, issue.priority_id
227 assert_equal 7, issue.priority_id
218 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
228 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
219 :child => { :tag => 'option',
229 :child => { :tag => 'option',
220 :content => 'Urgent',
230 :content => 'Urgent',
221 :attributes => { :selected => 'selected' } }
231 :attributes => { :selected => 'selected' } }
222 end
232 end
223
233
224 def test_post_edit
234 def test_post_edit
225 @request.session[:user_id] = 2
235 @request.session[:user_id] = 2
226 ActionMailer::Base.deliveries.clear
236 ActionMailer::Base.deliveries.clear
227
237
228 issue = Issue.find(1)
238 issue = Issue.find(1)
229 old_subject = issue.subject
239 old_subject = issue.subject
230 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
240 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
231
241
232 post :edit, :id => 1, :issue => {:subject => new_subject}
242 post :edit, :id => 1, :issue => {:subject => new_subject}
233 assert_redirected_to 'issues/show/1'
243 assert_redirected_to 'issues/show/1'
234 issue.reload
244 issue.reload
235 assert_equal new_subject, issue.subject
245 assert_equal new_subject, issue.subject
236
246
237 mail = ActionMailer::Base.deliveries.last
247 mail = ActionMailer::Base.deliveries.last
238 assert_kind_of TMail::Mail, mail
248 assert_kind_of TMail::Mail, mail
239 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
249 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
240 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
250 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
241 end
251 end
242
252
243 def test_post_edit_with_status_and_assignee_change
253 def test_post_edit_with_status_and_assignee_change
244 issue = Issue.find(1)
254 issue = Issue.find(1)
245 assert_equal 1, issue.status_id
255 assert_equal 1, issue.status_id
246 @request.session[:user_id] = 2
256 @request.session[:user_id] = 2
247 post :edit,
257 post :edit,
248 :id => 1,
258 :id => 1,
249 :issue => { :status_id => 2, :assigned_to_id => 3 },
259 :issue => { :status_id => 2, :assigned_to_id => 3 },
250 :notes => 'Assigned to dlopper'
260 :notes => 'Assigned to dlopper'
251 assert_redirected_to 'issues/show/1'
261 assert_redirected_to 'issues/show/1'
252 issue.reload
262 issue.reload
253 assert_equal 2, issue.status_id
263 assert_equal 2, issue.status_id
254 j = issue.journals.find(:first, :order => 'id DESC')
264 j = issue.journals.find(:first, :order => 'id DESC')
255 assert_equal 'Assigned to dlopper', j.notes
265 assert_equal 'Assigned to dlopper', j.notes
256 assert_equal 2, j.details.size
266 assert_equal 2, j.details.size
257
267
258 mail = ActionMailer::Base.deliveries.last
268 mail = ActionMailer::Base.deliveries.last
259 assert mail.body.include?("Status changed from New to Assigned")
269 assert mail.body.include?("Status changed from New to Assigned")
260 end
270 end
261
271
262 def test_post_edit_with_note_only
272 def test_post_edit_with_note_only
263 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
273 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
264 # anonymous user
274 # anonymous user
265 post :edit,
275 post :edit,
266 :id => 1,
276 :id => 1,
267 :notes => notes
277 :notes => notes
268 assert_redirected_to 'issues/show/1'
278 assert_redirected_to 'issues/show/1'
269 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
279 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
270 assert_equal notes, j.notes
280 assert_equal notes, j.notes
271 assert_equal 0, j.details.size
281 assert_equal 0, j.details.size
272 assert_equal User.anonymous, j.user
282 assert_equal User.anonymous, j.user
273
283
274 mail = ActionMailer::Base.deliveries.last
284 mail = ActionMailer::Base.deliveries.last
275 assert mail.body.include?(notes)
285 assert mail.body.include?(notes)
276 end
286 end
277
287
278 def test_post_edit_with_note_and_spent_time
288 def test_post_edit_with_note_and_spent_time
279 @request.session[:user_id] = 2
289 @request.session[:user_id] = 2
280 spent_hours_before = Issue.find(1).spent_hours
290 spent_hours_before = Issue.find(1).spent_hours
281 post :edit,
291 post :edit,
282 :id => 1,
292 :id => 1,
283 :notes => '2.5 hours added',
293 :notes => '2.5 hours added',
284 :time_entry => { :hours => '2.5', :comments => '', :activity_id => Enumeration.get_values('ACTI').first }
294 :time_entry => { :hours => '2.5', :comments => '', :activity_id => Enumeration.get_values('ACTI').first }
285 assert_redirected_to 'issues/show/1'
295 assert_redirected_to 'issues/show/1'
286
296
287 issue = Issue.find(1)
297 issue = Issue.find(1)
288
298
289 j = issue.journals.find(:first, :order => 'id DESC')
299 j = issue.journals.find(:first, :order => 'id DESC')
290 assert_equal '2.5 hours added', j.notes
300 assert_equal '2.5 hours added', j.notes
291 assert_equal 0, j.details.size
301 assert_equal 0, j.details.size
292
302
293 t = issue.time_entries.find(:first, :order => 'id DESC')
303 t = issue.time_entries.find(:first, :order => 'id DESC')
294 assert_not_nil t
304 assert_not_nil t
295 assert_equal 2.5, t.hours
305 assert_equal 2.5, t.hours
296 assert_equal spent_hours_before + 2.5, issue.spent_hours
306 assert_equal spent_hours_before + 2.5, issue.spent_hours
297 end
307 end
298
308
299 def test_post_edit_with_attachment_only
309 def test_post_edit_with_attachment_only
300 # anonymous user
310 # anonymous user
301 post :edit,
311 post :edit,
302 :id => 1,
312 :id => 1,
303 :notes => '',
313 :notes => '',
304 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
314 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
305 assert_redirected_to 'issues/show/1'
315 assert_redirected_to 'issues/show/1'
306 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
316 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
307 assert j.notes.blank?
317 assert j.notes.blank?
308 assert_equal 1, j.details.size
318 assert_equal 1, j.details.size
309 assert_equal 'testfile.txt', j.details.first.value
319 assert_equal 'testfile.txt', j.details.first.value
310 assert_equal User.anonymous, j.user
320 assert_equal User.anonymous, j.user
311
321
312 mail = ActionMailer::Base.deliveries.last
322 mail = ActionMailer::Base.deliveries.last
313 assert mail.body.include?('testfile.txt')
323 assert mail.body.include?('testfile.txt')
314 end
324 end
315
325
316 def test_post_edit_with_no_change
326 def test_post_edit_with_no_change
317 issue = Issue.find(1)
327 issue = Issue.find(1)
318 issue.journals.clear
328 issue.journals.clear
319 ActionMailer::Base.deliveries.clear
329 ActionMailer::Base.deliveries.clear
320
330
321 post :edit,
331 post :edit,
322 :id => 1,
332 :id => 1,
323 :notes => ''
333 :notes => ''
324 assert_redirected_to 'issues/show/1'
334 assert_redirected_to 'issues/show/1'
325
335
326 issue.reload
336 issue.reload
327 assert issue.journals.empty?
337 assert issue.journals.empty?
328 # No email should be sent
338 # No email should be sent
329 assert ActionMailer::Base.deliveries.empty?
339 assert ActionMailer::Base.deliveries.empty?
330 end
340 end
331
341
332 def test_bulk_edit
342 def test_bulk_edit
333 @request.session[:user_id] = 2
343 @request.session[:user_id] = 2
334 # update issues priority
344 # update issues priority
335 post :bulk_edit, :ids => [1, 2], :priority_id => 7, :notes => 'Bulk editing', :assigned_to_id => ''
345 post :bulk_edit, :ids => [1, 2], :priority_id => 7, :notes => 'Bulk editing', :assigned_to_id => ''
336 assert_response 302
346 assert_response 302
337 # check that the issues were updated
347 # check that the issues were updated
338 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
348 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
339 assert_equal 'Bulk editing', Issue.find(1).journals.find(:first, :order => 'created_on DESC').notes
349 assert_equal 'Bulk editing', Issue.find(1).journals.find(:first, :order => 'created_on DESC').notes
340 end
350 end
341
351
342 def test_bulk_unassign
352 def test_bulk_unassign
343 assert_not_nil Issue.find(2).assigned_to
353 assert_not_nil Issue.find(2).assigned_to
344 @request.session[:user_id] = 2
354 @request.session[:user_id] = 2
345 # unassign issues
355 # unassign issues
346 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
356 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
347 assert_response 302
357 assert_response 302
348 # check that the issues were updated
358 # check that the issues were updated
349 assert_nil Issue.find(2).assigned_to
359 assert_nil Issue.find(2).assigned_to
350 end
360 end
351
361
352 def test_move_one_issue_to_another_project
362 def test_move_one_issue_to_another_project
353 @request.session[:user_id] = 1
363 @request.session[:user_id] = 1
354 post :move, :id => 1, :new_project_id => 2
364 post :move, :id => 1, :new_project_id => 2
355 assert_redirected_to 'projects/ecookbook/issues'
365 assert_redirected_to 'projects/ecookbook/issues'
356 assert_equal 2, Issue.find(1).project_id
366 assert_equal 2, Issue.find(1).project_id
357 end
367 end
358
368
359 def test_bulk_move_to_another_project
369 def test_bulk_move_to_another_project
360 @request.session[:user_id] = 1
370 @request.session[:user_id] = 1
361 post :move, :ids => [1, 2], :new_project_id => 2
371 post :move, :ids => [1, 2], :new_project_id => 2
362 assert_redirected_to 'projects/ecookbook/issues'
372 assert_redirected_to 'projects/ecookbook/issues'
363 # Issues moved to project 2
373 # Issues moved to project 2
364 assert_equal 2, Issue.find(1).project_id
374 assert_equal 2, Issue.find(1).project_id
365 assert_equal 2, Issue.find(2).project_id
375 assert_equal 2, Issue.find(2).project_id
366 # No tracker change
376 # No tracker change
367 assert_equal 1, Issue.find(1).tracker_id
377 assert_equal 1, Issue.find(1).tracker_id
368 assert_equal 2, Issue.find(2).tracker_id
378 assert_equal 2, Issue.find(2).tracker_id
369 end
379 end
370
380
371 def test_bulk_move_to_another_tracker
381 def test_bulk_move_to_another_tracker
372 @request.session[:user_id] = 1
382 @request.session[:user_id] = 1
373 post :move, :ids => [1, 2], :new_tracker_id => 2
383 post :move, :ids => [1, 2], :new_tracker_id => 2
374 assert_redirected_to 'projects/ecookbook/issues'
384 assert_redirected_to 'projects/ecookbook/issues'
375 assert_equal 2, Issue.find(1).tracker_id
385 assert_equal 2, Issue.find(1).tracker_id
376 assert_equal 2, Issue.find(2).tracker_id
386 assert_equal 2, Issue.find(2).tracker_id
377 end
387 end
378
388
379 def test_context_menu_one_issue
389 def test_context_menu_one_issue
380 @request.session[:user_id] = 2
390 @request.session[:user_id] = 2
381 get :context_menu, :ids => [1]
391 get :context_menu, :ids => [1]
382 assert_response :success
392 assert_response :success
383 assert_template 'context_menu'
393 assert_template 'context_menu'
384 assert_tag :tag => 'a', :content => 'Edit',
394 assert_tag :tag => 'a', :content => 'Edit',
385 :attributes => { :href => '/issues/edit/1',
395 :attributes => { :href => '/issues/edit/1',
386 :class => 'icon-edit' }
396 :class => 'icon-edit' }
387 assert_tag :tag => 'a', :content => 'Closed',
397 assert_tag :tag => 'a', :content => 'Closed',
388 :attributes => { :href => '/issues/edit/1?issue%5Bstatus_id%5D=5',
398 :attributes => { :href => '/issues/edit/1?issue%5Bstatus_id%5D=5',
389 :class => '' }
399 :class => '' }
390 assert_tag :tag => 'a', :content => 'Immediate',
400 assert_tag :tag => 'a', :content => 'Immediate',
391 :attributes => { :href => '/issues/edit/1?issue%5Bpriority_id%5D=8',
401 :attributes => { :href => '/issues/edit/1?issue%5Bpriority_id%5D=8',
392 :class => '' }
402 :class => '' }
393 assert_tag :tag => 'a', :content => 'Dave Lopper',
403 assert_tag :tag => 'a', :content => 'Dave Lopper',
394 :attributes => { :href => '/issues/edit/1?issue%5Bassigned_to_id%5D=3',
404 :attributes => { :href => '/issues/edit/1?issue%5Bassigned_to_id%5D=3',
395 :class => '' }
405 :class => '' }
396 assert_tag :tag => 'a', :content => 'Copy',
406 assert_tag :tag => 'a', :content => 'Copy',
397 :attributes => { :href => '/projects/ecookbook/issues/new?copy_from=1',
407 :attributes => { :href => '/projects/ecookbook/issues/new?copy_from=1',
398 :class => 'icon-copy' }
408 :class => 'icon-copy' }
399 assert_tag :tag => 'a', :content => 'Move',
409 assert_tag :tag => 'a', :content => 'Move',
400 :attributes => { :href => '/issues/move?ids%5B%5D=1',
410 :attributes => { :href => '/issues/move?ids%5B%5D=1',
401 :class => 'icon-move' }
411 :class => 'icon-move' }
402 assert_tag :tag => 'a', :content => 'Delete',
412 assert_tag :tag => 'a', :content => 'Delete',
403 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
413 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
404 :class => 'icon-del' }
414 :class => 'icon-del' }
405 end
415 end
406
416
407 def test_context_menu_one_issue_by_anonymous
417 def test_context_menu_one_issue_by_anonymous
408 get :context_menu, :ids => [1]
418 get :context_menu, :ids => [1]
409 assert_response :success
419 assert_response :success
410 assert_template 'context_menu'
420 assert_template 'context_menu'
411 assert_tag :tag => 'a', :content => 'Delete',
421 assert_tag :tag => 'a', :content => 'Delete',
412 :attributes => { :href => '#',
422 :attributes => { :href => '#',
413 :class => 'icon-del disabled' }
423 :class => 'icon-del disabled' }
414 end
424 end
415
425
416 def test_context_menu_multiple_issues_of_same_project
426 def test_context_menu_multiple_issues_of_same_project
417 @request.session[:user_id] = 2
427 @request.session[:user_id] = 2
418 get :context_menu, :ids => [1, 2]
428 get :context_menu, :ids => [1, 2]
419 assert_response :success
429 assert_response :success
420 assert_template 'context_menu'
430 assert_template 'context_menu'
421 assert_tag :tag => 'a', :content => 'Edit',
431 assert_tag :tag => 'a', :content => 'Edit',
422 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
432 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
423 :class => 'icon-edit' }
433 :class => 'icon-edit' }
424 assert_tag :tag => 'a', :content => 'Move',
434 assert_tag :tag => 'a', :content => 'Move',
425 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
435 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
426 :class => 'icon-move' }
436 :class => 'icon-move' }
427 assert_tag :tag => 'a', :content => 'Delete',
437 assert_tag :tag => 'a', :content => 'Delete',
428 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
438 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
429 :class => 'icon-del' }
439 :class => 'icon-del' }
430 end
440 end
431
441
432 def test_context_menu_multiple_issues_of_different_project
442 def test_context_menu_multiple_issues_of_different_project
433 @request.session[:user_id] = 2
443 @request.session[:user_id] = 2
434 get :context_menu, :ids => [1, 2, 4]
444 get :context_menu, :ids => [1, 2, 4]
435 assert_response :success
445 assert_response :success
436 assert_template 'context_menu'
446 assert_template 'context_menu'
437 assert_tag :tag => 'a', :content => 'Delete',
447 assert_tag :tag => 'a', :content => 'Delete',
438 :attributes => { :href => '#',
448 :attributes => { :href => '#',
439 :class => 'icon-del disabled' }
449 :class => 'icon-del disabled' }
440 end
450 end
441
451
442 def test_destroy_issue_with_no_time_entries
452 def test_destroy_issue_with_no_time_entries
443 @request.session[:user_id] = 2
453 @request.session[:user_id] = 2
444 post :destroy, :id => 3
454 post :destroy, :id => 3
445 assert_redirected_to 'projects/ecookbook/issues'
455 assert_redirected_to 'projects/ecookbook/issues'
446 assert_nil Issue.find_by_id(3)
456 assert_nil Issue.find_by_id(3)
447 end
457 end
448
458
449 def test_destroy_issues_with_time_entries
459 def test_destroy_issues_with_time_entries
450 @request.session[:user_id] = 2
460 @request.session[:user_id] = 2
451 post :destroy, :ids => [1, 3]
461 post :destroy, :ids => [1, 3]
452 assert_response :success
462 assert_response :success
453 assert_template 'destroy'
463 assert_template 'destroy'
454 assert_not_nil assigns(:hours)
464 assert_not_nil assigns(:hours)
455 assert Issue.find_by_id(1) && Issue.find_by_id(3)
465 assert Issue.find_by_id(1) && Issue.find_by_id(3)
456 end
466 end
457
467
458 def test_destroy_issues_and_destroy_time_entries
468 def test_destroy_issues_and_destroy_time_entries
459 @request.session[:user_id] = 2
469 @request.session[:user_id] = 2
460 post :destroy, :ids => [1, 3], :todo => 'destroy'
470 post :destroy, :ids => [1, 3], :todo => 'destroy'
461 assert_redirected_to 'projects/ecookbook/issues'
471 assert_redirected_to 'projects/ecookbook/issues'
462 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
472 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
463 assert_nil TimeEntry.find_by_id([1, 2])
473 assert_nil TimeEntry.find_by_id([1, 2])
464 end
474 end
465
475
466 def test_destroy_issues_and_assign_time_entries_to_project
476 def test_destroy_issues_and_assign_time_entries_to_project
467 @request.session[:user_id] = 2
477 @request.session[:user_id] = 2
468 post :destroy, :ids => [1, 3], :todo => 'nullify'
478 post :destroy, :ids => [1, 3], :todo => 'nullify'
469 assert_redirected_to 'projects/ecookbook/issues'
479 assert_redirected_to 'projects/ecookbook/issues'
470 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
480 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
471 assert_nil TimeEntry.find(1).issue_id
481 assert_nil TimeEntry.find(1).issue_id
472 assert_nil TimeEntry.find(2).issue_id
482 assert_nil TimeEntry.find(2).issue_id
473 end
483 end
474
484
475 def test_destroy_issues_and_reassign_time_entries_to_another_issue
485 def test_destroy_issues_and_reassign_time_entries_to_another_issue
476 @request.session[:user_id] = 2
486 @request.session[:user_id] = 2
477 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
487 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
478 assert_redirected_to 'projects/ecookbook/issues'
488 assert_redirected_to 'projects/ecookbook/issues'
479 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
489 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
480 assert_equal 2, TimeEntry.find(1).issue_id
490 assert_equal 2, TimeEntry.find(1).issue_id
481 assert_equal 2, TimeEntry.find(2).issue_id
491 assert_equal 2, TimeEntry.find(2).issue_id
482 end
492 end
483
493
484 def test_destroy_attachment
494 def test_destroy_attachment
485 issue = Issue.find(3)
495 issue = Issue.find(3)
486 a = issue.attachments.size
496 a = issue.attachments.size
487 @request.session[:user_id] = 2
497 @request.session[:user_id] = 2
488 post :destroy_attachment, :id => 3, :attachment_id => 1
498 post :destroy_attachment, :id => 3, :attachment_id => 1
489 assert_redirected_to 'issues/show/3'
499 assert_redirected_to 'issues/show/3'
490 assert_nil Attachment.find_by_id(1)
500 assert_nil Attachment.find_by_id(1)
491 issue.reload
501 issue.reload
492 assert_equal((a-1), issue.attachments.size)
502 assert_equal((a-1), issue.attachments.size)
493 j = issue.journals.find(:first, :order => 'created_on DESC')
503 j = issue.journals.find(:first, :order => 'created_on DESC')
494 assert_equal 'attachment', j.details.first.property
504 assert_equal 'attachment', j.details.first.property
495 end
505 end
496 end
506 end
General Comments 0
You need to be logged in to leave comments. Login now