"Fossies" - the Fresh Open Source Software Archive

Member "asciidoctor-2.0.10/test/lists_test.rb" (1 Jun 2019, 177195 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.

    1 # frozen_string_literal: true
    2 require_relative 'test_helper'
    3 
    4 context "Bulleted lists (:ulist)" do
    5   context "Simple lists" do
    6     test "dash elements with no blank lines" do
    7       input = <<~'EOS'
    8       List
    9       ====
   10 
   11       - Foo
   12       - Boo
   13       - Blech
   14       EOS
   15       output = convert_string input
   16       assert_xpath '//ul', output, 1
   17       assert_xpath '//ul/li', output, 3
   18     end
   19 
   20     test 'indented dash elements using spaces' do
   21       input = <<~EOS
   22       \x20- Foo
   23       \x20- Boo
   24       \x20- Blech
   25       EOS
   26       output = convert_string input
   27       assert_xpath '//ul', output, 1
   28       assert_xpath '//ul/li', output, 3
   29     end
   30 
   31     test 'indented dash elements using tabs' do
   32       input = <<~EOS
   33       \t-\tFoo
   34       \t-\tBoo
   35       \t-\tBlech
   36       EOS
   37       output = convert_string input
   38       assert_xpath '//ul', output, 1
   39       assert_xpath '//ul/li', output, 3
   40     end
   41 
   42     test "dash elements separated by blank lines should merge lists" do
   43       input = <<~'EOS'
   44       List
   45       ====
   46 
   47       - Foo
   48 
   49       - Boo
   50 
   51 
   52       - Blech
   53       EOS
   54       output = convert_string input
   55       assert_xpath '//ul', output, 1
   56       assert_xpath '//ul/li', output, 3
   57     end
   58 
   59     test 'dash elements with interspersed line comments should be skipped and not break list' do
   60       input = <<~'EOS'
   61       == List
   62 
   63       - Foo
   64       // line comment
   65       // another line comment
   66       - Boo
   67       // line comment
   68       more text
   69       // another line comment
   70       - Blech
   71       EOS
   72       output = convert_string_to_embedded input
   73       assert_xpath '//ul', output, 1
   74       assert_xpath '//ul/li', output, 3
   75       assert_xpath %((//ul/li)[2]/p[text()="Boo\nmore text"]), output, 1
   76     end
   77 
   78     test "dash elements separated by a line comment offset by blank lines should not merge lists" do
   79       input = <<~'EOS'
   80       List
   81       ====
   82 
   83       - Foo
   84       - Boo
   85 
   86       //
   87 
   88       - Blech
   89       EOS
   90       output = convert_string input
   91       assert_xpath '//ul', output, 2
   92       assert_xpath '(//ul)[1]/li', output, 2
   93       assert_xpath '(//ul)[2]/li', output, 1
   94     end
   95 
   96     test "dash elements separated by a block title offset by a blank line should not merge lists" do
   97       input = <<~'EOS'
   98       List
   99       ====
  100 
  101       - Foo
  102       - Boo
  103 
  104       .Also
  105       - Blech
  106       EOS
  107       output = convert_string input
  108       assert_xpath '//ul', output, 2
  109       assert_xpath '(//ul)[1]/li', output, 2
  110       assert_xpath '(//ul)[2]/li', output, 1
  111       assert_xpath '(//ul)[2]/preceding-sibling::*[@class = "title"][text() = "Also"]', output, 1
  112     end
  113 
  114     test "dash elements separated by an attribute entry offset by a blank line should not merge lists" do
  115       input = <<~'EOS'
  116       == List
  117 
  118       - Foo
  119       - Boo
  120 
  121       :foo: bar
  122       - Blech
  123       EOS
  124       output = convert_string_to_embedded input
  125       assert_xpath '//ul', output, 2
  126       assert_xpath '(//ul)[1]/li', output, 2
  127       assert_xpath '(//ul)[2]/li', output, 1
  128     end
  129 
  130     test 'a non-indented wrapped line is folded into text of list item' do
  131       input = <<~'EOS'
  132       List
  133       ====
  134 
  135       - Foo
  136       wrapped content
  137       - Boo
  138       - Blech
  139       EOS
  140       output = convert_string input
  141       assert_xpath '//ul', output, 1
  142       assert_xpath '//ul/li[1]/*', output, 1
  143       assert_xpath "//ul/li[1]/p[text() = 'Foo\nwrapped content']", output, 1
  144     end
  145 
  146     test 'a non-indented wrapped line that resembles a block title is folded into text of list item' do
  147       input = <<~'EOS'
  148       == List
  149 
  150       - Foo
  151       .wrapped content
  152       - Boo
  153       - Blech
  154       EOS
  155       output = convert_string_to_embedded input
  156       assert_xpath '//ul', output, 1
  157       assert_xpath '//ul/li[1]/*', output, 1
  158       assert_xpath "//ul/li[1]/p[text() = 'Foo\n.wrapped content']", output, 1
  159     end
  160 
  161     test 'a non-indented wrapped line that resembles an attribute entry is folded into text of list item' do
  162       input = <<~'EOS'
  163       == List
  164 
  165       - Foo
  166       :foo: bar
  167       - Boo
  168       - Blech
  169       EOS
  170       output = convert_string_to_embedded input
  171       assert_xpath '//ul', output, 1
  172       assert_xpath '//ul/li[1]/*', output, 1
  173       assert_xpath "//ul/li[1]/p[text() = 'Foo\n:foo: bar']", output, 1
  174     end
  175 
  176     test 'a list item with a nested marker terminates non-indented paragraph for text of list item' do
  177       input = <<~'EOS'
  178       - Foo
  179       Bar
  180       * Foo
  181       EOS
  182 
  183       output = convert_string_to_embedded input
  184       assert_css 'ul ul', output, 1
  185       refute_includes output, '* Foo'
  186     end
  187 
  188     test 'a list item for a different list terminates non-indented paragraph for text of list item' do
  189       input = <<~'EOS'
  190       == Example 1
  191 
  192       - Foo
  193       Bar
  194       . Foo
  195 
  196       == Example 2
  197 
  198       * Item
  199       text
  200       term:: def
  201       EOS
  202 
  203       output = convert_string_to_embedded input
  204       assert_css 'ul ol', output, 1
  205       refute_includes output, '* Foo'
  206       assert_css 'ul dl', output, 1
  207       refute_includes output, 'term:: def'
  208     end
  209 
  210     test 'an indented wrapped line is unindented and folded into text of list item' do
  211       input = <<~'EOS'
  212       List
  213       ====
  214 
  215       - Foo
  216         wrapped content
  217       - Boo
  218       - Blech
  219       EOS
  220       output = convert_string input
  221       assert_xpath '//ul', output, 1
  222       assert_xpath '//ul/li[1]/*', output, 1
  223       assert_xpath "//ul/li[1]/p[text() = 'Foo\nwrapped content']", output, 1
  224     end
  225 
  226     test 'wrapped list item with hanging indent followed by non-indented line' do
  227       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  228       input = <<~EOS
  229       == Lists
  230 
  231       - list item 1
  232         // not line comment
  233       second wrapped line
  234       - list item 2
  235       EOS
  236       output = convert_string_to_embedded input
  237       assert_css 'ul', output, 1
  238       assert_css 'ul li', output, 2
  239       # NOTE for some reason, we're getting an extra line after the indented line
  240       lines = xmlnodes_at_xpath('(//ul/li)[1]/p', output, 1).text.gsub(/\n[[:space:]]*\n/, ?\n).lines
  241       assert_equal 3, lines.size
  242       assert_equal 'list item 1', lines[0].chomp
  243       assert_equal '  // not line comment', lines[1].chomp
  244       assert_equal 'second wrapped line', lines[2].chomp
  245     end
  246 
  247     test 'a list item with a nested marker terminates indented paragraph for text of list item' do
  248       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  249       input = <<~EOS
  250       - Foo
  251         Bar
  252       * Foo
  253       EOS
  254 
  255       output = convert_string_to_embedded input
  256       assert_css 'ul ul', output, 1
  257       refute_includes output, '* Foo'
  258     end
  259 
  260     test 'a list item that starts with a sequence of list markers characters should not match a nested list' do
  261       input = <<~EOS
  262       \x20* first item
  263       \x20*. normal text
  264       EOS
  265 
  266       output = convert_string_to_embedded input
  267       assert_css 'ul', output, 1
  268       assert_css 'ul li', output, 1
  269       assert_xpath "//ul/li/p[text()='first item\n*. normal text']", output, 1
  270     end
  271 
  272     test 'a list item for a different list terminates indented paragraph for text of list item' do
  273       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  274       input = <<~EOS
  275       == Example 1
  276 
  277       - Foo
  278         Bar
  279       . Foo
  280 
  281       == Example 2
  282 
  283       * Item
  284         text
  285       term:: def
  286       EOS
  287 
  288       output = convert_string_to_embedded input
  289       assert_css 'ul ol', output, 1
  290       refute_includes output, '* Foo'
  291       assert_css 'ul dl', output, 1
  292       refute_includes output, 'term:: def'
  293     end
  294 
  295     test "a literal paragraph offset by blank lines in list content is appended as a literal block" do
  296       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  297       input = <<~EOS
  298       List
  299       ====
  300 
  301       - Foo
  302 
  303         literal
  304 
  305       - Boo
  306       - Blech
  307       EOS
  308       output = convert_string input
  309       assert_xpath '//ul', output, 1
  310       assert_xpath '//ul/li', output, 3
  311       assert_xpath '(//ul/li)[1]/p[text() = "Foo"]', output, 1
  312       assert_xpath '(//ul/li)[1]/*[@class="literalblock"]', output, 1
  313       assert_xpath '(//ul/li)[1]/p/following-sibling::*[@class="literalblock"]', output, 1
  314       assert_xpath '((//ul/li)[1]/*[@class="literalblock"])[1]//pre[text() = "literal"]', output, 1
  315     end
  316 
  317     test 'should escape special characters in all literal paragraphs attached to list item' do
  318       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  319       input = <<~EOS
  320       * first item
  321 
  322         <code>text</code>
  323 
  324         more <code>text</code>
  325 
  326       * second item
  327       EOS
  328 
  329       output = convert_string_to_embedded input
  330       assert_css 'li', output, 2
  331       assert_css 'code', output, 0
  332       assert_css 'li:first-of-type > *', output, 3
  333       assert_css 'li:first-of-type pre', output, 2
  334       assert_xpath '((//li)[1]//pre)[1][text()="<code>text</code>"]', output, 1
  335       assert_xpath '((//li)[1]//pre)[2][text()="more <code>text</code>"]', output, 1
  336     end
  337 
  338     test "a literal paragraph offset by a blank line in list content followed by line with continuation is appended as two blocks" do
  339       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  340       input = <<~EOS
  341       List
  342       ====
  343 
  344       - Foo
  345 
  346         literal
  347       +
  348       para
  349 
  350       - Boo
  351       - Blech
  352       EOS
  353       output = convert_string input
  354       assert_xpath '//ul', output, 1
  355       assert_xpath '//ul/li', output, 3
  356       assert_xpath '(//ul/li)[1]/p[text() = "Foo"]', output, 1
  357       assert_xpath '(//ul/li)[1]/*[@class="literalblock"]', output, 1
  358       assert_xpath '(//ul/li)[1]/p/following-sibling::*[@class="literalblock"]', output, 1
  359       assert_xpath '((//ul/li)[1]/*[@class="literalblock"])[1]//pre[text() = "literal"]', output, 1
  360       assert_xpath '(//ul/li)[1]/*[@class="literalblock"]/following-sibling::*[@class="paragraph"]', output, 1
  361       assert_xpath '(//ul/li)[1]/*[@class="literalblock"]/following-sibling::*[@class="paragraph"]/p[text()="para"]', output, 1
  362     end
  363 
  364     test 'an admonition paragraph attached by a line continuation to a list item with wrapped text should produce admonition' do
  365       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  366       input = <<~EOS
  367       - first-line text
  368         wrapped text
  369       +
  370       NOTE: This is a note.
  371       EOS
  372 
  373       output = convert_string_to_embedded input
  374       assert_css 'ul', output, 1
  375       assert_css 'ul > li', output, 1
  376       assert_css 'ul > li > p', output, 1
  377       assert_xpath %(//ul/li/p[text()="first-line text\nwrapped text"]), output, 1
  378       assert_css 'ul > li > p + .admonitionblock.note', output, 1
  379       assert_xpath '//ul/li/*[@class="admonitionblock note"]//td[@class="content"][normalize-space(text())="This is a note."]', output, 1
  380     end
  381 
  382     test 'paragraph-like blocks attached to an ancestory list item by a list continuation should produce blocks' do
  383       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  384       input = <<~EOS
  385       * parent
  386        ** child
  387 
  388       +
  389       NOTE: This is a note.
  390 
  391       * another parent
  392        ** another child
  393 
  394       +
  395       '''
  396       EOS
  397 
  398       output = convert_string_to_embedded input
  399       assert_css 'ul ul .admonitionblock.note', output, 0
  400       assert_xpath '(//ul)[1]/li/*[@class="admonitionblock note"]', output, 1
  401       assert_css 'ul ul hr', output, 0
  402       assert_xpath '(//ul)[1]/li/hr', output, 1
  403     end
  404 
  405     test 'should not inherit block attributes from previous block when block is attached using a list continuation' do
  406       input = <<~'EOS'
  407       * complex list item
  408       +
  409       [source,xml]
  410       ----
  411       <name>value</name> <!--1-->
  412       ----
  413       <1> a configuration value
  414       EOS
  415 
  416       doc = document_from_string input
  417       colist = doc.blocks[0].items[0].blocks[-1]
  418       assert_equal :colist, colist.context
  419       refute_equal 'source', colist.style
  420       output = doc.convert standalone: false
  421       assert_css 'ul', output, 1
  422       assert_css 'ul > li', output, 1
  423       assert_css 'ul > li > p', output, 1
  424       assert_css 'ul > li > .listingblock', output, 1
  425       assert_css 'ul > li > .colist', output, 1
  426     end
  427 
  428     test 'should continue to parse blocks attached by a list continuation after block is dropped' do
  429       input = <<~'EOS'
  430       * item
  431       +
  432       paragraph
  433       +
  434       [comment]
  435       comment
  436       +
  437       ====
  438       example
  439       ====
  440       '''
  441       EOS
  442 
  443       output = convert_string_to_embedded input
  444       assert_css 'ul > li > .paragraph', output, 1
  445       assert_css 'ul > li > .exampleblock', output, 1
  446     end
  447 
  448     test 'appends line as paragraph if attached by continuation following line comment' do
  449       input = <<~'EOS'
  450       - list item 1
  451       // line comment
  452       +
  453       paragraph in list item 1
  454 
  455       - list item 2
  456       EOS
  457       output = convert_string_to_embedded input
  458       assert_css 'ul', output, 1
  459       assert_css 'ul li', output, 2
  460       assert_xpath '(//ul/li)[1]/p[text()="list item 1"]', output, 1
  461       assert_xpath '(//ul/li)[1]/p/following-sibling::*[@class="paragraph"]', output, 1
  462       assert_xpath '(//ul/li)[1]/p/following-sibling::*[@class="paragraph"]/p[text()="paragraph in list item 1"]', output, 1
  463       assert_xpath '(//ul/li)[2]/p[text()="list item 2"]', output, 1
  464     end
  465 
  466     test "a literal paragraph with a line that appears as a list item that is followed by a continuation should create two blocks" do
  467       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  468       input = <<~EOS
  469       * Foo
  470       +
  471         literal
  472       . still literal
  473       +
  474       para
  475 
  476       * Bar
  477       EOS
  478       output = convert_string input
  479       assert_xpath '//ul', output, 1
  480       assert_xpath '//ul/li', output, 2
  481       assert_xpath '(//ul/li)[1]/p[text() = "Foo"]', output, 1
  482       assert_xpath '(//ul/li)[1]/*[@class="literalblock"]', output, 1
  483       assert_xpath '(//ul/li)[1]/p/following-sibling::*[@class="literalblock"]', output, 1
  484       assert_xpath %(((//ul/li)[1]/*[@class="literalblock"])[1]//pre[text() = "  literal\n. still literal"]), output, 1
  485       assert_xpath '(//ul/li)[1]/*[@class="literalblock"]/following-sibling::*[@class="paragraph"]', output, 1
  486       assert_xpath '(//ul/li)[1]/*[@class="literalblock"]/following-sibling::*[@class="paragraph"]/p[text()="para"]', output, 1
  487     end
  488 
  489     test "consecutive literal paragraph offset by blank lines in list content are appended as a literal blocks" do
  490       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  491       input = <<~EOS
  492       List
  493       ====
  494 
  495       - Foo
  496 
  497         literal
  498 
  499         more
  500         literal
  501 
  502       - Boo
  503       - Blech
  504       EOS
  505       output = convert_string input
  506       assert_xpath '//ul', output, 1
  507       assert_xpath '//ul/li', output, 3
  508       assert_xpath '(//ul/li)[1]/p[text() = "Foo"]', output, 1
  509       assert_xpath '(//ul/li)[1]/*[@class="literalblock"]', output, 2
  510       assert_xpath '(//ul/li)[1]/p/following-sibling::*[@class="literalblock"]', output, 2
  511       assert_xpath '((//ul/li)[1]/*[@class="literalblock"])[1]//pre[text()="literal"]', output, 1
  512       assert_xpath "((//ul/li)[1]/*[@class='literalblock'])[2]//pre[text()='more\nliteral']", output, 1
  513     end
  514 
  515     test "a literal paragraph without a trailing blank line consumes following list items" do
  516       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  517       input = <<~EOS
  518       List
  519       ====
  520 
  521       - Foo
  522 
  523         literal
  524       - Boo
  525       - Blech
  526       EOS
  527       output = convert_string input
  528       assert_xpath '//ul', output, 1
  529       assert_xpath '//ul/li', output, 1
  530       assert_xpath '(//ul/li)[1]/p[text() = "Foo"]', output, 1
  531       assert_xpath '(//ul/li)[1]/*[@class="literalblock"]', output, 1
  532       assert_xpath '(//ul/li)[1]/p/following-sibling::*[@class="literalblock"]', output, 1
  533       assert_xpath "((//ul/li)[1]/*[@class='literalblock'])[1]//pre[text() = '  literal\n- Boo\n- Blech']", output, 1
  534     end
  535 
  536     test "asterisk elements with no blank lines" do
  537       input = <<~'EOS'
  538       List
  539       ====
  540 
  541       * Foo
  542       * Boo
  543       * Blech
  544       EOS
  545       output = convert_string input
  546       assert_xpath '//ul', output, 1
  547       assert_xpath '//ul/li', output, 3
  548     end
  549 
  550     test 'indented asterisk elements using spaces' do
  551       input = <<~EOS
  552       \x20* Foo
  553       \x20* Boo
  554       \x20* Blech
  555       EOS
  556       output = convert_string input
  557       assert_xpath '//ul', output, 1
  558       assert_xpath '//ul/li', output, 3
  559     end
  560 
  561     test 'indented unicode bullet elements using spaces' do
  562       input = <<~EOS
  563       \x20• Foo
  564       \x20• Boo
  565       \x20• Blech
  566       EOS
  567       output = convert_string input
  568       assert_xpath '//ul', output, 1
  569       assert_xpath '//ul/li', output, 3
  570     end
  571 
  572     test 'indented asterisk elements using tabs' do
  573       input = <<~EOS
  574       \t*\tFoo
  575       \t*\tBoo
  576       \t*\tBlech
  577       EOS
  578       output = convert_string input
  579       assert_xpath '//ul', output, 1
  580       assert_xpath '//ul/li', output, 3
  581     end
  582 
  583     test 'should represent block style as style class' do
  584       ['disc', 'square', 'circle'].each do |style|
  585         input = <<~EOS
  586         [#{style}]
  587         * a
  588         * b
  589         * c
  590         EOS
  591         output = convert_string_to_embedded input
  592         assert_css ".ulist.#{style}", output, 1
  593         assert_css ".ulist.#{style} ul.#{style}", output, 1
  594       end
  595     end
  596 
  597     test "asterisk elements separated by blank lines should merge lists" do
  598       input = <<~'EOS'
  599       List
  600       ====
  601 
  602       * Foo
  603 
  604       * Boo
  605 
  606 
  607       * Blech
  608       EOS
  609       output = convert_string input
  610       assert_xpath '//ul', output, 1
  611       assert_xpath '//ul/li', output, 3
  612     end
  613 
  614     test 'asterisk elements with interspersed line comments should be skipped and not break list' do
  615       input = <<~'EOS'
  616       == List
  617 
  618       * Foo
  619       // line comment
  620       // another line comment
  621       * Boo
  622       // line comment
  623       more text
  624       // another line comment
  625       * Blech
  626       EOS
  627       output = convert_string_to_embedded input
  628       assert_xpath '//ul', output, 1
  629       assert_xpath '//ul/li', output, 3
  630       assert_xpath %((//ul/li)[2]/p[text()="Boo\nmore text"]), output, 1
  631     end
  632 
  633     test "asterisk elements separated by a line comment offset by blank lines should not merge lists" do
  634       input = <<~'EOS'
  635       List
  636       ====
  637 
  638       * Foo
  639       * Boo
  640 
  641       //
  642 
  643       * Blech
  644       EOS
  645       output = convert_string input
  646       assert_xpath '//ul', output, 2
  647       assert_xpath '(//ul)[1]/li', output, 2
  648       assert_xpath '(//ul)[2]/li', output, 1
  649     end
  650 
  651     test "asterisk elements separated by a block title offset by a blank line should not merge lists" do
  652       input = <<~'EOS'
  653       List
  654       ====
  655 
  656       * Foo
  657       * Boo
  658 
  659       .Also
  660       * Blech
  661       EOS
  662       output = convert_string input
  663       assert_xpath '//ul', output, 2
  664       assert_xpath '(//ul)[1]/li', output, 2
  665       assert_xpath '(//ul)[2]/li', output, 1
  666       assert_xpath '(//ul)[2]/preceding-sibling::*[@class = "title"][text() = "Also"]', output, 1
  667     end
  668 
  669     test "asterisk elements separated by an attribute entry offset by a blank line should not merge lists" do
  670       input = <<~'EOS'
  671       == List
  672 
  673       * Foo
  674       * Boo
  675 
  676       :foo: bar
  677       * Blech
  678       EOS
  679       output = convert_string_to_embedded input
  680       assert_xpath '//ul', output, 2
  681       assert_xpath '(//ul)[1]/li', output, 2
  682       assert_xpath '(//ul)[2]/li', output, 1
  683     end
  684 
  685     test "list should terminate before next lower section heading" do
  686       input = <<~'EOS'
  687       List
  688       ====
  689 
  690       * first
  691       item
  692       * second
  693       item
  694 
  695       == Section
  696       EOS
  697       output = convert_string input
  698       assert_xpath '//ul', output, 1
  699       assert_xpath '//ul/li', output, 2
  700       assert_xpath '//h2[text() = "Section"]', output, 1
  701     end
  702 
  703     test "list should terminate before next lower section heading with implicit id" do
  704       input = <<~'EOS'
  705       List
  706       ====
  707 
  708       * first
  709       item
  710       * second
  711       item
  712 
  713       [[sec]]
  714       == Section
  715       EOS
  716       output = convert_string input
  717       assert_xpath '//ul', output, 1
  718       assert_xpath '//ul/li', output, 2
  719       assert_xpath '//h2[@id = "sec"][text() = "Section"]', output, 1
  720     end
  721 
  722     test 'should not find section title immediately below last list item' do
  723       input = <<~'EOS'
  724       * first
  725       * second
  726       == Not a section
  727       EOS
  728 
  729       output = convert_string_to_embedded input
  730       assert_css 'ul', output, 1
  731       assert_css 'ul > li', output, 2
  732       assert_css 'h2', output, 0
  733       assert_includes output, '== Not a section'
  734       assert_xpath %((//li)[2]/p[text() = "second\n== Not a section"]), output, 1
  735     end
  736 
  737     test 'should match trailing line separator in text of list item' do
  738       input = <<~EOS.chop
  739       * a
  740       * b#{decode_char 8232}
  741       * c
  742       EOS
  743 
  744       output = convert_string input
  745       assert_css 'li', output, 3
  746       assert_xpath %((//li)[2]/p[text()="b#{decode_char 8232}"]), output, 1
  747     end
  748 
  749     test 'should match line separator in text of list item' do
  750       input = <<~EOS.chop
  751       * a
  752       * b#{decode_char 8232}b
  753       * c
  754       EOS
  755 
  756       output = convert_string input
  757       assert_css 'li', output, 3
  758       assert_xpath %((//li)[2]/p[text()="b#{decode_char 8232}b"]), output, 1
  759     end
  760   end
  761 
  762   context "Lists with inline markup" do
  763     test "quoted text" do
  764       input = <<~'EOS'
  765       List
  766       ====
  767 
  768       - I am *strong*.
  769       - I am _stressed_.
  770       - I am `flexible`.
  771       EOS
  772       output = convert_string input
  773       assert_xpath '//ul', output, 1
  774       assert_xpath '//ul/li', output, 3
  775       assert_xpath '(//ul/li)[1]//strong', output, 1
  776       assert_xpath '(//ul/li)[2]//em', output, 1
  777       assert_xpath '(//ul/li)[3]//code', output, 1
  778     end
  779 
  780     test "attribute substitutions" do
  781       input = <<~'EOS'
  782       List
  783       ====
  784       :foo: bar
  785 
  786       - side a {vbar} side b
  787       - Take me to a {foo}.
  788       EOS
  789       output = convert_string input
  790       assert_xpath '//ul', output, 1
  791       assert_xpath '//ul/li', output, 2
  792       assert_xpath '(//ul/li)[1]//p[text() = "side a | side b"]', output, 1
  793       assert_xpath '(//ul/li)[2]//p[text() = "Take me to a bar."]', output, 1
  794     end
  795 
  796     test "leading dot is treated as text not block title" do
  797       input = <<~'EOS'
  798       * .first
  799       * .second
  800       * .third
  801       EOS
  802       output = convert_string input
  803       assert_xpath '//ul', output, 1
  804       assert_xpath '//ul/li', output, 3
  805       %w(.first .second .third).each_with_index do |text, index|
  806         assert_xpath "(//ul/li)[#{index + 1}]//p[text() = '#{text}']", output, 1
  807       end
  808     end
  809 
  810     test "word ending sentence on continuing line not treated as a list item" do
  811       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
  812       input = <<~EOS
  813       A. This is the story about
  814          AsciiDoc. It begins here.
  815       B. And it ends here.
  816       EOS
  817       output = convert_string input
  818       assert_xpath '//ol', output, 1
  819       assert_xpath '//ol/li', output, 2
  820     end
  821 
  822     test 'should discover anchor at start of unordered list item text and register it as a reference' do
  823       input = <<~'EOS'
  824       The highest peak in the Front Range is <<grays-peak>>, which tops <<mount-evans>> by just a few feet.
  825 
  826       * [[mount-evans,Mount Evans]]At 14,271 feet, Mount Evans is the highest summit of the Chicago Peaks in the Front Range of the Rocky Mountains.
  827       * [[grays-peak,Grays Peak]]
  828       Grays Peak rises to 14,278 feet, making it the highest summit in the Front Range of the Rocky Mountains.
  829       * Longs Peak is a 14,259-foot high, prominent mountain summit in the northern Front Range of the Rocky Mountains.
  830       * Pikes Peak is the highest summit of the southern Front Range of the Rocky Mountains at 14,115 feet.
  831       EOS
  832 
  833       doc = document_from_string input
  834       refs = doc.catalog[:refs]
  835       assert refs.key?('mount-evans')
  836       assert refs.key?('grays-peak')
  837       output = doc.convert standalone: false
  838       assert_xpath '(//p)[1]/a[@href="#grays-peak"][text()="Grays Peak"]', output, 1
  839       assert_xpath '(//p)[1]/a[@href="#mount-evans"][text()="Mount Evans"]', output, 1
  840     end
  841 
  842     test 'should discover anchor at start of ordered list item text and register it as a reference' do
  843       input = <<~'EOS'
  844       This is a cross-reference to <<step-2>>.
  845       This is a cross-reference to <<step-4>>.
  846 
  847       . Ordered list, item 1, without anchor
  848       . [[step-2,Step 2]]Ordered list, item 2, with anchor
  849       . Ordered list, item 3, without anchor
  850       . [[step-4,Step 4]]Ordered list, item 4, with anchor
  851       EOS
  852 
  853       doc = document_from_string input
  854       refs = doc.catalog[:refs]
  855       assert refs.key?('step-2')
  856       assert refs.key?('step-4')
  857       output = doc.convert standalone: false
  858       assert_xpath '(//p)[1]/a[@href="#step-2"][text()="Step 2"]', output, 1
  859       assert_xpath '(//p)[1]/a[@href="#step-4"][text()="Step 4"]', output, 1
  860     end
  861 
  862     test 'should discover anchor at start of callout list item text and register it as a reference' do
  863       input = <<~'EOS'
  864       This is a cross-reference to <<url-mapping>>.
  865 
  866       [source,ruby]
  867       ----
  868       require 'sinatra' <1>
  869 
  870       get '/hi' do <2> <3>
  871         "Hello World!"
  872       end
  873       ----
  874       <1> Library import
  875       <2> [[url-mapping,url mapping]]URL mapping
  876       <3> Response block
  877       EOS
  878 
  879       doc = document_from_string input
  880       refs = doc.catalog[:refs]
  881       assert refs.key?('url-mapping')
  882       output = doc.convert standalone: false
  883       assert_xpath '(//p)[1]/a[@href="#url-mapping"][text()="url mapping"]', output, 1
  884     end
  885   end
  886 
  887   context "Nested lists" do
  888     test "asterisk element mixed with dash elements should be nested" do
  889       input = <<~'EOS'
  890       List
  891       ====
  892 
  893       - Foo
  894       * Boo
  895       - Blech
  896       EOS
  897       output = convert_string input
  898       assert_xpath '//ul', output, 2
  899       assert_xpath '//ul/li', output, 3
  900       assert_xpath '(//ul)[1]/li', output, 2
  901       assert_xpath '(//ul)[1]/li//ul/li', output, 1
  902     end
  903 
  904     test "dash element mixed with asterisks elements should be nested" do
  905       input = <<~'EOS'
  906       List
  907       ====
  908 
  909       * Foo
  910       - Boo
  911       * Blech
  912       EOS
  913       output = convert_string input
  914       assert_xpath '//ul', output, 2
  915       assert_xpath '//ul/li', output, 3
  916       assert_xpath '(//ul)[1]/li', output, 2
  917       assert_xpath '(//ul)[1]/li//ul/li', output, 1
  918     end
  919 
  920     test "lines prefixed with alternating list markers separated by blank lines should be nested" do
  921       input = <<~'EOS'
  922       List
  923       ====
  924 
  925       - Foo
  926 
  927       * Boo
  928 
  929 
  930       - Blech
  931       EOS
  932       output = convert_string input
  933       assert_xpath '//ul', output, 2
  934       assert_xpath '//ul/li', output, 3
  935       assert_xpath '(//ul)[1]/li', output, 2
  936       assert_xpath '(//ul)[1]/li//ul/li', output, 1
  937     end
  938 
  939     test "nested elements (2) with asterisks" do
  940       input = <<~'EOS'
  941       List
  942       ====
  943 
  944       * Foo
  945       ** Boo
  946       * Blech
  947       EOS
  948       output = convert_string input
  949       assert_xpath '//ul', output, 2
  950       assert_xpath '//ul/li', output, 3
  951       assert_xpath '(//ul)[1]/li', output, 2
  952       assert_xpath '(//ul)[1]/li//ul/li', output, 1
  953     end
  954 
  955     test "nested elements (3) with asterisks" do
  956       input = <<~'EOS'
  957       List
  958       ====
  959 
  960       * Foo
  961       ** Boo
  962       *** Snoo
  963       * Blech
  964       EOS
  965       output = convert_string input
  966       assert_xpath '//ul', output, 3
  967       assert_xpath '(//ul)[1]/li', output, 2
  968       assert_xpath '((//ul)[1]/li//ul)[1]/li', output, 1
  969       assert_xpath '(((//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
  970     end
  971 
  972     test "nested elements (4) with asterisks" do
  973       input = <<~'EOS'
  974       List
  975       ====
  976 
  977       * Foo
  978       ** Boo
  979       *** Snoo
  980       **** Froo
  981       * Blech
  982       EOS
  983       output = convert_string input
  984       assert_xpath '//ul', output, 4
  985       assert_xpath '(//ul)[1]/li', output, 2
  986       assert_xpath '((//ul)[1]/li//ul)[1]/li', output, 1
  987       assert_xpath '(((//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
  988       assert_xpath '((((//ul)[1]/li//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
  989     end
  990 
  991     test "nested elements (5) with asterisks" do
  992       input = <<~'EOS'
  993       List
  994       ====
  995 
  996       * Foo
  997       ** Boo
  998       *** Snoo
  999       **** Froo
 1000       ***** Groo
 1001       * Blech
 1002       EOS
 1003       output = convert_string input
 1004       assert_xpath '//ul', output, 5
 1005       assert_xpath '(//ul)[1]/li', output, 2
 1006       assert_xpath '((//ul)[1]/li//ul)[1]/li', output, 1
 1007       assert_xpath '(((//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
 1008       assert_xpath '((((//ul)[1]/li//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
 1009       assert_xpath '(((((//ul)[1]/li//ul)[1]/li//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
 1010     end
 1011 
 1012     test 'nested arbitrary depth with asterisks' do
 1013       input = []
 1014       ('a'..'z').each_with_index do |ch, i|
 1015         input << %(#{'*' * (i + 1)} #{ch})
 1016       end
 1017       output = convert_string_to_embedded input.join(%(\n))
 1018       refute_includes output, '*'
 1019       assert_css 'li', output, 26
 1020     end
 1021 
 1022     test 'level of unordered list should match section level' do
 1023       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 1024       input = <<~EOS
 1025       == Parent Section
 1026 
 1027       * item 1.1
 1028        ** item 2.1
 1029         *** item 3.1
 1030        ** item 2.2
 1031       * item 1.2
 1032 
 1033       === Nested Section
 1034 
 1035       * item 1.1
 1036       EOS
 1037 
 1038       doc = document_from_string input
 1039       lists = doc.find_by context: :ulist
 1040       assert_equal 1, lists[0].level
 1041       assert_equal 1, lists[1].level
 1042       assert_equal 1, lists[2].level
 1043       assert_equal 2, lists[3].level
 1044     end
 1045 
 1046     test 'does not recognize lists with repeating unicode bullets' do
 1047       input = '•• Boo'
 1048       output = convert_string input
 1049       assert_xpath '//ul', output, 0
 1050       assert_includes output, '•'
 1051     end
 1052 
 1053     test "nested ordered elements (2)" do
 1054       input = <<~'EOS'
 1055       List
 1056       ====
 1057 
 1058       . Foo
 1059       .. Boo
 1060       . Blech
 1061       EOS
 1062       output = convert_string input
 1063       assert_xpath '//ol', output, 2
 1064       assert_xpath '//ol/li', output, 3
 1065       assert_xpath '(//ol)[1]/li', output, 2
 1066       assert_xpath '(//ol)[1]/li//ol/li', output, 1
 1067     end
 1068 
 1069     test "nested ordered elements (3)" do
 1070       input = <<~'EOS'
 1071       List
 1072       ====
 1073 
 1074       . Foo
 1075       .. Boo
 1076       ... Snoo
 1077       . Blech
 1078       EOS
 1079       output = convert_string input
 1080       assert_xpath '//ol', output, 3
 1081       assert_xpath '(//ol)[1]/li', output, 2
 1082       assert_xpath '((//ol)[1]/li//ol)[1]/li', output, 1
 1083       assert_xpath '(((//ol)[1]/li//ol)[1]/li//ol)[1]/li', output, 1
 1084     end
 1085 
 1086     test 'nested arbitrary depth with dot marker' do
 1087       input = []
 1088       ('a'..'z').each_with_index do |ch, i|
 1089         input << %(#{'.' * (i + 1)} #{ch})
 1090       end
 1091       output = convert_string_to_embedded input.join(%(\n))
 1092       refute_includes output, '.'
 1093       assert_css 'li', output, 26
 1094     end
 1095 
 1096     test 'level of ordered list should match section level' do
 1097       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 1098       input = <<~EOS
 1099       == Parent Section
 1100 
 1101       . item 1.1
 1102        .. item 2.1
 1103         ... item 3.1
 1104        .. item 2.2
 1105       . item 1.2
 1106 
 1107       === Nested Section
 1108 
 1109       . item 1.1
 1110       EOS
 1111 
 1112       doc = document_from_string input
 1113       lists = doc.find_by context: :olist
 1114       assert_equal 1, lists[0].level
 1115       assert_equal 1, lists[1].level
 1116       assert_equal 1, lists[2].level
 1117       assert_equal 2, lists[3].level
 1118     end
 1119 
 1120     test "nested unordered inside ordered elements" do
 1121       input = <<~'EOS'
 1122       List
 1123       ====
 1124 
 1125       . Foo
 1126       * Boo
 1127       . Blech
 1128       EOS
 1129       output = convert_string input
 1130       assert_xpath '//ol', output, 1
 1131       assert_xpath '//ul', output, 1
 1132       assert_xpath '(//ol)[1]/li', output, 2
 1133       assert_xpath '((//ol)[1]/li//ul)[1]/li', output, 1
 1134     end
 1135 
 1136     test "nested ordered inside unordered elements" do
 1137       input = <<~'EOS'
 1138       List
 1139       ====
 1140 
 1141       * Foo
 1142       . Boo
 1143       * Blech
 1144       EOS
 1145       output = convert_string input
 1146       assert_xpath '//ul', output, 1
 1147       assert_xpath '//ol', output, 1
 1148       assert_xpath '(//ul)[1]/li', output, 2
 1149       assert_xpath '((//ul)[1]/li//ol)[1]/li', output, 1
 1150     end
 1151 
 1152     test 'three levels of alternating unordered and ordered elements' do
 1153       input = <<~'EOS'
 1154       == Lists
 1155 
 1156       * bullet 1
 1157       . numbered 1.1
 1158       ** bullet 1.1.1
 1159       * bullet 2
 1160       EOS
 1161 
 1162       output = convert_string_to_embedded input
 1163       assert_css '.ulist', output, 2
 1164       assert_css '.olist', output, 1
 1165       assert_css '.ulist > ul > li > p', output, 3
 1166       assert_css '.ulist > ul > li > p + .olist', output, 1
 1167       assert_css '.ulist > ul > li > p + .olist > ol > li > p', output, 1
 1168       assert_css '.ulist > ul > li > p + .olist > ol > li > p + .ulist', output, 1
 1169       assert_css '.ulist > ul > li > p + .olist > ol > li > p + .ulist > ul > li > p', output, 1
 1170       assert_css '.ulist > ul > li + li > p', output, 1
 1171     end
 1172 
 1173     test "lines with alternating markers of unordered and ordered list types separated by blank lines should be nested" do
 1174       input = <<~'EOS'
 1175       List
 1176       ====
 1177 
 1178       * Foo
 1179 
 1180       . Boo
 1181 
 1182 
 1183       * Blech
 1184       EOS
 1185       output = convert_string input
 1186       assert_xpath '//ul', output, 1
 1187       assert_xpath '//ol', output, 1
 1188       assert_xpath '(//ul)[1]/li', output, 2
 1189       assert_xpath '((//ul)[1]/li//ol)[1]/li', output, 1
 1190     end
 1191 
 1192     test 'list item with literal content should not consume nested list of different type' do
 1193       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 1194       input = <<~EOS
 1195       List
 1196       ====
 1197 
 1198       - bullet
 1199 
 1200         literal
 1201         but not
 1202         hungry
 1203 
 1204       . numbered
 1205       EOS
 1206       output = convert_string input
 1207       assert_xpath '//ul', output, 1
 1208       assert_xpath '//li', output, 2
 1209       assert_xpath '//ul//ol', output, 1
 1210       assert_xpath '//ul/li/p', output, 1
 1211       assert_xpath '//ul/li/p[text()="bullet"]', output, 1
 1212       assert_xpath '//ul/li/p/following-sibling::*[@class="literalblock"]', output, 1
 1213       assert_xpath %(//ul/li/p/following-sibling::*[@class="literalblock"]//pre[text()="literal\nbut not\nhungry"]), output, 1
 1214       assert_xpath '//*[@class="literalblock"]/following-sibling::*[@class="olist arabic"]', output, 1
 1215       assert_xpath '//*[@class="literalblock"]/following-sibling::*[@class="olist arabic"]//p[text()="numbered"]', output, 1
 1216     end
 1217 
 1218     test 'nested list item does not eat the title of the following detached block' do
 1219       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 1220       input = <<~EOS
 1221       List
 1222       ====
 1223 
 1224       - bullet
 1225         * nested bullet 1
 1226         * nested bullet 2
 1227 
 1228       .Title
 1229       ....
 1230       literal
 1231       ....
 1232       EOS
 1233       # use convert_string so we can match all ulists easier
 1234       output = convert_string input
 1235       assert_xpath '//*[@class="ulist"]/ul', output, 2
 1236       assert_xpath '(//*[@class="ulist"])[1]/following-sibling::*[@class="literalblock"]', output, 1
 1237       assert_xpath '(//*[@class="ulist"])[1]/following-sibling::*[@class="literalblock"]/*[@class="title"]', output, 1
 1238     end
 1239 
 1240     test "lines with alternating markers of bulleted and description list types separated by blank lines should be nested" do
 1241       input = <<~'EOS'
 1242       List
 1243       ====
 1244 
 1245       * Foo
 1246 
 1247       term1:: def1
 1248 
 1249       * Blech
 1250       EOS
 1251       output = convert_string input
 1252       assert_xpath '//ul', output, 1
 1253       assert_xpath '//dl', output, 1
 1254       assert_xpath '//ul[1]/li', output, 2
 1255       assert_xpath '//ul[1]/li//dl[1]/dt', output, 1
 1256       assert_xpath '//ul[1]/li//dl[1]/dd', output, 1
 1257     end
 1258 
 1259     test "nested ordered with attribute inside unordered elements" do
 1260       input = <<~'EOS'
 1261       Blah
 1262       ====
 1263 
 1264       * Foo
 1265       [start=2]
 1266       . Boo
 1267       * Blech
 1268       EOS
 1269       output = convert_string input
 1270       assert_xpath '//ul', output, 1
 1271       assert_xpath '//ol', output, 1
 1272       assert_xpath '(//ul)[1]/li', output, 2
 1273       assert_xpath '((//ul)[1]/li//ol)[1][@start = 2]/li', output, 1
 1274     end
 1275   end
 1276 
 1277   context "List continuations" do
 1278     test "adjacent list continuation line attaches following paragraph" do
 1279       input = <<~'EOS'
 1280       Lists
 1281       =====
 1282 
 1283       * Item one, paragraph one
 1284       +
 1285       Item one, paragraph two
 1286       +
 1287       * Item two
 1288       EOS
 1289       output = convert_string input
 1290       assert_xpath '//ul', output, 1
 1291       assert_xpath '//ul/li', output, 2
 1292       assert_xpath '//ul/li[1]/p', output, 1
 1293       assert_xpath '//ul/li[1]//p', output, 2
 1294       assert_xpath '//ul/li[1]/p[text() = "Item one, paragraph one"]', output, 1
 1295       assert_xpath '//ul/li[1]/*[@class = "paragraph"]/p[text() = "Item one, paragraph two"]', output, 1
 1296     end
 1297 
 1298     test "adjacent list continuation line attaches following block" do
 1299       input = <<~'EOS'
 1300       Lists
 1301       =====
 1302 
 1303       * Item one, paragraph one
 1304       +
 1305       ....
 1306       Item one, literal block
 1307       ....
 1308       +
 1309       * Item two
 1310       EOS
 1311       output = convert_string input
 1312       assert_xpath '//ul', output, 1
 1313       assert_xpath '//ul/li', output, 2
 1314       assert_xpath '//ul/li[1]/p', output, 1
 1315       assert_xpath '(//ul/li[1]/p/following-sibling::*)[1][@class = "literalblock"]', output, 1
 1316     end
 1317 
 1318     test 'adjacent list continuation line attaches following block with block attributes' do
 1319       input = <<~'EOS'
 1320       Lists
 1321       =====
 1322 
 1323       * Item one, paragraph one
 1324       +
 1325       :foo: bar
 1326       [[beck]]
 1327       .Read the following aloud to yourself
 1328       [source, ruby]
 1329       ----
 1330       5.times { print "Odelay!" }
 1331       ----
 1332 
 1333       * Item two
 1334       EOS
 1335       output = convert_string input
 1336       assert_xpath '//ul', output, 1
 1337       assert_xpath '//ul/li', output, 2
 1338       assert_xpath '//ul/li[1]/p', output, 1
 1339       assert_xpath '(//ul/li[1]/p/following-sibling::*)[1][@id="beck"][@class = "listingblock"]', output, 1
 1340       assert_xpath '(//ul/li[1]/p/following-sibling::*)[1][@id="beck"]/div[@class="title"][starts-with(text(),"Read")]', output, 1
 1341       assert_xpath '(//ul/li[1]/p/following-sibling::*)[1][@id="beck"]//code[@data-lang="ruby"][starts-with(text(),"5.times")]', output, 1
 1342     end
 1343 
 1344     test 'trailing block attribute line attached by continuation should not create block' do
 1345       input = <<~'EOS'
 1346       Lists
 1347       =====
 1348 
 1349       * Item one, paragraph one
 1350       +
 1351       [source]
 1352 
 1353       * Item two
 1354       EOS
 1355       output = convert_string input
 1356       assert_xpath '//ul', output, 1
 1357       assert_xpath '//ul/li', output, 2
 1358       assert_xpath '//ul/li[1]/*', output, 1
 1359       assert_xpath '//ul/li//*[@class="listingblock"]', output, 0
 1360     end
 1361 
 1362     test 'trailing block title line attached by continuation should not create block' do
 1363       input = <<~'EOS'
 1364     Lists
 1365     =====
 1366 
 1367     * Item one, paragraph one
 1368     +
 1369     .Disappears into the ether
 1370 
 1371     * Item two
 1372       EOS
 1373       output = convert_string input
 1374       assert_xpath '//ul', output, 1
 1375       assert_xpath '//ul/li', output, 2
 1376       assert_xpath '//ul/li[1]/*', output, 1
 1377     end
 1378 
 1379     test 'consecutive blocks in list continuation attach to list item' do
 1380       input = <<~'EOS'
 1381       Lists
 1382       =====
 1383 
 1384       * Item one, paragraph one
 1385       +
 1386       ....
 1387       Item one, literal block
 1388       ....
 1389       +
 1390       ____
 1391       Item one, quote block
 1392       ____
 1393       +
 1394       * Item two
 1395       EOS
 1396       output = convert_string_to_embedded input
 1397       assert_xpath '//ul', output, 1
 1398       assert_xpath '//ul/li', output, 2
 1399       assert_xpath '//ul/li[1]/p', output, 1
 1400       assert_xpath '(//ul/li[1]/p/following-sibling::*)[1][@class = "literalblock"]', output, 1
 1401       assert_xpath '(//ul/li[1]/p/following-sibling::*)[2][@class = "quoteblock"]', output, 1
 1402     end
 1403 
 1404     test 'list item with hanging indent followed by block attached by list continuation' do
 1405       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 1406       input = <<~EOS
 1407       == Lists
 1408 
 1409       . list item 1
 1410         continued
 1411       +
 1412       --
 1413       open block in list item 1
 1414       --
 1415 
 1416       . list item 2
 1417       EOS
 1418       output = convert_string_to_embedded input
 1419       assert_css 'ol', output, 1
 1420       assert_css 'ol li', output, 2
 1421       assert_xpath %((//ol/li)[1]/p[text()="list item 1\ncontinued"]), output, 1
 1422       assert_xpath '(//ol/li)[1]/p/following-sibling::*[@class="openblock"]', output, 1
 1423       assert_xpath '(//ol/li)[1]/p/following-sibling::*[@class="openblock"]//p[text()="open block in list item 1"]', output, 1
 1424       assert_xpath %((//ol/li)[2]/p[text()="list item 2"]), output, 1
 1425     end
 1426 
 1427     test 'list item paragraph in list item and nested list item' do
 1428       input = <<~'EOS'
 1429       == Lists
 1430 
 1431       . list item 1
 1432       +
 1433       list item 1 paragraph
 1434 
 1435       * nested list item
 1436       +
 1437       nested list item paragraph
 1438 
 1439       . list item 2
 1440       EOS
 1441       output = convert_string_to_embedded input
 1442       assert_css '.olist ol', output, 1
 1443       assert_css '.olist ol > li', output, 2
 1444       assert_css '.ulist ul', output, 1
 1445       assert_css '.ulist ul > li', output, 1
 1446       assert_xpath '(//ol/li)[1]/*', output, 3
 1447       assert_xpath '((//ol/li)[1]/*)[1]/self::p', output, 1
 1448       assert_xpath '((//ol/li)[1]/*)[1]/self::p[text()="list item 1"]', output, 1
 1449       assert_xpath '((//ol/li)[1]/*)[2]/self::div[@class="paragraph"]', output, 1
 1450       assert_xpath '((//ol/li)[1]/*)[3]/self::div[@class="ulist"]', output, 1
 1451       assert_xpath '((//ol/li)[1]/*)[3]/self::div[@class="ulist"]/ul/li', output, 1
 1452       assert_xpath '((//ol/li)[1]/*)[3]/self::div[@class="ulist"]/ul/li/p[text()="nested list item"]', output, 1
 1453       assert_xpath '((//ol/li)[1]/*)[3]/self::div[@class="ulist"]/ul/li/p/following-sibling::div[@class="paragraph"]', output, 1
 1454     end
 1455 
 1456     test 'trailing list continuations should attach to list items at respective levels' do
 1457       input = <<~'EOS'
 1458       == Lists
 1459 
 1460       . list item 1
 1461       +
 1462       * nested list item 1
 1463       * nested list item 2
 1464       +
 1465       paragraph for nested list item 2
 1466 
 1467       +
 1468       paragraph for list item 1
 1469 
 1470       . list item 2
 1471       EOS
 1472       output = convert_string_to_embedded input
 1473       assert_css '.olist ol', output, 1
 1474       assert_css '.olist ol > li', output, 2
 1475       assert_css '.ulist ul', output, 1
 1476       assert_css '.ulist ul > li', output, 2
 1477       assert_css '.olist .ulist', output, 1
 1478       assert_xpath '(//ol/li)[1]/*', output, 3
 1479       assert_xpath '((//ol/li)[1]/*)[1]/self::p', output, 1
 1480       assert_xpath '((//ol/li)[1]/*)[1]/self::p[text()="list item 1"]', output, 1
 1481       assert_xpath '((//ol/li)[1]/*)[2]/self::div[@class="ulist"]', output, 1
 1482       assert_xpath '((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li', output, 2
 1483       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/*', output, 2
 1484       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/p', output, 1
 1485       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/div[@class="paragraph"]', output, 1
 1486       assert_xpath '((//ol/li)[1]/*)[3]/self::div[@class="paragraph"]', output, 1
 1487     end
 1488 
 1489     test 'trailing list continuations should attach to list items of different types at respective levels' do
 1490       input = <<~'EOS'
 1491       == Lists
 1492 
 1493       * bullet 1
 1494       . numbered 1.1
 1495       ** bullet 1.1.1
 1496 
 1497       +
 1498       numbered 1.1 paragraph
 1499 
 1500       +
 1501       bullet 1 paragraph
 1502 
 1503       * bullet 2
 1504       EOS
 1505       output = convert_string_to_embedded input
 1506 
 1507       assert_xpath '(//ul)[1]/li', output, 2
 1508 
 1509       assert_xpath '((//ul)[1]/li[1])/*', output, 3
 1510       assert_xpath '(((//ul)[1]/li[1])/*)[1]/self::p[text()="bullet 1"]', output, 1
 1511       assert_xpath '(((//ul)[1]/li[1])/*)[2]/ol', output, 1
 1512       assert_xpath '(((//ul)[1]/li[1])/*)[3]/self::div[@class="paragraph"]/p[text()="bullet 1 paragraph"]', output, 1
 1513 
 1514       assert_xpath '((//ul)[1]/li)[1]/div/ol/li', output, 1
 1515       assert_xpath '((//ul)[1]/li)[1]/div/ol/li/*', output, 3
 1516       assert_xpath '(((//ul)[1]/li)[1]/div/ol/li/*)[1]/self::p[text()="numbered 1.1"]', output, 1
 1517       assert_xpath '(((//ul)[1]/li)[1]/div/ol/li/*)[2]/self::div[@class="ulist"]', output, 1
 1518       assert_xpath '(((//ul)[1]/li)[1]/div/ol/li/*)[3]/self::div[@class="paragraph"]/p[text()="numbered 1.1 paragraph"]', output, 1
 1519 
 1520       assert_xpath '((//ul)[1]/li)[1]/div/ol/li/div[@class="ulist"]/ul/li', output, 1
 1521       assert_xpath '((//ul)[1]/li)[1]/div/ol/li/div[@class="ulist"]/ul/li/*', output, 1
 1522       assert_xpath '((//ul)[1]/li)[1]/div/ol/li/div[@class="ulist"]/ul/li/p[text()="bullet 1.1.1"]', output, 1
 1523     end
 1524 
 1525     test 'repeated list continuations should attach to list items at respective levels' do
 1526       input = <<~'EOS'
 1527       == Lists
 1528 
 1529       . list item 1
 1530 
 1531       * nested list item 1
 1532       +
 1533       --
 1534       open block for nested list item 1
 1535       --
 1536       +
 1537       * nested list item 2
 1538       +
 1539       paragraph for nested list item 2
 1540 
 1541       +
 1542       paragraph for list item 1
 1543 
 1544       . list item 2
 1545       EOS
 1546       output = convert_string_to_embedded input
 1547       assert_css '.olist ol', output, 1
 1548       assert_css '.olist ol > li', output, 2
 1549       assert_css '.ulist ul', output, 1
 1550       assert_css '.ulist ul > li', output, 2
 1551       assert_css '.olist .ulist', output, 1
 1552       assert_xpath '(//ol/li)[1]/*', output, 3
 1553       assert_xpath '((//ol/li)[1]/*)[1]/self::p', output, 1
 1554       assert_xpath '((//ol/li)[1]/*)[1]/self::p[text()="list item 1"]', output, 1
 1555       assert_xpath '((//ol/li)[1]/*)[2]/self::div[@class="ulist"]', output, 1
 1556       assert_xpath '((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li', output, 2
 1557       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[1]/*', output, 2
 1558       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[1]/p', output, 1
 1559       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[1]/div[@class="openblock"]', output, 1
 1560       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/*', output, 2
 1561       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/p', output, 1
 1562       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/div[@class="paragraph"]', output, 1
 1563       assert_xpath '((//ol/li)[1]/*)[3]/self::div[@class="paragraph"]', output, 1
 1564     end
 1565 
 1566     test 'repeated list continuations attached directly to list item should attach to list items at respective levels' do
 1567       input = <<~'EOS'
 1568       == Lists
 1569 
 1570       . list item 1
 1571       +
 1572       * nested list item 1
 1573       +
 1574       --
 1575       open block for nested list item 1
 1576       --
 1577       +
 1578       * nested list item 2
 1579       +
 1580       paragraph for nested list item 2
 1581 
 1582       +
 1583       paragraph for list item 1
 1584 
 1585       . list item 2
 1586       EOS
 1587       output = convert_string_to_embedded input
 1588       assert_css '.olist ol', output, 1
 1589       assert_css '.olist ol > li', output, 2
 1590       assert_css '.ulist ul', output, 1
 1591       assert_css '.ulist ul > li', output, 2
 1592       assert_css '.olist .ulist', output, 1
 1593       assert_xpath '(//ol/li)[1]/*', output, 3
 1594       assert_xpath '((//ol/li)[1]/*)[1]/self::p', output, 1
 1595       assert_xpath '((//ol/li)[1]/*)[1]/self::p[text()="list item 1"]', output, 1
 1596       assert_xpath '((//ol/li)[1]/*)[2]/self::div[@class="ulist"]', output, 1
 1597       assert_xpath '((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li', output, 2
 1598       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[1]/*', output, 2
 1599       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[1]/p', output, 1
 1600       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[1]/div[@class="openblock"]', output, 1
 1601       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/*', output, 2
 1602       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/p', output, 1
 1603       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/div[@class="paragraph"]', output, 1
 1604       assert_xpath '((//ol/li)[1]/*)[3]/self::div[@class="paragraph"]', output, 1
 1605     end
 1606 
 1607     test 'repeated list continuations should attach to list items at respective levels ignoring blank lines' do
 1608       input = <<~'EOS'
 1609       == Lists
 1610 
 1611       . list item 1
 1612       +
 1613       * nested list item 1
 1614       +
 1615       --
 1616       open block for nested list item 1
 1617       --
 1618       +
 1619       * nested list item 2
 1620       +
 1621       paragraph for nested list item 2
 1622 
 1623 
 1624       +
 1625       paragraph for list item 1
 1626 
 1627       . list item 2
 1628       EOS
 1629       output = convert_string_to_embedded input
 1630       assert_css '.olist ol', output, 1
 1631       assert_css '.olist ol > li', output, 2
 1632       assert_css '.ulist ul', output, 1
 1633       assert_css '.ulist ul > li', output, 2
 1634       assert_css '.olist .ulist', output, 1
 1635       assert_xpath '(//ol/li)[1]/*', output, 3
 1636       assert_xpath '((//ol/li)[1]/*)[1]/self::p', output, 1
 1637       assert_xpath '((//ol/li)[1]/*)[1]/self::p[text()="list item 1"]', output, 1
 1638       assert_xpath '((//ol/li)[1]/*)[2]/self::div[@class="ulist"]', output, 1
 1639       assert_xpath '((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li', output, 2
 1640       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[1]/*', output, 2
 1641       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[1]/p', output, 1
 1642       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[1]/div[@class="openblock"]', output, 1
 1643       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/*', output, 2
 1644       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/p', output, 1
 1645       assert_xpath '(((//ol/li)[1]/*)[2]/self::div[@class="ulist"]/ul/li)[2]/div[@class="paragraph"]', output, 1
 1646       assert_xpath '((//ol/li)[1]/*)[3]/self::div[@class="paragraph"]', output, 1
 1647     end
 1648 
 1649     test 'trailing list continuations should ignore preceding blank lines' do
 1650       input = <<~'EOS'
 1651       == Lists
 1652 
 1653       * bullet 1
 1654       ** bullet 1.1
 1655       *** bullet 1.1.1
 1656       +
 1657       --
 1658       open block
 1659       --
 1660 
 1661 
 1662       +
 1663       bullet 1.1 paragraph
 1664 
 1665 
 1666       +
 1667       bullet 1 paragraph
 1668 
 1669       * bullet 2
 1670       EOS
 1671       output = convert_string_to_embedded input
 1672 
 1673       assert_xpath '((//ul)[1]/li[1])/*', output, 3
 1674       assert_xpath '(((//ul)[1]/li[1])/*)[1]/self::p[text()="bullet 1"]', output, 1
 1675       assert_xpath '(((//ul)[1]/li[1])/*)[2]/self::div[@class="ulist"]', output, 1
 1676       assert_xpath '(((//ul)[1]/li[1])/*)[3]/self::div[@class="paragraph"]/p[text()="bullet 1 paragraph"]', output, 1
 1677 
 1678       assert_xpath '((//ul)[1]/li)[1]/div[@class="ulist"]/ul/li', output, 1
 1679       assert_xpath '((//ul)[1]/li)[1]/div[@class="ulist"]/ul/li/*', output, 3
 1680       assert_xpath '(((//ul)[1]/li)[1]/div[@class="ulist"]/ul/li/*)[1]/self::p[text()="bullet 1.1"]', output, 1
 1681       assert_xpath '(((//ul)[1]/li)[1]/div[@class="ulist"]/ul/li/*)[2]/self::div[@class="ulist"]', output, 1
 1682       assert_xpath '(((//ul)[1]/li)[1]/div[@class="ulist"]/ul/li/*)[3]/self::div[@class="paragraph"]/p[text()="bullet 1.1 paragraph"]', output, 1
 1683 
 1684       assert_xpath '((//ul)[1]/li)[1]/div[@class="ulist"]/ul/li/div[@class="ulist"]/ul/li', output, 1
 1685       assert_xpath '((//ul)[1]/li)[1]/div[@class="ulist"]/ul/li/div[@class="ulist"]/ul/li/*', output, 2
 1686       assert_xpath '(((//ul)[1]/li)[1]/div[@class="ulist"]/ul/li/div[@class="ulist"]/ul/li/*)[1]/self::p', output, 1
 1687       assert_xpath '(((//ul)[1]/li)[1]/div[@class="ulist"]/ul/li/div[@class="ulist"]/ul/li/*)[2]/self::div[@class="openblock"]', output, 1
 1688     end
 1689 
 1690     test 'indented outline list item with different marker offset by a blank line should be recognized as a nested list' do
 1691       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 1692       input = <<~EOS
 1693       * item 1
 1694 
 1695         . item 1.1
 1696       +
 1697       attached paragraph
 1698 
 1699         . item 1.2
 1700       +
 1701       attached paragraph
 1702 
 1703       * item 2
 1704       EOS
 1705 
 1706       output = convert_string_to_embedded input
 1707 
 1708       assert_css 'ul', output, 1
 1709       assert_css 'ol', output, 1
 1710       assert_css 'ul ol', output, 1
 1711       assert_css 'ul > li', output, 2
 1712       assert_xpath '((//ul/li)[1]/*)', output, 2
 1713       assert_xpath '((//ul/li)[1]/*)[1]/self::p', output, 1
 1714       assert_xpath '((//ul/li)[1]/*)[2]/self::div/ol', output, 1
 1715       assert_xpath '((//ul/li)[1]/*)[2]/self::div/ol/li', output, 2
 1716       (1..2).each do |idx|
 1717         assert_xpath "(((//ul/li)[1]/*)[2]/self::div/ol/li)[#{idx}]/*", output, 2
 1718         assert_xpath "((((//ul/li)[1]/*)[2]/self::div/ol/li)[#{idx}]/*)[1]/self::p", output, 1
 1719         assert_xpath "((((//ul/li)[1]/*)[2]/self::div/ol/li)[#{idx}]/*)[2]/self::div[@class=\"paragraph\"]", output, 1
 1720       end
 1721     end
 1722 
 1723     test 'indented description list item inside outline list item offset by a blank line should be recognized as a nested list' do
 1724       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 1725       input = <<~EOS
 1726       * item 1
 1727 
 1728         term a:: description a
 1729       +
 1730       attached paragraph
 1731 
 1732         term b:: description b
 1733       +
 1734       attached paragraph
 1735 
 1736       * item 2
 1737       EOS
 1738 
 1739       output = convert_string_to_embedded input
 1740 
 1741       assert_css 'ul', output, 1
 1742       assert_css 'dl', output, 1
 1743       assert_css 'ul dl', output, 1
 1744       assert_css 'ul > li', output, 2
 1745       assert_xpath '((//ul/li)[1]/*)', output, 2
 1746       assert_xpath '((//ul/li)[1]/*)[1]/self::p', output, 1
 1747       assert_xpath '((//ul/li)[1]/*)[2]/self::div/dl', output, 1
 1748       assert_xpath '((//ul/li)[1]/*)[2]/self::div/dl/dt', output, 2
 1749       assert_xpath '((//ul/li)[1]/*)[2]/self::div/dl/dd', output, 2
 1750       (1..2).each do |idx|
 1751         assert_xpath "(((//ul/li)[1]/*)[2]/self::div/dl/dd)[#{idx}]/*", output, 2
 1752         assert_xpath "((((//ul/li)[1]/*)[2]/self::div/dl/dd)[#{idx}]/*)[1]/self::p", output, 1
 1753         assert_xpath "((((//ul/li)[1]/*)[2]/self::div/dl/dd)[#{idx}]/*)[2]/self::div[@class=\"paragraph\"]", output, 1
 1754       end
 1755     end
 1756 
 1757     # NOTE this is not consistent w/ AsciiDoc output, but this is some screwy input anyway
 1758 =begin
 1759     test "consecutive list continuation lines are folded" do
 1760       input = <<~'EOS'
 1761       Lists
 1762       =====
 1763 
 1764       * Item one, paragraph one
 1765       +
 1766       +
 1767       Item one, paragraph two
 1768       +
 1769       +
 1770       * Item two
 1771       +
 1772       +
 1773       EOS
 1774       output = convert_string input
 1775       assert_xpath '//ul', output, 1
 1776       assert_xpath '//ul/li', output, 2
 1777       assert_xpath '//ul/li[1]/p', output, 1
 1778       assert_xpath '//ul/li[1]//p', output, 2
 1779       assert_xpath '//ul/li[1]//p[text() = "Item one, paragraph one"]', output, 1
 1780       assert_xpath '//ul/li[1]//p[text() = "Item one, paragraph two"]', output, 1
 1781     end
 1782 =end
 1783 
 1784     test 'should warn if unterminated block is detected in list item' do
 1785       input = <<~'EOS'
 1786       * item
 1787       +
 1788       ====
 1789       example
 1790       * swallowed item
 1791       EOS
 1792 
 1793       using_memory_logger do |logger|
 1794         output = convert_string_to_embedded input
 1795         assert_xpath '//ul/li', output, 1
 1796         assert_xpath '//ul/li/*[@class="exampleblock"]', output, 1
 1797         assert_xpath %(//p[text()="example\n* swallowed item"]), output, 1
 1798         assert_message logger, :WARN, '<stdin>: line 3: unterminated example block', Hash
 1799       end
 1800     end
 1801   end
 1802 end
 1803 
 1804 context "Ordered lists (:olist)" do
 1805   context "Simple lists" do
 1806     test "dot elements with no blank lines" do
 1807       input = <<~'EOS'
 1808       List
 1809       ====
 1810 
 1811       . Foo
 1812       . Boo
 1813       . Blech
 1814       EOS
 1815       output = convert_string input
 1816       assert_xpath '//ol', output, 1
 1817       assert_xpath '//ol/li', output, 3
 1818     end
 1819 
 1820     test 'indented dot elements using spaces' do
 1821       input = <<~EOS
 1822       \x20. Foo
 1823       \x20. Boo
 1824       \x20. Blech
 1825       EOS
 1826       output = convert_string input
 1827       assert_xpath '//ol', output, 1
 1828       assert_xpath '//ol/li', output, 3
 1829     end
 1830 
 1831     test 'indented dot elements using tabs' do
 1832       input = <<~EOS
 1833       \t.\tFoo
 1834       \t.\tBoo
 1835       \t.\tBlech
 1836       EOS
 1837       output = convert_string input
 1838       assert_xpath '//ol', output, 1
 1839       assert_xpath '//ol/li', output, 3
 1840     end
 1841 
 1842     test 'should represent explicit role attribute as style class' do
 1843       input = <<~'EOS'
 1844       [role="dry"]
 1845       . Once
 1846       . Again
 1847       . Refactor!
 1848       EOS
 1849 
 1850       output = convert_string_to_embedded input
 1851       assert_css '.olist.arabic.dry', output, 1
 1852       assert_css '.olist ol.arabic', output, 1
 1853     end
 1854 
 1855     test 'should base list style on marker length rather than list depth' do
 1856       input = <<~'EOS'
 1857       ... parent
 1858       .. child
 1859       . grandchild
 1860       EOS
 1861 
 1862       output = convert_string_to_embedded input
 1863       assert_css '.olist.lowerroman', output, 1
 1864       assert_css '.olist.lowerroman .olist.loweralpha', output, 1
 1865       assert_css '.olist.lowerroman .olist.loweralpha .olist.arabic', output, 1
 1866     end
 1867 
 1868     test 'should allow list style to be specified explicitly when using markers with implicit style' do
 1869       input = <<~'EOS'
 1870       [loweralpha]
 1871       i) 1
 1872       ii) 2
 1873       iii) 3
 1874       EOS
 1875 
 1876       output = convert_string_to_embedded input
 1877       assert_css '.olist.loweralpha', output, 1
 1878       assert_css '.olist.lowerroman', output, 0
 1879     end
 1880 
 1881     test 'should represent custom numbering and explicit role attribute as style classes' do
 1882       input = <<~'EOS'
 1883       [loweralpha, role="dry"]
 1884       . Once
 1885       . Again
 1886       . Refactor!
 1887       EOS
 1888 
 1889       output = convert_string_to_embedded input
 1890       assert_css '.olist.loweralpha.dry', output, 1
 1891       assert_css '.olist ol.loweralpha', output, 1
 1892     end
 1893 
 1894     test 'should set reversed attribute on list if reversed option is set' do
 1895       input = <<~'EOS'
 1896       [%reversed, start=3]
 1897       . three
 1898       . two
 1899       . one
 1900       . blast off!
 1901       EOS
 1902 
 1903       output = convert_string_to_embedded input
 1904       assert_css 'ol[reversed][start="3"]', output, 1
 1905     end
 1906 
 1907     test 'should represent implicit role attribute as style class' do
 1908       input = <<~'EOS'
 1909       [.dry]
 1910       . Once
 1911       . Again
 1912       . Refactor!
 1913       EOS
 1914 
 1915       output = convert_string_to_embedded input
 1916       assert_css '.olist.arabic.dry', output, 1
 1917       assert_css '.olist ol.arabic', output, 1
 1918     end
 1919 
 1920     test 'should represent custom numbering and implicit role attribute as style classes' do
 1921       input = <<~'EOS'
 1922       [loweralpha.dry]
 1923       . Once
 1924       . Again
 1925       . Refactor!
 1926       EOS
 1927 
 1928       output = convert_string_to_embedded input
 1929       assert_css '.olist.loweralpha.dry', output, 1
 1930       assert_css '.olist ol.loweralpha', output, 1
 1931     end
 1932 
 1933     test "dot elements separated by blank lines should merge lists" do
 1934       input = <<~'EOS'
 1935       List
 1936       ====
 1937 
 1938       . Foo
 1939 
 1940       . Boo
 1941 
 1942 
 1943       . Blech
 1944       EOS
 1945       output = convert_string input
 1946       assert_xpath '//ol', output, 1
 1947       assert_xpath '//ol/li', output, 3
 1948     end
 1949 
 1950     test 'should escape special characters in all literal paragraphs attached to list item' do
 1951       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 1952       input = <<~EOS
 1953       . first item
 1954 
 1955         <code>text</code>
 1956 
 1957         more <code>text</code>
 1958 
 1959       . second item
 1960       EOS
 1961 
 1962       output = convert_string_to_embedded input
 1963       assert_css 'li', output, 2
 1964       assert_css 'code', output, 0
 1965       assert_css 'li:first-of-type > *', output, 3
 1966       assert_css 'li:first-of-type pre', output, 2
 1967       assert_xpath '((//li)[1]//pre)[1][text()="<code>text</code>"]', output, 1
 1968       assert_xpath '((//li)[1]//pre)[2][text()="more <code>text</code>"]', output, 1
 1969     end
 1970 
 1971     test 'dot elements with interspersed line comments should be skipped and not break list' do
 1972       input = <<~'EOS'
 1973       == List
 1974 
 1975       . Foo
 1976       // line comment
 1977       // another line comment
 1978       . Boo
 1979       // line comment
 1980       more text
 1981       // another line comment
 1982       . Blech
 1983       EOS
 1984       output = convert_string_to_embedded input
 1985       assert_xpath '//ol', output, 1
 1986       assert_xpath '//ol/li', output, 3
 1987       assert_xpath %((//ol/li)[2]/p[text()="Boo\nmore text"]), output, 1
 1988     end
 1989 
 1990     test "dot elements separated by line comment offset by blank lines should not merge lists" do
 1991       input = <<~'EOS'
 1992       List
 1993       ====
 1994 
 1995       . Foo
 1996       . Boo
 1997 
 1998       //
 1999 
 2000       . Blech
 2001       EOS
 2002       output = convert_string input
 2003       assert_xpath '//ol', output, 2
 2004       assert_xpath '(//ol)[1]/li', output, 2
 2005       assert_xpath '(//ol)[2]/li', output, 1
 2006     end
 2007 
 2008     test "dot elements separated by a block title offset by a blank line should not merge lists" do
 2009       input = <<~'EOS'
 2010       List
 2011       ====
 2012 
 2013       . Foo
 2014       . Boo
 2015 
 2016       .Also
 2017       . Blech
 2018       EOS
 2019       output = convert_string input
 2020       assert_xpath '//ol', output, 2
 2021       assert_xpath '(//ol)[1]/li', output, 2
 2022       assert_xpath '(//ol)[2]/li', output, 1
 2023       assert_xpath '(//ol)[2]/preceding-sibling::*[@class = "title"][text() = "Also"]', output, 1
 2024     end
 2025 
 2026     test "dot elements separated by an attribute entry offset by a blank line should not merge lists" do
 2027       input = <<~'EOS'
 2028       == List
 2029 
 2030       . Foo
 2031       . Boo
 2032 
 2033       :foo: bar
 2034       . Blech
 2035       EOS
 2036       output = convert_string_to_embedded input
 2037       assert_xpath '//ol', output, 2
 2038       assert_xpath '(//ol)[1]/li', output, 2
 2039       assert_xpath '(//ol)[2]/li', output, 1
 2040     end
 2041 
 2042     test 'should use start number in docbook5 backend' do
 2043       input = <<~'EOS'
 2044       == List
 2045 
 2046       [start=7]
 2047       . item 7
 2048       . item 8
 2049       EOS
 2050 
 2051       output = convert_string_to_embedded input, backend: 'docbook5'
 2052       assert_xpath '//orderedlist', output, 1
 2053       assert_xpath '(//orderedlist)/listitem', output, 2
 2054       assert_xpath '(//orderedlist)[@startingnumber = "7"]', output, 1
 2055     end
 2056 
 2057     test 'should match trailing line separator in text of list item' do
 2058       input = <<~EOS.chop
 2059       . a
 2060       . b#{decode_char 8232}
 2061       . c
 2062       EOS
 2063 
 2064       output = convert_string input
 2065       assert_css 'li', output, 3
 2066       assert_xpath %((//li)[2]/p[text()="b#{decode_char 8232}"]), output, 1
 2067     end
 2068 
 2069     test 'should match line separator in text of list item' do
 2070       input = <<~EOS.chop
 2071       . a
 2072       . b#{decode_char 8232}b
 2073       . c
 2074       EOS
 2075 
 2076       output = convert_string input
 2077       assert_css 'li', output, 3
 2078       assert_xpath %((//li)[2]/p[text()="b#{decode_char 8232}b"]), output, 1
 2079     end
 2080   end
 2081 
 2082   test 'should warn if explicit uppercase roman numerals in list are out of sequence' do
 2083     input = <<~'EOS'
 2084     I) one
 2085     III) three
 2086     EOS
 2087     using_memory_logger do |logger|
 2088       output = convert_string_to_embedded input
 2089       assert_xpath '//ol/li', output, 2
 2090       assert_message logger, :WARN, '<stdin>: line 2: list item index: expected II, got III', Hash
 2091     end
 2092   end
 2093 
 2094   test 'should warn if explicit lowercase roman numerals in list are out of sequence' do
 2095     input = <<~'EOS'
 2096     i) one
 2097     iii) three
 2098     EOS
 2099     using_memory_logger do |logger|
 2100       output = convert_string_to_embedded input
 2101       assert_xpath '//ol/li', output, 2
 2102       assert_message logger, :WARN, '<stdin>: line 2: list item index: expected ii, got iii', Hash
 2103     end
 2104   end
 2105 end
 2106 
 2107 context "Description lists (:dlist)" do
 2108   context "Simple lists" do
 2109     test 'should not parse a bare dlist delimiter as a dlist' do
 2110       input = '::'
 2111       output = convert_string_to_embedded input
 2112       assert_css 'dl', output, 0
 2113       assert_xpath '//p[text()="::"]', output, 1
 2114     end
 2115 
 2116     test 'should not parse an indented bare dlist delimiter as a dlist' do
 2117       input = ' ::'
 2118       output = convert_string_to_embedded input
 2119       assert_css 'dl', output, 0
 2120       assert_xpath '//pre[text()="::"]', output, 1
 2121     end
 2122 
 2123     test 'should parse a dlist delimiter preceded by a blank attribute as a dlist' do
 2124       input = '{blank}::'
 2125       output = convert_string_to_embedded input
 2126       assert_css 'dl', output, 1
 2127       assert_css 'dl > dt', output, 1
 2128       assert_css 'dl > dt:empty', output, 1
 2129     end
 2130 
 2131     test "single-line adjacent elements" do
 2132       input = <<~'EOS'
 2133       term1:: def1
 2134       term2:: def2
 2135       EOS
 2136       output = convert_string input
 2137       assert_xpath '//dl', output, 1
 2138       assert_xpath '//dl/dt', output, 2
 2139       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2140       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2141       assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2142       assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
 2143       assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2144     end
 2145 
 2146     test 'should parse sibling items using same rules' do
 2147       input = <<~'EOS'
 2148       term1;; ;; def1
 2149       term2;; ;; def2
 2150       EOS
 2151       output = convert_string input
 2152       assert_xpath '//dl', output, 1
 2153       assert_xpath '//dl/dt', output, 2
 2154       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2155       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2156       assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = ";; def1"]', output, 1
 2157       assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
 2158       assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = ";; def2"]', output, 1
 2159     end
 2160 
 2161     test 'should allow term to end with a semicolon when using double semicolon delimiter' do
 2162       input = <<~'EOS'
 2163       term;;; def
 2164       EOS
 2165       output = convert_string_to_embedded input
 2166       assert_css 'dl', output, 1
 2167       assert_css 'dl > dt', output, 1
 2168       assert_xpath '(//dl/dt)[1][text() = "term;"]', output, 1
 2169       assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def"]', output, 1
 2170     end
 2171 
 2172     test "single-line indented adjacent elements" do
 2173       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 2174       input = <<~EOS
 2175       term1:: def1
 2176        term2:: def2
 2177       EOS
 2178       output = convert_string input
 2179       assert_xpath '//dl', output, 1
 2180       assert_xpath '//dl/dt', output, 2
 2181       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2182       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2183       assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2184       assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
 2185       assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2186     end
 2187 
 2188     test "single-line indented adjacent elements with tabs" do
 2189       input = <<~EOS
 2190       term1::\tdef1
 2191       \tterm2::\tdef2
 2192       EOS
 2193       output = convert_string input
 2194       assert_xpath '//dl', output, 1
 2195       assert_xpath '//dl/dt', output, 2
 2196       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2197       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2198       assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2199       assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
 2200       assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2201     end
 2202 
 2203     test "single-line elements separated by blank line should create a single list" do
 2204       input = <<~'EOS'
 2205       term1:: def1
 2206 
 2207       term2:: def2
 2208       EOS
 2209       output = convert_string input
 2210       assert_xpath '//dl', output, 1
 2211       assert_xpath '//dl/dt', output, 2
 2212       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2213     end
 2214 
 2215     test "a line comment between elements should divide them into separate lists" do
 2216       input = <<~'EOS'
 2217       term1:: def1
 2218 
 2219       //
 2220 
 2221       term2:: def2
 2222       EOS
 2223       output = convert_string input
 2224       assert_xpath '//dl', output, 2
 2225       assert_xpath '//dl/dt', output, 2
 2226       assert_xpath '(//dl)[1]/dt', output, 1
 2227       assert_xpath '(//dl)[2]/dt', output, 1
 2228     end
 2229 
 2230     test "a ruler between elements should divide them into separate lists" do
 2231       input = <<~'EOS'
 2232       term1:: def1
 2233 
 2234       '''
 2235 
 2236       term2:: def2
 2237       EOS
 2238       output = convert_string input
 2239       assert_xpath '//dl', output, 2
 2240       assert_xpath '//dl/dt', output, 2
 2241       assert_xpath '//dl//hr', output, 0
 2242       assert_xpath '(//dl)[1]/dt', output, 1
 2243       assert_xpath '(//dl)[2]/dt', output, 1
 2244     end
 2245 
 2246     test "a block title between elements should divide them into separate lists" do
 2247       input = <<~'EOS'
 2248       term1:: def1
 2249 
 2250       .Some more
 2251       term2:: def2
 2252       EOS
 2253       output = convert_string input
 2254       assert_xpath '//dl', output, 2
 2255       assert_xpath '//dl/dt', output, 2
 2256       assert_xpath '(//dl)[1]/dt', output, 1
 2257       assert_xpath '(//dl)[2]/dt', output, 1
 2258       assert_xpath '(//dl)[2]/preceding-sibling::*[@class="title"][text() = "Some more"]', output, 1
 2259     end
 2260 
 2261     test "multi-line elements with paragraph content" do
 2262       input = <<~'EOS'
 2263       term1::
 2264       def1
 2265       term2::
 2266       def2
 2267       EOS
 2268       output = convert_string input
 2269       assert_xpath '//dl', output, 1
 2270       assert_xpath '//dl/dt', output, 2
 2271       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2272       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2273       assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2274       assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
 2275       assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2276     end
 2277 
 2278     test "multi-line elements with indented paragraph content" do
 2279       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 2280       input = <<~EOS
 2281       term1::
 2282        def1
 2283       term2::
 2284         def2
 2285       EOS
 2286       output = convert_string input
 2287       assert_xpath '//dl', output, 1
 2288       assert_xpath '//dl/dt', output, 2
 2289       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2290       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2291       assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2292       assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
 2293       assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2294     end
 2295 
 2296     test "multi-line elements with indented paragraph content that includes comment lines" do
 2297       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 2298       input = <<~EOS
 2299       term1::
 2300        def1
 2301       // comment
 2302       term2::
 2303         def2
 2304       // comment
 2305         def2 continued
 2306       EOS
 2307       output = convert_string_to_embedded input
 2308       assert_xpath '//dl', output, 1
 2309       assert_xpath '//dl/dt', output, 2
 2310       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2311       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2312       assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2313       assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
 2314       assert_xpath %((//dl/dt)[2]/following-sibling::dd/p[text() = "def2\ndef2 continued"]), output, 1
 2315     end
 2316 
 2317     test "should not strip comment line in literal paragraph block attached to list item" do
 2318       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 2319       input = <<~EOS
 2320       term1::
 2321       +
 2322        line 1
 2323       // not a comment
 2324        line 3
 2325       EOS
 2326       output = convert_string_to_embedded input
 2327       assert_xpath '//*[@class="literalblock"]', output, 1
 2328       assert_xpath %(//*[@class="literalblock"]//pre[text()=" line 1\n// not a comment\n line 3"]), output, 1
 2329     end
 2330 
 2331     test 'should escape special characters in all literal paragraphs attached to list item' do
 2332       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 2333       input = <<~EOS
 2334       term:: desc
 2335 
 2336         <code>text</code>
 2337 
 2338         more <code>text</code>
 2339 
 2340       another term::
 2341 
 2342         <code>text</code> in a paragraph
 2343       EOS
 2344 
 2345       output = convert_string_to_embedded input
 2346       assert_css 'dt', output, 2
 2347       assert_css 'code', output, 0
 2348       assert_css 'dd:first-of-type > *', output, 3
 2349       assert_css 'dd:first-of-type pre', output, 2
 2350       assert_xpath '((//dd)[1]//pre)[1][text()="<code>text</code>"]', output, 1
 2351       assert_xpath '((//dd)[1]//pre)[2][text()="more <code>text</code>"]', output, 1
 2352       assert_xpath '((//dd)[2]//p)[1][text()="<code>text</code> in a paragraph"]', output, 1
 2353     end
 2354 
 2355     test 'multi-line element with paragraph starting with multiple dashes should not be seen as list' do
 2356       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 2357       input = <<~EOS
 2358       term1::
 2359         def1
 2360         -- and a note
 2361 
 2362       term2::
 2363         def2
 2364       EOS
 2365       output = convert_string_to_embedded input
 2366       assert_xpath '//dl', output, 1
 2367       assert_xpath '//dl/dt', output, 2
 2368       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2369       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2370       assert_xpath %((//dl/dt)[1]/following-sibling::dd/p[text() = "def1#{decode_char 8201}#{decode_char 8212}#{decode_char 8201}and a note"]), output, 1
 2371       assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
 2372       assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2373     end
 2374 
 2375     test "multi-line element with multiple terms" do
 2376       input = <<~'EOS'
 2377       term1::
 2378       term2::
 2379       def2
 2380       EOS
 2381       output = convert_string input
 2382       assert_xpath '//dl', output, 1
 2383       assert_xpath '//dl/dt', output, 2
 2384       assert_xpath '//dl/dd', output, 1
 2385       assert_xpath '(//dl/dt)[1]/following-sibling::dt', output, 1
 2386       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2387       assert_xpath '(//dl/dt)[2]/following-sibling::dd', output, 1
 2388       assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2389     end
 2390 
 2391     test 'consecutive terms share same varlistentry in docbook' do
 2392       input = <<~'EOS'
 2393       term::
 2394       alt term::
 2395       description
 2396 
 2397       last::
 2398       EOS
 2399       output = convert_string_to_embedded input, backend: 'docbook'
 2400       assert_xpath '//varlistentry', output, 2
 2401       assert_xpath '(//varlistentry)[1]/term', output, 2
 2402       assert_xpath '(//varlistentry)[2]/term', output, 1
 2403       assert_xpath '(//varlistentry)[2]/listitem', output, 1
 2404       assert_xpath '(//varlistentry)[2]/listitem[normalize-space(text())=""]', output, 1
 2405     end
 2406 
 2407     test "multi-line elements with blank line before paragraph content" do
 2408       input = <<~'EOS'
 2409       term1::
 2410 
 2411       def1
 2412       term2::
 2413 
 2414       def2
 2415       EOS
 2416       output = convert_string input
 2417       assert_xpath '//dl', output, 1
 2418       assert_xpath '//dl/dt', output, 2
 2419       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2420       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2421       assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2422       assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
 2423       assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2424     end
 2425 
 2426     test "multi-line elements with paragraph and literal content" do
 2427       # blank line following literal paragraph is required or else it will gobble up the second term
 2428       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 2429       input = <<~EOS
 2430       term1::
 2431       def1
 2432 
 2433         literal
 2434 
 2435       term2::
 2436         def2
 2437       EOS
 2438       output = convert_string input
 2439       assert_xpath '//dl', output, 1
 2440       assert_xpath '//dl/dt', output, 2
 2441       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2442       assert_xpath '//dl/dt/following-sibling::dd//pre', output, 1
 2443       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2444       assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2445       assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
 2446       assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2447     end
 2448 
 2449     test "mixed single and multi-line adjacent elements" do
 2450       input = <<~'EOS'
 2451       term1:: def1
 2452       term2::
 2453       def2
 2454       EOS
 2455       output = convert_string input
 2456       assert_xpath '//dl', output, 1
 2457       assert_xpath '//dl/dt', output, 2
 2458       assert_xpath '//dl/dt/following-sibling::dd', output, 2
 2459       assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
 2460       assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2461       assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
 2462       assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2463     end
 2464 
 2465     test 'should discover anchor at start of description term text and register it as a reference' do
 2466       input = <<~'EOS'
 2467       The highest peak in the Front Range is <<grays-peak>>, which tops <<mount-evans>> by just a few feet.
 2468 
 2469       [[mount-evans,Mount Evans]]Mount Evans:: 14,271 feet
 2470       [[grays-peak]]Grays Peak:: 14,278 feet
 2471       EOS
 2472       doc = document_from_string input
 2473       refs = doc.catalog[:refs]
 2474       assert refs.key?('mount-evans')
 2475       assert refs.key?('grays-peak')
 2476       output = doc.convert standalone: false
 2477       assert_xpath '(//p)[1]/a[@href="#grays-peak"][text()="Grays Peak"]', output, 1
 2478       assert_xpath '(//p)[1]/a[@href="#mount-evans"][text()="Mount Evans"]', output, 1
 2479       assert_xpath '//dl', output, 1
 2480       assert_xpath '//dl/dt', output, 2
 2481       assert_xpath '(//dl/dt)[1]/a[@id="mount-evans"]', output, 1
 2482       assert_xpath '(//dl/dt)[2]/a[@id="grays-peak"]', output, 1
 2483     end
 2484 
 2485     test "missing space before term does not produce description list" do
 2486       input = <<~'EOS'
 2487       term1::def1
 2488       term2::def2
 2489       EOS
 2490       output = convert_string input
 2491       assert_xpath '//dl', output, 0
 2492     end
 2493 
 2494     test "literal block inside description list" do
 2495       input = <<~'EOS'
 2496       term::
 2497       +
 2498       ....
 2499       literal, line 1
 2500 
 2501       literal, line 2
 2502       ....
 2503       anotherterm:: def
 2504       EOS
 2505       output = convert_string input
 2506       assert_xpath '//dl/dt', output, 2
 2507       assert_xpath '//dl/dd', output, 2
 2508       assert_xpath '//dl/dd//pre', output, 1
 2509       assert_xpath '(//dl/dd)[1]/*[@class="literalblock"]//pre', output, 1
 2510       assert_xpath '(//dl/dd)[2]/p[text() = "def"]', output, 1
 2511     end
 2512 
 2513     test "literal block inside description list with trailing line continuation" do
 2514       input = <<~'EOS'
 2515       term::
 2516       +
 2517       ....
 2518       literal, line 1
 2519 
 2520       literal, line 2
 2521       ....
 2522       +
 2523       anotherterm:: def
 2524       EOS
 2525       output = convert_string input
 2526       assert_xpath '//dl/dt', output, 2
 2527       assert_xpath '//dl/dd', output, 2
 2528       assert_xpath '//dl/dd//pre', output, 1
 2529       assert_xpath '(//dl/dd)[1]/*[@class="literalblock"]//pre', output, 1
 2530       assert_xpath '(//dl/dd)[2]/p[text() = "def"]', output, 1
 2531     end
 2532 
 2533     test "multiple listing blocks inside description list" do
 2534       input = <<~'EOS'
 2535       term::
 2536       +
 2537       ----
 2538       listing, line 1
 2539 
 2540       listing, line 2
 2541       ----
 2542       +
 2543       ----
 2544       listing, line 1
 2545 
 2546       listing, line 2
 2547       ----
 2548       anotherterm:: def
 2549       EOS
 2550       output = convert_string input
 2551       assert_xpath '//dl/dt', output, 2
 2552       assert_xpath '//dl/dd', output, 2
 2553       assert_xpath '//dl/dd//pre', output, 2
 2554       assert_xpath '(//dl/dd)[1]/*[@class="listingblock"]//pre', output, 2
 2555       assert_xpath '(//dl/dd)[2]/p[text() = "def"]', output, 1
 2556     end
 2557 
 2558     test "open block inside description list" do
 2559       input = <<~'EOS'
 2560       term::
 2561       +
 2562       --
 2563       Open block as description of term.
 2564 
 2565       And some more detail...
 2566       --
 2567       anotherterm:: def
 2568       EOS
 2569       output = convert_string input
 2570       assert_xpath '//dl/dd//p', output, 3
 2571       assert_xpath '(//dl/dd)[1]//*[@class="openblock"]//p', output, 2
 2572     end
 2573 
 2574     test "paragraph attached by a list continuation on either side in a description list" do
 2575       input = <<~'EOS'
 2576       term1:: def1
 2577       +
 2578       more detail
 2579       +
 2580       term2:: def2
 2581       EOS
 2582       output = convert_string input
 2583       assert_xpath '(//dl/dt)[1][normalize-space(text())="term1"]', output, 1
 2584       assert_xpath '(//dl/dt)[2][normalize-space(text())="term2"]', output, 1
 2585       assert_xpath '(//dl/dd)[1]//p', output, 2
 2586       assert_xpath '((//dl/dd)[1]//p)[1][text()="def1"]', output, 1
 2587       assert_xpath '(//dl/dd)[1]/p/following-sibling::*[@class="paragraph"]/p[text() = "more detail"]', output, 1
 2588     end
 2589 
 2590     test "paragraph attached by a list continuation on either side to a multi-line element in a description list" do
 2591       input = <<~'EOS'
 2592       term1::
 2593       def1
 2594       +
 2595       more detail
 2596       +
 2597       term2:: def2
 2598       EOS
 2599       output = convert_string input
 2600       assert_xpath '(//dl/dt)[1][normalize-space(text())="term1"]', output, 1
 2601       assert_xpath '(//dl/dt)[2][normalize-space(text())="term2"]', output, 1
 2602       assert_xpath '(//dl/dd)[1]//p', output, 2
 2603       assert_xpath '((//dl/dd)[1]//p)[1][text()="def1"]', output, 1
 2604       assert_xpath '(//dl/dd)[1]/p/following-sibling::*[@class="paragraph"]/p[text() = "more detail"]', output, 1
 2605     end
 2606 
 2607     test 'should continue to parse subsequent blocks attached to list item after first block is dropped' do
 2608       input = <<~'EOS'
 2609       :attribute-missing: drop-line
 2610 
 2611       term::
 2612       +
 2613       image::{unresolved}[]
 2614       +
 2615       paragraph
 2616       EOS
 2617 
 2618       output = convert_string_to_embedded input
 2619       assert_css 'dl', output, 1
 2620       assert_css 'dl > dt', output, 1
 2621       assert_css 'dl > dt + dd', output, 1
 2622       assert_css 'dl > dt + dd > .imageblock', output, 0
 2623       assert_css 'dl > dt + dd > .paragraph', output, 1
 2624     end
 2625 
 2626     test "verse paragraph inside a description list" do
 2627       input = <<~'EOS'
 2628       term1:: def
 2629       +
 2630       [verse]
 2631       la la la
 2632 
 2633       term2:: def
 2634       EOS
 2635       output = convert_string input
 2636       assert_xpath '//dl/dd//p', output, 2
 2637       assert_xpath '(//dl/dd)[1]/*[@class="verseblock"]/pre[text() = "la la la"]', output, 1
 2638     end
 2639 
 2640     test "list inside a description list" do
 2641       input = <<~'EOS'
 2642       term1::
 2643       * level 1
 2644       ** level 2
 2645       * level 1
 2646       term2:: def
 2647       EOS
 2648       output = convert_string input
 2649       assert_xpath '//dl/dd', output, 2
 2650       assert_xpath '//dl/dd/p', output, 1
 2651       assert_xpath '(//dl/dd)[1]//ul', output, 2
 2652       assert_xpath '((//dl/dd)[1]//ul)[1]//ul', output, 1
 2653     end
 2654 
 2655     test "list inside a description list offset by blank lines" do
 2656       input = <<~'EOS'
 2657       term1::
 2658 
 2659       * level 1
 2660       ** level 2
 2661       * level 1
 2662 
 2663       term2:: def
 2664       EOS
 2665       output = convert_string input
 2666       assert_xpath '//dl/dd', output, 2
 2667       assert_xpath '//dl/dd/p', output, 1
 2668       assert_xpath '(//dl/dd)[1]//ul', output, 2
 2669       assert_xpath '((//dl/dd)[1]//ul)[1]//ul', output, 1
 2670     end
 2671 
 2672     test "should only grab one line following last item if item has no inline description" do
 2673       input = <<~'EOS'
 2674       term1::
 2675 
 2676       def1
 2677 
 2678       term2::
 2679 
 2680       def2
 2681 
 2682       A new paragraph
 2683 
 2684       Another new paragraph
 2685       EOS
 2686       output = convert_string input
 2687       assert_xpath '//dl', output, 1
 2688       assert_xpath '//dl/dd', output, 2
 2689       assert_xpath '(//dl/dd)[1]/p[text() = "def1"]', output, 1
 2690       assert_xpath '(//dl/dd)[2]/p[text() = "def2"]', output, 1
 2691       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="paragraph"]', output, 2
 2692       assert_xpath '(//*[@class="dlist"]/following-sibling::*[@class="paragraph"])[1]/p[text() = "A new paragraph"]', output, 1
 2693       assert_xpath '(//*[@class="dlist"]/following-sibling::*[@class="paragraph"])[2]/p[text() = "Another new paragraph"]', output, 1
 2694     end
 2695 
 2696     test "should only grab one literal line following last item if item has no inline description" do
 2697       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 2698       input = <<~EOS
 2699       term1::
 2700 
 2701       def1
 2702 
 2703       term2::
 2704 
 2705         def2
 2706 
 2707       A new paragraph
 2708 
 2709       Another new paragraph
 2710       EOS
 2711       output = convert_string input
 2712       assert_xpath '//dl', output, 1
 2713       assert_xpath '//dl/dd', output, 2
 2714       assert_xpath '(//dl/dd)[1]/p[text() = "def1"]', output, 1
 2715       assert_xpath '(//dl/dd)[2]/p[text() = "def2"]', output, 1
 2716       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="paragraph"]', output, 2
 2717       assert_xpath '(//*[@class="dlist"]/following-sibling::*[@class="paragraph"])[1]/p[text() = "A new paragraph"]', output, 1
 2718       assert_xpath '(//*[@class="dlist"]/following-sibling::*[@class="paragraph"])[2]/p[text() = "Another new paragraph"]', output, 1
 2719     end
 2720 
 2721     test "should append subsequent paragraph literals to list item as block content" do
 2722       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 2723       input = <<~EOS
 2724       term1::
 2725 
 2726       def1
 2727 
 2728       term2::
 2729 
 2730         def2
 2731 
 2732         literal
 2733 
 2734       A new paragraph.
 2735       EOS
 2736       output = convert_string input
 2737       assert_xpath '//dl', output, 1
 2738       assert_xpath '//dl/dd', output, 2
 2739       assert_xpath '(//dl/dd)[1]/p[text() = "def1"]', output, 1
 2740       assert_xpath '(//dl/dd)[2]/p[text() = "def2"]', output, 1
 2741       assert_xpath '(//dl/dd)[2]/p/following-sibling::*[@class="literalblock"]', output, 1
 2742       assert_xpath '(//dl/dd)[2]/p/following-sibling::*[@class="literalblock"]//pre[text() = "literal"]', output, 1
 2743       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="paragraph"]', output, 1
 2744       assert_xpath '(//*[@class="dlist"]/following-sibling::*[@class="paragraph"])[1]/p[text() = "A new paragraph."]', output, 1
 2745     end
 2746 
 2747     test 'should not match comment line that looks like description list term' do
 2748       input = <<~'EOS'
 2749       before
 2750 
 2751       //key:: val
 2752 
 2753       after
 2754       EOS
 2755 
 2756       output = convert_string_to_embedded input
 2757       assert_css 'dl', output, 0
 2758     end
 2759 
 2760     test 'should not match comment line following list that looks like description list term' do
 2761       input = <<~'EOS'
 2762       * item
 2763 
 2764       //term:: desc
 2765       == Section
 2766 
 2767       section text
 2768       EOS
 2769 
 2770       output = convert_string_to_embedded input
 2771       assert_xpath '/*[@class="ulist"]', output, 1
 2772       assert_xpath '/*[@class="sect1"]', output, 1
 2773       assert_xpath '/*[@class="sect1"]/h2[text()="Section"]', output, 1
 2774       assert_xpath '/*[@class="ulist"]/following-sibling::*[@class="sect1"]', output, 1
 2775     end
 2776 
 2777     test 'should not match comment line that looks like sibling description list term' do
 2778       input = <<~'EOS'
 2779       before
 2780 
 2781       foo:: bar
 2782       //yin:: yang
 2783 
 2784       after
 2785       EOS
 2786 
 2787       output = convert_string_to_embedded input
 2788       assert_css '.dlist', output, 1
 2789       assert_css '.dlist dt', output, 1
 2790       refute_includes output, 'yin'
 2791     end
 2792 
 2793     test 'should not hang on description list item in list that begins with ///' do
 2794       input = <<~'EOS'
 2795       * a
 2796       ///b::
 2797       c
 2798       EOS
 2799 
 2800       output = convert_string_to_embedded input
 2801       assert_css 'ul', output, 1
 2802       assert_css 'ul li dl', output, 1
 2803       assert_xpath '//ul/li/p[text()="a"]', output, 1
 2804       assert_xpath '//dt[text()="///b"]', output, 1
 2805       assert_xpath '//dd/p[text()="c"]', output, 1
 2806     end
 2807 
 2808     test 'should not hang on sibling description list item that begins with ///' do
 2809       input = <<~'EOS'
 2810       a::
 2811       ///b::
 2812       c
 2813       EOS
 2814 
 2815       output = convert_string_to_embedded input
 2816       assert_css 'dl', output, 1
 2817       assert_xpath '(//dl/dt)[1][text()="a"]', output, 1
 2818       assert_xpath '(//dl/dt)[2][text()="///b"]', output, 1
 2819       assert_xpath '//dl/dd/p[text()="c"]', output, 1
 2820     end
 2821 
 2822     test 'should skip dlist term that begins with // unless it begins with ///' do
 2823       input = <<~'EOS'
 2824       category a::
 2825       //ignored term:: def
 2826 
 2827       category b::
 2828       ///term:: def
 2829       EOS
 2830 
 2831       output = convert_string_to_embedded input
 2832       refute_includes output, 'ignored term'
 2833       assert_xpath '//dt[text()="///term"]', output, 1
 2834     end
 2835 
 2836     test 'more than 4 consecutive colons should become part of description list term' do
 2837       input = <<~'EOS'
 2838       A term::::: a description
 2839       EOS
 2840 
 2841       output = convert_string_to_embedded input
 2842       assert_css 'dl', output, 1
 2843       assert_css 'dl > dt', output, 1
 2844       assert_xpath '//dl/dt[text()="A term:"]', output, 1
 2845       assert_xpath '//dl/dd/p[text()="a description"]', output, 1
 2846     end
 2847 
 2848     test 'text method of dd node should return nil if dd node only contains blocks' do
 2849       input = <<~'EOS'
 2850       term::
 2851       +
 2852       paragraph
 2853       EOS
 2854 
 2855       doc = document_from_string input
 2856       dd = doc.blocks[0].items[0][1]
 2857       assert_nil dd.text
 2858     end
 2859 
 2860     test 'should match trailing line separator in text of list item' do
 2861       input = <<~EOS.chop
 2862       A:: a
 2863       B:: b#{decode_char 8232}
 2864       C:: c
 2865       EOS
 2866 
 2867       output = convert_string input
 2868       assert_css 'dd', output, 3
 2869       assert_xpath %((//dd)[2]/p[text()="b#{decode_char 8232}"]), output, 1
 2870     end
 2871 
 2872     test 'should match line separator in text of list item' do
 2873       input = <<~EOS.chop
 2874       A:: a
 2875       B:: b#{decode_char 8232}b
 2876       C:: c
 2877       EOS
 2878 
 2879       output = convert_string input
 2880       assert_css 'dd', output, 3
 2881       assert_xpath %((//dd)[2]/p[text()="b#{decode_char 8232}b"]), output, 1
 2882     end
 2883   end
 2884 
 2885   context "Nested lists" do
 2886     test 'should not parse a nested dlist delimiter without a term as a dlist' do
 2887       input = <<~'EOS'
 2888       t::
 2889       ;;
 2890       EOS
 2891       output = convert_string_to_embedded input
 2892       assert_xpath '//dl', output, 1
 2893       assert_xpath '//dl/dd/p[text()=";;"]', output, 1
 2894     end
 2895 
 2896     test 'should not parse a nested indented dlist delimiter without a term as a dlist' do
 2897       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 2898       input = <<~EOS
 2899       t::
 2900       desc
 2901         ;;
 2902       EOS
 2903       output = convert_string_to_embedded input
 2904       assert_xpath '//dl', output, 1
 2905       assert_xpath %(//dl/dd/p[text()="desc\n  ;;"]), output, 1
 2906     end
 2907 
 2908     test "single-line adjacent nested elements" do
 2909       input = <<~'EOS'
 2910       term1:: def1
 2911       label1::: detail1
 2912       term2:: def2
 2913       EOS
 2914       output = convert_string input
 2915       assert_xpath '//dl', output, 2
 2916       assert_xpath '//dl//dl', output, 1
 2917       assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
 2918       assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2919       assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
 2920       assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
 2921       assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
 2922       assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2923     end
 2924 
 2925     test "single-line adjacent maximum nested elements" do
 2926       input = <<~'EOS'
 2927       term1:: def1
 2928       label1::: detail1
 2929       name1:::: value1
 2930       item1;; price1
 2931       term2:: def2
 2932       EOS
 2933       output = convert_string input
 2934       assert_xpath '//dl', output, 4
 2935       assert_xpath '//dl//dl//dl//dl', output, 1
 2936     end
 2937 
 2938     test "single-line nested elements seperated by blank line at top level" do
 2939       input = <<~'EOS'
 2940       term1:: def1
 2941 
 2942       label1::: detail1
 2943 
 2944       term2:: def2
 2945       EOS
 2946       output = convert_string input
 2947       assert_xpath '//dl', output, 2
 2948       assert_xpath '//dl//dl', output, 1
 2949       assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
 2950       assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2951       assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
 2952       assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
 2953       assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
 2954       assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2955     end
 2956 
 2957     test "single-line nested elements seperated by blank line at nested level" do
 2958       input = <<~'EOS'
 2959       term1:: def1
 2960       label1::: detail1
 2961 
 2962       label2::: detail2
 2963       term2:: def2
 2964       EOS
 2965       output = convert_string input
 2966       assert_xpath '//dl', output, 2
 2967       assert_xpath '//dl//dl', output, 1
 2968       assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
 2969       assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2970       assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
 2971       assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
 2972       assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
 2973       assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2974     end
 2975 
 2976     test "single-line adjacent nested elements with alternate delimiters" do
 2977       input = <<~'EOS'
 2978       term1:: def1
 2979       label1;; detail1
 2980       term2:: def2
 2981       EOS
 2982       output = convert_string input
 2983       assert_xpath '//dl', output, 2
 2984       assert_xpath '//dl//dl', output, 1
 2985       assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
 2986       assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 2987       assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
 2988       assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
 2989       assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
 2990       assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 2991     end
 2992 
 2993     test "multi-line adjacent nested elements" do
 2994       input = <<~'EOS'
 2995       term1::
 2996       def1
 2997       label1:::
 2998       detail1
 2999       term2::
 3000       def2
 3001       EOS
 3002       output = convert_string input
 3003       assert_xpath '//dl', output, 2
 3004       assert_xpath '//dl//dl', output, 1
 3005       assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
 3006       assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 3007       assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
 3008       assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
 3009       assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
 3010       assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 3011     end
 3012 
 3013     test "multi-line nested elements seperated by blank line at nested level repeated" do
 3014       input = <<~'EOS'
 3015       term1::
 3016       def1
 3017       label1:::
 3018 
 3019       detail1
 3020       label2:::
 3021       detail2
 3022 
 3023       term2:: def2
 3024       EOS
 3025       output = convert_string input
 3026       assert_xpath '//dl', output, 2
 3027       assert_xpath '//dl//dl', output, 1
 3028       assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
 3029       assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 3030       assert_xpath '(//dl//dl/dt)[1][normalize-space(text()) = "label1"]', output, 1
 3031       assert_xpath '(//dl//dl/dt)[1]/following-sibling::dd/p[text() = "detail1"]', output, 1
 3032       assert_xpath '(//dl//dl/dt)[2][normalize-space(text()) = "label2"]', output, 1
 3033       assert_xpath '(//dl//dl/dt)[2]/following-sibling::dd/p[text() = "detail2"]', output, 1
 3034     end
 3035 
 3036     test "multi-line element with indented nested element" do
 3037       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3038       input = <<~EOS
 3039       term1::
 3040         def1
 3041         label1;;
 3042          detail1
 3043       term2::
 3044         def2
 3045       EOS
 3046       output = convert_string input
 3047       assert_xpath '//dl', output, 2
 3048       assert_xpath '//dl//dl', output, 1
 3049       assert_xpath '(//dl)[1]/dt', output, 2
 3050       assert_xpath '(//dl)[1]/dd', output, 2
 3051       assert_xpath '((//dl)[1]/dt)[1][normalize-space(text()) = "term1"]', output, 1
 3052       assert_xpath '((//dl)[1]/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 3053       assert_xpath '//dl//dl/dt', output, 1
 3054       assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
 3055       assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
 3056       assert_xpath '((//dl)[1]/dt)[2][normalize-space(text()) = "term2"]', output, 1
 3057       assert_xpath '((//dl)[1]/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 3058     end
 3059 
 3060     test "mixed single and multi-line elements with indented nested elements" do
 3061       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3062       input = <<~EOS
 3063       term1:: def1
 3064         label1:::
 3065          detail1
 3066       term2:: def2
 3067       EOS
 3068       output = convert_string input
 3069       assert_xpath '//dl', output, 2
 3070       assert_xpath '//dl//dl', output, 1
 3071       assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
 3072       assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
 3073       assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
 3074       assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
 3075       assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
 3076       assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
 3077     end
 3078 
 3079     test "multi-line elements with first paragraph folded to text with adjacent nested element" do
 3080       input = <<~'EOS'
 3081       term1:: def1
 3082       continued
 3083       label1:::
 3084       detail1
 3085       EOS
 3086       output = convert_string_to_embedded input
 3087       assert_xpath '//dl', output, 2
 3088       assert_xpath '//dl//dl', output, 1
 3089       assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
 3090       assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[starts-with(text(), "def1")]', output, 1
 3091       assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[contains(text(), "continued")]', output, 1
 3092       assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
 3093       assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
 3094     end
 3095   end
 3096 
 3097   context 'Special lists' do
 3098     test 'should convert glossary list with proper semantics' do
 3099       input = <<~'EOS'
 3100       [glossary]
 3101       term 1:: def 1
 3102       term 2:: def 2
 3103       EOS
 3104       output = convert_string_to_embedded input
 3105       assert_css '.dlist.glossary', output, 1
 3106       assert_css '.dlist dt:not([class])', output, 2
 3107     end
 3108 
 3109     test 'consecutive glossary terms should share same glossentry element in docbook' do
 3110       input = <<~'EOS'
 3111       [glossary]
 3112       term::
 3113       alt term::
 3114       description
 3115 
 3116       last::
 3117       EOS
 3118       output = convert_string_to_embedded input, backend: 'docbook'
 3119       assert_xpath '/glossentry', output, 2
 3120       assert_xpath '(/glossentry)[1]/glossterm', output, 2
 3121       assert_xpath '(/glossentry)[2]/glossterm', output, 1
 3122       assert_xpath '(/glossentry)[2]/glossdef', output, 1
 3123       assert_xpath '(/glossentry)[2]/glossdef[normalize-space(text())=""]', output, 1
 3124     end
 3125 
 3126     test 'should convert horizontal list with proper markup' do
 3127       input = <<~'EOS'
 3128       [horizontal]
 3129       first term:: description
 3130       +
 3131       more detail
 3132 
 3133       second term:: description
 3134       EOS
 3135       output = convert_string_to_embedded input
 3136       assert_css '.hdlist', output, 1
 3137       assert_css '.hdlist table', output, 1
 3138       assert_css '.hdlist table colgroup', output, 0
 3139       assert_css '.hdlist table tr', output, 2
 3140       # see nokogiri#1803 for why this is necessary
 3141       tbody_path = jruby? ? 'tbody/' : ''
 3142       refute_includes output, '<tbody>'
 3143       assert_xpath %(/*[@class="hdlist"]/table/#{tbody_path}tr[1]/td), output, 2
 3144       assert_xpath %(/*[@class="hdlist"]/table/#{tbody_path}tr[1]/td[@class="hdlist1"]), output, 1
 3145       assert_xpath %(/*[@class="hdlist"]/table/#{tbody_path}tr[1]/td[@class="hdlist2"]), output, 1
 3146       assert_xpath %(/*[@class="hdlist"]/table/#{tbody_path}tr[1]/td[@class="hdlist2"]/p), output, 1
 3147       assert_xpath %(/*[@class="hdlist"]/table/#{tbody_path}tr[1]/td[@class="hdlist2"]/p/following-sibling::*[@class="paragraph"]), output, 1
 3148       assert_xpath '((//tr)[1]/td)[1][normalize-space(text())="first term"]', output, 1
 3149       assert_xpath '((//tr)[1]/td)[2]/p[normalize-space(text())="description"]', output, 1
 3150 
 3151       assert_xpath %(/*[@class="hdlist"]/table/#{tbody_path}tr[2]/td), output, 2
 3152       assert_xpath '((//tr)[2]/td)[1][normalize-space(text())="second term"]', output, 1
 3153       assert_xpath '((//tr)[2]/td)[2]/p[normalize-space(text())="description"]', output, 1
 3154     end
 3155 
 3156     test 'should set col widths of item and label if specified' do
 3157       input = <<~'EOS'
 3158       [horizontal]
 3159       [labelwidth="25", itemwidth="75"]
 3160       term:: def
 3161       EOS
 3162 
 3163       output = convert_string_to_embedded input
 3164       assert_css 'table', output, 1
 3165       assert_css 'table > colgroup', output, 1
 3166       assert_css 'table > colgroup > col', output, 2
 3167       assert_xpath '(//table/colgroup/col)[1][@style="width: 25%;"]', output, 1
 3168       assert_xpath '(//table/colgroup/col)[2][@style="width: 75%;"]', output, 1
 3169     end
 3170 
 3171     test 'should set col widths of item and label in docbook if specified' do
 3172       input = <<~'EOS'
 3173       [horizontal]
 3174       [labelwidth="25", itemwidth="75"]
 3175       term:: def
 3176       EOS
 3177 
 3178       output = convert_string_to_embedded input, backend: 'docbook'
 3179       assert_css 'informaltable', output, 1
 3180       assert_css 'informaltable > tgroup', output, 1
 3181       assert_css 'informaltable > tgroup > colspec', output, 2
 3182       assert_xpath '(/informaltable/tgroup/colspec)[1][@colwidth="25*"]', output, 1
 3183       assert_xpath '(/informaltable/tgroup/colspec)[2][@colwidth="75*"]', output, 1
 3184     end
 3185 
 3186     test 'should add strong class to label if strong option is set' do
 3187       input = <<~'EOS'
 3188       [horizontal, options="strong"]
 3189       term:: def
 3190       EOS
 3191 
 3192       output = convert_string_to_embedded input
 3193       assert_css '.hdlist', output, 1
 3194       assert_css '.hdlist td.hdlist1.strong', output, 1
 3195     end
 3196 
 3197     test 'consecutive terms in horizontal list should share same cell' do
 3198       input = <<~'EOS'
 3199       [horizontal]
 3200       term::
 3201       alt term::
 3202       description
 3203 
 3204       last::
 3205       EOS
 3206       output = convert_string_to_embedded input
 3207       assert_xpath '//tr', output, 2
 3208       assert_xpath '(//tr)[1]/td[@class="hdlist1"]', output, 1
 3209       # NOTE I'm trimming the trailing <br> in Asciidoctor
 3210       #assert_xpath '(//tr)[1]/td[@class="hdlist1"]/br', output, 2
 3211       assert_xpath '(//tr)[1]/td[@class="hdlist1"]/br', output, 1
 3212       assert_xpath '(//tr)[2]/td[@class="hdlist2"]', output, 1
 3213     end
 3214 
 3215     test 'consecutive terms in horizontal list should share same entry in docbook' do
 3216       input = <<~'EOS'
 3217       [horizontal]
 3218       term::
 3219       alt term::
 3220       description
 3221 
 3222       last::
 3223       EOS
 3224       output = convert_string_to_embedded input, backend: 'docbook'
 3225       assert_xpath '//row', output, 2
 3226       assert_xpath '(//row)[1]/entry', output, 2
 3227       assert_xpath '((//row)[1]/entry)[1]/simpara', output, 2
 3228       assert_xpath '(//row)[2]/entry', output, 2
 3229       assert_xpath '((//row)[2]/entry)[2][normalize-space(text())=""]', output, 1
 3230     end
 3231 
 3232     test 'should convert horizontal list in docbook with proper markup' do
 3233       input = <<~'EOS'
 3234       .Terms
 3235       [horizontal]
 3236       first term:: description
 3237       +
 3238       more detail
 3239 
 3240       second term:: description
 3241       EOS
 3242       output = convert_string_to_embedded input, backend: 'docbook'
 3243       assert_xpath '/table', output, 1
 3244       assert_xpath '/table[@tabstyle="horizontal"]', output, 1
 3245       assert_xpath '/table[@tabstyle="horizontal"]/title[text()="Terms"]', output, 1
 3246       assert_xpath '/table//row', output, 2
 3247       assert_xpath '(/table//row)[1]/entry', output, 2
 3248       assert_xpath '(/table//row)[2]/entry', output, 2
 3249       assert_xpath '((/table//row)[1]/entry)[2]/simpara', output, 2
 3250     end
 3251 
 3252     test 'should convert qanda list in HTML with proper semantics' do
 3253       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3254       input = <<~EOS
 3255       [qanda]
 3256       Question 1::
 3257               Answer 1.
 3258       Question 2::
 3259               Answer 2.
 3260       +
 3261       NOTE: A note about Answer 2.
 3262       EOS
 3263       output = convert_string_to_embedded input
 3264       assert_css '.qlist.qanda', output, 1
 3265       assert_css '.qanda > ol', output, 1
 3266       assert_css '.qanda > ol > li', output, 2
 3267       (1..2).each do |idx|
 3268         assert_css ".qanda > ol > li:nth-child(#{idx}) > p", output, 2
 3269         assert_css ".qanda > ol > li:nth-child(#{idx}) > p:first-child > em", output, 1
 3270         assert_xpath "/*[@class = 'qlist qanda']/ol/li[#{idx}]/p[1]/em[normalize-space(text()) = 'Question #{idx}']", output, 1
 3271         assert_css ".qanda > ol > li:nth-child(#{idx}) > p:last-child > *", output, 0
 3272         assert_xpath "/*[@class = 'qlist qanda']/ol/li[#{idx}]/p[2][normalize-space(text()) = 'Answer #{idx}.']", output, 1
 3273       end
 3274       assert_xpath "/*[@class = 'qlist qanda']/ol/li[2]/p[2]/following-sibling::div[@class='admonitionblock note']", output, 1
 3275     end
 3276 
 3277     test 'should convert qanda list in DocBook with proper semantics' do
 3278       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3279       input = <<~EOS
 3280       [qanda]
 3281       Question 1::
 3282               Answer 1.
 3283       Question 2::
 3284               Answer 2.
 3285       +
 3286       NOTE: A note about Answer 2.
 3287       EOS
 3288       output = convert_string_to_embedded input, backend: 'docbook'
 3289       assert_css 'qandaset', output, 1
 3290       assert_css 'qandaset > qandaentry', output, 2
 3291       (1..2).each do |idx|
 3292         assert_css "qandaset > qandaentry:nth-child(#{idx}) > question", output, 1
 3293         assert_css "qandaset > qandaentry:nth-child(#{idx}) > question > simpara", output, 1
 3294         assert_xpath "/qandaset/qandaentry[#{idx}]/question/simpara[normalize-space(text()) = 'Question #{idx}']", output, 1
 3295         assert_css "qandaset > qandaentry:nth-child(#{idx}) > answer", output, 1
 3296         assert_css "qandaset > qandaentry:nth-child(#{idx}) > answer > simpara", output, 1
 3297         assert_xpath "/qandaset/qandaentry[#{idx}]/answer/simpara[normalize-space(text()) = 'Answer #{idx}.']", output, 1
 3298       end
 3299       assert_xpath "/qandaset/qandaentry[2]/answer/simpara/following-sibling::note", output, 1
 3300     end
 3301 
 3302     test 'consecutive questions should share same question element in docbook' do
 3303       input = <<~'EOS'
 3304       [qanda]
 3305       question::
 3306       follow-up question::
 3307       response
 3308 
 3309       last question::
 3310       EOS
 3311       output = convert_string_to_embedded input, backend: 'docbook'
 3312       assert_xpath '//qandaentry', output, 2
 3313       assert_xpath '(//qandaentry)[1]/question', output, 1
 3314       assert_xpath '(//qandaentry)[1]/question/simpara', output, 2
 3315       assert_xpath '(//qandaentry)[2]/question', output, 1
 3316       assert_xpath '(//qandaentry)[2]/answer', output, 1
 3317       assert_xpath '(//qandaentry)[2]/answer[normalize-space(text())=""]', output, 1
 3318     end
 3319 
 3320     test 'should convert bibliography list with proper semantics' do
 3321       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3322       input = <<~EOS
 3323       [bibliography]
 3324       - [[[taoup]]] Eric Steven Raymond. 'The Art of Unix
 3325         Programming'. Addison-Wesley. ISBN 0-13-142901-9.
 3326       - [[[walsh-muellner]]] Norman Walsh & Leonard Muellner.
 3327         'DocBook - The Definitive Guide'. O'Reilly & Associates. 1999.
 3328         ISBN 1-56592-580-7.
 3329       EOS
 3330       output = convert_string_to_embedded input
 3331       assert_css '.ulist.bibliography', output, 1
 3332       assert_css '.ulist.bibliography ul', output, 1
 3333       assert_css '.ulist.bibliography ul li', output, 2
 3334       assert_css '.ulist.bibliography ul li p', output, 2
 3335       assert_css '.ulist.bibliography ul li:nth-child(1) p a#taoup', output, 1
 3336       assert_xpath '//a/*', output, 0
 3337       text = xmlnodes_at_xpath '(//a)[1]/following-sibling::text()', output, 1
 3338       assert text.text.start_with?('[taoup] ')
 3339     end
 3340 
 3341     test 'should convert bibliography list with proper semantics to DocBook' do
 3342       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3343       input = <<~EOS
 3344       [bibliography]
 3345       - [[[taoup]]] Eric Steven Raymond. 'The Art of Unix
 3346         Programming'. Addison-Wesley. ISBN 0-13-142901-9.
 3347       - [[[walsh-muellner]]] Norman Walsh & Leonard Muellner.
 3348         'DocBook - The Definitive Guide'. O'Reilly & Associates. 1999.
 3349         ISBN 1-56592-580-7.
 3350       EOS
 3351       output = convert_string_to_embedded input, backend: 'docbook'
 3352       assert_css 'bibliodiv', output, 1
 3353       assert_css 'bibliodiv > bibliomixed', output, 2
 3354       assert_css 'bibliodiv > bibliomixed > bibliomisc', output, 2
 3355       assert_css 'bibliodiv > bibliomixed:nth-child(1) > bibliomisc > anchor', output, 1
 3356       assert_css 'bibliodiv > bibliomixed:nth-child(1) > bibliomisc > anchor[xreflabel="[taoup]"]', output, 1
 3357       assert_css 'bibliodiv > bibliomixed:nth-child(2) > bibliomisc > anchor', output, 1
 3358       assert_css 'bibliodiv > bibliomixed:nth-child(2) > bibliomisc > anchor[xreflabel="[walsh-muellner]"]', output, 1
 3359     end
 3360 
 3361     test 'should warn if a bibliography ID is already in use' do
 3362       input = <<~'EOS'
 3363       [bibliography]
 3364       * [[[Fowler]]] Fowler M. _Analysis Patterns: Reusable Object Models_.
 3365       Addison-Wesley. 1997.
 3366       * [[[Fowler]]] Fowler M. _Analysis Patterns: Reusable Object Models_.
 3367       Addison-Wesley. 1997.
 3368       EOS
 3369       using_memory_logger do |logger|
 3370         output = convert_string_to_embedded input
 3371         assert_css '.ulist.bibliography', output, 1
 3372         assert_css '.ulist.bibliography ul li:nth-child(1) p a#Fowler', output, 1
 3373         assert_css '.ulist.bibliography ul li:nth-child(2) p a#Fowler', output, 1
 3374         assert_message logger, :WARN, '<stdin>: line 4: id assigned to bibliography anchor already in use: Fowler', Hash
 3375       end
 3376     end
 3377 
 3378     test 'should automatically add bibliography style to top-level lists in bibliography section' do
 3379       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3380       input = <<~EOS
 3381       [bibliography]
 3382       == Bibliography
 3383 
 3384       .Books
 3385       * [[[taoup]]] Eric Steven Raymond. _The Art of Unix
 3386         Programming_. Addison-Wesley. ISBN 0-13-142901-9.
 3387       * [[[walsh-muellner]]] Norman Walsh & Leonard Muellner.
 3388         _DocBook - The Definitive Guide_. O'Reilly & Associates. 1999.
 3389         ISBN 1-56592-580-7.
 3390 
 3391       .Periodicals
 3392       * [[[doc-writer]]] Doc Writer. _Documentation As Code_. Static Times, 54. August 2016.
 3393       EOS
 3394       doc = document_from_string input
 3395       ulists = doc.find_by context: :ulist
 3396       assert_equal 2, ulists.size
 3397       assert_equal ulists[0].style, 'bibliography'
 3398       assert_equal ulists[1].style, 'bibliography'
 3399     end
 3400 
 3401     test 'should not recognize bibliography anchor that begins with a digit' do
 3402       input = <<~'EOS'
 3403       [bibliography]
 3404       - [[[1984]]] George Orwell. '1984'. New American Library. 1950.
 3405       EOS
 3406 
 3407       output = convert_string_to_embedded input
 3408       assert_includes output, '[[[1984]]]'
 3409       assert_xpath '//a[@id="1984"]', output, 0
 3410     end
 3411 
 3412     test 'should recognize bibliography anchor that contains a digit but does not start with one' do
 3413       input = <<~'EOS'
 3414       [bibliography]
 3415       - [[[_1984]]] George Orwell. '1984'. New American Library. 1950.
 3416       EOS
 3417 
 3418       output = convert_string_to_embedded input
 3419       refute_includes output, '[[[_1984]]]'
 3420       assert_includes output, '[_1984]'
 3421       assert_xpath '//a[@id="_1984"]', output, 1
 3422     end
 3423 
 3424     test 'should catalog bibliography anchors in bibliography list' do
 3425       input = <<~'EOS'
 3426       = Article Title
 3427 
 3428       Please read <<Fowler_1997>>.
 3429 
 3430       [bibliography]
 3431       == References
 3432 
 3433       * [[[Fowler_1997]]] Fowler M. _Analysis Patterns: Reusable Object Models_. Addison-Wesley. 1997.
 3434       EOS
 3435 
 3436       doc = document_from_string input
 3437       assert doc.catalog[:refs].key? 'Fowler_1997'
 3438     end
 3439 
 3440     test 'should use reftext from bibliography anchor at xref and entry' do
 3441       input = <<~'EOS'
 3442       = Article Title
 3443 
 3444       Begin with <<TMMM>>.
 3445       Then move on to <<Fowler_1997>>.
 3446 
 3447       [bibliography]
 3448       == References
 3449 
 3450       * [[[TMMM]]] Brooks F. _The Mythical Man-Month_. Addison-Wesley. 1975.
 3451       * [[[Fowler_1997,1]]] Fowler M. _Analysis Patterns: Reusable Object Models_. Addison-Wesley. 1997.
 3452       EOS
 3453 
 3454       doc = document_from_string input, standalone: false
 3455       tmmm_ref = doc.catalog[:refs]['TMMM']
 3456       refute_nil tmmm_ref
 3457       assert_nil tmmm_ref.reftext
 3458       fowler_1997_ref = doc.catalog[:refs]['Fowler_1997']
 3459       refute_nil fowler_1997_ref
 3460       assert_equal '[1]', fowler_1997_ref.reftext
 3461       result = doc.convert standalone: false
 3462       assert_xpath '//a[@href="#Fowler_1997"]', result, 1
 3463       assert_xpath '//a[@href="#Fowler_1997"][text()="[1]"]', result, 1
 3464       assert_xpath '//a[@id="Fowler_1997"]', result, 1
 3465       fowler_1997_text = (xmlnodes_at_xpath '(//a[@id="Fowler_1997"])[1]/following-sibling::text()', result, 1).text
 3466       assert fowler_1997_text.start_with?('[1] ')
 3467       assert_xpath '//a[@href="#TMMM"]', result, 1
 3468       assert_xpath '//a[@href="#TMMM"][text()="[TMMM]"]', result, 1
 3469       assert_xpath '//a[@id="TMMM"]', result, 1
 3470       tmmm_text = (xmlnodes_at_xpath '(//a[@id="TMMM"])[1]/following-sibling::text()', result, 1).text
 3471       assert tmmm_text.start_with?('[TMMM] ')
 3472     end
 3473 
 3474     test 'should assign reftext of bibliography anchor to xreflabel in DocBook backend' do
 3475       input = <<~'EOS'
 3476       [bibliography]
 3477       * [[[Fowler_1997,1]]] Fowler M. _Analysis Patterns: Reusable Object Models_. Addison-Wesley. 1997.
 3478       EOS
 3479 
 3480       result = convert_string_to_embedded input, backend: :docbook
 3481       assert_includes result, '<anchor xml:id="Fowler_1997" xreflabel="[1]"/>'
 3482     end
 3483   end
 3484 end
 3485 
 3486 context 'Description lists redux' do
 3487 
 3488   context 'Label without text on same line' do
 3489 
 3490     test 'folds text from subsequent line' do
 3491       input = <<~'EOS'
 3492       == Lists
 3493 
 3494       term1::
 3495       def1
 3496       EOS
 3497 
 3498       output = convert_string_to_embedded input
 3499       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3500       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3501       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 3502     end
 3503 
 3504     test 'folds text from first line after blank lines' do
 3505       input = <<~'EOS'
 3506       == Lists
 3507 
 3508       term1::
 3509 
 3510 
 3511       def1
 3512       EOS
 3513 
 3514       output = convert_string_to_embedded input
 3515       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3516       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3517       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 3518     end
 3519 
 3520     test 'folds text from first line after blank line and immediately preceding next item' do
 3521       input = <<~'EOS'
 3522       == Lists
 3523 
 3524       term1::
 3525 
 3526       def1
 3527       term2:: def2
 3528       EOS
 3529 
 3530       output = convert_string_to_embedded input
 3531       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3532       assert_xpath '//*[@class="dlist"]//dd', output, 2
 3533       assert_xpath '(//*[@class="dlist"]//dd)[1]/p[text()="def1"]', output, 1
 3534     end
 3535 
 3536     test 'paragraph offset by blank lines does not break list if label does not have inline text' do
 3537       input = <<~'EOS'
 3538       == Lists
 3539 
 3540       term1::
 3541 
 3542       def1
 3543 
 3544       term2:: def2
 3545       EOS
 3546 
 3547       output = convert_string_to_embedded input
 3548       assert_css 'dl', output, 1
 3549       assert_css 'dl > dt', output, 2
 3550       assert_css 'dl > dd', output, 2
 3551       assert_xpath '(//dl/dd)[1]/p[text()="def1"]', output, 1
 3552     end
 3553 
 3554     test 'folds text from first line after comment line' do
 3555       input = <<~'EOS'
 3556       == Lists
 3557 
 3558       term1::
 3559       // comment
 3560       def1
 3561       EOS
 3562 
 3563       output = convert_string_to_embedded input
 3564       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3565       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3566       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 3567     end
 3568 
 3569     test 'folds text from line following comment line offset by blank line' do
 3570       input = <<~'EOS'
 3571       == Lists
 3572 
 3573       term1::
 3574 
 3575       // comment
 3576       def1
 3577       EOS
 3578 
 3579       output = convert_string_to_embedded input
 3580       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3581       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3582       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 3583     end
 3584 
 3585     test 'folds text from subsequent indented line' do
 3586       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3587       input = <<~EOS
 3588       == Lists
 3589 
 3590       term1::
 3591         def1
 3592       EOS
 3593 
 3594       output = convert_string_to_embedded input
 3595       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3596       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3597       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 3598     end
 3599 
 3600     test 'folds text from indented line after blank line' do
 3601       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3602       input = <<~EOS
 3603       == Lists
 3604 
 3605       term1::
 3606 
 3607         def1
 3608       EOS
 3609 
 3610       output = convert_string_to_embedded input
 3611       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3612       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3613       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 3614     end
 3615 
 3616     test 'folds text that looks like ruler offset by blank line' do
 3617       input = <<~'EOS'
 3618       == Lists
 3619 
 3620       term1::
 3621 
 3622       '''
 3623       EOS
 3624 
 3625       output = convert_string_to_embedded input
 3626       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3627       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3628       assert_xpath %(//*[@class="dlist"]//dd/p[text()="'''"]), output, 1
 3629     end
 3630 
 3631     test 'folds text that looks like ruler offset by blank line and line comment' do
 3632       input = <<~'EOS'
 3633       == Lists
 3634 
 3635       term1::
 3636 
 3637       // comment
 3638       '''
 3639       EOS
 3640 
 3641       output = convert_string_to_embedded input
 3642       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3643       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3644       assert_xpath %(//*[@class="dlist"]//dd/p[text()="'''"]), output, 1
 3645     end
 3646 
 3647     test 'folds text that looks like ruler and the line following it offset by blank line' do
 3648       input = <<~'EOS'
 3649       == Lists
 3650 
 3651       term1::
 3652 
 3653       '''
 3654       continued
 3655       EOS
 3656 
 3657       output = convert_string_to_embedded input
 3658       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3659       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3660       assert_xpath %(//*[@class="dlist"]//dd/p[normalize-space(text())="''' continued"]), output, 1
 3661     end
 3662 
 3663     test 'folds text that looks like title offset by blank line' do
 3664       input = <<~'EOS'
 3665       == Lists
 3666 
 3667       term1::
 3668 
 3669       .def1
 3670       EOS
 3671 
 3672       output = convert_string_to_embedded input
 3673       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3674       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3675       assert_xpath '//*[@class="dlist"]//dd/p[text()=".def1"]', output, 1
 3676     end
 3677 
 3678     test 'folds text that looks like title offset by blank line and line comment' do
 3679       input = <<~'EOS'
 3680       == Lists
 3681 
 3682       term1::
 3683 
 3684       // comment
 3685       .def1
 3686       EOS
 3687 
 3688       output = convert_string_to_embedded input
 3689       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3690       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3691       assert_xpath '//*[@class="dlist"]//dd/p[text()=".def1"]', output, 1
 3692     end
 3693 
 3694     test 'folds text that looks like admonition offset by blank line' do
 3695       input = <<~'EOS'
 3696       == Lists
 3697 
 3698       term1::
 3699 
 3700       NOTE: def1
 3701       EOS
 3702 
 3703       output = convert_string_to_embedded input
 3704       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3705       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3706       assert_xpath '//*[@class="dlist"]//dd/p[text()="NOTE: def1"]', output, 1
 3707     end
 3708 
 3709     test 'folds text that looks like section title offset by blank line' do
 3710       input = <<~'EOS'
 3711       == Lists
 3712 
 3713       term1::
 3714 
 3715       == Another Section
 3716       EOS
 3717 
 3718       output = convert_string_to_embedded input
 3719       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3720       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3721       assert_xpath '//*[@class="dlist"]//dd/p[text()="== Another Section"]', output, 1
 3722       assert_xpath '//h2', output, 1
 3723     end
 3724 
 3725     test 'folds text of first literal line offset by blank line appends subsequent literals offset by blank line as blocks' do
 3726       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3727       input = <<~EOS
 3728       == Lists
 3729 
 3730       term1::
 3731 
 3732         def1
 3733 
 3734         literal
 3735 
 3736 
 3737         literal
 3738       EOS
 3739 
 3740       output = convert_string_to_embedded input
 3741       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3742       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3743       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 3744       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="literalblock"]', output, 2
 3745       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="literalblock"]//pre[text()="literal"]', output, 2
 3746     end
 3747 
 3748     test 'folds text of subsequent line and appends following literal line offset by blank line as block if term has no inline description' do
 3749       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3750       input = <<~EOS
 3751       == Lists
 3752 
 3753       term1::
 3754       def1
 3755 
 3756         literal
 3757 
 3758       term2:: def2
 3759       EOS
 3760 
 3761       output = convert_string_to_embedded input
 3762       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3763       assert_xpath '//*[@class="dlist"]//dd', output, 2
 3764       assert_xpath '(//*[@class="dlist"]//dd)[1]/p[text()="def1"]', output, 1
 3765       assert_xpath '(//*[@class="dlist"]//dd)[1]/p/following-sibling::*[@class="literalblock"]', output, 1
 3766       assert_xpath '(//*[@class="dlist"]//dd)[1]/p/following-sibling::*[@class="literalblock"]//pre[text()="literal"]', output, 1
 3767     end
 3768 
 3769     test 'appends literal line attached by continuation as block if item has no inline description' do
 3770       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3771       input = <<~EOS
 3772       == Lists
 3773 
 3774       term1::
 3775       +
 3776         literal
 3777       EOS
 3778 
 3779       output = convert_string_to_embedded input
 3780       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3781       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3782       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 3783       assert_xpath '//*[@class="dlist"]//dd/*[@class="literalblock"]', output, 1
 3784       assert_xpath '//*[@class="dlist"]//dd/*[@class="literalblock"]//pre[text()="literal"]', output, 1
 3785     end
 3786 
 3787     test 'appends literal line attached by continuation as block if item has no inline description followed by ruler' do
 3788       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3789       input = <<~EOS
 3790       == Lists
 3791 
 3792       term1::
 3793       +
 3794         literal
 3795 
 3796       '''
 3797       EOS
 3798 
 3799       output = convert_string_to_embedded input
 3800       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3801       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3802       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 3803       assert_xpath '//*[@class="dlist"]//dd/*[@class="literalblock"]', output, 1
 3804       assert_xpath '//*[@class="dlist"]//dd/*[@class="literalblock"]//pre[text()="literal"]', output, 1
 3805       assert_xpath '//*[@class="dlist"]/following-sibling::hr', output, 1
 3806     end
 3807 
 3808     test 'appends line attached by continuation as block if item has no inline description followed by ruler' do
 3809       input = <<~'EOS'
 3810       == Lists
 3811 
 3812       term1::
 3813       +
 3814       para
 3815 
 3816       '''
 3817       EOS
 3818 
 3819       output = convert_string_to_embedded input
 3820       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3821       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3822       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 3823       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]', output, 1
 3824       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]/p[text()="para"]', output, 1
 3825       assert_xpath '//*[@class="dlist"]/following-sibling::hr', output, 1
 3826     end
 3827 
 3828     test 'appends line attached by continuation as block if item has no inline description followed by block' do
 3829       input = <<~'EOS'
 3830       == Lists
 3831 
 3832       term1::
 3833       +
 3834       para
 3835 
 3836       ....
 3837       literal
 3838       ....
 3839       EOS
 3840 
 3841       output = convert_string_to_embedded input
 3842       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3843       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3844       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 3845       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]', output, 1
 3846       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]/p[text()="para"]', output, 1
 3847       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="literalblock"]', output, 1
 3848       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="literalblock"]//pre[text()="literal"]', output, 1
 3849     end
 3850 
 3851     test 'appends block attached by continuation but not subsequent block not attached by continuation' do
 3852       input = <<~'EOS'
 3853       == Lists
 3854 
 3855       term1::
 3856       +
 3857       ....
 3858       literal
 3859       ....
 3860       ....
 3861       detached
 3862       ....
 3863       EOS
 3864 
 3865       output = convert_string_to_embedded input
 3866       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3867       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3868       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 3869       assert_xpath '//*[@class="dlist"]//dd/*[@class="literalblock"]', output, 1
 3870       assert_xpath '//*[@class="dlist"]//dd/*[@class="literalblock"]//pre[text()="literal"]', output, 1
 3871       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="literalblock"]', output, 1
 3872       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="literalblock"]//pre[text()="detached"]', output, 1
 3873     end
 3874 
 3875     test 'appends list if item has no inline description' do
 3876       input = <<~'EOS'
 3877       == Lists
 3878 
 3879       term1::
 3880 
 3881       * one
 3882       * two
 3883       * three
 3884       EOS
 3885 
 3886       output = convert_string_to_embedded input
 3887       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3888       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3889       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 3890       assert_xpath '//*[@class="dlist"]//dd//ul/li', output, 3
 3891     end
 3892 
 3893     test 'appends list to first term when followed immediately by second term' do
 3894       input = <<~'EOS'
 3895       == Lists
 3896 
 3897       term1::
 3898 
 3899       * one
 3900       * two
 3901       * three
 3902       term2:: def2
 3903       EOS
 3904 
 3905       output = convert_string_to_embedded input
 3906       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3907       assert_xpath '//*[@class="dlist"]//dd', output, 2
 3908       assert_xpath '(//*[@class="dlist"]//dd)[1]/p', output, 0
 3909       assert_xpath '(//*[@class="dlist"]//dd)[1]//ul/li', output, 3
 3910       assert_xpath '(//*[@class="dlist"]//dd)[2]/p[text()="def2"]', output, 1
 3911     end
 3912 
 3913     test 'appends indented list to first term that is adjacent to second term' do
 3914       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3915       input = <<~EOS
 3916       == Lists
 3917 
 3918       label 1::
 3919         description 1
 3920 
 3921         * one
 3922         * two
 3923         * three
 3924       label 2::
 3925         description 2
 3926 
 3927       paragraph
 3928       EOS
 3929       output = convert_string_to_embedded input
 3930       assert_css '.dlist > dl', output, 1
 3931       assert_css '.dlist dt', output, 2
 3932       assert_xpath '(//*[@class="dlist"]//dt)[1][normalize-space(text())="label 1"]', output, 1
 3933       assert_xpath '(//*[@class="dlist"]//dt)[2][normalize-space(text())="label 2"]', output, 1
 3934       assert_css '.dlist dd', output, 2
 3935       assert_xpath '(//*[@class="dlist"]//dd)[1]/p[text()="description 1"]', output, 1
 3936       assert_xpath '(//*[@class="dlist"]//dd)[2]/p[text()="description 2"]', output, 1
 3937       assert_xpath '(//*[@class="dlist"]//dd)[1]/p/following-sibling::*[@class="ulist"]', output, 1
 3938       assert_xpath '(//*[@class="dlist"]//dd)[1]/p/following-sibling::*[@class="ulist"]//li', output, 3
 3939       assert_css '.dlist + .paragraph', output, 1
 3940     end
 3941 
 3942     test 'appends indented list to first term that is attached by a continuation and adjacent to second term' do
 3943       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 3944       input = <<~EOS
 3945       == Lists
 3946 
 3947       label 1::
 3948         description 1
 3949       +
 3950         * one
 3951         * two
 3952         * three
 3953       label 2::
 3954         description 2
 3955 
 3956       paragraph
 3957       EOS
 3958       output = convert_string_to_embedded input
 3959       assert_css '.dlist > dl', output, 1
 3960       assert_css '.dlist dt', output, 2
 3961       assert_xpath '(//*[@class="dlist"]//dt)[1][normalize-space(text())="label 1"]', output, 1
 3962       assert_xpath '(//*[@class="dlist"]//dt)[2][normalize-space(text())="label 2"]', output, 1
 3963       assert_css '.dlist dd', output, 2
 3964       assert_xpath '(//*[@class="dlist"]//dd)[1]/p[text()="description 1"]', output, 1
 3965       assert_xpath '(//*[@class="dlist"]//dd)[2]/p[text()="description 2"]', output, 1
 3966       assert_xpath '(//*[@class="dlist"]//dd)[1]/p/following-sibling::*[@class="ulist"]', output, 1
 3967       assert_xpath '(//*[@class="dlist"]//dd)[1]/p/following-sibling::*[@class="ulist"]//li', output, 3
 3968       assert_css '.dlist + .paragraph', output, 1
 3969     end
 3970 
 3971     test 'appends list and paragraph block when line following list attached by continuation' do
 3972       input = <<~'EOS'
 3973       == Lists
 3974 
 3975       term1::
 3976 
 3977       * one
 3978       * two
 3979       * three
 3980 
 3981       +
 3982       para
 3983       EOS
 3984 
 3985       output = convert_string_to_embedded input
 3986       assert_xpath '//*[@class="dlist"]/dl', output, 1
 3987       assert_xpath '//*[@class="dlist"]//dd', output, 1
 3988       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 3989       assert_xpath '//*[@class="dlist"]//dd/*[@class="ulist"]', output, 1
 3990       assert_xpath '//*[@class="dlist"]//dd/*[@class="ulist"]/ul/li', output, 3
 3991       assert_xpath '//*[@class="dlist"]//dd/*[@class="ulist"]/following-sibling::*[@class="paragraph"]', output, 1
 3992       assert_xpath '//*[@class="dlist"]//dd/*[@class="ulist"]/following-sibling::*[@class="paragraph"]/p[text()="para"]', output, 1
 3993     end
 3994 
 3995     test 'first continued line associated with nested list item and second continued line associated with term' do
 3996       input = <<~'EOS'
 3997       == Lists
 3998 
 3999       term1::
 4000       * one
 4001       +
 4002       nested list para
 4003 
 4004       +
 4005       term1 para
 4006       EOS
 4007 
 4008       output = convert_string_to_embedded input
 4009       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4010       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4011       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 4012       assert_xpath '//*[@class="dlist"]//dd/*[@class="ulist"]', output, 1
 4013       assert_xpath '//*[@class="dlist"]//dd/*[@class="ulist"]/ul/li', output, 1
 4014       assert_xpath '//*[@class="dlist"]//dd/*[@class="ulist"]/ul/li/*[@class="paragraph"]/p[text()="nested list para"]', output, 1
 4015       assert_xpath '//*[@class="dlist"]//dd/*[@class="ulist"]/following-sibling::*[@class="paragraph"]', output, 1
 4016       assert_xpath '//*[@class="dlist"]//dd/*[@class="ulist"]/following-sibling::*[@class="paragraph"]/p[text()="term1 para"]', output, 1
 4017     end
 4018 
 4019     test 'literal line attached by continuation swallows adjacent line that looks like term' do
 4020       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 4021       input = <<~EOS
 4022       == Lists
 4023 
 4024       term1::
 4025       +
 4026         literal
 4027       notnestedterm:::
 4028       +
 4029         literal
 4030       notnestedterm:::
 4031       EOS
 4032 
 4033       output = convert_string_to_embedded input
 4034       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4035       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4036       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 4037       assert_xpath '//*[@class="dlist"]//dd/*[@class="literalblock"]', output, 2
 4038       assert_xpath %(//*[@class="dlist"]//dd/*[@class="literalblock"]//pre[text()="  literal\nnotnestedterm:::"]), output, 2
 4039     end
 4040 
 4041     test 'line attached by continuation is appended as paragraph if term has no inline description' do
 4042       input = <<~'EOS'
 4043       == Lists
 4044 
 4045       term1::
 4046       +
 4047       para
 4048       EOS
 4049 
 4050       output = convert_string_to_embedded input
 4051       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4052       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4053       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 4054       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]', output, 1
 4055       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]/p[text()="para"]', output, 1
 4056     end
 4057 
 4058     test 'attached paragraph does not break on adjacent nested description list term' do
 4059       input = <<~'EOS'
 4060       term1:: def
 4061       +
 4062       more description
 4063       not a term::: def
 4064       EOS
 4065 
 4066       output = convert_string_to_embedded input
 4067       assert_css '.dlist > dl > dt', output, 1
 4068       assert_css '.dlist > dl > dd', output, 1
 4069       assert_css '.dlist > dl > dd > .paragraph', output, 1
 4070       assert_includes output, 'not a term::: def'
 4071     end
 4072 
 4073     # FIXME pending
 4074 =begin
 4075     test 'attached paragraph does not break on adjacent sibling description list term' do
 4076       input = <<~'EOS'
 4077       term1:: def
 4078       +
 4079       more description
 4080       not a term:: def
 4081       EOS
 4082 
 4083       output = convert_string_to_embedded input
 4084       assert_css '.dlist > dl > dt', output, 1
 4085       assert_css '.dlist > dl > dd', output, 1
 4086       assert_css '.dlist > dl > dd > .paragraph', output, 1
 4087       assert_includes output, 'not a term:: def'
 4088     end
 4089 =end
 4090 
 4091     test 'attached styled paragraph does not break on adjacent nested description list term' do
 4092       input = <<~'EOS'
 4093       term1:: def
 4094       +
 4095       [quote]
 4096       more description
 4097       not a term::: def
 4098       EOS
 4099 
 4100       output = convert_string_to_embedded input
 4101       assert_css '.dlist > dl > dt', output, 1
 4102       assert_css '.dlist > dl > dd', output, 1
 4103       assert_css '.dlist > dl > dd > .quoteblock', output, 1
 4104       assert_includes output, 'not a term::: def'
 4105     end
 4106 
 4107     test 'appends line as paragraph if attached by continuation following blank line and line comment when term has no inline description' do
 4108       input = <<~'EOS'
 4109       == Lists
 4110 
 4111       term1::
 4112 
 4113       // comment
 4114       +
 4115       para
 4116       EOS
 4117 
 4118       output = convert_string_to_embedded input
 4119       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4120       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4121       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 4122       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]', output, 1
 4123       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]/p[text()="para"]', output, 1
 4124     end
 4125 
 4126     test 'line attached by continuation offset by blank line is appended as paragraph if term has no inline description' do
 4127       input = <<~'EOS'
 4128       == Lists
 4129 
 4130       term1::
 4131 
 4132       +
 4133       para
 4134       EOS
 4135 
 4136       output = convert_string_to_embedded input
 4137       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4138       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4139       assert_xpath '//*[@class="dlist"]//dd/p', output, 0
 4140       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]', output, 1
 4141       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]/p[text()="para"]', output, 1
 4142     end
 4143 
 4144     test 'delimited block breaks list even when term has no inline description' do
 4145       input = <<~'EOS'
 4146       == Lists
 4147 
 4148       term1::
 4149       ====
 4150       detached
 4151       ====
 4152       EOS
 4153 
 4154       output = convert_string_to_embedded input
 4155       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4156       assert_xpath '//*[@class="dlist"]//dd', output, 0
 4157       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="exampleblock"]', output, 1
 4158       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="exampleblock"]//p[text()="detached"]', output, 1
 4159     end
 4160 
 4161     test 'attribute line breaks list even when term has no inline description' do
 4162       input = <<~'EOS'
 4163       == Lists
 4164 
 4165       term1::
 4166       [verse]
 4167       detached
 4168       EOS
 4169 
 4170       output = convert_string_to_embedded input
 4171       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4172       assert_xpath '//*[@class="dlist"]//dd', output, 0
 4173       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="verseblock"]', output, 1
 4174       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="verseblock"]/pre[text()="detached"]', output, 1
 4175     end
 4176 
 4177     test 'id line breaks list even when term has no inline description' do
 4178       input = <<~'EOS'
 4179       == Lists
 4180 
 4181       term1::
 4182       [[id]]
 4183       detached
 4184       EOS
 4185 
 4186       output = convert_string_to_embedded input
 4187       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4188       assert_xpath '//*[@class="dlist"]//dd', output, 0
 4189       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="paragraph"]', output, 1
 4190       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="paragraph"]/p[text()="detached"]', output, 1
 4191     end
 4192   end
 4193 
 4194   context 'Item with text inline' do
 4195 
 4196     test 'folds text from inline description and subsequent line' do
 4197       input = <<~'EOS'
 4198       == Lists
 4199 
 4200       term1:: def1
 4201       continued
 4202       EOS
 4203 
 4204       output = convert_string_to_embedded input
 4205       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4206       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4207       assert_xpath %(//*[@class="dlist"]//dd/p[text()="def1\ncontinued"]), output, 1
 4208     end
 4209 
 4210     test 'folds text from inline description and subsequent lines' do
 4211       input = <<~'EOS'
 4212       == Lists
 4213 
 4214       term1:: def1
 4215       continued
 4216       continued
 4217       EOS
 4218 
 4219       output = convert_string_to_embedded input
 4220       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4221       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4222       assert_xpath %(//*[@class="dlist"]//dd/p[text()="def1\ncontinued\ncontinued"]), output, 1
 4223     end
 4224 
 4225     test 'folds text from inline description and line following comment line' do
 4226       input = <<~'EOS'
 4227       == Lists
 4228 
 4229       term1:: def1
 4230       // comment
 4231       continued
 4232       EOS
 4233 
 4234       output = convert_string_to_embedded input
 4235       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4236       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4237       assert_xpath %(//*[@class="dlist"]//dd/p[text()="def1\ncontinued"]), output, 1
 4238     end
 4239 
 4240     test 'folds text from inline description and subsequent indented line' do
 4241       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 4242       input = <<~EOS
 4243       == List
 4244 
 4245       term1:: def1
 4246         continued
 4247       EOS
 4248 
 4249       output = convert_string_to_embedded input
 4250       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4251       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4252       assert_xpath %(//*[@class="dlist"]//dd/p[text()="def1\ncontinued"]), output, 1
 4253     end
 4254 
 4255     test 'appends literal line offset by blank line as block if item has inline description' do
 4256       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 4257       input = <<~EOS
 4258       == Lists
 4259 
 4260       term1:: def1
 4261 
 4262         literal
 4263       EOS
 4264 
 4265       output = convert_string_to_embedded input
 4266       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4267       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4268       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 4269       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="literalblock"]', output, 1
 4270       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="literalblock"]//pre[text()="literal"]', output, 1
 4271     end
 4272 
 4273     test 'appends literal line offset by blank line as block and appends line after continuation as block if item has inline description' do
 4274       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 4275       input = <<~EOS
 4276       == Lists
 4277 
 4278       term1:: def1
 4279 
 4280         literal
 4281       +
 4282       para
 4283       EOS
 4284 
 4285       output = convert_string_to_embedded input
 4286       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4287       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4288       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 4289       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="literalblock"]', output, 1
 4290       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="literalblock"]//pre[text()="literal"]', output, 1
 4291       assert_xpath '//*[@class="dlist"]//dd/*[@class="literalblock"]/following-sibling::*[@class="paragraph"]', output, 1
 4292       assert_xpath '//*[@class="dlist"]//dd/*[@class="literalblock"]/following-sibling::*[@class="paragraph"]/p[text()="para"]', output, 1
 4293     end
 4294 
 4295     test 'appends line after continuation as block and literal line offset by blank line as block if item has inline description' do
 4296       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 4297       input = <<~EOS
 4298       == Lists
 4299 
 4300       term1:: def1
 4301       +
 4302       para
 4303 
 4304         literal
 4305       EOS
 4306 
 4307       output = convert_string_to_embedded input
 4308       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4309       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4310       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 4311       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="paragraph"]', output, 1
 4312       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="paragraph"]/p[text()="para"]', output, 1
 4313       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]/following-sibling::*[@class="literalblock"]', output, 1
 4314       assert_xpath '//*[@class="dlist"]//dd/*[@class="paragraph"]/following-sibling::*[@class="literalblock"]//pre[text()="literal"]', output, 1
 4315     end
 4316 
 4317     test 'appends list if item has inline description' do
 4318       input = <<~'EOS'
 4319       == Lists
 4320 
 4321       term1:: def1
 4322 
 4323       * one
 4324       * two
 4325       * three
 4326       EOS
 4327 
 4328       output = convert_string_to_embedded input
 4329       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4330       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 4331       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="ulist"]', output, 1
 4332       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="ulist"]/ul/li', output, 3
 4333     end
 4334 
 4335     test 'appends literal line attached by continuation as block if item has inline description followed by ruler' do
 4336       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 4337       input = <<~EOS
 4338       == Lists
 4339 
 4340       term1:: def1
 4341       +
 4342         literal
 4343 
 4344       '''
 4345       EOS
 4346 
 4347       output = convert_string_to_embedded input
 4348       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4349       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4350       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 4351       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="literalblock"]', output, 1
 4352       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="literalblock"]//pre[text()="literal"]', output, 1
 4353       assert_xpath '//*[@class="dlist"]/following-sibling::hr', output, 1
 4354     end
 4355 
 4356     test 'line offset by blank line breaks list if term has inline description' do
 4357       input = <<~'EOS'
 4358       == Lists
 4359 
 4360       term1:: def1
 4361 
 4362       detached
 4363       EOS
 4364 
 4365       output = convert_string_to_embedded input
 4366       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4367       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4368       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 4369       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="paragraph"]', output, 1
 4370       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="paragraph"]/p[text()="detached"]', output, 1
 4371     end
 4372 
 4373     test 'nested term with description does not consume following heading' do
 4374       # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 4375       input = <<~EOS
 4376       == Lists
 4377 
 4378       term::
 4379         def
 4380         nestedterm;;
 4381           nesteddef
 4382 
 4383       Detached
 4384       ~~~~~~~~
 4385       EOS
 4386 
 4387       output = convert_string_to_embedded input
 4388       assert_xpath '//*[@class="dlist"]/dl', output, 2
 4389       assert_xpath '//*[@class="dlist"]//dd', output, 2
 4390       assert_xpath '//*[@class="dlist"]/dl//dl', output, 1
 4391       assert_xpath '//*[@class="dlist"]/dl//dl/dt', output, 1
 4392       assert_xpath '((//*[@class="dlist"])[1]//dd)[1]/p[text()="def"]', output, 1
 4393       assert_xpath '((//*[@class="dlist"])[1]//dd)[1]/p/following-sibling::*[@class="dlist"]', output, 1
 4394       assert_xpath '((//*[@class="dlist"])[1]//dd)[1]/p/following-sibling::*[@class="dlist"]//dd/p[text()="nesteddef"]', output, 1
 4395       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="sect2"]', output, 1
 4396       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="sect2"]/h3[text()="Detached"]', output, 1
 4397     end
 4398 
 4399     test 'line attached by continuation is appended as paragraph if term has inline description followed by detached paragraph' do
 4400       input = <<~'EOS'
 4401       == Lists
 4402 
 4403       term1:: def1
 4404       +
 4405       para
 4406 
 4407       detached
 4408       EOS
 4409 
 4410       output = convert_string_to_embedded input
 4411       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4412       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4413       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 4414       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="paragraph"]', output, 1
 4415       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="paragraph"]/p[text()="para"]', output, 1
 4416       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="paragraph"]', output, 1
 4417       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="paragraph"]/p[text()="detached"]', output, 1
 4418     end
 4419 
 4420     test 'line attached by continuation is appended as paragraph if term has inline description followed by detached block' do
 4421       input = <<~'EOS'
 4422       == Lists
 4423 
 4424       term1:: def1
 4425       +
 4426       para
 4427 
 4428       ****
 4429       detached
 4430       ****
 4431       EOS
 4432 
 4433       output = convert_string_to_embedded input
 4434       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4435       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4436       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 4437       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="paragraph"]', output, 1
 4438       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="paragraph"]/p[text()="para"]', output, 1
 4439       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="sidebarblock"]', output, 1
 4440       assert_xpath '//*[@class="dlist"]/following-sibling::*[@class="sidebarblock"]//p[text()="detached"]', output, 1
 4441     end
 4442 
 4443     test 'line attached by continuation offset by line comment is appended as paragraph if term has inline description' do
 4444       input = <<~'EOS'
 4445       == Lists
 4446 
 4447       term1:: def1
 4448       // comment
 4449       +
 4450       para
 4451       EOS
 4452 
 4453       output = convert_string_to_embedded input
 4454       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4455       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4456       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 4457       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="paragraph"]', output, 1
 4458       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="paragraph"]/p[text()="para"]', output, 1
 4459     end
 4460 
 4461     test 'line attached by continuation offset by blank line is appended as paragraph if term has inline description' do
 4462       input = <<~'EOS'
 4463       == Lists
 4464 
 4465       term1:: def1
 4466 
 4467       +
 4468       para
 4469       EOS
 4470 
 4471       output = convert_string_to_embedded input
 4472       assert_xpath '//*[@class="dlist"]/dl', output, 1
 4473       assert_xpath '//*[@class="dlist"]//dd', output, 1
 4474       assert_xpath '//*[@class="dlist"]//dd/p[text()="def1"]', output, 1
 4475       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="paragraph"]', output, 1
 4476       assert_xpath '//*[@class="dlist"]//dd/p/following-sibling::*[@class="paragraph"]/p[text()="para"]', output, 1
 4477     end
 4478 
 4479     test 'line comment offset by blank line divides lists because item has text' do
 4480       input = <<~'EOS'
 4481       == Lists
 4482 
 4483       term1:: def1
 4484 
 4485       //
 4486 
 4487       term2:: def2
 4488       EOS
 4489 
 4490       output = convert_string_to_embedded input
 4491       assert_xpath '//*[@class="dlist"]/dl', output, 2
 4492     end
 4493 
 4494     test 'ruler offset by blank line divides lists because item has text' do
 4495       input = <<~'EOS'
 4496       == Lists
 4497 
 4498       term1:: def1
 4499 
 4500       '''
 4501 
 4502       term2:: def2
 4503       EOS
 4504 
 4505       output = convert_string_to_embedded input
 4506       assert_xpath '//*[@class="dlist"]/dl', output, 2
 4507     end
 4508 
 4509     test 'block title offset by blank line divides lists and becomes title of second list because item has text' do
 4510       input = <<~'EOS'
 4511       == Lists
 4512 
 4513       term1:: def1
 4514 
 4515       .title
 4516 
 4517       term2:: def2
 4518       EOS
 4519 
 4520       output = convert_string_to_embedded input
 4521       assert_xpath '//*[@class="dlist"]/dl', output, 2
 4522       assert_xpath '(//*[@class="dlist"])[2]/*[@class="title"][text()="title"]', output, 1
 4523     end
 4524   end
 4525 end
 4526 
 4527 context 'Callout lists' do
 4528   test 'does not recognize callout list denoted by markers that only have a trailing bracket' do
 4529     input  = <<~'EOS'
 4530     ----
 4531     require 'asciidoctor' # <1>
 4532     ----
 4533     1> Not a callout list item
 4534     EOS
 4535 
 4536     output = convert_string_to_embedded input
 4537     assert_css '.colist', output, 0
 4538   end
 4539 
 4540   test 'listing block with sequential callouts followed by adjacent callout list' do
 4541     input = <<~'EOS'
 4542     [source, ruby]
 4543     ----
 4544     require 'asciidoctor' # <1>
 4545     doc = Asciidoctor::Document.new('Hello, World!') # <2>
 4546     puts doc.convert # <3>
 4547     ----
 4548     <1> Describe the first line
 4549     <2> Describe the second line
 4550     <3> Describe the third line
 4551     EOS
 4552     output = convert_string input, attributes: { 'backend' => 'docbook' }
 4553     assert_xpath '//programlisting', output, 1
 4554     assert_xpath '//programlisting//co', output, 3
 4555     assert_xpath '(//programlisting//co)[1][@xml:id="CO1-1"]', output, 1
 4556     assert_xpath '(//programlisting//co)[2][@xml:id="CO1-2"]', output, 1
 4557     assert_xpath '(//programlisting//co)[3][@xml:id="CO1-3"]', output, 1
 4558     assert_xpath '//programlisting/following-sibling::calloutlist/callout', output, 3
 4559     assert_xpath '(//programlisting/following-sibling::calloutlist/callout)[1][@arearefs = "CO1-1"]', output, 1
 4560     assert_xpath '(//programlisting/following-sibling::calloutlist/callout)[2][@arearefs = "CO1-2"]', output, 1
 4561     assert_xpath '(//programlisting/following-sibling::calloutlist/callout)[3][@arearefs = "CO1-3"]', output, 1
 4562   end
 4563 
 4564   test 'listing block with sequential callouts followed by non-adjacent callout list' do
 4565     input = <<~'EOS'
 4566     [source, ruby]
 4567     ----
 4568     require 'asciidoctor' # <1>
 4569     doc = Asciidoctor::Document.new('Hello, World!') # <2>
 4570     puts doc.convert # <3>
 4571     ----
 4572 
 4573     Paragraph.
 4574 
 4575     <1> Describe the first line
 4576     <2> Describe the second line
 4577     <3> Describe the third line
 4578     EOS
 4579     output = convert_string input, attributes: { 'backend' => 'docbook' }
 4580     assert_xpath '//programlisting', output, 1
 4581     assert_xpath '//programlisting//co', output, 3
 4582     assert_xpath '(//programlisting//co)[1][@xml:id="CO1-1"]', output, 1
 4583     assert_xpath '(//programlisting//co)[2][@xml:id="CO1-2"]', output, 1
 4584     assert_xpath '(//programlisting//co)[3][@xml:id="CO1-3"]', output, 1
 4585     assert_xpath '//programlisting/following-sibling::*[1][self::simpara]', output, 1
 4586     assert_xpath '//programlisting/following-sibling::calloutlist/callout', output, 3
 4587     assert_xpath '(//programlisting/following-sibling::calloutlist/callout)[1][@arearefs = "CO1-1"]', output, 1
 4588     assert_xpath '(//programlisting/following-sibling::calloutlist/callout)[2][@arearefs = "CO1-2"]', output, 1
 4589     assert_xpath '(//programlisting/following-sibling::calloutlist/callout)[3][@arearefs = "CO1-3"]', output, 1
 4590   end
 4591 
 4592   test 'listing block with a callout that refers to two different lines' do
 4593     input = <<~'EOS'
 4594     [source, ruby]
 4595     ----
 4596     require 'asciidoctor' # <1>
 4597     doc = Asciidoctor::Document.new('Hello, World!') # <2>
 4598     puts doc.convert # <2>
 4599     ----
 4600     <1> Import the library
 4601     <2> Where the magic happens
 4602     EOS
 4603     output = convert_string input, attributes: { 'backend' => 'docbook' }
 4604     assert_xpath '//programlisting', output, 1
 4605     assert_xpath '//programlisting//co', output, 3
 4606     assert_xpath '(//programlisting//co)[1][@xml:id="CO1-1"]', output, 1
 4607     assert_xpath '(//programlisting//co)[2][@xml:id="CO1-2"]', output, 1
 4608     assert_xpath '(//programlisting//co)[3][@xml:id="CO1-3"]', output, 1
 4609     assert_xpath '//programlisting/following-sibling::calloutlist/callout', output, 2
 4610     assert_xpath '(//programlisting/following-sibling::calloutlist/callout)[1][@arearefs = "CO1-1"]', output, 1
 4611     assert_xpath '(//programlisting/following-sibling::calloutlist/callout)[2][@arearefs = "CO1-2 CO1-3"]', output, 1
 4612   end
 4613 
 4614   test 'source block with non-sequential callouts followed by adjacent callout list' do
 4615     input = <<~'EOS'
 4616     [source,ruby]
 4617     ----
 4618     require 'asciidoctor' # <2>
 4619     doc = Asciidoctor::Document.new('Hello, World!') # <3>
 4620     puts doc.convert # <1>
 4621     ----
 4622     <1> Describe the first line
 4623     <2> Describe the second line
 4624     <3> Describe the third line
 4625     EOS
 4626     output = convert_string input, attributes: { 'backend' => 'docbook' }
 4627     assert_xpath '//programlisting', output, 1
 4628     assert_xpath '//programlisting//co', output, 3
 4629     assert_xpath '(//programlisting//co)[1][@xml:id="CO1-1"]', output, 1
 4630     assert_xpath '(//programlisting//co)[2][@xml:id="CO1-2"]', output, 1
 4631     assert_xpath '(//programlisting//co)[3][@xml:id="CO1-3"]', output, 1
 4632     assert_xpath '//programlisting/following-sibling::calloutlist/callout', output, 3
 4633     assert_xpath '(//programlisting/following-sibling::calloutlist/callout)[1][@arearefs = "CO1-3"]', output, 1
 4634     assert_xpath '(//programlisting/following-sibling::calloutlist/callout)[2][@arearefs = "CO1-1"]', output, 1
 4635     assert_xpath '(//programlisting/following-sibling::calloutlist/callout)[3][@arearefs = "CO1-2"]', output, 1
 4636   end
 4637 
 4638   test 'two listing blocks can share the same callout list' do
 4639     input = <<~'EOS'
 4640     .Import library
 4641     [source, ruby]
 4642     ----
 4643     require 'asciidoctor' # <1>
 4644     ----
 4645 
 4646     .Use library
 4647     [source, ruby]
 4648     ----
 4649     doc = Asciidoctor::Document.new('Hello, World!') # <2>
 4650     puts doc.convert # <3>
 4651     ----
 4652 
 4653     <1> Describe the first line
 4654     <2> Describe the second line
 4655     <3> Describe the third line
 4656     EOS
 4657     output = convert_string input, attributes: { 'backend' => 'docbook' }
 4658     assert_xpath '//programlisting', output, 2
 4659     assert_xpath '(//programlisting)[1]//co', output, 1
 4660     assert_xpath '(//programlisting)[1]//co[@xml:id="CO1-1"]', output, 1
 4661     assert_xpath '(//programlisting)[2]//co', output, 2
 4662     assert_xpath '((//programlisting)[2]//co)[1][@xml:id="CO1-2"]', output, 1
 4663     assert_xpath '((//programlisting)[2]//co)[2][@xml:id="CO1-3"]', output, 1
 4664     assert_xpath '(//calloutlist/callout)[1][@arearefs = "CO1-1"]', output, 1
 4665     assert_xpath '(//calloutlist/callout)[2][@arearefs = "CO1-2"]', output, 1
 4666     assert_xpath '(//calloutlist/callout)[3][@arearefs = "CO1-3"]', output, 1
 4667   end
 4668 
 4669   test 'two listing blocks each followed by an adjacent callout list' do
 4670     input = <<~'EOS'
 4671     .Import library
 4672     [source, ruby]
 4673     ----
 4674     require 'asciidoctor' # <1>
 4675     ----
 4676     <1> Describe the first line
 4677 
 4678     .Use library
 4679     [source, ruby]
 4680     ----
 4681     doc = Asciidoctor::Document.new('Hello, World!') # <1>
 4682     puts doc.convert # <2>
 4683     ----
 4684     <1> Describe the second line
 4685     <2> Describe the third line
 4686     EOS
 4687     output = convert_string input, attributes: { 'backend' => 'docbook' }
 4688     assert_xpath '//programlisting', output, 2
 4689     assert_xpath '(//programlisting)[1]//co', output, 1
 4690     assert_xpath '(//programlisting)[1]//co[@xml:id="CO1-1"]', output, 1
 4691     assert_xpath '(//programlisting)[2]//co', output, 2
 4692     assert_xpath '((//programlisting)[2]//co)[1][@xml:id="CO2-1"]', output, 1
 4693     assert_xpath '((//programlisting)[2]//co)[2][@xml:id="CO2-2"]', output, 1
 4694     assert_xpath '//calloutlist', output, 2
 4695     assert_xpath '(//calloutlist)[1]/callout', output, 1
 4696     assert_xpath '((//calloutlist)[1]/callout)[1][@arearefs = "CO1-1"]', output, 1
 4697     assert_xpath '(//calloutlist)[2]/callout', output, 2
 4698     assert_xpath '((//calloutlist)[2]/callout)[1][@arearefs = "CO2-1"]', output, 1
 4699     assert_xpath '((//calloutlist)[2]/callout)[2][@arearefs = "CO2-2"]', output, 1
 4700   end
 4701 
 4702   test 'callout list retains block content' do
 4703     input = <<~'EOS'
 4704     [source, ruby]
 4705     ----
 4706     require 'asciidoctor' # <1>
 4707     doc = Asciidoctor::Document.new('Hello, World!') # <2>
 4708     puts doc.convert # <3>
 4709     ----
 4710     <1> Imports the library
 4711     as a RubyGem
 4712     <2> Creates a new document
 4713     * Scans the lines for known blocks
 4714     * Converts the lines into blocks
 4715     <3> Renders the document
 4716     +
 4717     You can write this to file rather than printing to stdout.
 4718     EOS
 4719     output = convert_string_to_embedded input
 4720     assert_xpath '//ol/li', output, 3
 4721     assert_xpath %((//ol/li)[1]/p[text()="Imports the library\nas a RubyGem"]), output, 1
 4722     assert_xpath %((//ol/li)[2]//ul), output, 1
 4723     assert_xpath %((//ol/li)[2]//ul/li), output, 2
 4724     assert_xpath %((//ol/li)[3]//p), output, 2
 4725   end
 4726 
 4727   test 'callout list retains block content when converted to DocBook' do
 4728     input = <<~'EOS'
 4729     [source, ruby]
 4730     ----
 4731     require 'asciidoctor' # <1>
 4732     doc = Asciidoctor::Document.new('Hello, World!') # <2>
 4733     puts doc.convert # <3>
 4734     ----
 4735     <1> Imports the library
 4736     as a RubyGem
 4737     <2> Creates a new document
 4738     * Scans the lines for known blocks
 4739     * Converts the lines into blocks
 4740     <3> Renders the document
 4741     +
 4742     You can write this to file rather than printing to stdout.
 4743     EOS
 4744     output = convert_string input, attributes: { 'backend' => 'docbook' }
 4745     assert_xpath '//calloutlist', output, 1
 4746     assert_xpath '//calloutlist/callout', output, 3
 4747     assert_xpath '(//calloutlist/callout)[1]/*', output, 1
 4748     assert_xpath '(//calloutlist/callout)[2]/para', output, 1
 4749     assert_xpath '(//calloutlist/callout)[2]/itemizedlist', output, 1
 4750     assert_xpath '(//calloutlist/callout)[3]/para', output, 1
 4751     assert_xpath '(//calloutlist/callout)[3]/simpara', output, 1
 4752   end
 4753 
 4754   test 'escaped callout should not be interpreted as a callout' do
 4755     input = <<~'EOS'
 4756     [source,text]
 4757     ----
 4758     require 'asciidoctor' # \<1>
 4759     Asciidoctor.convert 'convert me!' \<2>
 4760     ----
 4761     EOS
 4762     [{}, { 'source-highlighter' => 'coderay' }].each do |attributes|
 4763       output = convert_string_to_embedded input, attributes: attributes
 4764       assert_css 'pre b', output, 0
 4765       assert_includes output, ' # &lt;1&gt;'
 4766       assert_includes output, ' &lt;2&gt;'
 4767     end
 4768   end
 4769 
 4770   test 'should autonumber <.> callouts' do
 4771     input = <<~'EOS'
 4772     [source, ruby]
 4773     ----
 4774     require 'asciidoctor' # <.>
 4775     doc = Asciidoctor::Document.new('Hello, World!') # <.>
 4776     puts doc.convert # <.>
 4777     ----
 4778     <.> Describe the first line
 4779     <.> Describe the second line
 4780     <.> Describe the third line
 4781     EOS
 4782     output = convert_string_to_embedded input
 4783     pre_html = (xmlnodes_at_css 'pre', output)[0].inner_html
 4784     assert_includes pre_html, '(1)'
 4785     assert_includes pre_html, '(2)'
 4786     assert_includes pre_html, '(3)'
 4787     assert_css '.colist ol', output, 1
 4788     assert_css '.colist ol li', output, 3
 4789   end
 4790 
 4791   test 'should not recognize callouts in middle of line' do
 4792     input = <<~'EOS'
 4793     [source, ruby]
 4794     ----
 4795     puts "The syntax <1> at the end of the line makes a code callout"
 4796     ----
 4797     EOS
 4798     output = convert_string_to_embedded input
 4799     assert_xpath '//b', output, 0
 4800   end
 4801 
 4802   test 'should allow multiple callouts on the same line' do
 4803     input = <<~'EOS'
 4804     [source, ruby]
 4805     ----
 4806     require 'asciidoctor' <1>
 4807     doc = Asciidoctor.load('Hello, World!') # <2> <3> <4>
 4808     puts doc.convert <5><6>
 4809     exit 0
 4810     ----
 4811     <1> Require library
 4812     <2> Load document from String
 4813     <3> Uses default backend and doctype
 4814     <4> One more for good luck
 4815     <5> Renders document to String
 4816     <6> Prints output to stdout
 4817     EOS
 4818     output = convert_string_to_embedded input
 4819     assert_xpath '//code/b', output, 6
 4820     assert_match(/ <b class="conum">\(1\)<\/b>$/, output)
 4821     assert_match(/ <b class="conum">\(2\)<\/b> <b class="conum">\(3\)<\/b> <b class="conum">\(4\)<\/b>$/, output)
 4822     assert_match(/ <b class="conum">\(5\)<\/b><b class="conum">\(6\)<\/b>$/, output)
 4823   end
 4824 
 4825   test 'should allow XML comment-style callouts' do
 4826     input = <<~'EOS'
 4827     [source, xml]
 4828     ----
 4829     <section>
 4830       <title>Section Title</title> <!--1-->
 4831       <simpara>Just a paragraph</simpara> <!--2-->
 4832     </section>
 4833     ----
 4834     <1> The title is required
 4835     <2> The content isn't
 4836     EOS
 4837     output = convert_string_to_embedded input
 4838     assert_xpath '//b', output, 2
 4839     assert_xpath '//b[text()="(1)"]', output, 1
 4840     assert_xpath '//b[text()="(2)"]', output, 1
 4841   end
 4842 
 4843   test 'should not allow callouts with half an XML comment' do
 4844     input = <<~'EOS'
 4845     ----
 4846     First line <1-->
 4847     Second line <2-->
 4848     ----
 4849     EOS
 4850     output = convert_string_to_embedded input
 4851     assert_xpath '//b', output, 0
 4852   end
 4853 
 4854   test 'should not recognize callouts in an indented description list paragraph' do
 4855     # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 4856     input = <<~EOS
 4857     foo::
 4858       bar <1>
 4859 
 4860     <1> Not pointing to a callout
 4861     EOS
 4862     using_memory_logger do |logger|
 4863       output = convert_string_to_embedded input
 4864       assert_xpath '//dl//b', output, 0
 4865       assert_xpath '//dl/dd/p[text()="bar <1>"]', output, 1
 4866       assert_xpath '//ol/li/p[text()="Not pointing to a callout"]', output, 1
 4867       assert_message logger, :WARN, '<stdin>: line 4: no callout found for <1>', Hash
 4868     end
 4869   end
 4870 
 4871   test 'should not recognize callouts in an indented outline list paragraph' do
 4872     # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 4873     input = <<~EOS
 4874     * foo
 4875       bar <1>
 4876 
 4877     <1> Not pointing to a callout
 4878     EOS
 4879     using_memory_logger do |logger|
 4880       output = convert_string_to_embedded input
 4881       assert_xpath '//ul//b', output, 0
 4882       assert_xpath %(//ul/li/p[text()="foo\nbar <1>"]), output, 1
 4883       assert_xpath '//ol/li/p[text()="Not pointing to a callout"]', output, 1
 4884       assert_message logger, :WARN, '<stdin>: line 4: no callout found for <1>', Hash
 4885     end
 4886   end
 4887 
 4888   test 'should warn if numbers in callout list are out of sequence' do
 4889     input = <<~'EOS'
 4890     ----
 4891     <beans> <1>
 4892       <bean class="com.example.HelloWorld"/>
 4893     </beans>
 4894     ----
 4895     <1> Container of beans.
 4896     Beans are fun.
 4897     <3> An actual bean.
 4898     EOS
 4899     using_memory_logger do |logger|
 4900       output = convert_string_to_embedded input
 4901       assert_xpath '//ol/li', output, 2
 4902       assert_messages logger, [
 4903         [:WARN, '<stdin>: line 8: callout list item index: expected 2, got 3', Hash],
 4904         [:WARN, '<stdin>: line 8: no callout found for <2>', Hash]
 4905       ]
 4906     end
 4907   end
 4908 
 4909   test 'should preserve line comment chars that precede callout number if icons is not set' do
 4910     input = <<~'EOS'
 4911     [source,ruby]
 4912     ----
 4913     puts 'Hello, world!' # <1>
 4914     ----
 4915     <1> Ruby
 4916 
 4917     [source,groovy]
 4918     ----
 4919     println 'Hello, world!' // <1>
 4920     ----
 4921     <1> Groovy
 4922 
 4923     [source,clojure]
 4924     ----
 4925     (def hello (fn [] "Hello, world!")) ;; <1>
 4926     (hello)
 4927     ----
 4928     <1> Clojure
 4929 
 4930     [source,haskell]
 4931     ----
 4932     main = putStrLn "Hello, World!" -- <1>
 4933     ----
 4934     <1> Haskell
 4935     EOS
 4936     [{}, { 'source-highlighter' => 'coderay' }].each do |attributes|
 4937       output = convert_string_to_embedded input, attributes: attributes
 4938       assert_xpath '//b', output, 4
 4939       nodes = xmlnodes_at_css 'pre', output
 4940       assert_equal %(puts 'Hello, world!' # (1)), nodes[0].text
 4941       assert_equal %(println 'Hello, world!' // (1)), nodes[1].text
 4942       assert_equal %((def hello (fn [] "Hello, world!")) ;; (1)\n(hello)), nodes[2].text
 4943       assert_equal %(main = putStrLn "Hello, World!" -- (1)), nodes[3].text
 4944     end
 4945   end
 4946 
 4947   test 'should remove line comment chars that precede callout number if icons is font' do
 4948     input = <<~'EOS'
 4949     [source,ruby]
 4950     ----
 4951     puts 'Hello, world!' # <1>
 4952     ----
 4953     <1> Ruby
 4954 
 4955     [source,groovy]
 4956     ----
 4957     println 'Hello, world!' // <1>
 4958     ----
 4959     <1> Groovy
 4960 
 4961     [source,clojure]
 4962     ----
 4963     (def hello (fn [] "Hello, world!")) ;; <1>
 4964     (hello)
 4965     ----
 4966     <1> Clojure
 4967 
 4968     [source,haskell]
 4969     ----
 4970     main = putStrLn "Hello, World!" -- <1>
 4971     ----
 4972     <1> Haskell
 4973     EOS
 4974     [{}, { 'source-highlighter' => 'coderay' }].each do |attributes|
 4975       output = convert_string_to_embedded input, attributes: attributes.merge({ 'icons' => 'font' })
 4976       assert_css 'pre b', output, 4
 4977       assert_css 'pre i.conum', output, 4
 4978       nodes = xmlnodes_at_css 'pre', output
 4979       assert_equal %(puts 'Hello, world!' (1)), nodes[0].text
 4980       assert_equal %(println 'Hello, world!' (1)), nodes[1].text
 4981       assert_equal %((def hello (fn [] "Hello, world!")) (1)\n(hello)), nodes[2].text
 4982       assert_equal %(main = putStrLn "Hello, World!" (1)), nodes[3].text
 4983     end
 4984   end
 4985 
 4986   test 'should allow line comment chars that precede callout number to be specified' do
 4987     # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 4988     input = <<~EOS
 4989     [source,erlang,line-comment=%]
 4990     ----
 4991     hello_world() -> % <1>
 4992       io:fwrite("hello, world~n"). %<2>
 4993     ----
 4994     <1> Erlang function clause head.
 4995     <2> ~n adds a new line to the output.
 4996     EOS
 4997     output = convert_string_to_embedded input
 4998     assert_xpath '//b', output, 2
 4999     nodes = xmlnodes_at_css 'pre', output
 5000     assert_equal %(hello_world() -> % (1)\n  io:fwrite("hello, world~n"). %(2)), nodes[0].text
 5001   end
 5002 
 5003   test 'should allow line comment chars preceding callout number to be configurable when source-highlighter is coderay' do
 5004     input = <<~'EOS'
 5005     [source,html,line-comment=-#]
 5006     ----
 5007     -# <1>
 5008     %p Hello
 5009     ----
 5010     <1> Prints a paragraph with the text "Hello"
 5011     EOS
 5012     output = convert_string_to_embedded input, attributes: { 'source-highlighter' => 'coderay' }
 5013     assert_xpath '//b', output, 1
 5014     nodes = xmlnodes_at_css 'pre', output
 5015     assert_equal %(-# (1)\n%p Hello), nodes[0].text
 5016   end
 5017 
 5018   test 'should not eat whitespace before callout number if line-comment attribute is empty' do
 5019     input = <<~'EOS'
 5020     [source,asciidoc,line-comment=]
 5021     ----
 5022     -- <1>
 5023     ----
 5024     <1> The start of an open block.
 5025     EOS
 5026     output = convert_string_to_embedded input, attributes: { 'icons' => 'font' }
 5027     assert_includes output, '-- <i class="conum"'
 5028   end
 5029 
 5030   test 'literal block with callouts' do
 5031     input = <<~'EOS'
 5032     ....
 5033     Roses are red <1>
 5034     Violets are blue <2>
 5035     ....
 5036 
 5037 
 5038     <1> And so is Ruby
 5039     <2> But violet is more like purple
 5040     EOS
 5041     output = convert_string input, attributes: { 'backend' => 'docbook' }
 5042     assert_xpath '//literallayout', output, 1
 5043     assert_xpath '//literallayout//co', output, 2
 5044     assert_xpath '(//literallayout//co)[1][@xml:id="CO1-1"]', output, 1
 5045     assert_xpath '(//literallayout//co)[2][@xml:id="CO1-2"]', output, 1
 5046     assert_xpath '//literallayout/following-sibling::*[1][self::calloutlist]/callout', output, 2
 5047     assert_xpath '(//literallayout/following-sibling::*[1][self::calloutlist]/callout)[1][@arearefs = "CO1-1"]', output, 1
 5048     assert_xpath '(//literallayout/following-sibling::*[1][self::calloutlist]/callout)[2][@arearefs = "CO1-2"]', output, 1
 5049   end
 5050 
 5051   test 'callout list with icons enabled' do
 5052     input = <<~'EOS'
 5053     [source, ruby]
 5054     ----
 5055     require 'asciidoctor' # <1>
 5056     doc = Asciidoctor::Document.new('Hello, World!') # <2>
 5057     puts doc.convert # <3>
 5058     ----
 5059     <1> Describe the first line
 5060     <2> Describe the second line
 5061     <3> Describe the third line
 5062     EOS
 5063     output = convert_string_to_embedded input, attributes: { 'icons' => '' }
 5064     assert_css '.listingblock code > img', output, 3
 5065     (1..3).each do |i|
 5066       assert_xpath %((/div[@class="listingblock"]//code/img)[#{i}][@src="./images/icons/callouts/#{i}.png"][@alt="#{i}"]), output, 1
 5067     end
 5068     assert_css '.colist table td img', output, 3
 5069     (1..3).each do |i|
 5070       assert_xpath %((/div[@class="colist arabic"]//td/img)[#{i}][@src="./images/icons/callouts/#{i}.png"][@alt="#{i}"]), output, 1
 5071     end
 5072   end
 5073 
 5074   test 'callout list with font-based icons enabled' do
 5075     input = <<~'EOS'
 5076     [source]
 5077     ----
 5078     require 'asciidoctor' # <1>
 5079     doc = Asciidoctor::Document.new('Hello, World!') #<2>
 5080     puts doc.convert #<3>
 5081     ----
 5082     <1> Describe the first line
 5083     <2> Describe the second line
 5084     <3> Describe the third line
 5085     EOS
 5086     output = convert_string_to_embedded input, attributes: { 'icons' => 'font' }
 5087     assert_css '.listingblock code > i', output, 3
 5088     (1..3).each do |i|
 5089       assert_xpath %((/div[@class="listingblock"]//code/i)[#{i}]), output, 1
 5090       assert_xpath %((/div[@class="listingblock"]//code/i)[#{i}][@class="conum"][@data-value="#{i}"]), output, 1
 5091       assert_xpath %((/div[@class="listingblock"]//code/i)[#{i}]/following-sibling::b[text()="(#{i})"]), output, 1
 5092     end
 5093     assert_css '.colist table td i', output, 3
 5094     (1..3).each do |i|
 5095       assert_xpath %((/div[@class="colist arabic"]//td/i)[#{i}]), output, 1
 5096       assert_xpath %((/div[@class="colist arabic"]//td/i)[#{i}][@class="conum"][@data-value = "#{i}"]), output, 1
 5097       assert_xpath %((/div[@class="colist arabic"]//td/i)[#{i}]/following-sibling::b[text() = "#{i}"]), output, 1
 5098     end
 5099   end
 5100 
 5101   test 'should match trailing line separator in text of list item' do
 5102     input = <<~EOS.chop
 5103     ----
 5104     A <1>
 5105     B <2>
 5106     C <3>
 5107     ----
 5108     <1> a
 5109     <2> b#{decode_char 8232}
 5110     <3> c
 5111     EOS
 5112 
 5113     output = convert_string input
 5114     assert_css 'li', output, 3
 5115     assert_xpath %((//li)[2]/p[text()="b#{decode_char 8232}"]), output, 1
 5116   end
 5117 
 5118   test 'should match line separator in text of list item' do
 5119     input = <<~EOS.chop
 5120     ----
 5121     A <1>
 5122     B <2>
 5123     C <3>
 5124     ----
 5125     <1> a
 5126     <2> b#{decode_char 8232}b
 5127     <3> c
 5128     EOS
 5129 
 5130     output = convert_string input
 5131     assert_css 'li', output, 3
 5132     assert_xpath %((//li)[2]/p[text()="b#{decode_char 8232}b"]), output, 1
 5133   end
 5134 end
 5135 
 5136 context 'Checklists' do
 5137   test 'should create checklist if at least one item has checkbox syntax' do
 5138     input = <<~'EOS'
 5139     - [ ] todo
 5140     - [x] done
 5141     - [ ] another todo
 5142     - [*] another done
 5143     - plain
 5144     EOS
 5145 
 5146     doc = document_from_string input
 5147     checklist = doc.blocks[0]
 5148     assert checklist.option?('checklist')
 5149     assert checklist.items[0].attr?('checkbox')
 5150     refute checklist.items[0].attr?('checked')
 5151     assert checklist.items[1].attr?('checkbox')
 5152     assert checklist.items[1].attr?('checked')
 5153     refute checklist.items[4].attr?('checkbox')
 5154 
 5155     output = doc.convert standalone: false
 5156     assert_css '.ulist.checklist', output, 1
 5157     assert_xpath %((/*[@class="ulist checklist"]/ul/li)[1]/p[text()="#{decode_char 10063} todo"]), output, 1
 5158     assert_xpath %((/*[@class="ulist checklist"]/ul/li)[2]/p[text()="#{decode_char 10003} done"]), output, 1
 5159     assert_xpath %((/*[@class="ulist checklist"]/ul/li)[3]/p[text()="#{decode_char 10063} another todo"]), output, 1
 5160     assert_xpath %((/*[@class="ulist checklist"]/ul/li)[4]/p[text()="#{decode_char 10003} another done"]), output, 1
 5161     assert_xpath '(/*[@class="ulist checklist"]/ul/li)[5]/p[text()="plain"]', output, 1
 5162   end
 5163 
 5164   test 'should create checklist with font icons if at least one item has checkbox syntax and icons attribute is font' do
 5165     input = <<~'EOS'
 5166     - [ ] todo
 5167     - [x] done
 5168     - plain
 5169     EOS
 5170 
 5171     output = convert_string_to_embedded input, attributes: { 'icons' => 'font' }
 5172     assert_css '.ulist.checklist', output, 1
 5173     assert_css '.ulist.checklist li i.fa-check-square-o', output, 1
 5174     assert_css '.ulist.checklist li i.fa-square-o', output, 1
 5175     assert_xpath '(/*[@class="ulist checklist"]/ul/li)[3]/p[text()="plain"]', output, 1
 5176   end
 5177 
 5178   test 'should create interactive checklist if interactive option is set even with icons attribute is font' do
 5179     input = <<~'EOS'
 5180     :icons: font
 5181 
 5182     [%interactive]
 5183     - [ ] todo
 5184     - [x] done
 5185     EOS
 5186 
 5187     doc = document_from_string input
 5188     checklist = doc.blocks[0]
 5189     assert checklist.option?('checklist')
 5190     assert checklist.option?('interactive')
 5191 
 5192     output = doc.convert standalone: false
 5193     assert_css '.ulist.checklist', output, 1
 5194     assert_css '.ulist.checklist li input[type="checkbox"]', output, 2
 5195     assert_css '.ulist.checklist li input[type="checkbox"][disabled]', output, 0
 5196     assert_css '.ulist.checklist li input[type="checkbox"][checked]', output, 1
 5197   end
 5198 end
 5199 
 5200 context 'Lists model' do
 5201   test 'content should return items in list' do
 5202     input = <<~'EOS'
 5203     * one
 5204     * two
 5205     * three
 5206     EOS
 5207 
 5208     doc = document_from_string input
 5209     list = doc.blocks.first
 5210     assert_kind_of Asciidoctor::List, list
 5211     items = list.items
 5212     assert_equal 3, items.size
 5213     assert_equal list.items, list.content
 5214   end
 5215 
 5216   test 'list item should be the parent of block attached to a list item' do
 5217     input = <<~'EOS'
 5218     * list item 1
 5219     +
 5220     ----
 5221     listing block in list item 1
 5222     ----
 5223     EOS
 5224 
 5225     doc = document_from_string input
 5226     list = doc.blocks.first
 5227     list_item_1 = list.items.first
 5228     listing_block = list_item_1.blocks.first
 5229     assert_equal :listing, listing_block.context
 5230     assert_equal list_item_1, listing_block.parent
 5231   end
 5232 
 5233   test 'outline? should return true for unordered list' do
 5234     input = <<~'EOS'
 5235     * one
 5236     * two
 5237     * three
 5238     EOS
 5239 
 5240     doc = document_from_string input
 5241     list = doc.blocks.first
 5242     assert list.outline?
 5243   end
 5244 
 5245   test 'outline? should return true for ordered list' do
 5246     input = <<~'EOS'
 5247     . one
 5248     . two
 5249     . three
 5250     EOS
 5251 
 5252     doc = document_from_string input
 5253     list = doc.blocks.first
 5254     assert list.outline?
 5255   end
 5256 
 5257   test 'outline? should return false for description list' do
 5258     input = 'label:: desc'
 5259     doc = document_from_string input
 5260     list = doc.blocks.first
 5261     refute list.outline?
 5262   end
 5263 
 5264   test 'simple? should return true for list item with no nested blocks' do
 5265     input = <<~'EOS'
 5266     * one
 5267     * two
 5268     * three
 5269     EOS
 5270 
 5271     doc = document_from_string input
 5272     list = doc.blocks.first
 5273     assert list.items.first.simple?
 5274     refute list.items.first.compound?
 5275   end
 5276 
 5277   test 'simple? should return true for list item with nested outline list' do
 5278     # NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
 5279     input = <<~EOS
 5280     * one
 5281       ** more about one
 5282       ** and more
 5283     * two
 5284     * three
 5285     EOS
 5286 
 5287     doc = document_from_string input
 5288     list = doc.blocks.first
 5289     assert list.items.first.simple?
 5290     refute list.items.first.compound?
 5291   end
 5292 
 5293   test 'simple? should return false for list item with block content' do
 5294     input = <<~'EOS'
 5295     * one
 5296     +
 5297     ----
 5298     listing block in list item 1
 5299     ----
 5300     * two
 5301     * three
 5302     EOS
 5303 
 5304     doc = document_from_string input
 5305     list = doc.blocks.first
 5306     refute list.items.first.simple?
 5307     assert list.items.first.compound?
 5308   end
 5309 
 5310   test 'should allow text of ListItem to be assigned' do
 5311     input = <<~'EOS'
 5312     * one
 5313     * two
 5314     * three
 5315     EOS
 5316 
 5317     doc = document_from_string input
 5318     list = (doc.find_by context: :ulist).first
 5319     assert_equal 3, list.items.size
 5320     assert_equal 'one', list.items[0].text
 5321     list.items[0].text = 'un'
 5322     assert_equal 'un', list.items[0].text
 5323   end
 5324 
 5325   test 'id and role assigned to ulist item in model are transmitted to output' do
 5326     input = <<~'EOS'
 5327         * one
 5328         * two
 5329         * three
 5330     EOS
 5331 
 5332     doc = document_from_string input
 5333     item_0 = doc.blocks[0].items[0]
 5334     item_0.id = 'one'
 5335     item_0.add_role 'item'
 5336     output = doc.convert
 5337     assert_css 'li#one.item', output, 1
 5338   end
 5339 
 5340   test 'id and role assigned to olist item in model are transmitted to output' do
 5341     input = <<~'EOS'
 5342     . one
 5343     . two
 5344     . three
 5345     EOS
 5346 
 5347     doc = document_from_string input
 5348     item_0 = doc.blocks[0].items[0]
 5349     item_0.id = 'one'
 5350     item_0.add_role 'item'
 5351     output = doc.convert
 5352     assert_css 'li#one.item', output, 1
 5353   end
 5354 
 5355   test 'should allow API control over substitutions applied to ListItem text' do
 5356     input = <<~'EOS'
 5357     * *one*
 5358     * _two_
 5359     * `three`
 5360     * #four#
 5361     EOS
 5362 
 5363     doc = document_from_string input
 5364     list = (doc.find_by context: :ulist).first
 5365     assert_equal 4, list.items.size
 5366     list.items[0].remove_sub :quotes
 5367     assert_equal '*one*', list.items[0].text
 5368     refute_includes list.items[0].subs, :quotes
 5369     list.items[1].subs.clear
 5370     assert_empty list.items[1].subs
 5371     assert_equal '_two_', list.items[1].text
 5372     list.items[2].subs.replace [:specialcharacters]
 5373     assert_equal [:specialcharacters], list.items[2].subs
 5374     assert_equal '`three`', list.items[2].text
 5375     assert_equal '<mark>four</mark>', list.items[3].text
 5376   end
 5377 
 5378   test 'should set lineno to line number in source where list starts' do
 5379     input = <<~'EOS'
 5380     * bullet 1
 5381     ** bullet 1.1
 5382     *** bullet 1.1.1
 5383     * bullet 2
 5384     EOS
 5385     doc = document_from_string input, sourcemap: true
 5386     lists = doc.find_by context: :ulist
 5387     assert_equal 1, lists[0].lineno
 5388     assert_equal 2, lists[1].lineno
 5389     assert_equal 3, lists[2].lineno
 5390 
 5391     list_items = doc.find_by context: :list_item
 5392     assert_equal 1, list_items[0].lineno
 5393     assert_equal 2, list_items[1].lineno
 5394     assert_equal 3, list_items[2].lineno
 5395     assert_equal 4, list_items[3].lineno
 5396   end
 5397 end