##// END OF EJS Templates
Preview description if it was edited while updating an issue (#741)....
Jean-Philippe Lang -
r3509:2bbc948e06ef
parent child
Show More
@@ -0,0 +1,11
1 <% if @notes %>
2 <fieldset class="preview"><legend><%= l(:field_notes) %></legend>
3 <%= textilizable @notes, :attachments => @attachements, :object => @issue %>
4 </fieldset>
5 <% end %>
6
7 <% if @description %>
8 <fieldset class="preview"><legend><%= l(:field_description) %></legend>
9 <%= textilizable @description, :attachments => @attachements, :object => @issue %>
10 </fieldset>
11 <% end %>
@@ -1,585 +1,593
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 default_search_scope :issues
20 default_search_scope :issues
21
21
22 before_filter :find_issue, :only => [:show, :edit, :update, :reply]
22 before_filter :find_issue, :only => [:show, :edit, :update, :reply]
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, :auto_complete]
24 before_filter :find_project, :only => [:new, :update_form, :preview, :auto_complete]
25 before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :context_menu]
25 before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :context_menu]
26 before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
26 before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
27 accept_key_auth :index, :show, :changes
27 accept_key_auth :index, :show, :changes
28
28
29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
30
30
31 helper :journals
31 helper :journals
32 helper :projects
32 helper :projects
33 include ProjectsHelper
33 include ProjectsHelper
34 helper :custom_fields
34 helper :custom_fields
35 include CustomFieldsHelper
35 include CustomFieldsHelper
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 include QueriesHelper
43 include QueriesHelper
44 helper :sort
44 helper :sort
45 include SortHelper
45 include SortHelper
46 include IssuesHelper
46 include IssuesHelper
47 helper :timelog
47 helper :timelog
48 include Redmine::Export::PDF
48 include Redmine::Export::PDF
49
49
50 verify :method => [:post, :delete],
50 verify :method => [:post, :delete],
51 :only => :destroy,
51 :only => :destroy,
52 :render => { :nothing => true, :status => :method_not_allowed }
52 :render => { :nothing => true, :status => :method_not_allowed }
53
53
54 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
54 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
55
55
56 def index
56 def index
57 retrieve_query
57 retrieve_query
58 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
58 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
59 sort_update(@query.sortable_columns)
59 sort_update(@query.sortable_columns)
60
60
61 if @query.valid?
61 if @query.valid?
62 limit = case params[:format]
62 limit = case params[:format]
63 when 'csv', 'pdf'
63 when 'csv', 'pdf'
64 Setting.issues_export_limit.to_i
64 Setting.issues_export_limit.to_i
65 when 'atom'
65 when 'atom'
66 Setting.feeds_limit.to_i
66 Setting.feeds_limit.to_i
67 else
67 else
68 per_page_option
68 per_page_option
69 end
69 end
70
70
71 @issue_count = @query.issue_count
71 @issue_count = @query.issue_count
72 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
72 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
73 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
73 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
74 :order => sort_clause,
74 :order => sort_clause,
75 :offset => @issue_pages.current.offset,
75 :offset => @issue_pages.current.offset,
76 :limit => limit)
76 :limit => limit)
77 @issue_count_by_group = @query.issue_count_by_group
77 @issue_count_by_group = @query.issue_count_by_group
78
78
79 respond_to do |format|
79 respond_to do |format|
80 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
80 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
81 format.xml { render :layout => false }
81 format.xml { render :layout => false }
82 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
82 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
83 format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
83 format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
84 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
84 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
85 end
85 end
86 else
86 else
87 # Send html if the query is not valid
87 # Send html if the query is not valid
88 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
88 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
89 end
89 end
90 rescue ActiveRecord::RecordNotFound
90 rescue ActiveRecord::RecordNotFound
91 render_404
91 render_404
92 end
92 end
93
93
94 def changes
94 def changes
95 retrieve_query
95 retrieve_query
96 sort_init 'id', 'desc'
96 sort_init 'id', 'desc'
97 sort_update(@query.sortable_columns)
97 sort_update(@query.sortable_columns)
98
98
99 if @query.valid?
99 if @query.valid?
100 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
100 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
101 :limit => 25)
101 :limit => 25)
102 end
102 end
103 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
103 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
104 render :layout => false, :content_type => 'application/atom+xml'
104 render :layout => false, :content_type => 'application/atom+xml'
105 rescue ActiveRecord::RecordNotFound
105 rescue ActiveRecord::RecordNotFound
106 render_404
106 render_404
107 end
107 end
108
108
109 def show
109 def show
110 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
110 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
111 @journals.each_with_index {|j,i| j.indice = i+1}
111 @journals.each_with_index {|j,i| j.indice = i+1}
112 @journals.reverse! if User.current.wants_comments_in_reverse_order?
112 @journals.reverse! if User.current.wants_comments_in_reverse_order?
113 @changesets = @issue.changesets.visible.all
113 @changesets = @issue.changesets.visible.all
114 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
114 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
115 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
115 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
116 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
116 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
117 @priorities = IssuePriority.all
117 @priorities = IssuePriority.all
118 @time_entry = TimeEntry.new
118 @time_entry = TimeEntry.new
119 respond_to do |format|
119 respond_to do |format|
120 format.html { render :template => 'issues/show.rhtml' }
120 format.html { render :template => 'issues/show.rhtml' }
121 format.xml { render :layout => false }
121 format.xml { render :layout => false }
122 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
122 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
123 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
123 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
124 end
124 end
125 end
125 end
126
126
127 # Add a new issue
127 # Add a new issue
128 # The new issue will be created from an existing one if copy_from parameter is given
128 # The new issue will be created from an existing one if copy_from parameter is given
129 def new
129 def new
130 @issue = Issue.new
130 @issue = Issue.new
131 @issue.copy_from(params[:copy_from]) if params[:copy_from]
131 @issue.copy_from(params[:copy_from]) if params[:copy_from]
132 @issue.project = @project
132 @issue.project = @project
133 # Tracker must be set before custom field values
133 # Tracker must be set before custom field values
134 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
134 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
135 if @issue.tracker.nil?
135 if @issue.tracker.nil?
136 render_error l(:error_no_tracker_in_project)
136 render_error l(:error_no_tracker_in_project)
137 return
137 return
138 end
138 end
139 if @issue.status.nil?
139 if @issue.status.nil?
140 render_error l(:error_no_default_issue_status)
140 render_error l(:error_no_default_issue_status)
141 return
141 return
142 end
142 end
143 if params[:issue].is_a?(Hash)
143 if params[:issue].is_a?(Hash)
144 @issue.safe_attributes = params[:issue]
144 @issue.safe_attributes = params[:issue]
145 @issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
145 @issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
146 end
146 end
147 @issue.author = User.current
147 @issue.author = User.current
148
148
149 if request.get? || request.xhr?
149 if request.get? || request.xhr?
150 @issue.start_date ||= Date.today
150 @issue.start_date ||= Date.today
151 else
151 else
152 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
152 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
153 if @issue.save
153 if @issue.save
154 attachments = Attachment.attach_files(@issue, params[:attachments])
154 attachments = Attachment.attach_files(@issue, params[:attachments])
155 render_attachment_warning_if_needed(@issue)
155 render_attachment_warning_if_needed(@issue)
156 flash[:notice] = l(:notice_successful_create)
156 flash[:notice] = l(:notice_successful_create)
157 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
157 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
158 respond_to do |format|
158 respond_to do |format|
159 format.html {
159 format.html {
160 redirect_to(params[:continue] ? { :action => 'new', :issue => {:tracker_id => @issue.tracker,
160 redirect_to(params[:continue] ? { :action => 'new', :issue => {:tracker_id => @issue.tracker,
161 :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
161 :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
162 { :action => 'show', :id => @issue })
162 { :action => 'show', :id => @issue })
163 }
163 }
164 format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
164 format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
165 end
165 end
166 return
166 return
167 else
167 else
168 respond_to do |format|
168 respond_to do |format|
169 format.html { }
169 format.html { }
170 format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
170 format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
171 end
171 end
172 end
172 end
173 end
173 end
174 @priorities = IssuePriority.all
174 @priorities = IssuePriority.all
175 @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
175 @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
176 render :layout => !request.xhr?
176 render :layout => !request.xhr?
177 end
177 end
178
178
179 # Attributes that can be updated on workflow transition (without :edit permission)
179 # Attributes that can be updated on workflow transition (without :edit permission)
180 # TODO: make it configurable (at least per role)
180 # TODO: make it configurable (at least per role)
181 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
181 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
182
182
183 def edit
183 def edit
184 update_issue_from_params
184 update_issue_from_params
185
185
186 @journal = @issue.current_journal
186 @journal = @issue.current_journal
187
187
188 respond_to do |format|
188 respond_to do |format|
189 format.html { }
189 format.html { }
190 format.xml { }
190 format.xml { }
191 end
191 end
192 end
192 end
193
193
194 def update
194 def update
195 update_issue_from_params
195 update_issue_from_params
196
196
197 if @issue.save_issue_with_child_records(params, @time_entry)
197 if @issue.save_issue_with_child_records(params, @time_entry)
198 render_attachment_warning_if_needed(@issue)
198 render_attachment_warning_if_needed(@issue)
199 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
199 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
200
200
201 respond_to do |format|
201 respond_to do |format|
202 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
202 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
203 format.xml { head :ok }
203 format.xml { head :ok }
204 end
204 end
205 else
205 else
206 render_attachment_warning_if_needed(@issue)
206 render_attachment_warning_if_needed(@issue)
207 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
207 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
208 @journal = @issue.current_journal
208 @journal = @issue.current_journal
209
209
210 respond_to do |format|
210 respond_to do |format|
211 format.html { render :action => 'edit' }
211 format.html { render :action => 'edit' }
212 format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
212 format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
213 end
213 end
214 end
214 end
215
215
216 rescue ActiveRecord::StaleObjectError
216 rescue ActiveRecord::StaleObjectError
217 # Optimistic locking exception
217 # Optimistic locking exception
218 flash.now[:error] = l(:notice_locking_conflict)
218 flash.now[:error] = l(:notice_locking_conflict)
219 # Remove the previously added attachments if issue was not updated
219 # Remove the previously added attachments if issue was not updated
220 attachments[:files].each(&:destroy) if attachments[:files]
220 attachments[:files].each(&:destroy) if attachments[:files]
221 end
221 end
222
222
223 def reply
223 def reply
224 journal = Journal.find(params[:journal_id]) if params[:journal_id]
224 journal = Journal.find(params[:journal_id]) if params[:journal_id]
225 if journal
225 if journal
226 user = journal.user
226 user = journal.user
227 text = journal.notes
227 text = journal.notes
228 else
228 else
229 user = @issue.author
229 user = @issue.author
230 text = @issue.description
230 text = @issue.description
231 end
231 end
232 # Replaces pre blocks with [...]
232 # Replaces pre blocks with [...]
233 text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
233 text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
234 content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
234 content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
235 content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
235 content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
236
236
237 render(:update) { |page|
237 render(:update) { |page|
238 page.<< "$('notes').value = \"#{escape_javascript content}\";"
238 page.<< "$('notes').value = \"#{escape_javascript content}\";"
239 page.show 'update'
239 page.show 'update'
240 page << "Form.Element.focus('notes');"
240 page << "Form.Element.focus('notes');"
241 page << "Element.scrollTo('update');"
241 page << "Element.scrollTo('update');"
242 page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
242 page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
243 }
243 }
244 end
244 end
245
245
246 # Bulk edit a set of issues
246 # Bulk edit a set of issues
247 def bulk_edit
247 def bulk_edit
248 @issues.sort!
248 @issues.sort!
249 if request.post?
249 if request.post?
250 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
250 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
251 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
251 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
252 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
252 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
253
253
254 unsaved_issue_ids = []
254 unsaved_issue_ids = []
255 @issues.each do |issue|
255 @issues.each do |issue|
256 issue.reload
256 issue.reload
257 journal = issue.init_journal(User.current, params[:notes])
257 journal = issue.init_journal(User.current, params[:notes])
258 issue.safe_attributes = attributes
258 issue.safe_attributes = attributes
259 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
259 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
260 unless issue.save
260 unless issue.save
261 # Keep unsaved issue ids to display them in flash error
261 # Keep unsaved issue ids to display them in flash error
262 unsaved_issue_ids << issue.id
262 unsaved_issue_ids << issue.id
263 end
263 end
264 end
264 end
265 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
265 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
266 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
266 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
267 return
267 return
268 end
268 end
269 @available_statuses = Workflow.available_statuses(@project)
269 @available_statuses = Workflow.available_statuses(@project)
270 @custom_fields = @project.all_issue_custom_fields
270 @custom_fields = @project.all_issue_custom_fields
271 end
271 end
272
272
273 def move
273 def move
274 @issues.sort!
274 @issues.sort!
275 @copy = params[:copy_options] && params[:copy_options][:copy]
275 @copy = params[:copy_options] && params[:copy_options][:copy]
276 @allowed_projects = []
276 @allowed_projects = []
277 # find projects to which the user is allowed to move the issue
277 # find projects to which the user is allowed to move the issue
278 if User.current.admin?
278 if User.current.admin?
279 # admin is allowed to move issues to any active (visible) project
279 # admin is allowed to move issues to any active (visible) project
280 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current))
280 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current))
281 else
281 else
282 User.current.memberships.each {|m| @allowed_projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
282 User.current.memberships.each {|m| @allowed_projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
283 end
283 end
284 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
284 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
285 @target_project ||= @project
285 @target_project ||= @project
286 @trackers = @target_project.trackers
286 @trackers = @target_project.trackers
287 @available_statuses = Workflow.available_statuses(@project)
287 @available_statuses = Workflow.available_statuses(@project)
288 if request.post?
288 if request.post?
289 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
289 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
290 unsaved_issue_ids = []
290 unsaved_issue_ids = []
291 moved_issues = []
291 moved_issues = []
292 @issues.each do |issue|
292 @issues.each do |issue|
293 issue.reload
293 issue.reload
294 changed_attributes = {}
294 changed_attributes = {}
295 [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute|
295 [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute|
296 unless params[valid_attribute].blank?
296 unless params[valid_attribute].blank?
297 changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute])
297 changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute])
298 end
298 end
299 end
299 end
300 issue.init_journal(User.current)
300 issue.init_journal(User.current)
301 call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy })
301 call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy })
302 if r = issue.move_to_project(@target_project, new_tracker, {:copy => @copy, :attributes => changed_attributes})
302 if r = issue.move_to_project(@target_project, new_tracker, {:copy => @copy, :attributes => changed_attributes})
303 moved_issues << r
303 moved_issues << r
304 else
304 else
305 unsaved_issue_ids << issue.id
305 unsaved_issue_ids << issue.id
306 end
306 end
307 end
307 end
308 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
308 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
309
309
310 if params[:follow]
310 if params[:follow]
311 if @issues.size == 1 && moved_issues.size == 1
311 if @issues.size == 1 && moved_issues.size == 1
312 redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
312 redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
313 else
313 else
314 redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project)
314 redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project)
315 end
315 end
316 else
316 else
317 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
317 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
318 end
318 end
319 return
319 return
320 end
320 end
321 render :layout => false if request.xhr?
321 render :layout => false if request.xhr?
322 end
322 end
323
323
324 def destroy
324 def destroy
325 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
325 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
326 if @hours > 0
326 if @hours > 0
327 case params[:todo]
327 case params[:todo]
328 when 'destroy'
328 when 'destroy'
329 # nothing to do
329 # nothing to do
330 when 'nullify'
330 when 'nullify'
331 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
331 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
332 when 'reassign'
332 when 'reassign'
333 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
333 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
334 if reassign_to.nil?
334 if reassign_to.nil?
335 flash.now[:error] = l(:error_issue_not_found_in_project)
335 flash.now[:error] = l(:error_issue_not_found_in_project)
336 return
336 return
337 else
337 else
338 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
338 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
339 end
339 end
340 else
340 else
341 unless params[:format] == 'xml'
341 unless params[:format] == 'xml'
342 # display the destroy form if it's a user request
342 # display the destroy form if it's a user request
343 return
343 return
344 end
344 end
345 end
345 end
346 end
346 end
347 @issues.each(&:destroy)
347 @issues.each(&:destroy)
348 respond_to do |format|
348 respond_to do |format|
349 format.html { redirect_to :action => 'index', :project_id => @project }
349 format.html { redirect_to :action => 'index', :project_id => @project }
350 format.xml { head :ok }
350 format.xml { head :ok }
351 end
351 end
352 end
352 end
353
353
354 def gantt
354 def gantt
355 @gantt = Redmine::Helpers::Gantt.new(params)
355 @gantt = Redmine::Helpers::Gantt.new(params)
356 retrieve_query
356 retrieve_query
357 @query.group_by = nil
357 @query.group_by = nil
358 if @query.valid?
358 if @query.valid?
359 events = []
359 events = []
360 # Issues that have start and due dates
360 # Issues that have start and due dates
361 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
361 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
362 :order => "start_date, due_date",
362 :order => "start_date, due_date",
363 :conditions => ["(((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]
363 :conditions => ["(((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]
364 )
364 )
365 # Issues that don't have a due date but that are assigned to a version with a date
365 # Issues that don't have a due date but that are assigned to a version with a date
366 events += @query.issues(:include => [:tracker, :assigned_to, :priority, :fixed_version],
366 events += @query.issues(:include => [:tracker, :assigned_to, :priority, :fixed_version],
367 :order => "start_date, effective_date",
367 :order => "start_date, effective_date",
368 :conditions => ["(((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]
368 :conditions => ["(((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]
369 )
369 )
370 # Versions
370 # Versions
371 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
371 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
372
372
373 @gantt.events = events
373 @gantt.events = events
374 end
374 end
375
375
376 basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
376 basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
377
377
378 respond_to do |format|
378 respond_to do |format|
379 format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
379 format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
380 format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
380 format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
381 format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
381 format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
382 end
382 end
383 end
383 end
384
384
385 def calendar
385 def calendar
386 if params[:year] and params[:year].to_i > 1900
386 if params[:year] and params[:year].to_i > 1900
387 @year = params[:year].to_i
387 @year = params[:year].to_i
388 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
388 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
389 @month = params[:month].to_i
389 @month = params[:month].to_i
390 end
390 end
391 end
391 end
392 @year ||= Date.today.year
392 @year ||= Date.today.year
393 @month ||= Date.today.month
393 @month ||= Date.today.month
394
394
395 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
395 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
396 retrieve_query
396 retrieve_query
397 @query.group_by = nil
397 @query.group_by = nil
398 if @query.valid?
398 if @query.valid?
399 events = []
399 events = []
400 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
400 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
401 :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
401 :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
402 )
402 )
403 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
403 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
404
404
405 @calendar.events = events
405 @calendar.events = events
406 end
406 end
407
407
408 render :layout => false if request.xhr?
408 render :layout => false if request.xhr?
409 end
409 end
410
410
411 def context_menu
411 def context_menu
412 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
412 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
413 if (@issues.size == 1)
413 if (@issues.size == 1)
414 @issue = @issues.first
414 @issue = @issues.first
415 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
415 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
416 end
416 end
417 projects = @issues.collect(&:project).compact.uniq
417 projects = @issues.collect(&:project).compact.uniq
418 @project = projects.first if projects.size == 1
418 @project = projects.first if projects.size == 1
419
419
420 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
420 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
421 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
421 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
422 :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
422 :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
423 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
423 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
424 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
424 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
425 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
425 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
426 }
426 }
427 if @project
427 if @project
428 @assignables = @project.assignable_users
428 @assignables = @project.assignable_users
429 @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
429 @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
430 @trackers = @project.trackers
430 @trackers = @project.trackers
431 end
431 end
432
432
433 @priorities = IssuePriority.all.reverse
433 @priorities = IssuePriority.all.reverse
434 @statuses = IssueStatus.find(:all, :order => 'position')
434 @statuses = IssueStatus.find(:all, :order => 'position')
435 @back = params[:back_url] || request.env['HTTP_REFERER']
435 @back = params[:back_url] || request.env['HTTP_REFERER']
436
436
437 render :layout => false
437 render :layout => false
438 end
438 end
439
439
440 def update_form
440 def update_form
441 if params[:id].blank?
441 if params[:id].blank?
442 @issue = Issue.new
442 @issue = Issue.new
443 @issue.project = @project
443 @issue.project = @project
444 else
444 else
445 @issue = @project.issues.visible.find(params[:id])
445 @issue = @project.issues.visible.find(params[:id])
446 end
446 end
447 @issue.attributes = params[:issue]
447 @issue.attributes = params[:issue]
448 @allowed_statuses = ([@issue.status] + @issue.status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
448 @allowed_statuses = ([@issue.status] + @issue.status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
449 @priorities = IssuePriority.all
449 @priorities = IssuePriority.all
450
450
451 render :partial => 'attributes'
451 render :partial => 'attributes'
452 end
452 end
453
453
454 def preview
454 def preview
455 @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
455 @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
456 @attachements = @issue.attachments if @issue
456 if @issue
457 @text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
457 @attachements = @issue.attachments
458 render :partial => 'common/preview'
458 @description = params[:issue] && params[:issue][:description]
459 if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n")
460 @description = nil
461 end
462 @notes = params[:notes]
463 else
464 @description = (params[:issue] ? params[:issue][:description] : nil)
465 end
466 render :layout => false
459 end
467 end
460
468
461 def auto_complete
469 def auto_complete
462 @issues = []
470 @issues = []
463 q = params[:q].to_s
471 q = params[:q].to_s
464 if q.match(/^\d+$/)
472 if q.match(/^\d+$/)
465 @issues << @project.issues.visible.find_by_id(q.to_i)
473 @issues << @project.issues.visible.find_by_id(q.to_i)
466 end
474 end
467 unless q.blank?
475 unless q.blank?
468 @issues += @project.issues.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
476 @issues += @project.issues.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
469 end
477 end
470 render :layout => false
478 render :layout => false
471 end
479 end
472
480
473 private
481 private
474 def find_issue
482 def find_issue
475 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
483 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
476 @project = @issue.project
484 @project = @issue.project
477 rescue ActiveRecord::RecordNotFound
485 rescue ActiveRecord::RecordNotFound
478 render_404
486 render_404
479 end
487 end
480
488
481 # Filter for bulk operations
489 # Filter for bulk operations
482 def find_issues
490 def find_issues
483 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
491 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
484 raise ActiveRecord::RecordNotFound if @issues.empty?
492 raise ActiveRecord::RecordNotFound if @issues.empty?
485 projects = @issues.collect(&:project).compact.uniq
493 projects = @issues.collect(&:project).compact.uniq
486 if projects.size == 1
494 if projects.size == 1
487 @project = projects.first
495 @project = projects.first
488 else
496 else
489 # TODO: let users bulk edit/move/destroy issues from different projects
497 # TODO: let users bulk edit/move/destroy issues from different projects
490 render_error 'Can not bulk edit/move/destroy issues from different projects'
498 render_error 'Can not bulk edit/move/destroy issues from different projects'
491 return false
499 return false
492 end
500 end
493 rescue ActiveRecord::RecordNotFound
501 rescue ActiveRecord::RecordNotFound
494 render_404
502 render_404
495 end
503 end
496
504
497 def find_project
505 def find_project
498 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
506 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
499 @project = Project.find(project_id)
507 @project = Project.find(project_id)
500 rescue ActiveRecord::RecordNotFound
508 rescue ActiveRecord::RecordNotFound
501 render_404
509 render_404
502 end
510 end
503
511
504 def find_optional_project
512 def find_optional_project
505 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
513 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
506 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
514 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
507 allowed ? true : deny_access
515 allowed ? true : deny_access
508 rescue ActiveRecord::RecordNotFound
516 rescue ActiveRecord::RecordNotFound
509 render_404
517 render_404
510 end
518 end
511
519
512 # Retrieve query from session or build a new query
520 # Retrieve query from session or build a new query
513 def retrieve_query
521 def retrieve_query
514 if !params[:query_id].blank?
522 if !params[:query_id].blank?
515 cond = "project_id IS NULL"
523 cond = "project_id IS NULL"
516 cond << " OR project_id = #{@project.id}" if @project
524 cond << " OR project_id = #{@project.id}" if @project
517 @query = Query.find(params[:query_id], :conditions => cond)
525 @query = Query.find(params[:query_id], :conditions => cond)
518 @query.project = @project
526 @query.project = @project
519 session[:query] = {:id => @query.id, :project_id => @query.project_id}
527 session[:query] = {:id => @query.id, :project_id => @query.project_id}
520 sort_clear
528 sort_clear
521 else
529 else
522 if api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
530 if api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
523 # Give it a name, required to be valid
531 # Give it a name, required to be valid
524 @query = Query.new(:name => "_")
532 @query = Query.new(:name => "_")
525 @query.project = @project
533 @query.project = @project
526 if params[:fields] and params[:fields].is_a? Array
534 if params[:fields] and params[:fields].is_a? Array
527 params[:fields].each do |field|
535 params[:fields].each do |field|
528 @query.add_filter(field, params[:operators][field], params[:values][field])
536 @query.add_filter(field, params[:operators][field], params[:values][field])
529 end
537 end
530 else
538 else
531 @query.available_filters.keys.each do |field|
539 @query.available_filters.keys.each do |field|
532 @query.add_short_filter(field, params[field]) if params[field]
540 @query.add_short_filter(field, params[field]) if params[field]
533 end
541 end
534 end
542 end
535 @query.group_by = params[:group_by]
543 @query.group_by = params[:group_by]
536 @query.column_names = params[:query] && params[:query][:column_names]
544 @query.column_names = params[:query] && params[:query][:column_names]
537 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
545 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
538 else
546 else
539 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
547 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
540 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
548 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
541 @query.project = @project
549 @query.project = @project
542 end
550 end
543 end
551 end
544 end
552 end
545
553
546 # Rescues an invalid query statement. Just in case...
554 # Rescues an invalid query statement. Just in case...
547 def query_statement_invalid(exception)
555 def query_statement_invalid(exception)
548 logger.error "Query::StatementInvalid: #{exception.message}" if logger
556 logger.error "Query::StatementInvalid: #{exception.message}" if logger
549 session.delete(:query)
557 session.delete(:query)
550 sort_clear
558 sort_clear
551 render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
559 render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
552 end
560 end
553
561
554 # Used by #edit and #update to set some common instance variables
562 # Used by #edit and #update to set some common instance variables
555 # from the params
563 # from the params
556 # TODO: Refactor, not everything in here is needed by #edit
564 # TODO: Refactor, not everything in here is needed by #edit
557 def update_issue_from_params
565 def update_issue_from_params
558 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
566 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
559 @priorities = IssuePriority.all
567 @priorities = IssuePriority.all
560 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
568 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
561 @time_entry = TimeEntry.new
569 @time_entry = TimeEntry.new
562
570
563 @notes = params[:notes]
571 @notes = params[:notes]
564 @issue.init_journal(User.current, @notes)
572 @issue.init_journal(User.current, @notes)
565 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
573 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
566 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
574 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
567 attrs = params[:issue].dup
575 attrs = params[:issue].dup
568 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
576 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
569 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
577 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
570 @issue.safe_attributes = attrs
578 @issue.safe_attributes = attrs
571 end
579 end
572
580
573 end
581 end
574
582
575 def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids)
583 def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids)
576 if unsaved_issue_ids.empty?
584 if unsaved_issue_ids.empty?
577 flash[:notice] = l(:notice_successful_update) unless issues.empty?
585 flash[:notice] = l(:notice_successful_update) unless issues.empty?
578 else
586 else
579 flash[:error] = l(:notice_failed_to_save_issues,
587 flash[:error] = l(:notice_failed_to_save_issues,
580 :count => unsaved_issue_ids.size,
588 :count => unsaved_issue_ids.size,
581 :total => issues.size,
589 :total => issues.size,
582 :ids => '#' + unsaved_issue_ids.join(', #'))
590 :ids => '#' + unsaved_issue_ids.join(', #'))
583 end
591 end
584 end
592 end
585 end
593 end
@@ -1,1352 +1,1368
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 < ActionController::TestCase
24 class IssuesControllerTest < ActionController::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_projects,
42 :custom_fields_projects,
43 :custom_fields_trackers,
43 :custom_fields_trackers,
44 :time_entries,
44 :time_entries,
45 :journals,
45 :journals,
46 :journal_details,
46 :journal_details,
47 :queries
47 :queries
48
48
49 def setup
49 def setup
50 @controller = IssuesController.new
50 @controller = IssuesController.new
51 @request = ActionController::TestRequest.new
51 @request = ActionController::TestRequest.new
52 @response = ActionController::TestResponse.new
52 @response = ActionController::TestResponse.new
53 User.current = nil
53 User.current = nil
54 end
54 end
55
55
56 def test_index
56 def test_index
57 Setting.default_language = 'en'
57 Setting.default_language = 'en'
58
58
59 get :index
59 get :index
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 assert_nil assigns(:project)
63 assert_nil assigns(:project)
64 assert_tag :tag => 'a', :content => /Can't print recipes/
64 assert_tag :tag => 'a', :content => /Can't print recipes/
65 assert_tag :tag => 'a', :content => /Subproject issue/
65 assert_tag :tag => 'a', :content => /Subproject issue/
66 # private projects hidden
66 # private projects hidden
67 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
67 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
68 assert_no_tag :tag => 'a', :content => /Issue on project 2/
68 assert_no_tag :tag => 'a', :content => /Issue on project 2/
69 # project column
69 # project column
70 assert_tag :tag => 'th', :content => /Project/
70 assert_tag :tag => 'th', :content => /Project/
71 end
71 end
72
72
73 def test_index_should_not_list_issues_when_module_disabled
73 def test_index_should_not_list_issues_when_module_disabled
74 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
74 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
75 get :index
75 get :index
76 assert_response :success
76 assert_response :success
77 assert_template 'index.rhtml'
77 assert_template 'index.rhtml'
78 assert_not_nil assigns(:issues)
78 assert_not_nil assigns(:issues)
79 assert_nil assigns(:project)
79 assert_nil assigns(:project)
80 assert_no_tag :tag => 'a', :content => /Can't print recipes/
80 assert_no_tag :tag => 'a', :content => /Can't print recipes/
81 assert_tag :tag => 'a', :content => /Subproject issue/
81 assert_tag :tag => 'a', :content => /Subproject issue/
82 end
82 end
83
83
84 def test_index_should_not_list_issues_when_module_disabled
84 def test_index_should_not_list_issues_when_module_disabled
85 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
85 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
86 get :index
86 get :index
87 assert_response :success
87 assert_response :success
88 assert_template 'index.rhtml'
88 assert_template 'index.rhtml'
89 assert_not_nil assigns(:issues)
89 assert_not_nil assigns(:issues)
90 assert_nil assigns(:project)
90 assert_nil assigns(:project)
91 assert_no_tag :tag => 'a', :content => /Can't print recipes/
91 assert_no_tag :tag => 'a', :content => /Can't print recipes/
92 assert_tag :tag => 'a', :content => /Subproject issue/
92 assert_tag :tag => 'a', :content => /Subproject issue/
93 end
93 end
94
94
95 def test_index_with_project
95 def test_index_with_project
96 Setting.display_subprojects_issues = 0
96 Setting.display_subprojects_issues = 0
97 get :index, :project_id => 1
97 get :index, :project_id => 1
98 assert_response :success
98 assert_response :success
99 assert_template 'index.rhtml'
99 assert_template 'index.rhtml'
100 assert_not_nil assigns(:issues)
100 assert_not_nil assigns(:issues)
101 assert_tag :tag => 'a', :content => /Can't print recipes/
101 assert_tag :tag => 'a', :content => /Can't print recipes/
102 assert_no_tag :tag => 'a', :content => /Subproject issue/
102 assert_no_tag :tag => 'a', :content => /Subproject issue/
103 end
103 end
104
104
105 def test_index_with_project_and_subprojects
105 def test_index_with_project_and_subprojects
106 Setting.display_subprojects_issues = 1
106 Setting.display_subprojects_issues = 1
107 get :index, :project_id => 1
107 get :index, :project_id => 1
108 assert_response :success
108 assert_response :success
109 assert_template 'index.rhtml'
109 assert_template 'index.rhtml'
110 assert_not_nil assigns(:issues)
110 assert_not_nil assigns(:issues)
111 assert_tag :tag => 'a', :content => /Can't print recipes/
111 assert_tag :tag => 'a', :content => /Can't print recipes/
112 assert_tag :tag => 'a', :content => /Subproject issue/
112 assert_tag :tag => 'a', :content => /Subproject issue/
113 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
113 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
114 end
114 end
115
115
116 def test_index_with_project_and_subprojects_should_show_private_subprojects
116 def test_index_with_project_and_subprojects_should_show_private_subprojects
117 @request.session[:user_id] = 2
117 @request.session[:user_id] = 2
118 Setting.display_subprojects_issues = 1
118 Setting.display_subprojects_issues = 1
119 get :index, :project_id => 1
119 get :index, :project_id => 1
120 assert_response :success
120 assert_response :success
121 assert_template 'index.rhtml'
121 assert_template 'index.rhtml'
122 assert_not_nil assigns(:issues)
122 assert_not_nil assigns(:issues)
123 assert_tag :tag => 'a', :content => /Can't print recipes/
123 assert_tag :tag => 'a', :content => /Can't print recipes/
124 assert_tag :tag => 'a', :content => /Subproject issue/
124 assert_tag :tag => 'a', :content => /Subproject issue/
125 assert_tag :tag => 'a', :content => /Issue of a private subproject/
125 assert_tag :tag => 'a', :content => /Issue of a private subproject/
126 end
126 end
127
127
128 def test_index_with_project_and_filter
128 def test_index_with_project_and_filter
129 get :index, :project_id => 1, :set_filter => 1
129 get :index, :project_id => 1, :set_filter => 1
130 assert_response :success
130 assert_response :success
131 assert_template 'index.rhtml'
131 assert_template 'index.rhtml'
132 assert_not_nil assigns(:issues)
132 assert_not_nil assigns(:issues)
133 end
133 end
134
134
135 def test_index_with_query
135 def test_index_with_query
136 get :index, :project_id => 1, :query_id => 5
136 get :index, :project_id => 1, :query_id => 5
137 assert_response :success
137 assert_response :success
138 assert_template 'index.rhtml'
138 assert_template 'index.rhtml'
139 assert_not_nil assigns(:issues)
139 assert_not_nil assigns(:issues)
140 assert_nil assigns(:issue_count_by_group)
140 assert_nil assigns(:issue_count_by_group)
141 end
141 end
142
142
143 def test_index_with_query_grouped_by_tracker
143 def test_index_with_query_grouped_by_tracker
144 get :index, :project_id => 1, :query_id => 6
144 get :index, :project_id => 1, :query_id => 6
145 assert_response :success
145 assert_response :success
146 assert_template 'index.rhtml'
146 assert_template 'index.rhtml'
147 assert_not_nil assigns(:issues)
147 assert_not_nil assigns(:issues)
148 assert_not_nil assigns(:issue_count_by_group)
148 assert_not_nil assigns(:issue_count_by_group)
149 end
149 end
150
150
151 def test_index_with_query_grouped_by_list_custom_field
151 def test_index_with_query_grouped_by_list_custom_field
152 get :index, :project_id => 1, :query_id => 9
152 get :index, :project_id => 1, :query_id => 9
153 assert_response :success
153 assert_response :success
154 assert_template 'index.rhtml'
154 assert_template 'index.rhtml'
155 assert_not_nil assigns(:issues)
155 assert_not_nil assigns(:issues)
156 assert_not_nil assigns(:issue_count_by_group)
156 assert_not_nil assigns(:issue_count_by_group)
157 end
157 end
158
158
159 def test_index_sort_by_field_not_included_in_columns
159 def test_index_sort_by_field_not_included_in_columns
160 Setting.issue_list_default_columns = %w(subject author)
160 Setting.issue_list_default_columns = %w(subject author)
161 get :index, :sort => 'tracker'
161 get :index, :sort => 'tracker'
162 end
162 end
163
163
164 def test_index_csv_with_project
164 def test_index_csv_with_project
165 Setting.default_language = 'en'
165 Setting.default_language = 'en'
166
166
167 get :index, :format => 'csv'
167 get :index, :format => 'csv'
168 assert_response :success
168 assert_response :success
169 assert_not_nil assigns(:issues)
169 assert_not_nil assigns(:issues)
170 assert_equal 'text/csv', @response.content_type
170 assert_equal 'text/csv', @response.content_type
171 assert @response.body.starts_with?("#,")
171 assert @response.body.starts_with?("#,")
172
172
173 get :index, :project_id => 1, :format => 'csv'
173 get :index, :project_id => 1, :format => 'csv'
174 assert_response :success
174 assert_response :success
175 assert_not_nil assigns(:issues)
175 assert_not_nil assigns(:issues)
176 assert_equal 'text/csv', @response.content_type
176 assert_equal 'text/csv', @response.content_type
177 end
177 end
178
178
179 def test_index_pdf
179 def test_index_pdf
180 get :index, :format => 'pdf'
180 get :index, :format => 'pdf'
181 assert_response :success
181 assert_response :success
182 assert_not_nil assigns(:issues)
182 assert_not_nil assigns(:issues)
183 assert_equal 'application/pdf', @response.content_type
183 assert_equal 'application/pdf', @response.content_type
184
184
185 get :index, :project_id => 1, :format => 'pdf'
185 get :index, :project_id => 1, :format => 'pdf'
186 assert_response :success
186 assert_response :success
187 assert_not_nil assigns(:issues)
187 assert_not_nil assigns(:issues)
188 assert_equal 'application/pdf', @response.content_type
188 assert_equal 'application/pdf', @response.content_type
189
189
190 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
190 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
191 assert_response :success
191 assert_response :success
192 assert_not_nil assigns(:issues)
192 assert_not_nil assigns(:issues)
193 assert_equal 'application/pdf', @response.content_type
193 assert_equal 'application/pdf', @response.content_type
194 end
194 end
195
195
196 def test_index_pdf_with_query_grouped_by_list_custom_field
196 def test_index_pdf_with_query_grouped_by_list_custom_field
197 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
197 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
198 assert_response :success
198 assert_response :success
199 assert_not_nil assigns(:issues)
199 assert_not_nil assigns(:issues)
200 assert_not_nil assigns(:issue_count_by_group)
200 assert_not_nil assigns(:issue_count_by_group)
201 assert_equal 'application/pdf', @response.content_type
201 assert_equal 'application/pdf', @response.content_type
202 end
202 end
203
203
204 def test_index_sort
204 def test_index_sort
205 get :index, :sort => 'tracker,id:desc'
205 get :index, :sort => 'tracker,id:desc'
206 assert_response :success
206 assert_response :success
207
207
208 sort_params = @request.session['issues_index_sort']
208 sort_params = @request.session['issues_index_sort']
209 assert sort_params.is_a?(String)
209 assert sort_params.is_a?(String)
210 assert_equal 'tracker,id:desc', sort_params
210 assert_equal 'tracker,id:desc', sort_params
211
211
212 issues = assigns(:issues)
212 issues = assigns(:issues)
213 assert_not_nil issues
213 assert_not_nil issues
214 assert !issues.empty?
214 assert !issues.empty?
215 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
215 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
216 end
216 end
217
217
218 def test_index_with_columns
218 def test_index_with_columns
219 columns = ['tracker', 'subject', 'assigned_to']
219 columns = ['tracker', 'subject', 'assigned_to']
220 get :index, :set_filter => 1, :query => { 'column_names' => columns}
220 get :index, :set_filter => 1, :query => { 'column_names' => columns}
221 assert_response :success
221 assert_response :success
222
222
223 # query should use specified columns
223 # query should use specified columns
224 query = assigns(:query)
224 query = assigns(:query)
225 assert_kind_of Query, query
225 assert_kind_of Query, query
226 assert_equal columns, query.column_names.map(&:to_s)
226 assert_equal columns, query.column_names.map(&:to_s)
227
227
228 # columns should be stored in session
228 # columns should be stored in session
229 assert_kind_of Hash, session[:query]
229 assert_kind_of Hash, session[:query]
230 assert_kind_of Array, session[:query][:column_names]
230 assert_kind_of Array, session[:query][:column_names]
231 assert_equal columns, session[:query][:column_names].map(&:to_s)
231 assert_equal columns, session[:query][:column_names].map(&:to_s)
232 end
232 end
233
233
234 def test_gantt
234 def test_gantt
235 get :gantt, :project_id => 1
235 get :gantt, :project_id => 1
236 assert_response :success
236 assert_response :success
237 assert_template 'gantt.rhtml'
237 assert_template 'gantt.rhtml'
238 assert_not_nil assigns(:gantt)
238 assert_not_nil assigns(:gantt)
239 events = assigns(:gantt).events
239 events = assigns(:gantt).events
240 assert_not_nil events
240 assert_not_nil events
241 # Issue with start and due dates
241 # Issue with start and due dates
242 i = Issue.find(1)
242 i = Issue.find(1)
243 assert_not_nil i.due_date
243 assert_not_nil i.due_date
244 assert events.include?(Issue.find(1))
244 assert events.include?(Issue.find(1))
245 # Issue with without due date but targeted to a version with date
245 # Issue with without due date but targeted to a version with date
246 i = Issue.find(2)
246 i = Issue.find(2)
247 assert_nil i.due_date
247 assert_nil i.due_date
248 assert events.include?(i)
248 assert events.include?(i)
249 end
249 end
250
250
251 def test_cross_project_gantt
251 def test_cross_project_gantt
252 get :gantt
252 get :gantt
253 assert_response :success
253 assert_response :success
254 assert_template 'gantt.rhtml'
254 assert_template 'gantt.rhtml'
255 assert_not_nil assigns(:gantt)
255 assert_not_nil assigns(:gantt)
256 events = assigns(:gantt).events
256 events = assigns(:gantt).events
257 assert_not_nil events
257 assert_not_nil events
258 end
258 end
259
259
260 def test_gantt_export_to_pdf
260 def test_gantt_export_to_pdf
261 get :gantt, :project_id => 1, :format => 'pdf'
261 get :gantt, :project_id => 1, :format => 'pdf'
262 assert_response :success
262 assert_response :success
263 assert_equal 'application/pdf', @response.content_type
263 assert_equal 'application/pdf', @response.content_type
264 assert @response.body.starts_with?('%PDF')
264 assert @response.body.starts_with?('%PDF')
265 assert_not_nil assigns(:gantt)
265 assert_not_nil assigns(:gantt)
266 end
266 end
267
267
268 def test_cross_project_gantt_export_to_pdf
268 def test_cross_project_gantt_export_to_pdf
269 get :gantt, :format => 'pdf'
269 get :gantt, :format => 'pdf'
270 assert_response :success
270 assert_response :success
271 assert_equal 'application/pdf', @response.content_type
271 assert_equal 'application/pdf', @response.content_type
272 assert @response.body.starts_with?('%PDF')
272 assert @response.body.starts_with?('%PDF')
273 assert_not_nil assigns(:gantt)
273 assert_not_nil assigns(:gantt)
274 end
274 end
275
275
276 if Object.const_defined?(:Magick)
276 if Object.const_defined?(:Magick)
277 def test_gantt_image
277 def test_gantt_image
278 get :gantt, :project_id => 1, :format => 'png'
278 get :gantt, :project_id => 1, :format => 'png'
279 assert_response :success
279 assert_response :success
280 assert_equal 'image/png', @response.content_type
280 assert_equal 'image/png', @response.content_type
281 end
281 end
282 else
282 else
283 puts "RMagick not installed. Skipping tests !!!"
283 puts "RMagick not installed. Skipping tests !!!"
284 end
284 end
285
285
286 def test_calendar
286 def test_calendar
287 get :calendar, :project_id => 1
287 get :calendar, :project_id => 1
288 assert_response :success
288 assert_response :success
289 assert_template 'calendar'
289 assert_template 'calendar'
290 assert_not_nil assigns(:calendar)
290 assert_not_nil assigns(:calendar)
291 end
291 end
292
292
293 def test_cross_project_calendar
293 def test_cross_project_calendar
294 get :calendar
294 get :calendar
295 assert_response :success
295 assert_response :success
296 assert_template 'calendar'
296 assert_template 'calendar'
297 assert_not_nil assigns(:calendar)
297 assert_not_nil assigns(:calendar)
298 end
298 end
299
299
300 def test_changes
300 def test_changes
301 get :changes, :project_id => 1
301 get :changes, :project_id => 1
302 assert_response :success
302 assert_response :success
303 assert_not_nil assigns(:journals)
303 assert_not_nil assigns(:journals)
304 assert_equal 'application/atom+xml', @response.content_type
304 assert_equal 'application/atom+xml', @response.content_type
305 end
305 end
306
306
307 def test_show_by_anonymous
307 def test_show_by_anonymous
308 get :show, :id => 1
308 get :show, :id => 1
309 assert_response :success
309 assert_response :success
310 assert_template 'show.rhtml'
310 assert_template 'show.rhtml'
311 assert_not_nil assigns(:issue)
311 assert_not_nil assigns(:issue)
312 assert_equal Issue.find(1), assigns(:issue)
312 assert_equal Issue.find(1), assigns(:issue)
313
313
314 # anonymous role is allowed to add a note
314 # anonymous role is allowed to add a note
315 assert_tag :tag => 'form',
315 assert_tag :tag => 'form',
316 :descendant => { :tag => 'fieldset',
316 :descendant => { :tag => 'fieldset',
317 :child => { :tag => 'legend',
317 :child => { :tag => 'legend',
318 :content => /Notes/ } }
318 :content => /Notes/ } }
319 end
319 end
320
320
321 def test_show_by_manager
321 def test_show_by_manager
322 @request.session[:user_id] = 2
322 @request.session[:user_id] = 2
323 get :show, :id => 1
323 get :show, :id => 1
324 assert_response :success
324 assert_response :success
325
325
326 assert_tag :tag => 'form',
326 assert_tag :tag => 'form',
327 :descendant => { :tag => 'fieldset',
327 :descendant => { :tag => 'fieldset',
328 :child => { :tag => 'legend',
328 :child => { :tag => 'legend',
329 :content => /Change properties/ } },
329 :content => /Change properties/ } },
330 :descendant => { :tag => 'fieldset',
330 :descendant => { :tag => 'fieldset',
331 :child => { :tag => 'legend',
331 :child => { :tag => 'legend',
332 :content => /Log time/ } },
332 :content => /Log time/ } },
333 :descendant => { :tag => 'fieldset',
333 :descendant => { :tag => 'fieldset',
334 :child => { :tag => 'legend',
334 :child => { :tag => 'legend',
335 :content => /Notes/ } }
335 :content => /Notes/ } }
336 end
336 end
337
337
338 def test_show_should_deny_anonymous_access_without_permission
338 def test_show_should_deny_anonymous_access_without_permission
339 Role.anonymous.remove_permission!(:view_issues)
339 Role.anonymous.remove_permission!(:view_issues)
340 get :show, :id => 1
340 get :show, :id => 1
341 assert_response :redirect
341 assert_response :redirect
342 end
342 end
343
343
344 def test_show_should_deny_non_member_access_without_permission
344 def test_show_should_deny_non_member_access_without_permission
345 Role.non_member.remove_permission!(:view_issues)
345 Role.non_member.remove_permission!(:view_issues)
346 @request.session[:user_id] = 9
346 @request.session[:user_id] = 9
347 get :show, :id => 1
347 get :show, :id => 1
348 assert_response 403
348 assert_response 403
349 end
349 end
350
350
351 def test_show_should_deny_member_access_without_permission
351 def test_show_should_deny_member_access_without_permission
352 Role.find(1).remove_permission!(:view_issues)
352 Role.find(1).remove_permission!(:view_issues)
353 @request.session[:user_id] = 2
353 @request.session[:user_id] = 2
354 get :show, :id => 1
354 get :show, :id => 1
355 assert_response 403
355 assert_response 403
356 end
356 end
357
357
358 def test_show_should_not_disclose_relations_to_invisible_issues
358 def test_show_should_not_disclose_relations_to_invisible_issues
359 Setting.cross_project_issue_relations = '1'
359 Setting.cross_project_issue_relations = '1'
360 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
360 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
361 # Relation to a private project issue
361 # Relation to a private project issue
362 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
362 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
363
363
364 get :show, :id => 1
364 get :show, :id => 1
365 assert_response :success
365 assert_response :success
366
366
367 assert_tag :div, :attributes => { :id => 'relations' },
367 assert_tag :div, :attributes => { :id => 'relations' },
368 :descendant => { :tag => 'a', :content => /#2$/ }
368 :descendant => { :tag => 'a', :content => /#2$/ }
369 assert_no_tag :div, :attributes => { :id => 'relations' },
369 assert_no_tag :div, :attributes => { :id => 'relations' },
370 :descendant => { :tag => 'a', :content => /#4$/ }
370 :descendant => { :tag => 'a', :content => /#4$/ }
371 end
371 end
372
372
373 def test_show_atom
373 def test_show_atom
374 get :show, :id => 2, :format => 'atom'
374 get :show, :id => 2, :format => 'atom'
375 assert_response :success
375 assert_response :success
376 assert_template 'changes.rxml'
376 assert_template 'changes.rxml'
377 # Inline image
377 # Inline image
378 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
378 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
379 end
379 end
380
380
381 def test_show_export_to_pdf
381 def test_show_export_to_pdf
382 get :show, :id => 3, :format => 'pdf'
382 get :show, :id => 3, :format => 'pdf'
383 assert_response :success
383 assert_response :success
384 assert_equal 'application/pdf', @response.content_type
384 assert_equal 'application/pdf', @response.content_type
385 assert @response.body.starts_with?('%PDF')
385 assert @response.body.starts_with?('%PDF')
386 assert_not_nil assigns(:issue)
386 assert_not_nil assigns(:issue)
387 end
387 end
388
388
389 def test_get_new
389 def test_get_new
390 @request.session[:user_id] = 2
390 @request.session[:user_id] = 2
391 get :new, :project_id => 1, :tracker_id => 1
391 get :new, :project_id => 1, :tracker_id => 1
392 assert_response :success
392 assert_response :success
393 assert_template 'new'
393 assert_template 'new'
394
394
395 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
395 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
396 :value => 'Default string' }
396 :value => 'Default string' }
397 end
397 end
398
398
399 def test_get_new_without_tracker_id
399 def test_get_new_without_tracker_id
400 @request.session[:user_id] = 2
400 @request.session[:user_id] = 2
401 get :new, :project_id => 1
401 get :new, :project_id => 1
402 assert_response :success
402 assert_response :success
403 assert_template 'new'
403 assert_template 'new'
404
404
405 issue = assigns(:issue)
405 issue = assigns(:issue)
406 assert_not_nil issue
406 assert_not_nil issue
407 assert_equal Project.find(1).trackers.first, issue.tracker
407 assert_equal Project.find(1).trackers.first, issue.tracker
408 end
408 end
409
409
410 def test_get_new_with_no_default_status_should_display_an_error
410 def test_get_new_with_no_default_status_should_display_an_error
411 @request.session[:user_id] = 2
411 @request.session[:user_id] = 2
412 IssueStatus.delete_all
412 IssueStatus.delete_all
413
413
414 get :new, :project_id => 1
414 get :new, :project_id => 1
415 assert_response 500
415 assert_response 500
416 assert_not_nil flash[:error]
416 assert_not_nil flash[:error]
417 assert_tag :tag => 'div', :attributes => { :class => /error/ },
417 assert_tag :tag => 'div', :attributes => { :class => /error/ },
418 :content => /No default issue/
418 :content => /No default issue/
419 end
419 end
420
420
421 def test_get_new_with_no_tracker_should_display_an_error
421 def test_get_new_with_no_tracker_should_display_an_error
422 @request.session[:user_id] = 2
422 @request.session[:user_id] = 2
423 Tracker.delete_all
423 Tracker.delete_all
424
424
425 get :new, :project_id => 1
425 get :new, :project_id => 1
426 assert_response 500
426 assert_response 500
427 assert_not_nil flash[:error]
427 assert_not_nil flash[:error]
428 assert_tag :tag => 'div', :attributes => { :class => /error/ },
428 assert_tag :tag => 'div', :attributes => { :class => /error/ },
429 :content => /No tracker/
429 :content => /No tracker/
430 end
430 end
431
431
432 def test_update_new_form
432 def test_update_new_form
433 @request.session[:user_id] = 2
433 @request.session[:user_id] = 2
434 xhr :post, :update_form, :project_id => 1,
434 xhr :post, :update_form, :project_id => 1,
435 :issue => {:tracker_id => 2,
435 :issue => {:tracker_id => 2,
436 :subject => 'This is the test_new issue',
436 :subject => 'This is the test_new issue',
437 :description => 'This is the description',
437 :description => 'This is the description',
438 :priority_id => 5}
438 :priority_id => 5}
439 assert_response :success
439 assert_response :success
440 assert_template 'attributes'
440 assert_template 'attributes'
441
441
442 issue = assigns(:issue)
442 issue = assigns(:issue)
443 assert_kind_of Issue, issue
443 assert_kind_of Issue, issue
444 assert_equal 1, issue.project_id
444 assert_equal 1, issue.project_id
445 assert_equal 2, issue.tracker_id
445 assert_equal 2, issue.tracker_id
446 assert_equal 'This is the test_new issue', issue.subject
446 assert_equal 'This is the test_new issue', issue.subject
447 end
447 end
448
448
449 def test_post_new
449 def test_post_new
450 @request.session[:user_id] = 2
450 @request.session[:user_id] = 2
451 assert_difference 'Issue.count' do
451 assert_difference 'Issue.count' do
452 post :new, :project_id => 1,
452 post :new, :project_id => 1,
453 :issue => {:tracker_id => 3,
453 :issue => {:tracker_id => 3,
454 :status_id => 2,
454 :status_id => 2,
455 :subject => 'This is the test_new issue',
455 :subject => 'This is the test_new issue',
456 :description => 'This is the description',
456 :description => 'This is the description',
457 :priority_id => 5,
457 :priority_id => 5,
458 :estimated_hours => '',
458 :estimated_hours => '',
459 :custom_field_values => {'2' => 'Value for field 2'}}
459 :custom_field_values => {'2' => 'Value for field 2'}}
460 end
460 end
461 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
461 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
462
462
463 issue = Issue.find_by_subject('This is the test_new issue')
463 issue = Issue.find_by_subject('This is the test_new issue')
464 assert_not_nil issue
464 assert_not_nil issue
465 assert_equal 2, issue.author_id
465 assert_equal 2, issue.author_id
466 assert_equal 3, issue.tracker_id
466 assert_equal 3, issue.tracker_id
467 assert_equal 2, issue.status_id
467 assert_equal 2, issue.status_id
468 assert_nil issue.estimated_hours
468 assert_nil issue.estimated_hours
469 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
469 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
470 assert_not_nil v
470 assert_not_nil v
471 assert_equal 'Value for field 2', v.value
471 assert_equal 'Value for field 2', v.value
472 end
472 end
473
473
474 def test_post_new_and_continue
474 def test_post_new_and_continue
475 @request.session[:user_id] = 2
475 @request.session[:user_id] = 2
476 post :new, :project_id => 1,
476 post :new, :project_id => 1,
477 :issue => {:tracker_id => 3,
477 :issue => {:tracker_id => 3,
478 :subject => 'This is first issue',
478 :subject => 'This is first issue',
479 :priority_id => 5},
479 :priority_id => 5},
480 :continue => ''
480 :continue => ''
481 assert_redirected_to :controller => 'issues', :action => 'new', :issue => {:tracker_id => 3}
481 assert_redirected_to :controller => 'issues', :action => 'new', :issue => {:tracker_id => 3}
482 end
482 end
483
483
484 def test_post_new_without_custom_fields_param
484 def test_post_new_without_custom_fields_param
485 @request.session[:user_id] = 2
485 @request.session[:user_id] = 2
486 assert_difference 'Issue.count' do
486 assert_difference 'Issue.count' do
487 post :new, :project_id => 1,
487 post :new, :project_id => 1,
488 :issue => {:tracker_id => 1,
488 :issue => {:tracker_id => 1,
489 :subject => 'This is the test_new issue',
489 :subject => 'This is the test_new issue',
490 :description => 'This is the description',
490 :description => 'This is the description',
491 :priority_id => 5}
491 :priority_id => 5}
492 end
492 end
493 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
493 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
494 end
494 end
495
495
496 def test_post_new_with_required_custom_field_and_without_custom_fields_param
496 def test_post_new_with_required_custom_field_and_without_custom_fields_param
497 field = IssueCustomField.find_by_name('Database')
497 field = IssueCustomField.find_by_name('Database')
498 field.update_attribute(:is_required, true)
498 field.update_attribute(:is_required, true)
499
499
500 @request.session[:user_id] = 2
500 @request.session[:user_id] = 2
501 post :new, :project_id => 1,
501 post :new, :project_id => 1,
502 :issue => {:tracker_id => 1,
502 :issue => {:tracker_id => 1,
503 :subject => 'This is the test_new issue',
503 :subject => 'This is the test_new issue',
504 :description => 'This is the description',
504 :description => 'This is the description',
505 :priority_id => 5}
505 :priority_id => 5}
506 assert_response :success
506 assert_response :success
507 assert_template 'new'
507 assert_template 'new'
508 issue = assigns(:issue)
508 issue = assigns(:issue)
509 assert_not_nil issue
509 assert_not_nil issue
510 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
510 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
511 end
511 end
512
512
513 def test_post_new_with_watchers
513 def test_post_new_with_watchers
514 @request.session[:user_id] = 2
514 @request.session[:user_id] = 2
515 ActionMailer::Base.deliveries.clear
515 ActionMailer::Base.deliveries.clear
516
516
517 assert_difference 'Watcher.count', 2 do
517 assert_difference 'Watcher.count', 2 do
518 post :new, :project_id => 1,
518 post :new, :project_id => 1,
519 :issue => {:tracker_id => 1,
519 :issue => {:tracker_id => 1,
520 :subject => 'This is a new issue with watchers',
520 :subject => 'This is a new issue with watchers',
521 :description => 'This is the description',
521 :description => 'This is the description',
522 :priority_id => 5,
522 :priority_id => 5,
523 :watcher_user_ids => ['2', '3']}
523 :watcher_user_ids => ['2', '3']}
524 end
524 end
525 issue = Issue.find_by_subject('This is a new issue with watchers')
525 issue = Issue.find_by_subject('This is a new issue with watchers')
526 assert_not_nil issue
526 assert_not_nil issue
527 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
527 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
528
528
529 # Watchers added
529 # Watchers added
530 assert_equal [2, 3], issue.watcher_user_ids.sort
530 assert_equal [2, 3], issue.watcher_user_ids.sort
531 assert issue.watched_by?(User.find(3))
531 assert issue.watched_by?(User.find(3))
532 # Watchers notified
532 # Watchers notified
533 mail = ActionMailer::Base.deliveries.last
533 mail = ActionMailer::Base.deliveries.last
534 assert_kind_of TMail::Mail, mail
534 assert_kind_of TMail::Mail, mail
535 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
535 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
536 end
536 end
537
537
538 def test_post_new_subissue
538 def test_post_new_subissue
539 @request.session[:user_id] = 2
539 @request.session[:user_id] = 2
540
540
541 assert_difference 'Issue.count' do
541 assert_difference 'Issue.count' do
542 post :new, :project_id => 1,
542 post :new, :project_id => 1,
543 :issue => {:tracker_id => 1,
543 :issue => {:tracker_id => 1,
544 :subject => 'This is a child issue',
544 :subject => 'This is a child issue',
545 :parent_issue_id => 2}
545 :parent_issue_id => 2}
546 end
546 end
547 issue = Issue.find_by_subject('This is a child issue')
547 issue = Issue.find_by_subject('This is a child issue')
548 assert_not_nil issue
548 assert_not_nil issue
549 assert_equal Issue.find(2), issue.parent
549 assert_equal Issue.find(2), issue.parent
550 end
550 end
551
551
552 def test_post_new_should_send_a_notification
552 def test_post_new_should_send_a_notification
553 ActionMailer::Base.deliveries.clear
553 ActionMailer::Base.deliveries.clear
554 @request.session[:user_id] = 2
554 @request.session[:user_id] = 2
555 assert_difference 'Issue.count' do
555 assert_difference 'Issue.count' do
556 post :new, :project_id => 1,
556 post :new, :project_id => 1,
557 :issue => {:tracker_id => 3,
557 :issue => {:tracker_id => 3,
558 :subject => 'This is the test_new issue',
558 :subject => 'This is the test_new issue',
559 :description => 'This is the description',
559 :description => 'This is the description',
560 :priority_id => 5,
560 :priority_id => 5,
561 :estimated_hours => '',
561 :estimated_hours => '',
562 :custom_field_values => {'2' => 'Value for field 2'}}
562 :custom_field_values => {'2' => 'Value for field 2'}}
563 end
563 end
564 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
564 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
565
565
566 assert_equal 1, ActionMailer::Base.deliveries.size
566 assert_equal 1, ActionMailer::Base.deliveries.size
567 end
567 end
568
568
569 def test_post_should_preserve_fields_values_on_validation_failure
569 def test_post_should_preserve_fields_values_on_validation_failure
570 @request.session[:user_id] = 2
570 @request.session[:user_id] = 2
571 post :new, :project_id => 1,
571 post :new, :project_id => 1,
572 :issue => {:tracker_id => 1,
572 :issue => {:tracker_id => 1,
573 # empty subject
573 # empty subject
574 :subject => '',
574 :subject => '',
575 :description => 'This is a description',
575 :description => 'This is a description',
576 :priority_id => 6,
576 :priority_id => 6,
577 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
577 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
578 assert_response :success
578 assert_response :success
579 assert_template 'new'
579 assert_template 'new'
580
580
581 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
581 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
582 :content => 'This is a description'
582 :content => 'This is a description'
583 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
583 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
584 :child => { :tag => 'option', :attributes => { :selected => 'selected',
584 :child => { :tag => 'option', :attributes => { :selected => 'selected',
585 :value => '6' },
585 :value => '6' },
586 :content => 'High' }
586 :content => 'High' }
587 # Custom fields
587 # Custom fields
588 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
588 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
589 :child => { :tag => 'option', :attributes => { :selected => 'selected',
589 :child => { :tag => 'option', :attributes => { :selected => 'selected',
590 :value => 'Oracle' },
590 :value => 'Oracle' },
591 :content => 'Oracle' }
591 :content => 'Oracle' }
592 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
592 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
593 :value => 'Value for field 2'}
593 :value => 'Value for field 2'}
594 end
594 end
595
595
596 def test_post_new_should_ignore_non_safe_attributes
596 def test_post_new_should_ignore_non_safe_attributes
597 @request.session[:user_id] = 2
597 @request.session[:user_id] = 2
598 assert_nothing_raised do
598 assert_nothing_raised do
599 post :new, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
599 post :new, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
600 end
600 end
601 end
601 end
602
602
603 context "without workflow privilege" do
603 context "without workflow privilege" do
604 setup do
604 setup do
605 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
605 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
606 Role.anonymous.add_permission! :add_issues
606 Role.anonymous.add_permission! :add_issues
607 end
607 end
608
608
609 context "#new" do
609 context "#new" do
610 should "propose default status only" do
610 should "propose default status only" do
611 get :new, :project_id => 1
611 get :new, :project_id => 1
612 assert_response :success
612 assert_response :success
613 assert_template 'new'
613 assert_template 'new'
614 assert_tag :tag => 'select',
614 assert_tag :tag => 'select',
615 :attributes => {:name => 'issue[status_id]'},
615 :attributes => {:name => 'issue[status_id]'},
616 :children => {:count => 1},
616 :children => {:count => 1},
617 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
617 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
618 end
618 end
619
619
620 should "accept default status" do
620 should "accept default status" do
621 assert_difference 'Issue.count' do
621 assert_difference 'Issue.count' do
622 post :new, :project_id => 1,
622 post :new, :project_id => 1,
623 :issue => {:tracker_id => 1,
623 :issue => {:tracker_id => 1,
624 :subject => 'This is an issue',
624 :subject => 'This is an issue',
625 :status_id => 1}
625 :status_id => 1}
626 end
626 end
627 issue = Issue.last(:order => 'id')
627 issue = Issue.last(:order => 'id')
628 assert_equal IssueStatus.default, issue.status
628 assert_equal IssueStatus.default, issue.status
629 end
629 end
630
630
631 should "ignore unauthorized status" do
631 should "ignore unauthorized status" do
632 assert_difference 'Issue.count' do
632 assert_difference 'Issue.count' do
633 post :new, :project_id => 1,
633 post :new, :project_id => 1,
634 :issue => {:tracker_id => 1,
634 :issue => {:tracker_id => 1,
635 :subject => 'This is an issue',
635 :subject => 'This is an issue',
636 :status_id => 3}
636 :status_id => 3}
637 end
637 end
638 issue = Issue.last(:order => 'id')
638 issue = Issue.last(:order => 'id')
639 assert_equal IssueStatus.default, issue.status
639 assert_equal IssueStatus.default, issue.status
640 end
640 end
641 end
641 end
642 end
642 end
643
643
644 def test_copy_issue
644 def test_copy_issue
645 @request.session[:user_id] = 2
645 @request.session[:user_id] = 2
646 get :new, :project_id => 1, :copy_from => 1
646 get :new, :project_id => 1, :copy_from => 1
647 assert_template 'new'
647 assert_template 'new'
648 assert_not_nil assigns(:issue)
648 assert_not_nil assigns(:issue)
649 orig = Issue.find(1)
649 orig = Issue.find(1)
650 assert_equal orig.subject, assigns(:issue).subject
650 assert_equal orig.subject, assigns(:issue).subject
651 end
651 end
652
652
653 def test_get_edit
653 def test_get_edit
654 @request.session[:user_id] = 2
654 @request.session[:user_id] = 2
655 get :edit, :id => 1
655 get :edit, :id => 1
656 assert_response :success
656 assert_response :success
657 assert_template 'edit'
657 assert_template 'edit'
658 assert_not_nil assigns(:issue)
658 assert_not_nil assigns(:issue)
659 assert_equal Issue.find(1), assigns(:issue)
659 assert_equal Issue.find(1), assigns(:issue)
660 end
660 end
661
661
662 def test_get_edit_with_params
662 def test_get_edit_with_params
663 @request.session[:user_id] = 2
663 @request.session[:user_id] = 2
664 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
664 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
665 assert_response :success
665 assert_response :success
666 assert_template 'edit'
666 assert_template 'edit'
667
667
668 issue = assigns(:issue)
668 issue = assigns(:issue)
669 assert_not_nil issue
669 assert_not_nil issue
670
670
671 assert_equal 5, issue.status_id
671 assert_equal 5, issue.status_id
672 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
672 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
673 :child => { :tag => 'option',
673 :child => { :tag => 'option',
674 :content => 'Closed',
674 :content => 'Closed',
675 :attributes => { :selected => 'selected' } }
675 :attributes => { :selected => 'selected' } }
676
676
677 assert_equal 7, issue.priority_id
677 assert_equal 7, issue.priority_id
678 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
678 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
679 :child => { :tag => 'option',
679 :child => { :tag => 'option',
680 :content => 'Urgent',
680 :content => 'Urgent',
681 :attributes => { :selected => 'selected' } }
681 :attributes => { :selected => 'selected' } }
682 end
682 end
683
683
684 def test_update_edit_form
684 def test_update_edit_form
685 @request.session[:user_id] = 2
685 @request.session[:user_id] = 2
686 xhr :post, :update_form, :project_id => 1,
686 xhr :post, :update_form, :project_id => 1,
687 :id => 1,
687 :id => 1,
688 :issue => {:tracker_id => 2,
688 :issue => {:tracker_id => 2,
689 :subject => 'This is the test_new issue',
689 :subject => 'This is the test_new issue',
690 :description => 'This is the description',
690 :description => 'This is the description',
691 :priority_id => 5}
691 :priority_id => 5}
692 assert_response :success
692 assert_response :success
693 assert_template 'attributes'
693 assert_template 'attributes'
694
694
695 issue = assigns(:issue)
695 issue = assigns(:issue)
696 assert_kind_of Issue, issue
696 assert_kind_of Issue, issue
697 assert_equal 1, issue.id
697 assert_equal 1, issue.id
698 assert_equal 1, issue.project_id
698 assert_equal 1, issue.project_id
699 assert_equal 2, issue.tracker_id
699 assert_equal 2, issue.tracker_id
700 assert_equal 'This is the test_new issue', issue.subject
700 assert_equal 'This is the test_new issue', issue.subject
701 end
701 end
702
702
703 def test_reply_to_issue
703 def test_reply_to_issue
704 @request.session[:user_id] = 2
704 @request.session[:user_id] = 2
705 get :reply, :id => 1
705 get :reply, :id => 1
706 assert_response :success
706 assert_response :success
707 assert_select_rjs :show, "update"
707 assert_select_rjs :show, "update"
708 end
708 end
709
709
710 def test_reply_to_note
710 def test_reply_to_note
711 @request.session[:user_id] = 2
711 @request.session[:user_id] = 2
712 get :reply, :id => 1, :journal_id => 2
712 get :reply, :id => 1, :journal_id => 2
713 assert_response :success
713 assert_response :success
714 assert_select_rjs :show, "update"
714 assert_select_rjs :show, "update"
715 end
715 end
716
716
717 def test_update_using_invalid_http_verbs
717 def test_update_using_invalid_http_verbs
718 @request.session[:user_id] = 2
718 @request.session[:user_id] = 2
719 subject = 'Updated by an invalid http verb'
719 subject = 'Updated by an invalid http verb'
720
720
721 get :update, :id => 1, :issue => {:subject => subject}
721 get :update, :id => 1, :issue => {:subject => subject}
722 assert_not_equal subject, Issue.find(1).subject
722 assert_not_equal subject, Issue.find(1).subject
723
723
724 post :update, :id => 1, :issue => {:subject => subject}
724 post :update, :id => 1, :issue => {:subject => subject}
725 assert_not_equal subject, Issue.find(1).subject
725 assert_not_equal subject, Issue.find(1).subject
726
726
727 delete :update, :id => 1, :issue => {:subject => subject}
727 delete :update, :id => 1, :issue => {:subject => subject}
728 assert_not_equal subject, Issue.find(1).subject
728 assert_not_equal subject, Issue.find(1).subject
729 end
729 end
730
730
731 def test_put_update_without_custom_fields_param
731 def test_put_update_without_custom_fields_param
732 @request.session[:user_id] = 2
732 @request.session[:user_id] = 2
733 ActionMailer::Base.deliveries.clear
733 ActionMailer::Base.deliveries.clear
734
734
735 issue = Issue.find(1)
735 issue = Issue.find(1)
736 assert_equal '125', issue.custom_value_for(2).value
736 assert_equal '125', issue.custom_value_for(2).value
737 old_subject = issue.subject
737 old_subject = issue.subject
738 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
738 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
739
739
740 assert_difference('Journal.count') do
740 assert_difference('Journal.count') do
741 assert_difference('JournalDetail.count', 2) do
741 assert_difference('JournalDetail.count', 2) do
742 put :update, :id => 1, :issue => {:subject => new_subject,
742 put :update, :id => 1, :issue => {:subject => new_subject,
743 :priority_id => '6',
743 :priority_id => '6',
744 :category_id => '1' # no change
744 :category_id => '1' # no change
745 }
745 }
746 end
746 end
747 end
747 end
748 assert_redirected_to :action => 'show', :id => '1'
748 assert_redirected_to :action => 'show', :id => '1'
749 issue.reload
749 issue.reload
750 assert_equal new_subject, issue.subject
750 assert_equal new_subject, issue.subject
751 # Make sure custom fields were not cleared
751 # Make sure custom fields were not cleared
752 assert_equal '125', issue.custom_value_for(2).value
752 assert_equal '125', issue.custom_value_for(2).value
753
753
754 mail = ActionMailer::Base.deliveries.last
754 mail = ActionMailer::Base.deliveries.last
755 assert_kind_of TMail::Mail, mail
755 assert_kind_of TMail::Mail, mail
756 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
756 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
757 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
757 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
758 end
758 end
759
759
760 def test_put_update_with_custom_field_change
760 def test_put_update_with_custom_field_change
761 @request.session[:user_id] = 2
761 @request.session[:user_id] = 2
762 issue = Issue.find(1)
762 issue = Issue.find(1)
763 assert_equal '125', issue.custom_value_for(2).value
763 assert_equal '125', issue.custom_value_for(2).value
764
764
765 assert_difference('Journal.count') do
765 assert_difference('Journal.count') do
766 assert_difference('JournalDetail.count', 3) do
766 assert_difference('JournalDetail.count', 3) do
767 put :update, :id => 1, :issue => {:subject => 'Custom field change',
767 put :update, :id => 1, :issue => {:subject => 'Custom field change',
768 :priority_id => '6',
768 :priority_id => '6',
769 :category_id => '1', # no change
769 :category_id => '1', # no change
770 :custom_field_values => { '2' => 'New custom value' }
770 :custom_field_values => { '2' => 'New custom value' }
771 }
771 }
772 end
772 end
773 end
773 end
774 assert_redirected_to :action => 'show', :id => '1'
774 assert_redirected_to :action => 'show', :id => '1'
775 issue.reload
775 issue.reload
776 assert_equal 'New custom value', issue.custom_value_for(2).value
776 assert_equal 'New custom value', issue.custom_value_for(2).value
777
777
778 mail = ActionMailer::Base.deliveries.last
778 mail = ActionMailer::Base.deliveries.last
779 assert_kind_of TMail::Mail, mail
779 assert_kind_of TMail::Mail, mail
780 assert mail.body.include?("Searchable field changed from 125 to New custom value")
780 assert mail.body.include?("Searchable field changed from 125 to New custom value")
781 end
781 end
782
782
783 def test_put_update_with_status_and_assignee_change
783 def test_put_update_with_status_and_assignee_change
784 issue = Issue.find(1)
784 issue = Issue.find(1)
785 assert_equal 1, issue.status_id
785 assert_equal 1, issue.status_id
786 @request.session[:user_id] = 2
786 @request.session[:user_id] = 2
787 assert_difference('TimeEntry.count', 0) do
787 assert_difference('TimeEntry.count', 0) do
788 put :update,
788 put :update,
789 :id => 1,
789 :id => 1,
790 :issue => { :status_id => 2, :assigned_to_id => 3 },
790 :issue => { :status_id => 2, :assigned_to_id => 3 },
791 :notes => 'Assigned to dlopper',
791 :notes => 'Assigned to dlopper',
792 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
792 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
793 end
793 end
794 assert_redirected_to :action => 'show', :id => '1'
794 assert_redirected_to :action => 'show', :id => '1'
795 issue.reload
795 issue.reload
796 assert_equal 2, issue.status_id
796 assert_equal 2, issue.status_id
797 j = Journal.find(:first, :order => 'id DESC')
797 j = Journal.find(:first, :order => 'id DESC')
798 assert_equal 'Assigned to dlopper', j.notes
798 assert_equal 'Assigned to dlopper', j.notes
799 assert_equal 2, j.details.size
799 assert_equal 2, j.details.size
800
800
801 mail = ActionMailer::Base.deliveries.last
801 mail = ActionMailer::Base.deliveries.last
802 assert mail.body.include?("Status changed from New to Assigned")
802 assert mail.body.include?("Status changed from New to Assigned")
803 # subject should contain the new status
803 # subject should contain the new status
804 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
804 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
805 end
805 end
806
806
807 def test_put_update_with_note_only
807 def test_put_update_with_note_only
808 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
808 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
809 # anonymous user
809 # anonymous user
810 put :update,
810 put :update,
811 :id => 1,
811 :id => 1,
812 :notes => notes
812 :notes => notes
813 assert_redirected_to :action => 'show', :id => '1'
813 assert_redirected_to :action => 'show', :id => '1'
814 j = Journal.find(:first, :order => 'id DESC')
814 j = Journal.find(:first, :order => 'id DESC')
815 assert_equal notes, j.notes
815 assert_equal notes, j.notes
816 assert_equal 0, j.details.size
816 assert_equal 0, j.details.size
817 assert_equal User.anonymous, j.user
817 assert_equal User.anonymous, j.user
818
818
819 mail = ActionMailer::Base.deliveries.last
819 mail = ActionMailer::Base.deliveries.last
820 assert mail.body.include?(notes)
820 assert mail.body.include?(notes)
821 end
821 end
822
822
823 def test_put_update_with_note_and_spent_time
823 def test_put_update_with_note_and_spent_time
824 @request.session[:user_id] = 2
824 @request.session[:user_id] = 2
825 spent_hours_before = Issue.find(1).spent_hours
825 spent_hours_before = Issue.find(1).spent_hours
826 assert_difference('TimeEntry.count') do
826 assert_difference('TimeEntry.count') do
827 put :update,
827 put :update,
828 :id => 1,
828 :id => 1,
829 :notes => '2.5 hours added',
829 :notes => '2.5 hours added',
830 :time_entry => { :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first }
830 :time_entry => { :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first }
831 end
831 end
832 assert_redirected_to :action => 'show', :id => '1'
832 assert_redirected_to :action => 'show', :id => '1'
833
833
834 issue = Issue.find(1)
834 issue = Issue.find(1)
835
835
836 j = Journal.find(:first, :order => 'id DESC')
836 j = Journal.find(:first, :order => 'id DESC')
837 assert_equal '2.5 hours added', j.notes
837 assert_equal '2.5 hours added', j.notes
838 assert_equal 0, j.details.size
838 assert_equal 0, j.details.size
839
839
840 t = issue.time_entries.find(:first, :order => 'id DESC')
840 t = issue.time_entries.find(:first, :order => 'id DESC')
841 assert_not_nil t
841 assert_not_nil t
842 assert_equal 2.5, t.hours
842 assert_equal 2.5, t.hours
843 assert_equal spent_hours_before + 2.5, issue.spent_hours
843 assert_equal spent_hours_before + 2.5, issue.spent_hours
844 end
844 end
845
845
846 def test_put_update_with_attachment_only
846 def test_put_update_with_attachment_only
847 set_tmp_attachments_directory
847 set_tmp_attachments_directory
848
848
849 # Delete all fixtured journals, a race condition can occur causing the wrong
849 # Delete all fixtured journals, a race condition can occur causing the wrong
850 # journal to get fetched in the next find.
850 # journal to get fetched in the next find.
851 Journal.delete_all
851 Journal.delete_all
852
852
853 # anonymous user
853 # anonymous user
854 put :update,
854 put :update,
855 :id => 1,
855 :id => 1,
856 :notes => '',
856 :notes => '',
857 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
857 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
858 assert_redirected_to :action => 'show', :id => '1'
858 assert_redirected_to :action => 'show', :id => '1'
859 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
859 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
860 assert j.notes.blank?
860 assert j.notes.blank?
861 assert_equal 1, j.details.size
861 assert_equal 1, j.details.size
862 assert_equal 'testfile.txt', j.details.first.value
862 assert_equal 'testfile.txt', j.details.first.value
863 assert_equal User.anonymous, j.user
863 assert_equal User.anonymous, j.user
864
864
865 mail = ActionMailer::Base.deliveries.last
865 mail = ActionMailer::Base.deliveries.last
866 assert mail.body.include?('testfile.txt')
866 assert mail.body.include?('testfile.txt')
867 end
867 end
868
868
869 def test_put_update_with_attachment_that_fails_to_save
869 def test_put_update_with_attachment_that_fails_to_save
870 set_tmp_attachments_directory
870 set_tmp_attachments_directory
871
871
872 # Delete all fixtured journals, a race condition can occur causing the wrong
872 # Delete all fixtured journals, a race condition can occur causing the wrong
873 # journal to get fetched in the next find.
873 # journal to get fetched in the next find.
874 Journal.delete_all
874 Journal.delete_all
875
875
876 # Mock out the unsaved attachment
876 # Mock out the unsaved attachment
877 Attachment.any_instance.stubs(:create).returns(Attachment.new)
877 Attachment.any_instance.stubs(:create).returns(Attachment.new)
878
878
879 # anonymous user
879 # anonymous user
880 put :update,
880 put :update,
881 :id => 1,
881 :id => 1,
882 :notes => '',
882 :notes => '',
883 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
883 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
884 assert_redirected_to :action => 'show', :id => '1'
884 assert_redirected_to :action => 'show', :id => '1'
885 assert_equal '1 file(s) could not be saved.', flash[:warning]
885 assert_equal '1 file(s) could not be saved.', flash[:warning]
886
886
887 end if Object.const_defined?(:Mocha)
887 end if Object.const_defined?(:Mocha)
888
888
889 def test_put_update_with_no_change
889 def test_put_update_with_no_change
890 issue = Issue.find(1)
890 issue = Issue.find(1)
891 issue.journals.clear
891 issue.journals.clear
892 ActionMailer::Base.deliveries.clear
892 ActionMailer::Base.deliveries.clear
893
893
894 put :update,
894 put :update,
895 :id => 1,
895 :id => 1,
896 :notes => ''
896 :notes => ''
897 assert_redirected_to :action => 'show', :id => '1'
897 assert_redirected_to :action => 'show', :id => '1'
898
898
899 issue.reload
899 issue.reload
900 assert issue.journals.empty?
900 assert issue.journals.empty?
901 # No email should be sent
901 # No email should be sent
902 assert ActionMailer::Base.deliveries.empty?
902 assert ActionMailer::Base.deliveries.empty?
903 end
903 end
904
904
905 def test_put_update_should_send_a_notification
905 def test_put_update_should_send_a_notification
906 @request.session[:user_id] = 2
906 @request.session[:user_id] = 2
907 ActionMailer::Base.deliveries.clear
907 ActionMailer::Base.deliveries.clear
908 issue = Issue.find(1)
908 issue = Issue.find(1)
909 old_subject = issue.subject
909 old_subject = issue.subject
910 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
910 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
911
911
912 put :update, :id => 1, :issue => {:subject => new_subject,
912 put :update, :id => 1, :issue => {:subject => new_subject,
913 :priority_id => '6',
913 :priority_id => '6',
914 :category_id => '1' # no change
914 :category_id => '1' # no change
915 }
915 }
916 assert_equal 1, ActionMailer::Base.deliveries.size
916 assert_equal 1, ActionMailer::Base.deliveries.size
917 end
917 end
918
918
919 def test_put_update_with_invalid_spent_time
919 def test_put_update_with_invalid_spent_time
920 @request.session[:user_id] = 2
920 @request.session[:user_id] = 2
921 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
921 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
922
922
923 assert_no_difference('Journal.count') do
923 assert_no_difference('Journal.count') do
924 put :update,
924 put :update,
925 :id => 1,
925 :id => 1,
926 :notes => notes,
926 :notes => notes,
927 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
927 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
928 end
928 end
929 assert_response :success
929 assert_response :success
930 assert_template 'edit'
930 assert_template 'edit'
931
931
932 assert_tag :textarea, :attributes => { :name => 'notes' },
932 assert_tag :textarea, :attributes => { :name => 'notes' },
933 :content => notes
933 :content => notes
934 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
934 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
935 end
935 end
936
936
937 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
937 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
938 issue = Issue.find(2)
938 issue = Issue.find(2)
939 @request.session[:user_id] = 2
939 @request.session[:user_id] = 2
940
940
941 put :update,
941 put :update,
942 :id => issue.id,
942 :id => issue.id,
943 :issue => {
943 :issue => {
944 :fixed_version_id => 4
944 :fixed_version_id => 4
945 }
945 }
946
946
947 assert_response :redirect
947 assert_response :redirect
948 issue.reload
948 issue.reload
949 assert_equal 4, issue.fixed_version_id
949 assert_equal 4, issue.fixed_version_id
950 assert_not_equal issue.project_id, issue.fixed_version.project_id
950 assert_not_equal issue.project_id, issue.fixed_version.project_id
951 end
951 end
952
952
953 def test_put_update_should_redirect_back_using_the_back_url_parameter
953 def test_put_update_should_redirect_back_using_the_back_url_parameter
954 issue = Issue.find(2)
954 issue = Issue.find(2)
955 @request.session[:user_id] = 2
955 @request.session[:user_id] = 2
956
956
957 put :update,
957 put :update,
958 :id => issue.id,
958 :id => issue.id,
959 :issue => {
959 :issue => {
960 :fixed_version_id => 4
960 :fixed_version_id => 4
961 },
961 },
962 :back_url => '/issues'
962 :back_url => '/issues'
963
963
964 assert_response :redirect
964 assert_response :redirect
965 assert_redirected_to '/issues'
965 assert_redirected_to '/issues'
966 end
966 end
967
967
968 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
968 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
969 issue = Issue.find(2)
969 issue = Issue.find(2)
970 @request.session[:user_id] = 2
970 @request.session[:user_id] = 2
971
971
972 put :update,
972 put :update,
973 :id => issue.id,
973 :id => issue.id,
974 :issue => {
974 :issue => {
975 :fixed_version_id => 4
975 :fixed_version_id => 4
976 },
976 },
977 :back_url => 'http://google.com'
977 :back_url => 'http://google.com'
978
978
979 assert_response :redirect
979 assert_response :redirect
980 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
980 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
981 end
981 end
982
982
983 def test_get_bulk_edit
983 def test_get_bulk_edit
984 @request.session[:user_id] = 2
984 @request.session[:user_id] = 2
985 get :bulk_edit, :ids => [1, 2]
985 get :bulk_edit, :ids => [1, 2]
986 assert_response :success
986 assert_response :success
987 assert_template 'bulk_edit'
987 assert_template 'bulk_edit'
988
988
989 # Project specific custom field, date type
989 # Project specific custom field, date type
990 field = CustomField.find(9)
990 field = CustomField.find(9)
991 assert !field.is_for_all?
991 assert !field.is_for_all?
992 assert_equal 'date', field.field_format
992 assert_equal 'date', field.field_format
993 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
993 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
994
994
995 # System wide custom field
995 # System wide custom field
996 assert CustomField.find(1).is_for_all?
996 assert CustomField.find(1).is_for_all?
997 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
997 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
998 end
998 end
999
999
1000 def test_bulk_edit
1000 def test_bulk_edit
1001 @request.session[:user_id] = 2
1001 @request.session[:user_id] = 2
1002 # update issues priority
1002 # update issues priority
1003 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing',
1003 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing',
1004 :issue => {:priority_id => 7,
1004 :issue => {:priority_id => 7,
1005 :assigned_to_id => '',
1005 :assigned_to_id => '',
1006 :custom_field_values => {'2' => ''}}
1006 :custom_field_values => {'2' => ''}}
1007
1007
1008 assert_response 302
1008 assert_response 302
1009 # check that the issues were updated
1009 # check that the issues were updated
1010 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
1010 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
1011
1011
1012 issue = Issue.find(1)
1012 issue = Issue.find(1)
1013 journal = issue.journals.find(:first, :order => 'created_on DESC')
1013 journal = issue.journals.find(:first, :order => 'created_on DESC')
1014 assert_equal '125', issue.custom_value_for(2).value
1014 assert_equal '125', issue.custom_value_for(2).value
1015 assert_equal 'Bulk editing', journal.notes
1015 assert_equal 'Bulk editing', journal.notes
1016 assert_equal 1, journal.details.size
1016 assert_equal 1, journal.details.size
1017 end
1017 end
1018
1018
1019 def test_bullk_edit_should_send_a_notification
1019 def test_bullk_edit_should_send_a_notification
1020 @request.session[:user_id] = 2
1020 @request.session[:user_id] = 2
1021 ActionMailer::Base.deliveries.clear
1021 ActionMailer::Base.deliveries.clear
1022 post(:bulk_edit,
1022 post(:bulk_edit,
1023 {
1023 {
1024 :ids => [1, 2],
1024 :ids => [1, 2],
1025 :notes => 'Bulk editing',
1025 :notes => 'Bulk editing',
1026 :issue => {
1026 :issue => {
1027 :priority_id => 7,
1027 :priority_id => 7,
1028 :assigned_to_id => '',
1028 :assigned_to_id => '',
1029 :custom_field_values => {'2' => ''}
1029 :custom_field_values => {'2' => ''}
1030 }
1030 }
1031 })
1031 })
1032
1032
1033 assert_response 302
1033 assert_response 302
1034 assert_equal 2, ActionMailer::Base.deliveries.size
1034 assert_equal 2, ActionMailer::Base.deliveries.size
1035 end
1035 end
1036
1036
1037 def test_bulk_edit_status
1037 def test_bulk_edit_status
1038 @request.session[:user_id] = 2
1038 @request.session[:user_id] = 2
1039 # update issues priority
1039 # update issues priority
1040 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing status',
1040 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing status',
1041 :issue => {:priority_id => '',
1041 :issue => {:priority_id => '',
1042 :assigned_to_id => '',
1042 :assigned_to_id => '',
1043 :status_id => '5'}
1043 :status_id => '5'}
1044
1044
1045 assert_response 302
1045 assert_response 302
1046 issue = Issue.find(1)
1046 issue = Issue.find(1)
1047 assert issue.closed?
1047 assert issue.closed?
1048 end
1048 end
1049
1049
1050 def test_bulk_edit_custom_field
1050 def test_bulk_edit_custom_field
1051 @request.session[:user_id] = 2
1051 @request.session[:user_id] = 2
1052 # update issues priority
1052 # update issues priority
1053 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing custom field',
1053 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing custom field',
1054 :issue => {:priority_id => '',
1054 :issue => {:priority_id => '',
1055 :assigned_to_id => '',
1055 :assigned_to_id => '',
1056 :custom_field_values => {'2' => '777'}}
1056 :custom_field_values => {'2' => '777'}}
1057
1057
1058 assert_response 302
1058 assert_response 302
1059
1059
1060 issue = Issue.find(1)
1060 issue = Issue.find(1)
1061 journal = issue.journals.find(:first, :order => 'created_on DESC')
1061 journal = issue.journals.find(:first, :order => 'created_on DESC')
1062 assert_equal '777', issue.custom_value_for(2).value
1062 assert_equal '777', issue.custom_value_for(2).value
1063 assert_equal 1, journal.details.size
1063 assert_equal 1, journal.details.size
1064 assert_equal '125', journal.details.first.old_value
1064 assert_equal '125', journal.details.first.old_value
1065 assert_equal '777', journal.details.first.value
1065 assert_equal '777', journal.details.first.value
1066 end
1066 end
1067
1067
1068 def test_bulk_unassign
1068 def test_bulk_unassign
1069 assert_not_nil Issue.find(2).assigned_to
1069 assert_not_nil Issue.find(2).assigned_to
1070 @request.session[:user_id] = 2
1070 @request.session[:user_id] = 2
1071 # unassign issues
1071 # unassign issues
1072 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
1072 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
1073 assert_response 302
1073 assert_response 302
1074 # check that the issues were updated
1074 # check that the issues were updated
1075 assert_nil Issue.find(2).assigned_to
1075 assert_nil Issue.find(2).assigned_to
1076 end
1076 end
1077
1077
1078 def test_post_bulk_edit_should_allow_fixed_version_to_be_set_to_a_subproject
1078 def test_post_bulk_edit_should_allow_fixed_version_to_be_set_to_a_subproject
1079 @request.session[:user_id] = 2
1079 @request.session[:user_id] = 2
1080
1080
1081 post :bulk_edit, :ids => [1,2], :issue => {:fixed_version_id => 4}
1081 post :bulk_edit, :ids => [1,2], :issue => {:fixed_version_id => 4}
1082
1082
1083 assert_response :redirect
1083 assert_response :redirect
1084 issues = Issue.find([1,2])
1084 issues = Issue.find([1,2])
1085 issues.each do |issue|
1085 issues.each do |issue|
1086 assert_equal 4, issue.fixed_version_id
1086 assert_equal 4, issue.fixed_version_id
1087 assert_not_equal issue.project_id, issue.fixed_version.project_id
1087 assert_not_equal issue.project_id, issue.fixed_version.project_id
1088 end
1088 end
1089 end
1089 end
1090
1090
1091 def test_post_bulk_edit_should_redirect_back_using_the_back_url_parameter
1091 def test_post_bulk_edit_should_redirect_back_using_the_back_url_parameter
1092 @request.session[:user_id] = 2
1092 @request.session[:user_id] = 2
1093 post :bulk_edit, :ids => [1,2], :back_url => '/issues'
1093 post :bulk_edit, :ids => [1,2], :back_url => '/issues'
1094
1094
1095 assert_response :redirect
1095 assert_response :redirect
1096 assert_redirected_to '/issues'
1096 assert_redirected_to '/issues'
1097 end
1097 end
1098
1098
1099 def test_post_bulk_edit_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1099 def test_post_bulk_edit_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1100 @request.session[:user_id] = 2
1100 @request.session[:user_id] = 2
1101 post :bulk_edit, :ids => [1,2], :back_url => 'http://google.com'
1101 post :bulk_edit, :ids => [1,2], :back_url => 'http://google.com'
1102
1102
1103 assert_response :redirect
1103 assert_response :redirect
1104 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
1104 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
1105 end
1105 end
1106
1106
1107 def test_move_one_issue_to_another_project
1107 def test_move_one_issue_to_another_project
1108 @request.session[:user_id] = 2
1108 @request.session[:user_id] = 2
1109 post :move, :id => 1, :new_project_id => 2, :tracker_id => '', :assigned_to_id => '', :status_id => '', :start_date => '', :due_date => ''
1109 post :move, :id => 1, :new_project_id => 2, :tracker_id => '', :assigned_to_id => '', :status_id => '', :start_date => '', :due_date => ''
1110 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1110 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1111 assert_equal 2, Issue.find(1).project_id
1111 assert_equal 2, Issue.find(1).project_id
1112 end
1112 end
1113
1113
1114 def test_move_one_issue_to_another_project_should_follow_when_needed
1114 def test_move_one_issue_to_another_project_should_follow_when_needed
1115 @request.session[:user_id] = 2
1115 @request.session[:user_id] = 2
1116 post :move, :id => 1, :new_project_id => 2, :follow => '1'
1116 post :move, :id => 1, :new_project_id => 2, :follow => '1'
1117 assert_redirected_to '/issues/1'
1117 assert_redirected_to '/issues/1'
1118 end
1118 end
1119
1119
1120 def test_bulk_move_to_another_project
1120 def test_bulk_move_to_another_project
1121 @request.session[:user_id] = 2
1121 @request.session[:user_id] = 2
1122 post :move, :ids => [1, 2], :new_project_id => 2
1122 post :move, :ids => [1, 2], :new_project_id => 2
1123 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1123 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1124 # Issues moved to project 2
1124 # Issues moved to project 2
1125 assert_equal 2, Issue.find(1).project_id
1125 assert_equal 2, Issue.find(1).project_id
1126 assert_equal 2, Issue.find(2).project_id
1126 assert_equal 2, Issue.find(2).project_id
1127 # No tracker change
1127 # No tracker change
1128 assert_equal 1, Issue.find(1).tracker_id
1128 assert_equal 1, Issue.find(1).tracker_id
1129 assert_equal 2, Issue.find(2).tracker_id
1129 assert_equal 2, Issue.find(2).tracker_id
1130 end
1130 end
1131
1131
1132 def test_bulk_move_to_another_tracker
1132 def test_bulk_move_to_another_tracker
1133 @request.session[:user_id] = 2
1133 @request.session[:user_id] = 2
1134 post :move, :ids => [1, 2], :new_tracker_id => 2
1134 post :move, :ids => [1, 2], :new_tracker_id => 2
1135 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1135 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1136 assert_equal 2, Issue.find(1).tracker_id
1136 assert_equal 2, Issue.find(1).tracker_id
1137 assert_equal 2, Issue.find(2).tracker_id
1137 assert_equal 2, Issue.find(2).tracker_id
1138 end
1138 end
1139
1139
1140 def test_bulk_copy_to_another_project
1140 def test_bulk_copy_to_another_project
1141 @request.session[:user_id] = 2
1141 @request.session[:user_id] = 2
1142 assert_difference 'Issue.count', 2 do
1142 assert_difference 'Issue.count', 2 do
1143 assert_no_difference 'Project.find(1).issues.count' do
1143 assert_no_difference 'Project.find(1).issues.count' do
1144 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}
1144 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}
1145 end
1145 end
1146 end
1146 end
1147 assert_redirected_to 'projects/ecookbook/issues'
1147 assert_redirected_to 'projects/ecookbook/issues'
1148 end
1148 end
1149
1149
1150 context "#move via bulk copy" do
1150 context "#move via bulk copy" do
1151 should "allow not changing the issue's attributes" do
1151 should "allow not changing the issue's attributes" do
1152 @request.session[:user_id] = 2
1152 @request.session[:user_id] = 2
1153 issue_before_move = Issue.find(1)
1153 issue_before_move = Issue.find(1)
1154 assert_difference 'Issue.count', 1 do
1154 assert_difference 'Issue.count', 1 do
1155 assert_no_difference 'Project.find(1).issues.count' do
1155 assert_no_difference 'Project.find(1).issues.count' do
1156 post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :new_tracker_id => '', :assigned_to_id => '', :status_id => '', :start_date => '', :due_date => ''
1156 post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :new_tracker_id => '', :assigned_to_id => '', :status_id => '', :start_date => '', :due_date => ''
1157 end
1157 end
1158 end
1158 end
1159 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
1159 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
1160 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
1160 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
1161 assert_equal issue_before_move.status_id, issue_after_move.status_id
1161 assert_equal issue_before_move.status_id, issue_after_move.status_id
1162 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
1162 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
1163 end
1163 end
1164
1164
1165 should "allow changing the issue's attributes" do
1165 should "allow changing the issue's attributes" do
1166 @request.session[:user_id] = 2
1166 @request.session[:user_id] = 2
1167 assert_difference 'Issue.count', 2 do
1167 assert_difference 'Issue.count', 2 do
1168 assert_no_difference 'Project.find(1).issues.count' do
1168 assert_no_difference 'Project.find(1).issues.count' do
1169 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}, :new_tracker_id => '', :assigned_to_id => 4, :status_id => 3, :start_date => '2009-12-01', :due_date => '2009-12-31'
1169 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}, :new_tracker_id => '', :assigned_to_id => 4, :status_id => 3, :start_date => '2009-12-01', :due_date => '2009-12-31'
1170 end
1170 end
1171 end
1171 end
1172
1172
1173 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
1173 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
1174 assert_equal 2, copied_issues.size
1174 assert_equal 2, copied_issues.size
1175 copied_issues.each do |issue|
1175 copied_issues.each do |issue|
1176 assert_equal 2, issue.project_id, "Project is incorrect"
1176 assert_equal 2, issue.project_id, "Project is incorrect"
1177 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
1177 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
1178 assert_equal 3, issue.status_id, "Status is incorrect"
1178 assert_equal 3, issue.status_id, "Status is incorrect"
1179 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
1179 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
1180 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
1180 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
1181 end
1181 end
1182 end
1182 end
1183 end
1183 end
1184
1184
1185 def test_copy_to_another_project_should_follow_when_needed
1185 def test_copy_to_another_project_should_follow_when_needed
1186 @request.session[:user_id] = 2
1186 @request.session[:user_id] = 2
1187 post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :follow => '1'
1187 post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :follow => '1'
1188 issue = Issue.first(:order => 'id DESC')
1188 issue = Issue.first(:order => 'id DESC')
1189 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1189 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1190 end
1190 end
1191
1191
1192 def test_context_menu_one_issue
1192 def test_context_menu_one_issue
1193 @request.session[:user_id] = 2
1193 @request.session[:user_id] = 2
1194 get :context_menu, :ids => [1]
1194 get :context_menu, :ids => [1]
1195 assert_response :success
1195 assert_response :success
1196 assert_template 'context_menu'
1196 assert_template 'context_menu'
1197 assert_tag :tag => 'a', :content => 'Edit',
1197 assert_tag :tag => 'a', :content => 'Edit',
1198 :attributes => { :href => '/issues/1/edit',
1198 :attributes => { :href => '/issues/1/edit',
1199 :class => 'icon-edit' }
1199 :class => 'icon-edit' }
1200 assert_tag :tag => 'a', :content => 'Closed',
1200 assert_tag :tag => 'a', :content => 'Closed',
1201 :attributes => { :href => '/issues/1/edit?issue%5Bstatus_id%5D=5',
1201 :attributes => { :href => '/issues/1/edit?issue%5Bstatus_id%5D=5',
1202 :class => '' }
1202 :class => '' }
1203 assert_tag :tag => 'a', :content => 'Immediate',
1203 assert_tag :tag => 'a', :content => 'Immediate',
1204 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bpriority_id%5D=8',
1204 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bpriority_id%5D=8',
1205 :class => '' }
1205 :class => '' }
1206 # Versions
1206 # Versions
1207 assert_tag :tag => 'a', :content => '2.0',
1207 assert_tag :tag => 'a', :content => '2.0',
1208 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=3',
1208 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=3',
1209 :class => '' }
1209 :class => '' }
1210 assert_tag :tag => 'a', :content => 'eCookbook Subproject 1 - 2.0',
1210 assert_tag :tag => 'a', :content => 'eCookbook Subproject 1 - 2.0',
1211 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=4',
1211 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=4',
1212 :class => '' }
1212 :class => '' }
1213
1213
1214 assert_tag :tag => 'a', :content => 'Dave Lopper',
1214 assert_tag :tag => 'a', :content => 'Dave Lopper',
1215 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bassigned_to_id%5D=3',
1215 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bassigned_to_id%5D=3',
1216 :class => '' }
1216 :class => '' }
1217 assert_tag :tag => 'a', :content => 'Duplicate',
1217 assert_tag :tag => 'a', :content => 'Duplicate',
1218 :attributes => { :href => '/projects/ecookbook/issues/1/copy',
1218 :attributes => { :href => '/projects/ecookbook/issues/1/copy',
1219 :class => 'icon-duplicate' }
1219 :class => 'icon-duplicate' }
1220 assert_tag :tag => 'a', :content => 'Copy',
1220 assert_tag :tag => 'a', :content => 'Copy',
1221 :attributes => { :href => '/issues/move?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1',
1221 :attributes => { :href => '/issues/move?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1',
1222 :class => 'icon-copy' }
1222 :class => 'icon-copy' }
1223 assert_tag :tag => 'a', :content => 'Move',
1223 assert_tag :tag => 'a', :content => 'Move',
1224 :attributes => { :href => '/issues/move?ids%5B%5D=1',
1224 :attributes => { :href => '/issues/move?ids%5B%5D=1',
1225 :class => 'icon-move' }
1225 :class => 'icon-move' }
1226 assert_tag :tag => 'a', :content => 'Delete',
1226 assert_tag :tag => 'a', :content => 'Delete',
1227 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
1227 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
1228 :class => 'icon-del' }
1228 :class => 'icon-del' }
1229 end
1229 end
1230
1230
1231 def test_context_menu_one_issue_by_anonymous
1231 def test_context_menu_one_issue_by_anonymous
1232 get :context_menu, :ids => [1]
1232 get :context_menu, :ids => [1]
1233 assert_response :success
1233 assert_response :success
1234 assert_template 'context_menu'
1234 assert_template 'context_menu'
1235 assert_tag :tag => 'a', :content => 'Delete',
1235 assert_tag :tag => 'a', :content => 'Delete',
1236 :attributes => { :href => '#',
1236 :attributes => { :href => '#',
1237 :class => 'icon-del disabled' }
1237 :class => 'icon-del disabled' }
1238 end
1238 end
1239
1239
1240 def test_context_menu_multiple_issues_of_same_project
1240 def test_context_menu_multiple_issues_of_same_project
1241 @request.session[:user_id] = 2
1241 @request.session[:user_id] = 2
1242 get :context_menu, :ids => [1, 2]
1242 get :context_menu, :ids => [1, 2]
1243 assert_response :success
1243 assert_response :success
1244 assert_template 'context_menu'
1244 assert_template 'context_menu'
1245 assert_tag :tag => 'a', :content => 'Edit',
1245 assert_tag :tag => 'a', :content => 'Edit',
1246 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
1246 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
1247 :class => 'icon-edit' }
1247 :class => 'icon-edit' }
1248 assert_tag :tag => 'a', :content => 'Immediate',
1248 assert_tag :tag => 'a', :content => 'Immediate',
1249 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;issue%5Bpriority_id%5D=8',
1249 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;issue%5Bpriority_id%5D=8',
1250 :class => '' }
1250 :class => '' }
1251 assert_tag :tag => 'a', :content => 'Dave Lopper',
1251 assert_tag :tag => 'a', :content => 'Dave Lopper',
1252 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;issue%5Bassigned_to_id%5D=3',
1252 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;issue%5Bassigned_to_id%5D=3',
1253 :class => '' }
1253 :class => '' }
1254 assert_tag :tag => 'a', :content => 'Copy',
1254 assert_tag :tag => 'a', :content => 'Copy',
1255 :attributes => { :href => '/issues/move?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
1255 :attributes => { :href => '/issues/move?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
1256 :class => 'icon-copy' }
1256 :class => 'icon-copy' }
1257 assert_tag :tag => 'a', :content => 'Move',
1257 assert_tag :tag => 'a', :content => 'Move',
1258 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
1258 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
1259 :class => 'icon-move' }
1259 :class => 'icon-move' }
1260 assert_tag :tag => 'a', :content => 'Delete',
1260 assert_tag :tag => 'a', :content => 'Delete',
1261 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
1261 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
1262 :class => 'icon-del' }
1262 :class => 'icon-del' }
1263 end
1263 end
1264
1264
1265 def test_context_menu_multiple_issues_of_different_project
1265 def test_context_menu_multiple_issues_of_different_project
1266 @request.session[:user_id] = 2
1266 @request.session[:user_id] = 2
1267 get :context_menu, :ids => [1, 2, 4]
1267 get :context_menu, :ids => [1, 2, 4]
1268 assert_response :success
1268 assert_response :success
1269 assert_template 'context_menu'
1269 assert_template 'context_menu'
1270 assert_tag :tag => 'a', :content => 'Delete',
1270 assert_tag :tag => 'a', :content => 'Delete',
1271 :attributes => { :href => '#',
1271 :attributes => { :href => '#',
1272 :class => 'icon-del disabled' }
1272 :class => 'icon-del disabled' }
1273 end
1273 end
1274
1275 def test_preview_new_issue
1276 @request.session[:user_id] = 2
1277 post :preview, :project_id => '1', :issue => {:description => 'Foo'}
1278 assert_response :success
1279 assert_template 'preview'
1280 assert_not_nil assigns(:description)
1281 end
1282
1283 def test_preview_notes
1284 @request.session[:user_id] = 2
1285 post :preview, :project_id => '1', :id => 1, :issue => {:description => Issue.find(1).description}, :notes => 'Foo'
1286 assert_response :success
1287 assert_template 'preview'
1288 assert_not_nil assigns(:notes)
1289 end
1274
1290
1275 def test_auto_complete_routing
1291 def test_auto_complete_routing
1276 assert_routing(
1292 assert_routing(
1277 {:method => :get, :path => '/issues/auto_complete'},
1293 {:method => :get, :path => '/issues/auto_complete'},
1278 :controller => 'issues', :action => 'auto_complete'
1294 :controller => 'issues', :action => 'auto_complete'
1279 )
1295 )
1280 end
1296 end
1281
1297
1282 def test_auto_complete_should_not_be_case_sensitive
1298 def test_auto_complete_should_not_be_case_sensitive
1283 get :auto_complete, :project_id => 'ecookbook', :q => 'ReCiPe'
1299 get :auto_complete, :project_id => 'ecookbook', :q => 'ReCiPe'
1284 assert_response :success
1300 assert_response :success
1285 assert_not_nil assigns(:issues)
1301 assert_not_nil assigns(:issues)
1286 assert assigns(:issues).detect {|issue| issue.subject.match /recipe/}
1302 assert assigns(:issues).detect {|issue| issue.subject.match /recipe/}
1287 end
1303 end
1288
1304
1289 def test_auto_complete_should_return_issue_with_given_id
1305 def test_auto_complete_should_return_issue_with_given_id
1290 get :auto_complete, :project_id => 'subproject1', :q => '13'
1306 get :auto_complete, :project_id => 'subproject1', :q => '13'
1291 assert_response :success
1307 assert_response :success
1292 assert_not_nil assigns(:issues)
1308 assert_not_nil assigns(:issues)
1293 assert assigns(:issues).include?(Issue.find(13))
1309 assert assigns(:issues).include?(Issue.find(13))
1294 end
1310 end
1295
1311
1296 def test_destroy_routing
1312 def test_destroy_routing
1297 assert_recognizes( #TODO: use DELETE on issue URI (need to change forms)
1313 assert_recognizes( #TODO: use DELETE on issue URI (need to change forms)
1298 {:controller => 'issues', :action => 'destroy', :id => '1'},
1314 {:controller => 'issues', :action => 'destroy', :id => '1'},
1299 {:method => :post, :path => '/issues/1/destroy'}
1315 {:method => :post, :path => '/issues/1/destroy'}
1300 )
1316 )
1301 end
1317 end
1302
1318
1303 def test_destroy_issue_with_no_time_entries
1319 def test_destroy_issue_with_no_time_entries
1304 assert_nil TimeEntry.find_by_issue_id(2)
1320 assert_nil TimeEntry.find_by_issue_id(2)
1305 @request.session[:user_id] = 2
1321 @request.session[:user_id] = 2
1306 post :destroy, :id => 2
1322 post :destroy, :id => 2
1307 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1323 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1308 assert_nil Issue.find_by_id(2)
1324 assert_nil Issue.find_by_id(2)
1309 end
1325 end
1310
1326
1311 def test_destroy_issues_with_time_entries
1327 def test_destroy_issues_with_time_entries
1312 @request.session[:user_id] = 2
1328 @request.session[:user_id] = 2
1313 post :destroy, :ids => [1, 3]
1329 post :destroy, :ids => [1, 3]
1314 assert_response :success
1330 assert_response :success
1315 assert_template 'destroy'
1331 assert_template 'destroy'
1316 assert_not_nil assigns(:hours)
1332 assert_not_nil assigns(:hours)
1317 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1333 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1318 end
1334 end
1319
1335
1320 def test_destroy_issues_and_destroy_time_entries
1336 def test_destroy_issues_and_destroy_time_entries
1321 @request.session[:user_id] = 2
1337 @request.session[:user_id] = 2
1322 post :destroy, :ids => [1, 3], :todo => 'destroy'
1338 post :destroy, :ids => [1, 3], :todo => 'destroy'
1323 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1339 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1324 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1340 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1325 assert_nil TimeEntry.find_by_id([1, 2])
1341 assert_nil TimeEntry.find_by_id([1, 2])
1326 end
1342 end
1327
1343
1328 def test_destroy_issues_and_assign_time_entries_to_project
1344 def test_destroy_issues_and_assign_time_entries_to_project
1329 @request.session[:user_id] = 2
1345 @request.session[:user_id] = 2
1330 post :destroy, :ids => [1, 3], :todo => 'nullify'
1346 post :destroy, :ids => [1, 3], :todo => 'nullify'
1331 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1347 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1332 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1348 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1333 assert_nil TimeEntry.find(1).issue_id
1349 assert_nil TimeEntry.find(1).issue_id
1334 assert_nil TimeEntry.find(2).issue_id
1350 assert_nil TimeEntry.find(2).issue_id
1335 end
1351 end
1336
1352
1337 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1353 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1338 @request.session[:user_id] = 2
1354 @request.session[:user_id] = 2
1339 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1355 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1340 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1356 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1341 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1357 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1342 assert_equal 2, TimeEntry.find(1).issue_id
1358 assert_equal 2, TimeEntry.find(1).issue_id
1343 assert_equal 2, TimeEntry.find(2).issue_id
1359 assert_equal 2, TimeEntry.find(2).issue_id
1344 end
1360 end
1345
1361
1346 def test_default_search_scope
1362 def test_default_search_scope
1347 get :index
1363 get :index
1348 assert_tag :div, :attributes => {:id => 'quick-search'},
1364 assert_tag :div, :attributes => {:id => 'quick-search'},
1349 :child => {:tag => 'form',
1365 :child => {:tag => 'form',
1350 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1366 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1351 end
1367 end
1352 end
1368 end
General Comments 0
You need to be logged in to leave comments. Login now