# encoding: UTF-8 unless defined? ASCIIDOCTOR_PROJECT_DIR $: << File.dirname(__FILE__); $:.uniq! require 'test_helper' end context 'Attributes' do context 'Assignment' do test 'creates an attribute' do doc = document_from_string(':frog: Tanglefoot') assert_equal 'Tanglefoot', doc.attributes['frog'] end test 'requires a space after colon following attribute name' do doc = document_from_string 'foo:bar' assert_equal nil, doc.attributes['foo'] end test 'creates an attribute by fusing a legacy multi-line value' do str = <<-EOS :description: This is the first + Ruby implementation of + AsciiDoc. EOS doc = document_from_string(str) assert_equal 'This is the first Ruby implementation of AsciiDoc.', doc.attributes['description'] end test 'creates an attribute by fusing a multi-line value' do str = <<-EOS :description: This is the first \\ Ruby implementation of \\ AsciiDoc. EOS doc = document_from_string(str) assert_equal 'This is the first Ruby implementation of AsciiDoc.', doc.attributes['description'] end test 'honors line break characters in multi-line values' do str = <<-EOS :signature: Linus Torvalds + \\ Linux Hacker + \\ linus.torvalds@example.com EOS doc = document_from_string(str) assert_equal %(Linus Torvalds +\nLinux Hacker +\nlinus.torvalds@example.com), doc.attributes['signature'] end test 'should delete an attribute that ends with !' do doc = document_from_string(":frog: Tanglefoot\n:frog!:") assert_equal nil, doc.attributes['frog'] end test 'should delete an attribute that ends with ! set via API' do doc = document_from_string(":frog: Tanglefoot", :attributes => {'frog!' => ''}) assert_equal nil, doc.attributes['frog'] end test 'should delete an attribute that begins with !' do doc = document_from_string(":frog: Tanglefoot\n:!frog:") assert_equal nil, doc.attributes['frog'] end test 'should delete an attribute that begins with ! set via API' do doc = document_from_string(":frog: Tanglefoot", :attributes => {'!frog' => ''}) assert_equal nil, doc.attributes['frog'] end test 'should delete an attribute set via API to nil value' do doc = document_from_string(":frog: Tanglefoot", :attributes => {'frog' => nil}) assert_equal nil, doc.attributes['frog'] end test "doesn't choke when deleting a non-existing attribute" do doc = document_from_string(':frog!:') assert_equal nil, doc.attributes['frog'] end test "replaces special characters in attribute value" do doc = document_from_string(":xml-busters: <>&") assert_equal '<>&', doc.attributes['xml-busters'] end test "performs attribute substitution on attribute value" do doc = document_from_string(":version: 1.0\n:release: Asciidoctor {version}") assert_equal 'Asciidoctor 1.0', doc.attributes['release'] end test "assigns attribute to empty string if substitution fails to resolve attribute" do doc = document_from_string ":release: Asciidoctor {version}", :attributes => { 'attribute-missing' => 'drop-line' } assert_equal '', doc.attributes['release'] end test "assigns multi-line attribute to empty string if substitution fails to resolve attribute" do doc = document_from_string ":release: Asciidoctor +\n {version}", :attributes => { 'attribute-missing' => 'drop-line' } assert_equal '', doc.attributes['release'] end test 'resolves attributes inside attribute value within header' do input = <<-EOS = Document Title :big: big :bigfoot: {big}foot {bigfoot} EOS result = render_embedded_string input assert result.include? 'bigfoot' end test 'resolves attributes and pass macro inside attribute value outside header' do input = <<-EOS = Document Title content :big: pass:a,q[_big_] :bigfoot: {big}foot {bigfoot} EOS result = render_embedded_string input assert result.include? 'bigfoot' end test 'resolves user-home attribute if safe mode is less than SERVER' do input = <<-EOS :imagesdir: {user-home}/etc/images {imagesdir} EOS output = render_embedded_string input, :doctype => :inline, :safe => :safe if RUBY_VERSION >= '1.9' assert_equal %(#{Dir.home}/etc/images), output else assert_equal %(#{ENV['HOME']}/etc/images), output end end test 'user-home attribute resolves to . if safe mode is SERVER or greater' do input = <<-EOS :imagesdir: {user-home}/etc/images {imagesdir} EOS output = render_embedded_string input, :doctype => :inline, :safe => :server if RUBY_VERSION >= '1.9' assert_equal %(./etc/images), output else assert_equal %(./etc/images), output end end test "apply custom substitutions to text in passthrough macro and assign to attribute" do doc = document_from_string(":xml-busters: pass:[<>&]") assert_equal '<>&', doc.attributes['xml-busters'] doc = document_from_string(":xml-busters: pass:none[<>&]") assert_equal '<>&', doc.attributes['xml-busters'] doc = document_from_string(":xml-busters: pass:specialcharacters[<>&]") assert_equal '<>&', doc.attributes['xml-busters'] end test "attribute is treated as defined until it's not" do input = <<-EOS :holygrail: ifdef::holygrail[] The holy grail has been found! endif::holygrail[] :holygrail!: ifndef::holygrail[] Buggers! What happened to the grail? endif::holygrail[] EOS output = render_string input assert_xpath '//p', output, 2 assert_xpath '(//p)[1][text() = "The holy grail has been found!"]', output, 1 assert_xpath '(//p)[2][text() = "Buggers! What happened to the grail?"]', output, 1 end # Validates requirement: "Header attributes are overridden by command-line attributes." test 'attribute defined in document options overrides attribute in document' do doc = document_from_string(':cash: money', :attributes => {'cash' => 'heroes'}) assert_equal 'heroes', doc.attributes['cash'] end test 'attribute defined in document options cannot be unassigned in document' do doc = document_from_string(':cash!:', :attributes => {'cash' => 'heroes'}) assert_equal 'heroes', doc.attributes['cash'] end test 'attribute undefined in document options cannot be assigned in document' do doc = document_from_string(':cash: money', :attributes => {'cash!' => '' }) assert_equal nil, doc.attributes['cash'] doc = document_from_string(':cash: money', :attributes => {'cash' => nil }) assert_equal nil, doc.attributes['cash'] end test 'backend and doctype attributes are set by default in default configuration' do input = <<-EOS = Document Title Author Name content EOS doc = document_from_string input expect = { 'backend' => 'html5', 'backend-html5' => '', 'backend-html5-doctype-article' => '', 'outfilesuffix' => '.html', 'basebackend' => 'html', 'basebackend-html' => '', 'basebackend-html-doctype-article' => '', 'doctype' => 'article', 'doctype-article' => '', 'filetype' => 'html', 'filetype-html' => '' } expect.each do |key, val| assert doc.attributes.key? key assert_equal val, doc.attributes[key] end end test 'backend and doctype attributes are set by default in custom configuration' do input = <<-EOS = Document Title Author Name content EOS doc = document_from_string input, :doctype => 'book', :backend => 'docbook' expect = { 'backend' => 'docbook5', 'backend-docbook5' => '', 'backend-docbook5-doctype-book' => '', 'outfilesuffix' => '.xml', 'basebackend' => 'docbook', 'basebackend-docbook' => '', 'basebackend-docbook-doctype-book' => '', 'doctype' => 'book', 'doctype-book' => '', 'filetype' => 'xml', 'filetype-xml' => '' } expect.each do |key, val| assert doc.attributes.key? key assert_equal val, doc.attributes[key] end end test 'backend attributes are updated if backend attribute is defined in document and safe mode is less than SERVER' do input = <<-EOS = Document Title Author Name :backend: docbook :doctype: book content EOS doc = document_from_string input, :safe => Asciidoctor::SafeMode::SAFE expect = { 'backend' => 'docbook5', 'backend-docbook5' => '', 'backend-docbook5-doctype-book' => '', 'outfilesuffix' => '.xml', 'basebackend' => 'docbook', 'basebackend-docbook' => '', 'basebackend-docbook-doctype-book' => '', 'doctype' => 'book', 'doctype-book' => '', 'filetype' => 'xml', 'filetype-xml' => '' } expect.each do |key, val| assert doc.attributes.key?(key) assert_equal val, doc.attributes[key] end assert !doc.attributes.key?('backend-html5') assert !doc.attributes.key?('backend-html5-doctype-article') assert !doc.attributes.key?('basebackend-html') assert !doc.attributes.key?('basebackend-html-doctype-article') assert !doc.attributes.key?('doctype-article') assert !doc.attributes.key?('filetype-html') end test 'backend attributes defined in document options overrides backend attribute in document' do doc = document_from_string(':backend: docbook45', :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'backend' => 'html5'}) assert_equal 'html5', doc.attributes['backend'] assert doc.attributes.has_key? 'backend-html5' assert_equal 'html', doc.attributes['basebackend'] assert doc.attributes.has_key? 'basebackend-html' end test 'set_attr should not overwrite existing key if overwrite is false' do node = Asciidoctor::Block.new nil, :paragraph, :attributes => { 'foo' => 'bar' } assert_equal 'bar', (node.attr 'foo') node.set_attr 'foo', 'baz', false assert_equal 'bar', (node.attr 'foo') end test 'set_attr should overwrite existing key by default' do node = Asciidoctor::Block.new nil, :paragraph, :attributes => { 'foo' => 'bar' } assert_equal 'bar', (node.attr 'foo') node.set_attr 'foo', 'baz' assert_equal 'baz', (node.attr 'foo') end test 'verify toc attribute matrix' do expected_data = <<-EOS #attributes |toc|toc-position|toc-placement|toc-class toc | |nil |auto |nil toc=header | |nil |auto |nil toc=beeboo | |nil |auto |nil toc=left | |left |auto |toc2 toc2 | |left |auto |toc2 toc=right | |right |auto |toc2 toc=preamble | |content |preamble |nil toc=macro | |content |macro |nil toc toc-placement=macro toc-position=left | |content |macro |nil toc toc-placement! | |content |macro |nil EOS expected = expected_data.strip.lines.map {|l| next if l.start_with? '#' l.split('|').map {|e| (e = e.strip) == 'nil' ? nil : e } }.compact expected.each do |expect| raw_attrs, toc, toc_position, toc_placement, toc_class = expect attrs = Hash[*(raw_attrs.split ' ').map {|e| e.include?('=') ? e.split('=') : [e, ''] }.flatten] doc = document_from_string '', :attributes => attrs toc ? (assert doc.attr?('toc', toc)) : (assert !doc.attr?('toc')) toc_position ? (assert doc.attr?('toc-position', toc_position)) : (assert !doc.attr?('toc-position')) toc_placement ? (assert doc.attr?('toc-placement', toc_placement)) : (assert !doc.attr?('toc-placement')) toc_class ? (assert doc.attr?('toc-class', toc_class)) : (assert !doc.attr?('toc-class')) end end end context 'Interpolation' do test "render properly with simple names" do html = render_string(":frog: Tanglefoot\n:my_super-hero: Spiderman\n\nYo, {frog}!\nBeat {my_super-hero}!") result = Nokogiri::HTML(html) assert_equal "Yo, Tanglefoot!\nBeat Spiderman!", result.css("p").first.content.strip end test 'attribute lookup is not case sensitive' do input = <<-EOS :He-Man: The most powerful man in the universe He-Man: {He-Man} She-Ra: {She-Ra} EOS result = render_embedded_string input, :attributes => {'She-Ra' => 'The Princess of Power'} assert_xpath '//p[text()="He-Man: The most powerful man in the universe"]', result, 1 assert_xpath '//p[text()="She-Ra: The Princess of Power"]', result, 1 end test "render properly with single character name" do html = render_string(":r: Ruby\n\nR is for {r}!") result = Nokogiri::HTML(html) assert_equal 'R is for Ruby!', result.css("p").first.content.strip end test "collapses spaces in attribute names" do input = <<-EOS Main Header =========== :My frog: Tanglefoot Yo, {myfrog}! EOS output = render_string input assert_xpath '(//p)[1][text()="Yo, Tanglefoot!"]', output, 1 end test "ignores lines with bad attributes if attribute-missing is drop-line" do input = <<-EOS :attribute-missing: drop-line This is blah blah {foobarbaz} all there is. EOS html = render_embedded_string input result = Nokogiri::HTML(html) refute_match(/blah blah/m, result.css("p").first.content.strip) end test "attribute value gets interpretted when rendering" do doc = document_from_string(":google: http://google.com[Google]\n\n{google}") assert_equal 'http://google.com[Google]', doc.attributes['google'] output = doc.render assert_xpath '//a[@href="http://google.com"][text() = "Google"]', output, 1 end test 'should drop line with reference to missing attribute if attribute-missing attribute is drop-line' do input = <<-EOS :attribute-missing: drop-line Line 1: This line should appear in the output. Line 2: Oh no, a {bogus-attribute}! This line should not appear in the output. EOS output = render_embedded_string input assert_match(/Line 1/, output) refute_match(/Line 2/, output) end test 'should not drop line with reference to missing attribute by default' do input = <<-EOS Line 1: This line should appear in the output. Line 2: A {bogus-attribute}! This time, this line should appear in the output. EOS output = render_embedded_string input assert_match(/Line 1/, output) assert_match(/Line 2/, output) assert_match(/\{bogus-attribute\}/, output) end test 'should drop line with attribute unassignment by default' do input = <<-EOS :a: Line 1: This line should appear in the output. Line 2: {set:a!}This line should not appear in the output. EOS output = render_embedded_string input assert_match(/Line 1/, output) refute_match(/Line 2/, output) end test 'should not drop line with attribute unassignment if attribute-undefined is drop' do input = <<-EOS :attribute-undefined: drop :a: Line 1: This line should appear in the output. Line 2: {set:a!}This line should not appear in the output. EOS output = render_embedded_string input assert_match(/Line 1/, output) assert_match(/Line 2/, output) refute_match(/\{set:a!\}/, output) end test "substitutes inside unordered list items" do html = render_string(":foo: bar\n* snort at the {foo}\n* yawn") result = Nokogiri::HTML(html) assert_match(/snort at the bar/, result.css("li").first.content.strip) end test 'substitutes inside section title' do output = render_string(":prefix: Cool\n\n== {prefix} Title\n\ncontent") result = Nokogiri::HTML(output) assert_match(/Cool Title/, result.css('h2').first.content) assert_match(/_cool_title/, result.css('h2').first.attr('id')) end test 'interpolates attribute defined in header inside attribute entry in header' do input = <<-EOS = Title Author Name :attribute-a: value :attribute-b: {attribute-a} preamble EOS doc = document_from_string(input, :parse_header_only => true) assert_equal 'value', doc.attributes['attribute-b'] end test 'interpolates author attribute inside attribute entry in header' do input = <<-EOS = Title Author Name :name: {author} preamble EOS doc = document_from_string(input, :parse_header_only => true) assert_equal 'Author Name', doc.attributes['name'] end test 'interpolates revinfo attribute inside attribute entry in header' do input = <<-EOS = Title Author Name 2013-01-01 :date: {revdate} preamble EOS doc = document_from_string(input, :parse_header_only => true) assert_equal '2013-01-01', doc.attributes['date'] end test 'attribute entries can resolve previously defined attributes' do input = <<-EOS = Title Author Name v1.0, 2010-01-01: First release! :a: value :a2: {a} :revdate2: {revdate} {a} == {a2} {revdate} == {revdate2} EOS doc = document_from_string input assert_equal '2010-01-01', doc.attr('revdate') assert_equal '2010-01-01', doc.attr('revdate2') assert_equal 'value', doc.attr('a') assert_equal 'value', doc.attr('a2') output = doc.render assert output.include?('value == value') assert output.include?('2010-01-01 == 2010-01-01') end test 'substitutes inside block title' do input = <<-EOS :gem_name: asciidoctor .Require the +{gem_name}+ gem To use {gem_name}, the first thing to do is to import it in your Ruby source file. EOS output = render_embedded_string input, :attributes => {'compat-mode' => ''} assert_xpath '//*[@class="title"]/code[text()="asciidoctor"]', output, 1 input = <<-EOS :gem_name: asciidoctor .Require the `{gem_name}` gem To use {gem_name}, the first thing to do is to import it in your Ruby source file. EOS output = render_embedded_string input assert_xpath '//*[@class="title"]/code[text()="asciidoctor"]', output, 1 end test 'renders attribute until it is deleted' do input = <<-EOS :foo: bar Crossing the {foo}. :foo!: Belly up to the {foo}. EOS output = render_embedded_string input assert_xpath '//p[text()="Crossing the bar."]', output, 1 assert_xpath '//p[text()="Belly up to the bar."]', output, 0 end test 'should allow compat-mode to be set and unset in middle of document' do input = <<-EOS :foo: bar [[paragraph-a]] `{foo}` :compat-mode!: [[paragraph-b]] `{foo}` :compat-mode: [[paragraph-c]] `{foo}` EOS result = render_embedded_string input, :attributes => {'compat-mode' => '@'} assert_xpath '/*[@id="paragraph-a"]//code[text()="{foo}"]', result, 1 assert_xpath '/*[@id="paragraph-b"]//code[text()="bar"]', result, 1 assert_xpath '/*[@id="paragraph-c"]//code[text()="{foo}"]', result, 1 end test 'does not disturb attribute-looking things escaped with backslash' do html = render_string(":foo: bar\nThis is a \\{foo} day.") result = Nokogiri::HTML(html) assert_equal 'This is a {foo} day.', result.css('p').first.content.strip end test 'does not disturb attribute-looking things escaped with literals' do html = render_string(":foo: bar\nThis is a +++{foo}+++ day.") result = Nokogiri::HTML(html) assert_equal 'This is a {foo} day.', result.css('p').first.content.strip end test 'does not substitute attributes inside listing blocks' do input = <<-EOS :forecast: snow ---- puts 'The forecast for today is {forecast}' ---- EOS output = render_string(input) assert_match(/\{forecast\}/, output) end test 'does not substitute attributes inside literal blocks' do input = <<-EOS :foo: bar .... You insert the text {foo} to expand the value of the attribute named foo in your document. .... EOS output = render_string(input) assert_match(/\{foo\}/, output) end test 'does not show docdir and shows relative docfile if safe mode is SERVER or greater' do input = <<-EOS * docdir: {docdir} * docfile: {docfile} EOS docdir = Dir.pwd docfile = File.join(docdir, 'sample.asciidoc') output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docdir' => docdir, 'docfile' => docfile} assert_xpath '//li[1]/p[text()="docdir: "]', output, 1 assert_xpath '//li[2]/p[text()="docfile: sample.asciidoc"]', output, 1 end test 'shows absolute docdir and docfile paths if safe mode is less than SERVER' do input = <<-EOS * docdir: {docdir} * docfile: {docfile} EOS docdir = Dir.pwd docfile = File.join(docdir, 'sample.asciidoc') output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => docdir, 'docfile' => docfile} assert_xpath %(//li[1]/p[text()="docdir: #{docdir}"]), output, 1 assert_xpath %(//li[2]/p[text()="docfile: #{docfile}"]), output, 1 end test 'assigns attribute defined in attribute reference with set prefix and value' do input = '{set:foo:bar}{foo}' output = render_embedded_string input assert_xpath '//p', output, 1 assert_xpath '//p[text()="bar"]', output, 1 end test 'assigns attribute defined in attribute reference with set prefix and no value' do input = "{set:foo}\n{foo}yes" output = render_embedded_string input assert_xpath '//p', output, 1 assert_xpath '//p[normalize-space(text())="yes"]', output, 1 end test 'assigns attribute defined in attribute reference with set prefix and empty value' do input = "{set:foo:}\n{foo}yes" output = render_embedded_string input assert_xpath '//p', output, 1 assert_xpath '//p[normalize-space(text())="yes"]', output, 1 end test 'unassigns attribute defined in attribute reference with set prefix' do input = <<-EOS :attribute-missing: drop-line :foo: {set:foo!} {foo}yes EOS output = render_embedded_string input assert_xpath '//p', output, 1 assert_xpath '//p/child::text()', output, 0 end end context "Intrinsic attributes" do test "substitute intrinsics" do Asciidoctor::INTRINSIC_ATTRIBUTES.each_pair do |key, value| html = render_string("Look, a {#{key}} is here") # can't use Nokogiri because it interprets the HTML entities and we can't match them assert_match(/Look, a #{Regexp.escape(value)} is here/, html) end end test "don't escape intrinsic substitutions" do html = render_string('happy{nbsp}together') assert_match(/happy together/, html) end test "escape special characters" do html = render_string('&') assert_match(/<node>&<\/node>/, html) end test 'creates counter' do input = <<-EOS {counter:mycounter} EOS doc = document_from_string input output = doc.render assert_equal 1, doc.attributes['mycounter'] assert_xpath '//p[text()="1"]', output, 1 end test 'creates counter silently' do input = <<-EOS {counter2:mycounter} EOS doc = document_from_string input output = doc.render assert_equal 1, doc.attributes['mycounter'] assert_xpath '//p[text()="1"]', output, 0 end test 'creates counter with numeric seed value' do input = <<-EOS {counter2:mycounter:10} EOS doc = document_from_string input doc.render assert_equal 10, doc.attributes['mycounter'] end test 'creates counter with character seed value' do input = <<-EOS {counter2:mycounter:A} EOS doc = document_from_string input doc.render assert_equal 'A', doc.attributes['mycounter'] end test 'increments counter with numeric value' do input = <<-EOS :mycounter: 1 {counter:mycounter} {mycounter} EOS doc = document_from_string input output = doc.render assert_equal 2, doc.attributes['mycounter'] assert_xpath '//p[text()="2"]', output, 2 end test 'increments counter with character value' do input = <<-EOS :mycounter: @ {counter:mycounter} {mycounter} EOS doc = document_from_string input output = doc.render assert_equal 'A', doc.attributes['mycounter'] assert_xpath '//p[text()="A"]', output, 2 end test 'counter uses 0 as seed value if seed attribute is nil' do input = <<-EOS :mycounter: {counter:mycounter} {mycounter} EOS doc = document_from_string input output = doc.render :header_footer => false assert_equal 1, doc.attributes['mycounter'] assert_xpath '//p[text()="1"]', output, 2 end test 'counter value can be reset by attribute entry' do input = <<-EOS :mycounter: before: {counter:mycounter} {counter:mycounter} {counter:mycounter} :mycounter!: after: {counter:mycounter} EOS doc = document_from_string input output = doc.render :header_footer => false assert_equal 1, doc.attributes['mycounter'] assert_xpath '//p[text()="before: 1 2 3"]', output, 1 assert_xpath '//p[text()="after: 1"]', output, 1 end end context 'Block attributes' do test 'parses attribute names as name token' do input = <<-EOS [normal,foo="bar",_foo="_bar",foo1="bar1",foo-foo="bar-bar",foo.foo="bar.bar"] content EOS block = block_from_string input assert_equal 'bar', block.attr('foo') assert_equal '_bar', block.attr('_foo') assert_equal 'bar1', block.attr('foo1') assert_equal 'bar-bar', block.attr('foo-foo') assert_equal 'bar.bar', block.attr('foo.foo') end test 'positional attributes assigned to block' do input = <<-EOS [quote, author, source] ____ A famous quote. ____ EOS doc = document_from_string(input) qb = doc.blocks.first assert_equal 'quote', qb.style assert_equal 'author', qb.attr('attribution') assert_equal 'author', qb.attr(:attribution) assert_equal 'author', qb.attributes['attribution'] assert_equal 'source', qb.attributes['citetitle'] end test 'normal substitutions are performed on single-quoted positional attribute' do input = <<-EOS [quote, author, 'http://wikipedia.org[source]'] ____ A famous quote. ____ EOS doc = document_from_string(input) qb = doc.blocks.first assert_equal 'quote', qb.style assert_equal 'author', qb.attr('attribution') assert_equal 'author', qb.attr(:attribution) assert_equal 'author', qb.attributes['attribution'] assert_equal 'source', qb.attributes['citetitle'] end test 'normal substitutions are performed on single-quoted named attribute' do input = <<-EOS [quote, author, citetitle='http://wikipedia.org[source]'] ____ A famous quote. ____ EOS doc = document_from_string(input) qb = doc.blocks.first assert_equal 'quote', qb.style assert_equal 'author', qb.attr('attribution') assert_equal 'author', qb.attr(:attribution) assert_equal 'author', qb.attributes['attribution'] assert_equal 'source', qb.attributes['citetitle'] end test 'normal substitutions are performed once on single-quoted named title attribute' do input = <<-EOS [title='*title*'] content EOS output = render_embedded_string input assert_xpath '//*[@class="title"]/strong[text()="title"]', output, 1 end test 'attribute list may begin with space' do input = <<-EOS [ quote] ____ A famous quote. ____ EOS doc = document_from_string input qb = doc.blocks.first assert_equal 'quote', qb.style end test 'attribute list may begin with comma' do input = <<-EOS [, author, source] ____ A famous quote. ____ EOS doc = document_from_string input qb = doc.blocks.first assert_equal 'quote', qb.style assert_equal 'author', qb.attributes['attribution'] assert_equal 'source', qb.attributes['citetitle'] end test 'first attribute in list may be double quoted' do input = <<-EOS ["quote", "author", "source", role="famous"] ____ A famous quote. ____ EOS doc = document_from_string input qb = doc.blocks.first assert_equal 'quote', qb.style assert_equal 'author', qb.attributes['attribution'] assert_equal 'source', qb.attributes['citetitle'] assert_equal 'famous', qb.attributes['role'] end test 'first attribute in list may be single quoted' do input = <<-EOS ['quote', 'author', 'source', role='famous'] ____ A famous quote. ____ EOS doc = document_from_string input qb = doc.blocks.first assert_equal 'quote', qb.style assert_equal 'author', qb.attributes['attribution'] assert_equal 'source', qb.attributes['citetitle'] assert_equal 'famous', qb.attributes['role'] end test 'attribute with value None without quotes is ignored' do input = <<-EOS [id=None] paragraph EOS doc = document_from_string input para = doc.blocks.first assert !para.attributes.has_key?('id') end test 'role? returns true if role is assigned' do input = <<-EOS [role="lead"] A paragraph EOS doc = document_from_string input p = doc.blocks.first assert p.role? end test 'role? can check for exact role name match' do input = <<-EOS [role="lead"] A paragraph EOS doc = document_from_string input p = doc.blocks.first assert p.role?('lead') p2 = doc.blocks.last assert !p2.role?('final') end test 'has_role? can check for precense of role name' do input = <<-EOS [role="lead abstract"] A paragraph EOS doc = document_from_string input p = doc.blocks.first assert !p.role?('lead') assert p.has_role?('lead') end test 'roles returns array of role names' do input = <<-EOS [role="story lead"] A paragraph EOS doc = document_from_string input p = doc.blocks.first assert_equal ['story', 'lead'], p.roles end test 'roles returns empty array if role attribute is not set' do input = <<-EOS A paragraph EOS doc = document_from_string input p = doc.blocks.first assert_equal [], p.roles end test "Attribute substitutions are performed on attribute list before parsing attributes" do input = <<-EOS :lead: role="lead" [{lead}] A paragraph EOS doc = document_from_string(input) para = doc.blocks.first assert_equal 'lead', para.attributes['role'] end test 'id, role and options attributes can be specified on block style using shorthand syntax' do input = <<-EOS [normal#first.lead%step] A normal paragraph. EOS doc = document_from_string(input) para = doc.blocks.first assert_equal 'first', para.attributes['id'] assert_equal 'lead', para.attributes['role'] assert_equal 'step', para.attributes['options'] assert para.attributes.has_key?('step-option') end test 'multiple roles and options can be specified in block style using shorthand syntax' do input = <<-EOS [.role1%option1.role2%option2] Text EOS doc = document_from_string input para = doc.blocks.first assert_equal 'role1 role2', para.attributes['role'] assert_equal 'option1,option2', para.attributes['options'] assert para.attributes.has_key?('option1-option') assert para.attributes.has_key?('option2-option') end test 'option can be specified in first position of block style using shorthand syntax' do input = <<-EOS [%interactive] - [x] checked EOS doc = document_from_string input list = doc.blocks.first assert_equal 'interactive', list.attributes['options'] assert list.attributes.has_key?('interactive-option') assert list.attributes[1] == '%interactive' end test 'id and role attributes can be specified on section style using shorthand syntax' do input = <<-EOS [dedication#dedication.small] == Section Content. EOS output = render_embedded_string input assert_xpath '/div[@class="sect1 small"]', output, 1 assert_xpath '/div[@class="sect1 small"]/h2[@id="dedication"]', output, 1 end test 'id attribute specified using shorthand syntax should not create a special section' do input = <<-EOS [#idname] == Section content EOS doc = document_from_string input, :backend => 'docbook45' section = doc.blocks[0] refute_nil section assert_equal :section, section.context assert !section.special output = doc.convert assert_css 'section', output, 1 assert_css 'section#idname', output, 1 end test "Block attributes are additive" do input = <<-EOS [id='foo'] [role='lead'] A paragraph. EOS doc = document_from_string(input) para = doc.blocks.first assert_equal 'foo', para.id assert_equal 'lead', para.attributes['role'] end test "Last wins for id attribute" do input = <<-EOS [[bar]] [[foo]] == Section paragraph [[baz]] [id='coolio'] === Section EOS doc = document_from_string(input) sec = doc.first_section assert_equal 'foo', sec.id subsec = sec.blocks.last assert_equal 'coolio', subsec.id 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.render assert_xpath '//body[@id="reference"]', output, 1 end test "trailing block attributes tranfer to the following section" do input = <<-EOS [[one]] == Section One paragraph [[sub]] // try to mess this up! === Sub-section paragraph [role='classy'] //// block comment //// == Section Two content EOS doc = document_from_string(input) section_one = doc.blocks.first assert_equal 'one', section_one.id subsection = section_one.blocks.last assert_equal 'sub', subsection.id section_two = doc.blocks.last assert_equal 'classy', section_two.attr(:role) end end end