application_helper_test.rb 72.4 KB
Newer Older
1 2
# encoding: utf-8
#
3
# Redmine - project management software
jplang's avatar
jplang committed
4
# Copyright (C) 2006-2017  Jean-Philippe Lang
5 6 7 8 9
#
# 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.
10
#
11 12 13 14
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
15
#
16 17 18 19
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

20
require File.expand_path('../../test_helper', __FILE__)
21

jplang's avatar
jplang committed
22
class ApplicationHelperTest < Redmine::HelperTest
jplang's avatar
jplang committed
23
  include ERB::Util
24
  include Rails.application.routes.url_helpers
jplang's avatar
jplang committed
25

jplang's avatar
jplang committed
26 27 28
  fixtures :projects, :enabled_modules,
           :users, :email_addresses,
           :members, :member_roles, :roles,
29
           :repositories, :changesets,
30
           :projects_trackers,
31 32 33 34
           :trackers, :issue_statuses, :issues, :versions, :documents,
           :wikis, :wiki_pages, :wiki_contents,
           :boards, :messages, :news,
           :attachments, :enumerations
35 36 37

  def setup
    super
38
    set_tmp_attachments_directory
jplang's avatar
jplang committed
39
    @russian_test = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82".force_encoding('UTF-8')
40
  end
41

jplang's avatar
jplang committed
42 43
  test "#link_to_if_authorized for authorized user should allow using the :controller and :action for the target link" do
    User.current = User.find_by_login('admin')
44

jplang's avatar
jplang committed
45 46 47 48 49
    @project = Issue.first.project # Used by helper
    response = link_to_if_authorized('By controller/actionr',
                                    {:controller => 'issues', :action => 'edit', :id => Issue.first.id})
    assert_match /href/, response
  end
50

jplang's avatar
jplang committed
51 52 53 54 55
  test "#link_to_if_authorized for unauthorized user should display nothing if user isn't authorized" do
    User.current = User.find_by_login('dlopper')
    @project = Project.find('private-child')
    issue = @project.issues.first
    assert !issue.visible?
56

jplang's avatar
jplang committed
57 58 59
    response = link_to_if_authorized('Never displayed',
                                    {:controller => 'issues', :action => 'show', :id => issue})
    assert_nil response
60
  end
61

62 63
  def test_auto_links
    to_test = {
64
      'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
65
      'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
66
      'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
67
      'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
jplang's avatar
jplang committed
68 69
      '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>).',
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>.',
71 72 73 74 75 76 77
      '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>).',
78 79
      '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>',
80 81
      '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>',
82
      'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
jplang's avatar
jplang committed
83
      'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
84 85
      '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>',
86 87
      # 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>',
88
      # escaping
jplang's avatar
jplang committed
89
      'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo&quot;bar</a>',
90
      # wrap in angle brackets
91 92 93 94
      '<http://foo.bar>' => '&lt;<a class="external" href="http://foo.bar">http://foo.bar</a>&gt;',
      # invalid urls
      'http://' => 'http://',
      'www.' => 'www.',
95
      'test-www.bar.com' => 'test-www.bar.com',
96 97 98
    }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end
99

jplang's avatar
jplang committed
100 101 102 103 104 105
  def test_auto_links_with_non_ascii_characters
    to_test = {
      "http://foo.bar/#{@russian_test}" =>
        %|<a class="external" href="http://foo.bar/#{@russian_test}">http://foo.bar/#{@russian_test}</a>|
    }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
106 107
  end

108
  def test_auto_mailto
109 110 111 112 113
    to_test = {
      'test@foo.bar' => '<a class="email" href="mailto:test@foo.bar">test@foo.bar</a>',
      'test@www.foo.bar' => '<a class="email" href="mailto:test@www.foo.bar">test@www.foo.bar</a>',
    }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
114
  end
115

116
  def test_inline_images
117 118
    to_test = {
      '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
119
      'floating !>http://foo.bar/image.jpg!' => 'floating <span style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></span>',
120 121
      'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="wiki-class-some-class" alt="" />',
      'with class !(wiki-class-foo)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="wiki-class-foo" alt="" />',
jplang's avatar
jplang committed
122
      '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="" />',
jplang's avatar
jplang committed
123 124
      '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;" />',
125 126 127
    }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end
128

129 130 131 132 133 134 135 136 137 138 139 140
  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
141

142 143
  def test_attached_images
    to_test = {
144 145
      'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
      'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
146
      'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
147 148
      'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
      # link image
149
      '!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
    }
jplang's avatar
jplang committed
151
    attachments = Attachment.all
152 153
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
  end
154

155 156 157 158 159 160 161 162 163
  def test_attached_images_with_textile_and_non_ascii_filename
    attachment = Attachment.generate!(:filename => 'café.jpg')
    with_settings :text_formatting => 'textile' do
      assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="" />),
        textilizable("!café.jpg!)", :attachments => [attachment])
    end
  end

  def test_attached_images_with_markdown_and_non_ascii_filename
