asciidoctor 1.5.5 → 1.5.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.

Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +216 -1
  3. data/CONTRIBUTING.adoc +2 -2
  4. data/Gemfile +20 -1
  5. data/LICENSE.adoc +1 -1
  6. data/README-fr.adoc +4 -3
  7. data/README-jp.adoc +11 -10
  8. data/README-zh_CN.adoc +4 -3
  9. data/README.adoc +17 -202
  10. data/Rakefile +41 -25
  11. data/asciidoctor.gemspec +9 -10
  12. data/data/locale/attributes.adoc +216 -34
  13. data/data/stylesheets/asciidoctor-default.css +23 -16
  14. data/features/step_definitions.rb +15 -19
  15. data/features/xref.feature +584 -20
  16. data/lib/asciidoctor.rb +292 -278
  17. data/lib/asciidoctor/abstract_block.rb +155 -94
  18. data/lib/asciidoctor/abstract_node.rb +108 -94
  19. data/lib/asciidoctor/attribute_list.rb +30 -22
  20. data/lib/asciidoctor/block.rb +7 -7
  21. data/lib/asciidoctor/cli/invoker.rb +47 -34
  22. data/lib/asciidoctor/cli/options.rb +22 -11
  23. data/lib/asciidoctor/converter.rb +3 -3
  24. data/lib/asciidoctor/converter/base.rb +2 -2
  25. data/lib/asciidoctor/converter/composite.rb +1 -1
  26. data/lib/asciidoctor/converter/docbook45.rb +2 -2
  27. data/lib/asciidoctor/converter/docbook5.rb +132 -87
  28. data/lib/asciidoctor/converter/factory.rb +0 -1
  29. data/lib/asciidoctor/converter/html5.rb +116 -98
  30. data/lib/asciidoctor/converter/manpage.rb +51 -52
  31. data/lib/asciidoctor/converter/template.rb +47 -36
  32. data/lib/asciidoctor/core_ext.rb +8 -2
  33. data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +4 -0
  34. data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +6 -0
  35. data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +5 -0
  36. data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +1 -1
  37. data/lib/asciidoctor/core_ext/1.8.7/string/{limit.rb → limit_bytesize.rb} +7 -6
  38. data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +6 -0
  39. data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +1 -1
  40. data/lib/asciidoctor/core_ext/nil_or_empty.rb +5 -5
  41. data/lib/asciidoctor/core_ext/regexp/is_match.rb +3 -0
  42. data/lib/asciidoctor/core_ext/string/{limit.rb → limit_bytesize.rb} +2 -2
  43. data/lib/asciidoctor/document.rb +216 -213
  44. data/lib/asciidoctor/extensions.rb +318 -185
  45. data/lib/asciidoctor/helpers.rb +35 -35
  46. data/lib/asciidoctor/inline.rb +32 -1
  47. data/lib/asciidoctor/list.rb +22 -6
  48. data/lib/asciidoctor/parser.rb +1008 -1038
  49. data/lib/asciidoctor/path_resolver.rb +46 -50
  50. data/lib/asciidoctor/reader.rb +275 -251
  51. data/lib/asciidoctor/section.rb +86 -58
  52. data/lib/asciidoctor/stylesheets.rb +6 -6
  53. data/lib/asciidoctor/substitutors.rb +567 -649
  54. data/lib/asciidoctor/table.rb +163 -108
  55. data/lib/asciidoctor/version.rb +1 -1
  56. data/man/asciidoctor.1 +18 -16
  57. data/man/asciidoctor.adoc +15 -13
  58. data/test/attributes_test.rb +138 -22
  59. data/test/blocks_test.rb +377 -97
  60. data/test/converter_test.rb +13 -0
  61. data/test/document_test.rb +244 -34
  62. data/test/extensions_test.rb +409 -42
  63. data/test/fixtures/asciidoc_index.txt +521 -0
  64. data/test/fixtures/basic-docinfo-footer.html +6 -0
  65. data/test/fixtures/basic-docinfo-footer.xml +8 -0
  66. data/test/fixtures/basic-docinfo.html +1 -0
  67. data/test/fixtures/basic-docinfo.xml +4 -0
  68. data/test/fixtures/basic.asciidoc +5 -0
  69. data/test/fixtures/chapter-a.adoc +3 -0
  70. data/test/fixtures/child-include.adoc +5 -0
  71. data/test/fixtures/circle.svg +9 -0
  72. data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
  73. data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +6 -0
  74. data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +1 -0
  75. data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +3 -0
  76. data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +5 -0
  77. data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +6 -0
  78. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +3 -0
  79. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +5 -0
  80. data/test/fixtures/custom-docinfodir/basic-docinfo.html +1 -0
  81. data/test/fixtures/custom-docinfodir/docinfo.html +1 -0
  82. data/test/fixtures/docinfo-footer.html +1 -0
  83. data/test/fixtures/docinfo-footer.xml +9 -0
  84. data/test/fixtures/docinfo.html +1 -0
  85. data/test/fixtures/docinfo.xml +3 -0
  86. data/test/fixtures/dot.gif +0 -0
  87. data/test/fixtures/encoding.asciidoc +13 -0
  88. data/test/fixtures/grandchild-include.adoc +3 -0
  89. data/test/fixtures/hello-asciidoctor.pdf +69 -0
  90. data/test/fixtures/include-file.asciidoc +24 -0
  91. data/test/fixtures/include-file.ml +3 -0
  92. data/test/fixtures/include-file.xml +5 -0
  93. data/test/fixtures/master.adoc +5 -0
  94. data/test/fixtures/mismatched-end-tag.adoc +7 -0
  95. data/test/fixtures/parent-include-restricted.adoc +5 -0
  96. data/test/fixtures/parent-include.adoc +5 -0
  97. data/test/fixtures/sample.asciidoc +26 -0
  98. data/test/fixtures/stylesheets/custom.css +3 -0
  99. data/test/fixtures/subs-docinfo.html +2 -0
  100. data/test/fixtures/subs.adoc +7 -0
  101. data/test/fixtures/tagged-class-enclosed.rb +26 -0
  102. data/test/fixtures/tagged-class.rb +23 -0
  103. data/test/fixtures/tip.gif +0 -0
  104. data/test/invoker_test.rb +82 -4
  105. data/test/links_test.rb +312 -37
  106. data/test/lists_test.rb +204 -25
  107. data/test/manpage_test.rb +191 -4
  108. data/test/options_test.rb +18 -1
  109. data/test/paragraphs_test.rb +32 -7
  110. data/test/parser_test.rb +150 -30
  111. data/test/paths_test.rb +47 -13
  112. data/test/preamble_test.rb +1 -1
  113. data/test/reader_test.rb +366 -126
  114. data/test/sections_test.rb +203 -56
  115. data/test/substitutions_test.rb +339 -131
  116. data/test/tables_test.rb +315 -15
  117. data/test/test_helper.rb +400 -0
  118. data/test/text_test.rb +5 -5
  119. metadata +110 -22
@@ -6,7 +6,7 @@ end
6
6
  require 'asciidoctor/cli/options'
7
7
 
8
8
  context 'Options' do
9
- test 'should return error code 0 when help flag is present' do
9
+ test 'should print usage and return error code 0 when help flag is present' do
10
10
  redirect_streams do |stdout, stderr|
11
11
  exitval = Asciidoctor::Cli::Options.parse!(%w(-h))
12
12
  assert_equal 0, exitval
@@ -14,6 +14,23 @@ context 'Options' do
14
14
  end
15
15
  end
16
16
 
17
+ test 'should print usage and return error code 0 when help flag is unknown' do
18
+ exitval, output = redirect_streams do |out, _|
19
+ [Asciidoctor::Cli::Options.parse!(%w(-h unknown)), out.string]
20
+ end
21
+ assert_equal 0, exitval
22
+ assert_match(/^Usage:/, output)
23
+ end
24
+
25
+ test 'should dump man page and return error code 0 when help topic is manpage' do
26
+ exitval, output = redirect_streams do |out, _|
27
+ [Asciidoctor::Cli::Options.parse!(%w(-h manpage)), out.string]
28
+ end
29
+ assert_equal 0, exitval
30
+ assert_includes output, 'Manual: Asciidoctor Manual'
31
+ assert_includes output, '.TH "ASCIIDOCTOR"'
32
+ end
33
+
17
34
  test 'should return error code 1 when invalid option present' do
18
35
  redirect_streams do |stdout, stderr|
19
36
  exitval = Asciidoctor::Cli::Options.parse!(%w(--foobar))
@@ -156,29 +156,29 @@ Note that multi-entry terms generate separate index entries.
156
156
 
157
157
  output = render_embedded_string input, :attributes => {'backend' => 'docbook45'}
158
158
  assert_xpath '/simpara', output, 1
159
- term1 = (xmlnodes_at_xpath '(//indexterm)[1]', output, 1).first
159
+ term1 = xmlnodes_at_xpath '(//indexterm)[1]', output, 1
160
160
  assert_equal '<indexterm><primary>tigers</primary></indexterm>', term1.to_s
161
161
  assert term1.next.content.start_with?('tigers')
162
162
 
163
- term2 = (xmlnodes_at_xpath '(//indexterm)[2]', output, 1).first
163
+ term2 = xmlnodes_at_xpath '(//indexterm)[2]', output, 1
164
164
  term2_elements = term2.elements
165
165
  assert_equal 3, term2_elements.size
166
166
  assert_equal '<primary>Big cats</primary>', term2_elements[0].to_s
167
167
  assert_equal '<secondary>Tigers</secondary>', term2_elements[1].to_s
168
168
  assert_equal '<tertiary>Siberian Tiger</tertiary>', term2_elements[2].to_s
169
169
 
170
- term3 = (xmlnodes_at_xpath '(//indexterm)[3]', output, 1).first
170
+ term3 = xmlnodes_at_xpath '(//indexterm)[3]', output, 1
171
171
  term3_elements = term3.elements
172
172
  assert_equal 2, term3_elements.size
173
173
  assert_equal '<primary>Tigers</primary>', term3_elements[0].to_s
174
174
  assert_equal '<secondary>Siberian Tiger</secondary>', term3_elements[1].to_s
175
175
 
176
- term4 = (xmlnodes_at_xpath '(//indexterm)[4]', output, 1).first
176
+ term4 = xmlnodes_at_xpath '(//indexterm)[4]', output, 1
177
177
  term4_elements = term4.elements
178
178
  assert_equal 1, term4_elements.size
179
179
  assert_equal '<primary>Siberian Tiger</primary>', term4_elements[0].to_s
180
180
 
181
- term5 = (xmlnodes_at_xpath '(//indexterm)[5]', output, 1).first
181
+ term5 = xmlnodes_at_xpath '(//indexterm)[5]', output, 1
182
182
  assert_equal '<indexterm><primary>Linux</primary></indexterm>', term5.to_s
183
183
  assert term5.next.content.start_with?('Linux')
184
184
 
@@ -418,6 +418,26 @@ Content goes here
418
418
  assert_xpath "//*[@class='sidebarblock']//p", result, 1
419
419
  end
420
420
 
421
+ test 'should process preprocessor conditional in paragrpah content' do
422
+ input = <<-EOS
423
+ ifdef::asciidoctor-version[]
424
+ [sidebar]
425
+ First line of sidebar.
426
+ ifdef::backend[The backend is {backend}.]
427
+ Last line of sidebar.
428
+ endif::[]
429
+ EOS
430
+
431
+ result = render_embedded_string input
432
+ assert_equal %(<div class="sidebarblock">
433
+ <div class="content">
434
+ First line of sidebar.
435
+ The backend is html5.
436
+ Last line of sidebar.
437
+ </div>
438
+ </div>), result
439
+ end
440
+
421
441
  context 'Styled Paragraphs' do
422
442
  test 'should wrap text in simpara for styled paragraphs when rendered to DocBook' do
423
443
  input = <<-EOS
@@ -514,10 +534,15 @@ Wise words from a wise person.
514
534
  assert_equal '<a href="http://asciidoc.org">AsciiDoc</a> is a <em>lightweight</em> markup language&#8230;&#8203;', output
515
535
  end
516
536
 
517
- test 'should output nil if first block is not a paragraph' do
537
+ test 'should output nil and warn if first block is not a paragraph' do
518
538
  input = '* bullet'
519
- output = render_string input, :doctype => 'inline'
539
+ output = nil
540
+ warnings = redirect_streams do |_, err|
541
+ output = render_string input, :doctype => 'inline'
542
+ err.string
543
+ end
520
544
  assert output.nil?
545
+ assert_includes warnings, 'no inline candidate'
521
546
  end
522
547
  end
523
548
  end
@@ -17,6 +17,85 @@ context "Parser" do
17
17
  assert_equal 'foo3-bar', Asciidoctor::Parser.sanitize_attribute_name("Foo 3^ # - Bar[")
18
18
  end
19
19
 
