# frozen_string_literal: true # Redmine - project management software # Copyright (C) 2006-2020 Jean-Philippe Lang # # 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. # # 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. # # 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. require File.expand_path('../../test_helper', __FILE__) class ApplicationHelperTest < Redmine::HelperTest include ERB::Util include Rails.application.routes.url_helpers include AvatarsHelper fixtures :projects, :enabled_modules, :users, :email_addresses, :members, :member_roles, :roles, :repositories, :changesets, :projects_trackers, :trackers, :issue_statuses, :issues, :versions, :documents, :journals, :wikis, :wiki_pages, :wiki_contents, :boards, :messages, :news, :attachments, :enumerations, :custom_values, :custom_fields, :custom_fields_projects def setup super set_tmp_attachments_directory @russian_test = 'тест' end 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') @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 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? response = link_to_if_authorized( 'Never displayed', {:controller => 'issues', :action => 'show', :id => issue}) assert_nil response end def test_auto_links to_test = { 'http://foo.bar' => 'http://foo.bar', 'http://foo.bar/~user' => 'http://foo.bar/~user', 'http://foo.bar.' => 'http://foo.bar.', 'https://foo.bar.' => 'https://foo.bar.', 'This is a link: http://foo.bar.' => 'This is a link: http://foo.bar.', 'A link (eg. http://foo.bar).' => 'A link (eg. http://foo.bar).', 'http://foo.bar/foo.bar#foo.bar.' => 'http://foo.bar/foo.bar#foo.bar.', 'http://www.foo.bar/Test_(foobar)' => 'http://www.foo.bar/Test_(foobar)', '(see inline link : http://www.foo.bar/Test_(foobar))' => '(see inline link : http://www.foo.bar/Test_(foobar))', '(see inline link : http://www.foo.bar/Test)' => '(see inline link : http://www.foo.bar/Test)', '(see inline link : http://www.foo.bar/Test).' => '(see inline link : http://www.foo.bar/Test).', '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see inline link)', '(see "inline link":http://www.foo.bar/Test)' => '(see inline link)', '(see "inline link":http://www.foo.bar/Test).' => '(see inline link).', 'www.foo.bar' => 'www.foo.bar', 'http://foo.bar/page?p=1&t=z&s=' => 'http://foo.bar/page?p=1&t=z&s=', 'http://foo.bar/page#125' => 'http://foo.bar/page#125', 'http://foo@www.bar.com' => 'http://foo@www.bar.com', 'http://foo:bar@www.bar.com' => 'http://foo:bar@www.bar.com', 'ftp://foo.bar' => 'ftp://foo.bar', 'ftps://foo.bar' => 'ftps://foo.bar', 'sftp://foo.bar' => 'sftp://foo.bar', # two exclamation marks 'http://example.net/path!602815048C7B5C20!302.html' => 'http://example.net/path!602815048C7B5C20!302.html', # escaping 'http://foo"bar' => 'http://foo"bar', # wrap in angle brackets '' => '<http://foo.bar>', # invalid urls 'http://' => 'http://', 'www.' => 'www.', 'test-www.bar.com' => 'test-www.bar.com', # ends with a hyphen 'http://www.redmine.org/example-' => 'http://www.redmine.org/example-', } to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end def test_auto_links_with_non_ascii_characters to_test = { "http://foo.bar/#{@russian_test}" => %|http://foo.bar/#{@russian_test}| } to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end def test_auto_mailto to_test = { 'test@foo.bar' => 'test@foo.bar', 'test@www.foo.bar' => 'test@www.foo.bar', } to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end def test_inline_images to_test = { '!http://foo.bar/image.jpg!' => '', 'floating !>http://foo.bar/image.jpg!' => 'floating ', 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class ', 'with class !(wiki-class-foo)http://foo.bar/image.jpg!' => 'with class ', 'with style !{width:100px;height:100px}http://foo.bar/image.jpg!' => 'with style ', 'with title !http://foo.bar/image.jpg(This is a title)!' => 'with title This is a title', 'with title !http://foo.bar/image.jpg(This is a double-quoted "title")!' => 'with title This is a double-quoted "title"', } to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end def test_inline_images_inside_tags raw = <<~RAW h1. !foo.png! Heading Centered image: p=. !bar.gif! RAW assert textilizable(raw).include?('') assert textilizable(raw).include?('') end def test_attached_images to_test = { 'Inline image: !logo.gif!' => 'Inline image: This is a logo', 'Inline image: !logo.GIF!' => 'Inline image: This is a logo', 'No match: !ogo.gif!' => 'No match: ', 'No match: !ogo.GIF!' => 'No match: ', # link image '!logo.gif!:http://foo.bar/' => 'This is a logo', } attachments = Attachment.all to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text, :attachments => attachments) } end def test_attached_images_with_textile_and_non_ascii_filename to_test = { 'CAFÉ.JPG' => 'CAF%C3%89.JPG', 'crème.jpg' => 'cr%C3%A8me.jpg', } with_settings :text_formatting => 'textile' do to_test.each do |filename, result| attachment = Attachment.generate!(:filename => filename) assert_include %(), textilizable("!#{filename}!", :attachments => [attachment]) end end end def test_attached_images_with_markdown_and_non_ascii_filename skip unless Object.const_defined?(:Redcarpet) to_test = { 'CAFÉ.JPG' => 'CAF%C3%89.JPG', 'crème.jpg' => 'cr%C3%A8me.jpg', } with_settings :text_formatting => 'markdown' do to_test.each do |filename, result| attachment = Attachment.generate!(:filename => filename) assert_include %(), textilizable("![](#{filename})", :attachments => [attachment]) end end end def test_attached_images_with_hires_naming attachment = Attachment.generate!(:filename => 'image@2x.png') assert_equal( %(

), textilizable("!image@2x.png!", :attachments => [attachment])) end def test_attached_images_filename_extension 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!' => 'Inline image: ', 'Inline image: !testtest.jpeg!' => 'Inline image: ', 'Inline image: !testtest.jpe!' => 'Inline image: ', 'Inline image: !testtest.bmp!' => 'Inline image: ', } attachments = [a1, a2, a3, a4] to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text, :attachments => attachments) } end def test_attached_images_should_read_later set_fixtures_attachments_directory a1 = Attachment.find(16) assert_equal "testfile.png", a1.filename assert a1.readable? assert_not a1.visible?(User.anonymous) assert a1.visible?(User.find(2)) a2 = Attachment.find(17) assert_equal "testfile.PNG", a2.filename assert a2.readable? assert_not a2.visible?(User.anonymous) assert a2.visible?(User.find(2)) assert a1.created_on < a2.created_on to_test = { 'Inline image: !testfile.png!' => 'Inline image: ', 'Inline image: !Testfile.PNG!' => 'Inline image: ', } attachments = [a1, a2] to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text, :attachments => attachments) } ensure set_tmp_attachments_directory end def test_textile_external_links to_test = { 'This is a "link":http://foo.bar' => 'This is a link', 'This is an intern "link":/foo/bar' => 'This is an intern link', '"link (Link title)":http://foo.bar' => 'link', '"link (Link title with "double-quotes")":http://foo.bar' => 'link', "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":

