asciidoctor 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of asciidoctor might be problematic. Click here for more details.

@@ -62,8 +62,8 @@ class Asciidoctor::Renderer
62
62
  if stack_obj.context == :dlist
63
63
  dt_list = stack_obj.buffer.map{|dt,dd| dt.content.strip}.join(', ')
64
64
  "BLOCK :dlist (#{dt_list})"
65
- else
66
- "BLOCK #{stack_obj.context.inspect}"
65
+ #else
66
+ # "BLOCK #{stack_obj.context.inspect}"
67
67
  end
68
68
  else stack_obj.class
69
69
  end
@@ -71,7 +71,7 @@ class Asciidoctor::Renderer
71
71
  prefix << ' '
72
72
  end
73
73
  STDERR.puts '-' * 80
74
- STDERR.puts ret.inspect
74
+ #STDERR.puts ret.inspect
75
75
  STDERR.puts '=' * 80
76
76
  STDERR.puts
77
77
  end
@@ -24,14 +24,15 @@ class Asciidoctor::Section
24
24
  # Public: Set the String section name.
25
25
  attr_writer :name
26
26
 
27
- # Public: Get/Set the String section title.
28
- attr_accessor :title
29
-
30
27
  # Public: Get/Set the String section caption.
31
28
  attr_accessor :caption
32
29
 
33
30
  # Public: Get/Set the String section anchor name.
34
31
  attr_accessor :anchor
32
+ alias :id :anchor
33
+
34
+ # Public: Get the Hash of attributes for this block
35
+ attr_accessor :attributes
35
36
 
36
37
  # Public: Get the Array of section blocks.
37
38
  attr_reader :blocks
@@ -41,6 +42,7 @@ class Asciidoctor::Section
41
42
  # parent - The parent Asciidoc Object.
42
43
  def initialize(parent)
43
44
  @parent = parent
45
+ @attributes = {}
44
46
  @blocks = []
45
47
  end
46
48
 
