##// END OF EJS Templates
Fixed: empty list for user/version custom fields on bulk edit form (#2096)....
Jean-Philippe Lang -
r5234:d0ea5fae62b1
parent child
Show More
@@ -1,118 +1,118
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module CustomFieldsHelper
18 module CustomFieldsHelper
19
19
20 def custom_fields_tabs
20 def custom_fields_tabs
21 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
21 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
22 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
22 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
23 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
23 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
24 {:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
24 {:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
25 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
25 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
26 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
26 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
27 {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
27 {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
28 {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
28 {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
29 {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
29 {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
30 ]
30 ]
31 end
31 end
32
32
33 # Return custom field html tag corresponding to its format
33 # Return custom field html tag corresponding to its format
34 def custom_field_tag(name, custom_value)
34 def custom_field_tag(name, custom_value)
35 custom_field = custom_value.custom_field
35 custom_field = custom_value.custom_field
36 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
36 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
37 field_id = "#{name}_custom_field_values_#{custom_field.id}"
37 field_id = "#{name}_custom_field_values_#{custom_field.id}"
38
38
39 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
39 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
40 case field_format.try(:edit_as)
40 case field_format.try(:edit_as)
41 when "date"
41 when "date"
42 text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
42 text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
43 calendar_for(field_id)
43 calendar_for(field_id)
44 when "text"
44 when "text"
45 text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
45 text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
46 when "bool"
46 when "bool"
47 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
47 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
48 when "list"
48 when "list"
49 blank_option = custom_field.is_required? ?
49 blank_option = custom_field.is_required? ?
50 (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
50 (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
51 '<option></option>'
51 '<option></option>'
52 select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id)
52 select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id)
53 else
53 else
54 text_field_tag(field_name, custom_value.value, :id => field_id)
54 text_field_tag(field_name, custom_value.value, :id => field_id)
55 end
55 end
56 end
56 end
57
57
58 # Return custom field label tag
58 # Return custom field label tag
59 def custom_field_label_tag(name, custom_value)
59 def custom_field_label_tag(name, custom_value)
60 content_tag "label", custom_value.custom_field.name +
60 content_tag "label", custom_value.custom_field.name +
61 (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>" : ""),
61 (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>" : ""),
62 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}",
62 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}",
63 :class => (custom_value.errors.empty? ? nil : "error" )
63 :class => (custom_value.errors.empty? ? nil : "error" )
64 end
64 end
65
65
66 # Return custom field tag with its label tag
66 # Return custom field tag with its label tag
67 def custom_field_tag_with_label(name, custom_value)
67 def custom_field_tag_with_label(name, custom_value)
68 custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
68 custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
69 end
69 end
70
70
71 def custom_field_tag_for_bulk_edit(name, custom_field)
71 def custom_field_tag_for_bulk_edit(name, custom_field, projects)
72 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
72 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
73 field_id = "#{name}_custom_field_values_#{custom_field.id}"
73 field_id = "#{name}_custom_field_values_#{custom_field.id}"
74 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
74 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
75 case field_format.try(:edit_as)
75 case field_format.try(:edit_as)
76 when "date"
76 when "date"
77 text_field_tag(field_name, '', :id => field_id, :size => 10) +
77 text_field_tag(field_name, '', :id => field_id, :size => 10) +
78 calendar_for(field_id)
78 calendar_for(field_id)
79 when "text"
79 when "text"
80 text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
80 text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
81 when "bool"
81 when "bool"
82 select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
82 select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
83 [l(:general_text_yes), '1'],
83 [l(:general_text_yes), '1'],
84 [l(:general_text_no), '0']]), :id => field_id)
84 [l(:general_text_no), '0']]), :id => field_id)
85 when "list"
85 when "list"
86 select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values_options), :id => field_id)
86 select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values_options(projects)), :id => field_id)
87 else
87 else
88 text_field_tag(field_name, '', :id => field_id)
88 text_field_tag(field_name, '', :id => field_id)
89 end
89 end
90 end
90 end
91
91
92 # Return a string used to display a custom value
92 # Return a string used to display a custom value
93 def show_value(custom_value)
93 def show_value(custom_value)
94 return "" unless custom_value
94 return "" unless custom_value
95 format_value(custom_value.value, custom_value.custom_field.field_format)
95 format_value(custom_value.value, custom_value.custom_field.field_format)
96 end
96 end
97
97
98 # Return a string used to display a custom value
98 # Return a string used to display a custom value
99 def format_value(value, field_format)
99 def format_value(value, field_format)
100 Redmine::CustomFieldFormat.format_value(value, field_format) # Proxy
100 Redmine::CustomFieldFormat.format_value(value, field_format) # Proxy
101 end
101 end
102
102
103 # Return an array of custom field formats which can be used in select_tag
103 # Return an array of custom field formats which can be used in select_tag
104 def custom_field_formats_for_select(custom_field)
104 def custom_field_formats_for_select(custom_field)
105 Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
105 Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
106 end
106 end
107
107
108 # Renders the custom_values in api views
108 # Renders the custom_values in api views
109 def render_api_custom_values(custom_values, api)
109 def render_api_custom_values(custom_values, api)
110 api.array :custom_fields do
110 api.array :custom_fields do
111 custom_values.each do |custom_value|
111 custom_values.each do |custom_value|
112 api.custom_field :id => custom_value.custom_field_id, :name => custom_value.custom_field.name do
112 api.custom_field :id => custom_value.custom_field_id, :name => custom_value.custom_field.name do
113 api.value custom_value.value
113 api.value custom_value.value
114 end
114 end
115 end
115 end
116 end unless custom_values.empty?
116 end unless custom_values.empty?
117 end
117 end
118 end
118 end
@@ -1,149 +1,151
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class CustomField < ActiveRecord::Base
18 class CustomField < ActiveRecord::Base
19 has_many :custom_values, :dependent => :delete_all
19 has_many :custom_values, :dependent => :delete_all
20 acts_as_list :scope => 'type = \'#{self.class}\''
20 acts_as_list :scope => 'type = \'#{self.class}\''
21 serialize :possible_values
21 serialize :possible_values
22
22
23 validates_presence_of :name, :field_format
23 validates_presence_of :name, :field_format
24 validates_uniqueness_of :name, :scope => :type
24 validates_uniqueness_of :name, :scope => :type
25 validates_length_of :name, :maximum => 30
25 validates_length_of :name, :maximum => 30
26 validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
26 validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
27
27
28 def initialize(attributes = nil)
28 def initialize(attributes = nil)
29 super
29 super
30 self.possible_values ||= []
30 self.possible_values ||= []
31 end
31 end
32
32
33 def before_validation
33 def before_validation
34 # make sure these fields are not searchable
34 # make sure these fields are not searchable
35 self.searchable = false if %w(int float date bool).include?(field_format)
35 self.searchable = false if %w(int float date bool).include?(field_format)
36 true
36 true
37 end
37 end
38
38
39 def validate
39 def validate
40 if self.field_format == "list"
40 if self.field_format == "list"
41 errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty?
41 errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty?
42 errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array
42 errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array
43 end
43 end
44
44
45 # validate default value
45 # validate default value
46 v = CustomValue.new(:custom_field => self.clone, :value => default_value, :customized => nil)
46 v = CustomValue.new(:custom_field => self.clone, :value => default_value, :customized => nil)
47 v.custom_field.is_required = false
47 v.custom_field.is_required = false
48 errors.add(:default_value, :invalid) unless v.valid?
48 errors.add(:default_value, :invalid) unless v.valid?
49 end
49 end
50
50
51 def possible_values_options(obj=nil)
51 def possible_values_options(obj=nil)
52 case field_format
52 case field_format
53 when 'user', 'version'
53 when 'user', 'version'
54 if obj.respond_to?(:project) && obj.project
54 if obj.respond_to?(:project) && obj.project
55 case field_format
55 case field_format
56 when 'user'
56 when 'user'
57 obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]}
57 obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]}
58 when 'version'
58 when 'version'
59 obj.project.versions.sort.collect {|u| [u.to_s, u.id.to_s]}
59 obj.project.versions.sort.collect {|u| [u.to_s, u.id.to_s]}
60 end
60 end
61 elsif obj.is_a?(Array)
62 obj.collect {|o| possible_values_options(o)}.inject {|memo, v| memo & v}
61 else
63 else
62 []
64 []
63 end
65 end
64 else
66 else
65 read_attribute :possible_values
67 read_attribute :possible_values
66 end
68 end
67 end
69 end
68
70
69 def possible_values(obj=nil)
71 def possible_values(obj=nil)
70 case field_format
72 case field_format
71 when 'user', 'version'
73 when 'user', 'version'
72 possible_values_options(obj).collect(&:last)
74 possible_values_options(obj).collect(&:last)
73 else
75 else
74 read_attribute :possible_values
76 read_attribute :possible_values
75 end
77 end
76 end
78 end
77
79
78 # Makes possible_values accept a multiline string
80 # Makes possible_values accept a multiline string
79 def possible_values=(arg)
81 def possible_values=(arg)
80 if arg.is_a?(Array)
82 if arg.is_a?(Array)
81 write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?})
83 write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?})
82 else
84 else
83 self.possible_values = arg.to_s.split(/[\n\r]+/)
85 self.possible_values = arg.to_s.split(/[\n\r]+/)
84 end
86 end
85 end
87 end
86
88
87 def cast_value(value)
89 def cast_value(value)
88 casted = nil
90 casted = nil
89 unless value.blank?
91 unless value.blank?
90 case field_format
92 case field_format
91 when 'string', 'text', 'list'
93 when 'string', 'text', 'list'
92 casted = value
94 casted = value
93 when 'date'
95 when 'date'
94 casted = begin; value.to_date; rescue; nil end
96 casted = begin; value.to_date; rescue; nil end
95 when 'bool'
97 when 'bool'
96 casted = (value == '1' ? true : false)
98 casted = (value == '1' ? true : false)
97 when 'int'
99 when 'int'
98 casted = value.to_i
100 casted = value.to_i
99 when 'float'
101 when 'float'
100 casted = value.to_f
102 casted = value.to_f
101 when 'user', 'version'
103 when 'user', 'version'
102 casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i))
104 casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i))
103 end
105 end
104 end
106 end
105 casted
107 casted
106 end
108 end
107
109
108 # Returns a ORDER BY clause that can used to sort customized
110 # Returns a ORDER BY clause that can used to sort customized
109 # objects by their value of the custom field.
111 # objects by their value of the custom field.
110 # Returns false, if the custom field can not be used for sorting.
112 # Returns false, if the custom field can not be used for sorting.
111 def order_statement
113 def order_statement
112 case field_format
114 case field_format
113 when 'string', 'text', 'list', 'date', 'bool'
115 when 'string', 'text', 'list', 'date', 'bool'
114 # COALESCE is here to make sure that blank and NULL values are sorted equally
116 # COALESCE is here to make sure that blank and NULL values are sorted equally
115 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
117 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
116 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
118 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
117 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
119 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
118 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
120 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
119 when 'int', 'float'
121 when 'int', 'float'
120 # Make the database cast values into numeric
122 # Make the database cast values into numeric
121 # Postgresql will raise an error if a value can not be casted!
123 # Postgresql will raise an error if a value can not be casted!
122 # CustomValue validations should ensure that it doesn't occur
124 # CustomValue validations should ensure that it doesn't occur
123 "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
125 "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
124 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
126 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
125 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
127 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
126 " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
128 " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
127 else
129 else
128 nil
130 nil
129 end
131 end
130 end
132 end
131
133
132 def <=>(field)
134 def <=>(field)
133 position <=> field.position
135 position <=> field.position
134 end
136 end
135
137
136 def self.customized_class
138 def self.customized_class
137 self.name =~ /^(.+)CustomField$/
139 self.name =~ /^(.+)CustomField$/
138 begin; $1.constantize; rescue nil; end
140 begin; $1.constantize; rescue nil; end
139 end
141 end
140
142
141 # to move in project_custom_field
143 # to move in project_custom_field
142 def self.for_all
144 def self.for_all
143 find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
145 find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
144 end
146 end
145
147
146 def type_name
148 def type_name
147 nil
149 nil
148 end
150 end
149 end
151 end
@@ -1,91 +1,91
1 <h2><%= l(:label_bulk_edit_selected_issues) %></h2>
1 <h2><%= l(:label_bulk_edit_selected_issues) %></h2>
2
2
3 <ul><%= @issues.collect {|i| content_tag('li', link_to(h("#{i.tracker} ##{i.id}"), { :action => 'show', :id => i }) + h(": #{i.subject}")) }.join("\n") %></ul>
3 <ul><%= @issues.collect {|i| content_tag('li', link_to(h("#{i.tracker} ##{i.id}"), { :action => 'show', :id => i }) + h(": #{i.subject}")) }.join("\n") %></ul>
4
4
5 <% form_tag(:action => 'bulk_update') do %>
5 <% form_tag(:action => 'bulk_update') do %>
6 <%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
6 <%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
7 <div class="box tabular">
7 <div class="box tabular">
8 <fieldset class="attributes">
8 <fieldset class="attributes">
9 <legend><%= l(:label_change_properties) %></legend>
9 <legend><%= l(:label_change_properties) %></legend>
10
10
11 <div class="splitcontentleft">
11 <div class="splitcontentleft">
12 <p>
12 <p>
13 <label><%= l(:field_tracker) %></label>
13 <label><%= l(:field_tracker) %></label>
14 <%= select_tag('issue[tracker_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, :id, :name)) %>
14 <%= select_tag('issue[tracker_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, :id, :name)) %>
15 </p>
15 </p>
16 <% if @available_statuses.any? %>
16 <% if @available_statuses.any? %>
17 <p>
17 <p>
18 <label><%= l(:field_status) %></label>
18 <label><%= l(:field_status) %></label>
19 <%= select_tag('issue[status_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_statuses, :id, :name)) %>
19 <%= select_tag('issue[status_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_statuses, :id, :name)) %>
20 </p>
20 </p>
21 <% end %>
21 <% end %>
22 <p>
22 <p>
23 <label><%= l(:field_priority) %></label>
23 <label><%= l(:field_priority) %></label>
24 <%= select_tag('issue[priority_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(IssuePriority.all, :id, :name)) %>
24 <%= select_tag('issue[priority_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(IssuePriority.all, :id, :name)) %>
25 </p>
25 </p>
26 <p>
26 <p>
27 <label><%= l(:field_assigned_to) %></label>
27 <label><%= l(:field_assigned_to) %></label>
28 <%= select_tag('issue[assigned_to_id]', content_tag('option', l(:label_no_change_option), :value => '') +
28 <%= select_tag('issue[assigned_to_id]', content_tag('option', l(:label_no_change_option), :value => '') +
29 content_tag('option', l(:label_nobody), :value => 'none') +
29 content_tag('option', l(:label_nobody), :value => 'none') +
30 options_from_collection_for_select(@assignables, :id, :name)) %>
30 options_from_collection_for_select(@assignables, :id, :name)) %>
31 </p>
31 </p>
32 <% if @project %>
32 <% if @project %>
33 <p>
33 <p>
34 <label><%= l(:field_category) %></label>
34 <label><%= l(:field_category) %></label>
35 <%= select_tag('issue[category_id]', content_tag('option', l(:label_no_change_option), :value => '') +
35 <%= select_tag('issue[category_id]', content_tag('option', l(:label_no_change_option), :value => '') +
36 content_tag('option', l(:label_none), :value => 'none') +
36 content_tag('option', l(:label_none), :value => 'none') +
37 options_from_collection_for_select(@project.issue_categories, :id, :name)) %>
37 options_from_collection_for_select(@project.issue_categories, :id, :name)) %>
38 </p>
38 </p>
39 <% end %>
39 <% end %>
40 <% #TODO: allow editing versions when multiple projects %>
40 <% #TODO: allow editing versions when multiple projects %>
41 <% if @project %>
41 <% if @project %>
42 <p>
42 <p>
43 <label><%= l(:field_fixed_version) %></label>
43 <label><%= l(:field_fixed_version) %></label>
44 <%= select_tag('issue[fixed_version_id]', content_tag('option', l(:label_no_change_option), :value => '') +
44 <%= select_tag('issue[fixed_version_id]', content_tag('option', l(:label_no_change_option), :value => '') +
45 content_tag('option', l(:label_none), :value => 'none') +
45 content_tag('option', l(:label_none), :value => 'none') +
46 version_options_for_select(@project.shared_versions.open.sort)) %>
46 version_options_for_select(@project.shared_versions.open.sort)) %>
47 </p>
47 </p>
48 <% end %>
48 <% end %>
49
49
50 <% @custom_fields.each do |custom_field| %>
50 <% @custom_fields.each do |custom_field| %>
51 <p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('issue', custom_field) %></p>
51 <p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('issue', custom_field, @projects) %></p>
52 <% end %>
52 <% end %>
53
53
54 <%= call_hook(:view_issues_bulk_edit_details_bottom, { :issues => @issues }) %>
54 <%= call_hook(:view_issues_bulk_edit_details_bottom, { :issues => @issues }) %>
55 </div>
55 </div>
56
56
57 <div class="splitcontentright">
57 <div class="splitcontentright">
58 <% if @project && User.current.allowed_to?(:manage_subtasks, @project) %>
58 <% if @project && User.current.allowed_to?(:manage_subtasks, @project) %>
59 <p>
59 <p>
60 <label><%= l(:field_parent_issue) %></label>
60 <label><%= l(:field_parent_issue) %></label>
61 <%= text_field_tag 'issue[parent_issue_id]', '', :size => 10 %>
61 <%= text_field_tag 'issue[parent_issue_id]', '', :size => 10 %>
62 </p>
62 </p>
63 <div id="parent_issue_candidates" class="autocomplete"></div>
63 <div id="parent_issue_candidates" class="autocomplete"></div>
64 <%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:project_id => @project) }')" %>
64 <%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:project_id => @project) }')" %>
65 <% end %>
65 <% end %>
66 <p>
66 <p>
67 <label><%= l(:field_start_date) %></label>
67 <label><%= l(:field_start_date) %></label>
68 <%= text_field_tag 'issue[start_date]', '', :size => 10 %><%= calendar_for('issue_start_date') %>
68 <%= text_field_tag 'issue[start_date]', '', :size => 10 %><%= calendar_for('issue_start_date') %>
69 </p>
69 </p>
70 <p>
70 <p>
71 <label><%= l(:field_due_date) %></label>
71 <label><%= l(:field_due_date) %></label>
72 <%= text_field_tag 'issue[due_date]', '', :size => 10 %><%= calendar_for('issue_due_date') %>
72 <%= text_field_tag 'issue[due_date]', '', :size => 10 %><%= calendar_for('issue_due_date') %>
73 </p>
73 </p>
74 <% if Issue.use_field_for_done_ratio? %>
74 <% if Issue.use_field_for_done_ratio? %>
75 <p>
75 <p>
76 <label><%= l(:field_done_ratio) %></label>
76 <label><%= l(:field_done_ratio) %></label>
77 <%= select_tag 'issue[done_ratio]', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %>
77 <%= select_tag 'issue[done_ratio]', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %>
78 </p>
78 </p>
79 <% end %>
79 <% end %>
80 </div>
80 </div>
81
81
82 </fieldset>
82 </fieldset>
83
83
84 <fieldset><legend><%= l(:field_notes) %></legend>
84 <fieldset><legend><%= l(:field_notes) %></legend>
85 <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
85 <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
86 <%= wikitoolbar_for 'notes' %>
86 <%= wikitoolbar_for 'notes' %>
87 </fieldset>
87 </fieldset>
88 </div>
88 </div>
89
89
90 <p><%= submit_tag l(:button_submit) %></p>
90 <p><%= submit_tag l(:button_submit) %></p>
91 <% end %>
91 <% end %>
@@ -1,49 +1,49
1 <h2><%= l(:label_bulk_edit_selected_time_entries) %></h2>
1 <h2><%= l(:label_bulk_edit_selected_time_entries) %></h2>
2
2
3 <ul><%= @time_entries.collect {|i| content_tag('li', link_to(h("#{i.spent_on.strftime("%Y-%m-%d")} -- #{i.project}: #{l(:label_f_hour_plural, :value => i.hours)}"), { :action => 'edit', :id => i }))} %></ul>
3 <ul><%= @time_entries.collect {|i| content_tag('li', link_to(h("#{i.spent_on.strftime("%Y-%m-%d")} -- #{i.project}: #{l(:label_f_hour_plural, :value => i.hours)}"), { :action => 'edit', :id => i }))} %></ul>
4
4
5 <% form_tag(:action => 'bulk_update') do %>
5 <% form_tag(:action => 'bulk_update') do %>
6 <%= @time_entries.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
6 <%= @time_entries.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
7 <div class="box tabular">
7 <div class="box tabular">
8 <fieldset class="attributes">
8 <fieldset class="attributes">
9 <legend><%= l(:label_change_properties) %></legend>
9 <legend><%= l(:label_change_properties) %></legend>
10 <div>
10 <div>
11 <p>
11 <p>
12 <label><%= l(:field_issue) %></label>
12 <label><%= l(:field_issue) %></label>
13 <%= text_field :time_entry, :issue_id, :size => 6 %>
13 <%= text_field :time_entry, :issue_id, :size => 6 %>
14 </p>
14 </p>
15
15
16 <p>
16 <p>
17 <label><%= l(:field_spent_on) %></label>
17 <label><%= l(:field_spent_on) %></label>
18 <%= text_field :time_entry, :spent_on, :size => 10 %><%= calendar_for('time_entry_spent_on') %>
18 <%= text_field :time_entry, :spent_on, :size => 10 %><%= calendar_for('time_entry_spent_on') %>
19 </p>
19 </p>
20
20
21 <p>
21 <p>
22 <label><%= l(:field_hours) %></label>
22 <label><%= l(:field_hours) %></label>
23 <%= text_field :time_entry, :hours, :size => 6 %>
23 <%= text_field :time_entry, :hours, :size => 6 %>
24 </p>
24 </p>
25
25
26 <% if @available_activities.any? %>
26 <% if @available_activities.any? %>
27 <p>
27 <p>
28 <label><%= l(:field_activity) %></label>
28 <label><%= l(:field_activity) %></label>
29 <%= select_tag('time_entry[activity_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_activities, :id, :name)) %>
29 <%= select_tag('time_entry[activity_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_activities, :id, :name)) %>
30 </p>
30 </p>
31 <% end %>
31 <% end %>
32
32
33 <p>
33 <p>
34 <label><%= l(:field_comments) %></label>
34 <label><%= l(:field_comments) %></label>
35 <%= text_field(:time_entry, :comments, :size => 100) %>
35 <%= text_field(:time_entry, :comments, :size => 100) %>
36 </p>
36 </p>
37
37
38 <% @custom_fields.each do |custom_field| %>
38 <% @custom_fields.each do |custom_field| %>
39 <p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('time_entry', custom_field) %></p>
39 <p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('time_entry', custom_field, @projects) %></p>
40 <% end %>
40 <% end %>
41
41
42 <%= call_hook(:view_time_entries_bulk_edit_details_bottom, { :time_entries => @time_entries }) %>
42 <%= call_hook(:view_time_entries_bulk_edit_details_bottom, { :time_entries => @time_entries }) %>
43 </div>
43 </div>
44
44
45 </fieldset>
45 </fieldset>
46 </div>
46 </div>
47
47
48 <p><%= submit_tag l(:button_submit) %></p>
48 <p><%= submit_tag l(:button_submit) %></p>
49 <% end %>
49 <% end %>
@@ -1,1348 +1,1380
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19 require 'issues_controller'
19 require 'issues_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class IssuesController; def rescue_action(e) raise e end; end
22 class IssuesController; def rescue_action(e) raise e end; end
23
23
24 class IssuesControllerTest < ActionController::TestCase
24 class IssuesControllerTest < ActionController::TestCase
25 fixtures :projects,
25 fixtures :projects,
26 :users,
26 :users,
27 :roles,
27 :roles,
28 :members,
28 :members,
29 :member_roles,
29 :member_roles,
30 :issues,
30 :issues,
31 :issue_statuses,
31 :issue_statuses,
32 :versions,
32 :versions,
33 :trackers,
33 :trackers,
34 :projects_trackers,
34 :projects_trackers,
35 :issue_categories,
35 :issue_categories,
36 :enabled_modules,
36 :enabled_modules,
37 :enumerations,
37 :enumerations,
38 :attachments,
38 :attachments,
39 :workflows,
39 :workflows,
40 :custom_fields,
40 :custom_fields,
41 :custom_values,
41 :custom_values,
42 :custom_fields_projects,
42 :custom_fields_projects,
43 :custom_fields_trackers,
43 :custom_fields_trackers,
44 :time_entries,
44 :time_entries,
45 :journals,
45 :journals,
46 :journal_details,
46 :journal_details,
47 :queries
47 :queries
48
48
49 def setup
49 def setup
50 @controller = IssuesController.new
50 @controller = IssuesController.new
51 @request = ActionController::TestRequest.new
51 @request = ActionController::TestRequest.new
52 @response = ActionController::TestResponse.new
52 @response = ActionController::TestResponse.new
53 User.current = nil
53 User.current = nil
54 end
54 end
55
55
56 def test_index
56 def test_index
57 Setting.default_language = 'en'
57 Setting.default_language = 'en'
58
58
59 get :index
59 get :index
60 assert_response :success
60 assert_response :success
61 assert_template 'index.rhtml'
61 assert_template 'index.rhtml'
62 assert_not_nil assigns(:issues)
62 assert_not_nil assigns(:issues)
63 assert_nil assigns(:project)
63 assert_nil assigns(:project)
64 assert_tag :tag => 'a', :content => /Can't print recipes/
64 assert_tag :tag => 'a', :content => /Can't print recipes/
65 assert_tag :tag => 'a', :content => /Subproject issue/
65 assert_tag :tag => 'a', :content => /Subproject issue/
66 # private projects hidden
66 # private projects hidden
67 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
67 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
68 assert_no_tag :tag => 'a', :content => /Issue on project 2/
68 assert_no_tag :tag => 'a', :content => /Issue on project 2/
69 # project column
69 # project column
70 assert_tag :tag => 'th', :content => /Project/
70 assert_tag :tag => 'th', :content => /Project/
71 end
71 end
72
72
73 def test_index_should_not_list_issues_when_module_disabled
73 def test_index_should_not_list_issues_when_module_disabled
74 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
74 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
75 get :index
75 get :index
76 assert_response :success
76 assert_response :success
77 assert_template 'index.rhtml'
77 assert_template 'index.rhtml'
78 assert_not_nil assigns(:issues)
78 assert_not_nil assigns(:issues)
79 assert_nil assigns(:project)
79 assert_nil assigns(:project)
80 assert_no_tag :tag => 'a', :content => /Can't print recipes/
80 assert_no_tag :tag => 'a', :content => /Can't print recipes/
81 assert_tag :tag => 'a', :content => /Subproject issue/
81 assert_tag :tag => 'a', :content => /Subproject issue/
82 end
82 end
83
83
84 def test_index_should_not_list_issues_when_module_disabled
84 def test_index_should_not_list_issues_when_module_disabled
85 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
85 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
86 get :index
86 get :index
87 assert_response :success
87 assert_response :success
88 assert_template 'index.rhtml'
88 assert_template 'index.rhtml'
89 assert_not_nil assigns(:issues)
89 assert_not_nil assigns(:issues)
90 assert_nil assigns(:project)
90 assert_nil assigns(:project)
91 assert_no_tag :tag => 'a', :content => /Can't print recipes/
91 assert_no_tag :tag => 'a', :content => /Can't print recipes/
92 assert_tag :tag => 'a', :content => /Subproject issue/
92 assert_tag :tag => 'a', :content => /Subproject issue/
93 end
93 end
94
94
95 def test_index_with_project
95 def test_index_with_project
96 Setting.display_subprojects_issues = 0
96 Setting.display_subprojects_issues = 0
97 get :index, :project_id => 1
97 get :index, :project_id => 1
98 assert_response :success
98 assert_response :success
99 assert_template 'index.rhtml'
99 assert_template 'index.rhtml'
100 assert_not_nil assigns(:issues)
100 assert_not_nil assigns(:issues)
101 assert_tag :tag => 'a', :content => /Can't print recipes/
101 assert_tag :tag => 'a', :content => /Can't print recipes/
102 assert_no_tag :tag => 'a', :content => /Subproject issue/
102 assert_no_tag :tag => 'a', :content => /Subproject issue/
103 end
103 end
104
104
105 def test_index_with_project_and_subprojects
105 def test_index_with_project_and_subprojects
106 Setting.display_subprojects_issues = 1
106 Setting.display_subprojects_issues = 1
107 get :index, :project_id => 1
107 get :index, :project_id => 1
108 assert_response :success
108 assert_response :success
109 assert_template 'index.rhtml'
109 assert_template 'index.rhtml'
110 assert_not_nil assigns(:issues)
110 assert_not_nil assigns(:issues)
111 assert_tag :tag => 'a', :content => /Can't print recipes/
111 assert_tag :tag => 'a', :content => /Can't print recipes/
112 assert_tag :tag => 'a', :content => /Subproject issue/
112 assert_tag :tag => 'a', :content => /Subproject issue/
113 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
113 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
114 end
114 end
115
115
116 def test_index_with_project_and_subprojects_should_show_private_subprojects
116 def test_index_with_project_and_subprojects_should_show_private_subprojects
117 @request.session[:user_id] = 2
117 @request.session[:user_id] = 2
118 Setting.display_subprojects_issues = 1
118 Setting.display_subprojects_issues = 1
119 get :index, :project_id => 1
119 get :index, :project_id => 1
120 assert_response :success
120 assert_response :success
121 assert_template 'index.rhtml'
121 assert_template 'index.rhtml'
122 assert_not_nil assigns(:issues)
122 assert_not_nil assigns(:issues)
123 assert_tag :tag => 'a', :content => /Can't print recipes/
123 assert_tag :tag => 'a', :content => /Can't print recipes/
124 assert_tag :tag => 'a', :content => /Subproject issue/
124 assert_tag :tag => 'a', :content => /Subproject issue/
125 assert_tag :tag => 'a', :content => /Issue of a private subproject/
125 assert_tag :tag => 'a', :content => /Issue of a private subproject/
126 end
126 end
127
127
128 def test_index_with_project_and_default_filter
128 def test_index_with_project_and_default_filter
129 get :index, :project_id => 1, :set_filter => 1
129 get :index, :project_id => 1, :set_filter => 1
130 assert_response :success
130 assert_response :success
131 assert_template 'index.rhtml'
131 assert_template 'index.rhtml'
132 assert_not_nil assigns(:issues)
132 assert_not_nil assigns(:issues)
133
133
134 query = assigns(:query)
134 query = assigns(:query)
135 assert_not_nil query
135 assert_not_nil query
136 # default filter
136 # default filter
137 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
137 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
138 end
138 end
139
139
140 def test_index_with_project_and_filter
140 def test_index_with_project_and_filter
141 get :index, :project_id => 1, :set_filter => 1,
141 get :index, :project_id => 1, :set_filter => 1,
142 :f => ['tracker_id'],
142 :f => ['tracker_id'],
143 :op => {'tracker_id' => '='},
143 :op => {'tracker_id' => '='},
144 :v => {'tracker_id' => ['1']}
144 :v => {'tracker_id' => ['1']}
145 assert_response :success
145 assert_response :success
146 assert_template 'index.rhtml'
146 assert_template 'index.rhtml'
147 assert_not_nil assigns(:issues)
147 assert_not_nil assigns(:issues)
148
148
149 query = assigns(:query)
149 query = assigns(:query)
150 assert_not_nil query
150 assert_not_nil query
151 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
151 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
152 end
152 end
153
153
154 def test_index_with_project_and_empty_filters
154 def test_index_with_project_and_empty_filters
155 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
155 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
156 assert_response :success
156 assert_response :success
157 assert_template 'index.rhtml'
157 assert_template 'index.rhtml'
158 assert_not_nil assigns(:issues)
158 assert_not_nil assigns(:issues)
159
159
160 query = assigns(:query)
160 query = assigns(:query)
161 assert_not_nil query
161 assert_not_nil query
162 # no filter
162 # no filter
163 assert_equal({}, query.filters)
163 assert_equal({}, query.filters)
164 end
164 end
165
165
166 def test_index_with_query
166 def test_index_with_query
167 get :index, :project_id => 1, :query_id => 5
167 get :index, :project_id => 1, :query_id => 5
168 assert_response :success
168 assert_response :success
169 assert_template 'index.rhtml'
169 assert_template 'index.rhtml'
170 assert_not_nil assigns(:issues)
170 assert_not_nil assigns(:issues)
171 assert_nil assigns(:issue_count_by_group)
171 assert_nil assigns(:issue_count_by_group)
172 end
172 end
173
173
174 def test_index_with_query_grouped_by_tracker
174 def test_index_with_query_grouped_by_tracker
175 get :index, :project_id => 1, :query_id => 6
175 get :index, :project_id => 1, :query_id => 6
176 assert_response :success
176 assert_response :success
177 assert_template 'index.rhtml'
177 assert_template 'index.rhtml'
178 assert_not_nil assigns(:issues)
178 assert_not_nil assigns(:issues)
179 assert_not_nil assigns(:issue_count_by_group)
179 assert_not_nil assigns(:issue_count_by_group)
180 end
180 end
181
181
182 def test_index_with_query_grouped_by_list_custom_field
182 def test_index_with_query_grouped_by_list_custom_field
183 get :index, :project_id => 1, :query_id => 9
183 get :index, :project_id => 1, :query_id => 9
184 assert_response :success
184 assert_response :success
185 assert_template 'index.rhtml'
185 assert_template 'index.rhtml'
186 assert_not_nil assigns(:issues)
186 assert_not_nil assigns(:issues)
187 assert_not_nil assigns(:issue_count_by_group)
187 assert_not_nil assigns(:issue_count_by_group)
188 end
188 end
189
189
190 def test_index_sort_by_field_not_included_in_columns
190 def test_index_sort_by_field_not_included_in_columns
191 Setting.issue_list_default_columns = %w(subject author)
191 Setting.issue_list_default_columns = %w(subject author)
192 get :index, :sort => 'tracker'
192 get :index, :sort => 'tracker'
193 end
193 end
194
194
195 def test_index_csv_with_project
195 def test_index_csv_with_project
196 Setting.default_language = 'en'
196 Setting.default_language = 'en'
197
197
198 get :index, :format => 'csv'
198 get :index, :format => 'csv'
199 assert_response :success
199 assert_response :success
200 assert_not_nil assigns(:issues)
200 assert_not_nil assigns(:issues)
201 assert_equal 'text/csv', @response.content_type
201 assert_equal 'text/csv', @response.content_type
202 assert @response.body.starts_with?("#,")
202 assert @response.body.starts_with?("#,")
203
203
204 get :index, :project_id => 1, :format => 'csv'
204 get :index, :project_id => 1, :format => 'csv'
205 assert_response :success
205 assert_response :success
206 assert_not_nil assigns(:issues)
206 assert_not_nil assigns(:issues)
207 assert_equal 'text/csv', @response.content_type
207 assert_equal 'text/csv', @response.content_type
208 end
208 end
209
209
210 def test_index_pdf
210 def test_index_pdf
211 get :index, :format => 'pdf'
211 get :index, :format => 'pdf'
212 assert_response :success
212 assert_response :success
213 assert_not_nil assigns(:issues)
213 assert_not_nil assigns(:issues)
214 assert_equal 'application/pdf', @response.content_type
214 assert_equal 'application/pdf', @response.content_type
215
215
216 get :index, :project_id => 1, :format => 'pdf'
216 get :index, :project_id => 1, :format => 'pdf'
217 assert_response :success
217 assert_response :success
218 assert_not_nil assigns(:issues)
218 assert_not_nil assigns(:issues)
219 assert_equal 'application/pdf', @response.content_type
219 assert_equal 'application/pdf', @response.content_type
220
220
221 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
221 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
222 assert_response :success
222 assert_response :success
223 assert_not_nil assigns(:issues)
223 assert_not_nil assigns(:issues)
224 assert_equal 'application/pdf', @response.content_type
224 assert_equal 'application/pdf', @response.content_type
225 end
225 end
226
226
227 def test_index_pdf_with_query_grouped_by_list_custom_field
227 def test_index_pdf_with_query_grouped_by_list_custom_field
228 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
228 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
229 assert_response :success
229 assert_response :success
230 assert_not_nil assigns(:issues)
230 assert_not_nil assigns(:issues)
231 assert_not_nil assigns(:issue_count_by_group)
231 assert_not_nil assigns(:issue_count_by_group)
232 assert_equal 'application/pdf', @response.content_type
232 assert_equal 'application/pdf', @response.content_type
233 end
233 end
234
234
235 def test_index_sort
235 def test_index_sort
236 get :index, :sort => 'tracker,id:desc'
236 get :index, :sort => 'tracker,id:desc'
237 assert_response :success
237 assert_response :success
238
238
239 sort_params = @request.session['issues_index_sort']
239 sort_params = @request.session['issues_index_sort']
240 assert sort_params.is_a?(String)
240 assert sort_params.is_a?(String)
241 assert_equal 'tracker,id:desc', sort_params
241 assert_equal 'tracker,id:desc', sort_params
242
242
243 issues = assigns(:issues)
243 issues = assigns(:issues)
244 assert_not_nil issues
244 assert_not_nil issues
245 assert !issues.empty?
245 assert !issues.empty?
246 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
246 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
247 end
247 end
248
248
249 def test_index_with_columns
249 def test_index_with_columns
250 columns = ['tracker', 'subject', 'assigned_to']
250 columns = ['tracker', 'subject', 'assigned_to']
251 get :index, :set_filter => 1, :c => columns
251 get :index, :set_filter => 1, :c => columns
252 assert_response :success
252 assert_response :success
253
253
254 # query should use specified columns
254 # query should use specified columns
255 query = assigns(:query)
255 query = assigns(:query)
256 assert_kind_of Query, query
256 assert_kind_of Query, query
257 assert_equal columns, query.column_names.map(&:to_s)
257 assert_equal columns, query.column_names.map(&:to_s)
258
258
259 # columns should be stored in session
259 # columns should be stored in session
260 assert_kind_of Hash, session[:query]
260 assert_kind_of Hash, session[:query]
261 assert_kind_of Array, session[:query][:column_names]
261 assert_kind_of Array, session[:query][:column_names]
262 assert_equal columns, session[:query][:column_names].map(&:to_s)
262 assert_equal columns, session[:query][:column_names].map(&:to_s)
263 end
263 end
264
264
265 def test_index_with_custom_field_column
265 def test_index_with_custom_field_column
266 columns = %w(tracker subject cf_2)
266 columns = %w(tracker subject cf_2)
267 get :index, :set_filter => 1, :c => columns
267 get :index, :set_filter => 1, :c => columns
268 assert_response :success
268 assert_response :success
269
269
270 # query should use specified columns
270 # query should use specified columns
271 query = assigns(:query)
271 query = assigns(:query)
272 assert_kind_of Query, query
272 assert_kind_of Query, query
273 assert_equal columns, query.column_names.map(&:to_s)
273 assert_equal columns, query.column_names.map(&:to_s)
274
274
275 assert_tag :td,
275 assert_tag :td,
276 :attributes => {:class => 'cf_2 string'},
276 :attributes => {:class => 'cf_2 string'},
277 :ancestor => {:tag => 'table', :attributes => {:class => /issues/}}
277 :ancestor => {:tag => 'table', :attributes => {:class => /issues/}}
278 end
278 end
279
279
280 def test_show_by_anonymous
280 def test_show_by_anonymous
281 get :show, :id => 1
281 get :show, :id => 1
282 assert_response :success
282 assert_response :success
283 assert_template 'show.rhtml'
283 assert_template 'show.rhtml'
284 assert_not_nil assigns(:issue)
284 assert_not_nil assigns(:issue)
285 assert_equal Issue.find(1), assigns(:issue)
285 assert_equal Issue.find(1), assigns(:issue)
286
286
287 # anonymous role is allowed to add a note
287 # anonymous role is allowed to add a note
288 assert_tag :tag => 'form',
288 assert_tag :tag => 'form',
289 :descendant => { :tag => 'fieldset',
289 :descendant => { :tag => 'fieldset',
290 :child => { :tag => 'legend',
290 :child => { :tag => 'legend',
291 :content => /Notes/ } }
291 :content => /Notes/ } }
292 end
292 end
293
293
294 def test_show_by_manager
294 def test_show_by_manager
295 @request.session[:user_id] = 2
295 @request.session[:user_id] = 2
296 get :show, :id => 1
296 get :show, :id => 1
297 assert_response :success
297 assert_response :success
298
298
299 assert_tag :tag => 'a',
299 assert_tag :tag => 'a',
300 :content => /Quote/
300 :content => /Quote/
301
301
302 assert_tag :tag => 'form',
302 assert_tag :tag => 'form',
303 :descendant => { :tag => 'fieldset',
303 :descendant => { :tag => 'fieldset',
304 :child => { :tag => 'legend',
304 :child => { :tag => 'legend',
305 :content => /Change properties/ } },
305 :content => /Change properties/ } },
306 :descendant => { :tag => 'fieldset',
306 :descendant => { :tag => 'fieldset',
307 :child => { :tag => 'legend',
307 :child => { :tag => 'legend',
308 :content => /Log time/ } },
308 :content => /Log time/ } },
309 :descendant => { :tag => 'fieldset',
309 :descendant => { :tag => 'fieldset',
310 :child => { :tag => 'legend',
310 :child => { :tag => 'legend',
311 :content => /Notes/ } }
311 :content => /Notes/ } }
312 end
312 end
313
313
314 def test_show_should_deny_anonymous_access_without_permission
314 def test_show_should_deny_anonymous_access_without_permission
315 Role.anonymous.remove_permission!(:view_issues)
315 Role.anonymous.remove_permission!(:view_issues)
316 get :show, :id => 1
316 get :show, :id => 1
317 assert_response :redirect
317 assert_response :redirect
318 end
318 end
319
319
320 def test_show_should_deny_non_member_access_without_permission
320 def test_show_should_deny_non_member_access_without_permission
321 Role.non_member.remove_permission!(:view_issues)
321 Role.non_member.remove_permission!(:view_issues)
322 @request.session[:user_id] = 9
322 @request.session[:user_id] = 9
323 get :show, :id => 1
323 get :show, :id => 1
324 assert_response 403
324 assert_response 403
325 end
325 end
326
326
327 def test_show_should_deny_member_access_without_permission
327 def test_show_should_deny_member_access_without_permission
328 Role.find(1).remove_permission!(:view_issues)
328 Role.find(1).remove_permission!(:view_issues)
329 @request.session[:user_id] = 2
329 @request.session[:user_id] = 2
330 get :show, :id => 1
330 get :show, :id => 1
331 assert_response 403
331 assert_response 403
332 end
332 end
333
333
334 def test_show_should_not_disclose_relations_to_invisible_issues
334 def test_show_should_not_disclose_relations_to_invisible_issues
335 Setting.cross_project_issue_relations = '1'
335 Setting.cross_project_issue_relations = '1'
336 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
336 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
337 # Relation to a private project issue
337 # Relation to a private project issue
338 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
338 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
339
339
340 get :show, :id => 1
340 get :show, :id => 1
341 assert_response :success
341 assert_response :success
342
342
343 assert_tag :div, :attributes => { :id => 'relations' },
343 assert_tag :div, :attributes => { :id => 'relations' },
344 :descendant => { :tag => 'a', :content => /#2$/ }
344 :descendant => { :tag => 'a', :content => /#2$/ }
345 assert_no_tag :div, :attributes => { :id => 'relations' },
345 assert_no_tag :div, :attributes => { :id => 'relations' },
346 :descendant => { :tag => 'a', :content => /#4$/ }
346 :descendant => { :tag => 'a', :content => /#4$/ }
347 end
347 end
348
348
349 def test_show_atom
349 def test_show_atom
350 get :show, :id => 2, :format => 'atom'
350 get :show, :id => 2, :format => 'atom'
351 assert_response :success
351 assert_response :success
352 assert_template 'journals/index.rxml'
352 assert_template 'journals/index.rxml'
353 # Inline image
353 # Inline image
354 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
354 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
355 end
355 end
356
356
357 def test_show_export_to_pdf
357 def test_show_export_to_pdf
358 get :show, :id => 3, :format => 'pdf'
358 get :show, :id => 3, :format => 'pdf'
359 assert_response :success
359 assert_response :success
360 assert_equal 'application/pdf', @response.content_type
360 assert_equal 'application/pdf', @response.content_type
361 assert @response.body.starts_with?('%PDF')
361 assert @response.body.starts_with?('%PDF')
362 assert_not_nil assigns(:issue)
362 assert_not_nil assigns(:issue)
363 end
363 end
364
364
365 def test_get_new
365 def test_get_new
366 @request.session[:user_id] = 2
366 @request.session[:user_id] = 2
367 get :new, :project_id => 1, :tracker_id => 1
367 get :new, :project_id => 1, :tracker_id => 1
368 assert_response :success
368 assert_response :success
369 assert_template 'new'
369 assert_template 'new'
370
370
371 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
371 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
372 :value => 'Default string' }
372 :value => 'Default string' }
373 end
373 end
374
374
375 def test_get_new_without_tracker_id
375 def test_get_new_without_tracker_id
376 @request.session[:user_id] = 2
376 @request.session[:user_id] = 2
377 get :new, :project_id => 1
377 get :new, :project_id => 1
378 assert_response :success
378 assert_response :success
379 assert_template 'new'
379 assert_template 'new'
380
380
381 issue = assigns(:issue)
381 issue = assigns(:issue)
382 assert_not_nil issue
382 assert_not_nil issue
383 assert_equal Project.find(1).trackers.first, issue.tracker
383 assert_equal Project.find(1).trackers.first, issue.tracker
384 end
384 end
385
385
386 def test_get_new_with_no_default_status_should_display_an_error
386 def test_get_new_with_no_default_status_should_display_an_error
387 @request.session[:user_id] = 2
387 @request.session[:user_id] = 2
388 IssueStatus.delete_all
388 IssueStatus.delete_all
389
389
390 get :new, :project_id => 1
390 get :new, :project_id => 1
391 assert_response 500
391 assert_response 500
392 assert_error_tag :content => /No default issue/
392 assert_error_tag :content => /No default issue/
393 end
393 end
394
394
395 def test_get_new_with_no_tracker_should_display_an_error
395 def test_get_new_with_no_tracker_should_display_an_error
396 @request.session[:user_id] = 2
396 @request.session[:user_id] = 2
397 Tracker.delete_all
397 Tracker.delete_all
398
398
399 get :new, :project_id => 1
399 get :new, :project_id => 1
400 assert_response 500
400 assert_response 500
401 assert_error_tag :content => /No tracker/
401 assert_error_tag :content => /No tracker/
402 end
402 end
403
403
404 def test_update_new_form
404 def test_update_new_form
405 @request.session[:user_id] = 2
405 @request.session[:user_id] = 2
406 xhr :post, :new, :project_id => 1,
406 xhr :post, :new, :project_id => 1,
407 :issue => {:tracker_id => 2,
407 :issue => {:tracker_id => 2,
408 :subject => 'This is the test_new issue',
408 :subject => 'This is the test_new issue',
409 :description => 'This is the description',
409 :description => 'This is the description',
410 :priority_id => 5}
410 :priority_id => 5}
411 assert_response :success
411 assert_response :success
412 assert_template 'attributes'
412 assert_template 'attributes'
413
413
414 issue = assigns(:issue)
414 issue = assigns(:issue)
415 assert_kind_of Issue, issue
415 assert_kind_of Issue, issue
416 assert_equal 1, issue.project_id
416 assert_equal 1, issue.project_id
417 assert_equal 2, issue.tracker_id
417 assert_equal 2, issue.tracker_id
418 assert_equal 'This is the test_new issue', issue.subject
418 assert_equal 'This is the test_new issue', issue.subject
419 end
419 end
420
420
421 def test_post_create
421 def test_post_create
422 @request.session[:user_id] = 2
422 @request.session[:user_id] = 2
423 assert_difference 'Issue.count' do
423 assert_difference 'Issue.count' do
424 post :create, :project_id => 1,
424 post :create, :project_id => 1,
425 :issue => {:tracker_id => 3,
425 :issue => {:tracker_id => 3,
426 :status_id => 2,
426 :status_id => 2,
427 :subject => 'This is the test_new issue',
427 :subject => 'This is the test_new issue',
428 :description => 'This is the description',
428 :description => 'This is the description',
429 :priority_id => 5,
429 :priority_id => 5,
430 :start_date => '2010-11-07',
430 :start_date => '2010-11-07',
431 :estimated_hours => '',
431 :estimated_hours => '',
432 :custom_field_values => {'2' => 'Value for field 2'}}
432 :custom_field_values => {'2' => 'Value for field 2'}}
433 end
433 end
434 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
434 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
435
435
436 issue = Issue.find_by_subject('This is the test_new issue')
436 issue = Issue.find_by_subject('This is the test_new issue')
437 assert_not_nil issue
437 assert_not_nil issue
438 assert_equal 2, issue.author_id
438 assert_equal 2, issue.author_id
439 assert_equal 3, issue.tracker_id
439 assert_equal 3, issue.tracker_id
440 assert_equal 2, issue.status_id
440 assert_equal 2, issue.status_id
441 assert_equal Date.parse('2010-11-07'), issue.start_date
441 assert_equal Date.parse('2010-11-07'), issue.start_date
442 assert_nil issue.estimated_hours
442 assert_nil issue.estimated_hours
443 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
443 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
444 assert_not_nil v
444 assert_not_nil v
445 assert_equal 'Value for field 2', v.value
445 assert_equal 'Value for field 2', v.value
446 end
446 end
447
447
448 def test_post_create_without_start_date
448 def test_post_create_without_start_date
449 @request.session[:user_id] = 2
449 @request.session[:user_id] = 2
450 assert_difference 'Issue.count' do
450 assert_difference 'Issue.count' do
451 post :create, :project_id => 1,
451 post :create, :project_id => 1,
452 :issue => {:tracker_id => 3,
452 :issue => {:tracker_id => 3,
453 :status_id => 2,
453 :status_id => 2,
454 :subject => 'This is the test_new issue',
454 :subject => 'This is the test_new issue',
455 :description => 'This is the description',
455 :description => 'This is the description',
456 :priority_id => 5,
456 :priority_id => 5,
457 :start_date => '',
457 :start_date => '',
458 :estimated_hours => '',
458 :estimated_hours => '',
459 :custom_field_values => {'2' => 'Value for field 2'}}
459 :custom_field_values => {'2' => 'Value for field 2'}}
460 end
460 end
461 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
461 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
462
462
463 issue = Issue.find_by_subject('This is the test_new issue')
463 issue = Issue.find_by_subject('This is the test_new issue')
464 assert_not_nil issue
464 assert_not_nil issue
465 assert_nil issue.start_date
465 assert_nil issue.start_date
466 end
466 end
467
467
468 def test_post_create_and_continue
468 def test_post_create_and_continue
469 @request.session[:user_id] = 2
469 @request.session[:user_id] = 2
470 post :create, :project_id => 1,
470 post :create, :project_id => 1,
471 :issue => {:tracker_id => 3,
471 :issue => {:tracker_id => 3,
472 :subject => 'This is first issue',
472 :subject => 'This is first issue',
473 :priority_id => 5},
473 :priority_id => 5},
474 :continue => ''
474 :continue => ''
475 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook',
475 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook',
476 :issue => {:tracker_id => 3}
476 :issue => {:tracker_id => 3}
477 end
477 end
478
478
479 def test_post_create_without_custom_fields_param
479 def test_post_create_without_custom_fields_param
480 @request.session[:user_id] = 2
480 @request.session[:user_id] = 2
481 assert_difference 'Issue.count' do
481 assert_difference 'Issue.count' do
482 post :create, :project_id => 1,
482 post :create, :project_id => 1,
483 :issue => {:tracker_id => 1,
483 :issue => {:tracker_id => 1,
484 :subject => 'This is the test_new issue',
484 :subject => 'This is the test_new issue',
485 :description => 'This is the description',
485 :description => 'This is the description',
486 :priority_id => 5}
486 :priority_id => 5}
487 end
487 end
488 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
488 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
489 end
489 end
490
490
491 def test_post_create_with_required_custom_field_and_without_custom_fields_param
491 def test_post_create_with_required_custom_field_and_without_custom_fields_param
492 field = IssueCustomField.find_by_name('Database')
492 field = IssueCustomField.find_by_name('Database')
493 field.update_attribute(:is_required, true)
493 field.update_attribute(:is_required, true)
494
494
495 @request.session[:user_id] = 2
495 @request.session[:user_id] = 2
496 post :create, :project_id => 1,
496 post :create, :project_id => 1,
497 :issue => {:tracker_id => 1,
497 :issue => {:tracker_id => 1,
498 :subject => 'This is the test_new issue',
498 :subject => 'This is the test_new issue',
499 :description => 'This is the description',
499 :description => 'This is the description',
500 :priority_id => 5}
500 :priority_id => 5}
501 assert_response :success
501 assert_response :success
502 assert_template 'new'
502 assert_template 'new'
503 issue = assigns(:issue)
503 issue = assigns(:issue)
504 assert_not_nil issue
504 assert_not_nil issue
505 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
505 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
506 end
506 end
507
507
508 def test_post_create_with_watchers
508 def test_post_create_with_watchers
509 @request.session[:user_id] = 2
509 @request.session[:user_id] = 2
510 ActionMailer::Base.deliveries.clear
510 ActionMailer::Base.deliveries.clear
511
511
512 assert_difference 'Watcher.count', 2 do
512 assert_difference 'Watcher.count', 2 do
513 post :create, :project_id => 1,
513 post :create, :project_id => 1,
514 :issue => {:tracker_id => 1,
514 :issue => {:tracker_id => 1,
515 :subject => 'This is a new issue with watchers',
515 :subject => 'This is a new issue with watchers',
516 :description => 'This is the description',
516 :description => 'This is the description',
517 :priority_id => 5,
517 :priority_id => 5,
518 :watcher_user_ids => ['2', '3']}
518 :watcher_user_ids => ['2', '3']}
519 end
519 end
520 issue = Issue.find_by_subject('This is a new issue with watchers')
520 issue = Issue.find_by_subject('This is a new issue with watchers')
521 assert_not_nil issue
521 assert_not_nil issue
522 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
522 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
523
523
524 # Watchers added
524 # Watchers added
525 assert_equal [2, 3], issue.watcher_user_ids.sort
525 assert_equal [2, 3], issue.watcher_user_ids.sort
526 assert issue.watched_by?(User.find(3))
526 assert issue.watched_by?(User.find(3))
527 # Watchers notified
527 # Watchers notified
528 mail = ActionMailer::Base.deliveries.last
528 mail = ActionMailer::Base.deliveries.last
529 assert_kind_of TMail::Mail, mail
529 assert_kind_of TMail::Mail, mail
530 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
530 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
531 end
531 end
532
532
533 def test_post_create_subissue
533 def test_post_create_subissue
534 @request.session[:user_id] = 2
534 @request.session[:user_id] = 2
535
535
536 assert_difference 'Issue.count' do
536 assert_difference 'Issue.count' do
537 post :create, :project_id => 1,
537 post :create, :project_id => 1,
538 :issue => {:tracker_id => 1,
538 :issue => {:tracker_id => 1,
539 :subject => 'This is a child issue',
539 :subject => 'This is a child issue',
540 :parent_issue_id => 2}
540 :parent_issue_id => 2}
541 end
541 end
542 issue = Issue.find_by_subject('This is a child issue')
542 issue = Issue.find_by_subject('This is a child issue')
543 assert_not_nil issue
543 assert_not_nil issue
544 assert_equal Issue.find(2), issue.parent
544 assert_equal Issue.find(2), issue.parent
545 end
545 end
546
546
547 def test_post_create_subissue_with_non_numeric_parent_id
547 def test_post_create_subissue_with_non_numeric_parent_id
548 @request.session[:user_id] = 2
548 @request.session[:user_id] = 2
549
549
550 assert_difference 'Issue.count' do
550 assert_difference 'Issue.count' do
551 post :create, :project_id => 1,
551 post :create, :project_id => 1,
552 :issue => {:tracker_id => 1,
552 :issue => {:tracker_id => 1,
553 :subject => 'This is a child issue',
553 :subject => 'This is a child issue',
554 :parent_issue_id => 'ABC'}
554 :parent_issue_id => 'ABC'}
555 end
555 end
556 issue = Issue.find_by_subject('This is a child issue')
556 issue = Issue.find_by_subject('This is a child issue')
557 assert_not_nil issue
557 assert_not_nil issue
558 assert_nil issue.parent
558 assert_nil issue.parent
559 end
559 end
560
560
561 def test_post_create_should_send_a_notification
561 def test_post_create_should_send_a_notification
562 ActionMailer::Base.deliveries.clear
562 ActionMailer::Base.deliveries.clear
563 @request.session[:user_id] = 2
563 @request.session[:user_id] = 2
564 assert_difference 'Issue.count' do
564 assert_difference 'Issue.count' do
565 post :create, :project_id => 1,
565 post :create, :project_id => 1,
566 :issue => {:tracker_id => 3,
566 :issue => {:tracker_id => 3,
567 :subject => 'This is the test_new issue',
567 :subject => 'This is the test_new issue',
568 :description => 'This is the description',
568 :description => 'This is the description',
569 :priority_id => 5,
569 :priority_id => 5,
570 :estimated_hours => '',
570 :estimated_hours => '',
571 :custom_field_values => {'2' => 'Value for field 2'}}
571 :custom_field_values => {'2' => 'Value for field 2'}}
572 end
572 end
573 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
573 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
574
574
575 assert_equal 1, ActionMailer::Base.deliveries.size
575 assert_equal 1, ActionMailer::Base.deliveries.size
576 end
576 end
577
577
578 def test_post_create_should_preserve_fields_values_on_validation_failure
578 def test_post_create_should_preserve_fields_values_on_validation_failure
579 @request.session[:user_id] = 2
579 @request.session[:user_id] = 2
580 post :create, :project_id => 1,
580 post :create, :project_id => 1,
581 :issue => {:tracker_id => 1,
581 :issue => {:tracker_id => 1,
582 # empty subject
582 # empty subject
583 :subject => '',
583 :subject => '',
584 :description => 'This is a description',
584 :description => 'This is a description',
585 :priority_id => 6,
585 :priority_id => 6,
586 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
586 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
587 assert_response :success
587 assert_response :success
588 assert_template 'new'
588 assert_template 'new'
589
589
590 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
590 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
591 :content => 'This is a description'
591 :content => 'This is a description'
592 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
592 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
593 :child => { :tag => 'option', :attributes => { :selected => 'selected',
593 :child => { :tag => 'option', :attributes => { :selected => 'selected',
594 :value => '6' },
594 :value => '6' },
595 :content => 'High' }
595 :content => 'High' }
596 # Custom fields
596 # Custom fields
597 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
597 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
598 :child => { :tag => 'option', :attributes => { :selected => 'selected',
598 :child => { :tag => 'option', :attributes => { :selected => 'selected',
599 :value => 'Oracle' },
599 :value => 'Oracle' },
600 :content => 'Oracle' }
600 :content => 'Oracle' }
601 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
601 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
602 :value => 'Value for field 2'}
602 :value => 'Value for field 2'}
603 end
603 end
604
604
605 def test_post_create_should_ignore_non_safe_attributes
605 def test_post_create_should_ignore_non_safe_attributes
606 @request.session[:user_id] = 2
606 @request.session[:user_id] = 2
607 assert_nothing_raised do
607 assert_nothing_raised do
608 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
608 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
609 end
609 end
610 end
610 end
611
611
612 context "without workflow privilege" do
612 context "without workflow privilege" do
613 setup do
613 setup do
614 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
614 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
615 Role.anonymous.add_permission! :add_issues, :add_issue_notes
615 Role.anonymous.add_permission! :add_issues, :add_issue_notes
616 end
616 end
617
617
618 context "#new" do
618 context "#new" do
619 should "propose default status only" do
619 should "propose default status only" do
620 get :new, :project_id => 1
620 get :new, :project_id => 1
621 assert_response :success
621 assert_response :success
622 assert_template 'new'
622 assert_template 'new'
623 assert_tag :tag => 'select',
623 assert_tag :tag => 'select',
624 :attributes => {:name => 'issue[status_id]'},
624 :attributes => {:name => 'issue[status_id]'},
625 :children => {:count => 1},
625 :children => {:count => 1},
626 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
626 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
627 end
627 end
628
628
629 should "accept default status" do
629 should "accept default status" do
630 assert_difference 'Issue.count' do
630 assert_difference 'Issue.count' do
631 post :create, :project_id => 1,
631 post :create, :project_id => 1,
632 :issue => {:tracker_id => 1,
632 :issue => {:tracker_id => 1,
633 :subject => 'This is an issue',
633 :subject => 'This is an issue',
634 :status_id => 1}
634 :status_id => 1}
635 end
635 end
636 issue = Issue.last(:order => 'id')
636 issue = Issue.last(:order => 'id')
637 assert_equal IssueStatus.default, issue.status
637 assert_equal IssueStatus.default, issue.status
638 end
638 end
639
639
640 should "ignore unauthorized status" do
640 should "ignore unauthorized status" do
641 assert_difference 'Issue.count' do
641 assert_difference 'Issue.count' do
642 post :create, :project_id => 1,
642 post :create, :project_id => 1,
643 :issue => {:tracker_id => 1,
643 :issue => {:tracker_id => 1,
644 :subject => 'This is an issue',
644 :subject => 'This is an issue',
645 :status_id => 3}
645 :status_id => 3}
646 end
646 end
647 issue = Issue.last(:order => 'id')
647 issue = Issue.last(:order => 'id')
648 assert_equal IssueStatus.default, issue.status
648 assert_equal IssueStatus.default, issue.status
649 end
649 end
650 end
650 end
651
651
652 context "#update" do
652 context "#update" do
653 should "ignore status change" do
653 should "ignore status change" do
654 assert_difference 'Journal.count' do
654 assert_difference 'Journal.count' do
655 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
655 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
656 end
656 end
657 assert_equal 1, Issue.find(1).status_id
657 assert_equal 1, Issue.find(1).status_id
658 end
658 end
659
659
660 should "ignore attributes changes" do
660 should "ignore attributes changes" do
661 assert_difference 'Journal.count' do
661 assert_difference 'Journal.count' do
662 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
662 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
663 end
663 end
664 issue = Issue.find(1)
664 issue = Issue.find(1)
665 assert_equal "Can't print recipes", issue.subject
665 assert_equal "Can't print recipes", issue.subject
666 assert_nil issue.assigned_to
666 assert_nil issue.assigned_to
667 end
667 end
668 end
668 end
669 end
669 end
670
670
671 context "with workflow privilege" do
671 context "with workflow privilege" do
672 setup do
672 setup do
673 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
673 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
674 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
674 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
675 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
675 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
676 Role.anonymous.add_permission! :add_issues, :add_issue_notes
676 Role.anonymous.add_permission! :add_issues, :add_issue_notes
677 end
677 end
678
678
679 context "#update" do
679 context "#update" do
680 should "accept authorized status" do
680 should "accept authorized status" do
681 assert_difference 'Journal.count' do
681 assert_difference 'Journal.count' do
682 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
682 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
683 end
683 end
684 assert_equal 3, Issue.find(1).status_id
684 assert_equal 3, Issue.find(1).status_id
685 end
685 end
686
686
687 should "ignore unauthorized status" do
687 should "ignore unauthorized status" do
688 assert_difference 'Journal.count' do
688 assert_difference 'Journal.count' do
689 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
689 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
690 end
690 end
691 assert_equal 1, Issue.find(1).status_id
691 assert_equal 1, Issue.find(1).status_id
692 end
692 end
693
693
694 should "accept authorized attributes changes" do
694 should "accept authorized attributes changes" do
695 assert_difference 'Journal.count' do
695 assert_difference 'Journal.count' do
696 put :update, :id => 1, :notes => 'just trying', :issue => {:assigned_to_id => 2}
696 put :update, :id => 1, :notes => 'just trying', :issue => {:assigned_to_id => 2}
697 end
697 end
698 issue = Issue.find(1)
698 issue = Issue.find(1)
699 assert_equal 2, issue.assigned_to_id
699 assert_equal 2, issue.assigned_to_id
700 end
700 end
701
701
702 should "ignore unauthorized attributes changes" do
702 should "ignore unauthorized attributes changes" do
703 assert_difference 'Journal.count' do
703 assert_difference 'Journal.count' do
704 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed'}
704 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed'}
705 end
705 end
706 issue = Issue.find(1)
706 issue = Issue.find(1)
707 assert_equal "Can't print recipes", issue.subject
707 assert_equal "Can't print recipes", issue.subject
708 end
708 end
709 end
709 end
710
710
711 context "and :edit_issues permission" do
711 context "and :edit_issues permission" do
712 setup do
712 setup do
713 Role.anonymous.add_permission! :add_issues, :edit_issues
713 Role.anonymous.add_permission! :add_issues, :edit_issues
714 end
714 end
715
715
716 should "accept authorized status" do
716 should "accept authorized status" do
717 assert_difference 'Journal.count' do
717 assert_difference 'Journal.count' do
718 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
718 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
719 end
719 end
720 assert_equal 3, Issue.find(1).status_id
720 assert_equal 3, Issue.find(1).status_id
721 end
721 end
722
722
723 should "ignore unauthorized status" do
723 should "ignore unauthorized status" do
724 assert_difference 'Journal.count' do
724 assert_difference 'Journal.count' do
725 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
725 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
726 end
726 end
727 assert_equal 1, Issue.find(1).status_id
727 assert_equal 1, Issue.find(1).status_id
728 end
728 end
729
729
730 should "accept authorized attributes changes" do
730 should "accept authorized attributes changes" do
731 assert_difference 'Journal.count' do
731 assert_difference 'Journal.count' do
732 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
732 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
733 end
733 end
734 issue = Issue.find(1)
734 issue = Issue.find(1)
735 assert_equal "changed", issue.subject
735 assert_equal "changed", issue.subject
736 assert_equal 2, issue.assigned_to_id
736 assert_equal 2, issue.assigned_to_id
737 end
737 end
738 end
738 end
739 end
739 end
740
740
741 def test_copy_issue
741 def test_copy_issue
742 @request.session[:user_id] = 2
742 @request.session[:user_id] = 2
743 get :new, :project_id => 1, :copy_from => 1
743 get :new, :project_id => 1, :copy_from => 1
744 assert_template 'new'
744 assert_template 'new'
745 assert_not_nil assigns(:issue)
745 assert_not_nil assigns(:issue)
746 orig = Issue.find(1)
746 orig = Issue.find(1)
747 assert_equal orig.subject, assigns(:issue).subject
747 assert_equal orig.subject, assigns(:issue).subject
748 end
748 end
749
749
750 def test_get_edit
750 def test_get_edit
751 @request.session[:user_id] = 2
751 @request.session[:user_id] = 2
752 get :edit, :id => 1
752 get :edit, :id => 1
753 assert_response :success
753 assert_response :success
754 assert_template 'edit'
754 assert_template 'edit'
755 assert_not_nil assigns(:issue)
755 assert_not_nil assigns(:issue)
756 assert_equal Issue.find(1), assigns(:issue)
756 assert_equal Issue.find(1), assigns(:issue)
757 end
757 end
758
758
759 def test_get_edit_with_params
759 def test_get_edit_with_params
760 @request.session[:user_id] = 2
760 @request.session[:user_id] = 2
761 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
761 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
762 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
762 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
763 assert_response :success
763 assert_response :success
764 assert_template 'edit'
764 assert_template 'edit'
765
765
766 issue = assigns(:issue)
766 issue = assigns(:issue)
767 assert_not_nil issue
767 assert_not_nil issue
768
768
769 assert_equal 5, issue.status_id
769 assert_equal 5, issue.status_id
770 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
770 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
771 :child => { :tag => 'option',
771 :child => { :tag => 'option',
772 :content => 'Closed',
772 :content => 'Closed',
773 :attributes => { :selected => 'selected' } }
773 :attributes => { :selected => 'selected' } }
774
774
775 assert_equal 7, issue.priority_id
775 assert_equal 7, issue.priority_id
776 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
776 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
777 :child => { :tag => 'option',
777 :child => { :tag => 'option',
778 :content => 'Urgent',
778 :content => 'Urgent',
779 :attributes => { :selected => 'selected' } }
779 :attributes => { :selected => 'selected' } }
780
780
781 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
781 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
782 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
782 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
783 :child => { :tag => 'option',
783 :child => { :tag => 'option',
784 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
784 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
785 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
785 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
786 end
786 end
787
787
788 def test_update_edit_form
788 def test_update_edit_form
789 @request.session[:user_id] = 2
789 @request.session[:user_id] = 2
790 xhr :post, :new, :project_id => 1,
790 xhr :post, :new, :project_id => 1,
791 :id => 1,
791 :id => 1,
792 :issue => {:tracker_id => 2,
792 :issue => {:tracker_id => 2,
793 :subject => 'This is the test_new issue',
793 :subject => 'This is the test_new issue',
794 :description => 'This is the description',
794 :description => 'This is the description',
795 :priority_id => 5}
795 :priority_id => 5}
796 assert_response :success
796 assert_response :success
797 assert_template 'attributes'
797 assert_template 'attributes'
798
798
799 issue = assigns(:issue)
799 issue = assigns(:issue)
800 assert_kind_of Issue, issue
800 assert_kind_of Issue, issue
801 assert_equal 1, issue.id
801 assert_equal 1, issue.id
802 assert_equal 1, issue.project_id
802 assert_equal 1, issue.project_id
803 assert_equal 2, issue.tracker_id
803 assert_equal 2, issue.tracker_id
804 assert_equal 'This is the test_new issue', issue.subject
804 assert_equal 'This is the test_new issue', issue.subject
805 end
805 end
806
806
807 def test_update_using_invalid_http_verbs
807 def test_update_using_invalid_http_verbs
808 @request.session[:user_id] = 2
808 @request.session[:user_id] = 2
809 subject = 'Updated by an invalid http verb'
809 subject = 'Updated by an invalid http verb'
810
810
811 get :update, :id => 1, :issue => {:subject => subject}
811 get :update, :id => 1, :issue => {:subject => subject}
812 assert_not_equal subject, Issue.find(1).subject
812 assert_not_equal subject, Issue.find(1).subject
813
813
814 post :update, :id => 1, :issue => {:subject => subject}
814 post :update, :id => 1, :issue => {:subject => subject}
815 assert_not_equal subject, Issue.find(1).subject
815 assert_not_equal subject, Issue.find(1).subject
816
816
817 delete :update, :id => 1, :issue => {:subject => subject}
817 delete :update, :id => 1, :issue => {:subject => subject}
818 assert_not_equal subject, Issue.find(1).subject
818 assert_not_equal subject, Issue.find(1).subject
819 end
819 end
820
820
821 def test_put_update_without_custom_fields_param
821 def test_put_update_without_custom_fields_param
822 @request.session[:user_id] = 2
822 @request.session[:user_id] = 2
823 ActionMailer::Base.deliveries.clear
823 ActionMailer::Base.deliveries.clear
824
824
825 issue = Issue.find(1)
825 issue = Issue.find(1)
826 assert_equal '125', issue.custom_value_for(2).value
826 assert_equal '125', issue.custom_value_for(2).value
827 old_subject = issue.subject
827 old_subject = issue.subject
828 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
828 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
829
829
830 assert_difference('Journal.count') do
830 assert_difference('Journal.count') do
831 assert_difference('JournalDetail.count', 2) do
831 assert_difference('JournalDetail.count', 2) do
832 put :update, :id => 1, :issue => {:subject => new_subject,
832 put :update, :id => 1, :issue => {:subject => new_subject,
833 :priority_id => '6',
833 :priority_id => '6',
834 :category_id => '1' # no change
834 :category_id => '1' # no change
835 }
835 }
836 end
836 end
837 end
837 end
838 assert_redirected_to :action => 'show', :id => '1'
838 assert_redirected_to :action => 'show', :id => '1'
839 issue.reload
839 issue.reload
840 assert_equal new_subject, issue.subject
840 assert_equal new_subject, issue.subject
841 # Make sure custom fields were not cleared
841 # Make sure custom fields were not cleared
842 assert_equal '125', issue.custom_value_for(2).value
842 assert_equal '125', issue.custom_value_for(2).value
843
843
844 mail = ActionMailer::Base.deliveries.last
844 mail = ActionMailer::Base.deliveries.last
845 assert_kind_of TMail::Mail, mail
845 assert_kind_of TMail::Mail, mail
846 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
846 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
847 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
847 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
848 end
848 end
849
849
850 def test_put_update_with_custom_field_change
850 def test_put_update_with_custom_field_change
851 @request.session[:user_id] = 2
851 @request.session[:user_id] = 2
852 issue = Issue.find(1)
852 issue = Issue.find(1)
853 assert_equal '125', issue.custom_value_for(2).value
853 assert_equal '125', issue.custom_value_for(2).value
854
854
855 assert_difference('Journal.count') do
855 assert_difference('Journal.count') do
856 assert_difference('JournalDetail.count', 3) do
856 assert_difference('JournalDetail.count', 3) do
857 put :update, :id => 1, :issue => {:subject => 'Custom field change',
857 put :update, :id => 1, :issue => {:subject => 'Custom field change',
858 :priority_id => '6',
858 :priority_id => '6',
859 :category_id => '1', # no change
859 :category_id => '1', # no change
860 :custom_field_values => { '2' => 'New custom value' }
860 :custom_field_values => { '2' => 'New custom value' }
861 }
861 }
862 end
862 end
863 end
863 end
864 assert_redirected_to :action => 'show', :id => '1'
864 assert_redirected_to :action => 'show', :id => '1'
865 issue.reload
865 issue.reload
866 assert_equal 'New custom value', issue.custom_value_for(2).value
866 assert_equal 'New custom value', issue.custom_value_for(2).value
867
867
868 mail = ActionMailer::Base.deliveries.last
868 mail = ActionMailer::Base.deliveries.last
869 assert_kind_of TMail::Mail, mail
869 assert_kind_of TMail::Mail, mail
870 assert mail.body.include?("Searchable field changed from 125 to New custom value")
870 assert mail.body.include?("Searchable field changed from 125 to New custom value")
871 end
871 end
872
872
873 def test_put_update_with_status_and_assignee_change
873 def test_put_update_with_status_and_assignee_change
874 issue = Issue.find(1)
874 issue = Issue.find(1)
875 assert_equal 1, issue.status_id
875 assert_equal 1, issue.status_id
876 @request.session[:user_id] = 2
876 @request.session[:user_id] = 2
877 assert_difference('TimeEntry.count', 0) do
877 assert_difference('TimeEntry.count', 0) do
878 put :update,
878 put :update,
879 :id => 1,
879 :id => 1,
880 :issue => { :status_id => 2, :assigned_to_id => 3 },
880 :issue => { :status_id => 2, :assigned_to_id => 3 },
881 :notes => 'Assigned to dlopper',
881 :notes => 'Assigned to dlopper',
882 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
882 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
883 end
883 end
884 assert_redirected_to :action => 'show', :id => '1'
884 assert_redirected_to :action => 'show', :id => '1'
885 issue.reload
885 issue.reload
886 assert_equal 2, issue.status_id
886 assert_equal 2, issue.status_id
887 j = Journal.find(:first, :order => 'id DESC')
887 j = Journal.find(:first, :order => 'id DESC')
888 assert_equal 'Assigned to dlopper', j.notes
888 assert_equal 'Assigned to dlopper', j.notes
889 assert_equal 2, j.details.size
889 assert_equal 2, j.details.size
890
890
891 mail = ActionMailer::Base.deliveries.last
891 mail = ActionMailer::Base.deliveries.last
892 assert mail.body.include?("Status changed from New to Assigned")
892 assert mail.body.include?("Status changed from New to Assigned")
893 # subject should contain the new status
893 # subject should contain the new status
894 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
894 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
895 end
895 end
896
896
897 def test_put_update_with_note_only
897 def test_put_update_with_note_only
898 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
898 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
899 # anonymous user
899 # anonymous user
900 put :update,
900 put :update,
901 :id => 1,
901 :id => 1,
902 :notes => notes
902 :notes => notes
903 assert_redirected_to :action => 'show', :id => '1'
903 assert_redirected_to :action => 'show', :id => '1'
904 j = Journal.find(:first, :order => 'id DESC')
904 j = Journal.find(:first, :order => 'id DESC')
905 assert_equal notes, j.notes
905 assert_equal notes, j.notes
906 assert_equal 0, j.details.size
906 assert_equal 0, j.details.size
907 assert_equal User.anonymous, j.user
907 assert_equal User.anonymous, j.user
908
908
909 mail = ActionMailer::Base.deliveries.last
909 mail = ActionMailer::Base.deliveries.last
910 assert mail.body.include?(notes)
910 assert mail.body.include?(notes)
911 end
911 end
912
912
913 def test_put_update_with_note_and_spent_time
913 def test_put_update_with_note_and_spent_time
914 @request.session[:user_id] = 2
914 @request.session[:user_id] = 2
915 spent_hours_before = Issue.find(1).spent_hours
915 spent_hours_before = Issue.find(1).spent_hours
916 assert_difference('TimeEntry.count') do
916 assert_difference('TimeEntry.count') do
917 put :update,
917 put :update,
918 :id => 1,
918 :id => 1,
919 :notes => '2.5 hours added',
919 :notes => '2.5 hours added',
920 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
920 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
921 end
921 end
922 assert_redirected_to :action => 'show', :id => '1'
922 assert_redirected_to :action => 'show', :id => '1'
923
923
924 issue = Issue.find(1)
924 issue = Issue.find(1)
925
925
926 j = Journal.find(:first, :order => 'id DESC')
926 j = Journal.find(:first, :order => 'id DESC')
927 assert_equal '2.5 hours added', j.notes
927 assert_equal '2.5 hours added', j.notes
928 assert_equal 0, j.details.size
928 assert_equal 0, j.details.size
929
929
930 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
930 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
931 assert_not_nil t
931 assert_not_nil t
932 assert_equal 2.5, t.hours
932 assert_equal 2.5, t.hours
933 assert_equal spent_hours_before + 2.5, issue.spent_hours
933 assert_equal spent_hours_before + 2.5, issue.spent_hours
934 end
934 end
935
935
936 def test_put_update_with_attachment_only
936 def test_put_update_with_attachment_only
937 set_tmp_attachments_directory
937 set_tmp_attachments_directory
938
938
939 # Delete all fixtured journals, a race condition can occur causing the wrong
939 # Delete all fixtured journals, a race condition can occur causing the wrong
940 # journal to get fetched in the next find.
940 # journal to get fetched in the next find.
941 Journal.delete_all
941 Journal.delete_all
942
942
943 # anonymous user
943 # anonymous user
944 put :update,
944 put :update,
945 :id => 1,
945 :id => 1,
946 :notes => '',
946 :notes => '',
947 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
947 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
948 assert_redirected_to :action => 'show', :id => '1'
948 assert_redirected_to :action => 'show', :id => '1'
949 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
949 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
950 assert j.notes.blank?
950 assert j.notes.blank?
951 assert_equal 1, j.details.size
951 assert_equal 1, j.details.size
952 assert_equal 'testfile.txt', j.details.first.value
952 assert_equal 'testfile.txt', j.details.first.value
953 assert_equal User.anonymous, j.user
953 assert_equal User.anonymous, j.user
954
954
955 mail = ActionMailer::Base.deliveries.last
955 mail = ActionMailer::Base.deliveries.last
956 assert mail.body.include?('testfile.txt')
956 assert mail.body.include?('testfile.txt')
957 end
957 end
958
958
959 def test_put_update_with_attachment_that_fails_to_save
959 def test_put_update_with_attachment_that_fails_to_save
960 set_tmp_attachments_directory
960 set_tmp_attachments_directory
961
961
962 # Delete all fixtured journals, a race condition can occur causing the wrong
962 # Delete all fixtured journals, a race condition can occur causing the wrong
963 # journal to get fetched in the next find.
963 # journal to get fetched in the next find.
964 Journal.delete_all
964 Journal.delete_all
965
965
966 # Mock out the unsaved attachment
966 # Mock out the unsaved attachment
967 Attachment.any_instance.stubs(:create).returns(Attachment.new)
967 Attachment.any_instance.stubs(:create).returns(Attachment.new)
968
968
969 # anonymous user
969 # anonymous user
970 put :update,
970 put :update,
971 :id => 1,
971 :id => 1,
972 :notes => '',
972 :notes => '',
973 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
973 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
974 assert_redirected_to :action => 'show', :id => '1'
974 assert_redirected_to :action => 'show', :id => '1'
975 assert_equal '1 file(s) could not be saved.', flash[:warning]
975 assert_equal '1 file(s) could not be saved.', flash[:warning]
976
976
977 end if Object.const_defined?(:Mocha)
977 end if Object.const_defined?(:Mocha)
978
978
979 def test_put_update_with_no_change
979 def test_put_update_with_no_change
980 issue = Issue.find(1)
980 issue = Issue.find(1)
981 issue.journals.clear
981 issue.journals.clear
982 ActionMailer::Base.deliveries.clear
982 ActionMailer::Base.deliveries.clear
983
983
984 put :update,
984 put :update,
985 :id => 1,
985 :id => 1,
986 :notes => ''
986 :notes => ''
987 assert_redirected_to :action => 'show', :id => '1'
987 assert_redirected_to :action => 'show', :id => '1'
988
988
989 issue.reload
989 issue.reload
990 assert issue.journals.empty?
990 assert issue.journals.empty?
991 # No email should be sent
991 # No email should be sent
992 assert ActionMailer::Base.deliveries.empty?
992 assert ActionMailer::Base.deliveries.empty?
993 end
993 end
994
994
995 def test_put_update_should_send_a_notification
995 def test_put_update_should_send_a_notification
996 @request.session[:user_id] = 2
996 @request.session[:user_id] = 2
997 ActionMailer::Base.deliveries.clear
997 ActionMailer::Base.deliveries.clear
998 issue = Issue.find(1)
998 issue = Issue.find(1)
999 old_subject = issue.subject
999 old_subject = issue.subject
1000 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
1000 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
1001
1001
1002 put :update, :id => 1, :issue => {:subject => new_subject,
1002 put :update, :id => 1, :issue => {:subject => new_subject,
1003 :priority_id => '6',
1003 :priority_id => '6',
1004 :category_id => '1' # no change
1004 :category_id => '1' # no change
1005 }
1005 }
1006 assert_equal 1, ActionMailer::Base.deliveries.size
1006 assert_equal 1, ActionMailer::Base.deliveries.size
1007 end
1007 end
1008
1008
1009 def test_put_update_with_invalid_spent_time_hours_only
1009 def test_put_update_with_invalid_spent_time_hours_only
1010 @request.session[:user_id] = 2
1010 @request.session[:user_id] = 2
1011 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
1011 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
1012
1012
1013 assert_no_difference('Journal.count') do
1013 assert_no_difference('Journal.count') do
1014 put :update,
1014 put :update,
1015 :id => 1,
1015 :id => 1,
1016 :notes => notes,
1016 :notes => notes,
1017 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
1017 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
1018 end
1018 end
1019 assert_response :success
1019 assert_response :success
1020 assert_template 'edit'
1020 assert_template 'edit'
1021
1021
1022 assert_error_tag :descendant => {:content => /Activity can't be blank/}
1022 assert_error_tag :descendant => {:content => /Activity can't be blank/}
1023 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
1023 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
1024 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
1024 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
1025 end
1025 end
1026
1026
1027 def test_put_update_with_invalid_spent_time_comments_only
1027 def test_put_update_with_invalid_spent_time_comments_only
1028 @request.session[:user_id] = 2
1028 @request.session[:user_id] = 2
1029 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
1029 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
1030
1030
1031 assert_no_difference('Journal.count') do
1031 assert_no_difference('Journal.count') do
1032 put :update,
1032 put :update,
1033 :id => 1,
1033 :id => 1,
1034 :notes => notes,
1034 :notes => notes,
1035 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
1035 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
1036 end
1036 end
1037 assert_response :success
1037 assert_response :success
1038 assert_template 'edit'
1038 assert_template 'edit'
1039
1039
1040 assert_error_tag :descendant => {:content => /Activity can't be blank/}
1040 assert_error_tag :descendant => {:content => /Activity can't be blank/}
1041 assert_error_tag :descendant => {:content => /Hours can't be blank/}
1041 assert_error_tag :descendant => {:content => /Hours can't be blank/}
1042 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
1042 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
1043 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
1043 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
1044 end
1044 end
1045
1045
1046 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
1046 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
1047 issue = Issue.find(2)
1047 issue = Issue.find(2)
1048 @request.session[:user_id] = 2
1048 @request.session[:user_id] = 2
1049
1049
1050 put :update,
1050 put :update,
1051 :id => issue.id,
1051 :id => issue.id,
1052 :issue => {
1052 :issue => {
1053 :fixed_version_id => 4
1053 :fixed_version_id => 4
1054 }
1054 }
1055
1055
1056 assert_response :redirect
1056 assert_response :redirect
1057 issue.reload
1057 issue.reload
1058 assert_equal 4, issue.fixed_version_id
1058 assert_equal 4, issue.fixed_version_id
1059 assert_not_equal issue.project_id, issue.fixed_version.project_id
1059 assert_not_equal issue.project_id, issue.fixed_version.project_id
1060 end
1060 end
1061
1061
1062 def test_put_update_should_redirect_back_using_the_back_url_parameter
1062 def test_put_update_should_redirect_back_using_the_back_url_parameter
1063 issue = Issue.find(2)
1063 issue = Issue.find(2)
1064 @request.session[:user_id] = 2
1064 @request.session[:user_id] = 2
1065
1065
1066 put :update,
1066 put :update,
1067 :id => issue.id,
1067 :id => issue.id,
1068 :issue => {
1068 :issue => {
1069 :fixed_version_id => 4
1069 :fixed_version_id => 4
1070 },
1070 },
1071 :back_url => '/issues'
1071 :back_url => '/issues'
1072
1072
1073 assert_response :redirect
1073 assert_response :redirect
1074 assert_redirected_to '/issues'
1074 assert_redirected_to '/issues'
1075 end
1075 end
1076
1076
1077 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1077 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1078 issue = Issue.find(2)
1078 issue = Issue.find(2)
1079 @request.session[:user_id] = 2
1079 @request.session[:user_id] = 2
1080
1080
1081 put :update,
1081 put :update,
1082 :id => issue.id,
1082 :id => issue.id,
1083 :issue => {
1083 :issue => {
1084 :fixed_version_id => 4
1084 :fixed_version_id => 4
1085 },
1085 },
1086 :back_url => 'http://google.com'
1086 :back_url => 'http://google.com'
1087
1087
1088 assert_response :redirect
1088 assert_response :redirect
1089 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
1089 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
1090 end
1090 end
1091
1091
1092 def test_get_bulk_edit
1092 def test_get_bulk_edit
1093 @request.session[:user_id] = 2
1093 @request.session[:user_id] = 2
1094 get :bulk_edit, :ids => [1, 2]
1094 get :bulk_edit, :ids => [1, 2]
1095 assert_response :success
1095 assert_response :success
1096 assert_template 'bulk_edit'
1096 assert_template 'bulk_edit'
1097
1097
1098 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
1098 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
1099
1099
1100 # Project specific custom field, date type
1100 # Project specific custom field, date type
1101 field = CustomField.find(9)
1101 field = CustomField.find(9)
1102 assert !field.is_for_all?
1102 assert !field.is_for_all?
1103 assert_equal 'date', field.field_format
1103 assert_equal 'date', field.field_format
1104 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
1104 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
1105
1105
1106 # System wide custom field
1106 # System wide custom field
1107 assert CustomField.find(1).is_for_all?
1107 assert CustomField.find(1).is_for_all?
1108 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
1108 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
1109 end
1109 end
1110
1110
1111 def test_get_bulk_edit_on_different_projects
1111 def test_get_bulk_edit_on_different_projects
1112 @request.session[:user_id] = 2
1112 @request.session[:user_id] = 2
1113 get :bulk_edit, :ids => [1, 2, 6]
1113 get :bulk_edit, :ids => [1, 2, 6]
1114 assert_response :success
1114 assert_response :success
1115 assert_template 'bulk_edit'
1115 assert_template 'bulk_edit'
1116
1116
1117 # Can not set issues from different projects as children of an issue
1117 # Can not set issues from different projects as children of an issue
1118 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
1118 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
1119
1119
1120 # Project specific custom field, date type
1120 # Project specific custom field, date type
1121 field = CustomField.find(9)
1121 field = CustomField.find(9)
1122 assert !field.is_for_all?
1122 assert !field.is_for_all?
1123 assert !field.project_ids.include?(Issue.find(6).project_id)
1123 assert !field.project_ids.include?(Issue.find(6).project_id)
1124 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
1124 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
1125 end
1125 end
1126
1127 def test_get_bulk_edit_with_user_custom_field
1128 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
1129
1130 @request.session[:user_id] = 2
1131 get :bulk_edit, :ids => [1, 2]
1132 assert_response :success
1133 assert_template 'bulk_edit'
1134
1135 assert_tag :select,
1136 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
1137 :children => {
1138 :only => {:tag => 'option'},
1139 :count => Project.find(1).users.count + 1
1140 }
1141 end
1142
1143 def test_get_bulk_edit_with_version_custom_field
1144 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
1145
1146 @request.session[:user_id] = 2
1147 get :bulk_edit, :ids => [1, 2]
1148 assert_response :success
1149 assert_template 'bulk_edit'
1150
1151 assert_tag :select,
1152 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
1153 :children => {
1154 :only => {:tag => 'option'},
1155 :count => Project.find(1).versions.count + 1
1156 }
1157 end
1126
1158
1127 def test_bulk_update
1159 def test_bulk_update
1128 @request.session[:user_id] = 2
1160 @request.session[:user_id] = 2
1129 # update issues priority
1161 # update issues priority
1130 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
1162 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
1131 :issue => {:priority_id => 7,
1163 :issue => {:priority_id => 7,
1132 :assigned_to_id => '',
1164 :assigned_to_id => '',
1133 :custom_field_values => {'2' => ''}}
1165 :custom_field_values => {'2' => ''}}
1134
1166
1135 assert_response 302
1167 assert_response 302
1136 # check that the issues were updated
1168 # check that the issues were updated
1137 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
1169 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
1138
1170
1139 issue = Issue.find(1)
1171 issue = Issue.find(1)
1140 journal = issue.journals.find(:first, :order => 'created_on DESC')
1172 journal = issue.journals.find(:first, :order => 'created_on DESC')
1141 assert_equal '125', issue.custom_value_for(2).value
1173 assert_equal '125', issue.custom_value_for(2).value
1142 assert_equal 'Bulk editing', journal.notes
1174 assert_equal 'Bulk editing', journal.notes
1143 assert_equal 1, journal.details.size
1175 assert_equal 1, journal.details.size
1144 end
1176 end
1145
1177
1146 def test_bulk_update_on_different_projects
1178 def test_bulk_update_on_different_projects
1147 @request.session[:user_id] = 2
1179 @request.session[:user_id] = 2
1148 # update issues priority
1180 # update issues priority
1149 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
1181 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
1150 :issue => {:priority_id => 7,
1182 :issue => {:priority_id => 7,
1151 :assigned_to_id => '',
1183 :assigned_to_id => '',
1152 :custom_field_values => {'2' => ''}}
1184 :custom_field_values => {'2' => ''}}
1153
1185
1154 assert_response 302
1186 assert_response 302
1155 # check that the issues were updated
1187 # check that the issues were updated
1156 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
1188 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
1157
1189
1158 issue = Issue.find(1)
1190 issue = Issue.find(1)
1159 journal = issue.journals.find(:first, :order => 'created_on DESC')
1191 journal = issue.journals.find(:first, :order => 'created_on DESC')
1160 assert_equal '125', issue.custom_value_for(2).value
1192 assert_equal '125', issue.custom_value_for(2).value
1161 assert_equal 'Bulk editing', journal.notes
1193 assert_equal 'Bulk editing', journal.notes
1162 assert_equal 1, journal.details.size
1194 assert_equal 1, journal.details.size
1163 end
1195 end
1164
1196
1165 def test_bulk_update_on_different_projects_without_rights
1197 def test_bulk_update_on_different_projects_without_rights
1166 @request.session[:user_id] = 3
1198 @request.session[:user_id] = 3
1167 user = User.find(3)
1199 user = User.find(3)
1168 action = { :controller => "issues", :action => "bulk_update" }
1200 action = { :controller => "issues", :action => "bulk_update" }
1169 assert user.allowed_to?(action, Issue.find(1).project)
1201 assert user.allowed_to?(action, Issue.find(1).project)
1170 assert ! user.allowed_to?(action, Issue.find(6).project)
1202 assert ! user.allowed_to?(action, Issue.find(6).project)
1171 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
1203 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
1172 :issue => {:priority_id => 7,
1204 :issue => {:priority_id => 7,
1173 :assigned_to_id => '',
1205 :assigned_to_id => '',
1174 :custom_field_values => {'2' => ''}}
1206 :custom_field_values => {'2' => ''}}
1175 assert_response 403
1207 assert_response 403
1176 assert_not_equal "Bulk should fail", Journal.last.notes
1208 assert_not_equal "Bulk should fail", Journal.last.notes
1177 end
1209 end
1178
1210
1179 def test_bullk_update_should_send_a_notification
1211 def test_bullk_update_should_send_a_notification
1180 @request.session[:user_id] = 2
1212 @request.session[:user_id] = 2
1181 ActionMailer::Base.deliveries.clear
1213 ActionMailer::Base.deliveries.clear
1182 post(:bulk_update,
1214 post(:bulk_update,
1183 {
1215 {
1184 :ids => [1, 2],
1216 :ids => [1, 2],
1185 :notes => 'Bulk editing',
1217 :notes => 'Bulk editing',
1186 :issue => {
1218 :issue => {
1187 :priority_id => 7,
1219 :priority_id => 7,
1188 :assigned_to_id => '',
1220 :assigned_to_id => '',
1189 :custom_field_values => {'2' => ''}
1221 :custom_field_values => {'2' => ''}
1190 }
1222 }
1191 })
1223 })
1192
1224
1193 assert_response 302
1225 assert_response 302
1194 assert_equal 2, ActionMailer::Base.deliveries.size
1226 assert_equal 2, ActionMailer::Base.deliveries.size
1195 end
1227 end
1196
1228
1197 def test_bulk_update_status
1229 def test_bulk_update_status
1198 @request.session[:user_id] = 2
1230 @request.session[:user_id] = 2
1199 # update issues priority
1231 # update issues priority
1200 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
1232 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
1201 :issue => {:priority_id => '',
1233 :issue => {:priority_id => '',
1202 :assigned_to_id => '',
1234 :assigned_to_id => '',
1203 :status_id => '5'}
1235 :status_id => '5'}
1204
1236
1205 assert_response 302
1237 assert_response 302
1206 issue = Issue.find(1)
1238 issue = Issue.find(1)
1207 assert issue.closed?
1239 assert issue.closed?
1208 end
1240 end
1209
1241
1210 def test_bulk_update_parent_id
1242 def test_bulk_update_parent_id
1211 @request.session[:user_id] = 2
1243 @request.session[:user_id] = 2
1212 post :bulk_update, :ids => [1, 3],
1244 post :bulk_update, :ids => [1, 3],
1213 :notes => 'Bulk editing parent',
1245 :notes => 'Bulk editing parent',
1214 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
1246 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
1215
1247
1216 assert_response 302
1248 assert_response 302
1217 parent = Issue.find(2)
1249 parent = Issue.find(2)
1218 assert_equal parent.id, Issue.find(1).parent_id
1250 assert_equal parent.id, Issue.find(1).parent_id
1219 assert_equal parent.id, Issue.find(3).parent_id
1251 assert_equal parent.id, Issue.find(3).parent_id
1220 assert_equal [1, 3], parent.children.collect(&:id).sort
1252 assert_equal [1, 3], parent.children.collect(&:id).sort
1221 end
1253 end
1222
1254
1223 def test_bulk_update_custom_field
1255 def test_bulk_update_custom_field
1224 @request.session[:user_id] = 2
1256 @request.session[:user_id] = 2
1225 # update issues priority
1257 # update issues priority
1226 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
1258 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
1227 :issue => {:priority_id => '',
1259 :issue => {:priority_id => '',
1228 :assigned_to_id => '',
1260 :assigned_to_id => '',
1229 :custom_field_values => {'2' => '777'}}
1261 :custom_field_values => {'2' => '777'}}
1230
1262
1231 assert_response 302
1263 assert_response 302
1232
1264
1233 issue = Issue.find(1)
1265 issue = Issue.find(1)
1234 journal = issue.journals.find(:first, :order => 'created_on DESC')
1266 journal = issue.journals.find(:first, :order => 'created_on DESC')
1235 assert_equal '777', issue.custom_value_for(2).value
1267 assert_equal '777', issue.custom_value_for(2).value
1236 assert_equal 1, journal.details.size
1268 assert_equal 1, journal.details.size
1237 assert_equal '125', journal.details.first.old_value
1269 assert_equal '125', journal.details.first.old_value
1238 assert_equal '777', journal.details.first.value
1270 assert_equal '777', journal.details.first.value
1239 end
1271 end
1240
1272
1241 def test_bulk_update_unassign
1273 def test_bulk_update_unassign
1242 assert_not_nil Issue.find(2).assigned_to
1274 assert_not_nil Issue.find(2).assigned_to
1243 @request.session[:user_id] = 2
1275 @request.session[:user_id] = 2
1244 # unassign issues
1276 # unassign issues
1245 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
1277 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
1246 assert_response 302
1278 assert_response 302
1247 # check that the issues were updated
1279 # check that the issues were updated
1248 assert_nil Issue.find(2).assigned_to
1280 assert_nil Issue.find(2).assigned_to
1249 end
1281 end
1250
1282
1251 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
1283 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
1252 @request.session[:user_id] = 2
1284 @request.session[:user_id] = 2
1253
1285
1254 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
1286 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
1255
1287
1256 assert_response :redirect
1288 assert_response :redirect
1257 issues = Issue.find([1,2])
1289 issues = Issue.find([1,2])
1258 issues.each do |issue|
1290 issues.each do |issue|
1259 assert_equal 4, issue.fixed_version_id
1291 assert_equal 4, issue.fixed_version_id
1260 assert_not_equal issue.project_id, issue.fixed_version.project_id
1292 assert_not_equal issue.project_id, issue.fixed_version.project_id
1261 end
1293 end
1262 end
1294 end
1263
1295
1264 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
1296 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
1265 @request.session[:user_id] = 2
1297 @request.session[:user_id] = 2
1266 post :bulk_update, :ids => [1,2], :back_url => '/issues'
1298 post :bulk_update, :ids => [1,2], :back_url => '/issues'
1267
1299
1268 assert_response :redirect
1300 assert_response :redirect
1269 assert_redirected_to '/issues'
1301 assert_redirected_to '/issues'
1270 end
1302 end
1271
1303
1272 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1304 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1273 @request.session[:user_id] = 2
1305 @request.session[:user_id] = 2
1274 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
1306 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
1275
1307
1276 assert_response :redirect
1308 assert_response :redirect
1277 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
1309 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
1278 end
1310 end
1279
1311
1280 def test_destroy_issue_with_no_time_entries
1312 def test_destroy_issue_with_no_time_entries
1281 assert_nil TimeEntry.find_by_issue_id(2)
1313 assert_nil TimeEntry.find_by_issue_id(2)
1282 @request.session[:user_id] = 2
1314 @request.session[:user_id] = 2
1283 post :destroy, :id => 2
1315 post :destroy, :id => 2
1284 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1316 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1285 assert_nil Issue.find_by_id(2)
1317 assert_nil Issue.find_by_id(2)
1286 end
1318 end
1287
1319
1288 def test_destroy_issues_with_time_entries
1320 def test_destroy_issues_with_time_entries
1289 @request.session[:user_id] = 2
1321 @request.session[:user_id] = 2
1290 post :destroy, :ids => [1, 3]
1322 post :destroy, :ids => [1, 3]
1291 assert_response :success
1323 assert_response :success
1292 assert_template 'destroy'
1324 assert_template 'destroy'
1293 assert_not_nil assigns(:hours)
1325 assert_not_nil assigns(:hours)
1294 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1326 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1295 end
1327 end
1296
1328
1297 def test_destroy_issues_and_destroy_time_entries
1329 def test_destroy_issues_and_destroy_time_entries
1298 @request.session[:user_id] = 2
1330 @request.session[:user_id] = 2
1299 post :destroy, :ids => [1, 3], :todo => 'destroy'
1331 post :destroy, :ids => [1, 3], :todo => 'destroy'
1300 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1332 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1301 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1333 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1302 assert_nil TimeEntry.find_by_id([1, 2])
1334 assert_nil TimeEntry.find_by_id([1, 2])
1303 end
1335 end
1304
1336
1305 def test_destroy_issues_and_assign_time_entries_to_project
1337 def test_destroy_issues_and_assign_time_entries_to_project
1306 @request.session[:user_id] = 2
1338 @request.session[:user_id] = 2
1307 post :destroy, :ids => [1, 3], :todo => 'nullify'
1339 post :destroy, :ids => [1, 3], :todo => 'nullify'
1308 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1340 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1309 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1341 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1310 assert_nil TimeEntry.find(1).issue_id
1342 assert_nil TimeEntry.find(1).issue_id
1311 assert_nil TimeEntry.find(2).issue_id
1343 assert_nil TimeEntry.find(2).issue_id
1312 end
1344 end
1313
1345
1314 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1346 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1315 @request.session[:user_id] = 2
1347 @request.session[:user_id] = 2
1316 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1348 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1317 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1349 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1318 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1350 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1319 assert_equal 2, TimeEntry.find(1).issue_id
1351 assert_equal 2, TimeEntry.find(1).issue_id
1320 assert_equal 2, TimeEntry.find(2).issue_id
1352 assert_equal 2, TimeEntry.find(2).issue_id
1321 end
1353 end
1322
1354
1323 def test_destroy_issues_from_different_projects
1355 def test_destroy_issues_from_different_projects
1324 @request.session[:user_id] = 2
1356 @request.session[:user_id] = 2
1325 post :destroy, :ids => [1, 2, 6], :todo => 'destroy'
1357 post :destroy, :ids => [1, 2, 6], :todo => 'destroy'
1326 assert_redirected_to :controller => 'issues', :action => 'index'
1358 assert_redirected_to :controller => 'issues', :action => 'index'
1327 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
1359 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
1328 end
1360 end
1329
1361
1330 def test_destroy_parent_and_child_issues
1362 def test_destroy_parent_and_child_issues
1331 parent = Issue.generate!(:project_id => 1, :tracker_id => 1)
1363 parent = Issue.generate!(:project_id => 1, :tracker_id => 1)
1332 child = Issue.generate!(:project_id => 1, :tracker_id => 1, :parent_issue_id => parent.id)
1364 child = Issue.generate!(:project_id => 1, :tracker_id => 1, :parent_issue_id => parent.id)
1333 assert child.is_descendant_of?(parent.reload)
1365 assert child.is_descendant_of?(parent.reload)
1334
1366
1335 @request.session[:user_id] = 2
1367 @request.session[:user_id] = 2
1336 assert_difference 'Issue.count', -2 do
1368 assert_difference 'Issue.count', -2 do
1337 post :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
1369 post :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
1338 end
1370 end
1339 assert_response 302
1371 assert_response 302
1340 end
1372 end
1341
1373
1342 def test_default_search_scope
1374 def test_default_search_scope
1343 get :index
1375 get :index
1344 assert_tag :div, :attributes => {:id => 'quick-search'},
1376 assert_tag :div, :attributes => {:id => 'quick-search'},
1345 :child => {:tag => 'form',
1377 :child => {:tag => 'form',
1346 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1378 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1347 end
1379 end
1348 end
1380 end
@@ -1,70 +1,77
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class CustomFieldUserFormatTest < ActiveSupport::TestCase
20 class CustomFieldUserFormatTest < ActiveSupport::TestCase
21 fixtures :custom_fields, :projects, :members, :users, :member_roles, :trackers, :issues
21 fixtures :custom_fields, :projects, :members, :users, :member_roles, :trackers, :issues
22
22
23 def setup
23 def setup
24 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user')
24 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user')
25 end
25 end
26
26
27 def test_possible_values_with_no_arguments
27 def test_possible_values_with_no_arguments
28 assert_equal [], @field.possible_values
28 assert_equal [], @field.possible_values
29 assert_equal [], @field.possible_values(nil)
29 assert_equal [], @field.possible_values(nil)
30 end
30 end
31
31
32 def test_possible_values_with_project_resource
32 def test_possible_values_with_project_resource
33 project = Project.find(1)
33 project = Project.find(1)
34 possible_values = @field.possible_values(project.issues.first)
34 possible_values = @field.possible_values(project.issues.first)
35 assert possible_values.any?
35 assert possible_values.any?
36 assert_equal project.users.sort.collect(&:id).map(&:to_s), possible_values
36 assert_equal project.users.sort.collect(&:id).map(&:to_s), possible_values
37 end
37 end
38
38
39 def test_possible_values_with_nil_project_resource
39 def test_possible_values_with_nil_project_resource
40 project = Project.find(1)
40 project = Project.find(1)
41 assert_equal [], @field.possible_values(Issue.new)
41 assert_equal [], @field.possible_values(Issue.new)
42 end
42 end
43
43
44 def test_possible_values_options_with_no_arguments
44 def test_possible_values_options_with_no_arguments
45 assert_equal [], @field.possible_values_options
45 assert_equal [], @field.possible_values_options
46 assert_equal [], @field.possible_values_options(nil)
46 assert_equal [], @field.possible_values_options(nil)
47 end
47 end
48
48
49 def test_possible_values_options_with_project_resource
49 def test_possible_values_options_with_project_resource
50 project = Project.find(1)
50 project = Project.find(1)
51 possible_values_options = @field.possible_values_options(project.issues.first)
51 possible_values_options = @field.possible_values_options(project.issues.first)
52 assert possible_values_options.any?
52 assert possible_values_options.any?
53 assert_equal project.users.sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
53 assert_equal project.users.sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
54 end
54 end
55
55
56 def test_possible_values_options_with_array
57 projects = Project.find([1, 2])
58 possible_values_options = @field.possible_values_options(projects)
59 assert possible_values_options.any?
60 assert_equal (projects.first.users & projects.last.users).sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
61 end
62
56 def test_cast_blank_value
63 def test_cast_blank_value
57 assert_equal nil, @field.cast_value(nil)
64 assert_equal nil, @field.cast_value(nil)
58 assert_equal nil, @field.cast_value("")
65 assert_equal nil, @field.cast_value("")
59 end
66 end
60
67
61 def test_cast_valid_value
68 def test_cast_valid_value
62 user = @field.cast_value("2")
69 user = @field.cast_value("2")
63 assert_kind_of User, user
70 assert_kind_of User, user
64 assert_equal User.find(2), user
71 assert_equal User.find(2), user
65 end
72 end
66
73
67 def test_cast_invalid_value
74 def test_cast_invalid_value
68 assert_equal nil, @field.cast_value("187")
75 assert_equal nil, @field.cast_value("187")
69 end
76 end
70 end
77 end
General Comments 0
You need to be logged in to leave comments. Login now