\n\n\n\t

Another paragraph", # no multiline link text "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
and another on a second line\":test", # mailto link "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "system administrator", # two exclamation marks '"a link":http://example.net/path!602815048C7B5C20!302.html' => 'a link', # escaping '"test":http://foo"bar' => 'test', # ends with a hyphen '(see "inline link":http://www.foo.bar/Test-)' => '(see inline link)', 'http://foo.bar/page?p=1&t=z&s=-' => 'http://foo.bar/page?p=1&t=z&s=-', 'This is an intern "link":/foo/bar-' => 'This is an intern link', } to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end def test_textile_external_links_with_non_ascii_characters to_test = { %|This is a "link":http://foo.bar/#{@russian_test}| => %|This is a link| } to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end def test_redmine_links 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 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3}, :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)') ext_issue_link = link_to( 'Bug #3', {:controller => 'issues', :action => 'show', :id => 3}, :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)') + ": Error 281 when updating a recipe" note_link = link_to( '#3-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'}, :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)') ext_note_link = link_to( 'Bug #3-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'}, :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)') + ": Error 281 when updating a recipe" note_link2 = link_to( '#3#note-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'}, :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)') ext_note_link2 = link_to( 'Bug #3#note-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'}, :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)') + ": Error 281 when updating a recipe" revision_link = link_to( 'r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 10, :rev => 1}, :class => 'changeset', :title => 'My very first commit do not escaping #<>&') revision_link2 = link_to( 'r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 10, :rev => 2}, :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3') changeset_link2 = link_to( '691322a8eb01e11fd7', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 10, :rev => 1}, :class => 'changeset', :title => 'My very first commit do not escaping #<>&') document_link = link_to( 'Test document', {:controller => 'documents', :action => 'show', :id => 1}, :class => 'document') version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2}, :class => 'version') board_url = {:controller => 'boards', :action => 'show', :id => 2, :project_id => 'ecookbook'} message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4} news_url = {:controller => 'news', :action => 'show', :id => 1} project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'} 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' 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' to_test = { # tickets '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.", # ticket notes '#3-14' => note_link, '#3#note-14' => note_link2, # should not ignore leading zero '#03' => '#03', # tickets with more info '##3, [##3], (##3) and ##3.' => "#{ext_issue_link}, [#{ext_issue_link}], (#{ext_issue_link}) and #{ext_issue_link}.", '##3-14' => ext_note_link, '##3#note-14' => ext_note_link2, '##03' => '##03', # changesets 'r1' => revision_link, 'r1.' => "#{revision_link}.", 'r1, r2' => "#{revision_link}, #{revision_link2}", 'r1,r2' => "#{revision_link},#{revision_link2}", 'commit:691322a8eb01e11fd7' => changeset_link2, # 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'), 'source:/some/file' => link_to('source:/some/file', source_url, :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') + ".", '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@52' => link_to('source:/some/file@52', source_url_with_rev, :class => 'source'), 'source:/some/file@branch' => link_to('source:/some/file@branch', source_url_with_branch, :class => 'source'), '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'), 'export:/some/file@branch' => link_to('export:/some/file@branch', export_url_with_branch, :class => 'source download'), # forum 'forum#2' => link_to('Discussion', board_url, :class => 'board'), 'forum:Discussion' => link_to('Discussion', board_url, :class => 'board'), # message 'message#4' => link_to('Post 2', message_url, :class => 'message'), 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'), # 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'), # 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'), # not found '#0123456789' => '#0123456789', # invalid expressions 'source:' => 'source:', # url hash "http://foo.bar/FAQ#3" => 'http://foo.bar/FAQ#3', # user 'user:jsmith' => link_to_user(User.find_by_id(2)), 'user:JSMITH' => link_to_user(User.find_by_id(2)), 'user#2' => link_to_user(User.find_by_id(2)), '@jsmith' => link_to_user(User.find_by_id(2)), '@JSMITH' => link_to_user(User.find_by_id(2)), '@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)), # invalid user 'user:foobar' => 'user:foobar', } @project = Project.find(1) to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text), "#{text} failed" } end def test_link_to_note_within_the_same_page issue = Issue.find(1) assert_equal '

