application_helper_test.rb 33.5 KB
Newer Older
1
#-- encoding: UTF-8
2 3
#-- copyright
# ChiliProject is a project management system.
4
#
Holger Just's avatar
Holger Just committed
5
# Copyright (C) 2010-2012 the ChiliProject Team
6
#
7 8 9 10
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
11
#
12 13
# See doc/COPYRIGHT.rdoc for more details.
#++
14
require File.expand_path('../../../test_helper', __FILE__)
15

16
class ApplicationHelperTest < ActionView::TestCase
17

18
  fixtures :projects, :roles, :enabled_modules, :users,
19
                      :repositories, :changesets,
20 21
                      :trackers, :issue_statuses, :issues, :versions, :documents,
                      :wikis, :wiki_pages, :wiki_contents,
22
                      :boards, :messages,
23 24
                      :attachments,
                      :enumerations
25 26 27 28

  def setup
    super
  end
29

30 31 32 33
  def request
    @request ||= ActionController::TestRequest.new
  end

34 35 36 37
  context "#link_to_if_authorized" do
    context "authorized user" do
      should "be tested"
    end
38

39 40 41
    context "unauthorized user" do
      should "be tested"
    end
42

43 44 45 46 47 48 49 50
    should "allow using the :controller and :action for the target link" do
      User.current = User.find_by_login('admin')

      @project = Issue.first.project # Used by helper
      response = link_to_if_authorized("By controller/action",
                                       {:controller => 'issues', :action => 'edit', :id => Issue.first.id})
      assert_match /href/, response
    end
51

52
  end
53

54 55
  def test_auto_links
    to_test = {
56
      'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
57
      'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
58
      'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
59
      'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
60 61
      'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
      'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
62
      '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>.',
63 64 65 66 67 68 69
      'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
      '(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>)',
      '(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>)',
      '(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>).',
      '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
      '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
      '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
70 71
      'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
      '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>',
72 73
      'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
      'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
74
      'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
75
      'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
76 77
      'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
      'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
78 79
      # two exclamation marks
      '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>',
80 81
      # escaping
      'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo"bar</a>',
82 83
      # wrap in angle brackets
      '<http://foo.bar>' => '&lt;<a class="external" href="http://foo.bar">http://foo.bar</a>&gt;'
84 85 86
    }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end
87

88
  def test_auto_mailto
89
    assert_equal '<p><a class="email" href="mailto:test@foo.bar">test@foo.bar</a></p>',
90 91
      textilizable('test@foo.bar')
  end
92

93
  def test_inline_images
94 95 96
    to_test = {
      '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
      'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
97
      'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
98 99
      # inline styles should be stripped
      'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" alt="" />',
100 101
      '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" />',
      '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;" />',
102 103 104
    }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end
105

106 107 108 109 110 111 112 113 114 115 116 117
  def test_inline_images_inside_tags
    raw = <<-RAW
h1. !foo.png! Heading

Centered image:

p=. !bar.gif!
RAW

    assert textilizable(raw).include?('<img src="foo.png" alt="" />')
    assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
  end
118

119 120 121
  def test_attached_images
    to_test = {
      'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
122 123
      'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
      'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
124 125 126
      'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
      # link image
      '!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>',
127 128 129 130
    }
    attachments = Attachment.find(:all)
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
  end
131

132 133
  def test_textile_external_links
    to_test = {
134 135
      'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
      'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
136
      '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
137
      '"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>',
138
      "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
139
      # no multiline link text
140
      "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",
141 142
      # mailto link
      "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
143 144
      # two exclamation marks
      '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
145 146
      # escaping
      '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
147 148 149
    }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end
