##// END OF EJS Templates
Display of multi custom fields....
Jean-Philippe Lang -
r8606:79b12c73d9ac
parent child
Show More
@@ -1,151 +1,151
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2012 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 module CustomFieldsHelper
21 21
22 22 def custom_fields_tabs
23 23 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
24 24 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
25 25 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
26 26 {:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
27 27 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
28 28 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
29 29 {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
30 30 {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
31 31 {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
32 32 ]
33 33 end
34 34
35 35 # Return custom field html tag corresponding to its format
36 36 def custom_field_tag(name, custom_value)
37 37 custom_field = custom_value.custom_field
38 38 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
39 39 field_name << "[]" if custom_field.multiple?
40 40 field_id = "#{name}_custom_field_values_#{custom_field.id}"
41 41
42 42 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
43 43 case field_format.try(:edit_as)
44 44 when "date"
45 45 text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
46 46 calendar_for(field_id)
47 47 when "text"
48 48 text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
49 49 when "bool"
50 50 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
51 51 when "list"
52 52 blank_option = ''
53 53 unless custom_field.multiple?
54 54 if custom_field.is_required?
55 55 unless custom_field.default_value.present?
56 56 blank_option = "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>"
57 57 end
58 58 else
59 59 blank_option = '<option></option>'
60 60 end
61 61 end
62 62 s = select_tag(field_name, blank_option.html_safe + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value),
63 63 :id => field_id, :multiple => custom_field.multiple?)
64 64 if custom_field.multiple?
65 65 s << hidden_field_tag(field_name, '')
66 66 end
67 67 s
68 68 else
69 69 text_field_tag(field_name, custom_value.value, :id => field_id)
70 70 end
71 71 end
72 72
73 73 # Return custom field label tag
74 74 def custom_field_label_tag(name, custom_value)
75 75 content_tag "label", h(custom_value.custom_field.name) +
76 76 (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
77 77 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
78 78 end
79 79
80 80 # Return custom field tag with its label tag
81 81 def custom_field_tag_with_label(name, custom_value)
82 82 custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
83 83 end
84 84
85 85 def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
86 86 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
87 87 field_name << "[]" if custom_field.multiple?
88 88 field_id = "#{name}_custom_field_values_#{custom_field.id}"
89 89 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
90 90 case field_format.try(:edit_as)
91 91 when "date"
92 92 text_field_tag(field_name, '', :id => field_id, :size => 10) +
93 93 calendar_for(field_id)
94 94 when "text"
95 95 text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
96 96 when "bool"
97 97 select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
98 98 [l(:general_text_yes), '1'],
99 99 [l(:general_text_no), '0']]), :id => field_id)
100 100 when "list"
101 101 options = []
102 102 options << [l(:label_no_change_option), ''] unless custom_field.multiple?
103 103 options += custom_field.possible_values_options(projects)
104 104 select_tag(field_name, options_for_select(options),
105 105 :id => field_id, :multiple => custom_field.multiple?)
106 106 else
107 107 text_field_tag(field_name, '', :id => field_id)
108 108 end
109 109 end
110 110
111 111 # Return a string used to display a custom value
112 112 def show_value(custom_value)
113 113 return "" unless custom_value
114 114 format_value(custom_value.value, custom_value.custom_field.field_format)
115 115 end
116 116
117 117 # Return a string used to display a custom value
118 118 def format_value(value, field_format)
119 119 if value.is_a?(Array)
120 value.collect {|v| format_value(v, field_format)}.join(', ')
120 value.collect {|v| format_value(v, field_format)}.compact.sort.join(', ')
121 121 else
122 122 Redmine::CustomFieldFormat.format_value(value, field_format)
123 123 end
124 124 end
125 125
126 126 # Return an array of custom field formats which can be used in select_tag
127 127 def custom_field_formats_for_select(custom_field)
128 128 Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
129 129 end
130 130
131 131 # Renders the custom_values in api views
132 132 def render_api_custom_values(custom_values, api)
133 133 api.array :custom_fields do
134 134 custom_values.each do |custom_value|
135 135 attrs = {:id => custom_value.custom_field_id, :name => custom_value.custom_field.name}
136 136 attrs.merge!(:multiple => true) if custom_value.custom_field.multiple?
137 137 api.custom_field attrs do
138 138 if custom_value.value.is_a?(Array)
139 139 api.array :value do
140 140 custom_value.value.each do |value|
141 141 api.value value unless value.blank?
142 142 end
143 143 end
144 144 else
145 145 api.value custom_value.value
146 146 end
147 147 end
148 148 end
149 149 end unless custom_values.empty?
150 150 end
151 151 end
@@ -1,345 +1,345
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2011 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 module IssuesHelper
21 21 include ApplicationHelper
22 22
23 23 def issue_list(issues, &block)
24 24 ancestors = []
25 25 issues.each do |issue|
26 26 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
27 27 ancestors.pop
28 28 end
29 29 yield issue, ancestors.size
30 30 ancestors << issue unless issue.leaf?
31 31 end
32 32 end
33 33
34 34 # Renders a HTML/CSS tooltip
35 35 #
36 36 # To use, a trigger div is needed. This is a div with the class of "tooltip"
37 37 # that contains this method wrapped in a span with the class of "tip"
38 38 #
39 39 # <div class="tooltip"><%= link_to_issue(issue) %>
40 40 # <span class="tip"><%= render_issue_tooltip(issue) %></span>
41 41 # </div>
42 42 #
43 43 def render_issue_tooltip(issue)
44 44 @cached_label_status ||= l(:field_status)
45 45 @cached_label_start_date ||= l(:field_start_date)
46 46 @cached_label_due_date ||= l(:field_due_date)
47 47 @cached_label_assigned_to ||= l(:field_assigned_to)
48 48 @cached_label_priority ||= l(:field_priority)
49 49 @cached_label_project ||= l(:field_project)
50 50
51 51 link_to_issue(issue) + "<br /><br />".html_safe +
52 52 "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />".html_safe +
53 53 "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />".html_safe +
54 54 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />".html_safe +
55 55 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />".html_safe +
56 56 "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />".html_safe +
57 57 "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}".html_safe
58 58 end
59 59
60 60 def issue_heading(issue)
61 61 h("#{issue.tracker} ##{issue.id}")
62 62 end
63 63
64 64 def render_issue_subject_with_tree(issue)
65 65 s = ''
66 66 ancestors = issue.root? ? [] : issue.ancestors.visible.all
67 67 ancestors.each do |ancestor|
68 68 s << '<div>' + content_tag('p', link_to_issue(ancestor))
69 69 end
70 70 s << '<div>'
71 71 subject = h(issue.subject)
72 72 if issue.is_private?
73 73 subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
74 74 end
75 75 s << content_tag('h3', subject)
76 76 s << '</div>' * (ancestors.size + 1)
77 77 s.html_safe
78 78 end
79 79
80 80 def render_descendants_tree(issue)
81 81 s = '<form><table class="list issues">'
82 82 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
83 83 s << content_tag('tr',
84 84 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
85 85 content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
86 86 content_tag('td', h(child.status)) +
87 87 content_tag('td', link_to_user(child.assigned_to)) +
88 88 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
89 89 :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
90 90 end
91 91 s << '</table></form>'
92 92 s.html_safe
93 93 end
94 94
95 95 def render_custom_fields_rows(issue)
96 96 return if issue.custom_field_values.empty?
97 97 ordered_values = []
98 98 half = (issue.custom_field_values.size / 2.0).ceil
99 99 half.times do |i|
100 100 ordered_values << issue.custom_field_values[i]
101 101 ordered_values << issue.custom_field_values[i + half]
102 102 end
103 103 s = "<tr>\n"
104 104 n = 0
105 105 ordered_values.compact.each do |value|
106 106 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
107 107 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
108 108 n += 1
109 109 end
110 110 s << "</tr>\n"
111 111 s.html_safe
112 112 end
113 113
114 114 def issues_destroy_confirmation_message(issues)
115 115 issues = [issues] unless issues.is_a?(Array)
116 116 message = l(:text_issues_destroy_confirmation)
117 117 descendant_count = issues.inject(0) {|memo, i| memo += (i.right - i.left - 1)/2}
118 118 if descendant_count > 0
119 119 issues.each do |issue|
120 120 next if issue.root?
121 121 issues.each do |other_issue|
122 122 descendant_count -= 1 if issue.is_descendant_of?(other_issue)
123 123 end
124 124 end
125 125 if descendant_count > 0
126 126 message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
127 127 end
128 128 end
129 129 message
130 130 end
131 131
132 132 def sidebar_queries
133 133 unless @sidebar_queries
134 134 @sidebar_queries = Query.visible.all(
135 135 :order => "#{Query.table_name}.name ASC",
136 136 # Project specific queries and global queries
137 137 :conditions => (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
138 138 )
139 139 end
140 140 @sidebar_queries
141 141 end
142 142
143 143 def query_links(title, queries)
144 144 # links to #index on issues/show
145 145 url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
146 146
147 147 content_tag('h3', h(title)) +
148 148 queries.collect {|query|
149 149 css = 'query'
150 150 css << ' selected' if query == @query
151 151 link_to(h(query.name), url_params.merge(:query_id => query), :class => css)
152 152 }.join('<br />').html_safe
153 153 end
154 154
155 155 def render_sidebar_queries
156 156 out = ''.html_safe
157 157 queries = sidebar_queries.select {|q| !q.is_public?}
158 158 out << query_links(l(:label_my_queries), queries) if queries.any?
159 159 queries = sidebar_queries.select {|q| q.is_public?}
160 160 out << query_links(l(:label_query_plural), queries) if queries.any?
161 161 out
162 162 end
163 163
164 164 # Returns the textual representation of a journal details
165 165 # as an array of strings
166 166 def details_to_strings(details, no_html=false)
167 167 strings = []
168 168 values_by_field = {}
169 169 details.each do |detail|
170 170 if detail.property == 'cf'
171 171 field_id = detail.prop_key
172 172 field = CustomField.find_by_id(field_id)
173 173 if field && field.multiple?
174 174 values_by_field[field_id] ||= {:added => [], :deleted => []}
175 175 if detail.old_value
176 176 values_by_field[field_id][:deleted] << detail.old_value
177 177 end
178 178 if detail.value
179 179 values_by_field[field_id][:added] << detail.value
180 180 end
181 181 next
182 182 end
183 183 end
184 184 strings << show_detail(detail, no_html)
185 185 end
186 186 values_by_field.each do |field_id, changes|
187 187 detail = JournalDetail.new(:property => 'cf', :prop_key => field_id)
188 188 if changes[:added].any?
189 189 detail.value = changes[:added]
190 190 strings << show_detail(detail, no_html)
191 191 elsif changes[:deleted].any?
192 192 detail.old_value = changes[:deleted]
193 193 strings << show_detail(detail, no_html)
194 194 end
195 195 end
196 196 strings
197 197 end
198 198
199 199 # Returns the textual representation of a single journal detail
200 200 def show_detail(detail, no_html=false)
201 201 multiple = false
202 202 case detail.property
203 203 when 'attr'
204 204 field = detail.prop_key.to_s.gsub(/\_id$/, "")
205 205 label = l(("field_" + field).to_sym)
206 206 case detail.prop_key
207 207 when 'due_date', 'start_date'
208 208 value = format_date(detail.value.to_date) if detail.value
209 209 old_value = format_date(detail.old_value.to_date) if detail.old_value
210 210
211 211 when 'project_id', 'status_id', 'tracker_id', 'assigned_to_id',
212 212 'priority_id', 'category_id', 'fixed_version_id'
213 213 value = find_name_by_reflection(field, detail.value)
214 214 old_value = find_name_by_reflection(field, detail.old_value)
215 215
216 216 when 'estimated_hours'
217 217 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
218 218 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
219 219
220 220 when 'parent_id'
221 221 label = l(:field_parent_issue)
222 222 value = "##{detail.value}" unless detail.value.blank?
223 223 old_value = "##{detail.old_value}" unless detail.old_value.blank?
224 224
225 225 when 'is_private'
226 226 value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
227 227 old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
228 228 end
229 229 when 'cf'
230 230 custom_field = CustomField.find_by_id(detail.prop_key)
231 231 if custom_field
232 232 multiple = custom_field.multiple?
233 233 label = custom_field.name
234 234 value = format_value(detail.value, custom_field.field_format) if detail.value
235 235 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
236 236 end
237 237 when 'attachment'
238 238 label = l(:label_attachment)
239 239 end
240 240 call_hook(:helper_issues_show_detail_after_setting,
241 241 {:detail => detail, :label => label, :value => value, :old_value => old_value })
242 242
243 243 label ||= detail.prop_key
244 244 value ||= detail.value
245 245 old_value ||= detail.old_value
246 246
247 247 unless no_html
248 248 label = content_tag('strong', label)
249 249 old_value = content_tag("i", h(old_value)) if detail.old_value
250 250 old_value = content_tag("strike", old_value) if detail.old_value and detail.value.blank?
251 251 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
252 252 # Link to the attachment if it has not been removed
253 253 value = link_to_attachment(a, :download => true)
254 254 else
255 255 value = content_tag("i", h(value)) if value
256 256 end
257 257 end
258 258
259 259 if detail.property == 'attr' && detail.prop_key == 'description'
260 260 s = l(:text_journal_changed_no_detail, :label => label)
261 261 unless no_html
262 262 diff_link = link_to 'diff',
263 263 {:controller => 'journals', :action => 'diff', :id => detail.journal_id, :detail_id => detail.id},
264 264 :title => l(:label_view_diff)
265 265 s << " (#{ diff_link })"
266 266 end
267 267 s.html_safe
268 268 elsif detail.value.present?
269 269 case detail.property
270 270 when 'attr', 'cf'
271 271 if detail.old_value.present?
272 272 l(:text_journal_changed, :label => label, :old => old_value, :new => value).html_safe
273 273 elsif multiple
274 274 l(:text_journal_added, :label => label, :value => value).html_safe
275 275 else
276 276 l(:text_journal_set_to, :label => label, :value => value).html_safe
277 277 end
278 278 when 'attachment'
279 279 l(:text_journal_added, :label => label, :value => value).html_safe
280 280 end
281 281 else
282 282 l(:text_journal_deleted, :label => label, :old => old_value).html_safe
283 283 end
284 284 end
285 285
286 286 # Find the name of an associated record stored in the field attribute
287 287 def find_name_by_reflection(field, id)
288 288 association = Issue.reflect_on_association(field.to_sym)
289 289 if association
290 290 record = association.class_name.constantize.find_by_id(id)
291 291 return record.name if record
292 292 end
293 293 end
294 294
295 295 # Renders issue children recursively
296 296 def render_api_issue_children(issue, api)
297 297 return if issue.leaf?
298 298 api.array :children do
299 299 issue.children.each do |child|
300 300 api.issue(:id => child.id) do
301 301 api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
302 302 api.subject child.subject
303 303 render_api_issue_children(child, api)
304 304 end
305 305 end
306 306 end
307 307 end
308 308
309 309 def issues_to_csv(issues, project, query, options={})
310 310 decimal_separator = l(:general_csv_decimal_separator)
311 311 encoding = l(:general_csv_encoding)
312 312 columns = (options[:columns] == 'all' ? query.available_columns : query.columns)
313 313
314 314 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
315 315 # csv header fields
316 316 csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) } +
317 317 (options[:description] ? [Redmine::CodesetUtil.from_utf8(l(:field_description), encoding)] : [])
318 318
319 319 # csv lines
320 320 issues.each do |issue|
321 321 col_values = columns.collect do |column|
322 322 s = if column.is_a?(QueryCustomFieldColumn)
323 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
323 cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
324 324 show_value(cv)
325 325 else
326 326 value = issue.send(column.name)
327 327 if value.is_a?(Date)
328 328 format_date(value)
329 329 elsif value.is_a?(Time)
330 330 format_time(value)
331 331 elsif value.is_a?(Float)
332 332 value.to_s.gsub('.', decimal_separator)
333 333 else
334 334 value
335 335 end
336 336 end
337 337 s.to_s
338 338 end
339 339 csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } +
340 340 (options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : [])
341 341 end
342 342 end
343 343 export
344 344 end
345 345 end
@@ -1,196 +1,196
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2011 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 module TimelogHelper
21 21 include ApplicationHelper
22 22
23 23 def render_timelog_breadcrumb
24 24 links = []
25 25 links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
26 26 links << link_to(h(@project), {:project_id => @project, :issue_id => nil}) if @project
27 27 if @issue
28 28 if @issue.visible?
29 29 links << link_to_issue(@issue, :subject => false)
30 30 else
31 31 links << "##{@issue.id}"
32 32 end
33 33 end
34 34 breadcrumb links
35 35 end
36 36
37 37 # Returns a collection of activities for a select field. time_entry
38 38 # is optional and will be used to check if the selected TimeEntryActivity
39 39 # is active.
40 40 def activity_collection_for_select_options(time_entry=nil, project=nil)
41 41 project ||= @project
42 42 if project.nil?
43 43 activities = TimeEntryActivity.shared.active
44 44 else
45 45 activities = project.activities
46 46 end
47 47
48 48 collection = []
49 49 if time_entry && time_entry.activity && !time_entry.activity.active?
50 50 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ]
51 51 else
52 52 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ] unless activities.detect(&:is_default)
53 53 end
54 54 activities.each { |a| collection << [a.name, a.id] }
55 55 collection
56 56 end
57 57
58 58 def select_hours(data, criteria, value)
59 59 if value.to_s.empty?
60 60 data.select {|row| row[criteria].blank? }
61 61 else
62 62 data.select {|row| row[criteria].to_s == value.to_s}
63 63 end
64 64 end
65 65
66 66 def sum_hours(data)
67 67 sum = 0
68 68 data.each do |row|
69 69 sum += row['hours'].to_f
70 70 end
71 71 sum
72 72 end
73 73
74 74 def options_for_period_select(value)
75 75 options_for_select([[l(:label_all_time), 'all'],
76 76 [l(:label_today), 'today'],
77 77 [l(:label_yesterday), 'yesterday'],
78 78 [l(:label_this_week), 'current_week'],
79 79 [l(:label_last_week), 'last_week'],
80 80 [l(:label_last_n_days, 7), '7_days'],
81 81 [l(:label_this_month), 'current_month'],
82 82 [l(:label_last_month), 'last_month'],
83 83 [l(:label_last_n_days, 30), '30_days'],
84 84 [l(:label_this_year), 'current_year']],
85 85 value)
86 86 end
87 87
88 88 def entries_to_csv(entries)
89 89 decimal_separator = l(:general_csv_decimal_separator)
90 90 custom_fields = TimeEntryCustomField.find(:all)
91 91 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
92 92 # csv header fields
93 93 headers = [l(:field_spent_on),
94 94 l(:field_user),
95 95 l(:field_activity),
96 96 l(:field_project),
97 97 l(:field_issue),
98 98 l(:field_tracker),
99 99 l(:field_subject),
100 100 l(:field_hours),
101 101 l(:field_comments)
102 102 ]
103 103 # Export custom fields
104 104 headers += custom_fields.collect(&:name)
105 105
106 106 csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
107 107 c.to_s,
108 108 l(:general_csv_encoding) ) }
109 109 # csv lines
110 110 entries.each do |entry|
111 111 fields = [format_date(entry.spent_on),
112 112 entry.user,
113 113 entry.activity,
114 114 entry.project,
115 115 (entry.issue ? entry.issue.id : nil),
116 116 (entry.issue ? entry.issue.tracker : nil),
117 117 (entry.issue ? entry.issue.subject : nil),
118 118 entry.hours.to_s.gsub('.', decimal_separator),
119 119 entry.comments
120 120 ]
121 fields += custom_fields.collect {|f| show_value(entry.custom_value_for(f)) }
121 fields += custom_fields.collect {|f| show_value(entry.custom_field_values.detect {|v| v.custom_field_id == f.id}) }
122 122
123 123 csv << fields.collect {|c| Redmine::CodesetUtil.from_utf8(
124 124 c.to_s,
125 125 l(:general_csv_encoding) ) }
126 126 end
127 127 end
128 128 export
129 129 end
130 130
131 131 def format_criteria_value(criteria_options, value)
132 132 if value.blank?
133 133 "[#{l(:label_none)}]"
134 134 elsif k = criteria_options[:klass]
135 135 obj = k.find_by_id(value.to_i)
136 136 if obj.is_a?(Issue)
137 137 obj.visible? ? "#{obj.tracker} ##{obj.id}: #{obj.subject}" : "##{obj.id}"
138 138 else
139 139 obj
140 140 end
141 141 else
142 142 format_value(value, criteria_options[:format])
143 143 end
144 144 end
145 145
146 146 def report_to_csv(report)
147 147 decimal_separator = l(:general_csv_decimal_separator)
148 148 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
149 149 # Column headers
150 150 headers = report.criteria.collect {|criteria| l(report.available_criteria[criteria][:label]) }
151 151 headers += report.periods
152 152 headers << l(:label_total)
153 153 csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
154 154 c.to_s,
155 155 l(:general_csv_encoding) ) }
156 156 # Content
157 157 report_criteria_to_csv(csv, report.available_criteria, report.columns, report.criteria, report.periods, report.hours)
158 158 # Total row
159 159 str_total = Redmine::CodesetUtil.from_utf8(l(:label_total), l(:general_csv_encoding))
160 160 row = [ str_total ] + [''] * (report.criteria.size - 1)
161 161 total = 0
162 162 report.periods.each do |period|
163 163 sum = sum_hours(select_hours(report.hours, report.columns, period.to_s))
164 164 total += sum
165 165 row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
166 166 end
167 167 row << ("%.2f" % total).gsub('.',decimal_separator)
168 168 csv << row
169 169 end
170 170 export
171 171 end
172 172
173 173 def report_criteria_to_csv(csv, available_criteria, columns, criteria, periods, hours, level=0)
174 174 decimal_separator = l(:general_csv_decimal_separator)
175 175 hours.collect {|h| h[criteria[level]].to_s}.uniq.each do |value|
176 176 hours_for_value = select_hours(hours, criteria[level], value)
177 177 next if hours_for_value.empty?
178 178 row = [''] * level
179 179 row << Redmine::CodesetUtil.from_utf8(
180 180 format_criteria_value(available_criteria[criteria[level]], value).to_s,
181 181 l(:general_csv_encoding) )
182 182 row += [''] * (criteria.length - level - 1)
183 183 total = 0
184 184 periods.each do |period|
185 185 sum = sum_hours(select_hours(hours_for_value, columns, period.to_s))
186 186 total += sum
187 187 row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
188 188 end
189 189 row << ("%.2f" % total).gsub('.',decimal_separator)
190 190 csv << row
191 191 if criteria.length > level + 1
192 192 report_criteria_to_csv(csv, available_criteria, columns, criteria, periods, hours_for_value, level + 1)
193 193 end
194 194 end
195 195 end
196 196 end
@@ -1,32 +1,32
1 1 <% if version.completed? %>
2 2 <p><%= format_date(version.effective_date) %></p>
3 3 <% elsif version.effective_date %>
4 4 <p><strong><%= due_date_distance_in_words(version.effective_date) %></strong> (<%= format_date(version.effective_date) %>)</p>
5 5 <% end %>
6 6
7 7 <p><%=h version.description %></p>
8 <% if version.custom_values.any? %>
8 <% if version.custom_field_values.any? %>
9 9 <ul>
10 10 <% version.custom_field_values.each do |custom_value| %>
11 11 <% if custom_value.value.present? %>
12 12 <li><%=h custom_value.custom_field.name %>: <%=h show_value(custom_value) %></li>
13 13 <% end %>
14 14 <% end %>
15 15 </ul>
16 16 <% end %>
17 17
18 18 <% if version.fixed_issues.count > 0 %>
19 19 <%= progress_bar([version.closed_pourcent, version.completed_pourcent], :width => '40em', :legend => ('%0.0f%' % version.completed_pourcent)) %>
20 20 <p class="progress-info">
21 21 <%= link_to(l(:label_x_issues, :count => version.fixed_issues.count),
22 22 project_issues_path(version.project, :status_id => '*', :fixed_version_id => version, :set_filter => 1)) %>
23 23 &nbsp;
24 24 (<%= link_to_if(version.closed_issues_count > 0, l(:label_x_closed_issues_abbr, :count => version.closed_issues_count),
25 25 project_issues_path(version.project, :status_id => 'c', :fixed_version_id => version, :set_filter => 1)) %>
26 26 &#8212;
27 27 <%= link_to_if(version.open_issues_count > 0, l(:label_x_open_issues_abbr, :count => version.open_issues_count),
28 28 project_issues_path(version.project, :status_id => 'o', :fixed_version_id => version, :set_filter => 1)) %>)
29 29 </p>
30 30 <% else %>
31 31 <p class="progress-info"><%= l(:label_roadmap_no_issues) %></p>
32 32 <% end %>
@@ -1,553 +1,553
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2011 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 require 'iconv'
21 21 require 'fpdf/chinese'
22 22 require 'fpdf/japanese'
23 23 require 'fpdf/korean'
24 24 require 'core/rmagick'
25 25
26 26 module Redmine
27 27 module Export
28 28 module PDF
29 29 include ActionView::Helpers::TextHelper
30 30 include ActionView::Helpers::NumberHelper
31 31 include IssuesHelper
32 32
33 33 class ITCPDF < TCPDF
34 34 include Redmine::I18n
35 35 attr_accessor :footer_date
36 36
37 37 def initialize(lang)
38 38 @@k_path_cache = Rails.root.join('tmp', 'pdf')
39 39 FileUtils.mkdir_p @@k_path_cache unless File::exist?(@@k_path_cache)
40 40 set_language_if_valid lang
41 41 pdf_encoding = l(:general_pdf_encoding).upcase
42 42 super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
43 43 case current_language.to_s.downcase
44 44 when 'vi'
45 45 @font_for_content = 'DejaVuSans'
46 46 @font_for_footer = 'DejaVuSans'
47 47 else
48 48 case pdf_encoding
49 49 when 'UTF-8'
50 50 @font_for_content = 'FreeSans'
51 51 @font_for_footer = 'FreeSans'
52 52 when 'CP949'
53 53 extend(PDF_Korean)
54 54 AddUHCFont()
55 55 @font_for_content = 'UHC'
56 56 @font_for_footer = 'UHC'
57 57 when 'CP932', 'SJIS', 'SHIFT_JIS'
58 58 extend(PDF_Japanese)
59 59 AddSJISFont()
60 60 @font_for_content = 'SJIS'
61 61 @font_for_footer = 'SJIS'
62 62 when 'GB18030'
63 63 extend(PDF_Chinese)
64 64 AddGBFont()
65 65 @font_for_content = 'GB'
66 66 @font_for_footer = 'GB'
67 67 when 'BIG5'
68 68 extend(PDF_Chinese)
69 69 AddBig5Font()
70 70 @font_for_content = 'Big5'
71 71 @font_for_footer = 'Big5'
72 72 else
73 73 @font_for_content = 'Arial'
74 74 @font_for_footer = 'Helvetica'
75 75 end
76 76 end
77 77 SetCreator(Redmine::Info.app_name)
78 78 SetFont(@font_for_content)
79 79 end
80 80
81 81 def SetFontStyle(style, size)
82 82 SetFont(@font_for_content, style, size)
83 83 end
84 84
85 85 def SetTitle(txt)
86 86 txt = begin
87 87 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
88 88 hextxt = "<FEFF" # FEFF is BOM
89 89 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
90 90 hextxt << ">"
91 91 rescue
92 92 txt
93 93 end || ''
94 94 super(txt)
95 95 end
96 96
97 97 def textstring(s)
98 98 # Format a text string
99 99 if s =~ /^</ # This means the string is hex-dumped.
100 100 return s
101 101 else
102 102 return '('+escape(s)+')'
103 103 end
104 104 end
105 105
106 106 def fix_text_encoding(txt)
107 107 RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))
108 108 end
109 109
110 110 def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')
111 111 Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link)
112 112 end
113 113
114 114 def RDMMultiCell(w, h=0, txt='', border=0, align='', fill=0, ln=1)
115 115 MultiCell(w, h, fix_text_encoding(txt), border, align, fill, ln)
116 116 end
117 117
118 118 def RDMwriteHTMLCell(w, h, x, y, txt='', attachments=[], border=0, ln=1, fill=0)
119 119 @attachments = attachments
120 120 writeHTMLCell(w, h, x, y,
121 121 fix_text_encoding(
122 122 Redmine::WikiFormatting.to_html(Setting.text_formatting, txt)),
123 123 border, ln, fill)
124 124 end
125 125
126 126 def getImageFilename(attrname)
127 127 # attrname: general_pdf_encoding string file/uri name
128 128 atta = RDMPdfEncoding.attach(@attachments, attrname, l(:general_pdf_encoding))
129 129 if atta
130 130 return atta.diskfile
131 131 else
132 132 return nil
133 133 end
134 134 end
135 135
136 136 def Footer
137 137 SetFont(@font_for_footer, 'I', 8)
138 138 SetY(-15)
139 139 SetX(15)
140 140 RDMCell(0, 5, @footer_date, 0, 0, 'L')
141 141 SetY(-15)
142 142 SetX(-30)
143 143 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
144 144 end
145 145 end
146 146
147 147 # Returns a PDF string of a list of issues
148 148 def issues_to_pdf(issues, project, query)
149 149 pdf = ITCPDF.new(current_language)
150 150 title = query.new_record? ? l(:label_issue_plural) : query.name
151 151 title = "#{project} - #{title}" if project
152 152 pdf.SetTitle(title)
153 153 pdf.alias_nb_pages
154 154 pdf.footer_date = format_date(Date.today)
155 155 pdf.SetAutoPageBreak(false)
156 156 pdf.AddPage("L")
157 157
158 158 # Landscape A4 = 210 x 297 mm
159 159 page_height = 210
160 160 page_width = 297
161 161 right_margin = 10
162 162 bottom_margin = 20
163 163 col_id_width = 10
164 164 row_height = 5
165 165
166 166 # column widths
167 167 table_width = page_width - right_margin - 10 # fixed left margin
168 168 col_width = []
169 169 unless query.columns.empty?
170 170 col_width = query.columns.collect do |c|
171 171 (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) &&
172 172 ['string', 'text'].include?(c.custom_field.field_format))) ? 4.0 : 1.0
173 173 end
174 174 ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
175 175 col_width = col_width.collect {|w| w * ratio}
176 176 end
177 177
178 178 # title
179 179 pdf.SetFontStyle('B',11)
180 180 pdf.RDMCell(190,10, title)
181 181 pdf.Ln
182 182
183 183 # headers
184 184 pdf.SetFontStyle('B',8)
185 185 pdf.SetFillColor(230, 230, 230)
186 186
187 187 # render it background to find the max height used
188 188 base_x = pdf.GetX
189 189 base_y = pdf.GetY
190 190 max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
191 191 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
192 192 pdf.SetXY(base_x, base_y);
193 193
194 194 # write the cells on page
195 195 pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1)
196 196 issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
197 197 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
198 198 pdf.SetY(base_y + max_height);
199 199
200 200 # rows
201 201 pdf.SetFontStyle('',8)
202 202 pdf.SetFillColor(255, 255, 255)
203 203 previous_group = false
204 204 issue_list(issues) do |issue, level|
205 205 if query.grouped? &&
206 206 (group = query.group_by_column.value(issue)) != previous_group
207 207 pdf.SetFontStyle('B',9)
208 208 pdf.RDMCell(277, row_height,
209 209 (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})",
210 210 1, 1, 'L')
211 211 pdf.SetFontStyle('',8)
212 212 previous_group = group
213 213 end
214 214 # fetch all the row values
215 215 col_values = query.columns.collect do |column|
216 216 s = if column.is_a?(QueryCustomFieldColumn)
217 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
217 cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
218 218 show_value(cv)
219 219 else
220 220 value = issue.send(column.name)
221 221 if column.name == :subject
222 222 value = " " * level + value
223 223 end
224 224 if value.is_a?(Date)
225 225 format_date(value)
226 226 elsif value.is_a?(Time)
227 227 format_time(value)
228 228 else
229 229 value
230 230 end
231 231 end
232 232 s.to_s
233 233 end
234 234
235 235 # render it off-page to find the max height used
236 236 base_x = pdf.GetX
237 237 base_y = pdf.GetY
238 238 pdf.SetY(2 * page_height)
239 239 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
240 240 pdf.SetXY(base_x, base_y)
241 241
242 242 # make new page if it doesn't fit on the current one
243 243 space_left = page_height - base_y - bottom_margin
244 244 if max_height > space_left
245 245 pdf.AddPage("L")
246 246 base_x = pdf.GetX
247 247 base_y = pdf.GetY
248 248 end
249 249
250 250 # write the cells on page
251 251 pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1)
252 252 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
253 253 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
254 254 pdf.SetY(base_y + max_height);
255 255 end
256 256
257 257 if issues.size == Setting.issues_export_limit.to_i
258 258 pdf.SetFontStyle('B',10)
259 259 pdf.RDMCell(0, row_height, '...')
260 260 end
261 261 pdf.Output
262 262 end
263 263
264 264 # Renders MultiCells and returns the maximum height used
265 265 def issues_to_pdf_write_cells(pdf, col_values, col_widths,
266 266 row_height, head=false)
267 267 base_y = pdf.GetY
268 268 max_height = row_height
269 269 col_values.each_with_index do |column, i|
270 270 col_x = pdf.GetX
271 271 if head == true
272 272 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
273 273 else
274 274 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
275 275 end
276 276 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
277 277 pdf.SetXY(col_x + col_widths[i], base_y);
278 278 end
279 279 return max_height
280 280 end
281 281
282 282 # Draw lines to close the row (MultiCell border drawing in not uniform)
283 283 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,
284 284 id_width, col_widths)
285 285 col_x = top_x + id_width
286 286 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
287 287 col_widths.each do |width|
288 288 col_x += width
289 289 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
290 290 end
291 291 pdf.Line(top_x, top_y, top_x, lower_y) # left border
292 292 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
293 293 end
294 294
295 295 # Returns a PDF string of a single issue
296 296 def issue_to_pdf(issue)
297 297 pdf = ITCPDF.new(current_language)
298 298 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
299 299 pdf.alias_nb_pages
300 300 pdf.footer_date = format_date(Date.today)
301 301 pdf.AddPage
302 302 pdf.SetFontStyle('B',11)
303 303 buf = "#{issue.project} - #{issue.tracker} # #{issue.id}"
304 304 pdf.RDMMultiCell(190, 5, buf)
305 305 pdf.Ln
306 306 pdf.SetFontStyle('',8)
307 307 base_x = pdf.GetX
308 308 i = 1
309 309 issue.ancestors.each do |ancestor|
310 310 pdf.SetX(base_x + i)
311 311 buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
312 312 pdf.RDMMultiCell(190 - i, 5, buf)
313 313 i += 1 if i < 35
314 314 end
315 315 pdf.Ln
316 316
317 317 pdf.SetFontStyle('B',9)
318 318 pdf.RDMCell(35,5, l(:field_status) + ":","LT")
319 319 pdf.SetFontStyle('',9)
320 320 pdf.RDMCell(60,5, issue.status.to_s,"RT")
321 321 pdf.SetFontStyle('B',9)
322 322 pdf.RDMCell(35,5, l(:field_priority) + ":","LT")
323 323 pdf.SetFontStyle('',9)
324 324 pdf.RDMCell(60,5, issue.priority.to_s,"RT")
325 325 pdf.Ln
326 326
327 327 pdf.SetFontStyle('B',9)
328 328 pdf.RDMCell(35,5, l(:field_author) + ":","L")
329 329 pdf.SetFontStyle('',9)
330 330 pdf.RDMCell(60,5, issue.author.to_s,"R")
331 331 pdf.SetFontStyle('B',9)
332 332 pdf.RDMCell(35,5, l(:field_category) + ":","L")
333 333 pdf.SetFontStyle('',9)
334 334 pdf.RDMCell(60,5, issue.category.to_s,"R")
335 335 pdf.Ln
336 336
337 337 pdf.SetFontStyle('B',9)
338 338 pdf.RDMCell(35,5, l(:field_created_on) + ":","L")
339 339 pdf.SetFontStyle('',9)
340 340 pdf.RDMCell(60,5, format_date(issue.created_on),"R")
341 341 pdf.SetFontStyle('B',9)
342 342 pdf.RDMCell(35,5, l(:field_assigned_to) + ":","L")
343 343 pdf.SetFontStyle('',9)
344 344 pdf.RDMCell(60,5, issue.assigned_to.to_s,"R")
345 345 pdf.Ln
346 346
347 347 pdf.SetFontStyle('B',9)
348 348 pdf.RDMCell(35,5, l(:field_updated_on) + ":","LB")
349 349 pdf.SetFontStyle('',9)
350 350 pdf.RDMCell(60,5, format_date(issue.updated_on),"RB")
351 351 pdf.SetFontStyle('B',9)
352 352 pdf.RDMCell(35,5, l(:field_due_date) + ":","LB")
353 353 pdf.SetFontStyle('',9)
354 354 pdf.RDMCell(60,5, format_date(issue.due_date),"RB")
355 355 pdf.Ln
356 356
357 357 for custom_value in issue.custom_field_values
358 358 pdf.SetFontStyle('B',9)
359 359 pdf.RDMCell(35,5, custom_value.custom_field.name + ":","L")
360 360 pdf.SetFontStyle('',9)
361 361 pdf.RDMMultiCell(155,5, (show_value custom_value),"R")
362 362 end
363 363
364 364 y0 = pdf.GetY
365 365
366 366 pdf.SetFontStyle('B',9)
367 367 pdf.RDMCell(35,5, l(:field_subject) + ":","LT")
368 368 pdf.SetFontStyle('',9)
369 369 pdf.RDMMultiCell(155,5, issue.subject,"RT")
370 370 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
371 371
372 372 pdf.SetFontStyle('B',9)
373 373 pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
374 374 pdf.SetFontStyle('',9)
375 375
376 376 # Set resize image scale
377 377 pdf.SetImageScale(1.6)
378 378 pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
379 379 issue.description.to_s, issue.attachments, "LRB")
380 380
381 381 unless issue.leaf?
382 382 # for CJK
383 383 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
384 384
385 385 pdf.SetFontStyle('B',9)
386 386 pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
387 387 pdf.Ln
388 388 issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
389 389 buf = truncate("#{child.tracker} # #{child.id}: #{child.subject}",
390 390 :length => truncate_length)
391 391 level = 10 if level >= 10
392 392 pdf.SetFontStyle('',8)
393 393 pdf.RDMCell(35+135,5, (level >=1 ? " " * level : "") + buf, "L")
394 394 pdf.SetFontStyle('B',8)
395 395 pdf.RDMCell(20,5, child.status.to_s, "R")
396 396 pdf.Ln
397 397 end
398 398 end
399 399
400 400 relations = issue.relations.select { |r| r.other_issue(issue).visible? }
401 401 unless relations.empty?
402 402 # for CJK
403 403 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
404 404
405 405 pdf.SetFontStyle('B',9)
406 406 pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
407 407 pdf.Ln
408 408 relations.each do |relation|
409 409 buf = ""
410 410 buf += "#{l(relation.label_for(issue))} "
411 411 if relation.delay && relation.delay != 0
412 412 buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
413 413 end
414 414 if Setting.cross_project_issue_relations?
415 415 buf += "#{relation.other_issue(issue).project} - "
416 416 end
417 417 buf += "#{relation.other_issue(issue).tracker}" +
418 418 " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
419 419 buf = truncate(buf, :length => truncate_length)
420 420 pdf.SetFontStyle('', 8)
421 421 pdf.RDMCell(35+155-60, 5, buf, "L")
422 422 pdf.SetFontStyle('B',8)
423 423 pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, "")
424 424 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
425 425 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
426 426 pdf.Ln
427 427 end
428 428 end
429 429 pdf.RDMCell(190,5, "", "T")
430 430 pdf.Ln
431 431
432 432 if issue.changesets.any? &&
433 433 User.current.allowed_to?(:view_changesets, issue.project)
434 434 pdf.SetFontStyle('B',9)
435 435 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
436 436 pdf.Ln
437 437 for changeset in issue.changesets
438 438 pdf.SetFontStyle('B',8)
439 439 csstr = "#{l(:label_revision)} #{changeset.format_identifier} - "
440 440 csstr += format_time(changeset.committed_on) + " - " + changeset.author.to_s
441 441 pdf.RDMCell(190, 5, csstr)
442 442 pdf.Ln
443 443 unless changeset.comments.blank?
444 444 pdf.SetFontStyle('',8)
445 445 pdf.RDMwriteHTMLCell(190,5,0,0,
446 446 changeset.comments.to_s, issue.attachments, "")
447 447 end
448 448 pdf.Ln
449 449 end
450 450 end
451 451
452 452 pdf.SetFontStyle('B',9)
453 453 pdf.RDMCell(190,5, l(:label_history), "B")
454 454 pdf.Ln
455 455 indice = 0
456 456 for journal in issue.journals.find(
457 457 :all, :include => [:user, :details],
458 458 :order => "#{Journal.table_name}.created_on ASC")
459 459 indice = indice + 1
460 460 pdf.SetFontStyle('B',8)
461 461 pdf.RDMCell(190,5,
462 462 "#" + indice.to_s +
463 463 " - " + format_time(journal.created_on) +
464 464 " - " + journal.user.name)
465 465 pdf.Ln
466 466 pdf.SetFontStyle('I',8)
467 467 details_to_strings(journal.details, true).each do |string|
468 468 pdf.RDMMultiCell(190,5, "- " + string)
469 469 end
470 470 if journal.notes?
471 471 pdf.Ln unless journal.details.empty?
472 472 pdf.SetFontStyle('',8)
473 473 pdf.RDMwriteHTMLCell(190,5,0,0,
474 474 journal.notes.to_s, issue.attachments, "")
475 475 end
476 476 pdf.Ln
477 477 end
478 478
479 479 if issue.attachments.any?
480 480 pdf.SetFontStyle('B',9)
481 481 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
482 482 pdf.Ln
483 483 for attachment in issue.attachments
484 484 pdf.SetFontStyle('',8)
485 485 pdf.RDMCell(80,5, attachment.filename)
486 486 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
487 487 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
488 488 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
489 489 pdf.Ln
490 490 end
491 491 end
492 492 pdf.Output
493 493 end
494 494
495 495 # Returns a PDF string of a single wiki page
496 496 def wiki_to_pdf(page, project)
497 497 pdf = ITCPDF.new(current_language)
498 498 pdf.SetTitle("#{project} - #{page.title}")
499 499 pdf.alias_nb_pages
500 500 pdf.footer_date = format_date(Date.today)
501 501 pdf.AddPage
502 502 pdf.SetFontStyle('B',11)
503 503 pdf.RDMMultiCell(190,5,
504 504 "#{project} - #{page.title} - # #{page.content.version}")
505 505 pdf.Ln
506 506 # Set resize image scale
507 507 pdf.SetImageScale(1.6)
508 508 pdf.SetFontStyle('',9)
509 509 pdf.RDMwriteHTMLCell(190,5,0,0,
510 510 page.content.text.to_s, page.attachments, "TLRB")
511 511 if page.attachments.any?
512 512 pdf.Ln
513 513 pdf.SetFontStyle('B',9)
514 514 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
515 515 pdf.Ln
516 516 for attachment in page.attachments
517 517 pdf.SetFontStyle('',8)
518 518 pdf.RDMCell(80,5, attachment.filename)
519 519 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
520 520 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
521 521 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
522 522 pdf.Ln
523 523 end
524 524 end
525 525 pdf.Output
526 526 end
527 527
528 528 class RDMPdfEncoding
529 529 def self.rdm_from_utf8(txt, encoding)
530 530 txt ||= ''
531 531 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
532 532 if txt.respond_to?(:force_encoding)
533 533 txt.force_encoding('ASCII-8BIT')
534 534 end
535 535 txt
536 536 end
537 537
538 538 def self.attach(attachments, filename, encoding)
539 539 filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)
540 540 atta = nil
541 541 if filename_utf8 =~ /^[^\/"]+\.(gif|jpg|jpe|jpeg|png)$/i
542 542 atta = Attachment.latest_attach(attachments, filename_utf8)
543 543 end
544 544 if atta && atta.readable? && atta.visible?
545 545 return atta
546 546 else
547 547 return nil
548 548 end
549 549 end
550 550 end
551 551 end
552 552 end
553 553 end
@@ -1,2902 +1,2914
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 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 include Redmine::I18n
47 47
48 48 def setup
49 49 @controller = IssuesController.new
50 50 @request = ActionController::TestRequest.new
51 51 @response = ActionController::TestResponse.new
52 52 User.current = nil
53 53 end
54 54
55 55 def test_index
56 56 Setting.default_language = 'en'
57 57
58 58 get :index
59 59 assert_response :success
60 60 assert_template 'index'
61 61 assert_not_nil assigns(:issues)
62 62 assert_nil assigns(:project)
63 63 assert_tag :tag => 'a', :content => /Can't print recipes/
64 64 assert_tag :tag => 'a', :content => /Subproject issue/
65 65 # private projects hidden
66 66 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
67 67 assert_no_tag :tag => 'a', :content => /Issue on project 2/
68 68 # project column
69 69 assert_tag :tag => 'th', :content => /Project/
70 70 end
71 71
72 72 def test_index_should_not_list_issues_when_module_disabled
73 73 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
74 74 get :index
75 75 assert_response :success
76 76 assert_template 'index'
77 77 assert_not_nil assigns(:issues)
78 78 assert_nil assigns(:project)
79 79 assert_no_tag :tag => 'a', :content => /Can't print recipes/
80 80 assert_tag :tag => 'a', :content => /Subproject issue/
81 81 end
82 82
83 83 def test_index_should_list_visible_issues_only
84 84 get :index, :per_page => 100
85 85 assert_response :success
86 86 assert_not_nil assigns(:issues)
87 87 assert_nil assigns(:issues).detect {|issue| !issue.visible?}
88 88 end
89 89
90 90 def test_index_with_project
91 91 Setting.display_subprojects_issues = 0
92 92 get :index, :project_id => 1
93 93 assert_response :success
94 94 assert_template 'index'
95 95 assert_not_nil assigns(:issues)
96 96 assert_tag :tag => 'a', :content => /Can't print recipes/
97 97 assert_no_tag :tag => 'a', :content => /Subproject issue/
98 98 end
99 99
100 100 def test_index_with_project_and_subprojects
101 101 Setting.display_subprojects_issues = 1
102 102 get :index, :project_id => 1
103 103 assert_response :success
104 104 assert_template 'index'
105 105 assert_not_nil assigns(:issues)
106 106 assert_tag :tag => 'a', :content => /Can't print recipes/
107 107 assert_tag :tag => 'a', :content => /Subproject issue/
108 108 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
109 109 end
110 110
111 111 def test_index_with_project_and_subprojects_should_show_private_subprojects
112 112 @request.session[:user_id] = 2
113 113 Setting.display_subprojects_issues = 1
114 114 get :index, :project_id => 1
115 115 assert_response :success
116 116 assert_template 'index'
117 117 assert_not_nil assigns(:issues)
118 118 assert_tag :tag => 'a', :content => /Can't print recipes/
119 119 assert_tag :tag => 'a', :content => /Subproject issue/
120 120 assert_tag :tag => 'a', :content => /Issue of a private subproject/
121 121 end
122 122
123 123 def test_index_with_project_and_default_filter
124 124 get :index, :project_id => 1, :set_filter => 1
125 125 assert_response :success
126 126 assert_template 'index'
127 127 assert_not_nil assigns(:issues)
128 128
129 129 query = assigns(:query)
130 130 assert_not_nil query
131 131 # default filter
132 132 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
133 133 end
134 134
135 135 def test_index_with_project_and_filter
136 136 get :index, :project_id => 1, :set_filter => 1,
137 137 :f => ['tracker_id'],
138 138 :op => {'tracker_id' => '='},
139 139 :v => {'tracker_id' => ['1']}
140 140 assert_response :success
141 141 assert_template 'index'
142 142 assert_not_nil assigns(:issues)
143 143
144 144 query = assigns(:query)
145 145 assert_not_nil query
146 146 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
147 147 end
148 148
149 149 def test_index_with_short_filters
150 150
151 151 to_test = {
152 152 'status_id' => {
153 153 'o' => { :op => 'o', :values => [''] },
154 154 'c' => { :op => 'c', :values => [''] },
155 155 '7' => { :op => '=', :values => ['7'] },
156 156 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
157 157 '=7' => { :op => '=', :values => ['7'] },
158 158 '!3' => { :op => '!', :values => ['3'] },
159 159 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
160 160 'subject' => {
161 161 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
162 162 'o' => { :op => '=', :values => ['o'] },
163 163 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
164 164 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
165 165 'tracker_id' => {
166 166 '3' => { :op => '=', :values => ['3'] },
167 167 '=3' => { :op => '=', :values => ['3'] }},
168 168 'start_date' => {
169 169 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
170 170 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
171 171 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
172 172 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
173 173 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
174 174 '<t+2' => { :op => '<t+', :values => ['2'] },
175 175 '>t+2' => { :op => '>t+', :values => ['2'] },
176 176 't+2' => { :op => 't+', :values => ['2'] },
177 177 't' => { :op => 't', :values => [''] },
178 178 'w' => { :op => 'w', :values => [''] },
179 179 '>t-2' => { :op => '>t-', :values => ['2'] },
180 180 '<t-2' => { :op => '<t-', :values => ['2'] },
181 181 't-2' => { :op => 't-', :values => ['2'] }},
182 182 'created_on' => {
183 183 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
184 184 '<t+2' => { :op => '=', :values => ['<t+2'] },
185 185 '>t+2' => { :op => '=', :values => ['>t+2'] },
186 186 't+2' => { :op => 't', :values => ['+2'] }},
187 187 'cf_1' => {
188 188 'c' => { :op => '=', :values => ['c'] },
189 189 '!c' => { :op => '!', :values => ['c'] },
190 190 '!*' => { :op => '!*', :values => [''] },
191 191 '*' => { :op => '*', :values => [''] }},
192 192 'estimated_hours' => {
193 193 '=13.4' => { :op => '=', :values => ['13.4'] },
194 194 '>=45' => { :op => '>=', :values => ['45'] },
195 195 '<=125' => { :op => '<=', :values => ['125'] },
196 196 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
197 197 '!*' => { :op => '!*', :values => [''] },
198 198 '*' => { :op => '*', :values => [''] }}
199 199 }
200 200
201 201 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
202 202
203 203 to_test.each do |field, expression_and_expected|
204 204 expression_and_expected.each do |filter_expression, expected|
205 205
206 206 get :index, :set_filter => 1, field => filter_expression
207 207
208 208 assert_response :success
209 209 assert_template 'index'
210 210 assert_not_nil assigns(:issues)
211 211
212 212 query = assigns(:query)
213 213 assert_not_nil query
214 214 assert query.has_filter?(field)
215 215 assert_equal(default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}}), query.filters)
216 216 end
217 217 end
218 218
219 219 end
220 220
221 221 def test_index_with_project_and_empty_filters
222 222 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
223 223 assert_response :success
224 224 assert_template 'index'
225 225 assert_not_nil assigns(:issues)
226 226
227 227 query = assigns(:query)
228 228 assert_not_nil query
229 229 # no filter
230 230 assert_equal({}, query.filters)
231 231 end
232 232
233 233 def test_index_with_query
234 234 get :index, :project_id => 1, :query_id => 5
235 235 assert_response :success
236 236 assert_template 'index'
237 237 assert_not_nil assigns(:issues)
238 238 assert_nil assigns(:issue_count_by_group)
239 239 end
240 240
241 241 def test_index_with_query_grouped_by_tracker
242 242 get :index, :project_id => 1, :query_id => 6
243 243 assert_response :success
244 244 assert_template 'index'
245 245 assert_not_nil assigns(:issues)
246 246 assert_not_nil assigns(:issue_count_by_group)
247 247 end
248 248
249 249 def test_index_with_query_grouped_by_list_custom_field
250 250 get :index, :project_id => 1, :query_id => 9
251 251 assert_response :success
252 252 assert_template 'index'
253 253 assert_not_nil assigns(:issues)
254 254 assert_not_nil assigns(:issue_count_by_group)
255 255 end
256 256
257 257 def test_index_with_query_id_and_project_id_should_set_session_query
258 258 get :index, :project_id => 1, :query_id => 4
259 259 assert_response :success
260 260 assert_kind_of Hash, session[:query]
261 261 assert_equal 4, session[:query][:id]
262 262 assert_equal 1, session[:query][:project_id]
263 263 end
264 264
265 265 def test_index_with_cross_project_query_in_session_should_show_project_issues
266 266 q = Query.create!(:name => "test", :user_id => 2, :is_public => false, :project => nil)
267 267 @request.session[:query] = {:id => q.id, :project_id => 1}
268 268
269 269 with_settings :display_subprojects_issues => '0' do
270 270 get :index, :project_id => 1
271 271 end
272 272 assert_response :success
273 273 assert_not_nil assigns(:query)
274 274 assert_equal q.id, assigns(:query).id
275 275 assert_equal 1, assigns(:query).project_id
276 276 assert_equal [1], assigns(:issues).map(&:project_id).uniq
277 277 end
278 278
279 279 def test_private_query_should_not_be_available_to_other_users
280 280 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
281 281 @request.session[:user_id] = 3
282 282
283 283 get :index, :query_id => q.id
284 284 assert_response 403
285 285 end
286 286
287 287 def test_private_query_should_be_available_to_its_user
288 288 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
289 289 @request.session[:user_id] = 2
290 290
291 291 get :index, :query_id => q.id
292 292 assert_response :success
293 293 end
294 294
295 295 def test_public_query_should_be_available_to_other_users
296 296 q = Query.create!(:name => "private", :user => User.find(2), :is_public => true, :project => nil)
297 297 @request.session[:user_id] = 3
298 298
299 299 get :index, :query_id => q.id
300 300 assert_response :success
301 301 end
302 302
303 303 def test_index_csv
304 304 get :index, :format => 'csv'
305 305 assert_response :success
306 306 assert_not_nil assigns(:issues)
307 307 assert_equal 'text/csv', @response.content_type
308 308 assert @response.body.starts_with?("#,")
309 309 lines = @response.body.chomp.split("\n")
310 310 assert_equal assigns(:query).columns.size + 1, lines[0].split(',').size
311 311 end
312 312
313 313 def test_index_csv_with_project
314 314 get :index, :project_id => 1, :format => 'csv'
315 315 assert_response :success
316 316 assert_not_nil assigns(:issues)
317 317 assert_equal 'text/csv', @response.content_type
318 318 end
319 319
320 320 def test_index_csv_with_description
321 321 get :index, :format => 'csv', :description => '1'
322 322 assert_response :success
323 323 assert_not_nil assigns(:issues)
324 324 assert_equal 'text/csv', @response.content_type
325 325 assert @response.body.starts_with?("#,")
326 326 lines = @response.body.chomp.split("\n")
327 327 assert_equal assigns(:query).columns.size + 2, lines[0].split(',').size
328 328 end
329 329
330 330 def test_index_csv_with_all_columns
331 331 get :index, :format => 'csv', :columns => 'all'
332 332 assert_response :success
333 333 assert_not_nil assigns(:issues)
334 334 assert_equal 'text/csv', @response.content_type
335 335 assert @response.body.starts_with?("#,")
336 336 lines = @response.body.chomp.split("\n")
337 337 assert_equal assigns(:query).available_columns.size + 1, lines[0].split(',').size
338 338 end
339 339
340 def test_index_csv_with_multi_column_field
341 CustomField.find(1).update_attribute :multiple, true
342 issue = Issue.find(1)
343 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
344 issue.save!
345
346 get :index, :format => 'csv', :columns => 'all'
347 assert_response :success
348 lines = @response.body.chomp.split("\n")
349 assert lines.detect {|line| line.include?('"MySQL, Oracle"')}
350 end
351
340 352 def test_index_csv_big_5
341 353 with_settings :default_language => "zh-TW" do
342 354 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
343 355 str_big5 = "\xa4@\xa4\xeb"
344 356 if str_utf8.respond_to?(:force_encoding)
345 357 str_utf8.force_encoding('UTF-8')
346 358 str_big5.force_encoding('Big5')
347 359 end
348 360 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
349 361 :status_id => 1, :priority => IssuePriority.all.first,
350 362 :subject => str_utf8)
351 363 assert issue.save
352 364
353 365 get :index, :project_id => 1,
354 366 :f => ['subject'],
355 367 :op => '=', :values => [str_utf8],
356 368 :format => 'csv'
357 369 assert_equal 'text/csv', @response.content_type
358 370 lines = @response.body.chomp.split("\n")
359 371 s1 = "\xaa\xac\xbaA"
360 372 if str_utf8.respond_to?(:force_encoding)
361 373 s1.force_encoding('Big5')
362 374 end
363 375 assert lines[0].include?(s1)
364 376 assert lines[1].include?(str_big5)
365 377 end
366 378 end
367 379
368 380 def test_index_csv_cannot_convert_should_be_replaced_big_5
369 381 with_settings :default_language => "zh-TW" do
370 382 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
371 383 if str_utf8.respond_to?(:force_encoding)
372 384 str_utf8.force_encoding('UTF-8')
373 385 end
374 386 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
375 387 :status_id => 1, :priority => IssuePriority.all.first,
376 388 :subject => str_utf8)
377 389 assert issue.save
378 390
379 391 get :index, :project_id => 1,
380 392 :f => ['subject'],
381 393 :op => '=', :values => [str_utf8],
382 394 :c => ['status', 'subject'],
383 395 :format => 'csv',
384 396 :set_filter => 1
385 397 assert_equal 'text/csv', @response.content_type
386 398 lines = @response.body.chomp.split("\n")
387 399 s1 = "\xaa\xac\xbaA" # status
388 400 if str_utf8.respond_to?(:force_encoding)
389 401 s1.force_encoding('Big5')
390 402 end
391 403 assert lines[0].include?(s1)
392 404 s2 = lines[1].split(",")[2]
393 405 if s1.respond_to?(:force_encoding)
394 406 s3 = "\xa5H?" # subject
395 407 s3.force_encoding('Big5')
396 408 assert_equal s3, s2
397 409 elsif RUBY_PLATFORM == 'java'
398 410 assert_equal "??", s2
399 411 else
400 412 assert_equal "\xa5H???", s2
401 413 end
402 414 end
403 415 end
404 416
405 417 def test_index_csv_tw
406 418 with_settings :default_language => "zh-TW" do
407 419 str1 = "test_index_csv_tw"
408 420 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
409 421 :status_id => 1, :priority => IssuePriority.all.first,
410 422 :subject => str1, :estimated_hours => '1234.5')
411 423 assert issue.save
412 424 assert_equal 1234.5, issue.estimated_hours
413 425
414 426 get :index, :project_id => 1,
415 427 :f => ['subject'],
416 428 :op => '=', :values => [str1],
417 429 :c => ['estimated_hours', 'subject'],
418 430 :format => 'csv',
419 431 :set_filter => 1
420 432 assert_equal 'text/csv', @response.content_type
421 433 lines = @response.body.chomp.split("\n")
422 434 assert_equal "#{issue.id},1234.5,#{str1}", lines[1]
423 435
424 436 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
425 437 if str_tw.respond_to?(:force_encoding)
426 438 str_tw.force_encoding('UTF-8')
427 439 end
428 440 assert_equal str_tw, l(:general_lang_name)
429 441 assert_equal ',', l(:general_csv_separator)
430 442 assert_equal '.', l(:general_csv_decimal_separator)
431 443 end
432 444 end
433 445
434 446 def test_index_csv_fr
435 447 with_settings :default_language => "fr" do
436 448 str1 = "test_index_csv_fr"
437 449 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
438 450 :status_id => 1, :priority => IssuePriority.all.first,
439 451 :subject => str1, :estimated_hours => '1234.5')
440 452 assert issue.save
441 453 assert_equal 1234.5, issue.estimated_hours
442 454
443 455 get :index, :project_id => 1,
444 456 :f => ['subject'],
445 457 :op => '=', :values => [str1],
446 458 :c => ['estimated_hours', 'subject'],
447 459 :format => 'csv',
448 460 :set_filter => 1
449 461 assert_equal 'text/csv', @response.content_type
450 462 lines = @response.body.chomp.split("\n")
451 463 assert_equal "#{issue.id};1234,5;#{str1}", lines[1]
452 464
453 465 str_fr = "Fran\xc3\xa7ais"
454 466 if str_fr.respond_to?(:force_encoding)
455 467 str_fr.force_encoding('UTF-8')
456 468 end
457 469 assert_equal str_fr, l(:general_lang_name)
458 470 assert_equal ';', l(:general_csv_separator)
459 471 assert_equal ',', l(:general_csv_decimal_separator)
460 472 end
461 473 end
462 474
463 475 def test_index_pdf
464 476 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
465 477 with_settings :default_language => lang do
466 478
467 479 get :index
468 480 assert_response :success
469 481 assert_template 'index'
470 482
471 483 if lang == "ja"
472 484 if RUBY_PLATFORM != 'java'
473 485 assert_equal "CP932", l(:general_pdf_encoding)
474 486 end
475 487 if RUBY_PLATFORM == 'java' && l(:general_pdf_encoding) == "CP932"
476 488 next
477 489 end
478 490 end
479 491
480 492 get :index, :format => 'pdf'
481 493 assert_response :success
482 494 assert_not_nil assigns(:issues)
483 495 assert_equal 'application/pdf', @response.content_type
484 496
485 497 get :index, :project_id => 1, :format => 'pdf'
486 498 assert_response :success
487 499 assert_not_nil assigns(:issues)
488 500 assert_equal 'application/pdf', @response.content_type
489 501
490 502 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
491 503 assert_response :success
492 504 assert_not_nil assigns(:issues)
493 505 assert_equal 'application/pdf', @response.content_type
494 506 end
495 507 end
496 508 end
497 509
498 510 def test_index_pdf_with_query_grouped_by_list_custom_field
499 511 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
500 512 assert_response :success
501 513 assert_not_nil assigns(:issues)
502 514 assert_not_nil assigns(:issue_count_by_group)
503 515 assert_equal 'application/pdf', @response.content_type
504 516 end
505 517
506 518 def test_index_sort
507 519 get :index, :sort => 'tracker,id:desc'
508 520 assert_response :success
509 521
510 522 sort_params = @request.session['issues_index_sort']
511 523 assert sort_params.is_a?(String)
512 524 assert_equal 'tracker,id:desc', sort_params
513 525
514 526 issues = assigns(:issues)
515 527 assert_not_nil issues
516 528 assert !issues.empty?
517 529 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
518 530 end
519 531
520 532 def test_index_sort_by_field_not_included_in_columns
521 533 Setting.issue_list_default_columns = %w(subject author)
522 534 get :index, :sort => 'tracker'
523 535 end
524 536
525 537 def test_index_sort_by_assigned_to
526 538 get :index, :sort => 'assigned_to'
527 539 assert_response :success
528 540 assignees = assigns(:issues).collect(&:assigned_to).compact
529 541 assert_equal assignees.sort, assignees
530 542 end
531 543
532 544 def test_index_sort_by_assigned_to_desc
533 545 get :index, :sort => 'assigned_to:desc'
534 546 assert_response :success
535 547 assignees = assigns(:issues).collect(&:assigned_to).compact
536 548 assert_equal assignees.sort.reverse, assignees
537 549 end
538 550
539 551 def test_index_group_by_assigned_to
540 552 get :index, :group_by => 'assigned_to', :sort => 'priority'
541 553 assert_response :success
542 554 end
543 555
544 556 def test_index_sort_by_author
545 557 get :index, :sort => 'author'
546 558 assert_response :success
547 559 authors = assigns(:issues).collect(&:author)
548 560 assert_equal authors.sort, authors
549 561 end
550 562
551 563 def test_index_sort_by_author_desc
552 564 get :index, :sort => 'author:desc'
553 565 assert_response :success
554 566 authors = assigns(:issues).collect(&:author)
555 567 assert_equal authors.sort.reverse, authors
556 568 end
557 569
558 570 def test_index_group_by_author
559 571 get :index, :group_by => 'author', :sort => 'priority'
560 572 assert_response :success
561 573 end
562 574
563 575 def test_index_sort_by_spent_hours
564 576 get :index, :sort => 'spent_hours:desc'
565 577 assert_response :success
566 578 hours = assigns(:issues).collect(&:spent_hours)
567 579 assert_equal hours.sort.reverse, hours
568 580 end
569 581
570 582 def test_index_with_columns
571 583 columns = ['tracker', 'subject', 'assigned_to']
572 584 get :index, :set_filter => 1, :c => columns
573 585 assert_response :success
574 586
575 587 # query should use specified columns
576 588 query = assigns(:query)
577 589 assert_kind_of Query, query
578 590 assert_equal columns, query.column_names.map(&:to_s)
579 591
580 592 # columns should be stored in session
581 593 assert_kind_of Hash, session[:query]
582 594 assert_kind_of Array, session[:query][:column_names]
583 595 assert_equal columns, session[:query][:column_names].map(&:to_s)
584 596
585 597 # ensure only these columns are kept in the selected columns list
586 598 assert_tag :tag => 'select', :attributes => { :id => 'selected_columns' },
587 599 :children => { :count => 3 }
588 600 assert_no_tag :tag => 'option', :attributes => { :value => 'project' },
589 601 :parent => { :tag => 'select', :attributes => { :id => "selected_columns" } }
590 602 end
591 603
592 604 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
593 605 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
594 606 get :index, :set_filter => 1
595 607
596 608 # query should use specified columns
597 609 query = assigns(:query)
598 610 assert_kind_of Query, query
599 611 assert_equal [:project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
600 612 end
601 613
602 614 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
603 615 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
604 616 columns = ['tracker', 'subject', 'assigned_to']
605 617 get :index, :set_filter => 1, :c => columns
606 618
607 619 # query should use specified columns
608 620 query = assigns(:query)
609 621 assert_kind_of Query, query
610 622 assert_equal columns.map(&:to_sym), query.columns.map(&:name)
611 623 end
612 624
613 625 def test_index_with_custom_field_column
614 626 columns = %w(tracker subject cf_2)
615 627 get :index, :set_filter => 1, :c => columns
616 628 assert_response :success
617 629
618 630 # query should use specified columns
619 631 query = assigns(:query)
620 632 assert_kind_of Query, query
621 633 assert_equal columns, query.column_names.map(&:to_s)
622 634
623 635 assert_tag :td,
624 636 :attributes => {:class => 'cf_2 string'},
625 637 :ancestor => {:tag => 'table', :attributes => {:class => /issues/}}
626 638 end
627 639
628 640 def test_index_with_multi_custom_field_column
629 641 field = CustomField.find(1)
630 642 field.update_attribute :multiple, true
631 643 issue = Issue.find(1)
632 644 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
633 645 issue.save!
634 646
635 647 get :index, :set_filter => 1, :c => %w(tracker subject cf_1)
636 648 assert_response :success
637 649
638 650 assert_tag :td,
639 651 :attributes => {:class => /cf_1/},
640 652 :content => 'MySQL, Oracle'
641 653 end
642 654
643 655 def test_index_with_multi_user_custom_field_column
644 656 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
645 657 :tracker_ids => [1], :is_for_all => true)
646 658 issue = Issue.find(1)
647 659 issue.custom_field_values = {field.id => ['2', '3']}
648 660 issue.save!
649 661
650 662 get :index, :set_filter => 1, :c => ['tracker', 'subject', "cf_#{field.id}"]
651 663 assert_response :success
652 664
653 665 assert_tag :td,
654 666 :attributes => {:class => /cf_#{field.id}/},
655 667 :child => {:tag => 'a', :content => 'John Smith'}
656 668 end
657 669
658 670 def test_index_with_date_column
659 671 Issue.find(1).update_attribute :start_date, '1987-08-24'
660 672
661 673 with_settings :date_format => '%d/%m/%Y' do
662 674 get :index, :set_filter => 1, :c => %w(start_date)
663 675 assert_tag 'td', :attributes => {:class => /start_date/}, :content => '24/08/1987'
664 676 end
665 677 end
666 678
667 679 def test_index_with_done_ratio
668 680 Issue.find(1).update_attribute :done_ratio, 40
669 681
670 682 get :index, :set_filter => 1, :c => %w(done_ratio)
671 683 assert_tag 'td', :attributes => {:class => /done_ratio/},
672 684 :child => {:tag => 'table', :attributes => {:class => 'progress'},
673 685 :descendant => {:tag => 'td', :attributes => {:class => 'closed', :style => 'width: 40%;'}}
674 686 }
675 687 end
676 688
677 689 def test_index_with_spent_hours_column
678 690 get :index, :set_filter => 1, :c => %w(subject spent_hours)
679 691
680 692 assert_tag 'tr', :attributes => {:id => 'issue-3'},
681 693 :child => {
682 694 :tag => 'td', :attributes => {:class => /spent_hours/}, :content => '1.00'
683 695 }
684 696 end
685 697
686 698 def test_index_should_not_show_spent_hours_column_without_permission
687 699 Role.anonymous.remove_permission! :view_time_entries
688 700 get :index, :set_filter => 1, :c => %w(subject spent_hours)
689 701
690 702 assert_no_tag 'td', :attributes => {:class => /spent_hours/}
691 703 end
692 704
693 705 def test_index_with_fixed_version
694 706 get :index, :set_filter => 1, :c => %w(fixed_version)
695 707 assert_tag 'td', :attributes => {:class => /fixed_version/},
696 708 :child => {:tag => 'a', :content => '1.0', :attributes => {:href => '/versions/2'}}
697 709 end
698 710
699 711 def test_index_send_html_if_query_is_invalid
700 712 get :index, :f => ['start_date'], :op => {:start_date => '='}
701 713 assert_equal 'text/html', @response.content_type
702 714 assert_template 'index'
703 715 end
704 716
705 717 def test_index_send_nothing_if_query_is_invalid
706 718 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
707 719 assert_equal 'text/csv', @response.content_type
708 720 assert @response.body.blank?
709 721 end
710 722
711 723 def test_show_by_anonymous
712 724 get :show, :id => 1
713 725 assert_response :success
714 726 assert_template 'show'
715 727 assert_not_nil assigns(:issue)
716 728 assert_equal Issue.find(1), assigns(:issue)
717 729
718 730 # anonymous role is allowed to add a note
719 731 assert_tag :tag => 'form',
720 732 :descendant => { :tag => 'fieldset',
721 733 :child => { :tag => 'legend',
722 734 :content => /Notes/ } }
723 735 assert_tag :tag => 'title',
724 736 :content => "Bug #1: Can't print recipes - eCookbook - Redmine"
725 737 end
726 738
727 739 def test_show_by_manager
728 740 @request.session[:user_id] = 2
729 741 get :show, :id => 1
730 742 assert_response :success
731 743
732 744 assert_tag :tag => 'a',
733 745 :content => /Quote/
734 746
735 747 assert_tag :tag => 'form',
736 748 :descendant => { :tag => 'fieldset',
737 749 :child => { :tag => 'legend',
738 750 :content => /Change properties/ } },
739 751 :descendant => { :tag => 'fieldset',
740 752 :child => { :tag => 'legend',
741 753 :content => /Log time/ } },
742 754 :descendant => { :tag => 'fieldset',
743 755 :child => { :tag => 'legend',
744 756 :content => /Notes/ } }
745 757 end
746 758
747 759 def test_show_should_display_update_form
748 760 @request.session[:user_id] = 2
749 761 get :show, :id => 1
750 762 assert_response :success
751 763
752 764 assert_tag 'form', :attributes => {:id => 'issue-form'}
753 765 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
754 766 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
755 767 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
756 768 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
757 769 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
758 770 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
759 771 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
760 772 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
761 773 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
762 774 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
763 775 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
764 776 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
765 777 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
766 778 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
767 779 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
768 780 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
769 781 assert_tag 'textarea', :attributes => {:name => 'notes'}
770 782 end
771 783
772 784 def test_show_should_display_update_form_with_minimal_permissions
773 785 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
774 786 Workflow.delete_all :role_id => 1
775 787
776 788 @request.session[:user_id] = 2
777 789 get :show, :id => 1
778 790 assert_response :success
779 791
780 792 assert_tag 'form', :attributes => {:id => 'issue-form'}
781 793 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
782 794 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
783 795 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
784 796 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
785 797 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
786 798 assert_no_tag 'select', :attributes => {:name => 'issue[status_id]'}
787 799 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
788 800 assert_no_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
789 801 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
790 802 assert_no_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
791 803 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
792 804 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
793 805 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
794 806 assert_no_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
795 807 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
796 808 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
797 809 assert_tag 'textarea', :attributes => {:name => 'notes'}
798 810 end
799 811
800 812 def test_show_should_display_update_form_with_workflow_permissions
801 813 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
802 814
803 815 @request.session[:user_id] = 2
804 816 get :show, :id => 1
805 817 assert_response :success
806 818
807 819 assert_tag 'form', :attributes => {:id => 'issue-form'}
808 820 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
809 821 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
810 822 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
811 823 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
812 824 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
813 825 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
814 826 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
815 827 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
816 828 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
817 829 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
818 830 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
819 831 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
820 832 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
821 833 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
822 834 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
823 835 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
824 836 assert_tag 'textarea', :attributes => {:name => 'notes'}
825 837 end
826 838
827 839 def test_show_should_not_display_update_form_without_permissions
828 840 Role.find(1).update_attribute :permissions, [:view_issues]
829 841
830 842 @request.session[:user_id] = 2
831 843 get :show, :id => 1
832 844 assert_response :success
833 845
834 846 assert_no_tag 'form', :attributes => {:id => 'issue-form'}
835 847 end
836 848
837 849 def test_update_form_should_not_display_inactive_enumerations
838 850 @request.session[:user_id] = 2
839 851 get :show, :id => 1
840 852 assert_response :success
841 853
842 854 assert ! IssuePriority.find(15).active?
843 855 assert_no_tag :option, :attributes => {:value => '15'},
844 856 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
845 857 end
846 858
847 859 def test_update_form_should_allow_attachment_upload
848 860 @request.session[:user_id] = 2
849 861 get :show, :id => 1
850 862
851 863 assert_tag :tag => 'form',
852 864 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
853 865 :descendant => {
854 866 :tag => 'input',
855 867 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
856 868 }
857 869 end
858 870
859 871 def test_show_should_deny_anonymous_access_without_permission
860 872 Role.anonymous.remove_permission!(:view_issues)
861 873 get :show, :id => 1
862 874 assert_response :redirect
863 875 end
864 876
865 877 def test_show_should_deny_anonymous_access_to_private_issue
866 878 Issue.update_all(["is_private = ?", true], "id = 1")
867 879 get :show, :id => 1
868 880 assert_response :redirect
869 881 end
870 882
871 883 def test_show_should_deny_non_member_access_without_permission
872 884 Role.non_member.remove_permission!(:view_issues)
873 885 @request.session[:user_id] = 9
874 886 get :show, :id => 1
875 887 assert_response 403
876 888 end
877 889
878 890 def test_show_should_deny_non_member_access_to_private_issue
879 891 Issue.update_all(["is_private = ?", true], "id = 1")
880 892 @request.session[:user_id] = 9
881 893 get :show, :id => 1
882 894 assert_response 403
883 895 end
884 896
885 897 def test_show_should_deny_member_access_without_permission
886 898 Role.find(1).remove_permission!(:view_issues)
887 899 @request.session[:user_id] = 2
888 900 get :show, :id => 1
889 901 assert_response 403
890 902 end
891 903
892 904 def test_show_should_deny_member_access_to_private_issue_without_permission
893 905 Issue.update_all(["is_private = ?", true], "id = 1")
894 906 @request.session[:user_id] = 3
895 907 get :show, :id => 1
896 908 assert_response 403
897 909 end
898 910
899 911 def test_show_should_allow_author_access_to_private_issue
900 912 Issue.update_all(["is_private = ?, author_id = 3", true], "id = 1")
901 913 @request.session[:user_id] = 3
902 914 get :show, :id => 1
903 915 assert_response :success
904 916 end
905 917
906 918 def test_show_should_allow_assignee_access_to_private_issue
907 919 Issue.update_all(["is_private = ?, assigned_to_id = 3", true], "id = 1")
908 920 @request.session[:user_id] = 3
909 921 get :show, :id => 1
910 922 assert_response :success
911 923 end
912 924
913 925 def test_show_should_allow_member_access_to_private_issue_with_permission
914 926 Issue.update_all(["is_private = ?", true], "id = 1")
915 927 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
916 928 @request.session[:user_id] = 3
917 929 get :show, :id => 1
918 930 assert_response :success
919 931 end
920 932
921 933 def test_show_should_not_disclose_relations_to_invisible_issues
922 934 Setting.cross_project_issue_relations = '1'
923 935 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
924 936 # Relation to a private project issue
925 937 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
926 938
927 939 get :show, :id => 1
928 940 assert_response :success
929 941
930 942 assert_tag :div, :attributes => { :id => 'relations' },
931 943 :descendant => { :tag => 'a', :content => /#2$/ }
932 944 assert_no_tag :div, :attributes => { :id => 'relations' },
933 945 :descendant => { :tag => 'a', :content => /#4$/ }
934 946 end
935 947
936 948 def test_show_should_list_subtasks
937 949 Issue.generate!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
938 950
939 951 get :show, :id => 1
940 952 assert_response :success
941 953 assert_tag 'div', :attributes => {:id => 'issue_tree'},
942 954 :descendant => {:tag => 'td', :content => /Child Issue/, :attributes => {:class => /subject/}}
943 955 end
944 956
945 957 def test_show_should_list_parents
946 958 issue = Issue.generate!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
947 959
948 960 get :show, :id => issue.id
949 961 assert_response :success
950 962 assert_tag 'div', :attributes => {:class => 'subject'},
951 963 :descendant => {:tag => 'h3', :content => 'Child Issue'}
952 964 assert_tag 'div', :attributes => {:class => 'subject'},
953 965 :descendant => {:tag => 'a', :attributes => {:href => '/issues/1'}}
954 966 end
955 967
956 968 def test_show_should_not_display_prev_next_links_without_query_in_session
957 969 get :show, :id => 1
958 970 assert_response :success
959 971 assert_nil assigns(:prev_issue_id)
960 972 assert_nil assigns(:next_issue_id)
961 973
962 974 assert_no_tag 'div', :attributes => {:class => /next-prev-links/}
963 975 end
964 976
965 977 def test_show_should_display_prev_next_links_with_query_in_session
966 978 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
967 979 @request.session['issues_index_sort'] = 'id'
968 980
969 981 with_settings :display_subprojects_issues => '0' do
970 982 get :show, :id => 3
971 983 end
972 984
973 985 assert_response :success
974 986 # Previous and next issues for all projects
975 987 assert_equal 2, assigns(:prev_issue_id)
976 988 assert_equal 5, assigns(:next_issue_id)
977 989
978 990 assert_tag 'div', :attributes => {:class => /next-prev-links/}
979 991 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
980 992 assert_tag 'a', :attributes => {:href => '/issues/5'}, :content => /Next/
981 993
982 994 count = Issue.open.visible.count
983 995 assert_tag 'span', :attributes => {:class => 'position'}, :content => "3 of #{count}"
984 996 end
985 997
986 998 def test_show_should_display_prev_next_links_with_saved_query_in_session
987 999 query = Query.create!(:name => 'test', :is_public => true, :user_id => 1,
988 1000 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
989 1001 :sort_criteria => [['id', 'asc']])
990 1002 @request.session[:query] = {:id => query.id, :project_id => nil}
991 1003
992 1004 get :show, :id => 11
993 1005
994 1006 assert_response :success
995 1007 assert_equal query, assigns(:query)
996 1008 # Previous and next issues for all projects
997 1009 assert_equal 8, assigns(:prev_issue_id)
998 1010 assert_equal 12, assigns(:next_issue_id)
999 1011
1000 1012 assert_tag 'a', :attributes => {:href => '/issues/8'}, :content => /Previous/
1001 1013 assert_tag 'a', :attributes => {:href => '/issues/12'}, :content => /Next/
1002 1014 end
1003 1015
1004 1016 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
1005 1017 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1006 1018
1007 1019 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
1008 1020 @request.session['issues_index_sort'] = assoc_sort
1009 1021
1010 1022 get :show, :id => 3
1011 1023 assert_response :success, "Wrong response status for #{assoc_sort} sort"
1012 1024
1013 1025 assert_tag 'a', :content => /Previous/
1014 1026 assert_tag 'a', :content => /Next/
1015 1027 end
1016 1028 end
1017 1029
1018 1030 def test_show_should_display_prev_next_links_with_project_query_in_session
1019 1031 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1020 1032 @request.session['issues_index_sort'] = 'id'
1021 1033
1022 1034 with_settings :display_subprojects_issues => '0' do
1023 1035 get :show, :id => 3
1024 1036 end
1025 1037
1026 1038 assert_response :success
1027 1039 # Previous and next issues inside project
1028 1040 assert_equal 2, assigns(:prev_issue_id)
1029 1041 assert_equal 7, assigns(:next_issue_id)
1030 1042
1031 1043 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
1032 1044 assert_tag 'a', :attributes => {:href => '/issues/7'}, :content => /Next/
1033 1045 end
1034 1046
1035 1047 def test_show_should_not_display_prev_link_for_first_issue
1036 1048 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1037 1049 @request.session['issues_index_sort'] = 'id'
1038 1050
1039 1051 with_settings :display_subprojects_issues => '0' do
1040 1052 get :show, :id => 1
1041 1053 end
1042 1054
1043 1055 assert_response :success
1044 1056 assert_nil assigns(:prev_issue_id)
1045 1057 assert_equal 2, assigns(:next_issue_id)
1046 1058
1047 1059 assert_no_tag 'a', :content => /Previous/
1048 1060 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Next/
1049 1061 end
1050 1062
1051 1063 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1052 1064 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1053 1065 @request.session['issues_index_sort'] = 'id'
1054 1066
1055 1067 get :show, :id => 1
1056 1068
1057 1069 assert_response :success
1058 1070 assert_nil assigns(:prev_issue_id)
1059 1071 assert_nil assigns(:next_issue_id)
1060 1072
1061 1073 assert_no_tag 'a', :content => /Previous/
1062 1074 assert_no_tag 'a', :content => /Next/
1063 1075 end
1064 1076
1065 1077 def test_show_with_multi_custom_field
1066 1078 field = CustomField.find(1)
1067 1079 field.update_attribute :multiple, true
1068 1080 issue = Issue.find(1)
1069 1081 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1070 1082 issue.save!
1071 1083
1072 1084 get :show, :id => 1
1073 1085 assert_response :success
1074 1086
1075 1087 assert_tag :td, :content => 'MySQL, Oracle'
1076 1088 end
1077 1089
1078 1090 def test_show_with_multi_user_custom_field
1079 1091 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1080 1092 :tracker_ids => [1], :is_for_all => true)
1081 1093 issue = Issue.find(1)
1082 1094 issue.custom_field_values = {field.id => ['2', '3']}
1083 1095 issue.save!
1084 1096
1085 1097 get :show, :id => 1
1086 1098 assert_response :success
1087 1099
1088 1100 # TODO: should display links
1089 assert_tag :td, :content => 'John Smith, Dave Lopper'
1101 assert_tag :td, :content => 'Dave Lopper, John Smith'
1090 1102 end
1091 1103
1092 1104 def test_show_atom
1093 1105 get :show, :id => 2, :format => 'atom'
1094 1106 assert_response :success
1095 1107 assert_template 'journals/index'
1096 1108 # Inline image
1097 1109 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1098 1110 end
1099 1111
1100 1112 def test_show_export_to_pdf
1101 1113 get :show, :id => 3, :format => 'pdf'
1102 1114 assert_response :success
1103 1115 assert_equal 'application/pdf', @response.content_type
1104 1116 assert @response.body.starts_with?('%PDF')
1105 1117 assert_not_nil assigns(:issue)
1106 1118 end
1107 1119
1108 1120 def test_get_new
1109 1121 @request.session[:user_id] = 2
1110 1122 get :new, :project_id => 1, :tracker_id => 1
1111 1123 assert_response :success
1112 1124 assert_template 'new'
1113 1125
1114 1126 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
1115 1127 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1116 1128 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1117 1129 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1118 1130 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1119 1131 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1120 1132 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1121 1133 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1122 1134 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1123 1135 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1124 1136 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1125 1137 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1126 1138 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1127 1139 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1128 1140 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1129 1141 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1130 1142
1131 1143 # Be sure we don't display inactive IssuePriorities
1132 1144 assert ! IssuePriority.find(15).active?
1133 1145 assert_no_tag :option, :attributes => {:value => '15'},
1134 1146 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1135 1147 end
1136 1148
1137 1149 def test_get_new_with_minimal_permissions
1138 1150 Role.find(1).update_attribute :permissions, [:add_issues]
1139 1151 Workflow.delete_all :role_id => 1
1140 1152
1141 1153 @request.session[:user_id] = 2
1142 1154 get :new, :project_id => 1, :tracker_id => 1
1143 1155 assert_response :success
1144 1156 assert_template 'new'
1145 1157
1146 1158 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
1147 1159 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1148 1160 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1149 1161 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1150 1162 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1151 1163 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1152 1164 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1153 1165 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1154 1166 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1155 1167 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1156 1168 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1157 1169 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1158 1170 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1159 1171 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1160 1172 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1161 1173 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1162 1174 end
1163 1175
1164 1176 def test_get_new_with_multi_custom_field
1165 1177 field = IssueCustomField.find(1)
1166 1178 field.update_attribute :multiple, true
1167 1179
1168 1180 @request.session[:user_id] = 2
1169 1181 get :new, :project_id => 1, :tracker_id => 1
1170 1182 assert_response :success
1171 1183 assert_template 'new'
1172 1184
1173 1185 assert_tag 'select',
1174 1186 :attributes => {:name => 'issue[custom_field_values][1][]', :multiple => 'multiple'},
1175 1187 :children => {:count => 3},
1176 1188 :child => {:tag => 'option', :attributes => {:value => 'MySQL'}, :content => 'MySQL'}
1177 1189 assert_tag 'input',
1178 1190 :attributes => {:name => 'issue[custom_field_values][1][]', :value => ''}
1179 1191 end
1180 1192
1181 1193 def test_get_new_with_multi_user_custom_field
1182 1194 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1183 1195 :tracker_ids => [1], :is_for_all => true)
1184 1196
1185 1197 @request.session[:user_id] = 2
1186 1198 get :new, :project_id => 1, :tracker_id => 1
1187 1199 assert_response :success
1188 1200 assert_template 'new'
1189 1201
1190 1202 assert_tag 'select',
1191 1203 :attributes => {:name => "issue[custom_field_values][#{field.id}][]", :multiple => 'multiple'},
1192 1204 :children => {:count => Project.find(1).users.count},
1193 1205 :child => {:tag => 'option', :attributes => {:value => '2'}, :content => 'John Smith'}
1194 1206 assert_tag 'input',
1195 1207 :attributes => {:name => "issue[custom_field_values][#{field.id}][]", :value => ''}
1196 1208 end
1197 1209
1198 1210 def test_get_new_without_default_start_date_is_creation_date
1199 1211 Setting.default_issue_start_date_to_creation_date = 0
1200 1212
1201 1213 @request.session[:user_id] = 2
1202 1214 get :new, :project_id => 1, :tracker_id => 1
1203 1215 assert_response :success
1204 1216 assert_template 'new'
1205 1217
1206 1218 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1207 1219 :value => nil }
1208 1220 end
1209 1221
1210 1222 def test_get_new_with_default_start_date_is_creation_date
1211 1223 Setting.default_issue_start_date_to_creation_date = 1
1212 1224
1213 1225 @request.session[:user_id] = 2
1214 1226 get :new, :project_id => 1, :tracker_id => 1
1215 1227 assert_response :success
1216 1228 assert_template 'new'
1217 1229
1218 1230 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1219 1231 :value => Date.today.to_s }
1220 1232 end
1221 1233
1222 1234 def test_get_new_form_should_allow_attachment_upload
1223 1235 @request.session[:user_id] = 2
1224 1236 get :new, :project_id => 1, :tracker_id => 1
1225 1237
1226 1238 assert_tag :tag => 'form',
1227 1239 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
1228 1240 :descendant => {
1229 1241 :tag => 'input',
1230 1242 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
1231 1243 }
1232 1244 end
1233 1245
1234 1246 def test_get_new_without_tracker_id
1235 1247 @request.session[:user_id] = 2
1236 1248 get :new, :project_id => 1
1237 1249 assert_response :success
1238 1250 assert_template 'new'
1239 1251
1240 1252 issue = assigns(:issue)
1241 1253 assert_not_nil issue
1242 1254 assert_equal Project.find(1).trackers.first, issue.tracker
1243 1255 end
1244 1256
1245 1257 def test_get_new_with_no_default_status_should_display_an_error
1246 1258 @request.session[:user_id] = 2
1247 1259 IssueStatus.delete_all
1248 1260
1249 1261 get :new, :project_id => 1
1250 1262 assert_response 500
1251 1263 assert_error_tag :content => /No default issue/
1252 1264 end
1253 1265
1254 1266 def test_get_new_with_no_tracker_should_display_an_error
1255 1267 @request.session[:user_id] = 2
1256 1268 Tracker.delete_all
1257 1269
1258 1270 get :new, :project_id => 1
1259 1271 assert_response 500
1260 1272 assert_error_tag :content => /No tracker/
1261 1273 end
1262 1274
1263 1275 def test_update_new_form
1264 1276 @request.session[:user_id] = 2
1265 1277 xhr :post, :new, :project_id => 1,
1266 1278 :issue => {:tracker_id => 2,
1267 1279 :subject => 'This is the test_new issue',
1268 1280 :description => 'This is the description',
1269 1281 :priority_id => 5}
1270 1282 assert_response :success
1271 1283 assert_template 'attributes'
1272 1284
1273 1285 issue = assigns(:issue)
1274 1286 assert_kind_of Issue, issue
1275 1287 assert_equal 1, issue.project_id
1276 1288 assert_equal 2, issue.tracker_id
1277 1289 assert_equal 'This is the test_new issue', issue.subject
1278 1290 end
1279 1291
1280 1292 def test_post_create
1281 1293 @request.session[:user_id] = 2
1282 1294 assert_difference 'Issue.count' do
1283 1295 post :create, :project_id => 1,
1284 1296 :issue => {:tracker_id => 3,
1285 1297 :status_id => 2,
1286 1298 :subject => 'This is the test_new issue',
1287 1299 :description => 'This is the description',
1288 1300 :priority_id => 5,
1289 1301 :start_date => '2010-11-07',
1290 1302 :estimated_hours => '',
1291 1303 :custom_field_values => {'2' => 'Value for field 2'}}
1292 1304 end
1293 1305 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1294 1306
1295 1307 issue = Issue.find_by_subject('This is the test_new issue')
1296 1308 assert_not_nil issue
1297 1309 assert_equal 2, issue.author_id
1298 1310 assert_equal 3, issue.tracker_id
1299 1311 assert_equal 2, issue.status_id
1300 1312 assert_equal Date.parse('2010-11-07'), issue.start_date
1301 1313 assert_nil issue.estimated_hours
1302 1314 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
1303 1315 assert_not_nil v
1304 1316 assert_equal 'Value for field 2', v.value
1305 1317 end
1306 1318
1307 1319 def test_post_new_with_group_assignment
1308 1320 group = Group.find(11)
1309 1321 project = Project.find(1)
1310 1322 project.members << Member.new(:principal => group, :roles => [Role.first])
1311 1323
1312 1324 with_settings :issue_group_assignment => '1' do
1313 1325 @request.session[:user_id] = 2
1314 1326 assert_difference 'Issue.count' do
1315 1327 post :create, :project_id => project.id,
1316 1328 :issue => {:tracker_id => 3,
1317 1329 :status_id => 1,
1318 1330 :subject => 'This is the test_new_with_group_assignment issue',
1319 1331 :assigned_to_id => group.id}
1320 1332 end
1321 1333 end
1322 1334 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1323 1335
1324 1336 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
1325 1337 assert_not_nil issue
1326 1338 assert_equal group, issue.assigned_to
1327 1339 end
1328 1340
1329 1341 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
1330 1342 Setting.default_issue_start_date_to_creation_date = 0
1331 1343
1332 1344 @request.session[:user_id] = 2
1333 1345 assert_difference 'Issue.count' do
1334 1346 post :create, :project_id => 1,
1335 1347 :issue => {:tracker_id => 3,
1336 1348 :status_id => 2,
1337 1349 :subject => 'This is the test_new issue',
1338 1350 :description => 'This is the description',
1339 1351 :priority_id => 5,
1340 1352 :estimated_hours => '',
1341 1353 :custom_field_values => {'2' => 'Value for field 2'}}
1342 1354 end
1343 1355 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1344 1356
1345 1357 issue = Issue.find_by_subject('This is the test_new issue')
1346 1358 assert_not_nil issue
1347 1359 assert_nil issue.start_date
1348 1360 end
1349 1361
1350 1362 def test_post_create_without_start_date_and_default_start_date_is_creation_date
1351 1363 Setting.default_issue_start_date_to_creation_date = 1
1352 1364
1353 1365 @request.session[:user_id] = 2
1354 1366 assert_difference 'Issue.count' do
1355 1367 post :create, :project_id => 1,
1356 1368 :issue => {:tracker_id => 3,
1357 1369 :status_id => 2,
1358 1370 :subject => 'This is the test_new issue',
1359 1371 :description => 'This is the description',
1360 1372 :priority_id => 5,
1361 1373 :estimated_hours => '',
1362 1374 :custom_field_values => {'2' => 'Value for field 2'}}
1363 1375 end
1364 1376 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1365 1377
1366 1378 issue = Issue.find_by_subject('This is the test_new issue')
1367 1379 assert_not_nil issue
1368 1380 assert_equal Date.today, issue.start_date
1369 1381 end
1370 1382
1371 1383 def test_post_create_and_continue
1372 1384 @request.session[:user_id] = 2
1373 1385 assert_difference 'Issue.count' do
1374 1386 post :create, :project_id => 1,
1375 1387 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
1376 1388 :continue => ''
1377 1389 end
1378 1390
1379 1391 issue = Issue.first(:order => 'id DESC')
1380 1392 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
1381 1393 assert_not_nil flash[:notice], "flash was not set"
1382 1394 assert flash[:notice].include?("<a href='/issues/#{issue.id}'>##{issue.id}</a>"), "issue link not found in flash: #{flash[:notice]}"
1383 1395 end
1384 1396
1385 1397 def test_post_create_without_custom_fields_param
1386 1398 @request.session[:user_id] = 2
1387 1399 assert_difference 'Issue.count' do
1388 1400 post :create, :project_id => 1,
1389 1401 :issue => {:tracker_id => 1,
1390 1402 :subject => 'This is the test_new issue',
1391 1403 :description => 'This is the description',
1392 1404 :priority_id => 5}
1393 1405 end
1394 1406 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1395 1407 end
1396 1408
1397 1409 def test_post_create_with_multi_custom_field
1398 1410 field = IssueCustomField.find_by_name('Database')
1399 1411 field.update_attribute(:multiple, true)
1400 1412
1401 1413 @request.session[:user_id] = 2
1402 1414 assert_difference 'Issue.count' do
1403 1415 post :create, :project_id => 1,
1404 1416 :issue => {:tracker_id => 1,
1405 1417 :subject => 'This is the test_new issue',
1406 1418 :description => 'This is the description',
1407 1419 :priority_id => 5,
1408 1420 :custom_field_values => {'1' => ['', 'MySQL', 'Oracle']}}
1409 1421 end
1410 1422 assert_response 302
1411 1423 issue = Issue.first(:order => 'id DESC')
1412 1424 assert_equal ['MySQL', 'Oracle'], issue.custom_field_value(1).sort
1413 1425 end
1414 1426
1415 1427 def test_post_create_with_empty_multi_custom_field
1416 1428 field = IssueCustomField.find_by_name('Database')
1417 1429 field.update_attribute(:multiple, true)
1418 1430
1419 1431 @request.session[:user_id] = 2
1420 1432 assert_difference 'Issue.count' do
1421 1433 post :create, :project_id => 1,
1422 1434 :issue => {:tracker_id => 1,
1423 1435 :subject => 'This is the test_new issue',
1424 1436 :description => 'This is the description',
1425 1437 :priority_id => 5,
1426 1438 :custom_field_values => {'1' => ['']}}
1427 1439 end
1428 1440 assert_response 302
1429 1441 issue = Issue.first(:order => 'id DESC')
1430 1442 assert_equal [''], issue.custom_field_value(1).sort
1431 1443 end
1432 1444
1433 1445 def test_post_create_with_multi_user_custom_field
1434 1446 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1435 1447 :tracker_ids => [1], :is_for_all => true)
1436 1448
1437 1449 @request.session[:user_id] = 2
1438 1450 assert_difference 'Issue.count' do
1439 1451 post :create, :project_id => 1,
1440 1452 :issue => {:tracker_id => 1,
1441 1453 :subject => 'This is the test_new issue',
1442 1454 :description => 'This is the description',
1443 1455 :priority_id => 5,
1444 1456 :custom_field_values => {field.id.to_s => ['', '2', '3']}}
1445 1457 end
1446 1458 assert_response 302
1447 1459 issue = Issue.first(:order => 'id DESC')
1448 1460 assert_equal ['2', '3'], issue.custom_field_value(field).sort
1449 1461 end
1450 1462
1451 1463 def test_post_create_with_required_custom_field_and_without_custom_fields_param
1452 1464 field = IssueCustomField.find_by_name('Database')
1453 1465 field.update_attribute(:is_required, true)
1454 1466
1455 1467 @request.session[:user_id] = 2
1456 1468 assert_no_difference 'Issue.count' do
1457 1469 post :create, :project_id => 1,
1458 1470 :issue => {:tracker_id => 1,
1459 1471 :subject => 'This is the test_new issue',
1460 1472 :description => 'This is the description',
1461 1473 :priority_id => 5}
1462 1474 end
1463 1475 assert_response :success
1464 1476 assert_template 'new'
1465 1477 issue = assigns(:issue)
1466 1478 assert_not_nil issue
1467 1479 assert_error_tag :content => /Database can't be blank/
1468 1480 end
1469 1481
1470 1482 def test_post_create_with_watchers
1471 1483 @request.session[:user_id] = 2
1472 1484 ActionMailer::Base.deliveries.clear
1473 1485
1474 1486 assert_difference 'Watcher.count', 2 do
1475 1487 post :create, :project_id => 1,
1476 1488 :issue => {:tracker_id => 1,
1477 1489 :subject => 'This is a new issue with watchers',
1478 1490 :description => 'This is the description',
1479 1491 :priority_id => 5,
1480 1492 :watcher_user_ids => ['2', '3']}
1481 1493 end
1482 1494 issue = Issue.find_by_subject('This is a new issue with watchers')
1483 1495 assert_not_nil issue
1484 1496 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1485 1497
1486 1498 # Watchers added
1487 1499 assert_equal [2, 3], issue.watcher_user_ids.sort
1488 1500 assert issue.watched_by?(User.find(3))
1489 1501 # Watchers notified
1490 1502 mail = ActionMailer::Base.deliveries.last
1491 1503 assert_kind_of TMail::Mail, mail
1492 1504 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
1493 1505 end
1494 1506
1495 1507 def test_post_create_subissue
1496 1508 @request.session[:user_id] = 2
1497 1509
1498 1510 assert_difference 'Issue.count' do
1499 1511 post :create, :project_id => 1,
1500 1512 :issue => {:tracker_id => 1,
1501 1513 :subject => 'This is a child issue',
1502 1514 :parent_issue_id => 2}
1503 1515 end
1504 1516 issue = Issue.find_by_subject('This is a child issue')
1505 1517 assert_not_nil issue
1506 1518 assert_equal Issue.find(2), issue.parent
1507 1519 end
1508 1520
1509 1521 def test_post_create_subissue_with_non_numeric_parent_id
1510 1522 @request.session[:user_id] = 2
1511 1523
1512 1524 assert_difference 'Issue.count' do
1513 1525 post :create, :project_id => 1,
1514 1526 :issue => {:tracker_id => 1,
1515 1527 :subject => 'This is a child issue',
1516 1528 :parent_issue_id => 'ABC'}
1517 1529 end
1518 1530 issue = Issue.find_by_subject('This is a child issue')
1519 1531 assert_not_nil issue
1520 1532 assert_nil issue.parent
1521 1533 end
1522 1534
1523 1535 def test_post_create_private
1524 1536 @request.session[:user_id] = 2
1525 1537
1526 1538 assert_difference 'Issue.count' do
1527 1539 post :create, :project_id => 1,
1528 1540 :issue => {:tracker_id => 1,
1529 1541 :subject => 'This is a private issue',
1530 1542 :is_private => '1'}
1531 1543 end
1532 1544 issue = Issue.first(:order => 'id DESC')
1533 1545 assert issue.is_private?
1534 1546 end
1535 1547
1536 1548 def test_post_create_private_with_set_own_issues_private_permission
1537 1549 role = Role.find(1)
1538 1550 role.remove_permission! :set_issues_private
1539 1551 role.add_permission! :set_own_issues_private
1540 1552
1541 1553 @request.session[:user_id] = 2
1542 1554
1543 1555 assert_difference 'Issue.count' do
1544 1556 post :create, :project_id => 1,
1545 1557 :issue => {:tracker_id => 1,
1546 1558 :subject => 'This is a private issue',
1547 1559 :is_private => '1'}
1548 1560 end
1549 1561 issue = Issue.first(:order => 'id DESC')
1550 1562 assert issue.is_private?
1551 1563 end
1552 1564
1553 1565 def test_post_create_should_send_a_notification
1554 1566 ActionMailer::Base.deliveries.clear
1555 1567 @request.session[:user_id] = 2
1556 1568 assert_difference 'Issue.count' do
1557 1569 post :create, :project_id => 1,
1558 1570 :issue => {:tracker_id => 3,
1559 1571 :subject => 'This is the test_new issue',
1560 1572 :description => 'This is the description',
1561 1573 :priority_id => 5,
1562 1574 :estimated_hours => '',
1563 1575 :custom_field_values => {'2' => 'Value for field 2'}}
1564 1576 end
1565 1577 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1566 1578
1567 1579 assert_equal 1, ActionMailer::Base.deliveries.size
1568 1580 end
1569 1581
1570 1582 def test_post_create_should_preserve_fields_values_on_validation_failure
1571 1583 @request.session[:user_id] = 2
1572 1584 post :create, :project_id => 1,
1573 1585 :issue => {:tracker_id => 1,
1574 1586 # empty subject
1575 1587 :subject => '',
1576 1588 :description => 'This is a description',
1577 1589 :priority_id => 6,
1578 1590 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
1579 1591 assert_response :success
1580 1592 assert_template 'new'
1581 1593
1582 1594 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
1583 1595 :content => 'This is a description'
1584 1596 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1585 1597 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1586 1598 :value => '6' },
1587 1599 :content => 'High' }
1588 1600 # Custom fields
1589 1601 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
1590 1602 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1591 1603 :value => 'Oracle' },
1592 1604 :content => 'Oracle' }
1593 1605 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
1594 1606 :value => 'Value for field 2'}
1595 1607 end
1596 1608
1597 1609 def test_post_create_should_ignore_non_safe_attributes
1598 1610 @request.session[:user_id] = 2
1599 1611 assert_nothing_raised do
1600 1612 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
1601 1613 end
1602 1614 end
1603 1615
1604 1616 def test_post_create_with_attachment
1605 1617 set_tmp_attachments_directory
1606 1618 @request.session[:user_id] = 2
1607 1619
1608 1620 assert_difference 'Issue.count' do
1609 1621 assert_difference 'Attachment.count' do
1610 1622 post :create, :project_id => 1,
1611 1623 :issue => { :tracker_id => '1', :subject => 'With attachment' },
1612 1624 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1613 1625 end
1614 1626 end
1615 1627
1616 1628 issue = Issue.first(:order => 'id DESC')
1617 1629 attachment = Attachment.first(:order => 'id DESC')
1618 1630
1619 1631 assert_equal issue, attachment.container
1620 1632 assert_equal 2, attachment.author_id
1621 1633 assert_equal 'testfile.txt', attachment.filename
1622 1634 assert_equal 'text/plain', attachment.content_type
1623 1635 assert_equal 'test file', attachment.description
1624 1636 assert_equal 59, attachment.filesize
1625 1637 assert File.exists?(attachment.diskfile)
1626 1638 assert_equal 59, File.size(attachment.diskfile)
1627 1639 end
1628 1640
1629 1641 context "without workflow privilege" do
1630 1642 setup do
1631 1643 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1632 1644 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1633 1645 end
1634 1646
1635 1647 context "#new" do
1636 1648 should "propose default status only" do
1637 1649 get :new, :project_id => 1
1638 1650 assert_response :success
1639 1651 assert_template 'new'
1640 1652 assert_tag :tag => 'select',
1641 1653 :attributes => {:name => 'issue[status_id]'},
1642 1654 :children => {:count => 1},
1643 1655 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
1644 1656 end
1645 1657
1646 1658 should "accept default status" do
1647 1659 assert_difference 'Issue.count' do
1648 1660 post :create, :project_id => 1,
1649 1661 :issue => {:tracker_id => 1,
1650 1662 :subject => 'This is an issue',
1651 1663 :status_id => 1}
1652 1664 end
1653 1665 issue = Issue.last(:order => 'id')
1654 1666 assert_equal IssueStatus.default, issue.status
1655 1667 end
1656 1668
1657 1669 should "ignore unauthorized status" do
1658 1670 assert_difference 'Issue.count' do
1659 1671 post :create, :project_id => 1,
1660 1672 :issue => {:tracker_id => 1,
1661 1673 :subject => 'This is an issue',
1662 1674 :status_id => 3}
1663 1675 end
1664 1676 issue = Issue.last(:order => 'id')
1665 1677 assert_equal IssueStatus.default, issue.status
1666 1678 end
1667 1679 end
1668 1680
1669 1681 context "#update" do
1670 1682 should "ignore status change" do
1671 1683 assert_difference 'Journal.count' do
1672 1684 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1673 1685 end
1674 1686 assert_equal 1, Issue.find(1).status_id
1675 1687 end
1676 1688
1677 1689 should "ignore attributes changes" do
1678 1690 assert_difference 'Journal.count' do
1679 1691 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1680 1692 end
1681 1693 issue = Issue.find(1)
1682 1694 assert_equal "Can't print recipes", issue.subject
1683 1695 assert_nil issue.assigned_to
1684 1696 end
1685 1697 end
1686 1698 end
1687 1699
1688 1700 context "with workflow privilege" do
1689 1701 setup do
1690 1702 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1691 1703 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
1692 1704 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
1693 1705 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1694 1706 end
1695 1707
1696 1708 context "#update" do
1697 1709 should "accept authorized status" do
1698 1710 assert_difference 'Journal.count' do
1699 1711 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1700 1712 end
1701 1713 assert_equal 3, Issue.find(1).status_id
1702 1714 end
1703 1715
1704 1716 should "ignore unauthorized status" do
1705 1717 assert_difference 'Journal.count' do
1706 1718 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1707 1719 end
1708 1720 assert_equal 1, Issue.find(1).status_id
1709 1721 end
1710 1722
1711 1723 should "accept authorized attributes changes" do
1712 1724 assert_difference 'Journal.count' do
1713 1725 put :update, :id => 1, :notes => 'just trying', :issue => {:assigned_to_id => 2}
1714 1726 end
1715 1727 issue = Issue.find(1)
1716 1728 assert_equal 2, issue.assigned_to_id
1717 1729 end
1718 1730
1719 1731 should "ignore unauthorized attributes changes" do
1720 1732 assert_difference 'Journal.count' do
1721 1733 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed'}
1722 1734 end
1723 1735 issue = Issue.find(1)
1724 1736 assert_equal "Can't print recipes", issue.subject
1725 1737 end
1726 1738 end
1727 1739
1728 1740 context "and :edit_issues permission" do
1729 1741 setup do
1730 1742 Role.anonymous.add_permission! :add_issues, :edit_issues
1731 1743 end
1732 1744
1733 1745 should "accept authorized status" do
1734 1746 assert_difference 'Journal.count' do
1735 1747 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1736 1748 end
1737 1749 assert_equal 3, Issue.find(1).status_id
1738 1750 end
1739 1751
1740 1752 should "ignore unauthorized status" do
1741 1753 assert_difference 'Journal.count' do
1742 1754 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1743 1755 end
1744 1756 assert_equal 1, Issue.find(1).status_id
1745 1757 end
1746 1758
1747 1759 should "accept authorized attributes changes" do
1748 1760 assert_difference 'Journal.count' do
1749 1761 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1750 1762 end
1751 1763 issue = Issue.find(1)
1752 1764 assert_equal "changed", issue.subject
1753 1765 assert_equal 2, issue.assigned_to_id
1754 1766 end
1755 1767 end
1756 1768 end
1757 1769
1758 1770 def test_new_as_copy
1759 1771 @request.session[:user_id] = 2
1760 1772 get :new, :project_id => 1, :copy_from => 1
1761 1773
1762 1774 assert_response :success
1763 1775 assert_template 'new'
1764 1776
1765 1777 assert_not_nil assigns(:issue)
1766 1778 orig = Issue.find(1)
1767 1779 assert_equal 1, assigns(:issue).project_id
1768 1780 assert_equal orig.subject, assigns(:issue).subject
1769 1781 assert assigns(:issue).copy?
1770 1782
1771 1783 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1772 1784 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1773 1785 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1774 1786 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}, :content => 'eCookbook'}
1775 1787 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1776 1788 :child => {:tag => 'option', :attributes => {:value => '2', :selected => nil}, :content => 'OnlineStore'}
1777 1789 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1778 1790 end
1779 1791
1780 1792 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
1781 1793 @request.session[:user_id] = 2
1782 1794 issue = Issue.find(3)
1783 1795 assert issue.attachments.count > 0
1784 1796 get :new, :project_id => 1, :copy_from => 3
1785 1797
1786 1798 assert_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
1787 1799 end
1788 1800
1789 1801 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
1790 1802 @request.session[:user_id] = 2
1791 1803 issue = Issue.find(3)
1792 1804 issue.attachments.delete_all
1793 1805 get :new, :project_id => 1, :copy_from => 3
1794 1806
1795 1807 assert_no_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
1796 1808 end
1797 1809
1798 1810 def test_new_as_copy_with_invalid_issue_should_respond_with_404
1799 1811 @request.session[:user_id] = 2
1800 1812 get :new, :project_id => 1, :copy_from => 99999
1801 1813 assert_response 404
1802 1814 end
1803 1815
1804 1816 def test_create_as_copy_on_different_project
1805 1817 @request.session[:user_id] = 2
1806 1818 assert_difference 'Issue.count' do
1807 1819 post :create, :project_id => 1, :copy_from => 1,
1808 1820 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1809 1821
1810 1822 assert_not_nil assigns(:issue)
1811 1823 assert assigns(:issue).copy?
1812 1824 end
1813 1825 issue = Issue.first(:order => 'id DESC')
1814 1826 assert_redirected_to "/issues/#{issue.id}"
1815 1827
1816 1828 assert_equal 2, issue.project_id
1817 1829 assert_equal 3, issue.tracker_id
1818 1830 assert_equal 'Copy', issue.subject
1819 1831 end
1820 1832
1821 1833 def test_create_as_copy_should_copy_attachments
1822 1834 @request.session[:user_id] = 2
1823 1835 issue = Issue.find(3)
1824 1836 count = issue.attachments.count
1825 1837 assert count > 0
1826 1838
1827 1839 assert_difference 'Issue.count' do
1828 1840 assert_difference 'Attachment.count', count do
1829 1841 assert_no_difference 'Journal.count' do
1830 1842 post :create, :project_id => 1, :copy_from => 3,
1831 1843 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
1832 1844 :copy_attachments => '1'
1833 1845 end
1834 1846 end
1835 1847 end
1836 1848 copy = Issue.first(:order => 'id DESC')
1837 1849 assert_equal count, copy.attachments.count
1838 1850 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
1839 1851 end
1840 1852
1841 1853 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
1842 1854 @request.session[:user_id] = 2
1843 1855 issue = Issue.find(3)
1844 1856 count = issue.attachments.count
1845 1857 assert count > 0
1846 1858
1847 1859 assert_difference 'Issue.count' do
1848 1860 assert_no_difference 'Attachment.count' do
1849 1861 assert_no_difference 'Journal.count' do
1850 1862 post :create, :project_id => 1, :copy_from => 3,
1851 1863 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'}
1852 1864 end
1853 1865 end
1854 1866 end
1855 1867 copy = Issue.first(:order => 'id DESC')
1856 1868 assert_equal 0, copy.attachments.count
1857 1869 end
1858 1870
1859 1871 def test_create_as_copy_with_attachments_should_add_new_files
1860 1872 @request.session[:user_id] = 2
1861 1873 issue = Issue.find(3)
1862 1874 count = issue.attachments.count
1863 1875 assert count > 0
1864 1876
1865 1877 assert_difference 'Issue.count' do
1866 1878 assert_difference 'Attachment.count', count + 1 do
1867 1879 assert_no_difference 'Journal.count' do
1868 1880 post :create, :project_id => 1, :copy_from => 3,
1869 1881 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
1870 1882 :copy_attachments => '1',
1871 1883 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1872 1884 end
1873 1885 end
1874 1886 end
1875 1887 copy = Issue.first(:order => 'id DESC')
1876 1888 assert_equal count + 1, copy.attachments.count
1877 1889 end
1878 1890
1879 1891 def test_create_as_copy_with_failure
1880 1892 @request.session[:user_id] = 2
1881 1893 post :create, :project_id => 1, :copy_from => 1,
1882 1894 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
1883 1895
1884 1896 assert_response :success
1885 1897 assert_template 'new'
1886 1898
1887 1899 assert_not_nil assigns(:issue)
1888 1900 assert assigns(:issue).copy?
1889 1901
1890 1902 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1891 1903 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1892 1904 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1893 1905 :child => {:tag => 'option', :attributes => {:value => '1', :selected => nil}, :content => 'eCookbook'}
1894 1906 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1895 1907 :child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}, :content => 'OnlineStore'}
1896 1908 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1897 1909 end
1898 1910
1899 1911 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
1900 1912 @request.session[:user_id] = 2
1901 1913 assert !User.find(2).member_of?(Project.find(4))
1902 1914
1903 1915 assert_difference 'Issue.count' do
1904 1916 post :create, :project_id => 1, :copy_from => 1,
1905 1917 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1906 1918 end
1907 1919 issue = Issue.first(:order => 'id DESC')
1908 1920 assert_equal 1, issue.project_id
1909 1921 end
1910 1922
1911 1923 def test_get_edit
1912 1924 @request.session[:user_id] = 2
1913 1925 get :edit, :id => 1
1914 1926 assert_response :success
1915 1927 assert_template 'edit'
1916 1928 assert_not_nil assigns(:issue)
1917 1929 assert_equal Issue.find(1), assigns(:issue)
1918 1930
1919 1931 # Be sure we don't display inactive IssuePriorities
1920 1932 assert ! IssuePriority.find(15).active?
1921 1933 assert_no_tag :option, :attributes => {:value => '15'},
1922 1934 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1923 1935 end
1924 1936
1925 1937 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
1926 1938 @request.session[:user_id] = 2
1927 1939 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
1928 1940
1929 1941 get :edit, :id => 1
1930 1942 assert_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1931 1943 end
1932 1944
1933 1945 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
1934 1946 @request.session[:user_id] = 2
1935 1947 Role.find_by_name('Manager').remove_permission! :log_time
1936 1948
1937 1949 get :edit, :id => 1
1938 1950 assert_no_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1939 1951 end
1940 1952
1941 1953 def test_get_edit_with_params
1942 1954 @request.session[:user_id] = 2
1943 1955 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
1944 1956 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
1945 1957 assert_response :success
1946 1958 assert_template 'edit'
1947 1959
1948 1960 issue = assigns(:issue)
1949 1961 assert_not_nil issue
1950 1962
1951 1963 assert_equal 5, issue.status_id
1952 1964 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
1953 1965 :child => { :tag => 'option',
1954 1966 :content => 'Closed',
1955 1967 :attributes => { :selected => 'selected' } }
1956 1968
1957 1969 assert_equal 7, issue.priority_id
1958 1970 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1959 1971 :child => { :tag => 'option',
1960 1972 :content => 'Urgent',
1961 1973 :attributes => { :selected => 'selected' } }
1962 1974
1963 1975 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
1964 1976 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
1965 1977 :child => { :tag => 'option',
1966 1978 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
1967 1979 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
1968 1980 end
1969 1981
1970 1982 def test_get_edit_with_multi_custom_field
1971 1983 field = CustomField.find(1)
1972 1984 field.update_attribute :multiple, true
1973 1985 issue = Issue.find(1)
1974 1986 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1975 1987 issue.save!
1976 1988
1977 1989 @request.session[:user_id] = 2
1978 1990 get :edit, :id => 1
1979 1991 assert_response :success
1980 1992 assert_template 'edit'
1981 1993
1982 1994 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]', :multiple => 'multiple'}
1983 1995 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
1984 1996 :child => {:tag => 'option', :attributes => {:value => 'MySQL', :selected => 'selected'}}
1985 1997 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
1986 1998 :child => {:tag => 'option', :attributes => {:value => 'PostgreSQL', :selected => nil}}
1987 1999 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
1988 2000 :child => {:tag => 'option', :attributes => {:value => 'Oracle', :selected => 'selected'}}
1989 2001 end
1990 2002
1991 2003 def test_update_edit_form
1992 2004 @request.session[:user_id] = 2
1993 2005 xhr :put, :new, :project_id => 1,
1994 2006 :id => 1,
1995 2007 :issue => {:tracker_id => 2,
1996 2008 :subject => 'This is the test_new issue',
1997 2009 :description => 'This is the description',
1998 2010 :priority_id => 5}
1999 2011 assert_response :success
2000 2012 assert_template 'attributes'
2001 2013
2002 2014 issue = assigns(:issue)
2003 2015 assert_kind_of Issue, issue
2004 2016 assert_equal 1, issue.id
2005 2017 assert_equal 1, issue.project_id
2006 2018 assert_equal 2, issue.tracker_id
2007 2019 assert_equal 'This is the test_new issue', issue.subject
2008 2020 end
2009 2021
2010 2022 def test_update_edit_form_with_project_change
2011 2023 @request.session[:user_id] = 2
2012 2024 xhr :put, :new, :project_id => 1,
2013 2025 :id => 1,
2014 2026 :project_change => '1',
2015 2027 :issue => {:project_id => 2,
2016 2028 :tracker_id => 2,
2017 2029 :subject => 'This is the test_new issue',
2018 2030 :description => 'This is the description',
2019 2031 :priority_id => 5}
2020 2032 assert_response :success
2021 2033 assert_template 'form'
2022 2034
2023 2035 issue = assigns(:issue)
2024 2036 assert_kind_of Issue, issue
2025 2037 assert_equal 1, issue.id
2026 2038 assert_equal 2, issue.project_id
2027 2039 assert_equal 2, issue.tracker_id
2028 2040 assert_equal 'This is the test_new issue', issue.subject
2029 2041 end
2030 2042
2031 2043 def test_update_using_invalid_http_verbs
2032 2044 @request.session[:user_id] = 2
2033 2045 subject = 'Updated by an invalid http verb'
2034 2046
2035 2047 get :update, :id => 1, :issue => {:subject => subject}
2036 2048 assert_not_equal subject, Issue.find(1).subject
2037 2049
2038 2050 post :update, :id => 1, :issue => {:subject => subject}
2039 2051 assert_not_equal subject, Issue.find(1).subject
2040 2052
2041 2053 delete :update, :id => 1, :issue => {:subject => subject}
2042 2054 assert_not_equal subject, Issue.find(1).subject
2043 2055 end
2044 2056
2045 2057 def test_put_update_without_custom_fields_param
2046 2058 @request.session[:user_id] = 2
2047 2059 ActionMailer::Base.deliveries.clear
2048 2060
2049 2061 issue = Issue.find(1)
2050 2062 assert_equal '125', issue.custom_value_for(2).value
2051 2063 old_subject = issue.subject
2052 2064 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
2053 2065
2054 2066 assert_difference('Journal.count') do
2055 2067 assert_difference('JournalDetail.count', 2) do
2056 2068 put :update, :id => 1, :issue => {:subject => new_subject,
2057 2069 :priority_id => '6',
2058 2070 :category_id => '1' # no change
2059 2071 }
2060 2072 end
2061 2073 end
2062 2074 assert_redirected_to :action => 'show', :id => '1'
2063 2075 issue.reload
2064 2076 assert_equal new_subject, issue.subject
2065 2077 # Make sure custom fields were not cleared
2066 2078 assert_equal '125', issue.custom_value_for(2).value
2067 2079
2068 2080 mail = ActionMailer::Base.deliveries.last
2069 2081 assert_kind_of TMail::Mail, mail
2070 2082 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2071 2083 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
2072 2084 end
2073 2085
2074 2086 def test_put_update_with_project_change
2075 2087 @request.session[:user_id] = 2
2076 2088 ActionMailer::Base.deliveries.clear
2077 2089
2078 2090 assert_difference('Journal.count') do
2079 2091 assert_difference('JournalDetail.count', 3) do
2080 2092 put :update, :id => 1, :issue => {:project_id => '2',
2081 2093 :tracker_id => '1', # no change
2082 2094 :priority_id => '6',
2083 2095 :category_id => '3'
2084 2096 }
2085 2097 end
2086 2098 end
2087 2099 assert_redirected_to :action => 'show', :id => '1'
2088 2100 issue = Issue.find(1)
2089 2101 assert_equal 2, issue.project_id
2090 2102 assert_equal 1, issue.tracker_id
2091 2103 assert_equal 6, issue.priority_id
2092 2104 assert_equal 3, issue.category_id
2093 2105
2094 2106 mail = ActionMailer::Base.deliveries.last
2095 2107 assert_not_nil mail
2096 2108 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2097 2109 assert mail.body.include?("Project changed from eCookbook to OnlineStore")
2098 2110 end
2099 2111
2100 2112 def test_put_update_with_tracker_change
2101 2113 @request.session[:user_id] = 2
2102 2114 ActionMailer::Base.deliveries.clear
2103 2115
2104 2116 assert_difference('Journal.count') do
2105 2117 assert_difference('JournalDetail.count', 2) do
2106 2118 put :update, :id => 1, :issue => {:project_id => '1',
2107 2119 :tracker_id => '2',
2108 2120 :priority_id => '6'
2109 2121 }
2110 2122 end
2111 2123 end
2112 2124 assert_redirected_to :action => 'show', :id => '1'
2113 2125 issue = Issue.find(1)
2114 2126 assert_equal 1, issue.project_id
2115 2127 assert_equal 2, issue.tracker_id
2116 2128 assert_equal 6, issue.priority_id
2117 2129 assert_equal 1, issue.category_id
2118 2130
2119 2131 mail = ActionMailer::Base.deliveries.last
2120 2132 assert_not_nil mail
2121 2133 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2122 2134 assert mail.body.include?("Tracker changed from Bug to Feature request")
2123 2135 end
2124 2136
2125 2137 def test_put_update_with_custom_field_change
2126 2138 @request.session[:user_id] = 2
2127 2139 issue = Issue.find(1)
2128 2140 assert_equal '125', issue.custom_value_for(2).value
2129 2141
2130 2142 assert_difference('Journal.count') do
2131 2143 assert_difference('JournalDetail.count', 3) do
2132 2144 put :update, :id => 1, :issue => {:subject => 'Custom field change',
2133 2145 :priority_id => '6',
2134 2146 :category_id => '1', # no change
2135 2147 :custom_field_values => { '2' => 'New custom value' }
2136 2148 }
2137 2149 end
2138 2150 end
2139 2151 assert_redirected_to :action => 'show', :id => '1'
2140 2152 issue.reload
2141 2153 assert_equal 'New custom value', issue.custom_value_for(2).value
2142 2154
2143 2155 mail = ActionMailer::Base.deliveries.last
2144 2156 assert_kind_of TMail::Mail, mail
2145 2157 assert mail.body.include?("Searchable field changed from 125 to New custom value")
2146 2158 end
2147 2159
2148 2160 def test_put_update_with_multi_custom_field_change
2149 2161 field = CustomField.find(1)
2150 2162 field.update_attribute :multiple, true
2151 2163 issue = Issue.find(1)
2152 2164 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
2153 2165 issue.save!
2154 2166
2155 2167 @request.session[:user_id] = 2
2156 2168 assert_difference('Journal.count') do
2157 2169 assert_difference('JournalDetail.count', 3) do
2158 2170 put :update, :id => 1,
2159 2171 :issue => {
2160 2172 :subject => 'Custom field change',
2161 2173 :custom_field_values => { '1' => ['', 'Oracle', 'PostgreSQL'] }
2162 2174 }
2163 2175 end
2164 2176 end
2165 2177 assert_redirected_to :action => 'show', :id => '1'
2166 2178 assert_equal ['Oracle', 'PostgreSQL'], Issue.find(1).custom_field_value(1).sort
2167 2179 end
2168 2180
2169 2181 def test_put_update_with_status_and_assignee_change
2170 2182 issue = Issue.find(1)
2171 2183 assert_equal 1, issue.status_id
2172 2184 @request.session[:user_id] = 2
2173 2185 assert_difference('TimeEntry.count', 0) do
2174 2186 put :update,
2175 2187 :id => 1,
2176 2188 :issue => { :status_id => 2, :assigned_to_id => 3 },
2177 2189 :notes => 'Assigned to dlopper',
2178 2190 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
2179 2191 end
2180 2192 assert_redirected_to :action => 'show', :id => '1'
2181 2193 issue.reload
2182 2194 assert_equal 2, issue.status_id
2183 2195 j = Journal.find(:first, :order => 'id DESC')
2184 2196 assert_equal 'Assigned to dlopper', j.notes
2185 2197 assert_equal 2, j.details.size
2186 2198
2187 2199 mail = ActionMailer::Base.deliveries.last
2188 2200 assert mail.body.include?("Status changed from New to Assigned")
2189 2201 # subject should contain the new status
2190 2202 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
2191 2203 end
2192 2204
2193 2205 def test_put_update_with_note_only
2194 2206 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
2195 2207 # anonymous user
2196 2208 put :update,
2197 2209 :id => 1,
2198 2210 :notes => notes
2199 2211 assert_redirected_to :action => 'show', :id => '1'
2200 2212 j = Journal.find(:first, :order => 'id DESC')
2201 2213 assert_equal notes, j.notes
2202 2214 assert_equal 0, j.details.size
2203 2215 assert_equal User.anonymous, j.user
2204 2216
2205 2217 mail = ActionMailer::Base.deliveries.last
2206 2218 assert mail.body.include?(notes)
2207 2219 end
2208 2220
2209 2221 def test_put_update_with_note_and_spent_time
2210 2222 @request.session[:user_id] = 2
2211 2223 spent_hours_before = Issue.find(1).spent_hours
2212 2224 assert_difference('TimeEntry.count') do
2213 2225 put :update,
2214 2226 :id => 1,
2215 2227 :notes => '2.5 hours added',
2216 2228 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
2217 2229 end
2218 2230 assert_redirected_to :action => 'show', :id => '1'
2219 2231
2220 2232 issue = Issue.find(1)
2221 2233
2222 2234 j = Journal.find(:first, :order => 'id DESC')
2223 2235 assert_equal '2.5 hours added', j.notes
2224 2236 assert_equal 0, j.details.size
2225 2237
2226 2238 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
2227 2239 assert_not_nil t
2228 2240 assert_equal 2.5, t.hours
2229 2241 assert_equal spent_hours_before + 2.5, issue.spent_hours
2230 2242 end
2231 2243
2232 2244 def test_put_update_with_attachment_only
2233 2245 set_tmp_attachments_directory
2234 2246
2235 2247 # Delete all fixtured journals, a race condition can occur causing the wrong
2236 2248 # journal to get fetched in the next find.
2237 2249 Journal.delete_all
2238 2250
2239 2251 # anonymous user
2240 2252 assert_difference 'Attachment.count' do
2241 2253 put :update, :id => 1,
2242 2254 :notes => '',
2243 2255 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2244 2256 end
2245 2257
2246 2258 assert_redirected_to :action => 'show', :id => '1'
2247 2259 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
2248 2260 assert j.notes.blank?
2249 2261 assert_equal 1, j.details.size
2250 2262 assert_equal 'testfile.txt', j.details.first.value
2251 2263 assert_equal User.anonymous, j.user
2252 2264
2253 2265 attachment = Attachment.first(:order => 'id DESC')
2254 2266 assert_equal Issue.find(1), attachment.container
2255 2267 assert_equal User.anonymous, attachment.author
2256 2268 assert_equal 'testfile.txt', attachment.filename
2257 2269 assert_equal 'text/plain', attachment.content_type
2258 2270 assert_equal 'test file', attachment.description
2259 2271 assert_equal 59, attachment.filesize
2260 2272 assert File.exists?(attachment.diskfile)
2261 2273 assert_equal 59, File.size(attachment.diskfile)
2262 2274
2263 2275 mail = ActionMailer::Base.deliveries.last
2264 2276 assert mail.body.include?('testfile.txt')
2265 2277 end
2266 2278
2267 2279 def test_put_update_with_attachment_that_fails_to_save
2268 2280 set_tmp_attachments_directory
2269 2281
2270 2282 # Delete all fixtured journals, a race condition can occur causing the wrong
2271 2283 # journal to get fetched in the next find.
2272 2284 Journal.delete_all
2273 2285
2274 2286 # Mock out the unsaved attachment
2275 2287 Attachment.any_instance.stubs(:create).returns(Attachment.new)
2276 2288
2277 2289 # anonymous user
2278 2290 put :update,
2279 2291 :id => 1,
2280 2292 :notes => '',
2281 2293 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
2282 2294 assert_redirected_to :action => 'show', :id => '1'
2283 2295 assert_equal '1 file(s) could not be saved.', flash[:warning]
2284 2296
2285 2297 end if Object.const_defined?(:Mocha)
2286 2298
2287 2299 def test_put_update_with_no_change
2288 2300 issue = Issue.find(1)
2289 2301 issue.journals.clear
2290 2302 ActionMailer::Base.deliveries.clear
2291 2303
2292 2304 put :update,
2293 2305 :id => 1,
2294 2306 :notes => ''
2295 2307 assert_redirected_to :action => 'show', :id => '1'
2296 2308
2297 2309 issue.reload
2298 2310 assert issue.journals.empty?
2299 2311 # No email should be sent
2300 2312 assert ActionMailer::Base.deliveries.empty?
2301 2313 end
2302 2314
2303 2315 def test_put_update_should_send_a_notification
2304 2316 @request.session[:user_id] = 2
2305 2317 ActionMailer::Base.deliveries.clear
2306 2318 issue = Issue.find(1)
2307 2319 old_subject = issue.subject
2308 2320 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
2309 2321
2310 2322 put :update, :id => 1, :issue => {:subject => new_subject,
2311 2323 :priority_id => '6',
2312 2324 :category_id => '1' # no change
2313 2325 }
2314 2326 assert_equal 1, ActionMailer::Base.deliveries.size
2315 2327 end
2316 2328
2317 2329 def test_put_update_with_invalid_spent_time_hours_only
2318 2330 @request.session[:user_id] = 2
2319 2331 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2320 2332
2321 2333 assert_no_difference('Journal.count') do
2322 2334 put :update,
2323 2335 :id => 1,
2324 2336 :notes => notes,
2325 2337 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
2326 2338 end
2327 2339 assert_response :success
2328 2340 assert_template 'edit'
2329 2341
2330 2342 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2331 2343 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2332 2344 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
2333 2345 end
2334 2346
2335 2347 def test_put_update_with_invalid_spent_time_comments_only
2336 2348 @request.session[:user_id] = 2
2337 2349 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2338 2350
2339 2351 assert_no_difference('Journal.count') do
2340 2352 put :update,
2341 2353 :id => 1,
2342 2354 :notes => notes,
2343 2355 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
2344 2356 end
2345 2357 assert_response :success
2346 2358 assert_template 'edit'
2347 2359
2348 2360 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2349 2361 assert_error_tag :descendant => {:content => /Hours can't be blank/}
2350 2362 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2351 2363 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
2352 2364 end
2353 2365
2354 2366 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
2355 2367 issue = Issue.find(2)
2356 2368 @request.session[:user_id] = 2
2357 2369
2358 2370 put :update,
2359 2371 :id => issue.id,
2360 2372 :issue => {
2361 2373 :fixed_version_id => 4
2362 2374 }
2363 2375
2364 2376 assert_response :redirect
2365 2377 issue.reload
2366 2378 assert_equal 4, issue.fixed_version_id
2367 2379 assert_not_equal issue.project_id, issue.fixed_version.project_id
2368 2380 end
2369 2381
2370 2382 def test_put_update_should_redirect_back_using_the_back_url_parameter
2371 2383 issue = Issue.find(2)
2372 2384 @request.session[:user_id] = 2
2373 2385
2374 2386 put :update,
2375 2387 :id => issue.id,
2376 2388 :issue => {
2377 2389 :fixed_version_id => 4
2378 2390 },
2379 2391 :back_url => '/issues'
2380 2392
2381 2393 assert_response :redirect
2382 2394 assert_redirected_to '/issues'
2383 2395 end
2384 2396
2385 2397 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2386 2398 issue = Issue.find(2)
2387 2399 @request.session[:user_id] = 2
2388 2400
2389 2401 put :update,
2390 2402 :id => issue.id,
2391 2403 :issue => {
2392 2404 :fixed_version_id => 4
2393 2405 },
2394 2406 :back_url => 'http://google.com'
2395 2407
2396 2408 assert_response :redirect
2397 2409 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
2398 2410 end
2399 2411
2400 2412 def test_get_bulk_edit
2401 2413 @request.session[:user_id] = 2
2402 2414 get :bulk_edit, :ids => [1, 2]
2403 2415 assert_response :success
2404 2416 assert_template 'bulk_edit'
2405 2417
2406 2418 assert_tag :select, :attributes => {:name => 'issue[project_id]'}
2407 2419 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2408 2420
2409 2421 # Project specific custom field, date type
2410 2422 field = CustomField.find(9)
2411 2423 assert !field.is_for_all?
2412 2424 assert_equal 'date', field.field_format
2413 2425 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2414 2426
2415 2427 # System wide custom field
2416 2428 assert CustomField.find(1).is_for_all?
2417 2429 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
2418 2430
2419 2431 # Be sure we don't display inactive IssuePriorities
2420 2432 assert ! IssuePriority.find(15).active?
2421 2433 assert_no_tag :option, :attributes => {:value => '15'},
2422 2434 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
2423 2435 end
2424 2436
2425 2437 def test_get_bulk_edit_on_different_projects
2426 2438 @request.session[:user_id] = 2
2427 2439 get :bulk_edit, :ids => [1, 2, 6]
2428 2440 assert_response :success
2429 2441 assert_template 'bulk_edit'
2430 2442
2431 2443 # Can not set issues from different projects as children of an issue
2432 2444 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2433 2445
2434 2446 # Project specific custom field, date type
2435 2447 field = CustomField.find(9)
2436 2448 assert !field.is_for_all?
2437 2449 assert !field.project_ids.include?(Issue.find(6).project_id)
2438 2450 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2439 2451 end
2440 2452
2441 2453 def test_get_bulk_edit_with_user_custom_field
2442 2454 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
2443 2455
2444 2456 @request.session[:user_id] = 2
2445 2457 get :bulk_edit, :ids => [1, 2]
2446 2458 assert_response :success
2447 2459 assert_template 'bulk_edit'
2448 2460
2449 2461 assert_tag :select,
2450 2462 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2451 2463 :children => {
2452 2464 :only => {:tag => 'option'},
2453 2465 :count => Project.find(1).users.count + 1
2454 2466 }
2455 2467 end
2456 2468
2457 2469 def test_get_bulk_edit_with_version_custom_field
2458 2470 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
2459 2471
2460 2472 @request.session[:user_id] = 2
2461 2473 get :bulk_edit, :ids => [1, 2]
2462 2474 assert_response :success
2463 2475 assert_template 'bulk_edit'
2464 2476
2465 2477 assert_tag :select,
2466 2478 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2467 2479 :children => {
2468 2480 :only => {:tag => 'option'},
2469 2481 :count => Project.find(1).shared_versions.count + 1
2470 2482 }
2471 2483 end
2472 2484
2473 2485 def test_get_bulk_edit_with_multi_custom_field
2474 2486 field = CustomField.find(1)
2475 2487 field.update_attribute :multiple, true
2476 2488
2477 2489 @request.session[:user_id] = 2
2478 2490 get :bulk_edit, :ids => [1, 2]
2479 2491 assert_response :success
2480 2492 assert_template 'bulk_edit'
2481 2493
2482 2494 assert_tag :select,
2483 2495 :attributes => {:name => "issue[custom_field_values][1][]"},
2484 2496 :children => {
2485 2497 :only => {:tag => 'option'},
2486 2498 :count => 3
2487 2499 }
2488 2500 end
2489 2501
2490 2502 def test_bulk_update
2491 2503 @request.session[:user_id] = 2
2492 2504 # update issues priority
2493 2505 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2494 2506 :issue => {:priority_id => 7,
2495 2507 :assigned_to_id => '',
2496 2508 :custom_field_values => {'2' => ''}}
2497 2509
2498 2510 assert_response 302
2499 2511 # check that the issues were updated
2500 2512 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
2501 2513
2502 2514 issue = Issue.find(1)
2503 2515 journal = issue.journals.find(:first, :order => 'created_on DESC')
2504 2516 assert_equal '125', issue.custom_value_for(2).value
2505 2517 assert_equal 'Bulk editing', journal.notes
2506 2518 assert_equal 1, journal.details.size
2507 2519 end
2508 2520
2509 2521 def test_bulk_update_with_group_assignee
2510 2522 group = Group.find(11)
2511 2523 project = Project.find(1)
2512 2524 project.members << Member.new(:principal => group, :roles => [Role.first])
2513 2525
2514 2526 @request.session[:user_id] = 2
2515 2527 # update issues assignee
2516 2528 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2517 2529 :issue => {:priority_id => '',
2518 2530 :assigned_to_id => group.id,
2519 2531 :custom_field_values => {'2' => ''}}
2520 2532
2521 2533 assert_response 302
2522 2534 assert_equal [group, group], Issue.find_all_by_id([1, 2]).collect {|i| i.assigned_to}
2523 2535 end
2524 2536
2525 2537 def test_bulk_update_on_different_projects
2526 2538 @request.session[:user_id] = 2
2527 2539 # update issues priority
2528 2540 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
2529 2541 :issue => {:priority_id => 7,
2530 2542 :assigned_to_id => '',
2531 2543 :custom_field_values => {'2' => ''}}
2532 2544
2533 2545 assert_response 302
2534 2546 # check that the issues were updated
2535 2547 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
2536 2548
2537 2549 issue = Issue.find(1)
2538 2550 journal = issue.journals.find(:first, :order => 'created_on DESC')
2539 2551 assert_equal '125', issue.custom_value_for(2).value
2540 2552 assert_equal 'Bulk editing', journal.notes
2541 2553 assert_equal 1, journal.details.size
2542 2554 end
2543 2555
2544 2556 def test_bulk_update_on_different_projects_without_rights
2545 2557 @request.session[:user_id] = 3
2546 2558 user = User.find(3)
2547 2559 action = { :controller => "issues", :action => "bulk_update" }
2548 2560 assert user.allowed_to?(action, Issue.find(1).project)
2549 2561 assert ! user.allowed_to?(action, Issue.find(6).project)
2550 2562 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
2551 2563 :issue => {:priority_id => 7,
2552 2564 :assigned_to_id => '',
2553 2565 :custom_field_values => {'2' => ''}}
2554 2566 assert_response 403
2555 2567 assert_not_equal "Bulk should fail", Journal.last.notes
2556 2568 end
2557 2569
2558 2570 def test_bullk_update_should_send_a_notification
2559 2571 @request.session[:user_id] = 2
2560 2572 ActionMailer::Base.deliveries.clear
2561 2573 post(:bulk_update,
2562 2574 {
2563 2575 :ids => [1, 2],
2564 2576 :notes => 'Bulk editing',
2565 2577 :issue => {
2566 2578 :priority_id => 7,
2567 2579 :assigned_to_id => '',
2568 2580 :custom_field_values => {'2' => ''}
2569 2581 }
2570 2582 })
2571 2583
2572 2584 assert_response 302
2573 2585 assert_equal 2, ActionMailer::Base.deliveries.size
2574 2586 end
2575 2587
2576 2588 def test_bulk_update_project
2577 2589 @request.session[:user_id] = 2
2578 2590 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
2579 2591 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2580 2592 # Issues moved to project 2
2581 2593 assert_equal 2, Issue.find(1).project_id
2582 2594 assert_equal 2, Issue.find(2).project_id
2583 2595 # No tracker change
2584 2596 assert_equal 1, Issue.find(1).tracker_id
2585 2597 assert_equal 2, Issue.find(2).tracker_id
2586 2598 end
2587 2599
2588 2600 def test_bulk_update_project_on_single_issue_should_follow_when_needed
2589 2601 @request.session[:user_id] = 2
2590 2602 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
2591 2603 assert_redirected_to '/issues/1'
2592 2604 end
2593 2605
2594 2606 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
2595 2607 @request.session[:user_id] = 2
2596 2608 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
2597 2609 assert_redirected_to '/projects/onlinestore/issues'
2598 2610 end
2599 2611
2600 2612 def test_bulk_update_tracker
2601 2613 @request.session[:user_id] = 2
2602 2614 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
2603 2615 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2604 2616 assert_equal 2, Issue.find(1).tracker_id
2605 2617 assert_equal 2, Issue.find(2).tracker_id
2606 2618 end
2607 2619
2608 2620 def test_bulk_update_status
2609 2621 @request.session[:user_id] = 2
2610 2622 # update issues priority
2611 2623 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
2612 2624 :issue => {:priority_id => '',
2613 2625 :assigned_to_id => '',
2614 2626 :status_id => '5'}
2615 2627
2616 2628 assert_response 302
2617 2629 issue = Issue.find(1)
2618 2630 assert issue.closed?
2619 2631 end
2620 2632
2621 2633 def test_bulk_update_priority
2622 2634 @request.session[:user_id] = 2
2623 2635 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
2624 2636
2625 2637 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2626 2638 assert_equal 6, Issue.find(1).priority_id
2627 2639 assert_equal 6, Issue.find(2).priority_id
2628 2640 end
2629 2641
2630 2642 def test_bulk_update_with_notes
2631 2643 @request.session[:user_id] = 2
2632 2644 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
2633 2645
2634 2646 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2635 2647 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
2636 2648 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
2637 2649 end
2638 2650
2639 2651 def test_bulk_update_parent_id
2640 2652 @request.session[:user_id] = 2
2641 2653 post :bulk_update, :ids => [1, 3],
2642 2654 :notes => 'Bulk editing parent',
2643 2655 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
2644 2656
2645 2657 assert_response 302
2646 2658 parent = Issue.find(2)
2647 2659 assert_equal parent.id, Issue.find(1).parent_id
2648 2660 assert_equal parent.id, Issue.find(3).parent_id
2649 2661 assert_equal [1, 3], parent.children.collect(&:id).sort
2650 2662 end
2651 2663
2652 2664 def test_bulk_update_custom_field
2653 2665 @request.session[:user_id] = 2
2654 2666 # update issues priority
2655 2667 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
2656 2668 :issue => {:priority_id => '',
2657 2669 :assigned_to_id => '',
2658 2670 :custom_field_values => {'2' => '777'}}
2659 2671
2660 2672 assert_response 302
2661 2673
2662 2674 issue = Issue.find(1)
2663 2675 journal = issue.journals.find(:first, :order => 'created_on DESC')
2664 2676 assert_equal '777', issue.custom_value_for(2).value
2665 2677 assert_equal 1, journal.details.size
2666 2678 assert_equal '125', journal.details.first.old_value
2667 2679 assert_equal '777', journal.details.first.value
2668 2680 end
2669 2681
2670 2682 def test_bulk_update_multi_custom_field
2671 2683 field = CustomField.find(1)
2672 2684 field.update_attribute :multiple, true
2673 2685
2674 2686 @request.session[:user_id] = 2
2675 2687 post :bulk_update, :ids => [1, 2, 3], :notes => 'Bulk editing multi custom field',
2676 2688 :issue => {:priority_id => '',
2677 2689 :assigned_to_id => '',
2678 2690 :custom_field_values => {'1' => ['MySQL', 'Oracle']}}
2679 2691
2680 2692 assert_response 302
2681 2693
2682 2694 assert_equal ['MySQL', 'Oracle'], Issue.find(1).custom_field_value(1).sort
2683 2695 assert_equal ['MySQL', 'Oracle'], Issue.find(3).custom_field_value(1).sort
2684 2696 # the custom field is not associated with the issue tracker
2685 2697 assert_nil Issue.find(2).custom_field_value(1)
2686 2698 end
2687 2699
2688 2700 def test_bulk_update_unassign
2689 2701 assert_not_nil Issue.find(2).assigned_to
2690 2702 @request.session[:user_id] = 2
2691 2703 # unassign issues
2692 2704 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
2693 2705 assert_response 302
2694 2706 # check that the issues were updated
2695 2707 assert_nil Issue.find(2).assigned_to
2696 2708 end
2697 2709
2698 2710 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
2699 2711 @request.session[:user_id] = 2
2700 2712
2701 2713 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
2702 2714
2703 2715 assert_response :redirect
2704 2716 issues = Issue.find([1,2])
2705 2717 issues.each do |issue|
2706 2718 assert_equal 4, issue.fixed_version_id
2707 2719 assert_not_equal issue.project_id, issue.fixed_version.project_id
2708 2720 end
2709 2721 end
2710 2722
2711 2723 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
2712 2724 @request.session[:user_id] = 2
2713 2725 post :bulk_update, :ids => [1,2], :back_url => '/issues'
2714 2726
2715 2727 assert_response :redirect
2716 2728 assert_redirected_to '/issues'
2717 2729 end
2718 2730
2719 2731 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2720 2732 @request.session[:user_id] = 2
2721 2733 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
2722 2734
2723 2735 assert_response :redirect
2724 2736 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
2725 2737 end
2726 2738
2727 2739 def test_bulk_copy_to_another_project
2728 2740 @request.session[:user_id] = 2
2729 2741 assert_difference 'Issue.count', 2 do
2730 2742 assert_no_difference 'Project.find(1).issues.count' do
2731 2743 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
2732 2744 end
2733 2745 end
2734 2746 assert_redirected_to '/projects/ecookbook/issues'
2735 2747 end
2736 2748
2737 2749 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
2738 2750 @request.session[:user_id] = 2
2739 2751 issue_before_move = Issue.find(1)
2740 2752 assert_difference 'Issue.count', 1 do
2741 2753 assert_no_difference 'Project.find(1).issues.count' do
2742 2754 post :bulk_update, :ids => [1], :copy => '1',
2743 2755 :issue => {
2744 2756 :project_id => '2', :tracker_id => '', :assigned_to_id => '',
2745 2757 :status_id => '', :start_date => '', :due_date => ''
2746 2758 }
2747 2759 end
2748 2760 end
2749 2761 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
2750 2762 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
2751 2763 assert_equal issue_before_move.status_id, issue_after_move.status_id
2752 2764 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
2753 2765 end
2754 2766
2755 2767 def test_bulk_copy_should_allow_changing_the_issue_attributes
2756 2768 # Fixes random test failure with Mysql
2757 2769 # where Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2758 2770 # doesn't return the expected results
2759 2771 Issue.delete_all("project_id=2")
2760 2772
2761 2773 @request.session[:user_id] = 2
2762 2774 assert_difference 'Issue.count', 2 do
2763 2775 assert_no_difference 'Project.find(1).issues.count' do
2764 2776 post :bulk_update, :ids => [1, 2], :copy => '1',
2765 2777 :issue => {
2766 2778 :project_id => '2', :tracker_id => '', :assigned_to_id => '4',
2767 2779 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2768 2780 }
2769 2781 end
2770 2782 end
2771 2783
2772 2784 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2773 2785 assert_equal 2, copied_issues.size
2774 2786 copied_issues.each do |issue|
2775 2787 assert_equal 2, issue.project_id, "Project is incorrect"
2776 2788 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
2777 2789 assert_equal 3, issue.status_id, "Status is incorrect"
2778 2790 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
2779 2791 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
2780 2792 end
2781 2793 end
2782 2794
2783 2795 def test_bulk_copy_should_allow_adding_a_note
2784 2796 @request.session[:user_id] = 2
2785 2797 assert_difference 'Issue.count', 1 do
2786 2798 post :bulk_update, :ids => [1], :copy => '1',
2787 2799 :notes => 'Copying one issue',
2788 2800 :issue => {
2789 2801 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
2790 2802 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2791 2803 }
2792 2804 end
2793 2805
2794 2806 issue = Issue.first(:order => 'id DESC')
2795 2807 assert_equal 1, issue.journals.size
2796 2808 journal = issue.journals.first
2797 2809 assert_equal 0, journal.details.size
2798 2810 assert_equal 'Copying one issue', journal.notes
2799 2811 end
2800 2812
2801 2813 def test_bulk_copy_to_another_project_should_follow_when_needed
2802 2814 @request.session[:user_id] = 2
2803 2815 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
2804 2816 issue = Issue.first(:order => 'id DESC')
2805 2817 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2806 2818 end
2807 2819
2808 2820 def test_destroy_issue_with_no_time_entries
2809 2821 assert_nil TimeEntry.find_by_issue_id(2)
2810 2822 @request.session[:user_id] = 2
2811 2823
2812 2824 assert_difference 'Issue.count', -1 do
2813 2825 delete :destroy, :id => 2
2814 2826 end
2815 2827 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2816 2828 assert_nil Issue.find_by_id(2)
2817 2829 end
2818 2830
2819 2831 def test_destroy_issues_with_time_entries
2820 2832 @request.session[:user_id] = 2
2821 2833
2822 2834 assert_no_difference 'Issue.count' do
2823 2835 delete :destroy, :ids => [1, 3]
2824 2836 end
2825 2837 assert_response :success
2826 2838 assert_template 'destroy'
2827 2839 assert_not_nil assigns(:hours)
2828 2840 assert Issue.find_by_id(1) && Issue.find_by_id(3)
2829 2841 assert_tag 'form',
2830 2842 :descendant => {:tag => 'input', :attributes => {:name => '_method', :value => 'delete'}}
2831 2843 end
2832 2844
2833 2845 def test_destroy_issues_and_destroy_time_entries
2834 2846 @request.session[:user_id] = 2
2835 2847
2836 2848 assert_difference 'Issue.count', -2 do
2837 2849 assert_difference 'TimeEntry.count', -3 do
2838 2850 delete :destroy, :ids => [1, 3], :todo => 'destroy'
2839 2851 end
2840 2852 end
2841 2853 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2842 2854 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2843 2855 assert_nil TimeEntry.find_by_id([1, 2])
2844 2856 end
2845 2857
2846 2858 def test_destroy_issues_and_assign_time_entries_to_project
2847 2859 @request.session[:user_id] = 2
2848 2860
2849 2861 assert_difference 'Issue.count', -2 do
2850 2862 assert_no_difference 'TimeEntry.count' do
2851 2863 delete :destroy, :ids => [1, 3], :todo => 'nullify'
2852 2864 end
2853 2865 end
2854 2866 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2855 2867 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2856 2868 assert_nil TimeEntry.find(1).issue_id
2857 2869 assert_nil TimeEntry.find(2).issue_id
2858 2870 end
2859 2871
2860 2872 def test_destroy_issues_and_reassign_time_entries_to_another_issue
2861 2873 @request.session[:user_id] = 2
2862 2874
2863 2875 assert_difference 'Issue.count', -2 do
2864 2876 assert_no_difference 'TimeEntry.count' do
2865 2877 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
2866 2878 end
2867 2879 end
2868 2880 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2869 2881 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2870 2882 assert_equal 2, TimeEntry.find(1).issue_id
2871 2883 assert_equal 2, TimeEntry.find(2).issue_id
2872 2884 end
2873 2885
2874 2886 def test_destroy_issues_from_different_projects
2875 2887 @request.session[:user_id] = 2
2876 2888
2877 2889 assert_difference 'Issue.count', -3 do
2878 2890 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
2879 2891 end
2880 2892 assert_redirected_to :controller => 'issues', :action => 'index'
2881 2893 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
2882 2894 end
2883 2895
2884 2896 def test_destroy_parent_and_child_issues
2885 2897 parent = Issue.generate!(:project_id => 1, :tracker_id => 1)
2886 2898 child = Issue.generate!(:project_id => 1, :tracker_id => 1, :parent_issue_id => parent.id)
2887 2899 assert child.is_descendant_of?(parent.reload)
2888 2900
2889 2901 @request.session[:user_id] = 2
2890 2902 assert_difference 'Issue.count', -2 do
2891 2903 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
2892 2904 end
2893 2905 assert_response 302
2894 2906 end
2895 2907
2896 2908 def test_default_search_scope
2897 2909 get :index
2898 2910 assert_tag :div, :attributes => {:id => 'quick-search'},
2899 2911 :child => {:tag => 'form',
2900 2912 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
2901 2913 end
2902 2914 end
@@ -1,718 +1,730
1 1 # -*- coding: utf-8 -*-
2 2 # Redmine - project management software
3 3 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 4 #
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; either version 2
8 8 # of the License, or (at your option) any later version.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 18
19 19 require File.expand_path('../../test_helper', __FILE__)
20 20 require 'timelog_controller'
21 21
22 22 # Re-raise errors caught by the controller.
23 23 class TimelogController; def rescue_action(e) raise e end; end
24 24
25 25 class TimelogControllerTest < ActionController::TestCase
26 26 fixtures :projects, :enabled_modules, :roles, :members,
27 27 :member_roles, :issues, :time_entries, :users,
28 28 :trackers, :enumerations, :issue_statuses,
29 29 :custom_fields, :custom_values
30 30
31 31 include Redmine::I18n
32 32
33 33 def setup
34 34 @controller = TimelogController.new
35 35 @request = ActionController::TestRequest.new
36 36 @response = ActionController::TestResponse.new
37 37 end
38 38
39 39 def test_get_new
40 40 @request.session[:user_id] = 3
41 41 get :new, :project_id => 1
42 42 assert_response :success
43 43 assert_template 'new'
44 44 # Default activity selected
45 45 assert_tag :tag => 'option', :attributes => { :selected => 'selected' },
46 46 :content => 'Development'
47 47 end
48 48
49 49 def test_get_new_should_only_show_active_time_entry_activities
50 50 @request.session[:user_id] = 3
51 51 get :new, :project_id => 1
52 52 assert_response :success
53 53 assert_template 'new'
54 54 assert_no_tag 'select', :attributes => {:name => 'time_entry[project_id]'}
55 55 assert_no_tag 'option', :content => 'Inactive Activity'
56 56 end
57 57
58 58 def test_new_without_project
59 59 @request.session[:user_id] = 3
60 60 get :new
61 61 assert_response :success
62 62 assert_template 'new'
63 63 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'}
64 64 end
65 65
66 66 def test_new_without_project_should_deny_without_permission
67 67 Role.all.each {|role| role.remove_permission! :log_time}
68 68 @request.session[:user_id] = 3
69 69
70 70 get :new
71 71 assert_response 403
72 72 end
73 73
74 74 def test_get_edit_existing_time
75 75 @request.session[:user_id] = 2
76 76 get :edit, :id => 2, :project_id => nil
77 77 assert_response :success
78 78 assert_template 'edit'
79 79 # Default activity selected
80 80 assert_tag :tag => 'form', :attributes => { :action => '/projects/ecookbook/time_entries/2' }
81 81 end
82 82
83 83 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
84 84 te = TimeEntry.find(1)
85 85 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
86 86 te.save!
87 87
88 88 @request.session[:user_id] = 1
89 89 get :edit, :project_id => 1, :id => 1
90 90 assert_response :success
91 91 assert_template 'edit'
92 92 # Blank option since nothing is pre-selected
93 93 assert_tag :tag => 'option', :content => '--- Please select ---'
94 94 end
95 95
96 96 def test_post_create
97 97 # TODO: should POST to issues’ time log instead of project. change form
98 98 # and routing
99 99 @request.session[:user_id] = 3
100 100 post :create, :project_id => 1,
101 101 :time_entry => {:comments => 'Some work on TimelogControllerTest',
102 102 # Not the default activity
103 103 :activity_id => '11',
104 104 :spent_on => '2008-03-14',
105 105 :issue_id => '1',
106 106 :hours => '7.3'}
107 107 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
108 108
109 109 i = Issue.find(1)
110 110 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
111 111 assert_not_nil t
112 112 assert_equal 11, t.activity_id
113 113 assert_equal 7.3, t.hours
114 114 assert_equal 3, t.user_id
115 115 assert_equal i, t.issue
116 116 assert_equal i.project, t.project
117 117 end
118 118
119 119 def test_post_create_with_blank_issue
120 120 # TODO: should POST to issues’ time log instead of project. change form
121 121 # and routing
122 122 @request.session[:user_id] = 3
123 123 post :create, :project_id => 1,
124 124 :time_entry => {:comments => 'Some work on TimelogControllerTest',
125 125 # Not the default activity
126 126 :activity_id => '11',
127 127 :issue_id => '',
128 128 :spent_on => '2008-03-14',
129 129 :hours => '7.3'}
130 130 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
131 131
132 132 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
133 133 assert_not_nil t
134 134 assert_equal 11, t.activity_id
135 135 assert_equal 7.3, t.hours
136 136 assert_equal 3, t.user_id
137 137 end
138 138
139 139 def test_create_and_continue
140 140 @request.session[:user_id] = 2
141 141 post :create, :project_id => 1,
142 142 :time_entry => {:activity_id => '11',
143 143 :issue_id => '',
144 144 :spent_on => '2008-03-14',
145 145 :hours => '7.3'},
146 146 :continue => '1'
147 147 assert_redirected_to '/projects/ecookbook/time_entries/new'
148 148 end
149 149
150 150 def test_create_and_continue_with_issue_id
151 151 @request.session[:user_id] = 2
152 152 post :create, :project_id => 1,
153 153 :time_entry => {:activity_id => '11',
154 154 :issue_id => '1',
155 155 :spent_on => '2008-03-14',
156 156 :hours => '7.3'},
157 157 :continue => '1'
158 158 assert_redirected_to '/projects/ecookbook/issues/1/time_entries/new'
159 159 end
160 160
161 161 def test_create_and_continue_without_project
162 162 @request.session[:user_id] = 2
163 163 post :create, :time_entry => {:project_id => '1',
164 164 :activity_id => '11',
165 165 :issue_id => '',
166 166 :spent_on => '2008-03-14',
167 167 :hours => '7.3'},
168 168 :continue => '1'
169 169
170 170 assert_redirected_to '/time_entries/new'
171 171 end
172 172
173 173 def test_create_without_log_time_permission_should_be_denied
174 174 @request.session[:user_id] = 2
175 175 Role.find_by_name('Manager').remove_permission! :log_time
176 176 post :create, :project_id => 1,
177 177 :time_entry => {:activity_id => '11',
178 178 :issue_id => '',
179 179 :spent_on => '2008-03-14',
180 180 :hours => '7.3'}
181 181
182 182 assert_response 403
183 183 end
184 184
185 185 def test_create_with_failure
186 186 @request.session[:user_id] = 2
187 187 post :create, :project_id => 1,
188 188 :time_entry => {:activity_id => '',
189 189 :issue_id => '',
190 190 :spent_on => '2008-03-14',
191 191 :hours => '7.3'}
192 192
193 193 assert_response :success
194 194 assert_template 'new'
195 195 end
196 196
197 197 def test_create_without_project
198 198 @request.session[:user_id] = 2
199 199 assert_difference 'TimeEntry.count' do
200 200 post :create, :time_entry => {:project_id => '1',
201 201 :activity_id => '11',
202 202 :issue_id => '',
203 203 :spent_on => '2008-03-14',
204 204 :hours => '7.3'}
205 205 end
206 206
207 207 assert_redirected_to '/projects/ecookbook/time_entries'
208 208 time_entry = TimeEntry.first(:order => 'id DESC')
209 209 assert_equal 1, time_entry.project_id
210 210 end
211 211
212 212 def test_create_without_project_should_fail_with_issue_not_inside_project
213 213 @request.session[:user_id] = 2
214 214 assert_no_difference 'TimeEntry.count' do
215 215 post :create, :time_entry => {:project_id => '1',
216 216 :activity_id => '11',
217 217 :issue_id => '5',
218 218 :spent_on => '2008-03-14',
219 219 :hours => '7.3'}
220 220 end
221 221
222 222 assert_response :success
223 223 assert assigns(:time_entry).errors[:issue_id].present?
224 224 end
225 225
226 226 def test_create_without_project_should_deny_without_permission
227 227 @request.session[:user_id] = 2
228 228 Project.find(3).disable_module!(:time_tracking)
229 229
230 230 assert_no_difference 'TimeEntry.count' do
231 231 post :create, :time_entry => {:project_id => '3',
232 232 :activity_id => '11',
233 233 :issue_id => '',
234 234 :spent_on => '2008-03-14',
235 235 :hours => '7.3'}
236 236 end
237 237
238 238 assert_response 403
239 239 end
240 240
241 241 def test_create_without_project_with_failure
242 242 @request.session[:user_id] = 2
243 243 assert_no_difference 'TimeEntry.count' do
244 244 post :create, :time_entry => {:project_id => '1',
245 245 :activity_id => '11',
246 246 :issue_id => '',
247 247 :spent_on => '2008-03-14',
248 248 :hours => ''}
249 249 end
250 250
251 251 assert_response :success
252 252 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'},
253 253 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}}
254 254 end
255 255
256 256 def test_update
257 257 entry = TimeEntry.find(1)
258 258 assert_equal 1, entry.issue_id
259 259 assert_equal 2, entry.user_id
260 260
261 261 @request.session[:user_id] = 1
262 262 put :update, :id => 1,
263 263 :time_entry => {:issue_id => '2',
264 264 :hours => '8'}
265 265 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
266 266 entry.reload
267 267
268 268 assert_equal 8, entry.hours
269 269 assert_equal 2, entry.issue_id
270 270 assert_equal 2, entry.user_id
271 271 end
272 272
273 273 def test_get_bulk_edit
274 274 @request.session[:user_id] = 2
275 275 get :bulk_edit, :ids => [1, 2]
276 276 assert_response :success
277 277 assert_template 'bulk_edit'
278 278
279 279 # System wide custom field
280 280 assert_tag :select, :attributes => {:name => 'time_entry[custom_field_values][10]'}
281 281 end
282 282
283 283 def test_get_bulk_edit_on_different_projects
284 284 @request.session[:user_id] = 2
285 285 get :bulk_edit, :ids => [1, 2, 6]
286 286 assert_response :success
287 287 assert_template 'bulk_edit'
288 288 end
289 289
290 290 def test_bulk_update
291 291 @request.session[:user_id] = 2
292 292 # update time entry activity
293 293 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
294 294
295 295 assert_response 302
296 296 # check that the issues were updated
297 297 assert_equal [9, 9], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.activity_id}
298 298 end
299 299
300 300 def test_bulk_update_with_failure
301 301 @request.session[:user_id] = 2
302 302 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
303 303
304 304 assert_response 302
305 305 assert_match /Failed to save 2 time entrie/, flash[:error]
306 306 end
307 307
308 308 def test_bulk_update_on_different_projects
309 309 @request.session[:user_id] = 2
310 310 # makes user a manager on the other project
311 311 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
312 312
313 313 # update time entry activity
314 314 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
315 315
316 316 assert_response 302
317 317 # check that the issues were updated
318 318 assert_equal [9, 9, 9], TimeEntry.find_all_by_id([1, 2, 4]).collect {|i| i.activity_id}
319 319 end
320 320
321 321 def test_bulk_update_on_different_projects_without_rights
322 322 @request.session[:user_id] = 3
323 323 user = User.find(3)
324 324 action = { :controller => "timelog", :action => "bulk_update" }
325 325 assert user.allowed_to?(action, TimeEntry.find(1).project)
326 326 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
327 327 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
328 328 assert_response 403
329 329 end
330 330
331 331 def test_bulk_update_custom_field
332 332 @request.session[:user_id] = 2
333 333 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
334 334
335 335 assert_response 302
336 336 assert_equal ["0", "0"], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.custom_value_for(10).value}
337 337 end
338 338
339 339 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
340 340 @request.session[:user_id] = 2
341 341 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
342 342
343 343 assert_response :redirect
344 344 assert_redirected_to '/time_entries'
345 345 end
346 346
347 347 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
348 348 @request.session[:user_id] = 2
349 349 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
350 350
351 351 assert_response :redirect
352 352 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
353 353 end
354 354
355 355 def test_post_bulk_update_without_edit_permission_should_be_denied
356 356 @request.session[:user_id] = 2
357 357 Role.find_by_name('Manager').remove_permission! :edit_time_entries
358 358 post :bulk_update, :ids => [1,2]
359 359
360 360 assert_response 403
361 361 end
362 362
363 363 def test_destroy
364 364 @request.session[:user_id] = 2
365 365 delete :destroy, :id => 1
366 366 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
367 367 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
368 368 assert_nil TimeEntry.find_by_id(1)
369 369 end
370 370
371 371 def test_destroy_should_fail
372 372 # simulate that this fails (e.g. due to a plugin), see #5700
373 373 TimeEntry.any_instance.expects(:destroy).returns(false)
374 374
375 375 @request.session[:user_id] = 2
376 376 delete :destroy, :id => 1
377 377 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
378 378 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
379 379 assert_not_nil TimeEntry.find_by_id(1)
380 380 end
381 381
382 382 def test_index_all_projects
383 383 get :index
384 384 assert_response :success
385 385 assert_template 'index'
386 386 assert_not_nil assigns(:total_hours)
387 387 assert_equal "162.90", "%.2f" % assigns(:total_hours)
388 388 assert_tag :form,
389 389 :attributes => {:action => "/time_entries", :id => 'query_form'}
390 390 end
391 391
392 392 def test_index_all_projects_should_show_log_time_link
393 393 @request.session[:user_id] = 2
394 394 get :index
395 395 assert_response :success
396 396 assert_template 'index'
397 397 assert_tag 'a', :attributes => {:href => '/time_entries/new'}, :content => /Log time/
398 398 end
399 399
400 400 def test_index_at_project_level
401 401 get :index, :project_id => 'ecookbook'
402 402 assert_response :success
403 403 assert_template 'index'
404 404 assert_not_nil assigns(:entries)
405 405 assert_equal 4, assigns(:entries).size
406 406 # project and subproject
407 407 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
408 408 assert_not_nil assigns(:total_hours)
409 409 assert_equal "162.90", "%.2f" % assigns(:total_hours)
410 410 # display all time by default
411 411 assert_nil assigns(:from)
412 412 assert_nil assigns(:to)
413 413 assert_tag :form,
414 414 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
415 415 end
416 416
417 417 def test_index_at_project_level_with_date_range
418 418 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
419 419 assert_response :success
420 420 assert_template 'index'
421 421 assert_not_nil assigns(:entries)
422 422 assert_equal 3, assigns(:entries).size
423 423 assert_not_nil assigns(:total_hours)
424 424 assert_equal "12.90", "%.2f" % assigns(:total_hours)
425 425 assert_equal '2007-03-20'.to_date, assigns(:from)
426 426 assert_equal '2007-04-30'.to_date, assigns(:to)
427 427 assert_tag :form,
428 428 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
429 429 end
430 430
431 431 def test_index_at_project_level_with_period
432 432 get :index, :project_id => 'ecookbook', :period => '7_days'
433 433 assert_response :success
434 434 assert_template 'index'
435 435 assert_not_nil assigns(:entries)
436 436 assert_not_nil assigns(:total_hours)
437 437 assert_equal Date.today - 7, assigns(:from)
438 438 assert_equal Date.today, assigns(:to)
439 439 assert_tag :form,
440 440 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
441 441 end
442 442
443 443 def test_index_one_day
444 444 get :index, :project_id => 'ecookbook', :from => "2007-03-23", :to => "2007-03-23"
445 445 assert_response :success
446 446 assert_template 'index'
447 447 assert_not_nil assigns(:total_hours)
448 448 assert_equal "4.25", "%.2f" % assigns(:total_hours)
449 449 assert_tag :form,
450 450 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
451 451 end
452 452
453 453 def test_index_today
454 454 Date.stubs(:today).returns('2011-12-15'.to_date)
455 455 get :index, :period => 'today'
456 456 assert_equal '2011-12-15'.to_date, assigns(:from)
457 457 assert_equal '2011-12-15'.to_date, assigns(:to)
458 458 end
459 459
460 460 def test_index_yesterday
461 461 Date.stubs(:today).returns('2011-12-15'.to_date)
462 462 get :index, :period => 'yesterday'
463 463 assert_equal '2011-12-14'.to_date, assigns(:from)
464 464 assert_equal '2011-12-14'.to_date, assigns(:to)
465 465 end
466 466
467 467 def test_index_current_week
468 468 Date.stubs(:today).returns('2011-12-15'.to_date)
469 469 get :index, :period => 'current_week'
470 470 assert_equal '2011-12-12'.to_date, assigns(:from)
471 471 assert_equal '2011-12-18'.to_date, assigns(:to)
472 472 end
473 473
474 474 def test_index_last_week
475 475 Date.stubs(:today).returns('2011-12-15'.to_date)
476 476 get :index, :period => 'current_week'
477 477 assert_equal '2011-12-05'.to_date, assigns(:from)
478 478 assert_equal '2011-12-11'.to_date, assigns(:to)
479 479 end
480 480
481 481 def test_index_last_week
482 482 Date.stubs(:today).returns('2011-12-15'.to_date)
483 483 get :index, :period => 'last_week'
484 484 assert_equal '2011-12-05'.to_date, assigns(:from)
485 485 assert_equal '2011-12-11'.to_date, assigns(:to)
486 486 end
487 487
488 488 def test_index_7_days
489 489 Date.stubs(:today).returns('2011-12-15'.to_date)
490 490 get :index, :period => '7_days'
491 491 assert_equal '2011-12-08'.to_date, assigns(:from)
492 492 assert_equal '2011-12-15'.to_date, assigns(:to)
493 493 end
494 494
495 495 def test_index_current_month
496 496 Date.stubs(:today).returns('2011-12-15'.to_date)
497 497 get :index, :period => 'current_month'
498 498 assert_equal '2011-12-01'.to_date, assigns(:from)
499 499 assert_equal '2011-12-31'.to_date, assigns(:to)
500 500 end
501 501
502 502 def test_index_last_month
503 503 Date.stubs(:today).returns('2011-12-15'.to_date)
504 504 get :index, :period => 'last_month'
505 505 assert_equal '2011-11-01'.to_date, assigns(:from)
506 506 assert_equal '2011-11-30'.to_date, assigns(:to)
507 507 end
508 508
509 509 def test_index_30_days
510 510 Date.stubs(:today).returns('2011-12-15'.to_date)
511 511 get :index, :period => '30_days'
512 512 assert_equal '2011-11-15'.to_date, assigns(:from)
513 513 assert_equal '2011-12-15'.to_date, assigns(:to)
514 514 end
515 515
516 516 def test_index_current_year
517 517 Date.stubs(:today).returns('2011-12-15'.to_date)
518 518 get :index, :period => 'current_year'
519 519 assert_equal '2011-01-01'.to_date, assigns(:from)
520 520 assert_equal '2011-12-31'.to_date, assigns(:to)
521 521 end
522 522
523 523 def test_index_at_issue_level
524 524 get :index, :issue_id => 1
525 525 assert_response :success
526 526 assert_template 'index'
527 527 assert_not_nil assigns(:entries)
528 528 assert_equal 2, assigns(:entries).size
529 529 assert_not_nil assigns(:total_hours)
530 530 assert_equal 154.25, assigns(:total_hours)
531 531 # display all time
532 532 assert_nil assigns(:from)
533 533 assert_nil assigns(:to)
534 534 # TODO: remove /projects/:project_id/issues/:issue_id/time_entries routes
535 535 # to use /issues/:issue_id/time_entries
536 536 assert_tag :form,
537 537 :attributes => {:action => "/projects/ecookbook/issues/1/time_entries", :id => 'query_form'}
538 538 end
539 539
540 540 def test_index_atom_feed
541 541 get :index, :project_id => 1, :format => 'atom'
542 542 assert_response :success
543 543 assert_equal 'application/atom+xml', @response.content_type
544 544 assert_not_nil assigns(:items)
545 545 assert assigns(:items).first.is_a?(TimeEntry)
546 546 end
547 547
548 548 def test_index_all_projects_csv_export
549 549 Setting.date_format = '%m/%d/%Y'
550 550 get :index, :format => 'csv'
551 551 assert_response :success
552 552 assert_equal 'text/csv', @response.content_type
553 553 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
554 554 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
555 555 end
556 556
557 557 def test_index_csv_export
558 558 Setting.date_format = '%m/%d/%Y'
559 559 get :index, :project_id => 1, :format => 'csv'
560 560 assert_response :success
561 561 assert_equal 'text/csv', @response.content_type
562 562 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
563 563 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
564 564 end
565 565
566 def test_index_csv_export_with_multi_custom_field
567 field = TimeEntryCustomField.create!(:name => 'Test', :field_format => 'list',
568 :multiple => true, :possible_values => ['value1', 'value2'])
569 entry = TimeEntry.find(1)
570 entry.custom_field_values = {field.id => ['value1', 'value2']}
571 entry.save!
572
573 get :index, :project_id => 1, :format => 'csv'
574 assert_response :success
575 assert_include '"value1, value2"', @response.body
576 end
577
566 578 def test_csv_big_5
567 579 user = User.find_by_id(3)
568 580 user.language = "zh-TW"
569 581 assert user.save
570 582 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
571 583 str_big5 = "\xa4@\xa4\xeb"
572 584 if str_utf8.respond_to?(:force_encoding)
573 585 str_utf8.force_encoding('UTF-8')
574 586 str_big5.force_encoding('Big5')
575 587 end
576 588 @request.session[:user_id] = 3
577 589 post :create, :project_id => 1,
578 590 :time_entry => {:comments => str_utf8,
579 591 # Not the default activity
580 592 :activity_id => '11',
581 593 :issue_id => '',
582 594 :spent_on => '2011-11-10',
583 595 :hours => '7.3'}
584 596 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
585 597
586 598 t = TimeEntry.find_by_comments(str_utf8)
587 599 assert_not_nil t
588 600 assert_equal 11, t.activity_id
589 601 assert_equal 7.3, t.hours
590 602 assert_equal 3, t.user_id
591 603
592 604 get :index, :project_id => 1, :format => 'csv',
593 605 :from => '2011-11-10', :to => '2011-11-10'
594 606 assert_response :success
595 607 assert_equal 'text/csv', @response.content_type
596 608 ar = @response.body.chomp.split("\n")
597 609 s1 = "\xa4\xe9\xb4\xc1"
598 610 if str_utf8.respond_to?(:force_encoding)
599 611 s1.force_encoding('Big5')
600 612 end
601 613 assert ar[0].include?(s1)
602 614 assert ar[1].include?(str_big5)
603 615 end
604 616
605 617 def test_csv_cannot_convert_should_be_replaced_big_5
606 618 user = User.find_by_id(3)
607 619 user.language = "zh-TW"
608 620 assert user.save
609 621 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
610 622 if str_utf8.respond_to?(:force_encoding)
611 623 str_utf8.force_encoding('UTF-8')
612 624 end
613 625 @request.session[:user_id] = 3
614 626 post :create, :project_id => 1,
615 627 :time_entry => {:comments => str_utf8,
616 628 # Not the default activity
617 629 :activity_id => '11',
618 630 :issue_id => '',
619 631 :spent_on => '2011-11-10',
620 632 :hours => '7.3'}
621 633 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
622 634
623 635 t = TimeEntry.find_by_comments(str_utf8)
624 636 assert_not_nil t
625 637 assert_equal 11, t.activity_id
626 638 assert_equal 7.3, t.hours
627 639 assert_equal 3, t.user_id
628 640
629 641 get :index, :project_id => 1, :format => 'csv',
630 642 :from => '2011-11-10', :to => '2011-11-10'
631 643 assert_response :success
632 644 assert_equal 'text/csv', @response.content_type
633 645 ar = @response.body.chomp.split("\n")
634 646 s1 = "\xa4\xe9\xb4\xc1"
635 647 if str_utf8.respond_to?(:force_encoding)
636 648 s1.force_encoding('Big5')
637 649 end
638 650 assert ar[0].include?(s1)
639 651 s2 = ar[1].split(",")[8]
640 652 if s2.respond_to?(:force_encoding)
641 653 s3 = "\xa5H?"
642 654 s3.force_encoding('Big5')
643 655 assert_equal s3, s2
644 656 elsif RUBY_PLATFORM == 'java'
645 657 assert_equal "??", s2
646 658 else
647 659 assert_equal "\xa5H???", s2
648 660 end
649 661 end
650 662
651 663 def test_csv_tw
652 664 with_settings :default_language => "zh-TW" do
653 665 str1 = "test_csv_tw"
654 666 user = User.find_by_id(3)
655 667 te1 = TimeEntry.create(:spent_on => '2011-11-10',
656 668 :hours => 999.9,
657 669 :project => Project.find(1),
658 670 :user => user,
659 671 :activity => TimeEntryActivity.find_by_name('Design'),
660 672 :comments => str1)
661 673 te2 = TimeEntry.find_by_comments(str1)
662 674 assert_not_nil te2
663 675 assert_equal 999.9, te2.hours
664 676 assert_equal 3, te2.user_id
665 677
666 678 get :index, :project_id => 1, :format => 'csv',
667 679 :from => '2011-11-10', :to => '2011-11-10'
668 680 assert_response :success
669 681 assert_equal 'text/csv', @response.content_type
670 682
671 683 ar = @response.body.chomp.split("\n")
672 684 s2 = ar[1].split(",")[7]
673 685 assert_equal '999.9', s2
674 686
675 687 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
676 688 if str_tw.respond_to?(:force_encoding)
677 689 str_tw.force_encoding('UTF-8')
678 690 end
679 691 assert_equal str_tw, l(:general_lang_name)
680 692 assert_equal ',', l(:general_csv_separator)
681 693 assert_equal '.', l(:general_csv_decimal_separator)
682 694 end
683 695 end
684 696
685 697 def test_csv_fr
686 698 with_settings :default_language => "fr" do
687 699 str1 = "test_csv_fr"
688 700 user = User.find_by_id(3)
689 701 te1 = TimeEntry.create(:spent_on => '2011-11-10',
690 702 :hours => 999.9,
691 703 :project => Project.find(1),
692 704 :user => user,
693 705 :activity => TimeEntryActivity.find_by_name('Design'),
694 706 :comments => str1)
695 707 te2 = TimeEntry.find_by_comments(str1)
696 708 assert_not_nil te2
697 709 assert_equal 999.9, te2.hours
698 710 assert_equal 3, te2.user_id
699 711
700 712 get :index, :project_id => 1, :format => 'csv',
701 713 :from => '2011-11-10', :to => '2011-11-10'
702 714 assert_response :success
703 715 assert_equal 'text/csv', @response.content_type
704 716
705 717 ar = @response.body.chomp.split("\n")
706 718 s2 = ar[1].split(";")[7]
707 719 assert_equal '999,9', s2
708 720
709 721 str_fr = "Fran\xc3\xa7ais"
710 722 if str_fr.respond_to?(:force_encoding)
711 723 str_fr.force_encoding('UTF-8')
712 724 end
713 725 assert_equal str_fr, l(:general_lang_name)
714 726 assert_equal ';', l(:general_csv_separator)
715 727 assert_equal ',', l(:general_csv_decimal_separator)
716 728 end
717 729 end
718 730 end
General Comments 0
You need to be logged in to leave comments. Login now