# encoding: UTF-8 unless defined? ASCIIDOCTOR_PROJECT_DIR $: << File.dirname(__FILE__); $:.uniq! require 'test_helper' end context 'Sections' do context 'Ids' do test 'synthetic id is generated by default' do sec = block_from_string('== Section One') assert_equal '_section_one', sec.id end test 'duplicate synthetic id is automatically enumerated' do doc = document_from_string <<-EOS == Section One == Section One EOS assert_equal 2, doc.blocks.size assert_equal '_section_one', doc.blocks[0].id assert_equal '_section_one_2', doc.blocks[1].id end test 'synthetic id removes non-word characters' do sec = block_from_string("== We’re back!") assert_equal '_were_back', sec.id end test 'synthetic id removes repeating separators' do sec = block_from_string('== Section $ One') assert_equal '_section_one', sec.id end test 'synthetic id removes entities' do sec = block_from_string('== Ben & Jerry & Company¹ "Ice Cream Brothers" あ') assert_equal '_ben_jerry_company_ice_cream_brothers', sec.id end test 'synthetic id removes adjacent entities with mixed case' do sec = block_from_string('== a ®&© b') assert_equal '_a_b', sec.id end test 'synthetic id removes XML tags' do sec = block_from_string('== Use the `run` command to make it icon:gear[]') assert_equal '_use_the_run_command_to_make_it_gear', sec.id end test 'synthetic id collapses repeating spaces' do sec = block_from_string('== Go Far') assert_equal '_go_far', sec.id end test 'synthetic id replaces hyphens with separator' do sec = block_from_string('== State-of-the-art design') assert_equal '_state_of_the_art_design', sec.id end test 'synthetic id replaces dots with separator' do sec = block_from_string("== Section 1.1.1") assert_equal '_section_1_1_1', sec.id end test 'synthetic id prefix can be customized' do sec = block_from_string(":idprefix: id_\n\n== Section One") assert_equal 'id_section_one', sec.id end test 'synthetic id prefix can be set to blank' do sec = block_from_string(":idprefix:\n\n== Section One") assert_equal 'section_one', sec.id end test 'synthetic id prefix is stripped from beginning of id if set to blank' do sec = block_from_string(":idprefix:\n\n== & ! More") assert_equal 'more', sec.id end test 'synthetic id separator can be customized' do sec = block_from_string(":idseparator: -\n\n== Section One") assert_equal '_section-one', sec.id end test 'synthetic id separator can be hyphen and hyphens are preserved' do sec = block_from_string(":idseparator: -\n\n== State-of-the-art design") assert_equal '_state-of-the-art-design', sec.id end test 'synthetic id separator can be dot and dots are preserved' do sec = block_from_string(":idseparator: .\n\n== Version 5.0.1") assert_equal '_version.5.0.1', sec.id end test 'synthetic id separator can only be one character' do input = <<-EOS :idseparator: -=- == This Section Is All You Need EOS sec = block_from_string input assert_equal '_this-section-is-all-you-need', sec.id end test 'synthetic id separator can be set to blank' do sec = block_from_string(":idseparator:\n\n== Section One") assert_equal '_sectionone', sec.id end test 'synthetic id separator can be set to blank when idprefix is blank' do sec = block_from_string(":idprefix:\n:idseparator:\n\n== Section One") assert_equal 'sectionone', sec.id end test 'synthetic id separator is removed from beginning of id when idprefix is blank' do sec = block_from_string(":idprefix:\n:idseparator: _\n\n== +Section One") assert_equal 'section_one', sec.id end test 'synthetic ids can be disabled' do sec = block_from_string(":sectids!:\n\n== Section One\n") assert_nil sec.id end test 'explicit id in anchor above section title overrides synthetic id' do sec = block_from_string("[[one]]\n== Section One") assert_equal 'one', sec.id end test 'explicit id in block attributes above section title overrides synthetic id' do sec = block_from_string("[id=one]\n== Section One") assert_equal 'one', sec.id end test 'explicit id set using shorthand in style above section title overrides synthetic id' do sec = block_from_string("[#one]\n== Section One") assert_equal 'one', sec.id end test 'should use explicit id from last block attribute line above section title that defines an explicit id' do input = <<-EOS [#un] [#one] == Section One EOS sec = block_from_string input assert_equal 'one', sec.id end test 'explicit id can be defined using an embedded anchor' do sec = block_from_string("== Section One [[one]] ==") assert_equal 'one', sec.id assert_equal 'Section One', sec.title end test 'explicit id can be defined using an embedded anchor when using setext section titles' do input = <<-EOS Section Title [[refid,reftext]] ------------------------------- EOS sec = block_from_string input assert_equal 'Section Title', sec.title assert_equal 'refid', sec.id assert_equal 'reftext', (sec.attr 'reftext') end test 'explicit id can be defined using an embedded anchor with reftext' do sec = block_from_string("== Section One [[one,Section Uno]] ==") assert_equal 'one', sec.id assert_equal 'Section One', sec.title assert_equal 'Section Uno', (sec.attr 'reftext') end test 'id and reftext in embedded anchor cannot be quoted' do sec = block_from_string(%(== Section One [["one","Section Uno"]] ==)) refute_equal 'one', sec.id assert_equal 'Section One [["one","Section Uno"]]', sec.title assert_nil(sec.attr 'reftext') end test 'reftext in embedded anchor may contain comma' do sec = block_from_string(%(== Section One [[one, Section,Uno]] ==)) assert_equal 'one', sec.id assert_equal 'Section One', sec.title assert_equal 'Section,Uno', (sec.attr 'reftext') end test 'should unescape but not process inline anchor' do sec = block_from_string(%(== Section One \\[[one]] ==)) refute_equal 'one', sec.id assert_equal 'Section One [[one]]', sec.title end test 'should not process inline anchor in section title if section has explicit ID' do sec = block_from_string(%([#sect-one]\n== Section One [[one]])) assert_equal 'sect-one', sec.id assert_equal 'Section One ', sec.title end test 'title substitutions are applied before generating id' do sec = block_from_string("== Section{sp}One\n") assert_equal '_section_one', sec.id end test 'synthetic ids are unique' do input = <<-EOS == Some section text == Some section text EOS doc = document_from_string input assert_equal '_some_section', doc.blocks[0].id assert_equal '_some_section_2', doc.blocks[1].id end # NOTE test cannot be run in parallel with other tests test 'can set start index of synthetic ids' do old_unique_id_start_index = Asciidoctor::Compliance.unique_id_start_index begin input = <<-EOS == Some section text == Some section text EOS Asciidoctor::Compliance.unique_id_start_index = 1 doc = document_from_string input assert_equal '_some_section', doc.blocks[0].id assert_equal '_some_section_1', doc.blocks[1].id ensure Asciidoctor::Compliance.unique_id_start_index = old_unique_id_start_index end end test 'should use specified id and reftext when registering section reference' do input = <<-EOS [[install,Install Procedure]] == Install content EOS doc = document_from_string input reftext = doc.catalog[:ids]['install'] refute_nil reftext assert_equal 'Install Procedure', reftext end test 'should use specified reftext when registering section reference' do input = <<-EOS [reftext="Install Procedure"] == Install content EOS doc = document_from_string input reftext = doc.catalog[:ids]['_install'] refute_nil reftext assert_equal 'Install Procedure', reftext end test 'should substitute attributes when registering reftext for section' do input = <<-EOS :platform-name: Linux [[install,install on {platform-name}]] == Install content EOS doc = document_from_string input reftext = doc.catalog[:ids]['install'] refute_nil reftext assert_equal 'install on Linux', reftext end test 'duplicate section id should not overwrite existing section id entry in references table' do input = <<-EOS [#install] == First Install content [#install] == Second Install content EOS using_memory_logger do |logger| doc = document_from_string input reftext = doc.catalog[:ids]['install'] refute_nil reftext assert_equal 'First Install', reftext assert_message logger, :WARN, ': line 7: id assigned to section already in use: install', Hash end end test 'should warn if explicit section ID matches auto-generated section ID' do input = <<-EOS == Do Not Repeat Yourself content [#_do_not_repeat_yourself] == Do Not Repeat Yourself content EOS using_memory_logger do |logger| doc = document_from_string input reftext = doc.catalog[:ids]['_do_not_repeat_yourself'] refute_nil reftext assert_equal 'Do Not Repeat Yourself', reftext assert_message logger, :WARN, ': line 6: id assigned to section already in use: _do_not_repeat_yourself', Hash assert_equal 2, (doc.convert.scan 'id="_do_not_repeat_yourself"').size end end test 'duplicate block id should not overwrite existing section id entry in references table' do input = <<-EOS [#install] == First Install content [#install] content EOS using_memory_logger do |logger| doc = document_from_string input reftext = doc.catalog[:ids]['install'] refute_nil reftext assert_equal 'First Install', reftext assert_message logger, :WARN, ': line 7: id assigned to block already in use: install', Hash end end end context "document title (level 0)" do test "document title with multiline syntax" do title = "My Title" chars = "=" * title.length assert_xpath "//h1[not(@id)][text() = 'My Title']", convert_string(title + "\n" + chars) assert_xpath "//h1[not(@id)][text() = 'My Title']", convert_string(title + "\n" + chars + "\n") end test "document title with multiline syntax, give a char" do title = "My Title" chars = "=" * (title.length + 1) assert_xpath "//h1[not(@id)][text() = 'My Title']", convert_string(title + "\n" + chars) assert_xpath "//h1[not(@id)][text() = 'My Title']", convert_string(title + "\n" + chars + "\n") end test "document title with multiline syntax, take a char" do title = "My Title" chars = "=" * (title.length - 1) assert_xpath "//h1[not(@id)][text() = 'My Title']", convert_string(title + "\n" + chars) assert_xpath "//h1[not(@id)][text() = 'My Title']", convert_string(title + "\n" + chars + "\n") end test 'document title with multiline syntax and unicode characters' do input = <<-EOS AsciiDoc Writer’s Guide ======================= Author Name preamble EOS result = convert_string input assert_xpath '//h1', result, 1 assert_xpath '//h1[text()="AsciiDoc Writer’s Guide"]', result, 1 end test "not enough chars for a multiline document title" do title = "My Title" chars = "=" * (title.length - 2) using_memory_logger do |logger| output = convert_string(title + "\n" + chars) assert_xpath '//h1', output, 0 refute logger.empty? logger.clear output = convert_string(title + "\n" + chars + "\n") assert_xpath '//h1', output, 0 refute logger.empty? end end test "too many chars for a multiline document title" do title = "My Title" chars = "=" * (title.length + 2) using_memory_logger do |logger| output = convert_string(title + "\n" + chars) assert_xpath '//h1', output, 0 refute logger.empty? logger.clear output = convert_string(title + "\n" + chars + "\n") assert_xpath '//h1', output, 0 refute logger.empty? end end test "document title with multiline syntax cannot begin with a dot" do title = ".My Title" chars = "=" * title.length using_memory_logger do |logger| output = convert_string(title + "\n" + chars) assert_xpath '//h1', output, 0 refute logger.empty? end end test "document title with atx syntax" do assert_xpath "//h1[not(@id)][text() = 'My Title']", convert_string("= My Title") end test "document title with symmetric syntax" do assert_xpath "//h1[not(@id)][text() = 'My Title']", convert_string("= My Title =") end test 'document title created from leveloffset shift defined in document' do assert_xpath "//h1[not(@id)][text() = 'Document Title']", convert_string(%(:leveloffset: -1\n== Document Title)) end test 'document title created from leveloffset shift defined in API' do assert_xpath "//h1[not(@id)][text() = 'Document Title']", convert_string('== Document Title', :attributes => { 'leveloffset' => '-1@' }) end test 'should assign id on document title to body' do input = <<-EOS [[idname]] = Document Title content EOS output = convert_string input assert_css 'body#idname', output, 1 end test 'should assign id defined using shorthand syntax on document title to body' do input = <<-EOS [#idname] = Document Title content EOS output = convert_string input assert_css 'body#idname', output, 1 end test 'should use ID defined in block attributes instead of ID defined inline' do input = <<-EOS [#idname-block] = Document Title [[idname-inline]] content EOS output = convert_string input assert_css 'body#idname-block', output, 1 end test 'block id above document title sets id on document' do input = <<-EOS [[reference]] = Reference Manual :css-signature: refguide preamble EOS doc = document_from_string input assert_equal 'reference', doc.id assert_equal 'refguide', doc.attr('css-signature') output = doc.convert assert_css 'body#reference', output, 1 end test 'should register document in catalog if id is set' do input = <<-EOS [[manual,Manual]] = Reference Manual preamble EOS doc = document_from_string input assert_equal 'manual', doc.id assert_equal 'Manual', doc.attributes['reftext'] assert_equal doc, doc.catalog[:refs]['manual'] end test 'should discard style, role and options shorthand attributes defined on document title' do input = <<-EOS [style#idname.rolename%optionname] = Document Title content EOS doc = document_from_string input assert_empty doc.blocks[0].attributes output = doc.convert assert_css '#idname', output, 1 assert_css 'body#idname', output, 1 assert_css '.rolename', output, 1 assert_css 'body.rolename', output, 1 end end context "level 1" do test "with multiline syntax" do assert_xpath "//h2[@id='_my_section'][text() = 'My Section']", convert_string("My Section\n-----------") end test 'should not recognize underline containing a mix of characters as setext section title' do input = <<-EOS My Section ----^^---- EOS result = convert_string_to_embedded input assert_xpath '//h2[@id="_my_section"][text() = "My Section"]', result, 0 assert_includes result, '----^^----' end test 'should preprocess second line of setext section title' do input = <<-EOS Section Title ifdef::asciidoctor[] ------------- endif::[] EOS result = convert_string_to_embedded input assert_xpath '//h2', result, 1 end test "heading title with multiline syntax cannot begin with a dot" do title = ".My Title" chars = "-" * title.length using_memory_logger do |logger| output = convert_string(title + "\n" + chars) assert_xpath '//h2', output, 0 refute logger.empty? end end test "with atx syntax" do assert_xpath "//h2[@id='_my_title'][text() = 'My Title']", convert_string("== My Title") end test "with atx symmetric syntax" do assert_xpath "//h2[@id='_my_title'][text() = 'My Title']", convert_string("== My Title ==") end test "with atx non-matching symmetric syntax" do assert_xpath "//h2[@id='_my_title'][text() = 'My Title ===']", convert_string("== My Title ===") end test "with XML entity" do assert_xpath "//h2[@id='_whats_new'][text() = \"What#{decode_char 8217}s new?\"]", convert_string("== What's new?") end test "with non-word character" do assert_xpath "//h2[@id='_whats_new'][text() = \"What’s new?\"]", convert_string("== What’s new?") end test "with sequential non-word characters" do assert_xpath "//h2[@id='_what_the_is_this'][text() = 'What the \#@$ is this?']", convert_string('== What the #@$ is this?') end test "with trailing whitespace" do assert_xpath "//h2[@id='_my_title'][text() = 'My Title']", convert_string("== My Title ") end test "with custom blank idprefix" do assert_xpath "//h2[@id='my_title'][text() = 'My Title']", convert_string(":idprefix:\n\n== My Title ") end test "with custom non-blank idprefix" do assert_xpath "//h2[@id='ref_my_title'][text() = 'My Title']", convert_string(":idprefix: ref_\n\n== My Title ") end test 'with multibyte characters' do input = <<-EOS == Asciidoctor in 中文 EOS output = convert_string input if ::RUBY_MIN_VERSION_1_9 assert_xpath '//h2[@id="_asciidoctor_in_中文"][text()="Asciidoctor in 中文"]', output else assert_xpath '//h2[@id="_asciidoctor_in"][text()="Asciidoctor in 中文"]', output end end test 'with only multibyte characters' do input = <<-EOS == 视图 EOS output = convert_string_to_embedded input assert_xpath '//h2[@id="_视图"][text()="视图"]', output end if ::RUBY_MIN_VERSION_1_9 test 'multiline syntax with only multibyte characters' do input = <<-EOS 视图 -- content 连接器 --- content EOS output = convert_string_to_embedded input assert_xpath '//h2[@id="_视图"][text()="视图"]', output assert_xpath '//h2[@id="_连接器"][text()="连接器"]', output end if ::RUBY_MIN_VERSION_1_9 end context "level 2" do test "with multiline syntax" do assert_xpath "//h3[@id='_my_section'][text() = 'My Section']", convert_string(":fragment:\nMy Section\n~~~~~~~~~~~") end test "with atx line syntax" do assert_xpath "//h3[@id='_my_title'][text() = 'My Title']", convert_string(":fragment:\n=== My Title") end end context "level 3" do test "with multiline syntax" do assert_xpath "//h4[@id='_my_section'][text() = 'My Section']", convert_string(":fragment:\nMy Section\n^^^^^^^^^^") end test "with atx line syntax" do assert_xpath "//h4[@id='_my_title'][text() = 'My Title']", convert_string(":fragment:\n==== My Title") end end context "level 4" do test "with multiline syntax" do assert_xpath "//h5[@id='_my_section'][text() = 'My Section']", convert_string(":fragment:\nMy Section\n++++++++++") end test "with atx line syntax" do assert_xpath "//h5[@id='_my_title'][text() = 'My Title']", convert_string(":fragment:\n===== My Title") end end context "level 5" do test "with atx line syntax" do assert_xpath "//h6[@id='_my_title'][text() = 'My Title']", convert_string(":fragment:\n====== My Title") end end context 'Nesting' do test 'should warn if section title is out of sequence' do input = <<-EOS = Document Title == Section A ==== Nested Section content == Section B content EOS using_memory_logger do |logger| result = convert_string_to_embedded input assert_xpath '//h4[text()="Nested Section"]', result, 1 assert_message logger, :WARN, ': line 5: section title out of sequence: expected level 2, got level 3', Hash end end test 'should warn if chapter title is out of sequence' do input = <<-EOS = Document Title :doctype: book === Not a Chapter content EOS using_memory_logger do |logger| result = convert_string_to_embedded input assert_xpath '//h3[text()="Not a Chapter"]', result, 1 assert_message logger, :WARN, ': line 4: section title out of sequence: expected levels 0 or 1, got level 2', Hash end end test 'should not warn if top-level section title is out of sequence when fragment attribute is set on document' do input = <<-EOS = Document Title === First Section content EOS using_memory_logger do |logger| convert_string_to_embedded input, :attributes => { 'fragment' => '' } assert logger.empty? end end test 'should warn if nested section title is out of sequence when fragment attribute is set on document' do input = <<-EOS = Document Title === First Section ===== Nested Section EOS using_memory_logger do |logger| convert_string_to_embedded input, :attributes => { 'fragment' => '' } assert_message logger, :WARN, ': line 5: section title out of sequence: expected level 3, got level 4', Hash end end test 'should log error if subsections are found in special sections in article that do not support subsections' do input = <<-EOS = Document Title == Section === Subsection of Section allowed [appendix] == Appendix === Subsection of Appendix allowed [glossary] == Glossary === Subsection of Glossary not allowed [bibliography] == Bibliography === Subsection of Bibliography not allowed EOS using_memory_logger do |logger| convert_string_to_embedded input assert_messages logger, [ [:ERROR, ': line 19: glossary sections do not support nested sections', Hash], [:ERROR, ': line 26: bibliography sections do not support nested sections', Hash], ] end end test 'should log error if subsections are found in special sections in book that do not support subsections' do input = <<-EOS = Document Title :doctype: book [preface] = Preface === Subsection of Preface allowed [colophon] = Colophon === Subsection of Colophon not allowed [dedication] = Dedication === Subsection of Dedication not allowed = Part 1 [abstract] == Abstract === Subsection of Abstract allowed == Chapter 1 === Subsection of Chapter allowed [appendix] = Appendix === Subsection of Appendix allowed [glossary] = Glossary === Subsection of Glossary not allowed [bibliography] = Bibliography === Subsection of Bibliography not allowed EOS using_memory_logger do |logger| convert_string_to_embedded input assert_messages logger, [ [:ERROR, ': line 14: colophon sections do not support nested sections', Hash], [:ERROR, ': line 21: dedication sections do not support nested sections', Hash], [:ERROR, ': line 50: glossary sections do not support nested sections', Hash], [:ERROR, ': line 57: bibliography sections do not support nested sections', Hash] ] end end end context 'Markdown-style headings' do test 'atx document title with leading marker' do input = <<-EOS # Document Title EOS output = convert_string input assert_xpath "//h1[not(@id)][text() = 'Document Title']", output, 1 end test 'atx document title with symmetric markers' do input = <<-EOS # Document Title # EOS output = convert_string input assert_xpath "//h1[not(@id)][text() = 'Document Title']", output, 1 end test 'atx section title with leading marker' do input = <<-EOS ## Section One blah blah EOS output = convert_string input assert_xpath "//h2[@id='_section_one'][text() = 'Section One']", output, 1 end test 'atx section title with symmetric markers' do input = <<-EOS ## Section One ## blah blah EOS output = convert_string input assert_xpath "//h2[@id='_section_one'][text() = 'Section One']", output, 1 end test 'should not match atx syntax with mixed markers' do input = <<-EOS =#= My Title EOS output = convert_string_to_embedded input assert_xpath "//h3[@id='_my_title'][text() = 'My Title']", output, 0 assert_includes output, '