164 165
    skip unless Object.const_defined?(:Redcarpet)

166 167
    attachment = Attachment.generate!(:filename => 'café.jpg')
    with_settings :text_formatting => 'markdown' do
jplang's avatar
jplang committed
168
      assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="" />),
169 170 171 172
        textilizable("![](café.jpg)", :attachments => [attachment])
    end
  end

jplang's avatar
jplang committed
173 174 175 176 177 178
  def test_attached_images_with_hires_naming
    attachment = Attachment.generate!(:filename => 'image@2x.png')
      assert_equal %(<p><img src="/attachments/download/#{attachment.id}/image@2x.png" srcset="/attachments/download/#{attachment.id}/image@2x.png 2x" alt="" /></p>),
        textilizable("!image@2x.png!", :attachments => [attachment])
  end

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
  def test_attached_images_filename_extension
    set_tmp_attachments_directory
    a1 = Attachment.new(
            :container => Issue.find(1),
            :file => mock_file_with_options({:original_filename => "testtest.JPG"}),
            :author => User.find(1))
    assert a1.save
    assert_equal "testtest.JPG", a1.filename
    assert_equal "image/jpeg", a1.content_type
    assert a1.image?

    a2 = Attachment.new(
            :container => Issue.find(1),
            :file => mock_file_with_options({:original_filename => "testtest.jpeg"}),
            :author => User.find(1))
    assert a2.save
    assert_equal "testtest.jpeg", a2.filename
    assert_equal "image/jpeg", a2.content_type
    assert a2.image?

    a3 = Attachment.new(
            :container => Issue.find(1),
            :file => mock_file_with_options({:original_filename => "testtest.JPE"}),
            :author => User.find(1))
    assert a3.save
    assert_equal "testtest.JPE", a3.filename
    assert_equal "image/jpeg", a3.content_type
    assert a3.image?

    a4 = Attachment.new(
            :container => Issue.find(1),
            :file => mock_file_with_options({:original_filename => "Testtest.BMP"}),
            :author => User.find(1))
    assert a4.save
    assert_equal "Testtest.BMP", a4.filename
    assert_equal "image/x-ms-bmp", a4.content_type
    assert a4.image?

    to_test = {
      'Inline image: !testtest.jpg!' =>
219
        'Inline image: <img src="/attachments/download/' + a1.id.to_s + '/testtest.JPG" alt="" />',
220
      'Inline image: !testtest.jpeg!' =>
221
        'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testtest.jpeg" alt="" />',
222
      'Inline image: !testtest.jpe!' =>
223
        'Inline image: <img src="/attachments/download/' + a3.id.to_s + '/testtest.JPE" alt="" />',
224
      'Inline image: !testtest.bmp!' =>
225
        'Inline image: <img src="/attachments/download/' + a4.id.to_s + '/Testtest.BMP" alt="" />',
226 227 228 229 230 231
    }

    attachments = [a1, a2, a3, a4]
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
  end

232
  def test_attached_images_should_read_later
233
    set_fixtures_attachments_directory
234 235 236 237 238 239 240 241 242 243 244 245 246 247
    a1 = Attachment.find(16)
    assert_equal "testfile.png", a1.filename
    assert a1.readable?
    assert (! a1.visible?(User.anonymous))
    assert a1.visible?(User.find(2))
    a2 = Attachment.find(17)
    assert_equal "testfile.PNG", a2.filename
    assert a2.readable?
    assert (! a2.visible?(User.anonymous))
    assert a2.visible?(User.find(2))
    assert a1.created_on < a2.created_on

    to_test = {
      'Inline image: !testfile.png!' =>
248
        'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
249
      'Inline image: !Testfile.PNG!' =>
250
        'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
251 252 253
    }
    attachments = [a1, a2]
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
254
    set_tmp_attachments_directory
255 256
  end

257 258
  def test_textile_external_links
    to_test = {
259 260
      '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>',
jplang's avatar
jplang committed
261
      '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
262
      '"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>',
263
      "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
jplang's avatar
jplang committed
264
      # no multiline link text
265
      "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",
jplang's avatar
jplang committed
266 267
      # mailto link
      "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
268 269
      # 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>',
270 271
      # escaping
      '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
272 273 274
    }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end
275

jplang's avatar
jplang committed
276 277 278 279 280 281
  def test_textile_external_links_with_non_ascii_characters
    to_test = {
      %|This is a "link":http://foo.bar/#{@russian_test}| =>
        %|This is a <a href="http://foo.bar/#{@russian_test}" class="external">link</a>|
    }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
282 283
  end

284
  def test_redmine_links
285 286 287 288 289
    user_with_email_login = User.generate!(:login => 'abcd@example.com')
    user_with_email_login_2 = User.generate!(:login => 'foo.bar@example.com')
    u_email_id = user_with_email_login.id
    u_email_id_2 = user_with_email_login_2.id

