Redmine 3.4.4
This commit is contained in:
commit
64924a6376
2112 changed files with 259028 additions and 0 deletions
35
test/unit/lib/redmine/wiki_formatting/html_parser_test.rb
Normal file
35
test/unit/lib/redmine/wiki_formatting/html_parser_test.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 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 Redmine::WikiFormatting::HtmlParserTest < ActiveSupport::TestCase
|
||||
|
||||
def setup
|
||||
@parser = Redmine::WikiFormatting::HtmlParser
|
||||
end
|
||||
|
||||
def test_convert_line_breaks
|
||||
assert_equal "A html snippet with\na new line.",
|
||||
@parser.to_text('<p>A html snippet with<br>a new line.</p>')
|
||||
end
|
||||
|
||||
def test_should_remove_style_tags_from_body
|
||||
assert_equal "Text",
|
||||
@parser.to_text('<html><body><style>body {font-size: 0.8em;}</style>Text</body></html>')
|
||||
end
|
||||
end
|
404
test/unit/lib/redmine/wiki_formatting/macros_test.rb
Normal file
404
test/unit/lib/redmine/wiki_formatting/macros_test.rb
Normal file
|
@ -0,0 +1,404 @@
|
|||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 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 Redmine::WikiFormatting::MacrosTest < Redmine::HelperTest
|
||||
include ApplicationHelper
|
||||
include ActionView::Helpers::TextHelper
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
include ERB::Util
|
||||
include Rails.application.routes.url_helpers
|
||||
extend ActionView::Helpers::SanitizeHelper::ClassMethods
|
||||
|
||||
fixtures :projects, :roles, :enabled_modules, :users,
|
||||
:repositories, :changesets,
|
||||
:trackers, :issue_statuses, :issues,
|
||||
:versions, :documents,
|
||||
:wikis, :wiki_pages, :wiki_contents,
|
||||
:boards, :messages,
|
||||
:attachments
|
||||
|
||||
def setup
|
||||
super
|
||||
@project = nil
|
||||
end
|
||||
|
||||
def teardown
|
||||
end
|
||||
|
||||
def test_macro_registration
|
||||
Redmine::WikiFormatting::Macros.register do
|
||||
macro :foo do |obj, args|
|
||||
"Foo: #{args.size} (#{args.join(',')}) (#{args.class.name})"
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal '<p>Foo: 0 () (Array)</p>', textilizable("{{foo}}")
|
||||
assert_equal '<p>Foo: 0 () (Array)</p>', textilizable("{{foo()}}")
|
||||
assert_equal '<p>Foo: 1 (arg1) (Array)</p>', textilizable("{{foo(arg1)}}")
|
||||
assert_equal '<p>Foo: 2 (arg1,arg2) (Array)</p>', textilizable("{{foo(arg1, arg2)}}")
|
||||
end
|
||||
|
||||
def test_macro_registration_parse_args_set_to_false_should_disable_arguments_parsing
|
||||
Redmine::WikiFormatting::Macros.register do
|
||||
macro :bar, :parse_args => false do |obj, args|
|
||||
"Bar: (#{args}) (#{args.class.name})"
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal '<p>Bar: (args, more args) (String)</p>', textilizable("{{bar(args, more args)}}")
|
||||
assert_equal '<p>Bar: () (String)</p>', textilizable("{{bar}}")
|
||||
assert_equal '<p>Bar: () (String)</p>', textilizable("{{bar()}}")
|
||||
end
|
||||
|
||||
def test_macro_registration_with_3_args_should_receive_text_argument
|
||||
Redmine::WikiFormatting::Macros.register do
|
||||
macro :baz do |obj, args, text|
|
||||
"Baz: (#{args.join(',')}) (#{text.class.name}) (#{text})"
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal "<p>Baz: () (NilClass) ()</p>", textilizable("{{baz}}")
|
||||
assert_equal "<p>Baz: () (NilClass) ()</p>", textilizable("{{baz()}}")
|
||||
assert_equal "<p>Baz: () (String) (line1\nline2)</p>", textilizable("{{baz()\nline1\nline2\n}}")
|
||||
assert_equal "<p>Baz: (arg1,arg2) (String) (line1\nline2)</p>", textilizable("{{baz(arg1, arg2)\nline1\nline2\n}}")
|
||||
end
|
||||
|
||||
def test_macro_name_with_upper_case
|
||||
Redmine::WikiFormatting::Macros.macro(:UpperCase) {|obj, args| "Upper"}
|
||||
|
||||
assert_equal "<p>Upper</p>", textilizable("{{UpperCase}}")
|
||||
end
|
||||
|
||||
def test_multiple_macros_on_the_same_line
|
||||
Redmine::WikiFormatting::Macros.macro :foo do |obj, args|
|
||||
args.any? ? "args: #{args.join(',')}" : "no args"
|
||||
end
|
||||
|
||||
assert_equal '<p>no args no args</p>', textilizable("{{foo}} {{foo}}")
|
||||
assert_equal '<p>args: a,b no args</p>', textilizable("{{foo(a,b)}} {{foo}}")
|
||||
assert_equal '<p>args: a,b args: c,d</p>', textilizable("{{foo(a,b)}} {{foo(c,d)}}")
|
||||
assert_equal '<p>no args args: c,d</p>', textilizable("{{foo}} {{foo(c,d)}}")
|
||||
end
|
||||
|
||||
def test_macro_should_receive_the_object_as_argument_when_with_object_and_attribute
|
||||
issue = Issue.find(1)
|
||||
issue.description = "{{hello_world}}"
|
||||
assert_equal '<p>Hello world! Object: Issue, Called with no argument and no block of text.</p>', textilizable(issue, :description)
|
||||
end
|
||||
|
||||
def test_macro_should_receive_the_object_as_argument_when_called_with_object_option
|
||||
text = "{{hello_world}}"
|
||||
assert_equal '<p>Hello world! Object: Issue, Called with no argument and no block of text.</p>', textilizable(text, :object => Issue.find(1))
|
||||
end
|
||||
|
||||
def test_extract_macro_options_should_with_args
|
||||
options = extract_macro_options(["arg1", "arg2"], :foo, :size)
|
||||
assert_equal([["arg1", "arg2"], {}], options)
|
||||
end
|
||||
|
||||
def test_extract_macro_options_should_with_options
|
||||
options = extract_macro_options(["foo=bar", "size=2"], :foo, :size)
|
||||
assert_equal([[], {:foo => "bar", :size => "2"}], options)
|
||||
end
|
||||
|
||||
def test_extract_macro_options_should_with_args_and_options
|
||||
options = extract_macro_options(["arg1", "arg2", "foo=bar", "size=2"], :foo, :size)
|
||||
assert_equal([["arg1", "arg2"], {:foo => "bar", :size => "2"}], options)
|
||||
end
|
||||
|
||||
def test_extract_macro_options_should_parse_options_lazily
|
||||
options = extract_macro_options(["params=x=1&y=2"], :params)
|
||||
assert_equal([[], {:params => "x=1&y=2"}], options)
|
||||
end
|
||||
|
||||
def test_macro_exception_should_be_displayed
|
||||
Redmine::WikiFormatting::Macros.macro :exception do |obj, args|
|
||||
raise "My message"
|
||||
end
|
||||
|
||||
text = "{{exception}}"
|
||||
assert_include '<div class="flash error">Error executing the <strong>exception</strong> macro (My message)</div>', textilizable(text)
|
||||
end
|
||||
|
||||
def test_macro_arguments_should_not_be_parsed_by_formatters
|
||||
text = '{{hello_world(http://www.redmine.org, #1)}}'
|
||||
assert_include 'Arguments: http://www.redmine.org, #1', textilizable(text)
|
||||
end
|
||||
|
||||
def test_exclamation_mark_should_not_run_macros
|
||||
text = "!{{hello_world}}"
|
||||
assert_equal '<p>{{hello_world}}</p>', textilizable(text)
|
||||
end
|
||||
|
||||
def test_exclamation_mark_should_escape_macros
|
||||
text = "!{{hello_world(<tag>)}}"
|
||||
assert_equal '<p>{{hello_world(<tag>)}}</p>', textilizable(text)
|
||||
end
|
||||
|
||||
def test_unknown_macros_should_not_be_replaced
|
||||
text = "{{unknown}}"
|
||||
assert_equal '<p>{{unknown}}</p>', textilizable(text)
|
||||
end
|
||||
|
||||
def test_unknown_macros_should_parsed_as_text
|
||||
text = "{{unknown(*test*)}}"
|
||||
assert_equal '<p>{{unknown(<strong>test</strong>)}}</p>', textilizable(text)
|
||||
end
|
||||
|
||||
def test_unknown_macros_should_be_escaped
|
||||
text = "{{unknown(<tag>)}}"
|
||||
assert_equal '<p>{{unknown(<tag>)}}</p>', textilizable(text)
|
||||
end
|
||||
|
||||
def test_html_safe_macro_output_should_not_be_escaped
|
||||
Redmine::WikiFormatting::Macros.macro :safe_macro do |obj, args|
|
||||
"<tag>".html_safe
|
||||
end
|
||||
assert_equal '<p><tag></p>', textilizable("{{safe_macro}}")
|
||||
end
|
||||
|
||||
def test_macro_hello_world
|
||||
text = "{{hello_world}}"
|
||||
assert textilizable(text).match(/Hello world!/)
|
||||
end
|
||||
|
||||
def test_macro_hello_world_should_escape_arguments
|
||||
text = "{{hello_world(<tag>)}}"
|
||||
assert_include 'Arguments: <tag>', textilizable(text)
|
||||
end
|
||||
|
||||
def test_macro_macro_list
|
||||
text = "{{macro_list}}"
|
||||
assert_match %r{<code>hello_world</code>}, textilizable(text)
|
||||
end
|
||||
|
||||
def test_macro_include
|
||||
@project = Project.find(1)
|
||||
# include a page of the current project wiki
|
||||
text = "{{include(Another page)}}"
|
||||
assert_include 'This is a link to a ticket', textilizable(text)
|
||||
|
||||
@project = nil
|
||||
# include a page of a specific project wiki
|
||||
text = "{{include(ecookbook:Another page)}}"
|
||||
assert_include 'This is a link to a ticket', textilizable(text)
|
||||
|
||||
text = "{{include(ecookbook:)}}"
|
||||
assert_include 'CookBook documentation', textilizable(text)
|
||||
|
||||
text = "{{include(unknowidentifier:somepage)}}"
|
||||
assert_include 'Page not found', textilizable(text)
|
||||
end
|
||||
|
||||
def test_macro_collapse
|
||||
text = "{{collapse\n*Collapsed* block of text\n}}"
|
||||
with_locale 'en' do
|
||||
result = textilizable(text)
|
||||
|
||||
assert_select_in result, 'div.collapsed-text'
|
||||
assert_select_in result, 'strong', :text => 'Collapsed'
|
||||
assert_select_in result, 'a.collapsible.collapsed', :text => 'Show'
|
||||
assert_select_in result, 'a.collapsible', :text => 'Hide'
|
||||
end
|
||||
end
|
||||
|
||||
def test_macro_collapse_with_one_arg
|
||||
text = "{{collapse(Example)\n*Collapsed* block of text\n}}"
|
||||
result = textilizable(text)
|
||||
|
||||
assert_select_in result, 'div.collapsed-text'
|
||||
assert_select_in result, 'strong', :text => 'Collapsed'
|
||||
assert_select_in result, 'a.collapsible.collapsed', :text => 'Example'
|
||||
assert_select_in result, 'a.collapsible', :text => 'Example'
|
||||
end
|
||||
|
||||
def test_macro_collapse_with_two_args
|
||||
text = "{{collapse(Show example, Hide example)\n*Collapsed* block of text\n}}"
|
||||
result = textilizable(text)
|
||||
|
||||
assert_select_in result, 'div.collapsed-text'
|
||||
assert_select_in result, 'strong', :text => 'Collapsed'
|
||||
assert_select_in result, 'a.collapsible.collapsed', :text => 'Show example'
|
||||
assert_select_in result, 'a.collapsible', :text => 'Hide example'
|
||||
end
|
||||
|
||||
def test_macro_collapse_should_not_break_toc
|
||||
set_language_if_valid 'en'
|
||||
|
||||
text = <<-RAW
|
||||
{{toc}}
|
||||
|
||||
h1. Title
|
||||
|
||||
{{collapse(Show example, Hide example)
|
||||
h2. Heading
|
||||
}}"
|
||||
RAW
|
||||
|
||||
expected_toc = '<ul class="toc"><li><strong>Table of contents</strong></li><li><a href="#Title">Title</a><ul><li><a href="#Heading">Heading</a></li></ul></li></ul>'
|
||||
|
||||
assert_include expected_toc, textilizable(text).gsub(/[\r\n]/, '')
|
||||
end
|
||||
|
||||
def test_macro_child_pages
|
||||
expected = "<p><ul class=\"pages-hierarchy\">\n" +
|
||||
"<li><a href=\"/projects/ecookbook/wiki/Child_1\">Child 1</a>\n" +
|
||||
"<ul class=\"pages-hierarchy\">\n<li><a href=\"/projects/ecookbook/wiki/Child_1_1\">Child 1 1</a></li>\n</ul>\n</li>\n" +
|
||||
"<li><a href=\"/projects/ecookbook/wiki/Child_2\">Child 2</a></li>\n" +
|
||||
"</ul>\n</p>"
|
||||
|
||||
@project = Project.find(1)
|
||||
# child pages of the current wiki page
|
||||
assert_equal expected, textilizable("{{child_pages}}", :object => WikiPage.find(2).content)
|
||||
# child pages of another page
|
||||
assert_equal expected, textilizable("{{child_pages(Another_page)}}", :object => WikiPage.find(1).content)
|
||||
|
||||
@project = Project.find(2)
|
||||
assert_equal expected, textilizable("{{child_pages(ecookbook:Another_page)}}", :object => WikiPage.find(1).content)
|
||||
end
|
||||
|
||||
def test_macro_child_pages_with_parent_option
|
||||
expected = "<p><ul class=\"pages-hierarchy\">\n" +
|
||||
"<li><a href=\"/projects/ecookbook/wiki/Another_page\">Another page</a>\n" +
|
||||
"<ul class=\"pages-hierarchy\">\n" +
|
||||
"<li><a href=\"/projects/ecookbook/wiki/Child_1\">Child 1</a>\n" +
|
||||
"<ul class=\"pages-hierarchy\">\n<li><a href=\"/projects/ecookbook/wiki/Child_1_1\">Child 1 1</a></li>\n</ul>\n</li>\n" +
|
||||
"<li><a href=\"/projects/ecookbook/wiki/Child_2\">Child 2</a></li>\n" +
|
||||
"</ul>\n</li>\n</ul>\n</p>"
|
||||
|
||||
@project = Project.find(1)
|
||||
# child pages of the current wiki page
|
||||
assert_equal expected, textilizable("{{child_pages(parent=1)}}", :object => WikiPage.find(2).content)
|
||||
# child pages of another page
|
||||
assert_equal expected, textilizable("{{child_pages(Another_page, parent=1)}}", :object => WikiPage.find(1).content)
|
||||
|
||||
@project = Project.find(2)
|
||||
assert_equal expected, textilizable("{{child_pages(ecookbook:Another_page, parent=1)}}", :object => WikiPage.find(1).content)
|
||||
end
|
||||
|
||||
def test_macro_child_pages_with_depth_option
|
||||
expected = "<p><ul class=\"pages-hierarchy\">\n" +
|
||||
"<li><a href=\"/projects/ecookbook/wiki/Child_1\">Child 1</a></li>\n" +
|
||||
"<li><a href=\"/projects/ecookbook/wiki/Child_2\">Child 2</a></li>\n" +
|
||||
"</ul>\n</p>"
|
||||
|
||||
@project = Project.find(1)
|
||||
assert_equal expected, textilizable("{{child_pages(depth=1)}}", :object => WikiPage.find(2).content)
|
||||
end
|
||||
|
||||
def test_macro_child_pages_without_wiki_page_should_fail
|
||||
assert_match /can be called from wiki pages only/, textilizable("{{child_pages}}")
|
||||
end
|
||||
|
||||
def test_macro_thumbnail
|
||||
link = link_to('<img alt="testfile.PNG" src="/attachments/thumbnail/17" />'.html_safe,
|
||||
"/attachments/17",
|
||||
:class => "thumbnail",
|
||||
:title => "testfile.PNG")
|
||||
assert_equal "<p>#{link}</p>",
|
||||
textilizable("{{thumbnail(testfile.png)}}", :object => Issue.find(14))
|
||||
end
|
||||
|
||||
def test_macro_thumbnail_with_full_path
|
||||
link = link_to('<img alt="testfile.PNG" src="http://test.host/attachments/thumbnail/17" />'.html_safe,
|
||||
"http://test.host/attachments/17",
|
||||
:class => "thumbnail",
|
||||
:title => "testfile.PNG")
|
||||
assert_equal "<p>#{link}</p>",
|
||||
textilizable("{{thumbnail(testfile.png)}}", :object => Issue.find(14), :only_path => false)
|
||||
end
|
||||
|
||||
def test_macro_thumbnail_with_size
|
||||
link = link_to('<img alt="testfile.PNG" src="/attachments/thumbnail/17/200" />'.html_safe,
|
||||
"/attachments/17",
|
||||
:class => "thumbnail",
|
||||
:title => "testfile.PNG")
|
||||
assert_equal "<p>#{link}</p>",
|
||||
textilizable("{{thumbnail(testfile.png, size=200)}}", :object => Issue.find(14))
|
||||
end
|
||||
|
||||
def test_macro_thumbnail_with_title
|
||||
link = link_to('<img alt="testfile.PNG" src="/attachments/thumbnail/17" />'.html_safe,
|
||||
"/attachments/17",
|
||||
:class => "thumbnail",
|
||||
:title => "Cool image")
|
||||
assert_equal "<p>#{link}</p>",
|
||||
textilizable("{{thumbnail(testfile.png, title=Cool image)}}", :object => Issue.find(14))
|
||||
end
|
||||
|
||||
def test_macro_thumbnail_with_invalid_filename_should_fail
|
||||
assert_include 'test.png not found',
|
||||
textilizable("{{thumbnail(test.png)}}", :object => Issue.find(14))
|
||||
end
|
||||
|
||||
def test_macros_should_not_be_executed_in_pre_tags
|
||||
text = <<-RAW
|
||||
{{hello_world(foo)}}
|
||||
|
||||
<pre>
|
||||
{{hello_world(pre)}}
|
||||
!{{hello_world(pre)}}
|
||||
</pre>
|
||||
|
||||
{{hello_world(bar)}}
|
||||
RAW
|
||||
|
||||
expected = <<-EXPECTED
|
||||
<p>Hello world! Object: NilClass, Arguments: foo and no block of text.</p>
|
||||
|
||||
<pre>
|
||||
{{hello_world(pre)}}
|
||||
!{{hello_world(pre)}}
|
||||
</pre>
|
||||
|
||||
<p>Hello world! Object: NilClass, Arguments: bar and no block of text.</p>
|
||||
EXPECTED
|
||||
|
||||
assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(text).gsub(%r{[\r\n\t]}, '')
|
||||
end
|
||||
|
||||
def test_macros_should_be_escaped_in_pre_tags
|
||||
text = "<pre>{{hello_world(<tag>)}}</pre>"
|
||||
assert_equal "<pre>{{hello_world(<tag>)}}</pre>", textilizable(text)
|
||||
end
|
||||
|
||||
def test_macros_should_not_mangle_next_macros_outputs
|
||||
text = '{{macro(2)}} !{{macro(2)}} {{hello_world(foo)}}'
|
||||
assert_equal '<p>{{macro(2)}} {{macro(2)}} Hello world! Object: NilClass, Arguments: foo and no block of text.</p>', textilizable(text)
|
||||
end
|
||||
|
||||
def test_macros_with_text_should_not_mangle_following_macros
|
||||
text = <<-RAW
|
||||
{{hello_world
|
||||
Line of text
|
||||
}}
|
||||
|
||||
{{hello_world
|
||||
Another line of text
|
||||
}}
|
||||
RAW
|
||||
|
||||
expected = <<-EXPECTED
|
||||
<p>Hello world! Object: NilClass, Called with no argument and a 12 bytes long block of text.</p>
|
||||
<p>Hello world! Object: NilClass, Called with no argument and a 20 bytes long block of text.</p>
|
||||
EXPECTED
|
||||
|
||||
assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(text).gsub(%r{[\r\n\t]}, '')
|
||||
end
|
||||
end
|
|
@ -0,0 +1,93 @@
|
|||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 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 Redmine::WikiFormatting::MarkdownFormatterTest < ActionView::TestCase
|
||||
if Object.const_defined?(:Redcarpet)
|
||||
|
||||
def setup
|
||||
@formatter = Redmine::WikiFormatting::Markdown::Formatter
|
||||
end
|
||||
|
||||
def test_syntax_error_in_image_reference_should_not_raise_exception
|
||||
assert @formatter.new("!>[](foo.png)").to_html
|
||||
end
|
||||
|
||||
def test_empty_image_should_not_raise_exception
|
||||
assert @formatter.new("![]()").to_html
|
||||
end
|
||||
|
||||
# re-using the formatter after getting above error crashes the
|
||||
# ruby interpreter. This seems to be related to
|
||||
# https://github.com/vmg/redcarpet/issues/318
|
||||
def test_should_not_crash_redcarpet_after_syntax_error
|
||||
@formatter.new("!>[](foo.png)").to_html rescue nil
|
||||
assert @formatter.new("").to_html.present?
|
||||
end
|
||||
|
||||
def test_inline_style
|
||||
assert_equal "<p><strong>foo</strong></p>", @formatter.new("**foo**").to_html.strip
|
||||
end
|
||||
|
||||
def test_not_set_intra_emphasis
|
||||
assert_equal "<p>foo_bar_baz</p>", @formatter.new("foo_bar_baz").to_html.strip
|
||||
end
|
||||
|
||||
def test_wiki_links_should_be_preserved
|
||||
text = 'This is a wiki link: [[Foo]]'
|
||||
assert_include '[[Foo]]', @formatter.new(text).to_html
|
||||
end
|
||||
|
||||
def test_redmine_links_with_double_quotes_should_be_preserved
|
||||
text = 'This is a redmine link: version:"1.0"'
|
||||
assert_include 'version:"1.0"', @formatter.new(text).to_html
|
||||
end
|
||||
|
||||
def test_should_support_syntax_highligth
|
||||
text = <<-STR
|
||||
~~~ruby
|
||||
def foo
|
||||
end
|
||||
~~~
|
||||
STR
|
||||
assert_select_in @formatter.new(text).to_html, 'pre code.ruby.syntaxhl' do
|
||||
assert_select 'span.keyword', :text => 'def'
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_allow_invalid_language_for_code_blocks
|
||||
text = <<-STR
|
||||
~~~foo
|
||||
test
|
||||
~~~
|
||||
STR
|
||||
assert_equal "<pre>test\n</pre>", @formatter.new(text).to_html
|
||||
end
|
||||
|
||||
def test_external_links_should_have_external_css_class
|
||||
text = 'This is a [link](http://example.net/)'
|
||||
assert_equal '<p>This is a <a href="http://example.net/" class="external">link</a></p>', @formatter.new(text).to_html.strip
|
||||
end
|
||||
|
||||
def test_locals_links_should_not_have_external_css_class
|
||||
text = 'This is a [link](/issues)'
|
||||
assert_equal '<p>This is a <a href="/issues">link</a></p>', @formatter.new(text).to_html.strip
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 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 Redmine::WikiFormatting::MarkdownHtmlParserTest < ActiveSupport::TestCase
|
||||
|
||||
def setup
|
||||
@parser = Redmine::WikiFormatting::Markdown::HtmlParser
|
||||
end
|
||||
|
||||
def test_should_convert_tags
|
||||
assert_equal 'A **simple** html snippet.',
|
||||
@parser.to_text('<p>A <b>simple</b> html snippet.</p>')
|
||||
end
|
||||
end
|
600
test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb
Normal file
600
test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb
Normal file
|
@ -0,0 +1,600 @@
|
|||
#encoding: utf-8
|
||||
#
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 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__)
|
||||
require 'digest/md5'
|
||||
|
||||
class Redmine::WikiFormatting::TextileFormatterTest < ActionView::TestCase
|
||||
|
||||
def setup
|
||||
@formatter = Redmine::WikiFormatting::Textile::Formatter
|
||||
end
|
||||
|
||||
MODIFIERS = {
|
||||
"*" => 'strong', # bold
|
||||
"_" => 'em', # italic
|
||||
"+" => 'ins', # underline
|
||||
"-" => 'del', # deleted
|
||||
"^" => 'sup', # superscript
|
||||
"~" => 'sub' # subscript
|
||||
}
|
||||
|
||||
def test_modifiers
|
||||
assert_html_output(
|
||||
'*bold*' => '<strong>bold</strong>',
|
||||
'before *bold*' => 'before <strong>bold</strong>',
|
||||
'*bold* after' => '<strong>bold</strong> after',
|
||||
'*two words*' => '<strong>two words</strong>',
|
||||
'*two*words*' => '<strong>two*words</strong>',
|
||||
'*two * words*' => '<strong>two * words</strong>',
|
||||
'*two* *words*' => '<strong>two</strong> <strong>words</strong>',
|
||||
'*(two)* *(words)*' => '<strong>(two)</strong> <strong>(words)</strong>'
|
||||
)
|
||||
end
|
||||
|
||||
def test_modifiers_combination
|
||||
MODIFIERS.each do |m1, tag1|
|
||||
MODIFIERS.each do |m2, tag2|
|
||||
next if m1 == m2
|
||||
text = "#{m2}#{m1}Phrase modifiers#{m1}#{m2}"
|
||||
html = "<#{tag2}><#{tag1}>Phrase modifiers</#{tag1}></#{tag2}>"
|
||||
assert_html_output text => html
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_modifier_should_work_with_one_non_ascii_character
|
||||
assert_html_output "*Ä*" => "<strong>Ä</strong>"
|
||||
end
|
||||
|
||||
def test_styles
|
||||
# single style
|
||||
assert_html_output({
|
||||
'p{color:red}. text' => '<p style="color:red;">text</p>',
|
||||
'p{color:red;}. text' => '<p style="color:red;">text</p>',
|
||||
'p{color: red}. text' => '<p style="color: red;">text</p>',
|
||||
'p{color:#f00}. text' => '<p style="color:#f00;">text</p>',
|
||||
'p{color:#ff0000}. text' => '<p style="color:#ff0000;">text</p>',
|
||||
'p{border:10px}. text' => '<p style="border:10px;">text</p>',
|
||||
'p{border:10}. text' => '<p style="border:10;">text</p>',
|
||||
'p{border:10%}. text' => '<p style="border:10%;">text</p>',
|
||||
'p{border:10em}. text' => '<p style="border:10em;">text</p>',
|
||||
'p{border:1.5em}. text' => '<p style="border:1.5em;">text</p>',
|
||||
'p{border-left:1px}. text' => '<p style="border-left:1px;">text</p>',
|
||||
'p{border-right:1px}. text' => '<p style="border-right:1px;">text</p>',
|
||||
'p{border-top:1px}. text' => '<p style="border-top:1px;">text</p>',
|
||||
'p{border-bottom:1px}. text' => '<p style="border-bottom:1px;">text</p>',
|
||||
}, false)
|
||||
|
||||
# multiple styles
|
||||
assert_html_output({
|
||||
'p{color:red; border-top:1px}. text' => '<p style="color:red;border-top:1px;">text</p>',
|
||||
'p{color:red ; border-top:1px}. text' => '<p style="color:red;border-top:1px;">text</p>',
|
||||
'p{color:red;border-top:1px}. text' => '<p style="color:red;border-top:1px;">text</p>',
|
||||
}, false)
|
||||
|
||||
# styles with multiple values
|
||||
assert_html_output({
|
||||
'p{border:1px solid red;}. text' => '<p style="border:1px solid red;">text</p>',
|
||||
'p{border-top-left-radius: 10px 5px;}. text' => '<p style="border-top-left-radius: 10px 5px;">text</p>',
|
||||
}, false)
|
||||
end
|
||||
|
||||
def test_invalid_styles_should_be_filtered
|
||||
assert_html_output({
|
||||
'p{invalid}. text' => '<p>text</p>',
|
||||
'p{invalid:red}. text' => '<p>text</p>',
|
||||
'p{color:(red)}. text' => '<p>text</p>',
|
||||
'p{color:red;invalid:blue}. text' => '<p style="color:red;">text</p>',
|
||||
'p{invalid:blue;color:red}. text' => '<p style="color:red;">text</p>',
|
||||
'p{color:"}. text' => '<p>p{color:"}. text</p>',
|
||||
}, false)
|
||||
end
|
||||
|
||||
def test_inline_code
|
||||
assert_html_output(
|
||||
'this is @some code@' => 'this is <code>some code</code>',
|
||||
'@<Location /redmine>@' => '<code><Location /redmine></code>'
|
||||
)
|
||||
end
|
||||
|
||||
def test_lang_attribute
|
||||
assert_html_output(
|
||||
'*[fr]French*' => '<strong lang="fr">French</strong>',
|
||||
'*[fr-fr]French*' => '<strong lang="fr-fr">French</strong>',
|
||||
'*[fr_fr]French*' => '<strong lang="fr_fr">French</strong>'
|
||||
)
|
||||
end
|
||||
|
||||
def test_lang_attribute_should_ignore_invalid_value
|
||||
assert_html_output(
|
||||
'*[fr3]French*' => '<strong>[fr3]French</strong>'
|
||||
)
|
||||
end
|
||||
|
||||
def test_nested_lists
|
||||
raw = <<-RAW
|
||||
# Item 1
|
||||
# Item 2
|
||||
** Item 2a
|
||||
** Item 2b
|
||||
# Item 3
|
||||
** Item 3a
|
||||
RAW
|
||||
|
||||
expected = <<-EXPECTED
|
||||
<ol>
|
||||
<li>Item 1</li>
|
||||
<li>Item 2
|
||||
<ul>
|
||||
<li>Item 2a</li>
|
||||
<li>Item 2b</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Item 3
|
||||
<ul>
|
||||
<li>Item 3a</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
EXPECTED
|
||||
|
||||
assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
|
||||
end
|
||||
|
||||
def test_escaping
|
||||
assert_html_output(
|
||||
'this is a <script>' => 'this is a <script>'
|
||||
)
|
||||
end
|
||||
|
||||
def test_kbd
|
||||
assert_html_output({
|
||||
'<kbd>test</kbd>' => '<kbd>test</kbd>'
|
||||
}, false)
|
||||
end
|
||||
|
||||
def test_use_of_backslashes_followed_by_numbers_in_headers
|
||||
assert_html_output({
|
||||
'h1. 2009\02\09' => '<h1>2009\02\09</h1>'
|
||||
}, false)
|
||||
end
|
||||
|
||||
def test_double_dashes_should_not_strikethrough
|
||||
assert_html_output(
|
||||
'double -- dashes -- test' => 'double -- dashes -- test',
|
||||
'double -- *dashes* -- test' => 'double -- <strong>dashes</strong> -- test'
|
||||
)
|
||||
end
|
||||
|
||||
def test_abbreviations
|
||||
assert_html_output(
|
||||
'this is an abbreviation: GPL(General Public License)' => 'this is an abbreviation: <abbr title="General Public License">GPL</abbr>',
|
||||
'2 letters JP(Jean-Philippe) abbreviation' => '2 letters <abbr title="Jean-Philippe">JP</abbr> abbreviation',
|
||||
'GPL(This is a double-quoted "title")' => '<abbr title="This is a double-quoted "title"">GPL</abbr>'
|
||||
)
|
||||
end
|
||||
|
||||
def test_blockquote
|
||||
# orig raw text
|
||||
raw = <<-RAW
|
||||
John said:
|
||||
> Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
|
||||
> Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
|
||||
> * Donec odio lorem,
|
||||
> * sagittis ac,
|
||||
> * malesuada in,
|
||||
> * adipiscing eu, dolor.
|
||||
>
|
||||
> >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
|
||||
> Proin a tellus. Nam vel neque.
|
||||
|
||||
He's right.
|
||||
RAW
|
||||
|
||||
# expected html
|
||||
expected = <<-EXPECTED
|
||||
<p>John said:</p>
|
||||
<blockquote>
|
||||
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.<br />
|
||||
Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
|
||||
<ul>
|
||||
<li>Donec odio lorem,</li>
|
||||
<li>sagittis ac,</li>
|
||||
<li>malesuada in,</li>
|
||||
<li>adipiscing eu, dolor.</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
|
||||
</blockquote>
|
||||
<p>Proin a tellus. Nam vel neque.</p>
|
||||
</blockquote>
|
||||
<p>He's right.</p>
|
||||
EXPECTED
|
||||
|
||||
assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
|
||||
end
|
||||
|
||||
def test_table
|
||||
raw = <<-RAW
|
||||
This is a table with empty cells:
|
||||
|
||||
|cell11|cell12||
|
||||
|cell21||cell23|
|
||||
|cell31|cell32|cell33|
|
||||
RAW
|
||||
|
||||
expected = <<-EXPECTED
|
||||
<p>This is a table with empty cells:</p>
|
||||
|
||||
<table>
|
||||
<tr><td>cell11</td><td>cell12</td><td></td></tr>
|
||||
<tr><td>cell21</td><td></td><td>cell23</td></tr>
|
||||
<tr><td>cell31</td><td>cell32</td><td>cell33</td></tr>
|
||||
</table>
|
||||
EXPECTED
|
||||
|
||||
assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
|
||||
end
|
||||
|
||||
def test_table_with_alignment
|
||||
raw = <<-RAW
|
||||
|>. right|
|
||||
|<. left|
|
||||
|<>. justify|
|
||||
RAW
|
||||
|
||||
expected = <<-EXPECTED
|
||||
<table>
|
||||
<tr><td style="text-align:right;">right</td></tr>
|
||||
<tr><td style="text-align:left;">left</td></tr>
|
||||
<tr><td style="text-align:justify;">justify</td></tr>
|
||||
</table>
|
||||
EXPECTED
|
||||
|
||||
assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
|
||||
end
|
||||
|
||||
def test_table_with_trailing_whitespace
|
||||
raw = <<-RAW
|
||||
This is a table with trailing whitespace in one row:
|
||||
|
||||
|cell11|cell12|
|
||||
|cell21|cell22|
|
||||
|cell31|cell32|
|
||||
RAW
|
||||
|
||||
expected = <<-EXPECTED
|
||||
<p>This is a table with trailing whitespace in one row:</p>
|
||||
|
||||
<table>
|
||||
<tr><td>cell11</td><td>cell12</td></tr>
|
||||
<tr><td>cell21</td><td>cell22</td></tr>
|
||||
<tr><td>cell31</td><td>cell32</td></tr>
|
||||
</table>
|
||||
EXPECTED
|
||||
|
||||
assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
|
||||
end
|
||||
|
||||
def test_table_with_line_breaks
|
||||
raw = <<-RAW
|
||||
This is a table with line breaks:
|
||||
|
||||
|cell11
|
||||
continued|cell12||
|
||||
|-cell21-||cell23
|
||||
cell23 line2
|
||||
cell23 *line3*|
|
||||
|cell31|cell32
|
||||
cell32 line2|cell33|
|
||||
|
||||
RAW
|
||||
|
||||
expected = <<-EXPECTED
|
||||
<p>This is a table with line breaks:</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>cell11<br />continued</td>
|
||||
<td>cell12</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><del>cell21</del></td>
|
||||
<td></td>
|
||||
<td>cell23<br/>cell23 line2<br/>cell23 <strong>line3</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>cell31</td>
|
||||
<td>cell32<br/>cell32 line2</td>
|
||||
<td>cell33</td>
|
||||
</tr>
|
||||
</table>
|
||||
EXPECTED
|
||||
|
||||
assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
|
||||
end
|
||||
|
||||
def test_tables_with_lists
|
||||
raw = <<-RAW
|
||||
This is a table with lists:
|
||||
|
||||
|cell11|cell12|
|
||||
|cell21|ordered list
|
||||
# item
|
||||
# item 2|
|
||||
|cell31|unordered list
|
||||
* item
|
||||
* item 2|
|
||||
|
||||
RAW
|
||||
|
||||
expected = <<-EXPECTED
|
||||
<p>This is a table with lists:</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>cell11</td>
|
||||
<td>cell12</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>cell21</td>
|
||||
<td>ordered list<br /># item<br /># item 2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>cell31</td>
|
||||
<td>unordered list<br />* item<br />* item 2</td>
|
||||
</tr>
|
||||
</table>
|
||||
EXPECTED
|
||||
|
||||
assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
|
||||
end
|
||||
|
||||
def test_textile_should_not_mangle_brackets
|
||||
assert_equal '<p>[msg1][msg2]</p>', to_html('[msg1][msg2]')
|
||||
end
|
||||
|
||||
def test_textile_should_escape_image_urls
|
||||
# this is onclick="alert('XSS');" in encoded form
|
||||
raw = '!/images/comment.png"onclick=alert('XSS');"!'
|
||||
expected = '<p><img src="/images/comment.png"onclick=&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x27;&#x58;&#x53;&#x53;&#x27;&#x29;;&#x22;" alt="" /></p>'
|
||||
assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
|
||||
end
|
||||
|
||||
|
||||
STR_WITHOUT_PRE = [
|
||||
# 0
|
||||
"h1. Title
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.",
|
||||
# 1
|
||||
"h2. Heading 2
|
||||
|
||||
Maecenas sed elit sit amet mi accumsan vestibulum non nec velit. Proin porta tincidunt lorem, consequat rhoncus dolor fermentum in.
|
||||
|
||||
Cras ipsum felis, ultrices at porttitor vel, faucibus eu nunc.",
|
||||
# 2
|
||||
"h2. Heading 2
|
||||
|
||||
Morbi facilisis accumsan orci non pharetra.
|
||||
|
||||
h3. Heading 3
|
||||
|
||||
Nulla nunc nisi, egestas in ornare vel, posuere ac libero.",
|
||||
# 3
|
||||
"h3. Heading 3
|
||||
|
||||
Praesent eget turpis nibh, a lacinia nulla.",
|
||||
# 4
|
||||
"h2. Heading 2
|
||||
|
||||
Ut rhoncus elementum adipiscing."]
|
||||
|
||||
TEXT_WITHOUT_PRE = STR_WITHOUT_PRE.join("\n\n").freeze
|
||||
|
||||
def test_get_section_should_return_the_requested_section_and_its_hash
|
||||
assert_section_with_hash STR_WITHOUT_PRE[1], TEXT_WITHOUT_PRE, 2
|
||||
assert_section_with_hash STR_WITHOUT_PRE[2..3].join("\n\n"), TEXT_WITHOUT_PRE, 3
|
||||
assert_section_with_hash STR_WITHOUT_PRE[3], TEXT_WITHOUT_PRE, 5
|
||||
assert_section_with_hash STR_WITHOUT_PRE[4], TEXT_WITHOUT_PRE, 6
|
||||
|
||||
assert_section_with_hash '', TEXT_WITHOUT_PRE, 0
|
||||
assert_section_with_hash '', TEXT_WITHOUT_PRE, 10
|
||||
end
|
||||
|
||||
def test_update_section_should_update_the_requested_section
|
||||
replacement = "New text"
|
||||
|
||||
assert_equal [STR_WITHOUT_PRE[0], replacement, STR_WITHOUT_PRE[2..4]].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(2, replacement)
|
||||
assert_equal [STR_WITHOUT_PRE[0..1], replacement, STR_WITHOUT_PRE[4]].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(3, replacement)
|
||||
assert_equal [STR_WITHOUT_PRE[0..2], replacement, STR_WITHOUT_PRE[4]].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(5, replacement)
|
||||
assert_equal [STR_WITHOUT_PRE[0..3], replacement].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(6, replacement)
|
||||
|
||||
assert_equal TEXT_WITHOUT_PRE, @formatter.new(TEXT_WITHOUT_PRE).update_section(0, replacement)
|
||||
assert_equal TEXT_WITHOUT_PRE, @formatter.new(TEXT_WITHOUT_PRE).update_section(10, replacement)
|
||||
end
|
||||
|
||||
def test_update_section_with_hash_should_update_the_requested_section
|
||||
replacement = "New text"
|
||||
|
||||
assert_equal [STR_WITHOUT_PRE[0], replacement, STR_WITHOUT_PRE[2..4]].flatten.join("\n\n"),
|
||||
@formatter.new(TEXT_WITHOUT_PRE).update_section(2, replacement, Digest::MD5.hexdigest(STR_WITHOUT_PRE[1]))
|
||||
end
|
||||
|
||||
def test_update_section_with_wrong_hash_should_raise_an_error
|
||||
assert_raise Redmine::WikiFormatting::StaleSectionError do
|
||||
@formatter.new(TEXT_WITHOUT_PRE).update_section(2, "New text", Digest::MD5.hexdigest("Old text"))
|
||||
end
|
||||
end
|
||||
|
||||
STR_WITH_PRE = [
|
||||
# 0
|
||||
"h1. Title
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.",
|
||||
# 1
|
||||
"h2. Heading 2
|
||||
|
||||
<pre><code class=\"ruby\">
|
||||
def foo
|
||||
end
|
||||
</code></pre>
|
||||
|
||||
<pre><code><pre><code class=\"ruby\">
|
||||
Place your code here.
|
||||
</code></pre>
|
||||
</code></pre>
|
||||
|
||||
Morbi facilisis accumsan orci non pharetra.
|
||||
|
||||
<pre>
|
||||
Pre Content:
|
||||
|
||||
h2. Inside pre
|
||||
|
||||
<tag> inside pre block
|
||||
|
||||
Morbi facilisis accumsan orci non pharetra.
|
||||
</pre>",
|
||||
# 2
|
||||
"h3. Heading 3
|
||||
|
||||
Nulla nunc nisi, egestas in ornare vel, posuere ac libero."]
|
||||
|
||||
def test_get_section_should_ignore_pre_content
|
||||
text = STR_WITH_PRE.join("\n\n")
|
||||
|
||||
assert_section_with_hash STR_WITH_PRE[1..2].join("\n\n"), text, 2
|
||||
assert_section_with_hash STR_WITH_PRE[2], text, 3
|
||||
end
|
||||
|
||||
def test_update_section_should_not_escape_pre_content_outside_section
|
||||
text = STR_WITH_PRE.join("\n\n")
|
||||
replacement = "New text"
|
||||
|
||||
assert_equal [STR_WITH_PRE[0..1], "New text"].flatten.join("\n\n"),
|
||||
@formatter.new(text).update_section(3, replacement)
|
||||
end
|
||||
|
||||
def test_get_section_should_support_lines_with_spaces_before_heading
|
||||
# the lines after Content 2 and Heading 4 contain a space
|
||||
text = <<-STR
|
||||
h1. Heading 1
|
||||
|
||||
Content 1
|
||||
|
||||
h1. Heading 2
|
||||
|
||||
Content 2
|
||||
|
||||
h1. Heading 3
|
||||
|
||||
Content 3
|
||||
|
||||
h1. Heading 4
|
||||
|
||||
Content 4
|
||||
STR
|
||||
|
||||
[1, 2, 3, 4].each do |index|
|
||||
assert_match /\Ah1. Heading #{index}.+Content #{index}/m, @formatter.new(text).get_section(index).first
|
||||
end
|
||||
end
|
||||
|
||||
def test_get_section_should_support_headings_starting_with_a_tab
|
||||
text = <<-STR
|
||||
h1.\tHeading 1
|
||||
|
||||
Content 1
|
||||
|
||||
h1. Heading 2
|
||||
|
||||
Content 2
|
||||
STR
|
||||
|
||||
assert_match /\Ah1.\tHeading 1\s+Content 1\z/, @formatter.new(text).get_section(1).first
|
||||
end
|
||||
|
||||
def test_should_not_allow_arbitrary_class_attribute_on_offtags
|
||||
%w(code pre kbd).each do |tag|
|
||||
assert_html_output({"<#{tag} class=\"foo\">test</#{tag}>" => "<#{tag}>test</#{tag}>"}, false)
|
||||
end
|
||||
|
||||
assert_html_output({"<notextile class=\"foo\">test</notextile>" => "test"}, false)
|
||||
end
|
||||
|
||||
def test_should_allow_valid_language_class_attribute_on_code_tags
|
||||
assert_html_output({"<code class=\"ruby\">test</code>" => "<code class=\"ruby syntaxhl\"><span class=\"CodeRay\">test</span></code>"}, false)
|
||||
end
|
||||
|
||||
def test_should_not_allow_valid_language_class_attribute_on_non_code_offtags
|
||||
%w(pre kbd).each do |tag|
|
||||
assert_html_output({"<#{tag} class=\"ruby\">test</#{tag}>" => "<#{tag}>test</#{tag}>"}, false)
|
||||
end
|
||||
|
||||
assert_html_output({"<notextile class=\"ruby\">test</notextile>" => "test"}, false)
|
||||
end
|
||||
|
||||
def test_should_prefix_class_attribute_on_tags
|
||||
assert_html_output({
|
||||
'!(foo)test.png!' => "<p><img src=\"test.png\" class=\"wiki-class-foo\" alt=\"\" /></p>",
|
||||
'%(foo)test%' => "<p><span class=\"wiki-class-foo\">test</span></p>",
|
||||
'p(foo). test' => "<p class=\"wiki-class-foo\">test</p>",
|
||||
'|(foo). test|' => "<table>\n\t\t<tr>\n\t\t\t<td class=\"wiki-class-foo\">test</td>\n\t\t</tr>\n\t</table>",
|
||||
}, false)
|
||||
end
|
||||
|
||||
def test_should_prefix_id_attribute_on_tags
|
||||
assert_html_output({
|
||||
'!(#foo)test.png!' => "<p><img src=\"test.png\" id=\"wiki-id-foo\" alt=\"\" /></p>",
|
||||
'%(#foo)test%' => "<p><span id=\"wiki-id-foo\">test</span></p>",
|
||||
'p(#foo). test' => "<p id=\"wiki-id-foo\">test</p>",
|
||||
'|(#foo). test|' => "<table>\n\t\t<tr>\n\t\t\t<td id=\"wiki-id-foo\">test</td>\n\t\t</tr>\n\t</table>",
|
||||
}, false)
|
||||
end
|
||||
|
||||
def test_should_not_prefix_class_and_id_attributes_already_prefixed
|
||||
assert_html_output({
|
||||
'!(wiki-class-foo#wiki-id-bar)test.png!' => "<p><img src=\"test.png\" class=\"wiki-class-foo\" id=\"wiki-id-bar\" alt=\"\" /></p>",
|
||||
}, false)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_html_output(to_test, expect_paragraph = true)
|
||||
to_test.each do |text, expected|
|
||||
assert_equal(( expect_paragraph ? "<p>#{expected}</p>" : expected ), @formatter.new(text).to_html, "Formatting the following text failed:\n===\n#{text}\n===\n")
|
||||
end
|
||||
end
|
||||
|
||||
def to_html(text)
|
||||
@formatter.new(text).to_html
|
||||
end
|
||||
|
||||
def assert_section_with_hash(expected, text, index)
|
||||
result = @formatter.new(text).get_section(index)
|
||||
|
||||
assert_kind_of Array, result
|
||||
assert_equal 2, result.size
|
||||
assert_equal expected, result.first, "section content did not match"
|
||||
assert_equal Digest::MD5.hexdigest(expected), result.last, "section hash did not match"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2017 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 Redmine::WikiFormatting::TextileHtmlParserTest < ActiveSupport::TestCase
|
||||
|
||||
def setup
|
||||
@parser = Redmine::WikiFormatting::Textile::HtmlParser
|
||||
end
|
||||
|
||||
def test_should_convert_tags
|
||||
assert_equal 'A *simple* html snippet.',
|
||||
@parser.to_text('<p>A <b>simple</b> html snippet.</p>')
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue