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