# frozen_string_literal: true
require_relative 'test_helper'
context 'Blocks' do
default_logger = Asciidoctor::LoggerManager.logger
setup do
Asciidoctor::LoggerManager.logger = (@logger = Asciidoctor::MemoryLogger.new)
end
teardown do
Asciidoctor::LoggerManager.logger = default_logger
end
context 'Layout Breaks' do
test 'horizontal rule' do
%w(''' '''' '''''').each do |line|
output = convert_string_to_embedded line
assert_includes output, '
'
end
end
test 'horizontal rule with markdown syntax disabled' do
old_markdown_syntax = Asciidoctor::Compliance.markdown_syntax
begin
Asciidoctor::Compliance.markdown_syntax = false
%w(''' '''' '''''').each do |line|
output = convert_string_to_embedded line
assert_includes output, ''
end
%w(--- *** ___).each do |line|
output = convert_string_to_embedded line
refute_includes output, ''
end
ensure
Asciidoctor::Compliance.markdown_syntax = old_markdown_syntax
end
end
test '< 3 chars does not make horizontal rule' do
%w(' '').each do |line|
output = convert_string_to_embedded line
refute_includes output, ''
assert_includes output, %(
#{line}
)
end
end
test 'mixed chars does not make horizontal rule' do
[%q(''<), %q('''<), %q(' ' ')].each do |line|
output = convert_string_to_embedded line
refute_includes output, ''
assert_includes output, %(
#{line.sub '<', '<'}
)
end
end
test 'horizontal rule between blocks' do
output = convert_string_to_embedded %(Block above\n\n'''\n\nBlock below)
assert_xpath '/hr', output, 1
assert_xpath '/hr/preceding-sibling::*', output, 1
assert_xpath '/hr/following-sibling::*', output, 1
end
test 'page break' do
output = convert_string_to_embedded %(page 1\n\n<<<\n\npage 2)
assert_xpath '/*[translate(@style, ";", "")="page-break-after: always"]', output, 1
assert_xpath '/*[translate(@style, ";", "")="page-break-after: always"]/preceding-sibling::div/p[text()="page 1"]', output, 1
assert_xpath '/*[translate(@style, ";", "")="page-break-after: always"]/following-sibling::div/p[text()="page 2"]', output, 1
end
end
context 'Comments' do
test 'line comment between paragraphs offset by blank lines' do
input = <<~'EOS'
first paragraph
// line comment
second paragraph
EOS
output = convert_string_to_embedded input
refute_match(/line comment/, output)
assert_xpath '//p', output, 2
end
test 'adjacent line comment between paragraphs' do
input = <<~'EOS'
first line
// line comment
second line
EOS
output = convert_string_to_embedded input
refute_match(/line comment/, output)
assert_xpath '//p', output, 1
assert_xpath "//p[1][text()='first line\nsecond line']", output, 1
end
test 'comment block between paragraphs offset by blank lines' do
input = <<~'EOS'
first paragraph
////
block comment
////
second paragraph
EOS
output = convert_string_to_embedded input
refute_match(/block comment/, output)
assert_xpath '//p', output, 2
end
test 'comment block between paragraphs offset by blank lines inside delimited block' do
input = <<~'EOS'
====
first paragraph
////
block comment
////
second paragraph
====
EOS
output = convert_string_to_embedded input
refute_match(/block comment/, output)
assert_xpath '//p', output, 2
end
test 'adjacent comment block between paragraphs' do
input = <<~'EOS'
first paragraph
////
block comment
////
second paragraph
EOS
output = convert_string_to_embedded input
refute_match(/block comment/, output)
assert_xpath '//p', output, 2
end
test "can convert with block comment at end of document with trailing newlines" do
input = <<~'EOS'
paragraph
////
block comment
////
EOS
output = convert_string_to_embedded input
refute_match(/block comment/, output)
end
test "trailing newlines after block comment at end of document does not create paragraph" do
input = <<~'EOS'
paragraph
////
block comment
////
EOS
d = document_from_string input
assert_equal 1, d.blocks.size
assert_xpath '//p', d.convert, 1
end
test 'line starting with three slashes should not be line comment' do
input = '/// not a line comment'
output = convert_string_to_embedded input
refute_empty output.strip, "Line should be emitted => #{input.rstrip}"
end
test 'preprocessor directives should not be processed within comment block within block metadata' do
input = <<~'EOS'
.sample title
////
ifdef::asciidoctor[////]
////
line should be shown
EOS
output = convert_string_to_embedded input
assert_xpath '//p[text()="line should be shown"]', output, 1
end
test 'preprocessor directives should not be processed within comment block' do
input = <<~'EOS'
dummy line
////
ifdef::asciidoctor[////]
////
line should be shown
EOS
output = convert_string_to_embedded input
assert_xpath '//p[text()="line should be shown"]', output, 1
end
test 'should warn if unterminated comment block is detected in body' do
input = <<~'EOS'
before comment block
////
content that has been disabled
supposed to be after comment block, except it got swallowed by block comment
EOS
convert_string_to_embedded input
assert_message @logger, :WARN, ': line 3: unterminated comment block', Hash
end
test 'should warn if unterminated comment block is detected inside another block' do
input = <<~'EOS'
before sidebar block
****
////
content that has been disabled
****
supposed to be after sidebar block, except it got swallowed by block comment
EOS
convert_string_to_embedded input
assert_message @logger, :WARN, ': line 4: unterminated comment block', Hash
end
# WARNING if first line of content is a directive, it will get interpretted before we know it's a comment block
# it happens because we always look a line ahead...not sure what we can do about it
test 'preprocessor directives should not be processed within comment open block' do
input = <<~'EOS'
[comment]
--
first line of comment
ifdef::asciidoctor[--]
line should not be shown
--
EOS
output = convert_string_to_embedded input
assert_xpath '//p', output, 0
end
# WARNING this assertion fails if the directive is the first line of the paragraph instead of the second
# it happens because we always look a line ahead; not sure what we can do about it
test 'preprocessor directives should not be processed on subsequent lines of a comment paragraph' do
input = <<~'EOS'
[comment]
first line of content
ifdef::asciidoctor[////]
this line should be shown
EOS
output = convert_string_to_embedded input
assert_xpath '//p[text()="this line should be shown"]', output, 1
end
test 'comment style on open block should only skip block' do
input = <<~'EOS'
[comment]
--
skip
this block
--
not this text
EOS
result = convert_string_to_embedded input
assert_xpath '//p', result, 1
assert_xpath '//p[text()="not this text"]', result, 1
end
test 'comment style on paragraph should only skip paragraph' do
input = <<~'EOS'
[comment]
skip
this paragraph
not this text
EOS
result = convert_string_to_embedded input
assert_xpath '//p', result, 1
assert_xpath '//p[text()="not this text"]', result, 1
end
test 'comment style on paragraph should not cause adjacent block to be skipped' do
input = <<~'EOS'
[comment]
skip
this paragraph
[example]
not this text
EOS
result = convert_string_to_embedded input
assert_xpath '/*[@class="exampleblock"]', result, 1
assert_xpath '/*[@class="exampleblock"]//*[normalize-space(text())="not this text"]', result, 1
end
# NOTE this test verifies the nil return value of Parser#next_block
test 'should not drop content that follows skipped content inside a delimited block' do
input = <<~'EOS'
====
paragraph
[comment#idname]
skip
paragraph
====
EOS
result = convert_string_to_embedded input
assert_xpath '/*[@class="exampleblock"]', result, 1
assert_xpath '/*[@class="exampleblock"]//*[@class="paragraph"]', result, 2
assert_xpath '//*[@class="paragraph"][@id="idname"]', result, 0
end
end
context 'Sidebar Blocks' do
test 'should parse sidebar block' do
input = <<~'EOS'
== Section
.Sidebar
****
Content goes here
****
EOS
result = convert_string input
assert_xpath "//*[@class='sidebarblock']//p", result, 1
end
end
context 'Quote and Verse Blocks' do
test 'quote block with no attribution' do
input = <<~'EOS'
____
A famous quote.
____
EOS
output = convert_string input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock > blockquote', output, 1
assert_css '.quoteblock > blockquote > .paragraph > p', output, 1
assert_css '.quoteblock > .attribution', output, 0
assert_xpath '//*[@class="quoteblock"]//p[text()="A famous quote."]', output, 1
end
test 'quote block with attribution' do
input = <<~'EOS'
[quote, Famous Person, Famous Book (1999)]
____
A famous quote.
____
EOS
output = convert_string input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock > blockquote', output, 1
assert_css '.quoteblock > blockquote > .paragraph > p', output, 1
assert_css '.quoteblock > .attribution', output, 1
assert_css '.quoteblock > .attribution > cite', output, 1
assert_css '.quoteblock > .attribution > br + cite', output, 1
assert_xpath '//*[@class="quoteblock"]/*[@class="attribution"]/cite[text()="Famous Book (1999)"]', output, 1
attribution = xmlnodes_at_xpath '//*[@class="quoteblock"]/*[@class="attribution"]', output, 1
author = attribution.children.first
assert_equal "#{decode_char 8212} Famous Person", author.text.strip
end
test 'quote block with attribute and id and role shorthand' do
input = <<~'EOS'
[quote#justice-to-all.solidarity, Martin Luther King, Jr.]
____
Injustice anywhere is a threat to justice everywhere.
____
EOS
output = convert_string_to_embedded input
assert_css '.quoteblock', output, 1
assert_css '#justice-to-all.quoteblock.solidarity', output, 1
assert_css '.quoteblock > .attribution', output, 1
end
test 'setting ID using style shorthand should not reset block style' do
input = <<~'EOS'
[quote]
[#justice-to-all.solidarity, Martin Luther King, Jr.]
____
Injustice anywhere is a threat to justice everywhere.
____
EOS
output = convert_string_to_embedded input
assert_css '.quoteblock', output, 1
assert_css '#justice-to-all.quoteblock.solidarity', output, 1
assert_css '.quoteblock > .attribution', output, 1
end
test 'quote block with complex content' do
input = <<~'EOS'
____
A famous quote.
NOTE: _That_ was inspiring.
____
EOS
output = convert_string input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock > blockquote', output, 1
assert_css '.quoteblock > blockquote > .paragraph', output, 1
assert_css '.quoteblock > blockquote > .paragraph + .admonitionblock', output, 1
end
test 'quote block with attribution converted to DocBook' do
input = <<~'EOS'
[quote, Famous Person, Famous Book (1999)]
____
A famous quote.
____
EOS
output = convert_string input, backend: :docbook
assert_css 'blockquote', output, 1
assert_css 'blockquote > simpara', output, 1
assert_css 'blockquote > attribution', output, 1
assert_css 'blockquote > attribution > citetitle', output, 1
assert_xpath '//blockquote/attribution/citetitle[text()="Famous Book (1999)"]', output, 1
attribution = xmlnodes_at_xpath '//blockquote/attribution', output, 1
author = attribution.children.first
assert_equal 'Famous Person', author.text.strip
end
test 'epigraph quote block with attribution converted to DocBook' do
input = <<~'EOS'
[.epigraph, Famous Person, Famous Book (1999)]
____
A famous quote.
____
EOS
output = convert_string input, backend: :docbook
assert_css 'epigraph', output, 1
assert_css 'epigraph > simpara', output, 1
assert_css 'epigraph > attribution', output, 1
assert_css 'epigraph > attribution > citetitle', output, 1
assert_xpath '//epigraph/attribution/citetitle[text()="Famous Book (1999)"]', output, 1
attribution = xmlnodes_at_xpath '//epigraph/attribution', output, 1
author = attribution.children.first
assert_equal 'Famous Person', author.text.strip
end
test 'markdown-style quote block with single paragraph and no attribution' do
input = <<~'EOS'
> A famous quote.
> Some more inspiring words.
EOS
output = convert_string input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock > blockquote', output, 1
assert_css '.quoteblock > blockquote > .paragraph > p', output, 1
assert_css '.quoteblock > .attribution', output, 0
assert_xpath %(//*[@class="quoteblock"]//p[text()="A famous quote.\nSome more inspiring words."]), output, 1
end
test 'lazy markdown-style quote block with single paragraph and no attribution' do
input = <<~'EOS'
> A famous quote.
Some more inspiring words.
EOS
output = convert_string input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock > blockquote', output, 1
assert_css '.quoteblock > blockquote > .paragraph > p', output, 1
assert_css '.quoteblock > .attribution', output, 0
assert_xpath %(//*[@class="quoteblock"]//p[text()="A famous quote.\nSome more inspiring words."]), output, 1
end
test 'markdown-style quote block with multiple paragraphs and no attribution' do
input = <<~'EOS'
> A famous quote.
>
> Some more inspiring words.
EOS
output = convert_string input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock > blockquote', output, 1
assert_css '.quoteblock > blockquote > .paragraph > p', output, 2
assert_css '.quoteblock > .attribution', output, 0
assert_xpath %((//*[@class="quoteblock"]//p)[1][text()="A famous quote."]), output, 1
assert_xpath %((//*[@class="quoteblock"]//p)[2][text()="Some more inspiring words."]), output, 1
end
test 'markdown-style quote block with multiple blocks and no attribution' do
input = <<~'EOS'
> A famous quote.
>
> NOTE: Some more inspiring words.
EOS
output = convert_string input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock > blockquote', output, 1
assert_css '.quoteblock > blockquote > .paragraph > p', output, 1
assert_css '.quoteblock > blockquote > .admonitionblock', output, 1
assert_css '.quoteblock > .attribution', output, 0
assert_xpath %((//*[@class="quoteblock"]//p)[1][text()="A famous quote."]), output, 1
assert_xpath %((//*[@class="quoteblock"]//*[@class="admonitionblock note"]//*[@class="content"])[1][normalize-space(text())="Some more inspiring words."]), output, 1
end
test 'markdown-style quote block with single paragraph and attribution' do
input = <<~'EOS'
> A famous quote.
> Some more inspiring words.
> -- Famous Person, Famous Source, Volume 1 (1999)
EOS
output = convert_string input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock > blockquote', output, 1
assert_css '.quoteblock > blockquote > .paragraph > p', output, 1
assert_xpath %(//*[@class="quoteblock"]//p[text()="A famous quote.\nSome more inspiring words."]), output, 1
assert_css '.quoteblock > .attribution', output, 1
assert_css '.quoteblock > .attribution > cite', output, 1
assert_css '.quoteblock > .attribution > br + cite', output, 1
assert_xpath '//*[@class="quoteblock"]/*[@class="attribution"]/cite[text()="Famous Source, Volume 1 (1999)"]', output, 1
attribution = xmlnodes_at_xpath '//*[@class="quoteblock"]/*[@class="attribution"]', output, 1
author = attribution.children.first
assert_equal "#{decode_char 8212} Famous Person", author.text.strip
end
test 'markdown-style quote block with only attribution' do
input = '> -- Anonymous'
output = convert_string input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock > blockquote', output, 1
assert_css '.quoteblock > blockquote > *', output, 0
assert_css '.quoteblock > .attribution', output, 1
assert_xpath %(//*[@class="quoteblock"]//*[@class="attribution"][contains(text(),"Anonymous")]), output, 1
end
test 'should parse credit line in markdown-style quote block like positional block attributes' do
input = <<~'EOS'
> I hold it that a little rebellion now and then is a good thing,
> and as necessary in the political world as storms in the physical.
-- Thomas Jefferson, https://jeffersonpapers.princeton.edu/selected-documents/james-madison-1[The Papers of Thomas Jefferson, Volume 11]
EOS
output = convert_string_to_embedded input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock cite a[href="https://jeffersonpapers.princeton.edu/selected-documents/james-madison-1"]', output, 1
end
test 'quoted paragraph-style quote block with attribution' do
input = <<~'EOS'
"A famous quote.
Some more inspiring words."
-- Famous Person, Famous Source, Volume 1 (1999)
EOS
output = convert_string input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock > blockquote', output, 1
assert_xpath %(//*[@class="quoteblock"]/blockquote[normalize-space(text())="A famous quote. Some more inspiring words."]), output, 1
assert_css '.quoteblock > .attribution', output, 1
assert_css '.quoteblock > .attribution > cite', output, 1
assert_css '.quoteblock > .attribution > br + cite', output, 1
assert_xpath '//*[@class="quoteblock"]/*[@class="attribution"]/cite[text()="Famous Source, Volume 1 (1999)"]', output, 1
attribution = xmlnodes_at_xpath '//*[@class="quoteblock"]/*[@class="attribution"]', output, 1
author = attribution.children.first
assert_equal "#{decode_char 8212} Famous Person", author.text.strip
end
test 'should parse credit line in quoted paragraph-style quote block like positional block attributes' do
input = <<~'EOS'
"I hold it that a little rebellion now and then is a good thing,
and as necessary in the political world as storms in the physical."
-- Thomas Jefferson, https://jeffersonpapers.princeton.edu/selected-documents/james-madison-1[The Papers of Thomas Jefferson, Volume 11]
EOS
output = convert_string_to_embedded input
assert_css '.quoteblock', output, 1
assert_css '.quoteblock cite a[href="https://jeffersonpapers.princeton.edu/selected-documents/james-madison-1"]', output, 1
end
test 'single-line verse block without attribution' do
input = <<~'EOS'
[verse]
____
A famous verse.
____
EOS
output = convert_string input
assert_css '.verseblock', output, 1
assert_css '.verseblock > pre', output, 1
assert_css '.verseblock > .attribution', output, 0
assert_css '.verseblock p', output, 0
assert_xpath '//*[@class="verseblock"]/pre[normalize-space(text())="A famous verse."]', output, 1
end
test 'single-line verse block with attribution' do
input = <<~'EOS'
[verse, Famous Poet, Famous Poem]
____
A famous verse.
____
EOS
output = convert_string input
assert_css '.verseblock', output, 1
assert_css '.verseblock p', output, 0
assert_css '.verseblock > pre', output, 1
assert_css '.verseblock > .attribution', output, 1
assert_css '.verseblock > .attribution > cite', output, 1
assert_css '.verseblock > .attribution > br + cite', output, 1
assert_xpath '//*[@class="verseblock"]/*[@class="attribution"]/cite[text()="Famous Poem"]', output, 1
attribution = xmlnodes_at_xpath '//*[@class="verseblock"]/*[@class="attribution"]', output, 1
author = attribution.children.first
assert_equal "#{decode_char 8212} Famous Poet", author.text.strip
end
test 'single-line verse block with attribution converted to DocBook' do
input = <<~'EOS'
[verse, Famous Poet, Famous Poem]
____
A famous verse.
____
EOS
output = convert_string input, backend: :docbook
assert_css 'blockquote', output, 1
assert_css 'blockquote simpara', output, 0
assert_css 'blockquote > literallayout', output, 1
assert_css 'blockquote > attribution', output, 1
assert_css 'blockquote > attribution > citetitle', output, 1
assert_xpath '//blockquote/attribution/citetitle[text()="Famous Poem"]', output, 1
attribution = xmlnodes_at_xpath '//blockquote/attribution', output, 1
author = attribution.children.first
assert_equal 'Famous Poet', author.text.strip
end
test 'single-line epigraph verse block with attribution converted to DocBook' do
input = <<~'EOS'
[verse.epigraph, Famous Poet, Famous Poem]
____
A famous verse.
____
EOS
output = convert_string input, backend: :docbook
assert_css 'epigraph', output, 1
assert_css 'epigraph simpara', output, 0
assert_css 'epigraph > literallayout', output, 1
assert_css 'epigraph > attribution', output, 1
assert_css 'epigraph > attribution > citetitle', output, 1
assert_xpath '//epigraph/attribution/citetitle[text()="Famous Poem"]', output, 1
attribution = xmlnodes_at_xpath '//epigraph/attribution', output, 1
author = attribution.children.first
assert_equal 'Famous Poet', author.text.strip
end
test 'multi-stanza verse block' do
input = <<~'EOS'
[verse]
____
A famous verse.
Stanza two.
____
EOS
output = convert_string input
assert_xpath '//*[@class="verseblock"]', output, 1
assert_xpath '//*[@class="verseblock"]/pre', output, 1
assert_xpath '//*[@class="verseblock"]//p', output, 0
assert_xpath '//*[@class="verseblock"]/pre[contains(text(), "A famous verse.")]', output, 1
assert_xpath '//*[@class="verseblock"]/pre[contains(text(), "Stanza two.")]', output, 1
end
test 'verse block does not contain block elements' do
input = <<~'EOS'
[verse]
____
A famous verse.
....
not a literal
....
____
EOS
output = convert_string input
assert_css '.verseblock', output, 1
assert_css '.verseblock > pre', output, 1
assert_css '.verseblock p', output, 0
assert_css '.verseblock .literalblock', output, 0
end
test 'verse should have normal subs' do
input = <<~'EOS'
[verse]
____
A famous verse
____
EOS
verse = block_from_string input
assert_equal Asciidoctor::Substitutors::NORMAL_SUBS, verse.subs
end
test 'should not recognize callouts in a verse' do
input = <<~'EOS'
[verse]
____
La la la <1>
____
<1> Not pointing to a callout
EOS
output = convert_string_to_embedded input
assert_xpath '//pre[text()="La la la <1>"]', output, 1
assert_message @logger, :WARN, ': line 5: no callout found for <1>', Hash
end
test 'should perform normal subs on a verse block' do
input = <<~'EOS'
[verse]
____
_GET /groups/link:#group-id[\{group-id\}]_
____
EOS
output = convert_string_to_embedded input
assert_includes output, '
'
end
end
context "Example Blocks" do
test "can convert example block" do
input = <<~'EOS'
====
This is an example of an example block.
How crazy is that?
====
EOS
output = convert_string input
assert_xpath '//*[@class="exampleblock"]//p', output, 2
end
test 'assigns sequential numbered caption to example block with title' do
input = <<~'EOS'
.Writing Docs with AsciiDoc
====
Here's how you write AsciiDoc.
You just write.
====
.Writing Docs with DocBook
====
Here's how you write DocBook.
You futz with XML.
====
EOS
doc = document_from_string input
assert_equal 1, doc.blocks[0].numeral
assert_equal 1, doc.blocks[0].number
assert_equal 2, doc.blocks[1].numeral
assert_equal 2, doc.blocks[1].number
output = doc.convert
assert_xpath '(//*[@class="exampleblock"])[1]/*[@class="title"][text()="Example 1. Writing Docs with AsciiDoc"]', output, 1
assert_xpath '(//*[@class="exampleblock"])[2]/*[@class="title"][text()="Example 2. Writing Docs with DocBook"]', output, 1
assert_equal 2, doc.attributes['example-number']
end
test 'assigns sequential character caption to example block with title' do
input = <<~'EOS'
:example-number: @
.Writing Docs with AsciiDoc
====
Here's how you write AsciiDoc.
You just write.
====
.Writing Docs with DocBook
====
Here's how you write DocBook.
You futz with XML.
====
EOS
doc = document_from_string input
assert_equal 'A', doc.blocks[0].numeral
assert_equal 'A', doc.blocks[0].number
assert_equal 'B', doc.blocks[1].numeral
assert_equal 'B', doc.blocks[1].number
output = doc.convert
assert_xpath '(//*[@class="exampleblock"])[1]/*[@class="title"][text()="Example A. Writing Docs with AsciiDoc"]', output, 1
assert_xpath '(//*[@class="exampleblock"])[2]/*[@class="title"][text()="Example B. Writing Docs with DocBook"]', output, 1
assert_equal 'B', doc.attributes['example-number']
end
test "explicit caption is used if provided" do
input = <<~'EOS'
[caption="Look! "]
.Writing Docs with AsciiDoc
====
Here's how you write AsciiDoc.
You just write.
====
EOS
doc = document_from_string input
assert_nil doc.blocks[0].numeral
output = doc.convert
assert_xpath '(//*[@class="exampleblock"])[1]/*[@class="title"][text()="Look! Writing Docs with AsciiDoc"]', output, 1
refute doc.attributes.has_key?('example-number')
end
test 'automatic caption can be turned off and on and modified' do
input = <<~'EOS'
.first example
====
an example
====
:caption:
.second example
====
another example
====
:caption!:
:example-caption: Exhibit
.third example
====
yet another example
====
EOS
output = convert_string_to_embedded input
assert_xpath '/*[@class="exampleblock"]', output, 3
assert_xpath '(/*[@class="exampleblock"])[1]/*[@class="title"][starts-with(text(), "Example ")]', output, 1
assert_xpath '(/*[@class="exampleblock"])[2]/*[@class="title"][text()="second example"]', output, 1
assert_xpath '(/*[@class="exampleblock"])[3]/*[@class="title"][starts-with(text(), "Exhibit ")]', output, 1
end
test 'should create details/summary set if collapsible option is set' do
input = <<~'EOS'
.Toggle Me
[%collapsible]
====
This content is revealed when the user clicks the words "Toggle Me".
====
EOS
output = convert_string_to_embedded input
assert_css 'details', output, 1
assert_css 'details[open]', output, 0
assert_css 'details > summary.title', output, 1
assert_xpath '//details/summary[text()="Toggle Me"]', output, 1
assert_css 'details > summary.title + .content', output, 1
assert_css 'details > summary.title + .content p', output, 1
end
test 'should open details/summary set if collapsible and open options are set' do
input = <<~'EOS'
.Toggle Me
[%collapsible%open]
====
This content is revealed when the user clicks the words "Toggle Me".
====
EOS
output = convert_string_to_embedded input
assert_css 'details', output, 1
assert_css 'details[open]', output, 1
assert_css 'details > summary.title', output, 1
assert_xpath '//details/summary[text()="Toggle Me"]', output, 1
end
test 'should add default summary element if collapsible option is set and title is not specifed' do
input = <<~'EOS'
[%collapsible]
====
This content is revealed when the user clicks the words "Toggle Me".
====
EOS
output = convert_string_to_embedded input
assert_css 'details', output, 1
assert_css 'details > summary.title', output, 1
assert_xpath '//details/summary[text()="Details"]', output, 1
end
test 'should warn if example block is not terminated' do
input = <<~'EOS'
outside
====
inside
still inside
eof
EOS
output = convert_string_to_embedded input
assert_xpath '/*[@class="exampleblock"]', output, 1
assert_message @logger, :WARN, ': line 3: unterminated example block', Hash
end
end
context 'Admonition Blocks' do
test 'caption block-level attribute should be used as caption' do
input = <<~'EOS'
:tip-caption: Pro Tip
[caption="Pro Tip"]
TIP: Override the caption of an admonition block using an attribute entry
EOS
output = convert_string_to_embedded input
assert_xpath '/*[@class="admonitionblock tip"]//*[@class="icon"]/*[@class="title"][text()="Pro Tip"]', output, 1
end
test 'can override caption of admonition block using document attribute' do
input = <<~'EOS'
:tip-caption: Pro Tip
TIP: Override the caption of an admonition block using an attribute entry
EOS
output = convert_string_to_embedded input
assert_xpath '/*[@class="admonitionblock tip"]//*[@class="icon"]/*[@class="title"][text()="Pro Tip"]', output, 1
end
test 'blank caption document attribute should not blank admonition block caption' do
input = <<~'EOS'
:caption:
TIP: Override the caption of an admonition block using an attribute entry
EOS
output = convert_string_to_embedded input
assert_xpath '/*[@class="admonitionblock tip"]//*[@class="icon"]/*[@class="title"][text()="Tip"]', output, 1
end
end
context "Preformatted Blocks" do
test 'should separate adjacent paragraphs and listing into blocks' do
input = <<~'EOS'
paragraph 1
----
listing content
----
paragraph 2
EOS
output = convert_string_to_embedded input
assert_xpath '/*[@class="paragraph"]/p', output, 2
assert_xpath '/*[@class="listingblock"]', output, 1
assert_xpath '(/*[@class="paragraph"]/following-sibling::*)[1][@class="listingblock"]', output, 1
end
test 'should warn if listing block is not terminated' do
input = <<~'EOS'
outside
----
inside
still inside
eof
EOS
output = convert_string_to_embedded input
assert_xpath '/*[@class="listingblock"]', output, 1
assert_message @logger, :WARN, ': line 3: unterminated listing block', Hash
end
test 'should not crash if listing block has no lines' do
input = <<~'EOS'
----
----
EOS
output = convert_string_to_embedded input
assert_css 'pre', output, 1
assert_css 'pre:empty', output, 1
end
test 'should preserve newlines in literal block' do
input = <<~'EOS'
....
line one
line two
line three
....
EOS
[true, false].each do |standalone|
output = convert_string input, standalone: standalone
assert_xpath '//pre', output, 1
assert_xpath '//pre/text()', output, 1
text = xmlnodes_at_xpath('//pre/text()', output, 1).text
lines = text.lines
assert_equal 5, lines.size
expected = "line one\n\nline two\n\nline three".lines
assert_equal expected, lines
blank_lines = output.scan(/\n[ \t]*\n/).size
assert blank_lines >= 2
end
end
test 'should preserve newlines in listing block' do
input = <<~'EOS'
----
line one
line two
line three
----
EOS
[true, false].each do |standalone|
output = convert_string input, standalone: standalone
assert_xpath '//pre', output, 1
assert_xpath '//pre/text()', output, 1
text = xmlnodes_at_xpath('//pre/text()', output, 1).text
lines = text.lines
assert_equal 5, lines.size
expected = "line one\n\nline two\n\nline three".lines
assert_equal expected, lines
blank_lines = output.scan(/\n[ \t]*\n/).size
assert blank_lines >= 2
end
end
test 'should preserve newlines in verse block' do
input = <<~'EOS'
--
[verse]
____
line one
line two
line three
____
--
EOS
[true, false].each do |standalone|
output = convert_string input, standalone: standalone
assert_xpath '//*[@class="verseblock"]/pre', output, 1
assert_xpath '//*[@class="verseblock"]/pre/text()', output, 1
text = xmlnodes_at_xpath('//*[@class="verseblock"]/pre/text()', output, 1).text
lines = text.lines
assert_equal 5, lines.size
expected = "line one\n\nline two\n\nline three".lines
assert_equal expected, lines
blank_lines = output.scan(/\n[ \t]*\n/).size
assert blank_lines >= 2
end
end
test 'should strip leading and trailing blank lines when converting verbatim block' do
# NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
input = <<~EOS
[subs="attributes"]
....
first line
last line
{empty}
....
EOS
doc = document_from_string input, standalone: false
block = doc.blocks.first
assert_equal ['', '', ' first line', '', 'last line', '', '{empty}', ''], block.lines
result = doc.convert
assert_xpath %(//pre[text()=" first line\n\nlast line"]), result, 1
end
test 'should process block with CRLF line endings' do
input = <<~EOS
----\r
source line 1\r
source line 2\r
----\r
EOS
output = convert_string_to_embedded input
assert_xpath '/*[@class="listingblock"]//pre', output, 1
assert_xpath %(/*[@class="listingblock"]//pre[text()="source line 1\nsource line 2"]), output, 1
end
test 'should remove block indent if indent attribute is 0' do
# NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
input = <<~EOS
[indent="0"]
----
def names
@names.split
end
----
EOS
# NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
expected = <<~EOS.chop
def names
@names.split
end
EOS
output = convert_string_to_embedded input
assert_css 'pre', output, 1
assert_css '.listingblock pre', output, 1
result = xmlnodes_at_xpath('//pre', output, 1).text
assert_equal expected, result
end
test 'should not remove block indent if indent attribute is -1' do
# NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
input = <<~EOS
[indent="-1"]
----
def names
@names.split
end
----
EOS
expected = (input.lines.slice 2, 5).join.chop
output = convert_string_to_embedded input
assert_css 'pre', output, 1
assert_css '.listingblock pre', output, 1
result = xmlnodes_at_xpath('//pre', output, 1).text
assert_equal expected, result
end
test 'should set block indent to value specified by indent attribute' do
# NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
input = <<~EOS
[indent="1"]
----
def names
@names.split
end
----
EOS
expected = (input.lines.slice 2, 5).map {|l| l.sub ' ', ' ' }.join.chop
output = convert_string_to_embedded input
assert_css 'pre', output, 1
assert_css '.listingblock pre', output, 1
result = xmlnodes_at_xpath('//pre', output, 1).text
assert_equal expected, result
end
test 'should set block indent to value specified by indent document attribute' do
# NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
input = <<~EOS
:source-indent: 1
[source,ruby]
----
def names
@names.split
end
----
EOS
expected = (input.lines.slice 4, 5).map {|l| l.sub ' ', ' '}.join.chop
output = convert_string_to_embedded input
assert_css 'pre', output, 1
assert_css '.listingblock pre', output, 1
result = xmlnodes_at_xpath('//pre', output, 1).text
assert_equal expected, result
end
test 'should expand tabs if tabsize attribute is positive' do
input = <<~EOS
:tabsize: 4
[indent=0]
----
\tdef names
\t\t@names.split
\tend
----
EOS
# NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
expected = <<~EOS.chop
def names
@names.split
end
EOS
output = convert_string_to_embedded input
assert_css 'pre', output, 1
assert_css '.listingblock pre', output, 1
result = xmlnodes_at_xpath('//pre', output, 1).text
assert_equal expected, result
end
test 'literal block should honor nowrap option' do
input = <<~'EOS'
[options="nowrap"]
----
Do not wrap me if I get too long.
----
EOS
output = convert_string_to_embedded input
assert_css 'pre.nowrap', output, 1
end
test 'literal block should set nowrap class if prewrap document attribute is disabled' do
input = <<~'EOS'
:prewrap!:
----
Do not wrap me if I get too long.
----
EOS
output = convert_string_to_embedded input
assert_css 'pre.nowrap', output, 1
end
test 'literal block should honor explicit subs list' do
input = <<~'EOS'
[subs="verbatim,quotes"]
----
Map *attributes*; //<1>
----
EOS
block = block_from_string input
assert_equal [:specialcharacters,:callouts,:quotes], block.subs
output = block.convert
assert_includes output, 'Map<String, String> attributes;'
assert_xpath '//pre/b[text()="(1)"]', output, 1
end
test 'should be able to disable callouts for literal block' do
input = <<~'EOS'
[subs="specialcharacters"]
----
No callout here <1>
----
EOS
block = block_from_string input
assert_equal [:specialcharacters], block.subs
output = block.convert
assert_xpath '//pre/b[text()="(1)"]', output, 0
end
test 'listing block should honor explicit subs list' do
input = <<~'EOS'
[subs="specialcharacters,quotes"]
----
$ *python functional_tests.py*
Traceback (most recent call last):
File "functional_tests.py", line 4, in
assert 'Django' in browser.title
AssertionError
----
EOS
output = convert_string_to_embedded input
assert_css '.listingblock pre', output, 1
assert_css '.listingblock pre strong', output, 1
assert_css '.listingblock pre em', output, 0
input2 = <<~'EOS'
[subs="specialcharacters,macros"]
----
$ pass:quotes[*python functional_tests.py*]
Traceback (most recent call last):
File "functional_tests.py", line 4, in
assert pass:quotes['Django'] in browser.title
AssertionError
----
EOS
output2 = convert_string_to_embedded input2
# FIXME JRuby is adding extra trailing newlines in the second document,
# for now, rstrip is necessary
assert_equal output.rstrip, output2.rstrip
end
test 'first character of block title may be a period if not followed by space' do
input = <<~'EOS'
..gitignore
----
/.bundle/
/build/
/Gemfile.lock
----
EOS
output = convert_string_to_embedded input
assert_xpath '//*[@class="title"][text()=".gitignore"]', output
end
test 'listing block without title should generate screen element in docbook' do
input = <<~'EOS'
----
listing block
----
EOS
output = convert_string_to_embedded input, backend: 'docbook'
assert_xpath '/screen[text()="listing block"]', output, 1
end
test 'listing block with title should generate screen element inside formalpara element in docbook' do
input = <<~'EOS'
.title
----
listing block
----
EOS
output = convert_string_to_embedded input, backend: 'docbook'
assert_xpath '/formalpara', output, 1
assert_xpath '/formalpara/title[text()="title"]', output, 1
assert_xpath '/formalpara/para/screen[text()="listing block"]', output, 1
end
test 'listing block without an explicit style and with a second positional argument should be promoted to a source block' do
input = <<~'EOS'
[,ruby]
----
puts 'Hello, Ruby!'
----
EOS
matches = (document_from_string input).find_by context: :listing, style: 'source'
assert_equal 1, matches.length
assert_equal 'ruby', (matches[0].attr 'language')
end
test 'listing block without an explicit style should be promoted to a source block if source-language is set' do
input = <<~'EOS'
:source-language: ruby
----
puts 'Hello, Ruby!'
----
EOS
matches = (document_from_string input).find_by context: :listing, style: 'source'
assert_equal 1, matches.length
assert_equal 'ruby', (matches[0].attr 'language')
end
test 'listing block with an explicit style and a second positional argument should not be promoted to a source block' do
input = <<~'EOS'
[listing,ruby]
----
puts 'Hello, Ruby!'
----
EOS
matches = (document_from_string input).find_by context: :listing
assert_equal 1, matches.length
assert_equal 'listing', matches[0].style
assert_nil (matches[0].attr 'language')
end
test 'listing block with an explicit style should not be promoted to a source block if source-language is set' do
input = <<~'EOS'
:source-language: ruby
[listing]
----
puts 'Hello, Ruby!'
----
EOS
matches = (document_from_string input).find_by context: :listing
assert_equal 1, matches.length
assert_equal 'listing', matches[0].style
assert_nil (matches[0].attr 'language')
end
test 'source block with no title or language should generate screen element in docbook' do
input = <<~'EOS'
[source]
----
source block
----
EOS
output = convert_string_to_embedded input, backend: 'docbook'
assert_xpath '/screen[@linenumbering="unnumbered"][text()="source block"]', output, 1
end
test 'source block with title and no language should generate screen element inside formalpara element for docbook' do
input = <<~'EOS'
[source]
.title
----
source block
----
EOS
output = convert_string_to_embedded input, backend: 'docbook'
assert_xpath '/formalpara', output, 1
assert_xpath '/formalpara/title[text()="title"]', output, 1
assert_xpath '/formalpara/para/screen[@linenumbering="unnumbered"][text()="source block"]', output, 1
end
end
context "Open Blocks" do
test "can convert open block" do
input = <<~'EOS'
--
This is an open block.
It can span multiple lines.
--
EOS
output = convert_string input
assert_xpath '//*[@class="openblock"]//p', output, 2
end
test "open block can contain another block" do
input = <<~'EOS'
--
This is an open block.
It can span multiple lines.
____
It can hold great quotes like this one.
____
--
EOS
output = convert_string input
assert_xpath '//*[@class="openblock"]//p', output, 3
assert_xpath '//*[@class="openblock"]//*[@class="quoteblock"]', output, 1
end
test 'should transfer id and reftext on open block to DocBook output' do
input = <<~'EOS'
Check out that <>!
[[open,Open Block]]
--
This is an open block.
TIP: An open block can have other blocks inside of it.
--
Back to our regularly scheduled programming.
EOS
output = convert_string input, backend: :docbook, keep_namespaces: true
assert_css 'article:root > para[xml|id="open"]', output, 1
assert_css 'article:root > para[xreflabel="Open Block"]', output, 1
assert_css 'article:root > simpara', output, 2
assert_css 'article:root > para', output, 1
assert_css 'article:root > para > simpara', output, 1
assert_css 'article:root > para > tip', output, 1
end
test 'should transfer id and reftext on open paragraph to DocBook output' do
input = <<~'EOS'
[open#openpara,reftext="Open Paragraph"]
This is an open paragraph.
EOS
output = convert_string input, backend: :docbook, keep_namespaces: true
assert_css 'article:root > simpara', output, 1
assert_css 'article:root > simpara[xml|id="openpara"]', output, 1
assert_css 'article:root > simpara[xreflabel="Open Paragraph"]', output, 1
end
test 'should transfer title on open block to DocBook output' do
input = <<~'EOS'
.Behold the open
--
This is an open block with a title.
--
EOS
output = convert_string input, backend: :docbook
assert_css 'article > formalpara', output, 1
assert_css 'article > formalpara > *', output, 2
assert_css 'article > formalpara > title', output, 1
assert_xpath '/article/formalpara/title[text()="Behold the open"]', output, 1
assert_css 'article > formalpara > para', output, 1
assert_css 'article > formalpara > para > simpara', output, 1
end
test 'should transfer title on open paragraph to DocBook output' do
input = <<~'EOS'
.Behold the open
This is an open paragraph with a title.
EOS
output = convert_string input, backend: :docbook
assert_css 'article > formalpara', output, 1
assert_css 'article > formalpara > *', output, 2
assert_css 'article > formalpara > title', output, 1
assert_xpath '/article/formalpara/title[text()="Behold the open"]', output, 1
assert_css 'article > formalpara > para', output, 1
assert_css 'article > formalpara > para[text()="This is an open paragraph with a title."]', output, 1
end
test 'should transfer role on open block to DocBook output' do
input = <<~'EOS'
[.container]
--
This is an open block.
It holds stuff.
--
EOS
output = convert_string input, backend: :docbook
assert_css 'article > para[role=container]', output, 1
assert_css 'article > para[role=container] > simpara', output, 1
end
test 'should transfer role on open paragraph to DocBook output' do
input = <<~'EOS'
[.container]
This is an open block.
It holds stuff.
EOS
output = convert_string input, backend: :docbook
assert_css 'article > simpara[role=container]', output, 1
end
end
context 'Passthrough Blocks' do
test 'can parse a passthrough block' do
input = <<~'EOS'
++++
This is a passthrough block.
++++
EOS
block = block_from_string input
refute_nil block
assert_equal 1, block.lines.size
assert_equal 'This is a passthrough block.', block.source
end
test 'does not perform subs on a passthrough block by default' do
input = <<~'EOS'
:type: passthrough
++++
This is a '{type}' block.
http://asciidoc.org
image:tiger.png[]
++++
EOS
expected = %(This is a '{type}' block.\nhttp://asciidoc.org\nimage:tiger.png[])
output = convert_string_to_embedded input
assert_equal expected, output.strip
end
test 'does not perform subs on a passthrough block with pass style by default' do
input = <<~'EOS'
:type: passthrough
[pass]
++++
This is a '{type}' block.
http://asciidoc.org
image:tiger.png[]
++++
EOS
expected = %(This is a '{type}' block.\nhttp://asciidoc.org\nimage:tiger.png[])
output = convert_string_to_embedded input
assert_equal expected, output.strip
end
test 'passthrough block honors explicit subs list' do
input = <<~'EOS'
:type: passthrough
[subs="attributes,quotes,macros"]
++++
This is a _{type}_ block.
http://asciidoc.org
++++
EOS
expected = %(This is a passthrough block.\nhttp://asciidoc.org)
output = convert_string_to_embedded input
assert_equal expected, output.strip
end
test 'should strip leading and trailing blank lines when converting raw block' do
# NOTE cannot use single-quoted heredoc because of https://github.com/jruby/jruby/issues/4260
input = <<~EOS
++++
line above
++++
++++
first line
last line
++++
++++
line below
++++
EOS
doc = document_from_string input, standalone: false
block = doc.blocks[1]
assert_equal ['', '', ' first line', '', 'last line', '', ''], block.lines
result = doc.convert
assert_equal "line above\n first line\n\nlast line\nline below", result, 1
end
end
context 'Math blocks' do
test 'should not crash when converting to HTML if stem block is empty' do
input = <<~'EOS'
[stem]
++++
++++
EOS
output = convert_string_to_embedded input
assert_css '.stemblock', output, 1
end
test 'should add LaTeX math delimiters around latexmath block content' do
input = <<~'EOS'
[latexmath]
++++
\sqrt{3x-1}+(1+x)^2 < y
++++
EOS
output = convert_string_to_embedded input
assert_css '.stemblock', output, 1
nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
assert_equal '\[\sqrt{3x-1}+(1+x)^2 < y\]', nodes.first.to_s.strip
end
test 'should not add LaTeX math delimiters around latexmath block content if already present' do
input = <<~'EOS'
[latexmath]
++++
\[\sqrt{3x-1}+(1+x)^2 < y\]
++++
EOS
output = convert_string_to_embedded input
assert_css '.stemblock', output, 1
nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
assert_equal '\[\sqrt{3x-1}+(1+x)^2 < y\]', nodes.first.to_s.strip
end
test 'should display latexmath block in alt of equation in DocBook backend' do
input = <<~'EOS'
[latexmath]
++++
\sqrt{3x-1}+(1+x)^2 < y
++++
EOS
expect = <<~'EOS'
EOS
output = convert_string_to_embedded input, backend: :docbook
assert_equal expect.strip, output.strip
end
test 'should not split equation in AsciiMath block at single newline' do
input = <<~'EOS'
[asciimath]
++++
f: bbb"N" -> bbb"N"
f: x |-> x + 1
++++
EOS
expected = <<~'EOS'.chop
\$f: bbb"N" -> bbb"N"
f: x |-> x + 1\$
EOS
output = convert_string_to_embedded input
assert_css '.stemblock', output, 1
nodes = xmlnodes_at_xpath '//*[@class="content"]', output
assert_equal expected, nodes.first.inner_html.strip
end
test 'should split equation in AsciiMath block at escaped newline' do
input = <<~'EOS'
[asciimath]
++++
f: bbb"N" -> bbb"N" \
f: x |-> x + 1
++++
EOS
expected = <<~'EOS'.chop
\$f: bbb"N" -> bbb"N"\$
\$f: x |-> x + 1\$
EOS
output = convert_string_to_embedded input
assert_css '.stemblock', output, 1
nodes = xmlnodes_at_xpath '//*[@class="content"]', output
assert_equal expected, nodes.first.inner_html.strip
end
test 'should split equation in AsciiMath block at sequence of escaped newlines' do
input = <<~'EOS'
[asciimath]
++++
f: bbb"N" -> bbb"N" \
\
f: x |-> x + 1
++++
EOS
expected = <<~'EOS'.chop
\$f: bbb"N" -> bbb"N"\$
\$f: x |-> x + 1\$
EOS
output = convert_string_to_embedded input
assert_css '.stemblock', output, 1
nodes = xmlnodes_at_xpath '//*[@class="content"]', output
assert_equal expected, nodes.first.inner_html.strip
end
test 'should split equation in AsciiMath block at newline sequence and preserve breaks' do
input = <<~'EOS'
[asciimath]
++++
f: bbb"N" -> bbb"N"
f: x |-> x + 1
++++
EOS
expected = <<~'EOS'.chop
\$f: bbb"N" -> bbb"N"\$
\$f: x |-> x + 1\$
EOS
output = convert_string_to_embedded input
assert_css '.stemblock', output, 1
nodes = xmlnodes_at_xpath '//*[@class="content"]', output
assert_equal expected, nodes.first.inner_html.strip
end
test 'should add AsciiMath delimiters around asciimath block content' do
input = <<~'EOS'
[asciimath]
++++
sqrt(3x-1)+(1+x)^2 < y
++++
EOS
output = convert_string_to_embedded input
assert_css '.stemblock', output, 1
nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
assert_equal '\$sqrt(3x-1)+(1+x)^2 < y\$', nodes.first.to_s.strip
end
test 'should not add AsciiMath delimiters around asciimath block content if already present' do
input = <<~'EOS'
[asciimath]
++++
\$sqrt(3x-1)+(1+x)^2 < y\$
++++
EOS
output = convert_string_to_embedded input
assert_css '.stemblock', output, 1
nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
assert_equal '\$sqrt(3x-1)+(1+x)^2 < y\$', nodes.first.to_s.strip
end
test 'should convert contents of asciimath block to MathML in DocBook output if asciimath gem is available' do
asciimath_available = !(Asciidoctor::Helpers.require_library 'asciimath', true, :ignore).nil?
input = <<~'EOS'
[asciimath]
++++
x+b/(2a)<+-sqrt((b^2)/(4a^2)-c/a)
++++
[asciimath]
++++
++++
EOS
expect = <<~'EOS'.chop
x+b2a<±b24a2−ca
EOS
using_memory_logger do |logger|
doc = document_from_string input, backend: :docbook, standalone: false
actual = doc.convert
if asciimath_available
assert_equal expect, actual.strip
assert_equal :loaded, doc.converter.instance_variable_get(:@asciimath_status)
else
assert_message logger, :WARN, 'optional gem \'asciimath\' is not available. Functionality disabled.'
assert_equal :unavailable, doc.converter.instance_variable_get(:@asciimath_status)
end
end
end
test 'should output title for latexmath block if defined' do
input = <<~'EOS'
.The Lorenz Equations
[latexmath]
++++
\begin{aligned}
\dot{x} & = \sigma(y-x) \\
\dot{y} & = \rho x - y - xz \\
\dot{z} & = -\beta z + xy
\end{aligned}
++++
EOS
output = convert_string_to_embedded input
assert_css '.stemblock', output, 1
assert_css '.stemblock .title', output, 1
assert_xpath '//*[@class="title"][text()="The Lorenz Equations"]', output, 1
end
test 'should output title for asciimath block if defined' do
input = <<~'EOS'
.Simple fraction
[asciimath]
++++
a//b
++++
EOS
output = convert_string_to_embedded input
assert_css '.stemblock', output, 1
assert_css '.stemblock .title', output, 1
assert_xpath '//*[@class="title"][text()="Simple fraction"]', output, 1
end
test 'should add AsciiMath delimiters around stem block content if stem attribute is asciimath, empty, or not set' do
input = <<~'EOS'
[stem]
++++
sqrt(3x-1)+(1+x)^2 < y
++++
EOS
[
{},
{ 'stem' => '' },
{ 'stem' => 'asciimath' },
{ 'stem' => 'bogus' },
].each do |attributes|
output = convert_string_to_embedded input, attributes: attributes
assert_css '.stemblock', output, 1
nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
assert_equal '\$sqrt(3x-1)+(1+x)^2 < y\$', nodes.first.to_s.strip
end
end
test 'should add LaTeX math delimiters around stem block content if stem attribute is latexmath, latex, or tex' do
input = <<~'EOS'
[stem]
++++
\sqrt{3x-1}+(1+x)^2 < y
++++
EOS
[
{ 'stem' => 'latexmath' },
{ 'stem' => 'latex' },
{ 'stem' => 'tex' },
].each do |attributes|
output = convert_string_to_embedded input, attributes: attributes
assert_css '.stemblock', output, 1
nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
assert_equal '\[\sqrt{3x-1}+(1+x)^2 < y\]', nodes.first.to_s.strip
end
end
test 'should allow stem style to be set using second positional argument of block attributes' do
input = <<~'EOS'
:stem: latexmath
[stem,asciimath]
++++
sqrt(3x-1)+(1+x)^2 < y
++++
EOS
doc = document_from_string input
stemblock = doc.blocks[0]
assert_equal :stem, stemblock.context
assert_equal 'asciimath', stemblock.attributes['style']
output = doc.convert standalone: false
assert_css '.stemblock', output, 1
nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
assert_equal '\$sqrt(3x-1)+(1+x)^2 < y\$', nodes.first.to_s.strip
end
end
context 'Custom Blocks' do
test 'should not warn if block style is unknown' do
input = <<~'EOS'
[foo]
--
bar
--
EOS
convert_string_to_embedded input
assert_empty @logger.messages
end
test 'should log debug message if block style is unknown and debug level is enabled' do
input = <<~'EOS'
[foo]
--
bar
--
EOS
using_memory_logger Logger::Severity::DEBUG do |logger|
convert_string_to_embedded input
assert_message logger, :DEBUG, ': line 2: unknown style for open block: foo', Hash
end
end
end
context 'Metadata' do
test 'block title above section gets carried over to first block in section' do
input = <<~'EOS'
.Title
== Section
paragraph
EOS
output = convert_string input
assert_xpath '//*[@class="paragraph"]', output, 1
assert_xpath '//*[@class="paragraph"]/*[@class="title"][text()="Title"]', output, 1
assert_xpath '//*[@class="paragraph"]/p[text()="paragraph"]', output, 1
end
test 'block title above document title demotes document title to a section title' do
input = <<~'EOS'
.Block title
= Section Title
section paragraph
EOS
output = convert_string input
assert_xpath '//*[@id="header"]/*', output, 0
assert_xpath '//*[@id="preamble"]/*', output, 0
assert_xpath '//*[@id="content"]/h1[text()="Section Title"]', output, 1
assert_xpath '//*[@class="paragraph"]', output, 1
assert_xpath '//*[@class="paragraph"]/*[@class="title"][text()="Block title"]', output, 1
assert_message @logger, :ERROR, ': line 2: level 0 sections can only be used when doctype is book', Hash
end
test 'block title above document title gets carried over to first block in first section if no preamble' do
input = <<~'EOS'
:doctype: book
.Block title
= Document Title
== First Section
paragraph
EOS
doc = document_from_string input
# NOTE block title demotes document title to level-0 section
refute doc.header?
output = doc.convert
assert_xpath '//*[@class="sect1"]//*[@class="paragraph"]/*[@class="title"][text()="Block title"]', output, 1
end
test 'should apply substitutions to a block title in normal order' do
input = <<~'EOS'
.{link-url}[{link-text}]{tm}
The one and only!
EOS
output = convert_string_to_embedded input, attributes: {
'link-url' => 'https://acme.com',
'link-text' => 'ACME',
'tm' => '(TM)',
}
assert_css '.title', output, 1
assert_css '.title a[href="https://acme.com"]', output, 1
assert_xpath %(//*[@class="title"][contains(text(),"#{decode_char 8482}")]), output, 1
end
test 'empty attribute list should not appear in output' do
input = <<~'EOS'
[]
--
Block content
--
EOS
output = convert_string_to_embedded input
assert_includes output, 'Block content'
refute_includes output, '[]'
end
test 'empty block anchor should not appear in output' do
input = <<~'EOS'
[[]]
--
Block content
--
EOS
output = convert_string_to_embedded input
assert_includes output, 'Block content'
refute_includes output, '[[]]'
end
end
context 'Images' do
test 'can convert block image with alt text defined in macro' do
input = 'image::images/tiger.png[Tiger]'
output = convert_string_to_embedded input
assert_xpath '/*[@class="imageblock"]//img[@src="images/tiger.png"][@alt="Tiger"]', output, 1
end
test 'converts SVG image using img element by default' do
input = 'image::tiger.svg[Tiger]'
output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SERVER
assert_xpath '/*[@class="imageblock"]//img[@src="tiger.svg"][@alt="Tiger"]', output, 1
end
test 'converts interactive SVG image with alt text using object element' do
input = <<~'EOS'
:imagesdir: images
[%interactive]
image::tiger.svg[Tiger,100]
EOS
output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SERVER
assert_xpath '/*[@class="imageblock"]//object[@type="image/svg+xml"][@data="images/tiger.svg"][@width="100"]/span[@class="alt"][text()="Tiger"]', output, 1
end
test 'converts SVG image with alt text using img element when safe mode is secure' do
input = <<~'EOS'
[%interactive]
image::images/tiger.svg[Tiger,100]
EOS
output = convert_string_to_embedded input
assert_xpath '/*[@class="imageblock"]//img[@src="images/tiger.svg"][@alt="Tiger"]', output, 1
end
test 'inserts fallback image for SVG inside object element using same dimensions' do
input = <<~'EOS'
:imagesdir: images
[%interactive]
image::tiger.svg[Tiger,100,fallback=tiger.png]
EOS
output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SERVER
assert_xpath '/*[@class="imageblock"]//object[@type="image/svg+xml"][@data="images/tiger.svg"][@width="100"]/img[@src="images/tiger.png"][@width="100"]', output, 1
end
test 'detects SVG image URI that contains a query string' do
input = <<~'EOS'
:imagesdir: images
[%interactive]
image::http://example.org/tiger.svg?foo=bar[Tiger,100]
EOS
output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SERVER
assert_xpath '/*[@class="imageblock"]//object[@type="image/svg+xml"][@data="http://example.org/tiger.svg?foo=bar"][@width="100"]/span[@class="alt"][text()="Tiger"]', output, 1
end
test 'detects SVG image when format attribute is svg' do
input = <<~'EOS'
:imagesdir: images
[%interactive]
image::http://example.org/tiger-svg[Tiger,100,format=svg]
EOS
output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SERVER
assert_xpath '/*[@class="imageblock"]//object[@type="image/svg+xml"][@data="http://example.org/tiger-svg"][@width="100"]/span[@class="alt"][text()="Tiger"]', output, 1
end
test 'converts inline SVG image using svg element' do
input = <<~'EOS'
:imagesdir: fixtures
[%inline]
image::circle.svg[Tiger,100]
EOS
output = convert_string_to_embedded input, safe: Asciidoctor::SafeMode::SERVER, attributes: { 'docdir' => testdir }
assert_match(/