##// END OF EJS Templates
Merged r5991 from trunk....
Jean-Philippe Lang -
r6038:2325831d81a3
parent child
Show More
@@ -1,335 +1,335
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class IssuesController < ApplicationController
19 19 menu_item :new_issue, :only => [:new, :create]
20 20 default_search_scope :issues
21 21
22 22 before_filter :find_issue, :only => [:show, :edit, :update]
23 23 before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :move, :perform_move, :destroy]
24 24 before_filter :check_project_uniqueness, :only => [:move, :perform_move]
25 25 before_filter :find_project, :only => [:new, :create]
26 26 before_filter :authorize, :except => [:index]
27 27 before_filter :find_optional_project, :only => [:index]
28 28 before_filter :check_for_default_issue_status, :only => [:new, :create]
29 29 before_filter :build_new_issue_from_params, :only => [:new, :create]
30 30 accept_key_auth :index, :show, :create, :update, :destroy
31 31
32 32 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
33 33
34 34 helper :journals
35 35 helper :projects
36 36 include ProjectsHelper
37 37 helper :custom_fields
38 38 include CustomFieldsHelper
39 39 helper :issue_relations
40 40 include IssueRelationsHelper
41 41 helper :watchers
42 42 include WatchersHelper
43 43 helper :attachments
44 44 include AttachmentsHelper
45 45 helper :queries
46 46 include QueriesHelper
47 47 helper :repositories
48 48 include RepositoriesHelper
49 49 helper :sort
50 50 include SortHelper
51 51 include IssuesHelper
52 52 helper :timelog
53 53 helper :gantt
54 54 include Redmine::Export::PDF
55 55
56 56 verify :method => [:post, :delete],
57 57 :only => :destroy,
58 58 :render => { :nothing => true, :status => :method_not_allowed }
59 59
60 60 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
61 61 verify :method => :post, :only => :bulk_update, :render => {:nothing => true, :status => :method_not_allowed }
62 62 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
63 63
64 64 def index
65 65 retrieve_query
66 66 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
67 67 sort_update(@query.sortable_columns)
68 68
69 69 if @query.valid?
70 70 case params[:format]
71 71 when 'csv', 'pdf'
72 72 @limit = Setting.issues_export_limit.to_i
73 73 when 'atom'
74 74 @limit = Setting.feeds_limit.to_i
75 75 when 'xml', 'json'
76 76 @offset, @limit = api_offset_and_limit
77 77 else
78 78 @limit = per_page_option
79 79 end
80 80
81 81 @issue_count = @query.issue_count
82 82 @issue_pages = Paginator.new self, @issue_count, @limit, params['page']
83 83 @offset ||= @issue_pages.current.offset
84 84 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
85 85 :order => sort_clause,
86 86 :offset => @offset,
87 87 :limit => @limit)
88 88 @issue_count_by_group = @query.issue_count_by_group
89 89
90 90 respond_to do |format|
91 91 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
92 92 format.api
93 93 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
94 94 format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
95 95 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
96 96 end
97 97 else
98 98 # Send html if the query is not valid
99 99 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
100 100 end
101 101 rescue ActiveRecord::RecordNotFound
102 102 render_404
103 103 end
104 104
105 105 def show
106 106 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
107 107 @journals.each_with_index {|j,i| j.indice = i+1}
108 108 @journals.reverse! if User.current.wants_comments_in_reverse_order?
109 109
110 110 if User.current.allowed_to?(:view_changesets, @project)
111 111 @changesets = @issue.changesets.visible.all
112 112 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
113 113 end
114 114
115 115 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
116 116 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
117 117 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
118 118 @priorities = IssuePriority.all
119 119 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
120 120 respond_to do |format|
121 121 format.html { render :template => 'issues/show.rhtml' }
122 122 format.api
123 123 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
124 124 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
125 125 end
126 126 end
127 127
128 128 # Add a new issue
129 129 # The new issue will be created from an existing one if copy_from parameter is given
130 130 def new
131 131 respond_to do |format|
132 132 format.html { render :action => 'new', :layout => !request.xhr? }
133 133 format.js { render :partial => 'attributes' }
134 134 end
135 135 end
136 136
137 137 def create
138 138 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
139 139 if @issue.save
140 140 attachments = Attachment.attach_files(@issue, params[:attachments])
141 141 render_attachment_warning_if_needed(@issue)
142 142 flash[:notice] = l(:notice_successful_create)
143 143 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
144 144 respond_to do |format|
145 145 format.html {
146 146 redirect_to(params[:continue] ? { :action => 'new', :project_id => @project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
147 147 { :action => 'show', :id => @issue })
148 148 }
149 149 format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
150 150 end
151 151 return
152 152 else
153 153 respond_to do |format|
154 154 format.html { render :action => 'new' }
155 155 format.api { render_validation_errors(@issue) }
156 156 end
157 157 end
158 158 end
159 159
160 160 def edit
161 161 update_issue_from_params
162 162
163 163 @journal = @issue.current_journal
164 164
165 165 respond_to do |format|
166 166 format.html { }
167 167 format.xml { }
168 168 end
169 169 end
170 170
171 171 def update
172 172 update_issue_from_params
173 173
174 174 if @issue.save_issue_with_child_records(params, @time_entry)
175 175 render_attachment_warning_if_needed(@issue)
176 176 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
177 177
178 178 respond_to do |format|
179 179 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
180 180 format.api { head :ok }
181 181 end
182 182 else
183 183 render_attachment_warning_if_needed(@issue)
184 184 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
185 185 @journal = @issue.current_journal
186 186
187 187 respond_to do |format|
188 188 format.html { render :action => 'edit' }
189 189 format.api { render_validation_errors(@issue) }
190 190 end
191 191 end
192 192 end
193 193
194 194 # Bulk edit a set of issues
195 195 def bulk_edit
196 196 @issues.sort!
197 197 @available_statuses = @projects.map{|p|Workflow.available_statuses(p)}.inject{|memo,w|memo & w}
198 198 @custom_fields = @projects.map{|p|p.all_issue_custom_fields}.inject{|memo,c|memo & c}
199 199 @assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
200 200 @trackers = @projects.map(&:trackers).inject{|memo,t| memo & t}
201 201 end
202 202
203 203 def bulk_update
204 204 @issues.sort!
205 205 attributes = parse_params_for_bulk_issue_attributes(params)
206 206
207 207 unsaved_issue_ids = []
208 208 @issues.each do |issue|
209 209 issue.reload
210 210 journal = issue.init_journal(User.current, params[:notes])
211 211 issue.safe_attributes = attributes
212 212 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
213 213 unless issue.save
214 214 # Keep unsaved issue ids to display them in flash error
215 215 unsaved_issue_ids << issue.id
216 216 end
217 217 end
218 218 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
219 219 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
220 220 end
221 221
222 222 def destroy
223 223 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
224 224 if @hours > 0
225 225 case params[:todo]
226 226 when 'destroy'
227 227 # nothing to do
228 228 when 'nullify'
229 229 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
230 230 when 'reassign'
231 231 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
232 232 if reassign_to.nil?
233 233 flash.now[:error] = l(:error_issue_not_found_in_project)
234 234 return
235 235 else
236 236 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
237 237 end
238 238 else
239 239 # display the destroy form if it's a user request
240 240 return unless api_request?
241 241 end
242 242 end
243 243 @issues.each do |issue|
244 244 begin
245 245 issue.reload.destroy
246 246 rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
247 247 # nothing to do, issue was already deleted (eg. by a parent)
248 248 end
249 249 end
250 250 respond_to do |format|
251 251 format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
252 252 format.api { head :ok }
253 253 end
254 254 end
255 255
256 256 private
257 257 def find_issue
258 258 # Issue.visible.find(...) can not be used to redirect user to the login form
259 259 # if the issue actually exists but requires authentication
260 260 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
261 261 unless @issue.visible?
262 262 deny_access
263 263 return
264 264 end
265 265 @project = @issue.project
266 266 rescue ActiveRecord::RecordNotFound
267 267 render_404
268 268 end
269 269
270 270 def find_project
271 271 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
272 272 @project = Project.find(project_id)
273 273 rescue ActiveRecord::RecordNotFound
274 274 render_404
275 275 end
276 276
277 277 # Used by #edit and #update to set some common instance variables
278 278 # from the params
279 279 # TODO: Refactor, not everything in here is needed by #edit
280 280 def update_issue_from_params
281 281 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
282 282 @priorities = IssuePriority.all
283 283 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
284 284 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
285 285 @time_entry.attributes = params[:time_entry]
286 286
287 287 @notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
288 288 @issue.init_journal(User.current, @notes)
289 289 @issue.safe_attributes = params[:issue]
290 290 end
291 291
292 292 # TODO: Refactor, lots of extra code in here
293 293 # TODO: Changing tracker on an existing issue should not trigger this
294 294 def build_new_issue_from_params
295 295 if params[:id].blank?
296 296 @issue = Issue.new
297 297 @issue.copy_from(params[:copy_from]) if params[:copy_from]
298 298 @issue.project = @project
299 299 else
300 300 @issue = @project.issues.visible.find(params[:id])
301 301 end
302 302
303 303 @issue.project = @project
304 @issue.author = User.current
304 305 # Tracker must be set before custom field values
305 306 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
306 307 if @issue.tracker.nil?
307 308 render_error l(:error_no_tracker_in_project)
308 309 return false
309 310 end
310 311 @issue.start_date ||= Date.today
311 312 if params[:issue].is_a?(Hash)
312 313 @issue.safe_attributes = params[:issue]
313 314 if User.current.allowed_to?(:add_issue_watchers, @project) && @issue.new_record?
314 315 @issue.watcher_user_ids = params[:issue]['watcher_user_ids']
315 316 end
316 317 end
317 @issue.author = User.current
318 318 @priorities = IssuePriority.all
319 319 @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
320 320 end
321 321
322 322 def check_for_default_issue_status
323 323 if IssueStatus.default.nil?
324 324 render_error l(:error_no_default_issue_status)
325 325 return false
326 326 end
327 327 end
328 328
329 329 def parse_params_for_bulk_issue_attributes(params)
330 330 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
331 331 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
332 332 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
333 333 attributes
334 334 end
335 335 end
@@ -1,193 +1,194
1 1 ---
2 2 roles_001:
3 3 name: Manager
4 4 id: 1
5 5 builtin: 0
6 6 issues_visibility: all
7 7 permissions: |
8 8 ---
9 9 - :add_project
10 10 - :edit_project
11 11 - :select_project_modules
12 12 - :manage_members
13 13 - :manage_versions
14 14 - :manage_categories
15 15 - :view_issues
16 16 - :add_issues
17 17 - :edit_issues
18 18 - :manage_issue_relations
19 19 - :manage_subtasks
20 20 - :add_issue_notes
21 21 - :move_issues
22 22 - :delete_issues
23 23 - :view_issue_watchers
24 24 - :add_issue_watchers
25 - :set_issues_private
25 26 - :delete_issue_watchers
26 27 - :manage_public_queries
27 28 - :save_queries
28 29 - :view_gantt
29 30 - :view_calendar
30 31 - :log_time
31 32 - :view_time_entries
32 33 - :edit_time_entries
33 34 - :delete_time_entries
34 35 - :manage_news
35 36 - :comment_news
36 37 - :view_documents
37 38 - :manage_documents
38 39 - :view_wiki_pages
39 40 - :export_wiki_pages
40 41 - :view_wiki_edits
41 42 - :edit_wiki_pages
42 43 - :delete_wiki_pages_attachments
43 44 - :protect_wiki_pages
44 45 - :delete_wiki_pages
45 46 - :rename_wiki_pages
46 47 - :add_messages
47 48 - :edit_messages
48 49 - :delete_messages
49 50 - :manage_boards
50 51 - :view_files
51 52 - :manage_files
52 53 - :browse_repository
53 54 - :manage_repository
54 55 - :view_changesets
55 56 - :manage_project_activities
56 57
57 58 position: 1
58 59 roles_002:
59 60 name: Developer
60 61 id: 2
61 62 builtin: 0
62 63 issues_visibility: default
63 64 permissions: |
64 65 ---
65 66 - :edit_project
66 67 - :manage_members
67 68 - :manage_versions
68 69 - :manage_categories
69 70 - :view_issues
70 71 - :add_issues
71 72 - :edit_issues
72 73 - :manage_issue_relations
73 74 - :manage_subtasks
74 75 - :add_issue_notes
75 76 - :move_issues
76 77 - :delete_issues
77 78 - :view_issue_watchers
78 79 - :save_queries
79 80 - :view_gantt
80 81 - :view_calendar
81 82 - :log_time
82 83 - :view_time_entries
83 84 - :edit_own_time_entries
84 85 - :manage_news
85 86 - :comment_news
86 87 - :view_documents
87 88 - :manage_documents
88 89 - :view_wiki_pages
89 90 - :view_wiki_edits
90 91 - :edit_wiki_pages
91 92 - :protect_wiki_pages
92 93 - :delete_wiki_pages
93 94 - :add_messages
94 95 - :edit_own_messages
95 96 - :delete_own_messages
96 97 - :manage_boards
97 98 - :view_files
98 99 - :manage_files
99 100 - :browse_repository
100 101 - :view_changesets
101 102
102 103 position: 2
103 104 roles_003:
104 105 name: Reporter
105 106 id: 3
106 107 builtin: 0
107 108 issues_visibility: default
108 109 permissions: |
109 110 ---
110 111 - :edit_project
111 112 - :manage_members
112 113 - :manage_versions
113 114 - :manage_categories
114 115 - :view_issues
115 116 - :add_issues
116 117 - :edit_issues
117 118 - :manage_issue_relations
118 119 - :add_issue_notes
119 120 - :move_issues
120 121 - :view_issue_watchers
121 122 - :save_queries
122 123 - :view_gantt
123 124 - :view_calendar
124 125 - :log_time
125 126 - :view_time_entries
126 127 - :manage_news
127 128 - :comment_news
128 129 - :view_documents
129 130 - :manage_documents
130 131 - :view_wiki_pages
131 132 - :view_wiki_edits
132 133 - :edit_wiki_pages
133 134 - :delete_wiki_pages
134 135 - :add_messages
135 136 - :manage_boards
136 137 - :view_files
137 138 - :manage_files
138 139 - :browse_repository
139 140 - :view_changesets
140 141
141 142 position: 3
142 143 roles_004:
143 144 name: Non member
144 145 id: 4
145 146 builtin: 1
146 147 issues_visibility: default
147 148 permissions: |
148 149 ---
149 150 - :view_issues
150 151 - :add_issues
151 152 - :edit_issues
152 153 - :manage_issue_relations
153 154 - :add_issue_notes
154 155 - :move_issues
155 156 - :save_queries
156 157 - :view_gantt
157 158 - :view_calendar
158 159 - :log_time
159 160 - :view_time_entries
160 161 - :comment_news
161 162 - :view_documents
162 163 - :manage_documents
163 164 - :view_wiki_pages
164 165 - :view_wiki_edits
165 166 - :edit_wiki_pages
166 167 - :add_messages
167 168 - :view_files
168 169 - :manage_files
169 170 - :browse_repository
170 171 - :view_changesets
171 172
172 173 position: 4
173 174 roles_005:
174 175 name: Anonymous
175 176 id: 5
176 177 builtin: 2
177 178 issues_visibility: default
178 179 permissions: |
179 180 ---
180 181 - :view_issues
181 182 - :add_issue_notes
182 183 - :view_gantt
183 184 - :view_calendar
184 185 - :view_time_entries
185 186 - :view_documents
186 187 - :view_wiki_pages
187 188 - :view_wiki_edits
188 189 - :view_files
189 190 - :browse_repository
190 191 - :view_changesets
191 192
192 193 position: 5
193 194
@@ -1,1435 +1,1465
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19 require 'issues_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class IssuesController; def rescue_action(e) raise e end; end
23 23
24 24 class IssuesControllerTest < ActionController::TestCase
25 25 fixtures :projects,
26 26 :users,
27 27 :roles,
28 28 :members,
29 29 :member_roles,
30 30 :issues,
31 31 :issue_statuses,
32 32 :versions,
33 33 :trackers,
34 34 :projects_trackers,
35 35 :issue_categories,
36 36 :enabled_modules,
37 37 :enumerations,
38 38 :attachments,
39 39 :workflows,
40 40 :custom_fields,
41 41 :custom_values,
42 42 :custom_fields_projects,
43 43 :custom_fields_trackers,
44 44 :time_entries,
45 45 :journals,
46 46 :journal_details,
47 47 :queries
48 48
49 49 def setup
50 50 @controller = IssuesController.new
51 51 @request = ActionController::TestRequest.new
52 52 @response = ActionController::TestResponse.new
53 53 User.current = nil
54 54 end
55 55
56 56 def test_index
57 57 Setting.default_language = 'en'
58 58
59 59 get :index
60 60 assert_response :success
61 61 assert_template 'index.rhtml'
62 62 assert_not_nil assigns(:issues)
63 63 assert_nil assigns(:project)
64 64 assert_tag :tag => 'a', :content => /Can't print recipes/
65 65 assert_tag :tag => 'a', :content => /Subproject issue/
66 66 # private projects hidden
67 67 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
68 68 assert_no_tag :tag => 'a', :content => /Issue on project 2/
69 69 # project column
70 70 assert_tag :tag => 'th', :content => /Project/
71 71 end
72 72
73 73 def test_index_should_not_list_issues_when_module_disabled
74 74 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
75 75 get :index
76 76 assert_response :success
77 77 assert_template 'index.rhtml'
78 78 assert_not_nil assigns(:issues)
79 79 assert_nil assigns(:project)
80 80 assert_no_tag :tag => 'a', :content => /Can't print recipes/
81 81 assert_tag :tag => 'a', :content => /Subproject issue/
82 82 end
83 83
84 84 def test_index_should_not_list_issues_when_module_disabled
85 85 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
86 86 get :index
87 87 assert_response :success
88 88 assert_template 'index.rhtml'
89 89 assert_not_nil assigns(:issues)
90 90 assert_nil assigns(:project)
91 91 assert_no_tag :tag => 'a', :content => /Can't print recipes/
92 92 assert_tag :tag => 'a', :content => /Subproject issue/
93 93 end
94 94
95 95 def test_index_should_list_visible_issues_only
96 96 get :index, :per_page => 100
97 97 assert_response :success
98 98 assert_not_nil assigns(:issues)
99 99 assert_nil assigns(:issues).detect {|issue| !issue.visible?}
100 100 end
101 101
102 102 def test_index_with_project
103 103 Setting.display_subprojects_issues = 0
104 104 get :index, :project_id => 1
105 105 assert_response :success
106 106 assert_template 'index.rhtml'
107 107 assert_not_nil assigns(:issues)
108 108 assert_tag :tag => 'a', :content => /Can't print recipes/
109 109 assert_no_tag :tag => 'a', :content => /Subproject issue/
110 110 end
111 111
112 112 def test_index_with_project_and_subprojects
113 113 Setting.display_subprojects_issues = 1
114 114 get :index, :project_id => 1
115 115 assert_response :success
116 116 assert_template 'index.rhtml'
117 117 assert_not_nil assigns(:issues)
118 118 assert_tag :tag => 'a', :content => /Can't print recipes/
119 119 assert_tag :tag => 'a', :content => /Subproject issue/
120 120 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
121 121 end
122 122
123 123 def test_index_with_project_and_subprojects_should_show_private_subprojects
124 124 @request.session[:user_id] = 2
125 125 Setting.display_subprojects_issues = 1
126 126 get :index, :project_id => 1
127 127 assert_response :success
128 128 assert_template 'index.rhtml'
129 129 assert_not_nil assigns(:issues)
130 130 assert_tag :tag => 'a', :content => /Can't print recipes/
131 131 assert_tag :tag => 'a', :content => /Subproject issue/
132 132 assert_tag :tag => 'a', :content => /Issue of a private subproject/
133 133 end
134 134
135 135 def test_index_with_project_and_default_filter
136 136 get :index, :project_id => 1, :set_filter => 1
137 137 assert_response :success
138 138 assert_template 'index.rhtml'
139 139 assert_not_nil assigns(:issues)
140 140
141 141 query = assigns(:query)
142 142 assert_not_nil query
143 143 # default filter
144 144 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
145 145 end
146 146
147 147 def test_index_with_project_and_filter
148 148 get :index, :project_id => 1, :set_filter => 1,
149 149 :f => ['tracker_id'],
150 150 :op => {'tracker_id' => '='},
151 151 :v => {'tracker_id' => ['1']}
152 152 assert_response :success
153 153 assert_template 'index.rhtml'
154 154 assert_not_nil assigns(:issues)
155 155
156 156 query = assigns(:query)
157 157 assert_not_nil query
158 158 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
159 159 end
160 160
161 161 def test_index_with_project_and_empty_filters
162 162 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
163 163 assert_response :success
164 164 assert_template 'index.rhtml'
165 165 assert_not_nil assigns(:issues)
166 166
167 167 query = assigns(:query)
168 168 assert_not_nil query
169 169 # no filter
170 170 assert_equal({}, query.filters)
171 171 end
172 172
173 173 def test_index_with_query
174 174 get :index, :project_id => 1, :query_id => 5
175 175 assert_response :success
176 176 assert_template 'index.rhtml'
177 177 assert_not_nil assigns(:issues)
178 178 assert_nil assigns(:issue_count_by_group)
179 179 end
180 180
181 181 def test_index_with_query_grouped_by_tracker
182 182 get :index, :project_id => 1, :query_id => 6
183 183 assert_response :success
184 184 assert_template 'index.rhtml'
185 185 assert_not_nil assigns(:issues)
186 186 assert_not_nil assigns(:issue_count_by_group)
187 187 end
188 188
189 189 def test_index_with_query_grouped_by_list_custom_field
190 190 get :index, :project_id => 1, :query_id => 9
191 191 assert_response :success
192 192 assert_template 'index.rhtml'
193 193 assert_not_nil assigns(:issues)
194 194 assert_not_nil assigns(:issue_count_by_group)
195 195 end
196 196
197 197 def test_index_sort_by_field_not_included_in_columns
198 198 Setting.issue_list_default_columns = %w(subject author)
199 199 get :index, :sort => 'tracker'
200 200 end
201 201
202 202 def test_index_csv_with_project
203 203 Setting.default_language = 'en'
204 204
205 205 get :index, :format => 'csv'
206 206 assert_response :success
207 207 assert_not_nil assigns(:issues)
208 208 assert_equal 'text/csv', @response.content_type
209 209 assert @response.body.starts_with?("#,")
210 210
211 211 get :index, :project_id => 1, :format => 'csv'
212 212 assert_response :success
213 213 assert_not_nil assigns(:issues)
214 214 assert_equal 'text/csv', @response.content_type
215 215 end
216 216
217 217 def test_index_pdf
218 218 get :index, :format => 'pdf'
219 219 assert_response :success
220 220 assert_not_nil assigns(:issues)
221 221 assert_equal 'application/pdf', @response.content_type
222 222
223 223 get :index, :project_id => 1, :format => 'pdf'
224 224 assert_response :success
225 225 assert_not_nil assigns(:issues)
226 226 assert_equal 'application/pdf', @response.content_type
227 227
228 228 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
229 229 assert_response :success
230 230 assert_not_nil assigns(:issues)
231 231 assert_equal 'application/pdf', @response.content_type
232 232 end
233 233
234 234 def test_index_pdf_with_query_grouped_by_list_custom_field
235 235 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
236 236 assert_response :success
237 237 assert_not_nil assigns(:issues)
238 238 assert_not_nil assigns(:issue_count_by_group)
239 239 assert_equal 'application/pdf', @response.content_type
240 240 end
241 241
242 242 def test_index_sort
243 243 get :index, :sort => 'tracker,id:desc'
244 244 assert_response :success
245 245
246 246 sort_params = @request.session['issues_index_sort']
247 247 assert sort_params.is_a?(String)
248 248 assert_equal 'tracker,id:desc', sort_params
249 249
250 250 issues = assigns(:issues)
251 251 assert_not_nil issues
252 252 assert !issues.empty?
253 253 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
254 254 end
255 255
256 256 def test_index_with_columns
257 257 columns = ['tracker', 'subject', 'assigned_to']
258 258 get :index, :set_filter => 1, :c => columns
259 259 assert_response :success
260 260
261 261 # query should use specified columns
262 262 query = assigns(:query)
263 263 assert_kind_of Query, query
264 264 assert_equal columns, query.column_names.map(&:to_s)
265 265
266 266 # columns should be stored in session
267 267 assert_kind_of Hash, session[:query]
268 268 assert_kind_of Array, session[:query][:column_names]
269 269 assert_equal columns, session[:query][:column_names].map(&:to_s)
270 270
271 271 # ensure only these columns are kept in the selected columns list
272 272 assert_tag :tag => 'select', :attributes => { :id => 'selected_columns' },
273 273 :children => { :count => 3 }
274 274 assert_no_tag :tag => 'option', :attributes => { :value => 'project' },
275 275 :parent => { :tag => 'select', :attributes => { :id => "selected_columns" } }
276 276 end
277 277
278 278 def test_index_with_custom_field_column
279 279 columns = %w(tracker subject cf_2)
280 280 get :index, :set_filter => 1, :c => columns
281 281 assert_response :success
282 282
283 283 # query should use specified columns
284 284 query = assigns(:query)
285 285 assert_kind_of Query, query
286 286 assert_equal columns, query.column_names.map(&:to_s)
287 287
288 288 assert_tag :td,
289 289 :attributes => {:class => 'cf_2 string'},
290 290 :ancestor => {:tag => 'table', :attributes => {:class => /issues/}}
291 291 end
292 292
293 293 def test_show_by_anonymous
294 294 get :show, :id => 1
295 295 assert_response :success
296 296 assert_template 'show.rhtml'
297 297 assert_not_nil assigns(:issue)
298 298 assert_equal Issue.find(1), assigns(:issue)
299 299
300 300 # anonymous role is allowed to add a note
301 301 assert_tag :tag => 'form',
302 302 :descendant => { :tag => 'fieldset',
303 303 :child => { :tag => 'legend',
304 304 :content => /Notes/ } }
305 305 end
306 306
307 307 def test_show_by_manager
308 308 @request.session[:user_id] = 2
309 309 get :show, :id => 1
310 310 assert_response :success
311 311
312 312 assert_tag :tag => 'a',
313 313 :content => /Quote/
314 314
315 315 assert_tag :tag => 'form',
316 316 :descendant => { :tag => 'fieldset',
317 317 :child => { :tag => 'legend',
318 318 :content => /Change properties/ } },
319 319 :descendant => { :tag => 'fieldset',
320 320 :child => { :tag => 'legend',
321 321 :content => /Log time/ } },
322 322 :descendant => { :tag => 'fieldset',
323 323 :child => { :tag => 'legend',
324 324 :content => /Notes/ } }
325 325 end
326 326
327 327 def test_show_should_deny_anonymous_access_without_permission
328 328 Role.anonymous.remove_permission!(:view_issues)
329 329 get :show, :id => 1
330 330 assert_response :redirect
331 331 end
332 332
333 333 def test_show_should_deny_anonymous_access_to_private_issue
334 334 Issue.update_all(["is_private = ?", true], "id = 1")
335 335 get :show, :id => 1
336 336 assert_response :redirect
337 337 end
338 338
339 339 def test_show_should_deny_non_member_access_without_permission
340 340 Role.non_member.remove_permission!(:view_issues)
341 341 @request.session[:user_id] = 9
342 342 get :show, :id => 1
343 343 assert_response 403
344 344 end
345 345
346 346 def test_show_should_deny_non_member_access_to_private_issue
347 347 Issue.update_all(["is_private = ?", true], "id = 1")
348 348 @request.session[:user_id] = 9
349 349 get :show, :id => 1
350 350 assert_response 403
351 351 end
352 352
353 353 def test_show_should_deny_member_access_without_permission
354 354 Role.find(1).remove_permission!(:view_issues)
355 355 @request.session[:user_id] = 2
356 356 get :show, :id => 1
357 357 assert_response 403
358 358 end
359 359
360 360 def test_show_should_deny_member_access_to_private_issue_without_permission
361 361 Issue.update_all(["is_private = ?", true], "id = 1")
362 362 @request.session[:user_id] = 3
363 363 get :show, :id => 1
364 364 assert_response 403
365 365 end
366 366
367 367 def test_show_should_allow_author_access_to_private_issue
368 368 Issue.update_all(["is_private = ?, author_id = 3", true], "id = 1")
369 369 @request.session[:user_id] = 3
370 370 get :show, :id => 1
371 371 assert_response :success
372 372 end
373 373
374 374 def test_show_should_allow_assignee_access_to_private_issue
375 375 Issue.update_all(["is_private = ?, assigned_to_id = 3", true], "id = 1")
376 376 @request.session[:user_id] = 3
377 377 get :show, :id => 1
378 378 assert_response :success
379 379 end
380 380
381 381 def test_show_should_allow_member_access_to_private_issue_with_permission
382 382 Issue.update_all(["is_private = ?", true], "id = 1")
383 383 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
384 384 @request.session[:user_id] = 3
385 385 get :show, :id => 1
386 386 assert_response :success
387 387 end
388 388
389 389 def test_show_should_not_disclose_relations_to_invisible_issues
390 390 Setting.cross_project_issue_relations = '1'
391 391 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
392 392 # Relation to a private project issue
393 393 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
394 394
395 395 get :show, :id => 1
396 396 assert_response :success
397 397
398 398 assert_tag :div, :attributes => { :id => 'relations' },
399 399 :descendant => { :tag => 'a', :content => /#2$/ }
400 400 assert_no_tag :div, :attributes => { :id => 'relations' },
401 401 :descendant => { :tag => 'a', :content => /#4$/ }
402 402 end
403 403
404 404 def test_show_atom
405 405 get :show, :id => 2, :format => 'atom'
406 406 assert_response :success
407 407 assert_template 'journals/index.rxml'
408 408 # Inline image
409 409 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
410 410 end
411 411
412 412 def test_show_export_to_pdf
413 413 get :show, :id => 3, :format => 'pdf'
414 414 assert_response :success
415 415 assert_equal 'application/pdf', @response.content_type
416 416 assert @response.body.starts_with?('%PDF')
417 417 assert_not_nil assigns(:issue)
418 418 end
419 419
420 420 def test_get_new
421 421 @request.session[:user_id] = 2
422 422 get :new, :project_id => 1, :tracker_id => 1
423 423 assert_response :success
424 424 assert_template 'new'
425 425
426 426 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
427 427 :value => 'Default string' }
428 428 end
429 429
430 430 def test_get_new_without_tracker_id
431 431 @request.session[:user_id] = 2
432 432 get :new, :project_id => 1
433 433 assert_response :success
434 434 assert_template 'new'
435 435
436 436 issue = assigns(:issue)
437 437 assert_not_nil issue
438 438 assert_equal Project.find(1).trackers.first, issue.tracker
439 439 end
440 440
441 441 def test_get_new_with_no_default_status_should_display_an_error
442 442 @request.session[:user_id] = 2
443 443 IssueStatus.delete_all
444 444
445 445 get :new, :project_id => 1
446 446 assert_response 500
447 447 assert_error_tag :content => /No default issue/
448 448 end
449 449
450 450 def test_get_new_with_no_tracker_should_display_an_error
451 451 @request.session[:user_id] = 2
452 452 Tracker.delete_all
453 453
454 454 get :new, :project_id => 1
455 455 assert_response 500
456 456 assert_error_tag :content => /No tracker/
457 457 end
458 458
459 459 def test_update_new_form
460 460 @request.session[:user_id] = 2
461 461 xhr :post, :new, :project_id => 1,
462 462 :issue => {:tracker_id => 2,
463 463 :subject => 'This is the test_new issue',
464 464 :description => 'This is the description',
465 465 :priority_id => 5}
466 466 assert_response :success
467 467 assert_template 'attributes'
468 468
469 469 issue = assigns(:issue)
470 470 assert_kind_of Issue, issue
471 471 assert_equal 1, issue.project_id
472 472 assert_equal 2, issue.tracker_id
473 473 assert_equal 'This is the test_new issue', issue.subject
474 474 end
475 475
476 476 def test_post_create
477 477 @request.session[:user_id] = 2
478 478 assert_difference 'Issue.count' do
479 479 post :create, :project_id => 1,
480 480 :issue => {:tracker_id => 3,
481 481 :status_id => 2,
482 482 :subject => 'This is the test_new issue',
483 483 :description => 'This is the description',
484 484 :priority_id => 5,
485 485 :start_date => '2010-11-07',
486 486 :estimated_hours => '',
487 487 :custom_field_values => {'2' => 'Value for field 2'}}
488 488 end
489 489 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
490 490
491 491 issue = Issue.find_by_subject('This is the test_new issue')
492 492 assert_not_nil issue
493 493 assert_equal 2, issue.author_id
494 494 assert_equal 3, issue.tracker_id
495 495 assert_equal 2, issue.status_id
496 496 assert_equal Date.parse('2010-11-07'), issue.start_date
497 497 assert_nil issue.estimated_hours
498 498 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
499 499 assert_not_nil v
500 500 assert_equal 'Value for field 2', v.value
501 501 end
502 502
503 503 def test_post_create_without_start_date
504 504 @request.session[:user_id] = 2
505 505 assert_difference 'Issue.count' do
506 506 post :create, :project_id => 1,
507 507 :issue => {:tracker_id => 3,
508 508 :status_id => 2,
509 509 :subject => 'This is the test_new issue',
510 510 :description => 'This is the description',
511 511 :priority_id => 5,
512 512 :start_date => '',
513 513 :estimated_hours => '',
514 514 :custom_field_values => {'2' => 'Value for field 2'}}
515 515 end
516 516 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
517 517
518 518 issue = Issue.find_by_subject('This is the test_new issue')
519 519 assert_not_nil issue
520 520 assert_nil issue.start_date
521 521 end
522 522
523 523 def test_post_create_and_continue
524 524 @request.session[:user_id] = 2
525 525 post :create, :project_id => 1,
526 526 :issue => {:tracker_id => 3,
527 527 :subject => 'This is first issue',
528 528 :priority_id => 5},
529 529 :continue => ''
530 530 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook',
531 531 :issue => {:tracker_id => 3}
532 532 end
533 533
534 534 def test_post_create_without_custom_fields_param
535 535 @request.session[:user_id] = 2
536 536 assert_difference 'Issue.count' do
537 537 post :create, :project_id => 1,
538 538 :issue => {:tracker_id => 1,
539 539 :subject => 'This is the test_new issue',
540 540 :description => 'This is the description',
541 541 :priority_id => 5}
542 542 end
543 543 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
544 544 end
545 545
546 546 def test_post_create_with_required_custom_field_and_without_custom_fields_param
547 547 field = IssueCustomField.find_by_name('Database')
548 548 field.update_attribute(:is_required, true)
549 549
550 550 @request.session[:user_id] = 2
551 551 post :create, :project_id => 1,
552 552 :issue => {:tracker_id => 1,
553 553 :subject => 'This is the test_new issue',
554 554 :description => 'This is the description',
555 555 :priority_id => 5}
556 556 assert_response :success
557 557 assert_template 'new'
558 558 issue = assigns(:issue)
559 559 assert_not_nil issue
560 560 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
561 561 end
562 562
563 563 def test_post_create_with_watchers
564 564 @request.session[:user_id] = 2
565 565 ActionMailer::Base.deliveries.clear
566 566
567 567 assert_difference 'Watcher.count', 2 do
568 568 post :create, :project_id => 1,
569 569 :issue => {:tracker_id => 1,
570 570 :subject => 'This is a new issue with watchers',
571 571 :description => 'This is the description',
572 572 :priority_id => 5,
573 573 :watcher_user_ids => ['2', '3']}
574 574 end
575 575 issue = Issue.find_by_subject('This is a new issue with watchers')
576 576 assert_not_nil issue
577 577 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
578 578
579 579 # Watchers added
580 580 assert_equal [2, 3], issue.watcher_user_ids.sort
581 581 assert issue.watched_by?(User.find(3))
582 582 # Watchers notified
583 583 mail = ActionMailer::Base.deliveries.last
584 584 assert_kind_of TMail::Mail, mail
585 585 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
586 586 end
587 587
588 588 def test_post_create_subissue
589 589 @request.session[:user_id] = 2
590 590
591 591 assert_difference 'Issue.count' do
592 592 post :create, :project_id => 1,
593 593 :issue => {:tracker_id => 1,
594 594 :subject => 'This is a child issue',
595 595 :parent_issue_id => 2}
596 596 end
597 597 issue = Issue.find_by_subject('This is a child issue')
598 598 assert_not_nil issue
599 599 assert_equal Issue.find(2), issue.parent
600 600 end
601 601
602 602 def test_post_create_subissue_with_non_numeric_parent_id
603 603 @request.session[:user_id] = 2
604 604
605 605 assert_difference 'Issue.count' do
606 606 post :create, :project_id => 1,
607 607 :issue => {:tracker_id => 1,
608 608 :subject => 'This is a child issue',
609 609 :parent_issue_id => 'ABC'}
610 610 end
611 611 issue = Issue.find_by_subject('This is a child issue')
612 612 assert_not_nil issue
613 613 assert_nil issue.parent
614 614 end
615
616 def test_post_create_private
617 @request.session[:user_id] = 2
618
619 assert_difference 'Issue.count' do
620 post :create, :project_id => 1,
621 :issue => {:tracker_id => 1,
622 :subject => 'This is a private issue',
623 :is_private => '1'}
624 end
625 issue = Issue.first(:order => 'id DESC')
626 assert issue.is_private?
627 end
628
629 def test_post_create_private_with_set_own_issues_private_permission
630 role = Role.find(1)
631 role.remove_permission! :set_issues_private
632 role.add_permission! :set_own_issues_private
633
634 @request.session[:user_id] = 2
635
636 assert_difference 'Issue.count' do
637 post :create, :project_id => 1,
638 :issue => {:tracker_id => 1,
639 :subject => 'This is a private issue',
640 :is_private => '1'}
641 end
642 issue = Issue.first(:order => 'id DESC')
643 assert issue.is_private?
644 end
615 645
616 646 def test_post_create_should_send_a_notification
617 647 ActionMailer::Base.deliveries.clear
618 648 @request.session[:user_id] = 2
619 649 assert_difference 'Issue.count' do
620 650 post :create, :project_id => 1,
621 651 :issue => {:tracker_id => 3,
622 652 :subject => 'This is the test_new issue',
623 653 :description => 'This is the description',
624 654 :priority_id => 5,
625 655 :estimated_hours => '',
626 656 :custom_field_values => {'2' => 'Value for field 2'}}
627 657 end
628 658 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
629 659
630 660 assert_equal 1, ActionMailer::Base.deliveries.size
631 661 end
632 662
633 663 def test_post_create_should_preserve_fields_values_on_validation_failure
634 664 @request.session[:user_id] = 2
635 665 post :create, :project_id => 1,
636 666 :issue => {:tracker_id => 1,
637 667 # empty subject
638 668 :subject => '',
639 669 :description => 'This is a description',
640 670 :priority_id => 6,
641 671 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
642 672 assert_response :success
643 673 assert_template 'new'
644 674
645 675 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
646 676 :content => 'This is a description'
647 677 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
648 678 :child => { :tag => 'option', :attributes => { :selected => 'selected',
649 679 :value => '6' },
650 680 :content => 'High' }
651 681 # Custom fields
652 682 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
653 683 :child => { :tag => 'option', :attributes => { :selected => 'selected',
654 684 :value => 'Oracle' },
655 685 :content => 'Oracle' }
656 686 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
657 687 :value => 'Value for field 2'}
658 688 end
659 689
660 690 def test_post_create_should_ignore_non_safe_attributes
661 691 @request.session[:user_id] = 2
662 692 assert_nothing_raised do
663 693 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
664 694 end
665 695 end
666 696
667 697 context "without workflow privilege" do
668 698 setup do
669 699 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
670 700 Role.anonymous.add_permission! :add_issues, :add_issue_notes
671 701 end
672 702
673 703 context "#new" do
674 704 should "propose default status only" do
675 705 get :new, :project_id => 1
676 706 assert_response :success
677 707 assert_template 'new'
678 708 assert_tag :tag => 'select',
679 709 :attributes => {:name => 'issue[status_id]'},
680 710 :children => {:count => 1},
681 711 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
682 712 end
683 713
684 714 should "accept default status" do
685 715 assert_difference 'Issue.count' do
686 716 post :create, :project_id => 1,
687 717 :issue => {:tracker_id => 1,
688 718 :subject => 'This is an issue',
689 719 :status_id => 1}
690 720 end
691 721 issue = Issue.last(:order => 'id')
692 722 assert_equal IssueStatus.default, issue.status
693 723 end
694 724
695 725 should "ignore unauthorized status" do
696 726 assert_difference 'Issue.count' do
697 727 post :create, :project_id => 1,
698 728 :issue => {:tracker_id => 1,
699 729 :subject => 'This is an issue',
700 730 :status_id => 3}
701 731 end
702 732 issue = Issue.last(:order => 'id')
703 733 assert_equal IssueStatus.default, issue.status
704 734 end
705 735 end
706 736
707 737 context "#update" do
708 738 should "ignore status change" do
709 739 assert_difference 'Journal.count' do
710 740 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
711 741 end
712 742 assert_equal 1, Issue.find(1).status_id
713 743 end
714 744
715 745 should "ignore attributes changes" do
716 746 assert_difference 'Journal.count' do
717 747 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
718 748 end
719 749 issue = Issue.find(1)
720 750 assert_equal "Can't print recipes", issue.subject
721 751 assert_nil issue.assigned_to
722 752 end
723 753 end
724 754 end
725 755
726 756 context "with workflow privilege" do
727 757 setup do
728 758 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
729 759 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
730 760 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
731 761 Role.anonymous.add_permission! :add_issues, :add_issue_notes
732 762 end
733 763
734 764 context "#update" do
735 765 should "accept authorized status" do
736 766 assert_difference 'Journal.count' do
737 767 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
738 768 end
739 769 assert_equal 3, Issue.find(1).status_id
740 770 end
741 771
742 772 should "ignore unauthorized status" do
743 773 assert_difference 'Journal.count' do
744 774 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
745 775 end
746 776 assert_equal 1, Issue.find(1).status_id
747 777 end
748 778
749 779 should "accept authorized attributes changes" do
750 780 assert_difference 'Journal.count' do
751 781 put :update, :id => 1, :notes => 'just trying', :issue => {:assigned_to_id => 2}
752 782 end
753 783 issue = Issue.find(1)
754 784 assert_equal 2, issue.assigned_to_id
755 785 end
756 786
757 787 should "ignore unauthorized attributes changes" do
758 788 assert_difference 'Journal.count' do
759 789 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed'}
760 790 end
761 791 issue = Issue.find(1)
762 792 assert_equal "Can't print recipes", issue.subject
763 793 end
764 794 end
765 795
766 796 context "and :edit_issues permission" do
767 797 setup do
768 798 Role.anonymous.add_permission! :add_issues, :edit_issues
769 799 end
770 800
771 801 should "accept authorized status" do
772 802 assert_difference 'Journal.count' do
773 803 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
774 804 end
775 805 assert_equal 3, Issue.find(1).status_id
776 806 end
777 807
778 808 should "ignore unauthorized status" do
779 809 assert_difference 'Journal.count' do
780 810 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
781 811 end
782 812 assert_equal 1, Issue.find(1).status_id
783 813 end
784 814
785 815 should "accept authorized attributes changes" do
786 816 assert_difference 'Journal.count' do
787 817 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
788 818 end
789 819 issue = Issue.find(1)
790 820 assert_equal "changed", issue.subject
791 821 assert_equal 2, issue.assigned_to_id
792 822 end
793 823 end
794 824 end
795 825
796 826 def test_copy_issue
797 827 @request.session[:user_id] = 2
798 828 get :new, :project_id => 1, :copy_from => 1
799 829 assert_template 'new'
800 830 assert_not_nil assigns(:issue)
801 831 orig = Issue.find(1)
802 832 assert_equal orig.subject, assigns(:issue).subject
803 833 end
804 834
805 835 def test_get_edit
806 836 @request.session[:user_id] = 2
807 837 get :edit, :id => 1
808 838 assert_response :success
809 839 assert_template 'edit'
810 840 assert_not_nil assigns(:issue)
811 841 assert_equal Issue.find(1), assigns(:issue)
812 842 end
813 843
814 844 def test_get_edit_with_params
815 845 @request.session[:user_id] = 2
816 846 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
817 847 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
818 848 assert_response :success
819 849 assert_template 'edit'
820 850
821 851 issue = assigns(:issue)
822 852 assert_not_nil issue
823 853
824 854 assert_equal 5, issue.status_id
825 855 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
826 856 :child => { :tag => 'option',
827 857 :content => 'Closed',
828 858 :attributes => { :selected => 'selected' } }
829 859
830 860 assert_equal 7, issue.priority_id
831 861 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
832 862 :child => { :tag => 'option',
833 863 :content => 'Urgent',
834 864 :attributes => { :selected => 'selected' } }
835 865
836 866 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
837 867 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
838 868 :child => { :tag => 'option',
839 869 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
840 870 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
841 871 end
842 872
843 873 def test_update_edit_form
844 874 @request.session[:user_id] = 2
845 875 xhr :post, :new, :project_id => 1,
846 876 :id => 1,
847 877 :issue => {:tracker_id => 2,
848 878 :subject => 'This is the test_new issue',
849 879 :description => 'This is the description',
850 880 :priority_id => 5}
851 881 assert_response :success
852 882 assert_template 'attributes'
853 883
854 884 issue = assigns(:issue)
855 885 assert_kind_of Issue, issue
856 886 assert_equal 1, issue.id
857 887 assert_equal 1, issue.project_id
858 888 assert_equal 2, issue.tracker_id
859 889 assert_equal 'This is the test_new issue', issue.subject
860 890 end
861 891
862 892 def test_update_using_invalid_http_verbs
863 893 @request.session[:user_id] = 2
864 894 subject = 'Updated by an invalid http verb'
865 895
866 896 get :update, :id => 1, :issue => {:subject => subject}
867 897 assert_not_equal subject, Issue.find(1).subject
868 898
869 899 post :update, :id => 1, :issue => {:subject => subject}
870 900 assert_not_equal subject, Issue.find(1).subject
871 901
872 902 delete :update, :id => 1, :issue => {:subject => subject}
873 903 assert_not_equal subject, Issue.find(1).subject
874 904 end
875 905
876 906 def test_put_update_without_custom_fields_param
877 907 @request.session[:user_id] = 2
878 908 ActionMailer::Base.deliveries.clear
879 909
880 910 issue = Issue.find(1)
881 911 assert_equal '125', issue.custom_value_for(2).value
882 912 old_subject = issue.subject
883 913 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
884 914
885 915 assert_difference('Journal.count') do
886 916 assert_difference('JournalDetail.count', 2) do
887 917 put :update, :id => 1, :issue => {:subject => new_subject,
888 918 :priority_id => '6',
889 919 :category_id => '1' # no change
890 920 }
891 921 end
892 922 end
893 923 assert_redirected_to :action => 'show', :id => '1'
894 924 issue.reload
895 925 assert_equal new_subject, issue.subject
896 926 # Make sure custom fields were not cleared
897 927 assert_equal '125', issue.custom_value_for(2).value
898 928
899 929 mail = ActionMailer::Base.deliveries.last
900 930 assert_kind_of TMail::Mail, mail
901 931 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
902 932 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
903 933 end
904 934
905 935 def test_put_update_with_custom_field_change
906 936 @request.session[:user_id] = 2
907 937 issue = Issue.find(1)
908 938 assert_equal '125', issue.custom_value_for(2).value
909 939
910 940 assert_difference('Journal.count') do
911 941 assert_difference('JournalDetail.count', 3) do
912 942 put :update, :id => 1, :issue => {:subject => 'Custom field change',
913 943 :priority_id => '6',
914 944 :category_id => '1', # no change
915 945 :custom_field_values => { '2' => 'New custom value' }
916 946 }
917 947 end
918 948 end
919 949 assert_redirected_to :action => 'show', :id => '1'
920 950 issue.reload
921 951 assert_equal 'New custom value', issue.custom_value_for(2).value
922 952
923 953 mail = ActionMailer::Base.deliveries.last
924 954 assert_kind_of TMail::Mail, mail
925 955 assert mail.body.include?("Searchable field changed from 125 to New custom value")
926 956 end
927 957
928 958 def test_put_update_with_status_and_assignee_change
929 959 issue = Issue.find(1)
930 960 assert_equal 1, issue.status_id
931 961 @request.session[:user_id] = 2
932 962 assert_difference('TimeEntry.count', 0) do
933 963 put :update,
934 964 :id => 1,
935 965 :issue => { :status_id => 2, :assigned_to_id => 3 },
936 966 :notes => 'Assigned to dlopper',
937 967 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
938 968 end
939 969 assert_redirected_to :action => 'show', :id => '1'
940 970 issue.reload
941 971 assert_equal 2, issue.status_id
942 972 j = Journal.find(:first, :order => 'id DESC')
943 973 assert_equal 'Assigned to dlopper', j.notes
944 974 assert_equal 2, j.details.size
945 975
946 976 mail = ActionMailer::Base.deliveries.last
947 977 assert mail.body.include?("Status changed from New to Assigned")
948 978 # subject should contain the new status
949 979 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
950 980 end
951 981
952 982 def test_put_update_with_note_only
953 983 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
954 984 # anonymous user
955 985 put :update,
956 986 :id => 1,
957 987 :notes => notes
958 988 assert_redirected_to :action => 'show', :id => '1'
959 989 j = Journal.find(:first, :order => 'id DESC')
960 990 assert_equal notes, j.notes
961 991 assert_equal 0, j.details.size
962 992 assert_equal User.anonymous, j.user
963 993
964 994 mail = ActionMailer::Base.deliveries.last
965 995 assert mail.body.include?(notes)
966 996 end
967 997
968 998 def test_put_update_with_note_and_spent_time
969 999 @request.session[:user_id] = 2
970 1000 spent_hours_before = Issue.find(1).spent_hours
971 1001 assert_difference('TimeEntry.count') do
972 1002 put :update,
973 1003 :id => 1,
974 1004 :notes => '2.5 hours added',
975 1005 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
976 1006 end
977 1007 assert_redirected_to :action => 'show', :id => '1'
978 1008
979 1009 issue = Issue.find(1)
980 1010
981 1011 j = Journal.find(:first, :order => 'id DESC')
982 1012 assert_equal '2.5 hours added', j.notes
983 1013 assert_equal 0, j.details.size
984 1014
985 1015 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
986 1016 assert_not_nil t
987 1017 assert_equal 2.5, t.hours
988 1018 assert_equal spent_hours_before + 2.5, issue.spent_hours
989 1019 end
990 1020
991 1021 def test_put_update_with_attachment_only
992 1022 set_tmp_attachments_directory
993 1023
994 1024 # Delete all fixtured journals, a race condition can occur causing the wrong
995 1025 # journal to get fetched in the next find.
996 1026 Journal.delete_all
997 1027
998 1028 # anonymous user
999 1029 put :update,
1000 1030 :id => 1,
1001 1031 :notes => '',
1002 1032 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
1003 1033 assert_redirected_to :action => 'show', :id => '1'
1004 1034 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
1005 1035 assert j.notes.blank?
1006 1036 assert_equal 1, j.details.size
1007 1037 assert_equal 'testfile.txt', j.details.first.value
1008 1038 assert_equal User.anonymous, j.user
1009 1039
1010 1040 mail = ActionMailer::Base.deliveries.last
1011 1041 assert mail.body.include?('testfile.txt')
1012 1042 end
1013 1043
1014 1044 def test_put_update_with_attachment_that_fails_to_save
1015 1045 set_tmp_attachments_directory
1016 1046
1017 1047 # Delete all fixtured journals, a race condition can occur causing the wrong
1018 1048 # journal to get fetched in the next find.
1019 1049 Journal.delete_all
1020 1050
1021 1051 # Mock out the unsaved attachment
1022 1052 Attachment.any_instance.stubs(:create).returns(Attachment.new)
1023 1053
1024 1054 # anonymous user
1025 1055 put :update,
1026 1056 :id => 1,
1027 1057 :notes => '',
1028 1058 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
1029 1059 assert_redirected_to :action => 'show', :id => '1'
1030 1060 assert_equal '1 file(s) could not be saved.', flash[:warning]
1031 1061
1032 1062 end if Object.const_defined?(:Mocha)
1033 1063
1034 1064 def test_put_update_with_no_change
1035 1065 issue = Issue.find(1)
1036 1066 issue.journals.clear
1037 1067 ActionMailer::Base.deliveries.clear
1038 1068
1039 1069 put :update,
1040 1070 :id => 1,
1041 1071 :notes => ''
1042 1072 assert_redirected_to :action => 'show', :id => '1'
1043 1073
1044 1074 issue.reload
1045 1075 assert issue.journals.empty?
1046 1076 # No email should be sent
1047 1077 assert ActionMailer::Base.deliveries.empty?
1048 1078 end
1049 1079
1050 1080 def test_put_update_should_send_a_notification
1051 1081 @request.session[:user_id] = 2
1052 1082 ActionMailer::Base.deliveries.clear
1053 1083 issue = Issue.find(1)
1054 1084 old_subject = issue.subject
1055 1085 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
1056 1086
1057 1087 put :update, :id => 1, :issue => {:subject => new_subject,
1058 1088 :priority_id => '6',
1059 1089 :category_id => '1' # no change
1060 1090 }
1061 1091 assert_equal 1, ActionMailer::Base.deliveries.size
1062 1092 end
1063 1093
1064 1094 def test_put_update_with_invalid_spent_time_hours_only
1065 1095 @request.session[:user_id] = 2
1066 1096 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
1067 1097
1068 1098 assert_no_difference('Journal.count') do
1069 1099 put :update,
1070 1100 :id => 1,
1071 1101 :notes => notes,
1072 1102 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
1073 1103 end
1074 1104 assert_response :success
1075 1105 assert_template 'edit'
1076 1106
1077 1107 assert_error_tag :descendant => {:content => /Activity can't be blank/}
1078 1108 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
1079 1109 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
1080 1110 end
1081 1111
1082 1112 def test_put_update_with_invalid_spent_time_comments_only
1083 1113 @request.session[:user_id] = 2
1084 1114 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
1085 1115
1086 1116 assert_no_difference('Journal.count') do
1087 1117 put :update,
1088 1118 :id => 1,
1089 1119 :notes => notes,
1090 1120 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
1091 1121 end
1092 1122 assert_response :success
1093 1123 assert_template 'edit'
1094 1124
1095 1125 assert_error_tag :descendant => {:content => /Activity can't be blank/}
1096 1126 assert_error_tag :descendant => {:content => /Hours can't be blank/}
1097 1127 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
1098 1128 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
1099 1129 end
1100 1130
1101 1131 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
1102 1132 issue = Issue.find(2)
1103 1133 @request.session[:user_id] = 2
1104 1134
1105 1135 put :update,
1106 1136 :id => issue.id,
1107 1137 :issue => {
1108 1138 :fixed_version_id => 4
1109 1139 }
1110 1140
1111 1141 assert_response :redirect
1112 1142 issue.reload
1113 1143 assert_equal 4, issue.fixed_version_id
1114 1144 assert_not_equal issue.project_id, issue.fixed_version.project_id
1115 1145 end
1116 1146
1117 1147 def test_put_update_should_redirect_back_using_the_back_url_parameter
1118 1148 issue = Issue.find(2)
1119 1149 @request.session[:user_id] = 2
1120 1150
1121 1151 put :update,
1122 1152 :id => issue.id,
1123 1153 :issue => {
1124 1154 :fixed_version_id => 4
1125 1155 },
1126 1156 :back_url => '/issues'
1127 1157
1128 1158 assert_response :redirect
1129 1159 assert_redirected_to '/issues'
1130 1160 end
1131 1161
1132 1162 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1133 1163 issue = Issue.find(2)
1134 1164 @request.session[:user_id] = 2
1135 1165
1136 1166 put :update,
1137 1167 :id => issue.id,
1138 1168 :issue => {
1139 1169 :fixed_version_id => 4
1140 1170 },
1141 1171 :back_url => 'http://google.com'
1142 1172
1143 1173 assert_response :redirect
1144 1174 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
1145 1175 end
1146 1176
1147 1177 def test_get_bulk_edit
1148 1178 @request.session[:user_id] = 2
1149 1179 get :bulk_edit, :ids => [1, 2]
1150 1180 assert_response :success
1151 1181 assert_template 'bulk_edit'
1152 1182
1153 1183 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
1154 1184
1155 1185 # Project specific custom field, date type
1156 1186 field = CustomField.find(9)
1157 1187 assert !field.is_for_all?
1158 1188 assert_equal 'date', field.field_format
1159 1189 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
1160 1190
1161 1191 # System wide custom field
1162 1192 assert CustomField.find(1).is_for_all?
1163 1193 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
1164 1194 end
1165 1195
1166 1196 def test_get_bulk_edit_on_different_projects
1167 1197 @request.session[:user_id] = 2
1168 1198 get :bulk_edit, :ids => [1, 2, 6]
1169 1199 assert_response :success
1170 1200 assert_template 'bulk_edit'
1171 1201
1172 1202 # Can not set issues from different projects as children of an issue
1173 1203 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
1174 1204
1175 1205 # Project specific custom field, date type
1176 1206 field = CustomField.find(9)
1177 1207 assert !field.is_for_all?
1178 1208 assert !field.project_ids.include?(Issue.find(6).project_id)
1179 1209 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
1180 1210 end
1181 1211
1182 1212 def test_get_bulk_edit_with_user_custom_field
1183 1213 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
1184 1214
1185 1215 @request.session[:user_id] = 2
1186 1216 get :bulk_edit, :ids => [1, 2]
1187 1217 assert_response :success
1188 1218 assert_template 'bulk_edit'
1189 1219
1190 1220 assert_tag :select,
1191 1221 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
1192 1222 :children => {
1193 1223 :only => {:tag => 'option'},
1194 1224 :count => Project.find(1).users.count + 1
1195 1225 }
1196 1226 end
1197 1227
1198 1228 def test_get_bulk_edit_with_version_custom_field
1199 1229 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
1200 1230
1201 1231 @request.session[:user_id] = 2
1202 1232 get :bulk_edit, :ids => [1, 2]
1203 1233 assert_response :success
1204 1234 assert_template 'bulk_edit'
1205 1235
1206 1236 assert_tag :select,
1207 1237 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
1208 1238 :children => {
1209 1239 :only => {:tag => 'option'},
1210 1240 :count => Project.find(1).versions.count + 1
1211 1241 }
1212 1242 end
1213 1243
1214 1244 def test_bulk_update
1215 1245 @request.session[:user_id] = 2
1216 1246 # update issues priority
1217 1247 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
1218 1248 :issue => {:priority_id => 7,
1219 1249 :assigned_to_id => '',
1220 1250 :custom_field_values => {'2' => ''}}
1221 1251
1222 1252 assert_response 302
1223 1253 # check that the issues were updated
1224 1254 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
1225 1255
1226 1256 issue = Issue.find(1)
1227 1257 journal = issue.journals.find(:first, :order => 'created_on DESC')
1228 1258 assert_equal '125', issue.custom_value_for(2).value
1229 1259 assert_equal 'Bulk editing', journal.notes
1230 1260 assert_equal 1, journal.details.size
1231 1261 end
1232 1262
1233 1263 def test_bulk_update_on_different_projects
1234 1264 @request.session[:user_id] = 2
1235 1265 # update issues priority
1236 1266 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
1237 1267 :issue => {:priority_id => 7,
1238 1268 :assigned_to_id => '',
1239 1269 :custom_field_values => {'2' => ''}}
1240 1270
1241 1271 assert_response 302
1242 1272 # check that the issues were updated
1243 1273 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
1244 1274
1245 1275 issue = Issue.find(1)
1246 1276 journal = issue.journals.find(:first, :order => 'created_on DESC')
1247 1277 assert_equal '125', issue.custom_value_for(2).value
1248 1278 assert_equal 'Bulk editing', journal.notes
1249 1279 assert_equal 1, journal.details.size
1250 1280 end
1251 1281
1252 1282 def test_bulk_update_on_different_projects_without_rights
1253 1283 @request.session[:user_id] = 3
1254 1284 user = User.find(3)
1255 1285 action = { :controller => "issues", :action => "bulk_update" }
1256 1286 assert user.allowed_to?(action, Issue.find(1).project)
1257 1287 assert ! user.allowed_to?(action, Issue.find(6).project)
1258 1288 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
1259 1289 :issue => {:priority_id => 7,
1260 1290 :assigned_to_id => '',
1261 1291 :custom_field_values => {'2' => ''}}
1262 1292 assert_response 403
1263 1293 assert_not_equal "Bulk should fail", Journal.last.notes
1264 1294 end
1265 1295
1266 1296 def test_bullk_update_should_send_a_notification
1267 1297 @request.session[:user_id] = 2
1268 1298 ActionMailer::Base.deliveries.clear
1269 1299 post(:bulk_update,
1270 1300 {
1271 1301 :ids => [1, 2],
1272 1302 :notes => 'Bulk editing',
1273 1303 :issue => {
1274 1304 :priority_id => 7,
1275 1305 :assigned_to_id => '',
1276 1306 :custom_field_values => {'2' => ''}
1277 1307 }
1278 1308 })
1279 1309
1280 1310 assert_response 302
1281 1311 assert_equal 2, ActionMailer::Base.deliveries.size
1282 1312 end
1283 1313
1284 1314 def test_bulk_update_status
1285 1315 @request.session[:user_id] = 2
1286 1316 # update issues priority
1287 1317 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
1288 1318 :issue => {:priority_id => '',
1289 1319 :assigned_to_id => '',
1290 1320 :status_id => '5'}
1291 1321
1292 1322 assert_response 302
1293 1323 issue = Issue.find(1)
1294 1324 assert issue.closed?
1295 1325 end
1296 1326
1297 1327 def test_bulk_update_parent_id
1298 1328 @request.session[:user_id] = 2
1299 1329 post :bulk_update, :ids => [1, 3],
1300 1330 :notes => 'Bulk editing parent',
1301 1331 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
1302 1332
1303 1333 assert_response 302
1304 1334 parent = Issue.find(2)
1305 1335 assert_equal parent.id, Issue.find(1).parent_id
1306 1336 assert_equal parent.id, Issue.find(3).parent_id
1307 1337 assert_equal [1, 3], parent.children.collect(&:id).sort
1308 1338 end
1309 1339
1310 1340 def test_bulk_update_custom_field
1311 1341 @request.session[:user_id] = 2
1312 1342 # update issues priority
1313 1343 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
1314 1344 :issue => {:priority_id => '',
1315 1345 :assigned_to_id => '',
1316 1346 :custom_field_values => {'2' => '777'}}
1317 1347
1318 1348 assert_response 302
1319 1349
1320 1350 issue = Issue.find(1)
1321 1351 journal = issue.journals.find(:first, :order => 'created_on DESC')
1322 1352 assert_equal '777', issue.custom_value_for(2).value
1323 1353 assert_equal 1, journal.details.size
1324 1354 assert_equal '125', journal.details.first.old_value
1325 1355 assert_equal '777', journal.details.first.value
1326 1356 end
1327 1357
1328 1358 def test_bulk_update_unassign
1329 1359 assert_not_nil Issue.find(2).assigned_to
1330 1360 @request.session[:user_id] = 2
1331 1361 # unassign issues
1332 1362 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
1333 1363 assert_response 302
1334 1364 # check that the issues were updated
1335 1365 assert_nil Issue.find(2).assigned_to
1336 1366 end
1337 1367
1338 1368 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
1339 1369 @request.session[:user_id] = 2
1340 1370
1341 1371 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
1342 1372
1343 1373 assert_response :redirect
1344 1374 issues = Issue.find([1,2])
1345 1375 issues.each do |issue|
1346 1376 assert_equal 4, issue.fixed_version_id
1347 1377 assert_not_equal issue.project_id, issue.fixed_version.project_id
1348 1378 end
1349 1379 end
1350 1380
1351 1381 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
1352 1382 @request.session[:user_id] = 2
1353 1383 post :bulk_update, :ids => [1,2], :back_url => '/issues'
1354 1384
1355 1385 assert_response :redirect
1356 1386 assert_redirected_to '/issues'
1357 1387 end
1358 1388
1359 1389 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1360 1390 @request.session[:user_id] = 2
1361 1391 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
1362 1392
1363 1393 assert_response :redirect
1364 1394 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
1365 1395 end
1366 1396
1367 1397 def test_destroy_issue_with_no_time_entries
1368 1398 assert_nil TimeEntry.find_by_issue_id(2)
1369 1399 @request.session[:user_id] = 2
1370 1400 post :destroy, :id => 2
1371 1401 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1372 1402 assert_nil Issue.find_by_id(2)
1373 1403 end
1374 1404
1375 1405 def test_destroy_issues_with_time_entries
1376 1406 @request.session[:user_id] = 2
1377 1407 post :destroy, :ids => [1, 3]
1378 1408 assert_response :success
1379 1409 assert_template 'destroy'
1380 1410 assert_not_nil assigns(:hours)
1381 1411 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1382 1412 end
1383 1413
1384 1414 def test_destroy_issues_and_destroy_time_entries
1385 1415 @request.session[:user_id] = 2
1386 1416 post :destroy, :ids => [1, 3], :todo => 'destroy'
1387 1417 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1388 1418 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1389 1419 assert_nil TimeEntry.find_by_id([1, 2])
1390 1420 end
1391 1421
1392 1422 def test_destroy_issues_and_assign_time_entries_to_project
1393 1423 @request.session[:user_id] = 2
1394 1424 post :destroy, :ids => [1, 3], :todo => 'nullify'
1395 1425 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1396 1426 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1397 1427 assert_nil TimeEntry.find(1).issue_id
1398 1428 assert_nil TimeEntry.find(2).issue_id
1399 1429 end
1400 1430
1401 1431 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1402 1432 @request.session[:user_id] = 2
1403 1433 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1404 1434 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1405 1435 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1406 1436 assert_equal 2, TimeEntry.find(1).issue_id
1407 1437 assert_equal 2, TimeEntry.find(2).issue_id
1408 1438 end
1409 1439
1410 1440 def test_destroy_issues_from_different_projects
1411 1441 @request.session[:user_id] = 2
1412 1442 post :destroy, :ids => [1, 2, 6], :todo => 'destroy'
1413 1443 assert_redirected_to :controller => 'issues', :action => 'index'
1414 1444 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
1415 1445 end
1416 1446
1417 1447 def test_destroy_parent_and_child_issues
1418 1448 parent = Issue.generate!(:project_id => 1, :tracker_id => 1)
1419 1449 child = Issue.generate!(:project_id => 1, :tracker_id => 1, :parent_issue_id => parent.id)
1420 1450 assert child.is_descendant_of?(parent.reload)
1421 1451
1422 1452 @request.session[:user_id] = 2
1423 1453 assert_difference 'Issue.count', -2 do
1424 1454 post :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
1425 1455 end
1426 1456 assert_response 302
1427 1457 end
1428 1458
1429 1459 def test_default_search_scope
1430 1460 get :index
1431 1461 assert_tag :div, :attributes => {:id => 'quick-search'},
1432 1462 :child => {:tag => 'form',
1433 1463 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1434 1464 end
1435 1465 end
General Comments 0
You need to be logged in to leave comments. Login now