##// END OF EJS Templates
Fixes r2226: exporting an issue with attachments to PDF raises an error (#2492)....
Jean-Philippe Lang -
r2260:31b3ebf071bc
parent child
Show More
@@ -1,459 +1,461
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2009 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 'iconv'
19 19 require 'rfpdf/chinese'
20 20
21 21 module Redmine
22 22 module Export
23 23 module PDF
24 include ActionView::Helpers::NumberHelper
25
24 26 class IFPDF < FPDF
25 27 include GLoc
26 28 attr_accessor :footer_date
27 29
28 30 def initialize(lang)
29 31 super()
30 32 set_language_if_valid lang
31 33 case current_language.to_s
32 34 when 'ja'
33 35 extend(PDF_Japanese)
34 36 AddSJISFont()
35 37 @font_for_content = 'SJIS'
36 38 @font_for_footer = 'SJIS'
37 39 when 'zh'
38 40 extend(PDF_Chinese)
39 41 AddGBFont()
40 42 @font_for_content = 'GB'
41 43 @font_for_footer = 'GB'
42 44 when 'zh-tw'
43 45 extend(PDF_Chinese)
44 46 AddBig5Font()
45 47 @font_for_content = 'Big5'
46 48 @font_for_footer = 'Big5'
47 49 else
48 50 @font_for_content = 'Arial'
49 51 @font_for_footer = 'Helvetica'
50 52 end
51 53 SetCreator(Redmine::Info.app_name)
52 54 SetFont(@font_for_content)
53 55 end
54 56
55 57 def SetFontStyle(style, size)
56 58 SetFont(@font_for_content, style, size)
57 59 end
58 60
59 61 def SetTitle(txt)
60 62 txt = begin
61 63 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
62 64 hextxt = "<FEFF" # FEFF is BOM
63 65 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
64 66 hextxt << ">"
65 67 rescue
66 68 txt
67 69 end || ''
68 70 super(txt)
69 71 end
70 72
71 73 def textstring(s)
72 74 # Format a text string
73 75 if s =~ /^</ # This means the string is hex-dumped.
74 76 return s
75 77 else
76 78 return '('+escape(s)+')'
77 79 end
78 80 end
79 81
80 82 def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
81 83 @ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
82 84 # these quotation marks are not correctly rendered in the pdf
83 85 txt = txt.gsub(/[Ò€œÒ€�]/, '"') if txt
84 86 txt = begin
85 87 # 0x5c char handling
86 88 txtar = txt.split('\\')
87 89 txtar << '' if txt[-1] == ?\\
88 90 txtar.collect {|x| @ic.iconv(x)}.join('\\').gsub(/\\/, "\\\\\\\\")
89 91 rescue
90 92 txt
91 93 end || ''
92 94 super w,h,txt,border,ln,align,fill,link
93 95 end
94 96
95 97 def Footer
96 98 SetFont(@font_for_footer, 'I', 8)
97 99 SetY(-15)
98 100 SetX(15)
99 101 Cell(0, 5, @footer_date, 0, 0, 'L')
100 102 SetY(-15)
101 103 SetX(-30)
102 104 Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
103 105 end
104 106 end
105 107
106 108 # Returns a PDF string of a list of issues
107 109 def issues_to_pdf(issues, project)
108 110 pdf = IFPDF.new(current_language)
109 111 title = project ? "#{project} - #{l(:label_issue_plural)}" : "#{l(:label_issue_plural)}"
110 112 pdf.SetTitle(title)
111 113 pdf.AliasNbPages
112 114 pdf.footer_date = format_date(Date.today)
113 115 pdf.AddPage("L")
114 116 row_height = 7
115 117
116 118 # title
117 119 pdf.SetFontStyle('B',11)
118 120 pdf.Cell(190,10, title)
119 121 pdf.Ln
120 122
121 123 # headers
122 124 pdf.SetFontStyle('B',10)
123 125 pdf.SetFillColor(230, 230, 230)
124 126 pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
125 127 pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
126 128 pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
127 129 pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
128 130 pdf.Cell(40, row_height, l(:field_assigned_to), 0, 0, 'L', 1)
129 131 pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
130 132 pdf.Cell(0, row_height, l(:field_subject), 0, 0, 'L', 1)
131 133 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
132 134 pdf.Ln
133 135 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
134 136 pdf.SetY(pdf.GetY() + 1)
135 137
136 138 # rows
137 139 pdf.SetFontStyle('',9)
138 140 pdf.SetFillColor(255, 255, 255)
139 141 issues.each do |issue|
140 142 pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
141 143 pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
142 144 pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
143 145 pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
144 146 pdf.Cell(40, row_height, issue.assigned_to ? issue.assigned_to.to_s : '', 0, 0, 'L', 1)
145 147 pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
146 148 pdf.MultiCell(0, row_height, (project == issue.project ? issue.subject : "#{issue.project} - #{issue.subject}"))
147 149 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
148 150 pdf.SetY(pdf.GetY() + 1)
149 151 end
150 152 pdf.Output
151 153 end
152 154
153 155 # Returns a PDF string of a single issue
154 156 def issue_to_pdf(issue)
155 157 pdf = IFPDF.new(current_language)
156 158 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
157 159 pdf.AliasNbPages
158 160 pdf.footer_date = format_date(Date.today)
159 161 pdf.AddPage
160 162
161 163 pdf.SetFontStyle('B',11)
162 164 pdf.Cell(190,10, "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}")
163 165 pdf.Ln
164 166
165 167 y0 = pdf.GetY
166 168
167 169 pdf.SetFontStyle('B',9)
168 170 pdf.Cell(35,5, l(:field_status) + ":","LT")
169 171 pdf.SetFontStyle('',9)
170 172 pdf.Cell(60,5, issue.status.to_s,"RT")
171 173 pdf.SetFontStyle('B',9)
172 174 pdf.Cell(35,5, l(:field_priority) + ":","LT")
173 175 pdf.SetFontStyle('',9)
174 176 pdf.Cell(60,5, issue.priority.to_s,"RT")
175 177 pdf.Ln
176 178
177 179 pdf.SetFontStyle('B',9)
178 180 pdf.Cell(35,5, l(:field_author) + ":","L")
179 181 pdf.SetFontStyle('',9)
180 182 pdf.Cell(60,5, issue.author.to_s,"R")
181 183 pdf.SetFontStyle('B',9)
182 184 pdf.Cell(35,5, l(:field_category) + ":","L")
183 185 pdf.SetFontStyle('',9)
184 186 pdf.Cell(60,5, issue.category.to_s,"R")
185 187 pdf.Ln
186 188
187 189 pdf.SetFontStyle('B',9)
188 190 pdf.Cell(35,5, l(:field_created_on) + ":","L")
189 191 pdf.SetFontStyle('',9)
190 192 pdf.Cell(60,5, format_date(issue.created_on),"R")
191 193 pdf.SetFontStyle('B',9)
192 194 pdf.Cell(35,5, l(:field_assigned_to) + ":","L")
193 195 pdf.SetFontStyle('',9)
194 196 pdf.Cell(60,5, issue.assigned_to.to_s,"R")
195 197 pdf.Ln
196 198
197 199 pdf.SetFontStyle('B',9)
198 200 pdf.Cell(35,5, l(:field_updated_on) + ":","LB")
199 201 pdf.SetFontStyle('',9)
200 202 pdf.Cell(60,5, format_date(issue.updated_on),"RB")
201 203 pdf.SetFontStyle('B',9)
202 204 pdf.Cell(35,5, l(:field_due_date) + ":","LB")
203 205 pdf.SetFontStyle('',9)
204 206 pdf.Cell(60,5, format_date(issue.due_date),"RB")
205 207 pdf.Ln
206 208
207 209 for custom_value in issue.custom_values
208 210 pdf.SetFontStyle('B',9)
209 211 pdf.Cell(35,5, custom_value.custom_field.name + ":","L")
210 212 pdf.SetFontStyle('',9)
211 213 pdf.MultiCell(155,5, (show_value custom_value),"R")
212 214 end
213 215
214 216 pdf.SetFontStyle('B',9)
215 217 pdf.Cell(35,5, l(:field_subject) + ":","LTB")
216 218 pdf.SetFontStyle('',9)
217 219 pdf.Cell(155,5, issue.subject,"RTB")
218 220 pdf.Ln
219 221
220 222 pdf.SetFontStyle('B',9)
221 223 pdf.Cell(35,5, l(:field_description) + ":")
222 224 pdf.SetFontStyle('',9)
223 225 pdf.MultiCell(155,5, @issue.description,"BR")
224 226
225 227 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
226 228 pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY)
227 229 pdf.Ln
228 230
229 231 if issue.changesets.any? && User.current.allowed_to?(:view_changesets, issue.project)
230 232 pdf.SetFontStyle('B',9)
231 233 pdf.Cell(190,5, l(:label_associated_revisions), "B")
232 234 pdf.Ln
233 235 for changeset in issue.changesets
234 236 pdf.SetFontStyle('B',8)
235 237 pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.author.to_s)
236 238 pdf.Ln
237 239 unless changeset.comments.blank?
238 240 pdf.SetFontStyle('',8)
239 241 pdf.MultiCell(190,5, changeset.comments)
240 242 end
241 243 pdf.Ln
242 244 end
243 245 end
244 246
245 247 pdf.SetFontStyle('B',9)
246 248 pdf.Cell(190,5, l(:label_history), "B")
247 249 pdf.Ln
248 250 for journal in issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
249 251 pdf.SetFontStyle('B',8)
250 252 pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name)
251 253 pdf.Ln
252 254 pdf.SetFontStyle('I',8)
253 255 for detail in journal.details
254 256 pdf.Cell(190,5, "- " + show_detail(detail, true))
255 257 pdf.Ln
256 258 end
257 259 if journal.notes?
258 260 pdf.SetFontStyle('',8)
259 261 pdf.MultiCell(190,5, journal.notes)
260 262 end
261 263 pdf.Ln
262 264 end
263 265
264 266 if issue.attachments.any?
265 267 pdf.SetFontStyle('B',9)
266 268 pdf.Cell(190,5, l(:label_attachment_plural), "B")
267 269 pdf.Ln
268 270 for attachment in issue.attachments
269 271 pdf.SetFontStyle('',8)
270 272 pdf.Cell(80,5, attachment.filename)
271 273 pdf.Cell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
272 274 pdf.Cell(25,5, format_date(attachment.created_on),0,0,"R")
273 275 pdf.Cell(65,5, attachment.author.name,0,0,"R")
274 276 pdf.Ln
275 277 end
276 278 end
277 279 pdf.Output
278 280 end
279 281
280 282 # Returns a PDF string of a gantt chart
281 283 def gantt_to_pdf(gantt, project)
282 284 pdf = IFPDF.new(current_language)
283 285 pdf.SetTitle("#{l(:label_gantt)} #{project}")
284 286 pdf.AliasNbPages
285 287 pdf.footer_date = format_date(Date.today)
286 288 pdf.AddPage("L")
287 289 pdf.SetFontStyle('B',12)
288 290 pdf.SetX(15)
289 291 pdf.Cell(70, 20, project.to_s)
290 292 pdf.Ln
291 293 pdf.SetFontStyle('B',9)
292 294
293 295 subject_width = 70
294 296 header_heigth = 5
295 297
296 298 headers_heigth = header_heigth
297 299 show_weeks = false
298 300 show_days = false
299 301
300 302 if gantt.months < 7
301 303 show_weeks = true
302 304 headers_heigth = 2*header_heigth
303 305 if gantt.months < 3
304 306 show_days = true
305 307 headers_heigth = 3*header_heigth
306 308 end
307 309 end
308 310
309 311 g_width = 210
310 312 zoom = (g_width) / (gantt.date_to - gantt.date_from + 1)
311 313 g_height = 120
312 314 t_height = g_height + headers_heigth
313 315
314 316 y_start = pdf.GetY
315 317
316 318 # Months headers
317 319 month_f = gantt.date_from
318 320 left = subject_width
319 321 height = header_heigth
320 322 gantt.months.times do
321 323 width = ((month_f >> 1) - month_f) * zoom
322 324 pdf.SetY(y_start)
323 325 pdf.SetX(left)
324 326 pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
325 327 left = left + width
326 328 month_f = month_f >> 1
327 329 end
328 330
329 331 # Weeks headers
330 332 if show_weeks
331 333 left = subject_width
332 334 height = header_heigth
333 335 if gantt.date_from.cwday == 1
334 336 # gantt.date_from is monday
335 337 week_f = gantt.date_from
336 338 else
337 339 # find next monday after gantt.date_from
338 340 week_f = gantt.date_from + (7 - gantt.date_from.cwday + 1)
339 341 width = (7 - gantt.date_from.cwday + 1) * zoom-1
340 342 pdf.SetY(y_start + header_heigth)
341 343 pdf.SetX(left)
342 344 pdf.Cell(width + 1, height, "", "LTR")
343 345 left = left + width+1
344 346 end
345 347 while week_f <= gantt.date_to
346 348 width = (week_f + 6 <= gantt.date_to) ? 7 * zoom : (gantt.date_to - week_f + 1) * zoom
347 349 pdf.SetY(y_start + header_heigth)
348 350 pdf.SetX(left)
349 351 pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
350 352 left = left + width
351 353 week_f = week_f+7
352 354 end
353 355 end
354 356
355 357 # Days headers
356 358 if show_days
357 359 left = subject_width
358 360 height = header_heigth
359 361 wday = gantt.date_from.cwday
360 362 pdf.SetFontStyle('B',7)
361 363 (gantt.date_to - gantt.date_from + 1).to_i.times do
362 364 width = zoom
363 365 pdf.SetY(y_start + 2 * header_heigth)
364 366 pdf.SetX(left)
365 367 pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C")
366 368 left = left + width
367 369 wday = wday + 1
368 370 wday = 1 if wday > 7
369 371 end
370 372 end
371 373
372 374 pdf.SetY(y_start)
373 375 pdf.SetX(15)
374 376 pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
375 377
376 378 # Tasks
377 379 top = headers_heigth + y_start
378 380 pdf.SetFontStyle('B',7)
379 381 gantt.events.each do |i|
380 382 pdf.SetY(top)
381 383 pdf.SetX(15)
382 384
383 385 if i.is_a? Issue
384 386 pdf.Cell(subject_width-15, 5, "#{i.tracker} #{i.id}: #{i.subject}".sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)'), "LR")
385 387 else
386 388 pdf.Cell(subject_width-15, 5, "#{l(:label_version)}: #{i.name}", "LR")
387 389 end
388 390
389 391 pdf.SetY(top)
390 392 pdf.SetX(subject_width)
391 393 pdf.Cell(g_width, 5, "", "LR")
392 394
393 395 pdf.SetY(top+1.5)
394 396
395 397 if i.is_a? Issue
396 398 i_start_date = (i.start_date >= gantt.date_from ? i.start_date : gantt.date_from )
397 399 i_end_date = (i.due_before <= gantt.date_to ? i.due_before : gantt.date_to )
398 400
399 401 i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor
400 402 i_done_date = (i_done_date <= gantt.date_from ? gantt.date_from : i_done_date )
401 403 i_done_date = (i_done_date >= gantt.date_to ? gantt.date_to : i_done_date )
402 404
403 405 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
404 406
405 407 i_left = ((i_start_date - gantt.date_from)*zoom)
406 408 i_width = ((i_end_date - i_start_date + 1)*zoom)
407 409 d_width = ((i_done_date - i_start_date)*zoom)
408 410 l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date
409 411 l_width ||= 0
410 412
411 413 pdf.SetX(subject_width + i_left)
412 414 pdf.SetFillColor(200,200,200)
413 415 pdf.Cell(i_width, 2, "", 0, 0, "", 1)
414 416
415 417 if l_width > 0
416 418 pdf.SetY(top+1.5)
417 419 pdf.SetX(subject_width + i_left)
418 420 pdf.SetFillColor(255,100,100)
419 421 pdf.Cell(l_width, 2, "", 0, 0, "", 1)
420 422 end
421 423 if d_width > 0
422 424 pdf.SetY(top+1.5)
423 425 pdf.SetX(subject_width + i_left)
424 426 pdf.SetFillColor(100,100,255)
425 427 pdf.Cell(d_width, 2, "", 0, 0, "", 1)
426 428 end
427 429
428 430 pdf.SetY(top+1.5)
429 431 pdf.SetX(subject_width + i_left + i_width)
430 432 pdf.Cell(30, 2, "#{i.status} #{i.done_ratio}%")
431 433 else
432 434 i_left = ((i.start_date - gantt.date_from)*zoom)
433 435
434 436 pdf.SetX(subject_width + i_left)
435 437 pdf.SetFillColor(50,200,50)
436 438 pdf.Cell(2, 2, "", 0, 0, "", 1)
437 439
438 440 pdf.SetY(top+1.5)
439 441 pdf.SetX(subject_width + i_left + 3)
440 442 pdf.Cell(30, 2, "#{i.name}")
441 443 end
442 444
443 445 top = top + 5
444 446 pdf.SetDrawColor(200, 200, 200)
445 447 pdf.Line(15, top, subject_width+g_width, top)
446 448 if pdf.GetY() > 180
447 449 pdf.AddPage("L")
448 450 top = 20
449 451 pdf.Line(15, top, subject_width+g_width, top)
450 452 end
451 453 pdf.SetDrawColor(0, 0, 0)
452 454 end
453 455
454 456 pdf.Line(15, top, subject_width+g_width, top)
455 457 pdf.Output
456 458 end
457 459 end
458 460 end
459 461 end
@@ -1,777 +1,777
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2008 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.dirname(__FILE__) + '/../test_helper'
19 19 require 'issues_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class IssuesController; def rescue_action(e) raise e end; end
23 23
24 24 class IssuesControllerTest < Test::Unit::TestCase
25 25 fixtures :projects,
26 26 :users,
27 27 :roles,
28 28 :members,
29 29 :issues,
30 30 :issue_statuses,
31 31 :versions,
32 32 :trackers,
33 33 :projects_trackers,
34 34 :issue_categories,
35 35 :enabled_modules,
36 36 :enumerations,
37 37 :attachments,
38 38 :workflows,
39 39 :custom_fields,
40 40 :custom_values,
41 41 :custom_fields_trackers,
42 42 :time_entries,
43 43 :journals,
44 44 :journal_details
45 45
46 46 def setup
47 47 @controller = IssuesController.new
48 48 @request = ActionController::TestRequest.new
49 49 @response = ActionController::TestResponse.new
50 50 User.current = nil
51 51 end
52 52
53 53 def test_index
54 54 get :index
55 55 assert_response :success
56 56 assert_template 'index.rhtml'
57 57 assert_not_nil assigns(:issues)
58 58 assert_nil assigns(:project)
59 59 assert_tag :tag => 'a', :content => /Can't print recipes/
60 60 assert_tag :tag => 'a', :content => /Subproject issue/
61 61 # private projects hidden
62 62 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
63 63 assert_no_tag :tag => 'a', :content => /Issue on project 2/
64 64 end
65 65
66 66 def test_index_should_not_list_issues_when_module_disabled
67 67 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
68 68 get :index
69 69 assert_response :success
70 70 assert_template 'index.rhtml'
71 71 assert_not_nil assigns(:issues)
72 72 assert_nil assigns(:project)
73 73 assert_no_tag :tag => 'a', :content => /Can't print recipes/
74 74 assert_tag :tag => 'a', :content => /Subproject issue/
75 75 end
76 76
77 77 def test_index_with_project
78 78 Setting.display_subprojects_issues = 0
79 79 get :index, :project_id => 1
80 80 assert_response :success
81 81 assert_template 'index.rhtml'
82 82 assert_not_nil assigns(:issues)
83 83 assert_tag :tag => 'a', :content => /Can't print recipes/
84 84 assert_no_tag :tag => 'a', :content => /Subproject issue/
85 85 end
86 86
87 87 def test_index_with_project_and_subprojects
88 88 Setting.display_subprojects_issues = 1
89 89 get :index, :project_id => 1
90 90 assert_response :success
91 91 assert_template 'index.rhtml'
92 92 assert_not_nil assigns(:issues)
93 93 assert_tag :tag => 'a', :content => /Can't print recipes/
94 94 assert_tag :tag => 'a', :content => /Subproject issue/
95 95 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
96 96 end
97 97
98 98 def test_index_with_project_and_subprojects_should_show_private_subprojects
99 99 @request.session[:user_id] = 2
100 100 Setting.display_subprojects_issues = 1
101 101 get :index, :project_id => 1
102 102 assert_response :success
103 103 assert_template 'index.rhtml'
104 104 assert_not_nil assigns(:issues)
105 105 assert_tag :tag => 'a', :content => /Can't print recipes/
106 106 assert_tag :tag => 'a', :content => /Subproject issue/
107 107 assert_tag :tag => 'a', :content => /Issue of a private subproject/
108 108 end
109 109
110 110 def test_index_with_project_and_filter
111 111 get :index, :project_id => 1, :set_filter => 1
112 112 assert_response :success
113 113 assert_template 'index.rhtml'
114 114 assert_not_nil assigns(:issues)
115 115 end
116 116
117 117 def test_index_csv_with_project
118 118 get :index, :format => 'csv'
119 119 assert_response :success
120 120 assert_not_nil assigns(:issues)
121 121 assert_equal 'text/csv', @response.content_type
122 122
123 123 get :index, :project_id => 1, :format => 'csv'
124 124 assert_response :success
125 125 assert_not_nil assigns(:issues)
126 126 assert_equal 'text/csv', @response.content_type
127 127 end
128 128
129 129 def test_index_pdf
130 130 get :index, :format => 'pdf'
131 131 assert_response :success
132 132 assert_not_nil assigns(:issues)
133 133 assert_equal 'application/pdf', @response.content_type
134 134
135 135 get :index, :project_id => 1, :format => 'pdf'
136 136 assert_response :success
137 137 assert_not_nil assigns(:issues)
138 138 assert_equal 'application/pdf', @response.content_type
139 139 end
140 140
141 141 def test_index_sort
142 142 get :index, :sort_key => 'tracker'
143 143 assert_response :success
144 144
145 145 sort_params = @request.session['issuesindex_sort']
146 146 assert sort_params.is_a?(Hash)
147 147 assert_equal 'tracker', sort_params[:key]
148 148 assert_equal 'ASC', sort_params[:order]
149 149 end
150 150
151 151 def test_gantt
152 152 get :gantt, :project_id => 1
153 153 assert_response :success
154 154 assert_template 'gantt.rhtml'
155 155 assert_not_nil assigns(:gantt)
156 156 events = assigns(:gantt).events
157 157 assert_not_nil events
158 158 # Issue with start and due dates
159 159 i = Issue.find(1)
160 160 assert_not_nil i.due_date
161 161 assert events.include?(Issue.find(1))
162 162 # Issue with without due date but targeted to a version with date
163 163 i = Issue.find(2)
164 164 assert_nil i.due_date
165 165 assert events.include?(i)
166 166 end
167 167
168 168 def test_cross_project_gantt
169 169 get :gantt
170 170 assert_response :success
171 171 assert_template 'gantt.rhtml'
172 172 assert_not_nil assigns(:gantt)
173 173 events = assigns(:gantt).events
174 174 assert_not_nil events
175 175 end
176 176
177 177 def test_gantt_export_to_pdf
178 178 get :gantt, :project_id => 1, :format => 'pdf'
179 179 assert_response :success
180 180 assert_equal 'application/pdf', @response.content_type
181 181 assert @response.body.starts_with?('%PDF')
182 182 assert_not_nil assigns(:gantt)
183 183 end
184 184
185 185 def test_cross_project_gantt_export_to_pdf
186 186 get :gantt, :format => 'pdf'
187 187 assert_response :success
188 188 assert_equal 'application/pdf', @response.content_type
189 189 assert @response.body.starts_with?('%PDF')
190 190 assert_not_nil assigns(:gantt)
191 191 end
192 192
193 193 if Object.const_defined?(:Magick)
194 194 def test_gantt_image
195 195 get :gantt, :project_id => 1, :format => 'png'
196 196 assert_response :success
197 197 assert_equal 'image/png', @response.content_type
198 198 end
199 199 else
200 200 puts "RMagick not installed. Skipping tests !!!"
201 201 end
202 202
203 203 def test_calendar
204 204 get :calendar, :project_id => 1
205 205 assert_response :success
206 206 assert_template 'calendar'
207 207 assert_not_nil assigns(:calendar)
208 208 end
209 209
210 210 def test_cross_project_calendar
211 211 get :calendar
212 212 assert_response :success
213 213 assert_template 'calendar'
214 214 assert_not_nil assigns(:calendar)
215 215 end
216 216
217 217 def test_changes
218 218 get :changes, :project_id => 1
219 219 assert_response :success
220 220 assert_not_nil assigns(:journals)
221 221 assert_equal 'application/atom+xml', @response.content_type
222 222 end
223 223
224 224 def test_show_by_anonymous
225 225 get :show, :id => 1
226 226 assert_response :success
227 227 assert_template 'show.rhtml'
228 228 assert_not_nil assigns(:issue)
229 229 assert_equal Issue.find(1), assigns(:issue)
230 230
231 231 # anonymous role is allowed to add a note
232 232 assert_tag :tag => 'form',
233 233 :descendant => { :tag => 'fieldset',
234 234 :child => { :tag => 'legend',
235 235 :content => /Notes/ } }
236 236 end
237 237
238 238 def test_show_by_manager
239 239 @request.session[:user_id] = 2
240 240 get :show, :id => 1
241 241 assert_response :success
242 242
243 243 assert_tag :tag => 'form',
244 244 :descendant => { :tag => 'fieldset',
245 245 :child => { :tag => 'legend',
246 246 :content => /Change properties/ } },
247 247 :descendant => { :tag => 'fieldset',
248 248 :child => { :tag => 'legend',
249 249 :content => /Log time/ } },
250 250 :descendant => { :tag => 'fieldset',
251 251 :child => { :tag => 'legend',
252 252 :content => /Notes/ } }
253 253 end
254 254
255 255 def test_show_export_to_pdf
256 get :show, :id => 1, :format => 'pdf'
256 get :show, :id => 3, :format => 'pdf'
257 257 assert_response :success
258 258 assert_equal 'application/pdf', @response.content_type
259 259 assert @response.body.starts_with?('%PDF')
260 260 assert_not_nil assigns(:issue)
261 261 end
262 262
263 263 def test_get_new
264 264 @request.session[:user_id] = 2
265 265 get :new, :project_id => 1, :tracker_id => 1
266 266 assert_response :success
267 267 assert_template 'new'
268 268
269 269 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
270 270 :value => 'Default string' }
271 271 end
272 272
273 273 def test_get_new_without_tracker_id
274 274 @request.session[:user_id] = 2
275 275 get :new, :project_id => 1
276 276 assert_response :success
277 277 assert_template 'new'
278 278
279 279 issue = assigns(:issue)
280 280 assert_not_nil issue
281 281 assert_equal Project.find(1).trackers.first, issue.tracker
282 282 end
283 283
284 284 def test_update_new_form
285 285 @request.session[:user_id] = 2
286 286 xhr :post, :new, :project_id => 1,
287 287 :issue => {:tracker_id => 2,
288 288 :subject => 'This is the test_new issue',
289 289 :description => 'This is the description',
290 290 :priority_id => 5}
291 291 assert_response :success
292 292 assert_template 'new'
293 293 end
294 294
295 295 def test_post_new
296 296 @request.session[:user_id] = 2
297 297 post :new, :project_id => 1,
298 298 :issue => {:tracker_id => 3,
299 299 :subject => 'This is the test_new issue',
300 300 :description => 'This is the description',
301 301 :priority_id => 5,
302 302 :estimated_hours => '',
303 303 :custom_field_values => {'2' => 'Value for field 2'}}
304 304 assert_redirected_to :controller => 'issues', :action => 'show'
305 305
306 306 issue = Issue.find_by_subject('This is the test_new issue')
307 307 assert_not_nil issue
308 308 assert_equal 2, issue.author_id
309 309 assert_equal 3, issue.tracker_id
310 310 assert_nil issue.estimated_hours
311 311 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
312 312 assert_not_nil v
313 313 assert_equal 'Value for field 2', v.value
314 314 end
315 315
316 316 def test_post_new_without_custom_fields_param
317 317 @request.session[:user_id] = 2
318 318 post :new, :project_id => 1,
319 319 :issue => {:tracker_id => 1,
320 320 :subject => 'This is the test_new issue',
321 321 :description => 'This is the description',
322 322 :priority_id => 5}
323 323 assert_redirected_to :controller => 'issues', :action => 'show'
324 324 end
325 325
326 326 def test_post_new_with_required_custom_field_and_without_custom_fields_param
327 327 field = IssueCustomField.find_by_name('Database')
328 328 field.update_attribute(:is_required, true)
329 329
330 330 @request.session[:user_id] = 2
331 331 post :new, :project_id => 1,
332 332 :issue => {:tracker_id => 1,
333 333 :subject => 'This is the test_new issue',
334 334 :description => 'This is the description',
335 335 :priority_id => 5}
336 336 assert_response :success
337 337 assert_template 'new'
338 338 issue = assigns(:issue)
339 339 assert_not_nil issue
340 340 assert_equal 'activerecord_error_invalid', issue.errors.on(:custom_values)
341 341 end
342 342
343 343 def test_post_new_with_watchers
344 344 @request.session[:user_id] = 2
345 345 ActionMailer::Base.deliveries.clear
346 346
347 347 assert_difference 'Watcher.count', 2 do
348 348 post :new, :project_id => 1,
349 349 :issue => {:tracker_id => 1,
350 350 :subject => 'This is a new issue with watchers',
351 351 :description => 'This is the description',
352 352 :priority_id => 5,
353 353 :watcher_user_ids => ['2', '3']}
354 354 end
355 355 issue = Issue.find_by_subject('This is a new issue with watchers')
356 356 assert_not_nil issue
357 357 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
358 358
359 359 # Watchers added
360 360 assert_equal [2, 3], issue.watcher_user_ids.sort
361 361 assert issue.watched_by?(User.find(3))
362 362 # Watchers notified
363 363 mail = ActionMailer::Base.deliveries.last
364 364 assert_kind_of TMail::Mail, mail
365 365 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
366 366 end
367 367
368 368 def test_post_should_preserve_fields_values_on_validation_failure
369 369 @request.session[:user_id] = 2
370 370 post :new, :project_id => 1,
371 371 :issue => {:tracker_id => 1,
372 372 # empty subject
373 373 :subject => '',
374 374 :description => 'This is a description',
375 375 :priority_id => 6,
376 376 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
377 377 assert_response :success
378 378 assert_template 'new'
379 379
380 380 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
381 381 :content => 'This is a description'
382 382 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
383 383 :child => { :tag => 'option', :attributes => { :selected => 'selected',
384 384 :value => '6' },
385 385 :content => 'High' }
386 386 # Custom fields
387 387 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
388 388 :child => { :tag => 'option', :attributes => { :selected => 'selected',
389 389 :value => 'Oracle' },
390 390 :content => 'Oracle' }
391 391 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
392 392 :value => 'Value for field 2'}
393 393 end
394 394
395 395 def test_copy_issue
396 396 @request.session[:user_id] = 2
397 397 get :new, :project_id => 1, :copy_from => 1
398 398 assert_template 'new'
399 399 assert_not_nil assigns(:issue)
400 400 orig = Issue.find(1)
401 401 assert_equal orig.subject, assigns(:issue).subject
402 402 end
403 403
404 404 def test_get_edit
405 405 @request.session[:user_id] = 2
406 406 get :edit, :id => 1
407 407 assert_response :success
408 408 assert_template 'edit'
409 409 assert_not_nil assigns(:issue)
410 410 assert_equal Issue.find(1), assigns(:issue)
411 411 end
412 412
413 413 def test_get_edit_with_params
414 414 @request.session[:user_id] = 2
415 415 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
416 416 assert_response :success
417 417 assert_template 'edit'
418 418
419 419 issue = assigns(:issue)
420 420 assert_not_nil issue
421 421
422 422 assert_equal 5, issue.status_id
423 423 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
424 424 :child => { :tag => 'option',
425 425 :content => 'Closed',
426 426 :attributes => { :selected => 'selected' } }
427 427
428 428 assert_equal 7, issue.priority_id
429 429 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
430 430 :child => { :tag => 'option',
431 431 :content => 'Urgent',
432 432 :attributes => { :selected => 'selected' } }
433 433 end
434 434
435 435 def test_reply_to_issue
436 436 @request.session[:user_id] = 2
437 437 get :reply, :id => 1
438 438 assert_response :success
439 439 assert_select_rjs :show, "update"
440 440 end
441 441
442 442 def test_reply_to_note
443 443 @request.session[:user_id] = 2
444 444 get :reply, :id => 1, :journal_id => 2
445 445 assert_response :success
446 446 assert_select_rjs :show, "update"
447 447 end
448 448
449 449 def test_post_edit_without_custom_fields_param
450 450 @request.session[:user_id] = 2
451 451 ActionMailer::Base.deliveries.clear
452 452
453 453 issue = Issue.find(1)
454 454 assert_equal '125', issue.custom_value_for(2).value
455 455 old_subject = issue.subject
456 456 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
457 457
458 458 assert_difference('Journal.count') do
459 459 assert_difference('JournalDetail.count', 2) do
460 460 post :edit, :id => 1, :issue => {:subject => new_subject,
461 461 :priority_id => '6',
462 462 :category_id => '1' # no change
463 463 }
464 464 end
465 465 end
466 466 assert_redirected_to 'issues/show/1'
467 467 issue.reload
468 468 assert_equal new_subject, issue.subject
469 469 # Make sure custom fields were not cleared
470 470 assert_equal '125', issue.custom_value_for(2).value
471 471
472 472 mail = ActionMailer::Base.deliveries.last
473 473 assert_kind_of TMail::Mail, mail
474 474 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
475 475 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
476 476 end
477 477
478 478 def test_post_edit_with_custom_field_change
479 479 @request.session[:user_id] = 2
480 480 issue = Issue.find(1)
481 481 assert_equal '125', issue.custom_value_for(2).value
482 482
483 483 assert_difference('Journal.count') do
484 484 assert_difference('JournalDetail.count', 3) do
485 485 post :edit, :id => 1, :issue => {:subject => 'Custom field change',
486 486 :priority_id => '6',
487 487 :category_id => '1', # no change
488 488 :custom_field_values => { '2' => 'New custom value' }
489 489 }
490 490 end
491 491 end
492 492 assert_redirected_to 'issues/show/1'
493 493 issue.reload
494 494 assert_equal 'New custom value', issue.custom_value_for(2).value
495 495
496 496 mail = ActionMailer::Base.deliveries.last
497 497 assert_kind_of TMail::Mail, mail
498 498 assert mail.body.include?("Searchable field changed from 125 to New custom value")
499 499 end
500 500
501 501 def test_post_edit_with_status_and_assignee_change
502 502 issue = Issue.find(1)
503 503 assert_equal 1, issue.status_id
504 504 @request.session[:user_id] = 2
505 505 assert_difference('TimeEntry.count', 0) do
506 506 post :edit,
507 507 :id => 1,
508 508 :issue => { :status_id => 2, :assigned_to_id => 3 },
509 509 :notes => 'Assigned to dlopper',
510 510 :time_entry => { :hours => '', :comments => '', :activity_id => Enumeration.get_values('ACTI').first }
511 511 end
512 512 assert_redirected_to 'issues/show/1'
513 513 issue.reload
514 514 assert_equal 2, issue.status_id
515 515 j = issue.journals.find(:first, :order => 'id DESC')
516 516 assert_equal 'Assigned to dlopper', j.notes
517 517 assert_equal 2, j.details.size
518 518
519 519 mail = ActionMailer::Base.deliveries.last
520 520 assert mail.body.include?("Status changed from New to Assigned")
521 521 end
522 522
523 523 def test_post_edit_with_note_only
524 524 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
525 525 # anonymous user
526 526 post :edit,
527 527 :id => 1,
528 528 :notes => notes
529 529 assert_redirected_to 'issues/show/1'
530 530 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
531 531 assert_equal notes, j.notes
532 532 assert_equal 0, j.details.size
533 533 assert_equal User.anonymous, j.user
534 534
535 535 mail = ActionMailer::Base.deliveries.last
536 536 assert mail.body.include?(notes)
537 537 end
538 538
539 539 def test_post_edit_with_note_and_spent_time
540 540 @request.session[:user_id] = 2
541 541 spent_hours_before = Issue.find(1).spent_hours
542 542 assert_difference('TimeEntry.count') do
543 543 post :edit,
544 544 :id => 1,
545 545 :notes => '2.5 hours added',
546 546 :time_entry => { :hours => '2.5', :comments => '', :activity_id => Enumeration.get_values('ACTI').first }
547 547 end
548 548 assert_redirected_to 'issues/show/1'
549 549
550 550 issue = Issue.find(1)
551 551
552 552 j = issue.journals.find(:first, :order => 'id DESC')
553 553 assert_equal '2.5 hours added', j.notes
554 554 assert_equal 0, j.details.size
555 555
556 556 t = issue.time_entries.find(:first, :order => 'id DESC')
557 557 assert_not_nil t
558 558 assert_equal 2.5, t.hours
559 559 assert_equal spent_hours_before + 2.5, issue.spent_hours
560 560 end
561 561
562 562 def test_post_edit_with_attachment_only
563 563 set_tmp_attachments_directory
564 564
565 565 # Delete all fixtured journals, a race condition can occur causing the wrong
566 566 # journal to get fetched in the next find.
567 567 Journal.delete_all
568 568
569 569 # anonymous user
570 570 post :edit,
571 571 :id => 1,
572 572 :notes => '',
573 573 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
574 574 assert_redirected_to 'issues/show/1'
575 575 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
576 576 assert j.notes.blank?
577 577 assert_equal 1, j.details.size
578 578 assert_equal 'testfile.txt', j.details.first.value
579 579 assert_equal User.anonymous, j.user
580 580
581 581 mail = ActionMailer::Base.deliveries.last
582 582 assert mail.body.include?('testfile.txt')
583 583 end
584 584
585 585 def test_post_edit_with_no_change
586 586 issue = Issue.find(1)
587 587 issue.journals.clear
588 588 ActionMailer::Base.deliveries.clear
589 589
590 590 post :edit,
591 591 :id => 1,
592 592 :notes => ''
593 593 assert_redirected_to 'issues/show/1'
594 594
595 595 issue.reload
596 596 assert issue.journals.empty?
597 597 # No email should be sent
598 598 assert ActionMailer::Base.deliveries.empty?
599 599 end
600 600
601 601 def test_post_edit_with_invalid_spent_time
602 602 @request.session[:user_id] = 2
603 603 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
604 604
605 605 assert_no_difference('Journal.count') do
606 606 post :edit,
607 607 :id => 1,
608 608 :notes => notes,
609 609 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
610 610 end
611 611 assert_response :success
612 612 assert_template 'edit'
613 613
614 614 assert_tag :textarea, :attributes => { :name => 'notes' },
615 615 :content => notes
616 616 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
617 617 end
618 618
619 619 def test_bulk_edit
620 620 @request.session[:user_id] = 2
621 621 # update issues priority
622 622 post :bulk_edit, :ids => [1, 2], :priority_id => 7, :notes => 'Bulk editing', :assigned_to_id => ''
623 623 assert_response 302
624 624 # check that the issues were updated
625 625 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
626 626 assert_equal 'Bulk editing', Issue.find(1).journals.find(:first, :order => 'created_on DESC').notes
627 627 end
628 628
629 629 def test_bulk_unassign
630 630 assert_not_nil Issue.find(2).assigned_to
631 631 @request.session[:user_id] = 2
632 632 # unassign issues
633 633 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
634 634 assert_response 302
635 635 # check that the issues were updated
636 636 assert_nil Issue.find(2).assigned_to
637 637 end
638 638
639 639 def test_move_one_issue_to_another_project
640 640 @request.session[:user_id] = 1
641 641 post :move, :id => 1, :new_project_id => 2
642 642 assert_redirected_to 'projects/ecookbook/issues'
643 643 assert_equal 2, Issue.find(1).project_id
644 644 end
645 645
646 646 def test_bulk_move_to_another_project
647 647 @request.session[:user_id] = 1
648 648 post :move, :ids => [1, 2], :new_project_id => 2
649 649 assert_redirected_to 'projects/ecookbook/issues'
650 650 # Issues moved to project 2
651 651 assert_equal 2, Issue.find(1).project_id
652 652 assert_equal 2, Issue.find(2).project_id
653 653 # No tracker change
654 654 assert_equal 1, Issue.find(1).tracker_id
655 655 assert_equal 2, Issue.find(2).tracker_id
656 656 end
657 657
658 658 def test_bulk_move_to_another_tracker
659 659 @request.session[:user_id] = 1
660 660 post :move, :ids => [1, 2], :new_tracker_id => 2
661 661 assert_redirected_to 'projects/ecookbook/issues'
662 662 assert_equal 2, Issue.find(1).tracker_id
663 663 assert_equal 2, Issue.find(2).tracker_id
664 664 end
665 665
666 666 def test_context_menu_one_issue
667 667 @request.session[:user_id] = 2
668 668 get :context_menu, :ids => [1]
669 669 assert_response :success
670 670 assert_template 'context_menu'
671 671 assert_tag :tag => 'a', :content => 'Edit',
672 672 :attributes => { :href => '/issues/edit/1',
673 673 :class => 'icon-edit' }
674 674 assert_tag :tag => 'a', :content => 'Closed',
675 675 :attributes => { :href => '/issues/edit/1?issue%5Bstatus_id%5D=5',
676 676 :class => '' }
677 677 assert_tag :tag => 'a', :content => 'Immediate',
678 678 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;priority_id=8',
679 679 :class => '' }
680 680 assert_tag :tag => 'a', :content => 'Dave Lopper',
681 681 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1',
682 682 :class => '' }
683 683 assert_tag :tag => 'a', :content => 'Copy',
684 684 :attributes => { :href => '/projects/ecookbook/issues/new?copy_from=1',
685 685 :class => 'icon-copy' }
686 686 assert_tag :tag => 'a', :content => 'Move',
687 687 :attributes => { :href => '/issues/move?ids%5B%5D=1',
688 688 :class => 'icon-move' }
689 689 assert_tag :tag => 'a', :content => 'Delete',
690 690 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
691 691 :class => 'icon-del' }
692 692 end
693 693
694 694 def test_context_menu_one_issue_by_anonymous
695 695 get :context_menu, :ids => [1]
696 696 assert_response :success
697 697 assert_template 'context_menu'
698 698 assert_tag :tag => 'a', :content => 'Delete',
699 699 :attributes => { :href => '#',
700 700 :class => 'icon-del disabled' }
701 701 end
702 702
703 703 def test_context_menu_multiple_issues_of_same_project
704 704 @request.session[:user_id] = 2
705 705 get :context_menu, :ids => [1, 2]
706 706 assert_response :success
707 707 assert_template 'context_menu'
708 708 assert_tag :tag => 'a', :content => 'Edit',
709 709 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
710 710 :class => 'icon-edit' }
711 711 assert_tag :tag => 'a', :content => 'Immediate',
712 712 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;priority_id=8',
713 713 :class => '' }
714 714 assert_tag :tag => 'a', :content => 'Dave Lopper',
715 715 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
716 716 :class => '' }
717 717 assert_tag :tag => 'a', :content => 'Move',
718 718 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
719 719 :class => 'icon-move' }
720 720 assert_tag :tag => 'a', :content => 'Delete',
721 721 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
722 722 :class => 'icon-del' }
723 723 end
724 724
725 725 def test_context_menu_multiple_issues_of_different_project
726 726 @request.session[:user_id] = 2
727 727 get :context_menu, :ids => [1, 2, 4]
728 728 assert_response :success
729 729 assert_template 'context_menu'
730 730 assert_tag :tag => 'a', :content => 'Delete',
731 731 :attributes => { :href => '#',
732 732 :class => 'icon-del disabled' }
733 733 end
734 734
735 735 def test_destroy_issue_with_no_time_entries
736 736 assert_nil TimeEntry.find_by_issue_id(2)
737 737 @request.session[:user_id] = 2
738 738 post :destroy, :id => 2
739 739 assert_redirected_to 'projects/ecookbook/issues'
740 740 assert_nil Issue.find_by_id(2)
741 741 end
742 742
743 743 def test_destroy_issues_with_time_entries
744 744 @request.session[:user_id] = 2
745 745 post :destroy, :ids => [1, 3]
746 746 assert_response :success
747 747 assert_template 'destroy'
748 748 assert_not_nil assigns(:hours)
749 749 assert Issue.find_by_id(1) && Issue.find_by_id(3)
750 750 end
751 751
752 752 def test_destroy_issues_and_destroy_time_entries
753 753 @request.session[:user_id] = 2
754 754 post :destroy, :ids => [1, 3], :todo => 'destroy'
755 755 assert_redirected_to 'projects/ecookbook/issues'
756 756 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
757 757 assert_nil TimeEntry.find_by_id([1, 2])
758 758 end
759 759
760 760 def test_destroy_issues_and_assign_time_entries_to_project
761 761 @request.session[:user_id] = 2
762 762 post :destroy, :ids => [1, 3], :todo => 'nullify'
763 763 assert_redirected_to 'projects/ecookbook/issues'
764 764 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
765 765 assert_nil TimeEntry.find(1).issue_id
766 766 assert_nil TimeEntry.find(2).issue_id
767 767 end
768 768
769 769 def test_destroy_issues_and_reassign_time_entries_to_another_issue
770 770 @request.session[:user_id] = 2
771 771 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
772 772 assert_redirected_to 'projects/ecookbook/issues'
773 773 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
774 774 assert_equal 2, TimeEntry.find(1).issue_id
775 775 assert_equal 2, TimeEntry.find(2).issue_id
776 776 end
777 777 end
General Comments 0
You need to be logged in to leave comments. Login now