150

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
  def test_textile_relative_to_full_links_in_a_controller
    # we have a request here
    {
      # shouldn't change non-relative links
      'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
      'This is an intern "link":/foo/bar' => 'This is an intern <a href="http://test.host/foo/bar">link</a>',
      'This is an intern "link":/foo/bar and an extern "link":http://foo.bar' => 'This is an intern <a href="http://test.host/foo/bar">link</a> and an extern <a href="http://foo.bar" class="external">link</a>',
    }.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :only_path => false) }
  end

  def test_textile_relative_to_full_links_in_the_mailer
    # we don't a request here
    undef request
    # mimic the mailer default_url_options
    @controller.class.class_eval {
      def self.default_url_options
        ::Mailer.default_url_options
      end
    }

    {
      # shouldn't change non-relative links
      'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
      'This is an intern "link":/foo/bar' => 'This is an intern <a href="http://localhost:3000/foo/bar">link</a>',
      'This is an intern "link":/foo/bar and an extern "link":http://foo.bar' => 'This is an intern <a href="http://localhost:3000/foo/bar">link</a> and an extern <a href="http://foo.bar" class="external">link</a>',
    }.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :only_path => false) }
  end

179
  def test_redmine_links
180
    issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
181
                               :class => 'issue status-1 priority-1 overdue', :title => 'Error 281 when updating a recipe (New)')
182

183
    changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
184
                                   :class => 'changeset', :title => 'My very first commit')
185 186
    changeset_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
                                    :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
187

188 189
    document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
                                             :class => 'document')
190

191 192
    version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
                                  :class => 'version')
193

194
    message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
195

196
    project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
197

198
    source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}
199
    source_url_with_ext = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file.ext']}
200

201
    to_test = {
202
      # tickets
203
      '#3, [#3], (#3) and #3.'      => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
204 205
      # changesets
      'r1'                          => changeset_link,
206 207 208
      'r1.'                         => "#{changeset_link}.",
      'r1, r2'                      => "#{changeset_link}, #{changeset_link2}",
      'r1,r2'                       => "#{changeset_link},#{changeset_link2}",
209 210 211 212 213 214 215 216 217
      # documents
      'document#1'                  => document_link,
      'document:"Test document"'    => document_link,
      # versions
      'version#2'                   => version_link,
      'version:1.0'                 => version_link,
      'version:"1.0"'               => version_link,
      # source
      'source:/some/file'           => link_to('source:/some/file', source_url, :class => 'source'),
218 219 220 221 222
      'source:/some/file.'          => link_to('source:/some/file', source_url, :class => 'source') + ".",
      'source:/some/file.ext.'      => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
      'source:/some/file. '         => link_to('source:/some/file', source_url, :class => 'source') + ".",
      'source:/some/file.ext. '     => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
      'source:/some/file, '         => link_to('source:/some/file', source_url, :class => 'source') + ",",
223
      'source:/some/file@52'        => link_to('source:/some/file@52', source_url.merge(:rev => 52), :class => 'source'),
224
      'source:/some/file.ext@52'    => link_to('source:/some/file.ext@52', source_url_with_ext.merge(:rev => 52), :class => 'source'),
225
      'source:/some/file#L110'      => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'),
226
      'source:/some/file.ext#L110'  => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'),
227 228
      'source:/some/file@52#L110'   => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'),
      'export:/some/file'           => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'),
229 230
      # message
      'message#4'                   => link_to('Post 2', message_url, :class => 'message'),
231
      'message#5'                   => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'),
232 233 234 235
      # project
      'project#3'                   => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
      'project:subproject1'         => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
      'project:"eCookbook subProject 1"'        => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
236
      # escaping
237 238 239 240 241 242 243 244
      '!#3.'                        => '#3.',
      '!r1'                         => 'r1',
      '!document#1'                 => 'document#1',
      '!document:"Test document"'   => 'document:"Test document"',
      '!version#2'                  => 'version#2',
      '!version:1.0'                => 'version:1.0',
      '!version:"1.0"'              => 'version:"1.0"',
      '!source:/some/file'          => 'source:/some/file',
245 246
      # not found
      '#0123456789'                 => '#0123456789',
247
      # invalid expressions
248 249 250
      'source:'                     => 'source:',
      # url hash
      "http://foo.bar/FAQ#3"       => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
251 252
    }
    @project = Project.find(1)
