asciidoctor 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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