OSDN Git Service

Added ability to delete issues from different projects through contextual menu (...
[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
57     Setting.default_language = 'en'
58     
59     get :index
60     assert_response :success
61     assert_template 'index.rhtml'
62     assert_not_nil assigns(:issues)
63     assert_nil assigns(:project)
64     assert_tag :tag => 'a', :content => /Can't print recipes/
65     assert_tag :tag => 'a', :content => /Subproject issue/
66     # private projects hidden
67     assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
68     assert_no_tag :tag => 'a', :content => /Issue on project 2/
69     # project column
70     assert_tag :tag => 'th', :content => /Project/
71   end
72   
73   def test_index_should_not_list_issues_when_module_disabled
74     EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
75     get :index
76     assert_response :success
77     assert_template 'index.rhtml'
78     assert_not_nil assigns(:issues)
79     assert_nil assigns(:project)
80     assert_no_tag :tag => 'a', :content => /Can't print recipes/
81     assert_tag :tag => 'a', :content => /Subproject issue/
82   end
83
84   def test_index_should_not_list_issues_when_module_disabled
85     EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
86     get :index
87     assert_response :success
88     assert_template 'index.rhtml'
89     assert_not_nil assigns(:issues)
90     assert_nil assigns(:project)
91     assert_no_tag :tag => 'a', :content => /Can't print recipes/
92     assert_tag :tag => 'a', :content => /Subproject issue/
93   end
94   
95   def test_index_with_project
96     Setting.display_subprojects_issues = 0
97     get :index, :project_id => 1
98     assert_response :success
99     assert_template 'index.rhtml'
100     assert_not_nil assigns(:issues)
101     assert_tag :tag => 'a', :content => /Can't print recipes/
102     assert_no_tag :tag => 'a', :content => /Subproject issue/
103   end
104   
105   def test_index_with_project_and_subprojects
106     Setting.display_subprojects_issues = 1
107     get :index, :project_id => 1
108     assert_response :success
109     assert_template 'index.rhtml'
110     assert_not_nil assigns(:issues)
111     assert_tag :tag => 'a', :content => /Can't print recipes/
112     assert_tag :tag => 'a', :content => /Subproject issue/
113     assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
114   end
115   
116   def test_index_with_project_and_subprojects_should_show_private_subprojects
117     @request.session[:user_id] = 2
118     Setting.display_subprojects_issues = 1
119     get :index, :project_id => 1
120     assert_response :success
121     assert_template 'index.rhtml'
122     assert_not_nil assigns(:issues)
123     assert_tag :tag => 'a', :content => /Can't print recipes/
124     assert_tag :tag => 'a', :content => /Subproject issue/
125     assert_tag :tag => 'a', :content => /Issue of a private subproject/
126   end
127   
128   def test_index_with_project_and_filter
129     get :index, :project_id => 1, :set_filter => 1
130     assert_response :success
131     assert_template 'index.rhtml'
132     assert_not_nil assigns(:issues)
133   end
134   
135   def test_index_with_query
136     get :index, :project_id => 1, :query_id => 5
137     assert_response :success
138     assert_template 'index.rhtml'
139     assert_not_nil assigns(:issues)
140     assert_nil assigns(:issue_count_by_group)
141   end
142   
143   def test_index_with_query_grouped_by_tracker
144     get :index, :project_id => 1, :query_id => 6
145     assert_response :success
146     assert_template 'index.rhtml'
147     assert_not_nil assigns(:issues)
148     assert_not_nil assigns(:issue_count_by_group)
149   end
150   
151   def test_index_with_query_grouped_by_list_custom_field
152     get :index, :project_id => 1, :query_id => 9
153     assert_response :success
154     assert_template 'index.rhtml'
155     assert_not_nil assigns(:issues)
156     assert_not_nil assigns(:issue_count_by_group)
157   end
158   
159   def test_index_sort_by_field_not_included_in_columns
160     Setting.issue_list_default_columns = %w(subject author)
161     get :index, :sort => 'tracker'
162   end
163   
164   def test_index_csv_with_project
165     Setting.default_language = 'en'
166     
167     get :index, :format => 'csv'
168     assert_response :success
169     assert_not_nil assigns(:issues)
170     assert_equal 'text/csv', @response.content_type
171     assert @response.body.starts_with?("#,")
172
173     get :index, :project_id => 1, :format => 'csv'
174     assert_response :success
175     assert_not_nil assigns(:issues)
176     assert_equal 'text/csv', @response.content_type
177   end
178   
179   def test_index_pdf
180     get :index, :format => 'pdf'
181     assert_response :success
182     assert_not_nil assigns(:issues)
183     assert_equal 'application/pdf', @response.content_type
184     
185     get :index, :project_id => 1, :format => 'pdf'
186     assert_response :success
187     assert_not_nil assigns(:issues)
188     assert_equal 'application/pdf', @response.content_type
189     
190     get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
191     assert_response :success
192     assert_not_nil assigns(:issues)
193     assert_equal 'application/pdf', @response.content_type
194   end
195   
196   def test_index_pdf_with_query_grouped_by_list_custom_field
197     get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
198     assert_response :success
199     assert_not_nil assigns(:issues)
200     assert_not_nil assigns(:issue_count_by_group)
201     assert_equal 'application/pdf', @response.content_type
202   end
203   
204   def test_index_sort
205     get :index, :sort => 'tracker,id:desc'
206     assert_response :success
207     
208     sort_params = @request.session['issues_index_sort']
209     assert sort_params.is_a?(String)
210     assert_equal 'tracker,id:desc', sort_params
211     
212     issues = assigns(:issues)
213     assert_not_nil issues
214     assert !issues.empty?
215     assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
216   end
217   
218   def test_index_with_columns
219     columns = ['tracker', 'subject', 'assigned_to']
220     get :index, :set_filter => 1, :query => { 'column_names' => columns}
221     assert_response :success
222     
223     # query should use specified columns
224     query = assigns(:query)
225     assert_kind_of Query, query
226     assert_equal columns, query.column_names.map(&:to_s)
227     
228     # columns should be stored in session
229     assert_kind_of Hash, session[:query]
230     assert_kind_of Array, session[:query][:column_names]
231     assert_equal columns, session[:query][:column_names].map(&:to_s)
232   end
233
234   def test_show_by_anonymous
235     get :show, :id => 1
236     assert_response :success
237     assert_template 'show.rhtml'
238     assert_not_nil assigns(:issue)
239     assert_equal Issue.find(1), assigns(:issue)
240     
241     # anonymous role is allowed to add a note
242     assert_tag :tag => 'form',
243                :descendant => { :tag => 'fieldset',
244                                 :child => { :tag => 'legend', 
245                                             :content => /Notes/ } }
246   end
247   
248   def test_show_by_manager
249     @request.session[:user_id] = 2
250     get :show, :id => 1
251     assert_response :success
252     
253     assert_tag :tag => 'form',
254                :descendant => { :tag => 'fieldset',
255                                 :child => { :tag => 'legend', 
256                                             :content => /Change properties/ } },
257                :descendant => { :tag => 'fieldset',
258                                 :child => { :tag => 'legend', 
259                                             :content => /Log time/ } },
260                :descendant => { :tag => 'fieldset',
261                                 :child => { :tag => 'legend', 
262                                             :content => /Notes/ } }
263   end
264   
265   def test_show_should_deny_anonymous_access_without_permission
266     Role.anonymous.remove_permission!(:view_issues)
267     get :show, :id => 1
268     assert_response :redirect
269   end
270   
271   def test_show_should_deny_non_member_access_without_permission
272     Role.non_member.remove_permission!(:view_issues)
273     @request.session[:user_id] = 9
274     get :show, :id => 1
275     assert_response 403
276   end
277   
278   def test_show_should_deny_member_access_without_permission
279     Role.find(1).remove_permission!(:view_issues)
280     @request.session[:user_id] = 2
281     get :show, :id => 1
282     assert_response 403
283   end
284   
285   def test_show_should_not_disclose_relations_to_invisible_issues
286     Setting.cross_project_issue_relations = '1'
287     IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
288     # Relation to a private project issue
289     IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
290     
291     get :show, :id => 1
292     assert_response :success
293     
294     assert_tag :div, :attributes => { :id => 'relations' },
295                      :descendant => { :tag => 'a', :content => /#2$/ }
296     assert_no_tag :div, :attributes => { :id => 'relations' },
297                         :descendant => { :tag => 'a', :content => /#4$/ }
298   end
299   
300   def test_show_atom
301     get :show, :id => 2, :format => 'atom'
302     assert_response :success
303     assert_template 'journals/index.rxml'
304     # Inline image
305     assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
306   end
307   
308   def test_show_export_to_pdf
309     get :show, :id => 3, :format => 'pdf'
310     assert_response :success
311     assert_equal 'application/pdf', @response.content_type
312     assert @response.body.starts_with?('%PDF')
313     assert_not_nil assigns(:issue)
314   end
315
316   def test_get_new
317     @request.session[:user_id] = 2
318     get :new, :project_id => 1, :tracker_id => 1
319     assert_response :success
320     assert_template 'new'
321     
322     assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
323                                                  :value => 'Default string' }
324   end
325
326   def test_get_new_without_tracker_id
327     @request.session[:user_id] = 2
328     get :new, :project_id => 1
329     assert_response :success
330     assert_template 'new'
331     
332     issue = assigns(:issue)
333     assert_not_nil issue
334     assert_equal Project.find(1).trackers.first, issue.tracker
335   end
336   
337   def test_get_new_with_no_default_status_should_display_an_error
338     @request.session[:user_id] = 2
339     IssueStatus.delete_all
340     
341     get :new, :project_id => 1
342     assert_response 500
343     assert_not_nil flash[:error]
344     assert_tag :tag => 'div', :attributes => { :class => /error/ },
345                               :content => /No default issue/
346   end
347   
348   def test_get_new_with_no_tracker_should_display_an_error
349     @request.session[:user_id] = 2
350     Tracker.delete_all
351     
352     get :new, :project_id => 1
353     assert_response 500
354     assert_not_nil flash[:error]
355     assert_tag :tag => 'div', :attributes => { :class => /error/ },
356                               :content => /No tracker/
357   end
358   
359   def test_update_new_form
360     @request.session[:user_id] = 2
361     xhr :post, :new, :project_id => 1,
362                      :issue => {:tracker_id => 2, 
363                                 :subject => 'This is the test_new issue',
364                                 :description => 'This is the description',
365                                 :priority_id => 5}
366     assert_response :success
367     assert_template 'attributes'
368     
369     issue = assigns(:issue)
370     assert_kind_of Issue, issue
371     assert_equal 1, issue.project_id
372     assert_equal 2, issue.tracker_id
373     assert_equal 'This is the test_new issue', issue.subject
374   end
375   
376   def test_post_create
377     @request.session[:user_id] = 2
378     assert_difference 'Issue.count' do
379       post :create, :project_id => 1, 
380                  :issue => {:tracker_id => 3,
381                             :status_id => 2,
382                             :subject => 'This is the test_new issue',
383                             :description => 'This is the description',
384                             :priority_id => 5,
385                             :estimated_hours => '',
386                             :custom_field_values => {'2' => 'Value for field 2'}}
387     end
388     assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
389     
390     issue = Issue.find_by_subject('This is the test_new issue')
391     assert_not_nil issue
392     assert_equal 2, issue.author_id
393     assert_equal 3, issue.tracker_id
394     assert_equal 2, issue.status_id
395     assert_nil issue.estimated_hours
396     v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
397     assert_not_nil v
398     assert_equal 'Value for field 2', v.value
399   end
400   
401   def test_post_create_and_continue
402     @request.session[:user_id] = 2
403     post :create, :project_id => 1, 
404                :issue => {:tracker_id => 3,
405                           :subject => 'This is first issue',
406                           :priority_id => 5},
407                :continue => ''
408     assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook',
409                          :issue => {:tracker_id => 3}
410   end
411   
412   def test_post_create_without_custom_fields_param
413     @request.session[:user_id] = 2
414     assert_difference 'Issue.count' do
415       post :create, :project_id => 1, 
416                  :issue => {:tracker_id => 1,
417                             :subject => 'This is the test_new issue',
418                             :description => 'This is the description',
419                             :priority_id => 5}
420     end
421     assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
422   end
423
424   def test_post_create_with_required_custom_field_and_without_custom_fields_param
425     field = IssueCustomField.find_by_name('Database')
426     field.update_attribute(:is_required, true)
427
428     @request.session[:user_id] = 2
429     post :create, :project_id => 1, 
430                :issue => {:tracker_id => 1,
431                           :subject => 'This is the test_new issue',
432                           :description => 'This is the description',
433                           :priority_id => 5}
434     assert_response :success
435     assert_template 'new'
436     issue = assigns(:issue)
437     assert_not_nil issue
438     assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
439   end
440   
441   def test_post_create_with_watchers
442     @request.session[:user_id] = 2
443     ActionMailer::Base.deliveries.clear
444     
445     assert_difference 'Watcher.count', 2 do
446       post :create, :project_id => 1, 
447                  :issue => {:tracker_id => 1,
448                             :subject => 'This is a new issue with watchers',
449                             :description => 'This is the description',
450                             :priority_id => 5,
451                             :watcher_user_ids => ['2', '3']}
452     end
453     issue = Issue.find_by_subject('This is a new issue with watchers')
454     assert_not_nil issue
455     assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
456     
457     # Watchers added
458     assert_equal [2, 3], issue.watcher_user_ids.sort
459     assert issue.watched_by?(User.find(3))
460     # Watchers notified
461     mail = ActionMailer::Base.deliveries.last
462     assert_kind_of TMail::Mail, mail
463     assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
464   end
465   
466   def test_post_create_subissue
467     @request.session[:user_id] = 2
468     
469     assert_difference 'Issue.count' do
470       post :create, :project_id => 1, 
471                  :issue => {:tracker_id => 1,
472                             :subject => 'This is a child issue',
473                             :parent_issue_id => 2}
474     end
475     issue = Issue.find_by_subject('This is a child issue')
476     assert_not_nil issue
477     assert_equal Issue.find(2), issue.parent
478   end
479   
480   def test_post_create_should_send_a_notification
481     ActionMailer::Base.deliveries.clear
482     @request.session[:user_id] = 2
483     assert_difference 'Issue.count' do
484       post :create, :project_id => 1, 
485                  :issue => {:tracker_id => 3,
486                             :subject => 'This is the test_new issue',
487                             :description => 'This is the description',
488                             :priority_id => 5,
489                             :estimated_hours => '',
490                             :custom_field_values => {'2' => 'Value for field 2'}}
491     end
492     assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
493     
494     assert_equal 1, ActionMailer::Base.deliveries.size
495   end
496   
497   def test_post_create_should_preserve_fields_values_on_validation_failure
498     @request.session[:user_id] = 2
499     post :create, :project_id => 1, 
500                :issue => {:tracker_id => 1,
501                           # empty subject
502                           :subject => '',
503                           :description => 'This is a description',
504                           :priority_id => 6,
505                           :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
506     assert_response :success
507     assert_template 'new'
508     
509     assert_tag :textarea, :attributes => { :name => 'issue[description]' },
510                           :content => 'This is a description'
511     assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
512                         :child => { :tag => 'option', :attributes => { :selected => 'selected',
513                                                                        :value => '6' },
514                                                       :content => 'High' }  
515     # Custom fields
516     assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
517                         :child => { :tag => 'option', :attributes => { :selected => 'selected',
518                                                                        :value => 'Oracle' },
519                                                       :content => 'Oracle' }  
520     assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
521                                         :value => 'Value for field 2'}
522   end
523   
524   def test_post_create_should_ignore_non_safe_attributes
525     @request.session[:user_id] = 2
526     assert_nothing_raised do
527       post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
528     end
529   end
530   
531   context "without workflow privilege" do
532     setup do
533       Workflow.delete_all(["role_id = ?", Role.anonymous.id])
534       Role.anonymous.add_permission! :add_issues
535     end
536     
537     context "#new" do
538       should "propose default status only" do
539         get :new, :project_id => 1
540         assert_response :success
541         assert_template 'new'
542         assert_tag :tag => 'select',
543           :attributes => {:name => 'issue[status_id]'},
544           :children => {:count => 1},
545           :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
546       end
547       
548       should "accept default status" do
549         assert_difference 'Issue.count' do
550           post :create, :project_id => 1, 
551                      :issue => {:tracker_id => 1,
552                                 :subject => 'This is an issue',
553                                 :status_id => 1}
554         end
555         issue = Issue.last(:order => 'id')
556         assert_equal IssueStatus.default, issue.status
557       end
558       
559       should "ignore unauthorized status" do
560         assert_difference 'Issue.count' do
561           post :create, :project_id => 1, 
562                      :issue => {:tracker_id => 1,
563                                 :subject => 'This is an issue',
564                                 :status_id => 3}
565         end
566         issue = Issue.last(:order => 'id')
567         assert_equal IssueStatus.default, issue.status
568       end
569     end
570   end
571   
572   def test_copy_issue
573     @request.session[:user_id] = 2
574     get :new, :project_id => 1, :copy_from => 1
575     assert_template 'new'
576     assert_not_nil assigns(:issue)
577     orig = Issue.find(1)
578     assert_equal orig.subject, assigns(:issue).subject
579   end
580   
581   def test_get_edit
582     @request.session[:user_id] = 2
583     get :edit, :id => 1
584     assert_response :success
585     assert_template 'edit'
586     assert_not_nil assigns(:issue)
587     assert_equal Issue.find(1), assigns(:issue)
588   end
589   
590   def test_get_edit_with_params
591     @request.session[:user_id] = 2
592     get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
593     assert_response :success
594     assert_template 'edit'
595     
596     issue = assigns(:issue)
597     assert_not_nil issue
598     
599     assert_equal 5, issue.status_id
600     assert_tag :select, :attributes => { :name => 'issue[status_id]' },
601                         :child => { :tag => 'option', 
602                                     :content => 'Closed',
603                                     :attributes => { :selected => 'selected' } }
604                                     
605     assert_equal 7, issue.priority_id
606     assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
607                         :child => { :tag => 'option', 
608                                     :content => 'Urgent',
609                                     :attributes => { :selected => 'selected' } }
610   end
611
612   def test_update_edit_form
613     @request.session[:user_id] = 2
614     xhr :post, :new, :project_id => 1,
615                              :id => 1,
616                              :issue => {:tracker_id => 2, 
617                                         :subject => 'This is the test_new issue',
618                                         :description => 'This is the description',
619                                         :priority_id => 5}
620     assert_response :success
621     assert_template 'attributes'
622     
623     issue = assigns(:issue)
624     assert_kind_of Issue, issue
625     assert_equal 1, issue.id
626     assert_equal 1, issue.project_id
627     assert_equal 2, issue.tracker_id
628     assert_equal 'This is the test_new issue', issue.subject
629   end
630   
631   def test_update_using_invalid_http_verbs
632     @request.session[:user_id] = 2
633     subject = 'Updated by an invalid http verb'
634
635     get :update, :id => 1, :issue => {:subject => subject}
636     assert_not_equal subject, Issue.find(1).subject
637
638     post :update, :id => 1, :issue => {:subject => subject}
639     assert_not_equal subject, Issue.find(1).subject
640
641     delete :update, :id => 1, :issue => {:subject => subject}
642     assert_not_equal subject, Issue.find(1).subject
643   end
644
645   def test_put_update_without_custom_fields_param
646     @request.session[:user_id] = 2
647     ActionMailer::Base.deliveries.clear
648     
649     issue = Issue.find(1)
650     assert_equal '125', issue.custom_value_for(2).value
651     old_subject = issue.subject
652     new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
653     
654     assert_difference('Journal.count') do
655       assert_difference('JournalDetail.count', 2) do
656         put :update, :id => 1, :issue => {:subject => new_subject,
657                                          :priority_id => '6',
658                                          :category_id => '1' # no change
659                                         }
660       end
661     end
662     assert_redirected_to :action => 'show', :id => '1'
663     issue.reload
664     assert_equal new_subject, issue.subject
665     # Make sure custom fields were not cleared
666     assert_equal '125', issue.custom_value_for(2).value
667     
668     mail = ActionMailer::Base.deliveries.last
669     assert_kind_of TMail::Mail, mail
670     assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
671     assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
672   end
673   
674   def test_put_update_with_custom_field_change
675     @request.session[:user_id] = 2
676     issue = Issue.find(1)
677     assert_equal '125', issue.custom_value_for(2).value
678     
679     assert_difference('Journal.count') do
680       assert_difference('JournalDetail.count', 3) do
681         put :update, :id => 1, :issue => {:subject => 'Custom field change',
682                                          :priority_id => '6',
683                                          :category_id => '1', # no change
684                                          :custom_field_values => { '2' => 'New custom value' }
685                                         }
686       end
687     end
688     assert_redirected_to :action => 'show', :id => '1'
689     issue.reload
690     assert_equal 'New custom value', issue.custom_value_for(2).value
691     
692     mail = ActionMailer::Base.deliveries.last
693     assert_kind_of TMail::Mail, mail
694     assert mail.body.include?("Searchable field changed from 125 to New custom value")
695   end
696   
697   def test_put_update_with_status_and_assignee_change
698     issue = Issue.find(1)
699     assert_equal 1, issue.status_id
700     @request.session[:user_id] = 2
701     assert_difference('TimeEntry.count', 0) do
702       put :update,
703            :id => 1,
704            :issue => { :status_id => 2, :assigned_to_id => 3 },
705            :notes => 'Assigned to dlopper',
706            :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
707     end
708     assert_redirected_to :action => 'show', :id => '1'
709     issue.reload
710     assert_equal 2, issue.status_id
711     j = Journal.find(:first, :order => 'id DESC')
712     assert_equal 'Assigned to dlopper', j.notes
713     assert_equal 2, j.details.size
714     
715     mail = ActionMailer::Base.deliveries.last
716     assert mail.body.include?("Status changed from New to Assigned")
717     # subject should contain the new status
718     assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
719   end
720   
721   def test_put_update_with_note_only
722     notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
723     # anonymous user
724     put :update,
725          :id => 1,
726          :notes => notes
727     assert_redirected_to :action => 'show', :id => '1'
728     j = Journal.find(:first, :order => 'id DESC')
729     assert_equal notes, j.notes
730     assert_equal 0, j.details.size
731     assert_equal User.anonymous, j.user
732     
733     mail = ActionMailer::Base.deliveries.last
734     assert mail.body.include?(notes)
735   end
736   
737   def test_put_update_with_note_and_spent_time
738     @request.session[:user_id] = 2
739     spent_hours_before = Issue.find(1).spent_hours
740     assert_difference('TimeEntry.count') do
741       put :update,
742            :id => 1,
743            :notes => '2.5 hours added',
744            :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
745     end
746     assert_redirected_to :action => 'show', :id => '1'
747     
748     issue = Issue.find(1)
749     
750     j = Journal.find(:first, :order => 'id DESC')
751     assert_equal '2.5 hours added', j.notes
752     assert_equal 0, j.details.size
753     
754     t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
755     assert_not_nil t
756     assert_equal 2.5, t.hours
757     assert_equal spent_hours_before + 2.5, issue.spent_hours
758   end
759   
760   def test_put_update_with_attachment_only
761     set_tmp_attachments_directory
762     
763     # Delete all fixtured journals, a race condition can occur causing the wrong
764     # journal to get fetched in the next find.
765     Journal.delete_all
766
767     # anonymous user
768     put :update,
769          :id => 1,
770          :notes => '',
771          :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
772     assert_redirected_to :action => 'show', :id => '1'
773     j = Issue.find(1).journals.find(:first, :order => 'id DESC')
774     assert j.notes.blank?
775     assert_equal 1, j.details.size
776     assert_equal 'testfile.txt', j.details.first.value
777     assert_equal User.anonymous, j.user
778     
779     mail = ActionMailer::Base.deliveries.last
780     assert mail.body.include?('testfile.txt')
781   end
782
783   def test_put_update_with_attachment_that_fails_to_save
784     set_tmp_attachments_directory
785     
786     # Delete all fixtured journals, a race condition can occur causing the wrong
787     # journal to get fetched in the next find.
788     Journal.delete_all
789
790     # Mock out the unsaved attachment
791     Attachment.any_instance.stubs(:create).returns(Attachment.new)
792     
793     # anonymous user
794     put :update,
795          :id => 1,
796          :notes => '',
797          :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
798     assert_redirected_to :action => 'show', :id => '1'
799     assert_equal '1 file(s) could not be saved.', flash[:warning]
800
801   end if Object.const_defined?(:Mocha)
802
803   def test_put_update_with_no_change
804     issue = Issue.find(1)
805     issue.journals.clear
806     ActionMailer::Base.deliveries.clear
807     
808     put :update,
809          :id => 1,
810          :notes => ''
811     assert_redirected_to :action => 'show', :id => '1'
812     
813     issue.reload
814     assert issue.journals.empty?
815     # No email should be sent
816     assert ActionMailer::Base.deliveries.empty?
817   end
818
819   def test_put_update_should_send_a_notification
820     @request.session[:user_id] = 2
821     ActionMailer::Base.deliveries.clear
822     issue = Issue.find(1)
823     old_subject = issue.subject
824     new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
825     
826     put :update, :id => 1, :issue => {:subject => new_subject,
827                                      :priority_id => '6',
828                                      :category_id => '1' # no change
829                                     }
830     assert_equal 1, ActionMailer::Base.deliveries.size
831   end
832   
833   def test_put_update_with_invalid_spent_time
834     @request.session[:user_id] = 2
835     notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
836     
837     assert_no_difference('Journal.count') do
838       put :update,
839            :id => 1,
840            :notes => notes,
841            :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
842     end
843     assert_response :success
844     assert_template 'edit'
845     
846     assert_tag :textarea, :attributes => { :name => 'notes' },
847                           :content => notes
848     assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
849   end
850   
851   def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
852     issue = Issue.find(2)
853     @request.session[:user_id] = 2
854
855     put :update,
856          :id => issue.id,
857          :issue => {
858            :fixed_version_id => 4
859          }
860
861     assert_response :redirect
862     issue.reload
863     assert_equal 4, issue.fixed_version_id
864     assert_not_equal issue.project_id, issue.fixed_version.project_id
865   end
866
867   def test_put_update_should_redirect_back_using_the_back_url_parameter
868     issue = Issue.find(2)
869     @request.session[:user_id] = 2
870
871     put :update,
872          :id => issue.id,
873          :issue => {
874            :fixed_version_id => 4
875          },
876          :back_url => '/issues'
877
878     assert_response :redirect
879     assert_redirected_to '/issues'
880   end
881   
882   def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
883     issue = Issue.find(2)
884     @request.session[:user_id] = 2
885
886     put :update,
887          :id => issue.id,
888          :issue => {
889            :fixed_version_id => 4
890          },
891          :back_url => 'http://google.com'
892
893     assert_response :redirect
894     assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
895   end
896   
897   def test_get_bulk_edit
898     @request.session[:user_id] = 2
899     get :bulk_edit, :ids => [1, 2]
900     assert_response :success
901     assert_template 'bulk_edit'
902     
903     # Project specific custom field, date type
904     field = CustomField.find(9)
905     assert !field.is_for_all?
906     assert_equal 'date', field.field_format
907     assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
908     
909     # System wide custom field
910     assert CustomField.find(1).is_for_all?
911     assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
912   end
913
914   def test_bulk_update
915     @request.session[:user_id] = 2
916     # update issues priority
917     post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
918                                      :issue => {:priority_id => 7,
919                                                 :assigned_to_id => '',
920                                                 :custom_field_values => {'2' => ''}}
921                                      
922     assert_response 302
923     # check that the issues were updated
924     assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
925     
926     issue = Issue.find(1)
927     journal = issue.journals.find(:first, :order => 'created_on DESC')
928     assert_equal '125', issue.custom_value_for(2).value
929     assert_equal 'Bulk editing', journal.notes
930     assert_equal 1, journal.details.size
931   end
932
933   def test_bullk_update_should_send_a_notification
934     @request.session[:user_id] = 2
935     ActionMailer::Base.deliveries.clear
936     post(:bulk_update,
937          {
938            :ids => [1, 2],
939            :notes => 'Bulk editing',
940            :issue => {
941              :priority_id => 7,
942              :assigned_to_id => '',
943              :custom_field_values => {'2' => ''}
944            }
945          })
946
947     assert_response 302
948     assert_equal 2, ActionMailer::Base.deliveries.size
949   end
950
951   def test_bulk_update_status
952     @request.session[:user_id] = 2
953     # update issues priority
954     post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
955                                      :issue => {:priority_id => '',
956                                                 :assigned_to_id => '',
957                                                 :status_id => '5'}
958                                      
959     assert_response 302
960     issue = Issue.find(1)
961     assert issue.closed?
962   end
963
964   def test_bulk_update_custom_field
965     @request.session[:user_id] = 2
966     # update issues priority
967     post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
968                                      :issue => {:priority_id => '',
969                                                 :assigned_to_id => '',
970                                                 :custom_field_values => {'2' => '777'}}
971                                      
972     assert_response 302
973     
974     issue = Issue.find(1)
975     journal = issue.journals.find(:first, :order => 'created_on DESC')
976     assert_equal '777', issue.custom_value_for(2).value
977     assert_equal 1, journal.details.size
978     assert_equal '125', journal.details.first.old_value
979     assert_equal '777', journal.details.first.value
980   end
981
982   def test_bulk_update_unassign
983     assert_not_nil Issue.find(2).assigned_to
984     @request.session[:user_id] = 2
985     # unassign issues
986     post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
987     assert_response 302
988     # check that the issues were updated
989     assert_nil Issue.find(2).assigned_to
990   end
991   
992   def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
993     @request.session[:user_id] = 2
994
995     post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
996
997     assert_response :redirect
998     issues = Issue.find([1,2])
999     issues.each do |issue|
1000       assert_equal 4, issue.fixed_version_id
1001       assert_not_equal issue.project_id, issue.fixed_version.project_id
1002     end
1003   end
1004
1005   def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
1006     @request.session[:user_id] = 2
1007     post :bulk_update, :ids => [1,2], :back_url => '/issues'
1008
1009     assert_response :redirect
1010     assert_redirected_to '/issues'
1011   end
1012
1013   def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1014     @request.session[:user_id] = 2
1015     post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
1016
1017     assert_response :redirect
1018     assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
1019   end
1020   
1021   def test_destroy_issue_with_no_time_entries
1022     assert_nil TimeEntry.find_by_issue_id(2)
1023     @request.session[:user_id] = 2
1024     post :destroy, :id => 2
1025     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1026     assert_nil Issue.find_by_id(2)
1027   end
1028
1029   def test_destroy_issues_with_time_entries
1030     @request.session[:user_id] = 2
1031     post :destroy, :ids => [1, 3]
1032     assert_response :success
1033     assert_template 'destroy'
1034     assert_not_nil assigns(:hours)
1035     assert Issue.find_by_id(1) && Issue.find_by_id(3)
1036   end
1037
1038   def test_destroy_issues_and_destroy_time_entries
1039     @request.session[:user_id] = 2
1040     post :destroy, :ids => [1, 3], :todo => 'destroy'
1041     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1042     assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1043     assert_nil TimeEntry.find_by_id([1, 2])
1044   end
1045
1046   def test_destroy_issues_and_assign_time_entries_to_project
1047     @request.session[:user_id] = 2
1048     post :destroy, :ids => [1, 3], :todo => 'nullify'
1049     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1050     assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1051     assert_nil TimeEntry.find(1).issue_id
1052     assert_nil TimeEntry.find(2).issue_id
1053   end
1054   
1055   def test_destroy_issues_and_reassign_time_entries_to_another_issue
1056     @request.session[:user_id] = 2
1057     post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1058     assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1059     assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1060     assert_equal 2, TimeEntry.find(1).issue_id
1061     assert_equal 2, TimeEntry.find(2).issue_id
1062   end
1063   
1064   def test_destroy_issues_from_different_projects
1065     @request.session[:user_id] = 2
1066     post :destroy, :ids => [1, 2, 6], :todo => 'destroy'
1067     assert_redirected_to :controller => 'issues', :action => 'index'
1068     assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
1069   end
1070   
1071   def test_default_search_scope
1072     get :index
1073     assert_tag :div, :attributes => {:id => 'quick-search'},
1074                      :child => {:tag => 'form',
1075                                 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1076   end
1077 end