20
+ test 'store attribute with value' do
21
+ attr_name, attr_value = Asciidoctor::Parser.store_attribute 'foo', 'bar'
22
+ assert_equal 'foo', attr_name
23
+ assert_equal 'bar', attr_value
24
+ end
25
+
26
+ test 'store attribute with negated value' do
27
+ { 'foo!' => nil, '!foo' => nil, 'foo' => nil }.each do |name, value|
28
+ attr_name, attr_value = Asciidoctor::Parser.store_attribute name, value
29
+ assert_equal name.sub('!', ''), attr_name
30
+ assert_nil attr_value
31
+ end
32
+ end
33
+
34
+ test 'store accessible attribute on document with value' do
35
+ doc = empty_document
36
+ doc.set_attribute 'foo', 'baz'
37
+ attrs = {}
38
+ attr_name, attr_value = Asciidoctor::Parser.store_attribute 'foo', 'bar', doc, attrs
39
+ assert_equal 'foo', attr_name
40
+ assert_equal 'bar', attr_value
41
+ assert_equal 'bar', (doc.attr 'foo')
42
+ assert attrs.key?(:attribute_entries)
43
+ assert_equal 1, attrs[:attribute_entries].size
44
+ assert_equal 'foo', attrs[:attribute_entries][0].name
45
+ assert_equal 'bar', attrs[:attribute_entries][0].value
46
+ end
47
+
48
+ test 'store accessible attribute on document with value that contains attribute reference' do
49
+ doc = empty_document
50
+ doc.set_attribute 'foo', 'baz'
51
+ doc.set_attribute 'release', 'ultramega'
52
+ attrs = {}
53
+ attr_name, attr_value = Asciidoctor::Parser.store_attribute 'foo', '{release}', doc, attrs
54
+ assert_equal 'foo', attr_name
55
+ assert_equal 'ultramega', attr_value
56
+ assert_equal 'ultramega', (doc.attr 'foo')
57
+ assert attrs.key?(:attribute_entries)
58
+ assert_equal 1, attrs[:attribute_entries].size
59
+ assert_equal 'foo', attrs[:attribute_entries][0].name
60
+ assert_equal 'ultramega', attrs[:attribute_entries][0].value
61
+ end
62
+
63
+ test 'store inaccessible attribute on document with value' do
64
+ doc = empty_document :attributes => { 'foo' => 'baz' }
65
+ attrs = {}
66
+ attr_name, attr_value = Asciidoctor::Parser.store_attribute 'foo', 'bar', doc, attrs
67
+ assert_equal 'foo', attr_name
68
+ assert_equal 'bar', attr_value
69
+ assert_equal 'baz', (doc.attr 'foo')
70
+ refute attrs.key?(:attribute_entries)
71
+ end
72
+
73
+ test 'store accessible attribute on document with negated value' do
74
+ { 'foo!' => nil, '!foo' => nil, 'foo' => nil }.each do |name, value|
75
+ doc = empty_document
76
+ doc.set_attribute 'foo', 'baz'
77
+ attrs = {}
78
+ attr_name, attr_value = Asciidoctor::Parser.store_attribute name, value, doc, attrs
79
+ assert_equal name.sub('!', ''), attr_name
80
+ assert_nil attr_value
81
+ assert attrs.key?(:attribute_entries)
82
+ assert_equal 1, attrs[:attribute_entries].size
83
+ assert_equal 'foo', attrs[:attribute_entries][0].name
84
+ assert_nil attrs[:attribute_entries][0].value
85
+ end
86
+ end
87
+
88
+ test 'store inaccessible attribute on document with negated value' do
89
+ { 'foo!' => nil, '!foo' => nil, 'foo' => nil }.each do |name, value|
90
+ doc = empty_document :attributes => { 'foo' => 'baz' }
91
+ attrs = {}
92
+ attr_name, attr_value = Asciidoctor::Parser.store_attribute name, value, doc, attrs
93
+ assert_equal name.sub('!', ''), attr_name
94
+ assert_nil attr_value
95
+ refute attrs.key?(:attribute_entries)
96
+ end
97
+ end
98
+
20
99
  test "collect unnamed attribute" do
21
100
  attributes = {}
22
101
  line = 'quote'
@@ -172,7 +251,7 @@ context "Parser" do
172
251
  test "collect options attribute" do
173
252
  attributes = {}
174
253
  line = "quote, options='opt1,opt2 , opt3'"
175
- expected = {1 => 'quote', 'options' => 'opt1,opt2 , opt3', 'opt1-option' => '', 'opt2-option' => '', 'opt3-option' => ''}
254
+ expected = {1 => 'quote', 'options' => 'opt1,opt2,opt3', 'opt1-option' => '', 'opt2-option' => '', 'opt3-option' => ''}
176
255
  Asciidoctor::AttributeList.new(line).parse_into(attributes)
