OSDN Git Service

Fixes fixtures loading.
[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_gantt
260     get :gantt, :project_id => 1
261     assert_response :success
262     assert_template 'gantt.rhtml'
263     assert_not_nil assigns(:gantt)
264     events = assigns(:gantt).events
265     assert_not_nil events
266     # Issue with start and due dates
267     i = Issue.find(1)
268     assert_not_nil i.due_date
269     assert events.include?(Issue.find(1))
270     # Issue with without due date but targeted to a version with date
271     i = Issue.find(2)
272     assert_nil i.due_date
273     assert events.include?(i)
274   end
275
276   def test_cross_project_gantt
277     get :gantt
278     assert_response :success
279     assert_template 'gantt.rhtml'
280     assert_not_nil assigns(:gantt)
281     events = assigns(:gantt).events
282     assert_not_nil events
283   end
284
285   def test_gantt_export_to_pdf
286     get :gantt, :project_id => 1, :format => 'pdf'
287     assert_response :success
288     assert_equal 'application/pdf', @response.content_type
289     assert @response.body.starts_with?('%PDF')
290     assert_not_nil assigns(:gantt)
291   end
292
293   def test_cross_project_gantt_export_to_pdf
294     get :gantt, :format => 'pdf'
295     assert_response :success
296     assert_equal 'application/pdf', @response.content_type
297     assert @response.body.starts_with?('%PDF')
298     assert_not_nil assigns(:gantt)
299   end
300   
301   if Object.const_defined?(:Magick)
302     def test_gantt_image
303       get :gantt, :project_id => 1, :format => 'png'
304       assert_response :success
305       assert_equal 'image/png', @response.content_type
306     end
307   else
308     puts "RMagick not installed. Skipping tests !!!"
309   end
310   
311   def test_calendar
312     get :calendar, :project_id => 1
313     assert_response :success
314     assert_template 'calendar'
315     assert_not_nil assigns(:calendar)
316   end
317   
318   def test_cross_project_calendar
319     get :calendar
320     assert_response :success
321     assert_template 'calendar'
322     assert_not_nil assigns(:calendar)
323   end
324   
325   def test_changes
326     get :changes, :project_id => 1
327     assert_response :success
328     assert_not_nil assigns(:journals)
329     assert_equal 'application/atom+xml', @response.content_type
330   end
331   
332   def test_show_routing
333     assert_routing(
334       {:method => :get, :path => '/issues/64'},
335       :controller => 'issues', :action => 'show', :id => '64'
336     )
337   end
338   
339   def test_show_routing_formatted
340     assert_routing(
341       {:method => :get, :path => '/issues/2332.pdf'},
342       :controller => 'issues', :action => 'show', :id => '2332', :format => 'pdf'
343     )
344     assert_routing(
345       {:method => :get, :path => '/issues/23123.atom'},
346       :controller => 'issues', :action => 'show', :id => '23123', :format => 'atom'
347     )
348   end
349   
350   def test_show_by_anonymous
351     get :show, :id => 1
352     assert_response :success
353     assert_template 'show.rhtml'
354     assert_not_nil assigns(:issue)
355     assert_equal Issue.find(1), assigns(:issue)
356     
357     # anonymous role is allowed to add a note
358     assert_tag :tag => 'form',
359                :descendant => { :tag => 'fieldset',
360                                 :child => { :tag => 'legend', 
361                                             :content => /Notes/ } }
362   end
363   
364   def test_show_by_manager
365     @request.session[:user_id] = 2
366     get :show, :id => 1
367     assert_response :success
368     
369     assert_tag :tag => 'form',
370                :descendant => { :tag => 'fieldset',
371                                 :child => { :tag => 'legend', 
372                                             :content => /Change properties/ } },
373                :descendant => { :tag => 'fieldset',
374                                 :child => { :tag => 'legend', 
375                                             :content => /Log time/ } },
376                :descendant => { :tag => 'fieldset',
377                                 :child => { :tag => 'legend', 
378                                             :content => /Notes/ } }
379   end
380   
381   def test_show_should_deny_anonymous_access_without_permission
382     Role.anonymous.remove_permission!(:view_issues)
383     get :show, :id => 1
384     assert_response :redirect
385   end
386   
387   def test_show_should_deny_non_member_access_without_permission
388     Role.non_member.remove_permission!(:view_issues)
389     @request.session[:user_id] = 9
390     get :show, :id => 1
391     assert_response 403
392   end
393   
394   def test_show_should_deny_member_access_without_permission
395     Role.find(1).remove_permission!(:view_issues)
396     @request.session[:user_id] = 2
397     get :show, :id => 1
398     assert_response 403
399   end
400   
401   def test_show_should_not_disclose_relations_to_invisible_issues
402     Setting.cross_project_issue_relations = '1'
403     IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
404     # Relation to a private project issue
405     IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
406     
407     get :show, :id => 1
408     assert_response :success
409     
410     assert_tag :div, :attributes => { :id => 'relations' },
411                      :descendant => { :tag => 'a', :content => /#2$/ }
412     assert_no_tag :div, :attributes => { :id => 'relations' },
413                         :descendant => { :tag => 'a', :content => /#4$/ }
414   end
415   
416   def test_show_atom
417     get :show, :id => 2, :format => 'atom'
418     assert_response :success
419     assert_template 'changes.rxml'
420     # Inline image
421     assert @response.body.include?("&lt;img src=\"http://test.host/attachments/download/10\" alt=\"\" /&gt;"), "Body did not match. Body: #{@response.body}"
422   end
423   
424   def test_new_routing
425     assert_routing(
426       {:method => :get, :path => '/projects/1/issues/new'},
427       :controller => 'issues', :action => 'new', :project_id => '1'
428     )
429     assert_recognizes(
430       {:controller => 'issues', :action => 'new', :project_id => '1'},
431       {:method => :post, :path => '/projects/1/issues'}
432     )
433   end
434
435   def test_show_export_to_pdf
436     get :show, :id => 3, :format => 'pdf'
437     assert_response :success
438     assert_equal 'application/pdf', @response.content_type
439     assert @response.body.starts_with?('%PDF')
440     assert_not_nil assigns(:issue)
441   end
442
443   def test_get_new
444     @request.session[:user_id] = 2
445     get :new, :project_id => 1, :tracker_id => 1
446     assert_response :success
447     assert_template 'new'
448     
449     assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
450                                                  :value => 'Default string' }
451   end
452
453   def test_get_new_without_tracker_id
454     @request.session[:user_id] = 2
455     get :new, :project_id => 1
456     assert_response :success
457     assert_template 'new'
458     
459     issue = assigns(:issue)
460     assert_not_nil issue
461     assert_equal Project.find(1).trackers.first, issue.tracker
462   end
463   
464   def test_get_new_with_no_default_status_should_display_an_error
465     @request.session[:user_id] = 2
466     IssueStatus.delete_all
467     
468     get :new, :project_id => 1
469     assert_response 500
470     assert_not_nil flash[:error]
471     assert_tag :tag => 'div', :attributes => { :class => /error/ },
472                               :content => /No default issue/
473   end
474   
475   def test_get_new_with_no_tracker_should_display_an_error
476     @request.session[:user_id] = 2
477     Tracker.delete_all
478     
479     get :new, :project_id => 1
480     assert_response 500
481     assert_not_nil flash[:error]
482     assert_tag :tag => 'div', :attributes => { :class => /error/ },
483                               :content => /No tracker/
484   end
485   
486   def test_update_new_form
487     @request.session[:user_id] = 2
488     xhr :post, :new, :project_id => 1,
489                      :issue => {:tracker_id => 2, 
490                                 :subject => 'This is the test_new issue',
491                                 :description => 'This is the description',
492                                 :priority_id => 5}
493     assert_response :success
494     assert_template 'new'
495   end 
496   
497   def test_post_new
498     @request.session[:user_id] = 2
499     assert_difference 'Issue.count' do
500       post :new, :project_id => 1, 
501                  :issue => {:tracker_id => 3,
502                             :subject => 'This is the test_new issue',
503                             :description => 'This is the description',
504                             :priority_id => 5,
505                             :estimated_hours => '',
506                             :custom_field_values => {'2' => 'Value for field 2'}}
507     end
508     assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
509     
510     issue = Issue.find_by_subject('This is the test_new issue')
511     assert_not_nil issue
512     assert_equal 2, issue.author_id
513     assert_equal 3, issue.tracker_id
514     assert_nil issue.estimated_hours
515     v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
516     assert_not_nil v
517     assert_equal 'Value for field 2', v.value
518   end
519   
520   def test_post_new_and_continue
521     @request.session[:user_id] = 2
522     post :new, :project_id => 1, 
523                :issue => {:tracker_id => 3,
524                           :subject => 'This is first issue',
525                           :priority_id => 5},
526                :continue => ''
527     assert_redirected_to :controller => 'issues', :action => 'new', :tracker_id => 3
528   end
529   
530   def test_post_new_without_custom_fields_param
531     @request.session[:user_id] = 2
532     assert_difference 'Issue.count' do
533       post :new, :project_id => 1, 
534                  :issue => {:tracker_id => 1,
535                             :subject => 'This is the test_new issue',
536                             :description => 'This is the description',
537                             :priority_id => 5}
538     end
539     assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
540   end
541
542   def test_post_new_with_required_custom_field_and_without_custom_fields_param
543     field = IssueCustomField.find_by_name('Database')
544     field.update_attribute(:is_required, true)
545
546     @request.session[:user_id] = 2
547     post :new, :project_id => 1, 
548                :issue => {:tracker_id => 1,
549                           :subject => 'This is the test_new issue',
550                           :description => 'This is the description',
551                           :priority_id => 5}
552     assert_response :success
553     assert_template 'new'
554     issue = assigns(:issue)
555     assert_not_nil issue
556     assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
557   end
558   
559   def test_post_new_with_watchers
560     @request.session[:user_id] = 2
561     ActionMailer::Base.deliveries.clear
562     
563     assert_difference 'Watcher.count', 2 do
564       post :new, :project_id => 1, 
565                  :issue => {:tracker_id => 1,
566                             :subject => 'This is a new issue with watchers',
567                             :description => 'This is the description',
568                             :priority_id => 5,
569                             :watcher_user_ids => ['2', '3']}
570     end
571     issue = Issue.find_by_subject('This is a new issue with watchers')
572     assert_not_nil issue
573     assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
574     
575     # Watchers added
576     assert_equal [2, 3], issue.watcher_user_ids.sort
577     assert issue.watched_by?(User.find(3))
578     # Watchers notified
579     mail = ActionMailer::Base.deliveries.last
580     assert_kind_of TMail::Mail, mail
581     assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
582   end
583   
584   def test_post_new_should_send_a_notification
585     ActionMailer::Base.deliveries.clear
586     @request.session[:user_id] = 2
587     assert_difference 'Issue.count' do
588       post :new, :project_id => 1, 
589                  :issue => {:tracker_id => 3,
590                             :subject => 'This is the test_new issue',
591                             :description => 'This is the description',
592                             :priority_id => 5,
593                             :estimated_hours => '',
594                             :custom_field_values => {'2' => 'Value for field 2'}}
595     end
596     assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
597     
598     assert_equal 1, ActionMailer::Base.deliveries.size
599   end
600   
601   def test_post_should_preserve_fields_values_on_validation_failure
602     @request.session[:user_id] = 2
603     post :new, :project_id => 1, 
604                :issue => {:tracker_id => 1,
605                           # empty subject
606                           :subject => '',
607                           :description => 'This is a description',
608                           :priority_id => 6,
609                           :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
610     assert_response :success
611     assert_template 'new'
612     
613     assert_tag :textarea, :attributes => { :name => 'issue[description]' },
614                           :content => 'This is a description'
615     assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
616                         :child => { :tag => 'option', :attributes => { :selected => 'selected',
617                                                                        :value => '6' },
618                                                       :content => 'High' }  
619     # Custom fields
620     assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
621                         :child => { :tag => 'option', :attributes => { :selected => 'selected',
622                                                                        :value => 'Oracle' },
623                                                       :content => 'Oracle' }  
624     assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
625                                         :value => 'Value for field 2'}
626   end
627   
628   def test_copy_routing
629     assert_routing(
630       {:method => :get, :path => '/projects/world_domination/issues/567/copy'},
631       :controller => 'issues', :action => 'new', :project_id => 'world_domination', :copy_from => '567'
632     )
633   end
634   
635   def test_copy_issue
636     @request.session[:user_id] = 2
637     get :new, :project_id => 1, :copy_from => 1
638     assert_template 'new'
639     assert_not_nil assigns(:issue)
640     orig = Issue.find(1)
641     assert_equal orig.subject, assigns(:issue).subject
642   end
643   
644   def test_edit_routing
645     assert_routing(
646       {:method => :get, :path => '/issues/1/edit'},
647       :controller => 'issues', :action => 'edit', :id => '1'
648     )
649     assert_recognizes( #TODO: use a PUT on the issue URI isntead, need to adjust form
650       {:controller => 'issues', :action => 'edit', :id => '1'},
651       {:method => :post, :path => '/issues/1/edit'}
652     )
653   end
654   
655   def test_get_edit
656     @request.session[:user_id] = 2
657     get :edit, :id => 1
658     assert_response :success
659     assert_template 'edit'
660     assert_not_nil assigns(:issue)
661     assert_equal Issue.find(1), assigns(:issue)
662   end
663   
664   def test_get_edit_with_params
665     @request.session[:user_id] = 2
666     get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
667     assert_response :success
668     assert_template 'edit'
669     
670     issue = assigns(:issue)
671     assert_not_nil issue
672     
673     assert_equal 5, issue.status_id
674     assert_tag :select, :attributes => { :name => 'issue[status_id]' },
675                         :child => { :tag => 'option', 
676                                     :content => 'Closed',
677                                     :attributes => { :selected => 'selected' } }
678                                     
679     assert_equal 7, issue.priority_id
680     assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
681                         :child => { :tag => 'option', 
682                                     :content => 'Urgent',
683                                     :attributes => { :selected => 'selected' } }
684   end
685   
686   def test_reply_routing
687     assert_routing(
688       {:method => :post, :path => '/issues/1/quoted'},
689       :controller => 'issues', :action => 'reply', :id => '1'
690     )
691   end
692   
693   def test_reply_to_issue
694     @request.session[:user_id] = 2
695     get :reply, :id => 1
696     assert_response :success
697     assert_select_rjs :show, "update"
698   end
699
700   def test_reply_to_note
701     @request.session[:user_id] = 2
702     get :reply, :id => 1, :journal_id => 2
703     assert_response :success
704     assert_select_rjs :show, "update"
705   end
706
707   def test_post_edit_without_custom_fields_param
708     @request.session[:user_id] = 2
709     ActionMailer::Base.deliveries.clear
710     
711     issue = Issue.find(1)
712     assert_equal '125', issue.custom_value_for(2).value
713     old_subject = issue.subject
714     new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
715     
716     assert_difference('Journal.count') do
717       assert_difference('JournalDetail.count', 2) do
718         post :edit, :id => 1, :issue => {:subject => new_subject,
719                                          :priority_id => '6',
720                                          :category_id => '1' # no change
721                                         }
722       end
723     end
724     assert_redirected_to :action => 'show', :id => '1'
725     issue.reload
726     assert_equal new_subject, issue.subject
727     # Make sure custom fields were not cleared
728     assert_equal '125', issue.custom_value_for(2).value
729     
730     mail = ActionMailer::Base.deliveries.last
731     assert_kind_of TMail::Mail, mail
732     assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
733     assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
734   end
735   
736   def test_post_edit_with_custom_field_change
737     @request.session[:user_id] = 2
738     issue = Issue.find(1)
739     assert_equal '125', issue.custom_value_for(2).value
740     
741     assert_difference('Journal.count') do
742       assert_difference('JournalDetail.count', 3) do
743         post :edit, :id => 1, :issue => {:subject => 'Custom field change',
744                                          :priority_id => '6',
745                                          :category_id => '1', # no change
746                                          :custom_field_values => { '2' => 'New custom value' }
747                                         }
748       end
749     end
750     assert_redirected_to :action => 'show', :id => '1'
751     issue.reload
752     assert_equal 'New custom value', issue.custom_value_for(2).value
753     
754     mail = ActionMailer::Base.deliveries.last
755     assert_kind_of TMail::Mail, mail
756     assert mail.body.include?("Searchable field changed from 125 to New custom value")
757   end
758   
759   def test_post_edit_with_status_and_assignee_change
760     issue = Issue.find(1)
761     assert_equal 1, issue.status_id
762     @request.session[:user_id] = 2
763     assert_difference('TimeEntry.count', 0) do
764       post :edit,
765            :id => 1,
766            :issue => { :status_id => 2, :assigned_to_id => 3 },
767            :notes => 'Assigned to dlopper',
768            :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
769     end
770     assert_redirected_to :action => 'show', :id => '1'
771     issue.reload
772     assert_equal 2, issue.status_id
773     j = issue.journals.find(:first, :order => 'id DESC')
774     assert_equal 'Assigned to dlopper', j.notes
775     assert_equal 2, j.details.size
776     
777     mail = ActionMailer::Base.deliveries.last
778     assert mail.body.include?("Status changed from New to Assigned")
779     # subject should contain the new status
780     assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
781   end
782   
783   def test_post_edit_with_note_only
784     notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
785     # anonymous user
786     post :edit,
787          :id => 1,
788          :notes => notes
789     assert_redirected_to :action => 'show', :id => '1'
790     j = Issue.find(1).journals.find(:first, :order => 'id DESC')
791     assert_equal notes, j.notes
792     assert_equal 0, j.details.size
793     assert_equal User.anonymous, j.user
794     
795     mail = ActionMailer::Base.deliveries.last
796     assert mail.body.include?(notes)
797   end
798   
799   def test_post_edit_with_note_and_spent_time
800     @request.session[:user_id] = 2
801     spent_hours_before = Issue.find(1).spent_hours
802     assert_difference('TimeEntry.count') do
803       post :edit,
804            :id => 1,
805            :notes => '2.5 hours added',
806            :time_entry => { :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first }
807     end
808     assert_redirected_to :action => 'show', :id => '1'
809     
810     issue = Issue.find(1)
811     
812     j = issue.journals.find(:first, :order => 'id DESC')
813     assert_equal '2.5 hours added', j.notes
814     assert_equal 0, j.details.size
815     
816     t = issue.time_entries.find(:first, :order => 'id DESC')
817     assert_not_nil t
818     assert_equal 2.5, t.hours
819     assert_equal spent_hours_before + 2.5, issue.spent_hours
820   end
821   
822   def test_post_edit_with_attachment_only
823     set_tmp_attachments_directory
824     
825     # Delete all fixtured journals, a race condition can occur causing the wrong
826     # journal to get fetched in the next find.
827     Journal.delete_all
828
829     # anonymous user
830     post :edit,
831          :id => 1,
832          :notes => '',
833          :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
834     assert_redirected_to :action => 'show', :id => '1'
835     j = Issue.find(1).journals.find(:first, :order => 'id DESC')
836     assert j.notes.blank?
837     assert_equal 1, j.details.size
838     assert_equal 'testfile.txt', j.details.first.value
839     assert_equal User.anonymous, j.user
840     
841     mail = ActionMailer::Base.deliveries.last
842     assert mail.body.include?('testfile.txt')
843   end
844   
845   def test_post_edit_with_no_change
846     issue = Issue.find(1)
847     issue.journals.clear
848     ActionMailer::Base.deliveries.clear
849     
850     post :edit,
851          :id => 1,
852          :notes => ''
853     assert_redirected_to :action => 'show', :id => '1'
854     
855     issue.reload
856     assert issue.journals.empty?
857     # No email should be sent
858     assert ActionMailer::Base.deliveries.empty?
859   end
860
861   def test_post_edit_should_send_a_notification
862     @request.session[:user_id] = 2
863     ActionMailer::Base.deliveries.clear
864     issue = Issue.find(1)
865     old_subject = issue.subject
866     new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
867     
868     post :edit, :id => 1, :issue => {:subject => new_subject,
869                                      :priority_id => '6',
870                                      :category_id => '1' # no change
871                                     }
872     assert_equal 1, ActionMailer::Base.deliveries.size
873   end
874   
875   def test_post_edit_with_invalid_spent_time
876     @request.session[:user_id] = 2
877     notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
878     
879     assert_no_difference('Journal.count') do
880       post :edit,
881            :id => 1,
882            :notes => notes,
883            :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
884     end
885     assert_response :success
886     assert_template 'edit'
887     
888     assert_tag :textarea, :attributes => { :name => 'notes' },
889                           :content => notes
890     assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
891   end
892   
893   def test_get_bulk_edit
894     @request.session[:user_id] = 2
895     get :bulk_edit, :ids => [1, 2]
896     assert_response :success
897     assert_template 'bulk_edit'
898   end
899
900   def test_bulk_edit
901     @request.session[:user_id] = 2
902     # update issues priority
903     post :bulk_edit, :ids => [1, 2], :priority_id => 7,
904                                      :assigned_to_id => '',
905                                      :custom_field_values => {'2' => ''},
906                                      :notes => 'Bulk editing'
907     assert_response 302
908     # check that the issues were updated
909     assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
910     
911     issue = Issue.find(1)
912     journal = issue.journals.find(:first, :order => 'created_on DESC')
913     assert_equal '125', issue.custom_value_for(2).value
914     assert_equal 'Bulk editing', journal.notes
915     assert_equal 1, journal.details.size
916   end
917
918   def test_bullk_edit_should_send_a_notification
919     @request.session[:user_id] = 2
920     ActionMailer::Base.deliveries.clear
921     post(:bulk_edit,
922          {
923            :ids => [1, 2],
924            :priority_id => 7,
925            :assigned_to_id => '',
926            :custom_field_values => {'2' => ''},
927            :notes => 'Bulk editing'
928          })
929
930     assert_response 302
931     assert_equal 2, ActionMailer::Base.deliveries.size
932   end
933
934   def test_bulk_edit_status
935     @request.session[:user_id] = 2
936     # update issues priority
937     post :bulk_edit, :ids => [1, 2], :priority_id => '',
938                                      :assigned_to_id => '',
939                                      :status_id => '5',
940                                      :notes => 'Bulk editing status'
941     assert_response 302
942     issue = Issue.find(1)
943     assert issue.closed?
944   end
945
946   def test_bulk_edit_custom_field
947     @request.session[:user_id] = 2
948     # update issues priority
949     post :bulk_edit, :ids => [1, 2], :priority_id => '',
950                                      :assigned_to_id => '',
951                                      :custom_field_values => {'2' => '777'},
952                                      :notes => 'Bulk editing custom field'
953     assert_response 302
954     
955     issue = Issue.find(1)
956     journal = issue.journals.find(:first, :order => 'created_on DESC')
957     assert_equal '777', issue.custom_value_for(2).value
958     assert_equal 1, journal.details.size
959     assert_equal '125', journal.details.first.old_value
960     assert_equal '777', journal.details.first.value
961   end
962
963   def test_bulk_unassign
964     assert_not_nil Issue.find(2).assigned_to
965     @request.session[:user_id] = 2
966     # unassign issues
967     post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
968     assert_response 302
969     # check that the issues were updated
970     assert_nil Issue.find(2).assigned_to
971   end
972   
973   def test_move_routing
974     assert_routing(
975       {:method => :get, :path => '/issues/1/move'},
976       :controller => 'issues', :action => 'move', :id => '1'
977     )
978     assert_recognizes(
979       {:controller => 'issues', :action => 'move', :id => '1'},
980       {:method => :post, :path => '/issues/1/move'}
981     )
982   end
983   
984   def test_move_one_issue_to_another_project
985     @request.session[:user_id] = 2
986     post :move, :id => 1, :new_project_id => 2
987     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
988     assert_equal 2, Issue.find(1).project_id
989   end
990
991   def test_move_one_issue_to_another_project_should_follow_when_needed
992     @request.session[:user_id] = 2
993     post :move, :id => 1, :new_project_id => 2, :follow => '1'
994     assert_redirected_to '/issues/1'
995   end
996
997   def test_bulk_move_to_another_project
998     @request.session[:user_id] = 2
999     post :move, :ids => [1, 2], :new_project_id => 2
1000     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1001     # Issues moved to project 2
1002     assert_equal 2, Issue.find(1).project_id
1003     assert_equal 2, Issue.find(2).project_id
1004     # No tracker change
1005     assert_equal 1, Issue.find(1).tracker_id
1006     assert_equal 2, Issue.find(2).tracker_id
1007   end
1008  
1009   def test_bulk_move_to_another_tracker
1010     @request.session[:user_id] = 2
1011     post :move, :ids => [1, 2], :new_tracker_id => 2
1012     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1013     assert_equal 2, Issue.find(1).tracker_id
1014     assert_equal 2, Issue.find(2).tracker_id
1015   end
1016
1017   def test_bulk_copy_to_another_project
1018     @request.session[:user_id] = 2
1019     assert_difference 'Issue.count', 2 do
1020       assert_no_difference 'Project.find(1).issues.count' do
1021         post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}
1022       end
1023     end
1024     assert_redirected_to 'projects/ecookbook/issues'
1025   end
1026
1027   def test_copy_to_another_project_should_follow_when_needed
1028     @request.session[:user_id] = 2
1029     post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :follow => '1'
1030     issue = Issue.first(:order => 'id DESC')
1031     assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1032   end
1033   
1034   def test_context_menu_one_issue
1035     @request.session[:user_id] = 2
1036     get :context_menu, :ids => [1]
1037     assert_response :success
1038     assert_template 'context_menu'
1039     assert_tag :tag => 'a', :content => 'Edit',
1040                             :attributes => { :href => '/issues/1/edit',
1041                                              :class => 'icon-edit' }
1042     assert_tag :tag => 'a', :content => 'Closed',
1043                             :attributes => { :href => '/issues/1/edit?issue%5Bstatus_id%5D=5',
1044                                              :class => '' }
1045     assert_tag :tag => 'a', :content => 'Immediate',
1046                             :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;priority_id=8',
1047                                              :class => '' }
1048     assert_tag :tag => 'a', :content => 'Dave Lopper',
1049                             :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1',
1050                                              :class => '' }
1051     assert_tag :tag => 'a', :content => 'Copy',
1052                             :attributes => { :href => '/projects/ecookbook/issues/1/copy',
1053                                              :class => 'icon-copy' }
1054     assert_tag :tag => 'a', :content => 'Move',
1055                             :attributes => { :href => '/issues/move?ids%5B%5D=1',
1056                                              :class => 'icon-move' }
1057     assert_tag :tag => 'a', :content => 'Delete',
1058                             :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
1059                                              :class => 'icon-del' }
1060   end
1061
1062   def test_context_menu_one_issue_by_anonymous
1063     get :context_menu, :ids => [1]
1064     assert_response :success
1065     assert_template 'context_menu'
1066     assert_tag :tag => 'a', :content => 'Delete',
1067                             :attributes => { :href => '#',
1068                                              :class => 'icon-del disabled' }
1069   end
1070   
1071   def test_context_menu_multiple_issues_of_same_project
1072     @request.session[:user_id] = 2
1073     get :context_menu, :ids => [1, 2]
1074     assert_response :success
1075     assert_template 'context_menu'
1076     assert_tag :tag => 'a', :content => 'Edit',
1077                             :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
1078                                              :class => 'icon-edit' }
1079     assert_tag :tag => 'a', :content => 'Immediate',
1080                             :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;priority_id=8',
1081                                              :class => '' }
1082     assert_tag :tag => 'a', :content => 'Dave Lopper',
1083                             :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
1084                                              :class => '' }
1085     assert_tag :tag => 'a', :content => 'Move',
1086                             :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
1087                                              :class => 'icon-move' }
1088     assert_tag :tag => 'a', :content => 'Delete',
1089                             :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
1090                                              :class => 'icon-del' }
1091   end
1092
1093   def test_context_menu_multiple_issues_of_different_project
1094     @request.session[:user_id] = 2
1095     get :context_menu, :ids => [1, 2, 4]
1096     assert_response :success
1097     assert_template 'context_menu'
1098     assert_tag :tag => 'a', :content => 'Delete',
1099                             :attributes => { :href => '#',
1100                                              :class => 'icon-del disabled' }
1101   end
1102   
1103   def test_destroy_routing
1104     assert_recognizes( #TODO: use DELETE on issue URI (need to change forms)
1105       {:controller => 'issues', :action => 'destroy', :id => '1'},
1106       {:method => :post, :path => '/issues/1/destroy'}
1107     )
1108   end
1109   
1110   def test_destroy_issue_with_no_time_entries
1111     assert_nil TimeEntry.find_by_issue_id(2)
1112     @request.session[:user_id] = 2
1113     post :destroy, :id => 2
1114     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1115     assert_nil Issue.find_by_id(2)
1116   end
1117
1118   def test_destroy_issues_with_time_entries
1119     @request.session[:user_id] = 2
1120     post :destroy, :ids => [1, 3]
1121     assert_response :success
1122     assert_template 'destroy'
1123     assert_not_nil assigns(:hours)
1124     assert Issue.find_by_id(1) && Issue.find_by_id(3)
1125   end
1126
1127   def test_destroy_issues_and_destroy_time_entries
1128     @request.session[:user_id] = 2
1129     post :destroy, :ids => [1, 3], :todo => 'destroy'
1130     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1131     assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1132     assert_nil TimeEntry.find_by_id([1, 2])
1133   end
1134
1135   def test_destroy_issues_and_assign_time_entries_to_project
1136     @request.session[:user_id] = 2
1137     post :destroy, :ids => [1, 3], :todo => 'nullify'
1138     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1139     assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1140     assert_nil TimeEntry.find(1).issue_id
1141     assert_nil TimeEntry.find(2).issue_id
1142   end
1143   
1144   def test_destroy_issues_and_reassign_time_entries_to_another_issue
1145     @request.session[:user_id] = 2
1146     post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1147     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1148     assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1149     assert_equal 2, TimeEntry.find(1).issue_id
1150     assert_equal 2, TimeEntry.find(2).issue_id
1151   end
1152   
1153   def test_default_search_scope
1154     get :index
1155     assert_tag :div, :attributes => {:id => 'quick-search'},
1156                      :child => {:tag => 'form',
1157                                 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1158   end
1159 end