PageRenderTime 30ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/test/unit/helpers/application_helper_test.rb

https://bitbucket.org/prdixit/redmine
Ruby | 1133 lines | 886 code | 169 blank | 78 comment | 1 complexity | 768119ab30042db4e51f80d3d8e10467 MD5 | raw file
Possible License(s): GPL-2.0
  1. # encoding: utf-8
  2. #
  3. # Redmine - project management software
  4. # Copyright (C) 2006-2012 Jean-Philippe Lang
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License
  8. # as published by the Free Software Foundation; either version 2
  9. # of the License, or (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. require File.expand_path('../../../test_helper', __FILE__)
  20. class ApplicationHelperTest < ActionView::TestCase
  21. include ERB::Util
  22. fixtures :projects, :roles, :enabled_modules, :users,
  23. :repositories, :changesets,
  24. :trackers, :issue_statuses, :issues, :versions, :documents,
  25. :wikis, :wiki_pages, :wiki_contents,
  26. :boards, :messages, :news,
  27. :attachments, :enumerations
  28. def setup
  29. super
  30. set_tmp_attachments_directory
  31. end
  32. context "#link_to_if_authorized" do
  33. context "authorized user" do
  34. should "be tested"
  35. end
  36. context "unauthorized user" do
  37. should "be tested"
  38. end
  39. should "allow using the :controller and :action for the target link" do
  40. User.current = User.find_by_login('admin')
  41. @project = Issue.first.project # Used by helper
  42. response = link_to_if_authorized("By controller/action",
  43. {:controller => 'issues', :action => 'edit', :id => Issue.first.id})
  44. assert_match /href/, response
  45. end
  46. end
  47. def test_auto_links
  48. to_test = {
  49. 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
  50. 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
  51. 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
  52. 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
  53. 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
  54. 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
  55. '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>.',
  56. 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
  57. '(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>)',
  58. '(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>)',
  59. '(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>).',
  60. '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
  61. '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
  62. '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
  63. 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
  64. '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>',
  65. 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
  66. 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
  67. 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
  68. 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
  69. 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
  70. 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
  71. # two exclamation marks
  72. '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>',
  73. # escaping
  74. 'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo&quot;bar</a>',
  75. # wrap in angle brackets
  76. '<http://foo.bar>' => '&lt;<a class="external" href="http://foo.bar">http://foo.bar</a>&gt;'
  77. }
  78. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  79. end
  80. def test_auto_mailto
  81. assert_equal '<p><a class="email" href="mailto:test@foo.bar">test@foo.bar</a></p>',
  82. textilizable('test@foo.bar')
  83. end
  84. def test_inline_images
  85. to_test = {
  86. '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
  87. 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
  88. 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
  89. '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="" />',
  90. '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" />',
  91. '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;" />',
  92. }
  93. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  94. end
  95. def test_inline_images_inside_tags
  96. raw = <<-RAW
  97. h1. !foo.png! Heading
  98. Centered image:
  99. p=. !bar.gif!
  100. RAW
  101. assert textilizable(raw).include?('<img src="foo.png" alt="" />')
  102. assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
  103. end
  104. def test_attached_images
  105. to_test = {
  106. 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
  107. 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
  108. 'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
  109. 'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
  110. # link image
  111. '!logo.gif!:http://foo.bar/' => '<a href="http://foo.bar/"><img src="/attachments/download/3" title="This is a logo" alt="This is a logo" /></a>',
  112. }
  113. attachments = Attachment.find(:all)
  114. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
  115. end
  116. def test_attached_images_filename_extension
  117. set_tmp_attachments_directory
  118. a1 = Attachment.new(
  119. :container => Issue.find(1),
  120. :file => mock_file_with_options({:original_filename => "testtest.JPG"}),
  121. :author => User.find(1))
  122. assert a1.save
  123. assert_equal "testtest.JPG", a1.filename
  124. assert_equal "image/jpeg", a1.content_type
  125. assert a1.image?
  126. a2 = Attachment.new(
  127. :container => Issue.find(1),
  128. :file => mock_file_with_options({:original_filename => "testtest.jpeg"}),
  129. :author => User.find(1))
  130. assert a2.save
  131. assert_equal "testtest.jpeg", a2.filename
  132. assert_equal "image/jpeg", a2.content_type
  133. assert a2.image?
  134. a3 = Attachment.new(
  135. :container => Issue.find(1),
  136. :file => mock_file_with_options({:original_filename => "testtest.JPE"}),
  137. :author => User.find(1))
  138. assert a3.save
  139. assert_equal "testtest.JPE", a3.filename
  140. assert_equal "image/jpeg", a3.content_type
  141. assert a3.image?
  142. a4 = Attachment.new(
  143. :container => Issue.find(1),
  144. :file => mock_file_with_options({:original_filename => "Testtest.BMP"}),
  145. :author => User.find(1))
  146. assert a4.save
  147. assert_equal "Testtest.BMP", a4.filename
  148. assert_equal "image/x-ms-bmp", a4.content_type
  149. assert a4.image?
  150. to_test = {
  151. 'Inline image: !testtest.jpg!' =>
  152. 'Inline image: <img src="/attachments/download/' + a1.id.to_s + '" alt="" />',
  153. 'Inline image: !testtest.jpeg!' =>
  154. 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '" alt="" />',
  155. 'Inline image: !testtest.jpe!' =>
  156. 'Inline image: <img src="/attachments/download/' + a3.id.to_s + '" alt="" />',
  157. 'Inline image: !testtest.bmp!' =>
  158. 'Inline image: <img src="/attachments/download/' + a4.id.to_s + '" alt="" />',
  159. }
  160. attachments = [a1, a2, a3, a4]
  161. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
  162. end
  163. def test_attached_images_should_read_later
  164. set_fixtures_attachments_directory
  165. a1 = Attachment.find(16)
  166. assert_equal "testfile.png", a1.filename
  167. assert a1.readable?
  168. assert (! a1.visible?(User.anonymous))
  169. assert a1.visible?(User.find(2))
  170. a2 = Attachment.find(17)
  171. assert_equal "testfile.PNG", a2.filename
  172. assert a2.readable?
  173. assert (! a2.visible?(User.anonymous))
  174. assert a2.visible?(User.find(2))
  175. assert a1.created_on < a2.created_on
  176. to_test = {
  177. 'Inline image: !testfile.png!' =>
  178. 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '" alt="" />',
  179. 'Inline image: !Testfile.PNG!' =>
  180. 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '" alt="" />',
  181. }
  182. attachments = [a1, a2]
  183. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
  184. set_tmp_attachments_directory
  185. end
  186. def test_textile_external_links
  187. to_test = {
  188. 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
  189. 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
  190. '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
  191. '"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>',
  192. "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
  193. # no multiline link text
  194. "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",
  195. # mailto link
  196. "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
  197. # two exclamation marks
  198. '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
  199. # escaping
  200. '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
  201. }
  202. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  203. end
  204. def test_redmine_links
  205. issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
  206. :class => 'issue status-1 priority-4 overdue', :title => 'Error 281 when updating a recipe (New)')
  207. note_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'},
  208. :class => 'issue status-1 priority-4 overdue', :title => 'Error 281 when updating a recipe (New)')
  209. changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
  210. :class => 'changeset', :title => 'My very first commit')
  211. changeset_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
  212. :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
  213. document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
  214. :class => 'document')
  215. version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
  216. :class => 'version')
  217. board_url = {:controller => 'boards', :action => 'show', :id => 2, :project_id => 'ecookbook'}
  218. message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
  219. news_url = {:controller => 'news', :action => 'show', :id => 1}
  220. project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
  221. source_url = '/projects/ecookbook/repository/entry/some/file'
  222. source_url_with_rev = '/projects/ecookbook/repository/revisions/52/entry/some/file'
  223. source_url_with_ext = '/projects/ecookbook/repository/entry/some/file.ext'
  224. source_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/entry/some/file.ext'
  225. export_url = '/projects/ecookbook/repository/raw/some/file'
  226. export_url_with_rev = '/projects/ecookbook/repository/revisions/52/raw/some/file'
  227. export_url_with_ext = '/projects/ecookbook/repository/raw/some/file.ext'
  228. export_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/raw/some/file.ext'
  229. to_test = {
  230. # tickets
  231. '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
  232. # ticket notes
  233. '#3-14' => note_link,
  234. '#3#note-14' => note_link,
  235. # should not ignore leading zero
  236. '#03' => '#03',
  237. # changesets
  238. 'r1' => changeset_link,
  239. 'r1.' => "#{changeset_link}.",
  240. 'r1, r2' => "#{changeset_link}, #{changeset_link2}",
  241. 'r1,r2' => "#{changeset_link},#{changeset_link2}",
  242. # documents
  243. 'document#1' => document_link,
  244. 'document:"Test document"' => document_link,
  245. # versions
  246. 'version#2' => version_link,
  247. 'version:1.0' => version_link,
  248. 'version:"1.0"' => version_link,
  249. # source
  250. 'source:some/file' => link_to('source:some/file', source_url, :class => 'source'),
  251. 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
  252. 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
  253. 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
  254. 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
  255. 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
  256. 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
  257. 'source:/some/file@52' => link_to('source:/some/file@52', source_url_with_rev, :class => 'source'),
  258. 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_rev_and_ext, :class => 'source'),
  259. 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url + "#L110", :class => 'source'),
  260. 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext + "#L110", :class => 'source'),
  261. 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url_with_rev + "#L110", :class => 'source'),
  262. # export
  263. 'export:/some/file' => link_to('export:/some/file', export_url, :class => 'source download'),
  264. 'export:/some/file.ext' => link_to('export:/some/file.ext', export_url_with_ext, :class => 'source download'),
  265. 'export:/some/file@52' => link_to('export:/some/file@52', export_url_with_rev, :class => 'source download'),
  266. 'export:/some/file.ext@52' => link_to('export:/some/file.ext@52', export_url_with_rev_and_ext, :class => 'source download'),
  267. # forum
  268. 'forum#2' => link_to('Discussion', board_url, :class => 'board'),
  269. 'forum:Discussion' => link_to('Discussion', board_url, :class => 'board'),
  270. # message
  271. 'message#4' => link_to('Post 2', message_url, :class => 'message'),
  272. 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'),
  273. # news
  274. 'news#1' => link_to('eCookbook first release !', news_url, :class => 'news'),
  275. 'news:"eCookbook first release !"' => link_to('eCookbook first release !', news_url, :class => 'news'),
  276. # project
  277. 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
  278. 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
  279. 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
  280. # not found
  281. '#0123456789' => '#0123456789',
  282. # invalid expressions
  283. 'source:' => 'source:',
  284. # url hash
  285. "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
  286. }
  287. @project = Project.find(1)
  288. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
  289. end
  290. def test_escaped_redmine_links_should_not_be_parsed
  291. to_test = [
  292. '#3.',
  293. '#3-14.',
  294. '#3#-note14.',
  295. 'r1',
  296. 'document#1',
  297. 'document:"Test document"',
  298. 'version#2',
  299. 'version:1.0',
  300. 'version:"1.0"',
  301. 'source:/some/file'
  302. ]
  303. @project = Project.find(1)
  304. to_test.each { |text| assert_equal "<p>#{text}</p>", textilizable("!" + text), "#{text} failed" }
  305. end
  306. def test_cross_project_redmine_links
  307. source_link = link_to('ecookbook:source:/some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']},
  308. :class => 'source')
  309. changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
  310. :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
  311. to_test = {
  312. # documents
  313. 'document:"Test document"' => 'document:"Test document"',
  314. 'ecookbook:document:"Test document"' => '<a href="/documents/1" class="document">Test document</a>',
  315. 'invalid:document:"Test document"' => 'invalid:document:"Test document"',
  316. # versions
  317. 'version:"1.0"' => 'version:"1.0"',
  318. 'ecookbook:version:"1.0"' => '<a href="/versions/2" class="version">1.0</a>',
  319. 'invalid:version:"1.0"' => 'invalid:version:"1.0"',
  320. # changeset
  321. 'r2' => 'r2',
  322. 'ecookbook:r2' => changeset_link,
  323. 'invalid:r2' => 'invalid:r2',
  324. # source
  325. 'source:/some/file' => 'source:/some/file',
  326. 'ecookbook:source:/some/file' => source_link,
  327. 'invalid:source:/some/file' => 'invalid:source:/some/file',
  328. }
  329. @project = Project.find(3)
  330. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
  331. end
  332. def test_multiple_repositories_redmine_links
  333. svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg')
  334. Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
  335. hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
  336. Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
  337. changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
  338. :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
  339. svn_changeset_link = link_to('svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123},
  340. :class => 'changeset', :title => '')
  341. hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
  342. :class => 'changeset', :title => '')
  343. source_link = link_to('source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
  344. hg_source_link = link_to('source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
  345. to_test = {
  346. 'r2' => changeset_link,
  347. 'svn1|r123' => svn_changeset_link,
  348. 'invalid|r123' => 'invalid|r123',
  349. 'commit:hg1|abcd' => hg_changeset_link,
  350. 'commit:invalid|abcd' => 'commit:invalid|abcd',
  351. # source
  352. 'source:some/file' => source_link,
  353. 'source:hg1|some/file' => hg_source_link,
  354. 'source:invalid|some/file' => 'source:invalid|some/file',
  355. }
  356. @project = Project.find(1)
  357. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
  358. end
  359. def test_cross_project_multiple_repositories_redmine_links
  360. svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg')
  361. Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
  362. hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
  363. Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
  364. changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
  365. :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
  366. svn_changeset_link = link_to('ecookbook:svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123},
  367. :class => 'changeset', :title => '')
  368. hg_changeset_link = link_to('ecookbook:hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
  369. :class => 'changeset', :title => '')
  370. source_link = link_to('ecookbook:source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
  371. hg_source_link = link_to('ecookbook:source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
  372. to_test = {
  373. 'ecookbook:r2' => changeset_link,
  374. 'ecookbook:svn1|r123' => svn_changeset_link,
  375. 'ecookbook:invalid|r123' => 'ecookbook:invalid|r123',
  376. 'ecookbook:commit:hg1|abcd' => hg_changeset_link,
  377. 'ecookbook:commit:invalid|abcd' => 'ecookbook:commit:invalid|abcd',
  378. 'invalid:commit:invalid|abcd' => 'invalid:commit:invalid|abcd',
  379. # source
  380. 'ecookbook:source:some/file' => source_link,
  381. 'ecookbook:source:hg1|some/file' => hg_source_link,
  382. 'ecookbook:source:invalid|some/file' => 'ecookbook:source:invalid|some/file',
  383. 'invalid:source:invalid|some/file' => 'invalid:source:invalid|some/file',
  384. }
  385. @project = Project.find(3)
  386. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
  387. end
  388. def test_redmine_links_git_commit
  389. changeset_link = link_to('abcd',
  390. {
  391. :controller => 'repositories',
  392. :action => 'revision',
  393. :id => 'subproject1',
  394. :rev => 'abcd',
  395. },
  396. :class => 'changeset', :title => 'test commit')
  397. to_test = {
  398. 'commit:abcd' => changeset_link,
  399. }
  400. @project = Project.find(3)
  401. r = Repository::Git.create!(:project => @project, :url => '/tmp/test/git')
  402. assert r
  403. c = Changeset.new(:repository => r,
  404. :committed_on => Time.now,
  405. :revision => 'abcd',
  406. :scmid => 'abcd',
  407. :comments => 'test commit')
  408. assert( c.save )
  409. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  410. end
  411. # TODO: Bazaar commit id contains mail address, so it contains '@' and '_'.
  412. def test_redmine_links_darcs_commit
  413. changeset_link = link_to('20080308225258-98289-abcd456efg.gz',
  414. {
  415. :controller => 'repositories',
  416. :action => 'revision',
  417. :id => 'subproject1',
  418. :rev => '123',
  419. },
  420. :class => 'changeset', :title => 'test commit')
  421. to_test = {
  422. 'commit:20080308225258-98289-abcd456efg.gz' => changeset_link,
  423. }
  424. @project = Project.find(3)
  425. r = Repository::Darcs.create!(
  426. :project => @project, :url => '/tmp/test/darcs',
  427. :log_encoding => 'UTF-8')
  428. assert r
  429. c = Changeset.new(:repository => r,
  430. :committed_on => Time.now,
  431. :revision => '123',
  432. :scmid => '20080308225258-98289-abcd456efg.gz',
  433. :comments => 'test commit')
  434. assert( c.save )
  435. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  436. end
  437. def test_redmine_links_mercurial_commit
  438. changeset_link_rev = link_to('r123',
  439. {
  440. :controller => 'repositories',
  441. :action => 'revision',
  442. :id => 'subproject1',
  443. :rev => '123' ,
  444. },
  445. :class => 'changeset', :title => 'test commit')
  446. changeset_link_commit = link_to('abcd',
  447. {
  448. :controller => 'repositories',
  449. :action => 'revision',
  450. :id => 'subproject1',
  451. :rev => 'abcd' ,
  452. },
  453. :class => 'changeset', :title => 'test commit')
  454. to_test = {
  455. 'r123' => changeset_link_rev,
  456. 'commit:abcd' => changeset_link_commit,
  457. }
  458. @project = Project.find(3)
  459. r = Repository::Mercurial.create!(:project => @project, :url => '/tmp/test')
  460. assert r
  461. c = Changeset.new(:repository => r,
  462. :committed_on => Time.now,
  463. :revision => '123',
  464. :scmid => 'abcd',
  465. :comments => 'test commit')
  466. assert( c.save )
  467. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  468. end
  469. def test_attachment_links
  470. attachment_link = link_to('error281.txt', {:controller => 'attachments', :action => 'download', :id => '1'}, :class => 'attachment')
  471. to_test = {
  472. 'attachment:error281.txt' => attachment_link
  473. }
  474. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => Issue.find(3).attachments), "#{text} failed" }
  475. end
  476. def test_wiki_links
  477. to_test = {
  478. '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
  479. '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
  480. # title content should be formatted
  481. '[[Another page|With _styled_ *title*]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">With <em>styled</em> <strong>title</strong></a>',
  482. '[[Another page|With title containing <strong>HTML entities &amp; markups</strong>]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">With title containing &lt;strong&gt;HTML entities &amp; markups&lt;/strong&gt;</a>',
  483. # link with anchor
  484. '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
  485. '[[Another page#anchor|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page#anchor" class="wiki-page">Page</a>',
  486. # UTF8 anchor
  487. '[[Another_page#????|????]]' => %|<a href="/projects/ecookbook/wiki/Another_page##{CGI.escape '????'}" class="wiki-page">????</a>|,
  488. # page that doesn't exist
  489. '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
  490. '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">404</a>',
  491. # link to another project wiki
  492. '[[onlinestore:]]' => '<a href="/projects/onlinestore/wiki" class="wiki-page">onlinestore</a>',
  493. '[[onlinestore:|Wiki]]' => '<a href="/projects/onlinestore/wiki" class="wiki-page">Wiki</a>',
  494. '[[onlinestore:Start page]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Start page</a>',
  495. '[[onlinestore:Start page|Text]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Text</a>',
  496. '[[onlinestore:Unknown page]]' => '<a href="/projects/onlinestore/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
  497. # striked through link
  498. '-[[Another page|Page]]-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a></del>',
  499. '-[[Another page|Page]] link-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a> link</del>',
  500. # escaping
  501. '![[Another page|Page]]' => '[[Another page|Page]]',
  502. # project does not exist
  503. '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
  504. '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
  505. }
  506. @project = Project.find(1)
  507. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  508. end
  509. def test_wiki_links_within_local_file_generation_context
  510. to_test = {
  511. # link to a page
  512. '[[CookBook documentation]]' => '<a href="CookBook_documentation.html" class="wiki-page">CookBook documentation</a>',
  513. '[[CookBook documentation|documentation]]' => '<a href="CookBook_documentation.html" class="wiki-page">documentation</a>',
  514. '[[CookBook documentation#One-section]]' => '<a href="CookBook_documentation.html#One-section" class="wiki-page">CookBook documentation</a>',
  515. '[[CookBook documentation#One-section|documentation]]' => '<a href="CookBook_documentation.html#One-section" class="wiki-page">documentation</a>',
  516. # page that doesn't exist
  517. '[[Unknown page]]' => '<a href="Unknown_page.html" class="wiki-page new">Unknown page</a>',
  518. '[[Unknown page|404]]' => '<a href="Unknown_page.html" class="wiki-page new">404</a>',
  519. '[[Unknown page#anchor]]' => '<a href="Unknown_page.html#anchor" class="wiki-page new">Unknown page</a>',
  520. '[[Unknown page#anchor|404]]' => '<a href="Unknown_page.html#anchor" class="wiki-page new">404</a>',
  521. }
  522. @project = Project.find(1)
  523. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :local) }
  524. end
  525. def test_wiki_links_within_wiki_page_context
  526. page = WikiPage.find_by_title('Another_page' )
  527. to_test = {
  528. # link to another page
  529. '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
  530. '[[CookBook documentation|documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">documentation</a>',
  531. '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
  532. '[[CookBook documentation#One-section|documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">documentation</a>',
  533. # link to the current page
  534. '[[Another page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Another page</a>',
  535. '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
  536. '[[Another page#anchor]]' => '<a href="#anchor" class="wiki-page">Another page</a>',
  537. '[[Another page#anchor|Page]]' => '<a href="#anchor" class="wiki-page">Page</a>',
  538. # page that doesn't exist
  539. '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page" class="wiki-page new">Unknown page</a>',
  540. '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page" class="wiki-page new">404</a>',
  541. '[[Unknown page#anchor]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor" class="wiki-page new">Unknown page</a>',
  542. '[[Unknown page#anchor|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor" class="wiki-page new">404</a>',
  543. }
  544. @project = Project.find(1)
  545. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(WikiContent.new( :text => text, :page => page ), :text) }
  546. end
  547. def test_wiki_links_anchor_option_should_prepend_page_title_to_href
  548. to_test = {
  549. # link to a page
  550. '[[CookBook documentation]]' => '<a href="#CookBook_documentation" class="wiki-page">CookBook documentation</a>',
  551. '[[CookBook documentation|documentation]]' => '<a href="#CookBook_documentation" class="wiki-page">documentation</a>',
  552. '[[CookBook documentation#One-section]]' => '<a href="#CookBook_documentation_One-section" class="wiki-page">CookBook documentation</a>',
  553. '[[CookBook documentation#One-section|documentation]]' => '<a href="#CookBook_documentation_One-section" class="wiki-page">documentation</a>',
  554. # page that doesn't exist
  555. '[[Unknown page]]' => '<a href="#Unknown_page" class="wiki-page new">Unknown page</a>',
  556. '[[Unknown page|404]]' => '<a href="#Unknown_page" class="wiki-page new">404</a>',
  557. '[[Unknown page#anchor]]' => '<a href="#Unknown_page_anchor" class="wiki-page new">Unknown page</a>',
  558. '[[Unknown page#anchor|404]]' => '<a href="#Unknown_page_anchor" class="wiki-page new">404</a>',
  559. }
  560. @project = Project.find(1)
  561. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :anchor) }
  562. end
  563. def test_html_tags
  564. to_test = {
  565. "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
  566. "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
  567. "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
  568. # do not escape pre/code tags
  569. "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
  570. "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
  571. "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
  572. "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
  573. "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
  574. # remove attributes except class
  575. "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
  576. '<pre class="foo">some text</pre>' => '<pre class="foo">some text</pre>',
  577. "<pre class='foo bar'>some text</pre>" => "<pre class='foo bar'>some text</pre>",
  578. '<pre class="foo bar">some text</pre>' => '<pre class="foo bar">some text</pre>',
  579. "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
  580. # xss
  581. '<pre><code class=""onmouseover="alert(1)">text</code></pre>' => '<pre><code>text</code></pre>',
  582. '<pre class=""onmouseover="alert(1)">text</pre>' => '<pre>text</pre>',
  583. }
  584. to_test.each { |text, result| assert_equal result, textilizable(text) }
  585. end
  586. def test_allowed_html_tags
  587. to_test = {
  588. "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
  589. "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
  590. "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
  591. }
  592. to_test.each { |text, result| assert_equal result, textilizable(text) }
  593. end
  594. def test_pre_tags
  595. raw = <<-RAW
  596. Before
  597. <pre>
  598. <prepared-statement-cache-size>32</prepared-statement-cache-size>
  599. </pre>
  600. After
  601. RAW
  602. expected = <<-EXPECTED
  603. <p>Before</p>
  604. <pre>
  605. &lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
  606. </pre>
  607. <p>After</p>
  608. EXPECTED
  609. assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
  610. end
  611. def test_pre_content_should_not_parse_wiki_and_redmine_links
  612. raw = <<-RAW
  613. [[CookBook documentation]]
  614. #1
  615. <pre>
  616. [[CookBook documentation]]
  617. #1
  618. </pre>
  619. RAW
  620. expected = <<-EXPECTED
  621. <p><a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a></p>
  622. <p><a href="/issues/1" class="issue status-1 priority-4" title="Can&#x27;t print recipes (New)">#1</a></p>
  623. <pre>
  624. [[CookBook documentation]]
  625. #1
  626. </pre>
  627. EXPECTED
  628. @project = Project.find(1)
  629. assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
  630. end
  631. def test_non_closing_pre_blocks_should_be_closed
  632. raw = <<-RAW
  633. <pre><code>
  634. RAW
  635. expected = <<-EXPECTED
  636. <pre><code>
  637. </code></pre>
  638. EXPECTED
  639. @project = Project.find(1)
  640. assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
  641. end
  642. def test_syntax_highlight
  643. raw = <<-RAW
  644. <pre><code class="ruby">
  645. # Some ruby code here
  646. </code></pre>
  647. RAW
  648. expected = <<-EXPECTED
  649. <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="comment"># Some ruby code here</span></span>
  650. </code></pre>
  651. EXPECTED
  652. assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
  653. end
  654. def test_to_path_param
  655. assert_equal 'test1/test2', to_path_param('test1/test2')
  656. assert_equal 'test1/test2', to_path_param('/test1/test2/')
  657. assert_equal 'test1/test2', to_path_param('//test1/test2/')
  658. assert_equal nil, to_path_param('/')
  659. end
  660. def test_wiki_links_in_tables
  661. to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
  662. '<tr><td><a href="/projects/ecookbook/wiki/Page" class="wiki-page new">Link title</a></td>' +
  663. '<td><a href="/projects/ecookbook/wiki/Other_Page" class="wiki-page new">Other title</a></td>' +
  664. '</tr><tr><td>Cell 21</td><td><a href="/projects/ecookbook/wiki/Last_page" class="wiki-page new">Last page</a></td></tr>'
  665. }
  666. @project = Project.find(1)
  667. to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
  668. end
  669. def test_text_formatting
  670. to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
  671. '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
  672. 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
  673. '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>',
  674. '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',
  675. }
  676. to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  677. end
  678. def test_wiki_horizontal_rule
  679. assert_equal '<hr />', textilizable('---')
  680. assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
  681. end
  682. def test_footnotes
  683. raw = <<-RAW
  684. This is some text[1].
  685. fn1. This is the foot note
  686. RAW
  687. expected = <<-EXPECTED
  688. <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
  689. <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
  690. EXPECTED
  691. assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
  692. end
  693. def test_headings
  694. raw = 'h1. Some heading'
  695. expected = %|<a name="Some-heading"></a>\n<h1 >Some heading<a href="#Some-heading" class="wiki-anchor">&para;</a></h1>|
  696. assert_equal expected, textilizable(raw)
  697. end
  698. def test_headings_with_special_chars
  699. # This test makes sure that the generated anchor names match the expected
  700. # ones even if the heading text contains unconventional characters
  701. raw = 'h1. Some heading related to version 0.5'
  702. anchor = sanitize_anchor_name("Some-heading-related-to-version-0.5")
  703. expected = %|<a name="#{anchor}"></a>\n<h1 >Some heading related to version 0.5<a href="##{anchor}" class="wiki-anchor">&para;</a></h1>|
  704. assert_equal expected, textilizable(raw)
  705. end
  706. def test_headings_in_wiki_single_page_export_should_be_prepended_with_page_title
  707. page = WikiPage.new( :title => 'Page Title', :wiki_id => 1 )
  708. content = WikiContent.new( :text => 'h1. Some heading', :page => page )
  709. expected = %|<a name="Page_Title_Some-heading"></a>\n<h1 >Some heading<a href="#Page_Title_Some-heading" class="wiki-anchor">&para;</a></h1>|
  710. assert_equal expected, textilizable(content, :text, :wiki_links => :anchor )
  711. end
  712. def test_table_of_content
  713. raw = <<-RAW
  714. {{toc}}
  715. h1. Title
  716. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
  717. h2. Subtitle with a [[Wiki]] link
  718. Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
  719. h2. Subtitle with [[Wiki|another Wiki]] link
  720. h2. Subtitle with %{color:red}red text%
  721. <pre>
  722. some code
  723. </pre>
  724. h3. Subtitle with *some* _modifiers_
  725. h3. Subtitle with @inline code@
  726. h1. Another title
  727. h3. An "Internet link":http://www.redmine.org/ inside subtitle
  728. h2. "Project Name !/attachments/1234/logo_small.gif! !/attachments/5678/logo_2.png!":/projects/projectname/issues
  729. RAW
  730. expected = '<ul class="toc">' +
  731. '<li><a href="#Title">Title</a>' +
  732. '<ul>' +
  733. '<li><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
  734. '<li><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
  735. '<li><a href="#Subtitle-with-red-text">Subtitle with red text</a>' +
  736. '<ul>' +
  737. '<li><a href="#Subtitle-with-some-modifiers">Subtitle with some modifiers</a></li>' +
  738. '<li><a href="#Subtitle-with-inline-code">Subtitle with inline code</a></li>' +
  739. '</ul>' +
  740. '</li>' +
  741. '</ul>' +
  742. '</li>' +
  743. '<li><a href="#Another-title">Another title</a>' +
  744. '<ul>' +
  745. '<li>' +
  746. '<ul>' +
  747. '<li><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
  748. '</ul>' +
  749. '</li>' +
  750. '<li><a href="#Project-Name">Project Name</a></li>' +
  751. '</ul>' +
  752. '</li>' +
  753. '</ul>'
  754. @project = Project.find(1)
  755. assert textilizable(raw).gsub("\n", "").include?(expected)
  756. end
  757. def test_table_of_content_should_generate_unique_anchors
  758. raw = <<-RAW
  759. {{toc}}
  760. h1. Title
  761. h2. Subtitle
  762. h2. Subtitle
  763. RAW
  764. expected = '<ul class="toc">' +
  765. '<li><a href="#Title">Title</a>' +
  766. '<ul>' +
  767. '<li><a href="#Subtitle">Subtitle</a></li>' +
  768. '<li><a href="#Subtitle-2">Subtitle</a></li>'
  769. '</ul>'
  770. '</li>' +
  771. '</ul>'
  772. @project = Project.find(1)
  773. result = textilizable(raw).gsub("\n", "")
  774. assert_include expected, result
  775. assert_include '<a name="Subtitle">', result
  776. assert_include '<a name="Subtitle-2">', result
  777. end
  778. def test_table_of_content_should_contain_included_page_headings
  779. raw = <<-RAW
  780. {{toc}}
  781. h1. Included
  782. {{include(Child_1)}}
  783. RAW
  784. expected = '<ul class="toc">' +
  785. '<li><a href="#Included">Included</a></li>' +
  786. '<li><a href="#Child-page-1">Child page 1</a></li>' +
  787. '</ul>'
  788. @project = Project.find(1)
  789. assert textilizable(raw).gsub("\n", "").include?(expected)
  790. end
  791. def test_section_edit_links
  792. raw = <<-RAW
  793. h1. Title
  794. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
  795. h2. Subtitle with a [[Wiki]] link
  796. h2. Subtitle with *some* _modifiers_
  797. h2. Subtitle with @inline code@
  798. <pre>
  799. some code
  800. h2. heading inside pre
  801. <h2>html heading inside pre</h2>
  802. </pre>
  803. h2. Subtitle after pre tag
  804. RAW
  805. @project = Project.find(1)
  806. set_language_if_valid 'en'
  807. result = textilizable(raw, :edit_section_links => {:controller => 'wiki', :action => 'edit', :project_id => '1', :id => 'Test'}).gsub("\n", "")
  808. # heading that contains inline code
  809. assert_match Regexp.new('<div class="contextual" title="Edit this section">' +
  810. '<a href="/projects/1/wiki/Test/edit\?section=4"><img alt="Edit" src="/images/edit.png(\?\d+)?" /></a></div>' +
  811. '<a name="Subtitle-with-inline-code"></a>' +
  812. '<h2 >Subtitle with <code>inline code</code><a href="#Subtitle-with-inline-code" class="wiki-anchor">&para;</a></h2>'),
  813. result
  814. # last heading
  815. assert_match Regexp.new('<div class="contextual" title="Edit this section">' +
  816. '<a href="/projects/1/wiki/Test/edit\?section=5"><img alt="Edit" src="/images/edit.png(\?\d+)?" /></a></div>' +
  817. '<a name="Subtitle-after-pre-tag"></a>' +
  818. '<h2 >Subtitle after pre tag<a href="#Subtitle-after-pre-tag" class="wiki-anchor">&para;</a></h2>'),
  819. result
  820. end
  821. def test_default_formatter
  822. with_settings :text_formatting => 'unknown' do
  823. text = 'a *link*: http://www.example.net/'
  824. assert_equal '<p>a *link*: <a class="external" href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
  825. end
  826. end
  827. def test_due_date_distance_in_words
  828. to_test = { Date.today => 'Due in 0 days',
  829. Date.today + 1 => 'Due in 1 day',
  830. Date.today + 100 => 'Due in about 3 months',
  831. Date.today + 20000 => 'Due in over 54 years',
  832. Date.today - 1 => '1 day late',
  833. Date.today - 100 => 'about 3 months late',
  834. Date.today - 20000 => 'over 54 years late',
  835. }
  836. ::I18n.locale = :en
  837. to_test.each do |date, expected|
  838. assert_equal expected, due_date_distance_in_words(date)
  839. end
  840. end
  841. def test_avatar_enabled
  842. with_settings :gravatar_enabled => '1' do
  843. assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
  844. assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
  845. # Default size is 50
  846. assert avatar('jsmith <jsmith@somenet.foo>').include?('size=50')
  847. assert avatar('jsmith <jsmith@somenet.foo>', :size => 24).include?('size=24')
  848. # Non-avatar options should be considered html options
  849. assert avatar('jsmith <jsmith@somenet.foo>', :title => 'John Smith').include?('title="John Smith"')
  850. # The default class of the img tag should be gravatar
  851. assert avatar('jsmith <jsmith@somenet.foo>').include?('class="gravatar"')
  852. assert !avatar('jsmith <jsmith@somenet.foo>', :class => 'picture').include?('class="gravatar"')
  853. assert_nil avatar('jsmith')
  854. assert_nil avatar(nil)
  855. end
  856. end
  857. def test_avatar_disabled
  858. with_settings :gravatar_enabled => '0' do
  859. assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo'))
  860. end
  861. end
  862. def test_link_to_user
  863. user = User.find(2)
  864. t = link_to_user(user)
  865. assert_equal "<a href=\"/users/2\">#{ user.name }</a>", t
  866. end
  867. def test_link_to_user_should_not_link_to_locked_user
  868. user = User.find(5)
  869. assert user.locked?
  870. t = link_to_user(user)
  871. assert_equal user.name, t
  872. end
  873. def test_link_to_user_should_not_link_to_anonymous
  874. user = User.anonymous
  875. assert user.anonymous?
  876. t = link_to_user(user)
  877. assert_equal ::I18n.t(:label_user_anonymous), t
  878. end
  879. def test_link_to_project
  880. project = Project.find(1)
  881. assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
  882. link_to_project(project)
  883. assert_equal %(<a href="/projects/ecookbook/settings">eCookbook</a>),
  884. link_to_project(project, :action => 'settings')
  885. assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
  886. link_to_project(project, {:only_path => false, :jump => 'blah'})
  887. assert_equal %(<a href="/projects/ecookbook/settings" class="project">eCookbook</a>),
  888. link_to_project(project, {:action => 'settings'}, :class => "project")
  889. end
  890. def test_link_to_legacy_project_with_numerical_identifier_should_use_id
  891. # numeric identifier are no longer allowed
  892. Project.update_all "identifier=25", "id=1"
  893. assert_equal '<a href="/projects/1">eCookbook</a>',
  894. link_to_project(Project.find(1))
  895. end
  896. def test_principals_options_for_select_with_users
  897. User.current = nil
  898. users = [User.find(2), User.find(4)]
  899. assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>),
  900. principals_options_for_select(users)
  901. end
  902. def test_principals_options_for_select_with_selected
  903. User.current = nil
  904. users = [User.find(2), User.find(4)]
  905. assert_equal %(<option value="2">John Smith</option><option value="4" selected="selected">Robert Hill</option>),
  906. principals_options_for_select(users, User.find(4))
  907. end
  908. def test_principals_options_for_select_with_users_and_groups
  909. User.current = nil
  910. users = [User.find(2), Group.find(11), User.find(4), Group.find(10)]
  911. assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>) +
  912. %(<optgroup label="Groups"><option value="10">A Team</option><option value="11">B Team</option></optgroup>),
  913. principals_options_for_select(users)
  914. end
  915. def test_principals_options_for_select_with_empty_collection
  916. assert_equal '', principals_options_for_select([])
  917. end
  918. def test_principals_options_for_select_should_include_me_option_when_current_user_is_in_collection
  919. users = [User.find(2), User.find(4)]
  920. User.current = User.find(4)
  921. assert_include '<option value="4">&lt;&lt; me &gt;&gt;</option>', principals_options_for_select(users)
  922. end
  923. def test_stylesheet_link_tag_should_pick_the_default_stylesheet
  924. assert_match 'href="/stylesheets/styles.css"', stylesheet_link_tag("styles")
  925. end
  926. def test_stylesheet_link_tag_for_plugin_should_pick_the_plugin_stylesheet
  927. assert_match 'href="/plugin_assets/foo/stylesheets/styles.css"', stylesheet_link_tag("styles", :plugin => :foo)
  928. end
  929. def test_image_tag_should_pick_the_default_image
  930. assert_match 'src="/images/image.png"', image_tag("image.png")
  931. end
  932. def test_image_tag_should_pick_the_theme_image_if_it_exists
  933. theme = Redmine::Themes.themes.last
  934. theme.images << 'image.png'
  935. with_settings :ui_theme => theme.id do
  936. assert_match %|src="/themes/#{theme.dir}/images/image.png"|, image_tag("image.png")
  937. assert_match %|src="/images/other.png"|, image_tag("other.png")
  938. end
  939. ensure
  940. theme.images.delete 'image.png'
  941. end
  942. def test_image_tag_sfor_plugin_should_pick_the_plugin_image
  943. assert_match 'src="/plugin_assets/foo/images/image.png"', image_tag("image.png", :plugin => :foo)
  944. end
  945. def test_javascript_include_tag_should_pick_the_default_javascript
  946. assert_match 'src="/javascripts/scripts.js"', javascript_include_tag("scripts")
  947. end
  948. def test_javascript_include_tag_for_plugin_should_pick_the_plugin_javascript
  949. assert_match 'src="/plugin_assets/foo/javascripts/scripts.js"', javascript_include_tag("scripts", :plugin => :foo)
  950. end
  951. def test_per_page_links_should_show_usefull_values
  952. set_language_if_valid 'en'
  953. stubs(:link_to).returns("[link]")
  954. with_settings :per_page_options => '10, 25, 50, 100' do
  955. assert_nil per_page_links(10, 3)
  956. assert_nil per_page_links(25, 3)
  957. assert_equal "Per page: 10, [link]", per_page_links(10, 22)
  958. assert_equal "Per page: [link], 25", per_page_links(25, 22)
  959. assert_equal "Per page: [link], [link], 50", per_page_links(50, 22)
  960. assert_equal "Per page: [link], 25, [link]", per_page_links(25, 26)
  961. assert_equal "Per page: [link], 25, [link], [link]", per_page_links(25, 120)
  962. end
  963. end
  964. end