290
    issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
291
                               :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
292
    note_link = link_to('#3-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'},
293
                               :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
294
    note_link2 = link_to('#3#note-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'},
295
                               :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
296

297
    revision_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 10, :rev => 1},
298
                                   :class => 'changeset', :title => 'My very first commit do not escaping #<>&')
299
    revision_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 10, :rev => 2},
300 301
                                    :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')

302
    changeset_link2 = link_to('691322a8eb01e11fd7',
303
                              {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 10, :rev => 1},
304
                               :class => 'changeset', :title => 'My very first commit do not escaping #<>&')
305

306 307
    document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
                                             :class => 'document')
308

309 310
    version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
                                  :class => 'version')
311

312 313
    board_url = {:controller => 'boards', :action => 'show', :id => 2, :project_id => 'ecookbook'}

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

316
    news_url = {:controller => 'news', :action => 'show', :id => 1}
317

jplang's avatar
jplang committed
318
    project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
319

320 321 322 323 324
    source_url = '/projects/ecookbook/repository/10/entry/some/file'
    source_url_with_rev = '/projects/ecookbook/repository/10/revisions/52/entry/some/file'
    source_url_with_ext = '/projects/ecookbook/repository/10/entry/some/file.ext'
    source_url_with_rev_and_ext = '/projects/ecookbook/repository/10/revisions/52/entry/some/file.ext'
    source_url_with_branch = '/projects/ecookbook/repository/10/revisions/branch/entry/some/file'
325

326 327 328 329 330
    export_url = '/projects/ecookbook/repository/10/raw/some/file'
    export_url_with_rev = '/projects/ecookbook/repository/10/revisions/52/raw/some/file'
    export_url_with_ext = '/projects/ecookbook/repository/10/raw/some/file.ext'
    export_url_with_rev_and_ext = '/projects/ecookbook/repository/10/revisions/52/raw/some/file.ext'
    export_url_with_branch = '/projects/ecookbook/repository/10/revisions/branch/raw/some/file'
331

332
    to_test = {
333
      # tickets
334
      '#3, [#3], (#3) and #3.'      => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
335 336
      # ticket notes
      '#3-14'                       => note_link,
337
      '#3#note-14'                  => note_link2,
338 339
      # should not ignore leading zero
      '#03'                         => '#03',
340
      # changesets
341 342 343 344 345
      'r1'                          => revision_link,
      'r1.'                         => "#{revision_link}.",
      'r1, r2'                      => "#{revision_link}, #{revision_link2}",
      'r1,r2'                       => "#{revision_link},#{revision_link2}",
      'commit:691322a8eb01e11fd7'   => changeset_link2,
346 347 348 349 350 351 352 353
      # 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
354
      'source:some/file'            => link_to('source:some/file', source_url, :class => 'source'),
355
      'source:/some/file'           => link_to('source:/some/file', source_url, :class => 'source'),
356 357 358 359 360
      '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') + ",",
361
      'source:/some/file@52'        => link_to('source:/some/file@52', source_url_with_rev, :class => 'source'),
362
      'source:/some/file@branch'    => link_to('source:/some/file@branch', source_url_with_branch, :class => 'source'),
363 364 365 366 367 368 369 370 371
      'source:/some/file.ext@52'    => link_to('source:/some/file.ext@52', source_url_with_rev_and_ext, :class => 'source'),
      'source:/some/file#L110'      => link_to('source:/some/file#L110', source_url + "#L110", :class => 'source'),
      'source:/some/file.ext#L110'  => link_to('source:/some/file.ext#L110', source_url_with_ext + "#L110", :class => 'source'),
      'source:/some/file@52#L110'   => link_to('source:/some/file@52#L110', source_url_with_rev + "#L110", :class => 'source'),
      # export
      'export:/some/file'           => link_to('export:/some/file', export_url, :class => 'source download'),
      'export:/some/file.ext'       => link_to('export:/some/file.ext', export_url_with_ext, :class => 'source download'),
      'export:/some/file@52'        => link_to('export:/some/file@52', export_url_with_rev, :class => 'source download'),
      'export:/some/file.ext@52'    => link_to('export:/some/file.ext@52', export_url_with_rev_and_ext, :class => 'source download'),
372
      'export:/some/file@branch'    => link_to('export:/some/file@branch', export_url_with_branch, :class => 'source download'),
373 374 375
      # forum
      'forum#2'                     => link_to('Discussion', board_url, :class => 'board'),
      'forum:Discussion'            => link_to('Discussion', board_url, :class => 'board'),
376 377
      # message
      'message#4'                   => link_to('Post 2', message_url, :class => 'message'),
378
      'message#5'                   => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'),
379 380 381
      # news
      'news#1'                      => link_to('eCookbook first release !', news_url, :class => 'news'),
      'news:"eCookbook first release !"'        => link_to('eCookbook first release !', news_url, :class => 'news'),
jplang's avatar
jplang committed
382 383 384 385
      # 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'),
386 387
      # not found
      '#0123456789'                 => '#0123456789',
388
      # invalid expressions
389 390
      'source:'                     => 'source:',
      # url hash
jplang's avatar
jplang committed
391 392 393
      "http://foo.bar/FAQ#3"        => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
      # user
      'user:jsmith'                 => link_to_user(User.find_by_id(2)),
jplang's avatar
jplang committed
394
      'user#2'                      => link_to_user(User.find_by_id(2)),
jplang's avatar
jplang committed
395
      '@jsmith'                     => link_to_user(User.find_by_id(2)),
396 397 398 399
      '@abcd@example.com'           => link_to_user(User.find_by_id(u_email_id)),
      'user:abcd@example.com'       => link_to_user(User.find_by_id(u_email_id)),
      '@foo.bar@example.com'        => link_to_user(User.find_by_id(u_email_id_2)),
      'user:foo.bar@example.com'    => link_to_user(User.find_by_id(u_email_id_2)),
jplang's avatar
jplang committed
400 401
      # invalid user
      'user:foobar'                 => 'user:foobar',
402 403
    }
    @project = Project.find(1)
jplang's avatar
jplang committed
404
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
405
  end
406

407 408 409
  def test_user_links_with_email_as_login_name_should_not_be_parsed_textile
    with_settings :text_formatting => 'textile' do
      u = User.generate!(:login => 'jsmith@somenet.foo')
jplang's avatar
jplang committed
410

411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
      # user link format: @jsmith@somenet.foo
      raw = "@jsmith@somenet.foo should not be parsed in jsmith@somenet.foo"
      assert_match %r{<p><a class="user active".*>#{u.name}</a> should not be parsed in <a class="email" href="mailto:jsmith@somenet.foo">jsmith@somenet.foo</a></p>},
        textilizable(raw, :project => Project.find(1))

      # user link format: user:jsmith@somenet.foo
      raw = "user:jsmith@somenet.foo should not be parsed in jsmith@somenet.foo"
      assert_match %r{<p><a class="user active".*>#{u.name}</a> should not be parsed in <a class="email" href="mailto:jsmith@somenet.foo">jsmith@somenet.foo</a></p>},
        textilizable(raw, :project => Project.find(1))
    end
  end

  def test_user_links_with_email_as_login_name_should_not_be_parsed_markdown
    with_settings :text_formatting => 'markdown' do
      u = User.generate!(:login => 'jsmith@somenet.foo')

      # user link format: @jsmith@somenet.foo
      raw = "@jsmith@somenet.foo should not be parsed in jsmith@somenet.foo"
      assert_match %r{<p><a class=\"user active\".*>#{u.name}</a> should not be parsed in <a href=\"mailto:jsmith@somenet.foo\">jsmith@somenet.foo</a></p>},
        textilizable(raw, :project => Project.find(1))

      # user link format: user:jsmith@somenet.foo
      raw = "user:jsmith@somenet.foo should not be parsed in jsmith@somenet.foo"
      assert_match %r{<p><a class=\"user active\".*>#{u.name}</a> should not be parsed in <a href=\"mailto:jsmith@somenet.foo\">jsmith@somenet.foo</a></p>},
        textilizable(raw, :project => Project.find(1))
    end
jplang's avatar
jplang committed
437 438
  end

439 440 441 442 443 444
  def test_should_not_parse_redmine_links_inside_link
    raw = "r1 should not be parsed in http://example.com/url-r1/"
    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>},
      textilizable(raw, :project => Project.find(1))
  end

445 446 447 448
  def test_redmine_links_with_a_different_project_before_current_project
    vp1 = Version.generate!(:project_id => 1, :name => '1.4.4')
    vp3 = Version.generate!(:project_id => 3, :name => '1.4.4')
    @project = Project.find(3)
449 450 451 452
    result1 = link_to("1.4.4", "/versions/#{vp1.id}", :class => "version")
    result2 = link_to("1.4.4", "/versions/#{vp3.id}", :class => "version")
    assert_equal "<p>#{result1} #{result2}</p>",
                 textilizable("ecookbook:version:1.4.4 version:1.4.4")
453 454
  end

455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
  def test_escaped_redmine_links_should_not_be_parsed
    to_test = [
      '#3.',
      '#3-14.',
      '#3#-note14.',
      'r1',
      'document#1',
      'document:"Test document"',
      'version#2',
      'version:1.0',
      'version:"1.0"',
      'source:/some/file'
    ]
    @project = Project.find(1)
    to_test.each { |text| assert_equal "<p>#{text}</p>", textilizable("!" + text), "#{text} failed" }
  end

472
  def test_cross_project_redmine_links
473 474
    source_link = link_to('ecookbook:source:/some/file',
                          {:controller => 'repositories', :action => 'entry',
475
                           :id => 'ecookbook', :repository_id => 10, :path => ['some', 'file']},
476 477 478
                          :class => 'source')
    changeset_link = link_to('ecookbook:r2',
                             {:controller => 'repositories', :action => 'revision',
479
                              :id => 'ecookbook', :repository_id => 10, :rev => 2},
480 481
                             :class => 'changeset',
                             :title => 'This commit fixes #1, #2 and references #1 & #3')
482 483 484
    to_test = {
      # documents
      'document:"Test document"'              => 'document:"Test document"',
485 486
      'ecookbook:document:"Test document"'    =>
          link_to("Test document", "/documents/1", :class => "document"),
487 488 489
      'invalid:document:"Test document"'      => 'invalid:document:"Test document"',
      # versions
      'version:"1.0"'                         => 'version:"1.0"',
490 491
      'ecookbook:version:"1.0"'               =>
          link_to("1.0", "/versions/2", :class => "version"),
492 493 494 495 496 497 498 499 500 501 502
      '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)
503 504 505
    to_test.each do |text, result|
      assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed"
    end
506
  end
507

508 509 510 511 512 513 514 515
  def test_redmine_links_by_name_should_work_with_html_escaped_characters
    v = Version.generate!(:name => "Test & Show.txt", :project_id => 1)
    link = link_to("Test & Show.txt", "/versions/#{v.id}", :class => "version")

    @project = v.project
    assert_equal "<p>#{link}</p>", textilizable('version:"Test & Show.txt"')
  end

516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
  def test_link_to_issue_subject
    issue = Issue.generate!(:subject => "01234567890123456789")
    str = link_to_issue(issue, :truncate => 10)
    result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
    assert_equal "#{result}: 0123456...", str

    issue = Issue.generate!(:subject => "<&>")
    str = link_to_issue(issue)
    result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
    assert_equal "#{result}: &lt;&amp;&gt;", str

    issue = Issue.generate!(:subject => "<&>0123456789012345")
    str = link_to_issue(issue, :truncate => 10)
    result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
    assert_equal "#{result}: &lt;&amp;&gt;0123...", str
  end

  def test_link_to_issue_title
    long_str = "0123456789" * 5

    issue = Issue.generate!(:subject => "#{long_str}01234567890123456789")
    str = link_to_issue(issue, :subject => false)
    result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}",
                     :class => issue.css_classes,
                     :title => "#{long_str}0123456...")
    assert_equal result, str

    issue = Issue.generate!(:subject => "<&>#{long_str}01234567890123456789")
    str = link_to_issue(issue, :subject => false)
    result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}",
                     :class => issue.css_classes,
                     :title => "<&>#{long_str}0123...")
    assert_equal result, str
  end