253
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
254
  end
255

256 257 258
  def test_cross_project_redmine_links
    source_link = link_to('ecookbook:source:/some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']},
      :class => 'source')
259

260 261
    changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
      :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
262

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
    to_test = {
      # documents
      'document:"Test document"'              => 'document:"Test document"',
      'ecookbook:document:"Test document"'    => '<a href="/documents/1" class="document">Test document</a>',
      'invalid:document:"Test document"'      => 'invalid:document:"Test document"',
      # versions
      'version:"1.0"'                         => 'version:"1.0"',
      'ecookbook:version:"1.0"'               => '<a href="/versions/show/2" class="version">1.0</a>',
      'invalid:version:"1.0"'                 => 'invalid:version:"1.0"',
      # changeset
      'r2'                                    => 'r2',
      'ecookbook:r2'                          => changeset_link,
      'invalid:r2'                            => 'invalid:r2',
      # source
      'source:/some/file'                     => 'source:/some/file',
      'ecookbook:source:/some/file'           => source_link,
      'invalid:source:/some/file'             => 'invalid:source:/some/file',
    }
    @project = Project.find(3)
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
  end
284 285 286

  def test_redmine_links_git_commit
    changeset_link = link_to('abcd',
287 288 289 290 291 292
                               {
                                 :controller => 'repositories',
                                 :action     => 'revision',
                                 :id         => 'subproject1',
                                 :rev        => 'abcd',
                                },
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
                              :class => 'changeset', :title => 'test commit')
    to_test = {
      'commit:abcd' => changeset_link,
     }
    @project = Project.find(3)
    r = Repository::Git.create!(:project => @project, :url => '/tmp/test/git')
    assert r
    c = Changeset.new(:repository => r,
                      :committed_on => Time.now,
                      :revision => 'abcd',
                      :scmid => 'abcd',
                      :comments => 'test commit')
    assert( c.save )
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end

  # TODO: Bazaar commit id contains mail address, so it contains '@' and '_'.
  def test_redmine_links_darcs_commit
    changeset_link = link_to('20080308225258-98289-abcd456efg.gz',
312 313 314 315 316 317
                               {
                                 :controller => 'repositories',
                                 :action     => 'revision',
                                 :id         => 'subproject1',
                                 :rev        => '123',
                                },
318 319 320 321 322
                              :class => 'changeset', :title => 'test commit')
    to_test = {
      'commit:20080308225258-98289-abcd456efg.gz' => changeset_link,
     }
    @project = Project.find(3)
323 324 325
    r = Repository::Darcs.create!(
            :project => @project, :url => '/tmp/test/darcs',
            :log_encoding => 'UTF-8')
326 327 328 329 330 331 332 333 334 335
    assert r
    c = Changeset.new(:repository => r,
                      :committed_on => Time.now,
                      :revision => '123',
                      :scmid => '20080308225258-98289-abcd456efg.gz',
                      :comments => 'test commit')
    assert( c.save )
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
  def test_redmine_links_mercurial_commit
    changeset_link_rev = link_to('r123',
                                  {
                                     :controller => 'repositories',
                                     :action     => 'revision',
                                     :id         => 'subproject1',
                                     :rev        => '123' ,
                                  },
                              :class => 'changeset', :title => 'test commit')
    changeset_link_commit = link_to('abcd',
                                  {
                                        :controller => 'repositories',
                                        :action     => 'revision',
                                        :id         => 'subproject1',
                                        :rev        => 'abcd' ,
                                  },
                              :class => 'changeset', :title => 'test commit')
    to_test = {
      'r123' => changeset_link_rev,
      'commit:abcd' => changeset_link_commit,
     }
    @project = Project.find(3)
    r = Repository::Mercurial.create!(:project => @project, :url => '/tmp/test')
    assert r
    c = Changeset.new(:repository => r,
                      :committed_on => Time.now,
                      :revision => '123',
                      :scmid => 'abcd',
                      :comments => 'test commit')
    assert( c.save )
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end

