2010-03-11 22:55:31 +03:00
# Redmine - project management software
2012-05-05 16:56:53 +04:00
# Copyright (C) 2006-2012 Jean-Philippe Lang
2010-03-11 22:55:31 +03:00
#
# 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.
2011-08-30 09:05:23 +04:00
#
2010-03-11 22:55:31 +03:00
# 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.
2011-08-30 09:05:23 +04:00
#
2010-03-11 22:55:31 +03:00
# 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.
2010-12-13 02:24:34 +03:00
require File . expand_path ( '../../../../../test_helper' , __FILE__ )
2011-11-18 20:25:00 +04:00
require 'digest/md5'
2010-03-11 22:55:31 +03:00
2011-06-22 08:02:27 +04:00
class Redmine :: WikiFormatting :: TextileFormatterTest < ActionView :: TestCase
2010-03-11 22:55:31 +03:00
def setup
@formatter = Redmine :: WikiFormatting :: Textile :: Formatter
end
2011-08-30 09:05:23 +04:00
2010-03-11 22:55:31 +03:00
MODIFIERS = {
" * " = > 'strong' , # bold
" _ " = > 'em' , # italic
" + " = > 'ins' , # underline
" - " = > 'del' , # deleted
" ^ " = > 'sup' , # superscript
" ~ " = > 'sub' # subscript
}
2011-08-30 09:05:23 +04:00
2010-03-12 16:43:11 +03:00
def test_modifiers
2010-03-12 18:17:53 +03:00
assert_html_output (
2010-03-12 16:43:11 +03:00
'*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>' ,
# with class
2010-03-12 18:17:53 +03:00
'*(foo)two words*' = > '<strong class="foo">two words</strong>'
)
2010-03-12 16:43:11 +03:00
end
2011-08-30 09:05:23 +04:00
2010-03-11 22:55:31 +03:00
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 } "
2010-03-12 18:19:49 +03:00
html = " < #{ tag2 } >< #{ tag1 } >Phrase modifiers</ #{ tag1 } ></ #{ tag2 } > "
2010-03-12 18:17:53 +03:00
assert_html_output text = > html
2010-03-11 22:55:31 +03:00
end
end
end
2011-08-30 09:05:23 +04:00
2012-02-12 18:00:43 +04:00
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>' ,
2012-02-18 02:31:01 +04:00
'p{color:"}. text' = > '<p>p{color:"}. text</p>' ,
2012-02-12 18:00:43 +04:00
} , false )
end
2010-03-12 18:12:23 +03:00
def test_inline_code
2010-03-12 18:17:53 +03:00
assert_html_output (
2010-03-12 18:12:23 +03:00
'this is @some code@' = > 'this is <code>some code</code>' ,
2010-03-12 18:17:53 +03:00
'@<Location /redmine>@' = > '<code><Location /redmine></code>'
)
2010-03-12 18:12:23 +03:00
end
2011-03-12 21:28:30 +03:00
2012-04-18 16:00:54 +04:00
def test_nested_lists
raw = <<-RAW
# Item 1
# Item 2
** Item 2 a
** Item 2 b
# Item 3
** Item 3 a
RAW
expected = <<-EXPECTED
< ol >
< li > Item 1 < / li>
< li > Item 2
< ul >
< li > Item 2 a < / li>
< li > Item 2 b < / li>
< / ul>
< / li>
< li > Item 3
< ul >
< li > Item 3 a < / li>
< / ul>
< / li>
< / ol>
EXPECTED
assert_equal expected . gsub ( %r{ \ s+ } , '' ) , to_html ( raw ) . gsub ( %r{ \ s+ } , '' )
end
2010-03-12 18:12:23 +03:00
def test_escaping
2010-03-12 18:17:53 +03:00
assert_html_output (
'this is a <script>' = > 'this is a <script>'
)
end
2011-03-12 21:28:30 +03:00
def test_use_of_backslashes_followed_by_numbers_in_headers
assert_html_output ( {
'h1. 2009\02\09' = > '<h1>2009\02\09</h1>'
} , false )
end
2011-08-30 09:05:23 +04:00
2010-03-20 15:54:23 +03:00
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
2011-08-30 09:05:23 +04:00
2010-11-06 16:29:23 +03:00
def test_acronyms
assert_html_output (
'this is an acronym: GPL(General Public License)' = > 'this is an acronym: <acronym title="General Public License">GPL</acronym>' ,
'2 letters JP(Jean-Philippe) acronym' = > '2 letters <acronym title="Jean-Philippe">JP</acronym> acronym' ,
'GPL(This is a double-quoted "title")' = > '<acronym title="This is a double-quoted "title"">GPL</acronym>'
)
end
2011-08-30 09:05:23 +04:00
2011-03-14 23:29:06 +03:00
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
2011-08-30 09:05:23 +04:00
2011-03-14 23:29:06 +03:00
# expected html
expected = <<-EXPECTED
< p > John said : < / p>
< blockquote >
2011-03-14 23:35:36 +03:00
Lorem ipsum dolor sit amet , consectetuer adipiscing elit . Maecenas sed libero . < br / >
2011-03-14 23:29:06 +03:00
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
2011-08-30 09:05:23 +04:00
2011-03-14 23:29:06 +03:00
assert_equal expected . gsub ( %r{ \ s+ } , '' ) , to_html ( raw ) . gsub ( %r{ \ s+ } , '' )
end
2011-08-30 09:05:23 +04:00
2011-03-14 23:29:06 +03:00
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
2011-08-30 09:05:23 +04:00
2011-03-14 23:29:06 +03:00
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
2011-08-30 09:05:23 +04:00
2011-03-14 23:29:06 +03:00
def test_textile_should_not_mangle_brackets
assert_equal '<p>[msg1][msg2]</p>' , to_html ( '[msg1][msg2]' )
end
2011-08-30 09:05:23 +04:00
2011-11-02 11:35:51 +04:00
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
2011-11-18 20:25:00 +04:00
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
2011-11-29 01:45:02 +04:00
< pre > < code class = \ " ruby \" >
def foo
end
< / code>< / pre >
2011-11-29 02:08:15 +04:00
< pre > < code > < pre > < code class = \ " ruby \" >
Place your code here .
< / code>< / pre >
< / code>< / pre >
2011-11-18 20:25:00 +04:00
Morbi facilisis accumsan orci non pharetra .
< pre >
Pre Content :
h2 . Inside pre
2011-11-28 21:56:50 +04:00
< tag > inside pre block
2011-11-18 20:25:00 +04:00
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
2011-11-02 11:35:51 +04:00
2011-11-28 21:56:50 +04:00
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
2012-06-11 21:51:14 +04:00
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
2010-03-12 18:17:53 +03:00
private
2011-08-30 09:05:23 +04:00
2011-03-12 21:28:30 +03:00
def assert_html_output ( to_test , expect_paragraph = true )
2010-03-12 18:12:23 +03:00
to_test . each do | text , expected |
2011-03-12 21:28:30 +03:00
assert_equal ( ( expect_paragraph ? " <p> #{ expected } </p> " : expected ) , @formatter . new ( text ) . to_html , " Formatting the following text failed: \n === \n #{ text } \n === \n " )
2010-03-12 18:12:23 +03:00
end
end
2011-08-30 09:05:23 +04:00
2011-03-14 23:29:06 +03:00
def to_html ( text )
@formatter . new ( text ) . to_html
end
2011-11-18 20:25:00 +04:00
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
2010-03-11 22:55:31 +03:00
end