551
  def test_multiple_repositories_redmine_links
552
    svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn_repo-1', :url => 'file:///foo/hg')
553 554 555 556
    Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
    hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
    Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')

557
    changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 10, :rev => 2},
558
                                    :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
559
    svn_changeset_link = link_to('svn_repo-1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn_repo-1', :rev => 123},
560 561 562 563
                                    :class => 'changeset', :title => '')
    hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
                                    :class => 'changeset', :title => '')

564
    source_link = link_to('source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 10, :path => ['some', 'file']}, :class => 'source')
565 566 567 568
    hg_source_link = link_to('source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')

    to_test = {
      'r2'                          => changeset_link,
569
      'svn_repo-1|r123'             => svn_changeset_link,
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
      'invalid|r123'                => 'invalid|r123',
      'commit:hg1|abcd'             => hg_changeset_link,
      'commit:invalid|abcd'         => 'commit:invalid|abcd',
      # source
      'source:some/file'            => source_link,
      'source:hg1|some/file'        => hg_source_link,
      'source:invalid|some/file'    => 'source:invalid|some/file',
    }

    @project = Project.find(1)
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
  end

  def test_cross_project_multiple_repositories_redmine_links
    svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg')
    Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
    hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
    Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')

589
    changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 10, :rev => 2},
590 591 592 593 594 595
                                    :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
    svn_changeset_link = link_to('ecookbook:svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123},
                                    :class => 'changeset', :title => '')
    hg_changeset_link = link_to('ecookbook:hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
                                    :class => 'changeset', :title => '')