369 370 371 372 373 374 375
  def test_attachment_links
    attachment_link = link_to('error281.txt', {:controller => 'attachments', :action => 'download', :id => '1'}, :class => 'attachment')
    to_test = {
      'attachment:error281.txt'      => attachment_link
    }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => Issue.find(3).attachments), "#{text} failed" }
  end
376

377 378
  def test_wiki_links
    to_test = {
379 380
      '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
      '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
381
      # link with anchor
382 383
      '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
      '[[Another page#anchor|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page#anchor" class="wiki-page">Page</a>',
384
      # page that doesn't exist
385 386
      '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
      '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">404</a>',
387
      # link to another project wiki
388 389
      '[[onlinestore:]]' => '<a href="/projects/onlinestore/wiki" class="wiki-page">onlinestore</a>',
      '[[onlinestore:|Wiki]]' => '<a href="/projects/onlinestore/wiki" class="wiki-page">Wiki</a>',
390 391 392
      '[[onlinestore:Start page]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Start page</a>',
      '[[onlinestore:Start page|Text]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Text</a>',
      '[[onlinestore:Unknown page]]' => '<a href="/projects/onlinestore/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
393
      # striked through link
394 395
      '-[[Another page|Page]]-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a></del>',
      '-[[Another page|Page]] link-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a> link</del>',
396 397
      # escaping
      '![[Another page|Page]]' => '[[Another page|Page]]',
398 399 400
      # project does not exist
      '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
      '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
401 402 403 404
    }
    @project = Project.find(1)
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end
405

406 407
  def test_html_tags
    to_test = {
408 409 410
      "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
      "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
      "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
411 412 413 414
      # do not escape pre/code tags
      "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
      "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
      "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
415
      "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
416
      "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
417 418
      # remove attributes except class
      "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
419 420 421
      '<pre class="foo">some text</pre>' => '<pre class="foo">some text</pre>',
      "<pre class='foo bar'>some text</pre>" => "<pre class='foo bar'>some text</pre>",
      '<pre class="foo bar">some text</pre>' => '<pre class="foo bar">some text</pre>',
422
      "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
423 424 425
      # xss
      '<pre><code class=""onmouseover="alert(1)">text</code></pre>' => '<pre><code>text</code></pre>',
      '<pre class=""onmouseover="alert(1)">text</pre>' => '<pre>text</pre>',
426 427
    }
    to_test.each { |text, result| assert_equal result, textilizable(text) }
Jean-Philippe Lang's avatar
Jean-Philippe Lang committed
428
  end
429

430 431 432 433
  def test_allowed_html_tags
    to_test = {
      "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
      "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
434
      "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
435 436 437
    }
    to_test.each { |text, result| assert_equal result, textilizable(text) }
  end
438

439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
  def test_pre_tags
    raw = <<-RAW
Before

<pre>
<prepared-statement-cache-size>32</prepared-statement-cache-size>
</pre>

After
RAW

    expected = <<-EXPECTED
<p>Before</p>
<pre>
&lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
</pre>
<p>After</p>
EXPECTED
457

458 459
    assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
  end
460

461 462 463
  def test_pre_content_should_not_parse_wiki_and_redmine_links
    raw = <<-RAW
[[CookBook documentation]]
464

465 466 467 468
#1

<pre>
[[CookBook documentation]]
469

470 471 472 473 474 475 476 477 478 479 480 481 482
#1
</pre>
RAW

    expected = <<-EXPECTED
<p><a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a></p>
<p><a href="/issues/1" class="issue status-1 priority-1" title="Can't print recipes (New)">#1</a></p>
<pre>
[[CookBook documentation]]