177
256
  assert_equal expected, attributes
178
257
  end
@@ -180,7 +259,7 @@ context "Parser" do
180
259
  test "collect opts attribute as options" do
181
260
  attributes = {}
182
261
  line = "quote, opts='opt1,opt2 , opt3'"
183
- expected = {1 => 'quote', 'options' => 'opt1,opt2 , opt3', 'opt1-option' => '', 'opt2-option' => '', 'opt3-option' => ''}
262
+ expected = {1 => 'quote', 'options' => 'opt1,opt2,opt3', 'opt1-option' => '', 'opt2-option' => '', 'opt3-option' => ''}
184
263
  Asciidoctor::AttributeList.new(line).parse_into(attributes)
185
264
  assert_equal expected, attributes
186
265
  end
@@ -202,9 +281,8 @@ context "Parser" do
202
281
 
203
282
  test 'parse style attribute with id and role' do
204
283
  attributes = {1 => 'style#id.role'}
205
- style, original_style = Asciidoctor::Parser.parse_style_attribute(attributes)
284
+ style = Asciidoctor::Parser.parse_style_attribute(attributes)
206
285
  assert_equal 'style', style
207
- assert_nil original_style
208
286
  assert_equal 'style', attributes['style']
209
287
  assert_equal 'id', attributes['id']
210
288
  assert_equal 'role', attributes['role']
@@ -213,9 +291,8 @@ context "Parser" do
213
291
 
214
292
  test 'parse style attribute with style, role, id and option' do
215
293
  attributes = {1 => 'style.role#id%fragment'}
216
- style, original_style = Asciidoctor::Parser.parse_style_attribute(attributes)
294
+ style = Asciidoctor::Parser.parse_style_attribute(attributes)
217
295
  assert_equal 'style', style
218
- assert_nil original_style
219
296
  assert_equal 'style', attributes['style']
220
297
  assert_equal 'id', attributes['id']
221
298
  assert_equal 'role', attributes['role']
@@ -226,9 +303,8 @@ context "Parser" do
226
303
 
227
304
  test 'parse style attribute with style, id and multiple roles' do
228
305
  attributes = {1 => 'style#id.role1.role2'}
229
- style, original_style = Asciidoctor::Parser.parse_style_attribute(attributes)
306
+ style = Asciidoctor::Parser.parse_style_attribute(attributes)
230
307
  assert_equal 'style', style
231
- assert_nil original_style
232
308
  assert_equal 'style', attributes['style']
233
309
  assert_equal 'id', attributes['id']
234
310
  assert_equal 'role1 role2', attributes['role']
@@ -237,9 +313,8 @@ context "Parser" do
237
313
 
238
314
  test 'parse style attribute with style, multiple roles and id' do
239
315
  attributes = {1 => 'style.role1.role2#id'}
240
- style, original_style = Asciidoctor::Parser.parse_style_attribute(attributes)
316
+ style = Asciidoctor::Parser.parse_style_attribute(attributes)
241
317
  assert_equal 'style', style
242
- assert_nil original_style
243
318
  assert_equal 'style', attributes['style']
244
319
  assert_equal 'id', attributes['id']
245
320
  assert_equal 'role1 role2', attributes['role']
@@ -248,18 +323,16 @@ context "Parser" do
248
323
 
249
324
  test 'parse style attribute with positional and original style' do
250
325
  attributes = {1 => 'new_style', 'style' => 'original_style'}
251
- style, original_style = Asciidoctor::Parser.parse_style_attribute(attributes)
326
+ style = Asciidoctor::Parser.parse_style_attribute(attributes)
252
327
  assert_equal 'new_style', style
253
- assert_equal 'original_style', original_style
254
328
  assert_equal 'new_style', attributes['style']
255
329
  assert_equal 'new_style', attributes[1]
256
330
  end
257
331
 
258
332
  test 'parse style attribute with id and role only' do
259
333
  attributes = {1 => '#id.role'}
260
- style, original_style = Asciidoctor::Parser.parse_style_attribute(attributes)
334
+ style = Asciidoctor::Parser.parse_style_attribute(attributes)
261
335
  assert_nil style
262
- assert_nil original_style
263
336
  assert_equal 'id', attributes['id']
264
337
  assert_equal 'role', attributes['role']
265
338
  assert_equal '#id.role', attributes[1]