596
    source_link = link_to('ecookbook:source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 10, :path => ['some', 'file']}, :class => 'source')
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
    hg_source_link = link_to('ecookbook:source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')

    to_test = {
      'ecookbook:r2'                           => changeset_link,
      'ecookbook:svn1|r123'                    => svn_changeset_link,
      'ecookbook:invalid|r123'                 => 'ecookbook:invalid|r123',
      'ecookbook:commit:hg1|abcd'              => hg_changeset_link,
      'ecookbook:commit:invalid|abcd'          => 'ecookbook:commit:invalid|abcd',
      'invalid:commit:invalid|abcd'            => 'invalid:commit:invalid|abcd',
      # source
      'ecookbook:source:some/file'             => source_link,
      'ecookbook:source:hg1|some/file'         => hg_source_link,
      'ecookbook:source:invalid|some/file'     => 'ecookbook:source:invalid|some/file',
      'invalid:source:invalid|some/file'       => 'invalid:source:invalid|some/file',
    }

    @project = Project.find(3)
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
  end

617
  def test_redmine_links_git_commit
618 619 620 621 622 623 624 625 626
    @project = Project.find(3)
    r = Repository::Git.create!(:project => @project, :url => '/tmp/test/git')

    c = Changeset.create!(:repository => r,
                      :committed_on => Time.now,
                      :revision => 'abcd',
                      :scmid => 'abcd',
                      :comments => 'test commit')

627
    changeset_link = link_to('abcd',
628 629 630 631
                               {
                                 :controller => 'repositories',
                                 :action     => 'revision',
                                 :id         => 'subproject1',
632
                                 :repository_id => r.id,
633 634
                                 :rev        => 'abcd',
                                },
635 636 637 638 639 640 641 642
                              :class => 'changeset', :title => 'test commit')
    to_test = {
      'commit:abcd' => changeset_link,
     }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end

  # TODO: Bazaar commit id contains mail address, so it contains '@' and '_'.
643
  def test_redmine_links_mercurial_commit
644 645 646 647 648 649 650 651
    @project = Project.find(3)
    r = Repository::Mercurial.create!(:project => @project, :url => '/tmp/test')
    c = Changeset.create!(:repository => r,
                      :committed_on => Time.now,
                      :revision => '123',
                      :scmid => 'abcd',
                      :comments => 'test commit')

652 653 654 655 656
    changeset_link_rev = link_to('r123',
                                  {
                                     :controller => 'repositories',
                                     :action     => 'revision',
                                     :id         => 'subproject1',
657
                                     :repository_id => r.id,
658 659 660 661 662
                                     :rev        => '123' ,
                                  },
                              :class => 'changeset', :title => 'test commit')
    changeset_link_commit = link_to('abcd',
                                  {
663 664 665 666 667
                                    :controller => 'repositories',
                                    :action     => 'revision',
                                    :id         => 'subproject1',
                                    :repository_id => r.id,
                                    :rev        => 'abcd' ,
668 669 670 671 672 673 674 675 676
                                  },
                              :class => 'changeset', :title => 'test commit')
    to_test = {
      'r123' => changeset_link_rev,
      'commit:abcd' => changeset_link_commit,
     }
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end

677
  def test_attachment_links
678
    text = 'attachment:error281.txt'
679
    result = link_to("error281.txt", "/attachments/1/error281.txt",
680 681 682 683 684
                     :class => "attachment")
    assert_equal "<p>#{result}</p>",
                 textilizable(text,
                              :attachments => Issue.find(3).attachments),
                 "#{text} failed"
685
  end
686

687 688 689 690
  def test_attachment_link_should_link_to_latest_attachment
    set_tmp_attachments_directory
    a1 = Attachment.generate!(:filename => "test.txt", :created_on => 1.hour.ago)
    a2 = Attachment.generate!(:filename => "test.txt")
691
    result = link_to("test.txt", "/attachments/#{a2.id}/test.txt",
692 693 694
                     :class => "attachment")
    assert_equal "<p>#{result}</p>",
                 textilizable('attachment:test.txt', :attachments => [a1, a2])
695 696
  end

697
  def test_wiki_links
698
    User.current = User.find_by_login('jsmith')
699
    russian_eacape = CGI.escape(@russian_test)
700
    to_test = {
701 702 703 704 705 706 707 708
      '[[CookBook documentation]]' =>
          link_to("CookBook documentation",
                  "/projects/ecookbook/wiki/CookBook_documentation",
                  :class => "wiki-page"),
      '[[Another page|Page]]' =>
          link_to("Page",
                  "/projects/ecookbook/wiki/Another_page",
                  :class => "wiki-page"),
709
      # title content should be formatted
710 711 712 713 714 715 716 717
      '[[Another page|With _styled_ *title*]]' =>
          link_to("With <em>styled</em> <strong>title</strong>".html_safe,
                  "/projects/ecookbook/wiki/Another_page",
                  :class => "wiki-page"),
      '[[Another page|With title containing <strong>HTML entities &amp; markups</strong>]]' =>
          link_to("With title containing &lt;strong&gt;HTML entities &amp; markups&lt;/strong&gt;".html_safe,
                  "/projects/ecookbook/wiki/Another_page",
                  :class => "wiki-page"),
718
      # link with anchor
719 720 721 722 723 724 725 726
      '[[CookBook documentation#One-section]]' =>
          link_to("CookBook documentation",
                  "/projects/ecookbook/wiki/CookBook_documentation#One-section",
                  :class => "wiki-page"),
      '[[Another page#anchor|Page]]' =>
          link_to("Page",
                  "/projects/ecookbook/wiki/Another_page#anchor",
                  :class => "wiki-page"),
727
      # UTF8 anchor
728
      "[[Another_page##{@russian_test}|#{@russian_test}]]" =>
729 730 731
          link_to(@russian_test,
                  "/projects/ecookbook/wiki/Another_page##{russian_eacape}",
                  :class => "wiki-page"),
maeda's avatar
maeda committed
732 733 734 735 736 737 738 739 740
      # link to anchor
      '[[#anchor]]' =>
          link_to("#anchor",
                  "#anchor",
                  :class => "wiki-page"),
      '[[#anchor|One-section]]' =>
          link_to("One-section",
                  "#anchor",
                  :class => "wiki-page"),
741
      # page that doesn't exist
742 743 744 745 746 747 748 749
      '[[Unknown page]]' =>
          link_to("Unknown page",
                  "/projects/ecookbook/wiki/Unknown_page",
                  :class => "wiki-page new"),
      '[[Unknown page|404]]' =>
          link_to("404",
                  "/projects/ecookbook/wiki/Unknown_page",
                  :class => "wiki-page new"),
750
      # link to another project wiki
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
      '[[onlinestore:]]' =>
          link_to("onlinestore",
                  "/projects/onlinestore/wiki",
                  :class => "wiki-page"),
      '[[onlinestore:|Wiki]]' =>
          link_to("Wiki",
                  "/projects/onlinestore/wiki",
                  :class => "wiki-page"),
      '[[onlinestore:Start page]]' =>
          link_to("Start page",
                  "/projects/onlinestore/wiki/Start_page",
                  :class => "wiki-page"),
      '[[onlinestore:Start page|Text]]' =>
          link_to("Text",
                  "/projects/onlinestore/wiki/Start_page",
                  :class => "wiki-page"),
      '[[onlinestore:Unknown page]]' =>
          link_to("Unknown page",
                  "/projects/onlinestore/wiki/Unknown_page",
                  :class => "wiki-page new"),
771
      # struck through link
772 773 774 775 776 777 778 779 780 781 782 783
      '-[[Another page|Page]]-' =>
          "<del>".html_safe +
            link_to("Page",
                    "/projects/ecookbook/wiki/Another_page",
                    :class => "wiki-page").html_safe +
            "</del>".html_safe,
      '-[[Another page|Page]] link-' =>
          "<del>".html_safe +
            link_to("Page",
                    "/projects/ecookbook/wiki/Another_page",
                    :class => "wiki-page").html_safe +
            " link</del>".html_safe,
784 785
      # escaping
      '![[Another page|Page]]' => '[[Another page|Page]]',
786 787 788
      # project does not exist
      '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
      '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
789 790 791
      # missing permission to view wiki in project
      '[[private-child:]]' => '[[private-child:]]',
      '[[private-child:Wiki]]' => '[[private-child:Wiki]]',
792 793 794 795
    }
    @project = Project.find(1)
    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
  end
796

797 798 799
  def test_wiki_links_within_local_file_generation_context
    to_test = {
      # link to a page
800 801 802 803 804 805 806 807 808 809 810 811
      '[[CookBook documentation]]' =>
         link_to("CookBook documentation", "CookBook_documentation.html",
                 :class => "wiki-page"),
      '[[CookBook documentation|documentation]]' =>
         link_to("documentation", "CookBook_documentation.html",
                 :class => "wiki-page"),
      '[[CookBook documentation#One-section]]' =>
         link_to("CookBook documentation", "CookBook_documentation.html#One-section",
                 :class => "wiki-page"),
      '[[CookBook documentation#One-section|documentation]]' =>
         link_to("documentation", "CookBook_documentation.html#One-section",
                 :class => "wiki-page"),
812
      # page that doesn't exist
813 814 815 816 817 818 819 820 821 822 823 824
      '[[Unknown page]]' =>
         link_to("Unknown page", "Unknown_page.html",
                 :class => "wiki-page new"),
      '[[Unknown page|404]]' =>
         link_to("404", "Unknown_page.html",
                 :class => "wiki-page new"),
      '[[Unknown page#anchor]]' =>
         link_to("Unknown page", "Unknown_page.html#anchor",
                 :class => "wiki-page new"),
      '[[Unknown page#anchor|404]]' =>
         link_to("404", "Unknown_page.html#anchor",
                 :class => "wiki-page new"),
825 826
    }
    @project = Project.find(1)
827 828 829
    to_test.each do |text, result|
      assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :local)
    end
