##// END OF EJS Templates
Reset locale to "en" before helpers tests....
Jean-Philippe Lang -
r15850:09f4a7d3f68c
parent child
Show More
@@ -1,404 +1,407
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 if ENV["COVERAGE"]
19 19 require 'simplecov'
20 20 require File.expand_path(File.dirname(__FILE__) + "/coverage/html_formatter")
21 21 SimpleCov.formatter = Redmine::Coverage::HtmlFormatter
22 22 SimpleCov.start 'rails'
23 23 end
24 24
25 25 $redmine_test_ldap_server = ENV['REDMINE_TEST_LDAP_SERVER'] || '127.0.0.1'
26 26
27 27 ENV["RAILS_ENV"] = "test"
28 28 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
29 29 require 'rails/test_help'
30 30 require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s
31 31
32 32 require File.expand_path(File.dirname(__FILE__) + '/object_helpers')
33 33 include ObjectHelpers
34 34
35 35 require 'net/ldap'
36 36 require 'mocha/setup'
37 37
38 38 Redmine::SudoMode.disable!
39 39
40 40 class ActionView::TestCase
41 41 helper :application
42 42 include ApplicationHelper
43 43 end
44 44
45 45 class ActiveSupport::TestCase
46 46 include ActionDispatch::TestProcess
47 47
48 48 self.use_transactional_fixtures = true
49 49 self.use_instantiated_fixtures = false
50 50
51 51 def uploaded_test_file(name, mime)
52 52 fixture_file_upload("files/#{name}", mime, true)
53 53 end
54 54
55 55 # Mock out a file
56 56 def self.mock_file
57 57 file = 'a_file.png'
58 58 file.stubs(:size).returns(32)
59 59 file.stubs(:original_filename).returns('a_file.png')
60 60 file.stubs(:content_type).returns('image/png')
61 61 file.stubs(:read).returns(false)
62 62 file
63 63 end
64 64
65 65 def mock_file
66 66 self.class.mock_file
67 67 end
68 68
69 69 def mock_file_with_options(options={})
70 70 file = ''
71 71 file.stubs(:size).returns(32)
72 72 original_filename = options[:original_filename] || nil
73 73 file.stubs(:original_filename).returns(original_filename)
74 74 content_type = options[:content_type] || nil
75 75 file.stubs(:content_type).returns(content_type)
76 76 file.stubs(:read).returns(false)
77 77 file
78 78 end
79 79
80 80 # Use a temporary directory for attachment related tests
81 81 def set_tmp_attachments_directory
82 82 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
83 83 unless File.directory?("#{Rails.root}/tmp/test/attachments")
84 84 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
85 85 end
86 86 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
87 87 end
88 88
89 89 def set_fixtures_attachments_directory
90 90 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
91 91 end
92 92
93 93 def with_settings(options, &block)
94 94 saved_settings = options.keys.inject({}) do |h, k|
95 95 h[k] = case Setting[k]
96 96 when Symbol, false, true, nil
97 97 Setting[k]
98 98 else
99 99 Setting[k].dup
100 100 end
101 101 h
102 102 end
103 103 options.each {|k, v| Setting[k] = v}
104 104 yield
105 105 ensure
106 106 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
107 107 end
108 108
109 109 # Yields the block with user as the current user
110 110 def with_current_user(user, &block)
111 111 saved_user = User.current
112 112 User.current = user
113 113 yield
114 114 ensure
115 115 User.current = saved_user
116 116 end
117 117
118 118 def with_locale(locale, &block)
119 119 saved_localed = ::I18n.locale
120 120 ::I18n.locale = locale
121 121 yield
122 122 ensure
123 123 ::I18n.locale = saved_localed
124 124 end
125 125
126 126 def self.ldap_configured?
127 127 @test_ldap = Net::LDAP.new(:host => $redmine_test_ldap_server, :port => 389)
128 128 return @test_ldap.bind
129 129 rescue Exception => e
130 130 # LDAP is not listening
131 131 return nil
132 132 end
133 133
134 134 def self.convert_installed?
135 135 Redmine::Thumbnail.convert_available?
136 136 end
137 137
138 138 def convert_installed?
139 139 self.class.convert_installed?
140 140 end
141 141
142 142 # Returns the path to the test +vendor+ repository
143 143 def self.repository_path(vendor)
144 144 path = Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
145 145 # Unlike ruby, JRuby returns Rails.root with backslashes under Windows
146 146 path.tr("\\", "/")
147 147 end
148 148
149 149 # Returns the url of the subversion test repository
150 150 def self.subversion_repository_url
151 151 path = repository_path('subversion')
152 152 path = '/' + path unless path.starts_with?('/')
153 153 "file://#{path}"
154 154 end
155 155
156 156 # Returns true if the +vendor+ test repository is configured
157 157 def self.repository_configured?(vendor)
158 158 File.directory?(repository_path(vendor))
159 159 end
160 160
161 161 def repository_path_hash(arr)
162 162 hs = {}
163 163 hs[:path] = arr.join("/")
164 164 hs[:param] = arr.join("/")
165 165 hs
166 166 end
167 167
168 168 def sqlite?
169 169 ActiveRecord::Base.connection.adapter_name =~ /sqlite/i
170 170 end
171 171
172 172 def mysql?
173 173 ActiveRecord::Base.connection.adapter_name =~ /mysql/i
174 174 end
175 175
176 176 def postgresql?
177 177 ActiveRecord::Base.connection.adapter_name =~ /postgresql/i
178 178 end
179 179
180 180 def quoted_date(date)
181 181 date = Date.parse(date) if date.is_a?(String)
182 182 ActiveRecord::Base.connection.quoted_date(date)
183 183 end
184 184
185 185 # Asserts that a new record for the given class is created
186 186 # and returns it
187 187 def new_record(klass, &block)
188 188 new_records(klass, 1, &block).first
189 189 end
190 190
191 191 # Asserts that count new records for the given class are created
192 192 # and returns them as an array order by object id
193 193 def new_records(klass, count, &block)
194 194 assert_difference "#{klass}.count", count do
195 195 yield
196 196 end
197 197 klass.order(:id => :desc).limit(count).to_a.reverse
198 198 end
199 199
200 200 def assert_save(object)
201 201 saved = object.save
202 202 message = "#{object.class} could not be saved"
203 203 errors = object.errors.full_messages.map {|m| "- #{m}"}
204 204 message << ":\n#{errors.join("\n")}" if errors.any?
205 205 assert_equal true, saved, message
206 206 end
207 207
208 208 def assert_select_error(arg)
209 209 assert_select '#errorExplanation', :text => arg
210 210 end
211 211
212 212 def assert_include(expected, s, message=nil)
213 213 assert s.include?(expected), (message || "\"#{expected}\" not found in \"#{s}\"")
214 214 end
215 215
216 216 def assert_not_include(expected, s, message=nil)
217 217 assert !s.include?(expected), (message || "\"#{expected}\" found in \"#{s}\"")
218 218 end
219 219
220 220 def assert_select_in(text, *args, &block)
221 221 d = Nokogiri::HTML(CGI::unescapeHTML(String.new(text))).root
222 222 assert_select(d, *args, &block)
223 223 end
224 224
225 225 def assert_select_email(*args, &block)
226 226 email = ActionMailer::Base.deliveries.last
227 227 assert_not_nil email
228 228 html_body = email.parts.detect {|part| part.content_type.include?('text/html')}.try(&:body)
229 229 assert_not_nil html_body
230 230 assert_select_in html_body.encoded, *args, &block
231 231 end
232 232
233 233 def assert_mail_body_match(expected, mail, message=nil)
234 234 if expected.is_a?(String)
235 235 assert_include expected, mail_body(mail), message
236 236 else
237 237 assert_match expected, mail_body(mail), message
238 238 end
239 239 end
240 240
241 241 def assert_mail_body_no_match(expected, mail, message=nil)
242 242 if expected.is_a?(String)
243 243 assert_not_include expected, mail_body(mail), message
244 244 else
245 245 assert_no_match expected, mail_body(mail), message
246 246 end
247 247 end
248 248
249 249 def mail_body(mail)
250 250 mail.parts.first.body.encoded
251 251 end
252 252
253 253 # Returns the lft value for a new root issue
254 254 def new_issue_lft
255 255 1
256 256 end
257 257 end
258 258
259 259 module Redmine
260 260 class RoutingTest < ActionDispatch::IntegrationTest
261 261 def should_route(arg)
262 262 arg = arg.dup
263 263 request = arg.keys.detect {|key| key.is_a?(String)}
264 264 raise ArgumentError unless request
265 265 options = arg.slice!(request)
266 266
267 267 raise ArgumentError unless request =~ /\A(GET|POST|PUT|PATCH|DELETE)\s+(.+)\z/
268 268 method, path = $1.downcase.to_sym, $2
269 269
270 270 raise ArgumentError unless arg.values.first =~ /\A(.+)#(.+)\z/
271 271 controller, action = $1, $2
272 272
273 273 assert_routing(
274 274 {:method => method, :path => path},
275 275 options.merge(:controller => controller, :action => action)
276 276 )
277 277 end
278 278 end
279 279
280 280 class HelperTest < ActionView::TestCase
281 include Redmine::I18n
282
281 283 def setup
282 284 super
283 285 User.current = nil
286 ::I18n.locale = 'en'
284 287 end
285 288 end
286 289
287 290 class ControllerTest < ActionController::TestCase
288 291 # Returns the issues that are displayed in the list in the same order
289 292 def issues_in_list
290 293 ids = css_select('tr.issue td.id').map(&:text).map(&:to_i)
291 294 Issue.where(:id => ids).sort_by {|issue| ids.index(issue.id)}
292 295 end
293 296
294 297 # Return the columns that are displayed in the list
295 298 def columns_in_issues_list
296 299 css_select('table.issues thead th:not(.checkbox)').map(&:text)
297 300 end
298 301
299 302 # Verifies that the query filters match the expected filters
300 303 def assert_query_filters(expected_filters)
301 304 response.body =~ /initFilters\(\);\s*((addFilter\(.+\);\s*)*)/
302 305 filter_init = $1.to_s
303 306
304 307 expected_filters.each do |field, operator, values|
305 308 s = "addFilter(#{field.to_json}, #{operator.to_json}, #{Array(values).to_json});"
306 309 assert_include s, filter_init
307 310 end
308 311 assert_equal expected_filters.size, filter_init.scan("addFilter").size, "filters counts don't match"
309 312 end
310 313
311 314 def process(method, path, parameters={}, session={}, flash={})
312 315 if parameters.key?(:params) || parameters.key?(:session)
313 316 raise ArgumentError if session.present?
314 317 super method, path, parameters[:params], parameters[:session], parameters.except(:params, :session)
315 318 else
316 319 super
317 320 end
318 321 end
319 322 end
320 323
321 324 class IntegrationTest < ActionDispatch::IntegrationTest
322 325 def log_user(login, password)
323 326 User.anonymous
324 327 get "/login"
325 328 assert_nil session[:user_id]
326 329 assert_response :success
327 330
328 331 post "/login", :username => login, :password => password
329 332 assert_equal login, User.find(session[:user_id]).login
330 333 end
331 334
332 335 def credentials(user, password=nil)
333 336 {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)}
334 337 end
335 338 end
336 339
337 340 module ApiTest
338 341 API_FORMATS = %w(json xml).freeze
339 342
340 343 # Base class for API tests
341 344 class Base < Redmine::IntegrationTest
342 345 def setup
343 346 Setting.rest_api_enabled = '1'
344 347 end
345 348
346 349 def teardown
347 350 Setting.rest_api_enabled = '0'
348 351 end
349 352
350 353 # Uploads content using the XML API and returns the attachment token
351 354 def xml_upload(content, credentials)
352 355 upload('xml', content, credentials)
353 356 end
354 357
355 358 # Uploads content using the JSON API and returns the attachment token
356 359 def json_upload(content, credentials)
357 360 upload('json', content, credentials)
358 361 end
359 362
360 363 def upload(format, content, credentials)
361 364 set_tmp_attachments_directory
362 365 assert_difference 'Attachment.count' do
363 366 post "/uploads.#{format}", content, {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials)
364 367 assert_response :created
365 368 end
366 369 data = response_data
367 370 assert_kind_of Hash, data['upload']
368 371 token = data['upload']['token']
369 372 assert_not_nil token
370 373 token
371 374 end
372 375
373 376 # Parses the response body based on its content type
374 377 def response_data
375 378 unless response.content_type.to_s =~ /^application\/(.+)/
376 379 raise "Unexpected response type: #{response.content_type}"
377 380 end
378 381 format = $1
379 382 case format
380 383 when 'xml'
381 384 Hash.from_xml(response.body)
382 385 when 'json'
383 386 ActiveSupport::JSON.decode(response.body)
384 387 else
385 388 raise "Unknown response format: #{format}"
386 389 end
387 390 end
388 391 end
389 392
390 393 class Routing < Redmine::RoutingTest
391 394 def should_route(arg)
392 395 arg = arg.dup
393 396 request = arg.keys.detect {|key| key.is_a?(String)}
394 397 raise ArgumentError unless request
395 398 options = arg.slice!(request)
396 399
397 400 API_FORMATS.each do |format|
398 401 format_request = request.sub /$/, ".#{format}"
399 402 super options.merge(format_request => arg[request], :format => format)
400 403 end
401 404 end
402 405 end
403 406 end
404 407 end
@@ -1,103 +1,102
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class ActivitiesHelperTest < Redmine::HelperTest
21 21 include ActivitiesHelper
22 include Redmine::I18n
23 22
24 23 class MockEvent
25 24 attr_reader :event_datetime, :event_group, :name
26 25
27 26 def initialize(group=nil)
28 27 @@count ||= 0
29 28 @name = "e#{@@count}"
30 29 @event_datetime = Time.now + @@count.hours
31 30 @event_group = group || self
32 31 @@count += 1
33 32 end
34 33
35 34 def self.clear
36 35 @@count = 0
37 36 end
38 37 end
39 38
40 39 def setup
41 40 super
42 41 MockEvent.clear
43 42 end
44 43
45 44 def test_sort_activity_events_should_sort_by_datetime
46 45 events = []
47 46 events << MockEvent.new
48 47 events << MockEvent.new
49 48 events << MockEvent.new
50 49
51 50 assert_equal [
52 51 ['e2', false],
53 52 ['e1', false],
54 53 ['e0', false]
55 54 ], sort_activity_events(events).map {|event, grouped| [event.name, grouped]}
56 55 end
57 56
58 57 def test_sort_activity_events_should_group_events
59 58 events = []
60 59 events << MockEvent.new
61 60 events << MockEvent.new(events[0])
62 61 events << MockEvent.new(events[0])
63 62
64 63 assert_equal [
65 64 ['e2', false],
66 65 ['e1', true],
67 66 ['e0', true]
68 67 ], sort_activity_events(events).map {|event, grouped| [event.name, grouped]}
69 68 end
70 69
71 70 def test_sort_activity_events_with_group_not_in_set_should_group_events
72 71 e = MockEvent.new
73 72 events = []
74 73 events << MockEvent.new(e)
75 74 events << MockEvent.new(e)
76 75
77 76 assert_equal [
78 77 ['e2', false],
79 78 ['e1', true]
80 79 ], sort_activity_events(events).map {|event, grouped| [event.name, grouped]}
81 80 end
82 81
83 82 def test_sort_activity_events_should_sort_by_datetime_and_group
84 83 events = []
85 84 events << MockEvent.new
86 85 events << MockEvent.new
87 86 events << MockEvent.new
88 87 events << MockEvent.new(events[1])
89 88 events << MockEvent.new(events[2])
90 89 events << MockEvent.new
91 90 events << MockEvent.new(events[2])
92 91
93 92 assert_equal [
94 93 ['e6', false],
95 94 ['e4', true],
96 95 ['e2', true],
97 96 ['e5', false],
98 97 ['e3', false],
99 98 ['e1', true],
100 99 ['e0', false]
101 100 ], sort_activity_events(events).map {|event, grouped| [event.name, grouped]}
102 101 end
103 102 end
@@ -1,1576 +1,1575
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2016 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 require File.expand_path('../../../test_helper', __FILE__)
21 21
22 22 class ApplicationHelperTest < Redmine::HelperTest
23 include Redmine::I18n
24 23 include ERB::Util
25 24 include Rails.application.routes.url_helpers
26 25
27 26 fixtures :projects, :enabled_modules,
28 27 :users, :email_addresses,
29 28 :members, :member_roles, :roles,
30 29 :repositories, :changesets,
31 30 :projects_trackers,
32 31 :trackers, :issue_statuses, :issues, :versions, :documents,
33 32 :wikis, :wiki_pages, :wiki_contents,
34 33 :boards, :messages, :news,
35 34 :attachments, :enumerations
36 35
37 36 def setup
38 37 super
39 38 set_tmp_attachments_directory
40 39 @russian_test = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82".force_encoding('UTF-8')
41 40 end
42 41
43 42 test "#link_to_if_authorized for authorized user should allow using the :controller and :action for the target link" do
44 43 User.current = User.find_by_login('admin')
45 44
46 45 @project = Issue.first.project # Used by helper
47 46 response = link_to_if_authorized('By controller/actionr',
48 47 {:controller => 'issues', :action => 'edit', :id => Issue.first.id})
49 48 assert_match /href/, response
50 49 end
51 50
52 51 test "#link_to_if_authorized for unauthorized user should display nothing if user isn't authorized" do
53 52 User.current = User.find_by_login('dlopper')
54 53 @project = Project.find('private-child')
55 54 issue = @project.issues.first
56 55 assert !issue.visible?
57 56
58 57 response = link_to_if_authorized('Never displayed',
59 58 {:controller => 'issues', :action => 'show', :id => issue})
60 59 assert_nil response
61 60 end
62 61
63 62 def test_auto_links
64 63 to_test = {
65 64 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
66 65 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
67 66 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
68 67 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
69 68 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
70 69 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
71 70 '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>.',
72 71 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
73 72 '(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>)',
74 73 '(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>)',
75 74 '(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>).',
76 75 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
77 76 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
78 77 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
79 78 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
80 79 '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>',
81 80 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
82 81 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
83 82 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
84 83 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
85 84 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
86 85 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
87 86 # two exclamation marks
88 87 'http://example.net/path!602815048C7B5C20!302.html' => '<a class="external" href="http://example.net/path!602815048C7B5C20!302.html">http://example.net/path!602815048C7B5C20!302.html</a>',
89 88 # escaping
90 89 'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo&quot;bar</a>',
91 90 # wrap in angle brackets
92 91 '<http://foo.bar>' => '&lt;<a class="external" href="http://foo.bar">http://foo.bar</a>&gt;',
93 92 # invalid urls
94 93 'http://' => 'http://',
95 94 'www.' => 'www.',
96 95 'test-www.bar.com' => 'test-www.bar.com',
97 96 }
98 97 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
99 98 end
100 99
101 100 def test_auto_links_with_non_ascii_characters
102 101 to_test = {
103 102 "http://foo.bar/#{@russian_test}" =>
104 103 %|<a class="external" href="http://foo.bar/#{@russian_test}">http://foo.bar/#{@russian_test}</a>|
105 104 }
106 105 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
107 106 end
108 107
109 108 def test_auto_mailto
110 109 to_test = {
111 110 'test@foo.bar' => '<a class="email" href="mailto:test@foo.bar">test@foo.bar</a>',
112 111 'test@www.foo.bar' => '<a class="email" href="mailto:test@www.foo.bar">test@www.foo.bar</a>',
113 112 }
114 113 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
115 114 end
116 115
117 116 def test_inline_images
118 117 to_test = {
119 118 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
120 119 'floating !>http://foo.bar/image.jpg!' => 'floating <span style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></span>',
121 120 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
122 121 'with style !{width:100px;height:100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height:100px;" alt="" />',
123 122 '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" />',
124 123 '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;" />',
125 124 }
126 125 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
127 126 end
128 127
129 128 def test_inline_images_inside_tags
130 129 raw = <<-RAW
131 130 h1. !foo.png! Heading
132 131
133 132 Centered image:
134 133
135 134 p=. !bar.gif!
136 135 RAW
137 136
138 137 assert textilizable(raw).include?('<img src="foo.png" alt="" />')
139 138 assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
140 139 end
141 140
142 141 def test_attached_images
143 142 to_test = {
144 143 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
145 144 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
146 145 'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
147 146 'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
148 147 # link image
149 148 '!logo.gif!:http://foo.bar/' => '<a href="http://foo.bar/"><img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" /></a>',
150 149 }
151 150 attachments = Attachment.all
152 151 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
153 152 end
154 153
155 154 def test_attached_images_with_textile_and_non_ascii_filename
156 155 attachment = Attachment.generate!(:filename => 'cafΓ©.jpg')
157 156 with_settings :text_formatting => 'textile' do
158 157 assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="" />),
159 158 textilizable("!cafΓ©.jpg!)", :attachments => [attachment])
160 159 end
161 160 end
162 161
163 162 def test_attached_images_with_markdown_and_non_ascii_filename
164 163 skip unless Object.const_defined?(:Redcarpet)
165 164
166 165 attachment = Attachment.generate!(:filename => 'cafΓ©.jpg')
167 166 with_settings :text_formatting => 'markdown' do
168 167 assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="" />),
169 168 textilizable("![](cafΓ©.jpg)", :attachments => [attachment])
170 169 end
171 170 end
172 171
173 172 def test_attached_images_filename_extension
174 173 set_tmp_attachments_directory
175 174 a1 = Attachment.new(
176 175 :container => Issue.find(1),
177 176 :file => mock_file_with_options({:original_filename => "testtest.JPG"}),
178 177 :author => User.find(1))
179 178 assert a1.save
180 179 assert_equal "testtest.JPG", a1.filename
181 180 assert_equal "image/jpeg", a1.content_type
182 181 assert a1.image?
183 182
184 183 a2 = Attachment.new(
185 184 :container => Issue.find(1),
186 185 :file => mock_file_with_options({:original_filename => "testtest.jpeg"}),
187 186 :author => User.find(1))
188 187 assert a2.save
189 188 assert_equal "testtest.jpeg", a2.filename
190 189 assert_equal "image/jpeg", a2.content_type
191 190 assert a2.image?
192 191
193 192 a3 = Attachment.new(
194 193 :container => Issue.find(1),
195 194 :file => mock_file_with_options({:original_filename => "testtest.JPE"}),
196 195 :author => User.find(1))
197 196 assert a3.save
198 197 assert_equal "testtest.JPE", a3.filename
199 198 assert_equal "image/jpeg", a3.content_type
200 199 assert a3.image?
201 200
202 201 a4 = Attachment.new(
203 202 :container => Issue.find(1),
204 203 :file => mock_file_with_options({:original_filename => "Testtest.BMP"}),
205 204 :author => User.find(1))
206 205 assert a4.save
207 206 assert_equal "Testtest.BMP", a4.filename
208 207 assert_equal "image/x-ms-bmp", a4.content_type
209 208 assert a4.image?
210 209
211 210 to_test = {
212 211 'Inline image: !testtest.jpg!' =>
213 212 'Inline image: <img src="/attachments/download/' + a1.id.to_s + '/testtest.JPG" alt="" />',
214 213 'Inline image: !testtest.jpeg!' =>
215 214 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testtest.jpeg" alt="" />',
216 215 'Inline image: !testtest.jpe!' =>
217 216 'Inline image: <img src="/attachments/download/' + a3.id.to_s + '/testtest.JPE" alt="" />',
218 217 'Inline image: !testtest.bmp!' =>
219 218 'Inline image: <img src="/attachments/download/' + a4.id.to_s + '/Testtest.BMP" alt="" />',
220 219 }
221 220
222 221 attachments = [a1, a2, a3, a4]
223 222 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
224 223 end
225 224
226 225 def test_attached_images_should_read_later
227 226 set_fixtures_attachments_directory
228 227 a1 = Attachment.find(16)
229 228 assert_equal "testfile.png", a1.filename
230 229 assert a1.readable?
231 230 assert (! a1.visible?(User.anonymous))
232 231 assert a1.visible?(User.find(2))
233 232 a2 = Attachment.find(17)
234 233 assert_equal "testfile.PNG", a2.filename
235 234 assert a2.readable?
236 235 assert (! a2.visible?(User.anonymous))
237 236 assert a2.visible?(User.find(2))
238 237 assert a1.created_on < a2.created_on
239 238
240 239 to_test = {
241 240 'Inline image: !testfile.png!' =>
242 241 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
243 242 'Inline image: !Testfile.PNG!' =>
244 243 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
245 244 }
246 245 attachments = [a1, a2]
247 246 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
248 247 set_tmp_attachments_directory
249 248 end
250 249
251 250 def test_textile_external_links
252 251 to_test = {
253 252 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
254 253 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
255 254 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
256 255 '"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>',
257 256 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
258 257 # no multiline link text
259 258 "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 />and another on a second line\":test",
260 259 # mailto link
261 260 "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
262 261 # two exclamation marks
263 262 '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
264 263 # escaping
265 264 '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
266 265 }
267 266 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
268 267 end
269 268
270 269 def test_textile_external_links_with_non_ascii_characters
271 270 to_test = {
272 271 %|This is a "link":http://foo.bar/#{@russian_test}| =>
273 272 %|This is a <a href="http://foo.bar/#{@russian_test}" class="external">link</a>|
274 273 }
275 274 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
276 275 end
277 276
278 277 def test_redmine_links
279 278 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
280 279 :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
281 280 note_link = link_to('#3-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'},
282 281 :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
283 282 note_link2 = link_to('#3#note-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'},
284 283 :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
285 284
286 285 revision_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
287 286 :class => 'changeset', :title => 'My very first commit do not escaping #<>&')
288 287 revision_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
289 288 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
290 289
291 290 changeset_link2 = link_to('691322a8eb01e11fd7',
292 291 {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
293 292 :class => 'changeset', :title => 'My very first commit do not escaping #<>&')
294 293
295 294 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
296 295 :class => 'document')
297 296
298 297 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
299 298 :class => 'version')
300 299
301 300 board_url = {:controller => 'boards', :action => 'show', :id => 2, :project_id => 'ecookbook'}
302 301
303 302 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
304 303
305 304 news_url = {:controller => 'news', :action => 'show', :id => 1}
306 305
307 306 project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
308 307
309 308 source_url = '/projects/ecookbook/repository/entry/some/file'
310 309 source_url_with_rev = '/projects/ecookbook/repository/revisions/52/entry/some/file'
311 310 source_url_with_ext = '/projects/ecookbook/repository/entry/some/file.ext'
312 311 source_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/entry/some/file.ext'
313 312 source_url_with_branch = '/projects/ecookbook/repository/revisions/branch/entry/some/file'
314 313
315 314 export_url = '/projects/ecookbook/repository/raw/some/file'
316 315 export_url_with_rev = '/projects/ecookbook/repository/revisions/52/raw/some/file'
317 316 export_url_with_ext = '/projects/ecookbook/repository/raw/some/file.ext'
318 317 export_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/raw/some/file.ext'
319 318 export_url_with_branch = '/projects/ecookbook/repository/revisions/branch/raw/some/file'
320 319
321 320 to_test = {
322 321 # tickets
323 322 '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
324 323 # ticket notes
325 324 '#3-14' => note_link,
326 325 '#3#note-14' => note_link2,
327 326 # should not ignore leading zero
328 327 '#03' => '#03',
329 328 # changesets
330 329 'r1' => revision_link,
331 330 'r1.' => "#{revision_link}.",
332 331 'r1, r2' => "#{revision_link}, #{revision_link2}",
333 332 'r1,r2' => "#{revision_link},#{revision_link2}",
334 333 'commit:691322a8eb01e11fd7' => changeset_link2,
335 334 # documents
336 335 'document#1' => document_link,
337 336 'document:"Test document"' => document_link,
338 337 # versions
339 338 'version#2' => version_link,
340 339 'version:1.0' => version_link,
341 340 'version:"1.0"' => version_link,
342 341 # source
343 342 'source:some/file' => link_to('source:some/file', source_url, :class => 'source'),
344 343 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
345 344 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
346 345 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
347 346 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
348 347 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
349 348 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
350 349 'source:/some/file@52' => link_to('source:/some/file@52', source_url_with_rev, :class => 'source'),
351 350 'source:/some/file@branch' => link_to('source:/some/file@branch', source_url_with_branch, :class => 'source'),
352 351 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_rev_and_ext, :class => 'source'),
353 352 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url + "#L110", :class => 'source'),
354 353 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext + "#L110", :class => 'source'),
355 354 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url_with_rev + "#L110", :class => 'source'),
356 355 # export
357 356 'export:/some/file' => link_to('export:/some/file', export_url, :class => 'source download'),
358 357 'export:/some/file.ext' => link_to('export:/some/file.ext', export_url_with_ext, :class => 'source download'),
359 358 'export:/some/file@52' => link_to('export:/some/file@52', export_url_with_rev, :class => 'source download'),
360 359 'export:/some/file.ext@52' => link_to('export:/some/file.ext@52', export_url_with_rev_and_ext, :class => 'source download'),
361 360 'export:/some/file@branch' => link_to('export:/some/file@branch', export_url_with_branch, :class => 'source download'),
362 361 # forum
363 362 'forum#2' => link_to('Discussion', board_url, :class => 'board'),
364 363 'forum:Discussion' => link_to('Discussion', board_url, :class => 'board'),
365 364 # message
366 365 'message#4' => link_to('Post 2', message_url, :class => 'message'),
367 366 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'),
368 367 # news
369 368 'news#1' => link_to('eCookbook first release !', news_url, :class => 'news'),
370 369 'news:"eCookbook first release !"' => link_to('eCookbook first release !', news_url, :class => 'news'),
371 370 # project
372 371 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
373 372 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
374 373 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
375 374 # not found
376 375 '#0123456789' => '#0123456789',
377 376 # invalid expressions
378 377 'source:' => 'source:',
379 378 # url hash
380 379 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
381 380 }
382 381 @project = Project.find(1)
383 382 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
384 383 end
385 384
386 385 def test_should_not_parse_redmine_links_inside_link
387 386 raw = "r1 should not be parsed in http://example.com/url-r1/"
388 387 assert_match %r{<p><a class="changeset".*>r1</a> should not be parsed in <a class="external" href="http://example.com/url-r1/">http://example.com/url-r1/</a></p>},
389 388 textilizable(raw, :project => Project.find(1))
390 389 end
391 390
392 391 def test_redmine_links_with_a_different_project_before_current_project
393 392 vp1 = Version.generate!(:project_id => 1, :name => '1.4.4')
394 393 vp3 = Version.generate!(:project_id => 3, :name => '1.4.4')
395 394 @project = Project.find(3)
396 395 result1 = link_to("1.4.4", "/versions/#{vp1.id}", :class => "version")
397 396 result2 = link_to("1.4.4", "/versions/#{vp3.id}", :class => "version")
398 397 assert_equal "<p>#{result1} #{result2}</p>",
399 398 textilizable("ecookbook:version:1.4.4 version:1.4.4")
400 399 end
401 400
402 401 def test_escaped_redmine_links_should_not_be_parsed
403 402 to_test = [
404 403 '#3.',
405 404 '#3-14.',
406 405 '#3#-note14.',
407 406 'r1',
408 407 'document#1',
409 408 'document:"Test document"',
410 409 'version#2',
411 410 'version:1.0',
412 411 'version:"1.0"',
413 412 'source:/some/file'
414 413 ]
415 414 @project = Project.find(1)
416 415 to_test.each { |text| assert_equal "<p>#{text}</p>", textilizable("!" + text), "#{text} failed" }
417 416 end
418 417
419 418 def test_cross_project_redmine_links
420 419 source_link = link_to('ecookbook:source:/some/file',
421 420 {:controller => 'repositories', :action => 'entry',
422 421 :id => 'ecookbook', :path => ['some', 'file']},
423 422 :class => 'source')
424 423 changeset_link = link_to('ecookbook:r2',
425 424 {:controller => 'repositories', :action => 'revision',
426 425 :id => 'ecookbook', :rev => 2},
427 426 :class => 'changeset',
428 427 :title => 'This commit fixes #1, #2 and references #1 & #3')
429 428 to_test = {
430 429 # documents
431 430 'document:"Test document"' => 'document:"Test document"',
432 431 'ecookbook:document:"Test document"' =>
433 432 link_to("Test document", "/documents/1", :class => "document"),
434 433 'invalid:document:"Test document"' => 'invalid:document:"Test document"',
435 434 # versions
436 435 'version:"1.0"' => 'version:"1.0"',
437 436 'ecookbook:version:"1.0"' =>
438 437 link_to("1.0", "/versions/2", :class => "version"),
439 438 'invalid:version:"1.0"' => 'invalid:version:"1.0"',
440 439 # changeset
441 440 'r2' => 'r2',
442 441 'ecookbook:r2' => changeset_link,
443 442 'invalid:r2' => 'invalid:r2',
444 443 # source
445 444 'source:/some/file' => 'source:/some/file',
446 445 'ecookbook:source:/some/file' => source_link,
447 446 'invalid:source:/some/file' => 'invalid:source:/some/file',
448 447 }
449 448 @project = Project.find(3)
450 449 to_test.each do |text, result|
451 450 assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed"
452 451 end
453 452 end
454 453
455 454 def test_redmine_links_by_name_should_work_with_html_escaped_characters
456 455 v = Version.generate!(:name => "Test & Show.txt", :project_id => 1)
457 456 link = link_to("Test & Show.txt", "/versions/#{v.id}", :class => "version")
458 457
459 458 @project = v.project
460 459 assert_equal "<p>#{link}</p>", textilizable('version:"Test & Show.txt"')
461 460 end
462 461
463 462 def test_link_to_issue_subject
464 463 issue = Issue.generate!(:subject => "01234567890123456789")
465 464 str = link_to_issue(issue, :truncate => 10)
466 465 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
467 466 assert_equal "#{result}: 0123456...", str
468 467
469 468 issue = Issue.generate!(:subject => "<&>")
470 469 str = link_to_issue(issue)
471 470 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
472 471 assert_equal "#{result}: &lt;&amp;&gt;", str
473 472
474 473 issue = Issue.generate!(:subject => "<&>0123456789012345")
475 474 str = link_to_issue(issue, :truncate => 10)
476 475 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
477 476 assert_equal "#{result}: &lt;&amp;&gt;0123...", str
478 477 end
479 478
480 479 def test_link_to_issue_title
481 480 long_str = "0123456789" * 5
482 481
483 482 issue = Issue.generate!(:subject => "#{long_str}01234567890123456789")
484 483 str = link_to_issue(issue, :subject => false)
485 484 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}",
486 485 :class => issue.css_classes,
487 486 :title => "#{long_str}0123456...")
488 487 assert_equal result, str
489 488
490 489 issue = Issue.generate!(:subject => "<&>#{long_str}01234567890123456789")
491 490 str = link_to_issue(issue, :subject => false)
492 491 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}",
493 492 :class => issue.css_classes,
494 493 :title => "<&>#{long_str}0123...")
495 494 assert_equal result, str
496 495 end
497 496
498 497 def test_multiple_repositories_redmine_links
499 498 svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn_repo-1', :url => 'file:///foo/hg')
500 499 Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
501 500 hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
502 501 Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
503 502
504 503 changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
505 504 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
506 505 svn_changeset_link = link_to('svn_repo-1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn_repo-1', :rev => 123},
507 506 :class => 'changeset', :title => '')
508 507 hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
509 508 :class => 'changeset', :title => '')
510 509
511 510 source_link = link_to('source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
512 511 hg_source_link = link_to('source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
513 512
514 513 to_test = {
515 514 'r2' => changeset_link,
516 515 'svn_repo-1|r123' => svn_changeset_link,
517 516 'invalid|r123' => 'invalid|r123',
518 517 'commit:hg1|abcd' => hg_changeset_link,
519 518 'commit:invalid|abcd' => 'commit:invalid|abcd',
520 519 # source
521 520 'source:some/file' => source_link,
522 521 'source:hg1|some/file' => hg_source_link,
523 522 'source:invalid|some/file' => 'source:invalid|some/file',
524 523 }
525 524
526 525 @project = Project.find(1)
527 526 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
528 527 end
529 528
530 529 def test_cross_project_multiple_repositories_redmine_links
531 530 svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg')
532 531 Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
533 532 hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
534 533 Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
535 534
536 535 changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
537 536 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
538 537 svn_changeset_link = link_to('ecookbook:svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123},
539 538 :class => 'changeset', :title => '')
540 539 hg_changeset_link = link_to('ecookbook:hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
541 540 :class => 'changeset', :title => '')
542 541
543 542 source_link = link_to('ecookbook:source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
544 543 hg_source_link = link_to('ecookbook:source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
545 544
546 545 to_test = {
547 546 'ecookbook:r2' => changeset_link,
548 547 'ecookbook:svn1|r123' => svn_changeset_link,
549 548 'ecookbook:invalid|r123' => 'ecookbook:invalid|r123',
550 549 'ecookbook:commit:hg1|abcd' => hg_changeset_link,
551 550 'ecookbook:commit:invalid|abcd' => 'ecookbook:commit:invalid|abcd',
552 551 'invalid:commit:invalid|abcd' => 'invalid:commit:invalid|abcd',
553 552 # source
554 553 'ecookbook:source:some/file' => source_link,
555 554 'ecookbook:source:hg1|some/file' => hg_source_link,
556 555 'ecookbook:source:invalid|some/file' => 'ecookbook:source:invalid|some/file',
557 556 'invalid:source:invalid|some/file' => 'invalid:source:invalid|some/file',
558 557 }
559 558
560 559 @project = Project.find(3)
561 560 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
562 561 end
563 562
564 563 def test_redmine_links_git_commit
565 564 changeset_link = link_to('abcd',
566 565 {
567 566 :controller => 'repositories',
568 567 :action => 'revision',
569 568 :id => 'subproject1',
570 569 :rev => 'abcd',
571 570 },
572 571 :class => 'changeset', :title => 'test commit')
573 572 to_test = {
574 573 'commit:abcd' => changeset_link,
575 574 }
576 575 @project = Project.find(3)
577 576 r = Repository::Git.create!(:project => @project, :url => '/tmp/test/git')
578 577 assert r
579 578 c = Changeset.new(:repository => r,
580 579 :committed_on => Time.now,
581 580 :revision => 'abcd',
582 581 :scmid => 'abcd',
583 582 :comments => 'test commit')
584 583 assert( c.save )
585 584 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
586 585 end
587 586
588 587 # TODO: Bazaar commit id contains mail address, so it contains '@' and '_'.
589 588 def test_redmine_links_darcs_commit
590 589 changeset_link = link_to('20080308225258-98289-abcd456efg.gz',
591 590 {
592 591 :controller => 'repositories',
593 592 :action => 'revision',
594 593 :id => 'subproject1',
595 594 :rev => '123',
596 595 },
597 596 :class => 'changeset', :title => 'test commit')
598 597 to_test = {
599 598 'commit:20080308225258-98289-abcd456efg.gz' => changeset_link,
600 599 }
601 600 @project = Project.find(3)
602 601 r = Repository::Darcs.create!(
603 602 :project => @project, :url => '/tmp/test/darcs',
604 603 :log_encoding => 'UTF-8')
605 604 assert r
606 605 c = Changeset.new(:repository => r,
607 606 :committed_on => Time.now,
608 607 :revision => '123',
609 608 :scmid => '20080308225258-98289-abcd456efg.gz',
610 609 :comments => 'test commit')
611 610 assert( c.save )
612 611 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
613 612 end
614 613
615 614 def test_redmine_links_mercurial_commit
616 615 changeset_link_rev = link_to('r123',
617 616 {
618 617 :controller => 'repositories',
619 618 :action => 'revision',
620 619 :id => 'subproject1',
621 620 :rev => '123' ,
622 621 },
623 622 :class => 'changeset', :title => 'test commit')
624 623 changeset_link_commit = link_to('abcd',
625 624 {
626 625 :controller => 'repositories',
627 626 :action => 'revision',
628 627 :id => 'subproject1',
629 628 :rev => 'abcd' ,
630 629 },
631 630 :class => 'changeset', :title => 'test commit')
632 631 to_test = {
633 632 'r123' => changeset_link_rev,
634 633 'commit:abcd' => changeset_link_commit,
635 634 }
636 635 @project = Project.find(3)
637 636 r = Repository::Mercurial.create!(:project => @project, :url => '/tmp/test')
638 637 assert r
639 638 c = Changeset.new(:repository => r,
640 639 :committed_on => Time.now,
641 640 :revision => '123',
642 641 :scmid => 'abcd',
643 642 :comments => 'test commit')
644 643 assert( c.save )
645 644 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
646 645 end
647 646
648 647 def test_attachment_links
649 648 text = 'attachment:error281.txt'
650 649 result = link_to("error281.txt", "/attachments/download/1/error281.txt",
651 650 :class => "attachment")
652 651 assert_equal "<p>#{result}</p>",
653 652 textilizable(text,
654 653 :attachments => Issue.find(3).attachments),
655 654 "#{text} failed"
656 655 end
657 656
658 657 def test_attachment_link_should_link_to_latest_attachment
659 658 set_tmp_attachments_directory
660 659 a1 = Attachment.generate!(:filename => "test.txt", :created_on => 1.hour.ago)
661 660 a2 = Attachment.generate!(:filename => "test.txt")
662 661 result = link_to("test.txt", "/attachments/download/#{a2.id}/test.txt",
663 662 :class => "attachment")
664 663 assert_equal "<p>#{result}</p>",
665 664 textilizable('attachment:test.txt', :attachments => [a1, a2])
666 665 end
667 666
668 667 def test_wiki_links
669 668 russian_eacape = CGI.escape(@russian_test)
670 669 to_test = {
671 670 '[[CookBook documentation]]' =>
672 671 link_to("CookBook documentation",
673 672 "/projects/ecookbook/wiki/CookBook_documentation",
674 673 :class => "wiki-page"),
675 674 '[[Another page|Page]]' =>
676 675 link_to("Page",
677 676 "/projects/ecookbook/wiki/Another_page",
678 677 :class => "wiki-page"),
679 678 # title content should be formatted
680 679 '[[Another page|With _styled_ *title*]]' =>
681 680 link_to("With <em>styled</em> <strong>title</strong>".html_safe,
682 681 "/projects/ecookbook/wiki/Another_page",
683 682 :class => "wiki-page"),
684 683 '[[Another page|With title containing <strong>HTML entities &amp; markups</strong>]]' =>
685 684 link_to("With title containing &lt;strong&gt;HTML entities &amp; markups&lt;/strong&gt;".html_safe,
686 685 "/projects/ecookbook/wiki/Another_page",
687 686 :class => "wiki-page"),
688 687 # link with anchor
689 688 '[[CookBook documentation#One-section]]' =>
690 689 link_to("CookBook documentation",
691 690 "/projects/ecookbook/wiki/CookBook_documentation#One-section",
692 691 :class => "wiki-page"),
693 692 '[[Another page#anchor|Page]]' =>
694 693 link_to("Page",
695 694 "/projects/ecookbook/wiki/Another_page#anchor",
696 695 :class => "wiki-page"),
697 696 # UTF8 anchor
698 697 "[[Another_page##{@russian_test}|#{@russian_test}]]" =>
699 698 link_to(@russian_test,
700 699 "/projects/ecookbook/wiki/Another_page##{russian_eacape}",
701 700 :class => "wiki-page"),
702 701 # page that doesn't exist
703 702 '[[Unknown page]]' =>
704 703 link_to("Unknown page",
705 704 "/projects/ecookbook/wiki/Unknown_page",
706 705 :class => "wiki-page new"),
707 706 '[[Unknown page|404]]' =>
708 707 link_to("404",
709 708 "/projects/ecookbook/wiki/Unknown_page",
710 709 :class => "wiki-page new"),
711 710 # link to another project wiki
712 711 '[[onlinestore:]]' =>
713 712 link_to("onlinestore",
714 713 "/projects/onlinestore/wiki",
715 714 :class => "wiki-page"),
716 715 '[[onlinestore:|Wiki]]' =>
717 716 link_to("Wiki",
718 717 "/projects/onlinestore/wiki",
719 718 :class => "wiki-page"),
720 719 '[[onlinestore:Start page]]' =>
721 720 link_to("Start page",
722 721 "/projects/onlinestore/wiki/Start_page",
723 722 :class => "wiki-page"),
724 723 '[[onlinestore:Start page|Text]]' =>
725 724 link_to("Text",
726 725 "/projects/onlinestore/wiki/Start_page",
727 726 :class => "wiki-page"),
728 727 '[[onlinestore:Unknown page]]' =>
729 728 link_to("Unknown page",
730 729 "/projects/onlinestore/wiki/Unknown_page",
731 730 :class => "wiki-page new"),
732 731 # struck through link
733 732 '-[[Another page|Page]]-' =>
734 733 "<del>".html_safe +
735 734 link_to("Page",
736 735 "/projects/ecookbook/wiki/Another_page",
737 736 :class => "wiki-page").html_safe +
738 737 "</del>".html_safe,
739 738 '-[[Another page|Page]] link-' =>
740 739 "<del>".html_safe +
741 740 link_to("Page",
742 741 "/projects/ecookbook/wiki/Another_page",
743 742 :class => "wiki-page").html_safe +
744 743 " link</del>".html_safe,
745 744 # escaping
746 745 '![[Another page|Page]]' => '[[Another page|Page]]',
747 746 # project does not exist
748 747 '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
749 748 '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
750 749 }
751 750 @project = Project.find(1)
752 751 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
753 752 end
754 753
755 754 def test_wiki_links_within_local_file_generation_context
756 755 to_test = {
757 756 # link to a page
758 757 '[[CookBook documentation]]' =>
759 758 link_to("CookBook documentation", "CookBook_documentation.html",
760 759 :class => "wiki-page"),
761 760 '[[CookBook documentation|documentation]]' =>
762 761 link_to("documentation", "CookBook_documentation.html",
763 762 :class => "wiki-page"),
764 763 '[[CookBook documentation#One-section]]' =>
765 764 link_to("CookBook documentation", "CookBook_documentation.html#One-section",
766 765 :class => "wiki-page"),
767 766 '[[CookBook documentation#One-section|documentation]]' =>
768 767 link_to("documentation", "CookBook_documentation.html#One-section",
769 768 :class => "wiki-page"),
770 769 # page that doesn't exist
771 770 '[[Unknown page]]' =>
772 771 link_to("Unknown page", "Unknown_page.html",
773 772 :class => "wiki-page new"),
774 773 '[[Unknown page|404]]' =>
775 774 link_to("404", "Unknown_page.html",
776 775 :class => "wiki-page new"),
777 776 '[[Unknown page#anchor]]' =>
778 777 link_to("Unknown page", "Unknown_page.html#anchor",
779 778 :class => "wiki-page new"),
780 779 '[[Unknown page#anchor|404]]' =>
781 780 link_to("404", "Unknown_page.html#anchor",
782 781 :class => "wiki-page new"),
783 782 }
784 783 @project = Project.find(1)
785 784 to_test.each do |text, result|
786 785 assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :local)
787 786 end
788 787 end
789 788
790 789 def test_wiki_links_within_wiki_page_context
791 790 page = WikiPage.find_by_title('Another_page' )
792 791 to_test = {
793 792 '[[CookBook documentation]]' =>
794 793 link_to("CookBook documentation",
795 794 "/projects/ecookbook/wiki/CookBook_documentation",
796 795 :class => "wiki-page"),
797 796 '[[CookBook documentation|documentation]]' =>
798 797 link_to("documentation",
799 798 "/projects/ecookbook/wiki/CookBook_documentation",
800 799 :class => "wiki-page"),
801 800 '[[CookBook documentation#One-section]]' =>
802 801 link_to("CookBook documentation",
803 802 "/projects/ecookbook/wiki/CookBook_documentation#One-section",
804 803 :class => "wiki-page"),
805 804 '[[CookBook documentation#One-section|documentation]]' =>
806 805 link_to("documentation",
807 806 "/projects/ecookbook/wiki/CookBook_documentation#One-section",
808 807 :class => "wiki-page"),
809 808 # link to the current page
810 809 '[[Another page]]' =>
811 810 link_to("Another page",
812 811 "/projects/ecookbook/wiki/Another_page",
813 812 :class => "wiki-page"),
814 813 '[[Another page|Page]]' =>
815 814 link_to("Page",
816 815 "/projects/ecookbook/wiki/Another_page",
817 816 :class => "wiki-page"),
818 817 '[[Another page#anchor]]' =>
819 818 link_to("Another page",
820 819 "#anchor",
821 820 :class => "wiki-page"),
822 821 '[[Another page#anchor|Page]]' =>
823 822 link_to("Page",
824 823 "#anchor",
825 824 :class => "wiki-page"),
826 825 # page that doesn't exist
827 826 '[[Unknown page]]' =>
828 827 link_to("Unknown page",
829 828 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page",
830 829 :class => "wiki-page new"),
831 830 '[[Unknown page|404]]' =>
832 831 link_to("404",
833 832 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page",
834 833 :class => "wiki-page new"),
835 834 '[[Unknown page#anchor]]' =>
836 835 link_to("Unknown page",
837 836 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor",
838 837 :class => "wiki-page new"),
839 838 '[[Unknown page#anchor|404]]' =>
840 839 link_to("404",
841 840 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor",
842 841 :class => "wiki-page new"),
843 842 }
844 843 @project = Project.find(1)
845 844 to_test.each do |text, result|
846 845 assert_equal "<p>#{result}</p>",
847 846 textilizable(WikiContent.new( :text => text, :page => page ), :text)
848 847 end
849 848 end
850 849
851 850 def test_wiki_links_anchor_option_should_prepend_page_title_to_href
852 851 to_test = {
853 852 # link to a page
854 853 '[[CookBook documentation]]' =>
855 854 link_to("CookBook documentation",
856 855 "#CookBook_documentation",
857 856 :class => "wiki-page"),
858 857 '[[CookBook documentation|documentation]]' =>
859 858 link_to("documentation",
860 859 "#CookBook_documentation",
861 860 :class => "wiki-page"),
862 861 '[[CookBook documentation#One-section]]' =>
863 862 link_to("CookBook documentation",
864 863 "#CookBook_documentation_One-section",
865 864 :class => "wiki-page"),
866 865 '[[CookBook documentation#One-section|documentation]]' =>
867 866 link_to("documentation",
868 867 "#CookBook_documentation_One-section",
869 868 :class => "wiki-page"),
870 869 # page that doesn't exist
871 870 '[[Unknown page]]' =>
872 871 link_to("Unknown page",
873 872 "#Unknown_page",
874 873 :class => "wiki-page new"),
875 874 '[[Unknown page|404]]' =>
876 875 link_to("404",
877 876 "#Unknown_page",
878 877 :class => "wiki-page new"),
879 878 '[[Unknown page#anchor]]' =>
880 879 link_to("Unknown page",
881 880 "#Unknown_page_anchor",
882 881 :class => "wiki-page new"),
883 882 '[[Unknown page#anchor|404]]' =>
884 883 link_to("404",
885 884 "#Unknown_page_anchor",
886 885 :class => "wiki-page new"),
887 886 }
888 887 @project = Project.find(1)
889 888 to_test.each do |text, result|
890 889 assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :anchor)
891 890 end
892 891 end
893 892
894 893 def test_html_tags
895 894 to_test = {
896 895 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
897 896 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
898 897 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
899 898 # do not escape pre/code tags
900 899 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
901 900 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
902 901 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
903 902 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
904 903 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
905 904 # remove attributes except class
906 905 "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
907 906 '<pre class="foo">some text</pre>' => '<pre class="foo">some text</pre>',
908 907 "<pre class='foo bar'>some text</pre>" => "<pre class='foo bar'>some text</pre>",
909 908 '<pre class="foo bar">some text</pre>' => '<pre class="foo bar">some text</pre>',
910 909 "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
911 910 # xss
912 911 '<pre><code class=""onmouseover="alert(1)">text</code></pre>' => '<pre><code>text</code></pre>',
913 912 '<pre class=""onmouseover="alert(1)">text</pre>' => '<pre>text</pre>',
914 913 }
915 914 to_test.each { |text, result| assert_equal result, textilizable(text) }
916 915 end
917 916
918 917 def test_allowed_html_tags
919 918 to_test = {
920 919 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
921 920 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
922 921 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
923 922 }
924 923 to_test.each { |text, result| assert_equal result, textilizable(text) }
925 924 end
926 925
927 926 def test_pre_tags
928 927 raw = <<-RAW
929 928 Before
930 929
931 930 <pre>
932 931 <prepared-statement-cache-size>32</prepared-statement-cache-size>
933 932 </pre>
934 933
935 934 After
936 935 RAW
937 936
938 937 expected = <<-EXPECTED
939 938 <p>Before</p>
940 939 <pre>
941 940 &lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
942 941 </pre>
943 942 <p>After</p>
944 943 EXPECTED
945 944
946 945 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
947 946 end
948 947
949 948 def test_pre_content_should_not_parse_wiki_and_redmine_links
950 949 raw = <<-RAW
951 950 [[CookBook documentation]]
952 951
953 952 #1
954 953
955 954 <pre>
956 955 [[CookBook documentation]]
957 956
958 957 #1
959 958 </pre>
960 959 RAW
961 960
962 961 result1 = link_to("CookBook documentation",
963 962 "/projects/ecookbook/wiki/CookBook_documentation",
964 963 :class => "wiki-page")
965 964 result2 = link_to('#1',
966 965 "/issues/1",
967 966 :class => Issue.find(1).css_classes,
968 967 :title => "Bug: Cannot print recipes (New)")
969 968
970 969 expected = <<-EXPECTED
971 970 <p>#{result1}</p>
972 971 <p>#{result2}</p>
973 972 <pre>
974 973 [[CookBook documentation]]
975 974
976 975 #1
977 976 </pre>
978 977 EXPECTED
979 978
980 979 @project = Project.find(1)
981 980 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
982 981 end
983 982
984 983 def test_non_closing_pre_blocks_should_be_closed
985 984 raw = <<-RAW
986 985 <pre><code>
987 986 RAW
988 987
989 988 expected = <<-EXPECTED
990 989 <pre><code>
991 990 </code></pre>
992 991 EXPECTED
993 992
994 993 @project = Project.find(1)
995 994 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
996 995 end
997 996
998 997 def test_unbalanced_closing_pre_tag_should_not_error
999 998 assert_nothing_raised do
1000 999 textilizable("unbalanced</pre>")
1001 1000 end
1002 1001 end
1003 1002
1004 1003 def test_syntax_highlight
1005 1004 raw = <<-RAW
1006 1005 <pre><code class="ruby">
1007 1006 # Some ruby code here
1008 1007 </code></pre>
1009 1008 RAW
1010 1009
1011 1010 expected = <<-EXPECTED
1012 1011 <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="comment"># Some ruby code here</span></span>
1013 1012 </code></pre>
1014 1013 EXPECTED
1015 1014
1016 1015 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
1017 1016 end
1018 1017
1019 1018 def test_to_path_param
1020 1019 assert_equal 'test1/test2', to_path_param('test1/test2')
1021 1020 assert_equal 'test1/test2', to_path_param('/test1/test2/')
1022 1021 assert_equal 'test1/test2', to_path_param('//test1/test2/')
1023 1022 assert_nil to_path_param('/')
1024 1023 end
1025 1024
1026 1025 def test_wiki_links_in_tables
1027 1026 text = "|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|"
1028 1027 link1 = link_to("Link title", "/projects/ecookbook/wiki/Page", :class => "wiki-page new")
1029 1028 link2 = link_to("Other title", "/projects/ecookbook/wiki/Other_Page", :class => "wiki-page new")
1030 1029 link3 = link_to("Last page", "/projects/ecookbook/wiki/Last_page", :class => "wiki-page new")
1031 1030 result = "<tr><td>#{link1}</td>" +
1032 1031 "<td>#{link2}</td>" +
1033 1032 "</tr><tr><td>Cell 21</td><td>#{link3}</td></tr>"
1034 1033 @project = Project.find(1)
1035 1034 assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '')
1036 1035 end
1037 1036
1038 1037 def test_text_formatting
1039 1038 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
1040 1039 '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
1041 1040 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
1042 1041 '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>',
1043 1042 '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',
1044 1043 }
1045 1044 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
1046 1045 end
1047 1046
1048 1047 def test_wiki_horizontal_rule
1049 1048 assert_equal '<hr />', textilizable('---')
1050 1049 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
1051 1050 end
1052 1051
1053 1052 def test_footnotes
1054 1053 raw = <<-RAW
1055 1054 This is some text[1].
1056 1055
1057 1056 fn1. This is the foot note
1058 1057 RAW
1059 1058
1060 1059 expected = <<-EXPECTED
1061 1060 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
1062 1061 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
1063 1062 EXPECTED
1064 1063
1065 1064 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
1066 1065 end
1067 1066
1068 1067 def test_headings
1069 1068 raw = 'h1. Some heading'
1070 1069 expected = %|<a name="Some-heading"></a>\n<h1 >Some heading<a href="#Some-heading" class="wiki-anchor">&para;</a></h1>|
1071 1070
1072 1071 assert_equal expected, textilizable(raw)
1073 1072 end
1074 1073
1075 1074 def test_headings_with_special_chars
1076 1075 # This test makes sure that the generated anchor names match the expected
1077 1076 # ones even if the heading text contains unconventional characters
1078 1077 raw = 'h1. Some heading related to version 0.5'
1079 1078 anchor = sanitize_anchor_name("Some-heading-related-to-version-0.5")
1080 1079 expected = %|<a name="#{anchor}"></a>\n<h1 >Some heading related to version 0.5<a href="##{anchor}" class="wiki-anchor">&para;</a></h1>|
1081 1080
1082 1081 assert_equal expected, textilizable(raw)
1083 1082 end
1084 1083
1085 1084 def test_headings_in_wiki_single_page_export_should_be_prepended_with_page_title
1086 1085 page = WikiPage.new( :title => 'Page Title', :wiki_id => 1 )
1087 1086 content = WikiContent.new( :text => 'h1. Some heading', :page => page )
1088 1087
1089 1088 expected = %|<a name="Page_Title_Some-heading"></a>\n<h1 >Some heading<a href="#Page_Title_Some-heading" class="wiki-anchor">&para;</a></h1>|
1090 1089
1091 1090 assert_equal expected, textilizable(content, :text, :wiki_links => :anchor )
1092 1091 end
1093 1092
1094 1093 def test_table_of_content
1095 1094 set_language_if_valid 'en'
1096 1095
1097 1096 raw = <<-RAW
1098 1097 {{toc}}
1099 1098
1100 1099 h1. Title
1101 1100
1102 1101 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
1103 1102
1104 1103 h2. Subtitle with a [[Wiki]] link
1105 1104
1106 1105 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
1107 1106
1108 1107 h2. Subtitle with [[Wiki|another Wiki]] link
1109 1108
1110 1109 h2. Subtitle with %{color:red}red text%
1111 1110
1112 1111 <pre>
1113 1112 some code
1114 1113 </pre>
1115 1114
1116 1115 h3. Subtitle with *some* _modifiers_
1117 1116
1118 1117 h3. Subtitle with @inline code@
1119 1118
1120 1119 h1. Another title
1121 1120
1122 1121 h3. An "Internet link":http://www.redmine.org/ inside subtitle
1123 1122
1124 1123 h2. "Project Name !/attachments/1234/logo_small.gif! !/attachments/5678/logo_2.png!":/projects/projectname/issues
1125 1124
1126 1125 RAW
1127 1126
1128 1127 expected = '<ul class="toc">' +
1129 1128 '<li><strong>Table of contents</strong></li>' +
1130 1129 '<li><a href="#Title">Title</a>' +
1131 1130 '<ul>' +
1132 1131 '<li><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
1133 1132 '<li><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
1134 1133 '<li><a href="#Subtitle-with-red-text">Subtitle with red text</a>' +
1135 1134 '<ul>' +
1136 1135 '<li><a href="#Subtitle-with-some-modifiers">Subtitle with some modifiers</a></li>' +
1137 1136 '<li><a href="#Subtitle-with-inline-code">Subtitle with inline code</a></li>' +
1138 1137 '</ul>' +
1139 1138 '</li>' +
1140 1139 '</ul>' +
1141 1140 '</li>' +
1142 1141 '<li><a href="#Another-title">Another title</a>' +
1143 1142 '<ul>' +
1144 1143 '<li>' +
1145 1144 '<ul>' +
1146 1145 '<li><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
1147 1146 '</ul>' +
1148 1147 '</li>' +
1149 1148 '<li><a href="#Project-Name">Project Name</a></li>' +
1150 1149 '</ul>' +
1151 1150 '</li>' +
1152 1151 '</ul>'
1153 1152
1154 1153 @project = Project.find(1)
1155 1154 assert textilizable(raw).gsub("\n", "").include?(expected)
1156 1155 end
1157 1156
1158 1157 def test_table_of_content_should_generate_unique_anchors
1159 1158 set_language_if_valid 'en'
1160 1159
1161 1160 raw = <<-RAW
1162 1161 {{toc}}
1163 1162
1164 1163 h1. Title
1165 1164
1166 1165 h2. Subtitle
1167 1166
1168 1167 h2. Subtitle
1169 1168 RAW
1170 1169
1171 1170 expected = '<ul class="toc">' +
1172 1171 '<li><strong>Table of contents</strong></li>' +
1173 1172 '<li><a href="#Title">Title</a>' +
1174 1173 '<ul>' +
1175 1174 '<li><a href="#Subtitle">Subtitle</a></li>' +
1176 1175 '<li><a href="#Subtitle-2">Subtitle</a></li>' +
1177 1176 '</ul>' +
1178 1177 '</li>' +
1179 1178 '</ul>'
1180 1179
1181 1180 @project = Project.find(1)
1182 1181 result = textilizable(raw).gsub("\n", "")
1183 1182 assert_include expected, result
1184 1183 assert_include '<a name="Subtitle">', result
1185 1184 assert_include '<a name="Subtitle-2">', result
1186 1185 end
1187 1186
1188 1187 def test_table_of_content_should_contain_included_page_headings
1189 1188 set_language_if_valid 'en'
1190 1189
1191 1190 raw = <<-RAW
1192 1191 {{toc}}
1193 1192
1194 1193 h1. Included
1195 1194
1196 1195 {{include(Child_1)}}
1197 1196 RAW
1198 1197
1199 1198 expected = '<ul class="toc">' +
1200 1199 '<li><strong>Table of contents</strong></li>' +
1201 1200 '<li><a href="#Included">Included</a></li>' +
1202 1201 '<li><a href="#Child-page-1">Child page 1</a></li>' +
1203 1202 '</ul>'
1204 1203
1205 1204 @project = Project.find(1)
1206 1205 assert textilizable(raw).gsub("\n", "").include?(expected)
1207 1206 end
1208 1207
1209 1208 def test_toc_with_textile_formatting_should_be_parsed
1210 1209 with_settings :text_formatting => 'textile' do
1211 1210 assert_select_in textilizable("{{toc}}\n\nh1. Heading"), 'ul.toc li', :text => 'Heading'
1212 1211 assert_select_in textilizable("{{<toc}}\n\nh1. Heading"), 'ul.toc.left li', :text => 'Heading'
1213 1212 assert_select_in textilizable("{{>toc}}\n\nh1. Heading"), 'ul.toc.right li', :text => 'Heading'
1214 1213 end
1215 1214 end
1216 1215
1217 1216 if Object.const_defined?(:Redcarpet)
1218 1217 def test_toc_with_markdown_formatting_should_be_parsed
1219 1218 with_settings :text_formatting => 'markdown' do
1220 1219 assert_select_in textilizable("{{toc}}\n\n# Heading"), 'ul.toc li', :text => 'Heading'
1221 1220 assert_select_in textilizable("{{<toc}}\n\n# Heading"), 'ul.toc.left li', :text => 'Heading'
1222 1221 assert_select_in textilizable("{{>toc}}\n\n# Heading"), 'ul.toc.right li', :text => 'Heading'
1223 1222 end
1224 1223 end
1225 1224 end
1226 1225
1227 1226 def test_section_edit_links
1228 1227 raw = <<-RAW
1229 1228 h1. Title
1230 1229
1231 1230 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
1232 1231
1233 1232 h2. Subtitle with a [[Wiki]] link
1234 1233
1235 1234 h2. Subtitle with *some* _modifiers_
1236 1235
1237 1236 h2. Subtitle with @inline code@
1238 1237
1239 1238 <pre>
1240 1239 some code
1241 1240
1242 1241 h2. heading inside pre
1243 1242
1244 1243 <h2>html heading inside pre</h2>
1245 1244 </pre>
1246 1245
1247 1246 h2. Subtitle after pre tag
1248 1247 RAW
1249 1248
1250 1249 @project = Project.find(1)
1251 1250 set_language_if_valid 'en'
1252 1251 result = textilizable(raw, :edit_section_links => {:controller => 'wiki', :action => 'edit', :project_id => '1', :id => 'Test'}).gsub("\n", "")
1253 1252
1254 1253 # heading that contains inline code
1255 1254 assert_match Regexp.new('<div class="contextual heading-2" title="Edit this section" id="section-4">' +
1256 1255 '<a class="icon-only icon-edit" href="/projects/1/wiki/Test/edit\?section=4">Edit this section</a></div>' +
1257 1256 '<a name="Subtitle-with-inline-code"></a>' +
1258 1257 '<h2 >Subtitle with <code>inline code</code><a href="#Subtitle-with-inline-code" class="wiki-anchor">&para;</a></h2>'),
1259 1258 result
1260 1259
1261 1260 # last heading
1262 1261 assert_match Regexp.new('<div class="contextual heading-2" title="Edit this section" id="section-5">' +
1263 1262 '<a class="icon-only icon-edit" href="/projects/1/wiki/Test/edit\?section=5">Edit this section</a></div>' +
1264 1263 '<a name="Subtitle-after-pre-tag"></a>' +
1265 1264 '<h2 >Subtitle after pre tag<a href="#Subtitle-after-pre-tag" class="wiki-anchor">&para;</a></h2>'),
1266 1265 result
1267 1266 end
1268 1267
1269 1268 def test_default_formatter
1270 1269 with_settings :text_formatting => 'unknown' do
1271 1270 text = 'a *link*: http://www.example.net/'
1272 1271 assert_equal '<p>a *link*: <a class="external" href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
1273 1272 end
1274 1273 end
1275 1274
1276 1275 def test_parse_redmine_links_should_handle_a_tag_without_attributes
1277 1276 text = '<a>http://example.com</a>'
1278 1277 expected = text.dup
1279 1278 parse_redmine_links(text, nil, nil, nil, true, {})
1280 1279 assert_equal expected, text
1281 1280 end
1282 1281
1283 1282 def test_due_date_distance_in_words
1284 1283 to_test = { Date.today => 'Due in 0 days',
1285 1284 Date.today + 1 => 'Due in 1 day',
1286 1285 Date.today + 100 => 'Due in about 3 months',
1287 1286 Date.today + 20000 => 'Due in over 54 years',
1288 1287 Date.today - 1 => '1 day late',
1289 1288 Date.today - 100 => 'about 3 months late',
1290 1289 Date.today - 20000 => 'over 54 years late',
1291 1290 }
1292 1291 ::I18n.locale = :en
1293 1292 to_test.each do |date, expected|
1294 1293 assert_equal expected, due_date_distance_in_words(date)
1295 1294 end
1296 1295 end
1297 1296
1298 1297 def test_avatar_enabled
1299 1298 with_settings :gravatar_enabled => '1' do
1300 1299 assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
1301 1300 assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
1302 1301 # Default size is 50
1303 1302 assert avatar('jsmith <jsmith@somenet.foo>').include?('size=50')
1304 1303 assert avatar('jsmith <jsmith@somenet.foo>', :size => 24).include?('size=24')
1305 1304 # Non-avatar options should be considered html options
1306 1305 assert avatar('jsmith <jsmith@somenet.foo>', :title => 'John Smith').include?('title="John Smith"')
1307 1306 # The default class of the img tag should be gravatar
1308 1307 assert avatar('jsmith <jsmith@somenet.foo>').include?('class="gravatar"')
1309 1308 assert !avatar('jsmith <jsmith@somenet.foo>', :class => 'picture').include?('class="gravatar"')
1310 1309 assert_nil avatar('jsmith')
1311 1310 assert_nil avatar(nil)
1312 1311 end
1313 1312 end
1314 1313
1315 1314 def test_avatar_disabled
1316 1315 with_settings :gravatar_enabled => '0' do
1317 1316 assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo'))
1318 1317 end
1319 1318 end
1320 1319
1321 1320 def test_link_to_user
1322 1321 user = User.find(2)
1323 1322 result = link_to("John Smith", "/users/2", :class => "user active")
1324 1323 assert_equal result, link_to_user(user)
1325 1324 end
1326 1325
1327 1326 def test_link_to_user_should_not_link_to_locked_user
1328 1327 with_current_user nil do
1329 1328 user = User.find(5)
1330 1329 assert user.locked?
1331 1330 assert_equal 'Dave2 Lopper2', link_to_user(user)
1332 1331 end
1333 1332 end
1334 1333
1335 1334 def test_link_to_user_should_link_to_locked_user_if_current_user_is_admin
1336 1335 with_current_user User.find(1) do
1337 1336 user = User.find(5)
1338 1337 assert user.locked?
1339 1338 result = link_to("Dave2 Lopper2", "/users/5", :class => "user locked")
1340 1339 assert_equal result, link_to_user(user)
1341 1340 end
1342 1341 end
1343 1342
1344 1343 def test_link_to_user_should_not_link_to_anonymous
1345 1344 user = User.anonymous
1346 1345 assert user.anonymous?
1347 1346 t = link_to_user(user)
1348 1347 assert_equal ::I18n.t(:label_user_anonymous), t
1349 1348 end
1350 1349
1351 1350 def test_link_to_attachment
1352 1351 a = Attachment.find(3)
1353 1352 assert_equal '<a href="/attachments/3/logo.gif">logo.gif</a>',
1354 1353 link_to_attachment(a)
1355 1354 assert_equal '<a href="/attachments/3/logo.gif">Text</a>',
1356 1355 link_to_attachment(a, :text => 'Text')
1357 1356 result = link_to("logo.gif", "/attachments/3/logo.gif", :class => "foo")
1358 1357 assert_equal result,
1359 1358 link_to_attachment(a, :class => 'foo')
1360 1359 assert_equal '<a href="/attachments/download/3/logo.gif">logo.gif</a>',
1361 1360 link_to_attachment(a, :download => true)
1362 1361 assert_equal '<a href="http://test.host/attachments/3/logo.gif">logo.gif</a>',
1363 1362 link_to_attachment(a, :only_path => false)
1364 1363 end
1365 1364
1366 1365 def test_thumbnail_tag
1367 1366 a = Attachment.find(3)
1368 1367 assert_select_in thumbnail_tag(a),
1369 1368 'a[href=?][title=?] img[alt="3"][src=?]',
1370 1369 "/attachments/3/logo.gif", "logo.gif", "/attachments/thumbnail/3"
1371 1370 end
1372 1371
1373 1372 def test_link_to_project
1374 1373 project = Project.find(1)
1375 1374 assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
1376 1375 link_to_project(project)
1377 1376 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
1378 1377 link_to_project(project, {:only_path => false, :jump => 'blah'})
1379 1378 end
1380 1379
1381 1380 def test_link_to_project_settings
1382 1381 project = Project.find(1)
1383 1382 assert_equal '<a href="/projects/ecookbook/settings">eCookbook</a>', link_to_project_settings(project)
1384 1383
1385 1384 project.status = Project::STATUS_CLOSED
1386 1385 assert_equal '<a href="/projects/ecookbook">eCookbook</a>', link_to_project_settings(project)
1387 1386
1388 1387 project.status = Project::STATUS_ARCHIVED
1389 1388 assert_equal 'eCookbook', link_to_project_settings(project)
1390 1389 end
1391 1390
1392 1391 def test_link_to_legacy_project_with_numerical_identifier_should_use_id
1393 1392 # numeric identifier are no longer allowed
1394 1393 Project.where(:id => 1).update_all(:identifier => 25)
1395 1394 assert_equal '<a href="/projects/1">eCookbook</a>',
1396 1395 link_to_project(Project.find(1))
1397 1396 end
1398 1397
1399 1398 def test_principals_options_for_select_with_users
1400 1399 User.current = nil
1401 1400 users = [User.find(2), User.find(4)]
1402 1401 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>),
1403 1402 principals_options_for_select(users)
1404 1403 end
1405 1404
1406 1405 def test_principals_options_for_select_with_selected
1407 1406 User.current = nil
1408 1407 users = [User.find(2), User.find(4)]
1409 1408 assert_equal %(<option value="2">John Smith</option><option value="4" selected="selected">Robert Hill</option>),
1410 1409 principals_options_for_select(users, User.find(4))
1411 1410 end
1412 1411
1413 1412 def test_principals_options_for_select_with_users_and_groups
1414 1413 User.current = nil
1415 1414 set_language_if_valid 'en'
1416 1415 users = [User.find(2), Group.find(11), User.find(4), Group.find(10)]
1417 1416 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>) +
1418 1417 %(<optgroup label="Groups"><option value="10">A Team</option><option value="11">B Team</option></optgroup>),
1419 1418 principals_options_for_select(users)
1420 1419 end
1421 1420
1422 1421 def test_principals_options_for_select_with_empty_collection
1423 1422 assert_equal '', principals_options_for_select([])
1424 1423 end
1425 1424
1426 1425 def test_principals_options_for_select_should_include_me_option_when_current_user_is_in_collection
1427 1426 set_language_if_valid 'en'
1428 1427 users = [User.find(2), User.find(4)]
1429 1428 User.current = User.find(4)
1430 1429 assert_include '<option value="4">&lt;&lt; me &gt;&gt;</option>', principals_options_for_select(users)
1431 1430 end
1432 1431
1433 1432 def test_stylesheet_link_tag_should_pick_the_default_stylesheet
1434 1433 assert_match 'href="/stylesheets/styles.css"', stylesheet_link_tag("styles")
1435 1434 end
1436 1435
1437 1436 def test_stylesheet_link_tag_for_plugin_should_pick_the_plugin_stylesheet
1438 1437 assert_match 'href="/plugin_assets/foo/stylesheets/styles.css"', stylesheet_link_tag("styles", :plugin => :foo)
1439 1438 end
1440 1439
1441 1440 def test_image_tag_should_pick_the_default_image
1442 1441 assert_match 'src="/images/image.png"', image_tag("image.png")
1443 1442 end
1444 1443
1445 1444 def test_image_tag_should_pick_the_theme_image_if_it_exists
1446 1445 theme = Redmine::Themes.themes.last
1447 1446 theme.images << 'image.png'
1448 1447
1449 1448 with_settings :ui_theme => theme.id do
1450 1449 assert_match %|src="/themes/#{theme.dir}/images/image.png"|, image_tag("image.png")
1451 1450 assert_match %|src="/images/other.png"|, image_tag("other.png")
1452 1451 end
1453 1452 ensure
1454 1453 theme.images.delete 'image.png'
1455 1454 end
1456 1455
1457 1456 def test_image_tag_sfor_plugin_should_pick_the_plugin_image
1458 1457 assert_match 'src="/plugin_assets/foo/images/image.png"', image_tag("image.png", :plugin => :foo)
1459 1458 end
1460 1459
1461 1460 def test_javascript_include_tag_should_pick_the_default_javascript
1462 1461 assert_match 'src="/javascripts/scripts.js"', javascript_include_tag("scripts")
1463 1462 end
1464 1463
1465 1464 def test_javascript_include_tag_for_plugin_should_pick_the_plugin_javascript
1466 1465 assert_match 'src="/plugin_assets/foo/javascripts/scripts.js"', javascript_include_tag("scripts", :plugin => :foo)
1467 1466 end
1468 1467
1469 1468 def test_raw_json_should_escape_closing_tags
1470 1469 s = raw_json(["<foo>bar</foo>"])
1471 1470 assert_include '\/foo', s
1472 1471 end
1473 1472
1474 1473 def test_raw_json_should_be_html_safe
1475 1474 s = raw_json(["foo"])
1476 1475 assert s.html_safe?
1477 1476 end
1478 1477
1479 1478 def test_html_title_should_app_title_if_not_set
1480 1479 assert_equal 'Redmine', html_title
1481 1480 end
1482 1481
1483 1482 def test_html_title_should_join_items
1484 1483 html_title 'Foo', 'Bar'
1485 1484 assert_equal 'Foo - Bar - Redmine', html_title
1486 1485 end
1487 1486
1488 1487 def test_html_title_should_append_current_project_name
1489 1488 @project = Project.find(1)
1490 1489 html_title 'Foo', 'Bar'
1491 1490 assert_equal 'Foo - Bar - eCookbook - Redmine', html_title
1492 1491 end
1493 1492
1494 1493 def test_title_should_return_a_h2_tag
1495 1494 assert_equal '<h2>Foo</h2>', title('Foo')
1496 1495 end
1497 1496
1498 1497 def test_title_should_set_html_title
1499 1498 title('Foo')
1500 1499 assert_equal 'Foo - Redmine', html_title
1501 1500 end
1502 1501
1503 1502 def test_title_should_turn_arrays_into_links
1504 1503 assert_equal '<h2><a href="/foo">Foo</a></h2>', title(['Foo', '/foo'])
1505 1504 assert_equal 'Foo - Redmine', html_title
1506 1505 end
1507 1506
1508 1507 def test_title_should_join_items
1509 1508 assert_equal '<h2>Foo &#187; Bar</h2>', title('Foo', 'Bar')
1510 1509 assert_equal 'Bar - Foo - Redmine', html_title
1511 1510 end
1512 1511
1513 1512 def test_favicon_path
1514 1513 assert_match %r{^/favicon\.ico}, favicon_path
1515 1514 end
1516 1515
1517 1516 def test_favicon_path_with_suburi
1518 1517 Redmine::Utils.relative_url_root = '/foo'
1519 1518 assert_match %r{^/foo/favicon\.ico}, favicon_path
1520 1519 ensure
1521 1520 Redmine::Utils.relative_url_root = ''
1522 1521 end
1523 1522
1524 1523 def test_favicon_url
1525 1524 assert_match %r{^http://test\.host/favicon\.ico}, favicon_url
1526 1525 end
1527 1526
1528 1527 def test_favicon_url_with_suburi
1529 1528 Redmine::Utils.relative_url_root = '/foo'
1530 1529 assert_match %r{^http://test\.host/foo/favicon\.ico}, favicon_url
1531 1530 ensure
1532 1531 Redmine::Utils.relative_url_root = ''
1533 1532 end
1534 1533
1535 1534 def test_truncate_single_line
1536 1535 str = "01234"
1537 1536 result = truncate_single_line_raw("#{str}\n#{str}", 10)
1538 1537 assert_equal "01234 0...", result
1539 1538 assert !result.html_safe?
1540 1539 result = truncate_single_line_raw("#{str}<&#>\n#{str}#{str}", 16)
1541 1540 assert_equal "01234<&#> 012...", result
1542 1541 assert !result.html_safe?
1543 1542 end
1544 1543
1545 1544 def test_truncate_single_line_non_ascii
1546 1545 ja = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e".force_encoding('UTF-8')
1547 1546 result = truncate_single_line_raw("#{ja}\n#{ja}\n#{ja}", 10)
1548 1547 assert_equal "#{ja} #{ja}...", result
1549 1548 assert !result.html_safe?
1550 1549 end
1551 1550
1552 1551 def test_back_url_should_remove_utf8_checkmark_from_referer
1553 1552 stubs(:request).returns(stub(:env => {'HTTP_REFERER' => "/path?utf8=\u2713&foo=bar"}))
1554 1553 assert_equal "/path?foo=bar", back_url
1555 1554 end
1556 1555
1557 1556 def test_hours_formatting
1558 1557 set_language_if_valid 'en'
1559 1558
1560 1559 with_settings :timespan_format => 'minutes' do
1561 1560 assert_equal '0:45', format_hours(0.75)
1562 1561 assert_equal '0:45 h', l_hours_short(0.75)
1563 1562 assert_equal '0:45 hour', l_hours(0.75)
1564 1563 end
1565 1564 with_settings :timespan_format => 'decimal' do
1566 1565 assert_equal '0.75', format_hours(0.75)
1567 1566 assert_equal '0.75 h', l_hours_short(0.75)
1568 1567 assert_equal '0.75 hour', l_hours(0.75)
1569 1568 end
1570 1569 end
1571 1570
1572 1571 def test_html_hours
1573 1572 assert_equal '<span class="hours hours-int">0</span><span class="hours hours-dec">:45</span>', html_hours('0:45')
1574 1573 assert_equal '<span class="hours hours-int">0</span><span class="hours hours-dec">.75</span>', html_hours('0.75')
1575 1574 end
1576 1575 end
@@ -1,60 +1,59
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class CustomFieldsHelperTest < Redmine::HelperTest
21 21 include ApplicationHelper
22 22 include CustomFieldsHelper
23 include Redmine::I18n
24 23 include ERB::Util
25 24
26 25 def test_format_boolean_value
27 26 I18n.locale = 'en'
28 27 assert_equal 'Yes', format_value('1', CustomField.new(:field_format => 'bool'))
29 28 assert_equal 'No', format_value('0', CustomField.new(:field_format => 'bool'))
30 29 end
31 30
32 31 def test_label_tag_should_include_description_as_span_title_if_present
33 32 field = CustomField.new(:field_format => 'string', :description => 'This is the description')
34 33 tag = custom_field_label_tag('foo', CustomValue.new(:custom_field => field))
35 34 assert_select_in tag, 'label span[title=?]', 'This is the description'
36 35 end
37 36
38 37 def test_label_tag_should_not_include_title_if_description_is_blank
39 38 field = CustomField.new(:field_format => 'string')
40 39 tag = custom_field_label_tag('foo', CustomValue.new(:custom_field => field))
41 40 assert_select_in tag, 'label span[title]', 0
42 41 end
43 42
44 43 def test_unknow_field_format_should_be_edited_as_string
45 44 field = CustomField.new(:field_format => 'foo')
46 45 value = CustomValue.new(:value => 'bar', :custom_field => field)
47 46 field.id = 52
48 47
49 48 assert_select_in custom_field_tag('object', value),
50 49 'input[type=text][value=bar][name=?]', 'object[custom_field_values][52]'
51 50 end
52 51
53 52 def test_unknow_field_format_should_be_bulk_edited_as_string
54 53 field = CustomField.new(:field_format => 'foo')
55 54 field.id = 52
56 55
57 56 assert_select_in custom_field_tag_for_bulk_edit('object', field),
58 57 'input[type=text][value=""][name=?]', 'object[custom_field_values][52]'
59 58 end
60 59 end
@@ -1,43 +1,42
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class GroupsHelperTest < Redmine::HelperTest
21 include Redmine::I18n
22 21 include ERB::Util
23 22 include GroupsHelper
24 23 include Rails.application.routes.url_helpers
25 24
26 25 fixtures :users
27 26
28 27 def test_render_principals_for_new_group_users
29 28 group = Group.generate!
30 29
31 30 result = render_principals_for_new_group_users(group)
32 31 assert_select_in result, 'input[name=?][value="2"]', 'user_ids[]'
33 32 end
34 33
35 34 def test_render_principals_for_new_group_users_with_limited_results_should_paginate
36 35 group = Group.generate!
37 36
38 37 result = render_principals_for_new_group_users(group, 3)
39 38 assert_select_in result, 'span.pagination'
40 39 assert_select_in result, 'span.pagination li.current span', :text => '1'
41 40 assert_select_in result, 'a[href=?]', "/groups/#{group.id}/autocomplete_for_user.js?page=2", :text => '2'
42 41 end
43 42 end
@@ -1,338 +1,332
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class IssuesHelperTest < Redmine::HelperTest
21 include Redmine::I18n
22 21 include IssuesHelper
23 22 include CustomFieldsHelper
24 23 include ERB::Util
25 24 include Rails.application.routes.url_helpers
26 25
27 26 fixtures :projects, :trackers, :issue_statuses, :issues,
28 27 :enumerations, :users, :issue_categories,
29 28 :projects_trackers,
30 29 :roles,
31 30 :member_roles,
32 31 :members,
33 32 :enabled_modules,
34 33 :custom_fields,
35 34 :attachments,
36 35 :versions
37 36
38 def setup
39 super
40 set_language_if_valid('en')
41 end
42
43 37 def test_issue_heading
44 38 assert_equal "Bug #1", issue_heading(Issue.find(1))
45 39 end
46 40
47 41 def test_issues_destroy_confirmation_message_with_one_root_issue
48 42 assert_equal l(:text_issues_destroy_confirmation),
49 43 issues_destroy_confirmation_message(Issue.find(1))
50 44 end
51 45
52 46 def test_issues_destroy_confirmation_message_with_an_arrayt_of_root_issues
53 47 assert_equal l(:text_issues_destroy_confirmation),
54 48 issues_destroy_confirmation_message(Issue.find([1, 2]))
55 49 end
56 50
57 51 def test_issues_destroy_confirmation_message_with_one_parent_issue
58 52 Issue.find(2).update! :parent_issue_id => 1
59 53 assert_equal l(:text_issues_destroy_confirmation) + "\n" +
60 54 l(:text_issues_destroy_descendants_confirmation, :count => 1),
61 55 issues_destroy_confirmation_message(Issue.find(1))
62 56 end
63 57
64 58 def test_issues_destroy_confirmation_message_with_one_parent_issue_and_its_child
65 59 Issue.find(2).update! :parent_issue_id => 1
66 60 assert_equal l(:text_issues_destroy_confirmation),
67 61 issues_destroy_confirmation_message(Issue.find([1, 2]))
68 62 end
69 63
70 64 def test_issues_destroy_confirmation_message_with_issues_that_share_descendants
71 65 root = Issue.generate!
72 66 child = Issue.generate!(:parent_issue_id => root.id)
73 67 Issue.generate!(:parent_issue_id => child.id)
74 68
75 69 assert_equal l(:text_issues_destroy_confirmation) + "\n" +
76 70 l(:text_issues_destroy_descendants_confirmation, :count => 1),
77 71 issues_destroy_confirmation_message([root.reload, child.reload])
78 72 end
79 73
80 74 test 'show_detail with no_html should show a changing attribute' do
81 75 detail = JournalDetail.new(:property => 'attr', :old_value => '40',
82 76 :value => '100', :prop_key => 'done_ratio')
83 77 assert_equal "% Done changed from 40 to 100", show_detail(detail, true)
84 78 end
85 79
86 80 test 'show_detail with no_html should show a new attribute' do
87 81 detail = JournalDetail.new(:property => 'attr', :old_value => nil,
88 82 :value => '100', :prop_key => 'done_ratio')
89 83 assert_equal "% Done set to 100", show_detail(detail, true)
90 84 end
91 85
92 86 test 'show_detail with no_html should show a deleted attribute' do
93 87 detail = JournalDetail.new(:property => 'attr', :old_value => '50',
94 88 :value => nil, :prop_key => 'done_ratio')
95 89 assert_equal "% Done deleted (50)", show_detail(detail, true)
96 90 end
97 91
98 92 test 'show_detail with html should show a changing attribute with HTML highlights' do
99 93 detail = JournalDetail.new(:property => 'attr', :old_value => '40',
100 94 :value => '100', :prop_key => 'done_ratio')
101 95 html = show_detail(detail, false)
102 96 assert_include '<strong>% Done</strong>', html
103 97 assert_include '<i>40</i>', html
104 98 assert_include '<i>100</i>', html
105 99 end
106 100
107 101 test 'show_detail with html should show a new attribute with HTML highlights' do
108 102 detail = JournalDetail.new(:property => 'attr', :old_value => nil,
109 103 :value => '100', :prop_key => 'done_ratio')
110 104 html = show_detail(detail, false)
111 105 assert_include '<strong>% Done</strong>', html
112 106 assert_include '<i>100</i>', html
113 107 end
114 108
115 109 test 'show_detail with html should show a deleted attribute with HTML highlights' do
116 110 detail = JournalDetail.new(:property => 'attr', :old_value => '50',
117 111 :value => nil, :prop_key => 'done_ratio')
118 112 html = show_detail(detail, false)
119 113 assert_include '<strong>% Done</strong>', html
120 114 assert_include '<del><i>50</i></del>', html
121 115 end
122 116
123 117 test 'show_detail with a start_date attribute should format the dates' do
124 118 detail = JournalDetail.new(
125 119 :property => 'attr',
126 120 :old_value => '2010-01-01',
127 121 :value => '2010-01-31',
128 122 :prop_key => 'start_date'
129 123 )
130 124 with_settings :date_format => '%m/%d/%Y' do
131 125 assert_match "01/31/2010", show_detail(detail, true)
132 126 assert_match "01/01/2010", show_detail(detail, true)
133 127 end
134 128 end
135 129
136 130 test 'show_detail with a due_date attribute should format the dates' do
137 131 detail = JournalDetail.new(
138 132 :property => 'attr',
139 133 :old_value => '2010-01-01',
140 134 :value => '2010-01-31',
141 135 :prop_key => 'due_date'
142 136 )
143 137 with_settings :date_format => '%m/%d/%Y' do
144 138 assert_match "01/31/2010", show_detail(detail, true)
145 139 assert_match "01/01/2010", show_detail(detail, true)
146 140 end
147 141 end
148 142
149 143 test 'show_detail should show old and new values with a project attribute' do
150 144 detail = JournalDetail.new(:property => 'attr', :prop_key => 'project_id',
151 145 :old_value => 1, :value => 2)
152 146 assert_match 'eCookbook', show_detail(detail, true)
153 147 assert_match 'OnlineStore', show_detail(detail, true)
154 148 end
155 149
156 150 test 'show_detail should show old and new values with a issue status attribute' do
157 151 detail = JournalDetail.new(:property => 'attr', :prop_key => 'status_id',
158 152 :old_value => 1, :value => 2)
159 153 assert_match 'New', show_detail(detail, true)
160 154 assert_match 'Assigned', show_detail(detail, true)
161 155 end
162 156
163 157 test 'show_detail should show old and new values with a tracker attribute' do
164 158 detail = JournalDetail.new(:property => 'attr', :prop_key => 'tracker_id',
165 159 :old_value => 1, :value => 2)
166 160 assert_match 'Bug', show_detail(detail, true)
167 161 assert_match 'Feature request', show_detail(detail, true)
168 162 end
169 163
170 164 test 'show_detail should show old and new values with a assigned to attribute' do
171 165 detail = JournalDetail.new(:property => 'attr', :prop_key => 'assigned_to_id',
172 166 :old_value => 1, :value => 2)
173 167 assert_match 'Redmine Admin', show_detail(detail, true)
174 168 assert_match 'John Smith', show_detail(detail, true)
175 169 end
176 170
177 171 test 'show_detail should show old and new values with a priority attribute' do
178 172 detail = JournalDetail.new(:property => 'attr', :prop_key => 'priority_id',
179 173 :old_value => 4, :value => 5)
180 174 assert_match 'Low', show_detail(detail, true)
181 175 assert_match 'Normal', show_detail(detail, true)
182 176 end
183 177
184 178 test 'show_detail should show old and new values with a category attribute' do
185 179 detail = JournalDetail.new(:property => 'attr', :prop_key => 'category_id',
186 180 :old_value => 1, :value => 2)
187 181 assert_match 'Printing', show_detail(detail, true)
188 182 assert_match 'Recipes', show_detail(detail, true)
189 183 end
190 184
191 185 test 'show_detail should show old and new values with a fixed version attribute' do
192 186 detail = JournalDetail.new(:property => 'attr', :prop_key => 'fixed_version_id',
193 187 :old_value => 1, :value => 2)
194 188 assert_match '0.1', show_detail(detail, true)
195 189 assert_match '1.0', show_detail(detail, true)
196 190 end
197 191
198 192 test 'show_detail should show old and new values with a estimated hours attribute' do
199 193 detail = JournalDetail.new(:property => 'attr', :prop_key => 'estimated_hours',
200 194 :old_value => '5', :value => '6.3')
201 195 assert_match '5.00', show_detail(detail, true)
202 196 assert_match '6.30', show_detail(detail, true)
203 197 end
204 198
205 199 test 'show_detail should not show values with a description attribute' do
206 200 detail = JournalDetail.new(:property => 'attr', :prop_key => 'description',
207 201 :old_value => 'Foo', :value => 'Bar')
208 202 assert_equal 'Description updated', show_detail(detail, true)
209 203 end
210 204
211 205 test 'show_detail should show old and new values with a custom field' do
212 206 detail = JournalDetail.new(:property => 'cf', :prop_key => '1',
213 207 :old_value => 'MySQL', :value => 'PostgreSQL')
214 208 assert_equal 'Database changed from MySQL to PostgreSQL', show_detail(detail, true)
215 209 end
216 210
217 211 test 'show_detail should not show values with a long text custom field' do
218 212 field = IssueCustomField.create!(:name => "Long field", :field_format => 'text')
219 213 detail = JournalDetail.new(:property => 'cf', :prop_key => field.id,
220 214 :old_value => 'Foo', :value => 'Bar')
221 215 assert_equal 'Long field updated', show_detail(detail, true)
222 216 end
223 217
224 218 test 'show_detail should show added file' do
225 219 detail = JournalDetail.new(:property => 'attachment', :prop_key => '1',
226 220 :old_value => nil, :value => 'error281.txt')
227 221 assert_match 'error281.txt', show_detail(detail, true)
228 222 end
229 223
230 224 test 'show_detail should show removed file' do
231 225 detail = JournalDetail.new(:property => 'attachment', :prop_key => '1',
232 226 :old_value => 'error281.txt', :value => nil)
233 227 assert_match 'error281.txt', show_detail(detail, true)
234 228 end
235 229
236 230 def test_show_detail_relation_added
237 231 detail = JournalDetail.new(:property => 'relation',
238 232 :prop_key => 'precedes',
239 233 :value => 1)
240 234 assert_equal "Precedes Bug #1: Cannot print recipes added", show_detail(detail, true)
241 235 str = link_to("Bug #1", "/issues/1", :class => Issue.find(1).css_classes)
242 236 assert_equal "<strong>Precedes</strong> <i>#{str}: Cannot print recipes</i> added",
243 237 show_detail(detail, false)
244 238 end
245 239
246 240 def test_show_detail_relation_added_with_inexistant_issue
247 241 inexistant_issue_number = 9999
248 242 assert_nil Issue.find_by_id(inexistant_issue_number)
249 243 detail = JournalDetail.new(:property => 'relation',
250 244 :prop_key => 'precedes',
251 245 :value => inexistant_issue_number)
252 246 assert_equal "Precedes Issue ##{inexistant_issue_number} added", show_detail(detail, true)
253 247 assert_equal "<strong>Precedes</strong> <i>Issue ##{inexistant_issue_number}</i> added", show_detail(detail, false)
254 248 end
255 249
256 250 def test_show_detail_relation_added_should_not_disclose_issue_that_is_not_visible
257 251 issue = Issue.generate!(:is_private => true)
258 252 detail = JournalDetail.new(:property => 'relation',
259 253 :prop_key => 'precedes',
260 254 :value => issue.id)
261 255
262 256 assert_equal "Precedes Issue ##{issue.id} added", show_detail(detail, true)
263 257 assert_equal "<strong>Precedes</strong> <i>Issue ##{issue.id}</i> added", show_detail(detail, false)
264 258 end
265 259
266 260 def test_show_detail_relation_deleted
267 261 detail = JournalDetail.new(:property => 'relation',
268 262 :prop_key => 'precedes',
269 263 :old_value => 1)
270 264 assert_equal "Precedes deleted (Bug #1: Cannot print recipes)", show_detail(detail, true)
271 265 str = link_to("Bug #1",
272 266 "/issues/1",
273 267 :class => Issue.find(1).css_classes)
274 268 assert_equal "<strong>Precedes</strong> deleted (<i>#{str}: Cannot print recipes</i>)",
275 269 show_detail(detail, false)
276 270 end
277 271
278 272 def test_show_detail_relation_deleted_with_inexistant_issue
279 273 inexistant_issue_number = 9999
280 274 assert_nil Issue.find_by_id(inexistant_issue_number)
281 275 detail = JournalDetail.new(:property => 'relation',
282 276 :prop_key => 'precedes',
283 277 :old_value => inexistant_issue_number)
284 278 assert_equal "Precedes deleted (Issue #9999)", show_detail(detail, true)
285 279 assert_equal "<strong>Precedes</strong> deleted (<i>Issue #9999</i>)", show_detail(detail, false)
286 280 end
287 281
288 282 def test_show_detail_relation_deleted_should_not_disclose_issue_that_is_not_visible
289 283 issue = Issue.generate!(:is_private => true)
290 284 detail = JournalDetail.new(:property => 'relation',
291 285 :prop_key => 'precedes',
292 286 :old_value => issue.id)
293 287
294 288 assert_equal "Precedes deleted (Issue ##{issue.id})", show_detail(detail, true)
295 289 assert_equal "<strong>Precedes</strong> deleted (<i>Issue ##{issue.id}</i>)", show_detail(detail, false)
296 290 end
297 291
298 292 def test_details_to_strings_with_multiple_values_removed_from_custom_field
299 293 field = IssueCustomField.generate!(:name => 'User', :field_format => 'user', :multiple => true)
300 294 details = []
301 295 details << JournalDetail.new(:property => 'cf', :prop_key => field.id.to_s, :old_value => '1', :value => nil)
302 296 details << JournalDetail.new(:property => 'cf', :prop_key => field.id.to_s, :old_value => '3', :value => nil)
303 297
304 298 assert_equal ["User deleted (Dave Lopper, Redmine Admin)"], details_to_strings(details, true)
305 299 assert_equal ["<strong>User</strong> deleted (<del><i>Dave Lopper, Redmine Admin</i></del>)"], details_to_strings(details, false)
306 300 end
307 301
308 302 def test_details_to_strings_with_multiple_values_added_to_custom_field
309 303 field = IssueCustomField.generate!(:name => 'User', :field_format => 'user', :multiple => true)
310 304 details = []
311 305 details << JournalDetail.new(:property => 'cf', :prop_key => field.id.to_s, :old_value => nil, :value => '1')
312 306 details << JournalDetail.new(:property => 'cf', :prop_key => field.id.to_s, :old_value => nil, :value => '3')
313 307
314 308 assert_equal ["User Dave Lopper, Redmine Admin added"], details_to_strings(details, true)
315 309 assert_equal ["<strong>User</strong> <i>Dave Lopper, Redmine Admin</i> added"], details_to_strings(details, false)
316 310 end
317 311
318 312 def test_details_to_strings_with_multiple_values_added_and_removed_from_custom_field
319 313 field = IssueCustomField.generate!(:name => 'User', :field_format => 'user', :multiple => true)
320 314 details = []
321 315 details << JournalDetail.new(:property => 'cf', :prop_key => field.id.to_s, :old_value => nil, :value => '1')
322 316 details << JournalDetail.new(:property => 'cf', :prop_key => field.id.to_s, :old_value => '2', :value => nil)
323 317 details << JournalDetail.new(:property => 'cf', :prop_key => field.id.to_s, :old_value => '3', :value => nil)
324 318
325 319 assert_equal [
326 320 "User Redmine Admin added",
327 321 "User deleted (Dave Lopper, John Smith)"
328 322 ], details_to_strings(details, true)
329 323 assert_equal [
330 324 "<strong>User</strong> <i>Redmine Admin</i> added",
331 325 "<strong>User</strong> deleted (<del><i>Dave Lopper, John Smith</i></del>)"
332 326 ], details_to_strings(details, false)
333 327 end
334 328
335 329 def test_find_name_by_reflection_should_return_nil_for_missing_record
336 330 assert_nil find_name_by_reflection('status', 99)
337 331 end
338 332 end
@@ -1,44 +1,43
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class MembersHelperTest < Redmine::HelperTest
21 include Redmine::I18n
22 21 include ERB::Util
23 22 include MembersHelper
24 23 include Rails.application.routes.url_helpers
25 24
26 25 fixtures :projects, :users, :members, :member_roles,
27 26 :trackers, :issue_statuses
28 27
29 28 def test_render_principals_for_new_members
30 29 project = Project.generate!
31 30
32 31 result = render_principals_for_new_members(project)
33 32 assert_select_in result, 'input[name=?][value="2"]', 'membership[user_ids][]'
34 33 end
35 34
36 35 def test_render_principals_for_new_members_with_limited_results_should_paginate
37 36 project = Project.generate!
38 37
39 38 result = render_principals_for_new_members(project, 3)
40 39 assert_select_in result, 'span.pagination'
41 40 assert_select_in result, 'span.pagination li.current span', :text => '1'
42 41 assert_select_in result, 'a[href=?]', "/projects/#{project.identifier}/memberships/autocomplete.js?page=2", :text => '2'
43 42 end
44 43 end
@@ -1,50 +1,49
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class MyHelperTest < Redmine::HelperTest
21 include Redmine::I18n
22 21 include ERB::Util
23 22 include MyHelper
24 23
25 24
26 25 fixtures :projects, :trackers, :issue_statuses, :issues,
27 26 :enumerations, :users, :issue_categories,
28 27 :projects_trackers,
29 28 :roles,
30 29 :member_roles,
31 30 :members,
32 31 :enabled_modules,
33 32 :versions
34 33
35 34 def test_timelog_items_should_include_time_entries_without_issue
36 35 User.current = User.find(2)
37 36 entry = TimeEntry.generate!(:spent_on => Date.today, :user_id => 2, :project_id => 1)
38 37 assert_nil entry.issue
39 38
40 39 assert_include entry, timelog_items.first
41 40 end
42 41
43 42 def test_timelog_items_should_include_time_entries_with_issue
44 43 User.current = User.find(2)
45 44 entry = TimeEntry.generate!(:spent_on => Date.today, :user_id => 2, :project_id => 1, :issue_id => 1)
46 45 assert_not_nil entry.issue
47 46
48 47 assert_include entry, timelog_items.first
49 48 end
50 49 end
@@ -1,84 +1,78
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class ProjectsHelperTest < Redmine::HelperTest
21 21 include ApplicationHelper
22 22 include ProjectsHelper
23 include Redmine::I18n
24 23 include ERB::Util
25 24 include Rails.application.routes.url_helpers
26 25
27 26 fixtures :projects, :trackers, :issue_statuses, :issues,
28 27 :enumerations, :users, :issue_categories,
29 28 :versions,
30 29 :projects_trackers,
31 30 :member_roles,
32 31 :members,
33 32 :groups_users,
34 33 :enabled_modules
35 34
36 def setup
37 super
38 set_language_if_valid('en')
39 end
40
41 35 def test_link_to_version_within_project
42 36 @project = Project.find(2)
43 37 User.current = User.find(1)
44 38 assert_equal '<a title="07/01/2006" href="/versions/5">Alpha</a>', link_to_version(Version.find(5))
45 39 end
46 40
47 41 def test_link_to_version
48 42 User.current = User.find(1)
49 43 assert_equal '<a title="07/01/2006" href="/versions/5">OnlineStore - Alpha</a>', link_to_version(Version.find(5))
50 44 end
51 45
52 46 def test_link_to_version_without_effective_date
53 47 User.current = User.find(1)
54 48 version = Version.find(5)
55 49 version.effective_date = nil
56 50 assert_equal '<a href="/versions/5">OnlineStore - Alpha</a>', link_to_version(version)
57 51 end
58 52
59 53 def test_link_to_private_version
60 54 assert_equal 'OnlineStore - Alpha', link_to_version(Version.find(5))
61 55 end
62 56
63 57 def test_link_to_version_invalid_version
64 58 assert_equal '', link_to_version(Object)
65 59 end
66 60
67 61 def test_format_version_name_within_project
68 62 @project = Project.find(1)
69 63 assert_equal "0.1", format_version_name(Version.find(1))
70 64 end
71 65
72 66 def test_format_version_name
73 67 assert_equal "eCookbook - 0.1", format_version_name(Version.find(1))
74 68 end
75 69
76 70 def test_format_version_name_for_system_version
77 71 assert_equal "OnlineStore - Systemwide visible version", format_version_name(Version.find(7))
78 72 end
79 73
80 74 def test_version_options_for_select_with_no_versions
81 75 assert_equal '', version_options_for_select([])
82 76 assert_equal '', version_options_for_select([], Version.find(1))
83 77 end
84 78 end
@@ -1,97 +1,96
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class QueriesHelperTest < Redmine::HelperTest
21 21 include QueriesHelper
22 include Redmine::I18n
23 22
24 23 fixtures :projects, :enabled_modules, :users, :members,
25 24 :member_roles, :roles, :trackers, :issue_statuses,
26 25 :issue_categories, :enumerations, :issues,
27 26 :watchers, :custom_fields, :custom_values, :versions,
28 27 :queries,
29 28 :projects_trackers,
30 29 :custom_fields_trackers
31 30
32 31 def test_filters_options_for_select_should_have_a_blank_option
33 32 options = filters_options_for_select(IssueQuery.new)
34 33 assert_select_in options, 'option[value=""]'
35 34 end
36 35
37 36 def test_filters_options_for_select_should_not_group_regular_filters
38 37 with_locale 'en' do
39 38 options = filters_options_for_select(IssueQuery.new)
40 39 assert_select_in options, 'optgroup option[value=status_id]', 0
41 40 assert_select_in options, 'option[value=status_id]', :text => 'Status'
42 41 end
43 42 end
44 43
45 44 def test_filters_options_for_select_should_group_date_filters
46 45 with_locale 'en' do
47 46 options = filters_options_for_select(IssueQuery.new)
48 47 assert_select_in options, 'optgroup[label=?]', 'Date', 1
49 48 assert_select_in options, 'optgroup > option[value=due_date]', :text => 'Due date'
50 49 end
51 50 end
52 51
53 52 def test_filters_options_for_select_should_not_group_only_one_date_filter
54 53 with_locale 'en' do
55 54 options = filters_options_for_select(TimeEntryQuery.new)
56 55 assert_select_in options, 'option[value=spent_on]'
57 56 assert_select_in options, 'optgroup[label=?]', 'Date', 0
58 57 assert_select_in options, 'optgroup option[value=spent_on]', 0
59 58 end
60 59 end
61 60
62 61 def test_filters_options_for_select_should_group_relations_filters
63 62 with_locale 'en' do
64 63 options = filters_options_for_select(IssueQuery.new)
65 64 assert_select_in options, 'optgroup[label=?]', 'Relations', 1
66 65 assert_select_in options, 'optgroup[label=?] > option', 'Relations', 11
67 66 assert_select_in options, 'optgroup > option[value=relates]', :text => 'Related to'
68 67 end
69 68 end
70 69
71 70 def test_filters_options_for_select_should_group_associations_filters
72 71 CustomField.delete_all
73 72 cf1 = ProjectCustomField.create!(:name => 'Foo', :field_format => 'string', :is_filter => true)
74 73 cf2 = ProjectCustomField.create!(:name => 'Bar', :field_format => 'string', :is_filter => true)
75 74
76 75 with_locale 'en' do
77 76 options = filters_options_for_select(IssueQuery.new)
78 77 assert_select_in options, 'optgroup[label=?]', 'Project', 1
79 78 assert_select_in options, 'optgroup[label=?] > option', 'Project', 2
80 79 assert_select_in options, 'optgroup > option[value=?]', "project.cf_#{cf1.id}", :text => "Project's Foo"
81 80 end
82 81 end
83 82
84 83 def test_query_to_csv_should_translate_boolean_custom_field_values
85 84 f = IssueCustomField.generate!(:field_format => 'bool', :name => 'Boolean', :is_for_all => true, :trackers => Tracker.all)
86 85 issues = [
87 86 Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {f.id.to_s => '0'}),
88 87 Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {f.id.to_s => '1'})
89 88 ]
90 89
91 90 with_locale 'fr' do
92 91 csv = query_to_csv(issues, IssueQuery.new(:column_names => ['id', "cf_#{f.id}"]))
93 92 assert_include "Oui", csv
94 93 assert_include "Non", csv
95 94 end
96 95 end
97 96 end
@@ -1,49 +1,48
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2016 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 require File.expand_path('../../../test_helper', __FILE__)
21 21
22 22 class SearchHelperTest < Redmine::HelperTest
23 23 include SearchHelper
24 include Redmine::I18n
25 24 include ERB::Util
26 25
27 26 def test_highlight_single_token
28 27 assert_equal 'This is a <span class="highlight token-0">token</span>.',
29 28 highlight_tokens('This is a token.', %w(token))
30 29 end
31 30
32 31 def test_highlight_multiple_tokens
33 32 assert_equal 'This is a <span class="highlight token-0">token</span> and <span class="highlight token-1">another</span> <span class="highlight token-0">token</span>.',
34 33 highlight_tokens('This is a token and another token.', %w(token another))
35 34 end
36 35
37 36 def test_highlight_should_not_exceed_maximum_length
38 37 s = (('1234567890' * 100) + ' token ') * 100
39 38 r = highlight_tokens(s, %w(token))
40 39 assert r.include?('<span class="highlight token-0">token</span>')
41 40 assert r.length <= 1300
42 41 end
43 42
44 43 def test_highlight_multibyte
45 44 s = ('ΠΉ' * 200) + ' token ' + ('ΠΉ' * 200)
46 45 r = highlight_tokens(s, %w(token))
47 46 assert_equal ('ΠΉ' * 45) + ' ... ' + ('ΠΉ' * 44) + ' <span class="highlight token-0">token</span> ' + ('ΠΉ' * 44) + ' ... ' + ('ΠΉ' * 45), r
48 47 end
49 48 end
@@ -1,31 +1,30
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class SettingsHelperTest < Redmine::HelperTest
21 21 include SettingsHelper
22 include Redmine::I18n
23 22 include ERB::Util
24 23
25 24 def test_date_format_setting_options_should_include_human_readable_format
26 25 Date.stubs(:today).returns(Date.parse("2015-07-14"))
27 26
28 27 options = date_format_setting_options('en')
29 28 assert_include ["2015-07-14 (yyyy-mm-dd)", "%Y-%m-%d"], options
30 29 end
31 30 end
@@ -1,110 +1,109
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class SortHelperTest < Redmine::HelperTest
21 21 include SortHelper
22 include Redmine::I18n
23 22 include ERB::Util
24 23
25 24 def setup
26 25 super
27 26 @session = nil
28 27 @sort_param = nil
29 28 end
30 29
31 30 def test_default_sort_clause_with_array
32 31 sort_init 'attr1', 'desc'
33 32 sort_update(['attr1', 'attr2'])
34 33
35 34 assert_equal ['attr1 DESC'], sort_clause
36 35 end
37 36
38 37 def test_default_sort_clause_with_hash
39 38 sort_init 'attr1', 'desc'
40 39 sort_update({'attr1' => 'table1.attr1', 'attr2' => 'table2.attr2'})
41 40
42 41 assert_equal ['table1.attr1 DESC'], sort_clause
43 42 end
44 43
45 44 def test_default_sort_clause_with_multiple_columns
46 45 sort_init 'attr1', 'desc'
47 46 sort_update({'attr1' => ['table1.attr1', 'table1.attr2'], 'attr2' => 'table2.attr2'})
48 47
49 48 assert_equal ['table1.attr1 DESC', 'table1.attr2 DESC'], sort_clause
50 49 end
51 50
52 51 def test_params_sort
53 52 @sort_param = 'attr1,attr2:desc'
54 53
55 54 sort_init 'attr1', 'desc'
56 55 sort_update({'attr1' => 'table1.attr1', 'attr2' => 'table2.attr2'})
57 56
58 57 assert_equal ['table1.attr1 ASC', 'table2.attr2 DESC'], sort_clause
59 58 assert_equal 'attr1,attr2:desc', @session['foo_bar_sort']
60 59 end
61 60
62 61 def test_invalid_params_sort
63 62 @sort_param = 'invalid_key'
64 63
65 64 sort_init 'attr1', 'desc'
66 65 sort_update({'attr1' => 'table1.attr1', 'attr2' => 'table2.attr2'})
67 66
68 67 assert_equal ['table1.attr1 DESC'], sort_clause
69 68 assert_equal 'attr1:desc', @session['foo_bar_sort']
70 69 end
71 70
72 71 def test_invalid_order_params_sort
73 72 @sort_param = 'attr1:foo:bar,attr2'
74 73
75 74 sort_init 'attr1', 'desc'
76 75 sort_update({'attr1' => 'table1.attr1', 'attr2' => 'table2.attr2'})
77 76
78 77 assert_equal ['table1.attr1 ASC', 'table2.attr2 ASC'], sort_clause
79 78 assert_equal 'attr1,attr2', @session['foo_bar_sort']
80 79 end
81 80
82 81 def test_sort_css_without_params_should_use_default_sort
83 82 sort_init 'attr1', 'desc'
84 83 sort_update(['attr1', 'attr2'])
85 84
86 85 assert_equal 'sort-by-attr1 sort-desc', sort_css_classes
87 86 end
88 87
89 88 def test_sort_css_should_use_params
90 89 @sort_param = 'attr2,attr1'
91 90 sort_init 'attr1', 'desc'
92 91 sort_update(['attr1', 'attr2'])
93 92
94 93 assert_equal 'sort-by-attr2 sort-asc', sort_css_classes
95 94 end
96 95
97 96 def test_sort_css_should_dasherize_sort_name
98 97 sort_init 'foo_bar'
99 98 sort_update(['foo_bar'])
100 99
101 100 assert_equal 'sort-by-foo-bar sort-asc', sort_css_classes
102 101 end
103 102
104 103 private
105 104
106 105 def controller_name; 'foo'; end
107 106 def action_name; 'bar'; end
108 107 def params; {:sort => @sort_param}; end
109 108 def session; @session ||= {}; end
110 109 end
@@ -1,54 +1,53
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class TimelogHelperTest < Redmine::HelperTest
21 21 include TimelogHelper
22 include Redmine::I18n
23 22 include ActionView::Helpers::TextHelper
24 23 include ActionView::Helpers::DateHelper
25 24 include ERB::Util
26 25
27 26 fixtures :projects, :roles, :enabled_modules, :users,
28 27 :repositories, :changesets,
29 28 :trackers, :issue_statuses, :issues, :versions, :documents,
30 29 :wikis, :wiki_pages, :wiki_contents,
31 30 :boards, :messages,
32 31 :attachments,
33 32 :enumerations
34 33
35 34 def test_activities_collection_for_select_options_should_return_array_of_activity_names_and_ids
36 35 activities = activity_collection_for_select_options
37 36 assert activities.include?(["Design", 9])
38 37 assert activities.include?(["Development", 10])
39 38 end
40 39
41 40 def test_activities_collection_for_select_options_should_not_include_inactive_activities
42 41 activities = activity_collection_for_select_options
43 42 assert !activities.include?(["Inactive Activity", 14])
44 43 end
45 44
46 45 def test_activities_collection_for_select_options_should_use_the_projects_override
47 46 project = Project.find(1)
48 47 override_activity = TimeEntryActivity.create!({:name => "Design override", :parent => TimeEntryActivity.find_by_name("Design"), :project => project})
49 48
50 49 activities = activity_collection_for_select_options(nil, project)
51 50 assert !activities.include?(["Design", 9]), "System activity found in: " + activities.inspect
52 51 assert activities.include?(["Design override", override_activity.id]), "Override activity not found in: " + activities.inspect
53 52 end
54 53 end
@@ -1,73 +1,67
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class WatchersHelperTest < Redmine::HelperTest
21 21 include WatchersHelper
22 include Redmine::I18n
23 22 include Rails.application.routes.url_helpers
24 23
25 24 fixtures :users, :issues
26 25
27 def setup
28 super
29 set_language_if_valid('en')
30 end
31
32 26 test '#watcher_link with a non-watched object' do
33 27 expected = link_to(
34 28 "Watch",
35 29 "/watchers/watch?object_id=1&object_type=issue",
36 30 :remote => true, :method => 'post', :class => "issue-1-watcher icon icon-fav-off"
37 31 )
38 32 assert_equal expected, watcher_link(Issue.find(1), User.find(1))
39 33 end
40 34
41 35 test '#watcher_link with a single objet array' do
42 36 expected = link_to(
43 37 "Watch",
44 38 "/watchers/watch?object_id=1&object_type=issue",
45 39 :remote => true, :method => 'post', :class => "issue-1-watcher icon icon-fav-off"
46 40 )
47 41 assert_equal expected, watcher_link([Issue.find(1)], User.find(1))
48 42 end
49 43
50 44 test '#watcher_link with a multiple objets array' do
51 45 expected = link_to(
52 46 "Watch",
53 47 "/watchers/watch?object_id%5B%5D=1&object_id%5B%5D=3&object_type=issue",
54 48 :remote => true, :method => 'post', :class => "issue-bulk-watcher icon icon-fav-off"
55 49 )
56 50 assert_equal expected, watcher_link([Issue.find(1), Issue.find(3)], User.find(1))
57 51 end
58 52
59 53 def test_watcher_link_with_nil_should_return_empty_string
60 54 assert_equal '', watcher_link(nil, User.find(1))
61 55 end
62 56
63 57 test '#watcher_link with a watched object' do
64 58 Watcher.create!(:watchable => Issue.find(1), :user => User.find(1))
65 59
66 60 expected = link_to(
67 61 "Unwatch",
68 62 "/watchers/watch?object_id=1&object_type=issue",
69 63 :remote => true, :method => 'delete', :class => "issue-1-watcher icon icon-fav"
70 64 )
71 65 assert_equal expected, watcher_link(Issue.find(1), User.find(1))
72 66 end
73 67 end
General Comments 0
You need to be logged in to leave comments. Login now