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