@@ -59,16 +61,22 @@ class Asciidoctor::Section
59
61
  gsub( /`([^`]+)`/, '<tt>\1</tt>' )
60
62
  end
61
63
 
62
- # Public: Get the String section id
64
+ # Public: Get the String section id prefixed with value of idprefix attribute, otherwise an underscore
65
+ #
66
+ # Section ID synthesis can be disabled by undefining the sectids attribute.
63
67
  #
64
68
  # Examples
65
69
  #
66
- # section = Section.new
70
+ # section = Section.new(parent)
67
71
  # section.name = "Foo"
68
72
  # section.section_id
69
73
  # => "_foo"
70
74
  def section_id
71
- "_#{name && name.downcase.gsub(/\W+/,'_').gsub(/_+$/, '')}".tr_s('_', '_')
75
+ if self.document.attributes.has_key? 'sectids'
76
+ self.document.attributes.fetch('idprefix', '_') + "#{name && name.downcase.gsub(/\W+/,'_').gsub(/_+$/, '')}".tr_s('_', '_')
77
+ else
78
+ nil
79
+ end
72
80
  end
73
81
 
74
82
  # Public: Get the Asciidoctor::Document instance to which this Block belongs
@@ -76,6 +84,19 @@ class Asciidoctor::Section
76
84
  @parent.is_a?(Asciidoctor::Document) ? @parent : @parent.document
77
85
  end
78
86
 
87
+ def attr(name, default = nil)
88
+ default.nil? ? @attributes.fetch(name.to_s, self.document.attr(name)) :
89
+ @attributes.fetch(name.to_s, self.document.attr(name, default))
90
+ end
91
+
92
+ def attr?(name)
93
+ @attributes.has_key?(name.to_s) || self.document.attr?(name)
94
+ end
95
+
96
+ def update_attributes(attributes)
97
+ @attributes.update(attributes)
98
+ end
99
+
79
100
  # Public: Get the Asciidoctor::Renderer instance being used for the ancestor
80
101
  # Asciidoctor::Document instance.
81
102
  def renderer
@@ -109,6 +130,11 @@ class Asciidoctor::Section
109
130
  end.join
110
131
  end
111
132
 
133
+ # Public: The title of this section, an alias of the section name
134
+ def title
135
+ @name
136
+ end
137
+
112
138
  # Public: Get the Integer number of blocks in the section.
113
139
  #
114
140
  # Examples
@@ -1,3 +1,3 @@
1
1
  module Asciidoctor
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -3,17 +3,27 @@ require 'test_helper'
3
3
  context "Attributes" do
4
4
  test "creates an attribute" do
5
5
  doc = document_from_string(":frog: Tanglefoot")
6
- assert_equal doc.defines['frog'], 'Tanglefoot'
6
+ assert_equal doc.attributes['frog'], 'Tanglefoot'
7
+ end
8
+
9
+ test "creates an attribute by fusing a multi-line value" do
10
+ str = <<-EOS
11
+ :description: This is the first +
12
+ Ruby implementation of +
13
+ AsciiDoc.
14
+ EOS
15
+ doc = document_from_string(str)
16
+ assert_equal doc.attributes['description'], 'This is the first Ruby implementation of AsciiDoc.'
7
17
  end
8
18
 
9
19
  test "deletes an attribute" do
10
20
  doc = document_from_string(":frog: Tanglefoot\n:frog!:")
11
- assert_equal nil, doc.defines['frog']
21
+ assert_equal nil, doc.attributes['frog']
12
22
  end
13
23
 
14
24
  test "doesn't choke when deleting a non-existing attribute" do
15
25
  doc = document_from_string(":frog!:")
16
- assert_equal nil, doc.defines['frog']
26
+ assert_equal nil, doc.attributes['frog']
17
27
  end
18
28
 
19
29
  test "render properly with simple names" do
@@ -66,7 +76,8 @@ context "Attributes" do
66
76
  test "doesn't disturb attribute-looking things escaped with literals" do
67
77
  html = render_string(":foo: bar\nThis is a +++{foo}+++ day.")
68
78
  result = Nokogiri::HTML(html)
69
- assert_equal 'This is a {foo} day.', result.css('p').first.content.strip
79
+ #assert_equal 'This is a {foo} day.', result.css('p').first.content.strip
80
+ pending "Don't yet have inline passthrough working"
70
81
  end
71
82
 
72
83
  test "doesn't substitute attributes inside code blocks" do
@@ -77,6 +88,35 @@ context "Attributes" do
77
88
  pending "whut?"
78
89
  end
79
90
 
91
+ context "Block attributes" do
92
+ test "Position attributes assigned to block" do
93
+ input = <<-EOS
94
+ [quote, Name, Source]
95
+ ____
96
+ A famous quote.
97
+ ____
98
+ EOS
99
+ doc = document_from_string(input)
100
+ qb = doc.elements.first
101
+ assert_equal 'quote', qb.attributes['style']
102
+ assert_equal 'quote', qb.attr(:style)
103
+ assert_equal 'Name', qb.attributes['attribution']
104
+ assert_equal 'Source', qb.attributes['citetitle']
105
+ end
106
+
107
+ test "Block attributes are additive" do
108
+ input = <<-EOS
109
+ [id='foo']
110
+ [role='lead']
111
+ A paragraph.
112
+ EOS
113
+ doc = document_from_string(input)
114
+ para = doc.elements.first
115
+ assert_equal 'foo', para.id
116
+ assert_equal 'lead', para.attributes['role']
117
+ end
118
+ end
119
+
80
120
  context "intrinsics" do
81
121
 
82
122
  test "substitute intrinsics" do
@@ -7,11 +7,30 @@ class DocumentTest < Test::Unit::TestCase
7
7
  end
8
8
 
9
9
  def test_title
10
- assert_equal "AsciiDoc Home Page", @doc.title
10
+ assert_equal "AsciiDoc Home Page", @doc.doctitle
11
+ assert_equal 14, @doc.elements.size
12
+ assert_equal :preamble, @doc.elements[0].context
13
+ assert @doc.elements[1].is_a? ::Asciidoctor::Section
11
14
  end
12
15
 
13
16
  def test_with_no_title
14
17
  d = Asciidoctor::Document.new(["Snorf"])
15
- assert_nil d.title
18
+ assert_nil d.doctitle
19
+ end
20
+
21
+ def test_with_header_footer
22
+ result = render_string("= Title\n\npreamble")
23
+ assert_xpath '/html', result, 1
24
+ assert_xpath '//*[@id="header"]', result, 1
25
+ assert_xpath '//*[@id="footer"]', result, 1
26
+ assert_xpath '//*[@id="preamble"]', result, 1
27
+ end
28
+
29
+ def test_with_no_header_footer
30
+ result = render_string("= Title\n\npreamble", :header_footer => false)
31
+ assert_xpath '/html', result, 0
32
+ assert_xpath '/*[@id="header"]', result, 0
33
+ assert_xpath '/*[@id="footer"]', result, 0
34
+ assert_xpath '/*[@id="preamble"]', result, 1
16
35
  end
17
36
  end
data/test/headers_test.rb CHANGED
@@ -1,55 +1,114 @@
1
1
  require 'test_helper'
2
2
 
3
3
  context "Headers" do
4
- test "main header" do
5
- assert_xpath "//h1", render_string("My Title\n=======")
4
+ test "document title with multiline syntax" do
5
+ title = "My Title"
6
+ chars = "=" * title.length
7
+ assert_xpath "//h1[not(@id)][text() = 'My Title']", render_string(title + "\n" + chars)
8
+ assert_xpath "//h1[not(@id)][text() = 'My Title']", render_string(title + "\n" + chars + "\n")
9
+ end
10
+
11
+ test "document title with multiline syntax, give a char" do
12
+ title = "My Title"
13
+ chars = "=" * (title.length + 1)
14
+ assert_xpath "//h1[not(@id)][text() = 'My Title']", render_string(title + "\n" + chars)
15
+ assert_xpath "//h1[not(@id)][text() = 'My Title']", render_string(title + "\n" + chars + "\n")
16
+ end
17
+
18
+ test "document title with multiline syntax, take a char" do
19
+ title = "My Title"
20
+ chars = "=" * (title.length - 1)
21
+ assert_xpath "//h1[not(@id)][text() = 'My Title']", render_string(title + "\n" + chars)
22
+ assert_xpath "//h1[not(@id)][text() = 'My Title']", render_string(title + "\n" + chars + "\n")
23
+ end
24
+
25
+ test "not enough chars for a multiline document title" do
26
+ title = "My Title"
27
+ chars = "=" * (title.length - 2)
28
+ assert_xpath '//h1', render_string(title + "\n" + chars), 0
29
+ assert_xpath '//h1', render_string(title + "\n" + chars + "\n"), 0
30
+ end
31
+
32
+ test "too many chars for a multiline document title" do
33
+ title = "My Title"
34
+ chars = "=" * (title.length + 2)
35
+ assert_xpath '//h1', render_string(title + "\n" + chars), 0
36
+ assert_xpath '//h1', render_string(title + "\n" + chars + "\n"), 0
37
+ end
38
+
39
+ test "document title with single-line syntax" do
40
+ assert_xpath "//h1[not(@id)][text() = 'My Title']", render_string("= My Title")
41
+ end
42
+
43
+ test "document title with symmetric syntax" do
44
+ assert_xpath "//h1[not(@id)][text() = 'My Title']", render_string("= My Title =")
6
45
  end
7
46
 
8
47
  context "level 1" do
9
48
  test "with multiline syntax" do
10
- assert_xpath "//h2[@id='_my_section']", render_string("My Section\n-----------")
49
+ assert_xpath "//h2[@id='_my_section'][text() = 'My Section']", render_string("My Section\n-----------")
11
50
  end
12
51
 
13
- test "with single line syntax" do
14
- assert_xpath "//h2[@id='_my_title']", render_string("== My Title")
52
+ test "with single-line syntax" do
53
+ assert_xpath "//h2[@id='_my_title'][text() = 'My Title']", render_string("== My Title")
54
+ end
55
+
56
+ test "with single-line symmetric syntax" do
57
+ assert_xpath "//h2[@id='_my_title'][text() = 'My Title']", render_string("== My Title ==")
58
+ end
59
+
60
+ test "with single-line non-matching symmetric syntax" do
61
+ assert_xpath "//h2[@id='_my_title'][text() = 'My Title ===']", render_string("== My Title ===")
15
62
  end
16
63
 
17
64
  test "with non-word character" do
18
- assert_xpath "//h2[@id='_where_s_the_love']", render_string("== Where's the love?")
65
+ assert_xpath "//h2[@id='_where_s_the_love'][text() = \"Where's the love?\"]", render_string("== Where's the love?")
19
66
  end
20
67
 
21
68
  test "with sequential non-word characters" do
22
- assert_xpath "//h2[@id='_what_the_is_that']", render_string('== What the #@$ is that')
69
+ assert_xpath "//h2[@id='_what_the_is_this'][text() = 'What the \#@$ is this?']", render_string('== What the #@$ is this?')
70
+ end
71
+
72
+ test "with trailing whitespace" do
73
+ assert_xpath "//h2[@id='_my_title'][text() = 'My Title']", render_string("== My Title ")
74
+ end
75
+
76
+ test "with custom blank idprefix" do
77
+ assert_xpath "//h2[@id='my_title'][text() = 'My Title']", render_string(":idprefix:\n\n== My Title ")
78
+ end
79
+
80
+ test "with custom non-blank idprefix" do
81
+ assert_xpath "//h2[@id='ref_my_title'][text() = 'My Title']", render_string(":idprefix: ref_\n\n== My Title ")
23
82
  end
24
83
  end
25
84
 
26
85
  context "level 2" do
27
86
  test "with multiline syntax" do
28
- assert_xpath "//h3", render_string("My Section\n~~~~~~~~~~~")
87
+ assert_xpath "//h3[@id='_my_section'][text() = 'My Section']", render_string("My Section\n~~~~~~~~~~~")
29
88
  end
30
89
 
31
90
  test "with single line syntax" do
32
- assert_xpath "//h3", render_string("=== My Title")
91
+ assert_xpath "//h3[@id='_my_title'][text() = 'My Title']", render_string("=== My Title")
33
92
  end
34
93
  end
35
94
 
36
95
  context "level 3" do
37
96
  test "with multiline syntax" do
38
- assert_xpath "//h4", render_string("My Section\n^^^^^^^^^^")
97
+ assert_xpath "//h4[@id='_my_section'][text() = 'My Section']", render_string("My Section\n^^^^^^^^^^")
39
98
  end
40
99
 
41
100
  test "with single line syntax" do
42
- assert_xpath "//h4", render_string("==== My Title")
101
+ assert_xpath "//h4[@id='_my_title'][text() = 'My Title']", render_string("==== My Title")
43
102
  end
44
103
  end
45
104
 
46
105
  context "level 4" do
47
106
  test "with multiline syntax" do
48
- assert_xpath "//h5", render_string("My Section\n++++++++++")
107
+ assert_xpath "//h5[@id='_my_section'][text() = 'My Section']", render_string("My Section\n++++++++++")
49
108
  end
50
109
 
51
110
  test "with single line syntax" do
52
- assert_xpath "//h5", render_string("===== My Title")
111
+ assert_xpath "//h5[@id='_my_title'][text() = 'My Title']", render_string("===== My Title")
53
112
  end
54
113
  end
55
114
  end
data/test/lexer_test.rb CHANGED
@@ -9,4 +9,58 @@ class LexerTest < Test::Unit::TestCase
9
9
  assert Asciidoctor::Lexer.is_section_heading?("AsciiDoc Home Page", "==================")
10
10
  assert Asciidoctor::Lexer.is_section_heading?("=== AsciiDoc Home Page")
11
11
  end
12
+
13
+ def test_collect_unnamed_attributes
14
+ attributes = {}
15
+ line = "first, second one, third"
16
+ Asciidoctor::Lexer.collect_attributes(line, attributes)
17
+ assert_equal 3, attributes.length
18
+ assert_equal 'first', attributes[0]
19
+ assert_equal 'second one', attributes[1]
20
+ assert_equal 'third', attributes[2]
21
+ end
22
+
23
+ def test_collect_named_attributes
24
+ attributes = {}
25
+ line = "first='value', second=\"value two\", third=three"
26
+ Asciidoctor::Lexer.collect_attributes(line, attributes)
27
+ assert_equal 3, attributes.length
28
+ assert_equal 'value', attributes['first']
29
+ assert_equal 'value two', attributes['second']
30
+ assert_equal 'three', attributes['third']
31
+ end
32
+
33
+ def test_collect_mixed_named_and_unnamed_attributes
34
+ attributes = {}
35
+ line = "first, second=\"value two\", third=three"
36
+ Asciidoctor::Lexer.collect_attributes(line, attributes)
37
+ assert_equal 3, attributes.length
38
+ assert_equal 'first', attributes[0]
39
+ assert_equal 'value two', attributes['second']
40
+ assert_equal 'three', attributes['third']
41
+ end
42
+
43
+ def test_collect_and_rekey_unnamed_attributes
44
+ attributes = {}
45
+ line = "first, second one, third, fourth"
46
+ Asciidoctor::Lexer.collect_attributes(line, attributes, ['a', 'b', 'c'])
47
+ assert_equal 7, attributes.length
48
+ assert_equal 'first', attributes['a']
49
+ assert_equal 'second one', attributes['b']
50
+ assert_equal 'third', attributes['c']
51
+ assert_equal 'first', attributes[0]
52
+ assert_equal 'second one', attributes[1]
53
+ assert_equal 'third', attributes[2]
54
+ assert_equal 'fourth', attributes[3]
55
+ end
56
+
57
+ def test_rekey_positional_attributes
58
+ attributes = {0 => 'source', 1 => 'java'}
59
+ Asciidoctor::Lexer.rekey_positional_attributes(attributes, ['style', 'language', 'linenums'])
60
+ assert_equal 4, attributes.length
61
+ assert_equal 'source', attributes[0]
62
+ assert_equal 'java', attributes[1]
63
+ assert_equal 'source', attributes['style']
64
+ assert_equal 'java', attributes['language']
65
+ end
12
66
  end
@@ -0,0 +1,406 @@
1
+ require 'test_helper'
2
+
3
+ context "Bulleted lists (:ulist)" do
4
+ context "Simple lists" do
5
+ test "dash elements with no blank lines" do
6
+ output = render_string("Blah\n====\n- Foo\n- Boo\n- Blech")
7
+ assert_xpath '//ul', output, 1
8
+ assert_xpath '//ul/li', output, 3
9
+ end
10
+
11
+ test "dash elements with blank lines" do
12
+ output = render_string("Blah\n====\n- Foo\n\n- Boo\n\n- Blech")
13
+ assert_xpath '//ul', output, 1
14
+ assert_xpath '//ul/li', output, 3
15
+ end
16
+
17
+ test "asterisk elements with no blank lines" do
18
+ output = render_string("Blah\n====\n* Foo\n* Boo\n* Blech")
19
+ assert_xpath '//ul', output, 1
20
+ assert_xpath '//ul/li', output, 3
21
+ end
22
+
23
+ test "asterisk elements with blank lines should merge lists" do
24
+ output = render_string("Blah\n====\n* Foo\n\n* Boo\n\n* Blech")
25
+ assert_xpath '//ul', output, 1
26
+ assert_xpath '//ul/li', output, 3
27
+ end
28
+
29
+ test "asterisk elements with blank lines separated by line comment should not merge lists" do
30
+ output = render_string("Blah\n====\n* Foo\n* Boo\n\n//\n\n* Blech")
31
+ assert_xpath '//ul', output, 2
32
+ assert_xpath '(//ul)[1]/li', output, 2
33
+ assert_xpath '(//ul)[2]/li', output, 1
34
+ end
35
+ end
36
+
37
+ context "Lists with inline markup" do
38
+ test "Quoted text" do
39
+ output = render_string("Blah\n====\n- I am *strong*.\n- I am 'stressed'.\n- I am `inflexible`.")
40
+ assert_xpath '//ul', output, 1
41
+ assert_xpath '//ul/li', output, 3
42
+ assert_xpath '(//ul/li)[1]//strong', output, 1
43
+ assert_xpath '(//ul/li)[2]//em', output, 1
44
+ assert_xpath '(//ul/li)[3]//tt', output, 1
45
+ end
46
+
47
+ test "Attribute substitutions" do
48
+ output = render_string("Blah\n====\n:foo: bar\n\n- side a {brvbar} side b\n- Take me to a {foo}.")
49
+ assert_xpath '//ul', output, 1
50
+ assert_xpath '//ul/li', output, 2
51
+ assert_xpath '(//ul/li)[1]//p[text() = "side a | side b"]', output, 1
52
+ assert_xpath '(//ul/li)[2]//p[text() = "Take me to a bar."]', output, 1
53
+ end
54
+ end
55
+
56
+ context "Nested lists" do
57
+ test "nested mixed elements (asterisk and dash)" do
58
+ output = render_string("Blah\n====\n- Foo\n* Boo\n- Blech")
59
+ assert_xpath '//ul', output, 1
60
+ assert_xpath '//ul/li', output, 3
61
+ end
62
+
63
+ test "nested elements (2) with asterisks" do
64
+ output = render_string("* Foo\n** Boo\n* Blech")
65
+ assert_xpath '//ul', output, 2
66
+ assert_xpath '(//ul)[1]/li', output, 2
67
+ assert_xpath '(//ul)[1]/li//ul/li', output, 1
68
+ end
69
+
70
+ test "nested elements (3) with asterisks" do
71
+ output = render_string("Blah\n====\n* Foo\n** Boo\n*** Snoo\n* Blech")
72
+ assert_xpath '//ul', output, 3
73
+ assert_xpath '(//ul)[1]/li', output, 2
74
+ assert_xpath '((//ul)[1]/li//ul)[1]/li', output, 1
75
+ assert_xpath '(((//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
76
+ end
77
+
78
+ test "nested elements (4) with asterisks" do
79
+ output = render_string("Blah\n====\n* Foo\n** Boo\n*** Snoo\n**** Froo\n* Blech")
80
+ assert_xpath '//ul', output, 4
81
+ assert_xpath '(//ul)[1]/li', output, 2
82
+ assert_xpath '((//ul)[1]/li//ul)[1]/li', output, 1
83
+ assert_xpath '(((//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
84
+ assert_xpath '((((//ul)[1]/li//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
85
+ end
86
+
87
+ test "nested elements (5) with asterisks" do
88
+ output = render_string("Blah\n====\n* Foo\n** Boo\n*** Snoo\n**** Froo\n***** Groo\n* Blech")
89
+ assert_xpath '//ul', output, 5
90
+ assert_xpath '(//ul)[1]/li', output, 2
91
+ assert_xpath '((//ul)[1]/li//ul)[1]/li', output, 1
92
+ assert_xpath '(((//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
93
+ assert_xpath '((((//ul)[1]/li//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
94
+ assert_xpath '(((((//ul)[1]/li//ul)[1]/li//ul)[1]/li//ul)[1]/li//ul)[1]/li', output, 1
95
+ end
96
+ end
97
+
98
+ context "List continuations" do
99
+ test "Adjacent list continuation line attaches following paragraph" do
100
+ input = <<-EOS
101
+ Lists
102
+ =====
103
+
104
+ * Item one, paragraph one
105
+ +
106
+ Item one, paragraph two
107
+ +
108
+ * Item two
109
+ EOS
110
+ output = render_string(input)
111
+ assert_xpath '//ul', output, 1
112
+ assert_xpath '//ul/li', output, 2
113
+ assert_xpath '//ul/li[1]/p', output, 1
114
+ assert_xpath '//ul/li[1]//p', output, 2
115
+ assert_xpath '//ul/li[1]//p[text() = "Item one, paragraph one"]', output, 1
116
+ assert_xpath '//ul/li[1]//p[text() = "Item one, paragraph two"]', output, 1
117
+ end
118
+
119
+ test "Adjacent list continuation line attaches following block" do
120
+ input = <<-EOS
121
+ Lists
122
+ =====
123
+
124
+ * Item one, paragraph one
125
+ +
126
+ ....
127
+ Item one, literal block
128
+ ....
129
+ +
130
+ * Item two
131
+ EOS
132
+ output = render_string(input)
133
+ assert_xpath '//ul', output, 1
134
+ assert_xpath '//ul/li', output, 2
135
+ assert_xpath '//ul/li[1]/p', output, 1
136
+ assert_xpath '(//ul/li[1]/p/following-sibling::*)[1][@class = "literalblock"]', output, 1
137
+ end
138
+
139
+ test "Consecutive blocks in list continuation attach to list item" do
140
+ input = <<-EOS
141
+ Lists
142
+ =====
143
+
144
+ * Item one, paragraph one
145
+ +
146
+ ....
147
+ Item one, literal block
148
+ ....
149
+ +
150
+ ____
151
+ Item one, quote block
152
+ ____
153
+ +
154
+ * Item two
155
+ EOS
156
+ output = render_string(input)
157
+ assert_xpath '//ul', output, 1
158
+ assert_xpath '//ul/li', output, 2
159
+ assert_xpath '//ul/li[1]/p', output, 1
160
+ assert_xpath '(//ul/li[1]/p/following-sibling::*)[1][@class = "literalblock"]', output, 1
161
+ assert_xpath '(//ul/li[1]/p/following-sibling::*)[2][@class = "quoteblock"]', output, 1
162
+ end
163
+
164
+ # NOTE this differs from AsciiDoc behavior, but is more logical
165
+ test "Consecutive list continuation lines are folded" do
166
+ input = <<-EOS
167
+ Lists
168
+ =====
169
+
170
+ * Item one, paragraph one
171
+ +
172
+ +
173
+ Item one, paragraph two
174
+ +
175
+ +
176
+ * Item two
177
+ +
178
+ +
179
+ EOS
180
+ output = render_string(input)
181
+ assert_xpath '//ul', output, 1
182
+ assert_xpath '//ul/li', output, 2
183
+ assert_xpath '//ul/li[1]/p', output, 1
184
+ assert_xpath '//ul/li[1]//p', output, 2
185
+ assert_xpath '//ul/li[1]//p[text() = "Item one, paragraph one"]', output, 1
186
+ assert_xpath '//ul/li[1]//p[text() = "Item one, paragraph two"]', output, 1
187
+ end
188
+ end
189
+ end
190
+
191
+ context "Ordered lists (:olist)" do
192
+ context "Simple lists" do
193
+ test "dot elements with no blank lines" do
194
+ output = render_string("Blah\n====\n\n. Foo\n. Boo\n. Blech")
195
+ assert_xpath '//ol', output, 1
196
+ assert_xpath '//ol/li', output, 3
197
+ end
198
+ end
199
+ end
200
+
201
+ context "Labeled lists (:dlist)" do
202
+ context "Simple lists" do
203
+ test "single-line adjacent elements" do
204
+ output = render_string("term1:: def1\nterm2:: def2")
205
+ assert_xpath '//dl', output, 1
206
+ assert_xpath '//dl/dt', output, 2
207
+ assert_xpath '//dl/dt/following-sibling::dd', output, 2
208
+ assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
209
+ assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
210
+ assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
211
+ assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
212
+ end
213
+
214
+ test "single-line indented adjacent elements" do
215
+ output = render_string("term1:: def1\n term2:: def2")
216
+ assert_xpath '//dl', output, 1
217
+ assert_xpath '//dl/dt', output, 2
218
+ assert_xpath '//dl/dt/following-sibling::dd', output, 2
219
+ assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
220
+ assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
221
+ assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
222
+ assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
223
+ end
224
+
225
+ test "single-line elements separated by blank line" do
226
+ output = render_string("term1:: def1\n\nterm2:: def2")
227
+ assert_xpath '//dl', output, 1
228
+ assert_xpath '//dl/dt', output, 2
229
+ assert_xpath '//dl/dt/following-sibling::dd', output, 2
230
+ end
231
+
232
+ test "multi-line elements with paragraph content" do
233
+ output = render_string("term1::\ndef1\nterm2::\ndef2")
234
+ assert_xpath '//dl', output, 1
235
+ assert_xpath '//dl/dt', output, 2
236
+ assert_xpath '//dl/dt/following-sibling::dd', output, 2
237
+ assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
238
+ assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
239
+ assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
240
+ assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
241
+ end
242
+
243
+ test "multi-line elements with indented paragraph content" do
244
+ output = render_string("term1::\n def1\nterm2::\n def2")
245
+ assert_xpath '//dl', output, 1
246
+ assert_xpath '//dl/dt', output, 2
247
+ assert_xpath '//dl/dt/following-sibling::dd', output, 2
248
+ assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
249
+ assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
250
+ assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
251
+ assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
252
+ end
253
+
254
+ test "multi-line elements with blank line before paragraph content" do
255
+ output = render_string("term1::\n\ndef1\nterm2::\n\ndef2")
256
+ assert_xpath '//dl', output, 1
257
+ assert_xpath '//dl/dt', output, 2
258
+ assert_xpath '//dl/dt/following-sibling::dd', output, 2
259
+ assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
260
+ assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
261
+ assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
262
+ assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
263
+ end
264
+
265
+ test "multi-line elements with paragraph and literal content" do
266
+ output = render_string("term1::\n def1\n\n literal\nterm2::\n def2")
267
+ assert_xpath '//dl', output, 1
268
+ assert_xpath '//dl/dt', output, 2
269
+ assert_xpath '//dl/dt/following-sibling::dd', output, 2
270
+ assert_xpath '//dl/dt/following-sibling::dd//pre', output, 1
271
+ assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
272
+ assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
273
+ assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
274
+ assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
275
+ end
276
+
277
+ test "mixed single and multi-line adjacent elements" do
278
+ output = render_string("term1:: def1\nterm2::\ndef2")
279
+ assert_xpath '//dl', output, 1
280
+ assert_xpath '//dl/dt', output, 2
281
+ assert_xpath '//dl/dt/following-sibling::dd', output, 2
282
+ assert_xpath '(//dl/dt)[1][normalize-space(text()) = "term1"]', output, 1
283
+ assert_xpath '(//dl/dt)[1]/following-sibling::dd/p[text() = "def1"]', output, 1
284
+ assert_xpath '(//dl/dt)[2][normalize-space(text()) = "term2"]', output, 1
285
+ assert_xpath '(//dl/dt)[2]/following-sibling::dd/p[text() = "def2"]', output, 1
286
+ end
287
+
288
+ test "element with anchor" do
289
+ output = render_string("[[term1]]term1:: def1\n[[term2]]term2:: def2")
290
+ assert_xpath '//dl', output, 1
291
+ assert_xpath '//dl/dt', output, 2
292
+ assert_xpath '(//dl/dt)[1]/a[@id = "term1"]', output, 1
293
+ assert_xpath '(//dl/dt)[2]/a[@id = "term2"]', output, 1
294
+ end
295
+
296
+ test "missing space before term does not produce labeled list" do
297
+ output = render_string("term1::def1\nterm2::def2")
298
+ assert_xpath '//dl', output, 0
299
+ end
300
+ end
301
+
302
+ context "Nested lists" do
303
+ test "single-line adjacent nested elements" do
304
+ output = render_string("term1:: def1\nlabel1::: detail1\nterm2:: def2")
305
+ assert_xpath '//dl', output, 2
306
+ assert_xpath '//dl//dl', output, 1
307
+ assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
308
+ assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
309
+ assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
310
+ assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
311
+ assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
312
+ assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
313
+ end
314
+
315
+ test "single-line adjacent maximum nested elements" do
316
+ output = render_string("term1:: def1\nlabel1::: detail1\nname1:::: value1\nitem1;; price1\nterm2:: def2")
317
+ assert_xpath '//dl', output, 4
318
+ assert_xpath '//dl//dl//dl//dl', output, 1
319
+ end
320
+
321
+ test "single-line nested elements seperated by blank line at top level" do
322
+ output = render_string("term1:: def1\n\nlabel1::: detail1\n\nterm2:: def2")
323
+ assert_xpath '//dl', output, 2
324
+ assert_xpath '//dl//dl', output, 1
325
+ assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
326
+ assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
327
+ assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
328
+ assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
329
+ assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
330
+ assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
331
+ end
332
+
333
+ # FIXME test pending, haven't fixed lexer to allow blank line at nested level
334
+ #test "single-line nested elements seperated by blank line at nested level" do
335
+ # output = render_string("term1:: def1\nlabel1::: detail1\n\nlabel2::: detail2\nterm2:: def2")
336
+ # assert_xpath '//dl', output, 2
337
+ # assert_xpath '//dl//dl', output, 1
338
+ # assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
339
+ # assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
340
+ # assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
341
+ # assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
342
+ # assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
343
+ # assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
344
+ #end
345
+
346
+ test "single-line adjacent nested elements with alternate delimiters" do
347
+ output = render_string("term1:: def1\nlabel1;; detail1\nterm2:: def2")
348
+ assert_xpath '//dl', output, 2
349
+ assert_xpath '//dl//dl', output, 1
350
+ assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
351
+ assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
352
+ assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
353
+ assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
354
+ assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
355
+ assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
356
+ end
357
+
358
+ test "multi-line adjacent nested elements" do
359
+ output = render_string("term1::\ndef1\nlabel1:::\ndetail1\nterm2::\ndef2")
360
+ assert_xpath '//dl', output, 2
361
+ assert_xpath '//dl//dl', output, 1
362
+ assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
363
+ assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
364
+ assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
365
+ assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
366
+ assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
367
+ assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
368
+ end
369
+
370
+ # FIXME test pending, haven't fixed lexer to allow blank line at nested level
371
+ #test "multi-line nested elements seperated by blank line at nested level" do
372
+ # output = render_string("term1::\ndef1\nlabel1:::\n\ndetail1\nlabel2:::\ndetail2\n\nterm2:: def2")
373
+ # assert_xpath '//dl', output, 2
374
+ # assert_xpath '//dl//dl', output, 1
375
+ # assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
376
+ # assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
377
+ # assert_xpath '(//dl//dl/dt)[1][normalize-space(text()) = "label1"]', output, 1
378
+ # assert_xpath '(//dl//dl/dt)[1]/following-sibling::dd/p[text() = "detail1"]', output, 1
379
+ # assert_xpath '(//dl//dl/dt)[2][normalize-space(text()) = "label2"]', output, 1
380
+ # assert_xpath '(//dl//dl/dt)[2]/following-sibling::dd/p[text() = "detail2"]', output, 1
381
+ #end
382
+
383
+ test "mixed single and multi-line elements with indented nested elements" do
384
+ output = render_string("term1:: def1\n label1:::\n detail1\nterm2:: def2")
385
+ assert_xpath '//dl', output, 2
386
+ assert_xpath '//dl//dl', output, 1
387
+ assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
388
+ assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[text() = "def1"]', output, 1
389
+ assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
390
+ assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
391
+ assert_xpath '(//dl)[1]/dt[2][normalize-space(text()) = "term2"]', output, 1
392
+ assert_xpath '(//dl)[1]/dt[2]/following-sibling::dd/p[text() = "def2"]', output, 1
393
+ end
394
+
395
+ test "multi-line elements with first paragraph folded to text with adjacent nested element" do
396
+ output = render_string("term1:: def1\ncontinued\nlabel1:::\ndetail1")
397
+ assert_xpath '//dl', output, 2
398
+ assert_xpath '//dl//dl', output, 1
399
+ assert_xpath '(//dl)[1]/dt[1][normalize-space(text()) = "term1"]', output, 1
400
+ assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[starts-with(text(), "def1")]', output, 1
401
+ assert_xpath '(//dl)[1]/dt[1]/following-sibling::dd/p[contains(text(), "continued")]', output, 1
402
+ assert_xpath '//dl//dl/dt[normalize-space(text()) = "label1"]', output, 1
403
+ assert_xpath '//dl//dl/dt/following-sibling::dd/p[text() = "detail1"]', output, 1
404
+ end
405
+ end
406
+ end