830 831
  end

jplang's avatar
jplang committed
832 833 834
  def test_wiki_links_within_wiki_page_context
    page = WikiPage.find_by_title('Another_page' )
    to_test = {
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
      '[[CookBook documentation]]' =>
         link_to("CookBook documentation",
                 "/projects/ecookbook/wiki/CookBook_documentation",
                 :class => "wiki-page"),
      '[[CookBook documentation|documentation]]' =>
         link_to("documentation",
                 "/projects/ecookbook/wiki/CookBook_documentation",
                 :class => "wiki-page"),
      '[[CookBook documentation#One-section]]' =>
         link_to("CookBook documentation",
                 "/projects/ecookbook/wiki/CookBook_documentation#One-section",
                 :class => "wiki-page"),
      '[[CookBook documentation#One-section|documentation]]' =>
         link_to("documentation",
                 "/projects/ecookbook/wiki/CookBook_documentation#One-section",
                 :class => "wiki-page"),
jplang's avatar
jplang committed
851
      # link to the current page
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
      '[[Another page]]' =>
         link_to("Another page",
                 "/projects/ecookbook/wiki/Another_page",
                 :class => "wiki-page"),
      '[[Another page|Page]]' =>
         link_to("Page",
                 "/projects/ecookbook/wiki/Another_page",
                 :class => "wiki-page"),
      '[[Another page#anchor]]' =>
         link_to("Another page",
                 "#anchor",
                 :class => "wiki-page"),
      '[[Another page#anchor|Page]]' =>
         link_to("Page",
                 "#anchor",
                 :class => "wiki-page"),
jplang's avatar
jplang committed
868
      # page that doesn't exist
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
      '[[Unknown page]]' =>
         link_to("Unknown page",
                 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page",
                 :class => "wiki-page new"),
      '[[Unknown page|404]]' =>
         link_to("404",
                 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page",
                 :class => "wiki-page new"),
      '[[Unknown page#anchor]]' =>
         link_to("Unknown page",
                 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor",
                 :class => "wiki-page new"),
      '[[Unknown page#anchor|404]]' =>
         link_to("404",
                 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor",
                 :class => "wiki-page new"),
jplang's avatar
jplang committed
885 886
    }
    @project = Project.find(1)