#note-14

', textilizable('#note-14', :object => issue) journal = Journal.find(2) assert_equal '

#note-2

', textilizable('#note-2', :object => journal) end 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') # user link format: @jsmith@somenet.foo raw = "@jsmith@somenet.foo should not be parsed in jsmith@somenet.foo" assert_match( %r{

#{u.name} should not be parsed in

}, 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{

#{u.name} should not be parsed in

}, 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{

#{u.name} should not be parsed in jsmith@somenet.foo

}, 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{

#{u.name} should not be parsed in jsmith@somenet.foo

}, textilizable(raw, :project => Project.find(1))) end end def test_should_not_parse_redmine_links_inside_link raw = "r1 should not be parsed in http://example.com/url-r1/" assert_match( %r{

r1 should not be parsed in http://example.com/url-r1/

}, textilizable(raw, :project => Project.find(1))) end 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) result1 = link_to("1.4.4", "/versions/#{vp1.id}", :class => "version") result2 = link_to("1.4.4", "/versions/#{vp3.id}", :class => "version") assert_equal "

#{result1} #{result2}

", textilizable("ecookbook:version:1.4.4 version:1.4.4") end 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 "

#{text}

", textilizable("!" + text), "#{text} failed" } end def test_cross_project_redmine_links source_link = link_to('ecookbook:source:/some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 10, :path => ['some', 'file']}, :class => 'source') changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 10, :rev => 2}, :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3') to_test = { # documents 'document:"Test document"' => 'document:"Test document"', 'ecookbook:document:"Test document"' => link_to("Test document", "/documents/1", :class => "document"), 'invalid:document:"Test document"' => 'invalid:document:"Test document"', # versions 'version:"1.0"' => 'version:"1.0"', 'ecookbook:version:"1.0"' => link_to("1.0", "/versions/2", :class => "version"), '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 do |text, result| assert_equal "

