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
@@ -2,7 +2,7 @@
2
2
  Dan Allen; Sarah White; Ryan Waldron
3
3
  :doctype: manpage
4
4
  :man manual: Asciidoctor Manual
5
- :man source: Asciidoctor 1.5.5
5
+ :man source: Asciidoctor 1.5.6
6
6
  :page-layout: base
7
7
 
8
8
  == NAME
@@ -26,7 +26,7 @@ If _FILE_ is _-_ then the AsciiDoc source is read from standard input.
26
26
  *-B, --base-dir*=_DIR_::
27
27
  Base directory containing the document and resources.
28
28
  Defaults to the directory containing the source file, or the working directory if the source is read from a stream.
29
- Can be used as a way to chroot the execution of the program.
29
+ When combined with the safe mode setting, can be used to chroot the execution of the program.
30
30
 
31
31
  *-S, --safe-mode*=_SAFE_MODE_::
32
32
  Set safe mode level: _unsafe_, _safe_, _server_ or _secure_.
@@ -35,7 +35,7 @@ If _FILE_ is _-_ then the AsciiDoc source is read from standard input.
35
35
 
36
36
  *--safe*::
37
37
  Set safe mode level to _safe_.
38
- Enables include macros, but restricts access to ancestor paths of source file.
38
+ Enables include directives, but prevents access to ancestor paths of source file.
39
39
  Provided for compatibility with the asciidoc command.
40
40
  If not set, the safe mode level defaults to _unsafe_ when Asciidoctor is invoked using this script.
41
41
 
@@ -43,7 +43,7 @@ If _FILE_ is _-_ then the AsciiDoc source is read from standard input.
43
43
 
44
44
  *-a, --attribute*=_ATTRIBUTE_::
45
45
  Define, override or delete a document attribute.
46
- Command-line attributes take precedence over attributes defined in the source file.
46
+ Command-line attributes take precedence over attributes defined in the source file unless the value ends with _@_.
47
47
  +
48
48
  _ATTRIBUTE_ is normally formatted as a key-value pair, in the form _NAME=VALUE_.
