##// END OF EJS Templates
Malformed SQL query with SQLServer when grouping and sorting by fixed version (#22808)....
Jean-Philippe Lang -
r15034:14beafcc24af
parent child
Show More
@@ -1,1041 +1,1041
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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 class QueryColumn
19 19 attr_accessor :name, :sortable, :groupable, :totalable, :default_order
20 20 include Redmine::I18n
21 21
22 22 def initialize(name, options={})
23 23 self.name = name
24 24 self.sortable = options[:sortable]
25 25 self.groupable = options[:groupable] || false
26 26 if groupable == true
27 27 self.groupable = name.to_s
28 28 end
29 29 self.totalable = options[:totalable] || false
30 30 self.default_order = options[:default_order]
31 31 @inline = options.key?(:inline) ? options[:inline] : true
32 32 @caption_key = options[:caption] || "field_#{name}".to_sym
33 33 @frozen = options[:frozen]
34 34 end
35 35
36 36 def caption
37 37 case @caption_key
38 38 when Symbol
39 39 l(@caption_key)
40 40 when Proc
41 41 @caption_key.call
42 42 else
43 43 @caption_key
44 44 end
45 45 end
46 46
47 47 # Returns true if the column is sortable, otherwise false
48 48 def sortable?
49 49 !@sortable.nil?
50 50 end
51 51
52 52 def sortable
53 53 @sortable.is_a?(Proc) ? @sortable.call : @sortable
54 54 end
55 55
56 56 def inline?
57 57 @inline
58 58 end
59 59
60 60 def frozen?
61 61 @frozen
62 62 end
63 63
64 64 def value(object)
65 65 object.send name
66 66 end
67 67
68 68 def value_object(object)
69 69 object.send name
70 70 end
71 71
72 72 def css_classes
73 73 name
74 74 end
75 75 end
76 76
77 77 class QueryCustomFieldColumn < QueryColumn
78 78
79 79 def initialize(custom_field)
80 80 self.name = "cf_#{custom_field.id}".to_sym
81 81 self.sortable = custom_field.order_statement || false
82 82 self.groupable = custom_field.group_statement || false
83 83 self.totalable = custom_field.totalable?
84 84 @inline = true
85 85 @cf = custom_field
86 86 end
87 87
88 88 def caption
89 89 @cf.name
90 90 end
91 91
92 92 def custom_field
93 93 @cf
94 94 end
95 95
96 96 def value_object(object)
97 97 if custom_field.visible_by?(object.project, User.current)
98 98 cv = object.custom_values.select {|v| v.custom_field_id == @cf.id}
99 99 cv.size > 1 ? cv.sort {|a,b| a.value.to_s <=> b.value.to_s} : cv.first
100 100 else
101 101 nil
102 102 end
103 103 end
104 104
105 105 def value(object)
106 106 raw = value_object(object)
107 107 if raw.is_a?(Array)
108 108 raw.map {|r| @cf.cast_value(r.value)}
109 109 elsif raw
110 110 @cf.cast_value(raw.value)
111 111 else
112 112 nil
113 113 end
114 114 end
115 115
116 116 def css_classes
117 117 @css_classes ||= "#{name} #{@cf.field_format}"
118 118 end
119 119 end
120 120
121 121 class QueryAssociationCustomFieldColumn < QueryCustomFieldColumn
122 122
123 123 def initialize(association, custom_field)
124 124 super(custom_field)
125 125 self.name = "#{association}.cf_#{custom_field.id}".to_sym
126 126 # TODO: support sorting/grouping by association custom field
127 127 self.sortable = false
128 128 self.groupable = false
129 129 @association = association
130 130 end
131 131
132 132 def value_object(object)
133 133 if assoc = object.send(@association)
134 134 super(assoc)
135 135 end
136 136 end
137 137
138 138 def css_classes
139 139 @css_classes ||= "#{@association}_cf_#{@cf.id} #{@cf.field_format}"
140 140 end
141 141 end
142 142
143 143 class Query < ActiveRecord::Base
144 144 class StatementInvalid < ::ActiveRecord::StatementInvalid
145 145 end
146 146
147 147 VISIBILITY_PRIVATE = 0
148 148 VISIBILITY_ROLES = 1
149 149 VISIBILITY_PUBLIC = 2
150 150
151 151 belongs_to :project
152 152 belongs_to :user
153 153 has_and_belongs_to_many :roles, :join_table => "#{table_name_prefix}queries_roles#{table_name_suffix}", :foreign_key => "query_id"
154 154 serialize :filters
155 155 serialize :column_names
156 156 serialize :sort_criteria, Array
157 157 serialize :options, Hash
158 158
159 159 attr_protected :project_id, :user_id
160 160
161 161 validates_presence_of :name
162 162 validates_length_of :name, :maximum => 255
163 163 validates :visibility, :inclusion => { :in => [VISIBILITY_PUBLIC, VISIBILITY_ROLES, VISIBILITY_PRIVATE] }
164 164 validate :validate_query_filters
165 165 validate do |query|
166 166 errors.add(:base, l(:label_role_plural) + ' ' + l('activerecord.errors.messages.blank')) if query.visibility == VISIBILITY_ROLES && roles.blank?
167 167 end
168 168
169 169 after_save do |query|
170 170 if query.visibility_changed? && query.visibility != VISIBILITY_ROLES
171 171 query.roles.clear
172 172 end
173 173 end
174 174
175 175 class_attribute :operators
176 176 self.operators = {
177 177 "=" => :label_equals,
178 178 "!" => :label_not_equals,
179 179 "o" => :label_open_issues,
180 180 "c" => :label_closed_issues,
181 181 "!*" => :label_none,
182 182 "*" => :label_any,
183 183 ">=" => :label_greater_or_equal,
184 184 "<=" => :label_less_or_equal,
185 185 "><" => :label_between,
186 186 "<t+" => :label_in_less_than,
187 187 ">t+" => :label_in_more_than,
188 188 "><t+"=> :label_in_the_next_days,
189 189 "t+" => :label_in,
190 190 "t" => :label_today,
191 191 "ld" => :label_yesterday,
192 192 "w" => :label_this_week,
193 193 "lw" => :label_last_week,
194 194 "l2w" => [:label_last_n_weeks, {:count => 2}],
195 195 "m" => :label_this_month,
196 196 "lm" => :label_last_month,
197 197 "y" => :label_this_year,
198 198 ">t-" => :label_less_than_ago,
199 199 "<t-" => :label_more_than_ago,
200 200 "><t-"=> :label_in_the_past_days,
201 201 "t-" => :label_ago,
202 202 "~" => :label_contains,
203 203 "!~" => :label_not_contains,
204 204 "=p" => :label_any_issues_in_project,
205 205 "=!p" => :label_any_issues_not_in_project,
206 206 "!p" => :label_no_issues_in_project,
207 207 "*o" => :label_any_open_issues,
208 208 "!o" => :label_no_open_issues
209 209 }
210 210
211 211 class_attribute :operators_by_filter_type
212 212 self.operators_by_filter_type = {
213 213 :list => [ "=", "!" ],
214 214 :list_status => [ "o", "=", "!", "c", "*" ],
215 215 :list_optional => [ "=", "!", "!*", "*" ],
216 216 :list_subprojects => [ "*", "!*", "=" ],
217 217 :date => [ "=", ">=", "<=", "><", "<t+", ">t+", "><t+", "t+", "t", "ld", "w", "lw", "l2w", "m", "lm", "y", ">t-", "<t-", "><t-", "t-", "!*", "*" ],
218 218 :date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "><t-", "t-", "t", "ld", "w", "lw", "l2w", "m", "lm", "y", "!*", "*" ],
219 219 :string => [ "=", "~", "!", "!~", "!*", "*" ],
220 220 :text => [ "~", "!~", "!*", "*" ],
221 221 :integer => [ "=", ">=", "<=", "><", "!*", "*" ],
222 222 :float => [ "=", ">=", "<=", "><", "!*", "*" ],
223 223 :relation => ["=", "=p", "=!p", "!p", "*o", "!o", "!*", "*"],
224 224 :tree => ["=", "~", "!*", "*"]
225 225 }
226 226
227 227 class_attribute :available_columns
228 228 self.available_columns = []
229 229
230 230 class_attribute :queried_class
231 231
232 232 def queried_table_name
233 233 @queried_table_name ||= self.class.queried_class.table_name
234 234 end
235 235
236 236 def initialize(attributes=nil, *args)
237 237 super attributes
238 238 @is_for_all = project.nil?
239 239 end
240 240
241 241 # Builds the query from the given params
242 242 def build_from_params(params)
243 243 if params[:fields] || params[:f]
244 244 self.filters = {}
245 245 add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v])
246 246 else
247 247 available_filters.keys.each do |field|
248 248 add_short_filter(field, params[field]) if params[field]
249 249 end
250 250 end
251 251 self.group_by = params[:group_by] || (params[:query] && params[:query][:group_by])
252 252 self.column_names = params[:c] || (params[:query] && params[:query][:column_names])
253 253 self.totalable_names = params[:t] || (params[:query] && params[:query][:totalable_names])
254 254 self
255 255 end
256 256
257 257 # Builds a new query from the given params and attributes
258 258 def self.build_from_params(params, attributes={})
259 259 new(attributes).build_from_params(params)
260 260 end
261 261
262 262 def validate_query_filters
263 263 filters.each_key do |field|
264 264 if values_for(field)
265 265 case type_for(field)
266 266 when :integer
267 267 add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/\A[+-]?\d+(,[+-]?\d+)*\z/) }
268 268 when :float
269 269 add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/\A[+-]?\d+(\.\d*)?\z/) }
270 270 when :date, :date_past
271 271 case operator_for(field)
272 272 when "=", ">=", "<=", "><"
273 273 add_filter_error(field, :invalid) if values_for(field).detect {|v|
274 274 v.present? && (!v.match(/\A\d{4}-\d{2}-\d{2}(T\d{2}((:)?\d{2}){0,2}(Z|\d{2}:?\d{2})?)?\z/) || parse_date(v).nil?)
275 275 }
276 276 when ">t-", "<t-", "t-", ">t+", "<t+", "t+", "><t+", "><t-"
277 277 add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^\d+$/) }
278 278 end
279 279 end
280 280 end
281 281
282 282 add_filter_error(field, :blank) unless
283 283 # filter requires one or more values
284 284 (values_for(field) and !values_for(field).first.blank?) or
285 285 # filter doesn't require any value
286 286 ["o", "c", "!*", "*", "t", "ld", "w", "lw", "l2w", "m", "lm", "y", "*o", "!o"].include? operator_for(field)
287 287 end if filters
288 288 end
289 289
290 290 def add_filter_error(field, message)
291 291 m = label_for(field) + " " + l(message, :scope => 'activerecord.errors.messages')
292 292 errors.add(:base, m)
293 293 end
294 294
295 295 def editable_by?(user)
296 296 return false unless user
297 297 # Admin can edit them all and regular users can edit their private queries
298 298 return true if user.admin? || (is_private? && self.user_id == user.id)
299 299 # Members can not edit public queries that are for all project (only admin is allowed to)
300 300 is_public? && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
301 301 end
302 302
303 303 def trackers
304 304 @trackers ||= project.nil? ? Tracker.sorted.to_a : project.rolled_up_trackers
305 305 end
306 306
307 307 # Returns a hash of localized labels for all filter operators
308 308 def self.operators_labels
309 309 operators.inject({}) {|h, operator| h[operator.first] = l(*operator.last); h}
310 310 end
311 311
312 312 # Returns a representation of the available filters for JSON serialization
313 313 def available_filters_as_json
314 314 json = {}
315 315 available_filters.each do |field, options|
316 316 options = options.slice(:type, :name, :values)
317 317 if options[:values] && values_for(field)
318 318 missing = Array(values_for(field)).select(&:present?) - options[:values].map(&:last)
319 319 if missing.any? && respond_to?(method = "find_#{field}_filter_values")
320 320 options[:values] += send(method, missing)
321 321 end
322 322 end
323 323 json[field] = options.stringify_keys
324 324 end
325 325 json
326 326 end
327 327
328 328 def all_projects
329 329 @all_projects ||= Project.visible.to_a
330 330 end
331 331
332 332 def all_projects_values
333 333 return @all_projects_values if @all_projects_values
334 334
335 335 values = []
336 336 Project.project_tree(all_projects) do |p, level|
337 337 prefix = (level > 0 ? ('--' * level + ' ') : '')
338 338 values << ["#{prefix}#{p.name}", p.id.to_s]
339 339 end
340 340 @all_projects_values = values
341 341 end
342 342
343 343 # Adds available filters
344 344 def initialize_available_filters
345 345 # implemented by sub-classes
346 346 end
347 347 protected :initialize_available_filters
348 348
349 349 # Adds an available filter
350 350 def add_available_filter(field, options)
351 351 @available_filters ||= ActiveSupport::OrderedHash.new
352 352 @available_filters[field] = options
353 353 @available_filters
354 354 end
355 355
356 356 # Removes an available filter
357 357 def delete_available_filter(field)
358 358 if @available_filters
359 359 @available_filters.delete(field)
360 360 end
361 361 end
362 362
363 363 # Return a hash of available filters
364 364 def available_filters
365 365 unless @available_filters
366 366 initialize_available_filters
367 367 @available_filters.each do |field, options|
368 368 options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, ''))
369 369 end
370 370 end
371 371 @available_filters
372 372 end
373 373
374 374 def add_filter(field, operator, values=nil)
375 375 # values must be an array
376 376 return unless values.nil? || values.is_a?(Array)
377 377 # check if field is defined as an available filter
378 378 if available_filters.has_key? field
379 379 filter_options = available_filters[field]
380 380 filters[field] = {:operator => operator, :values => (values || [''])}
381 381 end
382 382 end
383 383
384 384 def add_short_filter(field, expression)
385 385 return unless expression && available_filters.has_key?(field)
386 386 field_type = available_filters[field][:type]
387 387 operators_by_filter_type[field_type].sort.reverse.detect do |operator|
388 388 next unless expression =~ /^#{Regexp.escape(operator)}(.*)$/
389 389 values = $1
390 390 add_filter field, operator, values.present? ? values.split('|') : ['']
391 391 end || add_filter(field, '=', expression.split('|'))
392 392 end
393 393
394 394 # Add multiple filters using +add_filter+
395 395 def add_filters(fields, operators, values)
396 396 if fields.is_a?(Array) && operators.is_a?(Hash) && (values.nil? || values.is_a?(Hash))
397 397 fields.each do |field|
398 398 add_filter(field, operators[field], values && values[field])
399 399 end
400 400 end
401 401 end
402 402
403 403 def has_filter?(field)
404 404 filters and filters[field]
405 405 end
406 406
407 407 def type_for(field)
408 408 available_filters[field][:type] if available_filters.has_key?(field)
409 409 end
410 410
411 411 def operator_for(field)
412 412 has_filter?(field) ? filters[field][:operator] : nil
413 413 end
414 414
415 415 def values_for(field)
416 416 has_filter?(field) ? filters[field][:values] : nil
417 417 end
418 418
419 419 def value_for(field, index=0)
420 420 (values_for(field) || [])[index]
421 421 end
422 422
423 423 def label_for(field)
424 424 label = available_filters[field][:name] if available_filters.has_key?(field)
425 425 label ||= l("field_#{field.to_s.gsub(/_id$/, '')}", :default => field)
426 426 end
427 427
428 428 def self.add_available_column(column)
429 429 self.available_columns << (column) if column.is_a?(QueryColumn)
430 430 end
431 431
432 432 # Returns an array of columns that can be used to group the results
433 433 def groupable_columns
434 434 available_columns.select {|c| c.groupable}
435 435 end
436 436
437 437 # Returns a Hash of columns and the key for sorting
438 438 def sortable_columns
439 439 available_columns.inject({}) {|h, column|
440 440 h[column.name.to_s] = column.sortable
441 441 h
442 442 }
443 443 end
444 444
445 445 def columns
446 446 # preserve the column_names order
447 447 cols = (has_default_columns? ? default_columns_names : column_names).collect do |name|
448 448 available_columns.find { |col| col.name == name }
449 449 end.compact
450 450 available_columns.select(&:frozen?) | cols
451 451 end
452 452
453 453 def inline_columns
454 454 columns.select(&:inline?)
455 455 end
456 456
457 457 def block_columns
458 458 columns.reject(&:inline?)
459 459 end
460 460
461 461 def available_inline_columns
462 462 available_columns.select(&:inline?)
463 463 end
464 464
465 465 def available_block_columns
466 466 available_columns.reject(&:inline?)
467 467 end
468 468
469 469 def available_totalable_columns
470 470 available_columns.select(&:totalable)
471 471 end
472 472
473 473 def default_columns_names
474 474 []
475 475 end
476 476
477 477 def column_names=(names)
478 478 if names
479 479 names = names.select {|n| n.is_a?(Symbol) || !n.blank? }
480 480 names = names.collect {|n| n.is_a?(Symbol) ? n : n.to_sym }
481 481 # Set column_names to nil if default columns
482 482 if names == default_columns_names
483 483 names = nil
484 484 end
485 485 end
486 486 write_attribute(:column_names, names)
487 487 end
488 488
489 489 def has_column?(column)
490 490 column_names && column_names.include?(column.is_a?(QueryColumn) ? column.name : column)
491 491 end
492 492
493 493 def has_custom_field_column?
494 494 columns.any? {|column| column.is_a? QueryCustomFieldColumn}
495 495 end
496 496
497 497 def has_default_columns?
498 498 column_names.nil? || column_names.empty?
499 499 end
500 500
501 501 def totalable_columns
502 502 names = totalable_names
503 503 available_totalable_columns.select {|column| names.include?(column.name)}
504 504 end
505 505
506 506 def totalable_names=(names)
507 507 if names
508 508 names = names.select(&:present?).map {|n| n.is_a?(Symbol) ? n : n.to_sym}
509 509 end
510 510 options[:totalable_names] = names
511 511 end
512 512
513 513 def totalable_names
514 514 options[:totalable_names] || Setting.issue_list_default_totals.map(&:to_sym) || []
515 515 end
516 516
517 517 def sort_criteria=(arg)
518 518 c = []
519 519 if arg.is_a?(Hash)
520 520 arg = arg.keys.sort.collect {|k| arg[k]}
521 521 end
522 522 if arg
523 523 c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, (o == 'desc' || o == false) ? 'desc' : 'asc']}
524 524 end
525 525 write_attribute(:sort_criteria, c)
526 526 end
527 527
528 528 def sort_criteria
529 529 read_attribute(:sort_criteria) || []
530 530 end
531 531
532 532 def sort_criteria_key(arg)
533 533 sort_criteria && sort_criteria[arg] && sort_criteria[arg].first
534 534 end
535 535
536 536 def sort_criteria_order(arg)
537 537 sort_criteria && sort_criteria[arg] && sort_criteria[arg].last
538 538 end
539 539
540 540 def sort_criteria_order_for(key)
541 541 sort_criteria.detect {|k, order| key.to_s == k}.try(:last)
542 542 end
543 543
544 544 # Returns the SQL sort order that should be prepended for grouping
545 545 def group_by_sort_order
546 546 if grouped? && (column = group_by_column)
547 547 order = (sort_criteria_order_for(column.name) || column.default_order).try(:upcase)
548 548 column.sortable.is_a?(Array) ?
549 column.sortable.collect {|s| "#{s} #{order}"}.join(',') :
549 column.sortable.collect {|s| "#{s} #{order}"} :
550 550 "#{column.sortable} #{order}"
551 551 end
552 552 end
553 553
554 554 # Returns true if the query is a grouped query
555 555 def grouped?
556 556 !group_by_column.nil?
557 557 end
558 558
559 559 def group_by_column
560 560 groupable_columns.detect {|c| c.groupable && c.name.to_s == group_by}
561 561 end
562 562
563 563 def group_by_statement
564 564 group_by_column.try(:groupable)
565 565 end
566 566
567 567 def project_statement
568 568 project_clauses = []
569 569 if project && !project.descendants.active.empty?
570 570 if has_filter?("subproject_id")
571 571 case operator_for("subproject_id")
572 572 when '='
573 573 # include the selected subprojects
574 574 ids = [project.id] + values_for("subproject_id").each(&:to_i)
575 575 project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',')
576 576 when '!*'
577 577 # main project only
578 578 project_clauses << "#{Project.table_name}.id = %d" % project.id
579 579 else
580 580 # all subprojects
581 581 project_clauses << "#{Project.table_name}.lft >= #{project.lft} AND #{Project.table_name}.rgt <= #{project.rgt}"
582 582 end
583 583 elsif Setting.display_subprojects_issues?
584 584 project_clauses << "#{Project.table_name}.lft >= #{project.lft} AND #{Project.table_name}.rgt <= #{project.rgt}"
585 585 else
586 586 project_clauses << "#{Project.table_name}.id = %d" % project.id
587 587 end
588 588 elsif project
589 589 project_clauses << "#{Project.table_name}.id = %d" % project.id
590 590 end
591 591 project_clauses.any? ? project_clauses.join(' AND ') : nil
592 592 end
593 593
594 594 def statement
595 595 # filters clauses
596 596 filters_clauses = []
597 597 filters.each_key do |field|
598 598 next if field == "subproject_id"
599 599 v = values_for(field).clone
600 600 next unless v and !v.empty?
601 601 operator = operator_for(field)
602 602
603 603 # "me" value substitution
604 604 if %w(assigned_to_id author_id user_id watcher_id).include?(field)
605 605 if v.delete("me")
606 606 if User.current.logged?
607 607 v.push(User.current.id.to_s)
608 608 v += User.current.group_ids.map(&:to_s) if field == 'assigned_to_id'
609 609 else
610 610 v.push("0")
611 611 end
612 612 end
613 613 end
614 614
615 615 if field == 'project_id'
616 616 if v.delete('mine')
617 617 v += User.current.memberships.map(&:project_id).map(&:to_s)
618 618 end
619 619 end
620 620
621 621 if field =~ /cf_(\d+)$/
622 622 # custom field
623 623 filters_clauses << sql_for_custom_field(field, operator, v, $1)
624 624 elsif respond_to?("sql_for_#{field}_field")
625 625 # specific statement
626 626 filters_clauses << send("sql_for_#{field}_field", field, operator, v)
627 627 else
628 628 # regular field
629 629 filters_clauses << '(' + sql_for_field(field, operator, v, queried_table_name, field) + ')'
630 630 end
631 631 end if filters and valid?
632 632
633 633 if (c = group_by_column) && c.is_a?(QueryCustomFieldColumn)
634 634 # Excludes results for which the grouped custom field is not visible
635 635 filters_clauses << c.custom_field.visibility_by_project_condition
636 636 end
637 637
638 638 filters_clauses << project_statement
639 639 filters_clauses.reject!(&:blank?)
640 640
641 641 filters_clauses.any? ? filters_clauses.join(' AND ') : nil
642 642 end
643 643
644 644 # Returns the sum of values for the given column
645 645 def total_for(column)
646 646 total_with_scope(column, base_scope)
647 647 end
648 648
649 649 # Returns a hash of the sum of the given column for each group,
650 650 # or nil if the query is not grouped
651 651 def total_by_group_for(column)
652 652 grouped_query do |scope|
653 653 total_with_scope(column, scope)
654 654 end
655 655 end
656 656
657 657 def totals
658 658 totals = totalable_columns.map {|column| [column, total_for(column)]}
659 659 yield totals if block_given?
660 660 totals
661 661 end
662 662
663 663 def totals_by_group
664 664 totals = totalable_columns.map {|column| [column, total_by_group_for(column)]}
665 665 yield totals if block_given?
666 666 totals
667 667 end
668 668
669 669 private
670 670
671 671 def grouped_query(&block)
672 672 r = nil
673 673 if grouped?
674 674 begin
675 675 # Rails3 will raise an (unexpected) RecordNotFound if there's only a nil group value
676 676 r = yield base_group_scope
677 677 rescue ActiveRecord::RecordNotFound
678 678 r = {nil => yield(base_scope)}
679 679 end
680 680 c = group_by_column
681 681 if c.is_a?(QueryCustomFieldColumn)
682 682 r = r.keys.inject({}) {|h, k| h[c.custom_field.cast_value(k)] = r[k]; h}
683 683 end
684 684 end
685 685 r
686 686 rescue ::ActiveRecord::StatementInvalid => e
687 687 raise StatementInvalid.new(e.message)
688 688 end
689 689
690 690 def total_with_scope(column, scope)
691 691 unless column.is_a?(QueryColumn)
692 692 column = column.to_sym
693 693 column = available_totalable_columns.detect {|c| c.name == column}
694 694 end
695 695 if column.is_a?(QueryCustomFieldColumn)
696 696 custom_field = column.custom_field
697 697 send "total_for_custom_field", custom_field, scope
698 698 else
699 699 send "total_for_#{column.name}", scope
700 700 end
701 701 rescue ::ActiveRecord::StatementInvalid => e
702 702 raise StatementInvalid.new(e.message)
703 703 end
704 704
705 705 def base_scope
706 706 raise "unimplemented"
707 707 end
708 708
709 709 def base_group_scope
710 710 base_scope.
711 711 joins(joins_for_order_statement(group_by_statement)).
712 712 group(group_by_statement)
713 713 end
714 714
715 715 def total_for_custom_field(custom_field, scope, &block)
716 716 total = custom_field.format.total_for_scope(custom_field, scope)
717 717 total = map_total(total) {|t| custom_field.format.cast_total_value(custom_field, t)}
718 718 total
719 719 end
720 720
721 721 def map_total(total, &block)
722 722 if total.is_a?(Hash)
723 723 total.keys.each {|k| total[k] = yield total[k]}
724 724 else
725 725 total = yield total
726 726 end
727 727 total
728 728 end
729 729
730 730 def sql_for_custom_field(field, operator, value, custom_field_id)
731 731 db_table = CustomValue.table_name
732 732 db_field = 'value'
733 733 filter = @available_filters[field]
734 734 return nil unless filter
735 735 if filter[:field].format.target_class && filter[:field].format.target_class <= User
736 736 if value.delete('me')
737 737 value.push User.current.id.to_s
738 738 end
739 739 end
740 740 not_in = nil
741 741 if operator == '!'
742 742 # Makes ! operator work for custom fields with multiple values
743 743 operator = '='
744 744 not_in = 'NOT'
745 745 end
746 746 customized_key = "id"
747 747 customized_class = queried_class
748 748 if field =~ /^(.+)\.cf_/
749 749 assoc = $1
750 750 customized_key = "#{assoc}_id"
751 751 customized_class = queried_class.reflect_on_association(assoc.to_sym).klass.base_class rescue nil
752 752 raise "Unknown #{queried_class.name} association #{assoc}" unless customized_class
753 753 end
754 754 where = sql_for_field(field, operator, value, db_table, db_field, true)
755 755 if operator =~ /[<>]/
756 756 where = "(#{where}) AND #{db_table}.#{db_field} <> ''"
757 757 end
758 758 "#{queried_table_name}.#{customized_key} #{not_in} IN (" +
759 759 "SELECT #{customized_class.table_name}.id FROM #{customized_class.table_name}" +
760 760 " LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='#{customized_class}' AND #{db_table}.customized_id=#{customized_class.table_name}.id AND #{db_table}.custom_field_id=#{custom_field_id}" +
761 761 " WHERE (#{where}) AND (#{filter[:field].visibility_by_project_condition}))"
762 762 end
763 763
764 764 # Helper method to generate the WHERE sql for a +field+, +operator+ and a +value+
765 765 def sql_for_field(field, operator, value, db_table, db_field, is_custom_filter=false)
766 766 sql = ''
767 767 case operator
768 768 when "="
769 769 if value.any?
770 770 case type_for(field)
771 771 when :date, :date_past
772 772 sql = date_clause(db_table, db_field, parse_date(value.first), parse_date(value.first), is_custom_filter)
773 773 when :integer
774 774 int_values = value.first.to_s.scan(/[+-]?\d+/).map(&:to_i).join(",")
775 775 if int_values.present?
776 776 if is_custom_filter
777 777 sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) IN (#{int_values}))"
778 778 else
779 779 sql = "#{db_table}.#{db_field} IN (#{int_values})"
780 780 end
781 781 else
782 782 sql = "1=0"
783 783 end
784 784 when :float
785 785 if is_custom_filter
786 786 sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5})"
787 787 else
788 788 sql = "#{db_table}.#{db_field} BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5}"
789 789 end
790 790 else
791 791 sql = queried_class.send(:sanitize_sql_for_conditions, ["#{db_table}.#{db_field} IN (?)", value])
792 792 end
793 793 else
794 794 # IN an empty set
795 795 sql = "1=0"
796 796 end
797 797 when "!"
798 798 if value.any?
799 799 sql = queried_class.send(:sanitize_sql_for_conditions, ["(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (?))", value])
800 800 else
801 801 # NOT IN an empty set
802 802 sql = "1=1"
803 803 end
804 804 when "!*"
805 805 sql = "#{db_table}.#{db_field} IS NULL"
806 806 sql << " OR #{db_table}.#{db_field} = ''" if is_custom_filter
807 807 when "*"
808 808 sql = "#{db_table}.#{db_field} IS NOT NULL"
809 809 sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
810 810 when ">="
811 811 if [:date, :date_past].include?(type_for(field))
812 812 sql = date_clause(db_table, db_field, parse_date(value.first), nil, is_custom_filter)
813 813 else
814 814 if is_custom_filter
815 815 sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) >= #{value.first.to_f})"
816 816 else
817 817 sql = "#{db_table}.#{db_field} >= #{value.first.to_f}"
818 818 end
819 819 end
820 820 when "<="
821 821 if [:date, :date_past].include?(type_for(field))
822 822 sql = date_clause(db_table, db_field, nil, parse_date(value.first), is_custom_filter)
823 823 else
824 824 if is_custom_filter
825 825 sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) <= #{value.first.to_f})"
826 826 else
827 827 sql = "#{db_table}.#{db_field} <= #{value.first.to_f}"
828 828 end
829 829 end
830 830 when "><"
831 831 if [:date, :date_past].include?(type_for(field))
832 832 sql = date_clause(db_table, db_field, parse_date(value[0]), parse_date(value[1]), is_custom_filter)
833 833 else
834 834 if is_custom_filter
835 835 sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) BETWEEN #{value[0].to_f} AND #{value[1].to_f})"
836 836 else
837 837 sql = "#{db_table}.#{db_field} BETWEEN #{value[0].to_f} AND #{value[1].to_f}"
838 838 end
839 839 end
840 840 when "o"
841 841 sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_false})" if field == "status_id"
842 842 when "c"
843 843 sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_true})" if field == "status_id"
844 844 when "><t-"
845 845 # between today - n days and today
846 846 sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0, is_custom_filter)
847 847 when ">t-"
848 848 # >= today - n days
849 849 sql = relative_date_clause(db_table, db_field, - value.first.to_i, nil, is_custom_filter)
850 850 when "<t-"
851 851 # <= today - n days
852 852 sql = relative_date_clause(db_table, db_field, nil, - value.first.to_i, is_custom_filter)
853 853 when "t-"
854 854 # = n days in past
855 855 sql = relative_date_clause(db_table, db_field, - value.first.to_i, - value.first.to_i, is_custom_filter)
856 856 when "><t+"
857 857 # between today and today + n days
858 858 sql = relative_date_clause(db_table, db_field, 0, value.first.to_i, is_custom_filter)
859 859 when ">t+"
860 860 # >= today + n days
861 861 sql = relative_date_clause(db_table, db_field, value.first.to_i, nil, is_custom_filter)
862 862 when "<t+"
863 863 # <= today + n days
864 864 sql = relative_date_clause(db_table, db_field, nil, value.first.to_i, is_custom_filter)
865 865 when "t+"
866 866 # = today + n days
867 867 sql = relative_date_clause(db_table, db_field, value.first.to_i, value.first.to_i, is_custom_filter)
868 868 when "t"
869 869 # = today
870 870 sql = relative_date_clause(db_table, db_field, 0, 0, is_custom_filter)
871 871 when "ld"
872 872 # = yesterday
873 873 sql = relative_date_clause(db_table, db_field, -1, -1, is_custom_filter)
874 874 when "w"
875 875 # = this week
876 876 first_day_of_week = l(:general_first_day_of_week).to_i
877 877 day_of_week = User.current.today.cwday
878 878 days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
879 879 sql = relative_date_clause(db_table, db_field, - days_ago, - days_ago + 6, is_custom_filter)
880 880 when "lw"
881 881 # = last week
882 882 first_day_of_week = l(:general_first_day_of_week).to_i
883 883 day_of_week = User.current.today.cwday
884 884 days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
885 885 sql = relative_date_clause(db_table, db_field, - days_ago - 7, - days_ago - 1, is_custom_filter)
886 886 when "l2w"
887 887 # = last 2 weeks
888 888 first_day_of_week = l(:general_first_day_of_week).to_i
889 889 day_of_week = User.current.today.cwday
890 890 days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
891 891 sql = relative_date_clause(db_table, db_field, - days_ago - 14, - days_ago - 1, is_custom_filter)
892 892 when "m"
893 893 # = this month
894 894 date = User.current.today
895 895 sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month, is_custom_filter)
896 896 when "lm"
897 897 # = last month
898 898 date = User.current.today.prev_month
899 899 sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month, is_custom_filter)
900 900 when "y"
901 901 # = this year
902 902 date = User.current.today
903 903 sql = date_clause(db_table, db_field, date.beginning_of_year, date.end_of_year, is_custom_filter)
904 904 when "~"
905 905 sql = sql_contains("#{db_table}.#{db_field}", value.first)
906 906 when "!~"
907 907 sql = sql_contains("#{db_table}.#{db_field}", value.first, false)
908 908 else
909 909 raise "Unknown query operator #{operator}"
910 910 end
911 911
912 912 return sql
913 913 end
914 914
915 915 # Returns a SQL LIKE statement with wildcards
916 916 def sql_contains(db_field, value, match=true)
917 917 queried_class.send :sanitize_sql_for_conditions,
918 918 [Redmine::Database.like(db_field, '?', :match => match), "%#{value}%"]
919 919 end
920 920
921 921 # Adds a filter for the given custom field
922 922 def add_custom_field_filter(field, assoc=nil)
923 923 options = field.query_filter_options(self)
924 924 if field.format.target_class && field.format.target_class <= User
925 925 if options[:values].is_a?(Array) && User.current.logged?
926 926 options[:values].unshift ["<< #{l(:label_me)} >>", "me"]
927 927 end
928 928 end
929 929
930 930 filter_id = "cf_#{field.id}"
931 931 filter_name = field.name
932 932 if assoc.present?
933 933 filter_id = "#{assoc}.#{filter_id}"
934 934 filter_name = l("label_attribute_of_#{assoc}", :name => filter_name)
935 935 end
936 936 add_available_filter filter_id, options.merge({
937 937 :name => filter_name,
938 938 :field => field
939 939 })
940 940 end
941 941
942 942 # Adds filters for the given custom fields scope
943 943 def add_custom_fields_filters(scope, assoc=nil)
944 944 scope.visible.where(:is_filter => true).sorted.each do |field|
945 945 add_custom_field_filter(field, assoc)
946 946 end
947 947 end
948 948
949 949 # Adds filters for the given associations custom fields
950 950 def add_associations_custom_fields_filters(*associations)
951 951 fields_by_class = CustomField.visible.where(:is_filter => true).group_by(&:class)
952 952 associations.each do |assoc|
953 953 association_klass = queried_class.reflect_on_association(assoc).klass
954 954 fields_by_class.each do |field_class, fields|
955 955 if field_class.customized_class <= association_klass
956 956 fields.sort.each do |field|
957 957 add_custom_field_filter(field, assoc)
958 958 end
959 959 end
960 960 end
961 961 end
962 962 end
963 963
964 964 def quoted_time(time, is_custom_filter)
965 965 if is_custom_filter
966 966 # Custom field values are stored as strings in the DB
967 967 # using this format that does not depend on DB date representation
968 968 time.strftime("%Y-%m-%d %H:%M:%S")
969 969 else
970 970 self.class.connection.quoted_date(time)
971 971 end
972 972 end
973 973
974 974 def date_for_user_time_zone(y, m, d)
975 975 if tz = User.current.time_zone
976 976 tz.local y, m, d
977 977 else
978 978 Time.local y, m, d
979 979 end
980 980 end
981 981
982 982 # Returns a SQL clause for a date or datetime field.
983 983 def date_clause(table, field, from, to, is_custom_filter)
984 984 s = []
985 985 if from
986 986 if from.is_a?(Date)
987 987 from = date_for_user_time_zone(from.year, from.month, from.day).yesterday.end_of_day
988 988 else
989 989 from = from - 1 # second
990 990 end
991 991 if self.class.default_timezone == :utc
992 992 from = from.utc
993 993 end
994 994 s << ("#{table}.#{field} > '%s'" % [quoted_time(from, is_custom_filter)])
995 995 end
996 996 if to
997 997 if to.is_a?(Date)
998 998 to = date_for_user_time_zone(to.year, to.month, to.day).end_of_day
999 999 end
1000 1000 if self.class.default_timezone == :utc
1001 1001 to = to.utc
1002 1002 end
1003 1003 s << ("#{table}.#{field} <= '%s'" % [quoted_time(to, is_custom_filter)])
1004 1004 end
1005 1005 s.join(' AND ')
1006 1006 end
1007 1007
1008 1008 # Returns a SQL clause for a date or datetime field using relative dates.
1009 1009 def relative_date_clause(table, field, days_from, days_to, is_custom_filter)
1010 1010 date_clause(table, field, (days_from ? User.current.today + days_from : nil), (days_to ? User.current.today + days_to : nil), is_custom_filter)
1011 1011 end
1012 1012
1013 1013 # Returns a Date or Time from the given filter value
1014 1014 def parse_date(arg)
1015 1015 if arg.to_s =~ /\A\d{4}-\d{2}-\d{2}T/
1016 1016 Time.parse(arg) rescue nil
1017 1017 else
1018 1018 Date.parse(arg) rescue nil
1019 1019 end
1020 1020 end
1021 1021
1022 1022 # Additional joins required for the given sort options
1023 1023 def joins_for_order_statement(order_options)
1024 1024 joins = []
1025 1025
1026 1026 if order_options
1027 1027 if order_options.include?('authors')
1028 1028 joins << "LEFT OUTER JOIN #{User.table_name} authors ON authors.id = #{queried_table_name}.author_id"
1029 1029 end
1030 1030 order_options.scan(/cf_\d+/).uniq.each do |name|
1031 1031 column = available_columns.detect {|c| c.name.to_s == name}
1032 1032 join = column && column.custom_field.join_for_order_statement
1033 1033 if join
1034 1034 joins << join
1035 1035 end
1036 1036 end
1037 1037 end
1038 1038
1039 1039 joins.any? ? joins.join(' ') : nil
1040 1040 end
1041 1041 end
@@ -1,4663 +1,4679
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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
20 20 class IssuesControllerTest < ActionController::TestCase
21 21 fixtures :projects,
22 22 :users, :email_addresses, :user_preferences,
23 23 :roles,
24 24 :members,
25 25 :member_roles,
26 26 :issues,
27 27 :issue_statuses,
28 28 :issue_relations,
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 :repositories,
46 46 :changesets
47 47
48 48 include Redmine::I18n
49 49
50 50 def setup
51 51 User.current = nil
52 52 end
53 53
54 54 def test_index
55 55 with_settings :default_language => "en" do
56 56 get :index
57 57 assert_response :success
58 58 assert_template 'index'
59 59 assert_not_nil assigns(:issues)
60 60 assert_nil assigns(:project)
61 61
62 62 # links to visible issues
63 63 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
64 64 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
65 65 # private projects hidden
66 66 assert_select 'a[href="/issues/6"]', 0
67 67 assert_select 'a[href="/issues/4"]', 0
68 68 # project column
69 69 assert_select 'th', :text => /Project/
70 70 end
71 71 end
72 72
73 73 def test_index_should_not_list_issues_when_module_disabled
74 74 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
75 75 get :index
76 76 assert_response :success
77 77 assert_template 'index'
78 78 assert_not_nil assigns(:issues)
79 79 assert_nil assigns(:project)
80 80
81 81 assert_select 'a[href="/issues/1"]', 0
82 82 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
83 83 end
84 84
85 85 def test_index_should_list_visible_issues_only
86 86 get :index, :per_page => 100
87 87 assert_response :success
88 88 assert_not_nil assigns(:issues)
89 89 assert_nil assigns(:issues).detect {|issue| !issue.visible?}
90 90 end
91 91
92 92 def test_index_with_project
93 93 Setting.display_subprojects_issues = 0
94 94 get :index, :project_id => 1
95 95 assert_response :success
96 96 assert_template 'index'
97 97 assert_not_nil assigns(:issues)
98 98
99 99 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
100 100 assert_select 'a[href="/issues/5"]', 0
101 101 end
102 102
103 103 def test_index_with_project_and_subprojects
104 104 Setting.display_subprojects_issues = 1
105 105 get :index, :project_id => 1
106 106 assert_response :success
107 107 assert_template 'index'
108 108 assert_not_nil assigns(:issues)
109 109
110 110 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
111 111 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
112 112 assert_select 'a[href="/issues/6"]', 0
113 113 end
114 114
115 115 def test_index_with_project_and_subprojects_should_show_private_subprojects_with_permission
116 116 @request.session[:user_id] = 2
117 117 Setting.display_subprojects_issues = 1
118 118 get :index, :project_id => 1
119 119 assert_response :success
120 120 assert_template 'index'
121 121 assert_not_nil assigns(:issues)
122 122
123 123 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
124 124 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
125 125 assert_select 'a[href="/issues/6"]', :text => /Issue of a private subproject/
126 126 end
127 127
128 128 def test_index_with_project_and_default_filter
129 129 get :index, :project_id => 1, :set_filter => 1
130 130 assert_response :success
131 131 assert_template 'index'
132 132 assert_not_nil assigns(:issues)
133 133
134 134 query = assigns(:query)
135 135 assert_not_nil query
136 136 # default filter
137 137 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
138 138 end
139 139
140 140 def test_index_with_project_and_filter
141 141 get :index, :project_id => 1, :set_filter => 1,
142 142 :f => ['tracker_id'],
143 143 :op => {'tracker_id' => '='},
144 144 :v => {'tracker_id' => ['1']}
145 145 assert_response :success
146 146 assert_template 'index'
147 147 assert_not_nil assigns(:issues)
148 148
149 149 query = assigns(:query)
150 150 assert_not_nil query
151 151 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
152 152 end
153 153
154 154 def test_index_with_short_filters
155 155 to_test = {
156 156 'status_id' => {
157 157 'o' => { :op => 'o', :values => [''] },
158 158 'c' => { :op => 'c', :values => [''] },
159 159 '7' => { :op => '=', :values => ['7'] },
160 160 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
161 161 '=7' => { :op => '=', :values => ['7'] },
162 162 '!3' => { :op => '!', :values => ['3'] },
163 163 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
164 164 'subject' => {
165 165 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
166 166 'o' => { :op => '=', :values => ['o'] },
167 167 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
168 168 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
169 169 'tracker_id' => {
170 170 '3' => { :op => '=', :values => ['3'] },
171 171 '=3' => { :op => '=', :values => ['3'] }},
172 172 'start_date' => {
173 173 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
174 174 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
175 175 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
176 176 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
177 177 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
178 178 '<t+2' => { :op => '<t+', :values => ['2'] },
179 179 '>t+2' => { :op => '>t+', :values => ['2'] },
180 180 't+2' => { :op => 't+', :values => ['2'] },
181 181 't' => { :op => 't', :values => [''] },
182 182 'w' => { :op => 'w', :values => [''] },
183 183 '>t-2' => { :op => '>t-', :values => ['2'] },
184 184 '<t-2' => { :op => '<t-', :values => ['2'] },
185 185 't-2' => { :op => 't-', :values => ['2'] }},
186 186 'created_on' => {
187 187 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
188 188 '<t-2' => { :op => '<t-', :values => ['2'] },
189 189 '>t-2' => { :op => '>t-', :values => ['2'] },
190 190 't-2' => { :op => 't-', :values => ['2'] }},
191 191 'cf_1' => {
192 192 'c' => { :op => '=', :values => ['c'] },
193 193 '!c' => { :op => '!', :values => ['c'] },
194 194 '!*' => { :op => '!*', :values => [''] },
195 195 '*' => { :op => '*', :values => [''] }},
196 196 'estimated_hours' => {
197 197 '=13.4' => { :op => '=', :values => ['13.4'] },
198 198 '>=45' => { :op => '>=', :values => ['45'] },
199 199 '<=125' => { :op => '<=', :values => ['125'] },
200 200 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
201 201 '!*' => { :op => '!*', :values => [''] },
202 202 '*' => { :op => '*', :values => [''] }}
203 203 }
204 204
205 205 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
206 206
207 207 to_test.each do |field, expression_and_expected|
208 208 expression_and_expected.each do |filter_expression, expected|
209 209
210 210 get :index, :set_filter => 1, field => filter_expression
211 211
212 212 assert_response :success
213 213 assert_template 'index'
214 214 assert_not_nil assigns(:issues)
215 215
216 216 query = assigns(:query)
217 217 assert_not_nil query
218 218 assert query.has_filter?(field)
219 219 assert_equal(default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}}), query.filters)
220 220 end
221 221 end
222 222 end
223 223
224 224 def test_index_with_project_and_empty_filters
225 225 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
226 226 assert_response :success
227 227 assert_template 'index'
228 228 assert_not_nil assigns(:issues)
229 229
230 230 query = assigns(:query)
231 231 assert_not_nil query
232 232 # no filter
233 233 assert_equal({}, query.filters)
234 234 end
235 235
236 236 def test_index_with_project_custom_field_filter
237 237 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
238 238 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
239 239 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
240 240 filter_name = "project.cf_#{field.id}"
241 241 @request.session[:user_id] = 1
242 242
243 243 get :index, :set_filter => 1,
244 244 :f => [filter_name],
245 245 :op => {filter_name => '='},
246 246 :v => {filter_name => ['Foo']}
247 247 assert_response :success
248 248 assert_template 'index'
249 249 assert_equal [3, 5], assigns(:issues).map(&:project_id).uniq.sort
250 250 end
251 251
252 252 def test_index_with_query
253 253 get :index, :project_id => 1, :query_id => 5
254 254 assert_response :success
255 255 assert_template 'index'
256 256 assert_not_nil assigns(:issues)
257 257 assert_nil assigns(:issue_count_by_group)
258 258 end
259 259
260 260 def test_index_with_query_grouped_by_tracker
261 261 get :index, :project_id => 1, :query_id => 6
262 262 assert_response :success
263 263 assert_template 'index'
264 264 assert_not_nil assigns(:issues)
265 265 assert_not_nil assigns(:issue_count_by_group)
266 266 end
267 267
268 268 def test_index_with_query_grouped_and_sorted_by_category
269 269 get :index, :project_id => 1, :set_filter => 1, :group_by => "category", :sort => "category"
270 270 assert_response :success
271 271 assert_template 'index'
272 272 assert_not_nil assigns(:issues)
273 273 assert_not_nil assigns(:issue_count_by_group)
274 274 end
275 275
276 def test_index_with_query_grouped_and_sorted_by_fixed_version
277 get :index, :project_id => 1, :set_filter => 1, :group_by => "fixed_version", :sort => "fixed_version"
278 assert_response :success
279 assert_template 'index'
280 assert_not_nil assigns(:issues)
281 assert_not_nil assigns(:issue_count_by_group)
282 end
283
284 def test_index_with_query_grouped_and_sorted_by_fixed_version_in_reverse_order
285 get :index, :project_id => 1, :set_filter => 1, :group_by => "fixed_version", :sort => "fixed_version:desc"
286 assert_response :success
287 assert_template 'index'
288 assert_not_nil assigns(:issues)
289 assert_not_nil assigns(:issue_count_by_group)
290 end
291
276 292 def test_index_with_query_grouped_by_list_custom_field
277 293 get :index, :project_id => 1, :query_id => 9
278 294 assert_response :success
279 295 assert_template 'index'
280 296 assert_not_nil assigns(:issues)
281 297 assert_not_nil assigns(:issue_count_by_group)
282 298 end
283 299
284 300 def test_index_with_query_grouped_by_key_value_custom_field
285 301 cf = IssueCustomField.create!(:name => 'Key', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'enumeration')
286 302 cf.enumerations << valueb = CustomFieldEnumeration.new(:name => 'Value B', :position => 1)
287 303 cf.enumerations << valuea = CustomFieldEnumeration.new(:name => 'Value A', :position => 2)
288 304 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => valueb.id)
289 305 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => valueb.id)
290 306 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => valuea.id)
291 307 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
292 308
293 309 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
294 310 assert_response :success
295 311 assert_template 'index'
296 312 assert_not_nil assigns(:issues)
297 313 assert_not_nil assigns(:issue_count_by_group)
298 314
299 315 assert_select 'tr.group', 3
300 316 assert_select 'tr.group' do
301 317 assert_select 'span.name', :text => 'Value B'
302 318 assert_select 'span.count', :text => '2'
303 319 end
304 320 assert_select 'tr.group' do
305 321 assert_select 'span.name', :text => 'Value A'
306 322 assert_select 'span.count', :text => '1'
307 323 end
308 324 end
309 325
310 326 def test_index_with_query_grouped_by_user_custom_field
311 327 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
312 328 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
313 329 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
314 330 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
315 331 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
316 332
317 333 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
318 334 assert_response :success
319 335
320 336 assert_select 'tr.group', 3
321 337 assert_select 'tr.group' do
322 338 assert_select 'a', :text => 'John Smith'
323 339 assert_select 'span.count', :text => '1'
324 340 end
325 341 assert_select 'tr.group' do
326 342 assert_select 'a', :text => 'Dave Lopper'
327 343 assert_select 'span.count', :text => '2'
328 344 end
329 345 end
330 346
331 347 def test_index_grouped_by_boolean_custom_field_should_distinguish_blank_and_false_values
332 348 cf = IssueCustomField.create!(:name => 'Bool', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'bool')
333 349 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '1')
334 350 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
335 351 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '')
336 352
337 353 with_settings :default_language => 'en' do
338 354 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
339 355 assert_response :success
340 356 end
341 357
342 358 assert_select 'tr.group', 3
343 359 assert_select 'tr.group', :text => /Yes/
344 360 assert_select 'tr.group', :text => /No/
345 361 assert_select 'tr.group', :text => /blank/
346 362 end
347 363
348 364 def test_index_grouped_by_boolean_custom_field_with_false_group_in_first_position_should_show_the_group
349 365 cf = IssueCustomField.create!(:name => 'Bool', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'bool', :is_filter => true)
350 366 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '0')
351 367 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
352 368
353 369 with_settings :default_language => 'en' do
354 370 get :index, :project_id => 1, :set_filter => 1, "cf_#{cf.id}" => "*", :group_by => "cf_#{cf.id}"
355 371 assert_response :success
356 372 assert_equal [1, 2], assigns(:issues).map(&:id).sort
357 373 end
358 374
359 375 assert_select 'tr.group', 1
360 376 assert_select 'tr.group', :text => /No/
361 377 end
362 378
363 379 def test_index_with_query_grouped_by_tracker_in_normal_order
364 380 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
365 381
366 382 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc'
367 383 assert_response :success
368 384
369 385 trackers = assigns(:issues).map(&:tracker).uniq
370 386 assert_equal [1, 2, 3], trackers.map(&:id)
371 387 end
372 388
373 389 def test_index_with_query_grouped_by_tracker_in_reverse_order
374 390 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
375 391
376 392 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc,tracker:desc'
377 393 assert_response :success
378 394
379 395 trackers = assigns(:issues).map(&:tracker).uniq
380 396 assert_equal [3, 2, 1], trackers.map(&:id)
381 397 end
382 398
383 399 def test_index_with_query_id_and_project_id_should_set_session_query
384 400 get :index, :project_id => 1, :query_id => 4
385 401 assert_response :success
386 402 assert_kind_of Hash, session[:query]
387 403 assert_equal 4, session[:query][:id]
388 404 assert_equal 1, session[:query][:project_id]
389 405 end
390 406
391 407 def test_index_with_invalid_query_id_should_respond_404
392 408 get :index, :project_id => 1, :query_id => 999
393 409 assert_response 404
394 410 end
395 411
396 412 def test_index_with_cross_project_query_in_session_should_show_project_issues
397 413 q = IssueQuery.create!(:name => "test", :user_id => 2, :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
398 414 @request.session[:query] = {:id => q.id, :project_id => 1}
399 415
400 416 with_settings :display_subprojects_issues => '0' do
401 417 get :index, :project_id => 1
402 418 end
403 419 assert_response :success
404 420 assert_not_nil assigns(:query)
405 421 assert_equal q.id, assigns(:query).id
406 422 assert_equal 1, assigns(:query).project_id
407 423 assert_equal [1], assigns(:issues).map(&:project_id).uniq
408 424 end
409 425
410 426 def test_private_query_should_not_be_available_to_other_users
411 427 q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
412 428 @request.session[:user_id] = 3
413 429
414 430 get :index, :query_id => q.id
415 431 assert_response 403
416 432 end
417 433
418 434 def test_private_query_should_be_available_to_its_user
419 435 q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
420 436 @request.session[:user_id] = 2
421 437
422 438 get :index, :query_id => q.id
423 439 assert_response :success
424 440 end
425 441
426 442 def test_public_query_should_be_available_to_other_users
427 443 q = IssueQuery.create!(:name => "public", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PUBLIC, :project => nil)
428 444 @request.session[:user_id] = 3
429 445
430 446 get :index, :query_id => q.id
431 447 assert_response :success
432 448 end
433 449
434 450 def test_index_should_omit_page_param_in_export_links
435 451 get :index, :page => 2
436 452 assert_response :success
437 453 assert_select 'a.atom[href="/issues.atom"]'
438 454 assert_select 'a.csv[href="/issues.csv"]'
439 455 assert_select 'a.pdf[href="/issues.pdf"]'
440 456 assert_select 'form#csv-export-form[action="/issues.csv"]'
441 457 end
442 458
443 459 def test_index_should_not_warn_when_not_exceeding_export_limit
444 460 with_settings :issues_export_limit => 200 do
445 461 get :index
446 462 assert_select '#csv-export-options p.icon-warning', 0
447 463 end
448 464 end
449 465
450 466 def test_index_should_warn_when_exceeding_export_limit
451 467 with_settings :issues_export_limit => 2 do
452 468 get :index
453 469 assert_select '#csv-export-options p.icon-warning', :text => %r{limit: 2}
454 470 end
455 471 end
456 472
457 473 def test_index_should_include_query_params_as_hidden_fields_in_csv_export_form
458 474 get :index, :project_id => 1, :set_filter => "1", :tracker_id => "2", :sort => 'status', :c => ["status", "priority"]
459 475
460 476 assert_select '#csv-export-form[action=?]', '/projects/ecookbook/issues.csv'
461 477 assert_select '#csv-export-form[method=?]', 'get'
462 478
463 479 assert_select '#csv-export-form' do
464 480 assert_select 'input[name=?][value=?]', 'set_filter', '1'
465 481
466 482 assert_select 'input[name=?][value=?]', 'f[]', 'tracker_id'
467 483 assert_select 'input[name=?][value=?]', 'op[tracker_id]', '='
468 484 assert_select 'input[name=?][value=?]', 'v[tracker_id][]', '2'
469 485
470 486 assert_select 'input[name=?][value=?]', 'c[]', 'status'
471 487 assert_select 'input[name=?][value=?]', 'c[]', 'priority'
472 488
473 489 assert_select 'input[name=?][value=?]', 'sort', 'status'
474 490 end
475 491 end
476 492
477 493 def test_index_csv
478 494 get :index, :format => 'csv'
479 495 assert_response :success
480 496 assert_not_nil assigns(:issues)
481 497 assert_equal 'text/csv; header=present', @response.content_type
482 498 assert @response.body.starts_with?("#,")
483 499 lines = @response.body.chomp.split("\n")
484 500 assert_equal assigns(:query).columns.size, lines[0].split(',').size
485 501 end
486 502
487 503 def test_index_csv_with_project
488 504 get :index, :project_id => 1, :format => 'csv'
489 505 assert_response :success
490 506 assert_not_nil assigns(:issues)
491 507 assert_equal 'text/csv; header=present', @response.content_type
492 508 end
493 509
494 510 def test_index_csv_with_description
495 511 Issue.generate!(:description => 'test_index_csv_with_description')
496 512
497 513 with_settings :default_language => 'en' do
498 514 get :index, :format => 'csv', :csv => {:description => '1'}
499 515 assert_response :success
500 516 assert_not_nil assigns(:issues)
501 517 end
502 518
503 519 assert_equal 'text/csv; header=present', response.content_type
504 520 headers = response.body.chomp.split("\n").first.split(',')
505 521 assert_include 'Description', headers
506 522 assert_include 'test_index_csv_with_description', response.body
507 523 end
508 524
509 525 def test_index_csv_with_spent_time_column
510 526 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :subject => 'test_index_csv_with_spent_time_column', :author_id => 2)
511 527 TimeEntry.create!(:project => issue.project, :issue => issue, :hours => 7.33, :user => User.find(2), :spent_on => Date.today)
512 528
513 529 get :index, :format => 'csv', :set_filter => '1', :c => %w(subject spent_hours)
514 530 assert_response :success
515 531 assert_equal 'text/csv; header=present', @response.content_type
516 532 lines = @response.body.chomp.split("\n")
517 533 assert_include "#{issue.id},#{issue.subject},7.33", lines
518 534 end
519 535
520 536 def test_index_csv_with_all_columns
521 537 get :index, :format => 'csv', :csv => {:columns => 'all'}
522 538 assert_response :success
523 539 assert_not_nil assigns(:issues)
524 540 assert_equal 'text/csv; header=present', @response.content_type
525 541 assert_match /\A#,/, response.body
526 542 lines = response.body.chomp.split("\n")
527 543 assert_equal assigns(:query).available_inline_columns.size, lines[0].split(',').size
528 544 end
529 545
530 546 def test_index_csv_with_multi_column_field
531 547 CustomField.find(1).update_attribute :multiple, true
532 548 issue = Issue.find(1)
533 549 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
534 550 issue.save!
535 551
536 552 get :index, :format => 'csv', :csv => {:columns => 'all'}
537 553 assert_response :success
538 554 lines = @response.body.chomp.split("\n")
539 555 assert lines.detect {|line| line.include?('"MySQL, Oracle"')}
540 556 end
541 557
542 558 def test_index_csv_should_format_float_custom_fields_with_csv_decimal_separator
543 559 field = IssueCustomField.create!(:name => 'Float', :is_for_all => true, :tracker_ids => [1], :field_format => 'float')
544 560 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id => '185.6'})
545 561
546 562 with_settings :default_language => 'fr' do
547 563 get :index, :format => 'csv', :csv => {:columns => 'all'}
548 564 assert_response :success
549 565 issue_line = response.body.chomp.split("\n").map {|line| line.split(';')}.detect {|line| line[0]==issue.id.to_s}
550 566 assert_include '185,60', issue_line
551 567 end
552 568
553 569 with_settings :default_language => 'en' do
554 570 get :index, :format => 'csv', :csv => {:columns => 'all'}
555 571 assert_response :success
556 572 issue_line = response.body.chomp.split("\n").map {|line| line.split(',')}.detect {|line| line[0]==issue.id.to_s}
557 573 assert_include '185.60', issue_line
558 574 end
559 575 end
560 576
561 577 def test_index_csv_should_fill_parent_column_with_parent_id
562 578 Issue.delete_all
563 579 parent = Issue.generate!
564 580 child = Issue.generate!(:parent_issue_id => parent.id)
565 581
566 582 with_settings :default_language => 'en' do
567 583 get :index, :format => 'csv', :c => %w(parent)
568 584 end
569 585 lines = response.body.split("\n")
570 586 assert_include "#{child.id},#{parent.id}", lines
571 587 end
572 588
573 589 def test_index_csv_big_5
574 590 with_settings :default_language => "zh-TW" do
575 591 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88".force_encoding('UTF-8')
576 592 str_big5 = "\xa4@\xa4\xeb".force_encoding('Big5')
577 593 issue = Issue.generate!(:subject => str_utf8)
578 594
579 595 get :index, :project_id => 1,
580 596 :f => ['subject'],
581 597 :op => '=', :values => [str_utf8],
582 598 :format => 'csv'
583 599 assert_equal 'text/csv; header=present', @response.content_type
584 600 lines = @response.body.chomp.split("\n")
585 601 header = lines[0]
586 602 status = "\xaa\xac\xbaA".force_encoding('Big5')
587 603 assert_include status, header
588 604 issue_line = lines.find {|l| l =~ /^#{issue.id},/}
589 605 assert_include str_big5, issue_line
590 606 end
591 607 end
592 608
593 609 def test_index_csv_cannot_convert_should_be_replaced_big_5
594 610 with_settings :default_language => "zh-TW" do
595 611 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85".force_encoding('UTF-8')
596 612 issue = Issue.generate!(:subject => str_utf8)
597 613
598 614 get :index, :project_id => 1,
599 615 :f => ['subject'],
600 616 :op => '=', :values => [str_utf8],
601 617 :c => ['status', 'subject'],
602 618 :format => 'csv',
603 619 :set_filter => 1
604 620 assert_equal 'text/csv; header=present', @response.content_type
605 621 lines = @response.body.chomp.split("\n")
606 622 header = lines[0]
607 623 issue_line = lines.find {|l| l =~ /^#{issue.id},/}
608 624 s1 = "\xaa\xac\xbaA".force_encoding('Big5') # status
609 625 assert header.include?(s1)
610 626 s2 = issue_line.split(",")[2]
611 627 s3 = "\xa5H?".force_encoding('Big5') # subject
612 628 assert_equal s3, s2
613 629 end
614 630 end
615 631
616 632 def test_index_csv_tw
617 633 with_settings :default_language => "zh-TW" do
618 634 str1 = "test_index_csv_tw"
619 635 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
620 636
621 637 get :index, :project_id => 1,
622 638 :f => ['subject'],
623 639 :op => '=', :values => [str1],
624 640 :c => ['estimated_hours', 'subject'],
625 641 :format => 'csv',
626 642 :set_filter => 1
627 643 assert_equal 'text/csv; header=present', @response.content_type
628 644 lines = @response.body.chomp.split("\n")
629 645 assert_include "#{issue.id},1234.50,#{str1}", lines
630 646 end
631 647 end
632 648
633 649 def test_index_csv_fr
634 650 with_settings :default_language => "fr" do
635 651 str1 = "test_index_csv_fr"
636 652 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
637 653
638 654 get :index, :project_id => 1,
639 655 :f => ['subject'],
640 656 :op => '=', :values => [str1],
641 657 :c => ['estimated_hours', 'subject'],
642 658 :format => 'csv',
643 659 :set_filter => 1
644 660 assert_equal 'text/csv; header=present', @response.content_type
645 661 lines = @response.body.chomp.split("\n")
646 662 assert_include "#{issue.id};1234,50;#{str1}", lines
647 663 end
648 664 end
649 665
650 666 def test_index_pdf
651 667 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
652 668 with_settings :default_language => lang do
653 669
654 670 get :index
655 671 assert_response :success
656 672 assert_template 'index'
657 673
658 674 get :index, :format => 'pdf'
659 675 assert_response :success
660 676 assert_not_nil assigns(:issues)
661 677 assert_equal 'application/pdf', @response.content_type
662 678
663 679 get :index, :project_id => 1, :format => 'pdf'
664 680 assert_response :success
665 681 assert_not_nil assigns(:issues)
666 682 assert_equal 'application/pdf', @response.content_type
667 683
668 684 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
669 685 assert_response :success
670 686 assert_not_nil assigns(:issues)
671 687 assert_equal 'application/pdf', @response.content_type
672 688 end
673 689 end
674 690 end
675 691
676 692 def test_index_pdf_with_query_grouped_by_list_custom_field
677 693 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
678 694 assert_response :success
679 695 assert_not_nil assigns(:issues)
680 696 assert_not_nil assigns(:issue_count_by_group)
681 697 assert_equal 'application/pdf', @response.content_type
682 698 end
683 699
684 700 def test_index_atom
685 701 get :index, :project_id => 'ecookbook', :format => 'atom'
686 702 assert_response :success
687 703 assert_template 'common/feed'
688 704 assert_equal 'application/atom+xml', response.content_type
689 705
690 706 assert_select 'feed' do
691 707 assert_select 'link[rel=self][href=?]', 'http://test.host/projects/ecookbook/issues.atom'
692 708 assert_select 'link[rel=alternate][href=?]', 'http://test.host/projects/ecookbook/issues'
693 709 assert_select 'entry link[href=?]', 'http://test.host/issues/1'
694 710 end
695 711 end
696 712
697 713 def test_index_sort
698 714 get :index, :sort => 'tracker,id:desc'
699 715 assert_response :success
700 716
701 717 sort_params = @request.session['issues_index_sort']
702 718 assert sort_params.is_a?(String)
703 719 assert_equal 'tracker,id:desc', sort_params
704 720
705 721 issues = assigns(:issues)
706 722 assert_not_nil issues
707 723 assert !issues.empty?
708 724 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
709 725 assert_select 'table.issues.sort-by-tracker.sort-asc'
710 726 end
711 727
712 728 def test_index_sort_by_field_not_included_in_columns
713 729 Setting.issue_list_default_columns = %w(subject author)
714 730 get :index, :sort => 'tracker'
715 731 end
716 732
717 733 def test_index_sort_by_assigned_to
718 734 get :index, :sort => 'assigned_to'
719 735 assert_response :success
720 736 assignees = assigns(:issues).collect(&:assigned_to).compact
721 737 assert_equal assignees.sort, assignees
722 738 assert_select 'table.issues.sort-by-assigned-to.sort-asc'
723 739 end
724 740
725 741 def test_index_sort_by_assigned_to_desc
726 742 get :index, :sort => 'assigned_to:desc'
727 743 assert_response :success
728 744 assignees = assigns(:issues).collect(&:assigned_to).compact
729 745 assert_equal assignees.sort.reverse, assignees
730 746 assert_select 'table.issues.sort-by-assigned-to.sort-desc'
731 747 end
732 748
733 749 def test_index_group_by_assigned_to
734 750 get :index, :group_by => 'assigned_to', :sort => 'priority'
735 751 assert_response :success
736 752 end
737 753
738 754 def test_index_sort_by_author
739 755 get :index, :sort => 'author'
740 756 assert_response :success
741 757 authors = assigns(:issues).collect(&:author)
742 758 assert_equal authors.sort, authors
743 759 end
744 760
745 761 def test_index_sort_by_author_desc
746 762 get :index, :sort => 'author:desc'
747 763 assert_response :success
748 764 authors = assigns(:issues).collect(&:author)
749 765 assert_equal authors.sort.reverse, authors
750 766 end
751 767
752 768 def test_index_group_by_author
753 769 get :index, :group_by => 'author', :sort => 'priority'
754 770 assert_response :success
755 771 end
756 772
757 773 def test_index_sort_by_spent_hours
758 774 get :index, :sort => 'spent_hours:desc'
759 775 assert_response :success
760 776 hours = assigns(:issues).collect(&:spent_hours)
761 777 assert_equal hours.sort.reverse, hours
762 778 end
763 779
764 780 def test_index_sort_by_total_spent_hours
765 781 get :index, :sort => 'total_spent_hours:desc'
766 782 assert_response :success
767 783 hours = assigns(:issues).collect(&:total_spent_hours)
768 784 assert_equal hours.sort.reverse, hours
769 785 end
770 786
771 787 def test_index_sort_by_total_estimated_hours
772 788 get :index, :sort => 'total_estimated_hours:desc'
773 789 assert_response :success
774 790 hours = assigns(:issues).collect(&:total_estimated_hours)
775 791 assert_equal hours.sort.reverse, hours
776 792 end
777 793
778 794 def test_index_sort_by_user_custom_field
779 795 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
780 796 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
781 797 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
782 798 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
783 799 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
784 800
785 801 get :index, :project_id => 1, :set_filter => 1, :sort => "cf_#{cf.id},id"
786 802 assert_response :success
787 803
788 804 assert_equal [2, 3, 1], assigns(:issues).select {|issue| issue.custom_field_value(cf).present?}.map(&:id)
789 805 end
790 806
791 807 def test_index_with_columns
792 808 columns = ['tracker', 'subject', 'assigned_to']
793 809 get :index, :set_filter => 1, :c => columns
794 810 assert_response :success
795 811
796 812 # query should use specified columns
797 813 query = assigns(:query)
798 814 assert_kind_of IssueQuery, query
799 815 assert_equal columns, query.column_names.map(&:to_s)
800 816
801 817 # columns should be stored in session
802 818 assert_kind_of Hash, session[:query]
803 819 assert_kind_of Array, session[:query][:column_names]
804 820 assert_equal columns, session[:query][:column_names].map(&:to_s)
805 821
806 822 # ensure only these columns are kept in the selected columns list
807 823 assert_select 'select#selected_columns option' do
808 824 assert_select 'option', 3
809 825 assert_select 'option[value=tracker]'
810 826 assert_select 'option[value=project]', 0
811 827 end
812 828 end
813 829
814 830 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
815 831 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
816 832 get :index, :set_filter => 1
817 833
818 834 # query should use specified columns
819 835 query = assigns(:query)
820 836 assert_kind_of IssueQuery, query
821 837 assert_equal [:id, :project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
822 838 end
823 839
824 840 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
825 841 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
826 842 columns = ['id', 'tracker', 'subject', 'assigned_to']
827 843 get :index, :set_filter => 1, :c => columns
828 844
829 845 # query should use specified columns
830 846 query = assigns(:query)
831 847 assert_kind_of IssueQuery, query
832 848 assert_equal columns.map(&:to_sym), query.columns.map(&:name)
833 849 end
834 850
835 851 def test_index_with_default_columns_should_respect_default_columns_order
836 852 columns = ['assigned_to', 'subject', 'status', 'tracker']
837 853 with_settings :issue_list_default_columns => columns do
838 854 get :index, :project_id => 1, :set_filter => 1
839 855
840 856 query = assigns(:query)
841 857 assert_equal (['id'] + columns).map(&:to_sym), query.columns.map(&:name)
842 858 end
843 859 end
844 860
845 861 def test_index_with_custom_field_column
846 862 columns = %w(tracker subject cf_2)
847 863 get :index, :set_filter => 1, :c => columns
848 864 assert_response :success
849 865
850 866 # query should use specified columns
851 867 query = assigns(:query)
852 868 assert_kind_of IssueQuery, query
853 869 assert_equal columns, query.column_names.map(&:to_s)
854 870
855 871 assert_select 'table.issues td.cf_2.string'
856 872 end
857 873
858 874 def test_index_with_multi_custom_field_column
859 875 field = CustomField.find(1)
860 876 field.update_attribute :multiple, true
861 877 issue = Issue.find(1)
862 878 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
863 879 issue.save!
864 880
865 881 get :index, :set_filter => 1, :c => %w(tracker subject cf_1)
866 882 assert_response :success
867 883
868 884 assert_select 'table.issues td.cf_1', :text => 'MySQL, Oracle'
869 885 end
870 886
871 887 def test_index_with_multi_user_custom_field_column
872 888 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
873 889 :tracker_ids => [1], :is_for_all => true)
874 890 issue = Issue.find(1)
875 891 issue.custom_field_values = {field.id => ['2', '3']}
876 892 issue.save!
877 893
878 894 get :index, :set_filter => 1, :c => ['tracker', 'subject', "cf_#{field.id}"]
879 895 assert_response :success
880 896
881 897 assert_select "table.issues td.cf_#{field.id}" do
882 898 assert_select 'a', 2
883 899 assert_select 'a[href=?]', '/users/2', :text => 'John Smith'
884 900 assert_select 'a[href=?]', '/users/3', :text => 'Dave Lopper'
885 901 end
886 902 end
887 903
888 904 def test_index_with_date_column
889 905 with_settings :date_format => '%d/%m/%Y' do
890 906 Issue.find(1).update_attribute :start_date, '1987-08-24'
891 907 get :index, :set_filter => 1, :c => %w(start_date)
892 908 assert_select "table.issues td.start_date", :text => '24/08/1987'
893 909 end
894 910 end
895 911
896 912 def test_index_with_done_ratio_column
897 913 Issue.find(1).update_attribute :done_ratio, 40
898 914 get :index, :set_filter => 1, :c => %w(done_ratio)
899 915 assert_select 'table.issues td.done_ratio' do
900 916 assert_select 'table.progress' do
901 917 assert_select 'td.closed[style=?]', 'width: 40%;'
902 918 end
903 919 end
904 920 end
905 921
906 922 def test_index_with_spent_hours_column
907 923 Issue.expects(:load_visible_spent_hours).once
908 924 get :index, :set_filter => 1, :c => %w(subject spent_hours)
909 925 assert_select 'table.issues tr#issue-3 td.spent_hours', :text => '1.00'
910 926 end
911 927
912 928 def test_index_with_total_spent_hours_column
913 929 Issue.expects(:load_visible_total_spent_hours).once
914 930 get :index, :set_filter => 1, :c => %w(subject total_spent_hours)
915 931 assert_select 'table.issues tr#issue-3 td.total_spent_hours', :text => '1.00'
916 932 end
917 933
918 934 def test_index_with_total_estimated_hours_column
919 935 get :index, :set_filter => 1, :c => %w(subject total_estimated_hours)
920 936 assert_select 'table.issues td.total_estimated_hours'
921 937 end
922 938
923 939 def test_index_should_not_show_spent_hours_column_without_permission
924 940 Role.anonymous.remove_permission! :view_time_entries
925 941 get :index, :set_filter => 1, :c => %w(subject spent_hours)
926 942 assert_select 'td.spent_hours', 0
927 943 end
928 944
929 945 def test_index_with_fixed_version_column
930 946 get :index, :set_filter => 1, :c => %w(fixed_version)
931 947 assert_select 'table.issues td.fixed_version' do
932 948 assert_select 'a[href=?]', '/versions/2', :text => 'eCookbook - 1.0'
933 949 end
934 950 end
935 951
936 952 def test_index_with_relations_column
937 953 IssueRelation.delete_all
938 954 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(7))
939 955 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(8), :issue_to => Issue.find(1))
940 956 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(1), :issue_to => Issue.find(11))
941 957 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(12), :issue_to => Issue.find(2))
942 958
943 959 get :index, :set_filter => 1, :c => %w(subject relations)
944 960 assert_response :success
945 961 assert_select "tr#issue-1 td.relations" do
946 962 assert_select "span", 3
947 963 assert_select "span", :text => "Related to #7"
948 964 assert_select "span", :text => "Related to #8"
949 965 assert_select "span", :text => "Blocks #11"
950 966 end
951 967 assert_select "tr#issue-2 td.relations" do
952 968 assert_select "span", 1
953 969 assert_select "span", :text => "Blocked by #12"
954 970 end
955 971 assert_select "tr#issue-3 td.relations" do
956 972 assert_select "span", 0
957 973 end
958 974
959 975 get :index, :set_filter => 1, :c => %w(relations), :format => 'csv'
960 976 assert_response :success
961 977 assert_equal 'text/csv; header=present', response.content_type
962 978 lines = response.body.chomp.split("\n")
963 979 assert_include '1,"Related to #7, Related to #8, Blocks #11"', lines
964 980 assert_include '2,Blocked by #12', lines
965 981 assert_include '3,""', lines
966 982
967 983 get :index, :set_filter => 1, :c => %w(subject relations), :format => 'pdf'
968 984 assert_response :success
969 985 assert_equal 'application/pdf', response.content_type
970 986 end
971 987
972 988 def test_index_with_description_column
973 989 get :index, :set_filter => 1, :c => %w(subject description)
974 990
975 991 assert_select 'table.issues thead th', 3 # columns: chekbox + id + subject
976 992 assert_select 'td.description[colspan="3"]', :text => 'Unable to print recipes'
977 993
978 994 get :index, :set_filter => 1, :c => %w(subject description), :format => 'pdf'
979 995 assert_response :success
980 996 assert_equal 'application/pdf', response.content_type
981 997 end
982 998
983 999 def test_index_with_parent_column
984 1000 Issue.delete_all
985 1001 parent = Issue.generate!
986 1002 child = Issue.generate!(:parent_issue_id => parent.id)
987 1003
988 1004 get :index, :c => %w(parent)
989 1005
990 1006 assert_select 'td.parent', :text => "#{parent.tracker} ##{parent.id}"
991 1007 assert_select 'td.parent a[title=?]', parent.subject
992 1008 end
993 1009
994 1010 def test_index_with_estimated_hours_total
995 1011 Issue.delete_all
996 1012 Issue.generate!(:estimated_hours => 5.5)
997 1013 Issue.generate!(:estimated_hours => 1.1)
998 1014
999 1015 get :index, :t => %w(estimated_hours)
1000 1016 assert_response :success
1001 1017 assert_select '.query-totals'
1002 1018 assert_select '.total-for-estimated-hours span.value', :text => '6.60'
1003 1019 assert_select 'input[type=checkbox][name=?][value=estimated_hours][checked=checked]', 't[]'
1004 1020 end
1005 1021
1006 1022 def test_index_with_grouped_query_and_estimated_hours_total
1007 1023 Issue.delete_all
1008 1024 Issue.generate!(:estimated_hours => 5.5, :category_id => 1)
1009 1025 Issue.generate!(:estimated_hours => 2.3, :category_id => 1)
1010 1026 Issue.generate!(:estimated_hours => 1.1, :category_id => 2)
1011 1027 Issue.generate!(:estimated_hours => 4.6)
1012 1028
1013 1029 get :index, :t => %w(estimated_hours), :group_by => 'category'
1014 1030 assert_response :success
1015 1031 assert_select '.query-totals'
1016 1032 assert_select '.query-totals .total-for-estimated-hours span.value', :text => '13.50'
1017 1033 assert_select 'tr.group', :text => /Printing/ do
1018 1034 assert_select '.total-for-estimated-hours span.value', :text => '7.80'
1019 1035 end
1020 1036 assert_select 'tr.group', :text => /Recipes/ do
1021 1037 assert_select '.total-for-estimated-hours span.value', :text => '1.10'
1022 1038 end
1023 1039 assert_select 'tr.group', :text => /blank/ do
1024 1040 assert_select '.total-for-estimated-hours span.value', :text => '4.60'
1025 1041 end
1026 1042 end
1027 1043
1028 1044 def test_index_with_int_custom_field_total
1029 1045 field = IssueCustomField.generate!(:field_format => 'int', :is_for_all => true)
1030 1046 CustomValue.create!(:customized => Issue.find(1), :custom_field => field, :value => '2')
1031 1047 CustomValue.create!(:customized => Issue.find(2), :custom_field => field, :value => '7')
1032 1048
1033 1049 get :index, :t => ["cf_#{field.id}"]
1034 1050 assert_response :success
1035 1051 assert_select '.query-totals'
1036 1052 assert_select ".total-for-cf-#{field.id} span.value", :text => '9'
1037 1053 end
1038 1054
1039 1055 def test_index_totals_should_default_to_settings
1040 1056 with_settings :issue_list_default_totals => ['estimated_hours'] do
1041 1057 get :index
1042 1058 assert_response :success
1043 1059 assert_select '.total-for-estimated-hours span.value'
1044 1060 assert_select '.query-totals>span', 1
1045 1061 end
1046 1062 end
1047 1063
1048 1064 def test_index_send_html_if_query_is_invalid
1049 1065 get :index, :f => ['start_date'], :op => {:start_date => '='}
1050 1066 assert_equal 'text/html', @response.content_type
1051 1067 assert_template 'index'
1052 1068 end
1053 1069
1054 1070 def test_index_send_nothing_if_query_is_invalid
1055 1071 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
1056 1072 assert_equal 'text/csv', @response.content_type
1057 1073 assert @response.body.blank?
1058 1074 end
1059 1075
1060 1076 def test_index_should_include_new_issue_link
1061 1077 @request.session[:user_id] = 2
1062 1078 get :index, :project_id => 1
1063 1079 assert_select '#content a.new-issue[href="/projects/ecookbook/issues/new"]', :text => 'New issue'
1064 1080 end
1065 1081
1066 1082 def test_index_should_not_include_new_issue_link_for_project_without_trackers
1067 1083 Project.find(1).trackers.clear
1068 1084
1069 1085 @request.session[:user_id] = 2
1070 1086 get :index, :project_id => 1
1071 1087 assert_select '#content a.new-issue', 0
1072 1088 end
1073 1089
1074 1090 def test_index_should_not_include_new_issue_link_for_users_with_copy_issues_permission_only
1075 1091 role = Role.find(1)
1076 1092 role.remove_permission! :add_issues
1077 1093 role.add_permission! :copy_issues
1078 1094
1079 1095 @request.session[:user_id] = 2
1080 1096 get :index, :project_id => 1
1081 1097 assert_select '#content a.new-issue', 0
1082 1098 end
1083 1099
1084 1100 def test_index_without_project_should_include_new_issue_link
1085 1101 @request.session[:user_id] = 2
1086 1102 get :index
1087 1103 assert_select '#content a.new-issue[href="/issues/new"]', :text => 'New issue'
1088 1104 end
1089 1105
1090 1106 def test_index_should_not_include_new_issue_tab_when_disabled
1091 1107 with_settings :new_project_issue_tab_enabled => '0' do
1092 1108 @request.session[:user_id] = 2
1093 1109 get :index, :project_id => 1
1094 1110 assert_select '#main-menu a.new-issue', 0
1095 1111 end
1096 1112 end
1097 1113
1098 1114 def test_index_should_include_new_issue_tab_when_enabled
1099 1115 with_settings :new_project_issue_tab_enabled => '1' do
1100 1116 @request.session[:user_id] = 2
1101 1117 get :index, :project_id => 1
1102 1118 assert_select '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]', :text => 'New issue'
1103 1119 end
1104 1120 end
1105 1121
1106 1122 def test_new_should_have_new_issue_tab_as_current_menu_item
1107 1123 with_settings :new_project_issue_tab_enabled => '1' do
1108 1124 @request.session[:user_id] = 2
1109 1125 get :new, :project_id => 1
1110 1126 assert_select '#main-menu a.new-issue.selected'
1111 1127 end
1112 1128 end
1113 1129
1114 1130 def test_index_should_not_include_new_issue_tab_for_project_without_trackers
1115 1131 with_settings :new_project_issue_tab_enabled => '1' do
1116 1132 Project.find(1).trackers.clear
1117 1133
1118 1134 @request.session[:user_id] = 2
1119 1135 get :index, :project_id => 1
1120 1136 assert_select '#main-menu a.new-issue', 0
1121 1137 end
1122 1138 end
1123 1139
1124 1140 def test_index_should_not_include_new_issue_tab_for_users_with_copy_issues_permission_only
1125 1141 with_settings :new_project_issue_tab_enabled => '1' do
1126 1142 role = Role.find(1)
1127 1143 role.remove_permission! :add_issues
1128 1144 role.add_permission! :copy_issues
1129 1145
1130 1146 @request.session[:user_id] = 2
1131 1147 get :index, :project_id => 1
1132 1148 assert_select '#main-menu a.new-issue', 0
1133 1149 end
1134 1150 end
1135 1151
1136 1152 def test_show_by_anonymous
1137 1153 get :show, :id => 1
1138 1154 assert_response :success
1139 1155 assert_template 'show'
1140 1156 assert_equal Issue.find(1), assigns(:issue)
1141 1157 assert_select 'div.issue div.description', :text => /Unable to print recipes/
1142 1158 # anonymous role is allowed to add a note
1143 1159 assert_select 'form#issue-form' do
1144 1160 assert_select 'fieldset' do
1145 1161 assert_select 'legend', :text => 'Notes'
1146 1162 assert_select 'textarea[name=?]', 'issue[notes]'
1147 1163 end
1148 1164 end
1149 1165 assert_select 'title', :text => "Bug #1: Cannot print recipes - eCookbook - Redmine"
1150 1166 end
1151 1167
1152 1168 def test_show_by_manager
1153 1169 @request.session[:user_id] = 2
1154 1170 get :show, :id => 1
1155 1171 assert_response :success
1156 1172 assert_select 'a', :text => /Quote/
1157 1173 assert_select 'form#issue-form' do
1158 1174 assert_select 'fieldset' do
1159 1175 assert_select 'legend', :text => 'Change properties'
1160 1176 assert_select 'input[name=?]', 'issue[subject]'
1161 1177 end
1162 1178 assert_select 'fieldset' do
1163 1179 assert_select 'legend', :text => 'Log time'
1164 1180 assert_select 'input[name=?]', 'time_entry[hours]'
1165 1181 end
1166 1182 assert_select 'fieldset' do
1167 1183 assert_select 'legend', :text => 'Notes'
1168 1184 assert_select 'textarea[name=?]', 'issue[notes]'
1169 1185 end
1170 1186 end
1171 1187 end
1172 1188
1173 1189 def test_show_should_display_update_form
1174 1190 @request.session[:user_id] = 2
1175 1191 get :show, :id => 1
1176 1192 assert_response :success
1177 1193
1178 1194 assert_select 'form#issue-form' do
1179 1195 assert_select 'input[name=?]', 'issue[is_private]'
1180 1196 assert_select 'select[name=?]', 'issue[project_id]'
1181 1197 assert_select 'select[name=?]', 'issue[tracker_id]'
1182 1198 assert_select 'input[name=?]', 'issue[subject]'
1183 1199 assert_select 'textarea[name=?]', 'issue[description]'
1184 1200 assert_select 'select[name=?]', 'issue[status_id]'
1185 1201 assert_select 'select[name=?]', 'issue[priority_id]'
1186 1202 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1187 1203 assert_select 'select[name=?]', 'issue[category_id]'
1188 1204 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1189 1205 assert_select 'input[name=?]', 'issue[parent_issue_id]'
1190 1206 assert_select 'input[name=?]', 'issue[start_date]'
1191 1207 assert_select 'input[name=?]', 'issue[due_date]'
1192 1208 assert_select 'select[name=?]', 'issue[done_ratio]'
1193 1209 assert_select 'input[name=?]', 'issue[custom_field_values][2]'
1194 1210 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1195 1211 assert_select 'textarea[name=?]', 'issue[notes]'
1196 1212 end
1197 1213 end
1198 1214
1199 1215 def test_show_should_display_update_form_with_minimal_permissions
1200 1216 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
1201 1217 WorkflowTransition.delete_all :role_id => 1
1202 1218
1203 1219 @request.session[:user_id] = 2
1204 1220 get :show, :id => 1
1205 1221 assert_response :success
1206 1222
1207 1223 assert_select 'form#issue-form' do
1208 1224 assert_select 'input[name=?]', 'issue[is_private]', 0
1209 1225 assert_select 'select[name=?]', 'issue[project_id]', 0
1210 1226 assert_select 'select[name=?]', 'issue[tracker_id]', 0
1211 1227 assert_select 'input[name=?]', 'issue[subject]', 0
1212 1228 assert_select 'textarea[name=?]', 'issue[description]', 0
1213 1229 assert_select 'select[name=?]', 'issue[status_id]', 0
1214 1230 assert_select 'select[name=?]', 'issue[priority_id]', 0
1215 1231 assert_select 'select[name=?]', 'issue[assigned_to_id]', 0
1216 1232 assert_select 'select[name=?]', 'issue[category_id]', 0
1217 1233 assert_select 'select[name=?]', 'issue[fixed_version_id]', 0
1218 1234 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
1219 1235 assert_select 'input[name=?]', 'issue[start_date]', 0
1220 1236 assert_select 'input[name=?]', 'issue[due_date]', 0
1221 1237 assert_select 'select[name=?]', 'issue[done_ratio]', 0
1222 1238 assert_select 'input[name=?]', 'issue[custom_field_values][2]', 0
1223 1239 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1224 1240 assert_select 'textarea[name=?]', 'issue[notes]'
1225 1241 end
1226 1242 end
1227 1243
1228 1244 def test_show_should_not_display_update_form_without_permissions
1229 1245 Role.find(1).update_attribute :permissions, [:view_issues]
1230 1246
1231 1247 @request.session[:user_id] = 2
1232 1248 get :show, :id => 1
1233 1249 assert_response :success
1234 1250
1235 1251 assert_select 'form#issue-form', 0
1236 1252 end
1237 1253
1238 1254 def test_update_form_should_not_display_inactive_enumerations
1239 1255 assert !IssuePriority.find(15).active?
1240 1256
1241 1257 @request.session[:user_id] = 2
1242 1258 get :show, :id => 1
1243 1259 assert_response :success
1244 1260
1245 1261 assert_select 'form#issue-form' do
1246 1262 assert_select 'select[name=?]', 'issue[priority_id]' do
1247 1263 assert_select 'option[value="4"]'
1248 1264 assert_select 'option[value="15"]', 0
1249 1265 end
1250 1266 end
1251 1267 end
1252 1268
1253 1269 def test_update_form_should_allow_attachment_upload
1254 1270 @request.session[:user_id] = 2
1255 1271 get :show, :id => 1
1256 1272
1257 1273 assert_select 'form#issue-form[method=post][enctype="multipart/form-data"]' do
1258 1274 assert_select 'input[type=file][name=?]', 'attachments[dummy][file]'
1259 1275 end
1260 1276 end
1261 1277
1262 1278 def test_show_should_deny_anonymous_access_without_permission
1263 1279 Role.anonymous.remove_permission!(:view_issues)
1264 1280 get :show, :id => 1
1265 1281 assert_response :redirect
1266 1282 end
1267 1283
1268 1284 def test_show_should_deny_anonymous_access_to_private_issue
1269 1285 Issue.where(:id => 1).update_all(["is_private = ?", true])
1270 1286 get :show, :id => 1
1271 1287 assert_response :redirect
1272 1288 end
1273 1289
1274 1290 def test_show_should_deny_non_member_access_without_permission
1275 1291 Role.non_member.remove_permission!(:view_issues)
1276 1292 @request.session[:user_id] = 9
1277 1293 get :show, :id => 1
1278 1294 assert_response 403
1279 1295 end
1280 1296
1281 1297 def test_show_should_deny_non_member_access_to_private_issue
1282 1298 Issue.where(:id => 1).update_all(["is_private = ?", true])
1283 1299 @request.session[:user_id] = 9
1284 1300 get :show, :id => 1
1285 1301 assert_response 403
1286 1302 end
1287 1303
1288 1304 def test_show_should_deny_member_access_without_permission
1289 1305 Role.find(1).remove_permission!(:view_issues)
1290 1306 @request.session[:user_id] = 2
1291 1307 get :show, :id => 1
1292 1308 assert_response 403
1293 1309 end
1294 1310
1295 1311 def test_show_should_deny_member_access_to_private_issue_without_permission
1296 1312 Issue.where(:id => 1).update_all(["is_private = ?", true])
1297 1313 @request.session[:user_id] = 3
1298 1314 get :show, :id => 1
1299 1315 assert_response 403
1300 1316 end
1301 1317
1302 1318 def test_show_should_allow_author_access_to_private_issue
1303 1319 Issue.where(:id => 1).update_all(["is_private = ?, author_id = 3", true])
1304 1320 @request.session[:user_id] = 3
1305 1321 get :show, :id => 1
1306 1322 assert_response :success
1307 1323 end
1308 1324
1309 1325 def test_show_should_allow_assignee_access_to_private_issue
1310 1326 Issue.where(:id => 1).update_all(["is_private = ?, assigned_to_id = 3", true])
1311 1327 @request.session[:user_id] = 3
1312 1328 get :show, :id => 1
1313 1329 assert_response :success
1314 1330 end
1315 1331
1316 1332 def test_show_should_allow_member_access_to_private_issue_with_permission
1317 1333 Issue.where(:id => 1).update_all(["is_private = ?", true])
1318 1334 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
1319 1335 @request.session[:user_id] = 3
1320 1336 get :show, :id => 1
1321 1337 assert_response :success
1322 1338 end
1323 1339
1324 1340 def test_show_should_not_disclose_relations_to_invisible_issues
1325 1341 Setting.cross_project_issue_relations = '1'
1326 1342 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
1327 1343 # Relation to a private project issue
1328 1344 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
1329 1345
1330 1346 get :show, :id => 1
1331 1347 assert_response :success
1332 1348
1333 1349 assert_select 'div#relations' do
1334 1350 assert_select 'a', :text => /#2$/
1335 1351 assert_select 'a', :text => /#4$/, :count => 0
1336 1352 end
1337 1353 end
1338 1354
1339 1355 def test_show_should_list_subtasks
1340 1356 Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1341 1357
1342 1358 get :show, :id => 1
1343 1359 assert_response :success
1344 1360
1345 1361 assert_select 'div#issue_tree' do
1346 1362 assert_select 'td.subject', :text => /Child Issue/
1347 1363 end
1348 1364 end
1349 1365
1350 1366 def test_show_should_list_parents
1351 1367 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1352 1368
1353 1369 get :show, :id => issue.id
1354 1370 assert_response :success
1355 1371
1356 1372 assert_select 'div.subject' do
1357 1373 assert_select 'h3', 'Child Issue'
1358 1374 assert_select 'a[href="/issues/1"]'
1359 1375 end
1360 1376 end
1361 1377
1362 1378 def test_show_should_not_display_prev_next_links_without_query_in_session
1363 1379 get :show, :id => 1
1364 1380 assert_response :success
1365 1381 assert_nil assigns(:prev_issue_id)
1366 1382 assert_nil assigns(:next_issue_id)
1367 1383
1368 1384 assert_select 'div.next-prev-links', 0
1369 1385 end
1370 1386
1371 1387 def test_show_should_display_prev_next_links_with_query_in_session
1372 1388 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1373 1389 @request.session['issues_index_sort'] = 'id'
1374 1390
1375 1391 with_settings :display_subprojects_issues => '0' do
1376 1392 get :show, :id => 3
1377 1393 end
1378 1394
1379 1395 assert_response :success
1380 1396 # Previous and next issues for all projects
1381 1397 assert_equal 2, assigns(:prev_issue_id)
1382 1398 assert_equal 5, assigns(:next_issue_id)
1383 1399
1384 1400 count = Issue.open.visible.count
1385 1401
1386 1402 assert_select 'div.next-prev-links' do
1387 1403 assert_select 'a[href="/issues/2"]', :text => /Previous/
1388 1404 assert_select 'a[href="/issues/5"]', :text => /Next/
1389 1405 assert_select 'span.position', :text => "3 of #{count}"
1390 1406 end
1391 1407 end
1392 1408
1393 1409 def test_show_should_display_prev_next_links_with_saved_query_in_session
1394 1410 query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC, :user_id => 1,
1395 1411 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
1396 1412 :sort_criteria => [['id', 'asc']])
1397 1413 @request.session[:query] = {:id => query.id, :project_id => nil}
1398 1414
1399 1415 get :show, :id => 11
1400 1416
1401 1417 assert_response :success
1402 1418 assert_equal query, assigns(:query)
1403 1419 # Previous and next issues for all projects
1404 1420 assert_equal 8, assigns(:prev_issue_id)
1405 1421 assert_equal 12, assigns(:next_issue_id)
1406 1422
1407 1423 assert_select 'div.next-prev-links' do
1408 1424 assert_select 'a[href="/issues/8"]', :text => /Previous/
1409 1425 assert_select 'a[href="/issues/12"]', :text => /Next/
1410 1426 end
1411 1427 end
1412 1428
1413 1429 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
1414 1430 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1415 1431
1416 1432 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
1417 1433 @request.session['issues_index_sort'] = assoc_sort
1418 1434
1419 1435 get :show, :id => 3
1420 1436 assert_response :success, "Wrong response status for #{assoc_sort} sort"
1421 1437
1422 1438 assert_select 'div.next-prev-links' do
1423 1439 assert_select 'a', :text => /(Previous|Next)/
1424 1440 end
1425 1441 end
1426 1442 end
1427 1443
1428 1444 def test_show_should_display_prev_next_links_with_project_query_in_session
1429 1445 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1430 1446 @request.session['issues_index_sort'] = 'id'
1431 1447
1432 1448 with_settings :display_subprojects_issues => '0' do
1433 1449 get :show, :id => 3
1434 1450 end
1435 1451
1436 1452 assert_response :success
1437 1453 # Previous and next issues inside project
1438 1454 assert_equal 2, assigns(:prev_issue_id)
1439 1455 assert_equal 7, assigns(:next_issue_id)
1440 1456
1441 1457 assert_select 'div.next-prev-links' do
1442 1458 assert_select 'a[href="/issues/2"]', :text => /Previous/
1443 1459 assert_select 'a[href="/issues/7"]', :text => /Next/
1444 1460 end
1445 1461 end
1446 1462
1447 1463 def test_show_should_not_display_prev_link_for_first_issue
1448 1464 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1449 1465 @request.session['issues_index_sort'] = 'id'
1450 1466
1451 1467 with_settings :display_subprojects_issues => '0' do
1452 1468 get :show, :id => 1
1453 1469 end
1454 1470
1455 1471 assert_response :success
1456 1472 assert_nil assigns(:prev_issue_id)
1457 1473 assert_equal 2, assigns(:next_issue_id)
1458 1474
1459 1475 assert_select 'div.next-prev-links' do
1460 1476 assert_select 'a', :text => /Previous/, :count => 0
1461 1477 assert_select 'a[href="/issues/2"]', :text => /Next/
1462 1478 end
1463 1479 end
1464 1480
1465 1481 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1466 1482 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1467 1483 @request.session['issues_index_sort'] = 'id'
1468 1484
1469 1485 get :show, :id => 1
1470 1486
1471 1487 assert_response :success
1472 1488 assert_nil assigns(:prev_issue_id)
1473 1489 assert_nil assigns(:next_issue_id)
1474 1490
1475 1491 assert_select 'a', :text => /Previous/, :count => 0
1476 1492 assert_select 'a', :text => /Next/, :count => 0
1477 1493 end
1478 1494
1479 1495 def test_show_show_should_display_prev_next_links_with_query_sort_by_user_custom_field
1480 1496 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
1481 1497 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
1482 1498 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
1483 1499 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
1484 1500 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
1485 1501
1486 1502 query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC, :user_id => 1, :filters => {},
1487 1503 :sort_criteria => [["cf_#{cf.id}", 'asc'], ['id', 'asc']])
1488 1504 @request.session[:query] = {:id => query.id, :project_id => nil}
1489 1505
1490 1506 get :show, :id => 3
1491 1507 assert_response :success
1492 1508
1493 1509 assert_equal 2, assigns(:prev_issue_id)
1494 1510 assert_equal 1, assigns(:next_issue_id)
1495 1511
1496 1512 assert_select 'div.next-prev-links' do
1497 1513 assert_select 'a[href="/issues/2"]', :text => /Previous/
1498 1514 assert_select 'a[href="/issues/1"]', :text => /Next/
1499 1515 end
1500 1516 end
1501 1517
1502 1518 def test_show_should_display_prev_next_links_when_request_has_previous_and_next_issue_ids_params
1503 1519 get :show, :id => 1, :prev_issue_id => 1, :next_issue_id => 3, :issue_position => 2, :issue_count => 4
1504 1520 assert_response :success
1505 1521
1506 1522 assert_select 'div.next-prev-links' do
1507 1523 assert_select 'a[href="/issues/1"]', :text => /Previous/
1508 1524 assert_select 'a[href="/issues/3"]', :text => /Next/
1509 1525 assert_select 'span.position', :text => "2 of 4"
1510 1526 end
1511 1527 end
1512 1528
1513 1529 def test_show_should_display_category_field_if_categories_are_defined
1514 1530 Issue.update_all :category_id => nil
1515 1531
1516 1532 get :show, :id => 1
1517 1533 assert_response :success
1518 1534 assert_select '.attributes .category'
1519 1535 end
1520 1536
1521 1537 def test_show_should_not_display_category_field_if_no_categories_are_defined
1522 1538 Project.find(1).issue_categories.delete_all
1523 1539
1524 1540 get :show, :id => 1
1525 1541 assert_response :success
1526 1542 assert_select 'table.attributes .category', 0
1527 1543 end
1528 1544
1529 1545 def test_show_should_display_link_to_the_assignee
1530 1546 get :show, :id => 2
1531 1547 assert_response :success
1532 1548 assert_select '.assigned-to' do
1533 1549 assert_select 'a[href="/users/3"]'
1534 1550 end
1535 1551 end
1536 1552
1537 1553 def test_show_should_display_visible_changesets_from_other_projects
1538 1554 project = Project.find(2)
1539 1555 issue = project.issues.first
1540 1556 issue.changeset_ids = [102]
1541 1557 issue.save!
1542 1558 # changesets from other projects should be displayed even if repository
1543 1559 # is disabled on issue's project
1544 1560 project.disable_module! :repository
1545 1561
1546 1562 @request.session[:user_id] = 2
1547 1563 get :show, :id => issue.id
1548 1564
1549 1565 assert_select 'a[href=?]', '/projects/ecookbook/repository/revisions/3'
1550 1566 end
1551 1567
1552 1568 def test_show_should_display_watchers
1553 1569 @request.session[:user_id] = 2
1554 1570 Issue.find(1).add_watcher User.find(2)
1555 1571
1556 1572 get :show, :id => 1
1557 1573 assert_select 'div#watchers ul' do
1558 1574 assert_select 'li' do
1559 1575 assert_select 'a[href="/users/2"]'
1560 1576 assert_select 'a[class*=delete]'
1561 1577 end
1562 1578 end
1563 1579 end
1564 1580
1565 1581 def test_show_should_display_watchers_with_gravatars
1566 1582 @request.session[:user_id] = 2
1567 1583 Issue.find(1).add_watcher User.find(2)
1568 1584
1569 1585 with_settings :gravatar_enabled => '1' do
1570 1586 get :show, :id => 1
1571 1587 end
1572 1588
1573 1589 assert_select 'div#watchers ul' do
1574 1590 assert_select 'li' do
1575 1591 assert_select 'img.gravatar'
1576 1592 assert_select 'a[href="/users/2"]'
1577 1593 assert_select 'a[class*=delete]'
1578 1594 end
1579 1595 end
1580 1596 end
1581 1597
1582 1598 def test_show_with_thumbnails_enabled_should_display_thumbnails
1583 1599 @request.session[:user_id] = 2
1584 1600
1585 1601 with_settings :thumbnails_enabled => '1' do
1586 1602 get :show, :id => 14
1587 1603 assert_response :success
1588 1604 end
1589 1605
1590 1606 assert_select 'div.thumbnails' do
1591 1607 assert_select 'a[href="/attachments/16/testfile.png"]' do
1592 1608 assert_select 'img[src="/attachments/thumbnail/16"]'
1593 1609 end
1594 1610 end
1595 1611 end
1596 1612
1597 1613 def test_show_with_thumbnails_disabled_should_not_display_thumbnails
1598 1614 @request.session[:user_id] = 2
1599 1615
1600 1616 with_settings :thumbnails_enabled => '0' do
1601 1617 get :show, :id => 14
1602 1618 assert_response :success
1603 1619 end
1604 1620
1605 1621 assert_select 'div.thumbnails', 0
1606 1622 end
1607 1623
1608 1624 def test_show_with_multi_custom_field
1609 1625 field = CustomField.find(1)
1610 1626 field.update_attribute :multiple, true
1611 1627 issue = Issue.find(1)
1612 1628 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1613 1629 issue.save!
1614 1630
1615 1631 get :show, :id => 1
1616 1632 assert_response :success
1617 1633
1618 1634 assert_select ".cf_1 .value", :text => 'MySQL, Oracle'
1619 1635 end
1620 1636
1621 1637 def test_show_with_multi_user_custom_field
1622 1638 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1623 1639 :tracker_ids => [1], :is_for_all => true)
1624 1640 issue = Issue.find(1)
1625 1641 issue.custom_field_values = {field.id => ['2', '3']}
1626 1642 issue.save!
1627 1643
1628 1644 get :show, :id => 1
1629 1645 assert_response :success
1630 1646
1631 1647 assert_select ".cf_#{field.id} .value", :text => 'Dave Lopper, John Smith' do
1632 1648 assert_select 'a', :text => 'Dave Lopper'
1633 1649 assert_select 'a', :text => 'John Smith'
1634 1650 end
1635 1651 end
1636 1652
1637 1653 def test_show_should_display_private_notes_with_permission_only
1638 1654 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true, :user_id => 1)
1639 1655 @request.session[:user_id] = 2
1640 1656
1641 1657 get :show, :id => 2
1642 1658 assert_response :success
1643 1659 assert_include journal, assigns(:journals)
1644 1660
1645 1661 Role.find(1).remove_permission! :view_private_notes
1646 1662 get :show, :id => 2
1647 1663 assert_response :success
1648 1664 assert_not_include journal, assigns(:journals)
1649 1665 end
1650 1666
1651 1667 def test_show_atom
1652 1668 get :show, :id => 2, :format => 'atom'
1653 1669 assert_response :success
1654 1670 assert_template 'journals/index'
1655 1671 # Inline image
1656 1672 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1657 1673 end
1658 1674
1659 1675 def test_show_export_to_pdf
1660 1676 issue = Issue.find(3)
1661 1677 assert issue.relations.select{|r| r.other_issue(issue).visible?}.present?
1662 1678 get :show, :id => 3, :format => 'pdf'
1663 1679 assert_response :success
1664 1680 assert_equal 'application/pdf', @response.content_type
1665 1681 assert @response.body.starts_with?('%PDF')
1666 1682 assert_not_nil assigns(:issue)
1667 1683 end
1668 1684
1669 1685 def test_export_to_pdf_with_utf8_u_fffd
1670 1686 # U+FFFD
1671 1687 s = "\xef\xbf\xbd"
1672 1688 s.force_encoding('UTF-8') if s.respond_to?(:force_encoding)
1673 1689 issue = Issue.generate!(:subject => s)
1674 1690 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
1675 1691 with_settings :default_language => lang do
1676 1692 get :show, :id => issue.id, :format => 'pdf'
1677 1693 assert_response :success
1678 1694 assert_equal 'application/pdf', @response.content_type
1679 1695 assert @response.body.starts_with?('%PDF')
1680 1696 assert_not_nil assigns(:issue)
1681 1697 end
1682 1698 end
1683 1699 end
1684 1700
1685 1701 def test_show_export_to_pdf_with_ancestors
1686 1702 issue = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1687 1703
1688 1704 get :show, :id => issue.id, :format => 'pdf'
1689 1705 assert_response :success
1690 1706 assert_equal 'application/pdf', @response.content_type
1691 1707 assert @response.body.starts_with?('%PDF')
1692 1708 end
1693 1709
1694 1710 def test_show_export_to_pdf_with_descendants
1695 1711 c1 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1696 1712 c2 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1697 1713 c3 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => c1.id)
1698 1714
1699 1715 get :show, :id => 1, :format => 'pdf'
1700 1716 assert_response :success
1701 1717 assert_equal 'application/pdf', @response.content_type
1702 1718 assert @response.body.starts_with?('%PDF')
1703 1719 end
1704 1720
1705 1721 def test_show_export_to_pdf_with_journals
1706 1722 get :show, :id => 1, :format => 'pdf'
1707 1723 assert_response :success
1708 1724 assert_equal 'application/pdf', @response.content_type
1709 1725 assert @response.body.starts_with?('%PDF')
1710 1726 end
1711 1727
1712 1728 def test_show_export_to_pdf_with_changesets
1713 1729 [[100], [100, 101], [100, 101, 102]].each do |cs|
1714 1730 issue1 = Issue.find(3)
1715 1731 issue1.changesets = Changeset.find(cs)
1716 1732 issue1.save!
1717 1733 issue = Issue.find(3)
1718 1734 assert_equal issue.changesets.count, cs.size
1719 1735 get :show, :id => 3, :format => 'pdf'
1720 1736 assert_response :success
1721 1737 assert_equal 'application/pdf', @response.content_type
1722 1738 assert @response.body.starts_with?('%PDF')
1723 1739 end
1724 1740 end
1725 1741
1726 1742 def test_show_invalid_should_respond_with_404
1727 1743 get :show, :id => 999
1728 1744 assert_response 404
1729 1745 end
1730 1746
1731 1747 def test_get_new
1732 1748 @request.session[:user_id] = 2
1733 1749 get :new, :project_id => 1, :tracker_id => 1
1734 1750 assert_response :success
1735 1751 assert_template 'new'
1736 1752
1737 1753 assert_select 'form#issue-form[action=?]', '/projects/ecookbook/issues'
1738 1754 assert_select 'form#issue-form' do
1739 1755 assert_select 'input[name=?]', 'issue[is_private]'
1740 1756 assert_select 'select[name=?]', 'issue[project_id]', 0
1741 1757 assert_select 'select[name=?]', 'issue[tracker_id]'
1742 1758 assert_select 'input[name=?]', 'issue[subject]'
1743 1759 assert_select 'textarea[name=?]', 'issue[description]'
1744 1760 assert_select 'select[name=?]', 'issue[status_id]'
1745 1761 assert_select 'select[name=?]', 'issue[priority_id]'
1746 1762 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1747 1763 assert_select 'select[name=?]', 'issue[category_id]'
1748 1764 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1749 1765 assert_select 'input[name=?]', 'issue[parent_issue_id]'
1750 1766 assert_select 'input[name=?]', 'issue[start_date]'
1751 1767 assert_select 'input[name=?]', 'issue[due_date]'
1752 1768 assert_select 'select[name=?]', 'issue[done_ratio]'
1753 1769 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
1754 1770 assert_select 'input[name=?]', 'issue[watcher_user_ids][]'
1755 1771 end
1756 1772
1757 1773 # Be sure we don't display inactive IssuePriorities
1758 1774 assert ! IssuePriority.find(15).active?
1759 1775 assert_select 'select[name=?]', 'issue[priority_id]' do
1760 1776 assert_select 'option[value="15"]', 0
1761 1777 end
1762 1778 end
1763 1779
1764 1780 def test_get_new_with_minimal_permissions
1765 1781 Role.find(1).update_attribute :permissions, [:add_issues]
1766 1782 WorkflowTransition.delete_all :role_id => 1
1767 1783
1768 1784 @request.session[:user_id] = 2
1769 1785 get :new, :project_id => 1, :tracker_id => 1
1770 1786 assert_response :success
1771 1787 assert_template 'new'
1772 1788
1773 1789 assert_select 'form#issue-form' do
1774 1790 assert_select 'input[name=?]', 'issue[is_private]', 0
1775 1791 assert_select 'select[name=?]', 'issue[project_id]', 0
1776 1792 assert_select 'select[name=?]', 'issue[tracker_id]'
1777 1793 assert_select 'input[name=?]', 'issue[subject]'
1778 1794 assert_select 'textarea[name=?]', 'issue[description]'
1779 1795 assert_select 'select[name=?]', 'issue[status_id]'
1780 1796 assert_select 'select[name=?]', 'issue[priority_id]'
1781 1797 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1782 1798 assert_select 'select[name=?]', 'issue[category_id]'
1783 1799 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1784 1800 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
1785 1801 assert_select 'input[name=?]', 'issue[start_date]'
1786 1802 assert_select 'input[name=?]', 'issue[due_date]'
1787 1803 assert_select 'select[name=?]', 'issue[done_ratio]'
1788 1804 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
1789 1805 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1790 1806 end
1791 1807 end
1792 1808
1793 1809 def test_new_without_project_id
1794 1810 @request.session[:user_id] = 2
1795 1811 get :new
1796 1812 assert_response :success
1797 1813 assert_template 'new'
1798 1814
1799 1815 assert_select 'form#issue-form[action=?]', '/issues'
1800 1816 assert_select 'form#issue-form' do
1801 1817 assert_select 'select[name=?]', 'issue[project_id]'
1802 1818 end
1803 1819
1804 1820 assert_nil assigns(:project)
1805 1821 assert_not_nil assigns(:issue)
1806 1822 end
1807 1823
1808 1824 def test_new_should_select_default_status
1809 1825 @request.session[:user_id] = 2
1810 1826
1811 1827 get :new, :project_id => 1
1812 1828 assert_response :success
1813 1829 assert_template 'new'
1814 1830 assert_select 'select[name=?]', 'issue[status_id]' do
1815 1831 assert_select 'option[value="1"][selected=selected]'
1816 1832 end
1817 1833 assert_select 'input[name=was_default_status][value="1"]'
1818 1834 end
1819 1835
1820 1836 def test_new_should_propose_allowed_statuses
1821 1837 WorkflowTransition.delete_all
1822 1838 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 1)
1823 1839 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 3)
1824 1840 @request.session[:user_id] = 2
1825 1841
1826 1842 get :new, :project_id => 1
1827 1843 assert_response :success
1828 1844 assert_select 'select[name=?]', 'issue[status_id]' do
1829 1845 assert_select 'option[value="1"]'
1830 1846 assert_select 'option[value="3"]'
1831 1847 assert_select 'option', 2
1832 1848 assert_select 'option[value="1"][selected=selected]'
1833 1849 end
1834 1850 end
1835 1851
1836 1852 def test_new_should_propose_allowed_statuses_without_default_status_allowed
1837 1853 WorkflowTransition.delete_all
1838 1854 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 2)
1839 1855 assert_equal 1, Tracker.find(1).default_status_id
1840 1856 @request.session[:user_id] = 2
1841 1857
1842 1858 get :new, :project_id => 1
1843 1859 assert_response :success
1844 1860 assert_select 'select[name=?]', 'issue[status_id]' do
1845 1861 assert_select 'option[value="2"]'
1846 1862 assert_select 'option', 1
1847 1863 assert_select 'option[value="2"][selected=selected]'
1848 1864 end
1849 1865 end
1850 1866
1851 1867 def test_new_should_preselect_default_version
1852 1868 version = Version.generate!(:project_id => 1)
1853 1869 Project.find(1).update_attribute :default_version_id, version.id
1854 1870 @request.session[:user_id] = 2
1855 1871
1856 1872 get :new, :project_id => 1
1857 1873 assert_response :success
1858 1874 assert_equal version, assigns(:issue).fixed_version
1859 1875 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
1860 1876 assert_select 'option[value=?][selected=selected]', version.id.to_s
1861 1877 end
1862 1878 end
1863 1879
1864 1880 def test_get_new_with_list_custom_field
1865 1881 @request.session[:user_id] = 2
1866 1882 get :new, :project_id => 1, :tracker_id => 1
1867 1883 assert_response :success
1868 1884 assert_template 'new'
1869 1885
1870 1886 assert_select 'select.list_cf[name=?]', 'issue[custom_field_values][1]' do
1871 1887 assert_select 'option', 4
1872 1888 assert_select 'option[value=MySQL]', :text => 'MySQL'
1873 1889 end
1874 1890 end
1875 1891
1876 1892 def test_get_new_with_multi_custom_field
1877 1893 field = IssueCustomField.find(1)
1878 1894 field.update_attribute :multiple, true
1879 1895
1880 1896 @request.session[:user_id] = 2
1881 1897 get :new, :project_id => 1, :tracker_id => 1
1882 1898 assert_response :success
1883 1899 assert_template 'new'
1884 1900
1885 1901 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
1886 1902 assert_select 'option', 3
1887 1903 assert_select 'option[value=MySQL]', :text => 'MySQL'
1888 1904 end
1889 1905 assert_select 'input[name=?][type=hidden][value=?]', 'issue[custom_field_values][1][]', ''
1890 1906 end
1891 1907
1892 1908 def test_get_new_with_multi_user_custom_field
1893 1909 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1894 1910 :tracker_ids => [1], :is_for_all => true)
1895 1911
1896 1912 @request.session[:user_id] = 2
1897 1913 get :new, :project_id => 1, :tracker_id => 1
1898 1914 assert_response :success
1899 1915 assert_template 'new'
1900 1916
1901 1917 assert_select 'select[name=?][multiple=multiple]', "issue[custom_field_values][#{field.id}][]" do
1902 1918 assert_select 'option', Project.find(1).users.count
1903 1919 assert_select 'option[value="2"]', :text => 'John Smith'
1904 1920 end
1905 1921 assert_select 'input[name=?][type=hidden][value=?]', "issue[custom_field_values][#{field.id}][]", ''
1906 1922 end
1907 1923
1908 1924 def test_get_new_with_date_custom_field
1909 1925 field = IssueCustomField.create!(:name => 'Date', :field_format => 'date', :tracker_ids => [1], :is_for_all => true)
1910 1926
1911 1927 @request.session[:user_id] = 2
1912 1928 get :new, :project_id => 1, :tracker_id => 1
1913 1929 assert_response :success
1914 1930
1915 1931 assert_select 'input[name=?]', "issue[custom_field_values][#{field.id}]"
1916 1932 end
1917 1933
1918 1934 def test_get_new_with_text_custom_field
1919 1935 field = IssueCustomField.create!(:name => 'Text', :field_format => 'text', :tracker_ids => [1], :is_for_all => true)
1920 1936
1921 1937 @request.session[:user_id] = 2
1922 1938 get :new, :project_id => 1, :tracker_id => 1
1923 1939 assert_response :success
1924 1940
1925 1941 assert_select 'textarea[name=?]', "issue[custom_field_values][#{field.id}]"
1926 1942 end
1927 1943
1928 1944 def test_get_new_without_default_start_date_is_creation_date
1929 1945 with_settings :default_issue_start_date_to_creation_date => 0 do
1930 1946 @request.session[:user_id] = 2
1931 1947 get :new, :project_id => 1, :tracker_id => 1
1932 1948 assert_response :success
1933 1949 assert_template 'new'
1934 1950 assert_select 'input[name=?]', 'issue[start_date]'
1935 1951 assert_select 'input[name=?][value]', 'issue[start_date]', 0
1936 1952 end
1937 1953 end
1938 1954
1939 1955 def test_get_new_with_default_start_date_is_creation_date
1940 1956 with_settings :default_issue_start_date_to_creation_date => 1 do
1941 1957 @request.session[:user_id] = 2
1942 1958 get :new, :project_id => 1, :tracker_id => 1
1943 1959 assert_response :success
1944 1960 assert_template 'new'
1945 1961 assert_select 'input[name=?][value=?]', 'issue[start_date]',
1946 1962 Date.today.to_s
1947 1963 end
1948 1964 end
1949 1965
1950 1966 def test_get_new_form_should_allow_attachment_upload
1951 1967 @request.session[:user_id] = 2
1952 1968 get :new, :project_id => 1, :tracker_id => 1
1953 1969
1954 1970 assert_select 'form[id=issue-form][method=post][enctype="multipart/form-data"]' do
1955 1971 assert_select 'input[name=?][type=file]', 'attachments[dummy][file]'
1956 1972 end
1957 1973 end
1958 1974
1959 1975 def test_get_new_should_prefill_the_form_from_params
1960 1976 @request.session[:user_id] = 2
1961 1977 get :new, :project_id => 1,
1962 1978 :issue => {:tracker_id => 3, :description => 'Prefilled', :custom_field_values => {'2' => 'Custom field value'}}
1963 1979
1964 1980 issue = assigns(:issue)
1965 1981 assert_equal 3, issue.tracker_id
1966 1982 assert_equal 'Prefilled', issue.description
1967 1983 assert_equal 'Custom field value', issue.custom_field_value(2)
1968 1984
1969 1985 assert_select 'select[name=?]', 'issue[tracker_id]' do
1970 1986 assert_select 'option[value="3"][selected=selected]'
1971 1987 end
1972 1988 assert_select 'textarea[name=?]', 'issue[description]', :text => /Prefilled/
1973 1989 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Custom field value'
1974 1990 end
1975 1991
1976 1992 def test_get_new_should_mark_required_fields
1977 1993 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1978 1994 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1979 1995 WorkflowPermission.delete_all
1980 1996 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
1981 1997 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
1982 1998 @request.session[:user_id] = 2
1983 1999
1984 2000 get :new, :project_id => 1
1985 2001 assert_response :success
1986 2002 assert_template 'new'
1987 2003
1988 2004 assert_select 'label[for=issue_start_date]' do
1989 2005 assert_select 'span[class=required]', 0
1990 2006 end
1991 2007 assert_select 'label[for=issue_due_date]' do
1992 2008 assert_select 'span[class=required]'
1993 2009 end
1994 2010 assert_select 'label[for=?]', "issue_custom_field_values_#{cf1.id}" do
1995 2011 assert_select 'span[class=required]', 0
1996 2012 end
1997 2013 assert_select 'label[for=?]', "issue_custom_field_values_#{cf2.id}" do
1998 2014 assert_select 'span[class=required]'
1999 2015 end
2000 2016 end
2001 2017
2002 2018 def test_get_new_should_not_display_readonly_fields
2003 2019 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2004 2020 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2005 2021 WorkflowPermission.delete_all
2006 2022 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
2007 2023 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
2008 2024 @request.session[:user_id] = 2
2009 2025
2010 2026 get :new, :project_id => 1
2011 2027 assert_response :success
2012 2028 assert_template 'new'
2013 2029
2014 2030 assert_select 'input[name=?]', 'issue[start_date]'
2015 2031 assert_select 'input[name=?]', 'issue[due_date]', 0
2016 2032 assert_select 'input[name=?]', "issue[custom_field_values][#{cf1.id}]"
2017 2033 assert_select 'input[name=?]', "issue[custom_field_values][#{cf2.id}]", 0
2018 2034 end
2019 2035
2020 2036 def test_new_with_tracker_set_as_readonly_should_accept_status
2021 2037 WorkflowPermission.delete_all
2022 2038 [1, 2].each do |status_id|
2023 2039 WorkflowPermission.create!(:tracker_id => 1, :old_status_id => status_id, :role_id => 1, :field_name => 'tracker_id', :rule => 'readonly')
2024 2040 end
2025 2041 @request.session[:user_id] = 2
2026 2042
2027 2043 get :new, :project_id => 1, :issue => {:status_id => 2}
2028 2044 assert_select 'select[name=?]', 'issue[tracker_id]', 0
2029 2045 assert_equal 2, assigns(:issue).status_id
2030 2046 end
2031 2047
2032 2048 def test_get_new_without_tracker_id
2033 2049 @request.session[:user_id] = 2
2034 2050 get :new, :project_id => 1
2035 2051 assert_response :success
2036 2052 assert_template 'new'
2037 2053
2038 2054 issue = assigns(:issue)
2039 2055 assert_not_nil issue
2040 2056 assert_equal Project.find(1).trackers.first, issue.tracker
2041 2057 end
2042 2058
2043 2059 def test_get_new_with_no_default_status_should_display_an_error
2044 2060 @request.session[:user_id] = 2
2045 2061 IssueStatus.delete_all
2046 2062
2047 2063 get :new, :project_id => 1
2048 2064 assert_response 500
2049 2065 assert_select_error /No default issue/
2050 2066 end
2051 2067
2052 2068 def test_get_new_with_no_tracker_should_display_an_error
2053 2069 @request.session[:user_id] = 2
2054 2070 Tracker.delete_all
2055 2071
2056 2072 get :new, :project_id => 1
2057 2073 assert_response 500
2058 2074 assert_select_error /No tracker/
2059 2075 end
2060 2076
2061 2077 def test_new_with_invalid_project_id
2062 2078 @request.session[:user_id] = 1
2063 2079 get :new, :project_id => 'invalid'
2064 2080 assert_response 404
2065 2081 end
2066 2082
2067 2083 def test_new_with_parent_id_should_only_propose_valid_trackers
2068 2084 @request.session[:user_id] = 2
2069 2085 t = Tracker.find(3)
2070 2086 assert !t.disabled_core_fields.include?('parent_issue_id')
2071 2087
2072 2088 get :new, :project_id => 1, issue: { parent_issue_id: 1 }
2073 2089 assert_response :success
2074 2090 assert_select 'option', text: /#{t.name}/, count: 1
2075 2091
2076 2092 t.core_fields = Tracker::CORE_FIELDS - ['parent_issue_id']
2077 2093 t.save!
2078 2094 assert t.disabled_core_fields.include?('parent_issue_id')
2079 2095 get :new, :project_id => 1, issue: { parent_issue_id: 1 }
2080 2096 assert_response :success
2081 2097 assert_select 'option', text: /#{t.name}/, count: 0
2082 2098 end
2083 2099
2084 2100 def test_update_form_for_new_issue
2085 2101 @request.session[:user_id] = 2
2086 2102 xhr :post, :new, :project_id => 1,
2087 2103 :issue => {:tracker_id => 2,
2088 2104 :subject => 'This is the test_new issue',
2089 2105 :description => 'This is the description',
2090 2106 :priority_id => 5}
2091 2107 assert_response :success
2092 2108 assert_template 'new'
2093 2109 assert_template :partial => '_form'
2094 2110 assert_equal 'text/javascript', response.content_type
2095 2111
2096 2112 issue = assigns(:issue)
2097 2113 assert_kind_of Issue, issue
2098 2114 assert_equal 1, issue.project_id
2099 2115 assert_equal 2, issue.tracker_id
2100 2116 assert_equal 'This is the test_new issue', issue.subject
2101 2117 end
2102 2118
2103 2119 def test_update_form_for_new_issue_should_propose_transitions_based_on_initial_status
2104 2120 @request.session[:user_id] = 2
2105 2121 WorkflowTransition.delete_all
2106 2122 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 2)
2107 2123 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 5)
2108 2124 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4)
2109 2125
2110 2126 xhr :post, :new, :project_id => 1,
2111 2127 :issue => {:tracker_id => 1,
2112 2128 :status_id => 5,
2113 2129 :subject => 'This is an issue'}
2114 2130
2115 2131 assert_equal 5, assigns(:issue).status_id
2116 2132 assert_equal [2,5], assigns(:allowed_statuses).map(&:id).sort
2117 2133 end
2118 2134
2119 2135 def test_update_form_with_default_status_should_ignore_submitted_status_id_if_equals
2120 2136 @request.session[:user_id] = 2
2121 2137 tracker = Tracker.find(2)
2122 2138 tracker.update! :default_status_id => 2
2123 2139 tracker.generate_transitions! 2, 1, :clear => true
2124 2140
2125 2141 xhr :post, :new, :project_id => 1,
2126 2142 :issue => {:tracker_id => 2,
2127 2143 :status_id => 1},
2128 2144 :was_default_status => 1
2129 2145
2130 2146 assert_equal 2, assigns(:issue).status_id
2131 2147 end
2132 2148
2133 2149 def test_update_form_for_new_issue_should_ignore_version_when_changing_project
2134 2150 version = Version.generate!(:project_id => 1)
2135 2151 Project.find(1).update_attribute :default_version_id, version.id
2136 2152 @request.session[:user_id] = 2
2137 2153
2138 2154 xhr :post, :new, :issue => {:project_id => 1,
2139 2155 :fixed_version_id => ''},
2140 2156 :form_update_triggered_by => 'issue_project_id'
2141 2157 assert_response :success
2142 2158 assert_template 'new'
2143 2159
2144 2160 issue = assigns(:issue)
2145 2161 assert_equal 1, issue.project_id
2146 2162 assert_equal version, issue.fixed_version
2147 2163 end
2148 2164
2149 2165 def test_post_create
2150 2166 @request.session[:user_id] = 2
2151 2167 assert_difference 'Issue.count' do
2152 2168 assert_no_difference 'Journal.count' do
2153 2169 post :create, :project_id => 1,
2154 2170 :issue => {:tracker_id => 3,
2155 2171 :status_id => 2,
2156 2172 :subject => 'This is the test_new issue',
2157 2173 :description => 'This is the description',
2158 2174 :priority_id => 5,
2159 2175 :start_date => '2010-11-07',
2160 2176 :estimated_hours => '',
2161 2177 :custom_field_values => {'2' => 'Value for field 2'}}
2162 2178 end
2163 2179 end
2164 2180 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2165 2181
2166 2182 issue = Issue.find_by_subject('This is the test_new issue')
2167 2183 assert_not_nil issue
2168 2184 assert_equal 2, issue.author_id
2169 2185 assert_equal 3, issue.tracker_id
2170 2186 assert_equal 2, issue.status_id
2171 2187 assert_equal Date.parse('2010-11-07'), issue.start_date
2172 2188 assert_nil issue.estimated_hours
2173 2189 v = issue.custom_values.where(:custom_field_id => 2).first
2174 2190 assert_not_nil v
2175 2191 assert_equal 'Value for field 2', v.value
2176 2192 end
2177 2193
2178 2194 def test_post_new_with_group_assignment
2179 2195 group = Group.find(11)
2180 2196 project = Project.find(1)
2181 2197 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
2182 2198
2183 2199 with_settings :issue_group_assignment => '1' do
2184 2200 @request.session[:user_id] = 2
2185 2201 assert_difference 'Issue.count' do
2186 2202 post :create, :project_id => project.id,
2187 2203 :issue => {:tracker_id => 3,
2188 2204 :status_id => 1,
2189 2205 :subject => 'This is the test_new_with_group_assignment issue',
2190 2206 :assigned_to_id => group.id}
2191 2207 end
2192 2208 end
2193 2209 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2194 2210
2195 2211 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
2196 2212 assert_not_nil issue
2197 2213 assert_equal group, issue.assigned_to
2198 2214 end
2199 2215
2200 2216 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
2201 2217 with_settings :default_issue_start_date_to_creation_date => 0 do
2202 2218 @request.session[:user_id] = 2
2203 2219 assert_difference 'Issue.count' do
2204 2220 post :create, :project_id => 1,
2205 2221 :issue => {:tracker_id => 3,
2206 2222 :status_id => 2,
2207 2223 :subject => 'This is the test_new issue',
2208 2224 :description => 'This is the description',
2209 2225 :priority_id => 5,
2210 2226 :estimated_hours => '',
2211 2227 :custom_field_values => {'2' => 'Value for field 2'}}
2212 2228 end
2213 2229 assert_redirected_to :controller => 'issues', :action => 'show',
2214 2230 :id => Issue.last.id
2215 2231 issue = Issue.find_by_subject('This is the test_new issue')
2216 2232 assert_not_nil issue
2217 2233 assert_nil issue.start_date
2218 2234 end
2219 2235 end
2220 2236
2221 2237 def test_post_create_without_start_date_and_default_start_date_is_creation_date
2222 2238 with_settings :default_issue_start_date_to_creation_date => 1 do
2223 2239 @request.session[:user_id] = 2
2224 2240 assert_difference 'Issue.count' do
2225 2241 post :create, :project_id => 1,
2226 2242 :issue => {:tracker_id => 3,
2227 2243 :status_id => 2,
2228 2244 :subject => 'This is the test_new issue',
2229 2245 :description => 'This is the description',
2230 2246 :priority_id => 5,
2231 2247 :estimated_hours => '',
2232 2248 :custom_field_values => {'2' => 'Value for field 2'}}
2233 2249 end
2234 2250 assert_redirected_to :controller => 'issues', :action => 'show',
2235 2251 :id => Issue.last.id
2236 2252 issue = Issue.find_by_subject('This is the test_new issue')
2237 2253 assert_not_nil issue
2238 2254 assert_equal Date.today, issue.start_date
2239 2255 end
2240 2256 end
2241 2257
2242 2258 def test_post_create_and_continue
2243 2259 @request.session[:user_id] = 2
2244 2260 assert_difference 'Issue.count' do
2245 2261 post :create, :project_id => 1,
2246 2262 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
2247 2263 :continue => ''
2248 2264 end
2249 2265
2250 2266 issue = Issue.order('id DESC').first
2251 2267 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
2252 2268 assert_not_nil flash[:notice], "flash was not set"
2253 2269 assert_select_in flash[:notice],
2254 2270 'a[href=?][title=?]', "/issues/#{issue.id}", "This is first issue", :text => "##{issue.id}"
2255 2271 end
2256 2272
2257 2273 def test_post_create_without_custom_fields_param
2258 2274 @request.session[:user_id] = 2
2259 2275 assert_difference 'Issue.count' do
2260 2276 post :create, :project_id => 1,
2261 2277 :issue => {:tracker_id => 1,
2262 2278 :subject => 'This is the test_new issue',
2263 2279 :description => 'This is the description',
2264 2280 :priority_id => 5}
2265 2281 end
2266 2282 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2267 2283 end
2268 2284
2269 2285 def test_post_create_with_multi_custom_field
2270 2286 field = IssueCustomField.find_by_name('Database')
2271 2287 field.update_attribute(:multiple, true)
2272 2288
2273 2289 @request.session[:user_id] = 2
2274 2290 assert_difference 'Issue.count' do
2275 2291 post :create, :project_id => 1,
2276 2292 :issue => {:tracker_id => 1,
2277 2293 :subject => 'This is the test_new issue',
2278 2294 :description => 'This is the description',
2279 2295 :priority_id => 5,
2280 2296 :custom_field_values => {'1' => ['', 'MySQL', 'Oracle']}}
2281 2297 end
2282 2298 assert_response 302
2283 2299 issue = Issue.order('id DESC').first
2284 2300 assert_equal ['MySQL', 'Oracle'], issue.custom_field_value(1).sort
2285 2301 end
2286 2302
2287 2303 def test_post_create_with_empty_multi_custom_field
2288 2304 field = IssueCustomField.find_by_name('Database')
2289 2305 field.update_attribute(:multiple, true)
2290 2306
2291 2307 @request.session[:user_id] = 2
2292 2308 assert_difference 'Issue.count' do
2293 2309 post :create, :project_id => 1,
2294 2310 :issue => {:tracker_id => 1,
2295 2311 :subject => 'This is the test_new issue',
2296 2312 :description => 'This is the description',
2297 2313 :priority_id => 5,
2298 2314 :custom_field_values => {'1' => ['']}}
2299 2315 end
2300 2316 assert_response 302
2301 2317 issue = Issue.order('id DESC').first
2302 2318 assert_equal [''], issue.custom_field_value(1).sort
2303 2319 end
2304 2320
2305 2321 def test_post_create_with_multi_user_custom_field
2306 2322 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
2307 2323 :tracker_ids => [1], :is_for_all => true)
2308 2324
2309 2325 @request.session[:user_id] = 2
2310 2326 assert_difference 'Issue.count' do
2311 2327 post :create, :project_id => 1,
2312 2328 :issue => {:tracker_id => 1,
2313 2329 :subject => 'This is the test_new issue',
2314 2330 :description => 'This is the description',
2315 2331 :priority_id => 5,
2316 2332 :custom_field_values => {field.id.to_s => ['', '2', '3']}}
2317 2333 end
2318 2334 assert_response 302
2319 2335 issue = Issue.order('id DESC').first
2320 2336 assert_equal ['2', '3'], issue.custom_field_value(field).sort
2321 2337 end
2322 2338
2323 2339 def test_post_create_with_required_custom_field_and_without_custom_fields_param
2324 2340 field = IssueCustomField.find_by_name('Database')
2325 2341 field.update_attribute(:is_required, true)
2326 2342
2327 2343 @request.session[:user_id] = 2
2328 2344 assert_no_difference 'Issue.count' do
2329 2345 post :create, :project_id => 1,
2330 2346 :issue => {:tracker_id => 1,
2331 2347 :subject => 'This is the test_new issue',
2332 2348 :description => 'This is the description',
2333 2349 :priority_id => 5}
2334 2350 end
2335 2351 assert_response :success
2336 2352 assert_template 'new'
2337 2353 issue = assigns(:issue)
2338 2354 assert_not_nil issue
2339 2355 assert_select_error /Database cannot be blank/
2340 2356 end
2341 2357
2342 2358 def test_create_should_validate_required_fields
2343 2359 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2344 2360 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2345 2361 WorkflowPermission.delete_all
2346 2362 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'required')
2347 2363 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
2348 2364 @request.session[:user_id] = 2
2349 2365
2350 2366 assert_no_difference 'Issue.count' do
2351 2367 post :create, :project_id => 1, :issue => {
2352 2368 :tracker_id => 2,
2353 2369 :status_id => 1,
2354 2370 :subject => 'Test',
2355 2371 :start_date => '',
2356 2372 :due_date => '',
2357 2373 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ''}
2358 2374 }
2359 2375 assert_response :success
2360 2376 assert_template 'new'
2361 2377 end
2362 2378
2363 2379 assert_select_error /Due date cannot be blank/i
2364 2380 assert_select_error /Bar cannot be blank/i
2365 2381 end
2366 2382
2367 2383 def test_create_should_validate_required_list_fields
2368 2384 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'list', :is_for_all => true, :tracker_ids => [1, 2], :multiple => false, :possible_values => ['a', 'b'])
2369 2385 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'list', :is_for_all => true, :tracker_ids => [1, 2], :multiple => true, :possible_values => ['a', 'b'])
2370 2386 WorkflowPermission.delete_all
2371 2387 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf1.id.to_s, :rule => 'required')
2372 2388 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
2373 2389 @request.session[:user_id] = 2
2374 2390
2375 2391 assert_no_difference 'Issue.count' do
2376 2392 post :create, :project_id => 1, :issue => {
2377 2393 :tracker_id => 2,
2378 2394 :status_id => 1,
2379 2395 :subject => 'Test',
2380 2396 :start_date => '',
2381 2397 :due_date => '',
2382 2398 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ['']}
2383 2399 }
2384 2400 assert_response :success
2385 2401 assert_template 'new'
2386 2402 end
2387 2403
2388 2404 assert_select_error /Foo cannot be blank/i
2389 2405 assert_select_error /Bar cannot be blank/i
2390 2406 end
2391 2407
2392 2408 def test_create_should_ignore_readonly_fields
2393 2409 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2394 2410 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2395 2411 WorkflowPermission.delete_all
2396 2412 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
2397 2413 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
2398 2414 @request.session[:user_id] = 2
2399 2415
2400 2416 assert_difference 'Issue.count' do
2401 2417 post :create, :project_id => 1, :issue => {
2402 2418 :tracker_id => 2,
2403 2419 :status_id => 1,
2404 2420 :subject => 'Test',
2405 2421 :start_date => '2012-07-14',
2406 2422 :due_date => '2012-07-16',
2407 2423 :custom_field_values => {cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'}
2408 2424 }
2409 2425 assert_response 302
2410 2426 end
2411 2427
2412 2428 issue = Issue.order('id DESC').first
2413 2429 assert_equal Date.parse('2012-07-14'), issue.start_date
2414 2430 assert_nil issue.due_date
2415 2431 assert_equal 'value1', issue.custom_field_value(cf1)
2416 2432 assert_nil issue.custom_field_value(cf2)
2417 2433 end
2418 2434
2419 2435 def test_post_create_with_watchers
2420 2436 @request.session[:user_id] = 2
2421 2437 ActionMailer::Base.deliveries.clear
2422 2438
2423 2439 with_settings :notified_events => %w(issue_added) do
2424 2440 assert_difference 'Watcher.count', 2 do
2425 2441 post :create, :project_id => 1,
2426 2442 :issue => {:tracker_id => 1,
2427 2443 :subject => 'This is a new issue with watchers',
2428 2444 :description => 'This is the description',
2429 2445 :priority_id => 5,
2430 2446 :watcher_user_ids => ['2', '3']}
2431 2447 end
2432 2448 end
2433 2449 issue = Issue.find_by_subject('This is a new issue with watchers')
2434 2450 assert_not_nil issue
2435 2451 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2436 2452
2437 2453 # Watchers added
2438 2454 assert_equal [2, 3], issue.watcher_user_ids.sort
2439 2455 assert issue.watched_by?(User.find(3))
2440 2456 # Watchers notified
2441 2457 mail = ActionMailer::Base.deliveries.last
2442 2458 assert_not_nil mail
2443 2459 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
2444 2460 end
2445 2461
2446 2462 def test_post_create_subissue
2447 2463 @request.session[:user_id] = 2
2448 2464
2449 2465 assert_difference 'Issue.count' do
2450 2466 post :create, :project_id => 1,
2451 2467 :issue => {:tracker_id => 1,
2452 2468 :subject => 'This is a child issue',
2453 2469 :parent_issue_id => '2'}
2454 2470 assert_response 302
2455 2471 end
2456 2472 issue = Issue.order('id DESC').first
2457 2473 assert_equal Issue.find(2), issue.parent
2458 2474 end
2459 2475
2460 2476 def test_post_create_subissue_with_sharp_parent_id
2461 2477 @request.session[:user_id] = 2
2462 2478
2463 2479 assert_difference 'Issue.count' do
2464 2480 post :create, :project_id => 1,
2465 2481 :issue => {:tracker_id => 1,
2466 2482 :subject => 'This is a child issue',
2467 2483 :parent_issue_id => '#2'}
2468 2484 assert_response 302
2469 2485 end
2470 2486 issue = Issue.order('id DESC').first
2471 2487 assert_equal Issue.find(2), issue.parent
2472 2488 end
2473 2489
2474 2490 def test_post_create_subissue_with_non_visible_parent_id_should_not_validate
2475 2491 @request.session[:user_id] = 2
2476 2492
2477 2493 assert_no_difference 'Issue.count' do
2478 2494 post :create, :project_id => 1,
2479 2495 :issue => {:tracker_id => 1,
2480 2496 :subject => 'This is a child issue',
2481 2497 :parent_issue_id => '4'}
2482 2498
2483 2499 assert_response :success
2484 2500 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '4'
2485 2501 assert_select_error /Parent task is invalid/i
2486 2502 end
2487 2503 end
2488 2504
2489 2505 def test_post_create_subissue_with_non_numeric_parent_id_should_not_validate
2490 2506 @request.session[:user_id] = 2
2491 2507
2492 2508 assert_no_difference 'Issue.count' do
2493 2509 post :create, :project_id => 1,
2494 2510 :issue => {:tracker_id => 1,
2495 2511 :subject => 'This is a child issue',
2496 2512 :parent_issue_id => '01ABC'}
2497 2513
2498 2514 assert_response :success
2499 2515 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '01ABC'
2500 2516 assert_select_error /Parent task is invalid/i
2501 2517 end
2502 2518 end
2503 2519
2504 2520 def test_post_create_private
2505 2521 @request.session[:user_id] = 2
2506 2522
2507 2523 assert_difference 'Issue.count' do
2508 2524 post :create, :project_id => 1,
2509 2525 :issue => {:tracker_id => 1,
2510 2526 :subject => 'This is a private issue',
2511 2527 :is_private => '1'}
2512 2528 end
2513 2529 issue = Issue.order('id DESC').first
2514 2530 assert issue.is_private?
2515 2531 end
2516 2532
2517 2533 def test_post_create_private_with_set_own_issues_private_permission
2518 2534 role = Role.find(1)
2519 2535 role.remove_permission! :set_issues_private
2520 2536 role.add_permission! :set_own_issues_private
2521 2537
2522 2538 @request.session[:user_id] = 2
2523 2539
2524 2540 assert_difference 'Issue.count' do
2525 2541 post :create, :project_id => 1,
2526 2542 :issue => {:tracker_id => 1,
2527 2543 :subject => 'This is a private issue',
2528 2544 :is_private => '1'}
2529 2545 end
2530 2546 issue = Issue.order('id DESC').first
2531 2547 assert issue.is_private?
2532 2548 end
2533 2549
2534 2550 def test_create_without_project_id
2535 2551 @request.session[:user_id] = 2
2536 2552
2537 2553 assert_difference 'Issue.count' do
2538 2554 post :create,
2539 2555 :issue => {:project_id => 3,
2540 2556 :tracker_id => 2,
2541 2557 :subject => 'Foo'}
2542 2558 assert_response 302
2543 2559 end
2544 2560 issue = Issue.order('id DESC').first
2545 2561 assert_equal 3, issue.project_id
2546 2562 assert_equal 2, issue.tracker_id
2547 2563 end
2548 2564
2549 2565 def test_create_without_project_id_and_continue_should_redirect_without_project_id
2550 2566 @request.session[:user_id] = 2
2551 2567
2552 2568 assert_difference 'Issue.count' do
2553 2569 post :create,
2554 2570 :issue => {:project_id => 3,
2555 2571 :tracker_id => 2,
2556 2572 :subject => 'Foo'},
2557 2573 :continue => '1'
2558 2574 assert_redirected_to '/issues/new?issue%5Bproject_id%5D=3&issue%5Btracker_id%5D=2'
2559 2575 end
2560 2576 end
2561 2577
2562 2578 def test_create_without_project_id_should_be_denied_without_permission
2563 2579 Role.non_member.remove_permission! :add_issues
2564 2580 Role.anonymous.remove_permission! :add_issues
2565 2581 @request.session[:user_id] = 2
2566 2582
2567 2583 assert_no_difference 'Issue.count' do
2568 2584 post :create,
2569 2585 :issue => {:project_id => 3,
2570 2586 :tracker_id => 2,
2571 2587 :subject => 'Foo'}
2572 2588 assert_response 422
2573 2589 end
2574 2590 end
2575 2591
2576 2592 def test_create_without_project_id_with_failure
2577 2593 @request.session[:user_id] = 2
2578 2594
2579 2595 post :create,
2580 2596 :issue => {:project_id => 3,
2581 2597 :tracker_id => 2,
2582 2598 :subject => ''}
2583 2599 assert_response :success
2584 2600 assert_nil assigns(:project)
2585 2601 end
2586 2602
2587 2603 def test_post_create_should_send_a_notification
2588 2604 ActionMailer::Base.deliveries.clear
2589 2605 @request.session[:user_id] = 2
2590 2606 with_settings :notified_events => %w(issue_added) do
2591 2607 assert_difference 'Issue.count' do
2592 2608 post :create, :project_id => 1,
2593 2609 :issue => {:tracker_id => 3,
2594 2610 :subject => 'This is the test_new issue',
2595 2611 :description => 'This is the description',
2596 2612 :priority_id => 5,
2597 2613 :estimated_hours => '',
2598 2614 :custom_field_values => {'2' => 'Value for field 2'}}
2599 2615 end
2600 2616 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2601 2617
2602 2618 assert_equal 1, ActionMailer::Base.deliveries.size
2603 2619 end
2604 2620 end
2605 2621
2606 2622 def test_post_create_should_preserve_fields_values_on_validation_failure
2607 2623 @request.session[:user_id] = 2
2608 2624 post :create, :project_id => 1,
2609 2625 :issue => {:tracker_id => 1,
2610 2626 # empty subject
2611 2627 :subject => '',
2612 2628 :description => 'This is a description',
2613 2629 :priority_id => 6,
2614 2630 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
2615 2631 assert_response :success
2616 2632 assert_template 'new'
2617 2633
2618 2634 assert_select 'textarea[name=?]', 'issue[description]', :text => 'This is a description'
2619 2635 assert_select 'select[name=?]', 'issue[priority_id]' do
2620 2636 assert_select 'option[value="6"][selected=selected]', :text => 'High'
2621 2637 end
2622 2638 # Custom fields
2623 2639 assert_select 'select[name=?]', 'issue[custom_field_values][1]' do
2624 2640 assert_select 'option[value=Oracle][selected=selected]', :text => 'Oracle'
2625 2641 end
2626 2642 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Value for field 2'
2627 2643 end
2628 2644
2629 2645 def test_post_create_with_failure_should_preserve_watchers
2630 2646 assert !User.find(8).member_of?(Project.find(1))
2631 2647
2632 2648 @request.session[:user_id] = 2
2633 2649 post :create, :project_id => 1,
2634 2650 :issue => {:tracker_id => 1,
2635 2651 :watcher_user_ids => ['3', '8']}
2636 2652 assert_response :success
2637 2653 assert_template 'new'
2638 2654
2639 2655 assert_select 'input[name=?][value="2"]:not(checked)', 'issue[watcher_user_ids][]'
2640 2656 assert_select 'input[name=?][value="3"][checked=checked]', 'issue[watcher_user_ids][]'
2641 2657 assert_select 'input[name=?][value="8"][checked=checked]', 'issue[watcher_user_ids][]'
2642 2658 end
2643 2659
2644 2660 def test_post_create_should_ignore_non_safe_attributes
2645 2661 @request.session[:user_id] = 2
2646 2662 assert_nothing_raised do
2647 2663 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
2648 2664 end
2649 2665 end
2650 2666
2651 2667 def test_post_create_with_attachment
2652 2668 set_tmp_attachments_directory
2653 2669 @request.session[:user_id] = 2
2654 2670
2655 2671 assert_difference 'Issue.count' do
2656 2672 assert_difference 'Attachment.count' do
2657 2673 assert_no_difference 'Journal.count' do
2658 2674 post :create, :project_id => 1,
2659 2675 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2660 2676 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2661 2677 end
2662 2678 end
2663 2679 end
2664 2680
2665 2681 issue = Issue.order('id DESC').first
2666 2682 attachment = Attachment.order('id DESC').first
2667 2683
2668 2684 assert_equal issue, attachment.container
2669 2685 assert_equal 2, attachment.author_id
2670 2686 assert_equal 'testfile.txt', attachment.filename
2671 2687 assert_equal 'text/plain', attachment.content_type
2672 2688 assert_equal 'test file', attachment.description
2673 2689 assert_equal 59, attachment.filesize
2674 2690 assert File.exists?(attachment.diskfile)
2675 2691 assert_equal 59, File.size(attachment.diskfile)
2676 2692 end
2677 2693
2678 2694 def test_post_create_with_attachment_should_notify_with_attachments
2679 2695 ActionMailer::Base.deliveries.clear
2680 2696 set_tmp_attachments_directory
2681 2697 @request.session[:user_id] = 2
2682 2698
2683 2699 with_settings :notified_events => %w(issue_added) do
2684 2700 assert_difference 'Issue.count' do
2685 2701 post :create, :project_id => 1,
2686 2702 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2687 2703 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2688 2704 end
2689 2705 end
2690 2706
2691 2707 assert_not_nil ActionMailer::Base.deliveries.last
2692 2708 assert_select_email do
2693 2709 assert_select 'a[href^=?]', 'http://localhost:3000/attachments/download', 'testfile.txt'
2694 2710 end
2695 2711 end
2696 2712
2697 2713 def test_post_create_with_failure_should_save_attachments
2698 2714 set_tmp_attachments_directory
2699 2715 @request.session[:user_id] = 2
2700 2716
2701 2717 assert_no_difference 'Issue.count' do
2702 2718 assert_difference 'Attachment.count' do
2703 2719 post :create, :project_id => 1,
2704 2720 :issue => { :tracker_id => '1', :subject => '' },
2705 2721 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2706 2722 assert_response :success
2707 2723 assert_template 'new'
2708 2724 end
2709 2725 end
2710 2726
2711 2727 attachment = Attachment.order('id DESC').first
2712 2728 assert_equal 'testfile.txt', attachment.filename
2713 2729 assert File.exists?(attachment.diskfile)
2714 2730 assert_nil attachment.container
2715 2731
2716 2732 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
2717 2733 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
2718 2734 end
2719 2735
2720 2736 def test_post_create_with_failure_should_keep_saved_attachments
2721 2737 set_tmp_attachments_directory
2722 2738 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2723 2739 @request.session[:user_id] = 2
2724 2740
2725 2741 assert_no_difference 'Issue.count' do
2726 2742 assert_no_difference 'Attachment.count' do
2727 2743 post :create, :project_id => 1,
2728 2744 :issue => { :tracker_id => '1', :subject => '' },
2729 2745 :attachments => {'p0' => {'token' => attachment.token}}
2730 2746 assert_response :success
2731 2747 assert_template 'new'
2732 2748 end
2733 2749 end
2734 2750
2735 2751 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
2736 2752 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
2737 2753 end
2738 2754
2739 2755 def test_post_create_should_attach_saved_attachments
2740 2756 set_tmp_attachments_directory
2741 2757 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2742 2758 @request.session[:user_id] = 2
2743 2759
2744 2760 assert_difference 'Issue.count' do
2745 2761 assert_no_difference 'Attachment.count' do
2746 2762 post :create, :project_id => 1,
2747 2763 :issue => { :tracker_id => '1', :subject => 'Saved attachments' },
2748 2764 :attachments => {'p0' => {'token' => attachment.token}}
2749 2765 assert_response 302
2750 2766 end
2751 2767 end
2752 2768
2753 2769 issue = Issue.order('id DESC').first
2754 2770 assert_equal 1, issue.attachments.count
2755 2771
2756 2772 attachment.reload
2757 2773 assert_equal issue, attachment.container
2758 2774 end
2759 2775
2760 2776 def setup_without_workflow_privilege
2761 2777 WorkflowTransition.delete_all(["role_id = ?", Role.anonymous.id])
2762 2778 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2763 2779 end
2764 2780 private :setup_without_workflow_privilege
2765 2781
2766 2782 test "without workflow privilege #new should propose default status only" do
2767 2783 setup_without_workflow_privilege
2768 2784 get :new, :project_id => 1
2769 2785 assert_response :success
2770 2786 assert_template 'new'
2771 2787
2772 2788 issue = assigns(:issue)
2773 2789 assert_not_nil issue.default_status
2774 2790
2775 2791 assert_select 'select[name=?]', 'issue[status_id]' do
2776 2792 assert_select 'option', 1
2777 2793 assert_select 'option[value=?]', issue.default_status.id.to_s
2778 2794 end
2779 2795 end
2780 2796
2781 2797 test "without workflow privilege #create should accept default status" do
2782 2798 setup_without_workflow_privilege
2783 2799 assert_difference 'Issue.count' do
2784 2800 post :create, :project_id => 1,
2785 2801 :issue => {:tracker_id => 1,
2786 2802 :subject => 'This is an issue',
2787 2803 :status_id => 1}
2788 2804 end
2789 2805 issue = Issue.order('id').last
2790 2806 assert_not_nil issue.default_status
2791 2807 assert_equal issue.default_status, issue.status
2792 2808 end
2793 2809
2794 2810 test "without workflow privilege #create should ignore unauthorized status" do
2795 2811 setup_without_workflow_privilege
2796 2812 assert_difference 'Issue.count' do
2797 2813 post :create, :project_id => 1,
2798 2814 :issue => {:tracker_id => 1,
2799 2815 :subject => 'This is an issue',
2800 2816 :status_id => 3}
2801 2817 end
2802 2818 issue = Issue.order('id').last
2803 2819 assert_not_nil issue.default_status
2804 2820 assert_equal issue.default_status, issue.status
2805 2821 end
2806 2822
2807 2823 test "without workflow privilege #update should ignore status change" do
2808 2824 setup_without_workflow_privilege
2809 2825 assert_difference 'Journal.count' do
2810 2826 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2811 2827 end
2812 2828 assert_equal 1, Issue.find(1).status_id
2813 2829 end
2814 2830
2815 2831 test "without workflow privilege #update ignore attributes changes" do
2816 2832 setup_without_workflow_privilege
2817 2833 assert_difference 'Journal.count' do
2818 2834 put :update, :id => 1,
2819 2835 :issue => {:subject => 'changed', :assigned_to_id => 2,
2820 2836 :notes => 'just trying'}
2821 2837 end
2822 2838 issue = Issue.find(1)
2823 2839 assert_equal "Cannot print recipes", issue.subject
2824 2840 assert_nil issue.assigned_to
2825 2841 end
2826 2842
2827 2843 def setup_with_workflow_privilege
2828 2844 WorkflowTransition.delete_all(["role_id = ?", Role.anonymous.id])
2829 2845 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
2830 2846 :old_status_id => 1, :new_status_id => 3)
2831 2847 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
2832 2848 :old_status_id => 1, :new_status_id => 4)
2833 2849 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2834 2850 end
2835 2851 private :setup_with_workflow_privilege
2836 2852
2837 2853 def setup_with_workflow_privilege_and_edit_issues_permission
2838 2854 setup_with_workflow_privilege
2839 2855 Role.anonymous.add_permission! :add_issues, :edit_issues
2840 2856 end
2841 2857 private :setup_with_workflow_privilege_and_edit_issues_permission
2842 2858
2843 2859 test "with workflow privilege and :edit_issues permission should accept authorized status" do
2844 2860 setup_with_workflow_privilege_and_edit_issues_permission
2845 2861 assert_difference 'Journal.count' do
2846 2862 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2847 2863 end
2848 2864 assert_equal 3, Issue.find(1).status_id
2849 2865 end
2850 2866
2851 2867 test "with workflow privilege and :edit_issues permission should ignore unauthorized status" do
2852 2868 setup_with_workflow_privilege_and_edit_issues_permission
2853 2869 assert_difference 'Journal.count' do
2854 2870 put :update, :id => 1, :issue => {:status_id => 2, :notes => 'just trying'}
2855 2871 end
2856 2872 assert_equal 1, Issue.find(1).status_id
2857 2873 end
2858 2874
2859 2875 test "with workflow privilege and :edit_issues permission should accept authorized attributes changes" do
2860 2876 setup_with_workflow_privilege_and_edit_issues_permission
2861 2877 assert_difference 'Journal.count' do
2862 2878 put :update, :id => 1,
2863 2879 :issue => {:subject => 'changed', :assigned_to_id => 2,
2864 2880 :notes => 'just trying'}
2865 2881 end
2866 2882 issue = Issue.find(1)
2867 2883 assert_equal "changed", issue.subject
2868 2884 assert_equal 2, issue.assigned_to_id
2869 2885 end
2870 2886
2871 2887 def test_new_as_copy
2872 2888 @request.session[:user_id] = 2
2873 2889 get :new, :project_id => 1, :copy_from => 1
2874 2890
2875 2891 assert_response :success
2876 2892 assert_template 'new'
2877 2893
2878 2894 assert_not_nil assigns(:issue)
2879 2895 orig = Issue.find(1)
2880 2896 assert_equal 1, assigns(:issue).project_id
2881 2897 assert_equal orig.subject, assigns(:issue).subject
2882 2898 assert assigns(:issue).copy?
2883 2899
2884 2900 assert_select 'form[id=issue-form][action="/projects/ecookbook/issues"]' do
2885 2901 assert_select 'select[name=?]', 'issue[project_id]' do
2886 2902 assert_select 'option[value="1"][selected=selected]', :text => 'eCookbook'
2887 2903 assert_select 'option[value="2"]:not([selected])', :text => 'OnlineStore'
2888 2904 end
2889 2905 assert_select 'input[name=copy_from][value="1"]'
2890 2906 end
2891 2907 end
2892 2908
2893 2909 def test_new_as_copy_without_add_issues_permission_should_not_propose_current_project_as_target
2894 2910 user = setup_user_with_copy_but_not_add_permission
2895 2911
2896 2912 @request.session[:user_id] = user.id
2897 2913 get :new, :project_id => 1, :copy_from => 1
2898 2914
2899 2915 assert_response :success
2900 2916 assert_template 'new'
2901 2917 assert_select 'select[name=?]', 'issue[project_id]' do
2902 2918 assert_select 'option[value="1"]', 0
2903 2919 assert_select 'option[value="2"]', :text => 'OnlineStore'
2904 2920 end
2905 2921 end
2906 2922
2907 2923 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
2908 2924 @request.session[:user_id] = 2
2909 2925 issue = Issue.find(3)
2910 2926 assert issue.attachments.count > 0
2911 2927 get :new, :project_id => 1, :copy_from => 3
2912 2928
2913 2929 assert_select 'input[name=copy_attachments][type=checkbox][checked=checked][value="1"]'
2914 2930 end
2915 2931
2916 2932 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
2917 2933 @request.session[:user_id] = 2
2918 2934 issue = Issue.find(3)
2919 2935 issue.attachments.delete_all
2920 2936 get :new, :project_id => 1, :copy_from => 3
2921 2937
2922 2938 assert_select 'input[name=copy_attachments]', 0
2923 2939 end
2924 2940
2925 2941 def test_new_as_copy_should_preserve_parent_id
2926 2942 @request.session[:user_id] = 2
2927 2943 issue = Issue.generate!(:parent_issue_id => 2)
2928 2944 get :new, :project_id => 1, :copy_from => issue.id
2929 2945
2930 2946 assert_select 'input[name=?][value="2"]', 'issue[parent_issue_id]'
2931 2947 end
2932 2948
2933 2949 def test_new_as_copy_with_subtasks_should_show_copy_subtasks_checkbox
2934 2950 @request.session[:user_id] = 2
2935 2951 issue = Issue.generate_with_descendants!
2936 2952 get :new, :project_id => 1, :copy_from => issue.id
2937 2953
2938 2954 assert_select 'input[type=checkbox][name=copy_subtasks][checked=checked][value="1"]'
2939 2955 end
2940 2956
2941 2957 def test_new_as_copy_with_invalid_issue_should_respond_with_404
2942 2958 @request.session[:user_id] = 2
2943 2959 get :new, :project_id => 1, :copy_from => 99999
2944 2960 assert_response 404
2945 2961 end
2946 2962
2947 2963 def test_create_as_copy_on_different_project
2948 2964 @request.session[:user_id] = 2
2949 2965 assert_difference 'Issue.count' do
2950 2966 post :create, :project_id => 1, :copy_from => 1,
2951 2967 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
2952 2968
2953 2969 assert_not_nil assigns(:issue)
2954 2970 assert assigns(:issue).copy?
2955 2971 end
2956 2972 issue = Issue.order('id DESC').first
2957 2973 assert_redirected_to "/issues/#{issue.id}"
2958 2974
2959 2975 assert_equal 2, issue.project_id
2960 2976 assert_equal 3, issue.tracker_id
2961 2977 assert_equal 'Copy', issue.subject
2962 2978 end
2963 2979
2964 2980 def test_create_as_copy_should_allow_status_to_be_set_to_default
2965 2981 copied = Issue.generate! :status_id => 2
2966 2982 assert_equal 2, copied.reload.status_id
2967 2983
2968 2984 @request.session[:user_id] = 2
2969 2985 assert_difference 'Issue.count' do
2970 2986 post :create, :project_id => 1, :copy_from => copied.id,
2971 2987 :issue => {:project_id => '1', :tracker_id => '1', :status_id => '1'},
2972 2988 :was_default_status => '1'
2973 2989 end
2974 2990 issue = Issue.order('id DESC').first
2975 2991 assert_equal 1, issue.status_id
2976 2992 end
2977 2993
2978 2994 def test_create_as_copy_should_copy_attachments
2979 2995 @request.session[:user_id] = 2
2980 2996 issue = Issue.find(3)
2981 2997 count = issue.attachments.count
2982 2998 assert count > 0
2983 2999 assert_difference 'Issue.count' do
2984 3000 assert_difference 'Attachment.count', count do
2985 3001 post :create, :project_id => 1, :copy_from => 3,
2986 3002 :issue => {:project_id => '1', :tracker_id => '3',
2987 3003 :status_id => '1', :subject => 'Copy with attachments'},
2988 3004 :copy_attachments => '1'
2989 3005 end
2990 3006 end
2991 3007 copy = Issue.order('id DESC').first
2992 3008 assert_equal count, copy.attachments.count
2993 3009 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
2994 3010 end
2995 3011
2996 3012 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
2997 3013 @request.session[:user_id] = 2
2998 3014 issue = Issue.find(3)
2999 3015 count = issue.attachments.count
3000 3016 assert count > 0
3001 3017 assert_difference 'Issue.count' do
3002 3018 assert_no_difference 'Attachment.count' do
3003 3019 post :create, :project_id => 1, :copy_from => 3,
3004 3020 :issue => {:project_id => '1', :tracker_id => '3',
3005 3021 :status_id => '1', :subject => 'Copy with attachments'}
3006 3022 end
3007 3023 end
3008 3024 copy = Issue.order('id DESC').first
3009 3025 assert_equal 0, copy.attachments.count
3010 3026 end
3011 3027
3012 3028 def test_create_as_copy_with_attachments_should_also_add_new_files
3013 3029 @request.session[:user_id] = 2
3014 3030 issue = Issue.find(3)
3015 3031 count = issue.attachments.count
3016 3032 assert count > 0
3017 3033 assert_difference 'Issue.count' do
3018 3034 assert_difference 'Attachment.count', count + 1 do
3019 3035 post :create, :project_id => 1, :copy_from => 3,
3020 3036 :issue => {:project_id => '1', :tracker_id => '3',
3021 3037 :status_id => '1', :subject => 'Copy with attachments'},
3022 3038 :copy_attachments => '1',
3023 3039 :attachments => {'1' =>
3024 3040 {'file' => uploaded_test_file('testfile.txt', 'text/plain'),
3025 3041 'description' => 'test file'}}
3026 3042 end
3027 3043 end
3028 3044 copy = Issue.order('id DESC').first
3029 3045 assert_equal count + 1, copy.attachments.count
3030 3046 end
3031 3047
3032 3048 def test_create_as_copy_should_add_relation_with_copied_issue
3033 3049 @request.session[:user_id] = 2
3034 3050 assert_difference 'Issue.count' do
3035 3051 assert_difference 'IssueRelation.count' do
3036 3052 post :create, :project_id => 1, :copy_from => 1, :link_copy => '1',
3037 3053 :issue => {:project_id => '1', :tracker_id => '3',
3038 3054 :status_id => '1', :subject => 'Copy'}
3039 3055 end
3040 3056 end
3041 3057 copy = Issue.order('id DESC').first
3042 3058 assert_equal 1, copy.relations.size
3043 3059 end
3044 3060
3045 3061 def test_create_as_copy_should_allow_not_to_add_relation_with_copied_issue
3046 3062 @request.session[:user_id] = 2
3047 3063 assert_difference 'Issue.count' do
3048 3064 assert_no_difference 'IssueRelation.count' do
3049 3065 post :create, :project_id => 1, :copy_from => 1,
3050 3066 :issue => {:subject => 'Copy'}
3051 3067 end
3052 3068 end
3053 3069 end
3054 3070
3055 3071 def test_create_as_copy_should_always_add_relation_with_copied_issue_by_setting
3056 3072 with_settings :link_copied_issue => 'yes' do
3057 3073 @request.session[:user_id] = 2
3058 3074 assert_difference 'Issue.count' do
3059 3075 assert_difference 'IssueRelation.count' do
3060 3076 post :create, :project_id => 1, :copy_from => 1,
3061 3077 :issue => {:subject => 'Copy'}
3062 3078 end
3063 3079 end
3064 3080 end
3065 3081 end
3066 3082
3067 3083 def test_create_as_copy_should_never_add_relation_with_copied_issue_by_setting
3068 3084 with_settings :link_copied_issue => 'no' do
3069 3085 @request.session[:user_id] = 2
3070 3086 assert_difference 'Issue.count' do
3071 3087 assert_no_difference 'IssueRelation.count' do
3072 3088 post :create, :project_id => 1, :copy_from => 1, :link_copy => '1',
3073 3089 :issue => {:subject => 'Copy'}
3074 3090 end
3075 3091 end
3076 3092 end
3077 3093 end
3078 3094
3079 3095 def test_create_as_copy_should_copy_subtasks
3080 3096 @request.session[:user_id] = 2
3081 3097 issue = Issue.generate_with_descendants!
3082 3098 count = issue.descendants.count
3083 3099 assert_difference 'Issue.count', count + 1 do
3084 3100 post :create, :project_id => 1, :copy_from => issue.id,
3085 3101 :issue => {:project_id => '1', :tracker_id => '3',
3086 3102 :status_id => '1', :subject => 'Copy with subtasks'},
3087 3103 :copy_subtasks => '1'
3088 3104 end
3089 3105 copy = Issue.where(:parent_id => nil).order('id DESC').first
3090 3106 assert_equal count, copy.descendants.count
3091 3107 assert_equal issue.descendants.map(&:subject).sort, copy.descendants.map(&:subject).sort
3092 3108 end
3093 3109
3094 3110 def test_create_as_copy_to_a_different_project_should_copy_subtask_custom_fields
3095 3111 issue = Issue.generate! {|i| i.custom_field_values = {'2' => 'Foo'}}
3096 3112 child = Issue.generate!(:parent_issue_id => issue.id) {|i| i.custom_field_values = {'2' => 'Bar'}}
3097 3113 @request.session[:user_id] = 1
3098 3114
3099 3115 assert_difference 'Issue.count', 2 do
3100 3116 post :create, :project_id => 'ecookbook', :copy_from => issue.id,
3101 3117 :issue => {:project_id => '2', :tracker_id => 1, :status_id => '1',
3102 3118 :subject => 'Copy with subtasks', :custom_field_values => {'2' => 'Foo'}},
3103 3119 :copy_subtasks => '1'
3104 3120 end
3105 3121
3106 3122 child_copy, issue_copy = Issue.order(:id => :desc).limit(2).to_a
3107 3123 assert_equal 2, issue_copy.project_id
3108 3124 assert_equal 'Foo', issue_copy.custom_field_value(2)
3109 3125 assert_equal 'Bar', child_copy.custom_field_value(2)
3110 3126 end
3111 3127
3112 3128 def test_create_as_copy_without_copy_subtasks_option_should_not_copy_subtasks
3113 3129 @request.session[:user_id] = 2
3114 3130 issue = Issue.generate_with_descendants!
3115 3131 assert_difference 'Issue.count', 1 do
3116 3132 post :create, :project_id => 1, :copy_from => 3,
3117 3133 :issue => {:project_id => '1', :tracker_id => '3',
3118 3134 :status_id => '1', :subject => 'Copy with subtasks'}
3119 3135 end
3120 3136 copy = Issue.where(:parent_id => nil).order('id DESC').first
3121 3137 assert_equal 0, copy.descendants.count
3122 3138 end
3123 3139
3124 3140 def test_create_as_copy_with_failure
3125 3141 @request.session[:user_id] = 2
3126 3142 post :create, :project_id => 1, :copy_from => 1,
3127 3143 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
3128 3144
3129 3145 assert_response :success
3130 3146 assert_template 'new'
3131 3147
3132 3148 assert_not_nil assigns(:issue)
3133 3149 assert assigns(:issue).copy?
3134 3150
3135 3151 assert_select 'form#issue-form[action="/projects/ecookbook/issues"]' do
3136 3152 assert_select 'select[name=?]', 'issue[project_id]' do
3137 3153 assert_select 'option[value="1"]:not([selected])', :text => 'eCookbook'
3138 3154 assert_select 'option[value="2"][selected=selected]', :text => 'OnlineStore'
3139 3155 end
3140 3156 assert_select 'input[name=copy_from][value="1"]'
3141 3157 end
3142 3158 end
3143 3159
3144 3160 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
3145 3161 @request.session[:user_id] = 2
3146 3162 assert !User.find(2).member_of?(Project.find(4))
3147 3163
3148 3164 assert_difference 'Issue.count' do
3149 3165 post :create, :project_id => 1, :copy_from => 1,
3150 3166 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
3151 3167 end
3152 3168 issue = Issue.order('id DESC').first
3153 3169 assert_equal 1, issue.project_id
3154 3170 end
3155 3171
3156 3172 def test_get_edit
3157 3173 @request.session[:user_id] = 2
3158 3174 get :edit, :id => 1
3159 3175 assert_response :success
3160 3176 assert_template 'edit'
3161 3177 assert_not_nil assigns(:issue)
3162 3178 assert_equal Issue.find(1), assigns(:issue)
3163 3179
3164 3180 # Be sure we don't display inactive IssuePriorities
3165 3181 assert ! IssuePriority.find(15).active?
3166 3182 assert_select 'select[name=?]', 'issue[priority_id]' do
3167 3183 assert_select 'option[value="15"]', 0
3168 3184 end
3169 3185 end
3170 3186
3171 3187 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
3172 3188 @request.session[:user_id] = 2
3173 3189 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
3174 3190
3175 3191 get :edit, :id => 1
3176 3192 assert_select 'input[name=?]', 'time_entry[hours]'
3177 3193 end
3178 3194
3179 3195 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
3180 3196 @request.session[:user_id] = 2
3181 3197 Role.find_by_name('Manager').remove_permission! :log_time
3182 3198
3183 3199 get :edit, :id => 1
3184 3200 assert_select 'input[name=?]', 'time_entry[hours]', 0
3185 3201 end
3186 3202
3187 3203 def test_get_edit_with_params
3188 3204 @request.session[:user_id] = 2
3189 3205 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
3190 3206 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => 10 }
3191 3207 assert_response :success
3192 3208 assert_template 'edit'
3193 3209
3194 3210 issue = assigns(:issue)
3195 3211 assert_not_nil issue
3196 3212
3197 3213 assert_equal 5, issue.status_id
3198 3214 assert_select 'select[name=?]', 'issue[status_id]' do
3199 3215 assert_select 'option[value="5"][selected=selected]', :text => 'Closed'
3200 3216 end
3201 3217
3202 3218 assert_equal 7, issue.priority_id
3203 3219 assert_select 'select[name=?]', 'issue[priority_id]' do
3204 3220 assert_select 'option[value="7"][selected=selected]', :text => 'Urgent'
3205 3221 end
3206 3222
3207 3223 assert_select 'input[name=?][value="2.5"]', 'time_entry[hours]'
3208 3224 assert_select 'select[name=?]', 'time_entry[activity_id]' do
3209 3225 assert_select 'option[value="10"][selected=selected]', :text => 'Development'
3210 3226 end
3211 3227 assert_select 'input[name=?][value=test_get_edit_with_params]', 'time_entry[comments]'
3212 3228 end
3213 3229
3214 3230 def test_get_edit_with_multi_custom_field
3215 3231 field = CustomField.find(1)
3216 3232 field.update_attribute :multiple, true
3217 3233 issue = Issue.find(1)
3218 3234 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
3219 3235 issue.save!
3220 3236
3221 3237 @request.session[:user_id] = 2
3222 3238 get :edit, :id => 1
3223 3239 assert_response :success
3224 3240 assert_template 'edit'
3225 3241
3226 3242 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
3227 3243 assert_select 'option', 3
3228 3244 assert_select 'option[value=MySQL][selected=selected]'
3229 3245 assert_select 'option[value=Oracle][selected=selected]'
3230 3246 assert_select 'option[value=PostgreSQL]:not([selected])'
3231 3247 end
3232 3248 end
3233 3249
3234 3250 def test_update_form_for_existing_issue
3235 3251 @request.session[:user_id] = 2
3236 3252 xhr :patch, :edit, :id => 1,
3237 3253 :issue => {:tracker_id => 2,
3238 3254 :subject => 'This is the test_new issue',
3239 3255 :description => 'This is the description',
3240 3256 :priority_id => 5}
3241 3257 assert_response :success
3242 3258 assert_equal 'text/javascript', response.content_type
3243 3259 assert_template 'edit'
3244 3260 assert_template :partial => '_form'
3245 3261
3246 3262 issue = assigns(:issue)
3247 3263 assert_kind_of Issue, issue
3248 3264 assert_equal 1, issue.id
3249 3265 assert_equal 1, issue.project_id
3250 3266 assert_equal 2, issue.tracker_id
3251 3267 assert_equal 'This is the test_new issue', issue.subject
3252 3268 end
3253 3269
3254 3270 def test_update_form_for_existing_issue_should_keep_issue_author
3255 3271 @request.session[:user_id] = 3
3256 3272 xhr :patch, :edit, :id => 1, :issue => {:subject => 'Changed'}
3257 3273 assert_response :success
3258 3274 assert_equal 'text/javascript', response.content_type
3259 3275
3260 3276 issue = assigns(:issue)
3261 3277 assert_equal User.find(2), issue.author
3262 3278 assert_equal 2, issue.author_id
3263 3279 assert_not_equal User.current, issue.author
3264 3280 end
3265 3281
3266 3282 def test_update_form_for_existing_issue_should_propose_transitions_based_on_initial_status
3267 3283 @request.session[:user_id] = 2
3268 3284 WorkflowTransition.delete_all
3269 3285 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
3270 3286 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
3271 3287 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 5, :new_status_id => 4)
3272 3288
3273 3289 xhr :patch, :edit, :id => 2,
3274 3290 :issue => {:tracker_id => 2,
3275 3291 :status_id => 5,
3276 3292 :subject => 'This is an issue'}
3277 3293
3278 3294 assert_equal 5, assigns(:issue).status_id
3279 3295 assert_equal [1,2,5], assigns(:allowed_statuses).map(&:id).sort
3280 3296 end
3281 3297
3282 3298 def test_update_form_for_existing_issue_with_project_change
3283 3299 @request.session[:user_id] = 2
3284 3300 xhr :patch, :edit, :id => 1,
3285 3301 :issue => {:project_id => 2,
3286 3302 :tracker_id => 2,
3287 3303 :subject => 'This is the test_new issue',
3288 3304 :description => 'This is the description',
3289 3305 :priority_id => 5}
3290 3306 assert_response :success
3291 3307 assert_template :partial => '_form'
3292 3308
3293 3309 issue = assigns(:issue)
3294 3310 assert_kind_of Issue, issue
3295 3311 assert_equal 1, issue.id
3296 3312 assert_equal 2, issue.project_id
3297 3313 assert_equal 2, issue.tracker_id
3298 3314 assert_equal 'This is the test_new issue', issue.subject
3299 3315 end
3300 3316
3301 3317 def test_update_form_should_keep_category_with_same_when_changing_project
3302 3318 source = Project.generate!
3303 3319 target = Project.generate!
3304 3320 source_category = IssueCategory.create!(:name => 'Foo', :project => source)
3305 3321 target_category = IssueCategory.create!(:name => 'Foo', :project => target)
3306 3322 issue = Issue.generate!(:project => source, :category => source_category)
3307 3323
3308 3324 @request.session[:user_id] = 1
3309 3325 patch :edit, :id => issue.id,
3310 3326 :issue => {:project_id => target.id, :category_id => source_category.id}
3311 3327 assert_response :success
3312 3328
3313 3329 issue = assigns(:issue)
3314 3330 assert_equal target_category, issue.category
3315 3331 end
3316 3332
3317 3333 def test_update_form_should_propose_default_status_for_existing_issue
3318 3334 @request.session[:user_id] = 2
3319 3335 WorkflowTransition.delete_all
3320 3336 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3)
3321 3337
3322 3338 xhr :patch, :edit, :id => 2
3323 3339 assert_response :success
3324 3340 assert_equal [2,3], assigns(:allowed_statuses).map(&:id).sort
3325 3341 end
3326 3342
3327 3343 def test_put_update_without_custom_fields_param
3328 3344 @request.session[:user_id] = 2
3329 3345
3330 3346 issue = Issue.find(1)
3331 3347 assert_equal '125', issue.custom_value_for(2).value
3332 3348
3333 3349 assert_difference('Journal.count') do
3334 3350 assert_difference('JournalDetail.count') do
3335 3351 put :update, :id => 1, :issue => {:subject => 'New subject'}
3336 3352 end
3337 3353 end
3338 3354 assert_redirected_to :action => 'show', :id => '1'
3339 3355 issue.reload
3340 3356 assert_equal 'New subject', issue.subject
3341 3357 # Make sure custom fields were not cleared
3342 3358 assert_equal '125', issue.custom_value_for(2).value
3343 3359 end
3344 3360
3345 3361 def test_put_update_with_project_change
3346 3362 @request.session[:user_id] = 2
3347 3363 ActionMailer::Base.deliveries.clear
3348 3364
3349 3365 with_settings :notified_events => %w(issue_updated) do
3350 3366 assert_difference('Journal.count') do
3351 3367 assert_difference('JournalDetail.count', 3) do
3352 3368 put :update, :id => 1, :issue => {:project_id => '2',
3353 3369 :tracker_id => '1', # no change
3354 3370 :priority_id => '6',
3355 3371 :category_id => '3'
3356 3372 }
3357 3373 end
3358 3374 end
3359 3375 end
3360 3376 assert_redirected_to :action => 'show', :id => '1'
3361 3377 issue = Issue.find(1)
3362 3378 assert_equal 2, issue.project_id
3363 3379 assert_equal 1, issue.tracker_id
3364 3380 assert_equal 6, issue.priority_id
3365 3381 assert_equal 3, issue.category_id
3366 3382
3367 3383 mail = ActionMailer::Base.deliveries.last
3368 3384 assert_not_nil mail
3369 3385 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
3370 3386 assert_mail_body_match "Project changed from eCookbook to OnlineStore", mail
3371 3387 end
3372 3388
3373 3389 def test_put_update_trying_to_move_issue_to_project_without_tracker_should_not_error
3374 3390 target = Project.generate!(:tracker_ids => [])
3375 3391 assert target.trackers.empty?
3376 3392 issue = Issue.generate!
3377 3393 @request.session[:user_id] = 1
3378 3394
3379 3395 put :update, :id => issue.id, :issue => {:project_id => target.id}
3380 3396 assert_response 302
3381 3397 end
3382 3398
3383 3399 def test_put_update_with_tracker_change
3384 3400 @request.session[:user_id] = 2
3385 3401 ActionMailer::Base.deliveries.clear
3386 3402
3387 3403 with_settings :notified_events => %w(issue_updated) do
3388 3404 assert_difference('Journal.count') do
3389 3405 assert_difference('JournalDetail.count', 2) do
3390 3406 put :update, :id => 1, :issue => {:project_id => '1',
3391 3407 :tracker_id => '2',
3392 3408 :priority_id => '6'
3393 3409 }
3394 3410 end
3395 3411 end
3396 3412 end
3397 3413 assert_redirected_to :action => 'show', :id => '1'
3398 3414 issue = Issue.find(1)
3399 3415 assert_equal 1, issue.project_id
3400 3416 assert_equal 2, issue.tracker_id
3401 3417 assert_equal 6, issue.priority_id
3402 3418 assert_equal 1, issue.category_id
3403 3419
3404 3420 mail = ActionMailer::Base.deliveries.last
3405 3421 assert_not_nil mail
3406 3422 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
3407 3423 assert_mail_body_match "Tracker changed from Bug to Feature request", mail
3408 3424 end
3409 3425
3410 3426 def test_put_update_with_custom_field_change
3411 3427 @request.session[:user_id] = 2
3412 3428 issue = Issue.find(1)
3413 3429 assert_equal '125', issue.custom_value_for(2).value
3414 3430
3415 3431 with_settings :notified_events => %w(issue_updated) do
3416 3432 assert_difference('Journal.count') do
3417 3433 assert_difference('JournalDetail.count', 3) do
3418 3434 put :update, :id => 1, :issue => {:subject => 'Custom field change',
3419 3435 :priority_id => '6',
3420 3436 :category_id => '1', # no change
3421 3437 :custom_field_values => { '2' => 'New custom value' }
3422 3438 }
3423 3439 end
3424 3440 end
3425 3441 end
3426 3442 assert_redirected_to :action => 'show', :id => '1'
3427 3443 issue.reload
3428 3444 assert_equal 'New custom value', issue.custom_value_for(2).value
3429 3445
3430 3446 mail = ActionMailer::Base.deliveries.last
3431 3447 assert_not_nil mail
3432 3448 assert_mail_body_match "Searchable field changed from 125 to New custom value", mail
3433 3449 end
3434 3450
3435 3451 def test_put_update_with_multi_custom_field_change
3436 3452 field = CustomField.find(1)
3437 3453 field.update_attribute :multiple, true
3438 3454 issue = Issue.find(1)
3439 3455 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
3440 3456 issue.save!
3441 3457
3442 3458 @request.session[:user_id] = 2
3443 3459 assert_difference('Journal.count') do
3444 3460 assert_difference('JournalDetail.count', 3) do
3445 3461 put :update, :id => 1,
3446 3462 :issue => {
3447 3463 :subject => 'Custom field change',
3448 3464 :custom_field_values => { '1' => ['', 'Oracle', 'PostgreSQL'] }
3449 3465 }
3450 3466 end
3451 3467 end
3452 3468 assert_redirected_to :action => 'show', :id => '1'
3453 3469 assert_equal ['Oracle', 'PostgreSQL'], Issue.find(1).custom_field_value(1).sort
3454 3470 end
3455 3471
3456 3472 def test_put_update_with_status_and_assignee_change
3457 3473 issue = Issue.find(1)
3458 3474 assert_equal 1, issue.status_id
3459 3475 @request.session[:user_id] = 2
3460 3476
3461 3477 with_settings :notified_events => %w(issue_updated) do
3462 3478 assert_difference('TimeEntry.count', 0) do
3463 3479 put :update,
3464 3480 :id => 1,
3465 3481 :issue => { :status_id => 2, :assigned_to_id => 3, :notes => 'Assigned to dlopper' },
3466 3482 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
3467 3483 end
3468 3484 end
3469 3485 assert_redirected_to :action => 'show', :id => '1'
3470 3486 issue.reload
3471 3487 assert_equal 2, issue.status_id
3472 3488 j = Journal.order('id DESC').first
3473 3489 assert_equal 'Assigned to dlopper', j.notes
3474 3490 assert_equal 2, j.details.size
3475 3491
3476 3492 mail = ActionMailer::Base.deliveries.last
3477 3493 assert_mail_body_match "Status changed from New to Assigned", mail
3478 3494 # subject should contain the new status
3479 3495 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
3480 3496 end
3481 3497
3482 3498 def test_put_update_with_note_only
3483 3499 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
3484 3500
3485 3501 with_settings :notified_events => %w(issue_updated) do
3486 3502 # anonymous user
3487 3503 put :update,
3488 3504 :id => 1,
3489 3505 :issue => { :notes => notes }
3490 3506 end
3491 3507 assert_redirected_to :action => 'show', :id => '1'
3492 3508 j = Journal.order('id DESC').first
3493 3509 assert_equal notes, j.notes
3494 3510 assert_equal 0, j.details.size
3495 3511 assert_equal User.anonymous, j.user
3496 3512
3497 3513 mail = ActionMailer::Base.deliveries.last
3498 3514 assert_mail_body_match notes, mail
3499 3515 end
3500 3516
3501 3517 def test_put_update_with_private_note_only
3502 3518 notes = 'Private note'
3503 3519 @request.session[:user_id] = 2
3504 3520
3505 3521 assert_difference 'Journal.count' do
3506 3522 put :update, :id => 1, :issue => {:notes => notes, :private_notes => '1'}
3507 3523 assert_redirected_to :action => 'show', :id => '1'
3508 3524 end
3509 3525
3510 3526 j = Journal.order('id DESC').first
3511 3527 assert_equal notes, j.notes
3512 3528 assert_equal true, j.private_notes
3513 3529 end
3514 3530
3515 3531 def test_put_update_with_private_note_and_changes
3516 3532 notes = 'Private note'
3517 3533 @request.session[:user_id] = 2
3518 3534
3519 3535 assert_difference 'Journal.count', 2 do
3520 3536 put :update, :id => 1, :issue => {:subject => 'New subject', :notes => notes, :private_notes => '1'}
3521 3537 assert_redirected_to :action => 'show', :id => '1'
3522 3538 end
3523 3539
3524 3540 j = Journal.order('id DESC').first
3525 3541 assert_equal notes, j.notes
3526 3542 assert_equal true, j.private_notes
3527 3543 assert_equal 0, j.details.count
3528 3544
3529 3545 j = Journal.order('id DESC').offset(1).first
3530 3546 assert_nil j.notes
3531 3547 assert_equal false, j.private_notes
3532 3548 assert_equal 1, j.details.count
3533 3549 end
3534 3550
3535 3551 def test_put_update_with_note_and_spent_time
3536 3552 @request.session[:user_id] = 2
3537 3553 spent_hours_before = Issue.find(1).spent_hours
3538 3554 assert_difference('TimeEntry.count') do
3539 3555 put :update,
3540 3556 :id => 1,
3541 3557 :issue => { :notes => '2.5 hours added' },
3542 3558 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
3543 3559 end
3544 3560 assert_redirected_to :action => 'show', :id => '1'
3545 3561
3546 3562 issue = Issue.find(1)
3547 3563
3548 3564 j = Journal.order('id DESC').first
3549 3565 assert_equal '2.5 hours added', j.notes
3550 3566 assert_equal 0, j.details.size
3551 3567
3552 3568 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
3553 3569 assert_not_nil t
3554 3570 assert_equal 2.5, t.hours
3555 3571 assert_equal spent_hours_before + 2.5, issue.spent_hours
3556 3572 end
3557 3573
3558 3574 def test_put_update_should_preserve_parent_issue_even_if_not_visible
3559 3575 parent = Issue.generate!(:project_id => 1, :is_private => true)
3560 3576 issue = Issue.generate!(:parent_issue_id => parent.id)
3561 3577 assert !parent.visible?(User.find(3))
3562 3578 @request.session[:user_id] = 3
3563 3579
3564 3580 get :edit, :id => issue.id
3565 3581 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', parent.id.to_s
3566 3582
3567 3583 put :update, :id => issue.id, :issue => {:subject => 'New subject', :parent_issue_id => parent.id.to_s}
3568 3584 assert_response 302
3569 3585 assert_equal parent, issue.parent
3570 3586 end
3571 3587
3572 3588 def test_put_update_with_attachment_only
3573 3589 set_tmp_attachments_directory
3574 3590
3575 3591 # Delete all fixtured journals, a race condition can occur causing the wrong
3576 3592 # journal to get fetched in the next find.
3577 3593 Journal.delete_all
3578 3594
3579 3595 with_settings :notified_events => %w(issue_updated) do
3580 3596 # anonymous user
3581 3597 assert_difference 'Attachment.count' do
3582 3598 put :update, :id => 1,
3583 3599 :issue => {:notes => ''},
3584 3600 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
3585 3601 end
3586 3602 end
3587 3603
3588 3604 assert_redirected_to :action => 'show', :id => '1'
3589 3605 j = Issue.find(1).journals.reorder('id DESC').first
3590 3606 assert j.notes.blank?
3591 3607 assert_equal 1, j.details.size
3592 3608 assert_equal 'testfile.txt', j.details.first.value
3593 3609 assert_equal User.anonymous, j.user
3594 3610
3595 3611 attachment = Attachment.order('id DESC').first
3596 3612 assert_equal Issue.find(1), attachment.container
3597 3613 assert_equal User.anonymous, attachment.author
3598 3614 assert_equal 'testfile.txt', attachment.filename
3599 3615 assert_equal 'text/plain', attachment.content_type
3600 3616 assert_equal 'test file', attachment.description
3601 3617 assert_equal 59, attachment.filesize
3602 3618 assert File.exists?(attachment.diskfile)
3603 3619 assert_equal 59, File.size(attachment.diskfile)
3604 3620
3605 3621 mail = ActionMailer::Base.deliveries.last
3606 3622 assert_mail_body_match 'testfile.txt', mail
3607 3623 end
3608 3624
3609 3625 def test_put_update_with_failure_should_save_attachments
3610 3626 set_tmp_attachments_directory
3611 3627 @request.session[:user_id] = 2
3612 3628
3613 3629 assert_no_difference 'Journal.count' do
3614 3630 assert_difference 'Attachment.count' do
3615 3631 put :update, :id => 1,
3616 3632 :issue => { :subject => '' },
3617 3633 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
3618 3634 assert_response :success
3619 3635 assert_template 'edit'
3620 3636 end
3621 3637 end
3622 3638
3623 3639 attachment = Attachment.order('id DESC').first
3624 3640 assert_equal 'testfile.txt', attachment.filename
3625 3641 assert File.exists?(attachment.diskfile)
3626 3642 assert_nil attachment.container
3627 3643
3628 3644 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
3629 3645 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
3630 3646 end
3631 3647
3632 3648 def test_put_update_with_failure_should_keep_saved_attachments
3633 3649 set_tmp_attachments_directory
3634 3650 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
3635 3651 @request.session[:user_id] = 2
3636 3652
3637 3653 assert_no_difference 'Journal.count' do
3638 3654 assert_no_difference 'Attachment.count' do
3639 3655 put :update, :id => 1,
3640 3656 :issue => { :subject => '' },
3641 3657 :attachments => {'p0' => {'token' => attachment.token}}
3642 3658 assert_response :success
3643 3659 assert_template 'edit'
3644 3660 end
3645 3661 end
3646 3662
3647 3663 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
3648 3664 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
3649 3665 end
3650 3666
3651 3667 def test_put_update_should_attach_saved_attachments
3652 3668 set_tmp_attachments_directory
3653 3669 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
3654 3670 @request.session[:user_id] = 2
3655 3671
3656 3672 assert_difference 'Journal.count' do
3657 3673 assert_difference 'JournalDetail.count' do
3658 3674 assert_no_difference 'Attachment.count' do
3659 3675 put :update, :id => 1,
3660 3676 :issue => {:notes => 'Attachment added'},
3661 3677 :attachments => {'p0' => {'token' => attachment.token}}
3662 3678 assert_redirected_to '/issues/1'
3663 3679 end
3664 3680 end
3665 3681 end
3666 3682
3667 3683 attachment.reload
3668 3684 assert_equal Issue.find(1), attachment.container
3669 3685
3670 3686 journal = Journal.order('id DESC').first
3671 3687 assert_equal 1, journal.details.size
3672 3688 assert_equal 'testfile.txt', journal.details.first.value
3673 3689 end
3674 3690
3675 3691 def test_put_update_with_attachment_that_fails_to_save
3676 3692 set_tmp_attachments_directory
3677 3693
3678 3694 # anonymous user
3679 3695 with_settings :attachment_max_size => 0 do
3680 3696 put :update,
3681 3697 :id => 1,
3682 3698 :issue => {:notes => ''},
3683 3699 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
3684 3700 assert_redirected_to :action => 'show', :id => '1'
3685 3701 assert_equal '1 file(s) could not be saved.', flash[:warning]
3686 3702 end
3687 3703 end
3688 3704
3689 3705 def test_put_update_with_no_change
3690 3706 issue = Issue.find(1)
3691 3707 issue.journals.clear
3692 3708 ActionMailer::Base.deliveries.clear
3693 3709
3694 3710 put :update,
3695 3711 :id => 1,
3696 3712 :issue => {:notes => ''}
3697 3713 assert_redirected_to :action => 'show', :id => '1'
3698 3714
3699 3715 issue.reload
3700 3716 assert issue.journals.empty?
3701 3717 # No email should be sent
3702 3718 assert ActionMailer::Base.deliveries.empty?
3703 3719 end
3704 3720
3705 3721 def test_put_update_should_send_a_notification
3706 3722 @request.session[:user_id] = 2
3707 3723 ActionMailer::Base.deliveries.clear
3708 3724 issue = Issue.find(1)
3709 3725 old_subject = issue.subject
3710 3726 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
3711 3727
3712 3728 with_settings :notified_events => %w(issue_updated) do
3713 3729 put :update, :id => 1, :issue => {:subject => new_subject,
3714 3730 :priority_id => '6',
3715 3731 :category_id => '1' # no change
3716 3732 }
3717 3733 assert_equal 1, ActionMailer::Base.deliveries.size
3718 3734 end
3719 3735 end
3720 3736
3721 3737 def test_put_update_with_invalid_spent_time_hours_only
3722 3738 @request.session[:user_id] = 2
3723 3739 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3724 3740
3725 3741 assert_no_difference('Journal.count') do
3726 3742 put :update,
3727 3743 :id => 1,
3728 3744 :issue => {:notes => notes},
3729 3745 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
3730 3746 end
3731 3747 assert_response :success
3732 3748 assert_template 'edit'
3733 3749
3734 3750 assert_select_error /Activity cannot be blank/
3735 3751 assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
3736 3752 assert_select 'input[name=?][value=?]', 'time_entry[hours]', '2z'
3737 3753 end
3738 3754
3739 3755 def test_put_update_with_invalid_spent_time_comments_only
3740 3756 @request.session[:user_id] = 2
3741 3757 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3742 3758
3743 3759 assert_no_difference('Journal.count') do
3744 3760 put :update,
3745 3761 :id => 1,
3746 3762 :issue => {:notes => notes},
3747 3763 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
3748 3764 end
3749 3765 assert_response :success
3750 3766 assert_template 'edit'
3751 3767
3752 3768 assert_select_error /Activity cannot be blank/
3753 3769 assert_select_error /Hours cannot be blank/
3754 3770 assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
3755 3771 assert_select 'input[name=?][value=?]', 'time_entry[comments]', 'this is my comment'
3756 3772 end
3757 3773
3758 3774 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
3759 3775 issue = Issue.find(2)
3760 3776 @request.session[:user_id] = 2
3761 3777
3762 3778 put :update,
3763 3779 :id => issue.id,
3764 3780 :issue => {
3765 3781 :fixed_version_id => 4
3766 3782 }
3767 3783
3768 3784 assert_response :redirect
3769 3785 issue.reload
3770 3786 assert_equal 4, issue.fixed_version_id
3771 3787 assert_not_equal issue.project_id, issue.fixed_version.project_id
3772 3788 end
3773 3789
3774 3790 def test_put_update_should_redirect_back_using_the_back_url_parameter
3775 3791 issue = Issue.find(2)
3776 3792 @request.session[:user_id] = 2
3777 3793
3778 3794 put :update,
3779 3795 :id => issue.id,
3780 3796 :issue => {
3781 3797 :fixed_version_id => 4
3782 3798 },
3783 3799 :back_url => '/issues'
3784 3800
3785 3801 assert_response :redirect
3786 3802 assert_redirected_to '/issues'
3787 3803 end
3788 3804
3789 3805 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
3790 3806 issue = Issue.find(2)
3791 3807 @request.session[:user_id] = 2
3792 3808
3793 3809 put :update,
3794 3810 :id => issue.id,
3795 3811 :issue => {
3796 3812 :fixed_version_id => 4
3797 3813 },
3798 3814 :back_url => 'http://google.com'
3799 3815
3800 3816 assert_response :redirect
3801 3817 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
3802 3818 end
3803 3819
3804 3820 def test_put_update_should_redirect_with_previous_and_next_issue_ids_params
3805 3821 @request.session[:user_id] = 2
3806 3822
3807 3823 put :update, :id => 11,
3808 3824 :issue => {:status_id => 6, :notes => 'Notes'},
3809 3825 :prev_issue_id => 8,
3810 3826 :next_issue_id => 12,
3811 3827 :issue_position => 2,
3812 3828 :issue_count => 3
3813 3829
3814 3830 assert_redirected_to '/issues/11?issue_count=3&issue_position=2&next_issue_id=12&prev_issue_id=8'
3815 3831 end
3816 3832
3817 3833 def test_get_bulk_edit
3818 3834 @request.session[:user_id] = 2
3819 3835 get :bulk_edit, :ids => [1, 3]
3820 3836 assert_response :success
3821 3837 assert_template 'bulk_edit'
3822 3838
3823 3839 assert_select 'ul#bulk-selection' do
3824 3840 assert_select 'li', 2
3825 3841 assert_select 'li a', :text => 'Bug #1'
3826 3842 end
3827 3843
3828 3844 assert_select 'form#bulk_edit_form[action=?]', '/issues/bulk_update' do
3829 3845 assert_select 'input[name=?]', 'ids[]', 2
3830 3846 assert_select 'input[name=?][value="1"][type=hidden]', 'ids[]'
3831 3847
3832 3848 assert_select 'select[name=?]', 'issue[project_id]'
3833 3849 assert_select 'input[name=?]', 'issue[parent_issue_id]'
3834 3850
3835 3851 # Project specific custom field, date type
3836 3852 field = CustomField.find(9)
3837 3853 assert !field.is_for_all?
3838 3854 assert_equal 'date', field.field_format
3839 3855 assert_select 'input[name=?]', 'issue[custom_field_values][9]'
3840 3856
3841 3857 # System wide custom field
3842 3858 assert CustomField.find(1).is_for_all?
3843 3859 assert_select 'select[name=?]', 'issue[custom_field_values][1]'
3844 3860
3845 3861 # Be sure we don't display inactive IssuePriorities
3846 3862 assert ! IssuePriority.find(15).active?
3847 3863 assert_select 'select[name=?]', 'issue[priority_id]' do
3848 3864 assert_select 'option[value="15"]', 0
3849 3865 end
3850 3866 end
3851 3867 end
3852 3868
3853 3869 def test_get_bulk_edit_on_different_projects
3854 3870 @request.session[:user_id] = 2
3855 3871 get :bulk_edit, :ids => [1, 2, 6]
3856 3872 assert_response :success
3857 3873 assert_template 'bulk_edit'
3858 3874
3859 3875 # Can not set issues from different projects as children of an issue
3860 3876 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
3861 3877
3862 3878 # Project specific custom field, date type
3863 3879 field = CustomField.find(9)
3864 3880 assert !field.is_for_all?
3865 3881 assert !field.project_ids.include?(Issue.find(6).project_id)
3866 3882 assert_select 'input[name=?]', 'issue[custom_field_values][9]', 0
3867 3883 end
3868 3884
3869 3885 def test_get_bulk_edit_with_user_custom_field
3870 3886 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true, :tracker_ids => [1,2,3])
3871 3887
3872 3888 @request.session[:user_id] = 2
3873 3889 get :bulk_edit, :ids => [1, 2]
3874 3890 assert_response :success
3875 3891 assert_template 'bulk_edit'
3876 3892
3877 3893 assert_select 'select.user_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
3878 3894 assert_select 'option', Project.find(1).users.count + 2 # "no change" + "none" options
3879 3895 end
3880 3896 end
3881 3897
3882 3898 def test_get_bulk_edit_with_version_custom_field
3883 3899 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
3884 3900
3885 3901 @request.session[:user_id] = 2
3886 3902 get :bulk_edit, :ids => [1, 2]
3887 3903 assert_response :success
3888 3904 assert_template 'bulk_edit'
3889 3905
3890 3906 assert_select 'select.version_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
3891 3907 assert_select 'option', Project.find(1).shared_versions.count + 2 # "no change" + "none" options
3892 3908 end
3893 3909 end
3894 3910
3895 3911 def test_get_bulk_edit_with_multi_custom_field
3896 3912 field = CustomField.find(1)
3897 3913 field.update_attribute :multiple, true
3898 3914
3899 3915 @request.session[:user_id] = 2
3900 3916 get :bulk_edit, :ids => [1, 3]
3901 3917 assert_response :success
3902 3918 assert_template 'bulk_edit'
3903 3919
3904 3920 assert_select 'select[name=?]', 'issue[custom_field_values][1][]' do
3905 3921 assert_select 'option', field.possible_values.size + 1 # "none" options
3906 3922 end
3907 3923 end
3908 3924
3909 3925 def test_bulk_edit_should_propose_to_clear_text_custom_fields
3910 3926 @request.session[:user_id] = 2
3911 3927 get :bulk_edit, :ids => [1, 3]
3912 3928 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', '__none__'
3913 3929 end
3914 3930
3915 3931 def test_bulk_edit_should_only_propose_statuses_allowed_for_all_issues
3916 3932 WorkflowTransition.delete_all
3917 3933 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3918 3934 :old_status_id => 1, :new_status_id => 1)
3919 3935 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3920 3936 :old_status_id => 1, :new_status_id => 3)
3921 3937 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3922 3938 :old_status_id => 1, :new_status_id => 4)
3923 3939 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3924 3940 :old_status_id => 2, :new_status_id => 1)
3925 3941 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3926 3942 :old_status_id => 2, :new_status_id => 3)
3927 3943 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3928 3944 :old_status_id => 2, :new_status_id => 5)
3929 3945 @request.session[:user_id] = 2
3930 3946 get :bulk_edit, :ids => [1, 2]
3931 3947
3932 3948 assert_response :success
3933 3949 statuses = assigns(:available_statuses)
3934 3950 assert_not_nil statuses
3935 3951 assert_equal [1, 3], statuses.map(&:id).sort
3936 3952
3937 3953 assert_select 'select[name=?]', 'issue[status_id]' do
3938 3954 assert_select 'option', 3 # 2 statuses + "no change" option
3939 3955 end
3940 3956 end
3941 3957
3942 3958 def test_bulk_edit_should_propose_target_project_open_shared_versions
3943 3959 @request.session[:user_id] = 2
3944 3960 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3945 3961 assert_response :success
3946 3962 assert_template 'bulk_edit'
3947 3963 assert_equal Project.find(1).shared_versions.open.to_a.sort, assigns(:versions).sort
3948 3964
3949 3965 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
3950 3966 assert_select 'option', :text => '2.0'
3951 3967 end
3952 3968 end
3953 3969
3954 3970 def test_bulk_edit_should_propose_target_project_categories
3955 3971 @request.session[:user_id] = 2
3956 3972 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3957 3973 assert_response :success
3958 3974 assert_template 'bulk_edit'
3959 3975 assert_equal Project.find(1).issue_categories.sort, assigns(:categories).sort
3960 3976
3961 3977 assert_select 'select[name=?]', 'issue[category_id]' do
3962 3978 assert_select 'option', :text => 'Recipes'
3963 3979 end
3964 3980 end
3965 3981
3966 3982 def test_bulk_edit_should_only_propose_issues_trackers_custom_fields
3967 3983 IssueCustomField.delete_all
3968 3984 field = IssueCustomField.generate!(:tracker_ids => [1], :is_for_all => true)
3969 3985 IssueCustomField.generate!(:tracker_ids => [2], :is_for_all => true)
3970 3986 @request.session[:user_id] = 2
3971 3987
3972 3988 issue_ids = Issue.where(:project_id => 1, :tracker_id => 1).limit(2).ids
3973 3989 get :bulk_edit, :ids => issue_ids
3974 3990 assert_equal [field], assigns(:custom_fields)
3975 3991 end
3976 3992
3977 3993 def test_bulk_update
3978 3994 @request.session[:user_id] = 2
3979 3995 # update issues priority
3980 3996 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
3981 3997 :issue => {:priority_id => 7,
3982 3998 :assigned_to_id => '',
3983 3999 :custom_field_values => {'2' => ''}}
3984 4000
3985 4001 assert_response 302
3986 4002 # check that the issues were updated
3987 4003 assert_equal [7, 7], Issue.where(:id =>[1, 2]).collect {|i| i.priority.id}
3988 4004
3989 4005 issue = Issue.find(1)
3990 4006 journal = issue.journals.reorder('created_on DESC').first
3991 4007 assert_equal '125', issue.custom_value_for(2).value
3992 4008 assert_equal 'Bulk editing', journal.notes
3993 4009 assert_equal 1, journal.details.size
3994 4010 end
3995 4011
3996 4012 def test_bulk_update_with_group_assignee
3997 4013 group = Group.find(11)
3998 4014 project = Project.find(1)
3999 4015 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
4000 4016
4001 4017 @request.session[:user_id] = 2
4002 4018 # update issues assignee
4003 4019 with_settings :issue_group_assignment => '1' do
4004 4020 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
4005 4021 :issue => {:priority_id => '',
4006 4022 :assigned_to_id => group.id,
4007 4023 :custom_field_values => {'2' => ''}}
4008 4024
4009 4025 assert_response 302
4010 4026 assert_equal [group, group], Issue.where(:id => [1, 2]).collect {|i| i.assigned_to}
4011 4027 end
4012 4028 end
4013 4029
4014 4030 def test_bulk_update_on_different_projects
4015 4031 @request.session[:user_id] = 2
4016 4032 # update issues priority
4017 4033 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
4018 4034 :issue => {:priority_id => 7,
4019 4035 :assigned_to_id => '',
4020 4036 :custom_field_values => {'2' => ''}}
4021 4037
4022 4038 assert_response 302
4023 4039 # check that the issues were updated
4024 4040 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
4025 4041
4026 4042 issue = Issue.find(1)
4027 4043 journal = issue.journals.reorder('created_on DESC').first
4028 4044 assert_equal '125', issue.custom_value_for(2).value
4029 4045 assert_equal 'Bulk editing', journal.notes
4030 4046 assert_equal 1, journal.details.size
4031 4047 end
4032 4048
4033 4049 def test_bulk_update_on_different_projects_without_rights
4034 4050 @request.session[:user_id] = 3
4035 4051 user = User.find(3)
4036 4052 action = { :controller => "issues", :action => "bulk_update" }
4037 4053 assert user.allowed_to?(action, Issue.find(1).project)
4038 4054 assert ! user.allowed_to?(action, Issue.find(6).project)
4039 4055 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
4040 4056 :issue => {:priority_id => 7,
4041 4057 :assigned_to_id => '',
4042 4058 :custom_field_values => {'2' => ''}}
4043 4059 assert_response 403
4044 4060 assert_not_equal "Bulk should fail", Journal.last.notes
4045 4061 end
4046 4062
4047 4063 def test_bullk_update_should_send_a_notification
4048 4064 @request.session[:user_id] = 2
4049 4065 ActionMailer::Base.deliveries.clear
4050 4066 with_settings :notified_events => %w(issue_updated) do
4051 4067 post(:bulk_update,
4052 4068 {
4053 4069 :ids => [1, 2],
4054 4070 :notes => 'Bulk editing',
4055 4071 :issue => {
4056 4072 :priority_id => 7,
4057 4073 :assigned_to_id => '',
4058 4074 :custom_field_values => {'2' => ''}
4059 4075 }
4060 4076 })
4061 4077 assert_response 302
4062 4078 assert_equal 2, ActionMailer::Base.deliveries.size
4063 4079 end
4064 4080 end
4065 4081
4066 4082 def test_bulk_update_project
4067 4083 @request.session[:user_id] = 2
4068 4084 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
4069 4085 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4070 4086 # Issues moved to project 2
4071 4087 assert_equal 2, Issue.find(1).project_id
4072 4088 assert_equal 2, Issue.find(2).project_id
4073 4089 # No tracker change
4074 4090 assert_equal 1, Issue.find(1).tracker_id
4075 4091 assert_equal 2, Issue.find(2).tracker_id
4076 4092 end
4077 4093
4078 4094 def test_bulk_update_project_on_single_issue_should_follow_when_needed
4079 4095 @request.session[:user_id] = 2
4080 4096 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
4081 4097 assert_redirected_to '/issues/1'
4082 4098 end
4083 4099
4084 4100 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
4085 4101 @request.session[:user_id] = 2
4086 4102 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
4087 4103 assert_redirected_to '/projects/onlinestore/issues'
4088 4104 end
4089 4105
4090 4106 def test_bulk_update_tracker
4091 4107 @request.session[:user_id] = 2
4092 4108 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
4093 4109 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4094 4110 assert_equal 2, Issue.find(1).tracker_id
4095 4111 assert_equal 2, Issue.find(2).tracker_id
4096 4112 end
4097 4113
4098 4114 def test_bulk_update_status
4099 4115 @request.session[:user_id] = 2
4100 4116 # update issues priority
4101 4117 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
4102 4118 :issue => {:priority_id => '',
4103 4119 :assigned_to_id => '',
4104 4120 :status_id => '5'}
4105 4121
4106 4122 assert_response 302
4107 4123 issue = Issue.find(1)
4108 4124 assert issue.closed?
4109 4125 end
4110 4126
4111 4127 def test_bulk_update_priority
4112 4128 @request.session[:user_id] = 2
4113 4129 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
4114 4130
4115 4131 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4116 4132 assert_equal 6, Issue.find(1).priority_id
4117 4133 assert_equal 6, Issue.find(2).priority_id
4118 4134 end
4119 4135
4120 4136 def test_bulk_update_with_notes
4121 4137 @request.session[:user_id] = 2
4122 4138 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
4123 4139
4124 4140 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4125 4141 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
4126 4142 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
4127 4143 end
4128 4144
4129 4145 def test_bulk_update_parent_id
4130 4146 IssueRelation.delete_all
4131 4147 @request.session[:user_id] = 2
4132 4148 post :bulk_update, :ids => [1, 3],
4133 4149 :notes => 'Bulk editing parent',
4134 4150 :issue => {:priority_id => '', :assigned_to_id => '',
4135 4151 :status_id => '', :parent_issue_id => '2'}
4136 4152 assert_response 302
4137 4153 parent = Issue.find(2)
4138 4154 assert_equal parent.id, Issue.find(1).parent_id
4139 4155 assert_equal parent.id, Issue.find(3).parent_id
4140 4156 assert_equal [1, 3], parent.children.collect(&:id).sort
4141 4157 end
4142 4158
4143 4159 def test_bulk_update_estimated_hours
4144 4160 @request.session[:user_id] = 2
4145 4161 post :bulk_update, :ids => [1, 2], :issue => {:estimated_hours => 4.25}
4146 4162
4147 4163 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4148 4164 assert_equal 4.25, Issue.find(1).estimated_hours
4149 4165 assert_equal 4.25, Issue.find(2).estimated_hours
4150 4166 end
4151 4167
4152 4168 def test_bulk_update_custom_field
4153 4169 @request.session[:user_id] = 2
4154 4170 # update issues priority
4155 4171 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
4156 4172 :issue => {:priority_id => '',
4157 4173 :assigned_to_id => '',
4158 4174 :custom_field_values => {'2' => '777'}}
4159 4175
4160 4176 assert_response 302
4161 4177
4162 4178 issue = Issue.find(1)
4163 4179 journal = issue.journals.reorder('created_on DESC').first
4164 4180 assert_equal '777', issue.custom_value_for(2).value
4165 4181 assert_equal 1, journal.details.size
4166 4182 assert_equal '125', journal.details.first.old_value
4167 4183 assert_equal '777', journal.details.first.value
4168 4184 end
4169 4185
4170 4186 def test_bulk_update_custom_field_to_blank
4171 4187 @request.session[:user_id] = 2
4172 4188 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing custom field',
4173 4189 :issue => {:priority_id => '',
4174 4190 :assigned_to_id => '',
4175 4191 :custom_field_values => {'1' => '__none__'}}
4176 4192 assert_response 302
4177 4193 assert_equal '', Issue.find(1).custom_field_value(1)
4178 4194 assert_equal '', Issue.find(3).custom_field_value(1)
4179 4195 end
4180 4196
4181 4197 def test_bulk_update_multi_custom_field
4182 4198 field = CustomField.find(1)
4183 4199 field.update_attribute :multiple, true
4184 4200
4185 4201 @request.session[:user_id] = 2
4186 4202 post :bulk_update, :ids => [1, 2, 3], :notes => 'Bulk editing multi custom field',
4187 4203 :issue => {:priority_id => '',
4188 4204 :assigned_to_id => '',
4189 4205 :custom_field_values => {'1' => ['MySQL', 'Oracle']}}
4190 4206
4191 4207 assert_response 302
4192 4208
4193 4209 assert_equal ['MySQL', 'Oracle'], Issue.find(1).custom_field_value(1).sort
4194 4210 assert_equal ['MySQL', 'Oracle'], Issue.find(3).custom_field_value(1).sort
4195 4211 # the custom field is not associated with the issue tracker
4196 4212 assert_nil Issue.find(2).custom_field_value(1)
4197 4213 end
4198 4214
4199 4215 def test_bulk_update_multi_custom_field_to_blank
4200 4216 field = CustomField.find(1)
4201 4217 field.update_attribute :multiple, true
4202 4218
4203 4219 @request.session[:user_id] = 2
4204 4220 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing multi custom field',
4205 4221 :issue => {:priority_id => '',
4206 4222 :assigned_to_id => '',
4207 4223 :custom_field_values => {'1' => ['__none__']}}
4208 4224 assert_response 302
4209 4225 assert_equal [''], Issue.find(1).custom_field_value(1)
4210 4226 assert_equal [''], Issue.find(3).custom_field_value(1)
4211 4227 end
4212 4228
4213 4229 def test_bulk_update_unassign
4214 4230 assert_not_nil Issue.find(2).assigned_to
4215 4231 @request.session[:user_id] = 2
4216 4232 # unassign issues
4217 4233 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
4218 4234 assert_response 302
4219 4235 # check that the issues were updated
4220 4236 assert_nil Issue.find(2).assigned_to
4221 4237 end
4222 4238
4223 4239 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
4224 4240 @request.session[:user_id] = 2
4225 4241
4226 4242 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
4227 4243
4228 4244 assert_response :redirect
4229 4245 issues = Issue.find([1,2])
4230 4246 issues.each do |issue|
4231 4247 assert_equal 4, issue.fixed_version_id
4232 4248 assert_not_equal issue.project_id, issue.fixed_version.project_id
4233 4249 end
4234 4250 end
4235 4251
4236 4252 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
4237 4253 @request.session[:user_id] = 2
4238 4254 post :bulk_update, :ids => [1,2], :back_url => '/issues'
4239 4255
4240 4256 assert_response :redirect
4241 4257 assert_redirected_to '/issues'
4242 4258 end
4243 4259
4244 4260 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
4245 4261 @request.session[:user_id] = 2
4246 4262 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
4247 4263
4248 4264 assert_response :redirect
4249 4265 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
4250 4266 end
4251 4267
4252 4268 def test_bulk_update_with_all_failures_should_show_errors
4253 4269 @request.session[:user_id] = 2
4254 4270 post :bulk_update, :ids => [1, 2], :issue => {:start_date => 'foo'}
4255 4271
4256 4272 assert_response :success
4257 4273 assert_template 'bulk_edit'
4258 4274 assert_select '#errorExplanation span', :text => 'Failed to save 2 issue(s) on 2 selected: #1, #2.'
4259 4275 assert_select '#errorExplanation ul li', :text => 'Start date is not a valid date: #1, #2'
4260 4276
4261 4277 assert_equal [1, 2], assigns[:issues].map(&:id)
4262 4278 end
4263 4279
4264 4280 def test_bulk_update_with_some_failures_should_show_errors
4265 4281 issue1 = Issue.generate!(:start_date => '2013-05-12')
4266 4282 issue2 = Issue.generate!(:start_date => '2013-05-15')
4267 4283 issue3 = Issue.generate!
4268 4284 @request.session[:user_id] = 2
4269 4285 post :bulk_update, :ids => [issue1.id, issue2.id, issue3.id],
4270 4286 :issue => {:due_date => '2013-05-01'}
4271 4287 assert_response :success
4272 4288 assert_template 'bulk_edit'
4273 4289 assert_select '#errorExplanation span',
4274 4290 :text => "Failed to save 2 issue(s) on 3 selected: ##{issue1.id}, ##{issue2.id}."
4275 4291 assert_select '#errorExplanation ul li',
4276 4292 :text => "Due date must be greater than start date: ##{issue1.id}, ##{issue2.id}"
4277 4293 assert_equal [issue1.id, issue2.id], assigns[:issues].map(&:id)
4278 4294 end
4279 4295
4280 4296 def test_bulk_update_with_failure_should_preserved_form_values
4281 4297 @request.session[:user_id] = 2
4282 4298 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2', :start_date => 'foo'}
4283 4299
4284 4300 assert_response :success
4285 4301 assert_template 'bulk_edit'
4286 4302 assert_select 'select[name=?]', 'issue[tracker_id]' do
4287 4303 assert_select 'option[value="2"][selected=selected]'
4288 4304 end
4289 4305 assert_select 'input[name=?][value=?]', 'issue[start_date]', 'foo'
4290 4306 end
4291 4307
4292 4308 def test_get_bulk_copy
4293 4309 @request.session[:user_id] = 2
4294 4310 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
4295 4311 assert_response :success
4296 4312 assert_template 'bulk_edit'
4297 4313
4298 4314 issues = assigns(:issues)
4299 4315 assert_not_nil issues
4300 4316 assert_equal [1, 2, 3], issues.map(&:id).sort
4301 4317
4302 4318 assert_select 'select[name=?]', 'issue[project_id]' do
4303 4319 assert_select 'option[value=""]'
4304 4320 end
4305 4321 assert_select 'input[name=copy_attachments]'
4306 4322 end
4307 4323
4308 4324 def test_get_bulk_copy_without_add_issues_permission_should_not_propose_current_project_as_target
4309 4325 user = setup_user_with_copy_but_not_add_permission
4310 4326 @request.session[:user_id] = user.id
4311 4327
4312 4328 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
4313 4329 assert_response :success
4314 4330 assert_template 'bulk_edit'
4315 4331
4316 4332 assert_select 'select[name=?]', 'issue[project_id]' do
4317 4333 assert_select 'option[value=""]', 0
4318 4334 assert_select 'option[value="2"]'
4319 4335 end
4320 4336 end
4321 4337
4322 4338 def test_bulk_copy_to_another_project
4323 4339 @request.session[:user_id] = 2
4324 4340 assert_difference 'Issue.count', 2 do
4325 4341 assert_no_difference 'Project.find(1).issues.count' do
4326 4342 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
4327 4343 end
4328 4344 end
4329 4345 assert_redirected_to '/projects/ecookbook/issues'
4330 4346
4331 4347 copies = Issue.order('id DESC').limit(issues.size)
4332 4348 copies.each do |copy|
4333 4349 assert_equal 2, copy.project_id
4334 4350 end
4335 4351 end
4336 4352
4337 4353 def test_bulk_copy_without_add_issues_permission_should_be_allowed_on_project_with_permission
4338 4354 user = setup_user_with_copy_but_not_add_permission
4339 4355 @request.session[:user_id] = user.id
4340 4356
4341 4357 assert_difference 'Issue.count', 3 do
4342 4358 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '2'}, :copy => '1'
4343 4359 assert_response 302
4344 4360 end
4345 4361 end
4346 4362
4347 4363 def test_bulk_copy_on_same_project_without_add_issues_permission_should_be_denied
4348 4364 user = setup_user_with_copy_but_not_add_permission
4349 4365 @request.session[:user_id] = user.id
4350 4366
4351 4367 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => ''}, :copy => '1'
4352 4368 assert_response 403
4353 4369 end
4354 4370
4355 4371 def test_bulk_copy_on_different_project_without_add_issues_permission_should_be_denied
4356 4372 user = setup_user_with_copy_but_not_add_permission
4357 4373 @request.session[:user_id] = user.id
4358 4374
4359 4375 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '1'}, :copy => '1'
4360 4376 assert_response 403
4361 4377 end
4362 4378
4363 4379 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
4364 4380 @request.session[:user_id] = 2
4365 4381 issues = [
4366 4382 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
4367 4383 :priority_id => 2, :subject => 'issue 1', :author_id => 1,
4368 4384 :assigned_to_id => nil),
4369 4385 Issue.create!(:project_id => 2, :tracker_id => 3, :status_id => 2,
4370 4386 :priority_id => 1, :subject => 'issue 2', :author_id => 2,
4371 4387 :assigned_to_id => 3)
4372 4388 ]
4373 4389 assert_difference 'Issue.count', issues.size do
4374 4390 post :bulk_update, :ids => issues.map(&:id), :copy => '1',
4375 4391 :issue => {
4376 4392 :project_id => '', :tracker_id => '', :assigned_to_id => '',
4377 4393 :status_id => '', :start_date => '', :due_date => ''
4378 4394 }
4379 4395 end
4380 4396
4381 4397 copies = Issue.order('id DESC').limit(issues.size)
4382 4398 issues.each do |orig|
4383 4399 copy = copies.detect {|c| c.subject == orig.subject}
4384 4400 assert_not_nil copy
4385 4401 assert_equal orig.project_id, copy.project_id
4386 4402 assert_equal orig.tracker_id, copy.tracker_id
4387 4403 assert_equal orig.status_id, copy.status_id
4388 4404 assert_equal orig.assigned_to_id, copy.assigned_to_id
4389 4405 assert_equal orig.priority_id, copy.priority_id
4390 4406 end
4391 4407 end
4392 4408
4393 4409 def test_bulk_copy_should_allow_changing_the_issue_attributes
4394 4410 # Fixes random test failure with Mysql
4395 4411 # where Issue.where(:project_id => 2).limit(2).order('id desc')
4396 4412 # doesn't return the expected results
4397 4413 Issue.delete_all("project_id=2")
4398 4414
4399 4415 @request.session[:user_id] = 2
4400 4416 assert_difference 'Issue.count', 2 do
4401 4417 assert_no_difference 'Project.find(1).issues.count' do
4402 4418 post :bulk_update, :ids => [1, 2], :copy => '1',
4403 4419 :issue => {
4404 4420 :project_id => '2', :tracker_id => '', :assigned_to_id => '2',
4405 4421 :status_id => '1', :start_date => '2009-12-01', :due_date => '2009-12-31'
4406 4422 }
4407 4423 end
4408 4424 end
4409 4425
4410 4426 copied_issues = Issue.where(:project_id => 2).limit(2).order('id desc').to_a
4411 4427 assert_equal 2, copied_issues.size
4412 4428 copied_issues.each do |issue|
4413 4429 assert_equal 2, issue.project_id, "Project is incorrect"
4414 4430 assert_equal 2, issue.assigned_to_id, "Assigned to is incorrect"
4415 4431 assert_equal 1, issue.status_id, "Status is incorrect"
4416 4432 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
4417 4433 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
4418 4434 end
4419 4435 end
4420 4436
4421 4437 def test_bulk_copy_should_allow_adding_a_note
4422 4438 @request.session[:user_id] = 2
4423 4439 assert_difference 'Issue.count', 1 do
4424 4440 post :bulk_update, :ids => [1], :copy => '1',
4425 4441 :notes => 'Copying one issue',
4426 4442 :issue => {
4427 4443 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
4428 4444 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
4429 4445 }
4430 4446 end
4431 4447 issue = Issue.order('id DESC').first
4432 4448 assert_equal 1, issue.journals.size
4433 4449 journal = issue.journals.first
4434 4450 assert_equal 'Copying one issue', journal.notes
4435 4451 end
4436 4452
4437 4453 def test_bulk_copy_should_allow_not_copying_the_attachments
4438 4454 attachment_count = Issue.find(3).attachments.size
4439 4455 assert attachment_count > 0
4440 4456 @request.session[:user_id] = 2
4441 4457
4442 4458 assert_difference 'Issue.count', 1 do
4443 4459 assert_no_difference 'Attachment.count' do
4444 4460 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '0',
4445 4461 :issue => {
4446 4462 :project_id => ''
4447 4463 }
4448 4464 end
4449 4465 end
4450 4466 end
4451 4467
4452 4468 def test_bulk_copy_should_allow_copying_the_attachments
4453 4469 attachment_count = Issue.find(3).attachments.size
4454 4470 assert attachment_count > 0
4455 4471 @request.session[:user_id] = 2
4456 4472
4457 4473 assert_difference 'Issue.count', 1 do
4458 4474 assert_difference 'Attachment.count', attachment_count do
4459 4475 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '1',
4460 4476 :issue => {
4461 4477 :project_id => ''
4462 4478 }
4463 4479 end
4464 4480 end
4465 4481 end
4466 4482
4467 4483 def test_bulk_copy_should_add_relations_with_copied_issues
4468 4484 @request.session[:user_id] = 2
4469 4485
4470 4486 assert_difference 'Issue.count', 2 do
4471 4487 assert_difference 'IssueRelation.count', 2 do
4472 4488 post :bulk_update, :ids => [1, 3], :copy => '1', :link_copy => '1',
4473 4489 :issue => {
4474 4490 :project_id => '1'
4475 4491 }
4476 4492 end
4477 4493 end
4478 4494 end
4479 4495
4480 4496 def test_bulk_copy_should_allow_not_copying_the_subtasks
4481 4497 issue = Issue.generate_with_descendants!
4482 4498 @request.session[:user_id] = 2
4483 4499
4484 4500 assert_difference 'Issue.count', 1 do
4485 4501 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '0',
4486 4502 :issue => {
4487 4503 :project_id => ''
4488 4504 }
4489 4505 end
4490 4506 end
4491 4507
4492 4508 def test_bulk_copy_should_allow_copying_the_subtasks
4493 4509 issue = Issue.generate_with_descendants!
4494 4510 count = issue.descendants.count
4495 4511 @request.session[:user_id] = 2
4496 4512
4497 4513 assert_difference 'Issue.count', count+1 do
4498 4514 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '1',
4499 4515 :issue => {
4500 4516 :project_id => ''
4501 4517 }
4502 4518 end
4503 4519 copy = Issue.where(:parent_id => nil).order("id DESC").first
4504 4520 assert_equal count, copy.descendants.count
4505 4521 end
4506 4522
4507 4523 def test_bulk_copy_should_not_copy_selected_subtasks_twice
4508 4524 issue = Issue.generate_with_descendants!
4509 4525 count = issue.descendants.count
4510 4526 @request.session[:user_id] = 2
4511 4527
4512 4528 assert_difference 'Issue.count', count+1 do
4513 4529 post :bulk_update, :ids => issue.self_and_descendants.map(&:id), :copy => '1', :copy_subtasks => '1',
4514 4530 :issue => {
4515 4531 :project_id => ''
4516 4532 }
4517 4533 end
4518 4534 copy = Issue.where(:parent_id => nil).order("id DESC").first
4519 4535 assert_equal count, copy.descendants.count
4520 4536 end
4521 4537
4522 4538 def test_bulk_copy_to_another_project_should_follow_when_needed
4523 4539 @request.session[:user_id] = 2
4524 4540 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
4525 4541 issue = Issue.order('id DESC').first
4526 4542 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
4527 4543 end
4528 4544
4529 4545 def test_bulk_copy_with_all_failures_should_display_errors
4530 4546 @request.session[:user_id] = 2
4531 4547 post :bulk_update, :ids => [1, 2], :copy => '1', :issue => {:start_date => 'foo'}
4532 4548
4533 4549 assert_response :success
4534 4550 end
4535 4551
4536 4552 def test_destroy_issue_with_no_time_entries
4537 4553 assert_nil TimeEntry.find_by_issue_id(2)
4538 4554 @request.session[:user_id] = 2
4539 4555
4540 4556 assert_difference 'Issue.count', -1 do
4541 4557 delete :destroy, :id => 2
4542 4558 end
4543 4559 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4544 4560 assert_nil Issue.find_by_id(2)
4545 4561 end
4546 4562
4547 4563 def test_destroy_issues_with_time_entries
4548 4564 @request.session[:user_id] = 2
4549 4565
4550 4566 assert_no_difference 'Issue.count' do
4551 4567 delete :destroy, :ids => [1, 3]
4552 4568 end
4553 4569 assert_response :success
4554 4570 assert_template 'destroy'
4555 4571 assert_not_nil assigns(:hours)
4556 4572 assert Issue.find_by_id(1) && Issue.find_by_id(3)
4557 4573
4558 4574 assert_select 'form' do
4559 4575 assert_select 'input[name=_method][value=delete]'
4560 4576 end
4561 4577 end
4562 4578
4563 4579 def test_destroy_issues_and_destroy_time_entries
4564 4580 @request.session[:user_id] = 2
4565 4581
4566 4582 assert_difference 'Issue.count', -2 do
4567 4583 assert_difference 'TimeEntry.count', -3 do
4568 4584 delete :destroy, :ids => [1, 3], :todo => 'destroy'
4569 4585 end
4570 4586 end
4571 4587 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4572 4588 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4573 4589 assert_nil TimeEntry.find_by_id([1, 2])
4574 4590 end
4575 4591
4576 4592 def test_destroy_issues_and_assign_time_entries_to_project
4577 4593 @request.session[:user_id] = 2
4578 4594
4579 4595 assert_difference 'Issue.count', -2 do
4580 4596 assert_no_difference 'TimeEntry.count' do
4581 4597 delete :destroy, :ids => [1, 3], :todo => 'nullify'
4582 4598 end
4583 4599 end
4584 4600 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4585 4601 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4586 4602 assert_nil TimeEntry.find(1).issue_id
4587 4603 assert_nil TimeEntry.find(2).issue_id
4588 4604 end
4589 4605
4590 4606 def test_destroy_issues_and_reassign_time_entries_to_another_issue
4591 4607 @request.session[:user_id] = 2
4592 4608
4593 4609 assert_difference 'Issue.count', -2 do
4594 4610 assert_no_difference 'TimeEntry.count' do
4595 4611 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
4596 4612 end
4597 4613 end
4598 4614 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4599 4615 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4600 4616 assert_equal 2, TimeEntry.find(1).issue_id
4601 4617 assert_equal 2, TimeEntry.find(2).issue_id
4602 4618 end
4603 4619
4604 4620 def test_destroy_issues_and_reassign_time_entries_to_an_invalid_issue_should_fail
4605 4621 @request.session[:user_id] = 2
4606 4622
4607 4623 assert_no_difference 'Issue.count' do
4608 4624 assert_no_difference 'TimeEntry.count' do
4609 4625 # try to reassign time to an issue of another project
4610 4626 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 4
4611 4627 end
4612 4628 end
4613 4629 assert_response :success
4614 4630 assert_template 'destroy'
4615 4631 end
4616 4632
4617 4633 def test_destroy_issues_from_different_projects
4618 4634 @request.session[:user_id] = 2
4619 4635
4620 4636 assert_difference 'Issue.count', -3 do
4621 4637 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
4622 4638 end
4623 4639 assert_redirected_to :controller => 'issues', :action => 'index'
4624 4640 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
4625 4641 end
4626 4642
4627 4643 def test_destroy_parent_and_child_issues
4628 4644 parent = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Parent Issue')
4629 4645 child = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Child Issue', :parent_issue_id => parent.id)
4630 4646 assert child.is_descendant_of?(parent.reload)
4631 4647
4632 4648 @request.session[:user_id] = 2
4633 4649 assert_difference 'Issue.count', -2 do
4634 4650 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
4635 4651 end
4636 4652 assert_response 302
4637 4653 end
4638 4654
4639 4655 def test_destroy_invalid_should_respond_with_404
4640 4656 @request.session[:user_id] = 2
4641 4657 assert_no_difference 'Issue.count' do
4642 4658 delete :destroy, :id => 999
4643 4659 end
4644 4660 assert_response 404
4645 4661 end
4646 4662
4647 4663 def test_default_search_scope
4648 4664 get :index
4649 4665
4650 4666 assert_select 'div#quick-search form' do
4651 4667 assert_select 'input[name=issues][value="1"][type=hidden]'
4652 4668 end
4653 4669 end
4654 4670
4655 4671 def setup_user_with_copy_but_not_add_permission
4656 4672 Role.all.each {|r| r.remove_permission! :add_issues}
4657 4673 Role.find_by_name('Manager').add_permission! :add_issues
4658 4674 user = User.generate!
4659 4675 User.add_to_project(user, Project.find(1), Role.find_by_name('Developer'))
4660 4676 User.add_to_project(user, Project.find(2), Role.find_by_name('Manager'))
4661 4677 user
4662 4678 end
4663 4679 end
General Comments 0
You need to be logged in to leave comments. Login now