#1
</pre>
EXPECTED
483

484 485 486
    @project = Project.find(1)
    assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
  end
487

488 489 490 491 492 493 494 495 496
  def test_non_closing_pre_blocks_should_be_closed
    raw = <<-RAW
<pre><code>
RAW

    expected = <<-EXPECTED
<pre><code>
</code></pre>
EXPECTED
497

498 499 500
    @project = Project.find(1)
    assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
  end
501

502
  def test_syntax_highlight
503 504 505
    raw = <<-RAW
<pre><code class="ruby">
# Some ruby code here
506
</code></pre>
507 508 509
RAW

    expected = <<-EXPECTED
510
<pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="line-numbers">1</span><span class="comment"># Some ruby code here</span></span>
511
</code></pre>
512 513 514 515
EXPECTED

    assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
  end
516

Jean-Philippe Lang's avatar
Jean-Philippe Lang committed
517
  def test_wiki_links_in_tables
518
    to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
519 520 521
                 '<tr><td><a href="/projects/ecookbook/wiki/Page" class="wiki-page new">Link title</a></td>' +
                 '<td><a href="/projects/ecookbook/wiki/Other_Page" class="wiki-page new">Other title</a></td>' +
                 '</tr><tr><td>Cell 21</td><td><a href="/projects/ecookbook/wiki/Last_page" class="wiki-page new">Last page</a></td></tr>'
Jean-Philippe Lang's avatar
Jean-Philippe Lang committed
522 523 524
    }
    @project = Project.find(1)
    to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
525
  end
526

527 528
  def test_text_formatting
    to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
529 530 531 532
               '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
               'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
               '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>',
               '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',
533 534 535
              }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end
536

537 538 539 540
  def test_wiki_horizontal_rule
    assert_equal '<hr />', textilizable('---')
    assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
  end
541

542 543 544 545 546 547 548 549 550 551 552 553 554 555
  def test_footnotes
    raw = <<-RAW
This is some text[1].

fn1. This is the foot note
RAW

    expected = <<-EXPECTED
<p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
<p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
EXPECTED

    assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
  end
556

557 558 559
  def test_headings
    raw = 'h1. Some heading'
    expected = %|<a name="Some-heading"></a>\n<h1 >Some heading<a href="#Some-heading" class="wiki-anchor">&para;</a></h1>|
560

561 562
    assert_equal expected, textilizable(raw)
  end
563

564 565 566 567 568 569 570 571
  def test_table_of_content
    raw = <<-RAW
{{toc}}

h1. Title

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.

572
h2. Subtitle with a [[Wiki]] link
573 574 575

Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.

576 577
h2. Subtitle with [[Wiki|another Wiki]] link

578
h2. Subtitle with %{color:red}red text%
579 580 581 582 583

<pre>
some code
</pre>

584
h3. Subtitle with *some* _modifiers_
585 586 587

h1. Another title

588
h3. An "Internet link":http://www.redmine.org/ inside subtitle
589 590 591

h2. "Project Name !/attachments/1234/logo_small.gif! !/attachments/5678/logo_2.png!":/projects/projectname/issues

592 593
RAW

594 595 596
    expected =  '<ul class="toc">' +
                  '<li><a href="#Title">Title</a>' +
                    '<ul>' +
597 598
                      '<li><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
                      '<li><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
                      '<li><a href="#Subtitle-with-red-text">Subtitle with red text</a>' +
                        '<ul>' +
                          '<li><a href="#Subtitle-with-some-modifiers">Subtitle with some modifiers</a></li>' +
                        '</ul>' +
                      '</li>' +
                    '</ul>' +
                  '</li>' +
                  '<li><a href="#Another-title">Another title</a>' +
                    '<ul>' +
                      '<li>' +
                        '<ul>' +
                          '<li><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
                        '</ul>' +
                      '</li>' +
                      '<li><a href="#Project-Name">Project Name</a></li>' +
                    '</ul>' +
                  '</li>' +