#{result}

", textilizable(text), "#{text} failed" end end 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 "

#{link}

", textilizable('version:"Test & Show.txt"') end 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}: <&>", 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}: <&>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 def test_multiple_repositories_redmine_links svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn_repo-1', :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') changeset_link = link_to( 'r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 10, :rev => 2}, :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3') svn_changeset_link = link_to( 'svn_repo-1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn_repo-1', :rev => 123}, :class => 'changeset', :title => '') hg_changeset_link = link_to( 'hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'}, :class => 'changeset', :title => '') source_link = link_to('source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 10, :path => ['some', 'file']}, :class => 'source') 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, 'svn_repo-1|r123' => svn_changeset_link, '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 "

#{result}

", 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') changeset_link = link_to( 'ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 10, :rev => 2}, :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 => '') source_link = link_to('ecookbook:source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 10, :path => ['some', 'file']}, :class => 'source') 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 "

#{result}

", textilizable(text), "#{text} failed" } end def test_redmine_links_git_commit @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') changeset_link = link_to('abcd', { :controller => 'repositories', :action => 'revision', :id => 'subproject1', :repository_id => r.id, :rev => 'abcd', }, :class => 'changeset', :title => 'test commit') to_test = { 'commit:abcd' => changeset_link, } to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end # TODO: Bazaar commit id contains mail address, so it contains '@' and '_'. def test_redmine_links_mercurial_commit @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') changeset_link_rev = link_to( 'r123', { :controller => 'repositories', :action => 'revision', :id => 'subproject1', :repository_id => r.id, :rev => '123', }, :class => 'changeset', :title => 'test commit') changeset_link_commit = link_to( 'abcd', { :controller => 'repositories', :action => 'revision', :id => 'subproject1', :repository_id => r.id, :rev => 'abcd', }, :class => 'changeset', :title => 'test commit') to_test = { 'r123' => changeset_link_rev, 'commit:abcd' => changeset_link_commit, } to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end def test_attachment_links text = 'attachment:error281.txt' result = link_to("error281.txt", "/attachments/1", :class => "attachment") assert_equal "

#{result}

", textilizable(text, :attachments => Issue.find(3).attachments), "#{text} failed" end def test_attachment_link_should_link_to_latest_attachment a1 = Attachment.generate!(:filename => "test.txt", :created_on => 1.hour.ago) a2 = Attachment.generate!(:filename => "test.txt") result = link_to("test.txt", "/attachments/#{a2.id}", :class => "attachment") assert_equal "

#{result}

", textilizable('attachment:test.txt', :attachments => [a1, a2]) end def test_attachment_links_to_images_with_email_format_should_not_be_parsed attachment = Attachment.generate!(:filename => 'image@2x.png') with_settings :text_formatting => 'textile' do raw = "attachment:image@2x.png should not be parsed in image@2x.png" assert_match( %r{

image@2x.png should not be parsed in image@2x.png

}, textilizable(raw, :attachments => [attachment])) end with_settings :text_formatting => 'markdown' do raw = "attachment:image@2x.png should not be parsed in image@2x.png" assert_match( %r{

image@2x.png should not be parsed in image@2x.png

}, textilizable(raw, :attachments => [attachment])) end end def test_wiki_links User.current = User.find_by_login('jsmith') russian_eacape = CGI.escape(@russian_test) to_test = { '[[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"), # title content should be formatted '[[Another page|With _styled_ *title*]]' => link_to("With styled title".html_safe, "/projects/ecookbook/wiki/Another_page", :class => "wiki-page"), '[[Another page|With title containing HTML entities & markups]]' => link_to("With title containing <strong>HTML entities & markups</strong>".html_safe, "/projects/ecookbook/wiki/Another_page", :class => "wiki-page"), # link with anchor '[[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"), # UTF8 anchor "[[Another_page##{@russian_test}|#{@russian_test}]]" => link_to(@russian_test, "/projects/ecookbook/wiki/Another_page##{russian_eacape}", :class => "wiki-page"), # link to anchor '[[#anchor]]' => link_to("#anchor", "#anchor", :class => "wiki-page"), '[[#anchor|One-section]]' => link_to("One-section", "#anchor", :class => "wiki-page"), # page that doesn't exist '[[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"), # link to another project wiki '[[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"), # struck through link '-[[Another page|Page]]-' => "".html_safe + link_to("Page", "/projects/ecookbook/wiki/Another_page", :class => "wiki-page").html_safe + "".html_safe, '-[[Another page|Page]] link-' => "".html_safe + link_to("Page", "/projects/ecookbook/wiki/Another_page", :class => "wiki-page").html_safe + " link".html_safe, # escaping '![[Another page|Page]]' => '[[Another page|Page]]', # project does not exist '[[unknowproject:Start]]' => '[[unknowproject:Start]]', '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]', # missing permission to view wiki in project '[[private-child:]]' => '[[private-child:]]', '[[private-child:Wiki]]' => '[[private-child:Wiki]]', } @project = Project.find(1) to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end def test_wiki_links_with_special_characters_should_work_in_textile to_test = wiki_links_with_special_characters @project = Project.find(1) with_settings :text_formatting => 'textile' do to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end end def test_wiki_links_with_special_characters_should_work_in_markdown to_test = wiki_links_with_special_characters @project = Project.find(1) with_settings :text_formatting => 'markdown' do to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text).strip } end end def test_wiki_links_with_square_brackets_in_project_name User.current = User.find_by_login('jsmith') another_project = Project.find(1) # eCookbook another_project.name = "[foo]#{another_project.name}" another_project.save page = another_project.wiki.find_page('Another page') page.title = "[bar]#{page.title}" page.save to_test = { '[[[foo]eCookbook:]]' => link_to("[foo]eCookbook", "/projects/ecookbook/wiki", :class => "wiki-page"), '[[[foo]eCookbook:CookBook documentation]]' => link_to("CookBook documentation", "/projects/ecookbook/wiki/CookBook_documentation", :class => "wiki-page"), '[[[foo]eCookbook:[bar]Another page]]' => link_to("[bar]Another page", "/projects/ecookbook/wiki/%5Bbar%5DAnother_page", :class => "wiki-page"), '[[[foo]eCookbook:Unknown page]]' => link_to("Unknown page", "/projects/ecookbook/wiki/Unknown_page", :class => "wiki-page new"), '[[[foo]eCookbook:[baz]Unknown page]]' => link_to("[baz]Unknown page", "/projects/ecookbook/wiki/%5Bbaz%5DUnknown_page", :class => "wiki-page new"), } @project = Project.find(2) # OnlineStore with_settings :text_formatting => 'textile' do to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end with_settings :text_formatting => 'markdown' do to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text).strip } end end def test_wiki_links_within_local_file_generation_context to_test = { # link to a page '[[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"), # page that doesn't exist '[[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"), } @project = Project.find(1) to_test.each do |text, result| assert_equal "

#{result}

", textilizable(text, :wiki_links => :local) end end def test_wiki_links_within_wiki_page_context page = WikiPage.find_by_title('Another_page') to_test = { '[[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"), # link to the current page '[[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"), # page that doesn't exist '[[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"), } @project = Project.find(1) to_test.each do |text, result| assert_equal "

#{result}

", textilizable(WikiContent.new(:text => text, :page => page), :text) end end def test_wiki_links_anchor_option_should_prepend_page_title_to_href to_test = { # link to a page '[[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"), # page that doesn't exist '[[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"), } @project = Project.find(1) to_test.each do |text, result| assert_equal "

#{result}

", textilizable(text, :wiki_links => :anchor) end end def test_html_tags to_test = { "
content
" => "

<div>content</div>

", "
content
" => "

<div class=\"bold\">content</div>

", "" => "

<script>some script;</script>

", # do not escape pre/code tags "
\nline 1\nline2
" => "
\nline 1\nline2
", "
\nline 1\nline2
" => "
\nline 1\nline2
", "
content
" => "
<div>content</div>
", "HTML comment: " => "

HTML comment: <!-- no comments -->

", "