##// END OF EJS Templates
Removed the "New issue" menu item (#6204)....
Jean-Philippe Lang -
r14962:dcc569fa34f7
parent child
Show More
@@ -1,539 +1,538
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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 menu_item :new_issue, :only => [:new, :create]
20 19 default_search_scope :issues
21 20
22 21 before_filter :find_issue, :only => [:show, :edit, :update]
23 22 before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
24 23 before_filter :authorize, :except => [:index, :new, :create]
25 24 before_filter :find_optional_project, :only => [:index, :new, :create]
26 25 before_filter :build_new_issue_from_params, :only => [:new, :create]
27 26 accept_rss_auth :index, :show
28 27 accept_api_auth :index, :show, :create, :update, :destroy
29 28
30 29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
31 30
32 31 helper :journals
33 32 helper :projects
34 33 helper :custom_fields
35 34 helper :issue_relations
36 35 helper :watchers
37 36 helper :attachments
38 37 helper :queries
39 38 include QueriesHelper
40 39 helper :repositories
41 40 helper :sort
42 41 include SortHelper
43 42 helper :timelog
44 43
45 44 def index
46 45 retrieve_query
47 46 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
48 47 sort_update(@query.sortable_columns)
49 48 @query.sort_criteria = sort_criteria.to_a
50 49
51 50 if @query.valid?
52 51 case params[:format]
53 52 when 'csv', 'pdf'
54 53 @limit = Setting.issues_export_limit.to_i
55 54 if params[:columns] == 'all'
56 55 @query.column_names = @query.available_inline_columns.map(&:name)
57 56 end
58 57 when 'atom'
59 58 @limit = Setting.feeds_limit.to_i
60 59 when 'xml', 'json'
61 60 @offset, @limit = api_offset_and_limit
62 61 @query.column_names = %w(author)
63 62 else
64 63 @limit = per_page_option
65 64 end
66 65
67 66 @issue_count = @query.issue_count
68 67 @issue_pages = Paginator.new @issue_count, @limit, params['page']
69 68 @offset ||= @issue_pages.offset
70 69 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
71 70 :order => sort_clause,
72 71 :offset => @offset,
73 72 :limit => @limit)
74 73 @issue_count_by_group = @query.issue_count_by_group
75 74
76 75 respond_to do |format|
77 76 format.html { render :template => 'issues/index', :layout => !request.xhr? }
78 77 format.api {
79 78 Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
80 79 }
81 80 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
82 81 format.csv { send_data(query_to_csv(@issues, @query, params[:csv]), :type => 'text/csv; header=present', :filename => 'issues.csv') }
83 82 format.pdf { send_file_headers! :type => 'application/pdf', :filename => 'issues.pdf' }
84 83 end
85 84 else
86 85 respond_to do |format|
87 86 format.html { render(:template => 'issues/index', :layout => !request.xhr?) }
88 87 format.any(:atom, :csv, :pdf) { render(:nothing => true) }
89 88 format.api { render_validation_errors(@query) }
90 89 end
91 90 end
92 91 rescue ActiveRecord::RecordNotFound
93 92 render_404
94 93 end
95 94
96 95 def show
97 96 @journals = @issue.journals.includes(:user, :details).
98 97 references(:user, :details).
99 98 reorder(:created_on, :id).to_a
100 99 @journals.each_with_index {|j,i| j.indice = i+1}
101 100 @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
102 101 Journal.preload_journals_details_custom_fields(@journals)
103 102 @journals.select! {|journal| journal.notes? || journal.visible_details.any?}
104 103 @journals.reverse! if User.current.wants_comments_in_reverse_order?
105 104
106 105 @changesets = @issue.changesets.visible.preload(:repository, :user).to_a
107 106 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
108 107
109 108 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
110 109 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
111 110 @priorities = IssuePriority.active
112 111 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
113 112 @relation = IssueRelation.new
114 113
115 114 respond_to do |format|
116 115 format.html {
117 116 retrieve_previous_and_next_issue_ids
118 117 render :template => 'issues/show'
119 118 }
120 119 format.api
121 120 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
122 121 format.pdf {
123 122 send_file_headers! :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf"
124 123 }
125 124 end
126 125 end
127 126
128 127 def new
129 128 respond_to do |format|
130 129 format.html { render :action => 'new', :layout => !request.xhr? }
131 130 format.js
132 131 end
133 132 end
134 133
135 134 def create
136 135 unless User.current.allowed_to?(:add_issues, @issue.project, :global => true)
137 136 raise ::Unauthorized
138 137 end
139 138 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
140 139 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
141 140 if @issue.save
142 141 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
143 142 respond_to do |format|
144 143 format.html {
145 144 render_attachment_warning_if_needed(@issue)
146 145 flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
147 146 redirect_after_create
148 147 }
149 148 format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
150 149 end
151 150 return
152 151 else
153 152 respond_to do |format|
154 153 format.html {
155 154 if @issue.project.nil?
156 155 render_error :status => 422
157 156 else
158 157 render :action => 'new'
159 158 end
160 159 }
161 160 format.api { render_validation_errors(@issue) }
162 161 end
163 162 end
164 163 end
165 164
166 165 def edit
167 166 return unless update_issue_from_params
168 167
169 168 respond_to do |format|
170 169 format.html { }
171 170 format.js
172 171 end
173 172 end
174 173
175 174 def update
176 175 return unless update_issue_from_params
177 176 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
178 177 saved = false
179 178 begin
180 179 saved = save_issue_with_child_records
181 180 rescue ActiveRecord::StaleObjectError
182 181 @conflict = true
183 182 if params[:last_journal_id]
184 183 @conflict_journals = @issue.journals_after(params[:last_journal_id]).to_a
185 184 @conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
186 185 end
187 186 end
188 187
189 188 if saved
190 189 render_attachment_warning_if_needed(@issue)
191 190 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
192 191
193 192 respond_to do |format|
194 193 format.html { redirect_back_or_default issue_path(@issue, previous_and_next_issue_ids_params) }
195 194 format.api { render_api_ok }
196 195 end
197 196 else
198 197 respond_to do |format|
199 198 format.html { render :action => 'edit' }
200 199 format.api { render_validation_errors(@issue) }
201 200 end
202 201 end
203 202 end
204 203
205 204 # Bulk edit/copy a set of issues
206 205 def bulk_edit
207 206 @issues.sort!
208 207 @copy = params[:copy].present?
209 208 @notes = params[:notes]
210 209
211 210 if @copy
212 211 unless User.current.allowed_to?(:copy_issues, @projects)
213 212 raise ::Unauthorized
214 213 end
215 214 end
216 215
217 216 @allowed_projects = Issue.allowed_target_projects
218 217 if params[:issue]
219 218 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
220 219 if @target_project
221 220 target_projects = [@target_project]
222 221 end
223 222 end
224 223 target_projects ||= @projects
225 224
226 225 if @copy
227 226 # Copied issues will get their default statuses
228 227 @available_statuses = []
229 228 else
230 229 @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
231 230 end
232 231 @custom_fields = @issues.map{|i|i.editable_custom_fields}.reduce(:&)
233 232 @assignables = target_projects.map(&:assignable_users).reduce(:&)
234 233 @trackers = target_projects.map(&:trackers).reduce(:&)
235 234 @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
236 235 @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
237 236 if @copy
238 237 @attachments_present = @issues.detect {|i| i.attachments.any?}.present?
239 238 @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
240 239 end
241 240
242 241 @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
243 242
244 243 @issue_params = params[:issue] || {}
245 244 @issue_params[:custom_field_values] ||= {}
246 245 end
247 246
248 247 def bulk_update
249 248 @issues.sort!
250 249 @copy = params[:copy].present?
251 250
252 251 attributes = parse_params_for_bulk_issue_attributes(params)
253 252 copy_subtasks = (params[:copy_subtasks] == '1')
254 253 copy_attachments = (params[:copy_attachments] == '1')
255 254
256 255 if @copy
257 256 unless User.current.allowed_to?(:copy_issues, @projects)
258 257 raise ::Unauthorized
259 258 end
260 259 target_projects = @projects
261 260 if attributes['project_id'].present?
262 261 target_projects = Project.where(:id => attributes['project_id']).to_a
263 262 end
264 263 unless User.current.allowed_to?(:add_issues, target_projects)
265 264 raise ::Unauthorized
266 265 end
267 266 end
268 267
269 268 unsaved_issues = []
270 269 saved_issues = []
271 270
272 271 if @copy && copy_subtasks
273 272 # Descendant issues will be copied with the parent task
274 273 # Don't copy them twice
275 274 @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
276 275 end
277 276
278 277 @issues.each do |orig_issue|
279 278 orig_issue.reload
280 279 if @copy
281 280 issue = orig_issue.copy({},
282 281 :attachments => copy_attachments,
283 282 :subtasks => copy_subtasks,
284 283 :link => link_copy?(params[:link_copy])
285 284 )
286 285 else
287 286 issue = orig_issue
288 287 end
289 288 journal = issue.init_journal(User.current, params[:notes])
290 289 issue.safe_attributes = attributes
291 290 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
292 291 if issue.save
293 292 saved_issues << issue
294 293 else
295 294 unsaved_issues << orig_issue
296 295 end
297 296 end
298 297
299 298 if unsaved_issues.empty?
300 299 flash[:notice] = l(:notice_successful_update) unless saved_issues.empty?
301 300 if params[:follow]
302 301 if @issues.size == 1 && saved_issues.size == 1
303 302 redirect_to issue_path(saved_issues.first)
304 303 elsif saved_issues.map(&:project).uniq.size == 1
305 304 redirect_to project_issues_path(saved_issues.map(&:project).first)
306 305 end
307 306 else
308 307 redirect_back_or_default _project_issues_path(@project)
309 308 end
310 309 else
311 310 @saved_issues = @issues
312 311 @unsaved_issues = unsaved_issues
313 312 @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a
314 313 bulk_edit
315 314 render :action => 'bulk_edit'
316 315 end
317 316 end
318 317
319 318 def destroy
320 319 @hours = TimeEntry.where(:issue_id => @issues.map(&:id)).sum(:hours).to_f
321 320 if @hours > 0
322 321 case params[:todo]
323 322 when 'destroy'
324 323 # nothing to do
325 324 when 'nullify'
326 325 TimeEntry.where(['issue_id IN (?)', @issues]).update_all('issue_id = NULL')
327 326 when 'reassign'
328 327 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
329 328 if reassign_to.nil?
330 329 flash.now[:error] = l(:error_issue_not_found_in_project)
331 330 return
332 331 else
333 332 TimeEntry.where(['issue_id IN (?)', @issues]).
334 333 update_all("issue_id = #{reassign_to.id}")
335 334 end
336 335 else
337 336 # display the destroy form if it's a user request
338 337 return unless api_request?
339 338 end
340 339 end
341 340 @issues.each do |issue|
342 341 begin
343 342 issue.reload.destroy
344 343 rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
345 344 # nothing to do, issue was already deleted (eg. by a parent)
346 345 end
347 346 end
348 347 respond_to do |format|
349 348 format.html { redirect_back_or_default _project_issues_path(@project) }
350 349 format.api { render_api_ok }
351 350 end
352 351 end
353 352
354 353 private
355 354
356 355 def retrieve_previous_and_next_issue_ids
357 356 if params[:prev_issue_id].present? || params[:next_issue_id].present?
358 357 @prev_issue_id = params[:prev_issue_id].presence.try(:to_i)
359 358 @next_issue_id = params[:next_issue_id].presence.try(:to_i)
360 359 @issue_position = params[:issue_position].presence.try(:to_i)
361 360 @issue_count = params[:issue_count].presence.try(:to_i)
362 361 else
363 362 retrieve_query_from_session
364 363 if @query
365 364 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
366 365 sort_update(@query.sortable_columns, 'issues_index_sort')
367 366 limit = 500
368 367 issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])
369 368 if (idx = issue_ids.index(@issue.id)) && idx < limit
370 369 if issue_ids.size < 500
371 370 @issue_position = idx + 1
372 371 @issue_count = issue_ids.size
373 372 end
374 373 @prev_issue_id = issue_ids[idx - 1] if idx > 0
375 374 @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
376 375 end
377 376 end
378 377 end
379 378 end
380 379
381 380 def previous_and_next_issue_ids_params
382 381 {
383 382 :prev_issue_id => params[:prev_issue_id],
384 383 :next_issue_id => params[:next_issue_id],
385 384 :issue_position => params[:issue_position],
386 385 :issue_count => params[:issue_count]
387 386 }.reject {|k,v| k.blank?}
388 387 end
389 388
390 389 # Used by #edit and #update to set some common instance variables
391 390 # from the params
392 391 def update_issue_from_params
393 392 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
394 393 if params[:time_entry]
395 394 @time_entry.safe_attributes = params[:time_entry]
396 395 end
397 396
398 397 @issue.init_journal(User.current)
399 398
400 399 issue_attributes = params[:issue]
401 400 if issue_attributes && params[:conflict_resolution]
402 401 case params[:conflict_resolution]
403 402 when 'overwrite'
404 403 issue_attributes = issue_attributes.dup
405 404 issue_attributes.delete(:lock_version)
406 405 when 'add_notes'
407 406 issue_attributes = issue_attributes.slice(:notes, :private_notes)
408 407 when 'cancel'
409 408 redirect_to issue_path(@issue)
410 409 return false
411 410 end
412 411 end
413 412 @issue.safe_attributes = issue_attributes
414 413 @priorities = IssuePriority.active
415 414 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
416 415 true
417 416 end
418 417
419 418 # Used by #new and #create to build a new issue from the params
420 419 # The new issue will be copied from an existing one if copy_from parameter is given
421 420 def build_new_issue_from_params
422 421 @issue = Issue.new
423 422 if params[:copy_from]
424 423 begin
425 424 @issue.init_journal(User.current)
426 425 @copy_from = Issue.visible.find(params[:copy_from])
427 426 unless User.current.allowed_to?(:copy_issues, @copy_from.project)
428 427 raise ::Unauthorized
429 428 end
430 429 @link_copy = link_copy?(params[:link_copy]) || request.get?
431 430 @copy_attachments = params[:copy_attachments].present? || request.get?
432 431 @copy_subtasks = params[:copy_subtasks].present? || request.get?
433 432 @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks, :link => @link_copy)
434 433 @issue.parent_issue_id = @copy_from.parent_id
435 434 rescue ActiveRecord::RecordNotFound
436 435 render_404
437 436 return
438 437 end
439 438 end
440 439 @issue.project = @project
441 440 if request.get?
442 441 @issue.project ||= @issue.allowed_target_projects.first
443 442 end
444 443 @issue.author ||= User.current
445 444 @issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?
446 445
447 446 attrs = (params[:issue] || {}).deep_dup
448 447 if action_name == 'new' && params[:was_default_status] == attrs[:status_id]
449 448 attrs.delete(:status_id)
450 449 end
451 450 if action_name == 'new' && params[:form_update_triggered_by] == 'issue_project_id'
452 451 # Discard submitted version when changing the project on the issue form
453 452 # so we can use the default version for the new project
454 453 attrs.delete(:fixed_version_id)
455 454 end
456 455 @issue.safe_attributes = attrs
457 456
458 457 if @issue.project
459 458 @issue.tracker ||= @issue.project.trackers.first
460 459 if @issue.tracker.nil?
461 460 render_error l(:error_no_tracker_in_project)
462 461 return false
463 462 end
464 463 if @issue.status.nil?
465 464 render_error l(:error_no_default_issue_status)
466 465 return false
467 466 end
468 467 end
469 468
470 469 @priorities = IssuePriority.active
471 470 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
472 471 end
473 472
474 473 def parse_params_for_bulk_issue_attributes(params)
475 474 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
476 475 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
477 476 if custom = attributes[:custom_field_values]
478 477 custom.reject! {|k,v| v.blank?}
479 478 custom.keys.each do |k|
480 479 if custom[k].is_a?(Array)
481 480 custom[k] << '' if custom[k].delete('__none__')
482 481 else
483 482 custom[k] = '' if custom[k] == '__none__'
484 483 end
485 484 end
486 485 end
487 486 attributes
488 487 end
489 488
490 489 # Saves @issue and a time_entry from the parameters
491 490 def save_issue_with_child_records
492 491 Issue.transaction do
493 492 if params[:time_entry] && (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) && User.current.allowed_to?(:log_time, @issue.project)
494 493 time_entry = @time_entry || TimeEntry.new
495 494 time_entry.project = @issue.project
496 495 time_entry.issue = @issue
497 496 time_entry.user = User.current
498 497 time_entry.spent_on = User.current.today
499 498 time_entry.attributes = params[:time_entry]
500 499 @issue.time_entries << time_entry
501 500 end
502 501
503 502 call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
504 503 if @issue.save
505 504 call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
506 505 else
507 506 raise ActiveRecord::Rollback
508 507 end
509 508 end
510 509 end
511 510
512 511 # Returns true if the issue copy should be linked
513 512 # to the original issue
514 513 def link_copy?(param)
515 514 case Setting.link_copied_issue
516 515 when 'yes'
517 516 true
518 517 when 'no'
519 518 false
520 519 when 'ask'
521 520 param == '1'
522 521 end
523 522 end
524 523
525 524 # Redirects user after a successful issue creation
526 525 def redirect_after_create
527 526 if params[:continue]
528 527 attrs = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?}
529 528 if params[:project_id]
530 529 redirect_to new_project_issue_path(@issue.project, :issue => attrs)
531 530 else
532 531 attrs.merge! :project_id => @issue.project_id
533 532 redirect_to new_issue_path(:issue => attrs)
534 533 end
535 534 else
536 535 redirect_to issue_path(@issue)
537 536 end
538 537 end
539 538 end
@@ -1,121 +1,127
1 1 <div class="contextual">
2 <% if !@query.new_record? && @query.editable_by?(User.current) %>
3 <%= link_to l(:button_edit), edit_query_path(@query), :class => 'icon icon-edit' %>
4 <%= delete_link query_path(@query) %>
5 <% end %>
2 <% if @project && User.current.allowed_to?(:add_issues, @project) && @project.trackers.any? %>
3 <%= link_to l(:label_issue_new), new_project_issue_path(@project), :class => 'icon icon-add new-issue' %>
4 <% end %>
6 5 </div>
7 6
8 7 <h2><%= @query.new_record? ? l(:label_issue_plural) : @query.name %></h2>
9 8 <% html_title(@query.new_record? ? l(:label_issue_plural) : @query.name) %>
10 9
11 10 <%= form_tag({ :controller => 'issues', :action => 'index', :project_id => @project },
12 11 :method => :get, :id => 'query_form') do %>
13 12 <div id="query_form_with_buttons" class="hide-when-print">
14 13 <%= hidden_field_tag 'set_filter', '1' %>
15 14 <div id="query_form_content">
16 15 <fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
17 16 <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
18 17 <div style="<%= @query.new_record? ? "" : "display: none;" %>">
19 18 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
20 19 </div>
21 20 </fieldset>
22 21 <fieldset id="options" class="collapsible collapsed">
23 22 <legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
24 23 <div style="display: none;">
25 24 <table>
26 25 <tr>
27 26 <td class="field"><%= l(:field_column_names) %></td>
28 27 <td><%= render_query_columns_selection(@query) %></td>
29 28 </tr>
30 29 <tr>
31 30 <td class="field"><label for='group_by'><%= l(:field_group_by) %></label></td>
32 31 <td><%= select_tag('group_by',
33 32 options_for_select(
34 33 [[]] + @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]},
35 34 @query.group_by)
36 35 ) %></td>
37 36 </tr>
38 37 <tr>
39 38 <td class="field"><%= l(:button_show) %></td>
40 39 <td><%= available_block_columns_tags(@query) %></td>
41 40 </tr>
42 41 <tr>
43 42 <td><%= l(:label_total_plural) %></td>
44 43 <td><%= available_totalable_columns_tags(@query) %></td>
45 44 </tr>
46 45 </table>
47 46 </div>
48 47 </fieldset>
49 48 </div>
50 49 <p class="buttons">
51 50 <%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %>
52 51 <%= link_to l(:button_clear), { :set_filter => 1, :project_id => @project }, :class => 'icon icon-reload' %>
53 <% if @query.new_record? && User.current.allowed_to?(:save_queries, @project, :global => true) %>
52 <% if @query.new_record? %>
53 <% if User.current.allowed_to?(:save_queries, @project, :global => true) %>
54 54 <%= link_to_function l(:button_save),
55 55 "$('#query_form').attr('action', '#{ @project ? new_project_query_path(@project) : new_query_path }').submit()",
56 56 :class => 'icon icon-save' %>
57 <% end %>
58 <% else %>
59 <% if @query.editable_by?(User.current) %>
60 <%= link_to l(:button_edit), edit_query_path(@query), :class => 'icon icon-edit' %>
61 <%= delete_link query_path(@query) %>
62 <% end %>
57 63 <% end %>
58 64 </p>
59 65 </div>
60 66 <% end %>
61 67
62 68 <%= error_messages_for 'query' %>
63 69 <% if @query.valid? %>
64 70 <% if @issues.empty? %>
65 71 <p class="nodata"><%= l(:label_no_data) %></p>
66 72 <% else %>
67 73 <%= render_query_totals(@query) %>
68 74 <%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %>
69 75 <span class="pagination"><%= pagination_links_full @issue_pages, @issue_count %></span>
70 76 <% end %>
71 77
72 78 <% other_formats_links do |f| %>
73 79 <%= f.link_to 'Atom', :url => params.merge(:key => User.current.rss_key) %>
74 80 <%= f.link_to 'CSV', :url => params, :onclick => "showModal('csv-export-options', '350px'); return false;" %>
75 81 <%= f.link_to 'PDF', :url => params %>
76 82 <% end %>
77 83
78 84 <div id="csv-export-options" style="display:none;">
79 85 <h3 class="title"><%= l(:label_export_options, :export_format => 'CSV') %></h3>
80 86 <%= form_tag(_project_issues_path(@project, :format => 'csv'), :method => :get, :id => 'csv-export-form') do %>
81 87 <%= query_as_hidden_field_tags(@query) %>
82 88 <%= hidden_field_tag 'sort', @sort_criteria.to_param, :id => nil %>
83 89 <p>
84 90 <label><%= radio_button_tag 'csv[columns]', '', true %> <%= l(:description_selected_columns) %></label><br />
85 91 <label><%= radio_button_tag 'csv[columns]', 'all' %> <%= l(:description_all_columns) %></label>
86 92 </p>
87 93 <p>
88 94 <label><%= check_box_tag 'csv[description]', '1', @query.has_column?(:description) %> <%= l(:field_description) %></label>
89 95 </p>
90 96 <% if @issue_count > Setting.issues_export_limit.to_i %>
91 97 <p class="icon icon-warning">
92 98 <%= l(:setting_issues_export_limit) %>: <%= Setting.issues_export_limit.to_i %>
93 99 </p>
94 100 <% end %>
95 101 <p class="buttons">
96 102 <%= submit_tag l(:button_export), :name => nil, :onclick => "hideModal(this);" %>
97 103 <%= submit_tag l(:button_cancel), :name => nil, :onclick => "hideModal(this);", :type => 'button' %>
98 104 </p>
99 105 <% end %>
100 106 </div>
101 107
102 108 <% end %>
103 109 <%= call_hook(:view_issues_index_bottom, { :issues => @issues, :project => @project, :query => @query }) %>
104 110
105 111 <% content_for :sidebar do %>
106 112 <%= render :partial => 'issues/sidebar' %>
107 113 <% end %>
108 114
109 115 <% content_for :header_tags do %>
110 116 <%= auto_discovery_link_tag(:atom,
111 117 {:query_id => @query, :format => 'atom',
112 118 :page => nil, :key => User.current.rss_key},
113 119 :title => l(:label_issue_plural)) %>
114 120 <%= auto_discovery_link_tag(:atom,
115 121 {:controller => 'journals', :action => 'index',
116 122 :query_id => @query, :format => 'atom',
117 123 :page => nil, :key => User.current.rss_key},
118 124 :title => l(:label_changes_details)) %>
119 125 <% end %>
120 126
121 127 <%= context_menu issues_context_menu_path %>
@@ -1,278 +1,274
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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 'redmine/core_ext'
19 19
20 20 begin
21 21 require 'rmagick' unless Object.const_defined?(:Magick)
22 22 rescue LoadError
23 23 # RMagick is not available
24 24 end
25 25 begin
26 26 require 'redcarpet' unless Object.const_defined?(:Redcarpet)
27 27 rescue LoadError
28 28 # Redcarpet is not available
29 29 end
30 30
31 31 require 'redmine/acts/positioned'
32 32
33 33 require 'redmine/scm/base'
34 34 require 'redmine/access_control'
35 35 require 'redmine/access_keys'
36 36 require 'redmine/activity'
37 37 require 'redmine/activity/fetcher'
38 38 require 'redmine/ciphering'
39 39 require 'redmine/codeset_util'
40 40 require 'redmine/field_format'
41 41 require 'redmine/menu_manager'
42 42 require 'redmine/notifiable'
43 43 require 'redmine/platform'
44 44 require 'redmine/mime_type'
45 45 require 'redmine/notifiable'
46 46 require 'redmine/search'
47 47 require 'redmine/syntax_highlighting'
48 48 require 'redmine/thumbnail'
49 49 require 'redmine/unified_diff'
50 50 require 'redmine/utils'
51 51 require 'redmine/version'
52 52 require 'redmine/wiki_formatting'
53 53
54 54 require 'redmine/default_data/loader'
55 55 require 'redmine/helpers/calendar'
56 56 require 'redmine/helpers/diff'
57 57 require 'redmine/helpers/gantt'
58 58 require 'redmine/helpers/time_report'
59 59 require 'redmine/views/other_formats_builder'
60 60 require 'redmine/views/labelled_form_builder'
61 61 require 'redmine/views/builders'
62 62
63 63 require 'redmine/themes'
64 64 require 'redmine/hook'
65 65 require 'redmine/hook/listener'
66 66 require 'redmine/hook/view_listener'
67 67 require 'redmine/plugin'
68 68
69 69 Redmine::Scm::Base.add "Subversion"
70 70 Redmine::Scm::Base.add "Darcs"
71 71 Redmine::Scm::Base.add "Mercurial"
72 72 Redmine::Scm::Base.add "Cvs"
73 73 Redmine::Scm::Base.add "Bazaar"
74 74 Redmine::Scm::Base.add "Git"
75 75 Redmine::Scm::Base.add "Filesystem"
76 76
77 77 # Permissions
78 78 Redmine::AccessControl.map do |map|
79 79 map.permission :view_project, {:projects => [:show], :activities => [:index]}, :public => true, :read => true
80 80 map.permission :search_project, {:search => :index}, :public => true, :read => true
81 81 map.permission :add_project, {:projects => [:new, :create]}, :require => :loggedin
82 82 map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member
83 83 map.permission :close_project, {:projects => [:close, :reopen]}, :require => :member, :read => true
84 84 map.permission :select_project_modules, {:projects => :modules}, :require => :member
85 85 map.permission :view_members, {:members => [:index, :show]}, :public => true, :read => true
86 86 map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :update, :destroy, :autocomplete]}, :require => :member
87 87 map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member
88 88 map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member
89 89
90 90 map.project_module :issue_tracking do |map|
91 91 # Issue categories
92 92 map.permission :manage_categories, {:projects => :settings, :issue_categories => [:index, :show, :new, :create, :edit, :update, :destroy]}, :require => :member
93 93 # Issues
94 94 map.permission :view_issues, {:issues => [:index, :show],
95 95 :auto_complete => [:issues],
96 96 :context_menus => [:issues],
97 97 :versions => [:index, :show, :status_by],
98 98 :journals => [:index, :diff],
99 99 :queries => :index,
100 100 :reports => [:issue_report, :issue_report_details]},
101 101 :read => true
102 102 map.permission :add_issues, {:issues => [:new, :create], :attachments => :upload}
103 103 map.permission :edit_issues, {:issues => [:edit, :update, :bulk_edit, :bulk_update], :journals => [:new], :attachments => :upload}
104 104 map.permission :copy_issues, {:issues => [:new, :create, :bulk_edit, :bulk_update], :attachments => :upload}
105 105 map.permission :manage_issue_relations, {:issue_relations => [:index, :show, :create, :destroy]}
106 106 map.permission :manage_subtasks, {}
107 107 map.permission :set_issues_private, {}
108 108 map.permission :set_own_issues_private, {}, :require => :loggedin
109 109 map.permission :add_issue_notes, {:issues => [:edit, :update], :journals => [:new], :attachments => :upload}
110 110 map.permission :edit_issue_notes, {:journals => [:edit, :update]}, :require => :loggedin
111 111 map.permission :edit_own_issue_notes, {:journals => [:edit, :update]}, :require => :loggedin
112 112 map.permission :view_private_notes, {}, :read => true, :require => :member
113 113 map.permission :set_notes_private, {}, :require => :member
114 114 map.permission :delete_issues, {:issues => :destroy}, :require => :member
115 115 # Queries
116 116 map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member
117 117 map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin
118 118 # Watchers
119 119 map.permission :view_issue_watchers, {}, :read => true
120 120 map.permission :add_issue_watchers, {:watchers => [:new, :create, :append, :autocomplete_for_user]}
121 121 map.permission :delete_issue_watchers, {:watchers => :destroy}
122 122 map.permission :import_issues, {:imports => [:new, :create, :settings, :mapping, :run, :show]}
123 123 end
124 124
125 125 map.project_module :time_tracking do |map|
126 126 map.permission :log_time, {:timelog => [:new, :create]}, :require => :loggedin
127 127 map.permission :view_time_entries, {:timelog => [:index, :report, :show]}, :read => true
128 128 map.permission :edit_time_entries, {:timelog => [:edit, :update, :destroy, :bulk_edit, :bulk_update]}, :require => :member
129 129 map.permission :edit_own_time_entries, {:timelog => [:edit, :update, :destroy,:bulk_edit, :bulk_update]}, :require => :loggedin
130 130 map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member
131 131 end
132 132
133 133 map.project_module :news do |map|
134 134 map.permission :manage_news, {:news => [:new, :create, :edit, :update, :destroy], :comments => [:destroy], :attachments => :upload}, :require => :member
135 135 map.permission :view_news, {:news => [:index, :show]}, :public => true, :read => true
136 136 map.permission :comment_news, {:comments => :create}
137 137 end
138 138
139 139 map.project_module :documents do |map|
140 140 map.permission :add_documents, {:documents => [:new, :create, :add_attachment], :attachments => :upload}, :require => :loggedin
141 141 map.permission :edit_documents, {:documents => [:edit, :update, :add_attachment], :attachments => :upload}, :require => :loggedin
142 142 map.permission :delete_documents, {:documents => [:destroy]}, :require => :loggedin
143 143 map.permission :view_documents, {:documents => [:index, :show, :download]}, :read => true
144 144 end
145 145
146 146 map.project_module :files do |map|
147 147 map.permission :manage_files, {:files => [:new, :create], :attachments => :upload}, :require => :loggedin
148 148 map.permission :view_files, {:files => :index, :versions => :download}, :read => true
149 149 end
150 150
151 151 map.project_module :wiki do |map|
152 152 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
153 153 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
154 154 map.permission :delete_wiki_pages, {:wiki => [:destroy, :destroy_version]}, :require => :member
155 155 map.permission :view_wiki_pages, {:wiki => [:index, :show, :special, :date_index]}, :read => true
156 156 map.permission :export_wiki_pages, {:wiki => [:export]}, :read => true
157 157 map.permission :view_wiki_edits, {:wiki => [:history, :diff, :annotate]}, :read => true
158 158 map.permission :edit_wiki_pages, :wiki => [:edit, :update, :preview, :add_attachment], :attachments => :upload
159 159 map.permission :delete_wiki_pages_attachments, {}
160 160 map.permission :protect_wiki_pages, {:wiki => :protect}, :require => :member
161 161 end
162 162
163 163 map.project_module :repository do |map|
164 164 map.permission :manage_repository, {:repositories => [:new, :create, :edit, :update, :committers, :destroy]}, :require => :member
165 165 map.permission :browse_repository, {:repositories => [:show, :browse, :entry, :raw, :annotate, :changes, :diff, :stats, :graph]}, :read => true
166 166 map.permission :view_changesets, {:repositories => [:show, :revisions, :revision]}, :read => true
167 167 map.permission :commit_access, {}
168 168 map.permission :manage_related_issues, {:repositories => [:add_related_issue, :remove_related_issue]}
169 169 end
170 170
171 171 map.project_module :boards do |map|
172 172 map.permission :manage_boards, {:boards => [:new, :create, :edit, :update, :destroy]}, :require => :member
173 173 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true, :read => true
174 174 map.permission :add_messages, {:messages => [:new, :reply, :quote], :attachments => :upload}
175 175 map.permission :edit_messages, {:messages => :edit, :attachments => :upload}, :require => :member
176 176 map.permission :edit_own_messages, {:messages => :edit, :attachments => :upload}, :require => :loggedin
177 177 map.permission :delete_messages, {:messages => :destroy}, :require => :member
178 178 map.permission :delete_own_messages, {:messages => :destroy}, :require => :loggedin
179 179 end
180 180
181 181 map.project_module :calendar do |map|
182 182 map.permission :view_calendar, {:calendars => [:show, :update]}, :read => true
183 183 end
184 184
185 185 map.project_module :gantt do |map|
186 186 map.permission :view_gantt, {:gantts => [:show, :update]}, :read => true
187 187 end
188 188 end
189 189
190 190 Redmine::MenuManager.map :top_menu do |menu|
191 191 menu.push :home, :home_path
192 192 menu.push :my_page, { :controller => 'my', :action => 'page' }, :if => Proc.new { User.current.logged? }
193 193 menu.push :projects, { :controller => 'projects', :action => 'index' }, :caption => :label_project_plural
194 194 menu.push :administration, { :controller => 'admin', :action => 'index' }, :if => Proc.new { User.current.admin? }, :last => true
195 195 menu.push :help, Redmine::Info.help_url, :last => true
196 196 end
197 197
198 198 Redmine::MenuManager.map :account_menu do |menu|
199 199 menu.push :login, :signin_path, :if => Proc.new { !User.current.logged? }
200 200 menu.push :register, :register_path, :if => Proc.new { !User.current.logged? && Setting.self_registration? }
201 201 menu.push :my_account, { :controller => 'my', :action => 'account' }, :if => Proc.new { User.current.logged? }
202 202 menu.push :logout, :signout_path, :html => {:method => 'post'}, :if => Proc.new { User.current.logged? }
203 203 end
204 204
205 205 Redmine::MenuManager.map :application_menu do |menu|
206 206 # Empty
207 207 end
208 208
209 209 Redmine::MenuManager.map :admin_menu do |menu|
210 210 menu.push :projects, {:controller => 'admin', :action => 'projects'}, :caption => :label_project_plural
211 211 menu.push :users, {:controller => 'users'}, :caption => :label_user_plural
212 212 menu.push :groups, {:controller => 'groups'}, :caption => :label_group_plural
213 213 menu.push :roles, {:controller => 'roles'}, :caption => :label_role_and_permissions
214 214 menu.push :trackers, {:controller => 'trackers'}, :caption => :label_tracker_plural
215 215 menu.push :issue_statuses, {:controller => 'issue_statuses'}, :caption => :label_issue_status_plural,
216 216 :html => {:class => 'issue_statuses'}
217 217 menu.push :workflows, {:controller => 'workflows', :action => 'edit'}, :caption => :label_workflow
218 218 menu.push :custom_fields, {:controller => 'custom_fields'}, :caption => :label_custom_field_plural,
219 219 :html => {:class => 'custom_fields'}
220 220 menu.push :enumerations, {:controller => 'enumerations'}
221 221 menu.push :settings, {:controller => 'settings'}
222 222 menu.push :ldap_authentication, {:controller => 'auth_sources', :action => 'index'},
223 223 :html => {:class => 'server_authentication'}
224 224 menu.push :plugins, {:controller => 'admin', :action => 'plugins'}, :last => true
225 225 menu.push :info, {:controller => 'admin', :action => 'info'}, :caption => :label_information_plural, :last => true
226 226 end
227 227
228 228 Redmine::MenuManager.map :project_menu do |menu|
229 229 menu.push :overview, { :controller => 'projects', :action => 'show' }
230 230 menu.push :activity, { :controller => 'activities', :action => 'index' }
231 231 menu.push :roadmap, { :controller => 'versions', :action => 'index' }, :param => :project_id,
232 232 :if => Proc.new { |p| p.shared_versions.any? }
233 233 menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural
234 menu.push :new_issue, { :controller => 'issues', :action => 'new', :copy_from => nil }, :param => :project_id, :caption => :label_issue_new,
235 :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) },
236 :if => Proc.new { |p| p.trackers.any? },
237 :permission => :add_issues
238 234 menu.push :gantt, { :controller => 'gantts', :action => 'show' }, :param => :project_id, :caption => :label_gantt
239 235 menu.push :calendar, { :controller => 'calendars', :action => 'show' }, :param => :project_id, :caption => :label_calendar
240 236 menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
241 237 menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
242 238 menu.push :wiki, { :controller => 'wiki', :action => 'show', :id => nil }, :param => :project_id,
243 239 :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
244 240 menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
245 241 :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
246 242 menu.push :files, { :controller => 'files', :action => 'index' }, :caption => :label_file_plural, :param => :project_id
247 243 menu.push :repository, { :controller => 'repositories', :action => 'show', :repository_id => nil, :path => nil, :rev => nil },
248 244 :if => Proc.new { |p| p.repository && !p.repository.new_record? }
249 245 menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true
250 246 end
251 247
252 248 Redmine::Activity.map do |activity|
253 249 activity.register :issues, :class_name => %w(Issue Journal)
254 250 activity.register :changesets
255 251 activity.register :news
256 252 activity.register :documents, :class_name => %w(Document Attachment)
257 253 activity.register :files, :class_name => 'Attachment'
258 254 activity.register :wiki_edits, :class_name => 'WikiContent::Version', :default => false
259 255 activity.register :messages, :default => false
260 256 activity.register :time_entries, :default => false
261 257 end
262 258
263 259 Redmine::Search.map do |search|
264 260 search.register :issues
265 261 search.register :news
266 262 search.register :documents
267 263 search.register :changesets
268 264 search.register :wiki_pages
269 265 search.register :messages
270 266 search.register :projects
271 267 end
272 268
273 269 Redmine::WikiFormatting.map do |format|
274 270 format.register :textile
275 271 format.register :markdown if Object.const_defined?(:Redcarpet)
276 272 end
277 273
278 274 ActionView::Template.register_template_handler :rsb, Redmine::Views::ApiTemplateHandler
@@ -1,4573 +1,4594
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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
20 20 class IssuesControllerTest < ActionController::TestCase
21 21 fixtures :projects,
22 22 :users, :email_addresses, :user_preferences,
23 23 :roles,
24 24 :members,
25 25 :member_roles,
26 26 :issues,
27 27 :issue_statuses,
28 28 :issue_relations,
29 29 :versions,
30 30 :trackers,
31 31 :projects_trackers,
32 32 :issue_categories,
33 33 :enabled_modules,
34 34 :enumerations,
35 35 :attachments,
36 36 :workflows,
37 37 :custom_fields,
38 38 :custom_values,
39 39 :custom_fields_projects,
40 40 :custom_fields_trackers,
41 41 :time_entries,
42 42 :journals,
43 43 :journal_details,
44 44 :queries,
45 45 :repositories,
46 46 :changesets
47 47
48 48 include Redmine::I18n
49 49
50 50 def setup
51 51 User.current = nil
52 52 end
53 53
54 54 def test_index
55 55 with_settings :default_language => "en" do
56 56 get :index
57 57 assert_response :success
58 58 assert_template 'index'
59 59 assert_not_nil assigns(:issues)
60 60 assert_nil assigns(:project)
61 61
62 62 # links to visible issues
63 63 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
64 64 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
65 65 # private projects hidden
66 66 assert_select 'a[href="/issues/6"]', 0
67 67 assert_select 'a[href="/issues/4"]', 0
68 68 # project column
69 69 assert_select 'th', :text => /Project/
70 70 end
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'
78 78 assert_not_nil assigns(:issues)
79 79 assert_nil assigns(:project)
80 80
81 81 assert_select 'a[href="/issues/1"]', 0
82 82 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
83 83 end
84 84
85 85 def test_index_should_list_visible_issues_only
86 86 get :index, :per_page => 100
87 87 assert_response :success
88 88 assert_not_nil assigns(:issues)
89 89 assert_nil assigns(:issues).detect {|issue| !issue.visible?}
90 90 end
91 91
92 92 def test_index_with_project
93 93 Setting.display_subprojects_issues = 0
94 94 get :index, :project_id => 1
95 95 assert_response :success
96 96 assert_template 'index'
97 97 assert_not_nil assigns(:issues)
98 98
99 99 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
100 100 assert_select 'a[href="/issues/5"]', 0
101 101 end
102 102
103 103 def test_index_with_project_and_subprojects
104 104 Setting.display_subprojects_issues = 1
105 105 get :index, :project_id => 1
106 106 assert_response :success
107 107 assert_template 'index'
108 108 assert_not_nil assigns(:issues)
109 109
110 110 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
111 111 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
112 112 assert_select 'a[href="/issues/6"]', 0
113 113 end
114 114
115 115 def test_index_with_project_and_subprojects_should_show_private_subprojects_with_permission
116 116 @request.session[:user_id] = 2
117 117 Setting.display_subprojects_issues = 1
118 118 get :index, :project_id => 1
119 119 assert_response :success
120 120 assert_template 'index'
121 121 assert_not_nil assigns(:issues)
122 122
123 123 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
124 124 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
125 125 assert_select 'a[href="/issues/6"]', :text => /Issue of a private subproject/
126 126 end
127 127
128 128 def test_index_with_project_and_default_filter
129 129 get :index, :project_id => 1, :set_filter => 1
130 130 assert_response :success
131 131 assert_template 'index'
132 132 assert_not_nil assigns(:issues)
133 133
134 134 query = assigns(:query)
135 135 assert_not_nil query
136 136 # default filter
137 137 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
138 138 end
139 139
140 140 def test_index_with_project_and_filter
141 141 get :index, :project_id => 1, :set_filter => 1,
142 142 :f => ['tracker_id'],
143 143 :op => {'tracker_id' => '='},
144 144 :v => {'tracker_id' => ['1']}
145 145 assert_response :success
146 146 assert_template 'index'
147 147 assert_not_nil assigns(:issues)
148 148
149 149 query = assigns(:query)
150 150 assert_not_nil query
151 151 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
152 152 end
153 153
154 154 def test_index_with_short_filters
155 155 to_test = {
156 156 'status_id' => {
157 157 'o' => { :op => 'o', :values => [''] },
158 158 'c' => { :op => 'c', :values => [''] },
159 159 '7' => { :op => '=', :values => ['7'] },
160 160 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
161 161 '=7' => { :op => '=', :values => ['7'] },
162 162 '!3' => { :op => '!', :values => ['3'] },
163 163 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
164 164 'subject' => {
165 165 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
166 166 'o' => { :op => '=', :values => ['o'] },
167 167 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
168 168 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
169 169 'tracker_id' => {
170 170 '3' => { :op => '=', :values => ['3'] },
171 171 '=3' => { :op => '=', :values => ['3'] }},
172 172 'start_date' => {
173 173 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
174 174 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
175 175 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
176 176 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
177 177 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
178 178 '<t+2' => { :op => '<t+', :values => ['2'] },
179 179 '>t+2' => { :op => '>t+', :values => ['2'] },
180 180 't+2' => { :op => 't+', :values => ['2'] },
181 181 't' => { :op => 't', :values => [''] },
182 182 'w' => { :op => 'w', :values => [''] },
183 183 '>t-2' => { :op => '>t-', :values => ['2'] },
184 184 '<t-2' => { :op => '<t-', :values => ['2'] },
185 185 't-2' => { :op => 't-', :values => ['2'] }},
186 186 'created_on' => {
187 187 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
188 188 '<t-2' => { :op => '<t-', :values => ['2'] },
189 189 '>t-2' => { :op => '>t-', :values => ['2'] },
190 190 't-2' => { :op => 't-', :values => ['2'] }},
191 191 'cf_1' => {
192 192 'c' => { :op => '=', :values => ['c'] },
193 193 '!c' => { :op => '!', :values => ['c'] },
194 194 '!*' => { :op => '!*', :values => [''] },
195 195 '*' => { :op => '*', :values => [''] }},
196 196 'estimated_hours' => {
197 197 '=13.4' => { :op => '=', :values => ['13.4'] },
198 198 '>=45' => { :op => '>=', :values => ['45'] },
199 199 '<=125' => { :op => '<=', :values => ['125'] },
200 200 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
201 201 '!*' => { :op => '!*', :values => [''] },
202 202 '*' => { :op => '*', :values => [''] }}
203 203 }
204 204
205 205 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
206 206
207 207 to_test.each do |field, expression_and_expected|
208 208 expression_and_expected.each do |filter_expression, expected|
209 209
210 210 get :index, :set_filter => 1, field => filter_expression
211 211
212 212 assert_response :success
213 213 assert_template 'index'
214 214 assert_not_nil assigns(:issues)
215 215
216 216 query = assigns(:query)
217 217 assert_not_nil query
218 218 assert query.has_filter?(field)
219 219 assert_equal(default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}}), query.filters)
220 220 end
221 221 end
222 222 end
223 223
224 224 def test_index_with_project_and_empty_filters
225 225 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
226 226 assert_response :success
227 227 assert_template 'index'
228 228 assert_not_nil assigns(:issues)
229 229
230 230 query = assigns(:query)
231 231 assert_not_nil query
232 232 # no filter
233 233 assert_equal({}, query.filters)
234 234 end
235 235
236 236 def test_index_with_project_custom_field_filter
237 237 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
238 238 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
239 239 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
240 240 filter_name = "project.cf_#{field.id}"
241 241 @request.session[:user_id] = 1
242 242
243 243 get :index, :set_filter => 1,
244 244 :f => [filter_name],
245 245 :op => {filter_name => '='},
246 246 :v => {filter_name => ['Foo']}
247 247 assert_response :success
248 248 assert_template 'index'
249 249 assert_equal [3, 5], assigns(:issues).map(&:project_id).uniq.sort
250 250 end
251 251
252 252 def test_index_with_query
253 253 get :index, :project_id => 1, :query_id => 5
254 254 assert_response :success
255 255 assert_template 'index'
256 256 assert_not_nil assigns(:issues)
257 257 assert_nil assigns(:issue_count_by_group)
258 258 end
259 259
260 260 def test_index_with_query_grouped_by_tracker
261 261 get :index, :project_id => 1, :query_id => 6
262 262 assert_response :success
263 263 assert_template 'index'
264 264 assert_not_nil assigns(:issues)
265 265 assert_not_nil assigns(:issue_count_by_group)
266 266 end
267 267
268 268 def test_index_with_query_grouped_and_sorted_by_category
269 269 get :index, :project_id => 1, :set_filter => 1, :group_by => "category", :sort => "category"
270 270 assert_response :success
271 271 assert_template 'index'
272 272 assert_not_nil assigns(:issues)
273 273 assert_not_nil assigns(:issue_count_by_group)
274 274 end
275 275
276 276 def test_index_with_query_grouped_by_list_custom_field
277 277 get :index, :project_id => 1, :query_id => 9
278 278 assert_response :success
279 279 assert_template 'index'
280 280 assert_not_nil assigns(:issues)
281 281 assert_not_nil assigns(:issue_count_by_group)
282 282 end
283 283
284 284 def test_index_with_query_grouped_by_key_value_custom_field
285 285 cf = IssueCustomField.create!(:name => 'Key', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'enumeration')
286 286 cf.enumerations << valueb = CustomFieldEnumeration.new(:name => 'Value B', :position => 1)
287 287 cf.enumerations << valuea = CustomFieldEnumeration.new(:name => 'Value A', :position => 2)
288 288 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => valueb.id)
289 289 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => valueb.id)
290 290 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => valuea.id)
291 291 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
292 292
293 293 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
294 294 assert_response :success
295 295 assert_template 'index'
296 296 assert_not_nil assigns(:issues)
297 297 assert_not_nil assigns(:issue_count_by_group)
298 298
299 299 assert_select 'tr.group', 3
300 300 assert_select 'tr.group' do
301 301 assert_select 'span.name', :text => 'Value B'
302 302 assert_select 'span.count', :text => '2'
303 303 end
304 304 assert_select 'tr.group' do
305 305 assert_select 'span.name', :text => 'Value A'
306 306 assert_select 'span.count', :text => '1'
307 307 end
308 308 end
309 309
310 310 def test_index_with_query_grouped_by_user_custom_field
311 311 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
312 312 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
313 313 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
314 314 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
315 315 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
316 316
317 317 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
318 318 assert_response :success
319 319
320 320 assert_select 'tr.group', 3
321 321 assert_select 'tr.group' do
322 322 assert_select 'a', :text => 'John Smith'
323 323 assert_select 'span.count', :text => '1'
324 324 end
325 325 assert_select 'tr.group' do
326 326 assert_select 'a', :text => 'Dave Lopper'
327 327 assert_select 'span.count', :text => '2'
328 328 end
329 329 end
330 330
331 331 def test_index_grouped_by_boolean_custom_field_should_distinguish_blank_and_false_values
332 332 cf = IssueCustomField.create!(:name => 'Bool', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'bool')
333 333 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '1')
334 334 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
335 335 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '')
336 336
337 337 with_settings :default_language => 'en' do
338 338 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
339 339 assert_response :success
340 340 end
341 341
342 342 assert_select 'tr.group', 3
343 343 assert_select 'tr.group', :text => /Yes/
344 344 assert_select 'tr.group', :text => /No/
345 345 assert_select 'tr.group', :text => /blank/
346 346 end
347 347
348 348 def test_index_grouped_by_boolean_custom_field_with_false_group_in_first_position_should_show_the_group
349 349 cf = IssueCustomField.create!(:name => 'Bool', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'bool', :is_filter => true)
350 350 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '0')
351 351 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
352 352
353 353 with_settings :default_language => 'en' do
354 354 get :index, :project_id => 1, :set_filter => 1, "cf_#{cf.id}" => "*", :group_by => "cf_#{cf.id}"
355 355 assert_response :success
356 356 assert_equal [1, 2], assigns(:issues).map(&:id).sort
357 357 end
358 358
359 359 assert_select 'tr.group', 1
360 360 assert_select 'tr.group', :text => /No/
361 361 end
362 362
363 363 def test_index_with_query_grouped_by_tracker_in_normal_order
364 364 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
365 365
366 366 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc'
367 367 assert_response :success
368 368
369 369 trackers = assigns(:issues).map(&:tracker).uniq
370 370 assert_equal [1, 2, 3], trackers.map(&:id)
371 371 end
372 372
373 373 def test_index_with_query_grouped_by_tracker_in_reverse_order
374 374 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
375 375
376 376 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc,tracker:desc'
377 377 assert_response :success
378 378
379 379 trackers = assigns(:issues).map(&:tracker).uniq
380 380 assert_equal [3, 2, 1], trackers.map(&:id)
381 381 end
382 382
383 383 def test_index_with_query_id_and_project_id_should_set_session_query
384 384 get :index, :project_id => 1, :query_id => 4
385 385 assert_response :success
386 386 assert_kind_of Hash, session[:query]
387 387 assert_equal 4, session[:query][:id]
388 388 assert_equal 1, session[:query][:project_id]
389 389 end
390 390
391 391 def test_index_with_invalid_query_id_should_respond_404
392 392 get :index, :project_id => 1, :query_id => 999
393 393 assert_response 404
394 394 end
395 395
396 396 def test_index_with_cross_project_query_in_session_should_show_project_issues
397 397 q = IssueQuery.create!(:name => "test", :user_id => 2, :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
398 398 @request.session[:query] = {:id => q.id, :project_id => 1}
399 399
400 400 with_settings :display_subprojects_issues => '0' do
401 401 get :index, :project_id => 1
402 402 end
403 403 assert_response :success
404 404 assert_not_nil assigns(:query)
405 405 assert_equal q.id, assigns(:query).id
406 406 assert_equal 1, assigns(:query).project_id
407 407 assert_equal [1], assigns(:issues).map(&:project_id).uniq
408 408 end
409 409
410 410 def test_private_query_should_not_be_available_to_other_users
411 411 q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
412 412 @request.session[:user_id] = 3
413 413
414 414 get :index, :query_id => q.id
415 415 assert_response 403
416 416 end
417 417
418 418 def test_private_query_should_be_available_to_its_user
419 419 q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
420 420 @request.session[:user_id] = 2
421 421
422 422 get :index, :query_id => q.id
423 423 assert_response :success
424 424 end
425 425
426 426 def test_public_query_should_be_available_to_other_users
427 427 q = IssueQuery.create!(:name => "public", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PUBLIC, :project => nil)
428 428 @request.session[:user_id] = 3
429 429
430 430 get :index, :query_id => q.id
431 431 assert_response :success
432 432 end
433 433
434 434 def test_index_should_omit_page_param_in_export_links
435 435 get :index, :page => 2
436 436 assert_response :success
437 437 assert_select 'a.atom[href="/issues.atom"]'
438 438 assert_select 'a.csv[href="/issues.csv"]'
439 439 assert_select 'a.pdf[href="/issues.pdf"]'
440 440 assert_select 'form#csv-export-form[action="/issues.csv"]'
441 441 end
442 442
443 443 def test_index_should_not_warn_when_not_exceeding_export_limit
444 444 with_settings :issues_export_limit => 200 do
445 445 get :index
446 446 assert_select '#csv-export-options p.icon-warning', 0
447 447 end
448 448 end
449 449
450 450 def test_index_should_warn_when_exceeding_export_limit
451 451 with_settings :issues_export_limit => 2 do
452 452 get :index
453 453 assert_select '#csv-export-options p.icon-warning', :text => %r{limit: 2}
454 454 end
455 455 end
456 456
457 457 def test_index_should_include_query_params_as_hidden_fields_in_csv_export_form
458 458 get :index, :project_id => 1, :set_filter => "1", :tracker_id => "2", :sort => 'status', :c => ["status", "priority"]
459 459
460 460 assert_select '#csv-export-form[action=?]', '/projects/ecookbook/issues.csv'
461 461 assert_select '#csv-export-form[method=?]', 'get'
462 462
463 463 assert_select '#csv-export-form' do
464 464 assert_select 'input[name=?][value=?]', 'set_filter', '1'
465 465
466 466 assert_select 'input[name=?][value=?]', 'f[]', 'tracker_id'
467 467 assert_select 'input[name=?][value=?]', 'op[tracker_id]', '='
468 468 assert_select 'input[name=?][value=?]', 'v[tracker_id][]', '2'
469 469
470 470 assert_select 'input[name=?][value=?]', 'c[]', 'status'
471 471 assert_select 'input[name=?][value=?]', 'c[]', 'priority'
472 472
473 473 assert_select 'input[name=?][value=?]', 'sort', 'status'
474 474 end
475 475 end
476 476
477 477 def test_index_csv
478 478 get :index, :format => 'csv'
479 479 assert_response :success
480 480 assert_not_nil assigns(:issues)
481 481 assert_equal 'text/csv; header=present', @response.content_type
482 482 assert @response.body.starts_with?("#,")
483 483 lines = @response.body.chomp.split("\n")
484 484 assert_equal assigns(:query).columns.size, lines[0].split(',').size
485 485 end
486 486
487 487 def test_index_csv_with_project
488 488 get :index, :project_id => 1, :format => 'csv'
489 489 assert_response :success
490 490 assert_not_nil assigns(:issues)
491 491 assert_equal 'text/csv; header=present', @response.content_type
492 492 end
493 493
494 494 def test_index_csv_with_description
495 495 Issue.generate!(:description => 'test_index_csv_with_description')
496 496
497 497 with_settings :default_language => 'en' do
498 498 get :index, :format => 'csv', :csv => {:description => '1'}
499 499 assert_response :success
500 500 assert_not_nil assigns(:issues)
501 501 end
502 502
503 503 assert_equal 'text/csv; header=present', response.content_type
504 504 headers = response.body.chomp.split("\n").first.split(',')
505 505 assert_include 'Description', headers
506 506 assert_include 'test_index_csv_with_description', response.body
507 507 end
508 508
509 509 def test_index_csv_with_spent_time_column
510 510 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :subject => 'test_index_csv_with_spent_time_column', :author_id => 2)
511 511 TimeEntry.create!(:project => issue.project, :issue => issue, :hours => 7.33, :user => User.find(2), :spent_on => Date.today)
512 512
513 513 get :index, :format => 'csv', :set_filter => '1', :c => %w(subject spent_hours)
514 514 assert_response :success
515 515 assert_equal 'text/csv; header=present', @response.content_type
516 516 lines = @response.body.chomp.split("\n")
517 517 assert_include "#{issue.id},#{issue.subject},7.33", lines
518 518 end
519 519
520 520 def test_index_csv_with_all_columns
521 521 get :index, :format => 'csv', :csv => {:columns => 'all'}
522 522 assert_response :success
523 523 assert_not_nil assigns(:issues)
524 524 assert_equal 'text/csv; header=present', @response.content_type
525 525 assert_match /\A#,/, response.body
526 526 lines = response.body.chomp.split("\n")
527 527 assert_equal assigns(:query).available_inline_columns.size, lines[0].split(',').size
528 528 end
529 529
530 530 def test_index_csv_with_multi_column_field
531 531 CustomField.find(1).update_attribute :multiple, true
532 532 issue = Issue.find(1)
533 533 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
534 534 issue.save!
535 535
536 536 get :index, :format => 'csv', :csv => {:columns => 'all'}
537 537 assert_response :success
538 538 lines = @response.body.chomp.split("\n")
539 539 assert lines.detect {|line| line.include?('"MySQL, Oracle"')}
540 540 end
541 541
542 542 def test_index_csv_should_format_float_custom_fields_with_csv_decimal_separator
543 543 field = IssueCustomField.create!(:name => 'Float', :is_for_all => true, :tracker_ids => [1], :field_format => 'float')
544 544 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id => '185.6'})
545 545
546 546 with_settings :default_language => 'fr' do
547 547 get :index, :format => 'csv', :csv => {:columns => 'all'}
548 548 assert_response :success
549 549 issue_line = response.body.chomp.split("\n").map {|line| line.split(';')}.detect {|line| line[0]==issue.id.to_s}
550 550 assert_include '185,60', issue_line
551 551 end
552 552
553 553 with_settings :default_language => 'en' do
554 554 get :index, :format => 'csv', :csv => {:columns => 'all'}
555 555 assert_response :success
556 556 issue_line = response.body.chomp.split("\n").map {|line| line.split(',')}.detect {|line| line[0]==issue.id.to_s}
557 557 assert_include '185.60', issue_line
558 558 end
559 559 end
560 560
561 561 def test_index_csv_should_fill_parent_column_with_parent_id
562 562 Issue.delete_all
563 563 parent = Issue.generate!
564 564 child = Issue.generate!(:parent_issue_id => parent.id)
565 565
566 566 with_settings :default_language => 'en' do
567 567 get :index, :format => 'csv', :c => %w(parent)
568 568 end
569 569 lines = response.body.split("\n")
570 570 assert_include "#{child.id},#{parent.id}", lines
571 571 end
572 572
573 573 def test_index_csv_big_5
574 574 with_settings :default_language => "zh-TW" do
575 575 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88".force_encoding('UTF-8')
576 576 str_big5 = "\xa4@\xa4\xeb".force_encoding('Big5')
577 577 issue = Issue.generate!(:subject => str_utf8)
578 578
579 579 get :index, :project_id => 1,
580 580 :f => ['subject'],
581 581 :op => '=', :values => [str_utf8],
582 582 :format => 'csv'
583 583 assert_equal 'text/csv; header=present', @response.content_type
584 584 lines = @response.body.chomp.split("\n")
585 585 header = lines[0]
586 586 status = "\xaa\xac\xbaA".force_encoding('Big5')
587 587 assert_include status, header
588 588 issue_line = lines.find {|l| l =~ /^#{issue.id},/}
589 589 assert_include str_big5, issue_line
590 590 end
591 591 end
592 592
593 593 def test_index_csv_cannot_convert_should_be_replaced_big_5
594 594 with_settings :default_language => "zh-TW" do
595 595 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85".force_encoding('UTF-8')
596 596 issue = Issue.generate!(:subject => str_utf8)
597 597
598 598 get :index, :project_id => 1,
599 599 :f => ['subject'],
600 600 :op => '=', :values => [str_utf8],
601 601 :c => ['status', 'subject'],
602 602 :format => 'csv',
603 603 :set_filter => 1
604 604 assert_equal 'text/csv; header=present', @response.content_type
605 605 lines = @response.body.chomp.split("\n")
606 606 header = lines[0]
607 607 issue_line = lines.find {|l| l =~ /^#{issue.id},/}
608 608 s1 = "\xaa\xac\xbaA".force_encoding('Big5') # status
609 609 assert header.include?(s1)
610 610 s2 = issue_line.split(",")[2]
611 611 s3 = "\xa5H?".force_encoding('Big5') # subject
612 612 assert_equal s3, s2
613 613 end
614 614 end
615 615
616 616 def test_index_csv_tw
617 617 with_settings :default_language => "zh-TW" do
618 618 str1 = "test_index_csv_tw"
619 619 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
620 620
621 621 get :index, :project_id => 1,
622 622 :f => ['subject'],
623 623 :op => '=', :values => [str1],
624 624 :c => ['estimated_hours', 'subject'],
625 625 :format => 'csv',
626 626 :set_filter => 1
627 627 assert_equal 'text/csv; header=present', @response.content_type
628 628 lines = @response.body.chomp.split("\n")
629 629 assert_include "#{issue.id},1234.50,#{str1}", lines
630 630 end
631 631 end
632 632
633 633 def test_index_csv_fr
634 634 with_settings :default_language => "fr" do
635 635 str1 = "test_index_csv_fr"
636 636 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
637 637
638 638 get :index, :project_id => 1,
639 639 :f => ['subject'],
640 640 :op => '=', :values => [str1],
641 641 :c => ['estimated_hours', 'subject'],
642 642 :format => 'csv',
643 643 :set_filter => 1
644 644 assert_equal 'text/csv; header=present', @response.content_type
645 645 lines = @response.body.chomp.split("\n")
646 646 assert_include "#{issue.id};1234,50;#{str1}", lines
647 647 end
648 648 end
649 649
650 650 def test_index_pdf
651 651 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
652 652 with_settings :default_language => lang do
653 653
654 654 get :index
655 655 assert_response :success
656 656 assert_template 'index'
657 657
658 658 get :index, :format => 'pdf'
659 659 assert_response :success
660 660 assert_not_nil assigns(:issues)
661 661 assert_equal 'application/pdf', @response.content_type
662 662
663 663 get :index, :project_id => 1, :format => 'pdf'
664 664 assert_response :success
665 665 assert_not_nil assigns(:issues)
666 666 assert_equal 'application/pdf', @response.content_type
667 667
668 668 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
669 669 assert_response :success
670 670 assert_not_nil assigns(:issues)
671 671 assert_equal 'application/pdf', @response.content_type
672 672 end
673 673 end
674 674 end
675 675
676 676 def test_index_pdf_with_query_grouped_by_list_custom_field
677 677 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
678 678 assert_response :success
679 679 assert_not_nil assigns(:issues)
680 680 assert_not_nil assigns(:issue_count_by_group)
681 681 assert_equal 'application/pdf', @response.content_type
682 682 end
683 683
684 684 def test_index_atom
685 685 get :index, :project_id => 'ecookbook', :format => 'atom'
686 686 assert_response :success
687 687 assert_template 'common/feed'
688 688 assert_equal 'application/atom+xml', response.content_type
689 689
690 690 assert_select 'feed' do
691 691 assert_select 'link[rel=self][href=?]', 'http://test.host/projects/ecookbook/issues.atom'
692 692 assert_select 'link[rel=alternate][href=?]', 'http://test.host/projects/ecookbook/issues'
693 693 assert_select 'entry link[href=?]', 'http://test.host/issues/1'
694 694 end
695 695 end
696 696
697 697 def test_index_sort
698 698 get :index, :sort => 'tracker,id:desc'
699 699 assert_response :success
700 700
701 701 sort_params = @request.session['issues_index_sort']
702 702 assert sort_params.is_a?(String)
703 703 assert_equal 'tracker,id:desc', sort_params
704 704
705 705 issues = assigns(:issues)
706 706 assert_not_nil issues
707 707 assert !issues.empty?
708 708 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
709 709 assert_select 'table.issues.sort-by-tracker.sort-asc'
710 710 end
711 711
712 712 def test_index_sort_by_field_not_included_in_columns
713 713 Setting.issue_list_default_columns = %w(subject author)
714 714 get :index, :sort => 'tracker'
715 715 end
716 716
717 717 def test_index_sort_by_assigned_to
718 718 get :index, :sort => 'assigned_to'
719 719 assert_response :success
720 720 assignees = assigns(:issues).collect(&:assigned_to).compact
721 721 assert_equal assignees.sort, assignees
722 722 assert_select 'table.issues.sort-by-assigned-to.sort-asc'
723 723 end
724 724
725 725 def test_index_sort_by_assigned_to_desc
726 726 get :index, :sort => 'assigned_to:desc'
727 727 assert_response :success
728 728 assignees = assigns(:issues).collect(&:assigned_to).compact
729 729 assert_equal assignees.sort.reverse, assignees
730 730 assert_select 'table.issues.sort-by-assigned-to.sort-desc'
731 731 end
732 732
733 733 def test_index_group_by_assigned_to
734 734 get :index, :group_by => 'assigned_to', :sort => 'priority'
735 735 assert_response :success
736 736 end
737 737
738 738 def test_index_sort_by_author
739 739 get :index, :sort => 'author'
740 740 assert_response :success
741 741 authors = assigns(:issues).collect(&:author)
742 742 assert_equal authors.sort, authors
743 743 end
744 744
745 745 def test_index_sort_by_author_desc
746 746 get :index, :sort => 'author:desc'
747 747 assert_response :success
748 748 authors = assigns(:issues).collect(&:author)
749 749 assert_equal authors.sort.reverse, authors
750 750 end
751 751
752 752 def test_index_group_by_author
753 753 get :index, :group_by => 'author', :sort => 'priority'
754 754 assert_response :success
755 755 end
756 756
757 757 def test_index_sort_by_spent_hours
758 758 get :index, :sort => 'spent_hours:desc'
759 759 assert_response :success
760 760 hours = assigns(:issues).collect(&:spent_hours)
761 761 assert_equal hours.sort.reverse, hours
762 762 end
763 763
764 764 def test_index_sort_by_total_spent_hours
765 765 get :index, :sort => 'total_spent_hours:desc'
766 766 assert_response :success
767 767 hours = assigns(:issues).collect(&:total_spent_hours)
768 768 assert_equal hours.sort.reverse, hours
769 769 end
770 770
771 771 def test_index_sort_by_total_estimated_hours
772 772 get :index, :sort => 'total_estimated_hours:desc'
773 773 assert_response :success
774 774 hours = assigns(:issues).collect(&:total_estimated_hours)
775 775 assert_equal hours.sort.reverse, hours
776 776 end
777 777
778 778 def test_index_sort_by_user_custom_field
779 779 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
780 780 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
781 781 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
782 782 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
783 783 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
784 784
785 785 get :index, :project_id => 1, :set_filter => 1, :sort => "cf_#{cf.id},id"
786 786 assert_response :success
787 787
788 788 assert_equal [2, 3, 1], assigns(:issues).select {|issue| issue.custom_field_value(cf).present?}.map(&:id)
789 789 end
790 790
791 791 def test_index_with_columns
792 792 columns = ['tracker', 'subject', 'assigned_to']
793 793 get :index, :set_filter => 1, :c => columns
794 794 assert_response :success
795 795
796 796 # query should use specified columns
797 797 query = assigns(:query)
798 798 assert_kind_of IssueQuery, query
799 799 assert_equal columns, query.column_names.map(&:to_s)
800 800
801 801 # columns should be stored in session
802 802 assert_kind_of Hash, session[:query]
803 803 assert_kind_of Array, session[:query][:column_names]
804 804 assert_equal columns, session[:query][:column_names].map(&:to_s)
805 805
806 806 # ensure only these columns are kept in the selected columns list
807 807 assert_select 'select#selected_columns option' do
808 808 assert_select 'option', 3
809 809 assert_select 'option[value=tracker]'
810 810 assert_select 'option[value=project]', 0
811 811 end
812 812 end
813 813
814 814 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
815 815 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
816 816 get :index, :set_filter => 1
817 817
818 818 # query should use specified columns
819 819 query = assigns(:query)
820 820 assert_kind_of IssueQuery, query
821 821 assert_equal [:id, :project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
822 822 end
823 823
824 824 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
825 825 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
826 826 columns = ['id', 'tracker', 'subject', 'assigned_to']
827 827 get :index, :set_filter => 1, :c => columns
828 828
829 829 # query should use specified columns
830 830 query = assigns(:query)
831 831 assert_kind_of IssueQuery, query
832 832 assert_equal columns.map(&:to_sym), query.columns.map(&:name)
833 833 end
834 834
835 835 def test_index_with_default_columns_should_respect_default_columns_order
836 836 columns = ['assigned_to', 'subject', 'status', 'tracker']
837 837 with_settings :issue_list_default_columns => columns do
838 838 get :index, :project_id => 1, :set_filter => 1
839 839
840 840 query = assigns(:query)
841 841 assert_equal (['id'] + columns).map(&:to_sym), query.columns.map(&:name)
842 842 end
843 843 end
844 844
845 845 def test_index_with_custom_field_column
846 846 columns = %w(tracker subject cf_2)
847 847 get :index, :set_filter => 1, :c => columns
848 848 assert_response :success
849 849
850 850 # query should use specified columns
851 851 query = assigns(:query)
852 852 assert_kind_of IssueQuery, query
853 853 assert_equal columns, query.column_names.map(&:to_s)
854 854
855 855 assert_select 'table.issues td.cf_2.string'
856 856 end
857 857
858 858 def test_index_with_multi_custom_field_column
859 859 field = CustomField.find(1)
860 860 field.update_attribute :multiple, true
861 861 issue = Issue.find(1)
862 862 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
863 863 issue.save!
864 864
865 865 get :index, :set_filter => 1, :c => %w(tracker subject cf_1)
866 866 assert_response :success
867 867
868 868 assert_select 'table.issues td.cf_1', :text => 'MySQL, Oracle'
869 869 end
870 870
871 871 def test_index_with_multi_user_custom_field_column
872 872 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
873 873 :tracker_ids => [1], :is_for_all => true)
874 874 issue = Issue.find(1)
875 875 issue.custom_field_values = {field.id => ['2', '3']}
876 876 issue.save!
877 877
878 878 get :index, :set_filter => 1, :c => ['tracker', 'subject', "cf_#{field.id}"]
879 879 assert_response :success
880 880
881 881 assert_select "table.issues td.cf_#{field.id}" do
882 882 assert_select 'a', 2
883 883 assert_select 'a[href=?]', '/users/2', :text => 'John Smith'
884 884 assert_select 'a[href=?]', '/users/3', :text => 'Dave Lopper'
885 885 end
886 886 end
887 887
888 888 def test_index_with_date_column
889 889 with_settings :date_format => '%d/%m/%Y' do
890 890 Issue.find(1).update_attribute :start_date, '1987-08-24'
891 891 get :index, :set_filter => 1, :c => %w(start_date)
892 892 assert_select "table.issues td.start_date", :text => '24/08/1987'
893 893 end
894 894 end
895 895
896 896 def test_index_with_done_ratio_column
897 897 Issue.find(1).update_attribute :done_ratio, 40
898 898 get :index, :set_filter => 1, :c => %w(done_ratio)
899 899 assert_select 'table.issues td.done_ratio' do
900 900 assert_select 'table.progress' do
901 901 assert_select 'td.closed[style=?]', 'width: 40%;'
902 902 end
903 903 end
904 904 end
905 905
906 906 def test_index_with_spent_hours_column
907 907 Issue.expects(:load_visible_spent_hours).once
908 908 get :index, :set_filter => 1, :c => %w(subject spent_hours)
909 909 assert_select 'table.issues tr#issue-3 td.spent_hours', :text => '1.00'
910 910 end
911 911
912 912 def test_index_with_total_spent_hours_column
913 913 Issue.expects(:load_visible_total_spent_hours).once
914 914 get :index, :set_filter => 1, :c => %w(subject total_spent_hours)
915 915 assert_select 'table.issues tr#issue-3 td.total_spent_hours', :text => '1.00'
916 916 end
917 917
918 918 def test_index_with_total_estimated_hours_column
919 919 get :index, :set_filter => 1, :c => %w(subject total_estimated_hours)
920 920 assert_select 'table.issues td.total_estimated_hours'
921 921 end
922 922
923 923 def test_index_should_not_show_spent_hours_column_without_permission
924 924 Role.anonymous.remove_permission! :view_time_entries
925 925 get :index, :set_filter => 1, :c => %w(subject spent_hours)
926 926 assert_select 'td.spent_hours', 0
927 927 end
928 928
929 929 def test_index_with_fixed_version_column
930 930 get :index, :set_filter => 1, :c => %w(fixed_version)
931 931 assert_select 'table.issues td.fixed_version' do
932 932 assert_select 'a[href=?]', '/versions/2', :text => 'eCookbook - 1.0'
933 933 end
934 934 end
935 935
936 936 def test_index_with_relations_column
937 937 IssueRelation.delete_all
938 938 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(7))
939 939 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(8), :issue_to => Issue.find(1))
940 940 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(1), :issue_to => Issue.find(11))
941 941 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(12), :issue_to => Issue.find(2))
942 942
943 943 get :index, :set_filter => 1, :c => %w(subject relations)
944 944 assert_response :success
945 945 assert_select "tr#issue-1 td.relations" do
946 946 assert_select "span", 3
947 947 assert_select "span", :text => "Related to #7"
948 948 assert_select "span", :text => "Related to #8"
949 949 assert_select "span", :text => "Blocks #11"
950 950 end
951 951 assert_select "tr#issue-2 td.relations" do
952 952 assert_select "span", 1
953 953 assert_select "span", :text => "Blocked by #12"
954 954 end
955 955 assert_select "tr#issue-3 td.relations" do
956 956 assert_select "span", 0
957 957 end
958 958
959 959 get :index, :set_filter => 1, :c => %w(relations), :format => 'csv'
960 960 assert_response :success
961 961 assert_equal 'text/csv; header=present', response.content_type
962 962 lines = response.body.chomp.split("\n")
963 963 assert_include '1,"Related to #7, Related to #8, Blocks #11"', lines
964 964 assert_include '2,Blocked by #12', lines
965 965 assert_include '3,""', lines
966 966
967 967 get :index, :set_filter => 1, :c => %w(subject relations), :format => 'pdf'
968 968 assert_response :success
969 969 assert_equal 'application/pdf', response.content_type
970 970 end
971 971
972 972 def test_index_with_description_column
973 973 get :index, :set_filter => 1, :c => %w(subject description)
974 974
975 975 assert_select 'table.issues thead th', 3 # columns: chekbox + id + subject
976 976 assert_select 'td.description[colspan="3"]', :text => 'Unable to print recipes'
977 977
978 978 get :index, :set_filter => 1, :c => %w(subject description), :format => 'pdf'
979 979 assert_response :success
980 980 assert_equal 'application/pdf', response.content_type
981 981 end
982 982
983 983 def test_index_with_parent_column
984 984 Issue.delete_all
985 985 parent = Issue.generate!
986 986 child = Issue.generate!(:parent_issue_id => parent.id)
987 987
988 988 get :index, :c => %w(parent)
989 989
990 990 assert_select 'td.parent', :text => "#{parent.tracker} ##{parent.id}"
991 991 assert_select 'td.parent a[title=?]', parent.subject
992 992 end
993 993
994 994 def test_index_with_estimated_hours_total
995 995 Issue.delete_all
996 996 Issue.generate!(:estimated_hours => 5.5)
997 997 Issue.generate!(:estimated_hours => 1.1)
998 998
999 999 get :index, :t => %w(estimated_hours)
1000 1000 assert_response :success
1001 1001 assert_select '.query-totals'
1002 1002 assert_select '.total-for-estimated-hours span.value', :text => '6.60'
1003 1003 assert_select 'input[type=checkbox][name=?][value=estimated_hours][checked=checked]', 't[]'
1004 1004 end
1005 1005
1006 1006 def test_index_with_grouped_query_and_estimated_hours_total
1007 1007 Issue.delete_all
1008 1008 Issue.generate!(:estimated_hours => 5.5, :category_id => 1)
1009 1009 Issue.generate!(:estimated_hours => 2.3, :category_id => 1)
1010 1010 Issue.generate!(:estimated_hours => 1.1, :category_id => 2)
1011 1011 Issue.generate!(:estimated_hours => 4.6)
1012 1012
1013 1013 get :index, :t => %w(estimated_hours), :group_by => 'category'
1014 1014 assert_response :success
1015 1015 assert_select '.query-totals'
1016 1016 assert_select '.query-totals .total-for-estimated-hours span.value', :text => '13.50'
1017 1017 assert_select 'tr.group', :text => /Printing/ do
1018 1018 assert_select '.total-for-estimated-hours span.value', :text => '7.80'
1019 1019 end
1020 1020 assert_select 'tr.group', :text => /Recipes/ do
1021 1021 assert_select '.total-for-estimated-hours span.value', :text => '1.10'
1022 1022 end
1023 1023 assert_select 'tr.group', :text => /blank/ do
1024 1024 assert_select '.total-for-estimated-hours span.value', :text => '4.60'
1025 1025 end
1026 1026 end
1027 1027
1028 1028 def test_index_with_int_custom_field_total
1029 1029 field = IssueCustomField.generate!(:field_format => 'int', :is_for_all => true)
1030 1030 CustomValue.create!(:customized => Issue.find(1), :custom_field => field, :value => '2')
1031 1031 CustomValue.create!(:customized => Issue.find(2), :custom_field => field, :value => '7')
1032 1032
1033 1033 get :index, :t => ["cf_#{field.id}"]
1034 1034 assert_response :success
1035 1035 assert_select '.query-totals'
1036 1036 assert_select ".total-for-cf-#{field.id} span.value", :text => '9'
1037 1037 end
1038 1038
1039 1039 def test_index_totals_should_default_to_settings
1040 1040 with_settings :issue_list_default_totals => ['estimated_hours'] do
1041 1041 get :index
1042 1042 assert_response :success
1043 1043 assert_select '.total-for-estimated-hours span.value'
1044 1044 assert_select '.query-totals>span', 1
1045 1045 end
1046 1046 end
1047 1047
1048 1048 def test_index_send_html_if_query_is_invalid
1049 1049 get :index, :f => ['start_date'], :op => {:start_date => '='}
1050 1050 assert_equal 'text/html', @response.content_type
1051 1051 assert_template 'index'
1052 1052 end
1053 1053
1054 1054 def test_index_send_nothing_if_query_is_invalid
1055 1055 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
1056 1056 assert_equal 'text/csv', @response.content_type
1057 1057 assert @response.body.blank?
1058 1058 end
1059 1059
1060 def test_index_should_include_new_issue_link
1061 @request.session[:user_id] = 2
1062 get :index, :project_id => 1
1063 assert_select 'a.new-issue[href="/projects/ecookbook/issues/new"]', :text => 'New issue'
1064 end
1065
1066 def test_index_should_not_include_new_issue_link_for_project_without_trackers
1067 Project.find(1).trackers.clear
1068
1069 @request.session[:user_id] = 2
1070 get :index, :project_id => 1
1071 assert_select 'a.new-issue', 0
1072 end
1073
1074 def test_index_should_not_include_new_issue_link_for_users_with_copy_issues_permission_only
1075 role = Role.find(1)
1076 role.remove_permission! :add_issues
1077 role.add_permission! :copy_issues
1078
1079 @request.session[:user_id] = 2
1080 get :index, :project_id => 1
1081 assert_select 'a.new-issue', 0
1082 end
1083
1060 1084 def test_show_by_anonymous
1061 1085 get :show, :id => 1
1062 1086 assert_response :success
1063 1087 assert_template 'show'
1064 1088 assert_equal Issue.find(1), assigns(:issue)
1065 1089 assert_select 'div.issue div.description', :text => /Unable to print recipes/
1066 1090 # anonymous role is allowed to add a note
1067 1091 assert_select 'form#issue-form' do
1068 1092 assert_select 'fieldset' do
1069 1093 assert_select 'legend', :text => 'Notes'
1070 1094 assert_select 'textarea[name=?]', 'issue[notes]'
1071 1095 end
1072 1096 end
1073 1097 assert_select 'title', :text => "Bug #1: Cannot print recipes - eCookbook - Redmine"
1074 1098 end
1075 1099
1076 1100 def test_show_by_manager
1077 1101 @request.session[:user_id] = 2
1078 1102 get :show, :id => 1
1079 1103 assert_response :success
1080 1104 assert_select 'a', :text => /Quote/
1081 1105 assert_select 'form#issue-form' do
1082 1106 assert_select 'fieldset' do
1083 1107 assert_select 'legend', :text => 'Change properties'
1084 1108 assert_select 'input[name=?]', 'issue[subject]'
1085 1109 end
1086 1110 assert_select 'fieldset' do
1087 1111 assert_select 'legend', :text => 'Log time'
1088 1112 assert_select 'input[name=?]', 'time_entry[hours]'
1089 1113 end
1090 1114 assert_select 'fieldset' do
1091 1115 assert_select 'legend', :text => 'Notes'
1092 1116 assert_select 'textarea[name=?]', 'issue[notes]'
1093 1117 end
1094 1118 end
1095 1119 end
1096 1120
1097 1121 def test_show_should_display_update_form
1098 1122 @request.session[:user_id] = 2
1099 1123 get :show, :id => 1
1100 1124 assert_response :success
1101 1125
1102 1126 assert_select 'form#issue-form' do
1103 1127 assert_select 'input[name=?]', 'issue[is_private]'
1104 1128 assert_select 'select[name=?]', 'issue[project_id]'
1105 1129 assert_select 'select[name=?]', 'issue[tracker_id]'
1106 1130 assert_select 'input[name=?]', 'issue[subject]'
1107 1131 assert_select 'textarea[name=?]', 'issue[description]'
1108 1132 assert_select 'select[name=?]', 'issue[status_id]'
1109 1133 assert_select 'select[name=?]', 'issue[priority_id]'
1110 1134 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1111 1135 assert_select 'select[name=?]', 'issue[category_id]'
1112 1136 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1113 1137 assert_select 'input[name=?]', 'issue[parent_issue_id]'
1114 1138 assert_select 'input[name=?]', 'issue[start_date]'
1115 1139 assert_select 'input[name=?]', 'issue[due_date]'
1116 1140 assert_select 'select[name=?]', 'issue[done_ratio]'
1117 1141 assert_select 'input[name=?]', 'issue[custom_field_values][2]'
1118 1142 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1119 1143 assert_select 'textarea[name=?]', 'issue[notes]'
1120 1144 end
1121 1145 end
1122 1146
1123 1147 def test_show_should_display_update_form_with_minimal_permissions
1124 1148 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
1125 1149 WorkflowTransition.delete_all :role_id => 1
1126 1150
1127 1151 @request.session[:user_id] = 2
1128 1152 get :show, :id => 1
1129 1153 assert_response :success
1130 1154
1131 1155 assert_select 'form#issue-form' do
1132 1156 assert_select 'input[name=?]', 'issue[is_private]', 0
1133 1157 assert_select 'select[name=?]', 'issue[project_id]', 0
1134 1158 assert_select 'select[name=?]', 'issue[tracker_id]', 0
1135 1159 assert_select 'input[name=?]', 'issue[subject]', 0
1136 1160 assert_select 'textarea[name=?]', 'issue[description]', 0
1137 1161 assert_select 'select[name=?]', 'issue[status_id]', 0
1138 1162 assert_select 'select[name=?]', 'issue[priority_id]', 0
1139 1163 assert_select 'select[name=?]', 'issue[assigned_to_id]', 0
1140 1164 assert_select 'select[name=?]', 'issue[category_id]', 0
1141 1165 assert_select 'select[name=?]', 'issue[fixed_version_id]', 0
1142 1166 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
1143 1167 assert_select 'input[name=?]', 'issue[start_date]', 0
1144 1168 assert_select 'input[name=?]', 'issue[due_date]', 0
1145 1169 assert_select 'select[name=?]', 'issue[done_ratio]', 0
1146 1170 assert_select 'input[name=?]', 'issue[custom_field_values][2]', 0
1147 1171 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1148 1172 assert_select 'textarea[name=?]', 'issue[notes]'
1149 1173 end
1150 1174 end
1151 1175
1152 1176 def test_show_should_not_display_update_form_without_permissions
1153 1177 Role.find(1).update_attribute :permissions, [:view_issues]
1154 1178
1155 1179 @request.session[:user_id] = 2
1156 1180 get :show, :id => 1
1157 1181 assert_response :success
1158 1182
1159 1183 assert_select 'form#issue-form', 0
1160 1184 end
1161 1185
1162 1186 def test_update_form_should_not_display_inactive_enumerations
1163 1187 assert !IssuePriority.find(15).active?
1164 1188
1165 1189 @request.session[:user_id] = 2
1166 1190 get :show, :id => 1
1167 1191 assert_response :success
1168 1192
1169 1193 assert_select 'form#issue-form' do
1170 1194 assert_select 'select[name=?]', 'issue[priority_id]' do
1171 1195 assert_select 'option[value="4"]'
1172 1196 assert_select 'option[value="15"]', 0
1173 1197 end
1174 1198 end
1175 1199 end
1176 1200
1177 1201 def test_update_form_should_allow_attachment_upload
1178 1202 @request.session[:user_id] = 2
1179 1203 get :show, :id => 1
1180 1204
1181 1205 assert_select 'form#issue-form[method=post][enctype="multipart/form-data"]' do
1182 1206 assert_select 'input[type=file][name=?]', 'attachments[dummy][file]'
1183 1207 end
1184 1208 end
1185 1209
1186 1210 def test_show_should_deny_anonymous_access_without_permission
1187 1211 Role.anonymous.remove_permission!(:view_issues)
1188 1212 get :show, :id => 1
1189 1213 assert_response :redirect
1190 1214 end
1191 1215
1192 1216 def test_show_should_deny_anonymous_access_to_private_issue
1193 1217 Issue.where(:id => 1).update_all(["is_private = ?", true])
1194 1218 get :show, :id => 1
1195 1219 assert_response :redirect
1196 1220 end
1197 1221
1198 1222 def test_show_should_deny_non_member_access_without_permission
1199 1223 Role.non_member.remove_permission!(:view_issues)
1200 1224 @request.session[:user_id] = 9
1201 1225 get :show, :id => 1
1202 1226 assert_response 403
1203 1227 end
1204 1228
1205 1229 def test_show_should_deny_non_member_access_to_private_issue
1206 1230 Issue.where(:id => 1).update_all(["is_private = ?", true])
1207 1231 @request.session[:user_id] = 9
1208 1232 get :show, :id => 1
1209 1233 assert_response 403
1210 1234 end
1211 1235
1212 1236 def test_show_should_deny_member_access_without_permission
1213 1237 Role.find(1).remove_permission!(:view_issues)
1214 1238 @request.session[:user_id] = 2
1215 1239 get :show, :id => 1
1216 1240 assert_response 403
1217 1241 end
1218 1242
1219 1243 def test_show_should_deny_member_access_to_private_issue_without_permission
1220 1244 Issue.where(:id => 1).update_all(["is_private = ?", true])
1221 1245 @request.session[:user_id] = 3
1222 1246 get :show, :id => 1
1223 1247 assert_response 403
1224 1248 end
1225 1249
1226 1250 def test_show_should_allow_author_access_to_private_issue
1227 1251 Issue.where(:id => 1).update_all(["is_private = ?, author_id = 3", true])
1228 1252 @request.session[:user_id] = 3
1229 1253 get :show, :id => 1
1230 1254 assert_response :success
1231 1255 end
1232 1256
1233 1257 def test_show_should_allow_assignee_access_to_private_issue
1234 1258 Issue.where(:id => 1).update_all(["is_private = ?, assigned_to_id = 3", true])
1235 1259 @request.session[:user_id] = 3
1236 1260 get :show, :id => 1
1237 1261 assert_response :success
1238 1262 end
1239 1263
1240 1264 def test_show_should_allow_member_access_to_private_issue_with_permission
1241 1265 Issue.where(:id => 1).update_all(["is_private = ?", true])
1242 1266 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
1243 1267 @request.session[:user_id] = 3
1244 1268 get :show, :id => 1
1245 1269 assert_response :success
1246 1270 end
1247 1271
1248 1272 def test_show_should_not_disclose_relations_to_invisible_issues
1249 1273 Setting.cross_project_issue_relations = '1'
1250 1274 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
1251 1275 # Relation to a private project issue
1252 1276 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
1253 1277
1254 1278 get :show, :id => 1
1255 1279 assert_response :success
1256 1280
1257 1281 assert_select 'div#relations' do
1258 1282 assert_select 'a', :text => /#2$/
1259 1283 assert_select 'a', :text => /#4$/, :count => 0
1260 1284 end
1261 1285 end
1262 1286
1263 1287 def test_show_should_list_subtasks
1264 1288 Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1265 1289
1266 1290 get :show, :id => 1
1267 1291 assert_response :success
1268 1292
1269 1293 assert_select 'div#issue_tree' do
1270 1294 assert_select 'td.subject', :text => /Child Issue/
1271 1295 end
1272 1296 end
1273 1297
1274 1298 def test_show_should_list_parents
1275 1299 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1276 1300
1277 1301 get :show, :id => issue.id
1278 1302 assert_response :success
1279 1303
1280 1304 assert_select 'div.subject' do
1281 1305 assert_select 'h3', 'Child Issue'
1282 1306 assert_select 'a[href="/issues/1"]'
1283 1307 end
1284 1308 end
1285 1309
1286 1310 def test_show_should_not_display_prev_next_links_without_query_in_session
1287 1311 get :show, :id => 1
1288 1312 assert_response :success
1289 1313 assert_nil assigns(:prev_issue_id)
1290 1314 assert_nil assigns(:next_issue_id)
1291 1315
1292 1316 assert_select 'div.next-prev-links', 0
1293 1317 end
1294 1318
1295 1319 def test_show_should_display_prev_next_links_with_query_in_session
1296 1320 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1297 1321 @request.session['issues_index_sort'] = 'id'
1298 1322
1299 1323 with_settings :display_subprojects_issues => '0' do
1300 1324 get :show, :id => 3
1301 1325 end
1302 1326
1303 1327 assert_response :success
1304 1328 # Previous and next issues for all projects
1305 1329 assert_equal 2, assigns(:prev_issue_id)
1306 1330 assert_equal 5, assigns(:next_issue_id)
1307 1331
1308 1332 count = Issue.open.visible.count
1309 1333
1310 1334 assert_select 'div.next-prev-links' do
1311 1335 assert_select 'a[href="/issues/2"]', :text => /Previous/
1312 1336 assert_select 'a[href="/issues/5"]', :text => /Next/
1313 1337 assert_select 'span.position', :text => "3 of #{count}"
1314 1338 end
1315 1339 end
1316 1340
1317 1341 def test_show_should_display_prev_next_links_with_saved_query_in_session
1318 1342 query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC, :user_id => 1,
1319 1343 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
1320 1344 :sort_criteria => [['id', 'asc']])
1321 1345 @request.session[:query] = {:id => query.id, :project_id => nil}
1322 1346
1323 1347 get :show, :id => 11
1324 1348
1325 1349 assert_response :success
1326 1350 assert_equal query, assigns(:query)
1327 1351 # Previous and next issues for all projects
1328 1352 assert_equal 8, assigns(:prev_issue_id)
1329 1353 assert_equal 12, assigns(:next_issue_id)
1330 1354
1331 1355 assert_select 'div.next-prev-links' do
1332 1356 assert_select 'a[href="/issues/8"]', :text => /Previous/
1333 1357 assert_select 'a[href="/issues/12"]', :text => /Next/
1334 1358 end
1335 1359 end
1336 1360
1337 1361 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
1338 1362 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1339 1363
1340 1364 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
1341 1365 @request.session['issues_index_sort'] = assoc_sort
1342 1366
1343 1367 get :show, :id => 3
1344 1368 assert_response :success, "Wrong response status for #{assoc_sort} sort"
1345 1369
1346 1370 assert_select 'div.next-prev-links' do
1347 1371 assert_select 'a', :text => /(Previous|Next)/
1348 1372 end
1349 1373 end
1350 1374 end
1351 1375
1352 1376 def test_show_should_display_prev_next_links_with_project_query_in_session
1353 1377 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1354 1378 @request.session['issues_index_sort'] = 'id'
1355 1379
1356 1380 with_settings :display_subprojects_issues => '0' do
1357 1381 get :show, :id => 3
1358 1382 end
1359 1383
1360 1384 assert_response :success
1361 1385 # Previous and next issues inside project
1362 1386 assert_equal 2, assigns(:prev_issue_id)
1363 1387 assert_equal 7, assigns(:next_issue_id)
1364 1388
1365 1389 assert_select 'div.next-prev-links' do
1366 1390 assert_select 'a[href="/issues/2"]', :text => /Previous/
1367 1391 assert_select 'a[href="/issues/7"]', :text => /Next/
1368 1392 end
1369 1393 end
1370 1394
1371 1395 def test_show_should_not_display_prev_link_for_first_issue
1372 1396 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1373 1397 @request.session['issues_index_sort'] = 'id'
1374 1398
1375 1399 with_settings :display_subprojects_issues => '0' do
1376 1400 get :show, :id => 1
1377 1401 end
1378 1402
1379 1403 assert_response :success
1380 1404 assert_nil assigns(:prev_issue_id)
1381 1405 assert_equal 2, assigns(:next_issue_id)
1382 1406
1383 1407 assert_select 'div.next-prev-links' do
1384 1408 assert_select 'a', :text => /Previous/, :count => 0
1385 1409 assert_select 'a[href="/issues/2"]', :text => /Next/
1386 1410 end
1387 1411 end
1388 1412
1389 1413 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1390 1414 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1391 1415 @request.session['issues_index_sort'] = 'id'
1392 1416
1393 1417 get :show, :id => 1
1394 1418
1395 1419 assert_response :success
1396 1420 assert_nil assigns(:prev_issue_id)
1397 1421 assert_nil assigns(:next_issue_id)
1398 1422
1399 1423 assert_select 'a', :text => /Previous/, :count => 0
1400 1424 assert_select 'a', :text => /Next/, :count => 0
1401 1425 end
1402 1426
1403 1427 def test_show_show_should_display_prev_next_links_with_query_sort_by_user_custom_field
1404 1428 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
1405 1429 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
1406 1430 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
1407 1431 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
1408 1432 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
1409 1433
1410 1434 query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC, :user_id => 1, :filters => {},
1411 1435 :sort_criteria => [["cf_#{cf.id}", 'asc'], ['id', 'asc']])
1412 1436 @request.session[:query] = {:id => query.id, :project_id => nil}
1413 1437
1414 1438 get :show, :id => 3
1415 1439 assert_response :success
1416 1440
1417 1441 assert_equal 2, assigns(:prev_issue_id)
1418 1442 assert_equal 1, assigns(:next_issue_id)
1419 1443
1420 1444 assert_select 'div.next-prev-links' do
1421 1445 assert_select 'a[href="/issues/2"]', :text => /Previous/
1422 1446 assert_select 'a[href="/issues/1"]', :text => /Next/
1423 1447 end
1424 1448 end
1425 1449
1426 1450 def test_show_should_display_prev_next_links_when_request_has_previous_and_next_issue_ids_params
1427 1451 get :show, :id => 1, :prev_issue_id => 1, :next_issue_id => 3, :issue_position => 2, :issue_count => 4
1428 1452 assert_response :success
1429 1453
1430 1454 assert_select 'div.next-prev-links' do
1431 1455 assert_select 'a[href="/issues/1"]', :text => /Previous/
1432 1456 assert_select 'a[href="/issues/3"]', :text => /Next/
1433 1457 assert_select 'span.position', :text => "2 of 4"
1434 1458 end
1435 1459 end
1436 1460
1437 1461 def test_show_should_display_category_field_if_categories_are_defined
1438 1462 Issue.update_all :category_id => nil
1439 1463
1440 1464 get :show, :id => 1
1441 1465 assert_response :success
1442 1466 assert_select '.attributes .category'
1443 1467 end
1444 1468
1445 1469 def test_show_should_not_display_category_field_if_no_categories_are_defined
1446 1470 Project.find(1).issue_categories.delete_all
1447 1471
1448 1472 get :show, :id => 1
1449 1473 assert_response :success
1450 1474 assert_select 'table.attributes .category', 0
1451 1475 end
1452 1476
1453 1477 def test_show_should_display_link_to_the_assignee
1454 1478 get :show, :id => 2
1455 1479 assert_response :success
1456 1480 assert_select '.assigned-to' do
1457 1481 assert_select 'a[href="/users/3"]'
1458 1482 end
1459 1483 end
1460 1484
1461 1485 def test_show_should_display_visible_changesets_from_other_projects
1462 1486 project = Project.find(2)
1463 1487 issue = project.issues.first
1464 1488 issue.changeset_ids = [102]
1465 1489 issue.save!
1466 1490 # changesets from other projects should be displayed even if repository
1467 1491 # is disabled on issue's project
1468 1492 project.disable_module! :repository
1469 1493
1470 1494 @request.session[:user_id] = 2
1471 1495 get :show, :id => issue.id
1472 1496
1473 1497 assert_select 'a[href=?]', '/projects/ecookbook/repository/revisions/3'
1474 1498 end
1475 1499
1476 1500 def test_show_should_display_watchers
1477 1501 @request.session[:user_id] = 2
1478 1502 Issue.find(1).add_watcher User.find(2)
1479 1503
1480 1504 get :show, :id => 1
1481 1505 assert_select 'div#watchers ul' do
1482 1506 assert_select 'li' do
1483 1507 assert_select 'a[href="/users/2"]'
1484 1508 assert_select 'a[class*=delete]'
1485 1509 end
1486 1510 end
1487 1511 end
1488 1512
1489 1513 def test_show_should_display_watchers_with_gravatars
1490 1514 @request.session[:user_id] = 2
1491 1515 Issue.find(1).add_watcher User.find(2)
1492 1516
1493 1517 with_settings :gravatar_enabled => '1' do
1494 1518 get :show, :id => 1
1495 1519 end
1496 1520
1497 1521 assert_select 'div#watchers ul' do
1498 1522 assert_select 'li' do
1499 1523 assert_select 'img.gravatar'
1500 1524 assert_select 'a[href="/users/2"]'
1501 1525 assert_select 'a[class*=delete]'
1502 1526 end
1503 1527 end
1504 1528 end
1505 1529
1506 1530 def test_show_with_thumbnails_enabled_should_display_thumbnails
1507 1531 @request.session[:user_id] = 2
1508 1532
1509 1533 with_settings :thumbnails_enabled => '1' do
1510 1534 get :show, :id => 14
1511 1535 assert_response :success
1512 1536 end
1513 1537
1514 1538 assert_select 'div.thumbnails' do
1515 1539 assert_select 'a[href="/attachments/16/testfile.png"]' do
1516 1540 assert_select 'img[src="/attachments/thumbnail/16"]'
1517 1541 end
1518 1542 end
1519 1543 end
1520 1544
1521 1545 def test_show_with_thumbnails_disabled_should_not_display_thumbnails
1522 1546 @request.session[:user_id] = 2
1523 1547
1524 1548 with_settings :thumbnails_enabled => '0' do
1525 1549 get :show, :id => 14
1526 1550 assert_response :success
1527 1551 end
1528 1552
1529 1553 assert_select 'div.thumbnails', 0
1530 1554 end
1531 1555
1532 1556 def test_show_with_multi_custom_field
1533 1557 field = CustomField.find(1)
1534 1558 field.update_attribute :multiple, true
1535 1559 issue = Issue.find(1)
1536 1560 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1537 1561 issue.save!
1538 1562
1539 1563 get :show, :id => 1
1540 1564 assert_response :success
1541 1565
1542 1566 assert_select ".cf_1 .value", :text => 'MySQL, Oracle'
1543 1567 end
1544 1568
1545 1569 def test_show_with_multi_user_custom_field
1546 1570 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1547 1571 :tracker_ids => [1], :is_for_all => true)
1548 1572 issue = Issue.find(1)
1549 1573 issue.custom_field_values = {field.id => ['2', '3']}
1550 1574 issue.save!
1551 1575
1552 1576 get :show, :id => 1
1553 1577 assert_response :success
1554 1578
1555 1579 assert_select ".cf_#{field.id} .value", :text => 'Dave Lopper, John Smith' do
1556 1580 assert_select 'a', :text => 'Dave Lopper'
1557 1581 assert_select 'a', :text => 'John Smith'
1558 1582 end
1559 1583 end
1560 1584
1561 1585 def test_show_should_display_private_notes_with_permission_only
1562 1586 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true, :user_id => 1)
1563 1587 @request.session[:user_id] = 2
1564 1588
1565 1589 get :show, :id => 2
1566 1590 assert_response :success
1567 1591 assert_include journal, assigns(:journals)
1568 1592
1569 1593 Role.find(1).remove_permission! :view_private_notes
1570 1594 get :show, :id => 2
1571 1595 assert_response :success
1572 1596 assert_not_include journal, assigns(:journals)
1573 1597 end
1574 1598
1575 1599 def test_show_atom
1576 1600 get :show, :id => 2, :format => 'atom'
1577 1601 assert_response :success
1578 1602 assert_template 'journals/index'
1579 1603 # Inline image
1580 1604 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1581 1605 end
1582 1606
1583 1607 def test_show_export_to_pdf
1584 1608 issue = Issue.find(3)
1585 1609 assert issue.relations.select{|r| r.other_issue(issue).visible?}.present?
1586 1610 get :show, :id => 3, :format => 'pdf'
1587 1611 assert_response :success
1588 1612 assert_equal 'application/pdf', @response.content_type
1589 1613 assert @response.body.starts_with?('%PDF')
1590 1614 assert_not_nil assigns(:issue)
1591 1615 end
1592 1616
1593 1617 def test_export_to_pdf_with_utf8_u_fffd
1594 1618 # U+FFFD
1595 1619 s = "\xef\xbf\xbd"
1596 1620 s.force_encoding('UTF-8') if s.respond_to?(:force_encoding)
1597 1621 issue = Issue.generate!(:subject => s)
1598 1622 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
1599 1623 with_settings :default_language => lang do
1600 1624 get :show, :id => issue.id, :format => 'pdf'
1601 1625 assert_response :success
1602 1626 assert_equal 'application/pdf', @response.content_type
1603 1627 assert @response.body.starts_with?('%PDF')
1604 1628 assert_not_nil assigns(:issue)
1605 1629 end
1606 1630 end
1607 1631 end
1608 1632
1609 1633 def test_show_export_to_pdf_with_ancestors
1610 1634 issue = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1611 1635
1612 1636 get :show, :id => issue.id, :format => 'pdf'
1613 1637 assert_response :success
1614 1638 assert_equal 'application/pdf', @response.content_type
1615 1639 assert @response.body.starts_with?('%PDF')
1616 1640 end
1617 1641
1618 1642 def test_show_export_to_pdf_with_descendants
1619 1643 c1 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1620 1644 c2 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1621 1645 c3 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => c1.id)
1622 1646
1623 1647 get :show, :id => 1, :format => 'pdf'
1624 1648 assert_response :success
1625 1649 assert_equal 'application/pdf', @response.content_type
1626 1650 assert @response.body.starts_with?('%PDF')
1627 1651 end
1628 1652
1629 1653 def test_show_export_to_pdf_with_journals
1630 1654 get :show, :id => 1, :format => 'pdf'
1631 1655 assert_response :success
1632 1656 assert_equal 'application/pdf', @response.content_type
1633 1657 assert @response.body.starts_with?('%PDF')
1634 1658 end
1635 1659
1636 1660 def test_show_export_to_pdf_with_changesets
1637 1661 [[100], [100, 101], [100, 101, 102]].each do |cs|
1638 1662 issue1 = Issue.find(3)
1639 1663 issue1.changesets = Changeset.find(cs)
1640 1664 issue1.save!
1641 1665 issue = Issue.find(3)
1642 1666 assert_equal issue.changesets.count, cs.size
1643 1667 get :show, :id => 3, :format => 'pdf'
1644 1668 assert_response :success
1645 1669 assert_equal 'application/pdf', @response.content_type
1646 1670 assert @response.body.starts_with?('%PDF')
1647 1671 end
1648 1672 end
1649 1673
1650 1674 def test_show_invalid_should_respond_with_404
1651 1675 get :show, :id => 999
1652 1676 assert_response 404
1653 1677 end
1654 1678
1655 1679 def test_get_new
1656 1680 @request.session[:user_id] = 2
1657 1681 get :new, :project_id => 1, :tracker_id => 1
1658 1682 assert_response :success
1659 1683 assert_template 'new'
1660 1684
1661 1685 assert_select 'form#issue-form[action=?]', '/projects/ecookbook/issues'
1662 1686 assert_select 'form#issue-form' do
1663 1687 assert_select 'input[name=?]', 'issue[is_private]'
1664 1688 assert_select 'select[name=?]', 'issue[project_id]', 0
1665 1689 assert_select 'select[name=?]', 'issue[tracker_id]'
1666 1690 assert_select 'input[name=?]', 'issue[subject]'
1667 1691 assert_select 'textarea[name=?]', 'issue[description]'
1668 1692 assert_select 'select[name=?]', 'issue[status_id]'
1669 1693 assert_select 'select[name=?]', 'issue[priority_id]'
1670 1694 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1671 1695 assert_select 'select[name=?]', 'issue[category_id]'
1672 1696 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1673 1697 assert_select 'input[name=?]', 'issue[parent_issue_id]'
1674 1698 assert_select 'input[name=?]', 'issue[start_date]'
1675 1699 assert_select 'input[name=?]', 'issue[due_date]'
1676 1700 assert_select 'select[name=?]', 'issue[done_ratio]'
1677 1701 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
1678 1702 assert_select 'input[name=?]', 'issue[watcher_user_ids][]'
1679 1703 end
1680 1704
1681 1705 # Be sure we don't display inactive IssuePriorities
1682 1706 assert ! IssuePriority.find(15).active?
1683 1707 assert_select 'select[name=?]', 'issue[priority_id]' do
1684 1708 assert_select 'option[value="15"]', 0
1685 1709 end
1686 1710 end
1687 1711
1688 1712 def test_get_new_with_minimal_permissions
1689 1713 Role.find(1).update_attribute :permissions, [:add_issues]
1690 1714 WorkflowTransition.delete_all :role_id => 1
1691 1715
1692 1716 @request.session[:user_id] = 2
1693 1717 get :new, :project_id => 1, :tracker_id => 1
1694 1718 assert_response :success
1695 1719 assert_template 'new'
1696 1720
1697 1721 assert_select 'form#issue-form' do
1698 1722 assert_select 'input[name=?]', 'issue[is_private]', 0
1699 1723 assert_select 'select[name=?]', 'issue[project_id]', 0
1700 1724 assert_select 'select[name=?]', 'issue[tracker_id]'
1701 1725 assert_select 'input[name=?]', 'issue[subject]'
1702 1726 assert_select 'textarea[name=?]', 'issue[description]'
1703 1727 assert_select 'select[name=?]', 'issue[status_id]'
1704 1728 assert_select 'select[name=?]', 'issue[priority_id]'
1705 1729 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1706 1730 assert_select 'select[name=?]', 'issue[category_id]'
1707 1731 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1708 1732 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
1709 1733 assert_select 'input[name=?]', 'issue[start_date]'
1710 1734 assert_select 'input[name=?]', 'issue[due_date]'
1711 1735 assert_select 'select[name=?]', 'issue[done_ratio]'
1712 1736 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
1713 1737 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1714 1738 end
1715 1739 end
1716 1740
1717 1741 def test_new_without_project_id
1718 1742 @request.session[:user_id] = 2
1719 1743 get :new
1720 1744 assert_response :success
1721 1745 assert_template 'new'
1722 1746
1723 1747 assert_select 'form#issue-form[action=?]', '/issues'
1724 1748 assert_select 'form#issue-form' do
1725 1749 assert_select 'select[name=?]', 'issue[project_id]'
1726 1750 end
1727 1751
1728 1752 assert_nil assigns(:project)
1729 1753 assert_not_nil assigns(:issue)
1730 1754 end
1731 1755
1732 1756 def test_new_should_select_default_status
1733 1757 @request.session[:user_id] = 2
1734 1758
1735 1759 get :new, :project_id => 1
1736 1760 assert_response :success
1737 1761 assert_template 'new'
1738 1762 assert_select 'select[name=?]', 'issue[status_id]' do
1739 1763 assert_select 'option[value="1"][selected=selected]'
1740 1764 end
1741 1765 assert_select 'input[name=was_default_status][value="1"]'
1742 1766 end
1743 1767
1744 1768 def test_new_should_propose_allowed_statuses
1745 1769 WorkflowTransition.delete_all
1746 1770 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 1)
1747 1771 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 3)
1748 1772 @request.session[:user_id] = 2
1749 1773
1750 1774 get :new, :project_id => 1
1751 1775 assert_response :success
1752 1776 assert_select 'select[name=?]', 'issue[status_id]' do
1753 1777 assert_select 'option[value="1"]'
1754 1778 assert_select 'option[value="3"]'
1755 1779 assert_select 'option', 2
1756 1780 assert_select 'option[value="1"][selected=selected]'
1757 1781 end
1758 1782 end
1759 1783
1760 1784 def test_new_should_propose_allowed_statuses_without_default_status_allowed
1761 1785 WorkflowTransition.delete_all
1762 1786 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 2)
1763 1787 assert_equal 1, Tracker.find(1).default_status_id
1764 1788 @request.session[:user_id] = 2
1765 1789
1766 1790 get :new, :project_id => 1
1767 1791 assert_response :success
1768 1792 assert_select 'select[name=?]', 'issue[status_id]' do
1769 1793 assert_select 'option[value="2"]'
1770 1794 assert_select 'option', 1
1771 1795 assert_select 'option[value="2"][selected=selected]'
1772 1796 end
1773 1797 end
1774 1798
1775 1799 def test_new_should_preselect_default_version
1776 1800 version = Version.generate!(:project_id => 1)
1777 1801 Project.find(1).update_attribute :default_version_id, version.id
1778 1802 @request.session[:user_id] = 2
1779 1803
1780 1804 get :new, :project_id => 1
1781 1805 assert_response :success
1782 1806 assert_equal version, assigns(:issue).fixed_version
1783 1807 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
1784 1808 assert_select 'option[value=?][selected=selected]', version.id.to_s
1785 1809 end
1786 1810 end
1787 1811
1788 1812 def test_get_new_with_list_custom_field
1789 1813 @request.session[:user_id] = 2
1790 1814 get :new, :project_id => 1, :tracker_id => 1
1791 1815 assert_response :success
1792 1816 assert_template 'new'
1793 1817
1794 1818 assert_select 'select.list_cf[name=?]', 'issue[custom_field_values][1]' do
1795 1819 assert_select 'option', 4
1796 1820 assert_select 'option[value=MySQL]', :text => 'MySQL'
1797 1821 end
1798 1822 end
1799 1823
1800 1824 def test_get_new_with_multi_custom_field
1801 1825 field = IssueCustomField.find(1)
1802 1826 field.update_attribute :multiple, true
1803 1827
1804 1828 @request.session[:user_id] = 2
1805 1829 get :new, :project_id => 1, :tracker_id => 1
1806 1830 assert_response :success
1807 1831 assert_template 'new'
1808 1832
1809 1833 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
1810 1834 assert_select 'option', 3
1811 1835 assert_select 'option[value=MySQL]', :text => 'MySQL'
1812 1836 end
1813 1837 assert_select 'input[name=?][type=hidden][value=?]', 'issue[custom_field_values][1][]', ''
1814 1838 end
1815 1839
1816 1840 def test_get_new_with_multi_user_custom_field
1817 1841 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1818 1842 :tracker_ids => [1], :is_for_all => true)
1819 1843
1820 1844 @request.session[:user_id] = 2
1821 1845 get :new, :project_id => 1, :tracker_id => 1
1822 1846 assert_response :success
1823 1847 assert_template 'new'
1824 1848
1825 1849 assert_select 'select[name=?][multiple=multiple]', "issue[custom_field_values][#{field.id}][]" do
1826 1850 assert_select 'option', Project.find(1).users.count
1827 1851 assert_select 'option[value="2"]', :text => 'John Smith'
1828 1852 end
1829 1853 assert_select 'input[name=?][type=hidden][value=?]', "issue[custom_field_values][#{field.id}][]", ''
1830 1854 end
1831 1855
1832 1856 def test_get_new_with_date_custom_field
1833 1857 field = IssueCustomField.create!(:name => 'Date', :field_format => 'date', :tracker_ids => [1], :is_for_all => true)
1834 1858
1835 1859 @request.session[:user_id] = 2
1836 1860 get :new, :project_id => 1, :tracker_id => 1
1837 1861 assert_response :success
1838 1862
1839 1863 assert_select 'input[name=?]', "issue[custom_field_values][#{field.id}]"
1840 1864 end
1841 1865
1842 1866 def test_get_new_with_text_custom_field
1843 1867 field = IssueCustomField.create!(:name => 'Text', :field_format => 'text', :tracker_ids => [1], :is_for_all => true)
1844 1868
1845 1869 @request.session[:user_id] = 2
1846 1870 get :new, :project_id => 1, :tracker_id => 1
1847 1871 assert_response :success
1848 1872
1849 1873 assert_select 'textarea[name=?]', "issue[custom_field_values][#{field.id}]"
1850 1874 end
1851 1875
1852 1876 def test_get_new_without_default_start_date_is_creation_date
1853 1877 with_settings :default_issue_start_date_to_creation_date => 0 do
1854 1878 @request.session[:user_id] = 2
1855 1879 get :new, :project_id => 1, :tracker_id => 1
1856 1880 assert_response :success
1857 1881 assert_template 'new'
1858 1882 assert_select 'input[name=?]', 'issue[start_date]'
1859 1883 assert_select 'input[name=?][value]', 'issue[start_date]', 0
1860 1884 end
1861 1885 end
1862 1886
1863 1887 def test_get_new_with_default_start_date_is_creation_date
1864 1888 with_settings :default_issue_start_date_to_creation_date => 1 do
1865 1889 @request.session[:user_id] = 2
1866 1890 get :new, :project_id => 1, :tracker_id => 1
1867 1891 assert_response :success
1868 1892 assert_template 'new'
1869 1893 assert_select 'input[name=?][value=?]', 'issue[start_date]',
1870 1894 Date.today.to_s
1871 1895 end
1872 1896 end
1873 1897
1874 1898 def test_get_new_form_should_allow_attachment_upload
1875 1899 @request.session[:user_id] = 2
1876 1900 get :new, :project_id => 1, :tracker_id => 1
1877 1901
1878 1902 assert_select 'form[id=issue-form][method=post][enctype="multipart/form-data"]' do
1879 1903 assert_select 'input[name=?][type=file]', 'attachments[dummy][file]'
1880 1904 end
1881 1905 end
1882 1906
1883 1907 def test_get_new_should_prefill_the_form_from_params
1884 1908 @request.session[:user_id] = 2
1885 1909 get :new, :project_id => 1,
1886 1910 :issue => {:tracker_id => 3, :description => 'Prefilled', :custom_field_values => {'2' => 'Custom field value'}}
1887 1911
1888 1912 issue = assigns(:issue)
1889 1913 assert_equal 3, issue.tracker_id
1890 1914 assert_equal 'Prefilled', issue.description
1891 1915 assert_equal 'Custom field value', issue.custom_field_value(2)
1892 1916
1893 1917 assert_select 'select[name=?]', 'issue[tracker_id]' do
1894 1918 assert_select 'option[value="3"][selected=selected]'
1895 1919 end
1896 1920 assert_select 'textarea[name=?]', 'issue[description]', :text => /Prefilled/
1897 1921 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Custom field value'
1898 1922 end
1899 1923
1900 1924 def test_get_new_should_mark_required_fields
1901 1925 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1902 1926 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1903 1927 WorkflowPermission.delete_all
1904 1928 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
1905 1929 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
1906 1930 @request.session[:user_id] = 2
1907 1931
1908 1932 get :new, :project_id => 1
1909 1933 assert_response :success
1910 1934 assert_template 'new'
1911 1935
1912 1936 assert_select 'label[for=issue_start_date]' do
1913 1937 assert_select 'span[class=required]', 0
1914 1938 end
1915 1939 assert_select 'label[for=issue_due_date]' do
1916 1940 assert_select 'span[class=required]'
1917 1941 end
1918 1942 assert_select 'label[for=?]', "issue_custom_field_values_#{cf1.id}" do
1919 1943 assert_select 'span[class=required]', 0
1920 1944 end
1921 1945 assert_select 'label[for=?]', "issue_custom_field_values_#{cf2.id}" do
1922 1946 assert_select 'span[class=required]'
1923 1947 end
1924 1948 end
1925 1949
1926 1950 def test_get_new_should_not_display_readonly_fields
1927 1951 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1928 1952 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1929 1953 WorkflowPermission.delete_all
1930 1954 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
1931 1955 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
1932 1956 @request.session[:user_id] = 2
1933 1957
1934 1958 get :new, :project_id => 1
1935 1959 assert_response :success
1936 1960 assert_template 'new'
1937 1961
1938 1962 assert_select 'input[name=?]', 'issue[start_date]'
1939 1963 assert_select 'input[name=?]', 'issue[due_date]', 0
1940 1964 assert_select 'input[name=?]', "issue[custom_field_values][#{cf1.id}]"
1941 1965 assert_select 'input[name=?]', "issue[custom_field_values][#{cf2.id}]", 0
1942 1966 end
1943 1967
1944 1968 def test_new_with_tracker_set_as_readonly_should_accept_status
1945 1969 WorkflowPermission.delete_all
1946 1970 [1, 2].each do |status_id|
1947 1971 WorkflowPermission.create!(:tracker_id => 1, :old_status_id => status_id, :role_id => 1, :field_name => 'tracker_id', :rule => 'readonly')
1948 1972 end
1949 1973 @request.session[:user_id] = 2
1950 1974
1951 1975 get :new, :project_id => 1, :issue => {:status_id => 2}
1952 1976 assert_select 'select[name=?]', 'issue[tracker_id]', 0
1953 1977 assert_equal 2, assigns(:issue).status_id
1954 1978 end
1955 1979
1956 1980 def test_get_new_without_tracker_id
1957 1981 @request.session[:user_id] = 2
1958 1982 get :new, :project_id => 1
1959 1983 assert_response :success
1960 1984 assert_template 'new'
1961 1985
1962 1986 issue = assigns(:issue)
1963 1987 assert_not_nil issue
1964 1988 assert_equal Project.find(1).trackers.first, issue.tracker
1965 1989 end
1966 1990
1967 1991 def test_get_new_with_no_default_status_should_display_an_error
1968 1992 @request.session[:user_id] = 2
1969 1993 IssueStatus.delete_all
1970 1994
1971 1995 get :new, :project_id => 1
1972 1996 assert_response 500
1973 1997 assert_select_error /No default issue/
1974 1998 end
1975 1999
1976 2000 def test_get_new_with_no_tracker_should_display_an_error
1977 2001 @request.session[:user_id] = 2
1978 2002 Tracker.delete_all
1979 2003
1980 2004 get :new, :project_id => 1
1981 2005 assert_response 500
1982 2006 assert_select_error /No tracker/
1983 2007 end
1984 2008
1985 2009 def test_new_with_invalid_project_id
1986 2010 @request.session[:user_id] = 1
1987 2011 get :new, :project_id => 'invalid'
1988 2012 assert_response 404
1989 2013 end
1990 2014
1991 2015 def test_update_form_for_new_issue
1992 2016 @request.session[:user_id] = 2
1993 2017 xhr :post, :new, :project_id => 1,
1994 2018 :issue => {:tracker_id => 2,
1995 2019 :subject => 'This is the test_new issue',
1996 2020 :description => 'This is the description',
1997 2021 :priority_id => 5}
1998 2022 assert_response :success
1999 2023 assert_template 'new'
2000 2024 assert_template :partial => '_form'
2001 2025 assert_equal 'text/javascript', response.content_type
2002 2026
2003 2027 issue = assigns(:issue)
2004 2028 assert_kind_of Issue, issue
2005 2029 assert_equal 1, issue.project_id
2006 2030 assert_equal 2, issue.tracker_id
2007 2031 assert_equal 'This is the test_new issue', issue.subject
2008 2032 end
2009 2033
2010 2034 def test_update_form_for_new_issue_should_propose_transitions_based_on_initial_status
2011 2035 @request.session[:user_id] = 2
2012 2036 WorkflowTransition.delete_all
2013 2037 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 2)
2014 2038 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 5)
2015 2039 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4)
2016 2040
2017 2041 xhr :post, :new, :project_id => 1,
2018 2042 :issue => {:tracker_id => 1,
2019 2043 :status_id => 5,
2020 2044 :subject => 'This is an issue'}
2021 2045
2022 2046 assert_equal 5, assigns(:issue).status_id
2023 2047 assert_equal [2,5], assigns(:allowed_statuses).map(&:id).sort
2024 2048 end
2025 2049
2026 2050 def test_update_form_with_default_status_should_ignore_submitted_status_id_if_equals
2027 2051 @request.session[:user_id] = 2
2028 2052 tracker = Tracker.find(2)
2029 2053 tracker.update! :default_status_id => 2
2030 2054 tracker.generate_transitions! 2, 1, :clear => true
2031 2055
2032 2056 xhr :post, :new, :project_id => 1,
2033 2057 :issue => {:tracker_id => 2,
2034 2058 :status_id => 1},
2035 2059 :was_default_status => 1
2036 2060
2037 2061 assert_equal 2, assigns(:issue).status_id
2038 2062 end
2039 2063
2040 2064 def test_update_form_for_new_issue_should_ignore_version_when_changing_project
2041 2065 version = Version.generate!(:project_id => 1)
2042 2066 Project.find(1).update_attribute :default_version_id, version.id
2043 2067 @request.session[:user_id] = 2
2044 2068
2045 2069 xhr :post, :new, :issue => {:project_id => 1,
2046 2070 :fixed_version_id => ''},
2047 2071 :form_update_triggered_by => 'issue_project_id'
2048 2072 assert_response :success
2049 2073 assert_template 'new'
2050 2074
2051 2075 issue = assigns(:issue)
2052 2076 assert_equal 1, issue.project_id
2053 2077 assert_equal version, issue.fixed_version
2054 2078 end
2055 2079
2056 2080 def test_post_create
2057 2081 @request.session[:user_id] = 2
2058 2082 assert_difference 'Issue.count' do
2059 2083 assert_no_difference 'Journal.count' do
2060 2084 post :create, :project_id => 1,
2061 2085 :issue => {:tracker_id => 3,
2062 2086 :status_id => 2,
2063 2087 :subject => 'This is the test_new issue',
2064 2088 :description => 'This is the description',
2065 2089 :priority_id => 5,
2066 2090 :start_date => '2010-11-07',
2067 2091 :estimated_hours => '',
2068 2092 :custom_field_values => {'2' => 'Value for field 2'}}
2069 2093 end
2070 2094 end
2071 2095 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2072 2096
2073 2097 issue = Issue.find_by_subject('This is the test_new issue')
2074 2098 assert_not_nil issue
2075 2099 assert_equal 2, issue.author_id
2076 2100 assert_equal 3, issue.tracker_id
2077 2101 assert_equal 2, issue.status_id
2078 2102 assert_equal Date.parse('2010-11-07'), issue.start_date
2079 2103 assert_nil issue.estimated_hours
2080 2104 v = issue.custom_values.where(:custom_field_id => 2).first
2081 2105 assert_not_nil v
2082 2106 assert_equal 'Value for field 2', v.value
2083 2107 end
2084 2108
2085 2109 def test_post_new_with_group_assignment
2086 2110 group = Group.find(11)
2087 2111 project = Project.find(1)
2088 2112 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
2089 2113
2090 2114 with_settings :issue_group_assignment => '1' do
2091 2115 @request.session[:user_id] = 2
2092 2116 assert_difference 'Issue.count' do
2093 2117 post :create, :project_id => project.id,
2094 2118 :issue => {:tracker_id => 3,
2095 2119 :status_id => 1,
2096 2120 :subject => 'This is the test_new_with_group_assignment issue',
2097 2121 :assigned_to_id => group.id}
2098 2122 end
2099 2123 end
2100 2124 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2101 2125
2102 2126 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
2103 2127 assert_not_nil issue
2104 2128 assert_equal group, issue.assigned_to
2105 2129 end
2106 2130
2107 2131 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
2108 2132 with_settings :default_issue_start_date_to_creation_date => 0 do
2109 2133 @request.session[:user_id] = 2
2110 2134 assert_difference 'Issue.count' do
2111 2135 post :create, :project_id => 1,
2112 2136 :issue => {:tracker_id => 3,
2113 2137 :status_id => 2,
2114 2138 :subject => 'This is the test_new issue',
2115 2139 :description => 'This is the description',
2116 2140 :priority_id => 5,
2117 2141 :estimated_hours => '',
2118 2142 :custom_field_values => {'2' => 'Value for field 2'}}
2119 2143 end
2120 2144 assert_redirected_to :controller => 'issues', :action => 'show',
2121 2145 :id => Issue.last.id
2122 2146 issue = Issue.find_by_subject('This is the test_new issue')
2123 2147 assert_not_nil issue
2124 2148 assert_nil issue.start_date
2125 2149 end
2126 2150 end
2127 2151
2128 2152 def test_post_create_without_start_date_and_default_start_date_is_creation_date
2129 2153 with_settings :default_issue_start_date_to_creation_date => 1 do
2130 2154 @request.session[:user_id] = 2
2131 2155 assert_difference 'Issue.count' do
2132 2156 post :create, :project_id => 1,
2133 2157 :issue => {:tracker_id => 3,
2134 2158 :status_id => 2,
2135 2159 :subject => 'This is the test_new issue',
2136 2160 :description => 'This is the description',
2137 2161 :priority_id => 5,
2138 2162 :estimated_hours => '',
2139 2163 :custom_field_values => {'2' => 'Value for field 2'}}
2140 2164 end
2141 2165 assert_redirected_to :controller => 'issues', :action => 'show',
2142 2166 :id => Issue.last.id
2143 2167 issue = Issue.find_by_subject('This is the test_new issue')
2144 2168 assert_not_nil issue
2145 2169 assert_equal Date.today, issue.start_date
2146 2170 end
2147 2171 end
2148 2172
2149 2173 def test_post_create_and_continue
2150 2174 @request.session[:user_id] = 2
2151 2175 assert_difference 'Issue.count' do
2152 2176 post :create, :project_id => 1,
2153 2177 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
2154 2178 :continue => ''
2155 2179 end
2156 2180
2157 2181 issue = Issue.order('id DESC').first
2158 2182 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
2159 2183 assert_not_nil flash[:notice], "flash was not set"
2160 2184 assert_select_in flash[:notice],
2161 2185 'a[href=?][title=?]', "/issues/#{issue.id}", "This is first issue", :text => "##{issue.id}"
2162 2186 end
2163 2187
2164 2188 def test_post_create_without_custom_fields_param
2165 2189 @request.session[:user_id] = 2
2166 2190 assert_difference 'Issue.count' do
2167 2191 post :create, :project_id => 1,
2168 2192 :issue => {:tracker_id => 1,
2169 2193 :subject => 'This is the test_new issue',
2170 2194 :description => 'This is the description',
2171 2195 :priority_id => 5}
2172 2196 end
2173 2197 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2174 2198 end
2175 2199
2176 2200 def test_post_create_with_multi_custom_field
2177 2201 field = IssueCustomField.find_by_name('Database')
2178 2202 field.update_attribute(:multiple, true)
2179 2203
2180 2204 @request.session[:user_id] = 2
2181 2205 assert_difference 'Issue.count' do
2182 2206 post :create, :project_id => 1,
2183 2207 :issue => {:tracker_id => 1,
2184 2208 :subject => 'This is the test_new issue',
2185 2209 :description => 'This is the description',
2186 2210 :priority_id => 5,
2187 2211 :custom_field_values => {'1' => ['', 'MySQL', 'Oracle']}}
2188 2212 end
2189 2213 assert_response 302
2190 2214 issue = Issue.order('id DESC').first
2191 2215 assert_equal ['MySQL', 'Oracle'], issue.custom_field_value(1).sort
2192 2216 end
2193 2217
2194 2218 def test_post_create_with_empty_multi_custom_field
2195 2219 field = IssueCustomField.find_by_name('Database')
2196 2220 field.update_attribute(:multiple, true)
2197 2221
2198 2222 @request.session[:user_id] = 2
2199 2223 assert_difference 'Issue.count' do
2200 2224 post :create, :project_id => 1,
2201 2225 :issue => {:tracker_id => 1,
2202 2226 :subject => 'This is the test_new issue',
2203 2227 :description => 'This is the description',
2204 2228 :priority_id => 5,
2205 2229 :custom_field_values => {'1' => ['']}}
2206 2230 end
2207 2231 assert_response 302
2208 2232 issue = Issue.order('id DESC').first
2209 2233 assert_equal [''], issue.custom_field_value(1).sort
2210 2234 end
2211 2235
2212 2236 def test_post_create_with_multi_user_custom_field
2213 2237 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
2214 2238 :tracker_ids => [1], :is_for_all => true)
2215 2239
2216 2240 @request.session[:user_id] = 2
2217 2241 assert_difference 'Issue.count' do
2218 2242 post :create, :project_id => 1,
2219 2243 :issue => {:tracker_id => 1,
2220 2244 :subject => 'This is the test_new issue',
2221 2245 :description => 'This is the description',
2222 2246 :priority_id => 5,
2223 2247 :custom_field_values => {field.id.to_s => ['', '2', '3']}}
2224 2248 end
2225 2249 assert_response 302
2226 2250 issue = Issue.order('id DESC').first
2227 2251 assert_equal ['2', '3'], issue.custom_field_value(field).sort
2228 2252 end
2229 2253
2230 2254 def test_post_create_with_required_custom_field_and_without_custom_fields_param
2231 2255 field = IssueCustomField.find_by_name('Database')
2232 2256 field.update_attribute(:is_required, true)
2233 2257
2234 2258 @request.session[:user_id] = 2
2235 2259 assert_no_difference 'Issue.count' do
2236 2260 post :create, :project_id => 1,
2237 2261 :issue => {:tracker_id => 1,
2238 2262 :subject => 'This is the test_new issue',
2239 2263 :description => 'This is the description',
2240 2264 :priority_id => 5}
2241 2265 end
2242 2266 assert_response :success
2243 2267 assert_template 'new'
2244 2268 issue = assigns(:issue)
2245 2269 assert_not_nil issue
2246 2270 assert_select_error /Database cannot be blank/
2247 2271 end
2248 2272
2249 2273 def test_create_should_validate_required_fields
2250 2274 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2251 2275 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2252 2276 WorkflowPermission.delete_all
2253 2277 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'required')
2254 2278 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
2255 2279 @request.session[:user_id] = 2
2256 2280
2257 2281 assert_no_difference 'Issue.count' do
2258 2282 post :create, :project_id => 1, :issue => {
2259 2283 :tracker_id => 2,
2260 2284 :status_id => 1,
2261 2285 :subject => 'Test',
2262 2286 :start_date => '',
2263 2287 :due_date => '',
2264 2288 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ''}
2265 2289 }
2266 2290 assert_response :success
2267 2291 assert_template 'new'
2268 2292 end
2269 2293
2270 2294 assert_select_error /Due date cannot be blank/i
2271 2295 assert_select_error /Bar cannot be blank/i
2272 2296 end
2273 2297
2274 2298 def test_create_should_validate_required_list_fields
2275 2299 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'list', :is_for_all => true, :tracker_ids => [1, 2], :multiple => false, :possible_values => ['a', 'b'])
2276 2300 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'list', :is_for_all => true, :tracker_ids => [1, 2], :multiple => true, :possible_values => ['a', 'b'])
2277 2301 WorkflowPermission.delete_all
2278 2302 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf1.id.to_s, :rule => 'required')
2279 2303 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
2280 2304 @request.session[:user_id] = 2
2281 2305
2282 2306 assert_no_difference 'Issue.count' do
2283 2307 post :create, :project_id => 1, :issue => {
2284 2308 :tracker_id => 2,
2285 2309 :status_id => 1,
2286 2310 :subject => 'Test',
2287 2311 :start_date => '',
2288 2312 :due_date => '',
2289 2313 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ['']}
2290 2314 }
2291 2315 assert_response :success
2292 2316 assert_template 'new'
2293 2317 end
2294 2318
2295 2319 assert_select_error /Foo cannot be blank/i
2296 2320 assert_select_error /Bar cannot be blank/i
2297 2321 end
2298 2322
2299 2323 def test_create_should_ignore_readonly_fields
2300 2324 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2301 2325 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2302 2326 WorkflowPermission.delete_all
2303 2327 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
2304 2328 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
2305 2329 @request.session[:user_id] = 2
2306 2330
2307 2331 assert_difference 'Issue.count' do
2308 2332 post :create, :project_id => 1, :issue => {
2309 2333 :tracker_id => 2,
2310 2334 :status_id => 1,
2311 2335 :subject => 'Test',
2312 2336 :start_date => '2012-07-14',
2313 2337 :due_date => '2012-07-16',
2314 2338 :custom_field_values => {cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'}
2315 2339 }
2316 2340 assert_response 302
2317 2341 end
2318 2342
2319 2343 issue = Issue.order('id DESC').first
2320 2344 assert_equal Date.parse('2012-07-14'), issue.start_date
2321 2345 assert_nil issue.due_date
2322 2346 assert_equal 'value1', issue.custom_field_value(cf1)
2323 2347 assert_nil issue.custom_field_value(cf2)
2324 2348 end
2325 2349
2326 2350 def test_post_create_with_watchers
2327 2351 @request.session[:user_id] = 2
2328 2352 ActionMailer::Base.deliveries.clear
2329 2353
2330 2354 with_settings :notified_events => %w(issue_added) do
2331 2355 assert_difference 'Watcher.count', 2 do
2332 2356 post :create, :project_id => 1,
2333 2357 :issue => {:tracker_id => 1,
2334 2358 :subject => 'This is a new issue with watchers',
2335 2359 :description => 'This is the description',
2336 2360 :priority_id => 5,
2337 2361 :watcher_user_ids => ['2', '3']}
2338 2362 end
2339 2363 end
2340 2364 issue = Issue.find_by_subject('This is a new issue with watchers')
2341 2365 assert_not_nil issue
2342 2366 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2343 2367
2344 2368 # Watchers added
2345 2369 assert_equal [2, 3], issue.watcher_user_ids.sort
2346 2370 assert issue.watched_by?(User.find(3))
2347 2371 # Watchers notified
2348 2372 mail = ActionMailer::Base.deliveries.last
2349 2373 assert_not_nil mail
2350 2374 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
2351 2375 end
2352 2376
2353 2377 def test_post_create_subissue
2354 2378 @request.session[:user_id] = 2
2355 2379
2356 2380 assert_difference 'Issue.count' do
2357 2381 post :create, :project_id => 1,
2358 2382 :issue => {:tracker_id => 1,
2359 2383 :subject => 'This is a child issue',
2360 2384 :parent_issue_id => '2'}
2361 2385 assert_response 302
2362 2386 end
2363 2387 issue = Issue.order('id DESC').first
2364 2388 assert_equal Issue.find(2), issue.parent
2365 2389 end
2366 2390
2367 2391 def test_post_create_subissue_with_sharp_parent_id
2368 2392 @request.session[:user_id] = 2
2369 2393
2370 2394 assert_difference 'Issue.count' do
2371 2395 post :create, :project_id => 1,
2372 2396 :issue => {:tracker_id => 1,
2373 2397 :subject => 'This is a child issue',
2374 2398 :parent_issue_id => '#2'}
2375 2399 assert_response 302
2376 2400 end
2377 2401 issue = Issue.order('id DESC').first
2378 2402 assert_equal Issue.find(2), issue.parent
2379 2403 end
2380 2404
2381 2405 def test_post_create_subissue_with_non_visible_parent_id_should_not_validate
2382 2406 @request.session[:user_id] = 2
2383 2407
2384 2408 assert_no_difference 'Issue.count' do
2385 2409 post :create, :project_id => 1,
2386 2410 :issue => {:tracker_id => 1,
2387 2411 :subject => 'This is a child issue',
2388 2412 :parent_issue_id => '4'}
2389 2413
2390 2414 assert_response :success
2391 2415 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '4'
2392 2416 assert_select_error /Parent task is invalid/i
2393 2417 end
2394 2418 end
2395 2419
2396 2420 def test_post_create_subissue_with_non_numeric_parent_id_should_not_validate
2397 2421 @request.session[:user_id] = 2
2398 2422
2399 2423 assert_no_difference 'Issue.count' do
2400 2424 post :create, :project_id => 1,
2401 2425 :issue => {:tracker_id => 1,
2402 2426 :subject => 'This is a child issue',
2403 2427 :parent_issue_id => '01ABC'}
2404 2428
2405 2429 assert_response :success
2406 2430 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '01ABC'
2407 2431 assert_select_error /Parent task is invalid/i
2408 2432 end
2409 2433 end
2410 2434
2411 2435 def test_post_create_private
2412 2436 @request.session[:user_id] = 2
2413 2437
2414 2438 assert_difference 'Issue.count' do
2415 2439 post :create, :project_id => 1,
2416 2440 :issue => {:tracker_id => 1,
2417 2441 :subject => 'This is a private issue',
2418 2442 :is_private => '1'}
2419 2443 end
2420 2444 issue = Issue.order('id DESC').first
2421 2445 assert issue.is_private?
2422 2446 end
2423 2447
2424 2448 def test_post_create_private_with_set_own_issues_private_permission
2425 2449 role = Role.find(1)
2426 2450 role.remove_permission! :set_issues_private
2427 2451 role.add_permission! :set_own_issues_private
2428 2452
2429 2453 @request.session[:user_id] = 2
2430 2454
2431 2455 assert_difference 'Issue.count' do
2432 2456 post :create, :project_id => 1,
2433 2457 :issue => {:tracker_id => 1,
2434 2458 :subject => 'This is a private issue',
2435 2459 :is_private => '1'}
2436 2460 end
2437 2461 issue = Issue.order('id DESC').first
2438 2462 assert issue.is_private?
2439 2463 end
2440 2464
2441 2465 def test_create_without_project_id
2442 2466 @request.session[:user_id] = 2
2443 2467
2444 2468 assert_difference 'Issue.count' do
2445 2469 post :create,
2446 2470 :issue => {:project_id => 3,
2447 2471 :tracker_id => 2,
2448 2472 :subject => 'Foo'}
2449 2473 assert_response 302
2450 2474 end
2451 2475 issue = Issue.order('id DESC').first
2452 2476 assert_equal 3, issue.project_id
2453 2477 assert_equal 2, issue.tracker_id
2454 2478 end
2455 2479
2456 2480 def test_create_without_project_id_and_continue_should_redirect_without_project_id
2457 2481 @request.session[:user_id] = 2
2458 2482
2459 2483 assert_difference 'Issue.count' do
2460 2484 post :create,
2461 2485 :issue => {:project_id => 3,
2462 2486 :tracker_id => 2,
2463 2487 :subject => 'Foo'},
2464 2488 :continue => '1'
2465 2489 assert_redirected_to '/issues/new?issue%5Bproject_id%5D=3&issue%5Btracker_id%5D=2'
2466 2490 end
2467 2491 end
2468 2492
2469 2493 def test_create_without_project_id_should_be_denied_without_permission
2470 2494 Role.non_member.remove_permission! :add_issues
2471 2495 Role.anonymous.remove_permission! :add_issues
2472 2496 @request.session[:user_id] = 2
2473 2497
2474 2498 assert_no_difference 'Issue.count' do
2475 2499 post :create,
2476 2500 :issue => {:project_id => 3,
2477 2501 :tracker_id => 2,
2478 2502 :subject => 'Foo'}
2479 2503 assert_response 422
2480 2504 end
2481 2505 end
2482 2506
2483 2507 def test_create_without_project_id_with_failure
2484 2508 @request.session[:user_id] = 2
2485 2509
2486 2510 post :create,
2487 2511 :issue => {:project_id => 3,
2488 2512 :tracker_id => 2,
2489 2513 :subject => ''}
2490 2514 assert_response :success
2491 2515 assert_nil assigns(:project)
2492 2516 end
2493 2517
2494 2518 def test_post_create_should_send_a_notification
2495 2519 ActionMailer::Base.deliveries.clear
2496 2520 @request.session[:user_id] = 2
2497 2521 with_settings :notified_events => %w(issue_added) do
2498 2522 assert_difference 'Issue.count' do
2499 2523 post :create, :project_id => 1,
2500 2524 :issue => {:tracker_id => 3,
2501 2525 :subject => 'This is the test_new issue',
2502 2526 :description => 'This is the description',
2503 2527 :priority_id => 5,
2504 2528 :estimated_hours => '',
2505 2529 :custom_field_values => {'2' => 'Value for field 2'}}
2506 2530 end
2507 2531 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2508 2532
2509 2533 assert_equal 1, ActionMailer::Base.deliveries.size
2510 2534 end
2511 2535 end
2512 2536
2513 2537 def test_post_create_should_preserve_fields_values_on_validation_failure
2514 2538 @request.session[:user_id] = 2
2515 2539 post :create, :project_id => 1,
2516 2540 :issue => {:tracker_id => 1,
2517 2541 # empty subject
2518 2542 :subject => '',
2519 2543 :description => 'This is a description',
2520 2544 :priority_id => 6,
2521 2545 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
2522 2546 assert_response :success
2523 2547 assert_template 'new'
2524 2548
2525 2549 assert_select 'textarea[name=?]', 'issue[description]', :text => 'This is a description'
2526 2550 assert_select 'select[name=?]', 'issue[priority_id]' do
2527 2551 assert_select 'option[value="6"][selected=selected]', :text => 'High'
2528 2552 end
2529 2553 # Custom fields
2530 2554 assert_select 'select[name=?]', 'issue[custom_field_values][1]' do
2531 2555 assert_select 'option[value=Oracle][selected=selected]', :text => 'Oracle'
2532 2556 end
2533 2557 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Value for field 2'
2534 2558 end
2535 2559
2536 2560 def test_post_create_with_failure_should_preserve_watchers
2537 2561 assert !User.find(8).member_of?(Project.find(1))
2538 2562
2539 2563 @request.session[:user_id] = 2
2540 2564 post :create, :project_id => 1,
2541 2565 :issue => {:tracker_id => 1,
2542 2566 :watcher_user_ids => ['3', '8']}
2543 2567 assert_response :success
2544 2568 assert_template 'new'
2545 2569
2546 2570 assert_select 'input[name=?][value="2"]:not(checked)', 'issue[watcher_user_ids][]'
2547 2571 assert_select 'input[name=?][value="3"][checked=checked]', 'issue[watcher_user_ids][]'
2548 2572 assert_select 'input[name=?][value="8"][checked=checked]', 'issue[watcher_user_ids][]'
2549 2573 end
2550 2574
2551 2575 def test_post_create_should_ignore_non_safe_attributes
2552 2576 @request.session[:user_id] = 2
2553 2577 assert_nothing_raised do
2554 2578 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
2555 2579 end
2556 2580 end
2557 2581
2558 2582 def test_post_create_with_attachment
2559 2583 set_tmp_attachments_directory
2560 2584 @request.session[:user_id] = 2
2561 2585
2562 2586 assert_difference 'Issue.count' do
2563 2587 assert_difference 'Attachment.count' do
2564 2588 assert_no_difference 'Journal.count' do
2565 2589 post :create, :project_id => 1,
2566 2590 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2567 2591 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2568 2592 end
2569 2593 end
2570 2594 end
2571 2595
2572 2596 issue = Issue.order('id DESC').first
2573 2597 attachment = Attachment.order('id DESC').first
2574 2598
2575 2599 assert_equal issue, attachment.container
2576 2600 assert_equal 2, attachment.author_id
2577 2601 assert_equal 'testfile.txt', attachment.filename
2578 2602 assert_equal 'text/plain', attachment.content_type
2579 2603 assert_equal 'test file', attachment.description
2580 2604 assert_equal 59, attachment.filesize
2581 2605 assert File.exists?(attachment.diskfile)
2582 2606 assert_equal 59, File.size(attachment.diskfile)
2583 2607 end
2584 2608
2585 2609 def test_post_create_with_attachment_should_notify_with_attachments
2586 2610 ActionMailer::Base.deliveries.clear
2587 2611 set_tmp_attachments_directory
2588 2612 @request.session[:user_id] = 2
2589 2613
2590 2614 with_settings :notified_events => %w(issue_added) do
2591 2615 assert_difference 'Issue.count' do
2592 2616 post :create, :project_id => 1,
2593 2617 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2594 2618 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2595 2619 end
2596 2620 end
2597 2621
2598 2622 assert_not_nil ActionMailer::Base.deliveries.last
2599 2623 assert_select_email do
2600 2624 assert_select 'a[href^=?]', 'http://localhost:3000/attachments/download', 'testfile.txt'
2601 2625 end
2602 2626 end
2603 2627
2604 2628 def test_post_create_with_failure_should_save_attachments
2605 2629 set_tmp_attachments_directory
2606 2630 @request.session[:user_id] = 2
2607 2631
2608 2632 assert_no_difference 'Issue.count' do
2609 2633 assert_difference 'Attachment.count' do
2610 2634 post :create, :project_id => 1,
2611 2635 :issue => { :tracker_id => '1', :subject => '' },
2612 2636 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2613 2637 assert_response :success
2614 2638 assert_template 'new'
2615 2639 end
2616 2640 end
2617 2641
2618 2642 attachment = Attachment.order('id DESC').first
2619 2643 assert_equal 'testfile.txt', attachment.filename
2620 2644 assert File.exists?(attachment.diskfile)
2621 2645 assert_nil attachment.container
2622 2646
2623 2647 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
2624 2648 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
2625 2649 end
2626 2650
2627 2651 def test_post_create_with_failure_should_keep_saved_attachments
2628 2652 set_tmp_attachments_directory
2629 2653 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2630 2654 @request.session[:user_id] = 2
2631 2655
2632 2656 assert_no_difference 'Issue.count' do
2633 2657 assert_no_difference 'Attachment.count' do
2634 2658 post :create, :project_id => 1,
2635 2659 :issue => { :tracker_id => '1', :subject => '' },
2636 2660 :attachments => {'p0' => {'token' => attachment.token}}
2637 2661 assert_response :success
2638 2662 assert_template 'new'
2639 2663 end
2640 2664 end
2641 2665
2642 2666 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
2643 2667 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
2644 2668 end
2645 2669
2646 2670 def test_post_create_should_attach_saved_attachments
2647 2671 set_tmp_attachments_directory
2648 2672 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2649 2673 @request.session[:user_id] = 2
2650 2674
2651 2675 assert_difference 'Issue.count' do
2652 2676 assert_no_difference 'Attachment.count' do
2653 2677 post :create, :project_id => 1,
2654 2678 :issue => { :tracker_id => '1', :subject => 'Saved attachments' },
2655 2679 :attachments => {'p0' => {'token' => attachment.token}}
2656 2680 assert_response 302
2657 2681 end
2658 2682 end
2659 2683
2660 2684 issue = Issue.order('id DESC').first
2661 2685 assert_equal 1, issue.attachments.count
2662 2686
2663 2687 attachment.reload
2664 2688 assert_equal issue, attachment.container
2665 2689 end
2666 2690
2667 2691 def setup_without_workflow_privilege
2668 2692 WorkflowTransition.delete_all(["role_id = ?", Role.anonymous.id])
2669 2693 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2670 2694 end
2671 2695 private :setup_without_workflow_privilege
2672 2696
2673 2697 test "without workflow privilege #new should propose default status only" do
2674 2698 setup_without_workflow_privilege
2675 2699 get :new, :project_id => 1
2676 2700 assert_response :success
2677 2701 assert_template 'new'
2678 2702
2679 2703 issue = assigns(:issue)
2680 2704 assert_not_nil issue.default_status
2681 2705
2682 2706 assert_select 'select[name=?]', 'issue[status_id]' do
2683 2707 assert_select 'option', 1
2684 2708 assert_select 'option[value=?]', issue.default_status.id.to_s
2685 2709 end
2686 2710 end
2687 2711
2688 2712 test "without workflow privilege #create should accept default status" do
2689 2713 setup_without_workflow_privilege
2690 2714 assert_difference 'Issue.count' do
2691 2715 post :create, :project_id => 1,
2692 2716 :issue => {:tracker_id => 1,
2693 2717 :subject => 'This is an issue',
2694 2718 :status_id => 1}
2695 2719 end
2696 2720 issue = Issue.order('id').last
2697 2721 assert_not_nil issue.default_status
2698 2722 assert_equal issue.default_status, issue.status
2699 2723 end
2700 2724
2701 2725 test "without workflow privilege #create should ignore unauthorized status" do
2702 2726 setup_without_workflow_privilege
2703 2727 assert_difference 'Issue.count' do
2704 2728 post :create, :project_id => 1,
2705 2729 :issue => {:tracker_id => 1,
2706 2730 :subject => 'This is an issue',
2707 2731 :status_id => 3}
2708 2732 end
2709 2733 issue = Issue.order('id').last
2710 2734 assert_not_nil issue.default_status
2711 2735 assert_equal issue.default_status, issue.status
2712 2736 end
2713 2737
2714 2738 test "without workflow privilege #update should ignore status change" do
2715 2739 setup_without_workflow_privilege
2716 2740 assert_difference 'Journal.count' do
2717 2741 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2718 2742 end
2719 2743 assert_equal 1, Issue.find(1).status_id
2720 2744 end
2721 2745
2722 2746 test "without workflow privilege #update ignore attributes changes" do
2723 2747 setup_without_workflow_privilege
2724 2748 assert_difference 'Journal.count' do
2725 2749 put :update, :id => 1,
2726 2750 :issue => {:subject => 'changed', :assigned_to_id => 2,
2727 2751 :notes => 'just trying'}
2728 2752 end
2729 2753 issue = Issue.find(1)
2730 2754 assert_equal "Cannot print recipes", issue.subject
2731 2755 assert_nil issue.assigned_to
2732 2756 end
2733 2757
2734 2758 def setup_with_workflow_privilege
2735 2759 WorkflowTransition.delete_all(["role_id = ?", Role.anonymous.id])
2736 2760 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
2737 2761 :old_status_id => 1, :new_status_id => 3)
2738 2762 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
2739 2763 :old_status_id => 1, :new_status_id => 4)
2740 2764 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2741 2765 end
2742 2766 private :setup_with_workflow_privilege
2743 2767
2744 2768 def setup_with_workflow_privilege_and_edit_issues_permission
2745 2769 setup_with_workflow_privilege
2746 2770 Role.anonymous.add_permission! :add_issues, :edit_issues
2747 2771 end
2748 2772 private :setup_with_workflow_privilege_and_edit_issues_permission
2749 2773
2750 2774 test "with workflow privilege and :edit_issues permission should accept authorized status" do
2751 2775 setup_with_workflow_privilege_and_edit_issues_permission
2752 2776 assert_difference 'Journal.count' do
2753 2777 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2754 2778 end
2755 2779 assert_equal 3, Issue.find(1).status_id
2756 2780 end
2757 2781
2758 2782 test "with workflow privilege and :edit_issues permission should ignore unauthorized status" do
2759 2783 setup_with_workflow_privilege_and_edit_issues_permission
2760 2784 assert_difference 'Journal.count' do
2761 2785 put :update, :id => 1, :issue => {:status_id => 2, :notes => 'just trying'}
2762 2786 end
2763 2787 assert_equal 1, Issue.find(1).status_id
2764 2788 end
2765 2789
2766 2790 test "with workflow privilege and :edit_issues permission should accept authorized attributes changes" do
2767 2791 setup_with_workflow_privilege_and_edit_issues_permission
2768 2792 assert_difference 'Journal.count' do
2769 2793 put :update, :id => 1,
2770 2794 :issue => {:subject => 'changed', :assigned_to_id => 2,
2771 2795 :notes => 'just trying'}
2772 2796 end
2773 2797 issue = Issue.find(1)
2774 2798 assert_equal "changed", issue.subject
2775 2799 assert_equal 2, issue.assigned_to_id
2776 2800 end
2777 2801
2778 2802 def test_new_as_copy
2779 2803 @request.session[:user_id] = 2
2780 2804 get :new, :project_id => 1, :copy_from => 1
2781 2805
2782 2806 assert_response :success
2783 2807 assert_template 'new'
2784 2808
2785 2809 assert_not_nil assigns(:issue)
2786 2810 orig = Issue.find(1)
2787 2811 assert_equal 1, assigns(:issue).project_id
2788 2812 assert_equal orig.subject, assigns(:issue).subject
2789 2813 assert assigns(:issue).copy?
2790 2814
2791 2815 assert_select 'form[id=issue-form][action="/projects/ecookbook/issues"]' do
2792 2816 assert_select 'select[name=?]', 'issue[project_id]' do
2793 2817 assert_select 'option[value="1"][selected=selected]', :text => 'eCookbook'
2794 2818 assert_select 'option[value="2"]:not([selected])', :text => 'OnlineStore'
2795 2819 end
2796 2820 assert_select 'input[name=copy_from][value="1"]'
2797 2821 end
2798
2799 # "New issue" menu item should not link to copy
2800 assert_select '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]'
2801 2822 end
2802 2823
2803 2824 def test_new_as_copy_without_add_issues_permission_should_not_propose_current_project_as_target
2804 2825 user = setup_user_with_copy_but_not_add_permission
2805 2826
2806 2827 @request.session[:user_id] = user.id
2807 2828 get :new, :project_id => 1, :copy_from => 1
2808 2829
2809 2830 assert_response :success
2810 2831 assert_template 'new'
2811 2832 assert_select 'select[name=?]', 'issue[project_id]' do
2812 2833 assert_select 'option[value="1"]', 0
2813 2834 assert_select 'option[value="2"]', :text => 'OnlineStore'
2814 2835 end
2815 2836 end
2816 2837
2817 2838 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
2818 2839 @request.session[:user_id] = 2
2819 2840 issue = Issue.find(3)
2820 2841 assert issue.attachments.count > 0
2821 2842 get :new, :project_id => 1, :copy_from => 3
2822 2843
2823 2844 assert_select 'input[name=copy_attachments][type=checkbox][checked=checked][value="1"]'
2824 2845 end
2825 2846
2826 2847 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
2827 2848 @request.session[:user_id] = 2
2828 2849 issue = Issue.find(3)
2829 2850 issue.attachments.delete_all
2830 2851 get :new, :project_id => 1, :copy_from => 3
2831 2852
2832 2853 assert_select 'input[name=copy_attachments]', 0
2833 2854 end
2834 2855
2835 2856 def test_new_as_copy_should_preserve_parent_id
2836 2857 @request.session[:user_id] = 2
2837 2858 issue = Issue.generate!(:parent_issue_id => 2)
2838 2859 get :new, :project_id => 1, :copy_from => issue.id
2839 2860
2840 2861 assert_select 'input[name=?][value="2"]', 'issue[parent_issue_id]'
2841 2862 end
2842 2863
2843 2864 def test_new_as_copy_with_subtasks_should_show_copy_subtasks_checkbox
2844 2865 @request.session[:user_id] = 2
2845 2866 issue = Issue.generate_with_descendants!
2846 2867 get :new, :project_id => 1, :copy_from => issue.id
2847 2868
2848 2869 assert_select 'input[type=checkbox][name=copy_subtasks][checked=checked][value="1"]'
2849 2870 end
2850 2871
2851 2872 def test_new_as_copy_with_invalid_issue_should_respond_with_404
2852 2873 @request.session[:user_id] = 2
2853 2874 get :new, :project_id => 1, :copy_from => 99999
2854 2875 assert_response 404
2855 2876 end
2856 2877
2857 2878 def test_create_as_copy_on_different_project
2858 2879 @request.session[:user_id] = 2
2859 2880 assert_difference 'Issue.count' do
2860 2881 post :create, :project_id => 1, :copy_from => 1,
2861 2882 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
2862 2883
2863 2884 assert_not_nil assigns(:issue)
2864 2885 assert assigns(:issue).copy?
2865 2886 end
2866 2887 issue = Issue.order('id DESC').first
2867 2888 assert_redirected_to "/issues/#{issue.id}"
2868 2889
2869 2890 assert_equal 2, issue.project_id
2870 2891 assert_equal 3, issue.tracker_id
2871 2892 assert_equal 'Copy', issue.subject
2872 2893 end
2873 2894
2874 2895 def test_create_as_copy_should_allow_status_to_be_set_to_default
2875 2896 copied = Issue.generate! :status_id => 2
2876 2897 assert_equal 2, copied.reload.status_id
2877 2898
2878 2899 @request.session[:user_id] = 2
2879 2900 assert_difference 'Issue.count' do
2880 2901 post :create, :project_id => 1, :copy_from => copied.id,
2881 2902 :issue => {:project_id => '1', :tracker_id => '1', :status_id => '1'},
2882 2903 :was_default_status => '1'
2883 2904 end
2884 2905 issue = Issue.order('id DESC').first
2885 2906 assert_equal 1, issue.status_id
2886 2907 end
2887 2908
2888 2909 def test_create_as_copy_should_copy_attachments
2889 2910 @request.session[:user_id] = 2
2890 2911 issue = Issue.find(3)
2891 2912 count = issue.attachments.count
2892 2913 assert count > 0
2893 2914 assert_difference 'Issue.count' do
2894 2915 assert_difference 'Attachment.count', count do
2895 2916 post :create, :project_id => 1, :copy_from => 3,
2896 2917 :issue => {:project_id => '1', :tracker_id => '3',
2897 2918 :status_id => '1', :subject => 'Copy with attachments'},
2898 2919 :copy_attachments => '1'
2899 2920 end
2900 2921 end
2901 2922 copy = Issue.order('id DESC').first
2902 2923 assert_equal count, copy.attachments.count
2903 2924 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
2904 2925 end
2905 2926
2906 2927 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
2907 2928 @request.session[:user_id] = 2
2908 2929 issue = Issue.find(3)
2909 2930 count = issue.attachments.count
2910 2931 assert count > 0
2911 2932 assert_difference 'Issue.count' do
2912 2933 assert_no_difference 'Attachment.count' do
2913 2934 post :create, :project_id => 1, :copy_from => 3,
2914 2935 :issue => {:project_id => '1', :tracker_id => '3',
2915 2936 :status_id => '1', :subject => 'Copy with attachments'}
2916 2937 end
2917 2938 end
2918 2939 copy = Issue.order('id DESC').first
2919 2940 assert_equal 0, copy.attachments.count
2920 2941 end
2921 2942
2922 2943 def test_create_as_copy_with_attachments_should_also_add_new_files
2923 2944 @request.session[:user_id] = 2
2924 2945 issue = Issue.find(3)
2925 2946 count = issue.attachments.count
2926 2947 assert count > 0
2927 2948 assert_difference 'Issue.count' do
2928 2949 assert_difference 'Attachment.count', count + 1 do
2929 2950 post :create, :project_id => 1, :copy_from => 3,
2930 2951 :issue => {:project_id => '1', :tracker_id => '3',
2931 2952 :status_id => '1', :subject => 'Copy with attachments'},
2932 2953 :copy_attachments => '1',
2933 2954 :attachments => {'1' =>
2934 2955 {'file' => uploaded_test_file('testfile.txt', 'text/plain'),
2935 2956 'description' => 'test file'}}
2936 2957 end
2937 2958 end
2938 2959 copy = Issue.order('id DESC').first
2939 2960 assert_equal count + 1, copy.attachments.count
2940 2961 end
2941 2962
2942 2963 def test_create_as_copy_should_add_relation_with_copied_issue
2943 2964 @request.session[:user_id] = 2
2944 2965 assert_difference 'Issue.count' do
2945 2966 assert_difference 'IssueRelation.count' do
2946 2967 post :create, :project_id => 1, :copy_from => 1, :link_copy => '1',
2947 2968 :issue => {:project_id => '1', :tracker_id => '3',
2948 2969 :status_id => '1', :subject => 'Copy'}
2949 2970 end
2950 2971 end
2951 2972 copy = Issue.order('id DESC').first
2952 2973 assert_equal 1, copy.relations.size
2953 2974 end
2954 2975
2955 2976 def test_create_as_copy_should_allow_not_to_add_relation_with_copied_issue
2956 2977 @request.session[:user_id] = 2
2957 2978 assert_difference 'Issue.count' do
2958 2979 assert_no_difference 'IssueRelation.count' do
2959 2980 post :create, :project_id => 1, :copy_from => 1,
2960 2981 :issue => {:subject => 'Copy'}
2961 2982 end
2962 2983 end
2963 2984 end
2964 2985
2965 2986 def test_create_as_copy_should_always_add_relation_with_copied_issue_by_setting
2966 2987 with_settings :link_copied_issue => 'yes' do
2967 2988 @request.session[:user_id] = 2
2968 2989 assert_difference 'Issue.count' do
2969 2990 assert_difference 'IssueRelation.count' do
2970 2991 post :create, :project_id => 1, :copy_from => 1,
2971 2992 :issue => {:subject => 'Copy'}
2972 2993 end
2973 2994 end
2974 2995 end
2975 2996 end
2976 2997
2977 2998 def test_create_as_copy_should_never_add_relation_with_copied_issue_by_setting
2978 2999 with_settings :link_copied_issue => 'no' do
2979 3000 @request.session[:user_id] = 2
2980 3001 assert_difference 'Issue.count' do
2981 3002 assert_no_difference 'IssueRelation.count' do
2982 3003 post :create, :project_id => 1, :copy_from => 1, :link_copy => '1',
2983 3004 :issue => {:subject => 'Copy'}
2984 3005 end
2985 3006 end
2986 3007 end
2987 3008 end
2988 3009
2989 3010 def test_create_as_copy_should_copy_subtasks
2990 3011 @request.session[:user_id] = 2
2991 3012 issue = Issue.generate_with_descendants!
2992 3013 count = issue.descendants.count
2993 3014 assert_difference 'Issue.count', count + 1 do
2994 3015 post :create, :project_id => 1, :copy_from => issue.id,
2995 3016 :issue => {:project_id => '1', :tracker_id => '3',
2996 3017 :status_id => '1', :subject => 'Copy with subtasks'},
2997 3018 :copy_subtasks => '1'
2998 3019 end
2999 3020 copy = Issue.where(:parent_id => nil).order('id DESC').first
3000 3021 assert_equal count, copy.descendants.count
3001 3022 assert_equal issue.descendants.map(&:subject).sort, copy.descendants.map(&:subject).sort
3002 3023 end
3003 3024
3004 3025 def test_create_as_copy_to_a_different_project_should_copy_subtask_custom_fields
3005 3026 issue = Issue.generate! {|i| i.custom_field_values = {'2' => 'Foo'}}
3006 3027 child = Issue.generate!(:parent_issue_id => issue.id) {|i| i.custom_field_values = {'2' => 'Bar'}}
3007 3028 @request.session[:user_id] = 1
3008 3029
3009 3030 assert_difference 'Issue.count', 2 do
3010 3031 post :create, :project_id => 'ecookbook', :copy_from => issue.id,
3011 3032 :issue => {:project_id => '2', :tracker_id => 1, :status_id => '1',
3012 3033 :subject => 'Copy with subtasks', :custom_field_values => {'2' => 'Foo'}},
3013 3034 :copy_subtasks => '1'
3014 3035 end
3015 3036
3016 3037 child_copy, issue_copy = Issue.order(:id => :desc).limit(2).to_a
3017 3038 assert_equal 2, issue_copy.project_id
3018 3039 assert_equal 'Foo', issue_copy.custom_field_value(2)
3019 3040 assert_equal 'Bar', child_copy.custom_field_value(2)
3020 3041 end
3021 3042
3022 3043 def test_create_as_copy_without_copy_subtasks_option_should_not_copy_subtasks
3023 3044 @request.session[:user_id] = 2
3024 3045 issue = Issue.generate_with_descendants!
3025 3046 assert_difference 'Issue.count', 1 do
3026 3047 post :create, :project_id => 1, :copy_from => 3,
3027 3048 :issue => {:project_id => '1', :tracker_id => '3',
3028 3049 :status_id => '1', :subject => 'Copy with subtasks'}
3029 3050 end
3030 3051 copy = Issue.where(:parent_id => nil).order('id DESC').first
3031 3052 assert_equal 0, copy.descendants.count
3032 3053 end
3033 3054
3034 3055 def test_create_as_copy_with_failure
3035 3056 @request.session[:user_id] = 2
3036 3057 post :create, :project_id => 1, :copy_from => 1,
3037 3058 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
3038 3059
3039 3060 assert_response :success
3040 3061 assert_template 'new'
3041 3062
3042 3063 assert_not_nil assigns(:issue)
3043 3064 assert assigns(:issue).copy?
3044 3065
3045 3066 assert_select 'form#issue-form[action="/projects/ecookbook/issues"]' do
3046 3067 assert_select 'select[name=?]', 'issue[project_id]' do
3047 3068 assert_select 'option[value="1"]:not([selected])', :text => 'eCookbook'
3048 3069 assert_select 'option[value="2"][selected=selected]', :text => 'OnlineStore'
3049 3070 end
3050 3071 assert_select 'input[name=copy_from][value="1"]'
3051 3072 end
3052 3073 end
3053 3074
3054 3075 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
3055 3076 @request.session[:user_id] = 2
3056 3077 assert !User.find(2).member_of?(Project.find(4))
3057 3078
3058 3079 assert_difference 'Issue.count' do
3059 3080 post :create, :project_id => 1, :copy_from => 1,
3060 3081 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
3061 3082 end
3062 3083 issue = Issue.order('id DESC').first
3063 3084 assert_equal 1, issue.project_id
3064 3085 end
3065 3086
3066 3087 def test_get_edit
3067 3088 @request.session[:user_id] = 2
3068 3089 get :edit, :id => 1
3069 3090 assert_response :success
3070 3091 assert_template 'edit'
3071 3092 assert_not_nil assigns(:issue)
3072 3093 assert_equal Issue.find(1), assigns(:issue)
3073 3094
3074 3095 # Be sure we don't display inactive IssuePriorities
3075 3096 assert ! IssuePriority.find(15).active?
3076 3097 assert_select 'select[name=?]', 'issue[priority_id]' do
3077 3098 assert_select 'option[value="15"]', 0
3078 3099 end
3079 3100 end
3080 3101
3081 3102 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
3082 3103 @request.session[:user_id] = 2
3083 3104 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
3084 3105
3085 3106 get :edit, :id => 1
3086 3107 assert_select 'input[name=?]', 'time_entry[hours]'
3087 3108 end
3088 3109
3089 3110 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
3090 3111 @request.session[:user_id] = 2
3091 3112 Role.find_by_name('Manager').remove_permission! :log_time
3092 3113
3093 3114 get :edit, :id => 1
3094 3115 assert_select 'input[name=?]', 'time_entry[hours]', 0
3095 3116 end
3096 3117
3097 3118 def test_get_edit_with_params
3098 3119 @request.session[:user_id] = 2
3099 3120 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
3100 3121 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => 10 }
3101 3122 assert_response :success
3102 3123 assert_template 'edit'
3103 3124
3104 3125 issue = assigns(:issue)
3105 3126 assert_not_nil issue
3106 3127
3107 3128 assert_equal 5, issue.status_id
3108 3129 assert_select 'select[name=?]', 'issue[status_id]' do
3109 3130 assert_select 'option[value="5"][selected=selected]', :text => 'Closed'
3110 3131 end
3111 3132
3112 3133 assert_equal 7, issue.priority_id
3113 3134 assert_select 'select[name=?]', 'issue[priority_id]' do
3114 3135 assert_select 'option[value="7"][selected=selected]', :text => 'Urgent'
3115 3136 end
3116 3137
3117 3138 assert_select 'input[name=?][value="2.5"]', 'time_entry[hours]'
3118 3139 assert_select 'select[name=?]', 'time_entry[activity_id]' do
3119 3140 assert_select 'option[value="10"][selected=selected]', :text => 'Development'
3120 3141 end
3121 3142 assert_select 'input[name=?][value=test_get_edit_with_params]', 'time_entry[comments]'
3122 3143 end
3123 3144
3124 3145 def test_get_edit_with_multi_custom_field
3125 3146 field = CustomField.find(1)
3126 3147 field.update_attribute :multiple, true
3127 3148 issue = Issue.find(1)
3128 3149 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
3129 3150 issue.save!
3130 3151
3131 3152 @request.session[:user_id] = 2
3132 3153 get :edit, :id => 1
3133 3154 assert_response :success
3134 3155 assert_template 'edit'
3135 3156
3136 3157 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
3137 3158 assert_select 'option', 3
3138 3159 assert_select 'option[value=MySQL][selected=selected]'
3139 3160 assert_select 'option[value=Oracle][selected=selected]'
3140 3161 assert_select 'option[value=PostgreSQL]:not([selected])'
3141 3162 end
3142 3163 end
3143 3164
3144 3165 def test_update_form_for_existing_issue
3145 3166 @request.session[:user_id] = 2
3146 3167 xhr :patch, :edit, :id => 1,
3147 3168 :issue => {:tracker_id => 2,
3148 3169 :subject => 'This is the test_new issue',
3149 3170 :description => 'This is the description',
3150 3171 :priority_id => 5}
3151 3172 assert_response :success
3152 3173 assert_equal 'text/javascript', response.content_type
3153 3174 assert_template 'edit'
3154 3175 assert_template :partial => '_form'
3155 3176
3156 3177 issue = assigns(:issue)
3157 3178 assert_kind_of Issue, issue
3158 3179 assert_equal 1, issue.id
3159 3180 assert_equal 1, issue.project_id
3160 3181 assert_equal 2, issue.tracker_id
3161 3182 assert_equal 'This is the test_new issue', issue.subject
3162 3183 end
3163 3184
3164 3185 def test_update_form_for_existing_issue_should_keep_issue_author
3165 3186 @request.session[:user_id] = 3
3166 3187 xhr :patch, :edit, :id => 1, :issue => {:subject => 'Changed'}
3167 3188 assert_response :success
3168 3189 assert_equal 'text/javascript', response.content_type
3169 3190
3170 3191 issue = assigns(:issue)
3171 3192 assert_equal User.find(2), issue.author
3172 3193 assert_equal 2, issue.author_id
3173 3194 assert_not_equal User.current, issue.author
3174 3195 end
3175 3196
3176 3197 def test_update_form_for_existing_issue_should_propose_transitions_based_on_initial_status
3177 3198 @request.session[:user_id] = 2
3178 3199 WorkflowTransition.delete_all
3179 3200 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
3180 3201 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
3181 3202 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 5, :new_status_id => 4)
3182 3203
3183 3204 xhr :patch, :edit, :id => 2,
3184 3205 :issue => {:tracker_id => 2,
3185 3206 :status_id => 5,
3186 3207 :subject => 'This is an issue'}
3187 3208
3188 3209 assert_equal 5, assigns(:issue).status_id
3189 3210 assert_equal [1,2,5], assigns(:allowed_statuses).map(&:id).sort
3190 3211 end
3191 3212
3192 3213 def test_update_form_for_existing_issue_with_project_change
3193 3214 @request.session[:user_id] = 2
3194 3215 xhr :patch, :edit, :id => 1,
3195 3216 :issue => {:project_id => 2,
3196 3217 :tracker_id => 2,
3197 3218 :subject => 'This is the test_new issue',
3198 3219 :description => 'This is the description',
3199 3220 :priority_id => 5}
3200 3221 assert_response :success
3201 3222 assert_template :partial => '_form'
3202 3223
3203 3224 issue = assigns(:issue)
3204 3225 assert_kind_of Issue, issue
3205 3226 assert_equal 1, issue.id
3206 3227 assert_equal 2, issue.project_id
3207 3228 assert_equal 2, issue.tracker_id
3208 3229 assert_equal 'This is the test_new issue', issue.subject
3209 3230 end
3210 3231
3211 3232 def test_update_form_should_keep_category_with_same_when_changing_project
3212 3233 source = Project.generate!
3213 3234 target = Project.generate!
3214 3235 source_category = IssueCategory.create!(:name => 'Foo', :project => source)
3215 3236 target_category = IssueCategory.create!(:name => 'Foo', :project => target)
3216 3237 issue = Issue.generate!(:project => source, :category => source_category)
3217 3238
3218 3239 @request.session[:user_id] = 1
3219 3240 patch :edit, :id => issue.id,
3220 3241 :issue => {:project_id => target.id, :category_id => source_category.id}
3221 3242 assert_response :success
3222 3243
3223 3244 issue = assigns(:issue)
3224 3245 assert_equal target_category, issue.category
3225 3246 end
3226 3247
3227 3248 def test_update_form_should_propose_default_status_for_existing_issue
3228 3249 @request.session[:user_id] = 2
3229 3250 WorkflowTransition.delete_all
3230 3251 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3)
3231 3252
3232 3253 xhr :patch, :edit, :id => 2
3233 3254 assert_response :success
3234 3255 assert_equal [2,3], assigns(:allowed_statuses).map(&:id).sort
3235 3256 end
3236 3257
3237 3258 def test_put_update_without_custom_fields_param
3238 3259 @request.session[:user_id] = 2
3239 3260
3240 3261 issue = Issue.find(1)
3241 3262 assert_equal '125', issue.custom_value_for(2).value
3242 3263
3243 3264 assert_difference('Journal.count') do
3244 3265 assert_difference('JournalDetail.count') do
3245 3266 put :update, :id => 1, :issue => {:subject => 'New subject'}
3246 3267 end
3247 3268 end
3248 3269 assert_redirected_to :action => 'show', :id => '1'
3249 3270 issue.reload
3250 3271 assert_equal 'New subject', issue.subject
3251 3272 # Make sure custom fields were not cleared
3252 3273 assert_equal '125', issue.custom_value_for(2).value
3253 3274 end
3254 3275
3255 3276 def test_put_update_with_project_change
3256 3277 @request.session[:user_id] = 2
3257 3278 ActionMailer::Base.deliveries.clear
3258 3279
3259 3280 with_settings :notified_events => %w(issue_updated) do
3260 3281 assert_difference('Journal.count') do
3261 3282 assert_difference('JournalDetail.count', 3) do
3262 3283 put :update, :id => 1, :issue => {:project_id => '2',
3263 3284 :tracker_id => '1', # no change
3264 3285 :priority_id => '6',
3265 3286 :category_id => '3'
3266 3287 }
3267 3288 end
3268 3289 end
3269 3290 end
3270 3291 assert_redirected_to :action => 'show', :id => '1'
3271 3292 issue = Issue.find(1)
3272 3293 assert_equal 2, issue.project_id
3273 3294 assert_equal 1, issue.tracker_id
3274 3295 assert_equal 6, issue.priority_id
3275 3296 assert_equal 3, issue.category_id
3276 3297
3277 3298 mail = ActionMailer::Base.deliveries.last
3278 3299 assert_not_nil mail
3279 3300 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
3280 3301 assert_mail_body_match "Project changed from eCookbook to OnlineStore", mail
3281 3302 end
3282 3303
3283 3304 def test_put_update_trying_to_move_issue_to_project_without_tracker_should_not_error
3284 3305 target = Project.generate!(:tracker_ids => [])
3285 3306 assert target.trackers.empty?
3286 3307 issue = Issue.generate!
3287 3308 @request.session[:user_id] = 1
3288 3309
3289 3310 put :update, :id => issue.id, :issue => {:project_id => target.id}
3290 3311 assert_response 302
3291 3312 end
3292 3313
3293 3314 def test_put_update_with_tracker_change
3294 3315 @request.session[:user_id] = 2
3295 3316 ActionMailer::Base.deliveries.clear
3296 3317
3297 3318 with_settings :notified_events => %w(issue_updated) do
3298 3319 assert_difference('Journal.count') do
3299 3320 assert_difference('JournalDetail.count', 2) do
3300 3321 put :update, :id => 1, :issue => {:project_id => '1',
3301 3322 :tracker_id => '2',
3302 3323 :priority_id => '6'
3303 3324 }
3304 3325 end
3305 3326 end
3306 3327 end
3307 3328 assert_redirected_to :action => 'show', :id => '1'
3308 3329 issue = Issue.find(1)
3309 3330 assert_equal 1, issue.project_id
3310 3331 assert_equal 2, issue.tracker_id
3311 3332 assert_equal 6, issue.priority_id
3312 3333 assert_equal 1, issue.category_id
3313 3334
3314 3335 mail = ActionMailer::Base.deliveries.last
3315 3336 assert_not_nil mail
3316 3337 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
3317 3338 assert_mail_body_match "Tracker changed from Bug to Feature request", mail
3318 3339 end
3319 3340
3320 3341 def test_put_update_with_custom_field_change
3321 3342 @request.session[:user_id] = 2
3322 3343 issue = Issue.find(1)
3323 3344 assert_equal '125', issue.custom_value_for(2).value
3324 3345
3325 3346 with_settings :notified_events => %w(issue_updated) do
3326 3347 assert_difference('Journal.count') do
3327 3348 assert_difference('JournalDetail.count', 3) do
3328 3349 put :update, :id => 1, :issue => {:subject => 'Custom field change',
3329 3350 :priority_id => '6',
3330 3351 :category_id => '1', # no change
3331 3352 :custom_field_values => { '2' => 'New custom value' }
3332 3353 }
3333 3354 end
3334 3355 end
3335 3356 end
3336 3357 assert_redirected_to :action => 'show', :id => '1'
3337 3358 issue.reload
3338 3359 assert_equal 'New custom value', issue.custom_value_for(2).value
3339 3360
3340 3361 mail = ActionMailer::Base.deliveries.last
3341 3362 assert_not_nil mail
3342 3363 assert_mail_body_match "Searchable field changed from 125 to New custom value", mail
3343 3364 end
3344 3365
3345 3366 def test_put_update_with_multi_custom_field_change
3346 3367 field = CustomField.find(1)
3347 3368 field.update_attribute :multiple, true
3348 3369 issue = Issue.find(1)
3349 3370 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
3350 3371 issue.save!
3351 3372
3352 3373 @request.session[:user_id] = 2
3353 3374 assert_difference('Journal.count') do
3354 3375 assert_difference('JournalDetail.count', 3) do
3355 3376 put :update, :id => 1,
3356 3377 :issue => {
3357 3378 :subject => 'Custom field change',
3358 3379 :custom_field_values => { '1' => ['', 'Oracle', 'PostgreSQL'] }
3359 3380 }
3360 3381 end
3361 3382 end
3362 3383 assert_redirected_to :action => 'show', :id => '1'
3363 3384 assert_equal ['Oracle', 'PostgreSQL'], Issue.find(1).custom_field_value(1).sort
3364 3385 end
3365 3386
3366 3387 def test_put_update_with_status_and_assignee_change
3367 3388 issue = Issue.find(1)
3368 3389 assert_equal 1, issue.status_id
3369 3390 @request.session[:user_id] = 2
3370 3391
3371 3392 with_settings :notified_events => %w(issue_updated) do
3372 3393 assert_difference('TimeEntry.count', 0) do
3373 3394 put :update,
3374 3395 :id => 1,
3375 3396 :issue => { :status_id => 2, :assigned_to_id => 3, :notes => 'Assigned to dlopper' },
3376 3397 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
3377 3398 end
3378 3399 end
3379 3400 assert_redirected_to :action => 'show', :id => '1'
3380 3401 issue.reload
3381 3402 assert_equal 2, issue.status_id
3382 3403 j = Journal.order('id DESC').first
3383 3404 assert_equal 'Assigned to dlopper', j.notes
3384 3405 assert_equal 2, j.details.size
3385 3406
3386 3407 mail = ActionMailer::Base.deliveries.last
3387 3408 assert_mail_body_match "Status changed from New to Assigned", mail
3388 3409 # subject should contain the new status
3389 3410 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
3390 3411 end
3391 3412
3392 3413 def test_put_update_with_note_only
3393 3414 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
3394 3415
3395 3416 with_settings :notified_events => %w(issue_updated) do
3396 3417 # anonymous user
3397 3418 put :update,
3398 3419 :id => 1,
3399 3420 :issue => { :notes => notes }
3400 3421 end
3401 3422 assert_redirected_to :action => 'show', :id => '1'
3402 3423 j = Journal.order('id DESC').first
3403 3424 assert_equal notes, j.notes
3404 3425 assert_equal 0, j.details.size
3405 3426 assert_equal User.anonymous, j.user
3406 3427
3407 3428 mail = ActionMailer::Base.deliveries.last
3408 3429 assert_mail_body_match notes, mail
3409 3430 end
3410 3431
3411 3432 def test_put_update_with_private_note_only
3412 3433 notes = 'Private note'
3413 3434 @request.session[:user_id] = 2
3414 3435
3415 3436 assert_difference 'Journal.count' do
3416 3437 put :update, :id => 1, :issue => {:notes => notes, :private_notes => '1'}
3417 3438 assert_redirected_to :action => 'show', :id => '1'
3418 3439 end
3419 3440
3420 3441 j = Journal.order('id DESC').first
3421 3442 assert_equal notes, j.notes
3422 3443 assert_equal true, j.private_notes
3423 3444 end
3424 3445
3425 3446 def test_put_update_with_private_note_and_changes
3426 3447 notes = 'Private note'
3427 3448 @request.session[:user_id] = 2
3428 3449
3429 3450 assert_difference 'Journal.count', 2 do
3430 3451 put :update, :id => 1, :issue => {:subject => 'New subject', :notes => notes, :private_notes => '1'}
3431 3452 assert_redirected_to :action => 'show', :id => '1'
3432 3453 end
3433 3454
3434 3455 j = Journal.order('id DESC').first
3435 3456 assert_equal notes, j.notes
3436 3457 assert_equal true, j.private_notes
3437 3458 assert_equal 0, j.details.count
3438 3459
3439 3460 j = Journal.order('id DESC').offset(1).first
3440 3461 assert_nil j.notes
3441 3462 assert_equal false, j.private_notes
3442 3463 assert_equal 1, j.details.count
3443 3464 end
3444 3465
3445 3466 def test_put_update_with_note_and_spent_time
3446 3467 @request.session[:user_id] = 2
3447 3468 spent_hours_before = Issue.find(1).spent_hours
3448 3469 assert_difference('TimeEntry.count') do
3449 3470 put :update,
3450 3471 :id => 1,
3451 3472 :issue => { :notes => '2.5 hours added' },
3452 3473 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
3453 3474 end
3454 3475 assert_redirected_to :action => 'show', :id => '1'
3455 3476
3456 3477 issue = Issue.find(1)
3457 3478
3458 3479 j = Journal.order('id DESC').first
3459 3480 assert_equal '2.5 hours added', j.notes
3460 3481 assert_equal 0, j.details.size
3461 3482
3462 3483 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
3463 3484 assert_not_nil t
3464 3485 assert_equal 2.5, t.hours
3465 3486 assert_equal spent_hours_before + 2.5, issue.spent_hours
3466 3487 end
3467 3488
3468 3489 def test_put_update_should_preserve_parent_issue_even_if_not_visible
3469 3490 parent = Issue.generate!(:project_id => 1, :is_private => true)
3470 3491 issue = Issue.generate!(:parent_issue_id => parent.id)
3471 3492 assert !parent.visible?(User.find(3))
3472 3493 @request.session[:user_id] = 3
3473 3494
3474 3495 get :edit, :id => issue.id
3475 3496 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', parent.id.to_s
3476 3497
3477 3498 put :update, :id => issue.id, :issue => {:subject => 'New subject', :parent_issue_id => parent.id.to_s}
3478 3499 assert_response 302
3479 3500 assert_equal parent, issue.parent
3480 3501 end
3481 3502
3482 3503 def test_put_update_with_attachment_only
3483 3504 set_tmp_attachments_directory
3484 3505
3485 3506 # Delete all fixtured journals, a race condition can occur causing the wrong
3486 3507 # journal to get fetched in the next find.
3487 3508 Journal.delete_all
3488 3509
3489 3510 with_settings :notified_events => %w(issue_updated) do
3490 3511 # anonymous user
3491 3512 assert_difference 'Attachment.count' do
3492 3513 put :update, :id => 1,
3493 3514 :issue => {:notes => ''},
3494 3515 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
3495 3516 end
3496 3517 end
3497 3518
3498 3519 assert_redirected_to :action => 'show', :id => '1'
3499 3520 j = Issue.find(1).journals.reorder('id DESC').first
3500 3521 assert j.notes.blank?
3501 3522 assert_equal 1, j.details.size
3502 3523 assert_equal 'testfile.txt', j.details.first.value
3503 3524 assert_equal User.anonymous, j.user
3504 3525
3505 3526 attachment = Attachment.order('id DESC').first
3506 3527 assert_equal Issue.find(1), attachment.container
3507 3528 assert_equal User.anonymous, attachment.author
3508 3529 assert_equal 'testfile.txt', attachment.filename
3509 3530 assert_equal 'text/plain', attachment.content_type
3510 3531 assert_equal 'test file', attachment.description
3511 3532 assert_equal 59, attachment.filesize
3512 3533 assert File.exists?(attachment.diskfile)
3513 3534 assert_equal 59, File.size(attachment.diskfile)
3514 3535
3515 3536 mail = ActionMailer::Base.deliveries.last
3516 3537 assert_mail_body_match 'testfile.txt', mail
3517 3538 end
3518 3539
3519 3540 def test_put_update_with_failure_should_save_attachments
3520 3541 set_tmp_attachments_directory
3521 3542 @request.session[:user_id] = 2
3522 3543
3523 3544 assert_no_difference 'Journal.count' do
3524 3545 assert_difference 'Attachment.count' do
3525 3546 put :update, :id => 1,
3526 3547 :issue => { :subject => '' },
3527 3548 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
3528 3549 assert_response :success
3529 3550 assert_template 'edit'
3530 3551 end
3531 3552 end
3532 3553
3533 3554 attachment = Attachment.order('id DESC').first
3534 3555 assert_equal 'testfile.txt', attachment.filename
3535 3556 assert File.exists?(attachment.diskfile)
3536 3557 assert_nil attachment.container
3537 3558
3538 3559 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
3539 3560 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
3540 3561 end
3541 3562
3542 3563 def test_put_update_with_failure_should_keep_saved_attachments
3543 3564 set_tmp_attachments_directory
3544 3565 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
3545 3566 @request.session[:user_id] = 2
3546 3567
3547 3568 assert_no_difference 'Journal.count' do
3548 3569 assert_no_difference 'Attachment.count' do
3549 3570 put :update, :id => 1,
3550 3571 :issue => { :subject => '' },
3551 3572 :attachments => {'p0' => {'token' => attachment.token}}
3552 3573 assert_response :success
3553 3574 assert_template 'edit'
3554 3575 end
3555 3576 end
3556 3577
3557 3578 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
3558 3579 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
3559 3580 end
3560 3581
3561 3582 def test_put_update_should_attach_saved_attachments
3562 3583 set_tmp_attachments_directory
3563 3584 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
3564 3585 @request.session[:user_id] = 2
3565 3586
3566 3587 assert_difference 'Journal.count' do
3567 3588 assert_difference 'JournalDetail.count' do
3568 3589 assert_no_difference 'Attachment.count' do
3569 3590 put :update, :id => 1,
3570 3591 :issue => {:notes => 'Attachment added'},
3571 3592 :attachments => {'p0' => {'token' => attachment.token}}
3572 3593 assert_redirected_to '/issues/1'
3573 3594 end
3574 3595 end
3575 3596 end
3576 3597
3577 3598 attachment.reload
3578 3599 assert_equal Issue.find(1), attachment.container
3579 3600
3580 3601 journal = Journal.order('id DESC').first
3581 3602 assert_equal 1, journal.details.size
3582 3603 assert_equal 'testfile.txt', journal.details.first.value
3583 3604 end
3584 3605
3585 3606 def test_put_update_with_attachment_that_fails_to_save
3586 3607 set_tmp_attachments_directory
3587 3608
3588 3609 # anonymous user
3589 3610 with_settings :attachment_max_size => 0 do
3590 3611 put :update,
3591 3612 :id => 1,
3592 3613 :issue => {:notes => ''},
3593 3614 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
3594 3615 assert_redirected_to :action => 'show', :id => '1'
3595 3616 assert_equal '1 file(s) could not be saved.', flash[:warning]
3596 3617 end
3597 3618 end
3598 3619
3599 3620 def test_put_update_with_no_change
3600 3621 issue = Issue.find(1)
3601 3622 issue.journals.clear
3602 3623 ActionMailer::Base.deliveries.clear
3603 3624
3604 3625 put :update,
3605 3626 :id => 1,
3606 3627 :issue => {:notes => ''}
3607 3628 assert_redirected_to :action => 'show', :id => '1'
3608 3629
3609 3630 issue.reload
3610 3631 assert issue.journals.empty?
3611 3632 # No email should be sent
3612 3633 assert ActionMailer::Base.deliveries.empty?
3613 3634 end
3614 3635
3615 3636 def test_put_update_should_send_a_notification
3616 3637 @request.session[:user_id] = 2
3617 3638 ActionMailer::Base.deliveries.clear
3618 3639 issue = Issue.find(1)
3619 3640 old_subject = issue.subject
3620 3641 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
3621 3642
3622 3643 with_settings :notified_events => %w(issue_updated) do
3623 3644 put :update, :id => 1, :issue => {:subject => new_subject,
3624 3645 :priority_id => '6',
3625 3646 :category_id => '1' # no change
3626 3647 }
3627 3648 assert_equal 1, ActionMailer::Base.deliveries.size
3628 3649 end
3629 3650 end
3630 3651
3631 3652 def test_put_update_with_invalid_spent_time_hours_only
3632 3653 @request.session[:user_id] = 2
3633 3654 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3634 3655
3635 3656 assert_no_difference('Journal.count') do
3636 3657 put :update,
3637 3658 :id => 1,
3638 3659 :issue => {:notes => notes},
3639 3660 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
3640 3661 end
3641 3662 assert_response :success
3642 3663 assert_template 'edit'
3643 3664
3644 3665 assert_select_error /Activity cannot be blank/
3645 3666 assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
3646 3667 assert_select 'input[name=?][value=?]', 'time_entry[hours]', '2z'
3647 3668 end
3648 3669
3649 3670 def test_put_update_with_invalid_spent_time_comments_only
3650 3671 @request.session[:user_id] = 2
3651 3672 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3652 3673
3653 3674 assert_no_difference('Journal.count') do
3654 3675 put :update,
3655 3676 :id => 1,
3656 3677 :issue => {:notes => notes},
3657 3678 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
3658 3679 end
3659 3680 assert_response :success
3660 3681 assert_template 'edit'
3661 3682
3662 3683 assert_select_error /Activity cannot be blank/
3663 3684 assert_select_error /Hours cannot be blank/
3664 3685 assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
3665 3686 assert_select 'input[name=?][value=?]', 'time_entry[comments]', 'this is my comment'
3666 3687 end
3667 3688
3668 3689 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
3669 3690 issue = Issue.find(2)
3670 3691 @request.session[:user_id] = 2
3671 3692
3672 3693 put :update,
3673 3694 :id => issue.id,
3674 3695 :issue => {
3675 3696 :fixed_version_id => 4
3676 3697 }
3677 3698
3678 3699 assert_response :redirect
3679 3700 issue.reload
3680 3701 assert_equal 4, issue.fixed_version_id
3681 3702 assert_not_equal issue.project_id, issue.fixed_version.project_id
3682 3703 end
3683 3704
3684 3705 def test_put_update_should_redirect_back_using_the_back_url_parameter
3685 3706 issue = Issue.find(2)
3686 3707 @request.session[:user_id] = 2
3687 3708
3688 3709 put :update,
3689 3710 :id => issue.id,
3690 3711 :issue => {
3691 3712 :fixed_version_id => 4
3692 3713 },
3693 3714 :back_url => '/issues'
3694 3715
3695 3716 assert_response :redirect
3696 3717 assert_redirected_to '/issues'
3697 3718 end
3698 3719
3699 3720 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
3700 3721 issue = Issue.find(2)
3701 3722 @request.session[:user_id] = 2
3702 3723
3703 3724 put :update,
3704 3725 :id => issue.id,
3705 3726 :issue => {
3706 3727 :fixed_version_id => 4
3707 3728 },
3708 3729 :back_url => 'http://google.com'
3709 3730
3710 3731 assert_response :redirect
3711 3732 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
3712 3733 end
3713 3734
3714 3735 def test_put_update_should_redirect_with_previous_and_next_issue_ids_params
3715 3736 @request.session[:user_id] = 2
3716 3737
3717 3738 put :update, :id => 11,
3718 3739 :issue => {:status_id => 6, :notes => 'Notes'},
3719 3740 :prev_issue_id => 8,
3720 3741 :next_issue_id => 12,
3721 3742 :issue_position => 2,
3722 3743 :issue_count => 3
3723 3744
3724 3745 assert_redirected_to '/issues/11?issue_count=3&issue_position=2&next_issue_id=12&prev_issue_id=8'
3725 3746 end
3726 3747
3727 3748 def test_get_bulk_edit
3728 3749 @request.session[:user_id] = 2
3729 3750 get :bulk_edit, :ids => [1, 3]
3730 3751 assert_response :success
3731 3752 assert_template 'bulk_edit'
3732 3753
3733 3754 assert_select 'ul#bulk-selection' do
3734 3755 assert_select 'li', 2
3735 3756 assert_select 'li a', :text => 'Bug #1'
3736 3757 end
3737 3758
3738 3759 assert_select 'form#bulk_edit_form[action=?]', '/issues/bulk_update' do
3739 3760 assert_select 'input[name=?]', 'ids[]', 2
3740 3761 assert_select 'input[name=?][value="1"][type=hidden]', 'ids[]'
3741 3762
3742 3763 assert_select 'select[name=?]', 'issue[project_id]'
3743 3764 assert_select 'input[name=?]', 'issue[parent_issue_id]'
3744 3765
3745 3766 # Project specific custom field, date type
3746 3767 field = CustomField.find(9)
3747 3768 assert !field.is_for_all?
3748 3769 assert_equal 'date', field.field_format
3749 3770 assert_select 'input[name=?]', 'issue[custom_field_values][9]'
3750 3771
3751 3772 # System wide custom field
3752 3773 assert CustomField.find(1).is_for_all?
3753 3774 assert_select 'select[name=?]', 'issue[custom_field_values][1]'
3754 3775
3755 3776 # Be sure we don't display inactive IssuePriorities
3756 3777 assert ! IssuePriority.find(15).active?
3757 3778 assert_select 'select[name=?]', 'issue[priority_id]' do
3758 3779 assert_select 'option[value="15"]', 0
3759 3780 end
3760 3781 end
3761 3782 end
3762 3783
3763 3784 def test_get_bulk_edit_on_different_projects
3764 3785 @request.session[:user_id] = 2
3765 3786 get :bulk_edit, :ids => [1, 2, 6]
3766 3787 assert_response :success
3767 3788 assert_template 'bulk_edit'
3768 3789
3769 3790 # Can not set issues from different projects as children of an issue
3770 3791 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
3771 3792
3772 3793 # Project specific custom field, date type
3773 3794 field = CustomField.find(9)
3774 3795 assert !field.is_for_all?
3775 3796 assert !field.project_ids.include?(Issue.find(6).project_id)
3776 3797 assert_select 'input[name=?]', 'issue[custom_field_values][9]', 0
3777 3798 end
3778 3799
3779 3800 def test_get_bulk_edit_with_user_custom_field
3780 3801 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true, :tracker_ids => [1,2,3])
3781 3802
3782 3803 @request.session[:user_id] = 2
3783 3804 get :bulk_edit, :ids => [1, 2]
3784 3805 assert_response :success
3785 3806 assert_template 'bulk_edit'
3786 3807
3787 3808 assert_select 'select.user_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
3788 3809 assert_select 'option', Project.find(1).users.count + 2 # "no change" + "none" options
3789 3810 end
3790 3811 end
3791 3812
3792 3813 def test_get_bulk_edit_with_version_custom_field
3793 3814 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
3794 3815
3795 3816 @request.session[:user_id] = 2
3796 3817 get :bulk_edit, :ids => [1, 2]
3797 3818 assert_response :success
3798 3819 assert_template 'bulk_edit'
3799 3820
3800 3821 assert_select 'select.version_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
3801 3822 assert_select 'option', Project.find(1).shared_versions.count + 2 # "no change" + "none" options
3802 3823 end
3803 3824 end
3804 3825
3805 3826 def test_get_bulk_edit_with_multi_custom_field
3806 3827 field = CustomField.find(1)
3807 3828 field.update_attribute :multiple, true
3808 3829
3809 3830 @request.session[:user_id] = 2
3810 3831 get :bulk_edit, :ids => [1, 3]
3811 3832 assert_response :success
3812 3833 assert_template 'bulk_edit'
3813 3834
3814 3835 assert_select 'select[name=?]', 'issue[custom_field_values][1][]' do
3815 3836 assert_select 'option', field.possible_values.size + 1 # "none" options
3816 3837 end
3817 3838 end
3818 3839
3819 3840 def test_bulk_edit_should_propose_to_clear_text_custom_fields
3820 3841 @request.session[:user_id] = 2
3821 3842 get :bulk_edit, :ids => [1, 3]
3822 3843 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', '__none__'
3823 3844 end
3824 3845
3825 3846 def test_bulk_edit_should_only_propose_statuses_allowed_for_all_issues
3826 3847 WorkflowTransition.delete_all
3827 3848 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3828 3849 :old_status_id => 1, :new_status_id => 1)
3829 3850 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3830 3851 :old_status_id => 1, :new_status_id => 3)
3831 3852 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3832 3853 :old_status_id => 1, :new_status_id => 4)
3833 3854 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3834 3855 :old_status_id => 2, :new_status_id => 1)
3835 3856 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3836 3857 :old_status_id => 2, :new_status_id => 3)
3837 3858 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3838 3859 :old_status_id => 2, :new_status_id => 5)
3839 3860 @request.session[:user_id] = 2
3840 3861 get :bulk_edit, :ids => [1, 2]
3841 3862
3842 3863 assert_response :success
3843 3864 statuses = assigns(:available_statuses)
3844 3865 assert_not_nil statuses
3845 3866 assert_equal [1, 3], statuses.map(&:id).sort
3846 3867
3847 3868 assert_select 'select[name=?]', 'issue[status_id]' do
3848 3869 assert_select 'option', 3 # 2 statuses + "no change" option
3849 3870 end
3850 3871 end
3851 3872
3852 3873 def test_bulk_edit_should_propose_target_project_open_shared_versions
3853 3874 @request.session[:user_id] = 2
3854 3875 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3855 3876 assert_response :success
3856 3877 assert_template 'bulk_edit'
3857 3878 assert_equal Project.find(1).shared_versions.open.to_a.sort, assigns(:versions).sort
3858 3879
3859 3880 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
3860 3881 assert_select 'option', :text => '2.0'
3861 3882 end
3862 3883 end
3863 3884
3864 3885 def test_bulk_edit_should_propose_target_project_categories
3865 3886 @request.session[:user_id] = 2
3866 3887 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3867 3888 assert_response :success
3868 3889 assert_template 'bulk_edit'
3869 3890 assert_equal Project.find(1).issue_categories.sort, assigns(:categories).sort
3870 3891
3871 3892 assert_select 'select[name=?]', 'issue[category_id]' do
3872 3893 assert_select 'option', :text => 'Recipes'
3873 3894 end
3874 3895 end
3875 3896
3876 3897 def test_bulk_edit_should_only_propose_issues_trackers_custom_fields
3877 3898 IssueCustomField.delete_all
3878 3899 field = IssueCustomField.generate!(:tracker_ids => [1], :is_for_all => true)
3879 3900 IssueCustomField.generate!(:tracker_ids => [2], :is_for_all => true)
3880 3901 @request.session[:user_id] = 2
3881 3902
3882 3903 issue_ids = Issue.where(:project_id => 1, :tracker_id => 1).limit(2).ids
3883 3904 get :bulk_edit, :ids => issue_ids
3884 3905 assert_equal [field], assigns(:custom_fields)
3885 3906 end
3886 3907
3887 3908 def test_bulk_update
3888 3909 @request.session[:user_id] = 2
3889 3910 # update issues priority
3890 3911 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
3891 3912 :issue => {:priority_id => 7,
3892 3913 :assigned_to_id => '',
3893 3914 :custom_field_values => {'2' => ''}}
3894 3915
3895 3916 assert_response 302
3896 3917 # check that the issues were updated
3897 3918 assert_equal [7, 7], Issue.where(:id =>[1, 2]).collect {|i| i.priority.id}
3898 3919
3899 3920 issue = Issue.find(1)
3900 3921 journal = issue.journals.reorder('created_on DESC').first
3901 3922 assert_equal '125', issue.custom_value_for(2).value
3902 3923 assert_equal 'Bulk editing', journal.notes
3903 3924 assert_equal 1, journal.details.size
3904 3925 end
3905 3926
3906 3927 def test_bulk_update_with_group_assignee
3907 3928 group = Group.find(11)
3908 3929 project = Project.find(1)
3909 3930 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
3910 3931
3911 3932 @request.session[:user_id] = 2
3912 3933 # update issues assignee
3913 3934 with_settings :issue_group_assignment => '1' do
3914 3935 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
3915 3936 :issue => {:priority_id => '',
3916 3937 :assigned_to_id => group.id,
3917 3938 :custom_field_values => {'2' => ''}}
3918 3939
3919 3940 assert_response 302
3920 3941 assert_equal [group, group], Issue.where(:id => [1, 2]).collect {|i| i.assigned_to}
3921 3942 end
3922 3943 end
3923 3944
3924 3945 def test_bulk_update_on_different_projects
3925 3946 @request.session[:user_id] = 2
3926 3947 # update issues priority
3927 3948 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
3928 3949 :issue => {:priority_id => 7,
3929 3950 :assigned_to_id => '',
3930 3951 :custom_field_values => {'2' => ''}}
3931 3952
3932 3953 assert_response 302
3933 3954 # check that the issues were updated
3934 3955 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
3935 3956
3936 3957 issue = Issue.find(1)
3937 3958 journal = issue.journals.reorder('created_on DESC').first
3938 3959 assert_equal '125', issue.custom_value_for(2).value
3939 3960 assert_equal 'Bulk editing', journal.notes
3940 3961 assert_equal 1, journal.details.size
3941 3962 end
3942 3963
3943 3964 def test_bulk_update_on_different_projects_without_rights
3944 3965 @request.session[:user_id] = 3
3945 3966 user = User.find(3)
3946 3967 action = { :controller => "issues", :action => "bulk_update" }
3947 3968 assert user.allowed_to?(action, Issue.find(1).project)
3948 3969 assert ! user.allowed_to?(action, Issue.find(6).project)
3949 3970 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
3950 3971 :issue => {:priority_id => 7,
3951 3972 :assigned_to_id => '',
3952 3973 :custom_field_values => {'2' => ''}}
3953 3974 assert_response 403
3954 3975 assert_not_equal "Bulk should fail", Journal.last.notes
3955 3976 end
3956 3977
3957 3978 def test_bullk_update_should_send_a_notification
3958 3979 @request.session[:user_id] = 2
3959 3980 ActionMailer::Base.deliveries.clear
3960 3981 with_settings :notified_events => %w(issue_updated) do
3961 3982 post(:bulk_update,
3962 3983 {
3963 3984 :ids => [1, 2],
3964 3985 :notes => 'Bulk editing',
3965 3986 :issue => {
3966 3987 :priority_id => 7,
3967 3988 :assigned_to_id => '',
3968 3989 :custom_field_values => {'2' => ''}
3969 3990 }
3970 3991 })
3971 3992 assert_response 302
3972 3993 assert_equal 2, ActionMailer::Base.deliveries.size
3973 3994 end
3974 3995 end
3975 3996
3976 3997 def test_bulk_update_project
3977 3998 @request.session[:user_id] = 2
3978 3999 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
3979 4000 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
3980 4001 # Issues moved to project 2
3981 4002 assert_equal 2, Issue.find(1).project_id
3982 4003 assert_equal 2, Issue.find(2).project_id
3983 4004 # No tracker change
3984 4005 assert_equal 1, Issue.find(1).tracker_id
3985 4006 assert_equal 2, Issue.find(2).tracker_id
3986 4007 end
3987 4008
3988 4009 def test_bulk_update_project_on_single_issue_should_follow_when_needed
3989 4010 @request.session[:user_id] = 2
3990 4011 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
3991 4012 assert_redirected_to '/issues/1'
3992 4013 end
3993 4014
3994 4015 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
3995 4016 @request.session[:user_id] = 2
3996 4017 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
3997 4018 assert_redirected_to '/projects/onlinestore/issues'
3998 4019 end
3999 4020
4000 4021 def test_bulk_update_tracker
4001 4022 @request.session[:user_id] = 2
4002 4023 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
4003 4024 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4004 4025 assert_equal 2, Issue.find(1).tracker_id
4005 4026 assert_equal 2, Issue.find(2).tracker_id
4006 4027 end
4007 4028
4008 4029 def test_bulk_update_status
4009 4030 @request.session[:user_id] = 2
4010 4031 # update issues priority
4011 4032 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
4012 4033 :issue => {:priority_id => '',
4013 4034 :assigned_to_id => '',
4014 4035 :status_id => '5'}
4015 4036
4016 4037 assert_response 302
4017 4038 issue = Issue.find(1)
4018 4039 assert issue.closed?
4019 4040 end
4020 4041
4021 4042 def test_bulk_update_priority
4022 4043 @request.session[:user_id] = 2
4023 4044 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
4024 4045
4025 4046 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4026 4047 assert_equal 6, Issue.find(1).priority_id
4027 4048 assert_equal 6, Issue.find(2).priority_id
4028 4049 end
4029 4050
4030 4051 def test_bulk_update_with_notes
4031 4052 @request.session[:user_id] = 2
4032 4053 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
4033 4054
4034 4055 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4035 4056 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
4036 4057 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
4037 4058 end
4038 4059
4039 4060 def test_bulk_update_parent_id
4040 4061 IssueRelation.delete_all
4041 4062 @request.session[:user_id] = 2
4042 4063 post :bulk_update, :ids => [1, 3],
4043 4064 :notes => 'Bulk editing parent',
4044 4065 :issue => {:priority_id => '', :assigned_to_id => '',
4045 4066 :status_id => '', :parent_issue_id => '2'}
4046 4067 assert_response 302
4047 4068 parent = Issue.find(2)
4048 4069 assert_equal parent.id, Issue.find(1).parent_id
4049 4070 assert_equal parent.id, Issue.find(3).parent_id
4050 4071 assert_equal [1, 3], parent.children.collect(&:id).sort
4051 4072 end
4052 4073
4053 4074 def test_bulk_update_estimated_hours
4054 4075 @request.session[:user_id] = 2
4055 4076 post :bulk_update, :ids => [1, 2], :issue => {:estimated_hours => 4.25}
4056 4077
4057 4078 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4058 4079 assert_equal 4.25, Issue.find(1).estimated_hours
4059 4080 assert_equal 4.25, Issue.find(2).estimated_hours
4060 4081 end
4061 4082
4062 4083 def test_bulk_update_custom_field
4063 4084 @request.session[:user_id] = 2
4064 4085 # update issues priority
4065 4086 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
4066 4087 :issue => {:priority_id => '',
4067 4088 :assigned_to_id => '',
4068 4089 :custom_field_values => {'2' => '777'}}
4069 4090
4070 4091 assert_response 302
4071 4092
4072 4093 issue = Issue.find(1)
4073 4094 journal = issue.journals.reorder('created_on DESC').first
4074 4095 assert_equal '777', issue.custom_value_for(2).value
4075 4096 assert_equal 1, journal.details.size
4076 4097 assert_equal '125', journal.details.first.old_value
4077 4098 assert_equal '777', journal.details.first.value
4078 4099 end
4079 4100
4080 4101 def test_bulk_update_custom_field_to_blank
4081 4102 @request.session[:user_id] = 2
4082 4103 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing custom field',
4083 4104 :issue => {:priority_id => '',
4084 4105 :assigned_to_id => '',
4085 4106 :custom_field_values => {'1' => '__none__'}}
4086 4107 assert_response 302
4087 4108 assert_equal '', Issue.find(1).custom_field_value(1)
4088 4109 assert_equal '', Issue.find(3).custom_field_value(1)
4089 4110 end
4090 4111
4091 4112 def test_bulk_update_multi_custom_field
4092 4113 field = CustomField.find(1)
4093 4114 field.update_attribute :multiple, true
4094 4115
4095 4116 @request.session[:user_id] = 2
4096 4117 post :bulk_update, :ids => [1, 2, 3], :notes => 'Bulk editing multi custom field',
4097 4118 :issue => {:priority_id => '',
4098 4119 :assigned_to_id => '',
4099 4120 :custom_field_values => {'1' => ['MySQL', 'Oracle']}}
4100 4121
4101 4122 assert_response 302
4102 4123
4103 4124 assert_equal ['MySQL', 'Oracle'], Issue.find(1).custom_field_value(1).sort
4104 4125 assert_equal ['MySQL', 'Oracle'], Issue.find(3).custom_field_value(1).sort
4105 4126 # the custom field is not associated with the issue tracker
4106 4127 assert_nil Issue.find(2).custom_field_value(1)
4107 4128 end
4108 4129
4109 4130 def test_bulk_update_multi_custom_field_to_blank
4110 4131 field = CustomField.find(1)
4111 4132 field.update_attribute :multiple, true
4112 4133
4113 4134 @request.session[:user_id] = 2
4114 4135 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing multi custom field',
4115 4136 :issue => {:priority_id => '',
4116 4137 :assigned_to_id => '',
4117 4138 :custom_field_values => {'1' => ['__none__']}}
4118 4139 assert_response 302
4119 4140 assert_equal [''], Issue.find(1).custom_field_value(1)
4120 4141 assert_equal [''], Issue.find(3).custom_field_value(1)
4121 4142 end
4122 4143
4123 4144 def test_bulk_update_unassign
4124 4145 assert_not_nil Issue.find(2).assigned_to
4125 4146 @request.session[:user_id] = 2
4126 4147 # unassign issues
4127 4148 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
4128 4149 assert_response 302
4129 4150 # check that the issues were updated
4130 4151 assert_nil Issue.find(2).assigned_to
4131 4152 end
4132 4153
4133 4154 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
4134 4155 @request.session[:user_id] = 2
4135 4156
4136 4157 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
4137 4158
4138 4159 assert_response :redirect
4139 4160 issues = Issue.find([1,2])
4140 4161 issues.each do |issue|
4141 4162 assert_equal 4, issue.fixed_version_id
4142 4163 assert_not_equal issue.project_id, issue.fixed_version.project_id
4143 4164 end
4144 4165 end
4145 4166
4146 4167 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
4147 4168 @request.session[:user_id] = 2
4148 4169 post :bulk_update, :ids => [1,2], :back_url => '/issues'
4149 4170
4150 4171 assert_response :redirect
4151 4172 assert_redirected_to '/issues'
4152 4173 end
4153 4174
4154 4175 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
4155 4176 @request.session[:user_id] = 2
4156 4177 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
4157 4178
4158 4179 assert_response :redirect
4159 4180 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
4160 4181 end
4161 4182
4162 4183 def test_bulk_update_with_all_failures_should_show_errors
4163 4184 @request.session[:user_id] = 2
4164 4185 post :bulk_update, :ids => [1, 2], :issue => {:start_date => 'foo'}
4165 4186
4166 4187 assert_response :success
4167 4188 assert_template 'bulk_edit'
4168 4189 assert_select '#errorExplanation span', :text => 'Failed to save 2 issue(s) on 2 selected: #1, #2.'
4169 4190 assert_select '#errorExplanation ul li', :text => 'Start date is not a valid date: #1, #2'
4170 4191
4171 4192 assert_equal [1, 2], assigns[:issues].map(&:id)
4172 4193 end
4173 4194
4174 4195 def test_bulk_update_with_some_failures_should_show_errors
4175 4196 issue1 = Issue.generate!(:start_date => '2013-05-12')
4176 4197 issue2 = Issue.generate!(:start_date => '2013-05-15')
4177 4198 issue3 = Issue.generate!
4178 4199 @request.session[:user_id] = 2
4179 4200 post :bulk_update, :ids => [issue1.id, issue2.id, issue3.id],
4180 4201 :issue => {:due_date => '2013-05-01'}
4181 4202 assert_response :success
4182 4203 assert_template 'bulk_edit'
4183 4204 assert_select '#errorExplanation span',
4184 4205 :text => "Failed to save 2 issue(s) on 3 selected: ##{issue1.id}, ##{issue2.id}."
4185 4206 assert_select '#errorExplanation ul li',
4186 4207 :text => "Due date must be greater than start date: ##{issue1.id}, ##{issue2.id}"
4187 4208 assert_equal [issue1.id, issue2.id], assigns[:issues].map(&:id)
4188 4209 end
4189 4210
4190 4211 def test_bulk_update_with_failure_should_preserved_form_values
4191 4212 @request.session[:user_id] = 2
4192 4213 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2', :start_date => 'foo'}
4193 4214
4194 4215 assert_response :success
4195 4216 assert_template 'bulk_edit'
4196 4217 assert_select 'select[name=?]', 'issue[tracker_id]' do
4197 4218 assert_select 'option[value="2"][selected=selected]'
4198 4219 end
4199 4220 assert_select 'input[name=?][value=?]', 'issue[start_date]', 'foo'
4200 4221 end
4201 4222
4202 4223 def test_get_bulk_copy
4203 4224 @request.session[:user_id] = 2
4204 4225 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
4205 4226 assert_response :success
4206 4227 assert_template 'bulk_edit'
4207 4228
4208 4229 issues = assigns(:issues)
4209 4230 assert_not_nil issues
4210 4231 assert_equal [1, 2, 3], issues.map(&:id).sort
4211 4232
4212 4233 assert_select 'select[name=?]', 'issue[project_id]' do
4213 4234 assert_select 'option[value=""]'
4214 4235 end
4215 4236 assert_select 'input[name=copy_attachments]'
4216 4237 end
4217 4238
4218 4239 def test_get_bulk_copy_without_add_issues_permission_should_not_propose_current_project_as_target
4219 4240 user = setup_user_with_copy_but_not_add_permission
4220 4241 @request.session[:user_id] = user.id
4221 4242
4222 4243 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
4223 4244 assert_response :success
4224 4245 assert_template 'bulk_edit'
4225 4246
4226 4247 assert_select 'select[name=?]', 'issue[project_id]' do
4227 4248 assert_select 'option[value=""]', 0
4228 4249 assert_select 'option[value="2"]'
4229 4250 end
4230 4251 end
4231 4252
4232 4253 def test_bulk_copy_to_another_project
4233 4254 @request.session[:user_id] = 2
4234 4255 assert_difference 'Issue.count', 2 do
4235 4256 assert_no_difference 'Project.find(1).issues.count' do
4236 4257 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
4237 4258 end
4238 4259 end
4239 4260 assert_redirected_to '/projects/ecookbook/issues'
4240 4261
4241 4262 copies = Issue.order('id DESC').limit(issues.size)
4242 4263 copies.each do |copy|
4243 4264 assert_equal 2, copy.project_id
4244 4265 end
4245 4266 end
4246 4267
4247 4268 def test_bulk_copy_without_add_issues_permission_should_be_allowed_on_project_with_permission
4248 4269 user = setup_user_with_copy_but_not_add_permission
4249 4270 @request.session[:user_id] = user.id
4250 4271
4251 4272 assert_difference 'Issue.count', 3 do
4252 4273 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '2'}, :copy => '1'
4253 4274 assert_response 302
4254 4275 end
4255 4276 end
4256 4277
4257 4278 def test_bulk_copy_on_same_project_without_add_issues_permission_should_be_denied
4258 4279 user = setup_user_with_copy_but_not_add_permission
4259 4280 @request.session[:user_id] = user.id
4260 4281
4261 4282 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => ''}, :copy => '1'
4262 4283 assert_response 403
4263 4284 end
4264 4285
4265 4286 def test_bulk_copy_on_different_project_without_add_issues_permission_should_be_denied
4266 4287 user = setup_user_with_copy_but_not_add_permission
4267 4288 @request.session[:user_id] = user.id
4268 4289
4269 4290 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '1'}, :copy => '1'
4270 4291 assert_response 403
4271 4292 end
4272 4293
4273 4294 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
4274 4295 @request.session[:user_id] = 2
4275 4296 issues = [
4276 4297 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
4277 4298 :priority_id => 2, :subject => 'issue 1', :author_id => 1,
4278 4299 :assigned_to_id => nil),
4279 4300 Issue.create!(:project_id => 2, :tracker_id => 3, :status_id => 2,
4280 4301 :priority_id => 1, :subject => 'issue 2', :author_id => 2,
4281 4302 :assigned_to_id => 3)
4282 4303 ]
4283 4304 assert_difference 'Issue.count', issues.size do
4284 4305 post :bulk_update, :ids => issues.map(&:id), :copy => '1',
4285 4306 :issue => {
4286 4307 :project_id => '', :tracker_id => '', :assigned_to_id => '',
4287 4308 :status_id => '', :start_date => '', :due_date => ''
4288 4309 }
4289 4310 end
4290 4311
4291 4312 copies = Issue.order('id DESC').limit(issues.size)
4292 4313 issues.each do |orig|
4293 4314 copy = copies.detect {|c| c.subject == orig.subject}
4294 4315 assert_not_nil copy
4295 4316 assert_equal orig.project_id, copy.project_id
4296 4317 assert_equal orig.tracker_id, copy.tracker_id
4297 4318 assert_equal orig.status_id, copy.status_id
4298 4319 assert_equal orig.assigned_to_id, copy.assigned_to_id
4299 4320 assert_equal orig.priority_id, copy.priority_id
4300 4321 end
4301 4322 end
4302 4323
4303 4324 def test_bulk_copy_should_allow_changing_the_issue_attributes
4304 4325 # Fixes random test failure with Mysql
4305 4326 # where Issue.where(:project_id => 2).limit(2).order('id desc')
4306 4327 # doesn't return the expected results
4307 4328 Issue.delete_all("project_id=2")
4308 4329
4309 4330 @request.session[:user_id] = 2
4310 4331 assert_difference 'Issue.count', 2 do
4311 4332 assert_no_difference 'Project.find(1).issues.count' do
4312 4333 post :bulk_update, :ids => [1, 2], :copy => '1',
4313 4334 :issue => {
4314 4335 :project_id => '2', :tracker_id => '', :assigned_to_id => '2',
4315 4336 :status_id => '1', :start_date => '2009-12-01', :due_date => '2009-12-31'
4316 4337 }
4317 4338 end
4318 4339 end
4319 4340
4320 4341 copied_issues = Issue.where(:project_id => 2).limit(2).order('id desc').to_a
4321 4342 assert_equal 2, copied_issues.size
4322 4343 copied_issues.each do |issue|
4323 4344 assert_equal 2, issue.project_id, "Project is incorrect"
4324 4345 assert_equal 2, issue.assigned_to_id, "Assigned to is incorrect"
4325 4346 assert_equal 1, issue.status_id, "Status is incorrect"
4326 4347 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
4327 4348 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
4328 4349 end
4329 4350 end
4330 4351
4331 4352 def test_bulk_copy_should_allow_adding_a_note
4332 4353 @request.session[:user_id] = 2
4333 4354 assert_difference 'Issue.count', 1 do
4334 4355 post :bulk_update, :ids => [1], :copy => '1',
4335 4356 :notes => 'Copying one issue',
4336 4357 :issue => {
4337 4358 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
4338 4359 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
4339 4360 }
4340 4361 end
4341 4362 issue = Issue.order('id DESC').first
4342 4363 assert_equal 1, issue.journals.size
4343 4364 journal = issue.journals.first
4344 4365 assert_equal 'Copying one issue', journal.notes
4345 4366 end
4346 4367
4347 4368 def test_bulk_copy_should_allow_not_copying_the_attachments
4348 4369 attachment_count = Issue.find(3).attachments.size
4349 4370 assert attachment_count > 0
4350 4371 @request.session[:user_id] = 2
4351 4372
4352 4373 assert_difference 'Issue.count', 1 do
4353 4374 assert_no_difference 'Attachment.count' do
4354 4375 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '0',
4355 4376 :issue => {
4356 4377 :project_id => ''
4357 4378 }
4358 4379 end
4359 4380 end
4360 4381 end
4361 4382
4362 4383 def test_bulk_copy_should_allow_copying_the_attachments
4363 4384 attachment_count = Issue.find(3).attachments.size
4364 4385 assert attachment_count > 0
4365 4386 @request.session[:user_id] = 2
4366 4387
4367 4388 assert_difference 'Issue.count', 1 do
4368 4389 assert_difference 'Attachment.count', attachment_count do
4369 4390 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '1',
4370 4391 :issue => {
4371 4392 :project_id => ''
4372 4393 }
4373 4394 end
4374 4395 end
4375 4396 end
4376 4397
4377 4398 def test_bulk_copy_should_add_relations_with_copied_issues
4378 4399 @request.session[:user_id] = 2
4379 4400
4380 4401 assert_difference 'Issue.count', 2 do
4381 4402 assert_difference 'IssueRelation.count', 2 do
4382 4403 post :bulk_update, :ids => [1, 3], :copy => '1', :link_copy => '1',
4383 4404 :issue => {
4384 4405 :project_id => '1'
4385 4406 }
4386 4407 end
4387 4408 end
4388 4409 end
4389 4410
4390 4411 def test_bulk_copy_should_allow_not_copying_the_subtasks
4391 4412 issue = Issue.generate_with_descendants!
4392 4413 @request.session[:user_id] = 2
4393 4414
4394 4415 assert_difference 'Issue.count', 1 do
4395 4416 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '0',
4396 4417 :issue => {
4397 4418 :project_id => ''
4398 4419 }
4399 4420 end
4400 4421 end
4401 4422
4402 4423 def test_bulk_copy_should_allow_copying_the_subtasks
4403 4424 issue = Issue.generate_with_descendants!
4404 4425 count = issue.descendants.count
4405 4426 @request.session[:user_id] = 2
4406 4427
4407 4428 assert_difference 'Issue.count', count+1 do
4408 4429 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '1',
4409 4430 :issue => {
4410 4431 :project_id => ''
4411 4432 }
4412 4433 end
4413 4434 copy = Issue.where(:parent_id => nil).order("id DESC").first
4414 4435 assert_equal count, copy.descendants.count
4415 4436 end
4416 4437
4417 4438 def test_bulk_copy_should_not_copy_selected_subtasks_twice
4418 4439 issue = Issue.generate_with_descendants!
4419 4440 count = issue.descendants.count
4420 4441 @request.session[:user_id] = 2
4421 4442
4422 4443 assert_difference 'Issue.count', count+1 do
4423 4444 post :bulk_update, :ids => issue.self_and_descendants.map(&:id), :copy => '1', :copy_subtasks => '1',
4424 4445 :issue => {
4425 4446 :project_id => ''
4426 4447 }
4427 4448 end
4428 4449 copy = Issue.where(:parent_id => nil).order("id DESC").first
4429 4450 assert_equal count, copy.descendants.count
4430 4451 end
4431 4452
4432 4453 def test_bulk_copy_to_another_project_should_follow_when_needed
4433 4454 @request.session[:user_id] = 2
4434 4455 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
4435 4456 issue = Issue.order('id DESC').first
4436 4457 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
4437 4458 end
4438 4459
4439 4460 def test_bulk_copy_with_all_failures_should_display_errors
4440 4461 @request.session[:user_id] = 2
4441 4462 post :bulk_update, :ids => [1, 2], :copy => '1', :issue => {:start_date => 'foo'}
4442 4463
4443 4464 assert_response :success
4444 4465 end
4445 4466
4446 4467 def test_destroy_issue_with_no_time_entries
4447 4468 assert_nil TimeEntry.find_by_issue_id(2)
4448 4469 @request.session[:user_id] = 2
4449 4470
4450 4471 assert_difference 'Issue.count', -1 do
4451 4472 delete :destroy, :id => 2
4452 4473 end
4453 4474 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4454 4475 assert_nil Issue.find_by_id(2)
4455 4476 end
4456 4477
4457 4478 def test_destroy_issues_with_time_entries
4458 4479 @request.session[:user_id] = 2
4459 4480
4460 4481 assert_no_difference 'Issue.count' do
4461 4482 delete :destroy, :ids => [1, 3]
4462 4483 end
4463 4484 assert_response :success
4464 4485 assert_template 'destroy'
4465 4486 assert_not_nil assigns(:hours)
4466 4487 assert Issue.find_by_id(1) && Issue.find_by_id(3)
4467 4488
4468 4489 assert_select 'form' do
4469 4490 assert_select 'input[name=_method][value=delete]'
4470 4491 end
4471 4492 end
4472 4493
4473 4494 def test_destroy_issues_and_destroy_time_entries
4474 4495 @request.session[:user_id] = 2
4475 4496
4476 4497 assert_difference 'Issue.count', -2 do
4477 4498 assert_difference 'TimeEntry.count', -3 do
4478 4499 delete :destroy, :ids => [1, 3], :todo => 'destroy'
4479 4500 end
4480 4501 end
4481 4502 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4482 4503 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4483 4504 assert_nil TimeEntry.find_by_id([1, 2])
4484 4505 end
4485 4506
4486 4507 def test_destroy_issues_and_assign_time_entries_to_project
4487 4508 @request.session[:user_id] = 2
4488 4509
4489 4510 assert_difference 'Issue.count', -2 do
4490 4511 assert_no_difference 'TimeEntry.count' do
4491 4512 delete :destroy, :ids => [1, 3], :todo => 'nullify'
4492 4513 end
4493 4514 end
4494 4515 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4495 4516 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4496 4517 assert_nil TimeEntry.find(1).issue_id
4497 4518 assert_nil TimeEntry.find(2).issue_id
4498 4519 end
4499 4520
4500 4521 def test_destroy_issues_and_reassign_time_entries_to_another_issue
4501 4522 @request.session[:user_id] = 2
4502 4523
4503 4524 assert_difference 'Issue.count', -2 do
4504 4525 assert_no_difference 'TimeEntry.count' do
4505 4526 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
4506 4527 end
4507 4528 end
4508 4529 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4509 4530 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4510 4531 assert_equal 2, TimeEntry.find(1).issue_id
4511 4532 assert_equal 2, TimeEntry.find(2).issue_id
4512 4533 end
4513 4534
4514 4535 def test_destroy_issues_and_reassign_time_entries_to_an_invalid_issue_should_fail
4515 4536 @request.session[:user_id] = 2
4516 4537
4517 4538 assert_no_difference 'Issue.count' do
4518 4539 assert_no_difference 'TimeEntry.count' do
4519 4540 # try to reassign time to an issue of another project
4520 4541 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 4
4521 4542 end
4522 4543 end
4523 4544 assert_response :success
4524 4545 assert_template 'destroy'
4525 4546 end
4526 4547
4527 4548 def test_destroy_issues_from_different_projects
4528 4549 @request.session[:user_id] = 2
4529 4550
4530 4551 assert_difference 'Issue.count', -3 do
4531 4552 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
4532 4553 end
4533 4554 assert_redirected_to :controller => 'issues', :action => 'index'
4534 4555 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
4535 4556 end
4536 4557
4537 4558 def test_destroy_parent_and_child_issues
4538 4559 parent = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Parent Issue')
4539 4560 child = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Child Issue', :parent_issue_id => parent.id)
4540 4561 assert child.is_descendant_of?(parent.reload)
4541 4562
4542 4563 @request.session[:user_id] = 2
4543 4564 assert_difference 'Issue.count', -2 do
4544 4565 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
4545 4566 end
4546 4567 assert_response 302
4547 4568 end
4548 4569
4549 4570 def test_destroy_invalid_should_respond_with_404
4550 4571 @request.session[:user_id] = 2
4551 4572 assert_no_difference 'Issue.count' do
4552 4573 delete :destroy, :id => 999
4553 4574 end
4554 4575 assert_response 404
4555 4576 end
4556 4577
4557 4578 def test_default_search_scope
4558 4579 get :index
4559 4580
4560 4581 assert_select 'div#quick-search form' do
4561 4582 assert_select 'input[name=issues][value="1"][type=hidden]'
4562 4583 end
4563 4584 end
4564 4585
4565 4586 def setup_user_with_copy_but_not_add_permission
4566 4587 Role.all.each {|r| r.remove_permission! :add_issues}
4567 4588 Role.find_by_name('Manager').add_permission! :add_issues
4568 4589 user = User.generate!
4569 4590 User.add_to_project(user, Project.find(1), Role.find_by_name('Developer'))
4570 4591 User.add_to_project(user, Project.find(2), Role.find_by_name('Manager'))
4571 4592 user
4572 4593 end
4573 4594 end
@@ -1,718 +1,694
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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
20 20 class ProjectsControllerTest < ActionController::TestCase
21 21 fixtures :projects, :versions, :users, :email_addresses, :roles, :members,
22 22 :member_roles, :issues, :journals, :journal_details,
23 23 :trackers, :projects_trackers, :issue_statuses,
24 24 :enabled_modules, :enumerations, :boards, :messages,
25 25 :attachments, :custom_fields, :custom_values, :time_entries,
26 26 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
27 27
28 28 def setup
29 29 @request.session[:user_id] = nil
30 30 Setting.default_language = 'en'
31 31 end
32 32
33 33 def test_index_by_anonymous_should_not_show_private_projects
34 34 get :index
35 35 assert_response :success
36 36 assert_template 'index'
37 37 projects = assigns(:projects)
38 38 assert_not_nil projects
39 39 assert projects.all?(&:is_public?)
40 40
41 41 assert_select 'ul' do
42 42 assert_select 'li' do
43 43 assert_select 'a', :text => 'eCookbook'
44 44 assert_select 'ul' do
45 45 assert_select 'a', :text => 'Child of private child'
46 46 end
47 47 end
48 48 end
49 49 assert_select 'a', :text => /Private child of eCookbook/, :count => 0
50 50 end
51 51
52 52 def test_index_atom
53 53 get :index, :format => 'atom'
54 54 assert_response :success
55 55 assert_template 'common/feed'
56 56 assert_select 'feed>title', :text => 'Redmine: Latest projects'
57 57 assert_select 'feed>entry', :count => Project.visible(User.current).count
58 58 end
59 59
60 60 test "#index by non-admin user with view_time_entries permission should show overall spent time link" do
61 61 @request.session[:user_id] = 3
62 62 get :index
63 63 assert_template 'index'
64 64 assert_select 'a[href=?]', '/time_entries'
65 65 end
66 66
67 67 test "#index by non-admin user without view_time_entries permission should not show overall spent time link" do
68 68 Role.find(2).remove_permission! :view_time_entries
69 69 Role.non_member.remove_permission! :view_time_entries
70 70 Role.anonymous.remove_permission! :view_time_entries
71 71 @request.session[:user_id] = 3
72 72
73 73 get :index
74 74 assert_template 'index'
75 75 assert_select 'a[href=?]', '/time_entries', 0
76 76 end
77 77
78 78 test "#index by non-admin user with permission should show add project link" do
79 79 Role.find(1).add_permission! :add_project
80 80 @request.session[:user_id] = 2
81 81 get :index
82 82 assert_template 'index'
83 83 assert_select 'a[href=?]', '/projects/new'
84 84 end
85 85
86 86 test "#new by admin user should accept get" do
87 87 @request.session[:user_id] = 1
88 88
89 89 get :new
90 90 assert_response :success
91 91 assert_template 'new'
92 92 end
93 93
94 94 test "#new by non-admin user with add_project permission should accept get" do
95 95 Role.non_member.add_permission! :add_project
96 96 @request.session[:user_id] = 9
97 97
98 98 get :new
99 99 assert_response :success
100 100 assert_template 'new'
101 101 assert_select 'select[name=?]', 'project[parent_id]', 0
102 102 end
103 103
104 104 test "#new by non-admin user with add_subprojects permission should accept get" do
105 105 Role.find(1).remove_permission! :add_project
106 106 Role.find(1).add_permission! :add_subprojects
107 107 @request.session[:user_id] = 2
108 108
109 109 get :new, :parent_id => 'ecookbook'
110 110 assert_response :success
111 111 assert_template 'new'
112 112
113 113 assert_select 'select[name=?]', 'project[parent_id]' do
114 114 # parent project selected
115 115 assert_select 'option[value="1"][selected=selected]'
116 116 # no empty value
117 117 assert_select 'option[value=""]', 0
118 118 end
119 119 end
120 120
121 121 def test_new_should_not_display_invalid_search_link
122 122 @request.session[:user_id] = 1
123 123
124 124 get :new
125 125 assert_response :success
126 126 assert_select '#quick-search form[action=?]', '/search'
127 127 assert_select '#quick-search a[href=?]', '/search'
128 128 end
129 129
130 130 test "#create by admin user should create a new project" do
131 131 @request.session[:user_id] = 1
132 132
133 133 post :create,
134 134 :project => {
135 135 :name => "blog",
136 136 :description => "weblog",
137 137 :homepage => 'http://weblog',
138 138 :identifier => "blog",
139 139 :is_public => 1,
140 140 :custom_field_values => { '3' => 'Beta' },
141 141 :tracker_ids => ['1', '3'],
142 142 # an issue custom field that is not for all project
143 143 :issue_custom_field_ids => ['9'],
144 144 :enabled_module_names => ['issue_tracking', 'news', 'repository']
145 145 }
146 146 assert_redirected_to '/projects/blog/settings'
147 147
148 148 project = Project.find_by_name('blog')
149 149 assert_kind_of Project, project
150 150 assert project.active?
151 151 assert_equal 'weblog', project.description
152 152 assert_equal 'http://weblog', project.homepage
153 153 assert_equal true, project.is_public?
154 154 assert_nil project.parent
155 155 assert_equal 'Beta', project.custom_value_for(3).value
156 156 assert_equal [1, 3], project.trackers.map(&:id).sort
157 157 assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
158 158 assert project.issue_custom_fields.include?(IssueCustomField.find(9))
159 159 end
160 160
161 161 test "#create by admin user should create a new subproject" do
162 162 @request.session[:user_id] = 1
163 163
164 164 assert_difference 'Project.count' do
165 165 post :create, :project => { :name => "blog",
166 166 :description => "weblog",
167 167 :identifier => "blog",
168 168 :is_public => 1,
169 169 :custom_field_values => { '3' => 'Beta' },
170 170 :parent_id => 1
171 171 }
172 172 assert_redirected_to '/projects/blog/settings'
173 173 end
174 174
175 175 project = Project.find_by_name('blog')
176 176 assert_kind_of Project, project
177 177 assert_equal Project.find(1), project.parent
178 178 end
179 179
180 180 test "#create by admin user should continue" do
181 181 @request.session[:user_id] = 1
182 182
183 183 assert_difference 'Project.count' do
184 184 post :create, :project => {:name => "blog", :identifier => "blog"}, :continue => 'Create and continue'
185 185 end
186 186 assert_redirected_to '/projects/new'
187 187 end
188 188
189 189 test "#create by non-admin user with add_project permission should create a new project" do
190 190 Role.non_member.add_permission! :add_project
191 191 @request.session[:user_id] = 9
192 192
193 193 post :create, :project => { :name => "blog",
194 194 :description => "weblog",
195 195 :identifier => "blog",
196 196 :is_public => 1,
197 197 :custom_field_values => { '3' => 'Beta' },
198 198 :tracker_ids => ['1', '3'],
199 199 :enabled_module_names => ['issue_tracking', 'news', 'repository']
200 200 }
201 201
202 202 assert_redirected_to '/projects/blog/settings'
203 203
204 204 project = Project.find_by_name('blog')
205 205 assert_kind_of Project, project
206 206 assert_equal 'weblog', project.description
207 207 assert_equal true, project.is_public?
208 208 assert_equal [1, 3], project.trackers.map(&:id).sort
209 209 assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
210 210
211 211 # User should be added as a project member
212 212 assert User.find(9).member_of?(project)
213 213 assert_equal 1, project.members.size
214 214 end
215 215
216 216 test "#create by non-admin user with add_project permission should fail with parent_id" do
217 217 Role.non_member.add_permission! :add_project
218 218 @request.session[:user_id] = 9
219 219
220 220 assert_no_difference 'Project.count' do
221 221 post :create, :project => { :name => "blog",
222 222 :description => "weblog",
223 223 :identifier => "blog",
224 224 :is_public => 1,
225 225 :custom_field_values => { '3' => 'Beta' },
226 226 :parent_id => 1
227 227 }
228 228 end
229 229 assert_response :success
230 230 project = assigns(:project)
231 231 assert_kind_of Project, project
232 232 assert_not_equal [], project.errors[:parent_id]
233 233 end
234 234
235 235 test "#create by non-admin user with add_subprojects permission should create a project with a parent_id" do
236 236 Role.find(1).remove_permission! :add_project
237 237 Role.find(1).add_permission! :add_subprojects
238 238 @request.session[:user_id] = 2
239 239
240 240 post :create, :project => { :name => "blog",
241 241 :description => "weblog",
242 242 :identifier => "blog",
243 243 :is_public => 1,
244 244 :custom_field_values => { '3' => 'Beta' },
245 245 :parent_id => 1
246 246 }
247 247 assert_redirected_to '/projects/blog/settings'
248 248 project = Project.find_by_name('blog')
249 249 end
250 250
251 251 test "#create by non-admin user with add_subprojects permission should fail without parent_id" do
252 252 Role.find(1).remove_permission! :add_project
253 253 Role.find(1).add_permission! :add_subprojects
254 254 @request.session[:user_id] = 2
255 255
256 256 assert_no_difference 'Project.count' do
257 257 post :create, :project => { :name => "blog",
258 258 :description => "weblog",
259 259 :identifier => "blog",
260 260 :is_public => 1,
261 261 :custom_field_values => { '3' => 'Beta' }
262 262 }
263 263 end
264 264 assert_response :success
265 265 project = assigns(:project)
266 266 assert_kind_of Project, project
267 267 assert_not_equal [], project.errors[:parent_id]
268 268 end
269 269
270 270 test "#create by non-admin user with add_subprojects permission should fail with unauthorized parent_id" do
271 271 Role.find(1).remove_permission! :add_project
272 272 Role.find(1).add_permission! :add_subprojects
273 273 @request.session[:user_id] = 2
274 274
275 275 assert !User.find(2).member_of?(Project.find(6))
276 276 assert_no_difference 'Project.count' do
277 277 post :create, :project => { :name => "blog",
278 278 :description => "weblog",
279 279 :identifier => "blog",
280 280 :is_public => 1,
281 281 :custom_field_values => { '3' => 'Beta' },
282 282 :parent_id => 6
283 283 }
284 284 end
285 285 assert_response :success
286 286 project = assigns(:project)
287 287 assert_kind_of Project, project
288 288 assert_not_equal [], project.errors[:parent_id]
289 289 end
290 290
291 291 def test_create_subproject_with_inherit_members_should_inherit_members
292 292 Role.find_by_name('Manager').add_permission! :add_subprojects
293 293 parent = Project.find(1)
294 294 @request.session[:user_id] = 2
295 295
296 296 assert_difference 'Project.count' do
297 297 post :create, :project => {
298 298 :name => 'inherited', :identifier => 'inherited', :parent_id => parent.id, :inherit_members => '1'
299 299 }
300 300 assert_response 302
301 301 end
302 302
303 303 project = Project.order('id desc').first
304 304 assert_equal 'inherited', project.name
305 305 assert_equal parent, project.parent
306 306 assert project.memberships.count > 0
307 307 assert_equal parent.memberships.count, project.memberships.count
308 308 end
309 309
310 310 def test_create_should_preserve_modules_on_validation_failure
311 311 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
312 312 @request.session[:user_id] = 1
313 313 assert_no_difference 'Project.count' do
314 314 post :create, :project => {
315 315 :name => "blog",
316 316 :identifier => "",
317 317 :enabled_module_names => %w(issue_tracking news)
318 318 }
319 319 end
320 320 assert_response :success
321 321 project = assigns(:project)
322 322 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
323 323 end
324 324 end
325 325
326 326 def test_show_by_id
327 327 get :show, :id => 1
328 328 assert_response :success
329 329 assert_template 'show'
330 330 assert_not_nil assigns(:project)
331 331 end
332 332
333 333 def test_show_by_identifier
334 334 get :show, :id => 'ecookbook'
335 335 assert_response :success
336 336 assert_template 'show'
337 337 assert_not_nil assigns(:project)
338 338 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
339 339
340 340 assert_select 'li', :text => /Development status/
341 341 end
342 342
343 343 def test_show_should_not_display_empty_sidebar
344 344 p = Project.find(1)
345 345 p.enabled_module_names = []
346 346 p.save!
347 347
348 348 get :show, :id => 'ecookbook'
349 349 assert_response :success
350 350 assert_select '#main.nosidebar'
351 351 end
352 352
353 353 def test_show_should_not_display_hidden_custom_fields
354 354 ProjectCustomField.find_by_name('Development status').update_attribute :visible, false
355 355 get :show, :id => 'ecookbook'
356 356 assert_response :success
357 357 assert_template 'show'
358 358 assert_not_nil assigns(:project)
359 359
360 360 assert_select 'li', :text => /Development status/, :count => 0
361 361 end
362 362
363 363 def test_show_should_not_display_blank_custom_fields_with_multiple_values
364 364 f1 = ProjectCustomField.generate! :field_format => 'list', :possible_values => %w(Foo Bar), :multiple => true
365 365 f2 = ProjectCustomField.generate! :field_format => 'list', :possible_values => %w(Baz Qux), :multiple => true
366 366 project = Project.generate!(:custom_field_values => {f2.id.to_s => %w(Qux)})
367 367
368 368 get :show, :id => project.id
369 369 assert_response :success
370 370
371 371 assert_select 'li', :text => /#{f1.name}/, :count => 0
372 372 assert_select 'li', :text => /#{f2.name}/
373 373 end
374 374
375 375 def test_show_should_not_display_blank_text_custom_fields
376 376 f1 = ProjectCustomField.generate! :field_format => 'text'
377 377
378 378 get :show, :id => 1
379 379 assert_response :success
380 380
381 381 assert_select 'li', :text => /#{f1.name}/, :count => 0
382 382 end
383 383
384 384 def test_show_should_not_fail_when_custom_values_are_nil
385 385 project = Project.find_by_identifier('ecookbook')
386 386 project.custom_values.first.update_attribute(:value, nil)
387 387 get :show, :id => 'ecookbook'
388 388 assert_response :success
389 389 assert_template 'show'
390 390 assert_not_nil assigns(:project)
391 391 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
392 392 end
393 393
394 394 def show_archived_project_should_be_denied
395 395 project = Project.find_by_identifier('ecookbook')
396 396 project.archive!
397 397
398 398 get :show, :id => 'ecookbook'
399 399 assert_response 403
400 400 assert_nil assigns(:project)
401 401 assert_select 'p', :text => /archived/
402 402 end
403 403
404 404 def test_show_should_not_show_private_subprojects_that_are_not_visible
405 405 get :show, :id => 'ecookbook'
406 406 assert_response :success
407 407 assert_template 'show'
408 408 assert_select 'a', :text => /Private child/, :count => 0
409 409 end
410 410
411 411 def test_show_should_show_private_subprojects_that_are_visible
412 412 @request.session[:user_id] = 2 # manager who is a member of the private subproject
413 413 get :show, :id => 'ecookbook'
414 414 assert_response :success
415 415 assert_template 'show'
416 416 assert_select 'a', :text => /Private child/
417 417 end
418 418
419 419 def test_settings
420 420 @request.session[:user_id] = 2 # manager
421 421 get :settings, :id => 1
422 422 assert_response :success
423 423 assert_template 'settings'
424 424 end
425 425
426 426 def test_settings_of_subproject
427 427 @request.session[:user_id] = 2
428 428 get :settings, :id => 'private-child'
429 429 assert_response :success
430 430 assert_template 'settings'
431 431
432 432 assert_select 'input[type=checkbox][name=?]', 'project[inherit_members]'
433 433 end
434 434
435 435 def test_settings_should_be_denied_for_member_on_closed_project
436 436 Project.find(1).close
437 437 @request.session[:user_id] = 2 # manager
438 438
439 439 get :settings, :id => 1
440 440 assert_response 403
441 441 end
442 442
443 443 def test_settings_should_be_denied_for_anonymous_on_closed_project
444 444 Project.find(1).close
445 445
446 446 get :settings, :id => 1
447 447 assert_response 302
448 448 end
449 449
450 450 def test_setting_with_wiki_module_and_no_wiki
451 451 Project.find(1).wiki.destroy
452 452 Role.find(1).add_permission! :manage_wiki
453 453 @request.session[:user_id] = 2
454 454
455 455 get :settings, :id => 1
456 456 assert_response :success
457 457 assert_template 'settings'
458 458
459 459 assert_select 'form[action=?]', '/projects/ecookbook/wiki' do
460 460 assert_select 'input[name=?]', 'wiki[start_page]'
461 461 end
462 462 end
463 463
464 464 def test_update
465 465 @request.session[:user_id] = 2 # manager
466 466 post :update, :id => 1, :project => {:name => 'Test changed name',
467 467 :issue_custom_field_ids => ['']}
468 468 assert_redirected_to '/projects/ecookbook/settings'
469 469 project = Project.find(1)
470 470 assert_equal 'Test changed name', project.name
471 471 end
472 472
473 473 def test_update_with_failure
474 474 @request.session[:user_id] = 2 # manager
475 475 post :update, :id => 1, :project => {:name => ''}
476 476 assert_response :success
477 477 assert_template 'settings'
478 478 assert_select_error /name cannot be blank/i
479 479 end
480 480
481 481 def test_update_should_be_denied_for_member_on_closed_project
482 482 Project.find(1).close
483 483 @request.session[:user_id] = 2 # manager
484 484
485 485 post :update, :id => 1, :project => {:name => 'Closed'}
486 486 assert_response 403
487 487 assert_equal 'eCookbook', Project.find(1).name
488 488 end
489 489
490 490 def test_update_should_be_denied_for_anonymous_on_closed_project
491 491 Project.find(1).close
492 492
493 493 post :update, :id => 1, :project => {:name => 'Closed'}
494 494 assert_response 302
495 495 assert_equal 'eCookbook', Project.find(1).name
496 496 end
497 497
498 498 def test_update_child_project_without_parent_permission_should_not_show_validation_error
499 499 child = Project.generate_with_parent!
500 500 user = User.generate!
501 501 User.add_to_project(user, child, Role.generate!(:permissions => [:edit_project]))
502 502 @request.session[:user_id] = user.id
503 503
504 504 post :update, :id => child.id, :project => {:name => 'Updated'}
505 505 assert_response 302
506 506 assert_match /Successful update/, flash[:notice]
507 507 end
508 508
509 509 def test_modules
510 510 @request.session[:user_id] = 2
511 511 Project.find(1).enabled_module_names = ['issue_tracking', 'news']
512 512
513 513 post :modules, :id => 1, :enabled_module_names => ['issue_tracking', 'repository', 'documents']
514 514 assert_redirected_to '/projects/ecookbook/settings/modules'
515 515 assert_equal ['documents', 'issue_tracking', 'repository'], Project.find(1).enabled_module_names.sort
516 516 end
517 517
518 518 def test_destroy_leaf_project_without_confirmation_should_show_confirmation
519 519 @request.session[:user_id] = 1 # admin
520 520
521 521 assert_no_difference 'Project.count' do
522 522 delete :destroy, :id => 2
523 523 assert_response :success
524 524 assert_template 'destroy'
525 525 end
526 526 end
527 527
528 528 def test_destroy_without_confirmation_should_show_confirmation_with_subprojects
529 529 @request.session[:user_id] = 1 # admin
530 530
531 531 assert_no_difference 'Project.count' do
532 532 delete :destroy, :id => 1
533 533 assert_response :success
534 534 assert_template 'destroy'
535 535 end
536 536 assert_select 'strong',
537 537 :text => ['Private child of eCookbook',
538 538 'Child of private child, eCookbook Subproject 1',
539 539 'eCookbook Subproject 2'].join(', ')
540 540 end
541 541
542 542 def test_destroy_with_confirmation_should_destroy_the_project_and_subprojects
543 543 @request.session[:user_id] = 1 # admin
544 544
545 545 assert_difference 'Project.count', -5 do
546 546 delete :destroy, :id => 1, :confirm => 1
547 547 assert_redirected_to '/admin/projects'
548 548 end
549 549 assert_nil Project.find_by_id(1)
550 550 end
551 551
552 552 def test_archive
553 553 @request.session[:user_id] = 1 # admin
554 554 post :archive, :id => 1
555 555 assert_redirected_to '/admin/projects'
556 556 assert !Project.find(1).active?
557 557 end
558 558
559 559 def test_archive_with_failure
560 560 @request.session[:user_id] = 1
561 561 Project.any_instance.stubs(:archive).returns(false)
562 562 post :archive, :id => 1
563 563 assert_redirected_to '/admin/projects'
564 564 assert_match /project cannot be archived/i, flash[:error]
565 565 end
566 566
567 567 def test_unarchive
568 568 @request.session[:user_id] = 1 # admin
569 569 Project.find(1).archive
570 570 post :unarchive, :id => 1
571 571 assert_redirected_to '/admin/projects'
572 572 assert Project.find(1).active?
573 573 end
574 574
575 575 def test_close
576 576 @request.session[:user_id] = 2
577 577 post :close, :id => 1
578 578 assert_redirected_to '/projects/ecookbook'
579 579 assert_equal Project::STATUS_CLOSED, Project.find(1).status
580 580 end
581 581
582 582 def test_reopen
583 583 Project.find(1).close
584 584 @request.session[:user_id] = 2
585 585 post :reopen, :id => 1
586 586 assert_redirected_to '/projects/ecookbook'
587 587 assert Project.find(1).active?
588 588 end
589 589
590 590 def test_project_breadcrumbs_should_be_limited_to_3_ancestors
591 591 CustomField.delete_all
592 592 parent = nil
593 593 6.times do |i|
594 594 p = Project.generate_with_parent!(parent)
595 595 get :show, :id => p
596 596 assert_select '#header h1' do
597 597 assert_select 'a', :count => [i, 3].min
598 598 end
599 599
600 600 parent = p
601 601 end
602 602 end
603 603
604 604 def test_get_copy
605 605 @request.session[:user_id] = 1 # admin
606 606 get :copy, :id => 1
607 607 assert_response :success
608 608 assert_template 'copy'
609 609 assert assigns(:project)
610 610 assert_equal Project.find(1).description, assigns(:project).description
611 611 assert_nil assigns(:project).id
612 612
613 613 assert_select 'input[name=?][value=?]', 'project[enabled_module_names][]', 'issue_tracking', 1
614 614 end
615 615
616 616 def test_get_copy_with_invalid_source_should_respond_with_404
617 617 @request.session[:user_id] = 1
618 618 get :copy, :id => 99
619 619 assert_response 404
620 620 end
621 621
622 622 def test_get_copy_should_preselect_custom_fields
623 623 field1 = IssueCustomField.generate!(:is_for_all => false)
624 624 field2 = IssueCustomField.generate!(:is_for_all => false)
625 625 source = Project.generate!(:issue_custom_fields => [field1])
626 626 @request.session[:user_id] = 1
627 627
628 628 get :copy, :id => source.id
629 629 assert_response :success
630 630 assert_select 'fieldset#project_issue_custom_fields' do
631 631 assert_select 'input[type=checkbox][value=?][checked=checked]', field1.id.to_s
632 632 assert_select 'input[type=checkbox][value=?]:not([checked])', field2.id.to_s
633 633 end
634 634 end
635 635
636 636 def test_post_copy_should_copy_requested_items
637 637 @request.session[:user_id] = 1 # admin
638 638 CustomField.delete_all
639 639
640 640 assert_difference 'Project.count' do
641 641 post :copy, :id => 1,
642 642 :project => {
643 643 :name => 'Copy',
644 644 :identifier => 'unique-copy',
645 645 :tracker_ids => ['1', '2', '3', ''],
646 646 :enabled_module_names => %w(issue_tracking time_tracking)
647 647 },
648 648 :only => %w(issues versions)
649 649 end
650 650 project = Project.find('unique-copy')
651 651 source = Project.find(1)
652 652 assert_equal %w(issue_tracking time_tracking), project.enabled_module_names.sort
653 653
654 654 assert_equal source.versions.count, project.versions.count, "All versions were not copied"
655 655 assert_equal source.issues.count, project.issues.count, "All issues were not copied"
656 656 assert_equal 0, project.members.count
657 657 end
658 658
659 659 def test_post_copy_should_redirect_to_settings_when_successful
660 660 @request.session[:user_id] = 1 # admin
661 661 post :copy, :id => 1, :project => {:name => 'Copy', :identifier => 'unique-copy'}
662 662 assert_response :redirect
663 663 assert_redirected_to :controller => 'projects', :action => 'settings', :id => 'unique-copy'
664 664 end
665 665
666 666 def test_post_copy_with_failure
667 667 @request.session[:user_id] = 1
668 668 post :copy, :id => 1, :project => {:name => 'Copy', :identifier => ''}
669 669 assert_response :success
670 670 assert_template 'copy'
671 671 end
672 672
673 673 def test_jump_should_redirect_to_active_tab
674 674 get :show, :id => 1, :jump => 'issues'
675 675 assert_redirected_to '/projects/ecookbook/issues'
676 676 end
677 677
678 678 def test_jump_should_not_redirect_to_inactive_tab
679 679 get :show, :id => 3, :jump => 'documents'
680 680 assert_response :success
681 681 assert_template 'show'
682 682 end
683 683
684 684 def test_jump_should_not_redirect_to_unknown_tab
685 685 get :show, :id => 3, :jump => 'foobar'
686 686 assert_response :success
687 687 assert_template 'show'
688 688 end
689 689
690 690 def test_body_should_have_project_css_class
691 691 get :show, :id => 1
692 692 assert_select 'body.project-ecookbook'
693 693 end
694
695 def test_project_menu_should_include_new_issue_link
696 @request.session[:user_id] = 2
697 get :show, :id => 1
698 assert_select '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]', :text => 'New issue'
699 end
700
701 def test_project_menu_should_not_include_new_issue_link_for_project_without_trackers
702 Project.find(1).trackers.clear
703
704 @request.session[:user_id] = 2
705 get :show, :id => 1
706 assert_select '#main-menu a.new-issue', 0
707 end
708
709 def test_project_menu_should_not_include_new_issue_link_for_users_with_copy_issues_permission_only
710 role = Role.find(1)
711 role.remove_permission! :add_issues
712 role.add_permission! :copy_issues
713
714 @request.session[:user_id] = 2
715 get :show, :id => 1
716 assert_select '#main-menu a.new-issue', 0
717 end
718 694 end
@@ -1,86 +1,80
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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
20 20 module RedmineMenuTestHelper
21 21 # Assertions
22 22 def assert_number_of_items_in_menu(menu_name, count)
23 23 assert Redmine::MenuManager.items(menu_name).size >= count, "Menu has less than #{count} items"
24 24 end
25 25
26 26 def assert_menu_contains_item_named(menu_name, item_name)
27 27 assert Redmine::MenuManager.items(menu_name).collect(&:name).include?(item_name.to_sym), "Menu did not have an item named #{item_name}"
28 28 end
29 29
30 30 # Helpers
31 31 def get_menu_item(menu_name, item_name)
32 32 Redmine::MenuManager.items(menu_name).find {|item| item.name == item_name.to_sym}
33 33 end
34 34 end
35 35
36 36 class RedmineTest < ActiveSupport::TestCase
37 37 include RedmineMenuTestHelper
38 38
39 39 def test_top_menu
40 40 assert_number_of_items_in_menu :top_menu, 5
41 41 assert_menu_contains_item_named :top_menu, :home
42 42 assert_menu_contains_item_named :top_menu, :my_page
43 43 assert_menu_contains_item_named :top_menu, :projects
44 44 assert_menu_contains_item_named :top_menu, :administration
45 45 assert_menu_contains_item_named :top_menu, :help
46 46 end
47 47
48 48 def test_account_menu
49 49 assert_number_of_items_in_menu :account_menu, 4
50 50 assert_menu_contains_item_named :account_menu, :login
51 51 assert_menu_contains_item_named :account_menu, :register
52 52 assert_menu_contains_item_named :account_menu, :my_account
53 53 assert_menu_contains_item_named :account_menu, :logout
54 54 end
55 55
56 56 def test_application_menu
57 57 assert_number_of_items_in_menu :application_menu, 0
58 58 end
59 59
60 60 def test_admin_menu
61 61 assert_number_of_items_in_menu :admin_menu, 0
62 62 end
63 63
64 64 def test_project_menu
65 assert_number_of_items_in_menu :project_menu, 14
65 assert_number_of_items_in_menu :project_menu, 13
66 66 assert_menu_contains_item_named :project_menu, :overview
67 67 assert_menu_contains_item_named :project_menu, :activity
68 68 assert_menu_contains_item_named :project_menu, :roadmap
69 69 assert_menu_contains_item_named :project_menu, :issues
70 assert_menu_contains_item_named :project_menu, :new_issue
71 70 assert_menu_contains_item_named :project_menu, :calendar
72 71 assert_menu_contains_item_named :project_menu, :gantt
73 72 assert_menu_contains_item_named :project_menu, :news
74 73 assert_menu_contains_item_named :project_menu, :documents
75 74 assert_menu_contains_item_named :project_menu, :wiki
76 75 assert_menu_contains_item_named :project_menu, :boards
77 76 assert_menu_contains_item_named :project_menu, :files
78 77 assert_menu_contains_item_named :project_menu, :repository
79 78 assert_menu_contains_item_named :project_menu, :settings
80 79 end
81
82 def test_new_issue_should_have_root_as_a_parent
83 new_issue = get_menu_item(:project_menu, :new_issue)
84 assert_equal :root, new_issue.parent.name
85 end
86 80 end
General Comments 0
You need to be logged in to leave comments. Login now