616
               '</ul>'
617

618
    @project = Project.find(1)
619
    assert textilizable(raw).gsub("\n", "").include?(expected), textilizable(raw)
620
  end
621

622 623 624 625 626 627
  def test_table_of_content_should_contain_included_page_headings
    raw = <<-RAW
{{toc}}

h1. Included

628
{% include 'Child_1' %}
629 630 631
RAW

    expected = '<ul class="toc">' +
632
               '<li><a href="#Included">Included</a></li>' +
633
               '<li><a href="#Child-page-1">Child page 1</a></li>' +
634 635 636
               '</ul>'

    @project = Project.find(1)
637
    assert textilizable(raw).gsub("\n", "").include?(expected)
638
  end
639

640 641 642 643 644 645
  def test_default_formatter
    Setting.text_formatting = 'unknown'
    text = 'a *link*: http://www.example.net/'
    assert_equal '<p>a *link*: <a href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
    Setting.text_formatting = 'textile'
  end
646

647 648 649
  def test_due_date_distance_in_words
    to_test = { Date.today => 'Due in 0 days',
                Date.today + 1 => 'Due in 1 day',
650
                Date.today + 100 => 'Due in about 3 months',
651
                Date.today + 20000 => 'Due in over 54 years',
652
                Date.today - 1 => '1 day late',
653
                Date.today - 100 => 'about 3 months late',
654
                Date.today - 20000 => 'over 54 years late',
655
               }
656
    ::I18n.locale = :en
657 658 659 660
    to_test.each do |date, expected|
      assert_equal expected, due_date_distance_in_words(date)
    end
  end
661

662 663 664 665 666
  def test_avatar
    # turn on avatars
    Setting.gravatar_enabled = '1'
    assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
    assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
667 668 669 670 671
    # Default size is 50
    assert avatar('jsmith <jsmith@somenet.foo>').include?('s=50')
    assert avatar('jsmith <jsmith@somenet.foo>', :size => 24).include?('s=24')
    # Non-avatar options should be considered html options
    assert avatar('jsmith <jsmith@somenet.foo>', :title => 'John Smith').include?('title="John Smith"')
672 673 674
    # The default class of the img tag should be gravatar
    assert avatar('jsmith <jsmith@somenet.foo>').include?('class="gravatar"')
    assert !avatar('jsmith <jsmith@somenet.foo>', :class => 'picture').include?('class="gravatar"')
675 676
    assert_nil avatar('jsmith')
    assert_nil avatar(nil)
677

678 679
    # turn off avatars
    Setting.gravatar_enabled = '0'
680
    assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo'))
681
  end
682

683 684 685 686 687
  def test_link_to_user
    user = User.find(2)
    t = link_to_user(user)
    assert_equal "<a href=\"/users/2\">#{ user.name }</a>", t
  end
688

689 690 691 692 693 694
  def test_link_to_user_should_not_link_to_locked_user
    user = User.find(5)
    assert user.locked?
    t = link_to_user(user)
    assert_equal user.name, t
  end
695

696 697 698 699 700 701
  def test_link_to_user_should_not_link_to_anonymous
    user = User.anonymous
    assert user.anonymous?
    t = link_to_user(user)
    assert_equal ::I18n.t(:label_user_anonymous), t
  end
702 703 704 705 706 707 708 709 710 711 712 713

  def test_link_to_project
    project = Project.find(1)
    assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
                 link_to_project(project)
    assert_equal %(<a href="/projects/ecookbook/settings">eCookbook</a>),
                 link_to_project(project, :action => 'settings')
    assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
                 link_to_project(project, {:only_path => false, :jump => 'blah'})
    assert_equal %(<a href="/projects/ecookbook/settings" class="project">eCookbook</a>),
                 link_to_project(project, {:action => 'settings'}, :class => "project")
  end
714
end