OSDN Git Service

Enable tracker update on issue edit form (#2405).
[redminele/redmine.git] / test / functional / issues_controller_test.rb
1 # Redmine - project management software
2 # Copyright (C) 2006-2008  Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'issues_controller'
20
21 # Re-raise errors caught by the controller.
22 class IssuesController; def rescue_action(e) raise e end; end
23
24 class IssuesControllerTest < ActionController::TestCase
25   fixtures :projects,
26            :users,
27            :roles,
28            :members,
29            :member_roles,
30            :issues,
31            :issue_statuses,
32            :versions,
33            :trackers,
34            :projects_trackers,
35            :issue_categories,
36            :enabled_modules,
37            :enumerations,
38            :attachments,
39            :workflows,
40            :custom_fields,
41            :custom_values,
42            :custom_fields_projects,
43            :custom_fields_trackers,
44            :time_entries,
45            :journals,
46            :journal_details,
47            :queries
48   
49   def setup
50     @controller = IssuesController.new
51     @request    = ActionController::TestRequest.new
52     @response   = ActionController::TestResponse.new
53     User.current = nil
54   end
55   
56   def test_index_routing
57     assert_routing(
58       {:method => :get, :path => '/issues'},
59       :controller => 'issues', :action => 'index'
60     )
61   end
62
63   def test_index
64     Setting.default_language = 'en'
65     
66     get :index
67     assert_response :success
68     assert_template 'index.rhtml'
69     assert_not_nil assigns(:issues)
70     assert_nil assigns(:project)
71     assert_tag :tag => 'a', :content => /Can't print recipes/
72     assert_tag :tag => 'a', :content => /Subproject issue/
73     # private projects hidden
74     assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
75     assert_no_tag :tag => 'a', :content => /Issue on project 2/
76     # project column
77     assert_tag :tag => 'th', :content => /Project/
78   end
79   
80   def test_index_should_not_list_issues_when_module_disabled
81     EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
82     get :index
83     assert_response :success
84     assert_template 'index.rhtml'
85     assert_not_nil assigns(:issues)
86     assert_nil assigns(:project)
87     assert_no_tag :tag => 'a', :content => /Can't print recipes/
88     assert_tag :tag => 'a', :content => /Subproject issue/
89   end
90
91   def test_index_with_project_routing
92     assert_routing(
93       {:method => :get, :path => '/projects/23/issues'},
94       :controller => 'issues', :action => 'index', :project_id => '23'
95     )
96   end
97   
98   def test_index_should_not_list_issues_when_module_disabled
99     EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
100     get :index
101     assert_response :success
102     assert_template 'index.rhtml'
103     assert_not_nil assigns(:issues)
104     assert_nil assigns(:project)
105     assert_no_tag :tag => 'a', :content => /Can't print recipes/
106     assert_tag :tag => 'a', :content => /Subproject issue/
107   end
108
109   def test_index_with_project_routing
110     assert_routing(
111       {:method => :get, :path => 'projects/23/issues'},
112       :controller => 'issues', :action => 'index', :project_id => '23'
113     )
114   end
115   
116   def test_index_with_project
117     Setting.display_subprojects_issues = 0
118     get :index, :project_id => 1
119     assert_response :success
120     assert_template 'index.rhtml'
121     assert_not_nil assigns(:issues)
122     assert_tag :tag => 'a', :content => /Can't print recipes/
123     assert_no_tag :tag => 'a', :content => /Subproject issue/
124   end
125   
126   def test_index_with_project_and_subprojects
127     Setting.display_subprojects_issues = 1
128     get :index, :project_id => 1
129     assert_response :success
130     assert_template 'index.rhtml'
131     assert_not_nil assigns(:issues)
132     assert_tag :tag => 'a', :content => /Can't print recipes/
133     assert_tag :tag => 'a', :content => /Subproject issue/
134     assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
135   end
136   
137   def test_index_with_project_and_subprojects_should_show_private_subprojects
138     @request.session[:user_id] = 2
139     Setting.display_subprojects_issues = 1
140     get :index, :project_id => 1
141     assert_response :success
142     assert_template 'index.rhtml'
143     assert_not_nil assigns(:issues)
144     assert_tag :tag => 'a', :content => /Can't print recipes/
145     assert_tag :tag => 'a', :content => /Subproject issue/
146     assert_tag :tag => 'a', :content => /Issue of a private subproject/
147   end
148   
149   def test_index_with_project_routing_formatted
150     assert_routing(
151       {:method => :get, :path => 'projects/23/issues.pdf'},
152       :controller => 'issues', :action => 'index', :project_id => '23', :format => 'pdf'
153     )
154     assert_routing(
155       {:method => :get, :path => 'projects/23/issues.atom'},
156       :controller => 'issues', :action => 'index', :project_id => '23', :format => 'atom'
157     )
158   end
159   
160   def test_index_with_project_and_filter
161     get :index, :project_id => 1, :set_filter => 1
162     assert_response :success
163     assert_template 'index.rhtml'
164     assert_not_nil assigns(:issues)
165   end
166   
167   def test_index_with_query
168     get :index, :project_id => 1, :query_id => 5
169     assert_response :success
170     assert_template 'index.rhtml'
171     assert_not_nil assigns(:issues)
172     assert_nil assigns(:issue_count_by_group)
173   end
174   
175   def test_index_with_query_grouped_by_tracker
176     get :index, :project_id => 1, :query_id => 6
177     assert_response :success
178     assert_template 'index.rhtml'
179     assert_not_nil assigns(:issues)
180     count_by_group = assigns(:issue_count_by_group)
181     assert_kind_of Hash, count_by_group
182     assert_kind_of Tracker, count_by_group.keys.first
183     assert_not_nil count_by_group[Tracker.find(1)]
184   end
185   
186   def test_index_with_query_grouped_by_list_custom_field
187     get :index, :project_id => 1, :query_id => 9
188     assert_response :success
189     assert_template 'index.rhtml'
190     assert_not_nil assigns(:issues)
191     count_by_group = assigns(:issue_count_by_group)
192     assert_kind_of Hash, count_by_group
193     assert_kind_of String, count_by_group.keys.first
194     assert_not_nil count_by_group['MySQL']
195   end
196   
197   def test_index_sort_by_field_not_included_in_columns
198     Setting.issue_list_default_columns = %w(subject author)
199     get :index, :sort => 'tracker'
200   end
201   
202   def test_index_csv_with_project
203     Setting.default_language = 'en'
204     
205     get :index, :format => 'csv'
206     assert_response :success
207     assert_not_nil assigns(:issues)
208     assert_equal 'text/csv', @response.content_type
209     assert @response.body.starts_with?("#,")
210
211     get :index, :project_id => 1, :format => 'csv'
212     assert_response :success
213     assert_not_nil assigns(:issues)
214     assert_equal 'text/csv', @response.content_type
215   end
216   
217   def test_index_formatted
218     assert_routing(
219       {:method => :get, :path => 'issues.pdf'},
220       :controller => 'issues', :action => 'index', :format => 'pdf'
221     )
222     assert_routing(
223       {:method => :get, :path => 'issues.atom'},
224       :controller => 'issues', :action => 'index', :format => 'atom'
225     )
226   end
227   
228   def test_index_pdf
229     get :index, :format => 'pdf'
230     assert_response :success
231     assert_not_nil assigns(:issues)
232     assert_equal 'application/pdf', @response.content_type
233     
234     get :index, :project_id => 1, :format => 'pdf'
235     assert_response :success
236     assert_not_nil assigns(:issues)
237     assert_equal 'application/pdf', @response.content_type
238     
239     get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
240     assert_response :success
241     assert_not_nil assigns(:issues)
242     assert_equal 'application/pdf', @response.content_type
243   end
244   
245   def test_index_sort
246     get :index, :sort => 'tracker,id:desc'
247     assert_response :success
248     
249     sort_params = @request.session['issues_index_sort']
250     assert sort_params.is_a?(String)
251     assert_equal 'tracker,id:desc', sort_params
252     
253     issues = assigns(:issues)
254     assert_not_nil issues
255     assert !issues.empty?
256     assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
257   end
258   
259   def test_index_with_columns
260     columns = ['tracker', 'subject', 'assigned_to']
261     get :index, :set_filter => 1, :query => { 'column_names' => columns}
262     assert_response :success
263     
264     # query should use specified columns
265     query = assigns(:query)
266     assert_kind_of Query, query
267     assert_equal columns, query.column_names.map(&:to_s)
268     
269     # columns should be stored in session
270     assert_kind_of Hash, session[:query]
271     assert_kind_of Array, session[:query][:column_names]
272     assert_equal columns, session[:query][:column_names].map(&:to_s)
273   end
274
275   def test_gantt
276     get :gantt, :project_id => 1
277     assert_response :success
278     assert_template 'gantt.rhtml'
279     assert_not_nil assigns(:gantt)
280     events = assigns(:gantt).events
281     assert_not_nil events
282     # Issue with start and due dates
283     i = Issue.find(1)
284     assert_not_nil i.due_date
285     assert events.include?(Issue.find(1))
286     # Issue with without due date but targeted to a version with date
287     i = Issue.find(2)
288     assert_nil i.due_date
289     assert events.include?(i)
290   end
291
292   def test_cross_project_gantt
293     get :gantt
294     assert_response :success
295     assert_template 'gantt.rhtml'
296     assert_not_nil assigns(:gantt)
297     events = assigns(:gantt).events
298     assert_not_nil events
299   end
300
301   def test_gantt_export_to_pdf
302     get :gantt, :project_id => 1, :format => 'pdf'
303     assert_response :success
304     assert_equal 'application/pdf', @response.content_type
305     assert @response.body.starts_with?('%PDF')
306     assert_not_nil assigns(:gantt)
307   end
308
309   def test_cross_project_gantt_export_to_pdf
310     get :gantt, :format => 'pdf'
311     assert_response :success
312     assert_equal 'application/pdf', @response.content_type
313     assert @response.body.starts_with?('%PDF')
314     assert_not_nil assigns(:gantt)
315   end
316   
317   if Object.const_defined?(:Magick)
318     def test_gantt_image
319       get :gantt, :project_id => 1, :format => 'png'
320       assert_response :success
321       assert_equal 'image/png', @response.content_type
322     end
323   else
324     puts "RMagick not installed. Skipping tests !!!"
325   end
326   
327   def test_calendar
328     get :calendar, :project_id => 1
329     assert_response :success
330     assert_template 'calendar'
331     assert_not_nil assigns(:calendar)
332   end
333   
334   def test_cross_project_calendar
335     get :calendar
336     assert_response :success
337     assert_template 'calendar'
338     assert_not_nil assigns(:calendar)
339   end
340   
341   def test_changes
342     get :changes, :project_id => 1
343     assert_response :success
344     assert_not_nil assigns(:journals)
345     assert_equal 'application/atom+xml', @response.content_type
346   end
347   
348   def test_show_routing
349     assert_routing(
350       {:method => :get, :path => '/issues/64'},
351       :controller => 'issues', :action => 'show', :id => '64'
352     )
353   end
354   
355   def test_show_routing_formatted
356     assert_routing(
357       {:method => :get, :path => '/issues/2332.pdf'},
358       :controller => 'issues', :action => 'show', :id => '2332', :format => 'pdf'
359     )
360     assert_routing(
361       {:method => :get, :path => '/issues/23123.atom'},
362       :controller => 'issues', :action => 'show', :id => '23123', :format => 'atom'
363     )
364   end
365   
366   def test_show_by_anonymous
367     get :show, :id => 1
368     assert_response :success
369     assert_template 'show.rhtml'
370     assert_not_nil assigns(:issue)
371     assert_equal Issue.find(1), assigns(:issue)
372     
373     # anonymous role is allowed to add a note
374     assert_tag :tag => 'form',
375                :descendant => { :tag => 'fieldset',
376                                 :child => { :tag => 'legend', 
377                                             :content => /Notes/ } }
378   end
379   
380   def test_show_by_manager
381     @request.session[:user_id] = 2
382     get :show, :id => 1
383     assert_response :success
384     
385     assert_tag :tag => 'form',
386                :descendant => { :tag => 'fieldset',
387                                 :child => { :tag => 'legend', 
388                                             :content => /Change properties/ } },
389                :descendant => { :tag => 'fieldset',
390                                 :child => { :tag => 'legend', 
391                                             :content => /Log time/ } },
392                :descendant => { :tag => 'fieldset',
393                                 :child => { :tag => 'legend', 
394                                             :content => /Notes/ } }
395   end
396   
397   def test_show_should_deny_anonymous_access_without_permission
398     Role.anonymous.remove_permission!(:view_issues)
399     get :show, :id => 1
400     assert_response :redirect
401   end
402   
403   def test_show_should_deny_non_member_access_without_permission
404     Role.non_member.remove_permission!(:view_issues)
405     @request.session[:user_id] = 9
406     get :show, :id => 1
407     assert_response 403
408   end
409   
410   def test_show_should_deny_member_access_without_permission
411     Role.find(1).remove_permission!(:view_issues)
412     @request.session[:user_id] = 2
413     get :show, :id => 1
414     assert_response 403
415   end
416   
417   def test_show_should_not_disclose_relations_to_invisible_issues
418     Setting.cross_project_issue_relations = '1'
419     IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
420     # Relation to a private project issue
421     IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
422     
423     get :show, :id => 1
424     assert_response :success
425     
426     assert_tag :div, :attributes => { :id => 'relations' },
427                      :descendant => { :tag => 'a', :content => /#2$/ }
428     assert_no_tag :div, :attributes => { :id => 'relations' },
429                         :descendant => { :tag => 'a', :content => /#4$/ }
430   end
431   
432   def test_show_atom
433     get :show, :id => 2, :format => 'atom'
434     assert_response :success
435     assert_template 'changes.rxml'
436     # Inline image
437     assert @response.body.include?("&lt;img src=\"http://test.host/attachments/download/10\" alt=\"\" /&gt;"), "Body did not match. Body: #{@response.body}"
438   end
439   
440   def test_new_routing
441     assert_routing(
442       {:method => :get, :path => '/projects/1/issues/new'},
443       :controller => 'issues', :action => 'new', :project_id => '1'
444     )
445     assert_recognizes(
446       {:controller => 'issues', :action => 'new', :project_id => '1'},
447       {:method => :post, :path => '/projects/1/issues'}
448     )
449   end
450
451   def test_show_export_to_pdf
452     get :show, :id => 3, :format => 'pdf'
453     assert_response :success
454     assert_equal 'application/pdf', @response.content_type
455     assert @response.body.starts_with?('%PDF')
456     assert_not_nil assigns(:issue)
457   end
458
459   def test_get_new
460     @request.session[:user_id] = 2
461     get :new, :project_id => 1, :tracker_id => 1
462     assert_response :success
463     assert_template 'new'
464     
465     assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
466                                                  :value => 'Default string' }
467   end
468
469   def test_get_new_without_tracker_id
470     @request.session[:user_id] = 2
471     get :new, :project_id => 1
472     assert_response :success
473     assert_template 'new'
474     
475     issue = assigns(:issue)
476     assert_not_nil issue
477     assert_equal Project.find(1).trackers.first, issue.tracker
478   end
479   
480   def test_get_new_with_no_default_status_should_display_an_error
481     @request.session[:user_id] = 2
482     IssueStatus.delete_all
483     
484     get :new, :project_id => 1
485     assert_response 500
486     assert_not_nil flash[:error]
487     assert_tag :tag => 'div', :attributes => { :class => /error/ },
488                               :content => /No default issue/
489   end
490   
491   def test_get_new_with_no_tracker_should_display_an_error
492     @request.session[:user_id] = 2
493     Tracker.delete_all
494     
495     get :new, :project_id => 1
496     assert_response 500
497     assert_not_nil flash[:error]
498     assert_tag :tag => 'div', :attributes => { :class => /error/ },
499                               :content => /No tracker/
500   end
501   
502   def test_update_new_form
503     @request.session[:user_id] = 2
504     xhr :post, :update_form, :project_id => 1,
505                      :issue => {:tracker_id => 2, 
506                                 :subject => 'This is the test_new issue',
507                                 :description => 'This is the description',
508                                 :priority_id => 5}
509     assert_response :success
510     assert_template 'attributes'
511     
512     issue = assigns(:issue)
513     assert_kind_of Issue, issue
514     assert_equal 1, issue.project_id
515     assert_equal 2, issue.tracker_id
516     assert_equal 'This is the test_new issue', issue.subject
517   end
518   
519   def test_post_new
520     @request.session[:user_id] = 2
521     assert_difference 'Issue.count' do
522       post :new, :project_id => 1, 
523                  :issue => {:tracker_id => 3,
524                             :subject => 'This is the test_new issue',
525                             :description => 'This is the description',
526                             :priority_id => 5,
527                             :estimated_hours => '',
528                             :custom_field_values => {'2' => 'Value for field 2'}}
529     end
530     assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
531     
532     issue = Issue.find_by_subject('This is the test_new issue')
533     assert_not_nil issue
534     assert_equal 2, issue.author_id
535     assert_equal 3, issue.tracker_id
536     assert_nil issue.estimated_hours
537     v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
538     assert_not_nil v
539     assert_equal 'Value for field 2', v.value
540   end
541   
542   def test_post_new_and_continue
543     @request.session[:user_id] = 2
544     post :new, :project_id => 1, 
545                :issue => {:tracker_id => 3,
546                           :subject => 'This is first issue',
547                           :priority_id => 5},
548                :continue => ''
549     assert_redirected_to :controller => 'issues', :action => 'new', :tracker_id => 3
550   end
551   
552   def test_post_new_without_custom_fields_param
553     @request.session[:user_id] = 2
554     assert_difference 'Issue.count' do
555       post :new, :project_id => 1, 
556                  :issue => {:tracker_id => 1,
557                             :subject => 'This is the test_new issue',
558                             :description => 'This is the description',
559                             :priority_id => 5}
560     end
561     assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
562   end
563
564   def test_post_new_with_required_custom_field_and_without_custom_fields_param
565     field = IssueCustomField.find_by_name('Database')
566     field.update_attribute(:is_required, true)
567
568     @request.session[:user_id] = 2
569     post :new, :project_id => 1, 
570                :issue => {:tracker_id => 1,
571                           :subject => 'This is the test_new issue',
572                           :description => 'This is the description',
573                           :priority_id => 5}
574     assert_response :success
575     assert_template 'new'
576     issue = assigns(:issue)
577     assert_not_nil issue
578     assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
579   end
580   
581   def test_post_new_with_watchers
582     @request.session[:user_id] = 2
583     ActionMailer::Base.deliveries.clear
584     
585     assert_difference 'Watcher.count', 2 do
586       post :new, :project_id => 1, 
587                  :issue => {:tracker_id => 1,
588                             :subject => 'This is a new issue with watchers',
589                             :description => 'This is the description',
590                             :priority_id => 5,
591                             :watcher_user_ids => ['2', '3']}
592     end
593     issue = Issue.find_by_subject('This is a new issue with watchers')
594     assert_not_nil issue
595     assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
596     
597     # Watchers added
598     assert_equal [2, 3], issue.watcher_user_ids.sort
599     assert issue.watched_by?(User.find(3))
600     # Watchers notified
601     mail = ActionMailer::Base.deliveries.last
602     assert_kind_of TMail::Mail, mail
603     assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
604   end
605   
606   def test_post_new_should_send_a_notification
607     ActionMailer::Base.deliveries.clear
608     @request.session[:user_id] = 2
609     assert_difference 'Issue.count' do
610       post :new, :project_id => 1, 
611                  :issue => {:tracker_id => 3,
612                             :subject => 'This is the test_new issue',
613                             :description => 'This is the description',
614                             :priority_id => 5,
615                             :estimated_hours => '',
616                             :custom_field_values => {'2' => 'Value for field 2'}}
617     end
618     assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
619     
620     assert_equal 1, ActionMailer::Base.deliveries.size
621   end
622   
623   def test_post_should_preserve_fields_values_on_validation_failure
624     @request.session[:user_id] = 2
625     post :new, :project_id => 1, 
626                :issue => {:tracker_id => 1,
627                           # empty subject
628                           :subject => '',
629                           :description => 'This is a description',
630                           :priority_id => 6,
631                           :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
632     assert_response :success
633     assert_template 'new'
634     
635     assert_tag :textarea, :attributes => { :name => 'issue[description]' },
636                           :content => 'This is a description'
637     assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
638                         :child => { :tag => 'option', :attributes => { :selected => 'selected',
639                                                                        :value => '6' },
640                                                       :content => 'High' }  
641     # Custom fields
642     assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
643                         :child => { :tag => 'option', :attributes => { :selected => 'selected',
644                                                                        :value => 'Oracle' },
645                                                       :content => 'Oracle' }  
646     assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
647                                         :value => 'Value for field 2'}
648   end
649   
650   def test_copy_routing
651     assert_routing(
652       {:method => :get, :path => '/projects/world_domination/issues/567/copy'},
653       :controller => 'issues', :action => 'new', :project_id => 'world_domination', :copy_from => '567'
654     )
655   end
656   
657   def test_copy_issue
658     @request.session[:user_id] = 2
659     get :new, :project_id => 1, :copy_from => 1
660     assert_template 'new'
661     assert_not_nil assigns(:issue)
662     orig = Issue.find(1)
663     assert_equal orig.subject, assigns(:issue).subject
664   end
665   
666   def test_edit_routing
667     assert_routing(
668       {:method => :get, :path => '/issues/1/edit'},
669       :controller => 'issues', :action => 'edit', :id => '1'
670     )
671     assert_recognizes( #TODO: use a PUT on the issue URI isntead, need to adjust form
672       {:controller => 'issues', :action => 'edit', :id => '1'},
673       {:method => :post, :path => '/issues/1/edit'}
674     )
675   end
676   
677   def test_get_edit
678     @request.session[:user_id] = 2
679     get :edit, :id => 1
680     assert_response :success
681     assert_template 'edit'
682     assert_not_nil assigns(:issue)
683     assert_equal Issue.find(1), assigns(:issue)
684   end
685   
686   def test_get_edit_with_params
687     @request.session[:user_id] = 2
688     get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
689     assert_response :success
690     assert_template 'edit'
691     
692     issue = assigns(:issue)
693     assert_not_nil issue
694     
695     assert_equal 5, issue.status_id
696     assert_tag :select, :attributes => { :name => 'issue[status_id]' },
697                         :child => { :tag => 'option', 
698                                     :content => 'Closed',
699                                     :attributes => { :selected => 'selected' } }
700                                     
701     assert_equal 7, issue.priority_id
702     assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
703                         :child => { :tag => 'option', 
704                                     :content => 'Urgent',
705                                     :attributes => { :selected => 'selected' } }
706   end
707
708   def test_update_edit_form
709     @request.session[:user_id] = 2
710     xhr :post, :update_form, :project_id => 1,
711                              :id => 1,
712                              :issue => {:tracker_id => 2, 
713                                         :subject => 'This is the test_new issue',
714                                         :description => 'This is the description',
715                                         :priority_id => 5}
716     assert_response :success
717     assert_template 'attributes'
718     
719     issue = assigns(:issue)
720     assert_kind_of Issue, issue
721     assert_equal 1, issue.id
722     assert_equal 1, issue.project_id
723     assert_equal 2, issue.tracker_id
724     assert_equal 'This is the test_new issue', issue.subject
725   end
726   
727   def test_reply_routing
728     assert_routing(
729       {:method => :post, :path => '/issues/1/quoted'},
730       :controller => 'issues', :action => 'reply', :id => '1'
731     )
732   end
733   
734   def test_reply_to_issue
735     @request.session[:user_id] = 2
736     get :reply, :id => 1
737     assert_response :success
738     assert_select_rjs :show, "update"
739   end
740
741   def test_reply_to_note
742     @request.session[:user_id] = 2
743     get :reply, :id => 1, :journal_id => 2
744     assert_response :success
745     assert_select_rjs :show, "update"
746   end
747
748   def test_post_edit_without_custom_fields_param
749     @request.session[:user_id] = 2
750     ActionMailer::Base.deliveries.clear
751     
752     issue = Issue.find(1)
753     assert_equal '125', issue.custom_value_for(2).value
754     old_subject = issue.subject
755     new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
756     
757     assert_difference('Journal.count') do
758       assert_difference('JournalDetail.count', 2) do
759         post :edit, :id => 1, :issue => {:subject => new_subject,
760                                          :priority_id => '6',
761                                          :category_id => '1' # no change
762                                         }
763       end
764     end
765     assert_redirected_to :action => 'show', :id => '1'
766     issue.reload
767     assert_equal new_subject, issue.subject
768     # Make sure custom fields were not cleared
769     assert_equal '125', issue.custom_value_for(2).value
770     
771     mail = ActionMailer::Base.deliveries.last
772     assert_kind_of TMail::Mail, mail
773     assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
774     assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
775   end
776   
777   def test_post_edit_with_custom_field_change
778     @request.session[:user_id] = 2
779     issue = Issue.find(1)
780     assert_equal '125', issue.custom_value_for(2).value
781     
782     assert_difference('Journal.count') do
783       assert_difference('JournalDetail.count', 3) do
784         post :edit, :id => 1, :issue => {:subject => 'Custom field change',
785                                          :priority_id => '6',
786                                          :category_id => '1', # no change
787                                          :custom_field_values => { '2' => 'New custom value' }
788                                         }
789       end
790     end
791     assert_redirected_to :action => 'show', :id => '1'
792     issue.reload
793     assert_equal 'New custom value', issue.custom_value_for(2).value
794     
795     mail = ActionMailer::Base.deliveries.last
796     assert_kind_of TMail::Mail, mail
797     assert mail.body.include?("Searchable field changed from 125 to New custom value")
798   end
799   
800   def test_post_edit_with_status_and_assignee_change
801     issue = Issue.find(1)
802     assert_equal 1, issue.status_id
803     @request.session[:user_id] = 2
804     assert_difference('TimeEntry.count', 0) do
805       post :edit,
806            :id => 1,
807            :issue => { :status_id => 2, :assigned_to_id => 3 },
808            :notes => 'Assigned to dlopper',
809            :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
810     end
811     assert_redirected_to :action => 'show', :id => '1'
812     issue.reload
813     assert_equal 2, issue.status_id
814     j = issue.journals.find(:first, :order => 'id DESC')
815     assert_equal 'Assigned to dlopper', j.notes
816     assert_equal 2, j.details.size
817     
818     mail = ActionMailer::Base.deliveries.last
819     assert mail.body.include?("Status changed from New to Assigned")
820     # subject should contain the new status
821     assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
822   end
823   
824   def test_post_edit_with_note_only
825     notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
826     # anonymous user
827     post :edit,
828          :id => 1,
829          :notes => notes
830     assert_redirected_to :action => 'show', :id => '1'
831     j = Issue.find(1).journals.find(:first, :order => 'id DESC')
832     assert_equal notes, j.notes
833     assert_equal 0, j.details.size
834     assert_equal User.anonymous, j.user
835     
836     mail = ActionMailer::Base.deliveries.last
837     assert mail.body.include?(notes)
838   end
839   
840   def test_post_edit_with_note_and_spent_time
841     @request.session[:user_id] = 2
842     spent_hours_before = Issue.find(1).spent_hours
843     assert_difference('TimeEntry.count') do
844       post :edit,
845            :id => 1,
846            :notes => '2.5 hours added',
847            :time_entry => { :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first }
848     end
849     assert_redirected_to :action => 'show', :id => '1'
850     
851     issue = Issue.find(1)
852     
853     j = issue.journals.find(:first, :order => 'id DESC')
854     assert_equal '2.5 hours added', j.notes
855     assert_equal 0, j.details.size
856     
857     t = issue.time_entries.find(:first, :order => 'id DESC')
858     assert_not_nil t
859     assert_equal 2.5, t.hours
860     assert_equal spent_hours_before + 2.5, issue.spent_hours
861   end
862   
863   def test_post_edit_with_attachment_only
864     set_tmp_attachments_directory
865     
866     # Delete all fixtured journals, a race condition can occur causing the wrong
867     # journal to get fetched in the next find.
868     Journal.delete_all
869
870     # anonymous user
871     post :edit,
872          :id => 1,
873          :notes => '',
874          :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
875     assert_redirected_to :action => 'show', :id => '1'
876     j = Issue.find(1).journals.find(:first, :order => 'id DESC')
877     assert j.notes.blank?
878     assert_equal 1, j.details.size
879     assert_equal 'testfile.txt', j.details.first.value
880     assert_equal User.anonymous, j.user
881     
882     mail = ActionMailer::Base.deliveries.last
883     assert mail.body.include?('testfile.txt')
884   end
885   
886   def test_post_edit_with_no_change
887     issue = Issue.find(1)
888     issue.journals.clear
889     ActionMailer::Base.deliveries.clear
890     
891     post :edit,
892          :id => 1,
893          :notes => ''
894     assert_redirected_to :action => 'show', :id => '1'
895     
896     issue.reload
897     assert issue.journals.empty?
898     # No email should be sent
899     assert ActionMailer::Base.deliveries.empty?
900   end
901
902   def test_post_edit_should_send_a_notification
903     @request.session[:user_id] = 2
904     ActionMailer::Base.deliveries.clear
905     issue = Issue.find(1)
906     old_subject = issue.subject
907     new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
908     
909     post :edit, :id => 1, :issue => {:subject => new_subject,
910                                      :priority_id => '6',
911                                      :category_id => '1' # no change
912                                     }
913     assert_equal 1, ActionMailer::Base.deliveries.size
914   end
915   
916   def test_post_edit_with_invalid_spent_time
917     @request.session[:user_id] = 2
918     notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
919     
920     assert_no_difference('Journal.count') do
921       post :edit,
922            :id => 1,
923            :notes => notes,
924            :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
925     end
926     assert_response :success
927     assert_template 'edit'
928     
929     assert_tag :textarea, :attributes => { :name => 'notes' },
930                           :content => notes
931     assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
932   end
933   
934   def test_get_bulk_edit
935     @request.session[:user_id] = 2
936     get :bulk_edit, :ids => [1, 2]
937     assert_response :success
938     assert_template 'bulk_edit'
939   end
940
941   def test_bulk_edit
942     @request.session[:user_id] = 2
943     # update issues priority
944     post :bulk_edit, :ids => [1, 2], :priority_id => 7,
945                                      :assigned_to_id => '',
946                                      :custom_field_values => {'2' => ''},
947                                      :notes => 'Bulk editing'
948     assert_response 302
949     # check that the issues were updated
950     assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
951     
952     issue = Issue.find(1)
953     journal = issue.journals.find(:first, :order => 'created_on DESC')
954     assert_equal '125', issue.custom_value_for(2).value
955     assert_equal 'Bulk editing', journal.notes
956     assert_equal 1, journal.details.size
957   end
958
959   def test_bullk_edit_should_send_a_notification
960     @request.session[:user_id] = 2
961     ActionMailer::Base.deliveries.clear
962     post(:bulk_edit,
963          {
964            :ids => [1, 2],
965            :priority_id => 7,
966            :assigned_to_id => '',
967            :custom_field_values => {'2' => ''},
968            :notes => 'Bulk editing'
969          })
970
971     assert_response 302
972     assert_equal 2, ActionMailer::Base.deliveries.size
973   end
974
975   def test_bulk_edit_status
976     @request.session[:user_id] = 2
977     # update issues priority
978     post :bulk_edit, :ids => [1, 2], :priority_id => '',
979                                      :assigned_to_id => '',
980                                      :status_id => '5',
981                                      :notes => 'Bulk editing status'
982     assert_response 302
983     issue = Issue.find(1)
984     assert issue.closed?
985   end
986
987   def test_bulk_edit_custom_field
988     @request.session[:user_id] = 2
989     # update issues priority
990     post :bulk_edit, :ids => [1, 2], :priority_id => '',
991                                      :assigned_to_id => '',
992                                      :custom_field_values => {'2' => '777'},
993                                      :notes => 'Bulk editing custom field'
994     assert_response 302
995     
996     issue = Issue.find(1)
997     journal = issue.journals.find(:first, :order => 'created_on DESC')
998     assert_equal '777', issue.custom_value_for(2).value
999     assert_equal 1, journal.details.size
1000     assert_equal '125', journal.details.first.old_value
1001     assert_equal '777', journal.details.first.value
1002   end
1003
1004   def test_bulk_unassign
1005     assert_not_nil Issue.find(2).assigned_to
1006     @request.session[:user_id] = 2
1007     # unassign issues
1008     post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
1009     assert_response 302
1010     # check that the issues were updated
1011     assert_nil Issue.find(2).assigned_to
1012   end
1013   
1014   def test_move_routing
1015     assert_routing(
1016       {:method => :get, :path => '/issues/1/move'},
1017       :controller => 'issues', :action => 'move', :id => '1'
1018     )
1019     assert_recognizes(
1020       {:controller => 'issues', :action => 'move', :id => '1'},
1021       {:method => :post, :path => '/issues/1/move'}
1022     )
1023   end
1024   
1025   def test_move_one_issue_to_another_project
1026     @request.session[:user_id] = 2
1027     post :move, :id => 1, :new_project_id => 2
1028     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1029     assert_equal 2, Issue.find(1).project_id
1030   end
1031
1032   def test_move_one_issue_to_another_project_should_follow_when_needed
1033     @request.session[:user_id] = 2
1034     post :move, :id => 1, :new_project_id => 2, :follow => '1'
1035     assert_redirected_to '/issues/1'
1036   end
1037
1038   def test_bulk_move_to_another_project
1039     @request.session[:user_id] = 2
1040     post :move, :ids => [1, 2], :new_project_id => 2
1041     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1042     # Issues moved to project 2
1043     assert_equal 2, Issue.find(1).project_id
1044     assert_equal 2, Issue.find(2).project_id
1045     # No tracker change
1046     assert_equal 1, Issue.find(1).tracker_id
1047     assert_equal 2, Issue.find(2).tracker_id
1048   end
1049  
1050   def test_bulk_move_to_another_tracker
1051     @request.session[:user_id] = 2
1052     post :move, :ids => [1, 2], :new_tracker_id => 2
1053     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1054     assert_equal 2, Issue.find(1).tracker_id
1055     assert_equal 2, Issue.find(2).tracker_id
1056   end
1057
1058   def test_bulk_copy_to_another_project
1059     @request.session[:user_id] = 2
1060     assert_difference 'Issue.count', 2 do
1061       assert_no_difference 'Project.find(1).issues.count' do
1062         post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}
1063       end
1064     end
1065     assert_redirected_to 'projects/ecookbook/issues'
1066   end
1067
1068   def test_copy_to_another_project_should_follow_when_needed
1069     @request.session[:user_id] = 2
1070     post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :follow => '1'
1071     issue = Issue.first(:order => 'id DESC')
1072     assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1073   end
1074   
1075   def test_context_menu_one_issue
1076     @request.session[:user_id] = 2
1077     get :context_menu, :ids => [1]
1078     assert_response :success
1079     assert_template 'context_menu'
1080     assert_tag :tag => 'a', :content => 'Edit',
1081                             :attributes => { :href => '/issues/1/edit',
1082                                              :class => 'icon-edit' }
1083     assert_tag :tag => 'a', :content => 'Closed',
1084                             :attributes => { :href => '/issues/1/edit?issue%5Bstatus_id%5D=5',
1085                                              :class => '' }
1086     assert_tag :tag => 'a', :content => 'Immediate',
1087                             :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;priority_id=8',
1088                                              :class => '' }
1089     assert_tag :tag => 'a', :content => 'Dave Lopper',
1090                             :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1',
1091                                              :class => '' }
1092     assert_tag :tag => 'a', :content => 'Copy',
1093                             :attributes => { :href => '/projects/ecookbook/issues/1/copy',
1094                                              :class => 'icon-copy' }
1095     assert_tag :tag => 'a', :content => 'Move',
1096                             :attributes => { :href => '/issues/move?ids%5B%5D=1',
1097                                              :class => 'icon-move' }
1098     assert_tag :tag => 'a', :content => 'Delete',
1099                             :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
1100                                              :class => 'icon-del' }
1101   end
1102
1103   def test_context_menu_one_issue_by_anonymous
1104     get :context_menu, :ids => [1]
1105     assert_response :success
1106     assert_template 'context_menu'
1107     assert_tag :tag => 'a', :content => 'Delete',
1108                             :attributes => { :href => '#',
1109                                              :class => 'icon-del disabled' }
1110   end
1111   
1112   def test_context_menu_multiple_issues_of_same_project
1113     @request.session[:user_id] = 2
1114     get :context_menu, :ids => [1, 2]
1115     assert_response :success
1116     assert_template 'context_menu'
1117     assert_tag :tag => 'a', :content => 'Edit',
1118                             :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
1119                                              :class => 'icon-edit' }
1120     assert_tag :tag => 'a', :content => 'Immediate',
1121                             :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;priority_id=8',
1122                                              :class => '' }
1123     assert_tag :tag => 'a', :content => 'Dave Lopper',
1124                             :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
1125                                              :class => '' }
1126     assert_tag :tag => 'a', :content => 'Move',
1127                             :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
1128                                              :class => 'icon-move' }
1129     assert_tag :tag => 'a', :content => 'Delete',
1130                             :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
1131                                              :class => 'icon-del' }
1132   end
1133
1134   def test_context_menu_multiple_issues_of_different_project
1135     @request.session[:user_id] = 2
1136     get :context_menu, :ids => [1, 2, 4]
1137     assert_response :success
1138     assert_template 'context_menu'
1139     assert_tag :tag => 'a', :content => 'Delete',
1140                             :attributes => { :href => '#',
1141                                              :class => 'icon-del disabled' }
1142   end
1143   
1144   def test_destroy_routing
1145     assert_recognizes( #TODO: use DELETE on issue URI (need to change forms)
1146       {:controller => 'issues', :action => 'destroy', :id => '1'},
1147       {:method => :post, :path => '/issues/1/destroy'}
1148     )
1149   end
1150   
1151   def test_destroy_issue_with_no_time_entries
1152     assert_nil TimeEntry.find_by_issue_id(2)
1153     @request.session[:user_id] = 2
1154     post :destroy, :id => 2
1155     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1156     assert_nil Issue.find_by_id(2)
1157   end
1158
1159   def test_destroy_issues_with_time_entries
1160     @request.session[:user_id] = 2
1161     post :destroy, :ids => [1, 3]
1162     assert_response :success
1163     assert_template 'destroy'
1164     assert_not_nil assigns(:hours)
1165     assert Issue.find_by_id(1) && Issue.find_by_id(3)
1166   end
1167
1168   def test_destroy_issues_and_destroy_time_entries
1169     @request.session[:user_id] = 2
1170     post :destroy, :ids => [1, 3], :todo => 'destroy'
1171     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1172     assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1173     assert_nil TimeEntry.find_by_id([1, 2])
1174   end
1175
1176   def test_destroy_issues_and_assign_time_entries_to_project
1177     @request.session[:user_id] = 2
1178     post :destroy, :ids => [1, 3], :todo => 'nullify'
1179     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1180     assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1181     assert_nil TimeEntry.find(1).issue_id
1182     assert_nil TimeEntry.find(2).issue_id
1183   end
1184   
1185   def test_destroy_issues_and_reassign_time_entries_to_another_issue
1186     @request.session[:user_id] = 2
1187     post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1188     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1189     assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1190     assert_equal 2, TimeEntry.find(1).issue_id
1191     assert_equal 2, TimeEntry.find(2).issue_id
1192   end
1193   
1194   def test_default_search_scope
1195     get :index
1196     assert_tag :div, :attributes => {:id => 'quick-search'},
1197                      :child => {:tag => 'form',
1198                                 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1199   end
1200 end