##// END OF EJS Templates
fix malformed issues csv encoding in case of unable to convert (#8549)...
Toshi MARUYAMA -
r7702:eb498a86acde
parent child
Show More
@@ -1,323 +1,326
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 module IssuesHelper
19 19 include ApplicationHelper
20 20
21 21 def issue_list(issues, &block)
22 22 ancestors = []
23 23 issues.each do |issue|
24 24 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
25 25 ancestors.pop
26 26 end
27 27 yield issue, ancestors.size
28 28 ancestors << issue unless issue.leaf?
29 29 end
30 30 end
31 31
32 32 # Renders a HTML/CSS tooltip
33 33 #
34 34 # To use, a trigger div is needed. This is a div with the class of "tooltip"
35 35 # that contains this method wrapped in a span with the class of "tip"
36 36 #
37 37 # <div class="tooltip"><%= link_to_issue(issue) %>
38 38 # <span class="tip"><%= render_issue_tooltip(issue) %></span>
39 39 # </div>
40 40 #
41 41 def render_issue_tooltip(issue)
42 42 @cached_label_status ||= l(:field_status)
43 43 @cached_label_start_date ||= l(:field_start_date)
44 44 @cached_label_due_date ||= l(:field_due_date)
45 45 @cached_label_assigned_to ||= l(:field_assigned_to)
46 46 @cached_label_priority ||= l(:field_priority)
47 47 @cached_label_project ||= l(:field_project)
48 48
49 49 (link_to_issue(issue) + "<br /><br />" +
50 50 "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
51 51 "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />" +
52 52 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
53 53 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
54 54 "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />" +
55 55 "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}").html_safe
56 56 end
57 57
58 58 def issue_heading(issue)
59 59 h("#{issue.tracker} ##{issue.id}")
60 60 end
61 61
62 62 def render_issue_subject_with_tree(issue)
63 63 s = ''
64 64 ancestors = issue.root? ? [] : issue.ancestors.visible.all
65 65 ancestors.each do |ancestor|
66 66 s << '<div>' + content_tag('p', link_to_issue(ancestor))
67 67 end
68 68 s << '<div>'
69 69 subject = h(issue.subject)
70 70 if issue.is_private?
71 71 subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
72 72 end
73 73 s << content_tag('h3', subject)
74 74 s << '</div>' * (ancestors.size + 1)
75 75 s.html_safe
76 76 end
77 77
78 78 def render_descendants_tree(issue)
79 79 s = '<form><table class="list issues">'
80 80 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
81 81 s << content_tag('tr',
82 82 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
83 83 content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
84 84 content_tag('td', h(child.status)) +
85 85 content_tag('td', link_to_user(child.assigned_to)) +
86 86 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
87 87 :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
88 88 end
89 89 s << '</form></table>'
90 90 s.html_safe
91 91 end
92 92
93 93 def render_custom_fields_rows(issue)
94 94 return if issue.custom_field_values.empty?
95 95 ordered_values = []
96 96 half = (issue.custom_field_values.size / 2.0).ceil
97 97 half.times do |i|
98 98 ordered_values << issue.custom_field_values[i]
99 99 ordered_values << issue.custom_field_values[i + half]
100 100 end
101 101 s = "<tr>\n"
102 102 n = 0
103 103 ordered_values.compact.each do |value|
104 104 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
105 105 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
106 106 n += 1
107 107 end
108 108 s << "</tr>\n"
109 109 s.html_safe
110 110 end
111 111
112 112 def issues_destroy_confirmation_message(issues)
113 113 issues = [issues] unless issues.is_a?(Array)
114 114 message = l(:text_issues_destroy_confirmation)
115 115 descendant_count = issues.inject(0) {|memo, i| memo += (i.right - i.left - 1)/2}
116 116 if descendant_count > 0
117 117 issues.each do |issue|
118 118 next if issue.root?
119 119 issues.each do |other_issue|
120 120 descendant_count -= 1 if issue.is_descendant_of?(other_issue)
121 121 end
122 122 end
123 123 if descendant_count > 0
124 124 message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
125 125 end
126 126 end
127 127 message
128 128 end
129 129
130 130 def sidebar_queries
131 131 unless @sidebar_queries
132 132 # User can see public queries and his own queries
133 133 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
134 134 # Project specific queries and global queries
135 135 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
136 136 @sidebar_queries = Query.find(:all,
137 137 :select => 'id, name, is_public',
138 138 :order => "name ASC",
139 139 :conditions => visible.conditions)
140 140 end
141 141 @sidebar_queries
142 142 end
143 143
144 144 def query_links(title, queries)
145 145 # links to #index on issues/show
146 146 url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
147 147
148 148 content_tag('h3', h(title)) +
149 149 queries.collect {|query|
150 150 link_to(h(query.name), url_params.merge(:query_id => query))
151 151 }.join('<br />')
152 152 end
153 153
154 154 def render_sidebar_queries
155 155 out = ''
156 156 queries = sidebar_queries.select {|q| !q.is_public?}
157 157 out << query_links(l(:label_my_queries), queries) if queries.any?
158 158 queries = sidebar_queries.select {|q| q.is_public?}
159 159 out << query_links(l(:label_query_plural), queries) if queries.any?
160 160 out
161 161 end
162 162
163 163 def show_detail(detail, no_html=false)
164 164 case detail.property
165 165 when 'attr'
166 166 field = detail.prop_key.to_s.gsub(/\_id$/, "")
167 167 label = l(("field_" + field).to_sym)
168 168 case
169 169 when ['due_date', 'start_date'].include?(detail.prop_key)
170 170 value = format_date(detail.value.to_date) if detail.value
171 171 old_value = format_date(detail.old_value.to_date) if detail.old_value
172 172
173 173 when ['project_id', 'status_id', 'tracker_id', 'assigned_to_id', 'priority_id', 'category_id', 'fixed_version_id'].include?(detail.prop_key)
174 174 value = find_name_by_reflection(field, detail.value)
175 175 old_value = find_name_by_reflection(field, detail.old_value)
176 176
177 177 when detail.prop_key == 'estimated_hours'
178 178 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
179 179 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
180 180
181 181 when detail.prop_key == 'parent_id'
182 182 label = l(:field_parent_issue)
183 183 value = "##{detail.value}" unless detail.value.blank?
184 184 old_value = "##{detail.old_value}" unless detail.old_value.blank?
185 185
186 186 when detail.prop_key == 'is_private'
187 187 value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
188 188 old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
189 189 end
190 190 when 'cf'
191 191 custom_field = CustomField.find_by_id(detail.prop_key)
192 192 if custom_field
193 193 label = custom_field.name
194 194 value = format_value(detail.value, custom_field.field_format) if detail.value
195 195 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
196 196 end
197 197 when 'attachment'
198 198 label = l(:label_attachment)
199 199 end
200 200 call_hook(:helper_issues_show_detail_after_setting, {:detail => detail, :label => label, :value => value, :old_value => old_value })
201 201
202 202 label ||= detail.prop_key
203 203 value ||= detail.value
204 204 old_value ||= detail.old_value
205 205
206 206 unless no_html
207 207 label = content_tag('strong', label)
208 208 old_value = content_tag("i", h(old_value)) if detail.old_value
209 209 old_value = content_tag("strike", old_value) if detail.old_value and detail.value.blank?
210 210 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
211 211 # Link to the attachment if it has not been removed
212 212 value = link_to_attachment(a)
213 213 else
214 214 value = content_tag("i", h(value)) if value
215 215 end
216 216 end
217 217
218 218 if detail.property == 'attr' && detail.prop_key == 'description'
219 219 s = l(:text_journal_changed_no_detail, :label => label)
220 220 unless no_html
221 221 diff_link = link_to 'diff',
222 222 {:controller => 'journals', :action => 'diff', :id => detail.journal_id, :detail_id => detail.id},
223 223 :title => l(:label_view_diff)
224 224 s << " (#{ diff_link })"
225 225 end
226 226 s
227 227 elsif !detail.value.blank?
228 228 case detail.property
229 229 when 'attr', 'cf'
230 230 if !detail.old_value.blank?
231 231 l(:text_journal_changed, :label => label, :old => old_value, :new => value)
232 232 else
233 233 l(:text_journal_set_to, :label => label, :value => value)
234 234 end
235 235 when 'attachment'
236 236 l(:text_journal_added, :label => label, :value => value)
237 237 end
238 238 else
239 239 l(:text_journal_deleted, :label => label, :old => old_value)
240 240 end
241 241 end
242 242
243 243 # Find the name of an associated record stored in the field attribute
244 244 def find_name_by_reflection(field, id)
245 245 association = Issue.reflect_on_association(field.to_sym)
246 246 if association
247 247 record = association.class_name.constantize.find_by_id(id)
248 248 return record.name if record
249 249 end
250 250 end
251 251
252 252 # Renders issue children recursively
253 253 def render_api_issue_children(issue, api)
254 254 return if issue.leaf?
255 255 api.array :children do
256 256 issue.children.each do |child|
257 257 api.issue(:id => child.id) do
258 258 api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
259 259 api.subject child.subject
260 260 render_api_issue_children(child, api)
261 261 end
262 262 end
263 263 end
264 264 end
265 265
266 266 def issues_to_csv(issues, project = nil)
267 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
268 267 decimal_separator = l(:general_csv_decimal_separator)
269 268 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
270 269 # csv header fields
271 270 headers = [ "#",
272 271 l(:field_status),
273 272 l(:field_project),
274 273 l(:field_tracker),
275 274 l(:field_priority),
276 275 l(:field_subject),
277 276 l(:field_assigned_to),
278 277 l(:field_category),
279 278 l(:field_fixed_version),
280 279 l(:field_author),
281 280 l(:field_start_date),
282 281 l(:field_due_date),
283 282 l(:field_done_ratio),
284 283 l(:field_estimated_hours),
285 284 l(:field_parent_issue),
286 285 l(:field_created_on),
287 286 l(:field_updated_on)
288 287 ]
289 288 # Export project custom fields if project is given
290 289 # otherwise export custom fields marked as "For all projects"
291 290 custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
292 291 custom_fields.each {|f| headers << f.name}
293 292 # Description in the last column
294 293 headers << l(:field_description)
295 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
294 csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
295 c.to_s,
296 l(:general_csv_encoding) ) }
296 297 # csv lines
297 298 issues.each do |issue|
298 299 fields = [issue.id,
299 300 issue.status.name,
300 301 issue.project.name,
301 302 issue.tracker.name,
302 303 issue.priority.name,
303 304 issue.subject,
304 305 issue.assigned_to,
305 306 issue.category,
306 307 issue.fixed_version,
307 308 issue.author.name,
308 309 format_date(issue.start_date),
309 310 format_date(issue.due_date),
310 311 issue.done_ratio,
311 312 issue.estimated_hours.to_s.gsub('.', decimal_separator),
312 313 issue.parent_id,
313 314 format_time(issue.created_on),
314 315 format_time(issue.updated_on)
315 316 ]
316 317 custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
317 318 fields << issue.description
318 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
319 csv << fields.collect {|c| Redmine::CodesetUtil.from_utf8(
320 c.to_s,
321 l(:general_csv_encoding) ) }
319 322 end
320 323 end
321 324 export
322 325 end
323 326 end
@@ -1,1781 +1,1816
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19 require 'issues_controller'
20 20
21 21 class IssuesControllerTest < ActionController::TestCase
22 22 fixtures :projects,
23 23 :users,
24 24 :roles,
25 25 :members,
26 26 :member_roles,
27 27 :issues,
28 28 :issue_statuses,
29 29 :versions,
30 30 :trackers,
31 31 :projects_trackers,
32 32 :issue_categories,
33 33 :enabled_modules,
34 34 :enumerations,
35 35 :attachments,
36 36 :workflows,
37 37 :custom_fields,
38 38 :custom_values,
39 39 :custom_fields_projects,
40 40 :custom_fields_trackers,
41 41 :time_entries,
42 42 :journals,
43 43 :journal_details,
44 44 :queries
45 45
46 46 def setup
47 47 @controller = IssuesController.new
48 48 @request = ActionController::TestRequest.new
49 49 @response = ActionController::TestResponse.new
50 50 User.current = nil
51 51 end
52 52
53 53 def test_index
54 54 Setting.default_language = 'en'
55 55
56 56 get :index
57 57 assert_response :success
58 58 assert_template 'index'
59 59 assert_not_nil assigns(:issues)
60 60 assert_nil assigns(:project)
61 61 assert_tag :tag => 'a', :content => /Can't print recipes/
62 62 assert_tag :tag => 'a', :content => /Subproject issue/
63 63 # private projects hidden
64 64 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
65 65 assert_no_tag :tag => 'a', :content => /Issue on project 2/
66 66 # project column
67 67 assert_tag :tag => 'th', :content => /Project/
68 68 end
69 69
70 70 def test_index_should_not_list_issues_when_module_disabled
71 71 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
72 72 get :index
73 73 assert_response :success
74 74 assert_template 'index'
75 75 assert_not_nil assigns(:issues)
76 76 assert_nil assigns(:project)
77 77 assert_no_tag :tag => 'a', :content => /Can't print recipes/
78 78 assert_tag :tag => 'a', :content => /Subproject issue/
79 79 end
80 80
81 81 def test_index_should_list_visible_issues_only
82 82 get :index, :per_page => 100
83 83 assert_response :success
84 84 assert_not_nil assigns(:issues)
85 85 assert_nil assigns(:issues).detect {|issue| !issue.visible?}
86 86 end
87 87
88 88 def test_index_with_project
89 89 Setting.display_subprojects_issues = 0
90 90 get :index, :project_id => 1
91 91 assert_response :success
92 92 assert_template 'index'
93 93 assert_not_nil assigns(:issues)
94 94 assert_tag :tag => 'a', :content => /Can't print recipes/
95 95 assert_no_tag :tag => 'a', :content => /Subproject issue/
96 96 end
97 97
98 98 def test_index_with_project_and_subprojects
99 99 Setting.display_subprojects_issues = 1
100 100 get :index, :project_id => 1
101 101 assert_response :success
102 102 assert_template 'index'
103 103 assert_not_nil assigns(:issues)
104 104 assert_tag :tag => 'a', :content => /Can't print recipes/
105 105 assert_tag :tag => 'a', :content => /Subproject issue/
106 106 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
107 107 end
108 108
109 109 def test_index_with_project_and_subprojects_should_show_private_subprojects
110 110 @request.session[:user_id] = 2
111 111 Setting.display_subprojects_issues = 1
112 112 get :index, :project_id => 1
113 113 assert_response :success
114 114 assert_template 'index'
115 115 assert_not_nil assigns(:issues)
116 116 assert_tag :tag => 'a', :content => /Can't print recipes/
117 117 assert_tag :tag => 'a', :content => /Subproject issue/
118 118 assert_tag :tag => 'a', :content => /Issue of a private subproject/
119 119 end
120 120
121 121 def test_index_with_project_and_default_filter
122 122 get :index, :project_id => 1, :set_filter => 1
123 123 assert_response :success
124 124 assert_template 'index'
125 125 assert_not_nil assigns(:issues)
126 126
127 127 query = assigns(:query)
128 128 assert_not_nil query
129 129 # default filter
130 130 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
131 131 end
132 132
133 133 def test_index_with_project_and_filter
134 134 get :index, :project_id => 1, :set_filter => 1,
135 135 :f => ['tracker_id'],
136 136 :op => {'tracker_id' => '='},
137 137 :v => {'tracker_id' => ['1']}
138 138 assert_response :success
139 139 assert_template 'index'
140 140 assert_not_nil assigns(:issues)
141 141
142 142 query = assigns(:query)
143 143 assert_not_nil query
144 144 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
145 145 end
146 146
147 147 def test_index_with_short_filters
148 148
149 149 to_test = {
150 150 'status_id' => {
151 151 'o' => { :op => 'o', :values => [''] },
152 152 'c' => { :op => 'c', :values => [''] },
153 153 '7' => { :op => '=', :values => ['7'] },
154 154 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
155 155 '=7' => { :op => '=', :values => ['7'] },
156 156 '!3' => { :op => '!', :values => ['3'] },
157 157 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
158 158 'subject' => {
159 159 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
160 160 'o' => { :op => '=', :values => ['o'] },
161 161 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
162 162 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
163 163 'tracker_id' => {
164 164 '3' => { :op => '=', :values => ['3'] },
165 165 '=3' => { :op => '=', :values => ['3'] }},
166 166 'start_date' => {
167 167 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
168 168 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
169 169 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
170 170 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
171 171 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
172 172 '<t+2' => { :op => '<t+', :values => ['2'] },
173 173 '>t+2' => { :op => '>t+', :values => ['2'] },
174 174 't+2' => { :op => 't+', :values => ['2'] },
175 175 't' => { :op => 't', :values => [''] },
176 176 'w' => { :op => 'w', :values => [''] },
177 177 '>t-2' => { :op => '>t-', :values => ['2'] },
178 178 '<t-2' => { :op => '<t-', :values => ['2'] },
179 179 't-2' => { :op => 't-', :values => ['2'] }},
180 180 'created_on' => {
181 181 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
182 182 '<t+2' => { :op => '=', :values => ['<t+2'] },
183 183 '>t+2' => { :op => '=', :values => ['>t+2'] },
184 184 't+2' => { :op => 't', :values => ['+2'] }},
185 185 'cf_1' => {
186 186 'c' => { :op => '=', :values => ['c'] },
187 187 '!c' => { :op => '!', :values => ['c'] },
188 188 '!*' => { :op => '!*', :values => [''] },
189 189 '*' => { :op => '*', :values => [''] }},
190 190 'estimated_hours' => {
191 191 '=13.4' => { :op => '=', :values => ['13.4'] },
192 192 '>=45' => { :op => '>=', :values => ['45'] },
193 193 '<=125' => { :op => '<=', :values => ['125'] },
194 194 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
195 195 '!*' => { :op => '!*', :values => [''] },
196 196 '*' => { :op => '*', :values => [''] }}
197 197 }
198 198
199 199 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
200 200
201 201 to_test.each do |field, expression_and_expected|
202 202 expression_and_expected.each do |filter_expression, expected|
203 203
204 204 get :index, :set_filter => 1, field => filter_expression
205 205
206 206 assert_response :success
207 207 assert_template 'index'
208 208 assert_not_nil assigns(:issues)
209 209
210 210 query = assigns(:query)
211 211 assert_not_nil query
212 212 assert query.has_filter?(field)
213 213 assert_equal(default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}}), query.filters)
214 214 end
215 215 end
216 216
217 217 end
218 218
219 219 def test_index_with_project_and_empty_filters
220 220 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
221 221 assert_response :success
222 222 assert_template 'index'
223 223 assert_not_nil assigns(:issues)
224 224
225 225 query = assigns(:query)
226 226 assert_not_nil query
227 227 # no filter
228 228 assert_equal({}, query.filters)
229 229 end
230 230
231 231 def test_index_with_query
232 232 get :index, :project_id => 1, :query_id => 5
233 233 assert_response :success
234 234 assert_template 'index'
235 235 assert_not_nil assigns(:issues)
236 236 assert_nil assigns(:issue_count_by_group)
237 237 end
238 238
239 239 def test_index_with_query_grouped_by_tracker
240 240 get :index, :project_id => 1, :query_id => 6
241 241 assert_response :success
242 242 assert_template 'index'
243 243 assert_not_nil assigns(:issues)
244 244 assert_not_nil assigns(:issue_count_by_group)
245 245 end
246 246
247 247 def test_index_with_query_grouped_by_list_custom_field
248 248 get :index, :project_id => 1, :query_id => 9
249 249 assert_response :success
250 250 assert_template 'index'
251 251 assert_not_nil assigns(:issues)
252 252 assert_not_nil assigns(:issue_count_by_group)
253 253 end
254 254
255 255 def test_private_query_should_not_be_available_to_other_users
256 256 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
257 257 @request.session[:user_id] = 3
258 258
259 259 get :index, :query_id => q.id
260 260 assert_response 403
261 261 end
262 262
263 263 def test_private_query_should_be_available_to_its_user
264 264 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
265 265 @request.session[:user_id] = 2
266 266
267 267 get :index, :query_id => q.id
268 268 assert_response :success
269 269 end
270 270
271 271 def test_public_query_should_be_available_to_other_users
272 272 q = Query.create!(:name => "private", :user => User.find(2), :is_public => true, :project => nil)
273 273 @request.session[:user_id] = 3
274 274
275 275 get :index, :query_id => q.id
276 276 assert_response :success
277 277 end
278 278
279 279 def test_index_sort_by_field_not_included_in_columns
280 280 Setting.issue_list_default_columns = %w(subject author)
281 281 get :index, :sort => 'tracker'
282 282 end
283 283
284 284 def test_index_csv_with_project
285 285 Setting.default_language = 'en'
286 286
287 287 get :index, :format => 'csv'
288 288 assert_response :success
289 289 assert_not_nil assigns(:issues)
290 290 assert_equal 'text/csv', @response.content_type
291 291 assert @response.body.starts_with?("#,")
292 292
293 293 get :index, :project_id => 1, :format => 'csv'
294 294 assert_response :success
295 295 assert_not_nil assigns(:issues)
296 296 assert_equal 'text/csv', @response.content_type
297 297 end
298 298
299 299 def test_index_csv_big_5
300 300 with_settings :default_language => "zh-TW" do
301 301 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
302 302 str_big5 = "\xa4@\xa4\xeb"
303 303 if str_utf8.respond_to?(:force_encoding)
304 304 str_utf8.force_encoding('UTF-8')
305 305 str_big5.force_encoding('Big5')
306 306 end
307 307 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
308 308 :status_id => 1, :priority => IssuePriority.all.first,
309 309 :subject => str_utf8)
310 310 assert issue.save
311 311
312 312 get :index, :project_id => 1,
313 313 :f => ['subject'],
314 314 :op => '=', :values => [str_utf8],
315 315 :format => 'csv'
316 316 assert_equal 'text/csv', @response.content_type
317 317 lines = @response.body.chomp.split("\n")
318 318 s1 = "\xaa\xac\xbaA"
319 319 if str_utf8.respond_to?(:force_encoding)
320 320 s1.force_encoding('Big5')
321 321 end
322 322 assert lines[0].include?(s1)
323 323 assert lines[1].include?(str_big5)
324 324 end
325 325 end
326 326
327 def test_index_csv_cannot_convert_should_be_replaced_big_5
328 with_settings :default_language => "zh-TW" do
329 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
330 if str_utf8.respond_to?(:force_encoding)
331 str_utf8.force_encoding('UTF-8')
332 end
333 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
334 :status_id => 1, :priority => IssuePriority.all.first,
335 :subject => str_utf8)
336 assert issue.save
337
338 get :index, :project_id => 1,
339 :f => ['subject'],
340 :op => '=', :values => [str_utf8],
341 :format => 'csv'
342 assert_equal 'text/csv', @response.content_type
343 lines = @response.body.chomp.split("\n")
344 s1 = "\xaa\xac\xbaA"
345 if str_utf8.respond_to?(:force_encoding)
346 s1.force_encoding('Big5')
347 end
348 assert lines[0].include?(s1)
349 s2 = lines[1].split(",")[5]
350 if s1.respond_to?(:force_encoding)
351 s3 = "\xa5H?"
352 s3.force_encoding('Big5')
353 assert_equal s3, s2
354 elsif RUBY_PLATFORM == 'java'
355 assert_equal "??", s2
356 else
357 assert_equal "\xa5H???", s2
358 end
359 end
360 end
361
327 362 def test_index_pdf
328 363 get :index, :format => 'pdf'
329 364 assert_response :success
330 365 assert_not_nil assigns(:issues)
331 366 assert_equal 'application/pdf', @response.content_type
332 367
333 368 get :index, :project_id => 1, :format => 'pdf'
334 369 assert_response :success
335 370 assert_not_nil assigns(:issues)
336 371 assert_equal 'application/pdf', @response.content_type
337 372
338 373 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
339 374 assert_response :success
340 375 assert_not_nil assigns(:issues)
341 376 assert_equal 'application/pdf', @response.content_type
342 377 end
343 378
344 379 def test_index_pdf_with_query_grouped_by_list_custom_field
345 380 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
346 381 assert_response :success
347 382 assert_not_nil assigns(:issues)
348 383 assert_not_nil assigns(:issue_count_by_group)
349 384 assert_equal 'application/pdf', @response.content_type
350 385 end
351 386
352 387 def test_index_sort
353 388 get :index, :sort => 'tracker,id:desc'
354 389 assert_response :success
355 390
356 391 sort_params = @request.session['issues_index_sort']
357 392 assert sort_params.is_a?(String)
358 393 assert_equal 'tracker,id:desc', sort_params
359 394
360 395 issues = assigns(:issues)
361 396 assert_not_nil issues
362 397 assert !issues.empty?
363 398 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
364 399 end
365 400
366 401 def test_index_with_columns
367 402 columns = ['tracker', 'subject', 'assigned_to']
368 403 get :index, :set_filter => 1, :c => columns
369 404 assert_response :success
370 405
371 406 # query should use specified columns
372 407 query = assigns(:query)
373 408 assert_kind_of Query, query
374 409 assert_equal columns, query.column_names.map(&:to_s)
375 410
376 411 # columns should be stored in session
377 412 assert_kind_of Hash, session[:query]
378 413 assert_kind_of Array, session[:query][:column_names]
379 414 assert_equal columns, session[:query][:column_names].map(&:to_s)
380 415
381 416 # ensure only these columns are kept in the selected columns list
382 417 assert_tag :tag => 'select', :attributes => { :id => 'selected_columns' },
383 418 :children => { :count => 3 }
384 419 assert_no_tag :tag => 'option', :attributes => { :value => 'project' },
385 420 :parent => { :tag => 'select', :attributes => { :id => "selected_columns" } }
386 421 end
387 422
388 423 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
389 424 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
390 425 get :index, :set_filter => 1
391 426
392 427 # query should use specified columns
393 428 query = assigns(:query)
394 429 assert_kind_of Query, query
395 430 assert_equal [:project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
396 431 end
397 432
398 433 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
399 434 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
400 435 columns = ['tracker', 'subject', 'assigned_to']
401 436 get :index, :set_filter => 1, :c => columns
402 437
403 438 # query should use specified columns
404 439 query = assigns(:query)
405 440 assert_kind_of Query, query
406 441 assert_equal columns.map(&:to_sym), query.columns.map(&:name)
407 442 end
408 443
409 444 def test_index_with_custom_field_column
410 445 columns = %w(tracker subject cf_2)
411 446 get :index, :set_filter => 1, :c => columns
412 447 assert_response :success
413 448
414 449 # query should use specified columns
415 450 query = assigns(:query)
416 451 assert_kind_of Query, query
417 452 assert_equal columns, query.column_names.map(&:to_s)
418 453
419 454 assert_tag :td,
420 455 :attributes => {:class => 'cf_2 string'},
421 456 :ancestor => {:tag => 'table', :attributes => {:class => /issues/}}
422 457 end
423 458
424 459 def test_index_send_html_if_query_is_invalid
425 460 get :index, :f => ['start_date'], :op => {:start_date => '='}
426 461 assert_equal 'text/html', @response.content_type
427 462 assert_template 'index'
428 463 end
429 464
430 465 def test_index_send_nothing_if_query_is_invalid
431 466 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
432 467 assert_equal 'text/csv', @response.content_type
433 468 assert @response.body.blank?
434 469 end
435 470
436 471 def test_show_by_anonymous
437 472 get :show, :id => 1
438 473 assert_response :success
439 474 assert_template 'show'
440 475 assert_not_nil assigns(:issue)
441 476 assert_equal Issue.find(1), assigns(:issue)
442 477
443 478 # anonymous role is allowed to add a note
444 479 assert_tag :tag => 'form',
445 480 :descendant => { :tag => 'fieldset',
446 481 :child => { :tag => 'legend',
447 482 :content => /Notes/ } }
448 483 end
449 484
450 485 def test_show_by_manager
451 486 @request.session[:user_id] = 2
452 487 get :show, :id => 1
453 488 assert_response :success
454 489
455 490 assert_tag :tag => 'a',
456 491 :content => /Quote/
457 492
458 493 assert_tag :tag => 'form',
459 494 :descendant => { :tag => 'fieldset',
460 495 :child => { :tag => 'legend',
461 496 :content => /Change properties/ } },
462 497 :descendant => { :tag => 'fieldset',
463 498 :child => { :tag => 'legend',
464 499 :content => /Log time/ } },
465 500 :descendant => { :tag => 'fieldset',
466 501 :child => { :tag => 'legend',
467 502 :content => /Notes/ } }
468 503 end
469 504
470 505 def test_update_form_should_not_display_inactive_enumerations
471 506 @request.session[:user_id] = 2
472 507 get :show, :id => 1
473 508 assert_response :success
474 509
475 510 assert ! IssuePriority.find(15).active?
476 511 assert_no_tag :option, :attributes => {:value => '15'},
477 512 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
478 513 end
479 514
480 515 def test_update_form_should_allow_attachment_upload
481 516 @request.session[:user_id] = 2
482 517 get :show, :id => 1
483 518
484 519 assert_tag :tag => 'form',
485 520 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
486 521 :descendant => {
487 522 :tag => 'input',
488 523 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
489 524 }
490 525 end
491 526
492 527 def test_show_should_deny_anonymous_access_without_permission
493 528 Role.anonymous.remove_permission!(:view_issues)
494 529 get :show, :id => 1
495 530 assert_response :redirect
496 531 end
497 532
498 533 def test_show_should_deny_anonymous_access_to_private_issue
499 534 Issue.update_all(["is_private = ?", true], "id = 1")
500 535 get :show, :id => 1
501 536 assert_response :redirect
502 537 end
503 538
504 539 def test_show_should_deny_non_member_access_without_permission
505 540 Role.non_member.remove_permission!(:view_issues)
506 541 @request.session[:user_id] = 9
507 542 get :show, :id => 1
508 543 assert_response 403
509 544 end
510 545
511 546 def test_show_should_deny_non_member_access_to_private_issue
512 547 Issue.update_all(["is_private = ?", true], "id = 1")
513 548 @request.session[:user_id] = 9
514 549 get :show, :id => 1
515 550 assert_response 403
516 551 end
517 552
518 553 def test_show_should_deny_member_access_without_permission
519 554 Role.find(1).remove_permission!(:view_issues)
520 555 @request.session[:user_id] = 2
521 556 get :show, :id => 1
522 557 assert_response 403
523 558 end
524 559
525 560 def test_show_should_deny_member_access_to_private_issue_without_permission
526 561 Issue.update_all(["is_private = ?", true], "id = 1")
527 562 @request.session[:user_id] = 3
528 563 get :show, :id => 1
529 564 assert_response 403
530 565 end
531 566
532 567 def test_show_should_allow_author_access_to_private_issue
533 568 Issue.update_all(["is_private = ?, author_id = 3", true], "id = 1")
534 569 @request.session[:user_id] = 3
535 570 get :show, :id => 1
536 571 assert_response :success
537 572 end
538 573
539 574 def test_show_should_allow_assignee_access_to_private_issue
540 575 Issue.update_all(["is_private = ?, assigned_to_id = 3", true], "id = 1")
541 576 @request.session[:user_id] = 3
542 577 get :show, :id => 1
543 578 assert_response :success
544 579 end
545 580
546 581 def test_show_should_allow_member_access_to_private_issue_with_permission
547 582 Issue.update_all(["is_private = ?", true], "id = 1")
548 583 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
549 584 @request.session[:user_id] = 3
550 585 get :show, :id => 1
551 586 assert_response :success
552 587 end
553 588
554 589 def test_show_should_not_disclose_relations_to_invisible_issues
555 590 Setting.cross_project_issue_relations = '1'
556 591 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
557 592 # Relation to a private project issue
558 593 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
559 594
560 595 get :show, :id => 1
561 596 assert_response :success
562 597
563 598 assert_tag :div, :attributes => { :id => 'relations' },
564 599 :descendant => { :tag => 'a', :content => /#2$/ }
565 600 assert_no_tag :div, :attributes => { :id => 'relations' },
566 601 :descendant => { :tag => 'a', :content => /#4$/ }
567 602 end
568 603
569 604 def test_show_atom
570 605 get :show, :id => 2, :format => 'atom'
571 606 assert_response :success
572 607 assert_template 'journals/index'
573 608 # Inline image
574 609 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
575 610 end
576 611
577 612 def test_show_export_to_pdf
578 613 get :show, :id => 3, :format => 'pdf'
579 614 assert_response :success
580 615 assert_equal 'application/pdf', @response.content_type
581 616 assert @response.body.starts_with?('%PDF')
582 617 assert_not_nil assigns(:issue)
583 618 end
584 619
585 620 def test_get_new
586 621 @request.session[:user_id] = 2
587 622 get :new, :project_id => 1, :tracker_id => 1
588 623 assert_response :success
589 624 assert_template 'new'
590 625
591 626 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
592 627 :value => 'Default string' }
593 628
594 629 # Be sure we don't display inactive IssuePriorities
595 630 assert ! IssuePriority.find(15).active?
596 631 assert_no_tag :option, :attributes => {:value => '15'},
597 632 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
598 633 end
599 634
600 635 def test_get_new_without_default_start_date_is_creation_date
601 636 Setting.default_issue_start_date_to_creation_date = 0
602 637
603 638 @request.session[:user_id] = 2
604 639 get :new, :project_id => 1, :tracker_id => 1
605 640 assert_response :success
606 641 assert_template 'new'
607 642
608 643 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
609 644 :value => nil }
610 645 end
611 646
612 647 def test_get_new_with_default_start_date_is_creation_date
613 648 Setting.default_issue_start_date_to_creation_date = 1
614 649
615 650 @request.session[:user_id] = 2
616 651 get :new, :project_id => 1, :tracker_id => 1
617 652 assert_response :success
618 653 assert_template 'new'
619 654
620 655 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
621 656 :value => Date.today.to_s }
622 657 end
623 658
624 659 def test_get_new_form_should_allow_attachment_upload
625 660 @request.session[:user_id] = 2
626 661 get :new, :project_id => 1, :tracker_id => 1
627 662
628 663 assert_tag :tag => 'form',
629 664 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
630 665 :descendant => {
631 666 :tag => 'input',
632 667 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
633 668 }
634 669 end
635 670
636 671 def test_get_new_without_tracker_id
637 672 @request.session[:user_id] = 2
638 673 get :new, :project_id => 1
639 674 assert_response :success
640 675 assert_template 'new'
641 676
642 677 issue = assigns(:issue)
643 678 assert_not_nil issue
644 679 assert_equal Project.find(1).trackers.first, issue.tracker
645 680 end
646 681
647 682 def test_get_new_with_no_default_status_should_display_an_error
648 683 @request.session[:user_id] = 2
649 684 IssueStatus.delete_all
650 685
651 686 get :new, :project_id => 1
652 687 assert_response 500
653 688 assert_error_tag :content => /No default issue/
654 689 end
655 690
656 691 def test_get_new_with_no_tracker_should_display_an_error
657 692 @request.session[:user_id] = 2
658 693 Tracker.delete_all
659 694
660 695 get :new, :project_id => 1
661 696 assert_response 500
662 697 assert_error_tag :content => /No tracker/
663 698 end
664 699
665 700 def test_update_new_form
666 701 @request.session[:user_id] = 2
667 702 xhr :post, :new, :project_id => 1,
668 703 :issue => {:tracker_id => 2,
669 704 :subject => 'This is the test_new issue',
670 705 :description => 'This is the description',
671 706 :priority_id => 5}
672 707 assert_response :success
673 708 assert_template 'attributes'
674 709
675 710 issue = assigns(:issue)
676 711 assert_kind_of Issue, issue
677 712 assert_equal 1, issue.project_id
678 713 assert_equal 2, issue.tracker_id
679 714 assert_equal 'This is the test_new issue', issue.subject
680 715 end
681 716
682 717 def test_post_create
683 718 @request.session[:user_id] = 2
684 719 assert_difference 'Issue.count' do
685 720 post :create, :project_id => 1,
686 721 :issue => {:tracker_id => 3,
687 722 :status_id => 2,
688 723 :subject => 'This is the test_new issue',
689 724 :description => 'This is the description',
690 725 :priority_id => 5,
691 726 :start_date => '2010-11-07',
692 727 :estimated_hours => '',
693 728 :custom_field_values => {'2' => 'Value for field 2'}}
694 729 end
695 730 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
696 731
697 732 issue = Issue.find_by_subject('This is the test_new issue')
698 733 assert_not_nil issue
699 734 assert_equal 2, issue.author_id
700 735 assert_equal 3, issue.tracker_id
701 736 assert_equal 2, issue.status_id
702 737 assert_equal Date.parse('2010-11-07'), issue.start_date
703 738 assert_nil issue.estimated_hours
704 739 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
705 740 assert_not_nil v
706 741 assert_equal 'Value for field 2', v.value
707 742 end
708 743
709 744 def test_post_new_with_group_assignment
710 745 group = Group.find(11)
711 746 project = Project.find(1)
712 747 project.members << Member.new(:principal => group, :roles => [Role.first])
713 748
714 749 with_settings :issue_group_assignment => '1' do
715 750 @request.session[:user_id] = 2
716 751 assert_difference 'Issue.count' do
717 752 post :create, :project_id => project.id,
718 753 :issue => {:tracker_id => 3,
719 754 :status_id => 1,
720 755 :subject => 'This is the test_new_with_group_assignment issue',
721 756 :assigned_to_id => group.id}
722 757 end
723 758 end
724 759 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
725 760
726 761 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
727 762 assert_not_nil issue
728 763 assert_equal group, issue.assigned_to
729 764 end
730 765
731 766 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
732 767 Setting.default_issue_start_date_to_creation_date = 0
733 768
734 769 @request.session[:user_id] = 2
735 770 assert_difference 'Issue.count' do
736 771 post :create, :project_id => 1,
737 772 :issue => {:tracker_id => 3,
738 773 :status_id => 2,
739 774 :subject => 'This is the test_new issue',
740 775 :description => 'This is the description',
741 776 :priority_id => 5,
742 777 :estimated_hours => '',
743 778 :custom_field_values => {'2' => 'Value for field 2'}}
744 779 end
745 780 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
746 781
747 782 issue = Issue.find_by_subject('This is the test_new issue')
748 783 assert_not_nil issue
749 784 assert_nil issue.start_date
750 785 end
751 786
752 787 def test_post_create_without_start_date_and_default_start_date_is_creation_date
753 788 Setting.default_issue_start_date_to_creation_date = 1
754 789
755 790 @request.session[:user_id] = 2
756 791 assert_difference 'Issue.count' do
757 792 post :create, :project_id => 1,
758 793 :issue => {:tracker_id => 3,
759 794 :status_id => 2,
760 795 :subject => 'This is the test_new issue',
761 796 :description => 'This is the description',
762 797 :priority_id => 5,
763 798 :estimated_hours => '',
764 799 :custom_field_values => {'2' => 'Value for field 2'}}
765 800 end
766 801 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
767 802
768 803 issue = Issue.find_by_subject('This is the test_new issue')
769 804 assert_not_nil issue
770 805 assert_equal Date.today, issue.start_date
771 806 end
772 807
773 808 def test_post_create_and_continue
774 809 @request.session[:user_id] = 2
775 810 assert_difference 'Issue.count' do
776 811 post :create, :project_id => 1,
777 812 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
778 813 :continue => ''
779 814 end
780 815
781 816 issue = Issue.first(:order => 'id DESC')
782 817 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
783 818 assert_not_nil flash[:notice], "flash was not set"
784 819 assert flash[:notice].include?("<a href='/issues/#{issue.id}'>##{issue.id}</a>"), "issue link not found in flash: #{flash[:notice]}"
785 820 end
786 821
787 822 def test_post_create_without_custom_fields_param
788 823 @request.session[:user_id] = 2
789 824 assert_difference 'Issue.count' do
790 825 post :create, :project_id => 1,
791 826 :issue => {:tracker_id => 1,
792 827 :subject => 'This is the test_new issue',
793 828 :description => 'This is the description',
794 829 :priority_id => 5}
795 830 end
796 831 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
797 832 end
798 833
799 834 def test_post_create_with_required_custom_field_and_without_custom_fields_param
800 835 field = IssueCustomField.find_by_name('Database')
801 836 field.update_attribute(:is_required, true)
802 837
803 838 @request.session[:user_id] = 2
804 839 post :create, :project_id => 1,
805 840 :issue => {:tracker_id => 1,
806 841 :subject => 'This is the test_new issue',
807 842 :description => 'This is the description',
808 843 :priority_id => 5}
809 844 assert_response :success
810 845 assert_template 'new'
811 846 issue = assigns(:issue)
812 847 assert_not_nil issue
813 848 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
814 849 end
815 850
816 851 def test_post_create_with_watchers
817 852 @request.session[:user_id] = 2
818 853 ActionMailer::Base.deliveries.clear
819 854
820 855 assert_difference 'Watcher.count', 2 do
821 856 post :create, :project_id => 1,
822 857 :issue => {:tracker_id => 1,
823 858 :subject => 'This is a new issue with watchers',
824 859 :description => 'This is the description',
825 860 :priority_id => 5,
826 861 :watcher_user_ids => ['2', '3']}
827 862 end
828 863 issue = Issue.find_by_subject('This is a new issue with watchers')
829 864 assert_not_nil issue
830 865 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
831 866
832 867 # Watchers added
833 868 assert_equal [2, 3], issue.watcher_user_ids.sort
834 869 assert issue.watched_by?(User.find(3))
835 870 # Watchers notified
836 871 mail = ActionMailer::Base.deliveries.last
837 872 assert_kind_of TMail::Mail, mail
838 873 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
839 874 end
840 875
841 876 def test_post_create_subissue
842 877 @request.session[:user_id] = 2
843 878
844 879 assert_difference 'Issue.count' do
845 880 post :create, :project_id => 1,
846 881 :issue => {:tracker_id => 1,
847 882 :subject => 'This is a child issue',
848 883 :parent_issue_id => 2}
849 884 end
850 885 issue = Issue.find_by_subject('This is a child issue')
851 886 assert_not_nil issue
852 887 assert_equal Issue.find(2), issue.parent
853 888 end
854 889
855 890 def test_post_create_subissue_with_non_numeric_parent_id
856 891 @request.session[:user_id] = 2
857 892
858 893 assert_difference 'Issue.count' do
859 894 post :create, :project_id => 1,
860 895 :issue => {:tracker_id => 1,
861 896 :subject => 'This is a child issue',
862 897 :parent_issue_id => 'ABC'}
863 898 end
864 899 issue = Issue.find_by_subject('This is a child issue')
865 900 assert_not_nil issue
866 901 assert_nil issue.parent
867 902 end
868 903
869 904 def test_post_create_private
870 905 @request.session[:user_id] = 2
871 906
872 907 assert_difference 'Issue.count' do
873 908 post :create, :project_id => 1,
874 909 :issue => {:tracker_id => 1,
875 910 :subject => 'This is a private issue',
876 911 :is_private => '1'}
877 912 end
878 913 issue = Issue.first(:order => 'id DESC')
879 914 assert issue.is_private?
880 915 end
881 916
882 917 def test_post_create_private_with_set_own_issues_private_permission
883 918 role = Role.find(1)
884 919 role.remove_permission! :set_issues_private
885 920 role.add_permission! :set_own_issues_private
886 921
887 922 @request.session[:user_id] = 2
888 923
889 924 assert_difference 'Issue.count' do
890 925 post :create, :project_id => 1,
891 926 :issue => {:tracker_id => 1,
892 927 :subject => 'This is a private issue',
893 928 :is_private => '1'}
894 929 end
895 930 issue = Issue.first(:order => 'id DESC')
896 931 assert issue.is_private?
897 932 end
898 933
899 934 def test_post_create_should_send_a_notification
900 935 ActionMailer::Base.deliveries.clear
901 936 @request.session[:user_id] = 2
902 937 assert_difference 'Issue.count' do
903 938 post :create, :project_id => 1,
904 939 :issue => {:tracker_id => 3,
905 940 :subject => 'This is the test_new issue',
906 941 :description => 'This is the description',
907 942 :priority_id => 5,
908 943 :estimated_hours => '',
909 944 :custom_field_values => {'2' => 'Value for field 2'}}
910 945 end
911 946 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
912 947
913 948 assert_equal 1, ActionMailer::Base.deliveries.size
914 949 end
915 950
916 951 def test_post_create_should_preserve_fields_values_on_validation_failure
917 952 @request.session[:user_id] = 2
918 953 post :create, :project_id => 1,
919 954 :issue => {:tracker_id => 1,
920 955 # empty subject
921 956 :subject => '',
922 957 :description => 'This is a description',
923 958 :priority_id => 6,
924 959 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
925 960 assert_response :success
926 961 assert_template 'new'
927 962
928 963 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
929 964 :content => 'This is a description'
930 965 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
931 966 :child => { :tag => 'option', :attributes => { :selected => 'selected',
932 967 :value => '6' },
933 968 :content => 'High' }
934 969 # Custom fields
935 970 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
936 971 :child => { :tag => 'option', :attributes => { :selected => 'selected',
937 972 :value => 'Oracle' },
938 973 :content => 'Oracle' }
939 974 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
940 975 :value => 'Value for field 2'}
941 976 end
942 977
943 978 def test_post_create_should_ignore_non_safe_attributes
944 979 @request.session[:user_id] = 2
945 980 assert_nothing_raised do
946 981 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
947 982 end
948 983 end
949 984
950 985 def test_post_create_with_attachment
951 986 set_tmp_attachments_directory
952 987 @request.session[:user_id] = 2
953 988
954 989 assert_difference 'Issue.count' do
955 990 assert_difference 'Attachment.count' do
956 991 post :create, :project_id => 1,
957 992 :issue => { :tracker_id => '1', :subject => 'With attachment' },
958 993 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
959 994 end
960 995 end
961 996
962 997 issue = Issue.first(:order => 'id DESC')
963 998 attachment = Attachment.first(:order => 'id DESC')
964 999
965 1000 assert_equal issue, attachment.container
966 1001 assert_equal 2, attachment.author_id
967 1002 assert_equal 'testfile.txt', attachment.filename
968 1003 assert_equal 'text/plain', attachment.content_type
969 1004 assert_equal 'test file', attachment.description
970 1005 assert_equal 59, attachment.filesize
971 1006 assert File.exists?(attachment.diskfile)
972 1007 assert_equal 59, File.size(attachment.diskfile)
973 1008 end
974 1009
975 1010 context "without workflow privilege" do
976 1011 setup do
977 1012 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
978 1013 Role.anonymous.add_permission! :add_issues, :add_issue_notes
979 1014 end
980 1015
981 1016 context "#new" do
982 1017 should "propose default status only" do
983 1018 get :new, :project_id => 1
984 1019 assert_response :success
985 1020 assert_template 'new'
986 1021 assert_tag :tag => 'select',
987 1022 :attributes => {:name => 'issue[status_id]'},
988 1023 :children => {:count => 1},
989 1024 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
990 1025 end
991 1026
992 1027 should "accept default status" do
993 1028 assert_difference 'Issue.count' do
994 1029 post :create, :project_id => 1,
995 1030 :issue => {:tracker_id => 1,
996 1031 :subject => 'This is an issue',
997 1032 :status_id => 1}
998 1033 end
999 1034 issue = Issue.last(:order => 'id')
1000 1035 assert_equal IssueStatus.default, issue.status
1001 1036 end
1002 1037
1003 1038 should "ignore unauthorized status" do
1004 1039 assert_difference 'Issue.count' do
1005 1040 post :create, :project_id => 1,
1006 1041 :issue => {:tracker_id => 1,
1007 1042 :subject => 'This is an issue',
1008 1043 :status_id => 3}
1009 1044 end
1010 1045 issue = Issue.last(:order => 'id')
1011 1046 assert_equal IssueStatus.default, issue.status
1012 1047 end
1013 1048 end
1014 1049
1015 1050 context "#update" do
1016 1051 should "ignore status change" do
1017 1052 assert_difference 'Journal.count' do
1018 1053 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1019 1054 end
1020 1055 assert_equal 1, Issue.find(1).status_id
1021 1056 end
1022 1057
1023 1058 should "ignore attributes changes" do
1024 1059 assert_difference 'Journal.count' do
1025 1060 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1026 1061 end
1027 1062 issue = Issue.find(1)
1028 1063 assert_equal "Can't print recipes", issue.subject
1029 1064 assert_nil issue.assigned_to
1030 1065 end
1031 1066 end
1032 1067 end
1033 1068
1034 1069 context "with workflow privilege" do
1035 1070 setup do
1036 1071 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1037 1072 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
1038 1073 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
1039 1074 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1040 1075 end
1041 1076
1042 1077 context "#update" do
1043 1078 should "accept authorized status" do
1044 1079 assert_difference 'Journal.count' do
1045 1080 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1046 1081 end
1047 1082 assert_equal 3, Issue.find(1).status_id
1048 1083 end
1049 1084
1050 1085 should "ignore unauthorized status" do
1051 1086 assert_difference 'Journal.count' do
1052 1087 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1053 1088 end
1054 1089 assert_equal 1, Issue.find(1).status_id
1055 1090 end
1056 1091
1057 1092 should "accept authorized attributes changes" do
1058 1093 assert_difference 'Journal.count' do
1059 1094 put :update, :id => 1, :notes => 'just trying', :issue => {:assigned_to_id => 2}
1060 1095 end
1061 1096 issue = Issue.find(1)
1062 1097 assert_equal 2, issue.assigned_to_id
1063 1098 end
1064 1099
1065 1100 should "ignore unauthorized attributes changes" do
1066 1101 assert_difference 'Journal.count' do
1067 1102 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed'}
1068 1103 end
1069 1104 issue = Issue.find(1)
1070 1105 assert_equal "Can't print recipes", issue.subject
1071 1106 end
1072 1107 end
1073 1108
1074 1109 context "and :edit_issues permission" do
1075 1110 setup do
1076 1111 Role.anonymous.add_permission! :add_issues, :edit_issues
1077 1112 end
1078 1113
1079 1114 should "accept authorized status" do
1080 1115 assert_difference 'Journal.count' do
1081 1116 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1082 1117 end
1083 1118 assert_equal 3, Issue.find(1).status_id
1084 1119 end
1085 1120
1086 1121 should "ignore unauthorized status" do
1087 1122 assert_difference 'Journal.count' do
1088 1123 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1089 1124 end
1090 1125 assert_equal 1, Issue.find(1).status_id
1091 1126 end
1092 1127
1093 1128 should "accept authorized attributes changes" do
1094 1129 assert_difference 'Journal.count' do
1095 1130 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1096 1131 end
1097 1132 issue = Issue.find(1)
1098 1133 assert_equal "changed", issue.subject
1099 1134 assert_equal 2, issue.assigned_to_id
1100 1135 end
1101 1136 end
1102 1137 end
1103 1138
1104 1139 def test_copy_issue
1105 1140 @request.session[:user_id] = 2
1106 1141 get :new, :project_id => 1, :copy_from => 1
1107 1142 assert_template 'new'
1108 1143 assert_not_nil assigns(:issue)
1109 1144 orig = Issue.find(1)
1110 1145 assert_equal orig.subject, assigns(:issue).subject
1111 1146 end
1112 1147
1113 1148 def test_get_edit
1114 1149 @request.session[:user_id] = 2
1115 1150 get :edit, :id => 1
1116 1151 assert_response :success
1117 1152 assert_template 'edit'
1118 1153 assert_not_nil assigns(:issue)
1119 1154 assert_equal Issue.find(1), assigns(:issue)
1120 1155
1121 1156 # Be sure we don't display inactive IssuePriorities
1122 1157 assert ! IssuePriority.find(15).active?
1123 1158 assert_no_tag :option, :attributes => {:value => '15'},
1124 1159 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1125 1160 end
1126 1161
1127 1162 def test_get_edit_with_params
1128 1163 @request.session[:user_id] = 2
1129 1164 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
1130 1165 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
1131 1166 assert_response :success
1132 1167 assert_template 'edit'
1133 1168
1134 1169 issue = assigns(:issue)
1135 1170 assert_not_nil issue
1136 1171
1137 1172 assert_equal 5, issue.status_id
1138 1173 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
1139 1174 :child => { :tag => 'option',
1140 1175 :content => 'Closed',
1141 1176 :attributes => { :selected => 'selected' } }
1142 1177
1143 1178 assert_equal 7, issue.priority_id
1144 1179 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1145 1180 :child => { :tag => 'option',
1146 1181 :content => 'Urgent',
1147 1182 :attributes => { :selected => 'selected' } }
1148 1183
1149 1184 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
1150 1185 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
1151 1186 :child => { :tag => 'option',
1152 1187 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
1153 1188 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
1154 1189 end
1155 1190
1156 1191 def test_update_edit_form
1157 1192 @request.session[:user_id] = 2
1158 1193 xhr :post, :new, :project_id => 1,
1159 1194 :id => 1,
1160 1195 :issue => {:tracker_id => 2,
1161 1196 :subject => 'This is the test_new issue',
1162 1197 :description => 'This is the description',
1163 1198 :priority_id => 5}
1164 1199 assert_response :success
1165 1200 assert_template 'attributes'
1166 1201
1167 1202 issue = assigns(:issue)
1168 1203 assert_kind_of Issue, issue
1169 1204 assert_equal 1, issue.id
1170 1205 assert_equal 1, issue.project_id
1171 1206 assert_equal 2, issue.tracker_id
1172 1207 assert_equal 'This is the test_new issue', issue.subject
1173 1208 end
1174 1209
1175 1210 def test_update_using_invalid_http_verbs
1176 1211 @request.session[:user_id] = 2
1177 1212 subject = 'Updated by an invalid http verb'
1178 1213
1179 1214 get :update, :id => 1, :issue => {:subject => subject}
1180 1215 assert_not_equal subject, Issue.find(1).subject
1181 1216
1182 1217 post :update, :id => 1, :issue => {:subject => subject}
1183 1218 assert_not_equal subject, Issue.find(1).subject
1184 1219
1185 1220 delete :update, :id => 1, :issue => {:subject => subject}
1186 1221 assert_not_equal subject, Issue.find(1).subject
1187 1222 end
1188 1223
1189 1224 def test_put_update_without_custom_fields_param
1190 1225 @request.session[:user_id] = 2
1191 1226 ActionMailer::Base.deliveries.clear
1192 1227
1193 1228 issue = Issue.find(1)
1194 1229 assert_equal '125', issue.custom_value_for(2).value
1195 1230 old_subject = issue.subject
1196 1231 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
1197 1232
1198 1233 assert_difference('Journal.count') do
1199 1234 assert_difference('JournalDetail.count', 2) do
1200 1235 put :update, :id => 1, :issue => {:subject => new_subject,
1201 1236 :priority_id => '6',
1202 1237 :category_id => '1' # no change
1203 1238 }
1204 1239 end
1205 1240 end
1206 1241 assert_redirected_to :action => 'show', :id => '1'
1207 1242 issue.reload
1208 1243 assert_equal new_subject, issue.subject
1209 1244 # Make sure custom fields were not cleared
1210 1245 assert_equal '125', issue.custom_value_for(2).value
1211 1246
1212 1247 mail = ActionMailer::Base.deliveries.last
1213 1248 assert_kind_of TMail::Mail, mail
1214 1249 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1215 1250 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
1216 1251 end
1217 1252
1218 1253 def test_put_update_with_custom_field_change
1219 1254 @request.session[:user_id] = 2
1220 1255 issue = Issue.find(1)
1221 1256 assert_equal '125', issue.custom_value_for(2).value
1222 1257
1223 1258 assert_difference('Journal.count') do
1224 1259 assert_difference('JournalDetail.count', 3) do
1225 1260 put :update, :id => 1, :issue => {:subject => 'Custom field change',
1226 1261 :priority_id => '6',
1227 1262 :category_id => '1', # no change
1228 1263 :custom_field_values => { '2' => 'New custom value' }
1229 1264 }
1230 1265 end
1231 1266 end
1232 1267 assert_redirected_to :action => 'show', :id => '1'
1233 1268 issue.reload
1234 1269 assert_equal 'New custom value', issue.custom_value_for(2).value
1235 1270
1236 1271 mail = ActionMailer::Base.deliveries.last
1237 1272 assert_kind_of TMail::Mail, mail
1238 1273 assert mail.body.include?("Searchable field changed from 125 to New custom value")
1239 1274 end
1240 1275
1241 1276 def test_put_update_with_status_and_assignee_change
1242 1277 issue = Issue.find(1)
1243 1278 assert_equal 1, issue.status_id
1244 1279 @request.session[:user_id] = 2
1245 1280 assert_difference('TimeEntry.count', 0) do
1246 1281 put :update,
1247 1282 :id => 1,
1248 1283 :issue => { :status_id => 2, :assigned_to_id => 3 },
1249 1284 :notes => 'Assigned to dlopper',
1250 1285 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
1251 1286 end
1252 1287 assert_redirected_to :action => 'show', :id => '1'
1253 1288 issue.reload
1254 1289 assert_equal 2, issue.status_id
1255 1290 j = Journal.find(:first, :order => 'id DESC')
1256 1291 assert_equal 'Assigned to dlopper', j.notes
1257 1292 assert_equal 2, j.details.size
1258 1293
1259 1294 mail = ActionMailer::Base.deliveries.last
1260 1295 assert mail.body.include?("Status changed from New to Assigned")
1261 1296 # subject should contain the new status
1262 1297 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
1263 1298 end
1264 1299
1265 1300 def test_put_update_with_note_only
1266 1301 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
1267 1302 # anonymous user
1268 1303 put :update,
1269 1304 :id => 1,
1270 1305 :notes => notes
1271 1306 assert_redirected_to :action => 'show', :id => '1'
1272 1307 j = Journal.find(:first, :order => 'id DESC')
1273 1308 assert_equal notes, j.notes
1274 1309 assert_equal 0, j.details.size
1275 1310 assert_equal User.anonymous, j.user
1276 1311
1277 1312 mail = ActionMailer::Base.deliveries.last
1278 1313 assert mail.body.include?(notes)
1279 1314 end
1280 1315
1281 1316 def test_put_update_with_note_and_spent_time
1282 1317 @request.session[:user_id] = 2
1283 1318 spent_hours_before = Issue.find(1).spent_hours
1284 1319 assert_difference('TimeEntry.count') do
1285 1320 put :update,
1286 1321 :id => 1,
1287 1322 :notes => '2.5 hours added',
1288 1323 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
1289 1324 end
1290 1325 assert_redirected_to :action => 'show', :id => '1'
1291 1326
1292 1327 issue = Issue.find(1)
1293 1328
1294 1329 j = Journal.find(:first, :order => 'id DESC')
1295 1330 assert_equal '2.5 hours added', j.notes
1296 1331 assert_equal 0, j.details.size
1297 1332
1298 1333 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
1299 1334 assert_not_nil t
1300 1335 assert_equal 2.5, t.hours
1301 1336 assert_equal spent_hours_before + 2.5, issue.spent_hours
1302 1337 end
1303 1338
1304 1339 def test_put_update_with_attachment_only
1305 1340 set_tmp_attachments_directory
1306 1341
1307 1342 # Delete all fixtured journals, a race condition can occur causing the wrong
1308 1343 # journal to get fetched in the next find.
1309 1344 Journal.delete_all
1310 1345
1311 1346 # anonymous user
1312 1347 assert_difference 'Attachment.count' do
1313 1348 put :update, :id => 1,
1314 1349 :notes => '',
1315 1350 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1316 1351 end
1317 1352
1318 1353 assert_redirected_to :action => 'show', :id => '1'
1319 1354 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
1320 1355 assert j.notes.blank?
1321 1356 assert_equal 1, j.details.size
1322 1357 assert_equal 'testfile.txt', j.details.first.value
1323 1358 assert_equal User.anonymous, j.user
1324 1359
1325 1360 attachment = Attachment.first(:order => 'id DESC')
1326 1361 assert_equal Issue.find(1), attachment.container
1327 1362 assert_equal User.anonymous, attachment.author
1328 1363 assert_equal 'testfile.txt', attachment.filename
1329 1364 assert_equal 'text/plain', attachment.content_type
1330 1365 assert_equal 'test file', attachment.description
1331 1366 assert_equal 59, attachment.filesize
1332 1367 assert File.exists?(attachment.diskfile)
1333 1368 assert_equal 59, File.size(attachment.diskfile)
1334 1369
1335 1370 mail = ActionMailer::Base.deliveries.last
1336 1371 assert mail.body.include?('testfile.txt')
1337 1372 end
1338 1373
1339 1374 def test_put_update_with_attachment_that_fails_to_save
1340 1375 set_tmp_attachments_directory
1341 1376
1342 1377 # Delete all fixtured journals, a race condition can occur causing the wrong
1343 1378 # journal to get fetched in the next find.
1344 1379 Journal.delete_all
1345 1380
1346 1381 # Mock out the unsaved attachment
1347 1382 Attachment.any_instance.stubs(:create).returns(Attachment.new)
1348 1383
1349 1384 # anonymous user
1350 1385 put :update,
1351 1386 :id => 1,
1352 1387 :notes => '',
1353 1388 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
1354 1389 assert_redirected_to :action => 'show', :id => '1'
1355 1390 assert_equal '1 file(s) could not be saved.', flash[:warning]
1356 1391
1357 1392 end if Object.const_defined?(:Mocha)
1358 1393
1359 1394 def test_put_update_with_no_change
1360 1395 issue = Issue.find(1)
1361 1396 issue.journals.clear
1362 1397 ActionMailer::Base.deliveries.clear
1363 1398
1364 1399 put :update,
1365 1400 :id => 1,
1366 1401 :notes => ''
1367 1402 assert_redirected_to :action => 'show', :id => '1'
1368 1403
1369 1404 issue.reload
1370 1405 assert issue.journals.empty?
1371 1406 # No email should be sent
1372 1407 assert ActionMailer::Base.deliveries.empty?
1373 1408 end
1374 1409
1375 1410 def test_put_update_should_send_a_notification
1376 1411 @request.session[:user_id] = 2
1377 1412 ActionMailer::Base.deliveries.clear
1378 1413 issue = Issue.find(1)
1379 1414 old_subject = issue.subject
1380 1415 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
1381 1416
1382 1417 put :update, :id => 1, :issue => {:subject => new_subject,
1383 1418 :priority_id => '6',
1384 1419 :category_id => '1' # no change
1385 1420 }
1386 1421 assert_equal 1, ActionMailer::Base.deliveries.size
1387 1422 end
1388 1423
1389 1424 def test_put_update_with_invalid_spent_time_hours_only
1390 1425 @request.session[:user_id] = 2
1391 1426 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
1392 1427
1393 1428 assert_no_difference('Journal.count') do
1394 1429 put :update,
1395 1430 :id => 1,
1396 1431 :notes => notes,
1397 1432 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
1398 1433 end
1399 1434 assert_response :success
1400 1435 assert_template 'edit'
1401 1436
1402 1437 assert_error_tag :descendant => {:content => /Activity can't be blank/}
1403 1438 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
1404 1439 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
1405 1440 end
1406 1441
1407 1442 def test_put_update_with_invalid_spent_time_comments_only
1408 1443 @request.session[:user_id] = 2
1409 1444 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
1410 1445
1411 1446 assert_no_difference('Journal.count') do
1412 1447 put :update,
1413 1448 :id => 1,
1414 1449 :notes => notes,
1415 1450 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
1416 1451 end
1417 1452 assert_response :success
1418 1453 assert_template 'edit'
1419 1454
1420 1455 assert_error_tag :descendant => {:content => /Activity can't be blank/}
1421 1456 assert_error_tag :descendant => {:content => /Hours can't be blank/}
1422 1457 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
1423 1458 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
1424 1459 end
1425 1460
1426 1461 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
1427 1462 issue = Issue.find(2)
1428 1463 @request.session[:user_id] = 2
1429 1464
1430 1465 put :update,
1431 1466 :id => issue.id,
1432 1467 :issue => {
1433 1468 :fixed_version_id => 4
1434 1469 }
1435 1470
1436 1471 assert_response :redirect
1437 1472 issue.reload
1438 1473 assert_equal 4, issue.fixed_version_id
1439 1474 assert_not_equal issue.project_id, issue.fixed_version.project_id
1440 1475 end
1441 1476
1442 1477 def test_put_update_should_redirect_back_using_the_back_url_parameter
1443 1478 issue = Issue.find(2)
1444 1479 @request.session[:user_id] = 2
1445 1480
1446 1481 put :update,
1447 1482 :id => issue.id,
1448 1483 :issue => {
1449 1484 :fixed_version_id => 4
1450 1485 },
1451 1486 :back_url => '/issues'
1452 1487
1453 1488 assert_response :redirect
1454 1489 assert_redirected_to '/issues'
1455 1490 end
1456 1491
1457 1492 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1458 1493 issue = Issue.find(2)
1459 1494 @request.session[:user_id] = 2
1460 1495
1461 1496 put :update,
1462 1497 :id => issue.id,
1463 1498 :issue => {
1464 1499 :fixed_version_id => 4
1465 1500 },
1466 1501 :back_url => 'http://google.com'
1467 1502
1468 1503 assert_response :redirect
1469 1504 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
1470 1505 end
1471 1506
1472 1507 def test_get_bulk_edit
1473 1508 @request.session[:user_id] = 2
1474 1509 get :bulk_edit, :ids => [1, 2]
1475 1510 assert_response :success
1476 1511 assert_template 'bulk_edit'
1477 1512
1478 1513 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
1479 1514
1480 1515 # Project specific custom field, date type
1481 1516 field = CustomField.find(9)
1482 1517 assert !field.is_for_all?
1483 1518 assert_equal 'date', field.field_format
1484 1519 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
1485 1520
1486 1521 # System wide custom field
1487 1522 assert CustomField.find(1).is_for_all?
1488 1523 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
1489 1524
1490 1525 # Be sure we don't display inactive IssuePriorities
1491 1526 assert ! IssuePriority.find(15).active?
1492 1527 assert_no_tag :option, :attributes => {:value => '15'},
1493 1528 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1494 1529 end
1495 1530
1496 1531 def test_get_bulk_edit_on_different_projects
1497 1532 @request.session[:user_id] = 2
1498 1533 get :bulk_edit, :ids => [1, 2, 6]
1499 1534 assert_response :success
1500 1535 assert_template 'bulk_edit'
1501 1536
1502 1537 # Can not set issues from different projects as children of an issue
1503 1538 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
1504 1539
1505 1540 # Project specific custom field, date type
1506 1541 field = CustomField.find(9)
1507 1542 assert !field.is_for_all?
1508 1543 assert !field.project_ids.include?(Issue.find(6).project_id)
1509 1544 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
1510 1545 end
1511 1546
1512 1547 def test_get_bulk_edit_with_user_custom_field
1513 1548 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
1514 1549
1515 1550 @request.session[:user_id] = 2
1516 1551 get :bulk_edit, :ids => [1, 2]
1517 1552 assert_response :success
1518 1553 assert_template 'bulk_edit'
1519 1554
1520 1555 assert_tag :select,
1521 1556 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
1522 1557 :children => {
1523 1558 :only => {:tag => 'option'},
1524 1559 :count => Project.find(1).users.count + 1
1525 1560 }
1526 1561 end
1527 1562
1528 1563 def test_get_bulk_edit_with_version_custom_field
1529 1564 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
1530 1565
1531 1566 @request.session[:user_id] = 2
1532 1567 get :bulk_edit, :ids => [1, 2]
1533 1568 assert_response :success
1534 1569 assert_template 'bulk_edit'
1535 1570
1536 1571 assert_tag :select,
1537 1572 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
1538 1573 :children => {
1539 1574 :only => {:tag => 'option'},
1540 1575 :count => Project.find(1).shared_versions.count + 1
1541 1576 }
1542 1577 end
1543 1578
1544 1579 def test_bulk_update
1545 1580 @request.session[:user_id] = 2
1546 1581 # update issues priority
1547 1582 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
1548 1583 :issue => {:priority_id => 7,
1549 1584 :assigned_to_id => '',
1550 1585 :custom_field_values => {'2' => ''}}
1551 1586
1552 1587 assert_response 302
1553 1588 # check that the issues were updated
1554 1589 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
1555 1590
1556 1591 issue = Issue.find(1)
1557 1592 journal = issue.journals.find(:first, :order => 'created_on DESC')
1558 1593 assert_equal '125', issue.custom_value_for(2).value
1559 1594 assert_equal 'Bulk editing', journal.notes
1560 1595 assert_equal 1, journal.details.size
1561 1596 end
1562 1597
1563 1598 def test_bulk_update_with_group_assignee
1564 1599 group = Group.find(11)
1565 1600 project = Project.find(1)
1566 1601 project.members << Member.new(:principal => group, :roles => [Role.first])
1567 1602
1568 1603 @request.session[:user_id] = 2
1569 1604 # update issues assignee
1570 1605 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
1571 1606 :issue => {:priority_id => '',
1572 1607 :assigned_to_id => group.id,
1573 1608 :custom_field_values => {'2' => ''}}
1574 1609
1575 1610 assert_response 302
1576 1611 assert_equal [group, group], Issue.find_all_by_id([1, 2]).collect {|i| i.assigned_to}
1577 1612 end
1578 1613
1579 1614 def test_bulk_update_on_different_projects
1580 1615 @request.session[:user_id] = 2
1581 1616 # update issues priority
1582 1617 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
1583 1618 :issue => {:priority_id => 7,
1584 1619 :assigned_to_id => '',
1585 1620 :custom_field_values => {'2' => ''}}
1586 1621
1587 1622 assert_response 302
1588 1623 # check that the issues were updated
1589 1624 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
1590 1625
1591 1626 issue = Issue.find(1)
1592 1627 journal = issue.journals.find(:first, :order => 'created_on DESC')
1593 1628 assert_equal '125', issue.custom_value_for(2).value
1594 1629 assert_equal 'Bulk editing', journal.notes
1595 1630 assert_equal 1, journal.details.size
1596 1631 end
1597 1632
1598 1633 def test_bulk_update_on_different_projects_without_rights
1599 1634 @request.session[:user_id] = 3
1600 1635 user = User.find(3)
1601 1636 action = { :controller => "issues", :action => "bulk_update" }
1602 1637 assert user.allowed_to?(action, Issue.find(1).project)
1603 1638 assert ! user.allowed_to?(action, Issue.find(6).project)
1604 1639 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
1605 1640 :issue => {:priority_id => 7,
1606 1641 :assigned_to_id => '',
1607 1642 :custom_field_values => {'2' => ''}}
1608 1643 assert_response 403
1609 1644 assert_not_equal "Bulk should fail", Journal.last.notes
1610 1645 end
1611 1646
1612 1647 def test_bullk_update_should_send_a_notification
1613 1648 @request.session[:user_id] = 2
1614 1649 ActionMailer::Base.deliveries.clear
1615 1650 post(:bulk_update,
1616 1651 {
1617 1652 :ids => [1, 2],
1618 1653 :notes => 'Bulk editing',
1619 1654 :issue => {
1620 1655 :priority_id => 7,
1621 1656 :assigned_to_id => '',
1622 1657 :custom_field_values => {'2' => ''}
1623 1658 }
1624 1659 })
1625 1660
1626 1661 assert_response 302
1627 1662 assert_equal 2, ActionMailer::Base.deliveries.size
1628 1663 end
1629 1664
1630 1665 def test_bulk_update_status
1631 1666 @request.session[:user_id] = 2
1632 1667 # update issues priority
1633 1668 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
1634 1669 :issue => {:priority_id => '',
1635 1670 :assigned_to_id => '',
1636 1671 :status_id => '5'}
1637 1672
1638 1673 assert_response 302
1639 1674 issue = Issue.find(1)
1640 1675 assert issue.closed?
1641 1676 end
1642 1677
1643 1678 def test_bulk_update_parent_id
1644 1679 @request.session[:user_id] = 2
1645 1680 post :bulk_update, :ids => [1, 3],
1646 1681 :notes => 'Bulk editing parent',
1647 1682 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
1648 1683
1649 1684 assert_response 302
1650 1685 parent = Issue.find(2)
1651 1686 assert_equal parent.id, Issue.find(1).parent_id
1652 1687 assert_equal parent.id, Issue.find(3).parent_id
1653 1688 assert_equal [1, 3], parent.children.collect(&:id).sort
1654 1689 end
1655 1690
1656 1691 def test_bulk_update_custom_field
1657 1692 @request.session[:user_id] = 2
1658 1693 # update issues priority
1659 1694 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
1660 1695 :issue => {:priority_id => '',
1661 1696 :assigned_to_id => '',
1662 1697 :custom_field_values => {'2' => '777'}}
1663 1698
1664 1699 assert_response 302
1665 1700
1666 1701 issue = Issue.find(1)
1667 1702 journal = issue.journals.find(:first, :order => 'created_on DESC')
1668 1703 assert_equal '777', issue.custom_value_for(2).value
1669 1704 assert_equal 1, journal.details.size
1670 1705 assert_equal '125', journal.details.first.old_value
1671 1706 assert_equal '777', journal.details.first.value
1672 1707 end
1673 1708
1674 1709 def test_bulk_update_unassign
1675 1710 assert_not_nil Issue.find(2).assigned_to
1676 1711 @request.session[:user_id] = 2
1677 1712 # unassign issues
1678 1713 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
1679 1714 assert_response 302
1680 1715 # check that the issues were updated
1681 1716 assert_nil Issue.find(2).assigned_to
1682 1717 end
1683 1718
1684 1719 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
1685 1720 @request.session[:user_id] = 2
1686 1721
1687 1722 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
1688 1723
1689 1724 assert_response :redirect
1690 1725 issues = Issue.find([1,2])
1691 1726 issues.each do |issue|
1692 1727 assert_equal 4, issue.fixed_version_id
1693 1728 assert_not_equal issue.project_id, issue.fixed_version.project_id
1694 1729 end
1695 1730 end
1696 1731
1697 1732 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
1698 1733 @request.session[:user_id] = 2
1699 1734 post :bulk_update, :ids => [1,2], :back_url => '/issues'
1700 1735
1701 1736 assert_response :redirect
1702 1737 assert_redirected_to '/issues'
1703 1738 end
1704 1739
1705 1740 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1706 1741 @request.session[:user_id] = 2
1707 1742 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
1708 1743
1709 1744 assert_response :redirect
1710 1745 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
1711 1746 end
1712 1747
1713 1748 def test_destroy_issue_with_no_time_entries
1714 1749 assert_nil TimeEntry.find_by_issue_id(2)
1715 1750 @request.session[:user_id] = 2
1716 1751 post :destroy, :id => 2
1717 1752 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1718 1753 assert_nil Issue.find_by_id(2)
1719 1754 end
1720 1755
1721 1756 def test_destroy_issues_with_time_entries
1722 1757 @request.session[:user_id] = 2
1723 1758 post :destroy, :ids => [1, 3]
1724 1759 assert_response :success
1725 1760 assert_template 'destroy'
1726 1761 assert_not_nil assigns(:hours)
1727 1762 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1728 1763 end
1729 1764
1730 1765 def test_destroy_issues_and_destroy_time_entries
1731 1766 @request.session[:user_id] = 2
1732 1767 post :destroy, :ids => [1, 3], :todo => 'destroy'
1733 1768 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1734 1769 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1735 1770 assert_nil TimeEntry.find_by_id([1, 2])
1736 1771 end
1737 1772
1738 1773 def test_destroy_issues_and_assign_time_entries_to_project
1739 1774 @request.session[:user_id] = 2
1740 1775 post :destroy, :ids => [1, 3], :todo => 'nullify'
1741 1776 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1742 1777 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1743 1778 assert_nil TimeEntry.find(1).issue_id
1744 1779 assert_nil TimeEntry.find(2).issue_id
1745 1780 end
1746 1781
1747 1782 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1748 1783 @request.session[:user_id] = 2
1749 1784 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1750 1785 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1751 1786 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1752 1787 assert_equal 2, TimeEntry.find(1).issue_id
1753 1788 assert_equal 2, TimeEntry.find(2).issue_id
1754 1789 end
1755 1790
1756 1791 def test_destroy_issues_from_different_projects
1757 1792 @request.session[:user_id] = 2
1758 1793 post :destroy, :ids => [1, 2, 6], :todo => 'destroy'
1759 1794 assert_redirected_to :controller => 'issues', :action => 'index'
1760 1795 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
1761 1796 end
1762 1797
1763 1798 def test_destroy_parent_and_child_issues
1764 1799 parent = Issue.generate!(:project_id => 1, :tracker_id => 1)
1765 1800 child = Issue.generate!(:project_id => 1, :tracker_id => 1, :parent_issue_id => parent.id)
1766 1801 assert child.is_descendant_of?(parent.reload)
1767 1802
1768 1803 @request.session[:user_id] = 2
1769 1804 assert_difference 'Issue.count', -2 do
1770 1805 post :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
1771 1806 end
1772 1807 assert_response 302
1773 1808 end
1774 1809
1775 1810 def test_default_search_scope
1776 1811 get :index
1777 1812 assert_tag :div, :attributes => {:id => 'quick-search'},
1778 1813 :child => {:tag => 'form',
1779 1814 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1780 1815 end
1781 1816 end
General Comments 0
You need to be logged in to leave comments. Login now