##// END OF EJS Templates
Converted routing and urls to follow the Rails REST convention....
Eric Davis -
r2315:765f7abc6033
parent child
Show More
@@ -0,0 +1,22
1 require File.dirname(__FILE__) + '/../test_helper'
2 require 'issue_relations_controller'
3
4 # Re-raise errors caught by the controller.
5 class IssueRelationsController; def rescue_action(e) raise e end; end
6
7
8 class IssueRelationsControllerTest < Test::Unit::TestCase
9 def test_new_routing
10 assert_routing(
11 {:method => :post, :path => '/issues/1/relations'},
12 {:controller => 'issue_relations', :action => 'new', :issue_id => '1'}
13 )
14 end
15
16 def test_destroy_routing
17 assert_recognizes( #TODO: use DELETE on issue URI
18 {:controller => 'issue_relations', :action => 'destroy', :issue_id => '1', :id => '23'},
19 {:method => :post, :path => '/issues/1/relations/23/destroy'}
20 )
21 end
22 end
@@ -0,0 +1,15
1 require File.dirname(__FILE__) + '/../test_helper'
2 require 'members_controller'
3
4 # Re-raise errors caught by the controller.
5 class MembersController; def rescue_action(e) raise e end; end
6
7
8 class MembersControllerTest < Test::Unit::TestCase
9 def test_members_routing
10 assert_routing(
11 {:method => :post, :path => 'projects/5234/members/new'},
12 :controller => 'members', :action => 'new', :id => '5234'
13 )
14 end
15 end
@@ -0,0 +1,20
1 require File.dirname(__FILE__) + '/../test_helper'
2 require 'reports_controller'
3
4 # Re-raise errors caught by the controller.
5 class ReportsController; def rescue_action(e) raise e end; end
6
7
8 class ReportsControllerTest < Test::Unit::TestCase
9 def test_issue_report_routing
10 assert_routing(
11 {:method => :get, :path => '/projects/567/issues/report'},
12 :controller => 'reports', :action => 'issue_report', :id => '567'
13 )
14 assert_routing(
15 {:method => :get, :path => '/projects/567/issues/report/assigned_to'},
16 :controller => 'reports', :action => 'issue_report', :id => '567', :detail => 'assigned_to'
17 )
18
19 end
20 end
@@ -1,667 +1,672
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'coderay'
19 19 require 'coderay/helpers/file_type'
20 20 require 'forwardable'
21 21 require 'cgi'
22 22
23 23 module ApplicationHelper
24 24 include Redmine::WikiFormatting::Macros::Definitions
25 25 include GravatarHelper::PublicMethods
26 26
27 27 extend Forwardable
28 28 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
29 29
30 30 def current_role
31 31 @current_role ||= User.current.role_for_project(@project)
32 32 end
33 33
34 34 # Return true if user is authorized for controller/action, otherwise false
35 35 def authorize_for(controller, action)
36 36 User.current.allowed_to?({:controller => controller, :action => action}, @project)
37 37 end
38 38
39 39 # Display a link if user is authorized
40 40 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
41 41 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
42 42 end
43 43
44 44 # Display a link to remote if user is authorized
45 45 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
46 46 url = options[:url] || {}
47 47 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
48 48 end
49 49
50 50 # Display a link to user's account page
51 51 def link_to_user(user, options={})
52 52 (user && !user.anonymous?) ? link_to(user.name(options[:format]), :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
53 53 end
54 54
55 55 def link_to_issue(issue, options={})
56 56 options[:class] ||= ''
57 57 options[:class] << ' issue'
58 58 options[:class] << ' closed' if issue.closed?
59 59 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
60 60 end
61 61
62 62 # Generates a link to an attachment.
63 63 # Options:
64 64 # * :text - Link text (default to attachment filename)
65 65 # * :download - Force download (default: false)
66 66 def link_to_attachment(attachment, options={})
67 67 text = options.delete(:text) || attachment.filename
68 68 action = options.delete(:download) ? 'download' : 'show'
69 69
70 70 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
71 71 end
72 72
73 73 def toggle_link(name, id, options={})
74 74 onclick = "Element.toggle('#{id}'); "
75 75 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
76 76 onclick << "return false;"
77 77 link_to(name, "#", :onclick => onclick)
78 78 end
79 79
80 80 def image_to_function(name, function, html_options = {})
81 81 html_options.symbolize_keys!
82 82 tag(:input, html_options.merge({
83 83 :type => "image", :src => image_path(name),
84 84 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
85 85 }))
86 86 end
87 87
88 88 def prompt_to_remote(name, text, param, url, html_options = {})
89 89 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
90 90 link_to name, {}, html_options
91 91 end
92 92
93 93 def format_date(date)
94 94 return nil unless date
95 95 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
96 96 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
97 97 date.strftime(@date_format)
98 98 end
99 99
100 100 def format_time(time, include_date = true)
101 101 return nil unless time
102 102 time = time.to_time if time.is_a?(String)
103 103 zone = User.current.time_zone
104 104 local = zone ? time.in_time_zone(zone) : (time.utc? ? time.localtime : time)
105 105 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
106 106 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
107 107 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
108 108 end
109 109
110 110 def format_activity_title(text)
111 111 h(truncate_single_line(text, 100))
112 112 end
113 113
114 114 def format_activity_day(date)
115 115 date == Date.today ? l(:label_today).titleize : format_date(date)
116 116 end
117 117
118 118 def format_activity_description(text)
119 119 h(truncate(text.to_s, 250).gsub(%r{<(pre|code)>.*$}m, '...'))
120 120 end
121 121
122 122 def distance_of_date_in_words(from_date, to_date = 0)
123 123 from_date = from_date.to_date if from_date.respond_to?(:to_date)
124 124 to_date = to_date.to_date if to_date.respond_to?(:to_date)
125 125 distance_in_days = (to_date - from_date).abs
126 126 lwr(:actionview_datehelper_time_in_words_day, distance_in_days)
127 127 end
128 128
129 129 def due_date_distance_in_words(date)
130 130 if date
131 131 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
132 132 end
133 133 end
134 134
135 135 def render_page_hierarchy(pages, node=nil)
136 136 content = ''
137 137 if pages[node]
138 138 content << "<ul class=\"pages-hierarchy\">\n"
139 139 pages[node].each do |page|
140 140 content << "<li>"
141 141 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title},
142 142 :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
143 143 content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
144 144 content << "</li>\n"
145 145 end
146 146 content << "</ul>\n"
147 147 end
148 148 content
149 149 end
150 150
151 151 # Renders flash messages
152 152 def render_flash_messages
153 153 s = ''
154 154 flash.each do |k,v|
155 155 s << content_tag('div', v, :class => "flash #{k}")
156 156 end
157 157 s
158 158 end
159 159
160 160 # Renders the project quick-jump box
161 161 def render_project_jump_box
162 162 # Retrieve them now to avoid a COUNT query
163 163 projects = User.current.projects.all
164 164 if projects.any?
165 165 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
166 166 "<option selected='selected'>#{ l(:label_jump_to_a_project) }</option>" +
167 167 '<option disabled="disabled">---</option>'
168 168 s << project_tree_options_for_select(projects) do |p|
169 169 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
170 170 end
171 171 s << '</select>'
172 172 s
173 173 end
174 174 end
175 175
176 176 def project_tree_options_for_select(projects, options = {})
177 177 s = ''
178 178 project_tree(projects) do |project, level|
179 179 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
180 180 tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
181 181 tag_options.merge!(yield(project)) if block_given?
182 182 s << content_tag('option', name_prefix + h(project), tag_options)
183 183 end
184 184 s
185 185 end
186 186
187 187 # Yields the given block for each project with its level in the tree
188 188 def project_tree(projects, &block)
189 189 ancestors = []
190 190 projects.sort_by(&:lft).each do |project|
191 191 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
192 192 ancestors.pop
193 193 end
194 194 yield project, ancestors.size
195 195 ancestors << project
196 196 end
197 197 end
198 198
199 199 # Truncates and returns the string as a single line
200 200 def truncate_single_line(string, *args)
201 201 truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
202 202 end
203 203
204 204 def html_hours(text)
205 205 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
206 206 end
207 207
208 208 def authoring(created, author, options={})
209 209 time_tag = @project.nil? ? content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created)) :
210 210 link_to(distance_of_time_in_words(Time.now, created),
211 211 {:controller => 'projects', :action => 'activity', :id => @project, :from => created.to_date},
212 212 :title => format_time(created))
213 213 author_tag = (author.is_a?(User) && !author.anonymous?) ? link_to(h(author), :controller => 'account', :action => 'show', :id => author) : h(author || 'Anonymous')
214 214 l(options[:label] || :label_added_time_by, author_tag, time_tag)
215 215 end
216 216
217 217 def l_or_humanize(s, options={})
218 218 k = "#{options[:prefix]}#{s}".to_sym
219 219 l_has_string?(k) ? l(k) : s.to_s.humanize
220 220 end
221 221
222 222 def day_name(day)
223 223 l(:general_day_names).split(',')[day-1]
224 224 end
225 225
226 226 def month_name(month)
227 227 l(:actionview_datehelper_select_month_names).split(',')[month-1]
228 228 end
229 229
230 230 def syntax_highlight(name, content)
231 231 type = CodeRay::FileType[name]
232 232 type ? CodeRay.scan(content, type).html : h(content)
233 233 end
234 234
235 235 def to_path_param(path)
236 236 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
237 237 end
238 238
239 239 def pagination_links_full(paginator, count=nil, options={})
240 240 page_param = options.delete(:page_param) || :page
241 241 url_param = params.dup
242 242 # don't reuse params if filters are present
243 243 url_param.clear if url_param.has_key?(:set_filter)
244 244
245 245 html = ''
246 html << link_to_remote(('&#171; ' + l(:label_previous)),
247 {:update => 'content',
248 :url => url_param.merge(page_param => paginator.current.previous),
249 :complete => 'window.scrollTo(0,0)'},
250 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
246 if paginator.current.previous
247 html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
248 end
251 249
252 250 html << (pagination_links_each(paginator, options) do |n|
253 link_to_remote(n.to_s,
254 {:url => {:params => url_param.merge(page_param => n)},
255 :update => 'content',
256 :complete => 'window.scrollTo(0,0)'},
257 {:href => url_for(:params => url_param.merge(page_param => n))})
251 link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
258 252 end || '')
259 253
260 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
261 {:update => 'content',
262 :url => url_param.merge(page_param => paginator.current.next),
263 :complete => 'window.scrollTo(0,0)'},
264 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
254 if paginator.current.next
255 html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
256 end
265 257
266 258 unless count.nil?
267 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
259 html << [
260 " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})",
261 per_page_links(paginator.items_per_page)
262 ].compact.join(' | ')
268 263 end
269 264
270 265 html
271 266 end
272 267
273 268 def per_page_links(selected=nil)
274 269 url_param = params.dup
275 270 url_param.clear if url_param.has_key?(:set_filter)
276 271
277 272 links = Setting.per_page_options_array.collect do |n|
278 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
273 n == selected ? n : link_to_remote(n, {:update => "content",
274 :url => params.dup.merge(:per_page => n),
275 :method => :get},
279 276 {:href => url_for(url_param.merge(:per_page => n))})
280 277 end
281 278 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
282 279 end
283 280
284 281 def breadcrumb(*args)
285 282 elements = args.flatten
286 283 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
287 284 end
288 285
289 286 def html_title(*args)
290 287 if args.empty?
291 288 title = []
292 289 title << @project.name if @project
293 290 title += @html_title if @html_title
294 291 title << Setting.app_title
295 292 title.compact.join(' - ')
296 293 else
297 294 @html_title ||= []
298 295 @html_title += args
299 296 end
300 297 end
301 298
302 299 def accesskey(s)
303 300 Redmine::AccessKeys.key_for s
304 301 end
305 302
306 303 # Formats text according to system settings.
307 304 # 2 ways to call this method:
308 305 # * with a String: textilizable(text, options)
309 306 # * with an object and one of its attribute: textilizable(issue, :description, options)
310 307 def textilizable(*args)
311 308 options = args.last.is_a?(Hash) ? args.pop : {}
312 309 case args.size
313 310 when 1
314 311 obj = options[:object]
315 312 text = args.shift
316 313 when 2
317 314 obj = args.shift
318 315 text = obj.send(args.shift).to_s
319 316 else
320 317 raise ArgumentError, 'invalid arguments to textilizable'
321 318 end
322 319 return '' if text.blank?
323 320
324 321 only_path = options.delete(:only_path) == false ? false : true
325 322
326 323 # when using an image link, try to use an attachment, if possible
327 324 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
328 325
329 326 if attachments
330 327 attachments = attachments.sort_by(&:created_on).reverse
331 328 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
332 329 style = $1
333 330 filename = $6
334 331 rf = Regexp.new(Regexp.escape(filename), Regexp::IGNORECASE)
335 332 # search for the picture in attachments
336 333 if found = attachments.detect { |att| att.filename =~ rf }
337 334 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
338 335 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
339 336 alt = desc.blank? ? nil : "(#{desc})"
340 337 "!#{style}#{image_url}#{alt}!"
341 338 else
342 339 "!#{style}#{filename}!"
343 340 end
344 341 end
345 342 end
346 343
347 344 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
348 345
349 346 # different methods for formatting wiki links
350 347 case options[:wiki_links]
351 348 when :local
352 349 # used for local links to html files
353 350 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
354 351 when :anchor
355 352 # used for single-file wiki export
356 353 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
357 354 else
358 355 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
359 356 end
360 357
361 358 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
362 359
363 360 # Wiki links
364 361 #
365 362 # Examples:
366 363 # [[mypage]]
367 364 # [[mypage|mytext]]
368 365 # wiki links can refer other project wikis, using project name or identifier:
369 366 # [[project:]] -> wiki starting page
370 367 # [[project:|mytext]]
371 368 # [[project:mypage]]
372 369 # [[project:mypage|mytext]]
373 370 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
374 371 link_project = project
375 372 esc, all, page, title = $1, $2, $3, $5
376 373 if esc.nil?
377 374 if page =~ /^([^\:]+)\:(.*)$/
378 375 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
379 376 page = $2
380 377 title ||= $1 if page.blank?
381 378 end
382 379
383 380 if link_project && link_project.wiki
384 381 # extract anchor
385 382 anchor = nil
386 383 if page =~ /^(.+?)\#(.+)$/
387 384 page, anchor = $1, $2
388 385 end
389 386 # check if page exists
390 387 wiki_page = link_project.wiki.find_page(page)
391 388 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
392 389 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
393 390 else
394 391 # project or wiki doesn't exist
395 392 title || page
396 393 end
397 394 else
398 395 all
399 396 end
400 397 end
401 398
402 399 # Redmine links
403 400 #
404 401 # Examples:
405 402 # Issues:
406 403 # #52 -> Link to issue #52
407 404 # Changesets:
408 405 # r52 -> Link to revision 52
409 406 # commit:a85130f -> Link to scmid starting with a85130f
410 407 # Documents:
411 408 # document#17 -> Link to document with id 17
412 409 # document:Greetings -> Link to the document with title "Greetings"
413 410 # document:"Some document" -> Link to the document with title "Some document"
414 411 # Versions:
415 412 # version#3 -> Link to version with id 3
416 413 # version:1.0.0 -> Link to version named "1.0.0"
417 414 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
418 415 # Attachments:
419 416 # attachment:file.zip -> Link to the attachment of the current object named file.zip
420 417 # Source files:
421 418 # source:some/file -> Link to the file located at /some/file in the project's repository
422 419 # source:some/file@52 -> Link to the file's revision 52
423 420 # source:some/file#L120 -> Link to line 120 of the file
424 421 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
425 422 # export:some/file -> Force the download of the file
426 423 # Forum messages:
427 424 # message#1218 -> Link to message with id 1218
428 425 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|\s|<|$)}) do |m|
429 426 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
430 427 link = nil
431 428 if esc.nil?
432 429 if prefix.nil? && sep == 'r'
433 430 if project && (changeset = project.changesets.find_by_revision(oid))
434 431 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
435 432 :class => 'changeset',
436 433 :title => truncate_single_line(changeset.comments, 100))
437 434 end
438 435 elsif sep == '#'
439 436 oid = oid.to_i
440 437 case prefix
441 438 when nil
442 439 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
443 440 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
444 441 :class => (issue.closed? ? 'issue closed' : 'issue'),
445 442 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
446 443 link = content_tag('del', link) if issue.closed?
447 444 end
448 445 when 'document'
449 446 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
450 447 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
451 448 :class => 'document'
452 449 end
453 450 when 'version'
454 451 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
455 452 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
456 453 :class => 'version'
457 454 end
458 455 when 'message'
459 456 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
460 457 link = link_to h(truncate(message.subject, 60)), {:only_path => only_path,
461 458 :controller => 'messages',
462 459 :action => 'show',
463 460 :board_id => message.board,
464 461 :id => message.root,
465 462 :anchor => (message.parent ? "message-#{message.id}" : nil)},
466 463 :class => 'message'
467 464 end
468 465 end
469 466 elsif sep == ':'
470 467 # removes the double quotes if any
471 468 name = oid.gsub(%r{^"(.*)"$}, "\\1")
472 469 case prefix
473 470 when 'document'
474 471 if project && document = project.documents.find_by_title(name)
475 472 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
476 473 :class => 'document'
477 474 end
478 475 when 'version'
479 476 if project && version = project.versions.find_by_name(name)
480 477 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
481 478 :class => 'version'
482 479 end
483 480 when 'commit'
484 481 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
485 482 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
486 483 :class => 'changeset',
487 484 :title => truncate_single_line(changeset.comments, 100)
488 485 end
489 486 when 'source', 'export'
490 487 if project && project.repository
491 488 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
492 489 path, rev, anchor = $1, $3, $5
493 490 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
494 491 :path => to_path_param(path),
495 492 :rev => rev,
496 493 :anchor => anchor,
497 494 :format => (prefix == 'export' ? 'raw' : nil)},
498 495 :class => (prefix == 'export' ? 'source download' : 'source')
499 496 end
500 497 when 'attachment'
501 498 if attachments && attachment = attachments.detect {|a| a.filename == name }
502 499 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
503 500 :class => 'attachment'
504 501 end
505 502 end
506 503 end
507 504 end
508 505 leading + (link || "#{prefix}#{sep}#{oid}")
509 506 end
510 507
511 508 text
512 509 end
513 510
514 511 # Same as Rails' simple_format helper without using paragraphs
515 512 def simple_format_without_paragraph(text)
516 513 text.to_s.
517 514 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
518 515 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
519 516 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
520 517 end
521 518
522 519 def error_messages_for(object_name, options = {})
523 520 options = options.symbolize_keys
524 521 object = instance_variable_get("@#{object_name}")
525 522 if object && !object.errors.empty?
526 523 # build full_messages here with controller current language
527 524 full_messages = []
528 525 object.errors.each do |attr, msg|
529 526 next if msg.nil?
530 527 msg = msg.first if msg.is_a? Array
531 528 if attr == "base"
532 529 full_messages << l(msg)
533 530 else
534 531 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
535 532 end
536 533 end
537 534 # retrieve custom values error messages
538 535 if object.errors[:custom_values]
539 536 object.custom_values.each do |v|
540 537 v.errors.each do |attr, msg|
541 538 next if msg.nil?
542 539 msg = msg.first if msg.is_a? Array
543 540 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
544 541 end
545 542 end
546 543 end
547 544 content_tag("div",
548 545 content_tag(
549 546 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
550 547 ) +
551 548 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
552 549 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
553 550 )
554 551 else
555 552 ""
556 553 end
557 554 end
558 555
559 556 def lang_options_for_select(blank=true)
560 557 (blank ? [["(auto)", ""]] : []) +
561 558 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
562 559 end
563 560
564 561 def label_tag_for(name, option_tags = nil, options = {})
565 562 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
566 563 content_tag("label", label_text)
567 564 end
568 565
569 566 def labelled_tabular_form_for(name, object, options, &proc)
570 567 options[:html] ||= {}
571 568 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
572 569 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
573 570 end
574 571
575 572 def back_url_hidden_field_tag
576 573 back_url = params[:back_url] || request.env['HTTP_REFERER']
577 574 back_url = CGI.unescape(back_url.to_s)
578 575 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
579 576 end
580 577
581 578 def check_all_links(form_name)
582 579 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
583 580 " | " +
584 581 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
585 582 end
586 583
587 584 def progress_bar(pcts, options={})
588 585 pcts = [pcts, pcts] unless pcts.is_a?(Array)
589 586 pcts[1] = pcts[1] - pcts[0]
590 587 pcts << (100 - pcts[1] - pcts[0])
591 588 width = options[:width] || '100px;'
592 589 legend = options[:legend] || ''
593 590 content_tag('table',
594 591 content_tag('tr',
595 592 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0].floor}%;", :class => 'closed') : '') +
596 593 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1].floor}%;", :class => 'done') : '') +
597 594 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2].floor}%;", :class => 'todo') : '')
598 595 ), :class => 'progress', :style => "width: #{width};") +
599 596 content_tag('p', legend, :class => 'pourcent')
600 597 end
601 598
602 599 def context_menu_link(name, url, options={})
603 600 options[:class] ||= ''
604 601 if options.delete(:selected)
605 602 options[:class] << ' icon-checked disabled'
606 603 options[:disabled] = true
607 604 end
608 605 if options.delete(:disabled)
609 606 options.delete(:method)
610 607 options.delete(:confirm)
611 608 options.delete(:onclick)
612 609 options[:class] << ' disabled'
613 610 url = '#'
614 611 end
615 612 link_to name, url, options
616 613 end
617 614
618 615 def calendar_for(field_id)
619 616 include_calendar_headers_tags
620 617 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
621 618 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
622 619 end
623 620
624 621 def include_calendar_headers_tags
625 622 unless @calendar_headers_tags_included
626 623 @calendar_headers_tags_included = true
627 624 content_for :header_tags do
628 625 javascript_include_tag('calendar/calendar') +
629 626 javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
630 627 javascript_include_tag('calendar/calendar-setup') +
631 628 stylesheet_link_tag('calendar')
632 629 end
633 630 end
634 631 end
635 632
636 633 def content_for(name, content = nil, &block)
637 634 @has_content ||= {}
638 635 @has_content[name] = true
639 636 super(name, content, &block)
640 637 end
641 638
642 639 def has_content?(name)
643 640 (@has_content && @has_content[name]) || false
644 641 end
645 642
646 643 # Returns the avatar image tag for the given +user+ if avatars are enabled
647 644 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
648 645 def avatar(user, options = { })
649 646 if Setting.gravatar_enabled?
650 647 email = nil
651 648 if user.respond_to?(:mail)
652 649 email = user.mail
653 650 elsif user.to_s =~ %r{<(.+?)>}
654 651 email = $1
655 652 end
656 653 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
657 654 end
658 655 end
659 656
660 657 private
661 658
662 659 def wiki_helper
663 660 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
664 661 extend helper
665 662 return self
666 663 end
664
665 def link_to_remote_content_update(text, url_params)
666 link_to_remote(text,
667 {:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
668 {:href => url_for(:params => url_params)}
669 )
670 end
671
667 672 end
@@ -1,168 +1,168
1 1 # Helpers to sort tables using clickable column headers.
2 2 #
3 3 # Author: Stuart Rackham <srackham@methods.co.nz>, March 2005.
4 4 # License: This source code is released under the MIT license.
5 5 #
6 6 # - Consecutive clicks toggle the column's sort order.
7 7 # - Sort state is maintained by a session hash entry.
8 8 # - Icon image identifies sort column and state.
9 9 # - Typically used in conjunction with the Pagination module.
10 10 #
11 11 # Example code snippets:
12 12 #
13 13 # Controller:
14 14 #
15 15 # helper :sort
16 16 # include SortHelper
17 17 #
18 18 # def list
19 19 # sort_init 'last_name'
20 20 # sort_update
21 21 # @items = Contact.find_all nil, sort_clause
22 22 # end
23 23 #
24 24 # Controller (using Pagination module):
25 25 #
26 26 # helper :sort
27 27 # include SortHelper
28 28 #
29 29 # def list
30 30 # sort_init 'last_name'
31 31 # sort_update
32 32 # @contact_pages, @items = paginate :contacts,
33 33 # :order_by => sort_clause,
34 34 # :per_page => 10
35 35 # end
36 36 #
37 37 # View (table header in list.rhtml):
38 38 #
39 39 # <thead>
40 40 # <tr>
41 41 # <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
42 42 # <%= sort_header_tag('last_name', :caption => 'Name') %>
43 43 # <%= sort_header_tag('phone') %>
44 44 # <%= sort_header_tag('address', :width => 200) %>
45 45 # </tr>
46 46 # </thead>
47 47 #
48 48 # - The ascending and descending sort icon images are sort_asc.png and
49 49 # sort_desc.png and reside in the application's images directory.
50 50 # - Introduces instance variables: @sort_name, @sort_default.
51 51 # - Introduces params :sort_key and :sort_order.
52 52 #
53 53 module SortHelper
54 54
55 55 # Initializes the default sort column (default_key) and sort order
56 56 # (default_order).
57 57 #
58 58 # - default_key is a column attribute name.
59 59 # - default_order is 'asc' or 'desc'.
60 60 # - name is the name of the session hash entry that stores the sort state,
61 61 # defaults to '<controller_name>_sort'.
62 62 #
63 63 def sort_init(default_key, default_order='asc', name=nil)
64 64 @sort_name = name || params[:controller] + params[:action] + '_sort'
65 65 @sort_default = {:key => default_key, :order => default_order}
66 66 end
67 67
68 68 # Updates the sort state. Call this in the controller prior to calling
69 69 # sort_clause.
70 70 # sort_keys can be either an array or a hash of allowed keys
71 71 def sort_update(sort_keys)
72 72 sort_key = params[:sort_key]
73 73 sort_key = nil unless (sort_keys.is_a?(Array) ? sort_keys.include?(sort_key) : sort_keys[sort_key])
74 74
75 75 sort_order = (params[:sort_order] == 'desc' ? 'DESC' : 'ASC')
76 76
77 77 if sort_key
78 78 sort = {:key => sort_key, :order => sort_order}
79 79 elsif session[@sort_name]
80 80 sort = session[@sort_name] # Previous sort.
81 81 else
82 82 sort = @sort_default
83 83 end
84 84 session[@sort_name] = sort
85 85
86 86 sort_column = (sort_keys.is_a?(Hash) ? sort_keys[sort[:key]] : sort[:key])
87 87 @sort_clause = (sort_column.blank? ? nil : "#{sort_column} #{sort[:order]}")
88 88 end
89 89
90 90 # Returns an SQL sort clause corresponding to the current sort state.
91 91 # Use this to sort the controller's table items collection.
92 92 #
93 93 def sort_clause()
94 94 @sort_clause
95 95 end
96 96
97 97 # Returns a link which sorts by the named column.
98 98 #
99 99 # - column is the name of an attribute in the sorted record collection.
100 100 # - The optional caption explicitly specifies the displayed link text.
101 101 # - A sort icon image is positioned to the right of the sort link.
102 102 #
103 103 def sort_link(column, caption, default_order)
104 104 key, order = session[@sort_name][:key], session[@sort_name][:order]
105 105 if key == column
106 106 if order.downcase == 'asc'
107 107 icon = 'sort_asc.png'
108 108 order = 'desc'
109 109 else
110 110 icon = 'sort_desc.png'
111 111 order = 'asc'
112 112 end
113 113 else
114 114 icon = nil
115 115 order = default_order
116 116 end
117 117 caption = titleize(Inflector::humanize(column)) unless caption
118 118
119 119 sort_options = { :sort_key => column, :sort_order => order }
120 120 # don't reuse params if filters are present
121 121 url_options = params.has_key?(:set_filter) ? sort_options : params.merge(sort_options)
122 122
123 123 link_to_remote(caption,
124 {:update => "content", :url => url_options},
124 {:update => "content", :url => url_options, :method => :get},
125 125 {:href => url_for(url_options)}) +
126 126 (icon ? nbsp(2) + image_tag(icon) : '')
127 127 end
128 128
129 129 # Returns a table header <th> tag with a sort link for the named column
130 130 # attribute.
131 131 #
132 132 # Options:
133 133 # :caption The displayed link name (defaults to titleized column name).
134 134 # :title The tag's 'title' attribute (defaults to 'Sort by :caption').
135 135 #
136 136 # Other options hash entries generate additional table header tag attributes.
137 137 #
138 138 # Example:
139 139 #
140 140 # <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
141 141 #
142 142 # Renders:
143 143 #
144 144 # <th title="Sort by contact ID" width="40">
145 145 # <a href="/contact/list?sort_order=desc&amp;sort_key=id">Id</a>
146 146 # &nbsp;&nbsp;<img alt="Sort_asc" src="/images/sort_asc.png" />
147 147 # </th>
148 148 #
149 149 def sort_header_tag(column, options = {})
150 150 caption = options.delete(:caption) || titleize(Inflector::humanize(column))
151 151 default_order = options.delete(:default_order) || 'asc'
152 152 options[:title]= l(:label_sort_by, "\"#{caption}\"") unless options[:title]
153 153 content_tag('th', sort_link(column, caption, default_order), options)
154 154 end
155 155
156 156 private
157 157
158 158 # Return n non-breaking spaces.
159 159 def nbsp(n)
160 160 '&nbsp;' * n
161 161 end
162 162
163 163 # Return capitalized title.
164 164 def titleize(title)
165 165 title.split.map {|w| w.capitalize }.join(' ')
166 166 end
167 167
168 168 end
@@ -1,42 +1,42
1 1 <h2><%=l(:label_change_log)%></h2>
2 2
3 3 <% if @versions.empty? %>
4 4 <p class="nodata"><%= l(:label_no_data) %></p>
5 5 <% end %>
6 6
7 7 <% @versions.each do |version| %>
8 8 <a name="<%= version.name %>"><h3 class="icon22 icon22-package"><%= version.name %></h3></a>
9 9 <% if version.effective_date %>
10 10 <p><%= format_date(version.effective_date) %></p>
11 11 <% end %>
12 12 <p><%=h version.description %></p>
13 13 <% issues = version.fixed_issues.find(:all,
14 14 :include => [:status, :tracker],
15 15 :conditions => ["#{IssueStatus.table_name}.is_closed=? AND #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')})", true],
16 16 :order => "#{Tracker.table_name}.position") unless @selected_tracker_ids.empty?
17 17 issues ||= []
18 18 %>
19 19 <% if !issues.empty? %>
20 20 <ul>
21 21 <% issues.each do |issue| %>
22 22 <li><%= link_to_issue(issue) %>: <%=h issue.subject %></li>
23 23 <% end %>
24 24 </ul>
25 25 <% end %>
26 26 <% end %>
27 27
28 28 <% content_for :sidebar do %>
29 <% form_tag do %>
29 <% form_tag({},:method => :get) do %>
30 30 <h3><%= l(:label_change_log) %></h3>
31 31 <% @trackers.each do |tracker| %>
32 32 <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %>
33 33 <%= tracker.name %></label><br />
34 34 <% end %>
35 35 <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
36 36 <% end %>
37 37
38 38 <h3><%= l(:label_version_plural) %></h3>
39 39 <% @versions.each do |version| %>
40 40 <%= link_to version.name, :anchor => version.name %><br />
41 41 <% end %>
42 42 <% end %>
@@ -1,34 +1,36
1 1 <div class="contextual">
2 2 <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time' %>
3 3 </div>
4 4
5 5 <%= render_timelog_breadcrumb %>
6 6
7 7 <h2><%= l(:label_spent_time) %></h2>
8 8
9 <% form_remote_tag( :url => {}, :method => :get, :update => 'content' ) do %>
9 <% form_remote_tag( :url => {}, :html => {:method => :get}, :method => :get, :update => 'content' ) do %>
10 <%# TOOD: remove the project_id and issue_id hidden fields, that information is
11 already in the URI %>
10 12 <%= hidden_field_tag 'project_id', params[:project_id] %>
11 13 <%= hidden_field_tag 'issue_id', params[:issue_id] if @issue %>
12 14 <%= render :partial => 'date_range' %>
13 15 <% end %>
14 16
15 17 <div class="total-hours">
16 18 <p><%= l(:label_total) %>: <%= html_hours(lwr(:label_f_hour, @total_hours)) %></p>
17 19 </div>
18 20
19 21 <% unless @entries.empty? %>
20 22 <%= render :partial => 'list', :locals => { :entries => @entries }%>
21 23 <p class="pagination"><%= pagination_links_full @entry_pages, @entry_count %></p>
22 24
23 25 <p class="other-formats">
24 26 <%= l(:label_export_to) %>
25 27 <span><%= link_to 'Atom', {:issue_id => @issue, :format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
26 28 <span><%= link_to 'CSV', params.merge(:format => 'csv'), :class => 'csv' %></span>
27 29 </p>
28 30 <% end %>
29 31
30 32 <% html_title l(:label_spent_time), l(:label_details) %>
31 33
32 34 <% content_for :header_tags do %>
33 35 <%= auto_discovery_link_tag(:atom, {:issue_id => @issue, :format => 'atom', :key => User.current.rss_key}, :title => l(:label_spent_time)) %>
34 36 <% end %>
@@ -1,74 +1,76
1 1 <div class="contextual">
2 2 <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time' %>
3 3 </div>
4 4
5 5 <%= render_timelog_breadcrumb %>
6 6
7 7 <h2><%= l(:label_spent_time) %></h2>
8 8
9 <% form_remote_tag(:url => {}, :update => 'content') do %>
9 <% form_remote_tag(:url => {}, :html => {:method => :get}, :method => :get, :update => 'content') do %>
10 10 <% @criterias.each do |criteria| %>
11 11 <%= hidden_field_tag 'criterias[]', criteria, :id => nil %>
12 12 <% end %>
13 <%# TODO: get rid of the project_id field, that should already be in the URL %>
13 14 <%= hidden_field_tag 'project_id', params[:project_id] %>
14 15 <%= render :partial => 'date_range' %>
15 16
16 17 <p><%= l(:label_details) %>: <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'],
17 18 [l(:label_month), 'month'],
18 19 [l(:label_week), 'week'],
19 20 [l(:label_day_plural).titleize, 'day']], @columns),
20 21 :onchange => "this.form.onsubmit();" %>
21 22
22 23 <%= l(:button_add) %>: <%= select_tag('criterias[]', options_for_select([[]] + (@available_criterias.keys - @criterias).collect{|k| [l(@available_criterias[k][:label]), k]}),
23 24 :onchange => "this.form.onsubmit();",
24 25 :style => 'width: 200px',
25 26 :id => nil,
26 27 :disabled => (@criterias.length >= 3)) %>
27 28 <%= link_to_remote l(:button_clear), {:url => {:project_id => @project, :period_type => params[:period_type], :period => params[:period], :from => @from, :to => @to, :columns => @columns},
29 :method => :get,
28 30 :update => 'content'
29 31 }, :class => 'icon icon-reload' %></p>
30 32 <% end %>
31 33
32 34 <% unless @criterias.empty? %>
33 35 <div class="total-hours">
34 36 <p><%= l(:label_total) %>: <%= html_hours(lwr(:label_f_hour, @total_hours)) %></p>
35 37 </div>
36 38
37 39 <% unless @hours.empty? %>
38 40 <table class="list" id="time-report">
39 41 <thead>
40 42 <tr>
41 43 <% @criterias.each do |criteria| %>
42 44 <th><%= l(@available_criterias[criteria][:label]) %></th>
43 45 <% end %>
44 46 <% columns_width = (40 / (@periods.length+1)).to_i %>
45 47 <% @periods.each do |period| %>
46 48 <th class="period" width="<%= columns_width %>%"><%= period %></th>
47 49 <% end %>
48 50 <th class="total" width="<%= columns_width %>%"><%= l(:label_total) %></th>
49 51 </tr>
50 52 </thead>
51 53 <tbody>
52 54 <%= render :partial => 'report_criteria', :locals => {:criterias => @criterias, :hours => @hours, :level => 0} %>
53 55 <tr class="total">
54 56 <td><%= l(:label_total) %></td>
55 57 <%= '<td></td>' * (@criterias.size - 1) %>
56 58 <% total = 0 -%>
57 59 <% @periods.each do |period| -%>
58 60 <% sum = sum_hours(select_hours(@hours, @columns, period.to_s)); total += sum -%>
59 61 <td class="hours"><%= html_hours("%.2f" % sum) if sum > 0 %></td>
60 62 <% end -%>
61 63 <td class="hours"><%= html_hours("%.2f" % total) if total > 0 %></td>
62 64 </tr>
63 65 </tbody>
64 66 </table>
65 67
66 68 <p class="other-formats">
67 69 <%= l(:label_export_to) %>
68 70 <span><%= link_to 'CSV', params.merge({:format => 'csv'}), :class => 'csv' %></span>
69 71 </p>
70 72 <% end %>
71 73 <% end %>
72 74
73 75 <% html_title l(:label_spent_time), l(:label_report) %>
74 76
@@ -1,4 +1,4
1 <% labelled_tabular_form_for :user, @user, :url => { :action => "edit" } do |f| %>
1 <% labelled_tabular_form_for :user, @user, :url => { :action => "edit", :tab => nil } do |f| %>
2 2 <%= render :partial => 'form', :locals => { :f => f } %>
3 3 <%= submit_tag l(:button_save) %>
4 4 <% end %>
@@ -1,51 +1,253
1 1 ActionController::Routing::Routes.draw do |map|
2 2 # Add your own custom routes here.
3 3 # The priority is based upon order of creation: first created -> highest priority.
4 4
5 5 # Here's a sample route:
6 6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
7 7 # Keep in mind you can assign values other than :controller and :action
8 8
9 9 # Allow Redmine plugins to map routes and potentially override them
10 10 Rails.plugins.each do |plugin|
11 11 map.from_plugin plugin.name.to_sym
12 12 end
13 13
14 14 map.home '', :controller => 'welcome'
15
15 16 map.signin 'login', :controller => 'account', :action => 'login'
16 17 map.signout 'logout', :controller => 'account', :action => 'logout'
17 18
18 map.connect 'wiki/:id/:page/:action', :controller => 'wiki', :page => nil
19 19 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
20 20 map.connect 'help/:ctrl/:page', :controller => 'help'
21 #map.connect ':controller/:action/:id/:sort_key/:sort_order'
22 21
23 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
22 map.connect 'time_entries/:id/edit', :action => 'edit', :controller => 'timelog'
23 map.connect 'projects/:project_id/time_entries/new', :action => 'edit', :controller => 'timelog'
24 map.connect 'projects/:project_id/issues/:issue_id/time_entries/new', :action => 'edit', :controller => 'timelog'
25
26 map.with_options :controller => 'timelog' do |timelog|
27 timelog.connect 'projects/:project_id/time_entries', :action => 'details'
28
29 timelog.with_options :action => 'details', :conditions => {:method => :get} do |time_details|
30 time_details.connect 'time_entries'
31 time_details.connect 'time_entries.:format'
32 time_details.connect 'issues/:issue_id/time_entries'
33 time_details.connect 'issues/:issue_id/time_entries.:format'
34 time_details.connect 'projects/:project_id/time_entries.:format'
35 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries'
36 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries.:format'
37 end
38 timelog.connect 'projects/:project_id/time_entries/report', :action => 'report'
39 timelog.with_options :action => 'report',:conditions => {:method => :get} do |time_report|
40 time_report.connect 'time_entries/report'
41 time_report.connect 'time_entries/report.:format'
42 time_report.connect 'projects/:project_id/time_entries/report.:format'
43 end
44
45 timelog.with_options :action => 'edit', :conditions => {:method => :get} do |time_edit|
46 time_edit.connect 'issues/:issue_id/time_entries/new'
47 end
48
49 timelog.connect 'time_entries/:id/destroy', :action => 'destroy', :conditions => {:method => :post}
50 end
51
52 map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post}
53 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get}
54 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
55 map.with_options :controller => 'wiki' do |wiki_routes|
56 wiki_routes.with_options :conditions => {:method => :get} do |wiki_views|
57 wiki_views.connect 'projects/:id/wiki/:page', :action => 'special', :page => /page_index|date_index|export/i
58 wiki_views.connect 'projects/:id/wiki/:page', :action => 'index', :page => nil
59 wiki_views.connect 'projects/:id/wiki/:page/edit', :action => 'edit'
60 wiki_views.connect 'projects/:id/wiki/:page/rename', :action => 'rename'
61 wiki_views.connect 'projects/:id/wiki/:page/history', :action => 'history'
62 wiki_views.connect 'projects/:id/wiki/:page/diff/:version/vs/:version_from', :action => 'diff'
63 wiki_views.connect 'projects/:id/wiki/:page/annotate/:version', :action => 'annotate'
64 end
65
66 wiki_routes.connect 'projects/:id/wiki/:page/:action',
67 :action => /edit|rename|destroy|preview|protect/,
68 :conditions => {:method => :post}
69 end
70
71 map.with_options :controller => 'messages' do |messages_routes|
72 messages_routes.with_options :conditions => {:method => :get} do |messages_views|
73 messages_views.connect 'boards/:board_id/topics/new', :action => 'new'
74 messages_views.connect 'boards/:board_id/topics/:id', :action => 'show'
75 messages_views.connect 'boards/:board_id/topics/:id/edit', :action => 'edit'
76 end
77 messages_routes.with_options :conditions => {:method => :post} do |messages_actions|
78 messages_actions.connect 'boards/:board_id/topics/new', :action => 'new'
79 messages_actions.connect 'boards/:board_id/topics/:id/replies', :action => 'reply'
80 messages_actions.connect 'boards/:board_id/topics/:id/:action', :action => /edit|destroy/
81 end
82 end
83
84 map.with_options :controller => 'boards' do |board_routes|
85 board_routes.with_options :conditions => {:method => :get} do |board_views|
86 board_views.connect 'projects/:project_id/boards', :action => 'index'
87 board_views.connect 'projects/:project_id/boards/new', :action => 'new'
88 board_views.connect 'projects/:project_id/boards/:id', :action => 'show'
89 board_views.connect 'projects/:project_id/boards/:id/edit', :action => 'edit'
90 end
91 board_routes.with_options :conditions => {:method => :post} do |board_actions|
92 board_actions.connect 'projects/:project_id/boards', :action => 'new'
93 board_actions.connect 'projects/:project_id/boards/:id/:action', :action => /edit|destroy/
94 end
95 end
96
97 map.with_options :controller => 'documents' do |document_routes|
98 document_routes.with_options :conditions => {:method => :get} do |document_views|
99 document_views.connect 'projects/:project_id/documents', :action => 'index'
100 document_views.connect 'projects/:project_id/documents/new', :action => 'new'
101 document_views.connect 'documents/:id', :action => 'show'
102 document_views.connect 'documents/:id/edit', :action => 'edit'
103 end
104 document_routes.with_options :conditions => {:method => :post} do |document_actions|
105 document_actions.connect 'projects/:project_id/documents', :action => 'new'
106 document_actions.connect 'documents/:id/:action', :action => /destroy|edit/
107 end
108 end
109
110 map.with_options :controller => 'issues' do |issues_routes|
111 issues_routes.with_options :conditions => {:method => :get} do |issues_views|
112 issues_views.connect 'issues', :action => 'index'
113 issues_views.connect 'issues.:format', :action => 'index'
114 issues_views.connect 'projects/:project_id/issues.:format', :action => 'index'
115 issues_views.connect 'projects/:project_id/issues/new', :action => 'new'
116 issues_views.connect 'projects/:project_id/issues/:copy_from/copy', :action => 'new'
117 issues_views.connect 'issues/:id', :action => 'show'
118 issues_views.connect 'issues/:id.:format', :action => 'show'
119 issues_views.connect 'issues/:id/edit', :action => 'edit'
120 issues_views.connect 'issues/:id/move', :action => 'move'
121 end
122 issues_routes.with_options :conditions => {:method => :post} do |issues_actions|
123 issues_actions.connect 'projects/:project_id/issues', :action => 'new'
124 issues_actions.connect 'issues/:id/quoted', :action => 'reply'
125 issues_actions.connect 'issues/:id/:action',
126 :action => /edit|move|destroy/
127 end
128 end
129
130 map.with_options :controller => 'issue_relations', :conditions => {:method => :post} do |relations|
131 relations.connect 'issues/:issue_id/relations/:id', :action => 'new'
132 relations.connect 'issues/:issue_id/relations/:id/destroy', :action => 'destroy'
133 end
134
135 map.with_options :controller => 'reports', :action => 'issue_report', :conditions => {:method => :get} do |reports|
136 reports.connect 'projects/:id/issues/report'
137 reports.connect 'projects/:id/issues/report/:detail'
138 end
139
140 map.with_options :controller => 'news' do |news_routes|
141 news_routes.with_options :conditions => {:method => :get} do |news_views|
142 news_views.connect 'news', :action => 'index'
143 news_views.connect 'projects/:project_id/news', :action => 'index'
144 news_views.connect 'projects/:project_id/news.:format', :action => 'index'
145 news_views.connect 'news.:format', :action => 'index'
146 news_views.connect 'projects/:project_id/news/new', :action => 'new'
147 news_views.connect 'news/:id', :action => 'show'
148 news_views.connect 'news/:id/edit', :action => 'edit'
149 end
150 news_routes.with_options do |news_actions|
151 news_actions.connect 'projects/:project_id/news', :action => 'new'
152 news_actions.connect 'news/:id/edit', :action => 'edit'
153 news_actions.connect 'news/:id/destroy', :action => 'destroy'
154 end
155 end
156
157 map.connect 'projects/:id/members/new', :controller => 'members', :action => 'new'
158
159 map.with_options :controller => 'users' do |users|
160 users.with_options :conditions => {:method => :get} do |user_views|
161 user_views.connect 'users', :action => 'list'
162 user_views.connect 'users', :action => 'index'
163 user_views.connect 'users/new', :action => 'add'
164 user_views.connect 'users/:id/edit/:tab', :action => 'edit', :tab => nil
165 end
166 users.with_options :conditions => {:method => :post} do |user_actions|
167 user_actions.connect 'users', :action => 'add'
168 user_actions.connect 'users/new', :action => 'add'
169 user_actions.connect 'users/:id/edit', :action => 'edit'
170 user_actions.connect 'users/:id/memberships', :action => 'edit_membership'
171 user_actions.connect 'users/:id/memberships/:membership_id', :action => 'edit_membership'
172 user_actions.connect 'users/:id/memberships/:membership_id/destroy', :action => 'destroy_membership'
173 end
174 end
175
176 map.with_options :controller => 'projects' do |projects|
177 projects.with_options :conditions => {:method => :get} do |project_views|
178 project_views.connect 'projects', :action => 'index'
179 project_views.connect 'projects.:format', :action => 'index'
180 project_views.connect 'projects/new', :action => 'add'
181 project_views.connect 'projects/:id', :action => 'show'
182 project_views.connect 'projects/:id/:action', :action => /roadmap|changelog|destroy|settings/
183 project_views.connect 'projects/:id/files', :action => 'list_files'
184 project_views.connect 'projects/:id/files/new', :action => 'add_file'
185 project_views.connect 'projects/:id/versions/new', :action => 'add_version'
186 project_views.connect 'projects/:id/categories/new', :action => 'add_issue_category'
187 project_views.connect 'projects/:id/settings/:tab', :action => 'settings'
188 end
189
190 projects.with_options :action => 'activity', :conditions => {:method => :get} do |activity|
191 activity.connect 'projects/:id/activity'
192 activity.connect 'projects/:id/activity.:format'
193 activity.connect 'activity'
194 activity.connect 'activity.:format'
195 end
196
197 projects.with_options :conditions => {:method => :post} do |project_actions|
198 project_actions.connect 'projects/new', :action => 'add'
199 project_actions.connect 'projects', :action => 'add'
200 project_actions.connect 'projects/:id/:action', :action => /destroy|archive|unarchive/
201 project_actions.connect 'projects/:id/files/new', :action => 'add_file'
202 project_actions.connect 'projects/:id/versions/new', :action => 'add_version'
203 project_actions.connect 'projects/:id/categories/new', :action => 'add_issue_category'
204 end
205 end
206
207 map.with_options :controller => 'repositories' do |repositories|
208 repositories.with_options :conditions => {:method => :get} do |repository_views|
209 repositories.connect 'projects/:id/repository', :action => 'show'
210 repositories.connect 'projects/:id/repository/edit', :action => 'edit'
211 repositories.connect 'projects/:id/repository/statistics', :action => 'stats'
212 repositories.connect 'projects/:id/repository/revisions', :action => 'revisions'
213 repositories.connect 'projects/:id/repository/revisions.:format', :action => 'revisions'
214 repositories.connect 'projects/:id/repository/revisions/:rev', :action => 'revision'
215 repositories.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff'
216 repositories.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff'
217 repositories.connect 'projects/:id/repository/revisions/:rev/:action/*path'
218 repositories.connect 'projects/:id/repository/:action/*path'
219 end
220
221 repositories.connect 'projects/:id/repository/edit', :action => 'edit', :conditions => {:method => :post}
222 end
223
224 map.connect 'attachments/:id', :controller => 'attachments', :action => 'show', :id => /\d+/
225 map.connect 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/
226 map.connect 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/
227
228
229 #left old routes at the bottom for backwards compat
24 230 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
25 map.connect 'projects/:project_id/news/:action', :controller => 'news'
26 231 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
27 232 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
28 map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog', :project_id => /.+/
29 233 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
30
234 map.connect 'wiki/:id/:page/:action', :page => nil, :controller => 'wiki'
235 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
236 map.connect 'projects/:project_id/news/:action', :controller => 'news'
237 map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog', :project_id => /.+/
31 238 map.with_options :controller => 'repositories' do |omap|
32 239 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
33 240 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
34 241 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
35 242 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
36 243 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
37 omap.repositories_revision 'repositories/revision/:id/:rev', :action => 'revision'
244 omap.connect 'repositories/revision/:id/:rev', :action => 'revision'
38 245 end
39 246
40 map.connect 'attachments/:id', :controller => 'attachments', :action => 'show', :id => /\d+/
41 map.connect 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/
42 map.connect 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/
43
44 247 # Allow downloading Web Service WSDL as a file with an extension
45 248 # instead of a file named 'wsdl'
46 249 map.connect ':controller/service.wsdl', :action => 'wsdl'
47 250
48
49 251 # Install the default route as the lowest priority.
50 252 map.connect ':controller/:action/:id'
51 253 end
@@ -1,124 +1,131
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'admin_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class AdminController; def rescue_action(e) raise e end; end
23 23
24 24 class AdminControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :users, :roles
26 26
27 27 def setup
28 28 @controller = AdminController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 @request.session[:user_id] = 1 # admin
33 33 end
34 34
35 35 def test_index
36 36 get :index
37 37 assert_no_tag :tag => 'div',
38 38 :attributes => { :class => /nodata/ }
39 39 end
40 40
41 def test_projects_routing
42 assert_routing(
43 {:method => :get, :path => '/admin/projects'},
44 :controller => 'admin', :action => 'projects'
45 )
46 end
47
41 48 def test_index_with_no_configuration_data
42 49 delete_configuration_data
43 50 get :index
44 51 assert_tag :tag => 'div',
45 52 :attributes => { :class => /nodata/ }
46 53 end
47 54
48 55 def test_projects
49 56 get :projects
50 57 assert_response :success
51 58 assert_template 'projects'
52 59 assert_not_nil assigns(:projects)
53 60 # active projects only
54 61 assert_nil assigns(:projects).detect {|u| !u.active?}
55 62 end
56 63
57 64 def test_projects_with_name_filter
58 65 get :projects, :name => 'store', :status => ''
59 66 assert_response :success
60 67 assert_template 'projects'
61 68 projects = assigns(:projects)
62 69 assert_not_nil projects
63 70 assert_equal 1, projects.size
64 71 assert_equal 'OnlineStore', projects.first.name
65 72 end
66 73
67 74 def test_load_default_configuration_data
68 75 delete_configuration_data
69 76 post :default_configuration, :lang => 'fr'
70 77 assert IssueStatus.find_by_name('Nouveau')
71 78 end
72 79
73 80 def test_test_email
74 81 get :test_email
75 82 assert_redirected_to '/settings/edit?tab=notifications'
76 83 mail = ActionMailer::Base.deliveries.last
77 84 assert_kind_of TMail::Mail, mail
78 85 user = User.find(1)
79 86 assert_equal [user.mail], mail.bcc
80 87 end
81 88
82 89 def test_no_plugins
83 90 Redmine::Plugin.clear
84 91
85 92 get :plugins
86 93 assert_response :success
87 94 assert_template 'plugins'
88 95 end
89 96
90 97 def test_plugins
91 98 # Register a few plugins
92 99 Redmine::Plugin.register :foo do
93 100 name 'Foo plugin'
94 101 author 'John Smith'
95 102 description 'This is a test plugin'
96 103 version '0.0.1'
97 104 settings :default => {'sample_setting' => 'value', 'foo'=>'bar'}, :partial => 'foo/settings'
98 105 end
99 106 Redmine::Plugin.register :bar do
100 107 end
101 108
102 109 get :plugins
103 110 assert_response :success
104 111 assert_template 'plugins'
105 112
106 113 assert_tag :td, :child => { :tag => 'span', :content => 'Foo plugin' }
107 114 assert_tag :td, :child => { :tag => 'span', :content => 'Bar' }
108 115 end
109 116
110 117 def test_info
111 118 get :info
112 119 assert_response :success
113 120 assert_template 'info'
114 121 end
115 122
116 123 private
117 124
118 125 def delete_configuration_data
119 126 Role.delete_all('builtin = 0')
120 127 Tracker.delete_all
121 128 IssueStatus.delete_all
122 129 Enumeration.delete_all
123 130 end
124 131 end
@@ -1,50 +1,93
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'boards_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class BoardsController; def rescue_action(e) raise e end; end
23 23
24 24 class BoardsControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :users, :members, :roles, :boards, :messages, :enabled_modules
26 26
27 27 def setup
28 28 @controller = BoardsController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 end
33 33
34 def test_index_routing
35 assert_routing(
36 {:method => :get, :path => '/projects/world_domination/boards'},
37 :controller => 'boards', :action => 'index', :project_id => 'world_domination'
38 )
39 end
40
34 41 def test_index
35 42 get :index, :project_id => 1
36 43 assert_response :success
37 44 assert_template 'index'
38 45 assert_not_nil assigns(:boards)
39 46 assert_not_nil assigns(:project)
40 47 end
41 48
49 def test_new_routing
50 assert_routing(
51 {:method => :get, :path => '/projects/world_domination/boards/new'},
52 :controller => 'boards', :action => 'new', :project_id => 'world_domination'
53 )
54 assert_recognizes(
55 {:controller => 'boards', :action => 'new', :project_id => 'world_domination'},
56 {:method => :post, :path => '/projects/world_domination/boards'}
57 )
58 end
59
60 def test_show_routing
61 assert_routing(
62 {:method => :get, :path => '/projects/world_domination/boards/44'},
63 :controller => 'boards', :action => 'show', :id => '44', :project_id => 'world_domination'
64 )
65 end
66
42 67 def test_show
43 68 get :show, :project_id => 1, :id => 1
44 69 assert_response :success
45 70 assert_template 'show'
46 71 assert_not_nil assigns(:board)
47 72 assert_not_nil assigns(:project)
48 73 assert_not_nil assigns(:topics)
49 74 end
75
76 def test_edit_routing
77 assert_routing(
78 {:method => :get, :path => '/projects/world_domination/boards/44/edit'},
79 :controller => 'boards', :action => 'edit', :id => '44', :project_id => 'world_domination'
80 )
81 assert_recognizes(#TODO: use PUT method to board_path, modify form accordingly
82 {:controller => 'boards', :action => 'edit', :id => '44', :project_id => 'world_domination'},
83 {:method => :post, :path => '/projects/world_domination/boards/44/edit'}
84 )
85 end
86
87 def test_destroy_routing
88 assert_routing(#TODO: use DELETE method to board_path, modify form accoringly
89 {:method => :post, :path => '/projects/world_domination/boards/44/destroy'},
90 :controller => 'boards', :action => 'destroy', :id => '44', :project_id => 'world_domination'
91 )
92 end
50 93 end
@@ -1,75 +1,118
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'documents_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class DocumentsController; def rescue_action(e) raise e end; end
23 23
24 24 class DocumentsControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :users, :roles, :members, :enabled_modules, :documents, :enumerations
26 26
27 27 def setup
28 28 @controller = DocumentsController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 end
33 33
34 def test_index_routing
35 assert_routing(
36 {:method => :get, :path => '/projects/567/documents'},
37 :controller => 'documents', :action => 'index', :project_id => '567'
38 )
39 end
40
34 41 def test_index
35 42 # Sets a default category
36 43 e = Enumeration.find_by_name('Technical documentation')
37 44 e.update_attributes(:is_default => true)
38 45
39 46 get :index, :project_id => 'ecookbook'
40 47 assert_response :success
41 48 assert_template 'index'
42 49 assert_not_nil assigns(:grouped)
43 50
44 51 # Default category selected in the new document form
45 52 assert_tag :select, :attributes => {:name => 'document[category_id]'},
46 53 :child => {:tag => 'option', :attributes => {:selected => 'selected'},
47 54 :content => 'Technical documentation'}
48 55 end
49 56
57 def test_new_routing
58 assert_routing(
59 {:method => :get, :path => '/projects/567/documents/new'},
60 :controller => 'documents', :action => 'new', :project_id => '567'
61 )
62 assert_recognizes(
63 {:controller => 'documents', :action => 'new', :project_id => '567'},
64 {:method => :post, :path => '/projects/567/documents'}
65 )
66 end
67
50 68 def test_new_with_one_attachment
51 69 @request.session[:user_id] = 2
52 70 set_tmp_attachments_directory
53 71
54 72 post :new, :project_id => 'ecookbook',
55 73 :document => { :title => 'DocumentsControllerTest#test_post_new',
56 74 :description => 'This is a new document',
57 75 :category_id => 2},
58 76 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
59 77
60 78 assert_redirected_to 'projects/ecookbook/documents'
61 79
62 80 document = Document.find_by_title('DocumentsControllerTest#test_post_new')
63 81 assert_not_nil document
64 82 assert_equal Enumeration.find(2), document.category
65 83 assert_equal 1, document.attachments.size
66 84 assert_equal 'testfile.txt', document.attachments.first.filename
67 85 end
68 86
87 def test_edit_routing
88 assert_routing(
89 {:method => :get, :path => '/documents/22/edit'},
90 :controller => 'documents', :action => 'edit', :id => '22'
91 )
92 assert_recognizes(#TODO: should be using PUT on document URI
93 {:controller => 'documents', :action => 'edit', :id => '567'},
94 {:method => :post, :path => '/documents/567/edit'}
95 )
96 end
97
98 def test_show_routing
99 assert_routing(
100 {:method => :get, :path => '/documents/22'},
101 :controller => 'documents', :action => 'show', :id => '22'
102 )
103 end
104
105 def test_destroy_routing
106 assert_recognizes(#TODO: should be using DELETE on document URI
107 {:controller => 'documents', :action => 'destroy', :id => '567'},
108 {:method => :post, :path => '/documents/567/destroy'}
109 )
110 end
111
69 112 def test_destroy
70 113 @request.session[:user_id] = 2
71 114 post :destroy, :id => 1
72 115 assert_redirected_to 'projects/ecookbook/documents'
73 116 assert_nil Document.find_by_id(1)
74 117 end
75 118 end
@@ -1,822 +1,948
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 def test_index_routing
54 assert_routing(
55 {:method => :get, :path => '/issues'},
56 :controller => 'issues', :action => 'index'
57 )
58 end
59
53 60 def test_index
54 61 get :index
55 62 assert_response :success
56 63 assert_template 'index.rhtml'
57 64 assert_not_nil assigns(:issues)
58 65 assert_nil assigns(:project)
59 66 assert_tag :tag => 'a', :content => /Can't print recipes/
60 67 assert_tag :tag => 'a', :content => /Subproject issue/
61 68 # private projects hidden
62 69 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
63 70 assert_no_tag :tag => 'a', :content => /Issue on project 2/
64 71 end
65 72
66 73 def test_index_should_not_list_issues_when_module_disabled
67 74 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
68 75 get :index
69 76 assert_response :success
70 77 assert_template 'index.rhtml'
71 78 assert_not_nil assigns(:issues)
72 79 assert_nil assigns(:project)
73 80 assert_no_tag :tag => 'a', :content => /Can't print recipes/
74 81 assert_tag :tag => 'a', :content => /Subproject issue/
75 82 end
76 83
84 def test_index_with_project_routing
85 assert_routing(
86 {:method => :get, :path => '/projects/23/issues'},
87 :controller => 'issues', :action => 'index', :project_id => '23'
88 )
89 end
90
91 def test_index_should_not_list_issues_when_module_disabled
92 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
93 get :index
94 assert_response :success
95 assert_template 'index.rhtml'
96 assert_not_nil assigns(:issues)
97 assert_nil assigns(:project)
98 assert_no_tag :tag => 'a', :content => /Can't print recipes/
99 assert_tag :tag => 'a', :content => /Subproject issue/
100 end
101
102 def test_index_with_project_routing
103 assert_routing(
104 {:method => :get, :path => 'projects/23/issues'},
105 :controller => 'issues', :action => 'index', :project_id => '23'
106 )
107 end
108
77 109 def test_index_with_project
78 110 Setting.display_subprojects_issues = 0
79 111 get :index, :project_id => 1
80 112 assert_response :success
81 113 assert_template 'index.rhtml'
82 114 assert_not_nil assigns(:issues)
83 115 assert_tag :tag => 'a', :content => /Can't print recipes/
84 116 assert_no_tag :tag => 'a', :content => /Subproject issue/
85 117 end
86 118
87 119 def test_index_with_project_and_subprojects
88 120 Setting.display_subprojects_issues = 1
89 121 get :index, :project_id => 1
90 122 assert_response :success
91 123 assert_template 'index.rhtml'
92 124 assert_not_nil assigns(:issues)
93 125 assert_tag :tag => 'a', :content => /Can't print recipes/
94 126 assert_tag :tag => 'a', :content => /Subproject issue/
95 127 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
96 128 end
97 129
98 130 def test_index_with_project_and_subprojects_should_show_private_subprojects
99 131 @request.session[:user_id] = 2
100 132 Setting.display_subprojects_issues = 1
101 133 get :index, :project_id => 1
102 134 assert_response :success
103 135 assert_template 'index.rhtml'
104 136 assert_not_nil assigns(:issues)
105 137 assert_tag :tag => 'a', :content => /Can't print recipes/
106 138 assert_tag :tag => 'a', :content => /Subproject issue/
107 139 assert_tag :tag => 'a', :content => /Issue of a private subproject/
108 140 end
109 141
142 def test_index_with_project_routing_formatted
143 assert_routing(
144 {:method => :get, :path => 'projects/23/issues.pdf'},
145 :controller => 'issues', :action => 'index', :project_id => '23', :format => 'pdf'
146 )
147 assert_routing(
148 {:method => :get, :path => 'projects/23/issues.atom'},
149 :controller => 'issues', :action => 'index', :project_id => '23', :format => 'atom'
150 )
151 end
152
110 153 def test_index_with_project_and_filter
111 154 get :index, :project_id => 1, :set_filter => 1
112 155 assert_response :success
113 156 assert_template 'index.rhtml'
114 157 assert_not_nil assigns(:issues)
115 158 end
116 159
117 160 def test_index_csv_with_project
118 161 get :index, :format => 'csv'
119 162 assert_response :success
120 163 assert_not_nil assigns(:issues)
121 164 assert_equal 'text/csv', @response.content_type
122 165
123 166 get :index, :project_id => 1, :format => 'csv'
124 167 assert_response :success
125 168 assert_not_nil assigns(:issues)
126 169 assert_equal 'text/csv', @response.content_type
127 170 end
128 171
172 def test_index_formatted
173 assert_routing(
174 {:method => :get, :path => 'issues.pdf'},
175 :controller => 'issues', :action => 'index', :format => 'pdf'
176 )
177 assert_routing(
178 {:method => :get, :path => 'issues.atom'},
179 :controller => 'issues', :action => 'index', :format => 'atom'
180 )
181 end
182
129 183 def test_index_pdf
130 184 get :index, :format => 'pdf'
131 185 assert_response :success
132 186 assert_not_nil assigns(:issues)
133 187 assert_equal 'application/pdf', @response.content_type
134 188
135 189 get :index, :project_id => 1, :format => 'pdf'
136 190 assert_response :success
137 191 assert_not_nil assigns(:issues)
138 192 assert_equal 'application/pdf', @response.content_type
139 193 end
140 194
141 195 def test_index_sort
142 196 get :index, :sort_key => 'tracker'
143 197 assert_response :success
144 198
145 199 sort_params = @request.session['issuesindex_sort']
146 200 assert sort_params.is_a?(Hash)
147 201 assert_equal 'tracker', sort_params[:key]
148 202 assert_equal 'ASC', sort_params[:order]
149 203 end
150 204
151 205 def test_gantt
152 206 get :gantt, :project_id => 1
153 207 assert_response :success
154 208 assert_template 'gantt.rhtml'
155 209 assert_not_nil assigns(:gantt)
156 210 events = assigns(:gantt).events
157 211 assert_not_nil events
158 212 # Issue with start and due dates
159 213 i = Issue.find(1)
160 214 assert_not_nil i.due_date
161 215 assert events.include?(Issue.find(1))
162 216 # Issue with without due date but targeted to a version with date
163 217 i = Issue.find(2)
164 218 assert_nil i.due_date
165 219 assert events.include?(i)
166 220 end
167 221
168 222 def test_cross_project_gantt
169 223 get :gantt
170 224 assert_response :success
171 225 assert_template 'gantt.rhtml'
172 226 assert_not_nil assigns(:gantt)
173 227 events = assigns(:gantt).events
174 228 assert_not_nil events
175 229 end
176 230
177 231 def test_gantt_export_to_pdf
178 232 get :gantt, :project_id => 1, :format => 'pdf'
179 233 assert_response :success
180 234 assert_equal 'application/pdf', @response.content_type
181 235 assert @response.body.starts_with?('%PDF')
182 236 assert_not_nil assigns(:gantt)
183 237 end
184 238
185 239 def test_cross_project_gantt_export_to_pdf
186 240 get :gantt, :format => 'pdf'
187 241 assert_response :success
188 242 assert_equal 'application/pdf', @response.content_type
189 243 assert @response.body.starts_with?('%PDF')
190 244 assert_not_nil assigns(:gantt)
191 245 end
192 246
193 247 if Object.const_defined?(:Magick)
194 248 def test_gantt_image
195 249 get :gantt, :project_id => 1, :format => 'png'
196 250 assert_response :success
197 251 assert_equal 'image/png', @response.content_type
198 252 end
199 253 else
200 254 puts "RMagick not installed. Skipping tests !!!"
201 255 end
202 256
203 257 def test_calendar
204 258 get :calendar, :project_id => 1
205 259 assert_response :success
206 260 assert_template 'calendar'
207 261 assert_not_nil assigns(:calendar)
208 262 end
209 263
210 264 def test_cross_project_calendar
211 265 get :calendar
212 266 assert_response :success
213 267 assert_template 'calendar'
214 268 assert_not_nil assigns(:calendar)
215 269 end
216 270
217 271 def test_changes
218 272 get :changes, :project_id => 1
219 273 assert_response :success
220 274 assert_not_nil assigns(:journals)
221 275 assert_equal 'application/atom+xml', @response.content_type
222 276 end
223 277
278 def test_show_routing
279 assert_routing(
280 {:method => :get, :path => '/issues/64'},
281 :controller => 'issues', :action => 'show', :id => '64'
282 )
283 end
284
285 def test_show_routing_formatted
286 assert_routing(
287 {:method => :get, :path => '/issues/2332.pdf'},
288 :controller => 'issues', :action => 'show', :id => '2332', :format => 'pdf'
289 )
290 assert_routing(
291 {:method => :get, :path => '/issues/23123.atom'},
292 :controller => 'issues', :action => 'show', :id => '23123', :format => 'atom'
293 )
294 end
295
224 296 def test_show_by_anonymous
225 297 get :show, :id => 1
226 298 assert_response :success
227 299 assert_template 'show.rhtml'
228 300 assert_not_nil assigns(:issue)
229 301 assert_equal Issue.find(1), assigns(:issue)
230 302
231 303 # anonymous role is allowed to add a note
232 304 assert_tag :tag => 'form',
233 305 :descendant => { :tag => 'fieldset',
234 306 :child => { :tag => 'legend',
235 307 :content => /Notes/ } }
236 308 end
237 309
238 310 def test_show_by_manager
239 311 @request.session[:user_id] = 2
240 312 get :show, :id => 1
241 313 assert_response :success
242 314
243 315 assert_tag :tag => 'form',
244 316 :descendant => { :tag => 'fieldset',
245 317 :child => { :tag => 'legend',
246 318 :content => /Change properties/ } },
247 319 :descendant => { :tag => 'fieldset',
248 320 :child => { :tag => 'legend',
249 321 :content => /Log time/ } },
250 322 :descendant => { :tag => 'fieldset',
251 323 :child => { :tag => 'legend',
252 324 :content => /Notes/ } }
253 325 end
254 326
327 def test_new_routing
328 assert_routing(
329 {:method => :get, :path => '/projects/1/issues/new'},
330 :controller => 'issues', :action => 'new', :project_id => '1'
331 )
332 assert_recognizes(
333 {:controller => 'issues', :action => 'new', :project_id => '1'},
334 {:method => :post, :path => '/projects/1/issues'}
335 )
336 end
337
255 338 def test_show_export_to_pdf
256 339 get :show, :id => 3, :format => 'pdf'
257 340 assert_response :success
258 341 assert_equal 'application/pdf', @response.content_type
259 342 assert @response.body.starts_with?('%PDF')
260 343 assert_not_nil assigns(:issue)
261 344 end
262 345
263 346 def test_get_new
264 347 @request.session[:user_id] = 2
265 348 get :new, :project_id => 1, :tracker_id => 1
266 349 assert_response :success
267 350 assert_template 'new'
268 351
269 352 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
270 353 :value => 'Default string' }
271 354 end
272 355
273 356 def test_get_new_without_tracker_id
274 357 @request.session[:user_id] = 2
275 358 get :new, :project_id => 1
276 359 assert_response :success
277 360 assert_template 'new'
278 361
279 362 issue = assigns(:issue)
280 363 assert_not_nil issue
281 364 assert_equal Project.find(1).trackers.first, issue.tracker
282 365 end
283 366
284 367 def test_update_new_form
285 368 @request.session[:user_id] = 2
286 369 xhr :post, :new, :project_id => 1,
287 370 :issue => {:tracker_id => 2,
288 371 :subject => 'This is the test_new issue',
289 372 :description => 'This is the description',
290 373 :priority_id => 5}
291 374 assert_response :success
292 375 assert_template 'new'
293 376 end
294 377
295 378 def test_post_new
296 379 @request.session[:user_id] = 2
297 380 post :new, :project_id => 1,
298 381 :issue => {:tracker_id => 3,
299 382 :subject => 'This is the test_new issue',
300 383 :description => 'This is the description',
301 384 :priority_id => 5,
302 385 :estimated_hours => '',
303 386 :custom_field_values => {'2' => 'Value for field 2'}}
304 assert_redirected_to :controller => 'issues', :action => 'show'
387 assert_redirected_to :action => 'show'
305 388
306 389 issue = Issue.find_by_subject('This is the test_new issue')
307 390 assert_not_nil issue
308 391 assert_equal 2, issue.author_id
309 392 assert_equal 3, issue.tracker_id
310 393 assert_nil issue.estimated_hours
311 394 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
312 395 assert_not_nil v
313 396 assert_equal 'Value for field 2', v.value
314 397 end
315 398
316 399 def test_post_new_and_continue
317 400 @request.session[:user_id] = 2
318 401 post :new, :project_id => 1,
319 402 :issue => {:tracker_id => 3,
320 403 :subject => 'This is first issue',
321 404 :priority_id => 5},
322 405 :continue => ''
323 406 assert_redirected_to :controller => 'issues', :action => 'new', :tracker_id => 3
324 407 end
325 408
326 409 def test_post_new_without_custom_fields_param
327 410 @request.session[:user_id] = 2
328 411 post :new, :project_id => 1,
329 412 :issue => {:tracker_id => 1,
330 413 :subject => 'This is the test_new issue',
331 414 :description => 'This is the description',
332 415 :priority_id => 5}
333 assert_redirected_to :controller => 'issues', :action => 'show'
416 assert_redirected_to :action => 'show'
334 417 end
335 418
336 419 def test_post_new_with_required_custom_field_and_without_custom_fields_param
337 420 field = IssueCustomField.find_by_name('Database')
338 421 field.update_attribute(:is_required, true)
339 422
340 423 @request.session[:user_id] = 2
341 424 post :new, :project_id => 1,
342 425 :issue => {:tracker_id => 1,
343 426 :subject => 'This is the test_new issue',
344 427 :description => 'This is the description',
345 428 :priority_id => 5}
346 429 assert_response :success
347 430 assert_template 'new'
348 431 issue = assigns(:issue)
349 432 assert_not_nil issue
350 433 assert_equal 'activerecord_error_invalid', issue.errors.on(:custom_values)
351 434 end
352 435
353 436 def test_post_new_with_watchers
354 437 @request.session[:user_id] = 2
355 438 ActionMailer::Base.deliveries.clear
356 439
357 440 assert_difference 'Watcher.count', 2 do
358 441 post :new, :project_id => 1,
359 442 :issue => {:tracker_id => 1,
360 443 :subject => 'This is a new issue with watchers',
361 444 :description => 'This is the description',
362 445 :priority_id => 5,
363 446 :watcher_user_ids => ['2', '3']}
364 447 end
365 448 issue = Issue.find_by_subject('This is a new issue with watchers')
366 449 assert_not_nil issue
367 450 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
368 451
369 452 # Watchers added
370 453 assert_equal [2, 3], issue.watcher_user_ids.sort
371 454 assert issue.watched_by?(User.find(3))
372 455 # Watchers notified
373 456 mail = ActionMailer::Base.deliveries.last
374 457 assert_kind_of TMail::Mail, mail
375 458 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
376 459 end
377 460
378 461 def test_post_should_preserve_fields_values_on_validation_failure
379 462 @request.session[:user_id] = 2
380 463 post :new, :project_id => 1,
381 464 :issue => {:tracker_id => 1,
382 465 # empty subject
383 466 :subject => '',
384 467 :description => 'This is a description',
385 468 :priority_id => 6,
386 469 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
387 470 assert_response :success
388 471 assert_template 'new'
389 472
390 473 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
391 474 :content => 'This is a description'
392 475 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
393 476 :child => { :tag => 'option', :attributes => { :selected => 'selected',
394 477 :value => '6' },
395 478 :content => 'High' }
396 479 # Custom fields
397 480 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
398 481 :child => { :tag => 'option', :attributes => { :selected => 'selected',
399 482 :value => 'Oracle' },
400 483 :content => 'Oracle' }
401 484 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
402 485 :value => 'Value for field 2'}
403 486 end
404 487
488 def test_copy_routing
489 assert_routing(
490 {:method => :get, :path => '/projects/world_domination/issues/567/copy'},
491 :controller => 'issues', :action => 'new', :project_id => 'world_domination', :copy_from => '567'
492 )
493 end
494
405 495 def test_copy_issue
406 496 @request.session[:user_id] = 2
407 497 get :new, :project_id => 1, :copy_from => 1
408 498 assert_template 'new'
409 499 assert_not_nil assigns(:issue)
410 500 orig = Issue.find(1)
411 501 assert_equal orig.subject, assigns(:issue).subject
412 502 end
413 503
504 def test_edit_routing
505 assert_routing(
506 {:method => :get, :path => '/issues/1/edit'},
507 :controller => 'issues', :action => 'edit', :id => '1'
508 )
509 assert_recognizes( #TODO: use a PUT on the issue URI isntead, need to adjust form
510 {:controller => 'issues', :action => 'edit', :id => '1'},
511 {:method => :post, :path => '/issues/1/edit'}
512 )
513 end
514
414 515 def test_get_edit
415 516 @request.session[:user_id] = 2
416 517 get :edit, :id => 1
417 518 assert_response :success
418 519 assert_template 'edit'
419 520 assert_not_nil assigns(:issue)
420 521 assert_equal Issue.find(1), assigns(:issue)
421 522 end
422 523
423 524 def test_get_edit_with_params
424 525 @request.session[:user_id] = 2
425 526 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
426 527 assert_response :success
427 528 assert_template 'edit'
428 529
429 530 issue = assigns(:issue)
430 531 assert_not_nil issue
431 532
432 533 assert_equal 5, issue.status_id
433 534 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
434 535 :child => { :tag => 'option',
435 536 :content => 'Closed',
436 537 :attributes => { :selected => 'selected' } }
437 538
438 539 assert_equal 7, issue.priority_id
439 540 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
440 541 :child => { :tag => 'option',
441 542 :content => 'Urgent',
442 543 :attributes => { :selected => 'selected' } }
443 544 end
444 545
546 def test_reply_routing
547 assert_routing(
548 {:method => :post, :path => '/issues/1/quoted'},
549 :controller => 'issues', :action => 'reply', :id => '1'
550 )
551 end
552
445 553 def test_reply_to_issue
446 554 @request.session[:user_id] = 2
447 555 get :reply, :id => 1
448 556 assert_response :success
449 557 assert_select_rjs :show, "update"
450 558 end
451 559
452 560 def test_reply_to_note
453 561 @request.session[:user_id] = 2
454 562 get :reply, :id => 1, :journal_id => 2
455 563 assert_response :success
456 564 assert_select_rjs :show, "update"
457 565 end
458 566
459 567 def test_post_edit_without_custom_fields_param
460 568 @request.session[:user_id] = 2
461 569 ActionMailer::Base.deliveries.clear
462 570
463 571 issue = Issue.find(1)
464 572 assert_equal '125', issue.custom_value_for(2).value
465 573 old_subject = issue.subject
466 574 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
467 575
468 576 assert_difference('Journal.count') do
469 577 assert_difference('JournalDetail.count', 2) do
470 578 post :edit, :id => 1, :issue => {:subject => new_subject,
471 579 :priority_id => '6',
472 580 :category_id => '1' # no change
473 581 }
474 582 end
475 583 end
476 assert_redirected_to 'issues/show/1'
584 assert_redirected_to :action => 'show', :id => '1'
477 585 issue.reload
478 586 assert_equal new_subject, issue.subject
479 587 # Make sure custom fields were not cleared
480 588 assert_equal '125', issue.custom_value_for(2).value
481 589
482 590 mail = ActionMailer::Base.deliveries.last
483 591 assert_kind_of TMail::Mail, mail
484 592 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
485 593 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
486 594 end
487 595
488 596 def test_post_edit_with_custom_field_change
489 597 @request.session[:user_id] = 2
490 598 issue = Issue.find(1)
491 599 assert_equal '125', issue.custom_value_for(2).value
492 600
493 601 assert_difference('Journal.count') do
494 602 assert_difference('JournalDetail.count', 3) do
495 603 post :edit, :id => 1, :issue => {:subject => 'Custom field change',
496 604 :priority_id => '6',
497 605 :category_id => '1', # no change
498 606 :custom_field_values => { '2' => 'New custom value' }
499 607 }
500 608 end
501 609 end
502 assert_redirected_to 'issues/show/1'
610 assert_redirected_to :action => 'show', :id => '1'
503 611 issue.reload
504 612 assert_equal 'New custom value', issue.custom_value_for(2).value
505 613
506 614 mail = ActionMailer::Base.deliveries.last
507 615 assert_kind_of TMail::Mail, mail
508 616 assert mail.body.include?("Searchable field changed from 125 to New custom value")
509 617 end
510 618
511 619 def test_post_edit_with_status_and_assignee_change
512 620 issue = Issue.find(1)
513 621 assert_equal 1, issue.status_id
514 622 @request.session[:user_id] = 2
515 623 assert_difference('TimeEntry.count', 0) do
516 624 post :edit,
517 625 :id => 1,
518 626 :issue => { :status_id => 2, :assigned_to_id => 3 },
519 627 :notes => 'Assigned to dlopper',
520 628 :time_entry => { :hours => '', :comments => '', :activity_id => Enumeration.get_values('ACTI').first }
521 629 end
522 assert_redirected_to 'issues/show/1'
630 assert_redirected_to :action => 'show', :id => '1'
523 631 issue.reload
524 632 assert_equal 2, issue.status_id
525 633 j = issue.journals.find(:first, :order => 'id DESC')
526 634 assert_equal 'Assigned to dlopper', j.notes
527 635 assert_equal 2, j.details.size
528 636
529 637 mail = ActionMailer::Base.deliveries.last
530 638 assert mail.body.include?("Status changed from New to Assigned")
531 639 end
532 640
533 641 def test_post_edit_with_note_only
534 642 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
535 643 # anonymous user
536 644 post :edit,
537 645 :id => 1,
538 646 :notes => notes
539 assert_redirected_to 'issues/show/1'
647 assert_redirected_to :action => 'show', :id => '1'
540 648 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
541 649 assert_equal notes, j.notes
542 650 assert_equal 0, j.details.size
543 651 assert_equal User.anonymous, j.user
544 652
545 653 mail = ActionMailer::Base.deliveries.last
546 654 assert mail.body.include?(notes)
547 655 end
548 656
549 657 def test_post_edit_with_note_and_spent_time
550 658 @request.session[:user_id] = 2
551 659 spent_hours_before = Issue.find(1).spent_hours
552 660 assert_difference('TimeEntry.count') do
553 661 post :edit,
554 662 :id => 1,
555 663 :notes => '2.5 hours added',
556 664 :time_entry => { :hours => '2.5', :comments => '', :activity_id => Enumeration.get_values('ACTI').first }
557 665 end
558 assert_redirected_to 'issues/show/1'
666 assert_redirected_to :action => 'show', :id => '1'
559 667
560 668 issue = Issue.find(1)
561 669
562 670 j = issue.journals.find(:first, :order => 'id DESC')
563 671 assert_equal '2.5 hours added', j.notes
564 672 assert_equal 0, j.details.size
565 673
566 674 t = issue.time_entries.find(:first, :order => 'id DESC')
567 675 assert_not_nil t
568 676 assert_equal 2.5, t.hours
569 677 assert_equal spent_hours_before + 2.5, issue.spent_hours
570 678 end
571 679
572 680 def test_post_edit_with_attachment_only
573 681 set_tmp_attachments_directory
574 682
575 683 # Delete all fixtured journals, a race condition can occur causing the wrong
576 684 # journal to get fetched in the next find.
577 685 Journal.delete_all
578 686
579 687 # anonymous user
580 688 post :edit,
581 689 :id => 1,
582 690 :notes => '',
583 691 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
584 assert_redirected_to 'issues/show/1'
692 assert_redirected_to :action => 'show', :id => '1'
585 693 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
586 694 assert j.notes.blank?
587 695 assert_equal 1, j.details.size
588 696 assert_equal 'testfile.txt', j.details.first.value
589 697 assert_equal User.anonymous, j.user
590 698
591 699 mail = ActionMailer::Base.deliveries.last
592 700 assert mail.body.include?('testfile.txt')
593 701 end
594 702
595 703 def test_post_edit_with_no_change
596 704 issue = Issue.find(1)
597 705 issue.journals.clear
598 706 ActionMailer::Base.deliveries.clear
599 707
600 708 post :edit,
601 709 :id => 1,
602 710 :notes => ''
603 assert_redirected_to 'issues/show/1'
711 assert_redirected_to :action => 'show', :id => '1'
604 712
605 713 issue.reload
606 714 assert issue.journals.empty?
607 715 # No email should be sent
608 716 assert ActionMailer::Base.deliveries.empty?
609 717 end
610 718
611 719 def test_post_edit_with_invalid_spent_time
612 720 @request.session[:user_id] = 2
613 721 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
614 722
615 723 assert_no_difference('Journal.count') do
616 724 post :edit,
617 725 :id => 1,
618 726 :notes => notes,
619 727 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
620 728 end
621 729 assert_response :success
622 730 assert_template 'edit'
623 731
624 732 assert_tag :textarea, :attributes => { :name => 'notes' },
625 733 :content => notes
626 734 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
627 735 end
628 736
629 737 def test_bulk_edit
630 738 @request.session[:user_id] = 2
631 739 # update issues priority
632 740 post :bulk_edit, :ids => [1, 2], :priority_id => 7,
633 741 :assigned_to_id => '',
634 742 :custom_field_values => {'2' => ''},
635 743 :notes => 'Bulk editing'
636 744 assert_response 302
637 745 # check that the issues were updated
638 746 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
639 747
640 748 issue = Issue.find(1)
641 749 journal = issue.journals.find(:first, :order => 'created_on DESC')
642 750 assert_equal '125', issue.custom_value_for(2).value
643 751 assert_equal 'Bulk editing', journal.notes
644 752 assert_equal 1, journal.details.size
645 753 end
646 754
647 755 def test_bulk_edit_custom_field
648 756 @request.session[:user_id] = 2
649 757 # update issues priority
650 758 post :bulk_edit, :ids => [1, 2], :priority_id => '',
651 759 :assigned_to_id => '',
652 760 :custom_field_values => {'2' => '777'},
653 761 :notes => 'Bulk editing custom field'
654 762 assert_response 302
655 763
656 764 issue = Issue.find(1)
657 765 journal = issue.journals.find(:first, :order => 'created_on DESC')
658 766 assert_equal '777', issue.custom_value_for(2).value
659 767 assert_equal 1, journal.details.size
660 768 assert_equal '125', journal.details.first.old_value
661 769 assert_equal '777', journal.details.first.value
662 770 end
663 771
664 772 def test_bulk_unassign
665 773 assert_not_nil Issue.find(2).assigned_to
666 774 @request.session[:user_id] = 2
667 775 # unassign issues
668 776 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
669 777 assert_response 302
670 778 # check that the issues were updated
671 779 assert_nil Issue.find(2).assigned_to
672 780 end
673 781
782 def test_move_routing
783 assert_routing(
784 {:method => :get, :path => '/issues/1/move'},
785 :controller => 'issues', :action => 'move', :id => '1'
786 )
787 assert_recognizes(
788 {:controller => 'issues', :action => 'move', :id => '1'},
789 {:method => :post, :path => '/issues/1/move'}
790 )
791 end
792
674 793 def test_move_one_issue_to_another_project
675 794 @request.session[:user_id] = 1
676 795 post :move, :id => 1, :new_project_id => 2
677 assert_redirected_to 'projects/ecookbook/issues'
796 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
678 797 assert_equal 2, Issue.find(1).project_id
679 798 end
680 799
681 800 def test_bulk_move_to_another_project
682 801 @request.session[:user_id] = 1
683 802 post :move, :ids => [1, 2], :new_project_id => 2
684 assert_redirected_to 'projects/ecookbook/issues'
803 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
685 804 # Issues moved to project 2
686 805 assert_equal 2, Issue.find(1).project_id
687 806 assert_equal 2, Issue.find(2).project_id
688 807 # No tracker change
689 808 assert_equal 1, Issue.find(1).tracker_id
690 809 assert_equal 2, Issue.find(2).tracker_id
691 810 end
692 811
693 812 def test_bulk_move_to_another_tracker
694 813 @request.session[:user_id] = 1
695 814 post :move, :ids => [1, 2], :new_tracker_id => 2
696 assert_redirected_to 'projects/ecookbook/issues'
815 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
697 816 assert_equal 2, Issue.find(1).tracker_id
698 817 assert_equal 2, Issue.find(2).tracker_id
699 818 end
700 819
701 820 def test_bulk_copy_to_another_project
702 821 @request.session[:user_id] = 1
703 822 assert_difference 'Issue.count', 2 do
704 823 assert_no_difference 'Project.find(1).issues.count' do
705 824 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}
706 825 end
707 826 end
708 827 assert_redirected_to 'projects/ecookbook/issues'
709 828 end
710 829
711 830 def test_context_menu_one_issue
712 831 @request.session[:user_id] = 2
713 832 get :context_menu, :ids => [1]
714 833 assert_response :success
715 834 assert_template 'context_menu'
716 835 assert_tag :tag => 'a', :content => 'Edit',
717 :attributes => { :href => '/issues/edit/1',
836 :attributes => { :href => '/issues/1/edit',
718 837 :class => 'icon-edit' }
719 838 assert_tag :tag => 'a', :content => 'Closed',
720 :attributes => { :href => '/issues/edit/1?issue%5Bstatus_id%5D=5',
839 :attributes => { :href => '/issues/1/edit?issue%5Bstatus_id%5D=5',
721 840 :class => '' }
722 841 assert_tag :tag => 'a', :content => 'Immediate',
723 842 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;priority_id=8',
724 843 :class => '' }
725 844 assert_tag :tag => 'a', :content => 'Dave Lopper',
726 845 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1',
727 846 :class => '' }
728 847 assert_tag :tag => 'a', :content => 'Copy',
729 :attributes => { :href => '/projects/ecookbook/issues/new?copy_from=1',
848 :attributes => { :href => '/projects/ecookbook/issues/1/copy',
730 849 :class => 'icon-copy' }
731 850 assert_tag :tag => 'a', :content => 'Move',
732 851 :attributes => { :href => '/issues/move?ids%5B%5D=1',
733 852 :class => 'icon-move' }
734 853 assert_tag :tag => 'a', :content => 'Delete',
735 854 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
736 855 :class => 'icon-del' }
737 856 end
738 857
739 858 def test_context_menu_one_issue_by_anonymous
740 859 get :context_menu, :ids => [1]
741 860 assert_response :success
742 861 assert_template 'context_menu'
743 862 assert_tag :tag => 'a', :content => 'Delete',
744 863 :attributes => { :href => '#',
745 864 :class => 'icon-del disabled' }
746 865 end
747 866
748 867 def test_context_menu_multiple_issues_of_same_project
749 868 @request.session[:user_id] = 2
750 869 get :context_menu, :ids => [1, 2]
751 870 assert_response :success
752 871 assert_template 'context_menu'
753 872 assert_tag :tag => 'a', :content => 'Edit',
754 873 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
755 874 :class => 'icon-edit' }
756 875 assert_tag :tag => 'a', :content => 'Immediate',
757 876 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;priority_id=8',
758 877 :class => '' }
759 878 assert_tag :tag => 'a', :content => 'Dave Lopper',
760 879 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
761 880 :class => '' }
762 881 assert_tag :tag => 'a', :content => 'Move',
763 882 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
764 883 :class => 'icon-move' }
765 884 assert_tag :tag => 'a', :content => 'Delete',
766 885 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
767 886 :class => 'icon-del' }
768 887 end
769 888
770 889 def test_context_menu_multiple_issues_of_different_project
771 890 @request.session[:user_id] = 2
772 891 get :context_menu, :ids => [1, 2, 4]
773 892 assert_response :success
774 893 assert_template 'context_menu'
775 894 assert_tag :tag => 'a', :content => 'Delete',
776 895 :attributes => { :href => '#',
777 896 :class => 'icon-del disabled' }
778 897 end
779 898
899 def test_destroy_routing
900 assert_recognizes( #TODO: use DELETE on issue URI (need to change forms)
901 {:controller => 'issues', :action => 'destroy', :id => '1'},
902 {:method => :post, :path => '/issues/1/destroy'}
903 )
904 end
905
780 906 def test_destroy_issue_with_no_time_entries
781 907 assert_nil TimeEntry.find_by_issue_id(2)
782 908 @request.session[:user_id] = 2
783 909 post :destroy, :id => 2
784 assert_redirected_to 'projects/ecookbook/issues'
910 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
785 911 assert_nil Issue.find_by_id(2)
786 912 end
787 913
788 914 def test_destroy_issues_with_time_entries
789 915 @request.session[:user_id] = 2
790 916 post :destroy, :ids => [1, 3]
791 917 assert_response :success
792 918 assert_template 'destroy'
793 919 assert_not_nil assigns(:hours)
794 920 assert Issue.find_by_id(1) && Issue.find_by_id(3)
795 921 end
796 922
797 923 def test_destroy_issues_and_destroy_time_entries
798 924 @request.session[:user_id] = 2
799 925 post :destroy, :ids => [1, 3], :todo => 'destroy'
800 assert_redirected_to 'projects/ecookbook/issues'
926 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
801 927 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
802 928 assert_nil TimeEntry.find_by_id([1, 2])
803 929 end
804 930
805 931 def test_destroy_issues_and_assign_time_entries_to_project
806 932 @request.session[:user_id] = 2
807 933 post :destroy, :ids => [1, 3], :todo => 'nullify'
808 assert_redirected_to 'projects/ecookbook/issues'
934 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
809 935 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
810 936 assert_nil TimeEntry.find(1).issue_id
811 937 assert_nil TimeEntry.find(2).issue_id
812 938 end
813 939
814 940 def test_destroy_issues_and_reassign_time_entries_to_another_issue
815 941 @request.session[:user_id] = 2
816 942 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
817 assert_redirected_to 'projects/ecookbook/issues'
943 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
818 944 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
819 945 assert_equal 2, TimeEntry.find(1).issue_id
820 946 assert_equal 2, TimeEntry.find(2).issue_id
821 947 end
822 948 end
@@ -1,127 +1,170
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'messages_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class MessagesController; def rescue_action(e) raise e end; end
23 23
24 24 class MessagesControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :users, :members, :roles, :boards, :messages, :enabled_modules
26 26
27 27 def setup
28 28 @controller = MessagesController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 end
33 33
34 def test_show_routing
35 assert_routing(
36 {:method => :get, :path => '/boards/22/topics/2'},
37 :controller => 'messages', :action => 'show', :id => '2', :board_id => '22'
38 )
39 end
40
34 41 def test_show
35 42 get :show, :board_id => 1, :id => 1
36 43 assert_response :success
37 44 assert_template 'show'
38 45 assert_not_nil assigns(:board)
39 46 assert_not_nil assigns(:project)
40 47 assert_not_nil assigns(:topic)
41 48 end
42 49
43 50 def test_show_with_reply_permission
44 51 @request.session[:user_id] = 2
45 52 get :show, :board_id => 1, :id => 1
46 53 assert_response :success
47 54 assert_template 'show'
48 55 assert_tag :div, :attributes => { :id => 'reply' },
49 56 :descendant => { :tag => 'textarea', :attributes => { :id => 'message_content' } }
50 57 end
51 58
52 59 def test_show_message_not_found
53 60 get :show, :board_id => 1, :id => 99999
54 61 assert_response 404
55 62 end
56 63
64 def test_new_routing
65 assert_routing(
66 {:method => :get, :path => '/boards/lala/topics/new'},
67 :controller => 'messages', :action => 'new', :board_id => 'lala'
68 )
69 assert_recognizes(#TODO: POST to collection, need to adjust form accordingly
70 {:controller => 'messages', :action => 'new', :board_id => 'lala'},
71 {:method => :post, :path => '/boards/lala/topics/new'}
72 )
73 end
74
57 75 def test_get_new
58 76 @request.session[:user_id] = 2
59 77 get :new, :board_id => 1
60 78 assert_response :success
61 79 assert_template 'new'
62 80 end
63 81
64 82 def test_post_new
65 83 @request.session[:user_id] = 2
66 84 ActionMailer::Base.deliveries.clear
67 85 Setting.notified_events = ['message_posted']
68 86
69 87 post :new, :board_id => 1,
70 88 :message => { :subject => 'Test created message',
71 89 :content => 'Message body'}
72 90 assert_redirected_to 'messages/show'
73 91 message = Message.find_by_subject('Test created message')
74 92 assert_not_nil message
75 93 assert_equal 'Message body', message.content
76 94 assert_equal 2, message.author_id
77 95 assert_equal 1, message.board_id
78 96
79 97 mail = ActionMailer::Base.deliveries.last
80 98 assert_kind_of TMail::Mail, mail
81 99 assert_equal "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] Test created message", mail.subject
82 100 assert mail.body.include?('Message body')
83 101 # author
84 102 assert mail.bcc.include?('jsmith@somenet.foo')
85 103 # project member
86 104 assert mail.bcc.include?('dlopper@somenet.foo')
87 105 end
88 106
107 def test_edit_routing
108 assert_routing(
109 {:method => :get, :path => '/boards/lala/topics/22/edit'},
110 :controller => 'messages', :action => 'edit', :board_id => 'lala', :id => '22'
111 )
112 assert_recognizes( #TODO: use PUT to topic_path, modify form accordingly
113 {:controller => 'messages', :action => 'edit', :board_id => 'lala', :id => '22'},
114 {:method => :post, :path => '/boards/lala/topics/22/edit'}
115 )
116 end
117
89 118 def test_get_edit
90 119 @request.session[:user_id] = 2
91 120 get :edit, :board_id => 1, :id => 1
92 121 assert_response :success
93 122 assert_template 'edit'
94 123 end
95 124
96 125 def test_post_edit
97 126 @request.session[:user_id] = 2
98 127 post :edit, :board_id => 1, :id => 1,
99 128 :message => { :subject => 'New subject',
100 129 :content => 'New body'}
101 130 assert_redirected_to 'messages/show'
102 131 message = Message.find(1)
103 132 assert_equal 'New subject', message.subject
104 133 assert_equal 'New body', message.content
105 134 end
106 135
136 def test_reply_routing
137 assert_recognizes(
138 {:controller => 'messages', :action => 'reply', :board_id => '22', :id => '555'},
139 {:method => :post, :path => '/boards/22/topics/555/replies'}
140 )
141 end
142
107 143 def test_reply
108 144 @request.session[:user_id] = 2
109 145 post :reply, :board_id => 1, :id => 1, :reply => { :content => 'This is a test reply', :subject => 'Test reply' }
110 146 assert_redirected_to 'messages/show'
111 147 assert Message.find_by_subject('Test reply')
112 148 end
113 149
150 def test_destroy_routing
151 assert_recognizes(#TODO: use DELETE to topic_path, adjust form accordingly
152 {:controller => 'messages', :action => 'destroy', :board_id => '22', :id => '555'},
153 {:method => :post, :path => '/boards/22/topics/555/destroy'}
154 )
155 end
156
114 157 def test_destroy_topic
115 158 @request.session[:user_id] = 2
116 159 post :destroy, :board_id => 1, :id => 1
117 160 assert_redirected_to 'boards/show'
118 161 assert_nil Message.find_by_id(1)
119 162 end
120 163
121 164 def test_quote
122 165 @request.session[:user_id] = 2
123 166 xhr :get, :quote, :board_id => 1, :id => 3
124 167 assert_response :success
125 168 assert_select_rjs :show, 'reply'
126 169 end
127 170 end
@@ -1,147 +1,211
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'news_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class NewsController; def rescue_action(e) raise e end; end
23 23
24 24 class NewsControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :users, :roles, :members, :enabled_modules, :news, :comments
26 26
27 27 def setup
28 28 @controller = NewsController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 end
33 33
34 def test_index_routing
35 assert_routing(
36 {:method => :get, :path => '/news'},
37 :controller => 'news', :action => 'index'
38 )
39 end
40
41 def test_index_routing_formatted
42 assert_routing(
43 {:method => :get, :path => '/news.atom'},
44 :controller => 'news', :action => 'index', :format => 'atom'
45 )
46 end
47
34 48 def test_index
35 49 get :index
36 50 assert_response :success
37 51 assert_template 'index'
38 52 assert_not_nil assigns(:newss)
39 53 assert_nil assigns(:project)
40 54 end
41 55
56 def test_index_with_project_routing
57 assert_routing(
58 {:method => :get, :path => '/projects/567/news'},
59 :controller => 'news', :action => 'index', :project_id => '567'
60 )
61 end
62
63 def test_index_with_project_routing_formatted
64 assert_routing(
65 {:method => :get, :path => '/projects/567/news.atom'},
66 :controller => 'news', :action => 'index', :project_id => '567', :format => 'atom'
67 )
68 end
69
42 70 def test_index_with_project
43 71 get :index, :project_id => 1
44 72 assert_response :success
45 73 assert_template 'index'
46 74 assert_not_nil assigns(:newss)
47 75 end
48 76
77 def test_show_routing
78 assert_routing(
79 {:method => :get, :path => '/news/2'},
80 :controller => 'news', :action => 'show', :id => '2'
81 )
82 end
83
49 84 def test_show
50 85 get :show, :id => 1
51 86 assert_response :success
52 87 assert_template 'show'
53 88 assert_tag :tag => 'h2', :content => /eCookbook first release/
54 89 end
55 90
56 91 def test_show_not_found
57 92 get :show, :id => 999
58 93 assert_response 404
59 94 end
60 95
96 def test_new_routing
97 assert_routing(
98 {:method => :get, :path => '/projects/567/news/new'},
99 :controller => 'news', :action => 'new', :project_id => '567'
100 )
101 assert_recognizes(
102 {:controller => 'news', :action => 'new', :project_id => '567'},
103 {:method => :post, :path => '/projects/567/news'}
104 )
105 end
106
61 107 def test_get_new
62 108 @request.session[:user_id] = 2
63 109 get :new, :project_id => 1
64 110 assert_response :success
65 111 assert_template 'new'
66 112 end
67 113
68 114 def test_post_new
69 115 @request.session[:user_id] = 2
70 116 post :new, :project_id => 1, :news => { :title => 'NewsControllerTest',
71 117 :description => 'This is the description',
72 118 :summary => '' }
73 119 assert_redirected_to 'projects/ecookbook/news'
74 120
75 121 news = News.find_by_title('NewsControllerTest')
76 122 assert_not_nil news
77 123 assert_equal 'This is the description', news.description
78 124 assert_equal User.find(2), news.author
79 125 assert_equal Project.find(1), news.project
80 126 end
81 127
128 def test_edit_routing
129 assert_routing(
130 {:method => :get, :path => '/news/234'},
131 :controller => 'news', :action => 'show', :id => '234'
132 )
133 assert_recognizes(#TODO: PUT to news URI instead, need to modify form
134 {:controller => 'news', :action => 'edit', :id => '567'},
135 {:method => :post, :path => '/news/567/edit'}
136 )
137 end
138
82 139 def test_get_edit
83 140 @request.session[:user_id] = 2
84 141 get :edit, :id => 1
85 142 assert_response :success
86 143 assert_template 'edit'
87 144 end
88 145
89 146 def test_post_edit
90 147 @request.session[:user_id] = 2
91 148 post :edit, :id => 1, :news => { :description => 'Description changed by test_post_edit' }
92 149 assert_redirected_to 'news/show/1'
93 150 news = News.find(1)
94 151 assert_equal 'Description changed by test_post_edit', news.description
95 152 end
96 153
97 154 def test_post_new_with_validation_failure
98 155 @request.session[:user_id] = 2
99 156 post :new, :project_id => 1, :news => { :title => '',
100 157 :description => 'This is the description',
101 158 :summary => '' }
102 159 assert_response :success
103 160 assert_template 'new'
104 161 assert_not_nil assigns(:news)
105 162 assert assigns(:news).new_record?
106 163 assert_tag :tag => 'div', :attributes => { :id => 'errorExplanation' },
107 164 :content => /1 error/
108 165 end
109 166
110 167 def test_add_comment
111 168 @request.session[:user_id] = 2
112 169 post :add_comment, :id => 1, :comment => { :comments => 'This is a NewsControllerTest comment' }
113 170 assert_redirected_to 'news/show/1'
114 171
115 172 comment = News.find(1).comments.find(:first, :order => 'created_on DESC')
116 173 assert_not_nil comment
117 174 assert_equal 'This is a NewsControllerTest comment', comment.comments
118 175 assert_equal User.find(2), comment.author
119 176 end
120 177
121 178 def test_destroy_comment
122 179 comments_count = News.find(1).comments.size
123 180 @request.session[:user_id] = 2
124 181 post :destroy_comment, :id => 1, :comment_id => 2
125 182 assert_redirected_to 'news/show/1'
126 183 assert_nil Comment.find_by_id(2)
127 184 assert_equal comments_count - 1, News.find(1).comments.size
128 185 end
129 186
187 def test_destroy_routing
188 assert_recognizes(#TODO: should use DELETE to news URI, need to change form
189 {:controller => 'news', :action => 'destroy', :id => '567'},
190 {:method => :post, :path => '/news/567/destroy'}
191 )
192 end
193
130 194 def test_destroy
131 195 @request.session[:user_id] = 2
132 196 post :destroy, :id => 1
133 197 assert_redirected_to 'projects/ecookbook/news'
134 198 assert_nil News.find_by_id(1)
135 199 end
136 200
137 201 def test_preview
138 202 get :preview, :project_id => 1,
139 203 :news => {:title => '',
140 204 :description => 'News description',
141 205 :summary => ''}
142 206 assert_response :success
143 207 assert_template 'common/_preview'
144 208 assert_tag :tag => 'fieldset', :attributes => { :class => 'preview' },
145 209 :content => /News description/
146 210 end
147 211 end
@@ -1,364 +1,517
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 'projects_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class ProjectsController; def rescue_action(e) raise e end; end
23 23
24 24 class ProjectsControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :versions, :users, :roles, :members, :issues, :journals, :journal_details,
26 26 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages,
27 27 :attachments
28 28
29 29 def setup
30 30 @controller = ProjectsController.new
31 31 @request = ActionController::TestRequest.new
32 32 @response = ActionController::TestResponse.new
33 33 @request.session[:user_id] = nil
34 34 Setting.default_language = 'en'
35 35 end
36 36
37 def test_index_routing
38 assert_routing(
39 {:method => :get, :path => '/projects'},
40 :controller => 'projects', :action => 'index'
41 )
42 end
43
37 44 def test_index
38 45 get :index
39 46 assert_response :success
40 47 assert_template 'index'
41 48 assert_not_nil assigns(:projects)
42 49
43 50 assert_tag :ul, :child => {:tag => 'li',
44 51 :descendant => {:tag => 'a', :content => 'eCookbook'},
45 52 :child => { :tag => 'ul',
46 53 :descendant => { :tag => 'a',
47 54 :content => 'Child of private child'
48 55 }
49 56 }
50 57 }
51 58
52 59 assert_no_tag :a, :content => /Private child of eCookbook/
53 60 end
54 61
62 def test_index_atom_routing
63 assert_routing(
64 {:method => :get, :path => '/projects.atom'},
65 :controller => 'projects', :action => 'index', :format => 'atom'
66 )
67 end
68
55 69 def test_index_atom
56 70 get :index, :format => 'atom'
57 71 assert_response :success
58 72 assert_template 'common/feed.atom.rxml'
59 73 assert_select 'feed>title', :text => 'Redmine: Latest projects'
60 74 assert_select 'feed>entry', :count => Project.count(:conditions => Project.visible_by(User.current))
61 75 end
62 76
77 def test_add_routing
78 assert_routing(
79 {:method => :get, :path => '/projects/new'},
80 :controller => 'projects', :action => 'add'
81 )
82 assert_recognizes(
83 {:controller => 'projects', :action => 'add'},
84 {:method => :post, :path => '/projects/new'}
85 )
86 assert_recognizes(
87 {:controller => 'projects', :action => 'add'},
88 {:method => :post, :path => '/projects'}
89 )
90 end
91
92 def test_show_routing
93 assert_routing(
94 {:method => :get, :path => '/projects/test'},
95 :controller => 'projects', :action => 'show', :id => 'test'
96 )
97 end
98
63 99 def test_show_by_id
64 100 get :show, :id => 1
65 101 assert_response :success
66 102 assert_template 'show'
67 103 assert_not_nil assigns(:project)
68 104 end
69 105
70 106 def test_show_by_identifier
71 107 get :show, :id => 'ecookbook'
72 108 assert_response :success
73 109 assert_template 'show'
74 110 assert_not_nil assigns(:project)
75 111 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
76 112 end
77 113
78 114 def test_private_subprojects_hidden
79 115 get :show, :id => 'ecookbook'
80 116 assert_response :success
81 117 assert_template 'show'
82 118 assert_no_tag :tag => 'a', :content => /Private child/
83 119 end
84 120
85 121 def test_private_subprojects_visible
86 122 @request.session[:user_id] = 2 # manager who is a member of the private subproject
87 123 get :show, :id => 'ecookbook'
88 124 assert_response :success
89 125 assert_template 'show'
90 126 assert_tag :tag => 'a', :content => /Private child/
91 127 end
92 128
129 def test_settings_routing
130 assert_routing(
131 {:method => :get, :path => '/projects/4223/settings'},
132 :controller => 'projects', :action => 'settings', :id => '4223'
133 )
134 assert_routing(
135 {:method => :get, :path => '/projects/4223/settings/members'},
136 :controller => 'projects', :action => 'settings', :id => '4223', :tab => 'members'
137 )
138 end
139
93 140 def test_settings
94 141 @request.session[:user_id] = 2 # manager
95 142 get :settings, :id => 1
96 143 assert_response :success
97 144 assert_template 'settings'
98 145 end
99 146
100 147 def test_edit
101 148 @request.session[:user_id] = 2 # manager
102 149 post :edit, :id => 1, :project => {:name => 'Test changed name',
103 150 :issue_custom_field_ids => ['']}
104 151 assert_redirected_to 'projects/settings/ecookbook'
105 152 project = Project.find(1)
106 153 assert_equal 'Test changed name', project.name
107 154 end
108 155
156 def test_add_version_routing
157 assert_routing(
158 {:method => :get, :path => 'projects/64/versions/new'},
159 :controller => 'projects', :action => 'add_version', :id => '64'
160 )
161 assert_routing(
162 #TODO: use PUT
163 {:method => :post, :path => 'projects/64/versions/new'},
164 :controller => 'projects', :action => 'add_version', :id => '64'
165 )
166 end
167
168 def test_add_issue_category_routing
169 assert_routing(
170 {:method => :get, :path => 'projects/test/categories/new'},
171 :controller => 'projects', :action => 'add_issue_category', :id => 'test'
172 )
173 assert_routing(
174 #TODO: use PUT and update form
175 {:method => :post, :path => 'projects/64/categories/new'},
176 :controller => 'projects', :action => 'add_issue_category', :id => '64'
177 )
178 end
179
180 def test_destroy_routing
181 assert_routing(
182 {:method => :get, :path => '/projects/567/destroy'},
183 :controller => 'projects', :action => 'destroy', :id => '567'
184 )
185 assert_routing(
186 #TODO: use DELETE and update form
187 {:method => :post, :path => 'projects/64/destroy'},
188 :controller => 'projects', :action => 'destroy', :id => '64'
189 )
190 end
191
109 192 def test_get_destroy
110 193 @request.session[:user_id] = 1 # admin
111 194 get :destroy, :id => 1
112 195 assert_response :success
113 196 assert_template 'destroy'
114 197 assert_not_nil Project.find_by_id(1)
115 198 end
116 199
117 200 def test_post_destroy
118 201 @request.session[:user_id] = 1 # admin
119 202 post :destroy, :id => 1, :confirm => 1
120 203 assert_redirected_to 'admin/projects'
121 204 assert_nil Project.find_by_id(1)
122 205 end
123 206
124 207 def test_add_file
125 208 set_tmp_attachments_directory
126 209 @request.session[:user_id] = 2
127 210 Setting.notified_events = ['file_added']
128 211 ActionMailer::Base.deliveries.clear
129 212
130 213 assert_difference 'Attachment.count' do
131 214 post :add_file, :id => 1, :version_id => '',
132 215 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
133 216 end
134 217 assert_redirected_to 'projects/list_files/ecookbook'
135 218 a = Attachment.find(:first, :order => 'created_on DESC')
136 219 assert_equal 'testfile.txt', a.filename
137 220 assert_equal Project.find(1), a.container
138 221
139 222 mail = ActionMailer::Base.deliveries.last
140 223 assert_kind_of TMail::Mail, mail
141 224 assert_equal "[eCookbook] New file", mail.subject
142 225 assert mail.body.include?('testfile.txt')
143 226 end
144 227
228 def test_add_file_routing
229 assert_routing(
230 {:method => :get, :path => '/projects/33/files/new'},
231 :controller => 'projects', :action => 'add_file', :id => '33'
232 )
233 assert_routing(
234 {:method => :post, :path => '/projects/33/files/new'},
235 :controller => 'projects', :action => 'add_file', :id => '33'
236 )
237 end
238
145 239 def test_add_version_file
146 240 set_tmp_attachments_directory
147 241 @request.session[:user_id] = 2
148 242 Setting.notified_events = ['file_added']
149 243
150 244 assert_difference 'Attachment.count' do
151 245 post :add_file, :id => 1, :version_id => '2',
152 246 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
153 247 end
154 248 assert_redirected_to 'projects/list_files/ecookbook'
155 249 a = Attachment.find(:first, :order => 'created_on DESC')
156 250 assert_equal 'testfile.txt', a.filename
157 251 assert_equal Version.find(2), a.container
158 252 end
159 253
160 254 def test_list_files
161 255 get :list_files, :id => 1
162 256 assert_response :success
163 257 assert_template 'list_files'
164 258 assert_not_nil assigns(:containers)
165 259
166 260 # file attached to the project
167 261 assert_tag :a, :content => 'project_file.zip',
168 262 :attributes => { :href => '/attachments/download/8/project_file.zip' }
169 263
170 264 # file attached to a project's version
171 265 assert_tag :a, :content => 'version_file.zip',
172 266 :attributes => { :href => '/attachments/download/9/version_file.zip' }
173 267 end
174 268
269 def test_list_files_routing
270 assert_routing(
271 {:method => :get, :path => '/projects/33/files'},
272 :controller => 'projects', :action => 'list_files', :id => '33'
273 )
274 end
275
276 def test_changelog_routing
277 assert_routing(
278 {:method => :get, :path => '/projects/44/changelog'},
279 :controller => 'projects', :action => 'changelog', :id => '44'
280 )
281 end
282
175 283 def test_changelog
176 284 get :changelog, :id => 1
177 285 assert_response :success
178 286 assert_template 'changelog'
179 287 assert_not_nil assigns(:versions)
180 288 end
181 289
290 def test_roadmap_routing
291 assert_routing(
292 {:method => :get, :path => 'projects/33/roadmap'},
293 :controller => 'projects', :action => 'roadmap', :id => '33'
294 )
295 end
296
182 297 def test_roadmap
183 298 get :roadmap, :id => 1
184 299 assert_response :success
185 300 assert_template 'roadmap'
186 301 assert_not_nil assigns(:versions)
187 302 # Version with no date set appears
188 303 assert assigns(:versions).include?(Version.find(3))
189 304 # Completed version doesn't appear
190 305 assert !assigns(:versions).include?(Version.find(1))
191 306 end
192 307
193 308 def test_roadmap_with_completed_versions
194 309 get :roadmap, :id => 1, :completed => 1
195 310 assert_response :success
196 311 assert_template 'roadmap'
197 312 assert_not_nil assigns(:versions)
198 313 # Version with no date set appears
199 314 assert assigns(:versions).include?(Version.find(3))
200 315 # Completed version appears
201 316 assert assigns(:versions).include?(Version.find(1))
202 317 end
203 318
319 def test_project_activity_routing
320 assert_routing(
321 {:method => :get, :path => '/projects/1/activity'},
322 :controller => 'projects', :action => 'activity', :id => '1'
323 )
324 end
325
326 def test_project_activity_atom_routing
327 assert_routing(
328 {:method => :get, :path => '/projects/1/activity.atom'},
329 :controller => 'projects', :action => 'activity', :id => '1', :format => 'atom'
330 )
331 end
332
204 333 def test_project_activity
205 334 get :activity, :id => 1, :with_subprojects => 0
206 335 assert_response :success
207 336 assert_template 'activity'
208 337 assert_not_nil assigns(:events_by_day)
209 338
210 339 assert_tag :tag => "h3",
211 340 :content => /#{2.days.ago.to_date.day}/,
212 341 :sibling => { :tag => "dl",
213 342 :child => { :tag => "dt",
214 343 :attributes => { :class => /issue-edit/ },
215 344 :child => { :tag => "a",
216 345 :content => /(#{IssueStatus.find(2).name})/,
217 346 }
218 347 }
219 348 }
220 349 end
221 350
222 351 def test_previous_project_activity
223 352 get :activity, :id => 1, :from => 3.days.ago.to_date
224 353 assert_response :success
225 354 assert_template 'activity'
226 355 assert_not_nil assigns(:events_by_day)
227 356
228 357 assert_tag :tag => "h3",
229 358 :content => /#{3.day.ago.to_date.day}/,
230 359 :sibling => { :tag => "dl",
231 360 :child => { :tag => "dt",
232 361 :attributes => { :class => /issue/ },
233 362 :child => { :tag => "a",
234 363 :content => /#{Issue.find(1).subject}/,
235 364 }
236 365 }
237 366 }
238 367 end
239 368
369 def test_global_activity_routing
370 assert_routing({:method => :get, :path => '/activity'}, :controller => 'projects', :action => 'activity')
371 end
372
240 373 def test_global_activity
241 374 get :activity
242 375 assert_response :success
243 376 assert_template 'activity'
244 377 assert_not_nil assigns(:events_by_day)
245 378
246 379 assert_tag :tag => "h3",
247 380 :content => /#{5.day.ago.to_date.day}/,
248 381 :sibling => { :tag => "dl",
249 382 :child => { :tag => "dt",
250 383 :attributes => { :class => /issue/ },
251 384 :child => { :tag => "a",
252 385 :content => /#{Issue.find(5).subject}/,
253 386 }
254 387 }
255 388 }
256 389 end
257 390
258 391 def test_user_activity
259 392 get :activity, :user_id => 2
260 393 assert_response :success
261 394 assert_template 'activity'
262 395 assert_not_nil assigns(:events_by_day)
263 396
264 397 assert_tag :tag => "h3",
265 398 :content => /#{3.day.ago.to_date.day}/,
266 399 :sibling => { :tag => "dl",
267 400 :child => { :tag => "dt",
268 401 :attributes => { :class => /issue/ },
269 402 :child => { :tag => "a",
270 403 :content => /#{Issue.find(1).subject}/,
271 404 }
272 405 }
273 406 }
274 407 end
275 408
409 def test_global_activity_atom_routing
410 assert_routing({:method => :get, :path => '/activity.atom'}, :controller => 'projects', :action => 'activity', :format => 'atom')
411 end
412
276 413 def test_activity_atom_feed
277 414 get :activity, :format => 'atom'
278 415 assert_response :success
279 416 assert_template 'common/feed.atom.rxml'
280 417 end
281 418
419 def test_archive_routing
420 assert_routing(
421 #TODO: use PUT to project path and modify form
422 {:method => :post, :path => 'projects/64/archive'},
423 :controller => 'projects', :action => 'archive', :id => '64'
424 )
425 end
426
282 427 def test_archive
283 428 @request.session[:user_id] = 1 # admin
284 429 post :archive, :id => 1
285 430 assert_redirected_to 'admin/projects'
286 431 assert !Project.find(1).active?
287 432 end
288 433
434 def test_unarchive_routing
435 assert_routing(
436 #TODO: use PUT to project path and modify form
437 {:method => :post, :path => '/projects/567/unarchive'},
438 :controller => 'projects', :action => 'unarchive', :id => '567'
439 )
440 end
441
289 442 def test_unarchive
290 443 @request.session[:user_id] = 1 # admin
291 444 Project.find(1).archive
292 445 post :unarchive, :id => 1
293 446 assert_redirected_to 'admin/projects'
294 447 assert Project.find(1).active?
295 448 end
296 449
297 450 def test_jump_should_redirect_to_active_tab
298 451 get :show, :id => 1, :jump => 'issues'
299 452 assert_redirected_to 'projects/ecookbook/issues'
300 453 end
301 454
302 455 def test_jump_should_not_redirect_to_inactive_tab
303 456 get :show, :id => 3, :jump => 'documents'
304 457 assert_response :success
305 458 assert_template 'show'
306 459 end
307 460
308 461 def test_jump_should_not_redirect_to_unknown_tab
309 462 get :show, :id => 3, :jump => 'foobar'
310 463 assert_response :success
311 464 assert_template 'show'
312 465 end
313 466
314 467 def test_project_menu
315 468 assert_no_difference 'Redmine::MenuManager.items(:project_menu).size' do
316 469 Redmine::MenuManager.map :project_menu do |menu|
317 470 menu.push :foo, { :controller => 'projects', :action => 'show' }, :cation => 'Foo'
318 471 menu.push :bar, { :controller => 'projects', :action => 'show' }, :before => :activity
319 472 menu.push :hello, { :controller => 'projects', :action => 'show' }, :caption => Proc.new {|p| p.name.upcase }, :after => :bar
320 473 end
321 474
322 475 get :show, :id => 1
323 476 assert_tag :div, :attributes => { :id => 'main-menu' },
324 477 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'Foo',
325 478 :attributes => { :class => 'foo' } } }
326 479
327 480 assert_tag :div, :attributes => { :id => 'main-menu' },
328 481 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'Bar',
329 482 :attributes => { :class => 'bar' } },
330 483 :before => { :tag => 'li', :child => { :tag => 'a', :content => 'ECOOKBOOK' } } }
331 484
332 485 assert_tag :div, :attributes => { :id => 'main-menu' },
333 486 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'ECOOKBOOK',
334 487 :attributes => { :class => 'hello' } },
335 488 :before => { :tag => 'li', :child => { :tag => 'a', :content => 'Activity' } } }
336 489
337 490 # Remove the menu items
338 491 Redmine::MenuManager.map :project_menu do |menu|
339 492 menu.delete :foo
340 493 menu.delete :bar
341 494 menu.delete :hello
342 495 end
343 496 end
344 497 end
345 498
346 499 # A hook that is manually registered later
347 500 class ProjectBasedTemplate < Redmine::Hook::ViewListener
348 501 def view_layouts_base_html_head(context)
349 502 # Adds a project stylesheet
350 503 stylesheet_link_tag(context[:project].identifier) if context[:project]
351 504 end
352 505 end
353 506 # Don't use this hook now
354 507 Redmine::Hook.clear_listeners
355 508
356 509 def test_hook_response
357 510 Redmine::Hook.add_listener(ProjectBasedTemplate)
358 511 get :show, :id => 1
359 512 assert_tag :tag => 'link', :attributes => {:href => '/stylesheets/ecookbook.css'},
360 513 :parent => {:tag => 'head'}
361 514
362 515 Redmine::Hook.clear_listeners
363 516 end
364 517 end
@@ -1,98 +1,207
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'repositories_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class RepositoriesController; def rescue_action(e) raise e end; end
23 23
24 24 class RepositoriesControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :users, :roles, :members, :repositories, :issues, :issue_statuses, :changesets, :changes, :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers
26 26
27 27 def setup
28 28 @controller = RepositoriesController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 end
33 33
34 def test_show_routing
35 assert_routing(
36 {:method => :get, :path => '/projects/redmine/repository'},
37 :controller => 'repositories', :action => 'show', :id => 'redmine'
38 )
39 end
40
41 def test_edit_routing
42 assert_routing(
43 {:method => :get, :path => '/projects/world_domination/repository/edit'},
44 :controller => 'repositories', :action => 'edit', :id => 'world_domination'
45 )
46 assert_routing(
47 {:method => :post, :path => '/projects/world_domination/repository/edit'},
48 :controller => 'repositories', :action => 'edit', :id => 'world_domination'
49 )
50 end
51
52 def test_revisions_routing
53 assert_routing(
54 {:method => :get, :path => '/projects/redmine/repository/revisions'},
55 :controller => 'repositories', :action => 'revisions', :id => 'redmine'
56 )
57 end
58
59 def test_revisions_atom_routing
60 assert_routing(
61 {:method => :get, :path => '/projects/redmine/repository/revisions.atom'},
62 :controller => 'repositories', :action => 'revisions', :id => 'redmine', :format => 'atom'
63 )
64 end
65
34 66 def test_revisions
35 67 get :revisions, :id => 1
36 68 assert_response :success
37 69 assert_template 'revisions'
38 70 assert_not_nil assigns(:changesets)
39 71 end
40 72
73 def test_revision_routing
74 assert_routing(
75 {:method => :get, :path => '/projects/restmine/repository/revisions/2457'},
76 :controller => 'repositories', :action => 'revision', :id => 'restmine', :rev => '2457'
77 )
78 end
79
41 80 def test_revision_with_before_nil_and_afer_normal
42 81 get :revision, {:id => 1, :rev => 1}
43 82 assert_response :success
44 83 assert_template 'revision'
45 84 assert_no_tag :tag => "div", :attributes => { :class => "contextual" },
46 :child => { :tag => "a", :attributes => { :href => '/repositories/revision/ecookbook/0'}
85 :child => { :tag => "a", :attributes => { :href => '/projects/ecookbook/repository/revisions/0'}
47 86 }
48 87 assert_tag :tag => "div", :attributes => { :class => "contextual" },
49 :child => { :tag => "a", :attributes => { :href => '/repositories/revision/ecookbook/2'}
88 :child => { :tag => "a", :attributes => { :href => '/projects/ecookbook/repository/revisions/2'}
50 89 }
51 90 end
52 91
92 def test_diff_routing
93 assert_routing(
94 {:method => :get, :path => '/projects/restmine/repository/revisions/2457/diff'},
95 :controller => 'repositories', :action => 'diff', :id => 'restmine', :rev => '2457'
96 )
97 end
98
99 def test_unified_diff_routing
100 assert_routing(
101 {:method => :get, :path => '/projects/restmine/repository/revisions/2457/diff.diff'},
102 :controller => 'repositories', :action => 'diff', :id => 'restmine', :rev => '2457', :format => 'diff'
103 )
104 end
105
106 def test_diff_path_routing
107 assert_routing(
108 {:method => :get, :path => '/projects/restmine/repository/diff/path/to/file.c'},
109 :controller => 'repositories', :action => 'diff', :id => 'restmine', :path => %w[path to file.c]
110 )
111 end
112
113 def test_diff_path_routing_with_revision
114 assert_routing(
115 {:method => :get, :path => '/projects/restmine/repository/revisions/2/diff/path/to/file.c'},
116 :controller => 'repositories', :action => 'diff', :id => 'restmine', :path => %w[path to file.c], :rev => '2'
117 )
118 end
119
120 def test_browse_routing
121 assert_routing(
122 {:method => :get, :path => '/projects/restmine/repository/browse/path/to/dir'},
123 :controller => 'repositories', :action => 'browse', :id => 'restmine', :path => %w[path to dir]
124 )
125 end
126
127 def test_entry_routing
128 assert_routing(
129 {:method => :get, :path => '/projects/restmine/repository/entry/path/to/file.c'},
130 :controller => 'repositories', :action => 'entry', :id => 'restmine', :path => %w[path to file.c]
131 )
132 end
133
134 def test_entry_routing_with_revision
135 assert_routing(
136 {:method => :get, :path => '/projects/restmine/repository/revisions/2/entry/path/to/file.c'},
137 :controller => 'repositories', :action => 'entry', :id => 'restmine', :path => %w[path to file.c], :rev => '2'
138 )
139 end
140
141 def test_annotate_routing
142 assert_routing(
143 {:method => :get, :path => '/projects/restmine/repository/annotate/path/to/file.c'},
144 :controller => 'repositories', :action => 'annotate', :id => 'restmine', :path => %w[path to file.c]
145 )
146 end
147
148 def test_changesrouting
149 assert_routing(
150 {:method => :get, :path => '/projects/restmine/repository/changes/path/to/file.c'},
151 :controller => 'repositories', :action => 'changes', :id => 'restmine', :path => %w[path to file.c]
152 )
153 end
154
155 def test_statistics_routing
156 assert_routing(
157 {:method => :get, :path => '/projects/restmine/repository/statistics'},
158 :controller => 'repositories', :action => 'stats', :id => 'restmine'
159 )
160 end
161
53 162 def test_graph_commits_per_month
54 163 get :graph, :id => 1, :graph => 'commits_per_month'
55 164 assert_response :success
56 165 assert_equal 'image/svg+xml', @response.content_type
57 166 end
58 167
59 168 def test_graph_commits_per_author
60 169 get :graph, :id => 1, :graph => 'commits_per_author'
61 170 assert_response :success
62 171 assert_equal 'image/svg+xml', @response.content_type
63 172 end
64 173
65 174 def test_committers
66 175 @request.session[:user_id] = 2
67 176 # add a commit with an unknown user
68 177 Changeset.create!(:repository => Project.find(1).repository, :committer => 'foo', :committed_on => Time.now, :revision => 100, :comments => 'Committed by foo.')
69 178
70 179 get :committers, :id => 1
71 180 assert_response :success
72 181 assert_template 'committers'
73 182
74 183 assert_tag :td, :content => 'dlopper',
75 184 :sibling => { :tag => 'td',
76 185 :child => { :tag => 'select', :attributes => { :name => %r{^committers\[\d+\]\[\]$} },
77 186 :child => { :tag => 'option', :content => 'Dave Lopper',
78 187 :attributes => { :value => '3', :selected => 'selected' }}}}
79 188 assert_tag :td, :content => 'foo',
80 189 :sibling => { :tag => 'td',
81 190 :child => { :tag => 'select', :attributes => { :name => %r{^committers\[\d+\]\[\]$} }}}
82 191 assert_no_tag :td, :content => 'foo',
83 192 :sibling => { :tag => 'td',
84 193 :descendant => { :tag => 'option', :attributes => { :selected => 'selected' }}}
85 194 end
86 195
87 196 def test_map_committers
88 197 @request.session[:user_id] = 2
89 198 # add a commit with an unknown user
90 199 c = Changeset.create!(:repository => Project.find(1).repository, :committer => 'foo', :committed_on => Time.now, :revision => 100, :comments => 'Committed by foo.')
91 200
92 201 assert_no_difference "Changeset.count(:conditions => 'user_id = 3')" do
93 202 post :committers, :id => 1, :committers => { '0' => ['foo', '2'], '1' => ['dlopper', '3']}
94 203 assert_redirected_to '/repositories/committers/ecookbook'
95 204 assert_equal User.find(2), c.reload.user
96 205 end
97 206 end
98 207 end
@@ -1,181 +1,181
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 'repositories_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class RepositoriesController; def rescue_action(e) raise e end; end
23 23
24 24 class RepositoriesSubversionControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :users, :roles, :members, :enabled_modules,
26 26 :repositories, :issues, :issue_statuses, :changesets, :changes,
27 27 :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers
28 28
29 29 # No '..' in the repository path for svn
30 30 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/subversion_repository'
31 31
32 32 def setup
33 33 @controller = RepositoriesController.new
34 34 @request = ActionController::TestRequest.new
35 35 @response = ActionController::TestResponse.new
36 36 Setting.default_language = 'en'
37 37 User.current = nil
38 38 end
39 39
40 40 if File.directory?(REPOSITORY_PATH)
41 41 def test_show
42 42 get :show, :id => 1
43 43 assert_response :success
44 44 assert_template 'show'
45 45 assert_not_nil assigns(:entries)
46 46 assert_not_nil assigns(:changesets)
47 47 end
48 48
49 49 def test_browse_root
50 50 get :browse, :id => 1
51 51 assert_response :success
52 52 assert_template 'browse'
53 53 assert_not_nil assigns(:entries)
54 54 entry = assigns(:entries).detect {|e| e.name == 'subversion_test'}
55 55 assert_equal 'dir', entry.kind
56 56 end
57 57
58 58 def test_browse_directory
59 59 get :browse, :id => 1, :path => ['subversion_test']
60 60 assert_response :success
61 61 assert_template 'browse'
62 62 assert_not_nil assigns(:entries)
63 63 assert_equal ['folder', '.project', 'helloworld.c', 'textfile.txt'], assigns(:entries).collect(&:name)
64 64 entry = assigns(:entries).detect {|e| e.name == 'helloworld.c'}
65 65 assert_equal 'file', entry.kind
66 66 assert_equal 'subversion_test/helloworld.c', entry.path
67 67 end
68 68
69 69 def test_browse_at_given_revision
70 70 get :browse, :id => 1, :path => ['subversion_test'], :rev => 4
71 71 assert_response :success
72 72 assert_template 'browse'
73 73 assert_not_nil assigns(:entries)
74 74 assert_equal ['folder', '.project', 'helloworld.c', 'helloworld.rb', 'textfile.txt'], assigns(:entries).collect(&:name)
75 75 end
76 76
77 77 def test_changes
78 78 get :changes, :id => 1, :path => ['subversion_test', 'folder', 'helloworld.rb' ]
79 79 assert_response :success
80 80 assert_template 'changes'
81 81 # svn properties displayed with svn >= 1.5 only
82 82 if Redmine::Scm::Adapters::SubversionAdapter.client_version_above?([1, 5, 0])
83 83 assert_not_nil assigns(:properties)
84 84 assert_equal 'native', assigns(:properties)['svn:eol-style']
85 85 assert_tag :ul,
86 86 :child => { :tag => 'li',
87 87 :child => { :tag => 'b', :content => 'svn:eol-style' },
88 88 :child => { :tag => 'span', :content => 'native' } }
89 89 end
90 90 end
91 91
92 92 def test_entry
93 93 get :entry, :id => 1, :path => ['subversion_test', 'helloworld.c']
94 94 assert_response :success
95 95 assert_template 'entry'
96 96 end
97 97
98 98 def test_entry_at_given_revision
99 99 get :entry, :id => 1, :path => ['subversion_test', 'helloworld.rb'], :rev => 2
100 100 assert_response :success
101 101 assert_template 'entry'
102 102 # this line was removed in r3 and file was moved in r6
103 103 assert_tag :tag => 'td', :attributes => { :class => /line-code/},
104 104 :content => /Here's the code/
105 105 end
106 106
107 107 def test_entry_not_found
108 108 get :entry, :id => 1, :path => ['subversion_test', 'zzz.c']
109 109 assert_tag :tag => 'div', :attributes => { :class => /error/ },
110 110 :content => /The entry or revision was not found in the repository/
111 111 end
112 112
113 113 def test_entry_download
114 114 get :entry, :id => 1, :path => ['subversion_test', 'helloworld.c'], :format => 'raw'
115 115 assert_response :success
116 116 end
117 117
118 118 def test_directory_entry
119 119 get :entry, :id => 1, :path => ['subversion_test', 'folder']
120 120 assert_response :success
121 121 assert_template 'browse'
122 122 assert_not_nil assigns(:entry)
123 123 assert_equal 'folder', assigns(:entry).name
124 124 end
125 125
126 126 def test_revision
127 127 get :revision, :id => 1, :rev => 2
128 128 assert_response :success
129 129 assert_template 'revision'
130 130 assert_tag :tag => 'ul',
131 131 :child => { :tag => 'li',
132 132 # link to the entry at rev 2
133 133 :child => { :tag => 'a',
134 :attributes => {:href => '/repositories/entry/ecookbook/test/some/path/in/the/repo?rev=2'},
134 :attributes => {:href => '/projects/ecookbook/repository/revisions/2/entry/test/some/path/in/the/repo'},
135 135 :content => 'repo',
136 136 # link to partial diff
137 137 :sibling => { :tag => 'a',
138 :attributes => { :href => '/repositories/diff/ecookbook/test/some/path/in/the/repo?rev=2' }
138 :attributes => { :href => '/projects/ecookbook/repository/revisions/2/diff/test/some/path/in/the/repo' }
139 139 }
140 140 }
141 141 }
142 142 end
143 143
144 144 def test_revision_with_repository_pointing_to_a_subdirectory
145 145 r = Project.find(1).repository
146 146 # Changes repository url to a subdirectory
147 147 r.update_attribute :url, (r.url + '/test/some')
148 148
149 149 get :revision, :id => 1, :rev => 2
150 150 assert_response :success
151 151 assert_template 'revision'
152 152 assert_tag :tag => 'ul',
153 153 :child => { :tag => 'li',
154 154 # link to the entry at rev 2
155 155 :child => { :tag => 'a',
156 :attributes => {:href => '/repositories/entry/ecookbook/path/in/the/repo?rev=2'},
156 :attributes => {:href => '/projects/ecookbook/repository/revisions/2/entry/path/in/the/repo'},
157 157 :content => 'repo',
158 158 # link to partial diff
159 159 :sibling => { :tag => 'a',
160 :attributes => { :href => '/repositories/diff/ecookbook/path/in/the/repo?rev=2' }
160 :attributes => { :href => '/projects/ecookbook/repository/revisions/2/diff/path/in/the/repo' }
161 161 }
162 162 }
163 163 }
164 164 end
165 165
166 166 def test_diff
167 167 get :diff, :id => 1, :rev => 3
168 168 assert_response :success
169 169 assert_template 'diff'
170 170 end
171 171
172 172 def test_annotate
173 173 get :annotate, :id => 1, :path => ['subversion_test', 'helloworld.c']
174 174 assert_response :success
175 175 assert_template 'annotate'
176 176 end
177 177 else
178 178 puts "Subversion test repository NOT FOUND. Skipping functional tests !!!"
179 179 def test_fake; assert true end
180 180 end
181 181 end
@@ -1,278 +1,385
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'timelog_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class TimelogController; def rescue_action(e) raise e end; end
23 23
24 24 class TimelogControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :enabled_modules, :roles, :members, :issues, :time_entries, :users, :trackers, :enumerations, :issue_statuses, :custom_fields, :custom_values
26 26
27 27 def setup
28 28 @controller = TimelogController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 end
32 32
33 def test_edit_routing
34 assert_routing(
35 {:method => :get, :path => '/issues/567/time_entries/new'},
36 :controller => 'timelog', :action => 'edit', :issue_id => '567'
37 )
38 assert_routing(
39 {:method => :get, :path => '/projects/ecookbook/time_entries/new'},
40 :controller => 'timelog', :action => 'edit', :project_id => 'ecookbook'
41 )
42 assert_routing(
43 {:method => :get, :path => '/projects/ecookbook/issues/567/time_entries/new'},
44 :controller => 'timelog', :action => 'edit', :project_id => 'ecookbook', :issue_id => '567'
45 )
46
47 #TODO: change new form to POST to issue_time_entries_path instead of to edit action
48 #TODO: change edit form to PUT to time_entry_path
49 assert_routing(
50 {:method => :get, :path => '/time_entries/22/edit'},
51 :controller => 'timelog', :action => 'edit', :id => '22'
52 )
53 end
54
33 55 def test_get_edit
34 56 @request.session[:user_id] = 3
35 57 get :edit, :project_id => 1
36 58 assert_response :success
37 59 assert_template 'edit'
38 60 # Default activity selected
39 61 assert_tag :tag => 'option', :attributes => { :selected => 'selected' },
40 62 :content => 'Development'
41 63 end
42 64
43 65 def test_post_edit
66 # TODO: should POST to issues’ time log instead of project. change form
67 # and routing
44 68 @request.session[:user_id] = 3
45 69 post :edit, :project_id => 1,
46 70 :time_entry => {:comments => 'Some work on TimelogControllerTest',
47 71 # Not the default activity
48 72 :activity_id => '11',
49 73 :spent_on => '2008-03-14',
50 74 :issue_id => '1',
51 75 :hours => '7.3'}
52 assert_redirected_to 'projects/ecookbook/timelog/details'
76 assert_redirected_to :action => 'details', :project_id => 'ecookbook'
53 77
54 78 i = Issue.find(1)
55 79 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
56 80 assert_not_nil t
57 81 assert_equal 11, t.activity_id
58 82 assert_equal 7.3, t.hours
59 83 assert_equal 3, t.user_id
60 84 assert_equal i, t.issue
61 85 assert_equal i.project, t.project
62 86 end
63 87
64 88 def test_update
65 89 entry = TimeEntry.find(1)
66 90 assert_equal 1, entry.issue_id
67 91 assert_equal 2, entry.user_id
68 92
69 93 @request.session[:user_id] = 1
70 94 post :edit, :id => 1,
71 95 :time_entry => {:issue_id => '2',
72 96 :hours => '8'}
73 assert_redirected_to 'projects/ecookbook/timelog/details'
97 assert_redirected_to :action => 'details', :project_id => 'ecookbook'
74 98 entry.reload
75 99
76 100 assert_equal 8, entry.hours
77 101 assert_equal 2, entry.issue_id
78 102 assert_equal 2, entry.user_id
79 103 end
80 104
105 def test_destroy_routing
106 #TODO: use DELETE to time_entry_path
107 assert_routing(
108 {:method => :post, :path => '/time_entries/55/destroy'},
109 :controller => 'timelog', :action => 'destroy', :id => '55'
110 )
111 end
112
81 113 def test_destroy
82 114 @request.session[:user_id] = 2
83 115 post :destroy, :id => 1
84 assert_redirected_to 'projects/ecookbook/timelog/details'
116 assert_redirected_to :action => 'details', :project_id => 'ecookbook'
85 117 assert_nil TimeEntry.find_by_id(1)
86 118 end
87 119
120 def test_report_routing
121 assert_routing(
122 {:method => :get, :path => '/projects/567/time_entries/report'},
123 :controller => 'timelog', :action => 'report', :project_id => '567'
124 )
125 assert_routing(
126 {:method => :get, :path => '/projects/567/time_entries/report.csv'},
127 :controller => 'timelog', :action => 'report', :project_id => '567', :format => 'csv'
128 )
129 end
130
88 131 def test_report_no_criteria
89 132 get :report, :project_id => 1
90 133 assert_response :success
91 134 assert_template 'report'
92 135 end
93 136
137 def test_report_routing_for_all_projects
138 assert_routing(
139 {:method => :get, :path => '/time_entries/report'},
140 :controller => 'timelog', :action => 'report'
141 )
142 end
143
94 144 def test_report_all_projects
95 145 get :report
96 146 assert_response :success
97 147 assert_template 'report'
98 148 end
99 149
100 150 def test_report_all_projects_denied
101 151 r = Role.anonymous
102 152 r.permissions.delete(:view_time_entries)
103 153 r.permissions_will_change!
104 154 r.save
105 155 get :report
106 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Ftimelog%2Freport'
156 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Ftime_entries%2Freport'
107 157 end
108 158
109 159 def test_report_all_projects_one_criteria
110 160 get :report, :columns => 'week', :from => "2007-04-01", :to => "2007-04-30", :criterias => ['project']
111 161 assert_response :success
112 162 assert_template 'report'
113 163 assert_not_nil assigns(:total_hours)
114 164 assert_equal "8.65", "%.2f" % assigns(:total_hours)
115 165 end
116 166
117 167 def test_report_all_time
118 168 get :report, :project_id => 1, :criterias => ['project', 'issue']
119 169 assert_response :success
120 170 assert_template 'report'
121 171 assert_not_nil assigns(:total_hours)
122 172 assert_equal "162.90", "%.2f" % assigns(:total_hours)
123 173 end
124 174
125 175 def test_report_all_time_by_day
126 176 get :report, :project_id => 1, :criterias => ['project', 'issue'], :columns => 'day'
127 177 assert_response :success
128 178 assert_template 'report'
129 179 assert_not_nil assigns(:total_hours)
130 180 assert_equal "162.90", "%.2f" % assigns(:total_hours)
131 181 assert_tag :tag => 'th', :content => '2007-03-12'
132 182 end
133 183
134 184 def test_report_one_criteria
135 185 get :report, :project_id => 1, :columns => 'week', :from => "2007-04-01", :to => "2007-04-30", :criterias => ['project']
136 186 assert_response :success
137 187 assert_template 'report'
138 188 assert_not_nil assigns(:total_hours)
139 189 assert_equal "8.65", "%.2f" % assigns(:total_hours)
140 190 end
141 191
142 192 def test_report_two_criterias
143 193 get :report, :project_id => 1, :columns => 'month', :from => "2007-01-01", :to => "2007-12-31", :criterias => ["member", "activity"]
144 194 assert_response :success
145 195 assert_template 'report'
146 196 assert_not_nil assigns(:total_hours)
147 197 assert_equal "162.90", "%.2f" % assigns(:total_hours)
148 198 end
149 199
150 200 def test_report_custom_field_criteria
151 201 get :report, :project_id => 1, :criterias => ['project', 'cf_1']
152 202 assert_response :success
153 203 assert_template 'report'
154 204 assert_not_nil assigns(:total_hours)
155 205 assert_not_nil assigns(:criterias)
156 206 assert_equal 2, assigns(:criterias).size
157 207 assert_equal "162.90", "%.2f" % assigns(:total_hours)
158 208 # Custom field column
159 209 assert_tag :tag => 'th', :content => 'Database'
160 210 # Custom field row
161 211 assert_tag :tag => 'td', :content => 'MySQL',
162 212 :sibling => { :tag => 'td', :attributes => { :class => 'hours' },
163 213 :child => { :tag => 'span', :attributes => { :class => 'hours hours-int' },
164 214 :content => '1' }}
165 215 end
166 216
167 217 def test_report_one_criteria_no_result
168 218 get :report, :project_id => 1, :columns => 'week', :from => "1998-04-01", :to => "1998-04-30", :criterias => ['project']
169 219 assert_response :success
170 220 assert_template 'report'
171 221 assert_not_nil assigns(:total_hours)
172 222 assert_equal "0.00", "%.2f" % assigns(:total_hours)
173 223 end
174 224
175 225 def test_report_all_projects_csv_export
176 226 get :report, :columns => 'month', :from => "2007-01-01", :to => "2007-06-30", :criterias => ["project", "member", "activity"], :format => "csv"
177 227 assert_response :success
178 228 assert_equal 'text/csv', @response.content_type
179 229 lines = @response.body.chomp.split("\n")
180 230 # Headers
181 231 assert_equal 'Project,Member,Activity,2007-1,2007-2,2007-3,2007-4,2007-5,2007-6,Total', lines.first
182 232 # Total row
183 233 assert_equal 'Total,"","","","",154.25,8.65,"","",162.90', lines.last
184 234 end
185 235
186 236 def test_report_csv_export
187 237 get :report, :project_id => 1, :columns => 'month', :from => "2007-01-01", :to => "2007-06-30", :criterias => ["project", "member", "activity"], :format => "csv"
188 238 assert_response :success
189 239 assert_equal 'text/csv', @response.content_type
190 240 lines = @response.body.chomp.split("\n")
191 241 # Headers
192 242 assert_equal 'Project,Member,Activity,2007-1,2007-2,2007-3,2007-4,2007-5,2007-6,Total', lines.first
193 243 # Total row
194 244 assert_equal 'Total,"","","","",154.25,8.65,"","",162.90', lines.last
195 245 end
196 246
197 247 def test_details_all_projects
198 248 get :details
199 249 assert_response :success
200 250 assert_template 'details'
201 251 assert_not_nil assigns(:total_hours)
202 252 assert_equal "162.90", "%.2f" % assigns(:total_hours)
203 253 end
204 254
255 def test_project_details_routing
256 assert_routing(
257 {:method => :get, :path => '/projects/567/time_entries'},
258 :controller => 'timelog', :action => 'details', :project_id => '567'
259 )
260 end
261
205 262 def test_details_at_project_level
206 263 get :details, :project_id => 1
207 264 assert_response :success
208 265 assert_template 'details'
209 266 assert_not_nil assigns(:entries)
210 267 assert_equal 4, assigns(:entries).size
211 268 # project and subproject
212 269 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
213 270 assert_not_nil assigns(:total_hours)
214 271 assert_equal "162.90", "%.2f" % assigns(:total_hours)
215 272 # display all time by default
216 273 assert_equal '2007-03-11'.to_date, assigns(:from)
217 274 assert_equal '2007-04-22'.to_date, assigns(:to)
218 275 end
219 276
220 277 def test_details_at_project_level_with_date_range
221 278 get :details, :project_id => 1, :from => '2007-03-20', :to => '2007-04-30'
222 279 assert_response :success
223 280 assert_template 'details'
224 281 assert_not_nil assigns(:entries)
225 282 assert_equal 3, assigns(:entries).size
226 283 assert_not_nil assigns(:total_hours)
227 284 assert_equal "12.90", "%.2f" % assigns(:total_hours)
228 285 assert_equal '2007-03-20'.to_date, assigns(:from)
229 286 assert_equal '2007-04-30'.to_date, assigns(:to)
230 287 end
231 288
232 289 def test_details_at_project_level_with_period
233 290 get :details, :project_id => 1, :period => '7_days'
234 291 assert_response :success
235 292 assert_template 'details'
236 293 assert_not_nil assigns(:entries)
237 294 assert_not_nil assigns(:total_hours)
238 295 assert_equal Date.today - 7, assigns(:from)
239 296 assert_equal Date.today, assigns(:to)
240 297 end
241 298
299 def test_issue_details_routing
300 assert_routing(
301 {:method => :get, :path => 'time_entries'},
302 :controller => 'timelog', :action => 'details'
303 )
304 assert_routing(
305 {:method => :get, :path => '/issues/234/time_entries'},
306 :controller => 'timelog', :action => 'details', :issue_id => '234'
307 )
308 # TODO: issue detail page shouldnt link to project_issue_time_entries_path but to normal issues one
309 # doesnt seem to have effect on resulting page so controller can be left untouched
310 assert_routing(
311 {:method => :get, :path => '/projects/ecookbook/issues/123/time_entries'},
312 :controller => 'timelog', :action => 'details', :project_id => 'ecookbook', :issue_id => '123'
313 )
314 end
315
242 316 def test_details_at_issue_level
243 317 get :details, :issue_id => 1
244 318 assert_response :success
245 319 assert_template 'details'
246 320 assert_not_nil assigns(:entries)
247 321 assert_equal 2, assigns(:entries).size
248 322 assert_not_nil assigns(:total_hours)
249 323 assert_equal 154.25, assigns(:total_hours)
250 324 # display all time by default
251 325 assert_equal '2007-03-11'.to_date, assigns(:from)
252 326 assert_equal '2007-04-22'.to_date, assigns(:to)
253 327 end
254 328
329 def test_details_formatted_routing
330 assert_routing(
331 {:method => :get, :path => 'time_entries.atom'},
332 :controller => 'timelog', :action => 'details', :format => 'atom'
333 )
334 assert_routing(
335 {:method => :get, :path => 'time_entries.csv'},
336 :controller => 'timelog', :action => 'details', :format => 'csv'
337 )
338 end
339
340 def test_details_for_project_formatted_routing
341 assert_routing(
342 {:method => :get, :path => '/projects/567/time_entries.atom'},
343 :controller => 'timelog', :action => 'details', :format => 'atom', :project_id => '567'
344 )
345 assert_routing(
346 {:method => :get, :path => '/projects/567/time_entries.csv'},
347 :controller => 'timelog', :action => 'details', :format => 'csv', :project_id => '567'
348 )
349 end
350
351 def test_details_for_issue_formatted_routing
352 assert_routing(
353 {:method => :get, :path => '/projects/ecookbook/issues/123/time_entries.atom'},
354 :controller => 'timelog', :action => 'details', :project_id => 'ecookbook', :issue_id => '123', :format => 'atom'
355 )
356 assert_routing(
357 {:method => :get, :path => '/projects/ecookbook/issues/123/time_entries.csv'},
358 :controller => 'timelog', :action => 'details', :project_id => 'ecookbook', :issue_id => '123', :format => 'csv'
359 )
360 end
361
255 362 def test_details_atom_feed
256 363 get :details, :project_id => 1, :format => 'atom'
257 364 assert_response :success
258 365 assert_equal 'application/atom+xml', @response.content_type
259 366 assert_not_nil assigns(:items)
260 367 assert assigns(:items).first.is_a?(TimeEntry)
261 368 end
262 369
263 370 def test_details_all_projects_csv_export
264 371 get :details, :format => 'csv'
265 372 assert_response :success
266 373 assert_equal 'text/csv', @response.content_type
267 374 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment\n")
268 375 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\"\n")
269 376 end
270 377
271 378 def test_details_csv_export
272 379 get :details, :project_id => 1, :format => 'csv'
273 380 assert_response :success
274 381 assert_equal 'text/csv', @response.content_type
275 382 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment\n")
276 383 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\"\n")
277 384 end
278 385 end
@@ -1,72 +1,142
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'users_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class UsersController; def rescue_action(e) raise e end; end
23 23
24 24 class UsersControllerTest < Test::Unit::TestCase
25 25 fixtures :users, :projects, :members
26 26
27 27 def setup
28 28 @controller = UsersController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 @request.session[:user_id] = 1 # admin
33 33 end
34 34
35 def test_index_routing
36 #TODO: unify with list
37 assert_generates(
38 '/users',
39 :controller => 'users', :action => 'index'
40 )
41 end
42
35 43 def test_index
36 44 get :index
37 45 assert_response :success
38 46 assert_template 'list'
39 47 end
40 48
49 def test_list_routing
50 #TODO: rename action to index
51 assert_routing(
52 {:method => :get, :path => '/users'},
53 :controller => 'users', :action => 'list'
54 )
55 end
56
41 57 def test_list
42 58 get :list
43 59 assert_response :success
44 60 assert_template 'list'
45 61 assert_not_nil assigns(:users)
46 62 # active users only
47 63 assert_nil assigns(:users).detect {|u| !u.active?}
48 64 end
49 65
50 66 def test_list_with_name_filter
51 67 get :list, :name => 'john'
52 68 assert_response :success
53 69 assert_template 'list'
54 70 users = assigns(:users)
55 71 assert_not_nil users
56 72 assert_equal 1, users.size
57 73 assert_equal 'John', users.first.firstname
58 74 end
59 75
76 def test_add_routing
77 assert_routing(
78 {:method => :get, :path => '/users/new'},
79 :controller => 'users', :action => 'add'
80 )
81 assert_recognizes(
82 #TODO: remove this and replace with POST to collection, need to modify form
83 {:controller => 'users', :action => 'add'},
84 {:method => :post, :path => '/users/new'}
85 )
86 assert_recognizes(
87 {:controller => 'users', :action => 'add'},
88 {:method => :post, :path => '/users'}
89 )
90 end
91
92 def test_edit_routing
93 assert_routing(
94 {:method => :get, :path => '/users/444/edit'},
95 :controller => 'users', :action => 'edit', :id => '444'
96 )
97 assert_routing(
98 {:method => :get, :path => '/users/222/edit/membership'},
99 :controller => 'users', :action => 'edit', :id => '222', :tab => 'membership'
100 )
101 assert_recognizes(
102 #TODO: use PUT on user_path, modify form
103 {:controller => 'users', :action => 'edit', :id => '444'},
104 {:method => :post, :path => '/users/444/edit'}
105 )
106 end
107
108 def test_add_membership_routing
109 assert_routing(
110 {:method => :post, :path => '/users/123/memberships'},
111 :controller => 'users', :action => 'edit_membership', :id => '123'
112 )
113 end
114
115 def test_edit_membership_routing
116 assert_routing(
117 {:method => :post, :path => '/users/123/memberships/55'},
118 :controller => 'users', :action => 'edit_membership', :id => '123', :membership_id => '55'
119 )
120 end
121
60 122 def test_edit_membership
61 123 post :edit_membership, :id => 2, :membership_id => 1,
62 124 :membership => { :role_id => 2}
63 assert_redirected_to '/users/edit/2?tab=memberships'
125 assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships'
64 126 assert_equal 2, Member.find(1).role_id
65 127 end
66 128
67 129 def test_destroy_membership
130 assert_routing(
131 #TODO: use DELETE method on user_membership_path, modify form
132 {:method => :post, :path => '/users/567/memberships/12/destroy'},
133 :controller => 'users', :action => 'destroy_membership', :id => '567', :membership_id => '12'
134 )
135 end
136
137 def test_destroy_membership
68 138 post :destroy_membership, :id => 2, :membership_id => 1
69 assert_redirected_to '/users/edit/2?tab=memberships'
139 assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships'
70 140 assert_nil Member.find_by_id(1)
71 141 end
72 142 end
@@ -1,73 +1,73
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'versions_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class VersionsController; def rescue_action(e) raise e end; end
23 23
24 24 class VersionsControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :versions, :issues, :users, :roles, :members, :enabled_modules
26 26
27 27 def setup
28 28 @controller = VersionsController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 end
33 33
34 34 def test_show
35 35 get :show, :id => 2
36 36 assert_response :success
37 37 assert_template 'show'
38 38 assert_not_nil assigns(:version)
39 39
40 40 assert_tag :tag => 'h2', :content => /1.0/
41 41 end
42 42
43 43 def test_get_edit
44 44 @request.session[:user_id] = 2
45 45 get :edit, :id => 2
46 46 assert_response :success
47 47 assert_template 'edit'
48 48 end
49 49
50 50 def test_post_edit
51 51 @request.session[:user_id] = 2
52 52 post :edit, :id => 2,
53 53 :version => { :name => 'New version name',
54 54 :effective_date => Date.today.strftime("%Y-%m-%d")}
55 assert_redirected_to '/projects/settings/ecookbook?tab=versions'
55 assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook'
56 56 version = Version.find(2)
57 57 assert_equal 'New version name', version.name
58 58 assert_equal Date.today, version.effective_date
59 59 end
60 60
61 61 def test_destroy
62 62 @request.session[:user_id] = 2
63 63 post :destroy, :id => 3
64 assert_redirected_to '/projects/settings/ecookbook?tab=versions'
64 assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook'
65 65 assert_nil Version.find_by_id(3)
66 66 end
67 67
68 68 def test_issue_status_by
69 69 xhr :get, :status_by, :id => 2
70 70 assert_response :success
71 71 assert_template '_issue_counts'
72 72 end
73 73 end
@@ -1,259 +1,359
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'wiki_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class WikiController; def rescue_action(e) raise e end; end
23 23
24 24 class WikiControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :users, :roles, :members, :enabled_modules, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, :attachments
26 26
27 27 def setup
28 28 @controller = WikiController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 end
33 33
34 def test_index_routing
35 assert_routing(
36 {:method => :get, :path => '/projects/567/wiki'},
37 :controller => 'wiki', :action => 'index', :id => '567'
38 )
39 assert_routing(
40 {:method => :get, :path => '/projects/567/wiki/lalala'},
41 :controller => 'wiki', :action => 'index', :id => '567', :page => 'lalala'
42 )
43 assert_generates(
44 '/projects/567/wiki',
45 :controller => 'wiki', :action => 'index', :id => '567', :page => nil
46 )
47 end
48
34 49 def test_show_start_page
35 50 get :index, :id => 'ecookbook'
36 51 assert_response :success
37 52 assert_template 'show'
38 53 assert_tag :tag => 'h1', :content => /CookBook documentation/
39 54
40 55 # child_pages macro
41 56 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
42 57 :child => { :tag => 'li',
43 :child => { :tag => 'a', :attributes => { :href => '/wiki/ecookbook/Page_with_an_inline_image' },
58 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
44 59 :content => 'Page with an inline image' } }
45 60 end
46 61
47 62 def test_show_page_with_name
48 63 get :index, :id => 1, :page => 'Another_page'
49 64 assert_response :success
50 65 assert_template 'show'
51 66 assert_tag :tag => 'h1', :content => /Another page/
52 67 # Included page with an inline image
53 68 assert_tag :tag => 'p', :content => /This is an inline image/
54 69 assert_tag :tag => 'img', :attributes => { :src => '/attachments/download/3',
55 70 :alt => 'This is a logo' }
56 71 end
57 72
58 73 def test_show_unexistent_page_without_edit_right
59 74 get :index, :id => 1, :page => 'Unexistent page'
60 75 assert_response 404
61 76 end
62 77
63 78 def test_show_unexistent_page_with_edit_right
64 79 @request.session[:user_id] = 2
65 80 get :index, :id => 1, :page => 'Unexistent page'
66 81 assert_response :success
67 82 assert_template 'edit'
68 83 end
69 84
85 def test_edit_routing
86 assert_routing(
87 {:method => :get, :path => '/projects/567/wiki/my_page/edit'},
88 :controller => 'wiki', :action => 'edit', :id => '567', :page => 'my_page'
89 )
90 assert_recognizes(#TODO: use PUT to page path, adjust forms accordingly
91 {:controller => 'wiki', :action => 'edit', :id => '567', :page => 'my_page'},
92 {:method => :post, :path => '/projects/567/wiki/my_page/edit'}
93 )
94 end
95
70 96 def test_create_page
71 97 @request.session[:user_id] = 2
72 98 post :edit, :id => 1,
73 99 :page => 'New page',
74 100 :content => {:comments => 'Created the page',
75 101 :text => "h1. New page\n\nThis is a new page",
76 102 :version => 0}
77 assert_redirected_to 'wiki/ecookbook/New_page'
103 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'New_page'
78 104 page = Project.find(1).wiki.find_page('New page')
79 105 assert !page.new_record?
80 106 assert_not_nil page.content
81 107 assert_equal 'Created the page', page.content.comments
82 108 end
83 109
110 def test_preview_routing
111 assert_routing(
112 {:method => :post, :path => '/projects/567/wiki/CookBook_documentation/preview'},
113 :controller => 'wiki', :action => 'preview', :id => '567', :page => 'CookBook_documentation'
114 )
115 end
116
84 117 def test_preview
85 118 @request.session[:user_id] = 2
86 119 xhr :post, :preview, :id => 1, :page => 'CookBook_documentation',
87 120 :content => { :comments => '',
88 121 :text => 'this is a *previewed text*',
89 122 :version => 3 }
90 123 assert_response :success
91 124 assert_template 'common/_preview'
92 125 assert_tag :tag => 'strong', :content => /previewed text/
93 126 end
94 127
95 128 def test_preview_new_page
96 129 @request.session[:user_id] = 2
97 130 xhr :post, :preview, :id => 1, :page => 'New page',
98 131 :content => { :text => 'h1. New page',
99 132 :comments => '',
100 133 :version => 0 }
101 134 assert_response :success
102 135 assert_template 'common/_preview'
103 136 assert_tag :tag => 'h1', :content => /New page/
104 137 end
105 138
139 def test_history_routing
140 assert_routing(
141 {:method => :get, :path => '/projects/1/wiki/CookBook_documentation/history'},
142 :controller => 'wiki', :action => 'history', :id => '1', :page => 'CookBook_documentation'
143 )
144 end
145
106 146 def test_history
107 147 get :history, :id => 1, :page => 'CookBook_documentation'
108 148 assert_response :success
109 149 assert_template 'history'
110 150 assert_not_nil assigns(:versions)
111 151 assert_equal 3, assigns(:versions).size
112 152 assert_select "input[type=submit][name=commit]"
113 153 end
114 154
115 155 def test_history_with_one_version
116 156 get :history, :id => 1, :page => 'Another_page'
117 157 assert_response :success
118 158 assert_template 'history'
119 159 assert_not_nil assigns(:versions)
120 160 assert_equal 1, assigns(:versions).size
121 161 assert_select "input[type=submit][name=commit]", false
122 162 end
123 163
164 def test_diff_routing
165 assert_routing(
166 {:method => :get, :path => '/projects/1/wiki/CookBook_documentation/diff/2/vs/1'},
167 :controller => 'wiki', :action => 'diff', :id => '1', :page => 'CookBook_documentation', :version => '2', :version_from => '1'
168 )
169 end
170
124 171 def test_diff
125 172 get :diff, :id => 1, :page => 'CookBook_documentation', :version => 2, :version_from => 1
126 173 assert_response :success
127 174 assert_template 'diff'
128 175 assert_tag :tag => 'span', :attributes => { :class => 'diff_in'},
129 176 :content => /updated/
130 177 end
131 178
179 def test_annotate_routing
180 assert_routing(
181 {:method => :get, :path => '/projects/1/wiki/CookBook_documentation/annotate/2'},
182 :controller => 'wiki', :action => 'annotate', :id => '1', :page => 'CookBook_documentation', :version => '2'
183 )
184 end
185
132 186 def test_annotate
133 187 get :annotate, :id => 1, :page => 'CookBook_documentation', :version => 2
134 188 assert_response :success
135 189 assert_template 'annotate'
136 190 # Line 1
137 191 assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '1' },
138 192 :child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /John Smith/ },
139 193 :child => { :tag => 'td', :content => /h1\. CookBook documentation/ }
140 194 # Line 2
141 195 assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '2' },
142 196 :child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /redMine Admin/ },
143 197 :child => { :tag => 'td', :content => /Some updated \[\[documentation\]\] here/ }
144 198 end
145 199
200 def test_rename_routing
201 assert_routing(
202 {:method => :get, :path => '/projects/22/wiki/ladida/rename'},
203 :controller => 'wiki', :action => 'rename', :id => '22', :page => 'ladida'
204 )
205 assert_recognizes(
206 #TODO: should be moved into a update action and use a PUT to the page URI
207 {:controller => 'wiki', :action => 'rename', :id => '22', :page => 'ladida'},
208 {:method => :post, :path => '/projects/22/wiki/ladida/rename'}
209 )
210 end
211
146 212 def test_rename_with_redirect
147 213 @request.session[:user_id] = 2
148 214 post :rename, :id => 1, :page => 'Another_page',
149 215 :wiki_page => { :title => 'Another renamed page',
150 216 :redirect_existing_links => 1 }
151 assert_redirected_to 'wiki/ecookbook/Another_renamed_page'
217 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'Another_renamed_page'
152 218 wiki = Project.find(1).wiki
153 219 # Check redirects
154 220 assert_not_nil wiki.find_page('Another page')
155 221 assert_nil wiki.find_page('Another page', :with_redirect => false)
156 222 end
157 223
158 224 def test_rename_without_redirect
159 225 @request.session[:user_id] = 2
160 226 post :rename, :id => 1, :page => 'Another_page',
161 227 :wiki_page => { :title => 'Another renamed page',
162 228 :redirect_existing_links => "0" }
163 assert_redirected_to 'wiki/ecookbook/Another_renamed_page'
229 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'Another_renamed_page'
164 230 wiki = Project.find(1).wiki
165 231 # Check that there's no redirects
166 232 assert_nil wiki.find_page('Another page')
167 233 end
168 234
235 def test_destroy_routing
236 assert_recognizes(
237 #TODO: should use DELETE on page URI
238 {:controller => 'wiki', :action => 'destroy', :id => '22', :page => 'ladida'},
239 {:method => :post, :path => 'projects/22/wiki/ladida/destroy'}
240 )
241 end
242
169 243 def test_destroy
170 244 @request.session[:user_id] = 2
171 245 post :destroy, :id => 1, :page => 'CookBook_documentation'
172 assert_redirected_to 'wiki/ecookbook/Page_index/special'
246 assert_redirected_to :action => 'special', :id => 'ecookbook', :page => 'Page_index'
247 end
248
249 def test_special_routing
250 assert_routing(
251 {:method => :get, :path => '/projects/567/wiki/page_index'},
252 :controller => 'wiki', :action => 'special', :id => '567', :page => 'page_index'
253 )
254 assert_routing(
255 {:method => :get, :path => '/projects/567/wiki/Page_Index'},
256 :controller => 'wiki', :action => 'special', :id => '567', :page => 'Page_Index'
257 )
258 assert_routing(
259 {:method => :get, :path => '/projects/567/wiki/date_index'},
260 :controller => 'wiki', :action => 'special', :id => '567', :page => 'date_index'
261 )
262 assert_routing(
263 {:method => :get, :path => '/projects/567/wiki/export'},
264 :controller => 'wiki', :action => 'special', :id => '567', :page => 'export'
265 )
173 266 end
174 267
175 268 def test_page_index
176 269 get :special, :id => 'ecookbook', :page => 'Page_index'
177 270 assert_response :success
178 271 assert_template 'special_page_index'
179 272 pages = assigns(:pages)
180 273 assert_not_nil pages
181 274 assert_equal Project.find(1).wiki.pages.size, pages.size
182 275
183 276 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
184 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/wiki/ecookbook/CookBook_documentation' },
277 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/CookBook_documentation' },
185 278 :content => 'CookBook documentation' },
186 279 :child => { :tag => 'ul',
187 280 :child => { :tag => 'li',
188 :child => { :tag => 'a', :attributes => { :href => '/wiki/ecookbook/Page_with_an_inline_image' },
281 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
189 282 :content => 'Page with an inline image' } } } },
190 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/wiki/ecookbook/Another_page' },
283 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Another_page' },
191 284 :content => 'Another page' } }
192 285 end
193 286
194 287 def test_not_found
195 288 get :index, :id => 999
196 289 assert_response 404
197 290 end
198 291
292 def test_protect_routing
293 assert_routing(
294 {:method => :post, :path => 'projects/22/wiki/ladida/protect'},
295 {:controller => 'wiki', :action => 'protect', :id => '22', :page => 'ladida'}
296 )
297 end
298
199 299 def test_protect_page
200 300 page = WikiPage.find_by_wiki_id_and_title(1, 'Another_page')
201 301 assert !page.protected?
202 302 @request.session[:user_id] = 2
203 303 post :protect, :id => 1, :page => page.title, :protected => '1'
204 assert_redirected_to 'wiki/ecookbook/Another_page'
304 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'Another_page'
205 305 assert page.reload.protected?
206 306 end
207 307
208 308 def test_unprotect_page
209 309 page = WikiPage.find_by_wiki_id_and_title(1, 'CookBook_documentation')
210 310 assert page.protected?
211 311 @request.session[:user_id] = 2
212 312 post :protect, :id => 1, :page => page.title, :protected => '0'
213 assert_redirected_to '/wiki/ecookbook/CookBook_documentation'
313 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'CookBook_documentation'
214 314 assert !page.reload.protected?
215 315 end
216 316
217 317 def test_show_page_with_edit_link
218 318 @request.session[:user_id] = 2
219 319 get :index, :id => 1
220 320 assert_response :success
221 321 assert_template 'show'
222 assert_tag :tag => 'a', :attributes => { :href => '/wiki/1/CookBook_documentation/edit' }
322 assert_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
223 323 end
224 324
225 325 def test_show_page_without_edit_link
226 326 @request.session[:user_id] = 4
227 327 get :index, :id => 1
228 328 assert_response :success
229 329 assert_template 'show'
230 assert_no_tag :tag => 'a', :attributes => { :href => '/wiki/1/CookBook_documentation/edit' }
330 assert_no_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
231 331 end
232 332
233 333 def test_edit_unprotected_page
234 334 # Non members can edit unprotected wiki pages
235 335 @request.session[:user_id] = 4
236 336 get :edit, :id => 1, :page => 'Another_page'
237 337 assert_response :success
238 338 assert_template 'edit'
239 339 end
240 340
241 341 def test_edit_protected_page_by_nonmember
242 342 # Non members can't edit protected wiki pages
243 343 @request.session[:user_id] = 4
244 344 get :edit, :id => 1, :page => 'CookBook_documentation'
245 345 assert_response 403
246 346 end
247 347
248 348 def test_edit_protected_page_by_member
249 349 @request.session[:user_id] = 2
250 350 get :edit, :id => 1, :page => 'CookBook_documentation'
251 351 assert_response :success
252 352 assert_template 'edit'
253 353 end
254 354
255 355 def test_history_of_non_existing_page_should_return_404
256 356 get :history, :id => 1, :page => 'Unknown_page'
257 357 assert_response 404
258 358 end
259 359 end
@@ -1,56 +1,75
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'wikis_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class WikisController; def rescue_action(e) raise e end; end
23 23
24 24 class WikisControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :users, :roles, :members, :enabled_modules, :wikis
26 26
27 27 def setup
28 28 @controller = WikisController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 end
33 33
34 def test_edit_routing
35 assert_routing(
36 #TODO: use PUT
37 {:method => :post, :path => 'projects/ladida/wiki'},
38 :controller => 'wikis', :action => 'edit', :id => 'ladida'
39 )
40 end
41
34 42 def test_create
35 43 @request.session[:user_id] = 1
36 44 assert_nil Project.find(3).wiki
37 45 post :edit, :id => 3, :wiki => { :start_page => 'Start page' }
38 46 assert_response :success
39 47 wiki = Project.find(3).wiki
40 48 assert_not_nil wiki
41 49 assert_equal 'Start page', wiki.start_page
42 50 end
43 51
52 def test_destroy_routing
53 assert_routing(
54 {:method => :get, :path => 'projects/ladida/wiki/destroy'},
55 :controller => 'wikis', :action => 'destroy', :id => 'ladida'
56 )
57 assert_recognizes( #TODO: use DELETE and update form
58 {:controller => 'wikis', :action => 'destroy', :id => 'ladida'},
59 {:method => :post, :path => 'projects/ladida/wiki/destroy'}
60 )
61 end
62
44 63 def test_destroy
45 64 @request.session[:user_id] = 1
46 65 post :destroy, :id => 1, :confirm => 1
47 assert_redirected_to '/projects/settings/ecookbook?tab=wiki'
66 assert_redirected_to :action => 'settings', :id => 'ecookbook', :tab => 'wiki'
48 67 assert_nil Project.find(1).wiki
49 68 end
50 69
51 70 def test_not_found
52 71 @request.session[:user_id] = 1
53 72 post :destroy, :id => 999, :confirm => 1
54 73 assert_response 404
55 74 end
56 75 end
@@ -1,66 +1,66
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 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
20 20 class AdminTest < ActionController::IntegrationTest
21 21 fixtures :users
22 22
23 23 def test_add_user
24 24 log_user("admin", "admin")
25 25 get "/users/add"
26 26 assert_response :success
27 27 assert_template "users/add"
28 28 post "/users/add", :user => { :login => "psmith", :firstname => "Paul", :lastname => "Smith", :mail => "psmith@somenet.foo", :language => "en" }, :password => "psmith09", :password_confirmation => "psmith09"
29 29 assert_redirected_to "users/list"
30 30
31 31 user = User.find_by_login("psmith")
32 32 assert_kind_of User, user
33 33 logged_user = User.try_to_login("psmith", "psmith09")
34 34 assert_kind_of User, logged_user
35 35 assert_equal "Paul", logged_user.firstname
36 36
37 37 post "users/edit", :id => user.id, :user => { :status => User::STATUS_LOCKED }
38 38 assert_redirected_to "users/list"
39 39 locked_user = User.try_to_login("psmith", "psmith09")
40 40 assert_equal nil, locked_user
41 41 end
42 42
43 43 def test_add_project
44 44 log_user("admin", "admin")
45 get "projects/add"
45 get "projects/new"
46 46 assert_response :success
47 47 assert_template "projects/add"
48 post "projects/add", :project => { :name => "blog",
48 post "projects", :project => { :name => "blog",
49 49 :description => "weblog",
50 50 :identifier => "blog",
51 51 :is_public => 1,
52 52 :custom_field_values => { '3' => 'Beta' }
53 53 }
54 54 assert_redirected_to "admin/projects"
55 55 assert_equal 'Successful creation.', flash[:notice]
56 56
57 57 project = Project.find_by_name("blog")
58 58 assert_kind_of Project, project
59 59 assert_equal "weblog", project.description
60 60 assert_equal true, project.is_public?
61 61
62 62 get "admin/projects"
63 63 assert_response :success
64 64 assert_template "admin/projects"
65 65 end
66 66 end
@@ -1,92 +1,92
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
20 20 class IssuesTest < ActionController::IntegrationTest
21 21 fixtures :projects,
22 22 :users,
23 23 :roles,
24 24 :members,
25 25 :trackers,
26 26 :projects_trackers,
27 27 :enabled_modules,
28 28 :issue_statuses,
29 29 :issues,
30 30 :enumerations,
31 31 :custom_fields,
32 32 :custom_values,
33 33 :custom_fields_trackers
34 34
35 35 # create an issue
36 36 def test_add_issue
37 37 log_user('jsmith', 'jsmith')
38 38 get 'projects/1/issues/new', :tracker_id => '1'
39 39 assert_response :success
40 40 assert_template 'issues/new'
41 41
42 post 'projects/1/issues/new', :tracker_id => "1",
42 post 'projects/1/issues', :tracker_id => "1",
43 43 :issue => { :start_date => "2006-12-26",
44 44 :priority_id => "3",
45 45 :subject => "new test issue",
46 46 :category_id => "",
47 47 :description => "new issue",
48 48 :done_ratio => "0",
49 49 :due_date => "",
50 50 :assigned_to_id => "" },
51 51 :custom_fields => {'2' => 'Value for field 2'}
52 52 # find created issue
53 53 issue = Issue.find_by_subject("new test issue")
54 54 assert_kind_of Issue, issue
55 55
56 56 # check redirection
57 assert_redirected_to "issues/show"
57 assert_redirected_to :controller => 'issues', :action => 'show'
58 58 follow_redirect!
59 59 assert_equal issue, assigns(:issue)
60 60
61 61 # check issue attributes
62 62 assert_equal 'jsmith', issue.author.login
63 63 assert_equal 1, issue.project.id
64 64 assert_equal 1, issue.status.id
65 65 end
66 66
67 67 # add then remove 2 attachments to an issue
68 68 def test_issue_attachements
69 69 log_user('jsmith', 'jsmith')
70 70 set_tmp_attachments_directory
71 71
72 post 'issues/edit/1',
72 post 'issues/1/edit',
73 73 :notes => 'Some notes',
74 74 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain'), 'description' => 'This is an attachment'}}
75 assert_redirected_to "issues/show/1"
75 assert_redirected_to "issues/1"
76 76
77 77 # make sure attachment was saved
78 78 attachment = Issue.find(1).attachments.find_by_filename("testfile.txt")
79 79 assert_kind_of Attachment, attachment
80 80 assert_equal Issue.find(1), attachment.container
81 81 assert_equal 'This is an attachment', attachment.description
82 82 # verify the size of the attachment stored in db
83 83 #assert_equal file_data_1.length, attachment.filesize
84 84 # verify that the attachment was written to disk
85 85 assert File.exist?(attachment.diskfile)
86 86
87 87 # remove the attachments
88 88 Issue.find(1).attachments.each(&:destroy)
89 89 assert_equal 0, Issue.find(1).attachments.length
90 90 end
91 91
92 92 end
@@ -1,44 +1,44
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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
20 20 class ProjectsTest < ActionController::IntegrationTest
21 21 fixtures :projects, :users, :members
22 22
23 23 def test_archive_project
24 24 subproject = Project.find(1).children.first
25 25 log_user("admin", "admin")
26 26 get "admin/projects"
27 27 assert_response :success
28 28 assert_template "admin/projects"
29 29 post "projects/archive", :id => 1
30 30 assert_redirected_to "admin/projects"
31 31 assert !Project.find(1).active?
32 32
33 get "projects/show", :id => 1
33 get 'projects/1'
34 34 assert_response 403
35 get "projects/show", :id => subproject.id
35 get "projects/#{subproject.id}"
36 36 assert_response 403
37 37
38 38 post "projects/unarchive", :id => 1
39 39 assert_redirected_to "admin/projects"
40 40 assert Project.find(1).active?
41 get "projects/show", :id => 1
41 get "projects/1"
42 42 assert_response :success
43 43 end
44 44 end
@@ -1,447 +1,447
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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
20 20 class ApplicationHelperTest < HelperTestCase
21 21 include ApplicationHelper
22 22 include ActionView::Helpers::TextHelper
23 23 fixtures :projects, :roles, :enabled_modules, :users,
24 24 :repositories, :changesets,
25 25 :trackers, :issue_statuses, :issues, :versions, :documents,
26 26 :wikis, :wiki_pages, :wiki_contents,
27 27 :boards, :messages,
28 28 :attachments
29 29
30 30 def setup
31 31 super
32 32 end
33 33
34 34 def test_auto_links
35 35 to_test = {
36 36 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
37 37 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
38 38 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
39 39 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
40 40 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
41 41 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
42 42 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.',
43 43 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
44 44 '(see inline link : http://www.foo.bar/Test_(foobar))' => '(see inline link : <a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>)',
45 45 '(see inline link : http://www.foo.bar/Test)' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>)',
46 46 '(see inline link : http://www.foo.bar/Test).' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>).',
47 47 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
48 48 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
49 49 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
50 50 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
51 51 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&#38;t=z&#38;s=">http://foo.bar/page?p=1&#38;t=z&#38;s=</a>',
52 52 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
53 53 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
54 54 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
55 55 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
56 56 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
57 57 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
58 58 }
59 59 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
60 60 end
61 61
62 62 def test_auto_mailto
63 63 assert_equal '<p><a href="mailto:test@foo.bar" class="email">test@foo.bar</a></p>',
64 64 textilizable('test@foo.bar')
65 65 end
66 66
67 67 def test_inline_images
68 68 to_test = {
69 69 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
70 70 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
71 71 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
72 72 # inline styles should be stripped
73 73 'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" alt="" />',
74 74 'with title !http://foo.bar/image.jpg(This is a title)!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a title" alt="This is a title" />',
75 75 'with title !http://foo.bar/image.jpg(This is a double-quoted "title")!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a double-quoted &quot;title&quot;" alt="This is a double-quoted &quot;title&quot;" />',
76 76 }
77 77 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
78 78 end
79 79
80 80 def test_acronyms
81 81 to_test = {
82 82 'this is an acronym: GPL(General Public License)' => 'this is an acronym: <acronym title="General Public License">GPL</acronym>',
83 83 'GPL(This is a double-quoted "title")' => '<acronym title="This is a double-quoted &quot;title&quot;">GPL</acronym>',
84 84 }
85 85 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
86 86
87 87 end
88 88
89 89 def test_attached_images
90 90 to_test = {
91 91 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
92 92 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />'
93 93 }
94 94 attachments = Attachment.find(:all)
95 95 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
96 96 end
97 97
98 98 def test_textile_external_links
99 99 to_test = {
100 100 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
101 101 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
102 102 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
103 103 '"link (Link title with "double-quotes")":http://foo.bar' => '<a href="http://foo.bar" title="Link title with &quot;double-quotes&quot;" class="external">link</a>',
104 104 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
105 105 # no multiline link text
106 106 "This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />\nand another on a second line\":test"
107 107 }
108 108 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
109 109 end
110 110
111 111 def test_redmine_links
112 112 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
113 113 :class => 'issue', :title => 'Error 281 when updating a recipe (New)')
114 114
115 115 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
116 116 :class => 'changeset', :title => 'My very first commit')
117 117
118 118 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
119 119 :class => 'document')
120 120
121 121 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
122 122 :class => 'version')
123 123
124 124 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
125 125
126 126 source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}
127 127 source_url_with_ext = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file.ext']}
128 128
129 129 to_test = {
130 130 # tickets
131 131 '#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.",
132 132 # changesets
133 133 'r1' => changeset_link,
134 134 # documents
135 135 'document#1' => document_link,
136 136 'document:"Test document"' => document_link,
137 137 # versions
138 138 'version#2' => version_link,
139 139 'version:1.0' => version_link,
140 140 'version:"1.0"' => version_link,
141 141 # source
142 142 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
143 143 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
144 144 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
145 145 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
146 146 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
147 147 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
148 148 'source:/some/file@52' => link_to('source:/some/file@52', source_url.merge(:rev => 52), :class => 'source'),
149 149 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_ext.merge(:rev => 52), :class => 'source'),
150 150 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'),
151 151 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'),
152 152 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'),
153 153 'export:/some/file' => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'),
154 154 # message
155 155 'message#4' => link_to('Post 2', message_url, :class => 'message'),
156 156 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5'), :class => 'message'),
157 157 # escaping
158 158 '!#3.' => '#3.',
159 159 '!r1' => 'r1',
160 160 '!document#1' => 'document#1',
161 161 '!document:"Test document"' => 'document:"Test document"',
162 162 '!version#2' => 'version#2',
163 163 '!version:1.0' => 'version:1.0',
164 164 '!version:"1.0"' => 'version:"1.0"',
165 165 '!source:/some/file' => 'source:/some/file',
166 166 # invalid expressions
167 167 'source:' => 'source:',
168 168 # url hash
169 169 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
170 170 }
171 171 @project = Project.find(1)
172 172 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
173 173 end
174 174
175 175 def test_wiki_links
176 176 to_test = {
177 '[[CookBook documentation]]' => '<a href="/wiki/ecookbook/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
178 '[[Another page|Page]]' => '<a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a>',
177 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
178 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
179 179 # link with anchor
180 '[[CookBook documentation#One-section]]' => '<a href="/wiki/ecookbook/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
181 '[[Another page#anchor|Page]]' => '<a href="/wiki/ecookbook/Another_page#anchor" class="wiki-page">Page</a>',
180 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
181 '[[Another page#anchor|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page#anchor" class="wiki-page">Page</a>',
182 182 # page that doesn't exist
183 '[[Unknown page]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">Unknown page</a>',
184 '[[Unknown page|404]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">404</a>',
183 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
184 '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">404</a>',
185 185 # link to another project wiki
186 '[[onlinestore:]]' => '<a href="/wiki/onlinestore/" class="wiki-page">onlinestore</a>',
187 '[[onlinestore:|Wiki]]' => '<a href="/wiki/onlinestore/" class="wiki-page">Wiki</a>',
188 '[[onlinestore:Start page]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Start page</a>',
189 '[[onlinestore:Start page|Text]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Text</a>',
190 '[[onlinestore:Unknown page]]' => '<a href="/wiki/onlinestore/Unknown_page" class="wiki-page new">Unknown page</a>',
186 '[[onlinestore:]]' => '<a href="/projects/onlinestore/wiki/" class="wiki-page">onlinestore</a>',
187 '[[onlinestore:|Wiki]]' => '<a href="/projects/onlinestore/wiki/" class="wiki-page">Wiki</a>',
188 '[[onlinestore:Start page]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Start page</a>',
189 '[[onlinestore:Start page|Text]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Text</a>',
190 '[[onlinestore:Unknown page]]' => '<a href="/projects/onlinestore/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
191 191 # striked through link
192 '-[[Another page|Page]]-' => '<del><a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a></del>',
193 '-[[Another page|Page]] link-' => '<del><a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a> link</del>',
192 '-[[Another page|Page]]-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a></del>',
193 '-[[Another page|Page]] link-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a> link</del>',
194 194 # escaping
195 195 '![[Another page|Page]]' => '[[Another page|Page]]',
196 196 }
197 197 @project = Project.find(1)
198 198 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
199 199 end
200 200
201 201 def test_html_tags
202 202 to_test = {
203 203 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
204 204 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
205 205 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
206 206 # do not escape pre/code tags
207 207 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
208 208 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
209 209 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
210 210 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
211 211 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
212 212 # remove attributes except class
213 213 "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
214 214 "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
215 215 }
216 216 to_test.each { |text, result| assert_equal result, textilizable(text) }
217 217 end
218 218
219 219 def test_allowed_html_tags
220 220 to_test = {
221 221 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
222 222 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
223 223 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
224 224 }
225 225 to_test.each { |text, result| assert_equal result, textilizable(text) }
226 226 end
227 227
228 228 def syntax_highlight
229 229 raw = <<-RAW
230 230 <pre><code class="ruby">
231 231 # Some ruby code here
232 232 </pre></code>
233 233 RAW
234 234
235 235 expected = <<-EXPECTED
236 236 <pre><code class="ruby CodeRay"><span class="no">1</span> <span class="c"># Some ruby code here</span>
237 237 </pre></code>
238 238 EXPECTED
239 239
240 240 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
241 241 end
242 242
243 243 def test_wiki_links_in_tables
244 244 to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
245 '<tr><td><a href="/wiki/ecookbook/Page" class="wiki-page new">Link title</a></td>' +
246 '<td><a href="/wiki/ecookbook/Other_Page" class="wiki-page new">Other title</a></td>' +
247 '</tr><tr><td>Cell 21</td><td><a href="/wiki/ecookbook/Last_page" class="wiki-page new">Last page</a></td></tr>'
245 '<tr><td><a href="/projects/ecookbook/wiki/Page" class="wiki-page new">Link title</a></td>' +
246 '<td><a href="/projects/ecookbook/wiki/Other_Page" class="wiki-page new">Other title</a></td>' +
247 '</tr><tr><td>Cell 21</td><td><a href="/projects/ecookbook/wiki/Last_page" class="wiki-page new">Last page</a></td></tr>'
248 248 }
249 249 @project = Project.find(1)
250 250 to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
251 251 end
252 252
253 253 def test_text_formatting
254 254 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
255 255 '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
256 256 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
257 257 'a H *umane* W *eb* T *ext* G *enerator*' => 'a H <strong>umane</strong> W <strong>eb</strong> T <strong>ext</strong> G <strong>enerator</strong>',
258 258 'a *H* umane *W* eb *T* ext *G* enerator' => 'a <strong>H</strong> umane <strong>W</strong> eb <strong>T</strong> ext <strong>G</strong> enerator',
259 259 }
260 260 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
261 261 end
262 262
263 263 def test_wiki_horizontal_rule
264 264 assert_equal '<hr />', textilizable('---')
265 265 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
266 266 end
267 267
268 268 def test_acronym
269 269 assert_equal '<p>This is an acronym: <acronym title="American Civil Liberties Union">ACLU</acronym>.</p>',
270 270 textilizable('This is an acronym: ACLU(American Civil Liberties Union).')
271 271 end
272 272
273 273 def test_footnotes
274 274 raw = <<-RAW
275 275 This is some text[1].
276 276
277 277 fn1. This is the foot note
278 278 RAW
279 279
280 280 expected = <<-EXPECTED
281 281 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
282 282 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
283 283 EXPECTED
284 284
285 285 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
286 286 end
287 287
288 288 def test_table_of_content
289 289 raw = <<-RAW
290 290 {{toc}}
291 291
292 292 h1. Title
293 293
294 294 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
295 295
296 296 h2. Subtitle
297 297
298 298 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
299 299
300 300 h2. Subtitle with %{color:red}red text%
301 301
302 302 h1. Another title
303 303
304 304 RAW
305 305
306 306 expected = '<ul class="toc">' +
307 307 '<li class="heading1"><a href="#Title">Title</a></li>' +
308 308 '<li class="heading2"><a href="#Subtitle">Subtitle</a></li>' +
309 309 '<li class="heading2"><a href="#Subtitle-with-red-text">Subtitle with red text</a></li>' +
310 310 '<li class="heading1"><a href="#Another-title">Another title</a></li>' +
311 311 '</ul>'
312 312
313 313 assert textilizable(raw).gsub("\n", "").include?(expected)
314 314 end
315 315
316 316 def test_blockquote
317 317 # orig raw text
318 318 raw = <<-RAW
319 319 John said:
320 320 > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
321 321 > Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
322 322 > * Donec odio lorem,
323 323 > * sagittis ac,
324 324 > * malesuada in,
325 325 > * adipiscing eu, dolor.
326 326 >
327 327 > >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
328 328 > Proin a tellus. Nam vel neque.
329 329
330 330 He's right.
331 331 RAW
332 332
333 333 # expected html
334 334 expected = <<-EXPECTED
335 335 <p>John said:</p>
336 336 <blockquote>
337 337 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
338 338 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
339 339 <ul>
340 340 <li>Donec odio lorem,</li>
341 341 <li>sagittis ac,</li>
342 342 <li>malesuada in,</li>
343 343 <li>adipiscing eu, dolor.</li>
344 344 </ul>
345 345 <blockquote>
346 346 <p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
347 347 </blockquote>
348 348 <p>Proin a tellus. Nam vel neque.</p>
349 349 </blockquote>
350 350 <p>He's right.</p>
351 351 EXPECTED
352 352
353 353 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
354 354 end
355 355
356 356 def test_table
357 357 raw = <<-RAW
358 358 This is a table with empty cells:
359 359
360 360 |cell11|cell12||
361 361 |cell21||cell23|
362 362 |cell31|cell32|cell33|
363 363 RAW
364 364
365 365 expected = <<-EXPECTED
366 366 <p>This is a table with empty cells:</p>
367 367
368 368 <table>
369 369 <tr><td>cell11</td><td>cell12</td><td></td></tr>
370 370 <tr><td>cell21</td><td></td><td>cell23</td></tr>
371 371 <tr><td>cell31</td><td>cell32</td><td>cell33</td></tr>
372 372 </table>
373 373 EXPECTED
374 374
375 375 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
376 376 end
377 377
378 378 def test_default_formatter
379 379 Setting.text_formatting = 'unknown'
380 380 text = 'a *link*: http://www.example.net/'
381 381 assert_equal '<p>a *link*: <a href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
382 382 Setting.text_formatting = 'textile'
383 383 end
384 384
385 385 def test_date_format_default
386 386 today = Date.today
387 387 Setting.date_format = ''
388 388 assert_equal l_date(today), format_date(today)
389 389 end
390 390
391 391 def test_date_format
392 392 today = Date.today
393 393 Setting.date_format = '%d %m %Y'
394 394 assert_equal today.strftime('%d %m %Y'), format_date(today)
395 395 end
396 396
397 397 def test_time_format_default
398 398 now = Time.now
399 399 Setting.date_format = ''
400 400 Setting.time_format = ''
401 401 assert_equal l_datetime(now), format_time(now)
402 402 assert_equal l_time(now), format_time(now, false)
403 403 end
404 404
405 405 def test_time_format
406 406 now = Time.now
407 407 Setting.date_format = '%d %m %Y'
408 408 Setting.time_format = '%H %M'
409 409 assert_equal now.strftime('%d %m %Y %H %M'), format_time(now)
410 410 assert_equal now.strftime('%H %M'), format_time(now, false)
411 411 end
412 412
413 413 def test_utc_time_format
414 414 now = Time.now.utc
415 415 Setting.date_format = '%d %m %Y'
416 416 Setting.time_format = '%H %M'
417 417 assert_equal Time.now.strftime('%d %m %Y %H %M'), format_time(now)
418 418 assert_equal Time.now.strftime('%H %M'), format_time(now, false)
419 419 end
420 420
421 421 def test_due_date_distance_in_words
422 422 to_test = { Date.today => 'Due in 0 days',
423 423 Date.today + 1 => 'Due in 1 day',
424 424 Date.today + 100 => 'Due in 100 days',
425 425 Date.today + 20000 => 'Due in 20000 days',
426 426 Date.today - 1 => '1 day late',
427 427 Date.today - 100 => '100 days late',
428 428 Date.today - 20000 => '20000 days late',
429 429 }
430 430 to_test.each do |date, expected|
431 431 assert_equal expected, due_date_distance_in_words(date)
432 432 end
433 433 end
434 434
435 435 def test_avatar
436 436 # turn on avatars
437 437 Setting.gravatar_enabled = '1'
438 438 assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
439 439 assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
440 440 assert_nil avatar('jsmith')
441 441 assert_nil avatar(nil)
442 442
443 443 # turn off avatars
444 444 Setting.gravatar_enabled = '0'
445 445 assert_nil avatar(User.find_by_mail('jsmith@somenet.foo'))
446 446 end
447 447 end
@@ -1,98 +1,98
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
20 20 class Redmine::WikiFormatting::MacrosTest < HelperTestCase
21 21 include ApplicationHelper
22 22 include ActionView::Helpers::TextHelper
23 23 fixtures :projects, :roles, :enabled_modules, :users,
24 24 :repositories, :changesets,
25 25 :trackers, :issue_statuses, :issues,
26 26 :versions, :documents,
27 27 :wikis, :wiki_pages, :wiki_contents,
28 28 :boards, :messages,
29 29 :attachments
30 30
31 31 def setup
32 32 super
33 33 @project = nil
34 34 end
35 35
36 36 def teardown
37 37 end
38 38
39 39 def test_macro_hello_world
40 40 text = "{{hello_world}}"
41 41 assert textilizable(text).match(/Hello world!/)
42 42 # escaping
43 43 text = "!{{hello_world}}"
44 44 assert_equal '<p>{{hello_world}}</p>', textilizable(text)
45 45 end
46 46
47 47 def test_macro_include
48 48 @project = Project.find(1)
49 49 # include a page of the current project wiki
50 50 text = "{{include(Another page)}}"
51 51 assert textilizable(text).match(/This is a link to a ticket/)
52 52
53 53 @project = nil
54 54 # include a page of a specific project wiki
55 55 text = "{{include(ecookbook:Another page)}}"
56 56 assert textilizable(text).match(/This is a link to a ticket/)
57 57
58 58 text = "{{include(ecookbook:)}}"
59 59 assert textilizable(text).match(/CookBook documentation/)
60 60
61 61 text = "{{include(unknowidentifier:somepage)}}"
62 62 assert textilizable(text).match(/Page not found/)
63 63 end
64 64
65 65 def test_macro_child_pages
66 66 expected = "<p><ul class=\"pages-hierarchy\">\n" +
67 "<li><a href=\"/wiki/ecookbook/Child_1\">Child 1</a></li>\n" +
68 "<li><a href=\"/wiki/ecookbook/Child_2\">Child 2</a></li>\n" +
67 "<li><a href=\"/projects/ecookbook/wiki/Child_1\">Child 1</a></li>\n" +
68 "<li><a href=\"/projects/ecookbook/wiki/Child_2\">Child 2</a></li>\n" +
69 69 "</ul>\n</p>"
70 70
71 71 @project = Project.find(1)
72 72 # child pages of the current wiki page
73 73 assert_equal expected, textilizable("{{child_pages}}", :object => WikiPage.find(2).content)
74 74 # child pages of another page
75 75 assert_equal expected, textilizable("{{child_pages(Another_page)}}", :object => WikiPage.find(1).content)
76 76
77 77 @project = Project.find(2)
78 78 assert_equal expected, textilizable("{{child_pages(ecookbook:Another_page)}}", :object => WikiPage.find(1).content)
79 79 end
80 80
81 81 def test_macro_child_pages_with_option
82 82 expected = "<p><ul class=\"pages-hierarchy\">\n" +
83 "<li><a href=\"/wiki/ecookbook/Another_page\">Another page</a>\n" +
83 "<li><a href=\"/projects/ecookbook/wiki/Another_page\">Another page</a>\n" +
84 84 "<ul class=\"pages-hierarchy\">\n" +
85 "<li><a href=\"/wiki/ecookbook/Child_1\">Child 1</a></li>\n" +
86 "<li><a href=\"/wiki/ecookbook/Child_2\">Child 2</a></li>\n" +
85 "<li><a href=\"/projects/ecookbook/wiki/Child_1\">Child 1</a></li>\n" +
86 "<li><a href=\"/projects/ecookbook/wiki/Child_2\">Child 2</a></li>\n" +
87 87 "</ul>\n</li>\n</ul>\n</p>"
88 88
89 89 @project = Project.find(1)
90 90 # child pages of the current wiki page
91 91 assert_equal expected, textilizable("{{child_pages(parent=1)}}", :object => WikiPage.find(2).content)
92 92 # child pages of another page
93 93 assert_equal expected, textilizable("{{child_pages(Another_page, parent=1)}}", :object => WikiPage.find(1).content)
94 94
95 95 @project = Project.find(2)
96 96 assert_equal expected, textilizable("{{child_pages(ecookbook:Another_page, parent=1)}}", :object => WikiPage.find(1).content)
97 97 end
98 98 end
@@ -1,224 +1,224
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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
20 20 class MailerTest < Test::Unit::TestCase
21 21 fixtures :projects, :issues, :users, :members, :documents, :attachments, :news, :tokens, :journals, :journal_details, :changesets, :trackers, :issue_statuses, :enumerations, :messages, :boards, :repositories
22 22
23 23 def test_generated_links_in_emails
24 24 ActionMailer::Base.deliveries.clear
25 25 Setting.host_name = 'mydomain.foo'
26 26 Setting.protocol = 'https'
27 27
28 28 journal = Journal.find(2)
29 29 assert Mailer.deliver_issue_edit(journal)
30 30
31 31 mail = ActionMailer::Base.deliveries.last
32 32 assert_kind_of TMail::Mail, mail
33 33 # link to the main ticket
34 assert mail.body.include?('<a href="https://mydomain.foo/issues/show/1">Bug #1: Can\'t print recipes</a>')
34 assert mail.body.include?('<a href="https://mydomain.foo/issues/1">Bug #1: Can\'t print recipes</a>')
35 35
36 36 # link to a referenced ticket
37 assert mail.body.include?('<a href="https://mydomain.foo/issues/show/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
37 assert mail.body.include?('<a href="https://mydomain.foo/issues/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
38 38 # link to a changeset
39 assert mail.body.include?('<a href="https://mydomain.foo/repositories/revision/ecookbook/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
39 assert mail.body.include?('<a href="https://mydomain.foo/projects/ecookbook/repository/revisions/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
40 40 end
41 41
42 42 def test_generated_links_with_prefix
43 43 relative_url_root = Redmine::Utils.relative_url_root
44 44 ActionMailer::Base.deliveries.clear
45 45 Setting.host_name = 'mydomain.foo/rdm'
46 46 Setting.protocol = 'http'
47 47 Redmine::Utils.relative_url_root = '/rdm'
48 48
49 49 journal = Journal.find(2)
50 50 assert Mailer.deliver_issue_edit(journal)
51 51
52 52 mail = ActionMailer::Base.deliveries.last
53 53 assert_kind_of TMail::Mail, mail
54 54 # link to the main ticket
55 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/1">Bug #1: Can\'t print recipes</a>')
55 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/1">Bug #1: Can\'t print recipes</a>')
56 56
57 57 # link to a referenced ticket
58 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
58 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
59 59 # link to a changeset
60 assert mail.body.include?('<a href="http://mydomain.foo/rdm/repositories/revision/ecookbook/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
60 assert mail.body.include?('<a href="http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
61 61 ensure
62 62 # restore it
63 63 Redmine::Utils.relative_url_root = relative_url_root
64 64 end
65 65
66 66 def test_generated_links_with_prefix_and_no_relative_url_root
67 67 relative_url_root = Redmine::Utils.relative_url_root
68 68 ActionMailer::Base.deliveries.clear
69 69 Setting.host_name = 'mydomain.foo/rdm'
70 70 Setting.protocol = 'http'
71 71 Redmine::Utils.relative_url_root = nil
72 72
73 73 journal = Journal.find(2)
74 74 assert Mailer.deliver_issue_edit(journal)
75 75
76 76 mail = ActionMailer::Base.deliveries.last
77 77 assert_kind_of TMail::Mail, mail
78 78 # link to the main ticket
79 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/1">Bug #1: Can\'t print recipes</a>')
79 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/1">Bug #1: Can\'t print recipes</a>')
80 80
81 81 # link to a referenced ticket
82 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
82 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
83 83 # link to a changeset
84 assert mail.body.include?('<a href="http://mydomain.foo/rdm/repositories/revision/ecookbook/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
84 assert mail.body.include?('<a href="http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
85 85 ensure
86 86 # restore it
87 87 Redmine::Utils.relative_url_root = relative_url_root
88 88 end
89 89
90 90 def test_plain_text_mail
91 91 Setting.plain_text_mail = 1
92 92 journal = Journal.find(2)
93 93 Mailer.deliver_issue_edit(journal)
94 94 mail = ActionMailer::Base.deliveries.last
95 assert !mail.body.include?('<a href="https://mydomain.foo/issues/show/1">Bug #1: Can\'t print recipes</a>')
95 assert !mail.body.include?('<a href="https://mydomain.foo/issues/1">Bug #1: Can\'t print recipes</a>')
96 96 end
97 97
98 98 def test_issue_add_message_id
99 99 ActionMailer::Base.deliveries.clear
100 100 issue = Issue.find(1)
101 101 Mailer.deliver_issue_add(issue)
102 102 mail = ActionMailer::Base.deliveries.last
103 103 assert_not_nil mail
104 104 assert_equal Mailer.message_id_for(issue), mail.message_id
105 105 assert_nil mail.references
106 106 end
107 107
108 108 def test_issue_edit_message_id
109 109 ActionMailer::Base.deliveries.clear
110 110 journal = Journal.find(1)
111 111 Mailer.deliver_issue_edit(journal)
112 112 mail = ActionMailer::Base.deliveries.last
113 113 assert_not_nil mail
114 114 assert_equal Mailer.message_id_for(journal), mail.message_id
115 115 assert_equal Mailer.message_id_for(journal.issue), mail.references.to_s
116 116 end
117 117
118 118 def test_message_posted_message_id
119 119 ActionMailer::Base.deliveries.clear
120 120 message = Message.find(1)
121 121 Mailer.deliver_message_posted(message, message.author.mail)
122 122 mail = ActionMailer::Base.deliveries.last
123 123 assert_not_nil mail
124 124 assert_equal Mailer.message_id_for(message), mail.message_id
125 125 assert_nil mail.references
126 126 end
127 127
128 128 def test_reply_posted_message_id
129 129 ActionMailer::Base.deliveries.clear
130 130 message = Message.find(3)
131 131 Mailer.deliver_message_posted(message, message.author.mail)
132 132 mail = ActionMailer::Base.deliveries.last
133 133 assert_not_nil mail
134 134 assert_equal Mailer.message_id_for(message), mail.message_id
135 135 assert_equal Mailer.message_id_for(message.parent), mail.references.to_s
136 136 end
137 137
138 138 # test mailer methods for each language
139 139 def test_issue_add
140 140 issue = Issue.find(1)
141 141 GLoc.valid_languages.each do |lang|
142 142 Setting.default_language = lang.to_s
143 143 assert Mailer.deliver_issue_add(issue)
144 144 end
145 145 end
146 146
147 147 def test_issue_edit
148 148 journal = Journal.find(1)
149 149 GLoc.valid_languages.each do |lang|
150 150 Setting.default_language = lang.to_s
151 151 assert Mailer.deliver_issue_edit(journal)
152 152 end
153 153 end
154 154
155 155 def test_document_added
156 156 document = Document.find(1)
157 157 GLoc.valid_languages.each do |lang|
158 158 Setting.default_language = lang.to_s
159 159 assert Mailer.deliver_document_added(document)
160 160 end
161 161 end
162 162
163 163 def test_attachments_added
164 164 attachements = [ Attachment.find_by_container_type('Document') ]
165 165 GLoc.valid_languages.each do |lang|
166 166 Setting.default_language = lang.to_s
167 167 assert Mailer.deliver_attachments_added(attachements)
168 168 end
169 169 end
170 170
171 171 def test_news_added
172 172 news = News.find(:first)
173 173 GLoc.valid_languages.each do |lang|
174 174 Setting.default_language = lang.to_s
175 175 assert Mailer.deliver_news_added(news)
176 176 end
177 177 end
178 178
179 179 def test_message_posted
180 180 message = Message.find(:first)
181 181 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
182 182 recipients = recipients.compact.uniq
183 183 GLoc.valid_languages.each do |lang|
184 184 Setting.default_language = lang.to_s
185 185 assert Mailer.deliver_message_posted(message, recipients)
186 186 end
187 187 end
188 188
189 189 def test_account_information
190 190 user = User.find(:first)
191 191 GLoc.valid_languages.each do |lang|
192 192 user.update_attribute :language, lang.to_s
193 193 user.reload
194 194 assert Mailer.deliver_account_information(user, 'pAsswORd')
195 195 end
196 196 end
197 197
198 198 def test_lost_password
199 199 token = Token.find(2)
200 200 GLoc.valid_languages.each do |lang|
201 201 token.user.update_attribute :language, lang.to_s
202 202 token.reload
203 203 assert Mailer.deliver_lost_password(token)
204 204 end
205 205 end
206 206
207 207 def test_register
208 208 token = Token.find(1)
209 209 GLoc.valid_languages.each do |lang|
210 210 token.user.update_attribute :language, lang.to_s
211 211 token.reload
212 212 assert Mailer.deliver_register(token)
213 213 end
214 214 end
215 215
216 216 def test_reminders
217 217 ActionMailer::Base.deliveries.clear
218 218 Mailer.reminders(:days => 42)
219 219 assert_equal 1, ActionMailer::Base.deliveries.size
220 220 mail = ActionMailer::Base.deliveries.last
221 221 assert mail.bcc.include?('dlopper@somenet.foo')
222 222 assert mail.body.include?('Bug #3: Error 281 when updating a recipe')
223 223 end
224 224 end
General Comments 0
You need to be logged in to leave comments. Login now