@@ -267,9 +340,8 @@ context "Parser" do
267
340
 
268
341
  test 'parse empty style attribute' do
269
342
  attributes = {1 => nil}
270
- style, original_style = Asciidoctor::Parser.parse_style_attribute(attributes)
343
+ style = Asciidoctor::Parser.parse_style_attribute(attributes)
271
344
  assert_nil style
272
- assert_nil original_style
273
345
  assert_nil attributes['id']
274
346
  assert_nil attributes['role']
275
347
  assert_nil attributes[1]
@@ -277,9 +349,8 @@ context "Parser" do
277
349
 
278
350
  test 'parse style attribute with option should preserve existing options' do
279
351
  attributes = {1 => '%header', 'options' => 'footer', 'footer-option' => ''}
280
- style, original_style = Asciidoctor::Parser.parse_style_attribute(attributes)
352
+ style = Asciidoctor::Parser.parse_style_attribute(attributes)
281
353
  assert_nil style
282
- assert_nil original_style
283
354
  assert_equal 'header,footer', attributes['options']
284
355
  assert_equal '', attributes['header-option']
285
356
  assert_equal '', attributes['footer-option']
@@ -455,6 +526,56 @@ context "Parser" do
455
526
  assert_equal 'John Smith', metadata['author_2']
456
527
  end
457
528
 
