"Fossies" - the Fresh Open Source Software Archive

Member "redmine-4.1.1/test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb" (6 Apr 2020, 21036 Bytes) of package /linux/www/redmine-4.1.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Ruby source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "textile_formatter_test.rb": 4.1.0_vs_4.1.1.

    1 # frozen_string_literal: true
    2 
    3 #
    4 # Redmine - project management software
    5 # Copyright (C) 2006-2019  Jean-Philippe Lang
    6 #
    7 # This program is free software; you can redistribute it and/or
    8 # modify it under the terms of the GNU General Public License
    9 # as published by the Free Software Foundation; either version 2
   10 # of the License, or (at your option) any later version.
   11 #
   12 # This program is distributed in the hope that it will be useful,
   13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
   14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15 # GNU General Public License for more details.
   16 #
   17 # You should have received a copy of the GNU General Public License
   18 # along with this program; if not, write to the Free Software
   19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   20 
   21 require File.expand_path('../../../../../test_helper', __FILE__)
   22 require 'digest/md5'
   23 
   24 class Redmine::WikiFormatting::TextileFormatterTest < ActionView::TestCase
   25 
   26   def setup
   27     @formatter = Redmine::WikiFormatting::Textile::Formatter
   28   end
   29 
   30   MODIFIERS = {
   31     "*" => 'strong', # bold
   32     "_" => 'em',     # italic
   33     "+" => 'ins',    # underline
   34     "-" => 'del',    # deleted
   35     "^" => 'sup',    # superscript
   36     "~" => 'sub'     # subscript
   37   }
   38 
   39   def test_modifiers
   40     assert_html_output(
   41       '*bold*'                => '<strong>bold</strong>',
   42       'before *bold*'         => 'before <strong>bold</strong>',
   43       '*bold* after'          => '<strong>bold</strong> after',
   44       '*two words*'           => '<strong>two words</strong>',
   45       '*two*words*'           => '<strong>two*words</strong>',
   46       '*two * words*'         => '<strong>two * words</strong>',
   47       '*two* *words*'         => '<strong>two</strong> <strong>words</strong>',
   48       '*(two)* *(words)*'     => '<strong>(two)</strong> <strong>(words)</strong>'
   49     )
   50   end
   51 
   52   def test_modifiers_combination
   53     MODIFIERS.each do |m1, tag1|
   54       MODIFIERS.each do |m2, tag2|
   55         next if m1 == m2
   56         text = "#{m2}#{m1}Phrase modifiers#{m1}#{m2}"
   57         html = "<#{tag2}><#{tag1}>Phrase modifiers</#{tag1}></#{tag2}>"
   58         assert_html_output text => html
   59       end
   60     end
   61   end
   62 
   63   def test_modifier_should_work_with_one_non_ascii_character
   64     assert_html_output "*Ä*" => "<strong>Ä</strong>"
   65   end
   66 
   67   def test_styles
   68     # single style
   69     assert_html_output({
   70       'p{color:red}. text'           => '<p style="color:red;">text</p>',
   71       'p{color:red;}. text'          => '<p style="color:red;">text</p>',
   72       'p{color: red}. text'          => '<p style="color: red;">text</p>',
   73       'p{color:#f00}. text'          => '<p style="color:#f00;">text</p>',
   74       'p{color:#ff0000}. text'       => '<p style="color:#ff0000;">text</p>',
   75       'p{border:10px}. text'         => '<p style="border:10px;">text</p>',
   76       'p{border:10}. text'           => '<p style="border:10;">text</p>',
   77       'p{border:10%}. text'          => '<p style="border:10%;">text</p>',
   78       'p{border:10em}. text'         => '<p style="border:10em;">text</p>',
   79       'p{border:1.5em}. text'        => '<p style="border:1.5em;">text</p>',
   80       'p{border-left:1px}. text'     => '<p style="border-left:1px;">text</p>',
   81       'p{border-right:1px}. text'    => '<p style="border-right:1px;">text</p>',
   82       'p{border-top:1px}. text'      => '<p style="border-top:1px;">text</p>',
   83       'p{border-bottom:1px}. text'   => '<p style="border-bottom:1px;">text</p>',
   84       'p{width:50px}. text'          => '<p style="width:50px;">text</p>',
   85       'p{max-width:100px}. text'     => '<p style="max-width:100px;">text</p>',
   86       'p{height:40px}. text'         => '<p style="height:40px;">text</p>',
   87       'p{max-height:80px}. text'     => '<p style="max-height:80px;">text</p>',
   88       }, false)
   89 
   90     # multiple styles
   91     assert_html_output({
   92       'p{color:red; border-top:1px}. text'   => '<p style="color:red;border-top:1px;">text</p>',
   93       'p{color:red ; border-top:1px}. text'  => '<p style="color:red;border-top:1px;">text</p>',
   94       'p{color:red;border-top:1px}. text'    => '<p style="color:red;border-top:1px;">text</p>',
   95       }, false)
   96 
   97     # styles with multiple values
   98     assert_html_output({
   99       'p{border:1px solid red;}. text'             => '<p style="border:1px solid red;">text</p>',
  100       'p{border-top-left-radius: 10px 5px;}. text' => '<p style="border-top-left-radius: 10px 5px;">text</p>',
  101       }, false)
  102   end
  103 
  104   def test_invalid_styles_should_be_filtered
  105     assert_html_output({
  106       'p{invalid}. text'                     => '<p>text</p>',
  107       'p{invalid:red}. text'                 => '<p>text</p>',
  108       'p{color:(red)}. text'                 => '<p>text</p>',
  109       'p{color:red;invalid:blue}. text'      => '<p style="color:red;">text</p>',
  110       'p{invalid:blue;color:red}. text'      => '<p style="color:red;">text</p>',
  111       'p{color:"}. text'                     => '<p>p{color:"}. text</p>',
  112       }, false)
  113   end
  114 
  115   def test_inline_code
  116     assert_html_output(
  117       'this is @some code@'      => 'this is <code>some code</code>',
  118       '@<Location /redmine>@'    => '<code>&lt;Location /redmine&gt;</code>'
  119     )
  120   end
  121 
  122   def test_lang_attribute
  123     assert_html_output(
  124       '*[fr]French*'      => '<strong lang="fr">French</strong>',
  125       '*[fr-fr]French*'   => '<strong lang="fr-fr">French</strong>',
  126       '*[fr_fr]French*'   => '<strong lang="fr_fr">French</strong>'
  127     )
  128   end
  129 
  130   def test_lang_attribute_should_ignore_invalid_value
  131     assert_html_output(
  132       '*[fr3]French*'      => '<strong>[fr3]French</strong>'
  133     )
  134   end
  135 
  136   def test_nested_lists
  137     raw = <<-RAW
  138 # Item 1
  139 # Item 2
  140 ** Item 2a
  141 ** Item 2b
  142 # Item 3
  143 ** Item 3a
  144 RAW
  145 
  146     expected = <<-EXPECTED
  147 <ol>
  148   <li>Item 1</li>
  149   <li>Item 2
  150     <ul>
  151       <li>Item 2a</li>
  152       <li>Item 2b</li>
  153     </ul>
  154   </li>
  155   <li>Item 3
  156     <ul>
  157       <li>Item 3a</li>
  158     </ul>
  159   </li>
  160 </ol>
  161 EXPECTED
  162 
  163     assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
  164 
  165     raw = <<~RAW
  166       * Item-1
  167 
  168         * Item-1a
  169         * Item-1b
  170     RAW
  171     expected = <<~EXPECTED
  172       <ul>
  173         <li>Item-1
  174           <ul>
  175             <li>Item-1a</li>
  176             <li>Item-1b</li>
  177           </ul>
  178         </li>
  179       </ul>
  180     EXPECTED
  181     assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
  182   end
  183 
  184   def test_escaping
  185     assert_html_output(
  186       'this is a <script>'      => 'this is a &lt;script&gt;'
  187     )
  188   end
  189 
  190   def test_kbd
  191     assert_html_output({
  192       '<kbd>test</kbd>'         => '<kbd>test</kbd>'
  193     }, false)
  194   end
  195 
  196   def test_use_of_backslashes_followed_by_numbers_in_headers
  197     assert_html_output({
  198       'h1. 2009\02\09'      => '<h1>2009\02\09</h1>'
  199     }, false)
  200   end
  201 
  202   def test_double_dashes_should_not_strikethrough
  203     assert_html_output(
  204       'double -- dashes -- test'  => 'double -- dashes -- test',
  205       'double -- *dashes* -- test'  => 'double -- <strong>dashes</strong> -- test'
  206     )
  207   end
  208 
  209   def test_abbreviations
  210     assert_html_output(
  211       'this is an abbreviation: GPL(General Public License)' => 'this is an abbreviation: <abbr title="General Public License">GPL</abbr>',
  212       '2 letters JP(Jean-Philippe) abbreviation' => '2 letters <abbr title="Jean-Philippe">JP</abbr> abbreviation',
  213       'GPL(This is a double-quoted "title")' => '<abbr title="This is a double-quoted &quot;title&quot;">GPL</abbr>'
  214     )
  215   end
  216 
  217   def test_blockquote
  218     # orig raw text
  219     raw = <<-RAW
  220 John said:
  221 > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
  222 > Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
  223 > * Donec odio lorem,
  224 > * sagittis ac,
  225 > * malesuada in,
  226 > * adipiscing eu, dolor.
  227 >
  228 > >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
  229 > Proin a tellus. Nam vel neque.
  230 
  231 He's right.
  232 RAW
  233 
  234     # expected html
  235     expected = <<-EXPECTED
  236 <p>John said:</p>
  237 <blockquote>
  238 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.<br />
  239 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
  240 <ul>
  241   <li>Donec odio lorem,</li>
  242   <li>sagittis ac,</li>
  243   <li>malesuada in,</li>
  244   <li>adipiscing eu, dolor.</li>
  245 </ul>
  246 <blockquote>
  247 <p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
  248 </blockquote>
  249 <p>Proin a tellus. Nam vel neque.</p>
  250 </blockquote>
  251 <p>He's right.</p>
  252 EXPECTED
  253 
  254     assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
  255   end
  256 
  257   def test_table
  258     raw = <<-RAW
  259 This is a table with empty cells:
  260 
  261 |cell11|cell12||
  262 |cell21||cell23|
  263 |cell31|cell32|cell33|
  264 RAW
  265 
  266     expected = <<-EXPECTED
  267 <p>This is a table with empty cells:</p>
  268 
  269 <table>
  270   <tr><td>cell11</td><td>cell12</td><td></td></tr>
  271   <tr><td>cell21</td><td></td><td>cell23</td></tr>
  272   <tr><td>cell31</td><td>cell32</td><td>cell33</td></tr>
  273 </table>
  274 EXPECTED
  275 
  276     assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
  277   end
  278 
  279   def test_table_with_alignment
  280     raw = <<-RAW
  281 |>. right|
  282 |<. left|
  283 |<>. justify|
  284 RAW
  285 
  286     expected = <<-EXPECTED
  287 <table>
  288   <tr><td style="text-align:right;">right</td></tr>
  289   <tr><td style="text-align:left;">left</td></tr>
  290   <tr><td style="text-align:justify;">justify</td></tr>
  291 </table>
  292 EXPECTED
  293 
  294     assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
  295   end
  296 
  297   def test_table_with_trailing_whitespace
  298     raw = <<-RAW
  299 This is a table with trailing whitespace in one row:
  300 
  301 |cell11|cell12|
  302 |cell21|cell22|
  303 |cell31|cell32|
  304 RAW
  305 
  306     expected = <<-EXPECTED
  307 <p>This is a table with trailing whitespace in one row:</p>
  308 
  309 <table>
  310   <tr><td>cell11</td><td>cell12</td></tr>
  311   <tr><td>cell21</td><td>cell22</td></tr>
  312   <tr><td>cell31</td><td>cell32</td></tr>
  313 </table>
  314 EXPECTED
  315 
  316     assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
  317   end
  318 
  319   def test_table_with_line_breaks
  320     raw = <<-RAW
  321 This is a table with line breaks:
  322 
  323 |cell11
  324 continued|cell12||
  325 |-cell21-||cell23
  326 cell23 line2
  327 cell23 *line3*|
  328 |cell31|cell32
  329 cell32 line2|cell33|
  330 
  331 RAW
  332 
  333     expected = <<-EXPECTED
  334 <p>This is a table with line breaks:</p>
  335 
  336 <table>
  337   <tr>
  338     <td>cell11<br />continued</td>
  339     <td>cell12</td>
  340     <td></td>
  341   </tr>
  342   <tr>
  343     <td><del>cell21</del></td>
  344     <td></td>
  345     <td>cell23<br/>cell23 line2<br/>cell23 <strong>line3</strong></td>
  346   </tr>
  347   <tr>
  348     <td>cell31</td>
  349     <td>cell32<br/>cell32 line2</td>
  350     <td>cell33</td>
  351   </tr>
  352 </table>
  353 EXPECTED
  354 
  355     assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
  356   end
  357 
  358   def test_tables_with_lists
  359     raw = <<-RAW
  360 This is a table with lists:
  361 
  362 |cell11|cell12|
  363 |cell21|ordered list
  364 # item
  365 # item 2|
  366 |cell31|unordered list
  367 * item
  368 * item 2|
  369 
  370 RAW
  371 
  372     expected = <<-EXPECTED
  373 <p>This is a table with lists:</p>
  374 
  375 <table>
  376   <tr>
  377     <td>cell11</td>
  378     <td>cell12</td>
  379   </tr>
  380   <tr>
  381     <td>cell21</td>
  382     <td>ordered list<br /># item<br /># item 2</td>
  383   </tr>
  384   <tr>
  385     <td>cell31</td>
  386     <td>unordered list<br />* item<br />* item 2</td>
  387   </tr>
  388 </table>
  389 EXPECTED
  390 
  391     assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
  392   end
  393 
  394   def test_textile_should_not_mangle_brackets
  395     assert_equal '<p>[msg1][msg2]</p>', to_html('[msg1][msg2]')
  396   end
  397 
  398   def test_textile_should_escape_image_urls
  399     # this is onclick="alert('XSS');" in encoded form
  400     raw = '!/images/comment.png"onclick=&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x27;&#x58;&#x53;&#x53;&#x27;&#x29;;&#x22;!'
  401     expected = '<p><img src="/images/comment.png&quot;onclick=&amp;#x61;&amp;#x6c;&amp;#x65;&amp;#x72;&amp;#x74;&amp;#x28;&amp;#x27;&amp;#x58;&amp;#x53;&amp;#x53;&amp;#x27;&amp;#x29;;&amp;#x22;" alt="" /></p>'
  402     assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
  403   end
  404 
  405   STR_WITHOUT_PRE = [
  406     # 0
  407     <<~STR.chomp,
  408       h1. Title
  409 
  410       Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
  411     STR
  412     # 1
  413     <<~STR.chomp,
  414       h2. Heading 2
  415 
  416       Maecenas sed elit sit amet mi accumsan vestibulum non nec velit. Proin porta tincidunt lorem, consequat rhoncus dolor fermentum in.
  417 
  418       Cras ipsum felis, ultrices at porttitor vel, faucibus eu nunc.
  419     STR
  420     # 2
  421     <<~STR.chomp,
  422       h2. Heading 2
  423 
  424       Morbi facilisis accumsan orci non pharetra.
  425 
  426       h3. Heading 3
  427 
  428       Nulla nunc nisi, egestas in ornare vel, posuere ac libero.
  429     STR
  430     # 3
  431     <<~STR.chomp,
  432       h3. Heading 3
  433 
  434       Praesent eget turpis nibh, a lacinia nulla.
  435     STR
  436     # 4
  437     <<~STR.chomp,
  438       h2. Heading 2
  439 
  440       Ut rhoncus elementum adipiscing.
  441     STR
  442   ]
  443   TEXT_WITHOUT_PRE = STR_WITHOUT_PRE.join("\n\n").freeze
  444 
  445   def test_get_section_should_return_the_requested_section_and_its_hash
  446     assert_section_with_hash STR_WITHOUT_PRE[1], TEXT_WITHOUT_PRE, 2
  447     assert_section_with_hash STR_WITHOUT_PRE[2..3].join("\n\n"), TEXT_WITHOUT_PRE, 3
  448     assert_section_with_hash STR_WITHOUT_PRE[3], TEXT_WITHOUT_PRE, 5
  449     assert_section_with_hash STR_WITHOUT_PRE[4], TEXT_WITHOUT_PRE, 6
  450 
  451     assert_section_with_hash '', TEXT_WITHOUT_PRE, 0
  452     assert_section_with_hash '', TEXT_WITHOUT_PRE, 10
  453   end
  454 
  455   def test_update_section_should_update_the_requested_section
  456     replacement = "New text"
  457 
  458     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)
  459     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)
  460     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)
  461     assert_equal [STR_WITHOUT_PRE[0..3], replacement].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(6, replacement)
  462 
  463     assert_equal TEXT_WITHOUT_PRE, @formatter.new(TEXT_WITHOUT_PRE).update_section(0, replacement)
  464     assert_equal TEXT_WITHOUT_PRE, @formatter.new(TEXT_WITHOUT_PRE).update_section(10, replacement)
  465   end
  466 
  467   def test_update_section_with_hash_should_update_the_requested_section
  468     replacement = "New text"
  469 
  470     assert_equal [STR_WITHOUT_PRE[0], replacement, STR_WITHOUT_PRE[2..4]].flatten.join("\n\n"),
  471       @formatter.new(TEXT_WITHOUT_PRE).update_section(2, replacement, Digest::MD5.hexdigest(STR_WITHOUT_PRE[1]))
  472   end
  473 
  474   def test_update_section_with_wrong_hash_should_raise_an_error
  475     assert_raise Redmine::WikiFormatting::StaleSectionError do
  476       @formatter.new(TEXT_WITHOUT_PRE).update_section(2, "New text", Digest::MD5.hexdigest("Old text"))
  477     end
  478   end
  479 
  480   STR_WITH_PRE = [
  481     # 0
  482     <<~STR.chomp,
  483       h1. Title
  484 
  485       Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
  486     STR
  487     # 1
  488     <<~STR.chomp,
  489       h2. Heading 2
  490 
  491       <pre><code class=\"ruby\">
  492         def foo
  493         end
  494       </code></pre>
  495 
  496       <pre><code><pre><code class=\"ruby\">
  497         Place your code here.
  498       </code></pre>
  499       </code></pre>
  500 
  501       Morbi facilisis accumsan orci non pharetra.
  502 
  503       <pre>
  504       Pre Content:
  505 
  506       h2. Inside pre
  507 
  508       <tag> inside pre block
  509 
  510       Morbi facilisis accumsan orci non pharetra.
  511       </pre>
  512     STR
  513     # 2
  514     <<~STR.chomp,
  515       h3. Heading 3
  516 
  517       Nulla nunc nisi, egestas in ornare vel, posuere ac libero.
  518     STR
  519   ]
  520 
  521   def test_get_section_should_ignore_pre_content
  522     text = STR_WITH_PRE.join("\n\n")
  523 
  524     assert_section_with_hash STR_WITH_PRE[1..2].join("\n\n"), text, 2
  525     assert_section_with_hash STR_WITH_PRE[2], text, 3
  526   end
  527 
  528   def test_update_section_should_not_escape_pre_content_outside_section
  529     text = STR_WITH_PRE.join("\n\n")
  530     replacement = "New text"
  531 
  532     assert_equal [STR_WITH_PRE[0..1], "New text"].flatten.join("\n\n"),
  533       @formatter.new(text).update_section(3, replacement)
  534   end
  535 
  536   def test_get_section_should_support_lines_with_spaces_before_heading
  537     # the lines after Content 2 and Heading 4 contain a space
  538     text = <<-STR
  539 h1. Heading 1
  540 
  541 Content 1
  542 
  543 h1. Heading 2
  544 
  545 Content 2
  546 
  547 h1. Heading 3
  548 
  549 Content 3
  550 
  551 h1. Heading 4
  552 
  553 Content 4
  554 STR
  555 
  556     [1, 2, 3, 4].each do |index|
  557       assert_match /\Ah1. Heading #{index}.+Content #{index}/m, @formatter.new(text).get_section(index).first
  558     end
  559   end
  560 
  561   def test_get_section_should_support_headings_starting_with_a_tab
  562     text = <<-STR
  563 h1.\tHeading 1
  564 
  565 Content 1
  566 
  567 h1. Heading 2
  568 
  569 Content 2
  570 STR
  571 
  572     assert_match /\Ah1.\tHeading 1\s+Content 1\z/, @formatter.new(text).get_section(1).first
  573   end
  574 
  575   def test_should_not_allow_arbitrary_class_attribute_on_offtags
  576     %w(code pre kbd).each do |tag|
  577       assert_html_output({"<#{tag} class=\"foo\">test</#{tag}>" => "<#{tag}>test</#{tag}>"}, false)
  578       assert_html_output({"<#{tag} class='foo'>test</#{tag}>" => "<#{tag}>test</#{tag}>"}, false)
  579       assert_html_output({"<#{tag} class=\"ruby foo\">test</#{tag}>" => "<#{tag}>test</#{tag}>"}, false)
  580       assert_html_output({"<#{tag} class='ruby foo'>test</#{tag}>" => "<#{tag}>test</#{tag}>"}, false)
  581       assert_html_output({"<#{tag} class=\"ruby \"foo\" bar\">test</#{tag}>" => "<#{tag}>test</#{tag}>"}, false)
  582     end
  583 
  584     assert_html_output({"<notextile class=\"foo\">test</notextile>" => "test"}, false)
  585     assert_html_output({"<notextile class='foo'>test</notextile>" => "test"}, false)
  586     assert_html_output({"<notextile class=\"ruby foo\">test</notextile>" => "test"}, false)
  587     assert_html_output({"<notextile class='ruby foo'>test</notextile>" => "test"}, false)
  588     assert_html_output({"<notextile class=\"ruby \"foo\" bar\">test</notextile>" => "test"}, false)
  589   end
  590 
  591   def test_should_allow_valid_language_class_attribute_on_code_tags
  592     # language name is double-quoted
  593     assert_html_output({"<code class=\"ruby\">test</code>" => "<code class=\"ruby syntaxhl\"><span class=\"nb\">test</span></code>"}, false)
  594     # language name is single-quoted
  595     assert_html_output({"<code class='ruby'>test</code>" => "<code class=\"ruby syntaxhl\"><span class=\"nb\">test</span></code>"}, false)
  596   end
  597 
  598   def test_should_not_allow_valid_language_class_attribute_on_non_code_offtags
  599     %w(pre kbd).each do |tag|
  600       assert_html_output({"<#{tag} class=\"ruby\">test</#{tag}>" => "<#{tag}>test</#{tag}>"}, false)
  601     end
  602 
  603     assert_html_output({"<notextile class=\"ruby\">test</notextile>" => "test"}, false)
  604   end
  605 
  606   def test_should_prefix_class_attribute_on_tags
  607     assert_html_output({
  608       '!(foo)test.png!' => "<p><img src=\"test.png\" class=\"wiki-class-foo\" alt=\"\" /></p>",
  609       '%(foo)test%'     => "<p><span class=\"wiki-class-foo\">test</span></p>",
  610       'p(foo). test'    => "<p class=\"wiki-class-foo\">test</p>",
  611       '|(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>",
  612     }, false)
  613   end
  614 
  615   def test_should_prefix_id_attribute_on_tags
  616     assert_html_output({
  617       '!(#foo)test.png!' => "<p><img src=\"test.png\" id=\"wiki-id-foo\" alt=\"\" /></p>",
  618       '%(#foo)test%'     => "<p><span id=\"wiki-id-foo\">test</span></p>",
  619       'p(#foo). test'    => "<p id=\"wiki-id-foo\">test</p>",
  620       '|(#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>",
  621     }, false)
  622   end
  623 
  624   def test_should_not_prefix_class_and_id_attributes_already_prefixed
  625     assert_html_output({
  626       '!(wiki-class-foo#wiki-id-bar)test.png!' => "<p><img src=\"test.png\" class=\"wiki-class-foo\" id=\"wiki-id-bar\" alt=\"\" /></p>",
  627     }, false)
  628   end
  629 
  630   def test_footnotes
  631     text = <<-STR
  632 This is some text[1].
  633 
  634 fn1. This is the foot note
  635 STR
  636 
  637     expected = <<-EXPECTED
  638 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
  639 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
  640 EXPECTED
  641 
  642     assert_equal expected.gsub(%r{[\r\n\t]}, ''), to_html(text).gsub(%r{[\r\n\t]}, '')
  643   end
  644 
  645   # TODO: Remove this test after migrating to RedCloth 4
  646   def test_should_not_crash_with_special_input
  647     assert_nothing_raised { to_html(" \f") }
  648     assert_nothing_raised { to_html(" \v") }
  649   end
  650 
  651   def test_should_not_handle_as_preformatted_text_tags_that_starts_with_pre
  652     text = <<-STR
  653 <pree>
  654   This is some text
  655 </pree>
  656 STR
  657 
  658     expected = <<-EXPECTED
  659 <p>&lt;pree&gt;<br />
  660   This is some text<br />
  661 &lt;/pree&gt;</p>
  662 EXPECTED
  663 
  664     assert_equal expected.gsub(%r{[\r\n\t]}, ''), to_html(text).gsub(%r{[\r\n\t]}, '')
  665   end
  666 
  667   private
  668 
  669   def assert_html_output(to_test, expect_paragraph = true)
  670     to_test.each do |text, expected|
  671       assert_equal(( expect_paragraph ? "<p>#{expected}</p>" : expected ), @formatter.new(text).to_html, "Formatting the following text failed:\n===\n#{text}\n===\n")
  672     end
  673   end
  674 
  675   def to_html(text)
  676     @formatter.new(text).to_html
  677   end
  678 
  679   def assert_section_with_hash(expected, text, index)
  680     result = @formatter.new(text).get_section(index)
  681 
  682     assert_kind_of Array, result
  683     assert_equal 2, result.size
  684     assert_equal expected, result.first, "section content did not match"
  685     assert_equal Digest::MD5.hexdigest(expected), result.last, "section hash did not match"
  686   end
  687 end