887 888 889
    to_test.each do |text, result|
      assert_equal "<p>#{result}</p>",
                   textilizable(WikiContent.new( :text => text, :page => page ), :text)
890
    end
jplang's avatar
jplang committed
891 892 893 894 895
  end

  def test_wiki_links_anchor_option_should_prepend_page_title_to_href
    to_test = {
      # link to a page
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
      '[[CookBook documentation]]' =>
          link_to("CookBook documentation",
                  "#CookBook_documentation",
                  :class => "wiki-page"),
      '[[CookBook documentation|documentation]]' =>
          link_to("documentation",
                  "#CookBook_documentation",
                  :class => "wiki-page"),
      '[[CookBook documentation#One-section]]' =>
          link_to("CookBook documentation",
                  "#CookBook_documentation_One-section",
                  :class => "wiki-page"),
      '[[CookBook documentation#One-section|documentation]]' =>
          link_to("documentation",
                  "#CookBook_documentation_One-section",
                  :class => "wiki-page"),
jplang's avatar
jplang committed
912
      # page that doesn't exist
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
      '[[Unknown page]]' =>
          link_to("Unknown page",
                  "#Unknown_page",
                  :class => "wiki-page new"),
      '[[Unknown page|404]]' =>
          link_to("404",
                  "#Unknown_page",
                  :class => "wiki-page new"),
      '[[Unknown page#anchor]]' =>
          link_to("Unknown page",
                  "#Unknown_page_anchor",
                  :class => "wiki-page new"),
      '[[Unknown page#anchor|404]]' =>
          link_to("404",
                  "#Unknown_page_anchor",
                  :class => "wiki-page new"),
jplang's avatar
jplang committed
929 930
    }
    @project = Project.find(1)
931 932 933
    to_test.each do |text, result|
      assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :anchor)
    end
jplang's avatar
jplang committed
934 935
  end

936 937
  def test_html_tags
    to_test = {
938 939 940
      "<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>",
941 942 943 944
      # 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>",
jplang's avatar
jplang committed
945
      "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
jplang's avatar
jplang committed
946
      "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
947 948 949 950 951
      # remove attributes including class
      "<pre class='foo'>some text</pre>" => "<pre>some text</pre>",
      '<pre class="foo">some text</pre>' => '<pre>some text</pre>',
      "<pre class='foo bar'>some text</pre>" => "<pre>some text</pre>",
      '<pre class="foo bar">some text</pre>' => '<pre>some text</pre>',
952
      "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
953 954 955
      # 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>',
956 957
    }
    to_test.each { |text, result| assert_equal result, textilizable(text) }
jplang's avatar
jplang committed
958
  end
959

jplang's avatar
jplang committed
960 961 962 963
  def test_allowed_html_tags
    to_test = {
      "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
      "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
jplang's avatar
jplang committed
964
      "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
jplang's avatar
jplang committed
965 966 967
    }
    to_test.each { |text, result| assert_equal result, textilizable(text) }
  end
968