529
+ test 'parse name with more than 3 parts in author attribute' do
530
+ doc = empty_document
531
+ parse_header_metadata ':author: Leroy Harold Scherer, Jr.', doc
532
+ assert_equal 'Leroy Harold Scherer, Jr.', doc.attributes['author']
533
+ assert_equal 'Leroy', doc.attributes['firstname']
534
+ assert_equal 'Harold', doc.attributes['middlename']
535
+ assert_equal 'Scherer, Jr.', doc.attributes['lastname']
536
+ end
537
+
538
+ test 'does not drop name joiner when using multiple authors' do
539
+ input = <<-EOS
540
+ Kismet Chameleon; Lazarus het_Draeke
541
+ EOS
542
+ doc = empty_document
543
+ parse_header_metadata input, doc
544
+ assert_equal 2, doc.attributes['authorcount']
545
+ assert_equal 'Kismet Chameleon, Lazarus het Draeke', doc.attributes['authors']
546
+ assert_equal 'Kismet Chameleon', doc.attributes['author_1']
547
+ assert_equal 'Lazarus het Draeke', doc.attributes['author_2']
548
+ assert_equal 'het Draeke', doc.attributes['lastname_2']
549
+ end
550
+
551
+ test 'allows authors to be overridden using explicit author attributes' do
552
+ input = <<-EOS
553
+ Kismet Chameleon; Johnny Bravo; Lazarus het_Draeke
554
+ :author_2: Danger Mouse
555
+ EOS
556
+ doc = empty_document
557
+ parse_header_metadata input, doc
558
+ assert_equal 3, doc.attributes['authorcount']
559
+ assert_equal 'Kismet Chameleon, Danger Mouse, Lazarus het Draeke', doc.attributes['authors']
560
+ assert_equal 'Kismet Chameleon', doc.attributes['author_1']
561
+ assert_equal 'Danger Mouse', doc.attributes['author_2']
562
+ assert_equal 'Lazarus het Draeke', doc.attributes['author_3']
563
+ assert_equal 'het Draeke', doc.attributes['lastname_3']
564
+ end
565
+
566
+ test 'removes formatting before partitioning author defined using author attribute' do
567
+ input = <<-EOS
568
+ :author: pass:n[http://example.org/community/team.html[Ze_**Project** team]]
569
+ EOS
570
+
571
+ doc = empty_document
572
+ parse_header_metadata input, doc
573
+ assert_equal 1, doc.attributes['authorcount']
574
+ assert_equal '<a href="http://example.org/community/team.html">Ze <strong>Project</strong> team</a>', doc.attributes['authors']
575
+ assert_equal 'Ze Project', doc.attributes['firstname']
576
+ assert_equal 'team', doc.attributes['lastname']
577
+ end
578
+
458
579
  test "parse rev number date remark" do
459
580
  input = <<-EOS
460
581
  Ryan Waldron
@@ -591,19 +712,18 @@ v0.0.7, 2013-12-18
591
712
  assert_equal '2013-12-18', metadata['revdate']
592
713
  end
593
714
 
594
- test "attribute entry overrides generated author initials" do
595
- blankdoc = Asciidoctor::Document.new
596
- reader = Asciidoctor::Reader.new "Stuart Rackham <founder@asciidoc.org>\n:Author Initials: SJR".lines.entries
597
- metadata = Asciidoctor::Parser.parse_header_metadata(reader, blankdoc)
715
+ test 'attribute entry overrides generated author initials' do
716
+ doc = empty_document
717
+ metadata, _ = parse_header_metadata %(Stuart Rackham <founder@asciidoc.org>\n:Author Initials: SJR), doc
598
718
  assert_equal 'SR', metadata['authorinitials']
599
- assert_equal 'SJR', blankdoc.attributes['authorinitials']
719
+ assert_equal 'SJR', doc.attributes['authorinitials']
600
720
  end
601
721
 
602
722
  test 'adjust indentation to 0' do
603
723
  input = <<-EOS.chomp
604
724
  def names
605
725
 
606
- @name.split ' '
726
+ @name.split
607
727
 
608
728
  end
609
729
  EOS
@@ -611,7 +731,7 @@ v0.0.7, 2013-12-18
611
731
  expected = <<-EOS.chomp
612
732
  def names
613
733
 
614
- @name.split ' '
734
+ @name.split
615
735
 
616
736
  end
617
737
  EOS
@@ -625,7 +745,7 @@ end
625
745
  input = <<-EOS.chomp
626
746
  def names
627
747
 
628
- \t @name.split ' '
748
+ \t @name.split
629
749
 
630
750
  end
631
751
  EOS
@@ -633,7 +753,7 @@ end
633
753
  expected = <<-EOS.chomp
634
754
  def names
635
755
 
636
- @name.split ' '
756
+ @name.split
637
757
 
638
758
  end
639
759
  EOS
@@ -667,7 +787,7 @@ devtmpfs 3.9G 0 3.9G 0% /dev
667
787
  input = <<-EOS.chomp
668
788
  def names
669
789
 
670
- @name.split ' '
790
+ @name.split
671
791
 
672
792
  end
673
793
  EOS
@@ -675,7 +795,7 @@ devtmpfs 3.9G 0 3.9G 0% /dev
675
795
  expected = <<-EOS.chomp
676
796
  def names
677
797
 
678
- @name.split ' '
798
+ @name.split
679
799
 
680
800
  end
681
801
  EOS
@@ -689,7 +809,7 @@ devtmpfs 3.9G 0 3.9G 0% /dev
689
809
  input = <<-EOS
690
810
  def names
691
811
 
692
- @name.split ' '
812
+ @name.split
693
813
 
694
814
  end
695
815
  EOS
@@ -88,13 +88,17 @@ context 'Path Resolver' do
88
88
  assert_equal 'assets/images', @resolver.web_path(nil, 'assets/images')
89
89
  end
90
90
 
91
- test 'posixfies windows paths' do
91
+ test 'posixifies windows paths' do
92
92
  assert_equal '/images', @resolver.web_path('\\images')
93
93
  assert_equal '../images', @resolver.web_path('..\\images')
94
94
  assert_equal '/images', @resolver.web_path('\\..\\images')
95
95
  assert_equal 'assets/images', @resolver.web_path('assets\\images')
96
96
  assert_equal '../assets/images', @resolver.web_path('assets\\images', '..\\images\\..')
97
97
  end
98
+
99
+ test 'URL encode spaces in path' do
100
+ assert_equal 'assets%20and%20stuff/lots%20of%20images', @resolver.web_path('lots of images', 'assets and stuff')
101
+ end
98
102
  end
99
103
 
100
104
  context 'System Paths' do
@@ -105,9 +109,23 @@ context 'Path Resolver' do
105
109
  end
106
110
 
107
111
  test 'prevents access to paths outside of jail' do
108
- assert_equal "#{JAIL}/css", @resolver.system_path('../../../../../css', "#{JAIL}/assets/stylesheets", JAIL)
109
- assert_equal "#{JAIL}/css", @resolver.system_path('/../../../../../css', "#{JAIL}/assets/stylesheets", JAIL)
110
- assert_equal "#{JAIL}/css", @resolver.system_path('../../../css', '../../..', JAIL)
112
+ result, warnings = redirect_streams do |_, err|
113
+ [(@resolver.system_path '../../../../../css', %(#{JAIL}/assets/stylesheets), JAIL), err.string]
114
+ end
115
+ assert_equal %(#{JAIL}/css), result
116
+ assert_includes warnings, 'path has illegal reference to ancestor of jail'
117
+
118
+ result, warnings = redirect_streams do |_, err|
119
+ [(@resolver.system_path '/../../../../../css', %(#{JAIL}/assets/stylesheets), JAIL), err.string]
120
+ end
121
+ assert_equal %(#{JAIL}/css), result
122
+ assert_includes warnings, 'path has illegal reference to ancestor of jail'
123
+
124
+ result, warnings = redirect_streams do |_, err|
125
+ [(@resolver.system_path '../../../css', '../../..', JAIL), err.string]
126
+ end
127
+ assert_equal %(#{JAIL}/css), result
128
+ assert_includes warnings, 'path has illegal reference to ancestor of jail'
111
129
  end
112
130
 
113
131
  test 'throws exception for illegal path access if recover is false' do
@@ -181,6 +199,7 @@ context 'Path Resolver' do
181
199
  pwd = File.expand_path(Dir.pwd)
182
200
  assert_equal "#{pwd}/images/tiger.png", @resolver.system_path('images/tiger.png', '')
183
201
  assert_equal "#{pwd}/images/tiger.png", @resolver.system_path('images/tiger.png', nil)
202
+ assert_equal "#{pwd}/images/tiger.png", @resolver.system_path('images/tiger.png')
184
203
  end
185
204
 
186
205
  test 'resolves relative hidden target relative to current directory if start is empty' do
@@ -190,22 +209,37 @@ context 'Path Resolver' do
190
209
  end
191
210
 
192
211
  test 'resolves and normalizes start with target is empty' do
193
- pwd = File.expand_path(Dir.pwd)
194
- assert_equal '/home/doctor/docs', @resolver.system_path('', '/home/doctor/docs')
195
- assert_equal '/home/doctor/docs', @resolver.system_path(nil, '/home/doctor/docs')
196
- assert_equal "#{pwd}/assets/images", @resolver.system_path(nil, 'assets/images')
197
- assert_equal "#{JAIL}/assets/images", @resolver.system_path('', '../assets/images', JAIL)
212
+ pwd = File.expand_path Dir.pwd
213
+ assert_equal '/home/doctor/docs', (@resolver.system_path '', '/home/doctor/docs')
214
+ assert_equal '/home/doctor/docs', (@resolver.system_path nil, '/home/doctor/docs')
215
+ assert_equal %(#{pwd}/assets/images), (@resolver.system_path nil, 'assets/images')
216
+ result, warnings = redirect_streams do |_, err|
217
+ [(@resolver.system_path '', '../assets/images', JAIL), err.string]
218
+ end
219
+ assert_equal %(#{JAIL}/assets/images), result
220
+ assert_includes warnings, 'path has illegal reference to ancestor of jail'
198
221
  end
199
222
 
200
- test 'posixfies windows paths' do
223
+ test 'posixifies windows paths' do
201
224
  assert_equal "#{JAIL}/assets/css", @resolver.system_path('..\\css', 'assets\\stylesheets', JAIL)
202
225
  end
203
226
 
204
227
  test 'resolves windows paths when file separator is backlash' do
205
228
  @resolver.file_separator = '\\'
206
- assert_equal 'C:/data/docs', @resolver.system_path('..', "C:\\data\\docs\\assets", 'C:\\data\\docs')
207
- assert_equal 'C:/data/docs', @resolver.system_path('..\\..', "C:\\data\\docs\\assets", 'C:\\data\\docs')
208
- assert_equal 'C:/data/docs/css', @resolver.system_path('..\\..\\css', "C:\\data\\docs\\assets", 'C:\\data\\docs')
229
+
230
+ assert_equal 'C:/data/docs', (@resolver.system_path '..', 'C:\\data\\docs\\assets', 'C:\\data\\docs')
231
+
232
+ result, warnings = redirect_streams do |_, err|
233
+ [(@resolver.system_path '..\\..', 'C:\\data\\docs\\assets', 'C:\\data\\docs'), err.string]
234
+ end
235
+ assert_equal 'C:/data/docs', result
236
+ assert_includes warnings, 'path has illegal reference to ancestor of jail'
237
+
238
+ result, warnings = redirect_streams do |_, err|
239
+ [(@resolver.system_path '..\\..\\css', 'C:\\data\\docs\\assets', 'C:\\data\\docs'), err.string]
240
+ end
241
+ assert_equal 'C:/data/docs/css', result
242
+ assert_includes warnings, 'path has illegal reference to ancestor of jail'
209
243
  end
210
244
 
211
245
  test 'should calculate relative path' do