49
49
  Alternate acceptable forms are _NAME_ (where the _VALUE_ defaults to an empty string), _NAME!_ (unassigns the _NAME_ attribute) and _NAME=VALUE@_ (where _VALUE_ does not override value of _NAME_ attribute if it's already defined in the source document).
@@ -54,14 +54,14 @@ This option may be specified more than once.
54
54
  *-b, --backend*=_BACKEND_::
55
55
  Backend output file format: _html5_, _docbook5_, _docbook45_ and _manpage_ are supported out of the box.
56
56
  You can also use the backend alias names _html_ (aliased to _html5_) or _docbook_ (aliased to _docbook5_).
57
+ Other values can be passed, but if Asciidoctor cannot resolve the backend to a converter, it will fail.
57
58
  Defaults to _html5_.
58
- Other options can be passed, but if Asciidoctor cannot find the backend, it will fail during conversion.
59
59
 
60
60
  *-d, --doctype*=_DOCTYPE_::
61
61
  Document type: _article_, _book_, _manpage_ or _inline_.
62
62
  Sets the root element when using the _docbook_ backend and the style class on the HTML body element when using the _html_ backend.
63
63
  The _book_ document type allows multiple level-0 section titles in a single document.
64
- The _manpage_ document type enables parsing of metadata necessary to produce a manpage.
64
+ The _manpage_ document type enables parsing of metadata necessary to produce a man page.
65
65
  The _inline_ document type allows the content of a single paragraph to be formatted and returned without wrapping it in a containing element.
66
66
  Defaults to _article_.
67
67
 
@@ -98,9 +98,9 @@ This option may be specified more than once.
98
98
  *-o, --out-file*=_OUT_FILE_::
99
99
  Write output to file _OUT_FILE_.
100
100
  Defaults to the base name of the input file suffixed with _backend_ extension.
101
- If the input is read from standard input, then the output file defaults to stdout.
102
- If _OUT_FILE_ is _-_ then the standard output is also used.
103
- If specified, the file is resolved relative to the working directory.
101
+ The file is resolved relative to the working directory.
102
+ If the input is read from standard input or a named pipe (fifo), then the output file defaults to stdout.
103
+ If _OUT_FILE_ is _-_, then the output file is written to standard output.
104
104
 
105
105
  *-r, --require*=_LIBRARY_::
106
106
  Require the specified library before executing the processor, using the standard Ruby require.
@@ -136,8 +136,10 @@ Matching templates found in subsequent directories override ones previously disc
136
136
 
137
137
  === Program Information
138
138
 
139
- *-h, --help*::
140
- Show the help message.
139
+ *-h, --help* [_TOPIC_]::
140
+ Print the help message.
141
+ Show the command usage if _TOPIC_ is not specified (or not recognized).
142
+ Dump the Asciidoctor man page (in troff/groff format) if _TOPIC_ is _manpage_.
141
143
 
142
144
  *-V, --version*::
143
145
  Print program version number.
@@ -146,7 +148,7 @@ Matching templates found in subsequent directories override ones previously disc
146
148
 
147
149
  == ENVIRONMENT
148
150
 
149
- *Asciidoctor* honors the SOURCE_DATE_EPOCH environment variable.
151
+ *Asciidoctor* honors the *SOURCE_DATE_EPOCH* environment variable.
150
152
  If this variable is assigned an integer value, that value is used as the epoch of all input documents and as the local date and time.
151
153
  See https://reproducible-builds.org/specs/source-date-epoch/ for more information about this environment variable.
152
154
 
@@ -180,5 +182,5 @@ Refer to the *Asciidoctor* issue tracker at https://github.com/asciidoctor/ascii
180
182
 
181
183
  == COPYING
182
184
 
183
- Copyright \(C) 2012-2016 Dan Allen, Ryan Waldron and the Asciidoctor Project.
185
+ Copyright \(C) 2012-2017 Dan Allen, Ryan Waldron and the Asciidoctor Project.
184
186
  Free use of this software is granted under the terms of the MIT License.
@@ -46,6 +46,16 @@ linus.torvalds@example.com
46
46
  assert_equal %(Linus Torvalds +\nLinux Hacker +\nlinus.torvalds@example.com), doc.attributes['signature']
47
47
  end
48
48
 
49
+ test 'should allow pass macro to surround a multi-line value that contains line breaks' do
50
+ str = <<-EOS
51
+ :signature: pass:a[{author} + \\
52
+ {title} + \\
53
+ {email}]
54
+ EOS
55
+ doc = document_from_string str, :attributes => { 'author' => 'Linus Torvalds', 'title' => 'Linux Hacker', 'email' => 'linus.torvalds@example.com' }
56
+ assert_equal %(Linus Torvalds +\nLinux Hacker +\nlinus.torvalds@example.com), (doc.attr 'signature')
57
+ end
58
+
49
59
  test 'should delete an attribute that ends with !' do
50
60
  doc = document_from_string(":frog: Tanglefoot\n:frog!:")
51
61
  assert_equal nil, doc.attributes['frog']
@@ -86,14 +96,25 @@ linus.torvalds@example.com
86
96
  assert_equal 'Asciidoctor 1.0', doc.attributes['release']
87
97
  end
88
98
 
89
- test "assigns attribute to empty string if substitution fails to resolve attribute" do
90
- doc = document_from_string ":release: Asciidoctor {version}", :attributes => { 'attribute-missing' => 'drop-line' }
99
+ test 'assigns attribute to empty string if substitution fails to resolve attribute' do
100
+ input = ':release: Asciidoctor {version}'
101
+ doc, warnings = redirect_streams do |_, err|
102
+ [(document_from_string input, :attributes => { 'attribute-missing' => 'drop-line' }), err.string]
103
+ end
91
104
  assert_equal '', doc.attributes['release']
105
+ assert_includes warnings, 'dropping line containing reference to missing attribute'
92
106
  end
93
107
 
94
- test "assigns multi-line attribute to empty string if substitution fails to resolve attribute" do
95
- doc = document_from_string ":release: Asciidoctor +\n {version}", :attributes => { 'attribute-missing' => 'drop-line' }
108
+ test 'assigns multi-line attribute to empty string if substitution fails to resolve attribute' do
109
+ input = <<-EOS
110
+ :release: Asciidoctor +
111
+ {version}
112
+ EOS
113
+ doc, warnings = redirect_streams do |_, err|
114
+ [(document_from_string input, :attributes => { 'attribute-missing' => 'drop-line' }), err.string]
115
+ end
96
116
  assert_equal '', doc.attributes['release']
117
+ assert_includes warnings, 'dropping line containing reference to missing attribute'
97
118
  end
98
119
 
99
120
  test 'resolves attributes inside attribute value within header' do
@@ -213,6 +234,13 @@ EOS
213
234
  assert_equal '&lt;&gt;&amp;', doc.attributes['xml-busters']
214
235
  end
215
236
 
237
+ test 'should not recognize pass macro with invalid substitution list in attribute value' do
238
+ [',', '42', 'a,'].each do |subs|
239
+ doc = document_from_string %(:pass-fail: pass:#{subs}[whale])
240
+ assert_equal %(pass:#{subs}[whale]), doc.attributes['pass-fail']
241
+ end
242
+ end
243
+
216
244
  test "attribute is treated as defined until it's not" do
217
245
  input = <<-EOS
218
246
  :holygrail:
@@ -350,6 +378,19 @@ content
350
378
  assert doc.attributes.has_key? 'basebackend-html'
351
379
  end
352
380
 
381
+ test 'set_attr should set value to empty string if no value is specified' do
382
+ node = Asciidoctor::Block.new nil, :paragraph, :attributes => {}
383
+ node.set_attr 'foo'
384
+ assert_equal '', (node.attr 'foo')
385
+ end
386
+
387
+ test 'remove_attr should remove attribute and return previous value' do
388
+ doc = empty_document
389
+ node = Asciidoctor::Block.new doc, :paragraph, :attributes => { 'foo' => 'bar' }
390
+ assert_equal 'bar', (node.remove_attr 'foo')
391
+ assert_nil node.attr('foo')
392
+ end
393
+
353
394
  test 'set_attr should not overwrite existing key if overwrite is false' do
354
395
  node = Asciidoctor::Block.new nil, :paragraph, :attributes => { 'foo' => 'bar' }
355
396
  assert_equal 'bar', (node.attr 'foo')
@@ -377,6 +418,31 @@ content
377
418
  assert_xpath '//a[@href="https://google.com"]', output, 1
378
419
  end
379
420
 
421
+ test 'set_attribute should set attribute if key is not locked' do
422
+ doc = empty_document
423
+ assert !(doc.attr? 'foo')
424
+ res = doc.set_attribute 'foo', 'baz'
425
+ assert res
426
+ assert_equal 'baz', (doc.attr 'foo')
427
+ end
428
+
429
+ test 'set_attribute should not set key if key is locked' do
430
+ doc = empty_document :attributes => { 'foo' => 'bar' }
431
+ assert_equal 'bar', (doc.attr 'foo')
432
+ res = doc.set_attribute 'foo', 'baz'
433
+ assert !res
434
+ assert_equal 'bar', (doc.attr 'foo')
435
+ end
436
+
437
+ test 'set_attribute should update backend attributes' do
438
+ doc = empty_document :attributes => { 'backend' => 'html5@' }
439
+ assert_equal '', (doc.attr 'backend-html5')
440
+ res = doc.set_attribute 'backend', 'docbook5'
441
+ assert res
442
+ assert !(doc.attr? 'backend-html5')
443
+ assert_equal '', (doc.attr 'backend-docbook5')
444
+ end
445
+
380
446
  test 'verify toc attribute matrix' do
381
447
  expected_data = <<-EOS
382
448
  #attributes |toc|toc-position|toc-placement|toc-class
@@ -399,7 +465,7 @@ toc toc-placement! | |content |macro |nil
399
465
 
400
466
  expected.each do |expect|
401
467
  raw_attrs, toc, toc_position, toc_placement, toc_class = expect
402
- attrs = Hash[*(raw_attrs.split ' ').map {|e| e.include?('=') ? e.split('=') : [e, ''] }.flatten]
468
+ attrs = Hash[*raw_attrs.split.map {|e| e.include?('=') ? e.split('=', 2) : [e, ''] }.flatten]
403
469
  doc = document_from_string '', :attributes => attrs
404
470
  toc ? (assert doc.attr?('toc', toc)) : (assert !doc.attr?('toc'))
405
471
  toc_position ? (assert doc.attr?('toc-position', toc_position)) : (assert !doc.attr?('toc-position'))
@@ -448,7 +514,7 @@ Yo, {myfrog}!
448
514
  assert_xpath '(//p)[1][text()="Yo, Tanglefoot!"]', output, 1
449
515
  end
450
516
 
451
- test "ignores lines with bad attributes if attribute-missing is drop-line" do
517
+ test 'ignores lines with bad attributes if attribute-missing is drop-line' do
452
518
  input = <<-EOS
453
519
  :attribute-missing: drop-line
454
520
 
@@ -456,9 +522,10 @@ This is
456
522
  blah blah {foobarbaz}
457
523
  all there is.
458
524
  EOS
459
- html = render_embedded_string input
460
- result = Nokogiri::HTML(html)
461
- refute_match(/blah blah/m, result.css("p").first.content.strip)
525
+ output, warnings = redirect_streams {|_, err| [(render_embedded_string input), err.string] }
526
+ para = xmlnodes_at_css 'p', output, 1
527
+ refute_includes 'blah blah', para.content
528
+ assert_includes warnings, 'dropping line containing reference to missing attribute'
462
529
  end
463
530
 
464
531
  test "attribute value gets interpretted when rendering" do
@@ -476,9 +543,10 @@ Line 1: This line should appear in the output.
476
543
  Line 2: Oh no, a {bogus-attribute}! This line should not appear in the output.
477
544
  EOS
478
545
 
479
- output = render_embedded_string input
546
+ output, warnings = redirect_streams {|_, err| [(render_embedded_string input), err.string] }
480
547
  assert_match(/Line 1/, output)
481
548
  refute_match(/Line 2/, output)
549
+ assert_includes warnings, 'dropping line containing reference to missing attribute'
482
550
  end
483
551
 
484
552
  test 'should not drop line with reference to missing attribute by default' do
@@ -512,7 +580,7 @@ Line 2: {set:a!}This line should not appear in the output.
512
580
  :a:
513
581
 
514
582
  Line 1: This line should appear in the output.
515
- Line 2: {set:a!}This line should not appear in the output.
583
+ Line 2: {set:a!}This line should appear in the output.
516
584
  EOS
517
585
 
518
586
  output = render_embedded_string input
@@ -748,7 +816,7 @@ of the attribute named foo in your document.
748
816
  {set:foo!}
749
817
  {foo}yes
750
818
  EOS
751
- output = render_embedded_string input
819
+ output = redirect_streams { render_embedded_string input }
752
820
  assert_xpath '//p', output, 1
753
821
  assert_xpath '//p/child::text()', output, 0
754
822
  end
@@ -878,6 +946,34 @@ after: {counter:mycounter}
878
946
  assert_xpath '//p[text()="before: 1 2 3"]', output, 1
879
947
  assert_xpath '//p[text()="after: 1"]', output, 1
880
948
  end
949
+
950
+ test 'nested document should use counter from parent document' do
951
+ input = <<-EOS
952
+ .Title for Foo
953
+ image::foo.jpg[]
954
+
955
+ [cols="2*a"]
956
+ |===
957
+ |
958
+ .Title for Bar
959
+ image::bar.jpg[]
960
+
961
+ |
962
+ .Title for Baz
963
+ image::baz.jpg[]
964
+ |===
965
+
966
+ .Title for Qux
967
+ image::qux.jpg[]
968
+ EOS
969
+
970
+ output = render_embedded_string input
971
+ assert_xpath '//div[@class="title"]', output, 4
972
+ assert_xpath '//div[@class="title"][text() = "Figure 1. Title for Foo"]', output, 1
973
+ assert_xpath '//div[@class="title"][text() = "Figure 2. Title for Bar"]', output, 1
974
+ assert_xpath '//div[@class="title"][text() = "Figure 3. Title for Baz"]', output, 1
975
+ assert_xpath '//div[@class="title"][text() = "Figure 4. Title for Qux"]', output, 1
976
+ end
881
977
  end
882
978
 
883
979
  context 'Block attributes' do
@@ -952,7 +1048,7 @@ content
952
1048
  assert_xpath '//*[@class="title"]/strong[text()="title"]', output, 1
953
1049
  end
954
1050
 
955
- test 'attribute list may begin with space' do
1051
+ test 'attribute list may not begin with space' do
956
1052
  input = <<-EOS
957
1053
  [ quote]
958
1054
  ____
@@ -961,8 +1057,8 @@ ____
961
1057
  EOS
962
1058
 
963
1059
  doc = document_from_string input
964
- qb = doc.blocks.first
965
- assert_equal 'quote', qb.style
1060
+ b1 = doc.blocks.first
1061
+ assert_equal ['[ quote]'], b1.lines
966
1062
  end
967
1063
 
968
1064
  test 'attribute list may begin with comma' do
@@ -1125,7 +1221,8 @@ A normal paragraph
1125
1221
  EOS
1126
1222
  doc = document_from_string(input)
1127
1223
  para = doc.blocks.first
1128
- para.add_role 'role1'
1224
+ res = para.add_role 'role1'
1225
+ assert res
1129
1226
  assert_equal 'role1', para.attributes['role']
1130
1227
  assert para.has_role? 'role1'
1131
1228
  end
@@ -1137,7 +1234,8 @@ A normal paragraph
1137
1234
  EOS
1138
1235
  doc = document_from_string(input)
1139
1236
  para = doc.blocks.first
1140
- para.add_role 'role2'
1237
+ res = para.add_role 'role2'
1238
+ assert res
1141
1239
  assert_equal 'role1 role2', para.attributes['role']
1142
1240
  assert para.has_role? 'role1'
1143
1241
  assert para.has_role? 'role2'
@@ -1150,7 +1248,8 @@ A normal paragraph
1150
1248
  EOS
1151
1249
  doc = document_from_string(input)
1152
1250
  para = doc.blocks.first
1153
- para.add_role 'role1'
1251
+ res = para.add_role 'role1'
1252
+ refute res
1154
1253
  assert_equal 'role1', para.attributes['role']
1155
1254
  assert para.has_role? 'role1'
1156
1255
  end
@@ -1162,12 +1261,27 @@ A normal paragraph
1162
1261
  EOS
1163
1262
  doc = document_from_string(input)
1164
1263
  para = doc.blocks.first
1165
- para.remove_role 'role1'
1264
+ res = para.remove_role 'role1'
1265
+ assert res
1166
1266
  assert_equal 'role2', para.attributes['role']
1167
1267
  assert para.has_role? 'role2'
1168
1268
  assert !para.has_role?('role1')
1169
1269
  end
1170
1270
 
1271
+ test 'roles are removed when last role is removed using remove_role' do
1272
+ input = <<-EOS
1273
+ [.role1]
1274
+ A normal paragraph
1275
+ EOS
1276
+ doc = document_from_string(input)
1277
+ para = doc.blocks.first
1278
+ res = para.remove_role 'role1'
1279
+ assert res
1280
+ refute para.role?
1281
+ assert_equal nil, para.attributes['role']
1282
+ refute para.has_role? 'role1'
1283
+ end
1284
+
1171
1285
  test 'roles are not changed when a non-existent role is removed using remove_role' do
1172
1286
  input = <<-EOS
1173
1287
  [.role1]
@@ -1175,7 +1289,8 @@ A normal paragraph
1175
1289
  EOS
1176
1290
  doc = document_from_string(input)
1177
1291
  para = doc.blocks.first
1178
- para.remove_role 'role2'
1292
+ res = para.remove_role 'role2'
1293
+ refute res
1179
1294
  assert_equal 'role1', para.attributes['role']
1180
1295
  assert para.has_role? 'role1'
1181
1296
  assert !para.has_role?('role2')
@@ -1187,7 +1302,8 @@ A normal paragraph
1187
1302
  EOS
1188
1303
  doc = document_from_string(input)
1189
1304
  para = doc.blocks.first
1190
- para.remove_role 'role1'
1305
+ res = para.remove_role 'role1'
1306
+ refute res
1191
1307
  assert_equal nil, para.attributes['role']
1192
1308
  assert !para.has_role?('role1')
1193
1309
  end
@@ -1265,7 +1381,7 @@ paragraph
1265
1381
  assert_equal 'coolio', subsec.id
1266
1382
  end
1267
1383
 
1268
- test "trailing block attributes tranfer to the following section" do
1384
+ test "trailing block attributes transfer to the following section" do
1269
1385
  input = <<-EOS
1270
1386
  [[one]]
1271
1387
 
@@ -5,22 +5,39 @@ unless defined? ASCIIDOCTOR_PROJECT_DIR
5
5
  end
6
6
 
7
7
  context "Blocks" do
8
- context 'Line Breaks' do
9
- test "ruler" do
10
- output = render_string("'''")
11
- assert_xpath '//*[@id="content"]/hr', output, 1
12
- assert_xpath '//*[@id="content"]/*', output, 1
8
+ context 'Layout Breaks' do
9
+ test 'horizontal rule' do
10
+ %w(''' '''' '''''').each do |line|
11
+ output = render_embedded_string line
12
+ assert_includes output, '<hr>'
13
+ end
14
+ end
15
+
16
+ test '< 3 chars does not make horizontal rule' do
17
+ %w(' '').each do |line|
18
+ output = render_embedded_string line
19
+ refute_includes output, '<hr>'
20
+ assert_includes output, %(<p>#{line}</p>)
21
+ end
22
+ end
23
+
24
+ test 'mixed chars does not make horizontal rule' do
25
+ [%q(''<), %q('''<), %q(' ' ')].each do |line|
26
+ output = render_embedded_string line
27
+ refute_includes output, '<hr>'
28
+ assert_includes output, %(<p>#{line.sub '<', '&lt;'}</p>)
29
+ end
13
30
  end
14
31
 
15
- test "ruler between blocks" do
16
- output = render_string("Block above\n\n'''\n\nBlock below")
17
- assert_xpath '//*[@id="content"]/hr', output, 1
18
- assert_xpath '//*[@id="content"]/hr/preceding-sibling::*', output, 1
19
- assert_xpath '//*[@id="content"]/hr/following-sibling::*', output, 1
32
+ test 'horizontal rule between blocks' do
33
+ output = render_embedded_string %(Block above\n\n'''\n\nBlock below)
34
+ assert_xpath '/hr', output, 1
35
+ assert_xpath '/hr/preceding-sibling::*', output, 1
36
+ assert_xpath '/hr/following-sibling::*', output, 1
20
37
  end
21
38
 
22
- test "page break" do
23
- output = render_embedded_string("page 1\n\n<<<\n\npage 2")
39
+ test 'page break' do
40
+ output = render_embedded_string %(page 1\n\n<<<\n\npage 2)
24
41
  assert_xpath '/*[translate(@style, ";", "")="page-break-after: always"]', output, 1
25
42
  assert_xpath '/*[translate(@style, ";", "")="page-break-after: always"]/preceding-sibling::div/p[text()="page 1"]', output, 1
26
43
  assert_xpath '/*[translate(@style, ";", "")="page-break-after: always"]/following-sibling::div/p[text()="page 2"]', output, 1
@@ -254,7 +271,7 @@ ____
254
271
  assert_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]/cite[text() = "Famous Book (1999)"]', output, 1
255
272
  attribution = xmlnodes_at_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]', output, 1
256
273
  author = attribution.children.first
257
- assert_equal "#{expand_entity 8212} Famous Person", author.text.strip
274
+ assert_equal "#{decode_char 8212} Famous Person", author.text.strip
258
275
  end
259
276
 
260
277
  test 'quote block with attribute and id and role shorthand' do
@@ -374,7 +391,7 @@ Some more inspiring words.
374
391
  assert_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]/cite[text() = "Famous Source, Volume 1 (1999)"]', output, 1
375
392
  attribution = xmlnodes_at_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]', output, 1
376
393
  author = attribution.children.first
377
- assert_equal "#{expand_entity 8212} Famous Person", author.text.strip
394
+ assert_equal "#{decode_char 8212} Famous Person", author.text.strip
378
395
  end
379
396
 
380
397
  test 'quoted paragraph-style quote block with attribution' do
@@ -393,7 +410,7 @@ Some more inspiring words."
393
410
  assert_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]/cite[text() = "Famous Source, Volume 1 (1999)"]', output, 1
394
411
  attribution = xmlnodes_at_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]', output, 1
395
412
  author = attribution.children.first
396
- assert_equal "#{expand_entity 8212} Famous Person", author.text.strip
413
+ assert_equal "#{decode_char 8212} Famous Person", author.text.strip
397
414
  end
398
415
 
399
416
  test 'single-line verse block without attribution' do
@@ -428,7 +445,7 @@ ____
428
445
  assert_xpath '//*[@class = "verseblock"]/*[@class = "attribution"]/cite[text() = "Famous Poem"]', output, 1
429
446
  attribution = xmlnodes_at_xpath '//*[@class = "verseblock"]/*[@class = "attribution"]', output, 1
430
447
  author = attribution.children.first
431
- assert_equal "#{expand_entity 8212} Famous Poet", author.text.strip
448
+ assert_equal "#{decode_char 8212} Famous Poet", author.text.strip
432
449
  end
433
450
 
434
451
  test 'multi-stanza verse block' do
@@ -475,7 +492,7 @@ ____
475
492
  EOS
476
493
 
477
494
  verse = block_from_string input
478
- assert_equal Asciidoctor::Substitutors::SUBS[:normal], verse.subs
495
+ assert_equal Asciidoctor::Substitutors::NORMAL_SUBS, verse.subs
479
496
  end
480
497
 
481
498
  test 'should not recognize callouts in a verse' do
@@ -487,8 +504,9 @@ ____
487
504
  <1> Not pointing to a callout
488
505
  EOS
489
506
 
490
- output = render_embedded_string input
507
+ output, warnings = redirect_streams {|_, err| [(render_embedded_string input), err.string] }
491
508
  assert_xpath '//pre[text()="La la la <1>"]', output, 1
509
+ assert_includes warnings, 'line 5: no callouts refer to list item 1'
492
510
  end
493
511
 
494
512
  test 'should perform normal subs on a verse block' do
@@ -536,6 +554,8 @@ You futz with XML.
536
554
  EOS
537
555
 
538
556
  doc = document_from_string input
557
+ assert_equal 1, doc.blocks[0].number
558
+ assert_equal 2, doc.blocks[1].number
539
559
  output = doc.render
540
560
  assert_xpath '(//*[@class="exampleblock"])[1]/*[@class="title"][text()="Example 1. Writing Docs with AsciiDoc"]', output, 1
541
561
  assert_xpath '(//*[@class="exampleblock"])[2]/*[@class="title"][text()="Example 2. Writing Docs with DocBook"]', output, 1
@@ -562,6 +582,8 @@ You futz with XML.
562
582
  EOS
563
583
 
564
584
  doc = document_from_string input
585
+ assert_equal 'A', doc.blocks[0].number
586
+ assert_equal 'B', doc.blocks[1].number
565
587
  output = doc.render
566
588
  assert_xpath '(//*[@class="exampleblock"])[1]/*[@class="title"][text()="Example A. Writing Docs with AsciiDoc"]', output, 1
567
589
  assert_xpath '(//*[@class="exampleblock"])[2]/*[@class="title"][text()="Example B. Writing Docs with DocBook"]', output, 1
@@ -580,25 +602,12 @@ You just write.
580
602
  EOS
581
603
 
582
604
  doc = document_from_string input
605
+ assert_nil doc.blocks[0].number
583
606
  output = doc.render
584
607
  assert_xpath '(//*[@class="exampleblock"])[1]/*[@class="title"][text()="Look! Writing Docs with AsciiDoc"]', output, 1
585
608
  assert !doc.attributes.has_key?('example-number')
586
609
  end
587
610
 
588
- test 'explicit caption is set on block even if block has no title' do
589
- input = <<-EOS
590
- [caption="Look!"]
591
- ====
592
- Just write.
593
- ====
594
- EOS
595
-
596
- doc = document_from_string input
597
- assert_equal 'Look!', doc.blocks.first.caption
598
- output = doc.render
599
- refute_match(/Look/, output)
600
- end
601
-
602
611
  test 'automatic caption can be turned off and on and modified' do
603
612
  input = <<-EOS
604
613
  .first example
@@ -802,7 +811,7 @@ source line 2\r
802
811
  ----
803
812
  def names
804
813
 
805
- @names.split ' '
814
+ @names.split
806
815
 
807
816
  end
808
817
  ----
@@ -811,7 +820,7 @@ source line 2\r
811
820
  expected = <<-EOS
812
821
  def names
813
822
 
814
- @names.split ' '
823
+ @names.split
815
824
 
816
825
  end
817
826
  EOS
@@ -829,7 +838,7 @@ end
829
838
  ----
830
839
  def names
831
840
 
832
- @names.split ' '
841
+ @names.split
833
842
 
834
843
  end
835
844
  ----
@@ -838,7 +847,7 @@ end
838
847
  expected = <<-EOS
839
848
  def names
840
849
 
841
- @names.split ' '
850
+ @names.split
842
851
 
843
852
  end
844
853
  EOS
@@ -856,7 +865,7 @@ end
856
865
  ----
857
866
  def names
858
867
 
859
- @names.split ' '
868
+ @names.split
860
869
 
861
870
  end
862
871
  ----
@@ -865,7 +874,7 @@ end
865
874
  expected = <<-EOS
866
875
  def names
867
876
 
868
- @names.split ' '
877
+ @names.split
869
878
 
870
879
  end
871
880
  EOS
@@ -885,7 +894,7 @@ end
885
894
  ----
886
895
  def names
887
896
 
888
- @names.split ' '
897
+ @names.split
889
898
 
890
899
  end
891
900
  ----
@@ -894,7 +903,7 @@ end
894
903
  expected = <<-EOS
895
904
  def names
896
905
 
897
- @names.split ' '
906
+ @names.split
898
907
 
899
908
  end
900
909
  EOS
@@ -914,7 +923,7 @@ end
914
923
  ----
915
924
  def names
916
925
 
917
- @names.split ' '
926
+ @names.split
918
927
 
919
928
  end
920
929
  ----
@@ -923,7 +932,7 @@ end
923
932
  expected = <<-EOS
924
933
  def names
925
934
 
926
- @names.split ' '
935
+ @names.split
927
936
 
928
937
  end
929
938
  EOS
@@ -1212,7 +1221,7 @@ line below
1212
1221
 
1213
1222
  output = render_embedded_string input
1214
1223
  assert_css '.stemblock', output, 1
1215
- nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output, 1
1224
+ nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
1216
1225
  assert_equal '\[\sqrt{3x-1}+(1+x)^2 &lt; y\]', nodes.first.to_s.strip
1217
1226
  end
1218
1227
 
@@ -1226,7 +1235,7 @@ line below
1226
1235
 
1227
1236
  output = render_embedded_string input
1228
1237
  assert_css '.stemblock', output, 1
1229
- nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output, 1
1238
+ nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
1230
1239
  assert_equal '\[\sqrt{3x-1}+(1+x)^2 &lt; y\]', nodes.first.to_s.strip
1231
1240
  end
1232
1241
 
@@ -1259,7 +1268,7 @@ sqrt(3x-1)+(1+x)^2 < y
1259
1268
 
1260
1269
  output = render_embedded_string input
1261
1270
  assert_css '.stemblock', output, 1
1262
- nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output, 1
1271
+ nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
1263
1272
  assert_equal '\$sqrt(3x-1)+(1+x)^2 &lt; y\$', nodes.first.to_s.strip
1264
1273
  end
1265
1274
 
@@ -1273,7 +1282,7 @@ sqrt(3x-1)+(1+x)^2 < y
1273
1282
 
1274
1283
  output = render_embedded_string input
1275
1284
  assert_css '.stemblock', output, 1
1276
- nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output, 1
1285
+ nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
1277
1286
  assert_equal '\$sqrt(3x-1)+(1+x)^2 &lt; y\$', nodes.first.to_s.strip
1278
1287
  end
1279
1288
 
@@ -1342,7 +1351,7 @@ sqrt(3x-1)+(1+x)^2 < y
1342
1351
  ].each do |attributes|
1343
1352
  output = render_embedded_string input, :attributes => attributes
1344
1353
  assert_css '.stemblock', output, 1
1345
- nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output, 1
1354
+ nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
1346
1355
  assert_equal '\$sqrt(3x-1)+(1+x)^2 &lt; y\$', nodes.first.to_s.strip
1347
1356
  end
1348
1357
  end
@@ -1357,7 +1366,7 @@ sqrt(3x-1)+(1+x)^2 < y
1357
1366
 
1358
1367
  output = render_embedded_string input, :attributes => {'stem' => 'latexmath'}
1359
1368
  assert_css '.stemblock', output, 1
1360
- nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output, 1
1369
+ nodes = xmlnodes_at_xpath '//*[@class="content"]/child::text()', output
1361
1370
  assert_equal '\[\sqrt{3x-1}+(1+x)^2 &lt; y\]', nodes.first.to_s.strip
1362
1371
  end
1363
1372
  end
@@ -1399,6 +1408,7 @@ section paragraph
1399
1408
 
1400
1409
  test 'block title above document title gets carried over to first block in first section if no preamble' do
1401
1410
  input = <<-EOS
1411
+ :doctype: book
1402
1412
  .Block title
1403
1413
  = Document Title
1404
1414
 
@@ -1406,7 +1416,10 @@ section paragraph
1406
1416
 
1407
1417
  paragraph
1408
1418
  EOS
1409
- output = render_string input
1419
+ doc = document_from_string input
1420
+ # NOTE block title demotes document title to level-0 section
1421
+ refute doc.header?
1422
+ output = doc.convert
1410
1423
  assert_xpath '//*[@class="sect1"]//*[@class="paragraph"]/*[@class="title"][text() = "Block title"]', output, 1
1411
1424
  end
1412
1425
 
@@ -1548,18 +1561,39 @@ image::circle.svg[Tiger,100]
1548
1561
  image::no-such-image.svg[Alt Text]
1549
1562
  EOS
1550
1563
 
1551
- output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SERVER
1564
+ output, warnings = redirect_streams {|_, err| [(render_embedded_string input, :safe => Asciidoctor::SafeMode::SERVER), err.string] }
1552
1565
  assert_xpath '//span[@class="alt"][text()="Alt Text"]', output, 1
1566
+ assert_includes warnings, 'SVG does not exist or cannot be read'
1553
1567
  end
1554
1568
 
1555
- test 'can render block image with alt text defined in macro containing escaped square bracket' do
1569
+ test 'can render block image with alt text defined in macro containing square bracket' do
1556
1570
  input = <<-EOS
1557
- image::images/tiger.png[A [Bengal\\] Tiger]
1571
+ image::images/tiger.png[A [Bengal] Tiger]
1558
1572
  EOS
1559
1573
 
1560
1574
  output = render_string input
1561
1575
  img = xmlnodes_at_xpath '//img', output, 1
1562
- assert_equal 'A [Bengal] Tiger', img.attr('alt').value
1576
+ assert_equal 'A [Bengal] Tiger', img.attr('alt')
1577
+ end
1578
+
1579
+ test 'can render block image with target containing spaces' do
1580
+ input = <<-EOS
1581
+ image::images/big tiger.png[A Big Tiger]
1582
+ EOS
1583
+
1584
+ output = render_string input
1585
+ img = xmlnodes_at_xpath '//img', output, 1
1586
+ assert_equal 'images/big%20tiger.png', img.attr('src')
1587
+ assert_equal 'A Big Tiger', img.attr('alt')
1588
+ end
1589
+
1590
+ test 'should not recognize block image if target has leading or trailing spaces' do
1591
+ [' tiger.png', 'tiger.png '].each do |target|
1592
+ input = %(image::#{target}[Tiger])
1593
+
1594
+ output = render_embedded_string input
1595
+ assert_xpath '//img', output, 0
1596
+ end
1563
1597
  end
1564
1598
 
1565
1599
  test 'can render block image with alt text defined in block attribute above macro' do
@@ -1582,31 +1616,52 @@ image::images/tiger.png[Tiger]
1582
1616
  assert_xpath '/*[@class="imageblock"]//img[@src="images/tiger.png"][@alt="Tiger"]', output, 1
1583
1617
  end
1584
1618
 
1585
- test 'alt text is escaped in HTML backend' do
1619
+ test 'should substitute attribute references in alt text defined in image block macro' do
1586
1620
  input = <<-EOS
1587
- image::images/open.png[File > Open]
1588
- EOS
1621
+ :alt-text: Tiger
1589
1622
 
1623
+ image::images/tiger.png[{alt-text}]
1624
+ EOS
1590
1625
  output = render_embedded_string input
1591
- assert_match(/File &gt; Open/, output)
1626
+ assert_xpath '/*[@class="imageblock"]//img[@src="images/tiger.png"][@alt="Tiger"]', output, 1
1592
1627
  end
1593
1628
 
1594
- test 'alt text is escaped in DocBook backend' do
1629
+ test 'style attribute is dropped from image macro' do
1595
1630
  input = <<-EOS
1596
- image::images/open.png[File > Open]
1631
+ [style=value]
1632
+ image::images/tiger.png[Tiger]
1597
1633
  EOS
1598
1634
 
1599
- output = render_embedded_string input, :backend => :docbook
1600
- assert_match(/File &gt; Open/, output)
1635
+ doc = document_from_string input
1636
+ img = doc.blocks[0]
1637
+ refute(img.attributes.key? 'style')
1638
+ assert_nil img.style
1639
+ end
1640
+
1641
+ test 'should apply specialcharacters and replacement substitutions to alt text' do
1642
+ input = 'A tiger\'s "roar" is < a bear\'s "growl"'
1643
+ expected = 'A tiger&#8217;s &quot;roar&quot; is &lt; a bear&#8217;s &quot;growl&quot;'
1644
+ result = render_embedded_string %(image::images/tiger-roar.png[#{input}])
1645
+ assert_includes result, %(alt="#{expected}")
1601
1646
  end
1602
1647
 
1603
- test "can render block image with auto-generated alt text" do
1648
+ test 'should not encode double quotes in alt text when converting to DocBook' do
1649
+ input = 'Select "File > Open"'
1650
+ expected = 'Select "File &gt; Open"'
1651
+ result = render_embedded_string %(image::images/open.png[#{input}]), :backend => :docbook
1652
+ assert_includes result, %(<phrase>#{expected}</phrase>)
1653
+ end
1654
+
1655
+ test 'should auto-generate alt text for block image if alt text is not specified' do
1604
1656
  input = <<-EOS
1605
- image::images/tiger.png[]
1657
+ image::images/lions-and-tigers.png[]
1606
1658
  EOS
1607
1659
 
1608
- output = render_embedded_string input
1609
- assert_xpath '/*[@class="imageblock"]//img[@src="images/tiger.png"][@alt="tiger"]', output, 1
1660
+ image = block_from_string input
1661
+ assert_equal 'lions and tigers', (image.attr 'alt')
1662
+ assert_equal 'lions and tigers', (image.attr 'default-alt')
1663
+ output = image.convert
1664
+ assert_xpath '/*[@class="imageblock"]//img[@src="images/lions-and-tigers.png"][@alt="lions and tigers"]', output, 1
1610
1665
  end
1611
1666
 
1612
1667
  test "can render block image with alt text and height and width" do
@@ -1627,13 +1682,32 @@ image::images/tiger.png[Tiger, link='http://en.wikipedia.org/wiki/Tiger']
1627
1682
  assert_xpath '/*[@class="imageblock"]//a[@class="image"][@href="http://en.wikipedia.org/wiki/Tiger"]/img[@src="images/tiger.png"][@alt="Tiger"]', output, 1
1628
1683
  end
1629
1684
 
1630
- test "can render block image with caption" do
1685
+ test 'adds rel=noopener attribute to block image with link that targets _blank window' do
1686
+ input = <<-EOS
1687
+ image::images/tiger.png[Tiger,link=http://en.wikipedia.org/wiki/Tiger,window=_blank]
1688
+ EOS
1689
+
1690
+ output = render_embedded_string input
1691
+ assert_xpath '/*[@class="imageblock"]//a[@class="image"][@href="http://en.wikipedia.org/wiki/Tiger"][@target="_blank"][@rel="noopener"]/img[@src="images/tiger.png"][@alt="Tiger"]', output, 1
1692
+ end
1693
+
1694
+ test 'adds rel=noopener attribute to block image with link that targets name window when the noopener option is set' do
1695
+ input = <<-EOS
1696
+ image::images/tiger.png[Tiger,link=http://en.wikipedia.org/wiki/Tiger,window=name,opts=noopener]
1697
+ EOS
1698
+
1699
+ output = render_embedded_string input
1700
+ assert_xpath '/*[@class="imageblock"]//a[@class="image"][@href="http://en.wikipedia.org/wiki/Tiger"][@target="name"][@rel="noopener"]/img[@src="images/tiger.png"][@alt="Tiger"]', output, 1
1701
+ end
1702
+
1703
+ test 'can render block image with caption' do
1631
1704
  input = <<-EOS
1632
1705
  .The AsciiDoc Tiger
1633
1706
  image::images/tiger.png[Tiger]
1634
1707
  EOS
1635
1708
 
1636
1709
  doc = document_from_string input
1710
+ assert_equal 1, doc.blocks[0].number
1637
1711
  output = doc.render
1638
1712
  assert_xpath '//*[@class="imageblock"]//img[@src="images/tiger.png"][@alt="Tiger"]', output, 1
1639
1713
  assert_xpath '//*[@class="imageblock"]/*[@class="title"][text() = "Figure 1. The AsciiDoc Tiger"]', output, 1
@@ -1648,6 +1722,7 @@ image::images/tiger.png[Tiger]
1648
1722
  EOS
1649
1723
 
1650
1724
  doc = document_from_string input
1725
+ assert_nil doc.blocks[0].number
1651
1726
  output = doc.render
1652
1727
  assert_xpath '//*[@class="imageblock"]//img[@src="images/tiger.png"][@alt="Tiger"]', output, 1
1653
1728
  assert_xpath '//*[@class="imageblock"]/*[@class="title"][text() = "Voila! The AsciiDoc Tiger"]', output, 1
@@ -1656,7 +1731,7 @@ image::images/tiger.png[Tiger]
1656
1731
 
1657
1732
  test 'can align image in DocBook backend' do
1658
1733
  input = <<-EOS
1659
- image::images/sunset.jpg[Sunset, align="right"]
1734
+ image::images/sunset.jpg[Sunset,align=right]
1660
1735
  EOS
1661
1736
 
1662
1737
  output = render_embedded_string input, :backend => :docbook
@@ -1664,36 +1739,54 @@ image::images/sunset.jpg[Sunset, align="right"]
1664
1739
  assert_xpath '//imagedata[@align="right"]', output, 1
1665
1740
  end
1666
1741
 
1742
+ test 'should set content width and depth in DocBook backend if no scaling' do
1743
+ input = <<-EOS
1744
+ image::images/sunset.jpg[Sunset,500,332]
1745
+ EOS
1746
+
1747
+ output = render_embedded_string input, :backend => :docbook
1748
+ assert_xpath '//imagedata', output, 1
1749
+ assert_xpath '//imagedata[@contentwidth="500"]', output, 1
1750
+ assert_xpath '//imagedata[@contentdepth="332"]', output, 1
1751
+ assert_xpath '//imagedata[@width]', output, 0
1752
+ assert_xpath '//imagedata[@depth]', output, 0
1753
+ end
1754
+
1667
1755
  test 'can scale image in DocBook backend' do
1668
1756
  input = <<-EOS
1669
- image::images/sunset.jpg[Sunset, scale="200"]
1757
+ image::images/sunset.jpg[Sunset,500,332,scale=200]
1670
1758
  EOS
1671
1759
 
1672
1760
  output = render_embedded_string input, :backend => :docbook
1673
1761
  assert_xpath '//imagedata', output, 1
1674
1762
  assert_xpath '//imagedata[@scale="200"]', output, 1
1763
+ assert_xpath '//imagedata[@width]', output, 0
1764
+ assert_xpath '//imagedata[@depth]', output, 0
1765
+ assert_xpath '//imagedata[@contentwidth]', output, 0
1766
+ assert_xpath '//imagedata[@contentdepth]', output, 0
1675
1767
  end
1676
1768
 
1677
- test 'can scale image width in DocBook backend' do
1769
+ test 'scale image width in DocBook backend' do
1678
1770
  input = <<-EOS
1679
- image::images/sunset.jpg[Sunset, scaledwidth="25%"]
1771
+ image::images/sunset.jpg[Sunset,500,332,scaledwidth=25%]
1680
1772
  EOS
1681
1773
 
1682
1774
  output = render_embedded_string input, :backend => :docbook
1683
1775
  assert_xpath '//imagedata', output, 1
1684
1776
  assert_xpath '//imagedata[@width="25%"]', output, 1
1685
- assert_xpath '//imagedata[@scalefit="1"]', output, 1
1777
+ assert_xpath '//imagedata[@depth]', output, 0
1778
+ assert_xpath '//imagedata[@contentwidth]', output, 0
1779
+ assert_xpath '//imagedata[@contentdepth]', output, 0
1686
1780
  end
1687
1781
 
1688
1782
  test 'adds % to scaled width if no units given in DocBook backend ' do
1689
1783
  input = <<-EOS
1690
- image::images/sunset.jpg[Sunset, scaledwidth="25"]
1784
+ image::images/sunset.jpg[Sunset,scaledwidth=25]
1691
1785
  EOS
1692
1786
 
1693
1787
  output = render_embedded_string input, :backend => :docbook
1694
1788
  assert_xpath '//imagedata', output, 1
1695
1789
  assert_xpath '//imagedata[@width="25%"]', output, 1
1696
- assert_xpath '//imagedata[@scalefit="1"]', output, 1
1697
1790
  end
1698
1791
 
1699
1792
  test 'keeps line unprocessed if image target is missing attribute reference and attribute-missing is skip' do
@@ -1703,8 +1796,9 @@ image::images/sunset.jpg[Sunset, scaledwidth="25"]
1703
1796
  image::{bogus}[]
1704
1797
  EOS
1705
1798
 
1706
- output = render_embedded_string input
1799
+ output, warnings = redirect_streams {|_, err| [(render_embedded_string input), err.string] }
1707
1800
  assert output.include?('image::{bogus}[]')
1801
+ assert_includes warnings, 'dropping line containing reference to missing attribute'
1708
1802
  end
1709
1803
 
1710
1804
  test 'drops line if image target is missing attribute reference and attribute-missing is drop' do
@@ -1714,8 +1808,9 @@ image::{bogus}[]
1714
1808
  image::{bogus}[]
1715
1809
  EOS
1716
1810
 
1717
- output = render_embedded_string input
1811
+ output, warnings = redirect_streams {|_, err| [(render_embedded_string input), err.string] }
1718
1812
  assert output.strip.empty?
1813
+ assert_includes warnings, 'dropping line containing reference to missing attribute'
1719
1814
  end
1720
1815
 
1721
1816
  test 'drops line if image target is missing attribute reference and attribute-missing is drop-line' do
@@ -1725,8 +1820,9 @@ image::{bogus}[]
1725
1820
  image::{bogus}[]
1726
1821
  EOS
1727
1822
 
1728
- output = render_embedded_string input
1823
+ output, warnings = redirect_streams {|_, err| [(render_embedded_string input), err.string] }
1729
1824
  assert output.strip.empty?
1825
+ assert_includes warnings, 'dropping line containing reference to missing attribute'
1730
1826
  end
1731
1827
 
1732
1828
  test 'dropped image does not break processing of following section and attribute-missing is drop-line' do
@@ -1738,10 +1834,11 @@ image::{bogus}[]
1738
1834
  == Section Title
1739
1835
  EOS
1740
1836
 
1741
- output = render_embedded_string input
1837
+ output, warnings = redirect_streams {|_, err| [(render_embedded_string input), err.string] }
1742
1838
  assert_css 'img', output, 0
1743
1839
  assert_css 'h2', output, 1
1744
1840
  assert !output.include?('== Section Title')
1841
+ assert_includes warnings, 'dropping line containing reference to missing attribute'
1745
1842
  end
1746
1843
 
1747
1844
  test 'should pass through image that references uri' do
@@ -1755,6 +1852,15 @@ image::http://asciidoc.org/images/tiger.png[Tiger]
1755
1852
  assert_xpath '/*[@class="imageblock"]//img[@src="http://asciidoc.org/images/tiger.png"][@alt="Tiger"]', output, 1
1756
1853
  end
1757
1854
 
1855
+ test 'should encode spaces in image target if value is a URI' do
1856
+ input = <<-EOS
1857
+ image::http://example.org/svg?digraph=digraph G { a -> b; }[diagram]
1858
+ EOS
1859
+
1860
+ output = render_embedded_string input
1861
+ assert_xpath %(/*[@class="imageblock"]//img[@src="http://example.org/svg?digraph=digraph%20G%20{%20a%20-#{decode_char 62}%20b;%20}"]), output, 1
1862
+ end
1863
+
1758
1864
  test 'can resolve image relative to imagesdir' do
1759
1865
  input = <<-EOS
1760
1866
  :imagesdir: images
@@ -1780,6 +1886,21 @@ image::dot.gif[Dot]
1780
1886
  assert_xpath '//img[@src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="][@alt="Dot"]', output, 1
1781
1887
  end
1782
1888
 
1889
+ test 'embeds empty base64-encoded data uri for unreadable image when data-uri attribute is set' do
1890
+ input = <<-EOS
1891
+ :data-uri:
1892
+ :imagesdir: fixtures
1893
+
1894
+ image::unreadable.gif[Dot]
1895
+ EOS
1896
+
1897
+ doc = document_from_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => File.dirname(__FILE__)}
1898
+ assert_equal 'fixtures', doc.attributes['imagesdir']
1899
+ output, warnings = redirect_streams {|_, err| [doc.render, err.string] }
1900
+ assert_xpath '//img[@src="data:image/gif;base64,"]', output, 1
1901
+ assert_includes warnings, 'image to embed not found or not readable'
1902
+ end
1903
+
1783
1904
  test 'embeds base64-encoded data uri for remote image when data-uri attribute is set' do
1784
1905
  input = <<-EOS
1785
1906
  :data-uri:
@@ -1817,11 +1938,16 @@ image::dot.gif[Dot]
1817
1938
  image::#{image_uri}[Missing image]
1818
1939
  EOS
1819
1940
 
1820
- output = using_test_webserver do
1821
- render_embedded_string input, :safe => :safe, :attributes => {'allow-uri-read' => ''}
1941
+ output = warnings = nil
1942
+ redirect_streams do |_, err|
1943
+ output = using_test_webserver do
1944
+ render_embedded_string input, :safe => :safe, :attributes => {'allow-uri-read' => ''}
1945
+ end
1946
+ warnings = err.string
1822
1947
  end
1823
1948
 
1824
1949
  assert_xpath %(/*[@class="imageblock"]//img[@src="#{image_uri}"][@alt="Missing image"]), output, 1
1950
+ assert_includes warnings, 'could not retrieve image data from URI'
1825
1951
  end
1826
1952
 
1827
1953
  test 'uses remote image uri when data-uri attribute is set and allow-uri-read is not set' do
@@ -1859,7 +1985,6 @@ image::data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=[Do
1859
1985
  assert_xpath '//img[@src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="][@alt="Dot"]', output, 1
1860
1986
  end
1861
1987
 
1862
- # this test will cause a warning to be printed to the console (until we have a message facility)
1863
1988
  test 'cleans reference to ancestor directories in imagesdir before reading image if safe mode level is at least SAFE' do
1864
1989
  input = <<-EOS
1865
1990
  :data-uri:
@@ -1870,10 +1995,11 @@ image::dot.gif[Dot]
1870
1995
 
1871
1996
  doc = document_from_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => File.dirname(__FILE__)}
1872
1997
  assert_equal '../..//fixtures/./../../fixtures', doc.attributes['imagesdir']
1873
- output = doc.render
1998
+ output, warnings = redirect_streams {|_, err| [doc.render, err.string] }
1874
1999
  # image target resolves to fixtures/dot.gif relative to docdir (which is explicitly set to the directory of this file)
1875
2000
  # the reference cannot fall outside of the document directory in safe mode
1876
2001
  assert_xpath '//img[@src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="][@alt="Dot"]', output, 1
2002
+ assert_includes warnings, 'image has illegal reference to ancestor of jail'
1877
2003
  end
1878
2004
 
1879
2005
  test 'cleans reference to ancestor directories in target before reading image if safe mode level is at least SAFE' do
@@ -1886,10 +2012,11 @@ image::../..//fixtures/./../../fixtures/dot.gif[Dot]
1886
2012
 
1887
2013
  doc = document_from_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => File.dirname(__FILE__)}
1888
2014
  assert_equal './', doc.attributes['imagesdir']
1889
- output = doc.render
2015
+ output, warnings = redirect_streams {|_, err| [doc.render, err.string] }
1890
2016
  # image target resolves to fixtures/dot.gif relative to docdir (which is explicitly set to the directory of this file)
1891
2017
  # the reference cannot fall outside of the document directory in safe mode
1892
2018
  assert_xpath '//img[@src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="][@alt="Dot"]', output, 1
2019
+ assert_includes warnings, 'image has illegal reference to ancestor of jail'
1893
2020
  end
1894
2021
  end
1895
2022
 
@@ -1919,7 +2046,7 @@ video::cats-vs-dogs.avi[cats-and-dogs.png, 200, 300]
1919
2046
 
1920
2047
  test 'video macro should honor all options' do
1921
2048
  input = <<-EOS
1922
- video::cats-vs-dogs.avi[options="autoplay,nocontrols,loop"]
2049
+ video::cats-vs-dogs.avi[options="autoplay,nocontrols,loop",preload="metadata"]
1923
2050
  EOS
1924
2051
 
1925
2052
  output = render_embedded_string input
@@ -1927,6 +2054,7 @@ video::cats-vs-dogs.avi[options="autoplay,nocontrols,loop"]
1927
2054
  assert_css 'video[autoplay]', output, 1
1928
2055
  assert_css 'video:not([controls])', output, 1
1929
2056
  assert_css 'video[loop]', output, 1
2057
+ assert_css 'video[preload=metadata]', output, 1
1930
2058
  end
1931
2059
 
1932
2060
  test 'video macro should add time range anchor with start time if start attribute is set' do
@@ -2067,6 +2195,17 @@ audio::podcast.mp3[options="autoplay,nocontrols,loop"]
2067
2195
  assert_css 'audio:not([controls])', output, 1
2068
2196
  assert_css 'audio[loop]', output, 1
2069
2197
  end
2198
+
2199
+ test 'audio macro should support start and end time' do
2200
+ input = <<-EOS
2201
+ audio::podcast.mp3[start=1,end=2]
2202
+ EOS
2203
+
2204
+ output = render_embedded_string input
2205
+ assert_css 'audio', output, 1
2206
+ assert_css 'audio[controls]', output, 1
2207
+ assert_css 'audio[src="podcast.mp3#t=1,2"]', output, 1
2208
+ end
2070
2209
  end
2071
2210
 
2072
2211
  context 'Admonition icons' do
@@ -2149,8 +2288,11 @@ You can use icons for admonitions by setting the 'icons' attribute.
2149
2288
  You can use icons for admonitions by setting the 'icons' attribute.
2150
2289
  EOS
2151
2290
 
2152
- output = render_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => File.dirname(__FILE__)}
2291
+ output, warnings = redirect_streams do |_, err|
2292
+ [(render_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => File.dirname(__FILE__)}), err.string]
2293
+ end
2153
2294
  assert_xpath '//*[@class="admonitionblock tip"]//*[@class="icon"]/img[@src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="][@alt="Tip"]', output, 1
2295
+ assert_includes warnings, 'image has illegal reference to ancestor of jail'
2154
2296
  end
2155
2297
 
2156
2298
  test 'should import Font Awesome and use font-based icons when value of icons attribute is font' do
@@ -2194,7 +2336,7 @@ puts "AsciiDoc, FTW!"
2194
2336
 
2195
2337
  output = render_string input, :safe => Asciidoctor::SafeMode::SAFE
2196
2338
  assert_css 'html > head > link[rel="stylesheet"][href="http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css"]', output, 1
2197
- assert_css 'html > body > script[src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js"]', output, 1
2339
+ assert_css 'html > body > script[src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"]', output, 1
2198
2340
  end
2199
2341
 
2200
2342
  test 'should use no uri scheme for assets when asset-uri-scheme is blank' do
@@ -2211,7 +2353,7 @@ puts "AsciiDoc, FTW!"
2211
2353
 
2212
2354
  output = render_string input, :safe => Asciidoctor::SafeMode::SAFE
2213
2355
  assert_css 'html > head > link[rel="stylesheet"][href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css"]', output, 1
2214
- assert_css 'html > body > script[src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js"]', output, 1
2356
+ assert_css 'html > body > script[src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"]', output, 1
2215
2357
  end
2216
2358
  end
2217
2359
 
@@ -2227,8 +2369,8 @@ image::asciidoctor.png[Asciidoctor]
2227
2369
  assert doc.safe >= Asciidoctor::SafeMode::SAFE
2228
2370
 
2229
2371
  assert_equal File.join(basedir, 'images'), block.normalize_asset_path('images')
2230
- assert_equal File.join(basedir, 'etc/images'), block.normalize_asset_path("#{disk_root}etc/images")
2231
- assert_equal File.join(basedir, 'images'), block.normalize_asset_path('../../images')
2372
+ assert_equal File.join(basedir, 'etc/images'), redirect_streams { block.normalize_asset_path("#{disk_root}etc/images") }
2373
+ assert_equal File.join(basedir, 'images'), redirect_streams { block.normalize_asset_path('../../images') }
2232
2374
  end
2233
2375
 
2234
2376
  test 'does not restrict access to ancestor directories when safe mode is disabled' do
@@ -2327,6 +2469,46 @@ html = CodeRay.scan("puts 'Hello, world!'", :ruby).div(:line_numbers => :table)
2327
2469
  assert_match(/\.CodeRay *\{/, output)
2328
2470
  end
2329
2471
 
2472
+ test 'should number lines if third positional attribute is set' do
2473
+ input = <<-EOS
2474
+ :source-highlighter: coderay
2475
+
2476
+ [source,ruby,linenums]
2477
+ ----
2478
+ puts 'Hello, World!'
2479
+ ----
2480
+ EOS
2481
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SAFE
2482
+ assert_xpath '//td[@class="line-numbers"]', output, 1
2483
+ end
2484
+
2485
+ test 'should number lines if linenums option is set on source block' do
2486
+ input = <<-EOS
2487
+ :source-highlighter: coderay
2488
+
2489
+ [source%linenums,ruby]
2490
+ ----
2491
+ puts 'Hello, World!'
2492
+ ----
2493
+ EOS
2494
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SAFE
2495
+ assert_xpath '//td[@class="line-numbers"]', output, 1
2496
+ end
2497
+
2498
+ test 'should number lines of source block if source-linenums-option document attribute is set' do
2499
+ input = <<-EOS
2500
+ :source-highlighter: coderay
2501
+ :source-linenums-option:
2502
+
2503
+ [source,ruby]
2504
+ ----
2505
+ puts 'Hello, World!'
2506
+ ----
2507
+ EOS
2508
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SAFE
2509
+ assert_xpath '//td[@class="line-numbers"]', output, 1
2510
+ end
2511
+
2330
2512
  test 'should read source language from source-language document attribute if not specified on source block' do
2331
2513
  input = <<-EOS
2332
2514
  :source-highlighter: coderay
@@ -2414,6 +2596,43 @@ exit 0 # <5><6>
2414
2596
  assert_match(/exit.* <b class="conum">\(5\)<\/b> <b class="conum">\(6\)<\/b><\/pre>/, output)
2415
2597
  end
2416
2598
 
2599
+ test 'should preserve space before callout on final line' do
2600
+ inputs = []
2601
+
2602
+ inputs << <<-EOS
2603
+ [source,yaml]
2604
+ ----
2605
+ a: 'a'
2606
+ key: 'value' #<1>
2607
+ ----
2608
+ <1> key-value pair
2609
+ EOS
2610
+
2611
+ inputs << <<-EOS
2612
+ [source,ruby]
2613
+ ----
2614
+ puts 'hi'
2615
+ puts 'value' #<1>
2616
+ ----
2617
+ <1> print to stdout
2618
+ EOS
2619
+
2620
+ inputs << <<-EOS
2621
+ [source,python]
2622
+ ----
2623
+ print 'hi'
2624
+ print 'value' #<1>
2625
+ ----
2626
+ <1> print to stdout
2627
+ EOS
2628
+
2629
+ inputs.each do |input|
2630
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => { 'source-highlighter' => 'coderay' }
2631
+ output = output.gsub(/<\/?span.*?>/, '')
2632
+ assert_includes output, '\'value\' <b class="conum">(1)</b>'
2633
+ end
2634
+ end
2635
+
2417
2636
  test 'should preserve passthrough placeholders when highlighting source using coderay' do
2418
2637
  input = <<-EOS
2419
2638
  :source-highlighter: coderay
@@ -2741,8 +2960,9 @@ content
2741
2960
  part intro paragraph
2742
2961
  EOS
2743
2962
 
2744
- output = render_string input
2963
+ output, warnings = redirect_streams {|_, err| [(render_string input), err.string] }
2745
2964
  assert_css '.partintro', output, 0
2965
+ assert_includes warnings, 'partintro block can only be used when doctype is book and it\'s a child of a book part'
2746
2966
  end
2747
2967
 
2748
2968
  test 'should not allow partintro unless doctype is book' do
@@ -2751,8 +2971,9 @@ part intro paragraph
2751
2971
  part intro paragraph
2752
2972
  EOS
2753
2973
 
2754
- output = render_string input
2974
+ output, warnings = redirect_streams {|_, err| [(render_string input), err.string] }
2755
2975
  assert_css '.partintro', output, 0
2976
+ assert_includes warnings, 'partintro block can only be used when doctype is book and it\'s a child of a book part'
2756
2977
  end
2757
2978
 
2758
2979
  test 'should accept partintro on open block without title rendered to DocBook' do
@@ -2814,8 +3035,9 @@ content
2814
3035
  part intro paragraph
2815
3036
  EOS
2816
3037
 
2817
- output = render_string input, :backend => 'docbook'
3038
+ output, warnings = redirect_streams {|_, err| [(render_string input, :backend => 'docbook'), err.string] }
2818
3039
  assert_css 'partintro', output, 0
3040
+ assert_includes warnings, 'partintro block can only be used when doctype is book and it\'s a child of a part section'
2819
3041
  end
2820
3042
 
2821
3043
  test 'should not allow partintro unless doctype is book rendered to DocBook' do
@@ -2824,12 +3046,26 @@ part intro paragraph
2824
3046
  part intro paragraph
2825
3047
  EOS
2826
3048
 
2827
- output = render_string input, :backend => 'docbook'
3049
+ output, warnings = redirect_streams {|_, err| [(render_string input, :backend => 'docbook'), err.string] }
2828
3050
  assert_css 'partintro', output, 0
3051
+ assert_includes warnings, 'partintro block can only be used when doctype is book and it\'s a child of a part section'
2829
3052
  end
2830
3053
  end
2831
3054
 
2832
3055
  context 'Substitutions' do
3056
+ test 'processor should not crash if subs are empty' do
3057
+ input = <<-EOS
3058
+ [subs=","]
3059
+ ....
3060
+ content
3061
+ ....
3062
+ EOS
3063
+
3064
+ doc = document_from_string input
3065
+ block = doc.blocks.first
3066
+ assert_equal [], block.subs
3067
+ end
3068
+
2833
3069
  test 'should be able to append subs to default block substitution list' do
2834
3070
  input = <<-EOS
2835
3071
  :application: Asciidoctor
@@ -2915,7 +3151,32 @@ content
2915
3151
  block = doc.blocks.first
2916
3152
  assert_nil block.id
2917
3153
  assert_nil(block.attr 'reftext')
2918
- assert !doc.references[:ids].has_key?('illegal$id')
3154
+ assert !doc.catalog[:ids].has_key?('illegal$id')
3155
+ end
3156
+
3157
+ test 'should not recognize block anchor that starts with digit' do
3158
+ input = <<-EOS
3159
+ [[3-blind-mice]]
3160
+ --
3161
+ see how they run
3162
+ --
3163
+ EOS
3164
+
3165
+ output = render_embedded_string input
3166
+ assert_includes output, '[[3-blind-mice]]'
3167
+ assert_xpath '/*[@id = ":3-blind-mice"]', output, 0
3168
+ end
3169
+
3170
+ test 'should recognize block anchor that starts with colon' do
3171
+ input = <<-EOS
3172
+ [[:idname]]
3173
+ --
3174
+ content
3175
+ --
3176
+ EOS
3177
+
3178
+ output = render_embedded_string input
3179
+ assert_xpath '/*[@id = ":idname"]', output, 1
2919
3180
  end
2920
3181
 
2921
3182
  test 'should use specified id and reftext when registering block reference' do
@@ -2928,7 +3189,7 @@ $ apt-get install asciidoctor
2928
3189
  EOS
2929
3190
 
2930
3191
  doc = document_from_string input
2931
- reftext = doc.references[:ids]['debian']
3192
+ reftext = doc.catalog[:ids]['debian']
2932
3193
  refute_nil reftext
2933
3194
  assert_equal 'Debian Install', reftext
2934
3195
  end
@@ -2943,7 +3204,7 @@ $ apt-get install asciidoctor
2943
3204
  EOS
2944
3205
 
2945
3206
  doc = document_from_string input
2946
- reftext = doc.references[:ids]['debian']
3207
+ reftext = doc.catalog[:ids]['debian']
2947
3208
  refute_nil reftext
2948
3209
  assert_equal '[Debian] Install', reftext
2949
3210
  end
@@ -2958,11 +3219,30 @@ $ apt-get install asciidoctor
2958
3219
  EOS
2959
3220
 
2960
3221
  doc = document_from_string input
2961
- reftext = doc.references[:ids]['debian']
3222
+ reftext = doc.catalog[:ids]['debian']
2962
3223
  refute_nil reftext
2963
3224
  assert_equal 'Debian, Ubuntu', reftext
2964
3225
  end
2965
3226
 
3227
+ test 'should substitute attribute references in reftext when registering block reference' do
3228
+ input = <<-EOS
3229
+ :label-tiger: Tiger
3230
+
3231
+ [[tiger-evolution,Evolution of the {label-tiger}]]
3232
+ ****
3233
+ Information about the evolution of the tiger.
3234
+ ****
3235
+ EOS
3236
+
3237
+ doc = document_from_string input
3238
+ reftext = doc.catalog[:ids]['tiger-evolution']
3239
+ refute_nil reftext
3240
+ assert_equal 'Evolution of the Tiger', reftext
3241
+ ref = doc.catalog[:refs]['tiger-evolution']
3242
+ refute_nil ref
3243
+ assert_equal 'Evolution of the Tiger', ref.attributes['reftext']
3244
+ end
3245
+
2966
3246
  test 'should use specified reftext when registering block reference' do
2967
3247
  input = <<-EOS
2968
3248
  [[debian]]
@@ -2974,7 +3254,7 @@ $ apt-get install asciidoctor
2974
3254
  EOS
2975
3255
 
2976
3256
  doc = document_from_string input
2977
- reftext = doc.references[:ids]['debian']
3257
+ reftext = doc.catalog[:ids]['debian']
2978
3258
  refute_nil reftext
2979
3259
  assert_equal 'Debian Install', reftext
2980
3260
  end