"Fossies" - the Fresh Open Source Software Archive

Member "asciidoctor-2.0.10/test/syntax_highlighter_test.rb" (1 Jun 2019, 36747 Bytes) of package /linux/www/asciidoctor-2.0.10.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 last Fossies "Diffs" side-by-side code changes report for "syntax_highlighter_test.rb": 2.0.7_vs_2.0.8.

    1 # frozen_string_literal: true
    2 require_relative 'test_helper'
    3 
    4 context 'Syntax Highlighter' do
    5   test 'should set syntax_highlighter property on document if source highlighter is set and basebackend is html' do
    6     input = <<~'EOS'
    7     :source-highlighter: coderay
    8 
    9     [source, ruby]
   10     ----
   11     puts 'Hello, World!'
   12     ----
   13     EOS
   14     doc = document_from_string input, safe: :safe, parse: true
   15     assert doc.basebackend? 'html'
   16     refute_nil doc.syntax_highlighter
   17     assert_kind_of Asciidoctor::SyntaxHighlighter, doc.syntax_highlighter
   18   end
   19 
   20   test 'should not set syntax_highlighter property on document if source highlighter is set and basebackend is not html' do
   21     input = <<~'EOS'
   22     :source-highlighter: coderay
   23 
   24     [source, ruby]
   25     ----
   26     puts 'Hello, World!'
   27     ----
   28     EOS
   29     doc = document_from_string input, safe: :safe, backend: 'docbook', parse: true
   30     refute doc.basebackend? 'html'
   31     assert_nil doc.syntax_highlighter
   32   end
   33 
   34   test 'should not set syntax_highlighter property on document if source highlighter is not set' do
   35     input = <<~'EOS'
   36     [source, ruby]
   37     ----
   38     puts 'Hello, World!'
   39     ----
   40     EOS
   41     doc = document_from_string input, safe: :safe, parse: true
   42     assert_nil doc.syntax_highlighter
   43   end
   44 
   45   test 'should not set syntax_highlighter property on document if syntax highlighter cannot be resolved' do
   46     input = <<~'EOS'
   47     :source-highlighter: unknown
   48 
   49     [source, ruby]
   50     ----
   51     puts 'Hello, World!'
   52     ----
   53     EOS
   54     doc = document_from_string input, safe: :safe, parse: true
   55     assert_nil doc.syntax_highlighter
   56   end
   57 
   58   test 'should not allow document to enable syntax highlighter if safe mode is at least SERVER' do
   59     input = ':source-highlighter: coderay'
   60     doc = document_from_string input, safe: Asciidoctor::SafeMode::SERVER, parse: true
   61     assert_nil doc.attributes['source-highlighter']
   62     assert_nil doc.syntax_highlighter
   63   end
   64 
   65   test 'should not invoke highlight method on syntax highlighter if highlight? is false' do
   66     Class.new Asciidoctor::SyntaxHighlighter::Base do
   67       register_for 'unavailable'
   68 
   69       def format node, language, opts
   70         %(<pre class="highlight"><code class="language-#{language}" data-lang="#{language}">#{node.content}</code></pre>)
   71       end
   72 
   73       def highlight?
   74         false
   75       end
   76     end
   77 
   78     input = <<~'EOS'
   79     [source,ruby]
   80     ----
   81     puts 'Hello, World!'
   82     ----
   83     EOS
   84 
   85     doc = document_from_string input, attributes: { 'source-highlighter' => 'unavailable' }
   86     output = doc.convert
   87     assert_css 'pre.highlight > code.language-ruby', output, 1
   88     source_block = (doc.find_by {|candidate| candidate.style == 'source' })[0]
   89     assert_raises NotImplementedError do
   90       doc.syntax_highlighter.highlight source_block, source_block.source, (source_block.attr 'language'), {}
   91     end
   92   end
   93 
   94   test 'should be able to register syntax highlighter from syntax highlighter class itself' do
   95     syntax_highlighter = Class.new Asciidoctor::SyntaxHighlighter::Base do
   96       def format node, language, opts
   97         %(<pre class="highlight"><code class="language-#{language}" data-lang="#{language}">#{node.content}</code></pre>)
   98       end
   99 
  100       def highlight?
  101         false
  102       end
  103     end
  104 
  105     syntax_highlighter.register_for 'foobar'
  106     assert_equal syntax_highlighter, (Asciidoctor::SyntaxHighlighter.for 'foobar')
  107   end
  108 
  109   test 'should be able to register syntax highlighter using symbol' do
  110     syntax_highlighter = Class.new Asciidoctor::SyntaxHighlighter::Base do
  111       register_for :foobaz
  112 
  113       def format node, language, opts
  114         %(<pre class="highlight"><code class="language-#{language}" data-lang="#{language}">#{node.content}</code></pre>)
  115       end
  116 
  117       def highlight?
  118         false
  119       end
  120     end
  121 
  122     assert_equal syntax_highlighter, (Asciidoctor::SyntaxHighlighter.for 'foobaz')
  123   end
  124 
  125   test 'should set language on output of source block when source-highlighter attribute is not set' do
  126     input = <<~'EOS'
  127     [source, ruby]
  128     ----
  129     puts 'Hello, World!'
  130     ----
  131     EOS
  132     output = convert_string input, safe: Asciidoctor::SafeMode::SAFE
  133     assert_css 'pre.highlight', output, 1
  134     assert_css 'pre.highlight > code.language-ruby', output, 1
  135     assert_css 'pre.highlight > code.language-ruby[data-lang="ruby"]', output, 1
  136   end
  137 
  138   test 'should set language on output of source block when source-highlighter attribute is not recognized' do
  139     input = <<~'EOS'
  140     :source-highlighter: unknown
  141 
  142     [source, ruby]
  143     ----
  144     puts 'Hello, World!'
  145     ----
  146     EOS
  147     output = convert_string input, safe: Asciidoctor::SafeMode::SAFE
  148     assert_css 'pre.highlight', output, 1
  149     assert_css 'pre.highlight > code.language-ruby', output, 1
  150     assert_css 'pre.highlight > code.language-ruby[data-lang="ruby"]', output, 1
  151   end
  152 
  153   test 'should highlight source if source highlighter is set even if language is not set' do
  154     input = <<~'EOS'
  155     :source-highlighter: coderay
  156 
  157     [source]
  158     ----
  159     [numbers]
  160     one
  161     two
  162     three
  163     ----
  164     EOS
  165     output = convert_string input, safe: :safe
  166     assert_css 'pre.CodeRay.highlight', output, 1
  167     assert_includes output, '<code>'
  168   end
  169 
  170   test 'should not crash if source block has no lines and source highlighter is set' do
  171     input = <<~'EOS'
  172     :source-highlighter: coderay
  173 
  174     [source,text]
  175     ----
  176     ----
  177     EOS
  178     output = convert_string_to_embedded input, safe: :safe
  179     assert_css 'pre.CodeRay', output, 1
  180     assert_css 'pre.CodeRay > code', output, 1
  181     assert_css 'pre.CodeRay > code:empty', output, 1
  182   end
  183 
  184   test 'should highlight source inside AsciiDoc table cell if source-highlighter attribute is set' do
  185     input = <<~'EOS'
  186     :source-highlighter: coderay
  187 
  188     |===
  189     a|
  190     [source, ruby]
  191     ----
  192     require 'coderay'
  193 
  194     html = CodeRay.scan("puts 'Hello, world!'", :ruby).div line_numbers: :table
  195     ----
  196     |===
  197     EOS
  198     output = convert_string_to_embedded input, safe: :safe
  199     assert_xpath '/table//pre[@class="CodeRay highlight"]/code[@data-lang="ruby"]//span[@class = "constant"][text() = "CodeRay"]', output, 1
  200   end
  201 
  202   test 'should set starting line number in DocBook output if linenums option is enabled and start attribute is set' do
  203     input = <<~'EOS'
  204     [source%linenums,java,start=3]
  205     ----
  206     public class HelloWorld {
  207       public static void main(String[] args) {
  208         out.println("Hello, World!");
  209       }
  210     }
  211     ----
  212     EOS
  213 
  214     output = convert_string_to_embedded input, backend: :docbook, safe: Asciidoctor::SafeMode::SAFE
  215     assert_css 'programlisting[startinglinenumber]', output, 1
  216     assert_css 'programlisting[startinglinenumber="3"]', output, 1
  217   end
  218 
  219   test 'should read source language from source-language document attribute if not specified on source block' do
  220     input = <<~'EOS'
  221     :source-highlighter: coderay
  222     :source-language: ruby
  223 
  224     [source]
  225     ----
  226     require 'coderay'
  227 
  228     html = CodeRay.scan("puts 'Hello, world!'", :ruby).div line_numbers: :table
  229     ----
  230     EOS
  231     output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE, linkcss_default: true
  232     assert_xpath '//pre[@class="CodeRay highlight"]/code[@data-lang="ruby"]//span[@class = "constant"][text() = "CodeRay"]', output, 1
  233   end
  234 
  235   test 'should rename document attribute named language to source-language when compat-mode is enabled' do
  236     input = <<~'EOS'
  237     :language: ruby
  238 
  239     {source-language}
  240     EOS
  241 
  242     assert_equal 'ruby', (convert_inline_string input, attributes: { 'compat-mode' => '' })
  243 
  244     input = <<~'EOS'
  245     :language: ruby
  246 
  247     {source-language}
  248     EOS
  249 
  250     assert_equal '{source-language}', (convert_inline_string input)
  251   end
  252 
  253   context 'CodeRay' do
  254     test 'should highlight source if source-highlighter attribute is set' do
  255       input = <<~'EOS'
  256       :source-highlighter: coderay
  257 
  258       [source, ruby]
  259       ----
  260       require 'coderay'
  261 
  262       html = CodeRay.scan("puts 'Hello, world!'", :ruby).div line_numbers: :table
  263       ----
  264       EOS
  265       output = convert_string input, safe: Asciidoctor::SafeMode::SAFE, linkcss_default: true
  266       assert_xpath '//pre[@class="CodeRay highlight"]/code[@data-lang="ruby"]//span[@class = "constant"][text() = "CodeRay"]', output, 1
  267       assert_match(/\.CodeRay *\{/, output)
  268     end
  269 
  270     test 'should not fail if source language is invalid' do
  271       input = <<~'EOS'
  272       :source-highlighter: coderay
  273 
  274       [source, n/a]
  275       ----
  276       PRINT 'yo'
  277       ----
  278       EOS
  279       output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE
  280       assert_css 'code[data-lang="n/a"]', output, 1
  281     end
  282 
  283     test 'should number lines if third positional attribute is set' do
  284       input = <<~'EOS'
  285       :source-highlighter: coderay
  286 
  287       [source,ruby,linenums]
  288       ----
  289       puts 'Hello, World!'
  290       ----
  291       EOS
  292       output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE
  293       assert_xpath '//td[@class="line-numbers"]', output, 1
  294     end
  295 
  296     test 'should number lines if linenums option is set on source block' do
  297       input = <<~'EOS'
  298       :source-highlighter: coderay
  299 
  300       [source%linenums,ruby]
  301       ----
  302       puts 'Hello, World!'
  303       ----
  304       EOS
  305       output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE
  306       assert_xpath '//td[@class="line-numbers"]', output, 1
  307     end
  308 
  309     test 'should number lines of source block if source-linenums-option document attribute is set' do
  310       input = <<~'EOS'
  311       :source-highlighter: coderay
  312       :source-linenums-option:
  313 
  314       [source,ruby]
  315       ----
  316       puts 'Hello, World!'
  317       ----
  318       EOS
  319       output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE
  320       assert_xpath '//td[@class="line-numbers"]', output, 1
  321     end
  322 
  323     test 'should set starting line number in HTML output if linenums option is enabled and start attribute is set' do
  324       input = <<~'EOS'
  325       :source-highlighter: coderay
  326       :coderay-linenums-mode: inline
  327 
  328       [source%linenums,ruby,start=10]
  329       ----
  330       puts 'Hello, World!'
  331       ----
  332       EOS
  333       output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE
  334       assert_xpath '//span[@class="line-numbers"]', output, 1
  335       assert_xpath '//span[@class="line-numbers"][text()="10"]', output, 1
  336     end
  337 
  338     test 'should highlight lines specified in highlight attribute if linenums is set and source-highlighter is coderay' do
  339       %w(highlight="1,4-6" highlight=1;4..6 highlight=1;4..;!7).each do |highlight_attr|
  340         input = <<~EOS
  341         :source-highlighter: coderay
  342 
  343         [source%linenums,java,#{highlight_attr}]
  344         ----
  345         import static java.lang.System.out;
  346 
  347         public class HelloWorld {
  348           public static void main(String[] args) {
  349             out.println("Hello, World!");
  350           }
  351         }
  352         ----
  353         EOS
  354         output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE
  355         assert_css 'strong.highlighted', output, 4
  356         assert_xpath '//strong[@class="highlighted"][text()="1"]', output, 1
  357         assert_xpath '//strong[@class="highlighted"][text()="2"]', output, 0
  358         assert_xpath '//strong[@class="highlighted"][text()="3"]', output, 0
  359         assert_xpath '//strong[@class="highlighted"][text()="4"]', output, 1
  360         assert_xpath '//strong[@class="highlighted"][text()="5"]', output, 1
  361         assert_xpath '//strong[@class="highlighted"][text()="6"]', output, 1
  362         assert_xpath '//strong[@class="highlighted"][text()="7"]', output, 0
  363       end
  364     end
  365 
  366     test 'should replace callout marks but not highlight them if source-highlighter attribute is coderay' do
  367       input = <<~'EOS'
  368       :source-highlighter: coderay
  369 
  370       [source, ruby]
  371       ----
  372       require 'coderay' # <1>
  373 
  374       html = CodeRay.scan("puts 'Hello, world!'", :ruby).div line_numbers: :table # <2>
  375       puts html # <3> <4>
  376       exit 0 # <5><6>
  377       ----
  378       <1> Load library
  379       <2> Highlight source
  380       <3> Print to stdout
  381       <4> Redirect to a file to capture output
  382       <5> Exit program
  383       <6> Reports success
  384       EOS
  385       output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE
  386       assert_match(/<span class="content">coderay<\/span>.* # <b class="conum">\(1\)<\/b>$/, output)
  387       assert_match(/<span class="content">puts 'Hello, world!'<\/span>.* # <b class="conum">\(2\)<\/b>$/, output)
  388       assert_match(/puts html.* # <b class="conum">\(3\)<\/b> <b class="conum">\(4\)<\/b>$/, output)
  389       assert_match(/exit.* # <b class="conum">\(5\)<\/b> <b class="conum">\(6\)<\/b><\/code>/, output)
  390     end
  391 
  392     test 'should support autonumbered callout marks if source-highlighter attribute is coderay' do
  393       input = <<~'EOS'
  394       :source-highlighter: coderay
  395 
  396       [source, ruby]
  397       ----
  398       require 'coderay' # <.><.>
  399 
  400       html = CodeRay.scan("puts 'Hello, world!'", :ruby).div line_numbers: :table # <.>
  401       puts html # <.>
  402       ----
  403       <.> Load library
  404       <.> Gem must be installed
  405       <.> Highlight source
  406       <.> Print to stdout
  407       EOS
  408       output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE
  409       assert_match(/<span class="content">coderay<\/span>.* # <b class="conum">\(1\)<\/b> <b class="conum">\(2\)<\/b>$/, output)
  410       assert_match(/<span class="content">puts 'Hello, world!'<\/span>.* # <b class="conum">\(3\)<\/b>$/, output)
  411       assert_match(/puts html.* # <b class="conum">\(4\)<\/b><\/code>/, output)
  412       assert_css '.colist ol', output, 1
  413       assert_css '.colist ol li', output, 4
  414     end
  415 
  416     test 'should restore callout marks to correct lines if source highlighter is coderay and table line numbering is enabled' do
  417       input = <<~'EOS'
  418       :source-highlighter: coderay
  419       :coderay-linenums-mode: table
  420 
  421       [source, ruby, numbered]
  422       ----
  423       require 'coderay' # <1>
  424 
  425       html = CodeRay.scan("puts 'Hello, world!'", :ruby).div line_numbers: :table # <2>
  426       puts html # <3> <4>
  427       exit 0 # <5><6>
  428       ----
  429       <1> Load library
  430       <2> Highlight source
  431       <3> Print to stdout
  432       <4> Redirect to a file to capture output
  433       <5> Exit program
  434       <6> Reports success
  435       EOS
  436       output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE
  437       assert_match(/<span class="content">coderay<\/span>.* # <b class="conum">\(1\)<\/b>$/, output)
  438       assert_match(/<span class="content">puts 'Hello, world!'<\/span>.* # <b class="conum">\(2\)<\/b>$/, output)
  439       assert_match(/puts html.* # <b class="conum">\(3\)<\/b> <b class="conum">\(4\)<\/b>$/, output)
  440       # NOTE notice there's a newline before the closing </pre> tag
  441       assert_match(/exit.* # <b class="conum">\(5\)<\/b> <b class="conum">\(6\)<\/b>\n<\/pre>/, output)
  442     end
  443 
  444     test 'should restore isolated callout mark on last line of source when source highlighter is coderay' do
  445       input = <<~'EOS'
  446       :source-highlighter: coderay
  447 
  448       [source,ruby,linenums]
  449       ----
  450       require 'app'
  451 
  452       launch_app
  453       # <1>
  454       ----
  455       <1> Profit.
  456       EOS
  457 
  458       output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE
  459       # NOTE notice there's a newline before the closing </pre> tag
  460       assert_match(/\n# <b class="conum">\(1\)<\/b>\n<\/pre>/, output)
  461     end
  462 
  463     test 'should preserve space before callout on final line' do
  464       inputs = []
  465 
  466       inputs << <<~'EOS'
  467       [source,yaml]
  468       ----
  469       a: 'a'
  470       key: 'value' #<1>
  471       ----
  472       <1> key-value pair
  473       EOS
  474 
  475       inputs << <<~'EOS'
  476       [source,ruby]
  477       ----
  478       puts 'hi'
  479       puts 'value' #<1>
  480       ----
  481       <1> print to stdout
  482       EOS
  483 
  484       inputs << <<~'EOS'
  485       [source,python]
  486       ----
  487       print 'hi'
  488       print 'value' #<1>
  489       ----
  490       <1> print to stdout
  491       EOS
  492 
  493       inputs.each do |input|
  494         output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE, attributes: { 'source-highlighter' => 'coderay' }
  495         output = output.gsub(/<\/?span.*?>/, '')
  496         assert_includes output, '\'value\' #<b class="conum">(1)</b>'
  497       end
  498     end
  499 
  500     test 'should preserve passthrough placeholders when highlighting source using coderay' do
  501       input = <<~'EOS'
  502       :source-highlighter: coderay
  503 
  504       [source,java]
  505       [subs="specialcharacters,macros,callouts"]
  506       ----
  507       public class Printer {
  508         public static void main(String[] args) {
  509           System.pass:quotes[_out_].println("*asterisks* make text pass:quotes[*bold*]");
  510         }
  511       }
  512       ----
  513       EOS
  514       output = convert_string input, safe: Asciidoctor::SafeMode::SAFE
  515       assert_match(/\.<em>out<\/em>\./, output, 1)
  516       assert_match(/\*asterisks\*/, output, 1)
  517       assert_match(/<strong>bold<\/strong>/, output, 1)
  518       refute_includes output, Asciidoctor::Substitutors::PASS_START
  519     end
  520 
  521     test 'should link to CodeRay stylesheet if source-highlighter is coderay and linkcss is set' do
  522       input = <<~'EOS'
  523       :source-highlighter: coderay
  524 
  525       [source, ruby]
  526       ----
  527       require 'coderay'
  528 
  529       html = CodeRay.scan("puts 'Hello, world!'", :ruby).div line_numbers: :table
  530       ----
  531       EOS
  532       output = convert_string input, safe: Asciidoctor::SafeMode::SAFE, attributes: { 'linkcss' => '' }
  533       assert_xpath '//pre[@class="CodeRay highlight"]/code[@data-lang="ruby"]//span[@class = "constant"][text() = "CodeRay"]', output, 1
  534       assert_css 'link[rel="stylesheet"][href="./coderay-asciidoctor.css"]', output, 1
  535     end
  536 
  537     test 'should highlight source inline if source-highlighter attribute is coderay and coderay-css is style' do
  538       input = <<~'EOS'
  539       :source-highlighter: coderay
  540       :coderay-css: style
  541 
  542       [source, ruby]
  543       ----
  544       require 'coderay'
  545 
  546       html = CodeRay.scan("puts 'Hello, world!'", :ruby).div line_numbers: :table
  547       ----
  548       EOS
  549       output = convert_string input, safe: Asciidoctor::SafeMode::SAFE, linkcss_default: true
  550       assert_xpath '//pre[@class="CodeRay highlight"]/code[@data-lang="ruby"]//span[@style = "color:#036;font-weight:bold"][text() = "CodeRay"]', output, 1
  551       refute_match(/\.CodeRay \{/, output)
  552     end
  553 
  554     test 'should read stylesheet' do
  555       css = (Asciidoctor::SyntaxHighlighter.for 'coderay').read_stylesheet
  556       refute_nil css
  557       assert_includes css, 'pre.CodeRay{background:#f7f7f8}'
  558     end
  559   end
  560 
  561   context 'Highlight.js' do
  562     test 'should include remote highlight.js assets if source-highlighter attribute is highlight.js' do
  563       input = <<~'EOS'
  564       :source-highlighter: highlight.js
  565 
  566       [source,html]
  567       ----
  568       <p>Highlight me!</p>
  569       ----
  570       EOS
  571       output = convert_string input, safe: Asciidoctor::SafeMode::SAFE
  572       assert_css 'pre.highlightjs.highlight', output, 1
  573       assert_css 'pre.highlightjs.highlight > code.language-html.hljs[data-lang="html"]', output, 1
  574       assert_includes output, '&lt;p&gt;Highlight me!&lt;/p&gt;'
  575       assert_css '#footer ~ link[href*="highlight.js"]', output, 1
  576       assert_css '#footer ~ script[src*="highlight.min.js"]', output, 1
  577       assert_xpath '//script[text()="hljs.initHighlighting()"]', output, 1
  578     end
  579 
  580     test 'should add language-none class to source block when source-highlighter is highlight.js and language is not set' do
  581       input = <<~'EOS'
  582       :source-highlighter: highlight.js
  583 
  584       [source]
  585       ----
  586       [numbers]
  587       one
  588       two
  589       three
  590       ----
  591       EOS
  592       output = convert_string input, safe: :safe
  593       assert_css 'code.language-none', output, 1
  594     end
  595 
  596     test 'should load additional languages specified by highlightjs-languages' do
  597       input = <<~'EOS'
  598       :source-highlighter: highlight.js
  599       :highlightjs-languages: yaml, scilab
  600 
  601       [source,yaml]
  602       ----
  603       key: value
  604       ----
  605       EOS
  606       output = convert_string input, safe: Asciidoctor::SafeMode::SAFE
  607       assert_css '#footer ~ script[src*="languages/yaml.min.js"]', output, 1
  608       assert_css '#footer ~ script[src*="languages/scilab.min.js"]', output, 1
  609     end
  610   end
  611 
  612   context 'Prettify' do
  613     test 'should add language classes to child code element when source-highlighter is prettify' do
  614       input = <<~'EOS'
  615       [source,ruby]
  616       ----
  617       puts "foo"
  618       ----
  619       EOS
  620 
  621       output = convert_string_to_embedded input, attributes: { 'source-highlighter' => 'prettify' }
  622       assert_css 'pre[class="prettyprint highlight"]', output, 1
  623       assert_css 'pre > code[data-lang="ruby"]', output, 1
  624     end
  625 
  626     test 'should set linenums start if linenums are enabled and start attribute is set when source-highlighter is prettify' do
  627       input = <<~'EOS'
  628       [source%linenums,ruby,start=5]
  629       ----
  630       puts "foo"
  631       ----
  632       EOS
  633 
  634       output = convert_string_to_embedded input, attributes: { 'source-highlighter' => 'prettify' }
  635       assert_css 'pre[class="prettyprint highlight linenums:5"]', output, 1
  636       assert_css 'pre > code[data-lang="ruby"]', output, 1
  637     end
  638   end
  639 
  640   context 'HTML Pipeline' do
  641     test 'should set lang attribute on pre when source-highlighter is html-pipeline' do
  642       input = <<~'EOS'
  643       [source,ruby]
  644       ----
  645       filters = [
  646       HTML::Pipeline::AsciiDocFilter,
  647       HTML::Pipeline::SanitizationFilter,
  648       HTML::Pipeline::SyntaxHighlightFilter
  649       ]
  650 
  651       puts HTML::Pipeline.new(filters, {}).call(input)[:output]
  652       ----
  653       EOS
  654 
  655       output = convert_string input, attributes: { 'source-highlighter' => 'html-pipeline' }
  656       assert_css 'pre[lang="ruby"]', output, 1
  657       assert_css 'pre[lang="ruby"] > code', output, 1
  658       assert_css 'pre[class]', output, 0
  659       assert_css 'code[class]', output, 0
  660     end
  661   end
  662 
  663   context 'Rouge' do
  664     test 'should syntax highlight source if source-highlighter attribute is set' do
  665       input = <<~'EOS'
  666       :source-highlighter: rouge
  667 
  668       [source,ruby]
  669       ----
  670       require 'rouge'
  671 
  672       html = Rouge::Formatters::HTML.new.format(Rouge::Lexers::Ruby.new.lex('puts "Hello, world!"'))
  673       ----
  674       EOS
  675       output = convert_string input, safe: :safe, linkcss_default: true
  676       assert_xpath '//pre[@class="rouge highlight"]/code[@data-lang="ruby"]/span[@class="no"][text()="Rouge"]', output, 2
  677       assert_includes output, 'pre.rouge .no {'
  678     end
  679 
  680     test 'should not crash if source-highlighter attribute is set and source block does not define a language' do
  681       input = <<~'EOS'
  682       :source-highlighter: rouge
  683 
  684       [source]
  685       ----
  686       require 'rouge'
  687 
  688       html = Rouge::Formatters::HTML.new.format(Rouge::Lexers::Ruby.new.lex('puts "Hello, world!"'))
  689       ----
  690       EOS
  691       output = convert_string_to_embedded input, safe: :safe
  692       assert_css 'pre > code:not([data-lang])', output, 1
  693     end
  694 
  695     test 'should default to plain text lexer if lexer cannot be resolved for language' do
  696       input = <<~'EOS'
  697       :source-highlighter: rouge
  698 
  699       [source,lolcode]
  700       ----
  701       CAN HAS STDIO?
  702       PLZ OPEN FILE "LOLCATS.TXT"?
  703       KTHXBYE
  704       ----
  705       EOS
  706       output = convert_string_to_embedded input, safe: :safe
  707       assert_css 'code[data-lang=lolcode]', output, 1
  708       assert_css 'code span', output, 0
  709       assert_xpath %(//code[text()='CAN HAS STDIO?\nPLZ OPEN FILE "LOLCATS.TXT"?\nKTHXBYE']), output, 1
  710     end
  711 
  712     test 'should honor cgi-style options on language' do
  713       input = <<~'EOS'
  714       :source-highlighter: rouge
  715 
  716       [source,"console?prompt=$> "]
  717       ----
  718       $> asciidoctor --version
  719       ----
  720       EOS
  721       output = convert_string_to_embedded input, safe: :safe
  722       assert_css 'code[data-lang=console]', output, 1
  723       assert_css 'code span.gp', output, 1
  724     end
  725 
  726     test 'should set starting line number to 1 by default in HTML output if linenums option is enabled' do
  727       input = <<~'EOS'
  728       [source%linenums,ruby]
  729       ----
  730       puts 'Hello, World!'
  731       puts 'Goodbye, World!'
  732       ----
  733       EOS
  734       output = convert_string_to_embedded input, attributes: { 'source-highlighter' => 'rouge' }
  735       assert_css 'table.linenotable', output, 1
  736       assert_css 'table.linenotable td.linenos', output, 1
  737       assert_css 'table.linenotable td.linenos pre.lineno', output, 1
  738       assert_css 'table.linenotable td.code', output, 1
  739       assert_css 'table.linenotable td.code pre:not([class])', output, 1
  740       assert_xpath %(//pre[@class="lineno"][text()="1\n2\n"]), output, 1
  741     end
  742 
  743     test 'should set starting line number in HTML output if linenums option is enabled and start attribute is set' do
  744       input = <<~'EOS'
  745       [source%linenums,ruby,start=9]
  746       ----
  747       puts 'Hello, World!'
  748       puts 'Goodbye, World!'
  749       ----
  750       EOS
  751       output = convert_string_to_embedded input, attributes: { 'source-highlighter' => 'rouge' }
  752       assert_css 'table.linenotable', output, 1
  753       assert_css 'table.linenotable td.linenos', output, 1
  754       assert_css 'table.linenotable td.linenos pre.lineno', output, 1
  755       assert_css 'table.linenotable td.code', output, 1
  756       assert_css 'table.linenotable td.code pre:not([class])', output, 1
  757       assert_xpath %(//pre[@class="lineno"][text()=" 9\n10\n"]), output, 1
  758     end
  759 
  760     test 'should restore callout marks to correct lines' do
  761       ['', '%linenums'].each do |opts|
  762         input = <<~EOS
  763         :source-highlighter: rouge
  764 
  765         [source#{opts},ruby]
  766         ----
  767         require 'rouge' # <1>
  768 
  769         html = Rouge::Formatters::HTML.new.format(Rouge::Lexers::Ruby.new.lex('puts "Hello, world!"')) # <2>
  770         puts html # <3> <4>
  771         exit 0 # <5><6>
  772         ----
  773         <1> Load library
  774         <2> Highlight source
  775         <3> Print to stdout
  776         <4> Redirect to a file to capture output
  777         <5> Exit program
  778         <6> Reports success
  779         EOS
  780         output = convert_string_to_embedded input, safe: :safe
  781         assert_match(/<span class="s1">'rouge'<\/span>.* # <b class="conum">\(1\)<\/b>$/, output)
  782         assert_match(/<span class="s1">'puts "Hello, world!"'<\/span>.* # <b class="conum">\(2\)<\/b>$/, output)
  783         assert_match(/<span class="n">html<\/span>.* # <b class="conum">\(3\)<\/b> <b class="conum">\(4\)<\/b>$/, output)
  784         # NOTE notice there's a newline before the closing </pre> tag when linenums are enabled
  785         assert_match(/<span class="mi">0<\/span>.* # <b class="conum">\(5\)<\/b> <b class="conum">\(6\)<\/b>#{opts == '%linenums' ? ?\n : '</code>'}<\/pre>/, output)
  786       end
  787     end
  788 
  789     test 'should line highlight specified lines when last line is not highlighted' do
  790       ['', '%linenums'].each do |opts|
  791         input = <<~EOS
  792         :source-highlighter: rouge
  793 
  794         [source#{opts},ruby,highlight=1]
  795         ----
  796         puts 'Hello, world!'
  797         puts 'Goodbye, world!'
  798         ----
  799         EOS
  800         # NOTE notice the newline in inside the closing </span> of the highlight span
  801         expected = <<~EOS.chop
  802         <span class="hll"><span class="nb">puts</span> <span class="s1">'Hello, world!'</span>
  803         </span><span class="nb">puts</span> <span class="s1">'Goodbye, world!'</span>#{opts == '%linenums' ? ?\n : '</code>'}</pre>
  804         EOS
  805 
  806         output = convert_string_to_embedded input, safe: :safe
  807         assert_includes output, expected
  808       end
  809     end
  810 
  811     test 'should line highlight specified lines when last line is highlighted' do
  812       ['', '%linenums'].each do |opts|
  813         input = <<~EOS
  814         :source-highlighter: rouge
  815 
  816         [source#{opts},ruby,highlight=2]
  817         ----
  818         puts 'Hello, world!'
  819         puts 'Goodbye, world!'
  820         ----
  821         EOS
  822         # NOTE notice the newline in inside the closing </span> of the highlight span
  823         expected = <<~EOS.chop
  824         <span class="nb">puts</span> <span class="s1">'Hello, world!'</span>
  825         <span class="hll"><span class="nb">puts</span> <span class="s1">'Goodbye, world!'</span>
  826         </span>#{opts == '%linenums' ? '' : '</code>'}</pre>
  827         EOS
  828 
  829         output = convert_string_to_embedded input, safe: :safe
  830         assert_includes output, expected
  831       end
  832     end
  833 
  834     test 'should restore callout marks to correct lines if line numbering and line highlighting are enabled' do
  835       [1, 2].each do |highlight|
  836         input = <<~EOS
  837         :source-highlighter: rouge
  838 
  839         [source%linenums,ruby,highlight=#{highlight}]
  840         ----
  841         require 'rouge' # <1>
  842         exit 0 # <2>
  843         ----
  844         <1> Load library
  845         <2> Exit program
  846         EOS
  847         output = convert_string_to_embedded input, safe: :safe
  848         assert_match(/<span class="s1">'rouge'<\/span>.* # <b class="conum">\(1\)<\/b>$/, output)
  849         # NOTE notice there's a newline before the closing </pre> tag
  850         assert_match(/<span class="mi">0<\/span>.* # <b class="conum">\(2\)<\/b>\n#{highlight == 2 ? '</span>' : ''}<\/pre>/, output)
  851       end
  852     end
  853 
  854     test 'should gracefully fallback to default style if specified style not recognized' do
  855       input = <<~'EOS'
  856       :source-highlighter: rouge
  857       :rouge-style: unknown
  858 
  859       [source,ruby]
  860       ----
  861       puts 'Hello, world!'
  862       ----
  863       EOS
  864       output = convert_string input, safe: :safe, linkcss_default: true
  865       assert_css 'pre.rouge', output, 1
  866       assert_includes output, 'pre.rouge .no {'
  867       assert_match %r/pre\.rouge \{\s*background-color: #f8f8f8;/m, output
  868     end
  869 
  870     test 'should restore isolated callout mark on last line of source' do
  871       input = <<~'EOS'
  872       :source-highlighter: rouge
  873 
  874       [source%linenums,ruby]
  875       ----
  876       require 'app'
  877 
  878       launch_app
  879       # <1>
  880       ----
  881       <1> Profit.
  882       EOS
  883 
  884       output = convert_string_to_embedded input, safe: :safe
  885       # NOTE notice there's a newline before the closing </pre> tag, but not before the closing </td> tag
  886       assert_match(/\n# <b class="conum">\(1\)<\/b>\n<\/pre><\/td>/, output)
  887     end
  888 
  889     test 'should number all lines when isolated callout mark is on last line of source and starting line number is set' do
  890       input = <<~'EOS'
  891       :source-highlighter: rouge
  892 
  893       [source%linenums,ruby,start=5]
  894       ----
  895       require 'app'
  896 
  897       launch_app
  898       # <1>
  899       ----
  900       <1> Profit.
  901       EOS
  902 
  903       output = convert_string_to_embedded input, safe: :safe
  904       assert_xpath %(//pre[@class="lineno"][text()="5\n6\n7\n8\n"]), output, 1
  905       # NOTE notice there's a newline before the closing </pre> tag, but not before the closing </td> tag
  906       assert_match(/\n# <b class="conum">\(1\)<\/b>\n<\/pre><\/td>/, output)
  907     end
  908 
  909     test 'should read stylesheet for specified style' do
  910       css = (Asciidoctor::SyntaxHighlighter.for 'rouge').read_stylesheet 'monokai'
  911       refute_nil css
  912       assert_includes css, 'pre.rouge {'
  913       assert_includes css, 'background-color: #49483e;'
  914     end
  915   end
  916 
  917   context 'Pygments' do
  918     test 'should syntax highlight source if source-highlighter attribute is set' do
  919       input = <<~'EOS'
  920       :source-highlighter: pygments
  921       :pygments-style: monokai
  922 
  923       [source,python]
  924       ----
  925       from pygments import highlight
  926       from pygments.lexers import PythonLexer
  927       from pygments.formatters import HtmlFormatter
  928 
  929       source = 'print "Hello World"'
  930       print(highlight(source, PythonLexer(), HtmlFormatter()))
  931       ----
  932       EOS
  933       output = convert_string input, safe: :safe, linkcss_default: true
  934       assert_xpath '//pre[@class="pygments highlight"]/code[@data-lang="python"]/span[@class="tok-kn"][text()="import"]', output, 3
  935       assert_includes output, 'pre.pygments '
  936     end
  937 
  938     test 'should gracefully fallback to default style if specified style not recognized' do
  939       input = <<~'EOS'
  940       :source-highlighter: pygments
  941       :pygments-style: unknown
  942 
  943       [source,python]
  944       ----
  945       from pygments import highlight
  946       from pygments.lexers import PythonLexer
  947       from pygments.formatters import HtmlFormatter
  948 
  949       source = 'print "Hello World"'
  950       print(highlight(source, PythonLexer(), HtmlFormatter()))
  951       ----
  952       EOS
  953       output = convert_string input, safe: :safe, linkcss_default: true
  954       assert_css 'pre.pygments', output, 1
  955       assert_includes output, 'pre.pygments '
  956       assert_includes output, '.tok-c { color: #408080;'
  957     end
  958 
  959     test 'should number lines if linenums option is set on source block' do
  960       input = <<~'EOS'
  961       :source-highlighter: pygments
  962 
  963       [source%linenums,ruby]
  964       ----
  965       puts 'Hello, World!'
  966       puts 'Goodbye, World!'
  967       ----
  968       EOS
  969       output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SAFE
  970       assert_css 'table.linenotable', output, 1
  971       assert_css 'table.linenotable td.linenos', output, 1
  972       assert_css 'table.linenotable td.linenos .linenodiv', output, 1
  973       assert_css 'table.linenotable td.linenos .linenodiv pre:not([class])', output, 1
  974       assert_css 'table.linenotable td.code', output, 1
  975       assert_css 'table.linenotable td.code pre:not([class])', output, 1
  976       assert_xpath %(//*[@class="linenodiv"]/pre[text()="1\n2"]), output, 1
  977     end
  978 
  979     test 'should restore callout marks to correct lines if table line numbering is enabled' do
  980       input = <<~'EOS'
  981       :source-highlighter: pygments
  982       :pygments-linenums-mode: table
  983 
  984       [source%linenums,ruby]
  985       ----
  986       from pygments import highlight # <1>
  987       from pygments.lexers import PythonLexer
  988       from pygments.formatters import HtmlFormatter
  989 
  990       code = 'print "Hello World"'
  991       print(highlight(code, PythonLexer(), HtmlFormatter())) # <2><3>
  992       ----
  993       <1> Load library
  994       <2> Highlight source
  995       <3> Print to stdout
  996       EOS
  997       output = convert_string_to_embedded input, safe: :safe
  998       assert_match(/highlight<\/span> # <b class="conum">\(1\)<\/b>$/, output)
  999       # NOTE notice there's a newline before the closing </pre> tag
 1000       assert_match(/\(\)\)\).*<\/span> # <b class="conum">\(2\)<\/b> <b class="conum">\(3\)<\/b>$/, output)
 1001     end
 1002 
 1003     test 'should restore isolated callout mark on last line of source' do
 1004       input = <<~'EOS'
 1005       :source-highlighter: pygments
 1006 
 1007       [source,ruby,linenums]
 1008       ----
 1009       require 'app'
 1010 
 1011       launch_app
 1012       # <1>
 1013       ----
 1014       <1> Profit.
 1015       EOS
 1016 
 1017       output = convert_string_to_embedded input, safe: :safe
 1018       # NOTE notice there's a newline before the closing </pre> tag, but not before the closing </td> tag
 1019       assert_match(/\n# <b class="conum">\(1\)<\/b>\n<\/pre><\/td>/, output)
 1020     end
 1021 
 1022     test 'should not hardcode inline styles on lineno div and pre elements when linenums are enabled in table mode' do
 1023       input = <<~'EOS'
 1024       :source-highlighter: pygments
 1025       :pygments-css: inline
 1026 
 1027       [source%linenums,ruby]
 1028       ----
 1029       puts 'Hello, World!'
 1030       ----
 1031       EOS
 1032 
 1033       output = convert_string_to_embedded input, safe: :safe
 1034       assert_css 'td.linenos', output, 1
 1035       assert_css 'div.linenodiv:not([style])', output, 1
 1036       assert_includes output, '<div class="linenodiv"><pre>'
 1037       assert_css 'pre:not([style])', output, 2
 1038     end
 1039 
 1040     test 'should not hardcode inline styles on lineno spans when linenums are enabled and source-highlighter is pygments' do
 1041       input = <<~'EOS'
 1042       :source-highlighter: pygments
 1043       :pygments-css: inline
 1044       :pygments-linenums-mode: inline
 1045 
 1046       [source%linenums,ruby]
 1047       ----
 1048       puts 'Hello, World!'
 1049       puts 'Hello, World!'
 1050       puts 'Hello, World!'
 1051       puts 'Hello, World!'
 1052       puts 'Hello, World!'
 1053       puts 'Hello, World!'
 1054       puts 'Hello, World!'
 1055       puts 'Hello, World!'
 1056       puts 'Hello, World!'
 1057       exit 0
 1058       ----
 1059       EOS
 1060 
 1061       output = convert_string_to_embedded input, safe: :safe
 1062       assert_css 'table.linenotable', output, 0
 1063       assert_css 'pre', output, 1
 1064       assert_includes output, '<span class="lineno"> 1 </span>'
 1065       assert_includes output, '<span class="lineno">10 </span>'
 1066     end
 1067 
 1068     test 'should line highlight specified lines' do
 1069       input = <<~'EOS'
 1070       :source-highlighter: pygments
 1071 
 1072       [source,ruby,highlight=1..2]
 1073       ----
 1074       puts 'Hello, world!'
 1075       puts 'Goodbye, world!'
 1076       ----
 1077       EOS
 1078       # NOTE notice the newline is inside the closing </span> of the highlight span
 1079       expected = <<~'EOS'.chop
 1080       <pre class="pygments highlight"><code data-lang="ruby"><span></span><span class="hll"><span class="tok-nb">puts</span> <span class="tok-s1">&#39;Hello, world!&#39;</span>
 1081       </span><span class="hll"><span class="tok-nb">puts</span> <span class="tok-s1">&#39;Goodbye, world!&#39;</span>
 1082       </span></code></pre>
 1083       EOS
 1084 
 1085       output = convert_string_to_embedded input, safe: :safe
 1086       assert_includes output, expected
 1087     end
 1088   end if ENV['PYGMENTS']
 1089 end