=#= My Title

' end end context 'Discrete Heading' do test 'should create discrete heading instead of section if style is float' do input = <<-EOS [float] = Independent Heading! not in section EOS output = convert_string_to_embedded input assert_xpath '/h1[@id="_independent_heading"]', output, 1 assert_xpath '/h1[@class="float"]', output, 1 assert_xpath %(/h1[@class="float"][text()="Independent Heading!"]), output, 1 assert_xpath '/h1/following-sibling::*[@class="paragraph"]', output, 1 assert_xpath '/h1/following-sibling::*[@class="paragraph"]/p', output, 1 assert_xpath '/h1/following-sibling::*[@class="paragraph"]/p[text()="not in section"]', output, 1 end test 'should create discrete heading instead of section if style is discrete' do input = <<-EOS [discrete] === Independent Heading! not in section EOS output = convert_string_to_embedded input assert_xpath '/h3', output, 1 assert_xpath '/h3[@id="_independent_heading"]', output, 1 assert_xpath '/h3[@class="discrete"]', output, 1 assert_xpath %(/h3[@class="discrete"][text()="Independent Heading!"]), output, 1 assert_xpath '/h3/following-sibling::*[@class="paragraph"]', output, 1 assert_xpath '/h3/following-sibling::*[@class="paragraph"]/p', output, 1 assert_xpath '/h3/following-sibling::*[@class="paragraph"]/p[text()="not in section"]', output, 1 end test 'should generate id for discrete heading from converted title' do input = <<-EOS [discrete] === {sp}Heading{sp} not in section EOS output = convert_string_to_embedded input assert_xpath '/h3', output, 1 assert_xpath '/h3[@class="discrete"][@id="_heading"]', output, 1 assert_xpath '/h3[@class="discrete"][@id="_heading"][text()=" Heading "]', output, 1 end test 'should create discrete heading if style is float with shorthand role and id' do input = <<-EOS [float.independent#first] = Independent Heading! not in section EOS output = convert_string_to_embedded input assert_xpath '/h1[@id="first"]', output, 1 assert_xpath '/h1[@class="float independent"]', output, 1 assert_xpath %(/h1[@class="float independent"][text()="Independent Heading!"]), output, 1 assert_xpath '/h1/following-sibling::*[@class="paragraph"]', output, 1 assert_xpath '/h1/following-sibling::*[@class="paragraph"]/p', output, 1 assert_xpath '/h1/following-sibling::*[@class="paragraph"]/p[text()="not in section"]', output, 1 end test 'should create discrete heading if style is discrete with shorthand role and id' do input = <<-EOS [discrete.independent#first] = Independent Heading! not in section EOS output = convert_string_to_embedded input assert_xpath '/h1[@id="first"]', output, 1 assert_xpath '/h1[@class="discrete independent"]', output, 1 assert_xpath %(/h1[@class="discrete independent"][text()="Independent Heading!"]), output, 1 assert_xpath '/h1/following-sibling::*[@class="paragraph"]', output, 1 assert_xpath '/h1/following-sibling::*[@class="paragraph"]/p', output, 1 assert_xpath '/h1/following-sibling::*[@class="paragraph"]/p[text()="not in section"]', output, 1 end test 'discrete heading should be a block with context floating_title' do input = <<-EOS [float] === Independent Heading! not in section EOS doc = document_from_string input heading = doc.blocks.first assert_kind_of Asciidoctor::Block, heading assert_equal :floating_title, heading.context assert_equal '_independent_heading', heading.id assert doc.catalog[:ids].has_key?('_independent_heading') end test 'should preprocess second line of setext discrete heading' do input = <<-EOS [discrete] Heading Title ifdef::asciidoctor[] ------------- endif::[] EOS result = convert_string_to_embedded input assert_xpath '//h2', result, 1 end test 'can assign explicit id to discrete heading' do input = <<-EOS [[unchained]] [float] === Independent Heading! not in section EOS doc = document_from_string input heading = doc.blocks.first assert_equal 'unchained', heading.id assert doc.catalog[:ids].has_key?('unchained') end test 'should not include discrete heading in toc' do input = <<-EOS :toc: == Section One [float] === Miss Independent == Section Two EOS output = convert_string input assert_xpath '//*[@id="toc"]', output, 1 assert_xpath %(//*[@id="toc"]//a[contains(text(), "Section ")]), output, 2 assert_xpath %(//*[@id="toc"]//a[text()="Miss Independent"]), output, 0 end test 'should not set id on discrete heading if sectids attribute is unset' do input = <<-EOS [float] === Independent Heading! not in section EOS output = convert_string_to_embedded input, :attributes => {'sectids' => nil} assert_xpath '/h3', output, 1 assert_xpath '/h3[@id="_independent_heading"]', output, 0 assert_xpath '/h3[@class="float"]', output, 1 end test 'should use explicit id for discrete heading if specified' do input = <<-EOS [[free]] [float] == Independent Heading! not in section EOS output = convert_string_to_embedded input assert_xpath '/h2', output, 1 assert_xpath '/h2[@id="free"]', output, 1 assert_xpath '/h2[@class="float"]', output, 1 end test 'should add role to class attribute on discrete heading' do input = <<-EOS [float, role="isolated"] == Independent Heading! not in section EOS output = convert_string_to_embedded input assert_xpath '/h2', output, 1 assert_xpath '/h2[@id="_independent_heading"]', output, 1 assert_xpath '/h2[@class="float isolated"]', output, 1 end test 'should ignore title attribute on discrete heading' do input = <<-EOS [discrete,title="Captured!"] == Independent Heading! not in section EOS doc = document_from_string input heading = doc.blocks[0] assert_equal 'Independent Heading!', heading.title refute heading.attributes.key? 'title' end test 'should use specified id and reftext when registering discrete section reference' do input = <<-EOS [[install,Install Procedure]] [discrete] == Install content EOS doc = document_from_string input reftext = doc.catalog[:ids]['install'] refute_nil reftext assert_equal 'Install Procedure', reftext end test 'should use specified reftext when registering discrete section reference' do input = <<-EOS [reftext="Install Procedure"] [discrete] == Install content EOS doc = document_from_string input reftext = doc.catalog[:ids]['_install'] refute_nil reftext assert_equal 'Install Procedure', reftext end test 'should not process inline anchor in discrete heading if explicit ID is assigned' do input = <<-EOS [discrete#install] == Install [[installation]] content EOS block = block_from_string input assert_equal block.id, 'install' assert_equal 'Install ', block.title end end context 'Level offset' do test 'should print error if standalone document is included without level offset' do input = <<-EOS = Master Document Doc Writer text in master // begin simulated include::[] = Standalone Document :author: Junior Writer text in standalone // end simulated include::[] EOS using_memory_logger do |logger| convert_string input assert_message logger, :ERROR, ': line 7: level 0 sections can only be used when doctype is book', Hash end end test 'should add level offset to section level' do input = <<-EOS = Master Document Doc Writer Master document written by {author}. :leveloffset: 1 // begin simulated include::[] = Standalone Document :author: Junior Writer Standalone document written by {author}. == Section in Standalone Standalone section text. // end simulated include::[] :leveloffset!: == Section in Master Master section text. EOS output = nil using_memory_logger do |logger| output = convert_string input assert logger.empty? end assert_match(/Master document written by Doc Writer/, output) assert_match(/Standalone document written by Junior Writer/, output) assert_xpath '//*[@class="sect1"]/h2[text() = "Standalone Document"]', output, 1 assert_xpath '//*[@class="sect2"]/h3[text() = "Section in Standalone"]', output, 1 assert_xpath '//*[@class="sect1"]/h2[text() = "Section in Master"]', output, 1 end test 'level offset should be added to discrete heading' do input = <<-EOS = Master Document Doc Writer :leveloffset: 1 [float] = Discrete Heading EOS output = convert_string input assert_xpath '//h2[@class="float"][text() = "Discrete Heading"]', output, 1 end test 'should be able to reset level offset' do input = <<-EOS = Master Document Doc Writer Master preamble. :leveloffset: 1 = Standalone Document Standalone preamble. :leveloffset!: == Level 1 Section EOS output = convert_string input assert_xpath '//*[@class = "sect1"]/h2[text() = "Standalone Document"]', output, 1 assert_xpath '//*[@class = "sect1"]/h2[text() = "Level 1 Section"]', output, 1 end test 'should add relative offset value to current leveloffset' do input = <<-EOS = Master Document Doc Writer Master preamble. :leveloffset: 1 = Chapter 1 content :leveloffset: +1 = Standalone Section content EOS output = convert_string input assert_xpath '//*[@class = "sect1"]/h2[text() = "Chapter 1"]', output, 1 assert_xpath '//*[@class = "sect2"]/h3[text() = "Standalone Section"]', output, 1 end end context 'Section Numbering' do test 'should create section number with one entry for level 1' do doc = empty_document sect1 = Asciidoctor::Section.new nil, nil, true doc << sect1 assert_equal '1.', sect1.sectnum end test 'should create section number with two entries for level 2' do doc = empty_document sect1 = Asciidoctor::Section.new nil, nil, true doc << sect1 sect1_1 = Asciidoctor::Section.new sect1, nil, true sect1 << sect1_1 assert_equal '1.1.', sect1_1.sectnum end test 'should create section number with three entries for level 3' do doc = empty_document sect1 = Asciidoctor::Section.new nil, nil, true doc << sect1 sect1_1 = Asciidoctor::Section.new sect1, nil, true sect1 << sect1_1 sect1_1_1 = Asciidoctor::Section.new sect1_1, nil, true sect1_1 << sect1_1_1 assert_equal '1.1.1.', sect1_1_1.sectnum end test 'should create section number for second section in level' do doc = empty_document sect1 = Asciidoctor::Section.new nil, nil, true doc << sect1 sect1_1 = Asciidoctor::Section.new sect1, nil, true sect1 << sect1_1 sect1_2 = Asciidoctor::Section.new sect1, nil, true sect1 << sect1_2 assert_equal '1.2.', sect1_2.sectnum end test 'sectnum should use specified delimiter and append string' do doc = empty_document sect1 = Asciidoctor::Section.new nil, nil, true doc << sect1 sect1_1 = Asciidoctor::Section.new sect1, nil, true sect1 << sect1_1 sect1_1_1 = Asciidoctor::Section.new sect1_1, nil, true sect1_1 << sect1_1_1 assert_equal '1,1,1,', sect1_1_1.sectnum(',') assert_equal '1:1:1', sect1_1_1.sectnum(':', false) end test 'should output section numbers when sectnums attribute is set' do input = <<-EOS = Title :sectnums: == Section_1 text === Section_1_1 text ==== Section_1_1_1 text == Section_2 text === Section_2_1 text === Section_2_2 text EOS output = convert_string input assert_xpath '//h2[@id="_section_1"][starts-with(text(), "1. ")]', output, 1 assert_xpath '//h3[@id="_section_1_1"][starts-with(text(), "1.1. ")]', output, 1 assert_xpath '//h4[@id="_section_1_1_1"][starts-with(text(), "1.1.1. ")]', output, 1 assert_xpath '//h2[@id="_section_2"][starts-with(text(), "2. ")]', output, 1 assert_xpath '//h3[@id="_section_2_1"][starts-with(text(), "2.1. ")]', output, 1 assert_xpath '//h3[@id="_section_2_2"][starts-with(text(), "2.2. ")]', output, 1 end test 'should output section numbers when numbered attribute is set' do input = <<-EOS = Title :numbered: == Section_1 text === Section_1_1 text ==== Section_1_1_1 text == Section_2 text === Section_2_1 text === Section_2_2 text EOS output = convert_string input assert_xpath '//h2[@id="_section_1"][starts-with(text(), "1. ")]', output, 1 assert_xpath '//h3[@id="_section_1_1"][starts-with(text(), "1.1. ")]', output, 1 assert_xpath '//h4[@id="_section_1_1_1"][starts-with(text(), "1.1.1. ")]', output, 1 assert_xpath '//h2[@id="_section_2"][starts-with(text(), "2. ")]', output, 1 assert_xpath '//h3[@id="_section_2_1"][starts-with(text(), "2.1. ")]', output, 1 assert_xpath '//h3[@id="_section_2_2"][starts-with(text(), "2.2. ")]', output, 1 end test 'should not crash if child section of part is out of sequence and part numbering is disabled' do input = <<-EOS = Document Title :doctype: book :sectnums: = Part === Out of Sequence Section EOS using_memory_logger do |logger| output = convert_string input assert_xpath '//h1[text()="Part"]', output, 1 assert_xpath '//h3[text()=".1. Out of Sequence Section"]', output, 1 end end test 'should number parts when doctype is book and partnums attributes is set' do input = <<-EOS = Book Title :doctype: book :sectnums: :partnums: = Language == Syntax content = Processor == CLI content EOS output = convert_string input assert_xpath '//h1[@id="_language"][text() = "I: Language"]', output, 1 assert_xpath '//h1[@id="_processor"][text() = "II: Processor"]', output, 1 end test 'should prepend value of part-signifier attribute to title of numbered part' do input = <<-EOS = Book Title :doctype: book :sectnums: :partnums: :part-signifier: Part = Language == Syntax content = Processor == CLI content EOS output = convert_string input assert_xpath '//h1[@id="_language"][text() = "Part I: Language"]', output, 1 assert_xpath '//h1[@id="_processor"][text() = "Part II: Processor"]', output, 1 end test 'should prepend value of chapter-signifier attribute to title of numbered chapter' do input = <<-EOS = Book Title :doctype: book :sectnums: :partnums: :chapter-signifier: Chapter = Language == Syntax content = Processor == CLI content EOS output = convert_string input assert_xpath '//h2[@id="_syntax"][text() = "Chapter 1. Syntax"]', output, 1 assert_xpath '//h2[@id="_cli"][text() = "Chapter 2. CLI"]', output, 1 end test 'blocks should have level' do input = <<-EOS = Title preamble == Section 1 paragraph === Section 1.1 paragraph EOS doc = document_from_string input assert_equal 0, doc.blocks[0].level assert_equal 1, doc.blocks[1].level assert_equal 1, doc.blocks[1].blocks[0].level assert_equal 2, doc.blocks[1].blocks[1].level assert_equal 2, doc.blocks[1].blocks[1].blocks[0].level end test 'section numbers should not increment when numbered attribute is turned off within document' do input = <<-EOS = Document Title :numbered: :numbered!: == Colophon Section == Another Colophon Section == Final Colophon Section :numbered: == Section One === Section One Subsection == Section Two == Section Three EOS output = convert_string input assert_xpath '//h1[text()="Document Title"]', output, 1 assert_xpath '//h2[@id="_colophon_section"][text()="Colophon Section"]', output, 1 assert_xpath '//h2[@id="_another_colophon_section"][text()="Another Colophon Section"]', output, 1 assert_xpath '//h2[@id="_final_colophon_section"][text()="Final Colophon Section"]', output, 1 assert_xpath '//h2[@id="_section_one"][text()="1. Section One"]', output, 1 assert_xpath '//h3[@id="_section_one_subsection"][text()="1.1. Section One Subsection"]', output, 1 assert_xpath '//h2[@id="_section_two"][text()="2. Section Two"]', output, 1 assert_xpath '//h2[@id="_section_three"][text()="3. Section Three"]', output, 1 end test 'section numbers can be toggled even if numbered attribute is enable via the API' do input = <<-EOS = Document Title :numbered!: == Colophon Section == Another Colophon Section == Final Colophon Section :numbered: == Section One === Section One Subsection == Section Two == Section Three EOS output = convert_string input, :attributes => {'numbered' => ''} assert_xpath '//h1[text()="Document Title"]', output, 1 assert_xpath '//h2[@id="_colophon_section"][text()="Colophon Section"]', output, 1 assert_xpath '//h2[@id="_another_colophon_section"][text()="Another Colophon Section"]', output, 1 assert_xpath '//h2[@id="_final_colophon_section"][text()="Final Colophon Section"]', output, 1 assert_xpath '//h2[@id="_section_one"][text()="1. Section One"]', output, 1 assert_xpath '//h3[@id="_section_one_subsection"][text()="1.1. Section One Subsection"]', output, 1 assert_xpath '//h2[@id="_section_two"][text()="2. Section Two"]', output, 1 assert_xpath '//h2[@id="_section_three"][text()="3. Section Three"]', output, 1 end test 'section numbers cannot be toggled even if numbered attribute is disabled via the API' do input = <<-EOS = Document Title :numbered!: == Colophon Section == Another Colophon Section == Final Colophon Section :numbered: == Section One === Section One Subsection == Section Two == Section Three EOS output = convert_string input, :attributes => {'numbered!' => ''} assert_xpath '//h1[text()="Document Title"]', output, 1 assert_xpath '//h2[@id="_colophon_section"][text()="Colophon Section"]', output, 1 assert_xpath '//h2[@id="_another_colophon_section"][text()="Another Colophon Section"]', output, 1 assert_xpath '//h2[@id="_final_colophon_section"][text()="Final Colophon Section"]', output, 1 assert_xpath '//h2[@id="_section_one"][text()="Section One"]', output, 1 assert_xpath '//h3[@id="_section_one_subsection"][text()="Section One Subsection"]', output, 1 assert_xpath '//h2[@id="_section_two"][text()="Section Two"]', output, 1 assert_xpath '//h2[@id="_section_three"][text()="Section Three"]', output, 1 end # NOTE AsciiDoc fails this test because it does not properly check for a None value when looking up the numbered attribute test 'section numbers should not increment until numbered attribute is turned back on' do input = <<-EOS = Document Title :numbered!: == Colophon Section == Another Colophon Section == Final Colophon Section :numbered: == Section One === Section One Subsection == Section Two == Section Three EOS output = convert_string input assert_xpath '//h1[text()="Document Title"]', output, 1 assert_xpath '//h2[@id="_colophon_section"][text()="Colophon Section"]', output, 1 assert_xpath '//h2[@id="_another_colophon_section"][text()="Another Colophon Section"]', output, 1 assert_xpath '//h2[@id="_final_colophon_section"][text()="Final Colophon Section"]', output, 1 assert_xpath '//h2[@id="_section_one"][text()="1. Section One"]', output, 1 assert_xpath '//h3[@id="_section_one_subsection"][text()="1.1. Section One Subsection"]', output, 1 assert_xpath '//h2[@id="_section_two"][text()="2. Section Two"]', output, 1 assert_xpath '//h2[@id="_section_three"][text()="3. Section Three"]', output, 1 end test 'table with asciidoc content should not disable numbering of subsequent sections' do input = <<-EOS = Document Title :numbered: preamble == Section One |=== a|content |=== == Section Two content EOS output = convert_string input assert_xpath '//h2[@id="_section_one"]', output, 1 assert_xpath '//h2[@id="_section_one"][text()="1. Section One"]', output, 1 assert_xpath '//h2[@id="_section_two"]', output, 1 assert_xpath '//h2[@id="_section_two"][text()="2. Section Two"]', output, 1 end test 'should not number parts when doctype is book' do input = <<-EOS = Document Title :doctype: book :numbered: = Part 1 == Chapter 1 content = Part 2 == Chapter 2 content EOS output = convert_string input assert_xpath '(//h1)[1][text()="Document Title"]', output, 1 assert_xpath '(//h1)[2][text()="Part 1"]', output, 1 assert_xpath '(//h1)[3][text()="Part 2"]', output, 1 assert_xpath '(//h2)[1][text()="1. Chapter 1"]', output, 1 assert_xpath '(//h2)[2][text()="2. Chapter 2"]', output, 1 end test 'should number chapters sequentially even when divided into parts' do input = <<-EOS = Document Title :doctype: book :numbered: == Chapter 1 content = Part 1 == Chapter 2 content = Part 2 == Chapter 3 content == Chapter 4 content EOS result = convert_string input (1..4).each do |num| assert_xpath %(//h2[@id="_chapter_#{num}"]), result, 1 assert_xpath %(//h2[@id="_chapter_#{num}"][text()="#{num}. Chapter #{num}"]), result, 1 end end test 'reindex_sections should correct section enumeration after sections are modified' do input = <<-EOS :sectnums: == First Section content == Last Section content EOS doc = document_from_string input second_section = Asciidoctor::Section.new doc, nil, true doc.blocks.insert 1, second_section doc.reindex_sections sections = doc.sections [0, 1, 2].each do |index| assert_equal index, sections[index].index assert_equal index + 1, sections[index].numeral assert_equal index + 1, sections[index].number end end test 'should allow sections to be renumbered using deprecated number property' do input = <<-EOS == Somewhere in the Middle == The End EOS doc = document_from_string input, :attributes => { 'sectnums' => '' } doc.sections.each do |sect| sect.number += 1 end output = doc.convert :header_footer => false assert_xpath '//h2[text()="2. Somewhere in the Middle"]', output, 1 assert_xpath '//h2[text()="3. The End"]', output, 1 end end context 'Links and anchors' do test 'should include anchor if sectanchors document attribute is set' do input = <<-EOS == Installation Installation section. === Linux Linux installation instructions. EOS output = convert_string_to_embedded input, :attributes => {'sectanchors' => ''} assert_xpath '/*[@class="sect1"]/h2[@id="_installation"]/a', output, 1 assert_xpath '/*[@class="sect1"]/h2[@id="_installation"]/a[@class="anchor"][@href="#_installation"]', output, 1 assert_xpath '/*[@class="sect1"]/h2[@id="_installation"]/a/following-sibling::text()="Installation"', output, true assert_xpath '//*[@class="sect2"]/h3[@id="_linux"]/a', output, 1 assert_xpath '//*[@class="sect2"]/h3[@id="_linux"]/a[@class="anchor"][@href="#_linux"]', output, 1 assert_xpath '//*[@class="sect2"]/h3[@id="_linux"]/a/following-sibling::text()="Linux"', output, true end test 'should position after title text if sectanchors is set to after' do input = <<-EOS == Installation Installation section. === Linux Linux installation instructions. EOS output = convert_string_to_embedded input, :attributes => {'sectanchors' => 'after'} assert_xpath '/*[@class="sect1"]/h2[@id="_installation"]/a', output, 1 assert_xpath '/*[@class="sect1"]/h2[@id="_installation"]/a[@class="anchor"][@href="#_installation"]', output, 1 assert_xpath '/*[@class="sect1"]/h2[@id="_installation"]/a/preceding-sibling::text()="Installation"', output, true assert_xpath '//*[@class="sect2"]/h3[@id="_linux"]/a', output, 1 assert_xpath '//*[@class="sect2"]/h3[@id="_linux"]/a[@class="anchor"][@href="#_linux"]', output, 1 assert_xpath '//*[@class="sect2"]/h3[@id="_linux"]/a/preceding-sibling::text()="Linux"', output, true end test 'should link section if sectlinks document attribute is set' do input = <<-EOS == Installation Installation section. === Linux Linux installation instructions. EOS output = convert_string_to_embedded input, :attributes => {'sectlinks' => ''} assert_xpath '/*[@class="sect1"]/h2[@id="_installation"]/a', output, 1 assert_xpath '/*[@class="sect1"]/h2[@id="_installation"]/a[@class="link"][@href="#_installation"]', output, 1 assert_xpath '/*[@class="sect1"]/h2[@id="_installation"]/a[text()="Installation"]', output, 1 assert_xpath '//*[@class="sect2"]/h3[@id="_linux"]/a', output, 1 assert_xpath '//*[@class="sect2"]/h3[@id="_linux"]/a[@class="link"][@href="#_linux"]', output, 1 assert_xpath '//*[@class="sect2"]/h3[@id="_linux"]/a[text()="Linux"]', output, 1 end end context 'Special sections' do test 'should assign sectname, caption, and numeral to appendix section by default' do input = <<-EOS [appendix] == Attribute Options Details EOS appendix = block_from_string input assert_equal 'appendix', appendix.sectname assert_equal 'Appendix A: ', appendix.caption assert_equal 'A', appendix.numeral assert_equal 'A', appendix.number assert_equal true, appendix.numbered end test 'should prefix appendix title by numbered label even when section numbering is disabled' do input = <<-EOS [appendix] == Attribute Options Details EOS output = convert_string_to_embedded input assert_xpath '//h2[text()="Appendix A: Attribute Options"]', output, 1 end test 'should use style from last block attribute line above section that defines a style' do input = <<-EOS [glossary] [appendix] == Attribute Options Details EOS output = convert_string_to_embedded input assert_xpath '//h2[text()="Appendix A: Attribute Options"]', output, 1 end test 'setting ID using style shorthand should not clear section style' do input = <<-EOS [appendix] [#attribute-options] == Attribute Options Details EOS output = convert_string_to_embedded input assert_xpath '//h2[@id="attribute-options"][text()="Appendix A: Attribute Options"]', output, 1 end test 'should use custom appendix caption if specified' do input = <<-EOS :appendix-caption: App [appendix] == Attribute Options Details EOS output = convert_string_to_embedded input assert_xpath '//h2[text()="App A: Attribute Options"]', output, 1 end test 'should only assign letter to appendix when numbered is enabled and appendix caption is not set' do input = <<-EOS :numbered: :!appendix-caption: [appendix] == Attribute Options Details EOS output = convert_string_to_embedded input assert_xpath '//h2[text()="A. Attribute Options"]', output, 1 end test 'should increment appendix number for each appendix section' do input = <<-EOS [appendix] == Attribute Options Details [appendix] == Migration Details EOS output = convert_string_to_embedded input assert_xpath '(//h2)[1][text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '(//h2)[2][text()="Appendix B: Migration"]', output, 1 end test 'should continue numbering after appendix' do input = <<-EOS :numbered: == First Section content [appendix] == Attribute Options content == Migration content EOS output = convert_string_to_embedded input assert_xpath '(//h2)[1][text()="1. First Section"]', output, 1 assert_xpath '(//h2)[2][text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '(//h2)[3][text()="2. Migration"]', output, 1 end test 'should number appendix subsections using appendix letter' do input = <<-EOS :numbered: [appendix] == Attribute Options Details === Optional Attributes Details EOS output = convert_string_to_embedded input assert_xpath '(//h2)[1][text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '(//h3)[1][text()="A.1. Optional Attributes"]', output, 1 end test 'should not number level 4 section by default' do input = <<-EOS :numbered: == Level_1 === Level_2 ==== Level_3 ===== Level_4 text EOS output = convert_string_to_embedded input assert_xpath '//h5', output, 1 assert_xpath '//h5[text()="Level_4"]', output, 1 end test 'should only number levels up to value defined by sectnumlevels attribute' do input = <<-EOS :numbered: :sectnumlevels: 2 == Level_1 === Level_2 ==== Level_3 ===== Level_4 text EOS output = convert_string_to_embedded input assert_xpath '//h2', output, 1 assert_xpath '//h2[text()="1. Level_1"]', output, 1 assert_xpath '//h3', output, 1 assert_xpath '//h3[text()="1.1. Level_2"]', output, 1 assert_xpath '//h4', output, 1 assert_xpath '//h4[text()="Level_3"]', output, 1 assert_xpath '//h5', output, 1 assert_xpath '//h5[text()="Level_4"]', output, 1 end test 'should not number sections or subsections in regions where numbered is off' do input = <<-EOS :numbered: == Section One :numbered!: [appendix] == Attribute Options Details [appendix] == Migration Details === Gotchas Details [glossary] == Glossary Terms EOS output = convert_string_to_embedded input assert_xpath '(//h2)[1][text()="1. Section One"]', output, 1 assert_xpath '(//h2)[2][text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '(//h2)[3][text()="Appendix B: Migration"]', output, 1 assert_xpath '(//h3)[1][text()="Gotchas"]', output, 1 assert_xpath '(//h2)[4][text()="Glossary"]', output, 1 end test 'should not number sections or subsections in toc in regions where numbered is off' do input = <<-EOS :numbered: :toc: == Section One :numbered!: [appendix] == Attribute Options Details [appendix] == Migration Details === Gotchas Details [glossary] == Glossary Terms EOS output = convert_string input assert_xpath '//*[@id="toc"]/ul//li/a[text()="1. Section One"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Appendix B: Migration"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Gotchas"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Glossary"]', output, 1 end test 'should only number sections in toc up to value defined by sectnumlevels attribute' do input = <<-EOS :numbered: :toc: :sectnumlevels: 2 :toclevels: 3 == Level 1 === Level 2 ==== Level 3 EOS output = convert_string input assert_xpath '//*[@id="toc"]//a[@href="#_level_1"][text()="1. Level 1"]', output, 1 assert_xpath '//*[@id="toc"]//a[@href="#_level_2"][text()="1.1. Level 2"]', output, 1 assert_xpath '//*[@id="toc"]//a[@href="#_level_3"][text()="Level 3"]', output, 1 end test 'should not number special sections or their subsections by default except for appendices' do input = <<-EOS :doctype: book :sectnums: [preface] == Preface === Preface Subsection content == Section One content [appendix] == Attribute Options Details [appendix] == Migration Details === Gotchas Details [glossary] == Glossary Terms EOS output = convert_string_to_embedded input assert_xpath '(//h2)[1][text()="Preface"]', output, 1 assert_xpath '(//h3)[1][text()="Preface Subsection"]', output, 1 assert_xpath '(//h2)[2][text()="1. Section One"]', output, 1 assert_xpath '(//h2)[3][text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '(//h2)[4][text()="Appendix B: Migration"]', output, 1 assert_xpath '(//h3)[2][text()="B.1. Gotchas"]', output, 1 assert_xpath '(//h2)[5][text()="Glossary"]', output, 1 end test 'should not number special sections or their subsections in toc by default except for appendices' do input = <<-EOS :doctype: book :sectnums: :toc: [preface] == Preface === Preface Subsection content == Section One content [appendix] == Attribute Options Details [appendix] == Migration Details === Gotchas Details [glossary] == Glossary Terms EOS output = convert_string input assert_xpath '//*[@id="toc"]/ul//li/a[text()="Preface"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Preface Subsection"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="1. Section One"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Appendix B: Migration"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="B.1. Gotchas"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Glossary"]', output, 1 end test 'should number special sections and their subsections when sectnums is all' do input = <<-EOS :doctype: book :sectnums: all [preface] == Preface === Preface Subsection content == Section One content [appendix] == Attribute Options Details [appendix] == Migration Details === Gotchas Details [glossary] == Glossary Terms EOS output = convert_string_to_embedded input assert_xpath '(//h2)[1][text()="1. Preface"]', output, 1 assert_xpath '(//h3)[1][text()="1.1. Preface Subsection"]', output, 1 assert_xpath '(//h2)[2][text()="2. Section One"]', output, 1 assert_xpath '(//h2)[3][text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '(//h2)[4][text()="Appendix B: Migration"]', output, 1 assert_xpath '(//h3)[2][text()="B.1. Gotchas"]', output, 1 assert_xpath '(//h2)[5][text()="3. Glossary"]', output, 1 end test 'should number special sections and their subsections in toc when sectnums is all' do input = <<-EOS :doctype: book :sectnums: all :toc: [preface] == Preface === Preface Subsection content == Section One content [appendix] == Attribute Options Details [appendix] == Migration Details === Gotchas Details [glossary] == Glossary Terms EOS output = convert_string input assert_xpath '//*[@id="toc"]/ul//li/a[text()="1. Preface"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="1.1. Preface Subsection"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="2. Section One"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Appendix B: Migration"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="B.1. Gotchas"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="3. Glossary"]', output, 1 end test 'level 0 special sections in multipart book should be coerced to level 1' do input = <<-EOS = Multipart Book Doc Writer :doctype: book [preface] = Preface Preface text [appendix] = Appendix Appendix text EOS output = convert_string input assert_xpath '//h2[@id = "_preface"]', output, 1 assert_xpath '//h2[@id = "_appendix"]', output, 1 end test 'should output docbook elements that correspond to special sections in book doctype' do input = <<-EOS = Multipart Book :doctype: book :idprefix: [abstract] = Abstract Title Normal chapter (no abstract in book) [dedication] = Dedication Title Dedication content [preface] = Preface Title Preface content === Preface sub-section Preface subsection content = Part 1 [partintro] .Part intro title Part intro content == Chapter 1 blah blah == Chapter 2 blah blah = Part 2 [partintro] blah blah == Chapter 3 blah blah == Chapter 4 blah blah [appendix] = Appendix Title Appendix content === Appendix sub-section Appendix sub-section content [bibliography] = Bibliography Title Bibliography content [glossary] = Glossary Title Glossary content [colophon] = Colophon Title Colophon content [index] = Index Title EOS output = convert_string_to_embedded input, :backend => 'docbook45' assert_xpath '/chapter[@id="abstract_title"]', output, 1 assert_xpath '/chapter[@id="abstract_title"]/title[text()="Abstract Title"]', output, 1 assert_xpath '/chapter/following-sibling::dedication[@id="dedication_title"]', output, 1 assert_xpath '/chapter/following-sibling::dedication[@id="dedication_title"]/title[text()="Dedication Title"]', output, 1 assert_xpath '/dedication/following-sibling::preface[@id="preface_title"]', output, 1 assert_xpath '/dedication/following-sibling::preface[@id="preface_title"]/title[text()="Preface Title"]', output, 1 assert_xpath '/preface/section[@id="preface_sub_section"]', output, 1 assert_xpath '/preface/section[@id="preface_sub_section"]/title[text()="Preface sub-section"]', output, 1 assert_xpath '/preface/following-sibling::part[@id="part_1"]', output, 1 assert_xpath '/preface/following-sibling::part[@id="part_1"]/title[text()="Part 1"]', output, 1 assert_xpath '/part[@id="part_1"]/partintro', output, 1 assert_xpath '/part[@id="part_1"]/partintro/title[text()="Part intro title"]', output, 1 assert_xpath '/part[@id="part_1"]/partintro/following-sibling::chapter[@id="chapter_1"]', output, 1 assert_xpath '/part[@id="part_1"]/partintro/following-sibling::chapter[@id="chapter_1"]/title[text()="Chapter 1"]', output, 1 assert_xpath '(/part)[2]/following-sibling::appendix[@id="appendix_title"]', output, 1 assert_xpath '(/part)[2]/following-sibling::appendix[@id="appendix_title"]/title[text()="Appendix Title"]', output, 1 assert_xpath '/appendix/section[@id="appendix_sub_section"]', output, 1 assert_xpath '/appendix/section[@id="appendix_sub_section"]/title[text()="Appendix sub-section"]', output, 1 assert_xpath '/appendix/following-sibling::bibliography[@id="bibliography_title"]', output, 1 assert_xpath '/appendix/following-sibling::bibliography[@id="bibliography_title"]/title[text()="Bibliography Title"]', output, 1 assert_xpath '/bibliography/following-sibling::glossary[@id="glossary_title"]', output, 1 assert_xpath '/bibliography/following-sibling::glossary[@id="glossary_title"]/title[text()="Glossary Title"]', output, 1 assert_xpath '/glossary/following-sibling::colophon[@id="colophon_title"]', output, 1 assert_xpath '/glossary/following-sibling::colophon[@id="colophon_title"]/title[text()="Colophon Title"]', output, 1 assert_xpath '/colophon/following-sibling::index[@id="index_title"]', output, 1 assert_xpath '/colophon/following-sibling::index[@id="index_title"]/title[text()="Index Title"]', output, 1 end test 'abstract section maps to abstract element in docbook for article doctype' do input = <<-EOS = Article :idprefix: [abstract] == Abstract Title Abstract content EOS output = convert_string_to_embedded input, :backend => 'docbook45' assert_xpath '/abstract[@id="abstract_title"]', output, 1 assert_xpath '/abstract[@id="abstract_title"]/title[text()="Abstract Title"]', output, 1 end test 'should allow a special section to be nested at arbitrary depth in DocBook output' do input = <<-EOS = Document Title :doctype: book == Glossaries [glossary] === Glossary A Glossaries are optional. Glossaries entries are an example of a style of AsciiDoc description lists. [glossary] A glossary term:: The corresponding definition. A second glossary term:: The corresponding definition. EOS output = convert_string input, :backend => :docbook assert_xpath '//glossary', output, 1 assert_xpath '//chapter/glossary', output, 1 assert_xpath '//glossary/title[text()="Glossary A"]', output, 1 assert_xpath '//glossary/glossentry', output, 2 end test 'should drop title on special section in DocBook output if untitled option is set' do input = <<-EOS [dedication%untitled] == Dedication content EOS output = convert_string_to_embedded input, :backend => :docbook assert_xpath '/dedication', output, 1 assert_xpath '/dedication/title', output, 0 end end context "heading patterns in blocks" do test "should not interpret a listing block as a heading" do input = <<-EOS Section ------- ---- code ---- fin. EOS output = convert_string input assert_xpath "//h2", output, 1 end test "should not interpret an open block as a heading" do input = <<-EOS Section ------- -- ha -- fin. EOS output = convert_string input assert_xpath "//h2", output, 1 end test "should not interpret an attribute list as a heading" do input = <<-EOS Section ======= preamble [TIP] ==== This should be a tip, not a heading. ==== EOS output = convert_string input assert_xpath "//*[@class='admonitionblock tip']//p[text() = 'This should be a tip, not a heading.']", output, 1 end test "should not match a heading in a description list" do input = <<-EOS Section ------- term1:: + ---- list = [1, 2, 3]; ---- term2:: == not a heading term3:: def // fin. EOS output = convert_string input assert_xpath "//h2", output, 1 assert_xpath "//dl", output, 1 end test "should not match a heading in a bulleted list" do input = <<-EOS Section ------- * first + ---- list = [1, 2, 3]; ---- + * second == not a heading * third fin. EOS output = convert_string input assert_xpath "//h2", output, 1 assert_xpath "//ul", output, 1 end test "should not match a heading in a block" do input = <<-EOS ==== == not a heading ==== EOS output = convert_string input assert_xpath "//h2", output, 0 assert_xpath "//*[@class='exampleblock']//p[text() = '== not a heading']", output, 1 end end context 'Table of Contents' do test 'should output unnumbered table of contents in header if toc attribute is set' do input = <<-EOS = Article :toc: == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... === Interlude While they were waiting... == Section Three That's all she wrote! EOS output = convert_string input assert_xpath '//*[@id="header"]//*[@id="toc"][@class="toc"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/*[@id="toctitle"][text()="Table of Contents"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul[@class="sectlevel1"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]//ul', output, 2 assert_xpath '//*[@id="header"]//*[@id="toc"]//li', output, 4 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li[1]/a[@href="#_section_one"][text()="Section One"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li/ul', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li/ul[@class="sectlevel2"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li/ul/li', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li/ul/li/a[@href="#_interlude"][text()="Interlude"]', output, 1 assert_xpath '((//*[@id="header"]//*[@id="toc"]/ul)[1]/li)[3]/a[@href="#_section_three"][text()="Section Three"]', output, 1 end test 'should output numbered table of contents in header if toc and numbered attributes are set' do input = <<-EOS = Article :toc: :numbered: == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... === Interlude While they were waiting... == Section Three That's all she wrote! EOS output = convert_string input assert_xpath '//*[@id="header"]//*[@id="toc"][@class="toc"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/*[@id="toctitle"][text()="Table of Contents"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]//ul', output, 2 assert_xpath '//*[@id="header"]//*[@id="toc"]//li', output, 4 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li[1]/a[@href="#_section_one"][text()="1. Section One"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li/ul/li', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li/ul/li/a[@href="#_interlude"][text()="2.1. Interlude"]', output, 1 assert_xpath '((//*[@id="header"]//*[@id="toc"]/ul)[1]/li)[3]/a[@href="#_section_three"][text()="3. Section Three"]', output, 1 end test 'should output a table of contents that honors numbered setting at position of section in document' do input = <<-EOS = Article :toc: :numbered: == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... === Interlude While they were waiting... :numbered!: == Section Three That's all she wrote! EOS output = convert_string input assert_xpath '//*[@id="header"]//*[@id="toc"][@class="toc"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/*[@id="toctitle"][text()="Table of Contents"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]//ul', output, 2 assert_xpath '//*[@id="header"]//*[@id="toc"]//li', output, 4 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li[1]/a[@href="#_section_one"][text()="1. Section One"]', output, 1 assert_xpath '((//*[@id="header"]//*[@id="toc"]/ul)[1]/li)[3]/a[@href="#_section_three"][text()="Section Three"]', output, 1 end test 'should not number parts in table of contents for book doctype when numbered attribute is set' do input = <<-EOS = Book :doctype: book :toc: :numbered: = Part 1 == First Section of Part 1 blah == Second Section of Part 1 blah = Part 2 == First Section of Part 2 blah EOS output = convert_string input assert_xpath '//*[@id="toc"]', output, 1 assert_xpath '//*[@id="toc"]/ul', output, 1 assert_xpath '//*[@id="toc"]/ul[@class="sectlevel0"]', output, 1 assert_xpath '//*[@id="toc"]/ul[@class="sectlevel0"]/li', output, 2 assert_xpath '(//*[@id="toc"]/ul[@class="sectlevel0"]/li)[1]/a[text()="Part 1"]', output, 1 assert_xpath '(//*[@id="toc"]/ul[@class="sectlevel0"]/li)[2]/a[text()="Part 2"]', output, 1 assert_xpath '(//*[@id="toc"]/ul[@class="sectlevel0"]/li)[1]/ul', output, 1 assert_xpath '(//*[@id="toc"]/ul[@class="sectlevel0"]/li)[1]/ul[@class="sectlevel1"]', output, 1 assert_xpath '(//*[@id="toc"]/ul[@class="sectlevel0"]/li)[1]/ul/li', output, 2 assert_xpath '((//*[@id="toc"]/ul[@class="sectlevel0"]/li)[1]/ul/li)[1]/a[text()="1. First Section of Part 1"]', output, 1 end test 'should output table of contents in header if toc2 attribute is set' do input = <<-EOS = Article :toc2: :numbered: == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string input assert_xpath '//body[@class="article toc2 toc-left"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"][@class="toc2"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li[1]/a[@href="#_section_one"][text()="1. Section One"]', output, 1 end test 'should set toc position if toc attribute is set to position' do input = <<-EOS = Article :toc: > :numbered: == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string input assert_xpath '//body[@class="article toc2 toc-right"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"][@class="toc2"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li[1]/a[@href="#_section_one"][text()="1. Section One"]', output, 1 end test 'should set toc position if toc and toc-position attributes are set' do input = <<-EOS = Article :toc: :toc-position: right :numbered: == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string input assert_xpath '//body[@class="article toc2 toc-right"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"][@class="toc2"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li[1]/a[@href="#_section_one"][text()="1. Section One"]', output, 1 end test 'should set toc position if toc2 and toc-position attribute are set' do input = <<-EOS = Article :toc2: :toc-position: right :numbered: == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string input assert_xpath '//body[@class="article toc2 toc-right"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"][@class="toc2"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li[1]/a[@href="#_section_one"][text()="1. Section One"]', output, 1 end test 'should set toc position if toc attribute is set to direction' do input = <<-EOS = Article :toc: right :numbered: == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string input assert_xpath '//body[@class="article toc2 toc-right"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"][@class="toc2"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li[1]/a[@href="#_section_one"][text()="1. Section One"]', output, 1 end test 'should set toc placement to preamble if toc attribute is set to preamble' do input = <<-EOS = Article :toc: preamble Yada yada == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string input assert_css '#preamble #toc', output, 1 assert_css '#preamble .sectionbody + #toc', output, 1 end test 'should use document attributes toc-class, toc-title and toclevels to create toc' do input = <<-EOS = Article :toc: :toc-title: Contents :toc-class: toc2 :toclevels: 1 == Section 1 === Section 1.1 ==== Section 1.1.1 ==== Section 1.1.2 === Section 1.2 == Section 2 Fin. EOS output = convert_string input assert_css '#header #toc', output, 1 assert_css '#header #toc.toc2', output, 1 assert_css '#header #toc li', output, 2 assert_css '#header #toc #toctitle', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/*[@id="toctitle"][text()="Contents"]', output, 1 end test 'should not output table of contents if toc-placement attribute is unset' do input = <<-EOS = Article :toc: :toc-placement!: == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string input assert_xpath '//*[@id="toc"]', output, 0 end test 'should output table of contents at location of toc macro' do input = <<-EOS = Article :toc: :toc-placement: macro Once upon a time... toc::[] == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string input assert_css '#preamble #toc', output, 1 assert_css '#preamble .paragraph + #toc', output, 1 end test 'should output table of contents at location of toc macro in embedded document' do input = <<-EOS = Article :toc: :toc-placement: macro Once upon a time... toc::[] == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string_to_embedded input assert_css '#preamble:root #toc', output, 1 assert_css '#preamble:root .paragraph + #toc', output, 1 end test 'should output table of contents at default location in embedded document if toc attribute is set' do input = <<-EOS = Article :showtitle: :toc: Once upon a time... == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string_to_embedded input assert_css 'h1:root', output, 1 assert_css 'h1:root + #toc:root', output, 1 assert_css 'h1:root + #toc:root + #preamble:root', output, 1 end test 'should not activate toc macro if toc-placement is not set' do input = <<-EOS = Article :toc: Once upon a time... toc::[] == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string input assert_css '#toc', output, 1 assert_css '#toctitle', output, 1 assert_css '.toc', output, 1 assert_css '#content .toc', output, 0 end test 'should only output toc at toc macro if toc is macro' do input = <<-EOS = Article :toc: macro Once upon a time... toc::[] == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... EOS output = convert_string input assert_css '#toc', output, 1 assert_css '#toctitle', output, 1 assert_css '.toc', output, 1 assert_css '#content .toc', output, 1 end test 'should use global attributes for toc-title, toc-class and toclevels for toc macro' do input = <<-EOS = Article :toc: :toc-placement: macro :toc-title: Contents :toc-class: contents :toclevels: 1 Preamble. toc::[] == Section 1 === Section 1.1 ==== Section 1.1.1 ==== Section 1.1.2 === Section 1.2 == Section 2 Fin. EOS output = convert_string input assert_css '#toc', output, 1 assert_css '#toctitle', output, 1 assert_css '#preamble #toc', output, 1 assert_css '#preamble #toc.contents', output, 1 assert_xpath '//*[@id="toc"]/*[@class="title"][text() = "Contents"]', output, 1 assert_css '#toc li', output, 2 assert_xpath '(//*[@id="toc"]//li)[1]/a[text() = "Section 1"]', output, 1 assert_xpath '(//*[@id="toc"]//li)[2]/a[text() = "Section 2"]', output, 1 end test 'should honor id, title, role and level attributes on toc macro' do input = <<-EOS = Article :toc: :toc-placement: macro :toc-title: Ignored :toc-class: ignored :toclevels: 5 :tocdepth: 1 Preamble. [[contents]] [role="contents"] .Contents toc::[levels={tocdepth}] == Section 1 === Section 1.1 ==== Section 1.1.1 ==== Section 1.1.2 === Section 1.2 == Section 2 Fin. EOS output = convert_string input assert_css '#toc', output, 0 assert_css '#toctitle', output, 0 assert_css '#preamble #contents', output, 1 assert_css '#preamble #contents.contents', output, 1 assert_xpath '//*[@id="contents"]/*[@class="title"][text() = "Contents"]', output, 1 assert_css '#contents li', output, 2 assert_xpath '(//*[@id="contents"]//li)[1]/a[text() = "Section 1"]', output, 1 assert_xpath '(//*[@id="contents"]//li)[2]/a[text() = "Section 2"]', output, 1 end test 'child toc levels should not have additional bullet at parent level in html' do input = <<-EOS = Article :toc: == Section One It was a dark and stormy night... == Section Two They couldn't believe their eyes when... === Interlude While they were waiting... == Section Three That's all she wrote! EOS output = convert_string input assert_xpath '//*[@id="header"]//*[@id="toc"][@class="toc"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/*[@id="toctitle"][text()="Table of Contents"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]//ul', output, 2 assert_xpath '//*[@id="header"]//*[@id="toc"]//li', output, 4 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li[2]/a[@href="#_section_two"][text()="Section Two"]', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li/ul/li', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li[2]/ul/li', output, 1 assert_xpath '//*[@id="header"]//*[@id="toc"]/ul/li/ul/li/a[@href="#_interlude"][text()="Interlude"]', output, 1 assert_xpath '((//*[@id="header"]//*[@id="toc"]/ul)[1]/li)[3]/a[@href="#_section_three"][text()="Section Three"]', output, 1 end test 'should not display a table of contents if document has no sections' do input_src = <<-EOS = Document Title :toc: toc::[] This document has no sections. It only has content. EOS ['', 'left', 'preamble', 'macro'].each do |placement| input = input_src.gsub(':toc:', "\\& #{placement}") output = convert_string input assert_css '#toctitle', output, 0 end end test 'should drop anchors from contents of entries in table of contents' do input = <<-EOS = Document Title :toc: == [[un]]Section One content == [[two]][[deux]]Section Two content == Plant Trees by https://ecosia.org[Searching] content EOS output = convert_string_to_embedded input assert_xpath '/*[@id="toc"]', output, 1 toc_links = xmlnodes_at_xpath '/*[@id="toc"]//li', output assert_equal 3, toc_links.size assert_equal 'Section One', toc_links[0].inner_html assert_equal 'Section Two', toc_links[1].inner_html assert_equal 'Plant Trees by Searching', toc_links[2].inner_html end test 'should not remove non-anchor tags from contents of entries in table of contents' do input = <<-EOS = Document Title :toc: :icons: font == `run` command content == icon:bug[] Issues content == https://ecosia.org[_Sustainable_ Searches] content EOS output = convert_string_to_embedded input, :safe => :safe assert_xpath '/*[@id="toc"]', output, 1 toc_links = xmlnodes_at_xpath '/*[@id="toc"]//li', output assert_equal 3, toc_links.size assert_equal 'run command', toc_links[0].inner_html assert_equal ' Issues', toc_links[1].inner_html assert_equal 'Sustainable Searches', toc_links[2].inner_html end end context 'article doctype' do test 'should create only sections in docbook backend' do input = <<-EOS = Article Doc Writer == Section 1 The adventure. === Subsection One It was a dark and stormy night... === Subsection Two They couldn't believe their eyes when... == Section 2 The return. === Subsection Three While they were returning... === Subsection Four That's all she wrote! EOS output = convert_string input, :backend => 'docbook' assert_xpath '//part', output, 0 assert_xpath '//chapter', output, 0 assert_xpath '/article/section', output, 2 assert_xpath '/article/section[1]/title[text() = "Section 1"]', output, 1 assert_xpath '/article/section[2]/title[text() = "Section 2"]', output, 1 assert_xpath '/article/section/section', output, 4 assert_xpath '/article/section[1]/section[1]/title[text() = "Subsection One"]', output, 1 assert_xpath '/article/section[2]/section[1]/title[text() = "Subsection Three"]', output, 1 end end context 'book doctype' do test 'document title with level 0 headings' do input = <<-EOS = Book Doc Writer :doctype: book = Chapter One [partintro] It was a dark and stormy night... == Scene One Someone's gonna get axed. = Chapter Two [partintro] They couldn't believe their eyes when... == Interlude While they were waiting... = Chapter Three == Scene One That's all she wrote! EOS output = convert_string(input) assert_css 'body.book', output, 1 assert_css 'h1', output, 4 assert_css '#header h1', output, 1 assert_css '#content h1', output, 3 assert_css '#content h1.sect0', output, 3 assert_css 'h2', output, 3 assert_css '#content h2', output, 3 assert_xpath '//h1[@id="_chapter_one"][text() = "Chapter One"]', output, 1 assert_xpath '//h1[@id="_chapter_two"][text() = "Chapter Two"]', output, 1 assert_xpath '//h1[@id="_chapter_three"][text() = "Chapter Three"]', output, 1 end test 'should print error if level 0 section comes after nested section and doctype is not book' do input = <<-EOS = Document Title == Level 1 Section === Level 2 Section = Level 0 Section EOS using_memory_logger do |logger| convert_string input assert_message logger, :ERROR, ': line 7: level 0 sections can only be used when doctype is book', Hash end end test 'should add class matching role to part' do input = <<-EOS = Book Title :doctype: book [.newbie] = Part 1 == Chapter A content = Part 2 == Chapter B content EOS result = convert_string_to_embedded input assert_css 'h1.sect0', result, 2 assert_css 'h1.sect0.newbie', result, 1 assert_css 'h1.sect0.newbie#_part_1', result, 1 end test 'should assign appropriate sectname for section type' do input = <<-EOS = Book Title :doctype: book :idprefix: :idseparator: - = Part Title == Chapter Title === Section Title content [appendix] == Appendix Title === Appendix Section Title content EOS doc = document_from_string input assert_equal 'header', doc.header.sectname assert_equal 'part', (doc.find_by :id => 'part-title')[0].sectname assert_equal 'chapter', (doc.find_by :id => 'chapter-title')[0].sectname assert_equal 'section', (doc.find_by :id => 'section-title')[0].sectname assert_equal 'appendix', (doc.find_by :id => 'appendix-title')[0].sectname assert_equal 'section', (doc.find_by :id => 'appendix-section-title')[0].sectname end test 'should add partintro style to child paragraph of part' do input = <<-EOS = Book :doctype: book = Part 1 part intro == Chapter 1 EOS doc = document_from_string input partintro = doc.blocks.first.blocks.first assert_equal :open, partintro.context assert_equal 'partintro', partintro.style end test 'should add partintro style to child open block of part' do input = <<-EOS = Book :doctype: book = Part 1 -- part intro -- == Chapter 1 EOS doc = document_from_string input partintro = doc.blocks.first.blocks.first assert_equal :open, partintro.context assert_equal 'partintro', partintro.style end test 'should wrap child paragraphs of part in partintro open block' do input = <<-EOS = Book :doctype: book = Part 1 part intro more part intro == Chapter 1 EOS doc = document_from_string input partintro = doc.blocks.first.blocks.first assert_equal :open, partintro.context assert_equal 'partintro', partintro.style assert_equal 2, partintro.blocks.size assert_equal :paragraph, partintro.blocks[0].context assert_equal :paragraph, partintro.blocks[1].context end test 'should warn if part has no sections' do input = <<-EOS = Book :doctype: book = Part 1 [partintro] intro EOS using_memory_logger do |logger| document_from_string input assert_message logger, :ERROR, ': line 8: invalid part, must have at least one section (e.g., chapter, appendix, etc.)', Hash end end test 'should create parts and chapters in docbook backend' do input = <<-EOS = Book Doc Writer :doctype: book = Part 1 [partintro] The adventure. == Chapter One It was a dark and stormy night... == Chapter Two They couldn't believe their eyes when... = Part 2 [partintro] The return. == Chapter Three While they were returning... == Chapter Four That's all she wrote! EOS output = convert_string input, :backend => 'docbook' assert_xpath '//chapter/chapter', output, 0 assert_xpath '/book/part', output, 2 assert_xpath '/book/part[1]/title[text() = "Part 1"]', output, 1 assert_xpath '/book/part[2]/title[text() = "Part 2"]', output, 1 assert_xpath '/book/part/chapter', output, 4 assert_xpath '/book/part[1]/chapter[1]/title[text() = "Chapter One"]', output, 1 assert_xpath '/book/part[2]/chapter[1]/title[text() = "Chapter Three"]', output, 1 end test 'subsections in preface and appendix should start at level 2' do input = <<-EOS = Multipart Book Doc Writer :doctype: book [preface] = Preface Preface content === Preface subsection Preface subsection content = Part 1 .Part intro title [partintro] Part intro content == Chapter 1 content [appendix] = Appendix Appendix content === Appendix subsection Appendix subsection content EOS output = nil using_memory_logger do |logger| output = convert_string input, :backend => 'docbook' assert logger.empty? end assert_xpath '/book/preface', output, 1 assert_xpath '/book/preface/section', output, 1 assert_xpath '/book/part', output, 1 assert_xpath '/book/part/partintro', output, 1 assert_xpath '/book/part/partintro/title', output, 1 assert_xpath '/book/part/partintro/simpara', output, 1 assert_xpath '/book/appendix', output, 1 assert_xpath '/book/appendix/section', output, 1 end end end