asciidoctor 0.1.3 → 0.1.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +387 -0
- data/README.adoc +358 -348
- data/asciidoctor.gemspec +30 -9
- data/bin/asciidoctor +3 -0
- data/bin/asciidoctor-safe +3 -0
- data/compat/asciidoc.conf +76 -4
- data/lib/asciidoctor.rb +174 -79
- data/lib/asciidoctor/abstract_block.rb +131 -101
- data/lib/asciidoctor/abstract_node.rb +108 -26
- data/lib/asciidoctor/attribute_list.rb +1 -1
- data/lib/asciidoctor/backends/_stylesheets.rb +204 -62
- data/lib/asciidoctor/backends/base_template.rb +11 -22
- data/lib/asciidoctor/backends/docbook45.rb +158 -163
- data/lib/asciidoctor/backends/docbook5.rb +103 -0
- data/lib/asciidoctor/backends/html5.rb +662 -445
- data/lib/asciidoctor/block.rb +54 -44
- data/lib/asciidoctor/cli/invoker.rb +41 -20
- data/lib/asciidoctor/cli/options.rb +66 -20
- data/lib/asciidoctor/debug.rb +1 -1
- data/lib/asciidoctor/document.rb +265 -100
- data/lib/asciidoctor/extensions.rb +443 -0
- data/lib/asciidoctor/helpers.rb +38 -6
- data/lib/asciidoctor/inline.rb +5 -5
- data/lib/asciidoctor/lexer.rb +532 -250
- data/lib/asciidoctor/{list_item.rb → list.rb} +33 -13
- data/lib/asciidoctor/path_resolver.rb +28 -2
- data/lib/asciidoctor/reader.rb +814 -455
- data/lib/asciidoctor/renderer.rb +128 -42
- data/lib/asciidoctor/section.rb +55 -41
- data/lib/asciidoctor/substituters.rb +380 -107
- data/lib/asciidoctor/table.rb +40 -30
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +32 -96
- data/man/{asciidoctor.ad → asciidoctor.adoc} +57 -48
- data/test/attributes_test.rb +200 -27
- data/test/blocks_test.rb +361 -22
- data/test/document_test.rb +496 -81
- data/test/extensions_test.rb +448 -0
- data/test/fixtures/basic-docinfo-footer.html +6 -0
- data/test/fixtures/basic-docinfo-footer.xml +8 -0
- data/test/fixtures/basic-docinfo.xml +3 -3
- data/test/fixtures/basic.asciidoc +1 -0
- data/test/fixtures/child-include.adoc +5 -0
- data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +6 -0
- data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +1 -0
- data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +3 -0
- data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +5 -0
- data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +6 -0
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +3 -0
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +5 -0
- data/test/fixtures/docinfo-footer.html +1 -0
- data/test/fixtures/docinfo-footer.xml +9 -0
- data/test/fixtures/docinfo.xml +1 -0
- data/test/fixtures/grandchild-include.adoc +3 -0
- data/test/fixtures/parent-include-restricted.adoc +5 -0
- data/test/fixtures/parent-include.adoc +5 -0
- data/test/invoker_test.rb +82 -8
- data/test/lexer_test.rb +21 -3
- data/test/links_test.rb +34 -2
- data/test/lists_test.rb +304 -7
- data/test/options_test.rb +19 -3
- data/test/paragraphs_test.rb +13 -0
- data/test/paths_test.rb +22 -0
- data/test/preamble_test.rb +20 -0
- data/test/reader_test.rb +1096 -644
- data/test/renderer_test.rb +152 -12
- data/test/sections_test.rb +417 -76
- data/test/substitutions_test.rb +339 -138
- data/test/tables_test.rb +109 -4
- data/test/test_helper.rb +79 -13
- data/test/text_test.rb +111 -11
- metadata +54 -18
data/test/tables_test.rb
CHANGED
@@ -147,7 +147,6 @@ A | here| a | there
|
|
147
147
|
input = <<-EOS
|
148
148
|
|====
|
149
149
|
|first |second |third |fourth
|
150
|
-
|
151
150
|
|1 |2 |3
|
152
151
|
|4
|
153
152
|
|====
|
@@ -224,6 +223,90 @@ A | here| a | there
|
|
224
223
|
assert_css 'table > tbody > tr', output, 3
|
225
224
|
end
|
226
225
|
|
226
|
+
test 'table with implicit header row' do
|
227
|
+
input = <<-EOS
|
228
|
+
|===
|
229
|
+
|Column 1 |Column 2
|
230
|
+
|
231
|
+
|Data A1
|
232
|
+
|Data B1
|
233
|
+
|
234
|
+
|Data A2
|
235
|
+
|Data B2
|
236
|
+
|===
|
237
|
+
EOS
|
238
|
+
output = render_embedded_string input
|
239
|
+
assert_css 'table', output, 1
|
240
|
+
assert_css 'table > colgroup > col', output, 2
|
241
|
+
assert_css 'table > thead', output, 1
|
242
|
+
assert_css 'table > thead > tr', output, 1
|
243
|
+
assert_css 'table > thead > tr > th', output, 2
|
244
|
+
assert_css 'table > tbody', output, 1
|
245
|
+
assert_css 'table > tbody > tr', output, 2
|
246
|
+
end
|
247
|
+
|
248
|
+
test 'no implicit header row if second line not blank' do
|
249
|
+
input = <<-EOS
|
250
|
+
|===
|
251
|
+
|Column 1 |Column 2
|
252
|
+
|Data A1
|
253
|
+
|Data B1
|
254
|
+
|
255
|
+
|Data A2
|
256
|
+
|Data B2
|
257
|
+
|===
|
258
|
+
EOS
|
259
|
+
output = render_embedded_string input
|
260
|
+
assert_css 'table', output, 1
|
261
|
+
assert_css 'table > colgroup > col', output, 2
|
262
|
+
assert_css 'table > thead', output, 0
|
263
|
+
assert_css 'table > tbody', output, 1
|
264
|
+
assert_css 'table > tbody > tr', output, 3
|
265
|
+
end
|
266
|
+
|
267
|
+
test 'no implicit header row if first line blank' do
|
268
|
+
input = <<-EOS
|
269
|
+
|===
|
270
|
+
|
271
|
+
|Column 1 |Column 2
|
272
|
+
|
273
|
+
|Data A1
|
274
|
+
|Data B1
|
275
|
+
|
276
|
+
|Data A2
|
277
|
+
|Data B2
|
278
|
+
|
279
|
+
|===
|
280
|
+
EOS
|
281
|
+
output = render_embedded_string input
|
282
|
+
assert_css 'table', output, 1
|
283
|
+
assert_css 'table > colgroup > col', output, 2
|
284
|
+
assert_css 'table > thead', output, 0
|
285
|
+
assert_css 'table > tbody', output, 1
|
286
|
+
assert_css 'table > tbody > tr', output, 3
|
287
|
+
end
|
288
|
+
|
289
|
+
test 'no implicit header row if options is specified' do
|
290
|
+
input = <<-EOS
|
291
|
+
[options=""]
|
292
|
+
|===
|
293
|
+
|Column 1 |Column 2
|
294
|
+
|
295
|
+
|Data A1
|
296
|
+
|Data B1
|
297
|
+
|
298
|
+
|Data A2
|
299
|
+
|Data B2
|
300
|
+
|===
|
301
|
+
EOS
|
302
|
+
output = render_embedded_string input
|
303
|
+
assert_css 'table', output, 1
|
304
|
+
assert_css 'table > colgroup > col', output, 2
|
305
|
+
assert_css 'table > thead', output, 0
|
306
|
+
assert_css 'table > tbody', output, 1
|
307
|
+
assert_css 'table > tbody > tr', output, 3
|
308
|
+
end
|
309
|
+
|
227
310
|
test 'styles not applied to header cells' do
|
228
311
|
input = <<-EOS
|
229
312
|
[cols="1h,1s,1e",options="header,footer"]
|
@@ -292,6 +375,20 @@ I am getting in shape!
|
|
292
375
|
assert_xpath '/table/tbody/tr[3]/td[4]/p[2][text()="I am getting in shape!"]', output, 1
|
293
376
|
end
|
294
377
|
|
378
|
+
test 'percentages as column widths' do
|
379
|
+
input = <<-EOS
|
380
|
+
[width="100%", cols="<.^10%,<90%"]
|
381
|
+
|===
|
382
|
+
|column A |column B
|
383
|
+
|===
|
384
|
+
EOS
|
385
|
+
|
386
|
+
output = render_embedded_string input
|
387
|
+
assert_xpath '/table/colgroup/col', output, 2
|
388
|
+
assert_xpath '(/table/colgroup/col)[1][@style="width:10%;"]', output, 1
|
389
|
+
assert_xpath '(/table/colgroup/col)[2][@style="width:90%;"]', output, 1
|
390
|
+
end
|
391
|
+
|
295
392
|
test 'spans, alignments and styles' do
|
296
393
|
input = <<-EOS
|
297
394
|
[cols="e,m,^,>s",width="25%"]
|
@@ -452,19 +549,27 @@ output file name is used.
|
|
452
549
|
assert_css 'table > tbody > tr:nth-child(2) > td:nth-child(3) div.dlist', output, 1
|
453
550
|
end
|
454
551
|
|
552
|
+
test 'preprocessor directive on first line of AsciiDoc cell should be processed' do
|
553
|
+
input = <<-EOS
|
554
|
+
|===
|
555
|
+
a|include::fixtures/include-file.asciidoc[]
|
556
|
+
|===
|
557
|
+
EOS
|
558
|
+
|
559
|
+
output = render_embedded_string input, :safe => :safe, :base_dir => File.dirname(__FILE__)
|
560
|
+
assert_match(/included content/, output)
|
561
|
+
end
|
562
|
+
|
455
563
|
test 'nested table' do
|
456
564
|
input = <<-EOS
|
457
565
|
[cols="1,2a"]
|
458
566
|
|===
|
459
567
|
|Normal cell
|
460
|
-
|
461
568
|
|Cell with nested table
|
462
|
-
|
463
569
|
[cols="2,1"]
|
464
570
|
!===
|
465
571
|
!Nested table cell 1 !Nested table cell 2
|
466
572
|
!===
|
467
|
-
|
468
573
|
|===
|
469
574
|
EOS
|
470
575
|
output = render_embedded_string input
|
data/test/test_helper.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
if RUBY_VERSION < '1.9'
|
2
|
+
require 'rubygems'
|
3
|
+
end
|
1
4
|
require 'fileutils'
|
2
5
|
require 'pathname'
|
3
6
|
require 'test/unit'
|
@@ -5,10 +8,11 @@ require 'test/unit'
|
|
5
8
|
require "#{File.expand_path(File.dirname(__FILE__))}/../lib/asciidoctor.rb"
|
6
9
|
|
7
10
|
require 'nokogiri'
|
8
|
-
require 'pending'
|
9
11
|
|
10
12
|
ENV['SUPPRESS_DEBUG'] ||= 'true'
|
11
13
|
|
14
|
+
RE_XMLNS_ATTRIBUTE = / xmlns="[^"]+"/
|
15
|
+
|
12
16
|
class Test::Unit::TestCase
|
13
17
|
def windows?
|
14
18
|
RbConfig::CONFIG['host_os'] =~ /win|ming/
|
@@ -18,6 +22,15 @@ class Test::Unit::TestCase
|
|
18
22
|
"#{windows? ? File.expand_path(__FILE__).split('/').first : nil}/"
|
19
23
|
end
|
20
24
|
|
25
|
+
def empty_document options = {}
|
26
|
+
Asciidoctor::Document.new [], options
|
27
|
+
end
|
28
|
+
|
29
|
+
def empty_safe_document options = {}
|
30
|
+
options[:safe] = :safe
|
31
|
+
Asciidoctor::Document.new [], options
|
32
|
+
end
|
33
|
+
|
21
34
|
def sample_doc_path(name)
|
22
35
|
name = name.to_s
|
23
36
|
unless name.include?('.')
|
@@ -36,8 +49,7 @@ class Test::Unit::TestCase
|
|
36
49
|
end
|
37
50
|
|
38
51
|
def example_document(name, opts = {})
|
39
|
-
|
40
|
-
Asciidoctor::Document.new(File.readlines(sample_doc_path(name)), opts)
|
52
|
+
document_from_string File.read(sample_doc_path(name)), opts
|
41
53
|
end
|
42
54
|
|
43
55
|
def assert_difference(expression, difference = 1, message = nil, &block)
|
@@ -69,7 +81,8 @@ class Test::Unit::TestCase
|
|
69
81
|
doc = xmldoc_from_string content
|
70
82
|
case type
|
71
83
|
when :xpath
|
72
|
-
|
84
|
+
namespaces = doc.respond_to?(:root) ? doc.root.namespaces : {}
|
85
|
+
results = doc.xpath("#{path.sub('/', './')}", namespaces)
|
73
86
|
when :css
|
74
87
|
results = doc.css(path)
|
75
88
|
end
|
@@ -115,10 +128,14 @@ class Test::Unit::TestCase
|
|
115
128
|
end
|
116
129
|
|
117
130
|
def xmldoc_from_string(content)
|
118
|
-
|
119
|
-
if !
|
120
|
-
|
121
|
-
|
131
|
+
doctype_match = content.match(/\s*<!DOCTYPE (.*)/)
|
132
|
+
if !doctype_match
|
133
|
+
if content.match(RE_XMLNS_ATTRIBUTE)
|
134
|
+
doc = Nokogiri::XML::Document.parse(content)
|
135
|
+
else
|
136
|
+
doc = Nokogiri::HTML::DocumentFragment.parse(content)
|
137
|
+
end
|
138
|
+
elsif doctype_match[1].start_with? 'html'
|
122
139
|
doc = Nokogiri::HTML::Document.parse(content)
|
123
140
|
else
|
124
141
|
doc = Nokogiri::XML::Document.parse(content)
|
@@ -126,18 +143,26 @@ class Test::Unit::TestCase
|
|
126
143
|
end
|
127
144
|
|
128
145
|
def document_from_string(src, opts = {})
|
129
|
-
|
146
|
+
assign_default_test_options opts
|
130
147
|
Asciidoctor::Document.new(src.lines.entries, opts)
|
131
148
|
end
|
132
149
|
|
133
150
|
def block_from_string(src, opts = {})
|
134
151
|
opts[:header_footer] = false
|
135
|
-
doc =
|
152
|
+
doc = document_from_string src, opts
|
136
153
|
doc.blocks.first
|
137
154
|
end
|
138
155
|
|
139
156
|
def render_string(src, opts = {})
|
140
|
-
|
157
|
+
keep_namespaces = opts.delete(:keep_namespaces)
|
158
|
+
if keep_namespaces
|
159
|
+
document_from_string(src, opts).render
|
160
|
+
else
|
161
|
+
# this is required because nokogiri is ignorant
|
162
|
+
result = document_from_string(src, opts).render
|
163
|
+
result = result.sub(RE_XMLNS_ATTRIBUTE, '')
|
164
|
+
result
|
165
|
+
end
|
141
166
|
end
|
142
167
|
|
143
168
|
def render_embedded_string(src, opts = {})
|
@@ -150,11 +175,52 @@ class Test::Unit::TestCase
|
|
150
175
|
[Asciidoctor::Lexer.parse_header_metadata(reader), reader]
|
151
176
|
end
|
152
177
|
|
153
|
-
|
154
|
-
|
178
|
+
def assign_default_test_options(opts)
|
179
|
+
opts[:header_footer] = true unless opts.has_key?(:header_footer)
|
180
|
+
if opts[:header_footer]
|
181
|
+
# don't embed stylesheet unless test requests the default behavior
|
182
|
+
if opts.has_key? :linkcss_default
|
183
|
+
opts.delete(:linkcss_default)
|
184
|
+
else
|
185
|
+
opts[:attributes] ||= {}
|
186
|
+
opts[:attributes]['linkcss'] = ''
|
187
|
+
end
|
188
|
+
end
|
189
|
+
#opts[:template_dir] = File.join(File.dirname(__FILE__), '..', '..', 'asciidoctor-backends', 'slim')
|
190
|
+
nil
|
191
|
+
end
|
192
|
+
|
193
|
+
# Expand the character for an entity such as — into a glyph
|
194
|
+
# so it can be used to match in an XPath expression
|
195
|
+
#
|
196
|
+
# Examples
|
197
|
+
#
|
198
|
+
# expand_entity 60
|
199
|
+
# # => "<"
|
200
|
+
#
|
201
|
+
# Returns the String entity expanded to its equivalent UTF-8 glyph
|
155
202
|
def expand_entity(number)
|
156
203
|
[number].pack('U*')
|
157
204
|
end
|
205
|
+
alias :entity :expand_entity
|
206
|
+
|
207
|
+
def invoke_cli_with_filenames(argv = [], filenames = [], &block)
|
208
|
+
|
209
|
+
filepaths = Array.new
|
210
|
+
|
211
|
+
filenames.each { |filename|
|
212
|
+
if filenames.nil?|| ::Pathname.new(filename).absolute?
|
213
|
+
filepaths.push(filename)
|
214
|
+
else
|
215
|
+
filepaths.push(File.join(File.dirname(__FILE__), 'fixtures', filename))
|
216
|
+
end
|
217
|
+
}
|
218
|
+
|
219
|
+
invoker = Asciidoctor::Cli::Invoker.new(argv + filepaths)
|
220
|
+
|
221
|
+
invoker.invoke!(&block)
|
222
|
+
invoker
|
223
|
+
end
|
158
224
|
|
159
225
|
def invoke_cli_to_buffer(argv = [], filename = 'sample.asciidoc', &block)
|
160
226
|
invoke_cli(argv, filename, [StringIO.new, StringIO.new], &block)
|
data/test/text_test.rb
CHANGED
@@ -16,8 +16,8 @@ context "Text" do
|
|
16
16
|
|
17
17
|
test "proper encoding to handle utf8 characters in document using docbook backend" do
|
18
18
|
output = example_document(:encoding, :attributes => {'backend' => 'docbook'}).render
|
19
|
-
assert_xpath '//simpara', output, 4
|
20
|
-
assert_xpath '//ulink', output, 1
|
19
|
+
assert_xpath '//xmlns:simpara', output, 4
|
20
|
+
assert_xpath '//xmlns:ulink', output, 1
|
21
21
|
end
|
22
22
|
|
23
23
|
test "proper encoding to handle utf8 characters in embedded document using docbook backend" do
|
@@ -27,22 +27,22 @@ context "Text" do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# NOTE this test ensures we have the encoding line on block templates too
|
30
|
-
test
|
30
|
+
test 'proper encoding to handle utf8 characters in arbitrary block' do
|
31
31
|
input = []
|
32
32
|
input << "[verse]\n"
|
33
33
|
input.concat(File.readlines(sample_doc_path(:encoding)))
|
34
|
-
doc =
|
35
|
-
reader = Asciidoctor::
|
34
|
+
doc = empty_document
|
35
|
+
reader = Asciidoctor::PreprocessorReader.new doc, input
|
36
36
|
block = Asciidoctor::Lexer.next_block(reader, doc)
|
37
37
|
assert_xpath '//pre', block.render.gsub(/^\s*\n/, ''), 1
|
38
38
|
end
|
39
39
|
|
40
|
-
test
|
40
|
+
test 'proper encoding to handle utf8 characters from included file' do
|
41
41
|
input = <<-EOS
|
42
42
|
include::fixtures/encoding.asciidoc[tags=romé]
|
43
43
|
EOS
|
44
|
-
doc =
|
45
|
-
reader = Asciidoctor::
|
44
|
+
doc = empty_safe_document :base_dir => File.expand_path(File.dirname(__FILE__))
|
45
|
+
reader = Asciidoctor::PreprocessorReader.new doc, input
|
46
46
|
block = Asciidoctor::Lexer.next_block(reader, doc)
|
47
47
|
output = block.render
|
48
48
|
assert_css '.paragraph', output, 1
|
@@ -63,9 +63,109 @@ include::fixtures/encoding.asciidoc[tags=romé]
|
|
63
63
|
assert_match(/‘The New Yorker.’/, rendered)
|
64
64
|
end
|
65
65
|
|
66
|
-
test
|
67
|
-
|
68
|
-
|
66
|
+
test 'horizontal rule' do
|
67
|
+
input = <<-EOS
|
68
|
+
This line is separated by a horizontal rule...
|
69
|
+
|
70
|
+
'''
|
71
|
+
|
72
|
+
...from this line.
|
73
|
+
EOS
|
74
|
+
output = render_embedded_string input
|
75
|
+
assert_xpath "//hr", output, 1
|
76
|
+
assert_xpath "/*[@class='paragraph']", output, 2
|
77
|
+
assert_xpath "(/*[@class='paragraph'])[1]/following-sibling::hr", output, 1
|
78
|
+
assert_xpath "/hr/following-sibling::*[@class='paragraph']", output, 1
|
79
|
+
end
|
80
|
+
|
81
|
+
test 'markdown horizontal rules' do
|
82
|
+
variants = [
|
83
|
+
'---',
|
84
|
+
'- - -',
|
85
|
+
'***',
|
86
|
+
'* * *',
|
87
|
+
'___',
|
88
|
+
'_ _ _'
|
89
|
+
]
|
90
|
+
|
91
|
+
offsets = [
|
92
|
+
'',
|
93
|
+
' ',
|
94
|
+
' ',
|
95
|
+
' '
|
96
|
+
]
|
97
|
+
|
98
|
+
variants.each do |variant|
|
99
|
+
offsets.each do |offset|
|
100
|
+
input = <<-EOS
|
101
|
+
This line is separated by a horizontal rule...
|
102
|
+
|
103
|
+
#{offset}#{variant}
|
104
|
+
|
105
|
+
...from this line.
|
106
|
+
EOS
|
107
|
+
output = render_embedded_string input
|
108
|
+
assert_xpath "//hr", output, 1
|
109
|
+
assert_xpath "/*[@class='paragraph']", output, 2
|
110
|
+
assert_xpath "(/*[@class='paragraph'])[1]/following-sibling::hr", output, 1
|
111
|
+
assert_xpath "/hr/following-sibling::*[@class='paragraph']", output, 1
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
test 'markdown horizontal rules negative case' do
|
117
|
+
|
118
|
+
bad_variants = [
|
119
|
+
'- - - -',
|
120
|
+
'* * * *',
|
121
|
+
'_ _ _ _'
|
122
|
+
]
|
123
|
+
|
124
|
+
good_offsets = [
|
125
|
+
'',
|
126
|
+
' ',
|
127
|
+
' ',
|
128
|
+
' '
|
129
|
+
]
|
130
|
+
|
131
|
+
bad_variants.each do |variant|
|
132
|
+
good_offsets.each do |offset|
|
133
|
+
input = <<-EOS
|
134
|
+
This line is separated something that is not a horizontal rule...
|
135
|
+
|
136
|
+
#{offset}#{variant}
|
137
|
+
|
138
|
+
...from this line.
|
139
|
+
EOS
|
140
|
+
output = render_embedded_string input
|
141
|
+
assert_xpath '//hr', output, 0
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
good_variants = [
|
146
|
+
'- - -',
|
147
|
+
'* * *',
|
148
|
+
'_ _ _'
|
149
|
+
]
|
150
|
+
|
151
|
+
bad_offsets = [
|
152
|
+
"\t",
|
153
|
+
' '
|
154
|
+
]
|
155
|
+
|
156
|
+
good_variants.each do |variant|
|
157
|
+
bad_offsets.each do |offset|
|
158
|
+
input = <<-EOS
|
159
|
+
This line is separated something that is not a horizontal rule...
|
160
|
+
|
161
|
+
#{offset}#{variant}
|
162
|
+
|
163
|
+
...from this line.
|
164
|
+
EOS
|
165
|
+
output = render_embedded_string input
|
166
|
+
assert_xpath '//hr', output, 0
|
167
|
+
end
|
168
|
+
end
|
69
169
|
end
|
70
170
|
|
71
171
|
test "emphasized text" do
|