asciidoctor 1.5.2 → 1.5.3

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +107 -1
  3. data/LICENSE.adoc +1 -1
  4. data/README.adoc +155 -230
  5. data/Rakefile +2 -1
  6. data/bin/asciidoctor +5 -1
  7. data/data/stylesheets/asciidoctor-default.css +37 -29
  8. data/data/stylesheets/coderay-asciidoctor.css +3 -3
  9. data/features/text_formatting.feature +2 -0
  10. data/lib/asciidoctor.rb +46 -21
  11. data/lib/asciidoctor/abstract_block.rb +14 -8
  12. data/lib/asciidoctor/abstract_node.rb +77 -24
  13. data/lib/asciidoctor/attribute_list.rb +1 -1
  14. data/lib/asciidoctor/block.rb +2 -3
  15. data/lib/asciidoctor/cli/options.rb +14 -15
  16. data/lib/asciidoctor/converter/docbook45.rb +8 -8
  17. data/lib/asciidoctor/converter/docbook5.rb +25 -17
  18. data/lib/asciidoctor/converter/factory.rb +6 -1
  19. data/lib/asciidoctor/converter/html5.rb +159 -117
  20. data/lib/asciidoctor/converter/manpage.rb +671 -0
  21. data/lib/asciidoctor/converter/template.rb +24 -17
  22. data/lib/asciidoctor/document.rb +89 -47
  23. data/lib/asciidoctor/extensions.rb +22 -21
  24. data/lib/asciidoctor/helpers.rb +73 -16
  25. data/lib/asciidoctor/list.rb +26 -5
  26. data/lib/asciidoctor/parser.rb +179 -122
  27. data/lib/asciidoctor/path_resolver.rb +6 -10
  28. data/lib/asciidoctor/reader.rb +37 -34
  29. data/lib/asciidoctor/stylesheets.rb +16 -10
  30. data/lib/asciidoctor/substitutors.rb +98 -21
  31. data/lib/asciidoctor/table.rb +21 -17
  32. data/lib/asciidoctor/timings.rb +3 -3
  33. data/lib/asciidoctor/version.rb +1 -1
  34. data/man/asciidoctor.1 +155 -89
  35. data/man/asciidoctor.adoc +19 -11
  36. data/test/attributes_test.rb +86 -0
  37. data/test/blocks_test.rb +203 -15
  38. data/test/converter_test.rb +15 -2
  39. data/test/document_test.rb +290 -36
  40. data/test/extensions_test.rb +22 -3
  41. data/test/fixtures/circle.svg +8 -0
  42. data/test/fixtures/subs-docinfo.html +2 -0
  43. data/test/fixtures/subs.adoc +7 -0
  44. data/test/invoker_test.rb +25 -0
  45. data/test/links_test.rb +17 -0
  46. data/test/lists_test.rb +173 -0
  47. data/test/options_test.rb +2 -2
  48. data/test/paragraphs_test.rb +2 -2
  49. data/test/parser_test.rb +56 -13
  50. data/test/reader_test.rb +35 -3
  51. data/test/sections_test.rb +59 -0
  52. data/test/substitutions_test.rb +53 -14
  53. data/test/tables_test.rb +158 -2
  54. data/test/test_helper.rb +7 -2
  55. metadata +22 -11
  56. data/benchmark/benchmark.rb +0 -129
  57. data/benchmark/sample-data/mdbasics.adoc +0 -334
  58. data/lib/asciidoctor/opal_ext.rb +0 -26
  59. data/lib/asciidoctor/opal_ext/comparable.rb +0 -38
  60. data/lib/asciidoctor/opal_ext/dir.rb +0 -13
  61. data/lib/asciidoctor/opal_ext/error.rb +0 -2
  62. data/lib/asciidoctor/opal_ext/file.rb +0 -145
@@ -312,6 +312,19 @@ content
312
312
  assert_equal 'baz', (node.attr 'foo')
313
313
  end
314
314
 
315
+ test 'set_attr should set header attribute in loaded document' do
316
+ input = <<-EOS
317
+ :uri: http://example.org
318
+
319
+ {uri}
320
+ EOS
321
+
322
+ doc = Asciidoctor.load input, :attributes => { 'uri' => 'https://github.com' }
323
+ doc.set_attr 'uri', 'https://google.com'
324
+ output = doc.convert
325
+ assert_xpath '//a[@href="https://google.com"]', output, 1
326
+ end
327
+
315
328
  test 'verify toc attribute matrix' do
316
329
  expected_data = <<-EOS
317
330
  #attributes |toc|toc-position|toc-placement|toc-class
@@ -1054,6 +1067,79 @@ Text
1054
1067
  assert para.attributes.has_key?('option2-option')
1055
1068
  end
1056
1069
 
1070
+ test 'a role can be added using add_role when the node has no roles' do
1071
+ input = <<-EOS
1072
+ A normal paragraph
1073
+ EOS
1074
+ doc = document_from_string(input)
1075
+ para = doc.blocks.first
1076
+ para.add_role 'role1'
1077
+ assert_equal 'role1', para.attributes['role']
1078
+ assert para.has_role? 'role1'
1079
+ end
1080
+
1081
+ test 'a role can be added using add_role when the node already has a role' do
1082
+ input = <<-EOS
1083
+ [.role1]
1084
+ A normal paragraph
1085
+ EOS
1086
+ doc = document_from_string(input)
1087
+ para = doc.blocks.first
1088
+ para.add_role 'role2'
1089
+ assert_equal 'role1 role2', para.attributes['role']
1090
+ assert para.has_role? 'role1'
1091
+ assert para.has_role? 'role2'
1092
+ end
1093
+
1094
+ test 'a role is not added using add_role if the node already has that role' do
1095
+ input = <<-EOS
1096
+ [.role1]
1097
+ A normal paragraph
1098
+ EOS
1099
+ doc = document_from_string(input)
1100
+ para = doc.blocks.first
1101
+ para.add_role 'role1'
1102
+ assert_equal 'role1', para.attributes['role']
1103
+ assert para.has_role? 'role1'
1104
+ end
1105
+
1106
+ test 'an existing role can be removed using remove_role' do
1107
+ input = <<-EOS
1108
+ [.role1.role2]
1109
+ A normal paragraph
1110
+ EOS
1111
+ doc = document_from_string(input)
1112
+ para = doc.blocks.first
1113
+ para.remove_role 'role1'
1114
+ assert_equal 'role2', para.attributes['role']
1115
+ assert para.has_role? 'role2'
1116
+ assert !para.has_role?('role1')
1117
+ end
1118
+
1119
+ test 'roles are not changed when a non-existent role is removed using remove_role' do
1120
+ input = <<-EOS
1121
+ [.role1]
1122
+ A normal paragraph
1123
+ EOS
1124
+ doc = document_from_string(input)
1125
+ para = doc.blocks.first
1126
+ para.remove_role 'role2'
1127
+ assert_equal 'role1', para.attributes['role']
1128
+ assert para.has_role? 'role1'
1129
+ assert !para.has_role?('role2')
1130
+ end
1131
+
1132
+ test 'roles are not changed when using remove_role if the node has no roles' do
1133
+ input = <<-EOS
1134
+ A normal paragraph
1135
+ EOS
1136
+ doc = document_from_string(input)
1137
+ para = doc.blocks.first
1138
+ para.remove_role 'role1'
1139
+ assert_equal nil, para.attributes['role']
1140
+ assert !para.has_role?('role1')
1141
+ end
1142
+
1057
1143
  test 'option can be specified in first position of block style using shorthand syntax' do
1058
1144
  input = <<-EOS
1059
1145
  [%interactive]
@@ -823,6 +823,33 @@ end
823
823
  assert_equal expected.chomp, result
824
824
  end
825
825
 
826
+ test 'should not remove block indent if indent attribute is -1' do
827
+ input = <<-EOS
828
+ [indent="-1"]
829
+ ----
830
+ def names
831
+
832
+ @names.split ' '
833
+
834
+ end
835
+ ----
836
+ EOS
837
+
838
+ expected = <<-EOS
839
+ def names
840
+
841
+ @names.split ' '
842
+
843
+ end
844
+ EOS
845
+
846
+ output = render_embedded_string input
847
+ assert_css 'pre', output, 1
848
+ assert_css '.listingblock pre', output, 1
849
+ result = xmlnodes_at_xpath('//pre', output, 1).text
850
+ assert_equal expected.chomp, result
851
+ end
852
+
826
853
  test 'should set block indent to value specified by indent attribute' do
827
854
  input = <<-EOS
828
855
  [indent="1"]
@@ -837,9 +864,9 @@ end
837
864
 
838
865
  expected = <<-EOS
839
866
  def names
840
-
867
+
841
868
  @names.split ' '
842
-
869
+
843
870
  end
844
871
  EOS
845
872
 
@@ -850,6 +877,64 @@ end
850
877
  assert_equal expected.chomp, result
851
878
  end
852
879
 
880
+ test 'should set block indent to value specified by indent document attribute' do
881
+ input = <<-EOS
882
+ :source-indent: 1
883
+
884
+ [source,ruby]
885
+ ----
886
+ def names
887
+
888
+ @names.split ' '
889
+
890
+ end
891
+ ----
892
+ EOS
893
+
894
+ expected = <<-EOS
895
+ def names
896
+
897
+ @names.split ' '
898
+
899
+ end
900
+ EOS
901
+
902
+ output = render_embedded_string input
903
+ assert_css 'pre', output, 1
904
+ assert_css '.listingblock pre', output, 1
905
+ result = xmlnodes_at_xpath('//pre', output, 1).text
906
+ assert_equal expected.chomp, result
907
+ end
908
+
909
+ test 'should expand tabs if tabsize attribute is positive' do
910
+ input = <<-EOS
911
+ :tabsize: 4
912
+
913
+ [indent=0]
914
+ ----
915
+ def names
916
+
917
+ @names.split ' '
918
+
919
+ end
920
+ ----
921
+ EOS
922
+
923
+ expected = <<-EOS
924
+ def names
925
+
926
+ @names.split ' '
927
+
928
+ end
929
+ EOS
930
+
931
+ output = render_embedded_string input
932
+ assert_css 'pre', output, 1
933
+ assert_css '.listingblock pre', output, 1
934
+ result = xmlnodes_at_xpath('//pre', output, 1).text
935
+ assert_equal expected.chomp, result
936
+ end
937
+
853
938
  test 'literal block should honor nowrap option' do
854
939
  input = <<-EOS
855
940
  [options="nowrap"]
@@ -1200,11 +1285,9 @@ x+b/(2a)<+-sqrt((b^2)/(4a^2)-c/a)
1200
1285
  ++++
1201
1286
  EOS
1202
1287
 
1203
- expect = <<-'EOS'
1204
- <informalequation>
1205
- <mediaobject><textobject><phrase><![CDATA[x+b/(2a)<+-sqrt((b^2)/(4a^2)-c/a)]]></phrase></textobject></mediaobject>
1206
- </informalequation>
1207
- EOS
1288
+ expect = %(<informalequation>
1289
+ <mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>x</mml:mi><mml:mo>+</mml:mo><mml:mfrac><mml:mi>b</mml:mi><mml:mrow><mml:mn>2</mml:mn><mml:mi>a</mml:mi></mml:mrow></mml:mfrac><mml:mo>&#x003C;</mml:mo><mml:mo>&#x00B1;</mml:mo><mml:msqrt><mml:mrow><mml:mfrac><mml:msup><mml:mi>b</mml:mi><mml:mn>2</mml:mn></mml:msup><mml:mrow><mml:mn>4</mml:mn><mml:msup><mml:mi>a</mml:mi><mml:mn>2</mml:mn></mml:msup></mml:mrow></mml:mfrac><mml:mo>&#x2212;</mml:mo><mml:mfrac><mml:mi>c</mml:mi><mml:mi>a</mml:mi></mml:mfrac></mml:mrow></mml:msqrt></mml:math>
1290
+ </informalequation>)
1208
1291
 
1209
1292
  output = render_embedded_string input, :backend => :docbook
1210
1293
  assert_equal expect.strip, output.strip
@@ -1364,6 +1447,111 @@ image::images/tiger.png[Tiger]
1364
1447
  assert_xpath '/*[@class="imageblock"]//img[@src="images/tiger.png"][@alt="Tiger"]', output, 1
1365
1448
  end
1366
1449
 
1450
+ test 'renders SVG image using img element by default' do
1451
+ input = <<-EOS
1452
+ image::tiger.svg[Tiger]
1453
+ EOS
1454
+
1455
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SERVER
1456
+ assert_xpath '/*[@class="imageblock"]//img[@src="tiger.svg"][@alt="Tiger"]', output, 1
1457
+ end
1458
+
1459
+ test 'renders interactive SVG image with alt text using object element' do
1460
+ input = <<-EOS
1461
+ :imagesdir: images
1462
+
1463
+ [%interactive]
1464
+ image::tiger.svg[Tiger,100]
1465
+ EOS
1466
+
1467
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SERVER
1468
+ assert_xpath '/*[@class="imageblock"]//object[@type="image/svg+xml"][@data="images/tiger.svg"][@width="100"]/span[@class="alt"][text()="Tiger"]', output, 1
1469
+ end
1470
+
1471
+ test 'renders SVG image with alt text using img element when safe mode is secure' do
1472
+ input = <<-EOS
1473
+ [%interactive]
1474
+ image::images/tiger.svg[Tiger,100]
1475
+ EOS
1476
+
1477
+ output = render_embedded_string input
1478
+ assert_xpath '/*[@class="imageblock"]//img[@src="images/tiger.svg"][@alt="Tiger"]', output, 1
1479
+ end
1480
+
1481
+ test 'inserts fallback image for SVG inside object element using same dimensions' do
1482
+ input = <<-EOS
1483
+ :imagesdir: images
1484
+
1485
+ [%interactive]
1486
+ image::tiger.svg[Tiger,100,fallback=tiger.png]
1487
+ EOS
1488
+
1489
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SERVER
1490
+ assert_xpath '/*[@class="imageblock"]//object[@type="image/svg+xml"][@data="images/tiger.svg"][@width="100"]/img[@src="images/tiger.png"][@width="100"]', output, 1
1491
+ end
1492
+
1493
+ test 'detects SVG image URI that contains a query string' do
1494
+ input = <<-EOS
1495
+ :imagesdir: images
1496
+
1497
+ [%interactive]
1498
+ image::http://example.org/tiger.svg?foo=bar[Tiger,100]
1499
+ EOS
1500
+
1501
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SERVER
1502
+ assert_xpath '/*[@class="imageblock"]//object[@type="image/svg+xml"][@data="http://example.org/tiger.svg?foo=bar"][@width="100"]/span[@class="alt"][text()="Tiger"]', output, 1
1503
+ end
1504
+
1505
+ test 'detects SVG image when format attribute is svg' do
1506
+ input = <<-EOS
1507
+ :imagesdir: images
1508
+
1509
+ [%interactive]
1510
+ image::http://example.org/tiger-svg[Tiger,100,format=svg]
1511
+ EOS
1512
+
1513
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SERVER
1514
+ assert_xpath '/*[@class="imageblock"]//object[@type="image/svg+xml"][@data="http://example.org/tiger-svg"][@width="100"]/span[@class="alt"][text()="Tiger"]', output, 1
1515
+ end
1516
+
1517
+ test 'renders inline SVG image using svg element' do
1518
+ input = <<-EOS
1519
+ :imagesdir: fixtures
1520
+
1521
+ [%inline]
1522
+ image::circle.svg[Tiger,100]
1523
+ EOS
1524
+
1525
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SERVER, :attributes => { 'docdir' => ::File.dirname(__FILE__) }
1526
+ assert_match(/<svg [^>]*width="100px"[^>]*>/, output, 1)
1527
+ refute_match(/<svg [^>]*width="500px"[^>]*>/, output)
1528
+ refute_match(/<svg [^>]*height="500px"[^>]*>/, output)
1529
+ refute_match(/<svg [^>]*style="width:500px;height:500px"[^>]*>/, output)
1530
+ end
1531
+
1532
+ test 'renders inline SVG image using svg element even when data-uri is set' do
1533
+ input = <<-EOS
1534
+ :imagesdir: fixtures
1535
+ :data-uri:
1536
+
1537
+ [%inline]
1538
+ image::circle.svg[Tiger,100]
1539
+ EOS
1540
+
1541
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SERVER, :attributes => { 'docdir' => ::File.dirname(__FILE__) }
1542
+ assert_match(/<svg [^>]*width="100px">/, output, 1)
1543
+ end
1544
+
1545
+ test 'renders alt text for inline svg element if svg cannot be read' do
1546
+ input = <<-EOS
1547
+ [%inline]
1548
+ image::no-such-image.svg[Alt Text]
1549
+ EOS
1550
+
1551
+ output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SERVER
1552
+ assert_xpath '//span[@class="alt"][text()="Alt Text"]', output, 1
1553
+ end
1554
+
1367
1555
  test 'can render block image with alt text defined in macro containing escaped square bracket' do
1368
1556
  input = <<-EOS
1369
1557
  image::images/tiger.png[A [Bengal\\] Tiger]
@@ -1805,7 +1993,7 @@ video::67480300[vimeo, 400, 300, start=60, options=autoplay]
1805
1993
  output = render_embedded_string input
1806
1994
  assert_css 'video', output, 0
1807
1995
  assert_css 'iframe', output, 1
1808
- assert_css 'iframe[src="//player.vimeo.com/video/67480300#at=60?autoplay=1"]', output, 1
1996
+ assert_css 'iframe[src="https://player.vimeo.com/video/67480300#at=60?autoplay=1"]', output, 1
1809
1997
  assert_css 'iframe[width="400"]', output, 1
1810
1998
  assert_css 'iframe[height="300"]', output, 1
1811
1999
  end
@@ -1817,7 +2005,7 @@ video::U8GBXvdmHT4/PLg7s6cbtAD15Das5LK9mXt_g59DLWxKUe[youtube, 640, 360, start=6
1817
2005
  output = render_embedded_string input
1818
2006
  assert_css 'video', output, 0
1819
2007
  assert_css 'iframe', output, 1
1820
- assert_css 'iframe[src="//www.youtube.com/embed/U8GBXvdmHT4?rel=0&start=60&autoplay=1&list=PLg7s6cbtAD15Das5LK9mXt_g59DLWxKUe&modestbranding=1&theme=light"]', output, 1
2008
+ assert_css 'iframe[src="https://www.youtube.com/embed/U8GBXvdmHT4?rel=0&start=60&autoplay=1&list=PLg7s6cbtAD15Das5LK9mXt_g59DLWxKUe&modestbranding=1&theme=light"]', output, 1
1821
2009
  assert_css 'iframe[width="640"]', output, 1
1822
2010
  assert_css 'iframe[height="360"]', output, 1
1823
2011
  end
@@ -1829,7 +2017,7 @@ video::SCZF6I-Rc4I,AsKGOeonbIs,HwrPhOp6-aM[youtube, 640, 360, start=60, options=
1829
2017
  output = render_embedded_string input
1830
2018
  assert_css 'video', output, 0
1831
2019
  assert_css 'iframe', output, 1
1832
- assert_css 'iframe[src="//www.youtube.com/embed/SCZF6I-Rc4I?rel=0&start=60&autoplay=1&playlist=AsKGOeonbIs,HwrPhOp6-aM"]', output, 1
2020
+ assert_css 'iframe[src="https://www.youtube.com/embed/SCZF6I-Rc4I?rel=0&start=60&autoplay=1&playlist=AsKGOeonbIs,HwrPhOp6-aM"]', output, 1
1833
2021
  assert_css 'iframe[width="640"]', output, 1
1834
2022
  assert_css 'iframe[height="360"]', output, 1
1835
2023
  end
@@ -1961,7 +2149,7 @@ You can use icons for admonitions by setting the 'icons' attribute.
1961
2149
  EOS
1962
2150
 
1963
2151
  output = render_string input, :safe => Asciidoctor::SafeMode::SERVER
1964
- assert_css 'html > head > link[rel="stylesheet"][href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.min.css"]', output, 1
2152
+ assert_css 'html > head > link[rel="stylesheet"][href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css"]', output, 1
1965
2153
  assert_xpath '//*[@class="admonitionblock tip"]//*[@class="icon"]/i[@class="fa icon-tip"]', output, 1
1966
2154
  end
1967
2155
 
@@ -1978,8 +2166,8 @@ puts "AsciiDoc, FTW!"
1978
2166
  EOS
1979
2167
 
1980
2168
  output = render_string input, :safe => Asciidoctor::SafeMode::SAFE
1981
- assert_css 'html > head > link[rel="stylesheet"][href="http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.min.css"]', output, 1
1982
- assert_css 'html > head > script[src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/highlight.min.js"]', output, 1
2169
+ assert_css 'html > head > link[rel="stylesheet"][href="http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css"]', output, 1
2170
+ assert_css 'html > body > script[src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js"]', output, 1
1983
2171
  end
1984
2172
 
1985
2173
  test 'should use no uri scheme for assets when asset-uri-scheme is blank' do
@@ -1995,8 +2183,8 @@ puts "AsciiDoc, FTW!"
1995
2183
  EOS
1996
2184
 
1997
2185
  output = render_string input, :safe => Asciidoctor::SafeMode::SAFE
1998
- assert_css 'html > head > link[rel="stylesheet"][href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.min.css"]', output, 1
1999
- assert_css 'html > head > script[src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/highlight.min.js"]', output, 1
2186
+ assert_css 'html > head > link[rel="stylesheet"][href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css"]', output, 1
2187
+ assert_css 'html > body > script[src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js"]', output, 1
2000
2188
  end
2001
2189
  end
2002
2190
 
@@ -26,13 +26,13 @@ context 'Converter' do
26
26
  assert_equal :xhtml, selected.templates['paragraph'].options[:format]
27
27
  end
28
28
 
29
- test 'should set Slim format to html5 for html5 backend' do
29
+ test 'should set Slim format to html for html5 backend' do
30
30
  doc = Asciidoctor::Document.new [], :template_dir => File.join(File.dirname(__FILE__), 'fixtures', 'custom-backends', 'slim'), :template_cache => false
31
31
  assert doc.converter.is_a?(Asciidoctor::Converter::CompositeConverter)
32
32
  selected = doc.converter.find_converter('paragraph')
33
33
  assert selected.is_a? Asciidoctor::Converter::TemplateConverter
34
34
  assert selected.templates['paragraph'].is_a? Slim::Template
35
- assert_equal :html5, selected.templates['paragraph'].options[:format]
35
+ assert_equal :html, selected.templates['paragraph'].options[:format]
36
36
  end
37
37
 
38
38
  test 'should set Slim format to nil for docbook backend' do
@@ -44,6 +44,19 @@ context 'Converter' do
44
44
  assert_nil selected.templates['paragraph'].options[:format]
45
45
  end
46
46
 
47
+ test 'should set safe mode of Slim AsciiDoc engine to match document safe mode when Slim >= 3' do
48
+ doc = Asciidoctor::Document.new [], :template_dir => File.join(File.dirname(__FILE__), 'fixtures', 'custom-backends', 'slim'), :template_cache => false, :safe => :unsafe
49
+ assert doc.converter.is_a?(Asciidoctor::Converter::CompositeConverter)
50
+ selected = doc.converter.find_converter('paragraph')
51
+ assert selected.is_a? Asciidoctor::Converter::TemplateConverter
52
+ slim_asciidoc_opts = selected.instance_variable_get(:@engine_options)[:slim][:asciidoc]
53
+ if ::Slim::VERSION >= '3.0'
54
+ assert_equal({ :safe => Asciidoctor::SafeMode::UNSAFE }, slim_asciidoc_opts)
55
+ else
56
+ assert_nil slim_asciidoc_opts
57
+ end
58
+ end
59
+
47
60
  test 'should support custom template engine options for known engine' do
48
61
  doc = Asciidoctor::Document.new [], :template_dir => File.join(File.dirname(__FILE__), 'fixtures', 'custom-backends', 'slim'), :template_cache => false, :template_engine_options => { :slim => { :pretty => true } }
49
62
  assert doc.converter.is_a?(Asciidoctor::Converter::CompositeConverter)
@@ -138,7 +138,9 @@ context 'Document' do
138
138
  exception = assert_raises ArgumentError do
139
139
  Asciidoctor.load_file(sample_input_path, :safe => Asciidoctor::SafeMode::SAFE)
140
140
  end
141
- assert_match(/Failed to parse source/, exception.message)
141
+ assert_match(/Failed to load AsciiDoc document/, exception.message)
142
+ # verify we have the correct backtrace (should be in at least first 5 lines)
143
+ assert_match((RUBY_ENGINE == 'rbx' ? /parser\.rb/ : /helpers\.rb/), exception.backtrace[0..4].join("\n"))
142
144
  end if RUBY_MIN_VERSION_1_9
143
145
 
144
146
  test 'should load input IO' do
@@ -317,7 +319,7 @@ preamble
317
319
  assert_equal 1, section_1.lineno
318
320
  end
319
321
 
320
- test 'find_by should return Array of blocks that match criteria' do
322
+ test 'find_by should return Array of blocks anywhere in document tree that match criteria' do
321
323
  input = <<-EOS
322
324
  = Document Title
323
325
 
@@ -334,7 +336,7 @@ Exhibit A::
334
336
  image::tiger.png[Tiger]
335
337
  --
336
338
 
337
- image::cat.png[Cat]
339
+ image::shoe.png[Shoe]
338
340
 
339
341
  == Section B
340
342
 
@@ -347,7 +349,124 @@ paragraph
347
349
  assert_equal :image, result[0].context
348
350
  assert_equal 'tiger.png', result[0].attr('target')
349
351
  assert_equal :image, result[1].context
350
- assert_equal 'cat.png', result[1].attr('target')
352
+ assert_equal 'shoe.png', result[1].attr('target')
353
+ end
354
+
355
+ test 'find_by should return an empty Array if no matches are found' do
356
+ input = <<-EOS
357
+ paragraph
358
+ EOS
359
+ doc = Asciidoctor.load input
360
+ result = doc.find_by :context => :section
361
+ refute_nil result
362
+ assert_equal 0, result.size
363
+ end
364
+
365
+ test 'find_by should return Array of blocks that match style criteria' do
366
+ input = <<-EOS
367
+ [square]
368
+ * one
369
+ * two
370
+ * three
371
+
372
+ ---
373
+
374
+ * apples
375
+ * bananas
376
+ * pears
377
+ EOS
378
+
379
+ doc = Asciidoctor.load input
380
+ result = doc.find_by :context => :ulist, :style => 'square'
381
+ assert_equal 1, result.size
382
+ assert_equal :ulist, result[0].context
383
+ end
384
+
385
+ test 'find_by should return Array of blocks that match role criteria' do
386
+ input = <<-EOS
387
+ [#tiger.animal]
388
+ image::tiger.png[Tiger]
389
+
390
+ image::shoe.png[Shoe]
391
+ EOS
392
+
393
+ doc = Asciidoctor.load input
394
+ result = doc.find_by :context => :image, :role => 'animal'
395
+ assert_equal 1, result.size
396
+ assert_equal :image, result[0].context
397
+ assert_equal 'tiger.png', result[0].attr('target')
398
+ end
399
+
400
+ test 'find_by should return the document title section if context selector is :section' do
401
+ input = <<-EOS
402
+ = Document Title
403
+
404
+ preamble
405
+
406
+ == Section One
407
+
408
+ content
409
+ EOS
410
+ doc = Asciidoctor.load input
411
+ result = doc.find_by :context => :section
412
+ refute_nil result
413
+ assert_equal 2, result.size
414
+ assert_equal :section, result[0].context
415
+ assert_equal 'Document Title', result[0].title
416
+ end
417
+
418
+ test 'find_by should only return results for which the block argument yields true' do
419
+ input = <<-EOS
420
+ == Section
421
+
422
+ content
423
+
424
+ === Subsection
425
+
426
+ content
427
+ EOS
428
+ doc = Asciidoctor.load input
429
+ result = doc.find_by(:context => :section) {|sect| sect.level == 1 }
430
+ refute_nil result
431
+ assert_equal 1, result.size
432
+ assert_equal :section, result[0].context
433
+ assert_equal 'Section', result[0].title
434
+ end
435
+
436
+ test 'find_by should only return one result when matching by id' do
437
+ input = <<-EOS
438
+ == Section
439
+
440
+ content
441
+
442
+ [#subsection]
443
+ === Subsection
444
+
445
+ content
446
+ EOS
447
+ doc = Asciidoctor.load input
448
+ result = doc.find_by(:context => :section, :id => 'subsection')
449
+ refute_nil result
450
+ assert_equal 1, result.size
451
+ assert_equal :section, result[0].context
452
+ assert_equal 'Subsection', result[0].title
453
+ end
454
+
455
+ test 'find_by should return an empty Array if the id criteria matches but the block argument yields false' do
456
+ input = <<-EOS
457
+ == Section
458
+
459
+ content
460
+
461
+ [#subsection]
462
+ === Subsection
463
+
464
+ content
465
+ EOS
466
+ doc = Asciidoctor.load input
467
+ result = doc.find_by(:context => :section, :id => 'subsection') {|sect| false }
468
+ refute_nil result
469
+ assert_equal 0, result.size
351
470
  end
352
471
  end
353
472
 
@@ -634,23 +753,38 @@ text
634
753
  test 'should include docinfo files for html backend' do
635
754
  sample_input_path = fixture_path('basic.asciidoc')
636
755
 
637
- output = Asciidoctor.convert_file sample_input_path, :to_file => false,
638
- :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo' => ''}
639
- assert !output.empty?
640
- assert_css 'script[src="modernizr.js"]', output, 1
641
- assert_css 'meta[http-equiv="imagetoolbar"]', output, 0
756
+ cases = {
757
+ 'docinfo' => { :head_script => 1, :meta => 0, :top_link => 0, :footer_script => 1 },
758
+ 'docinfo=private' => { :head_script => 1, :meta => 0, :top_link => 0, :footer_script => 1 },
759
+ 'docinfo1' => { :head_script => 0, :meta => 1, :top_link => 1, :footer_script => 0 },
760
+ 'docinfo=shared' => { :head_script => 0, :meta => 1, :top_link => 1, :footer_script => 0 },
761
+ 'docinfo2' => { :head_script => 1, :meta => 1, :top_link => 1, :footer_script => 1 },
762
+ 'docinfo docinfo2' => { :head_script => 1, :meta => 1, :top_link => 1, :footer_script => 1 },
763
+ 'docinfo=private,shared' => { :head_script => 1, :meta => 1, :top_link => 1, :footer_script => 1 },
764
+ 'docinfo=private-head' => { :head_script => 1, :meta => 0, :top_link => 0, :footer_script => 0 },
765
+ 'docinfo=shared-head' => { :head_script => 0, :meta => 1, :top_link => 0, :footer_script => 0 },
766
+ 'docinfo=private-footer' => { :head_script => 0, :meta => 0, :top_link => 0, :footer_script => 1 },
767
+ 'docinfo=shared-footer' => { :head_script => 0, :meta => 0, :top_link => 1, :footer_script => 0 },
768
+ 'docinfo=private-head\ ,\ shared-footer' => { :head_script => 1, :meta => 0, :top_link => 1, :footer_script => 0 }
769
+ }
642
770
 
643
- output = Asciidoctor.convert_file sample_input_path, :to_file => false,
644
- :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo1' => ''}
645
- assert !output.empty?
646
- assert_css 'script[src="modernizr.js"]', output, 0
647
- assert_css 'meta[http-equiv="imagetoolbar"]', output, 1
771
+ cases.each do |attr_val, markup|
772
+ output = Asciidoctor.convert_file sample_input_path, :to_file => false,
773
+ :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => %(linkcss copycss! #{attr_val})
774
+ assert !output.empty?
775
+ assert_css 'script[src="modernizr.js"]', output, markup[:head_script]
776
+ assert_css 'meta[http-equiv="imagetoolbar"]', output, markup[:meta]
777
+ assert_css 'body > a#top', output, markup[:top_link]
778
+ assert_css 'body > script', output, markup[:footer_script]
779
+ end
780
+ end
648
781
 
782
+ test 'should include docinfo footer even if nofooter attribute is set' do
783
+ sample_input_path = fixture_path('basic.asciidoc')
649
784
  output = Asciidoctor.convert_file sample_input_path, :to_file => false,
650
- :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo2' => ''}
785
+ :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo1' => '', 'nofooter' => ''}
651
786
  assert !output.empty?
652
- assert_css 'script[src="modernizr.js"]', output, 1
653
- assert_css 'meta[http-equiv="imagetoolbar"]', output, 1
787
+ assert_css 'body > a#top', output, 1
654
788
  end
655
789
 
656
790
  test 'should include docinfo files for html backend with custom docinfodir' do
@@ -817,12 +951,25 @@ text
817
951
  assert_css 'productname', output, 0
818
952
  assert_css 'copyright', output, 0
819
953
  end
954
+
955
+ test 'should apply explicit substitutions to docinfo files' do
956
+ sample_input_path = fixture_path('subs.adoc')
957
+
958
+ output = Asciidoctor.convert_file sample_input_path, :to_file => false,
959
+ :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo' => '', 'docinfosubs' => 'attributes,replacements', 'linkcss' => ''}
960
+ assert !output.empty?
961
+ assert_css 'script[src="bootstrap.3.2.0.min.js"]', output, 1
962
+ assert_xpath %(//meta[@name="copyright"][@content="#{entity 169} OpenDevise"]), output, 1
963
+ end
820
964
  end
821
965
 
822
966
  context 'MathJax' do
823
967
  test 'should add MathJax script to HTML head if stem attribute is set' do
824
968
  output = render_string '', :attributes => {'stem' => ''}
825
969
  assert_match('<script type="text/x-mathjax-config">', output)
970
+ assert_match('inlineMath: [["\\\\(", "\\\\)"]]', output)
971
+ assert_match('displayMath: [["\\\\[", "\\\\]"]]', output)
972
+ assert_match('delimiters: [["\\\\$", "\\\\$"]]', output)
826
973
  end
827
974
  end
828
975
 
@@ -878,11 +1025,40 @@ text
878
1025
  assert_nil doc.header
879
1026
  end
880
1027
 
1028
+ test 'title partition API with default separator' do
1029
+ title = Asciidoctor::Document::Title.new 'Main Title: And More: Subtitle'
1030
+ assert_equal 'Main Title: And More', title.main
1031
+ assert_equal 'Subtitle', title.subtitle
1032
+ end
1033
+
1034
+ test 'title partition API with custom separator' do
1035
+ title = Asciidoctor::Document::Title.new 'Main Title:: And More:: Subtitle', :separator => '::'
1036
+ assert_equal 'Main Title:: And More', title.main
1037
+ assert_equal 'Subtitle', title.subtitle
1038
+ end
1039
+
881
1040
  test 'document with subtitle' do
882
1041
  input = <<-EOS
883
1042
  = Main Title: *Subtitle*
884
1043
  Author Name
885
1044
 
1045
+ content
1046
+ EOS
1047
+
1048
+ doc = document_from_string input
1049
+ title = doc.doctitle :partition => true, :sanitize => true
1050
+ assert title.subtitle?
1051
+ assert title.sanitized?
1052
+ assert_equal 'Main Title', title.main
1053
+ assert_equal 'Subtitle', title.subtitle
1054
+ end
1055
+
1056
+ test 'document with subtitle and custom separator' do
1057
+ input = <<-EOS
1058
+ [separator=::]
1059
+ = Main Title:: *Subtitle*
1060
+ Author Name
1061
+
886
1062
  content
887
1063
  EOS
888
1064
 
@@ -1003,7 +1179,7 @@ text
1003
1179
 
1004
1180
  test 'should sanitize contents of HTML title element' do
1005
1181
  input = <<-EOS
1006
- = *Document* image:logo.png[] _Title_ image:another-logo.png[]
1182
+ = *Document* image:logo.png[] _Title_ image:another-logo.png[another logo]
1007
1183
 
1008
1184
  content
1009
1185
  EOS
@@ -1012,7 +1188,7 @@ content
1012
1188
  assert_xpath '/html/head/title[text()="Document Title"]', output, 1
1013
1189
  nodes = xmlnodes_at_xpath('//*[@id="header"]/h1', output, 1)
1014
1190
  assert_equal 1, nodes.size
1015
- assert_match(/<h1><strong>Document<\/strong> <span class="image"><img src="logo.png" alt="logo"><\/span> <em>Title<\/em> <span class="image"><img src="another-logo.png" alt="another-logo"><\/span><\/h1>/, output)
1191
+ assert_match('<h1><strong>Document</strong> <span class="image"><img src="logo.png" alt="logo"></span> <em>Title</em> <span class="image"><img src="another-logo.png" alt="another logo"></span></h1>', output)
1016
1192
  end
1017
1193
 
1018
1194
  test 'should not choke on empty source' do
@@ -1231,6 +1407,17 @@ content
1231
1407
  assert_xpath '//*[@id="content"]', result, 1
1232
1408
  end
1233
1409
 
1410
+ test 'does not render footer if nofooter is set' do
1411
+ input = <<-EOS
1412
+ :nofooter:
1413
+
1414
+ content
1415
+ EOS
1416
+
1417
+ result = render_string input
1418
+ assert_xpath '//*[@id="footer"]', result, 0
1419
+ end
1420
+
1234
1421
  test 'can disable last updated in footer' do
1235
1422
  doc = document_from_string "= Document Title\n\npreamble", :attributes => {'last-update-label!' => ''}
1236
1423
  result = doc.render
@@ -1327,6 +1514,11 @@ Text that has supporting information{empty}footnote:[An example footnote.].
1327
1514
 
1328
1515
  output = render_embedded_string input
1329
1516
  assert_css '#footnotes', output, 1
1517
+ assert_css '#footnotes .footnote', output, 1
1518
+ assert_css '#footnotes .footnote#_footnote_1', output, 1
1519
+ assert_xpath '/div[@id="footnotes"]/div[@id="_footnote_1"]/a[@href="#_footnoteref_1"][text()="1"]', output, 1
1520
+ text = xmlnodes_at_xpath '/div[@id="footnotes"]/div[@id="_footnote_1"]/text()', output, 1
1521
+ assert_equal '. An example footnote.', text.text.strip
1330
1522
  end
1331
1523
 
1332
1524
  test 'does not render footnotes block in embedded document if nofootnotes attribute is set' do
@@ -1374,6 +1566,42 @@ content
1374
1566
  assert_equal 'xml', (doc.attr 'htmlsyntax')
1375
1567
  end
1376
1568
 
1569
+ test 'honor htmlsyntax attribute passed via API if backend is html' do
1570
+ input = <<-EOS
1571
+ ---
1572
+ EOS
1573
+ doc = document_from_string input, :safe => :safe, :attributes => { 'htmlsyntax' => 'xml' }
1574
+ assert_equal 'html5', doc.backend
1575
+ assert_equal 'xml', (doc.attr 'htmlsyntax')
1576
+ result = doc.convert :header_footer => false
1577
+ assert_equal '<hr/>', result
1578
+ end
1579
+
1580
+ test 'honor htmlsyntax attribute in document header if followed by backend attribute' do
1581
+ input = <<-EOS
1582
+ :htmlsyntax: xml
1583
+ :backend: html5
1584
+
1585
+ ---
1586
+ EOS
1587
+ doc = document_from_string input, :safe => :safe
1588
+ assert_equal 'html5', doc.backend
1589
+ assert_equal 'xml', (doc.attr 'htmlsyntax')
1590
+ result = doc.convert :header_footer => false
1591
+ assert_equal '<hr/>', result
1592
+ end
1593
+
1594
+ test 'does not honor htmlsyntax attribute in document header if not followed by backend attribute' do
1595
+ input = <<-EOS
1596
+ :backend: html5
1597
+ :htmlsyntax: xml
1598
+
1599
+ ---
1600
+ EOS
1601
+ result = render_embedded_string input, :safe => :safe
1602
+ assert_equal '<hr>', result
1603
+ end
1604
+
1377
1605
  test 'should close all short tags when htmlsyntax is xml' do
1378
1606
  input = <<-EOS
1379
1607
  = Document Title
@@ -1428,23 +1656,6 @@ two
1428
1656
  rescue => e
1429
1657
  flunk "xhtml5 backend did not generate well-formed XML: #{e.message}\n#{result}"
1430
1658
  end
1431
- #refute_match(/<meta [^>]+[^\/]>/, result)
1432
- #refute_match(/<link [^>]+[^\/]>/, result)
1433
- #refute_match(/<img [^>]+[^\/]>/, result)
1434
- #refute_match(/<input [^>]+[^\/]>/, result)
1435
- #assert_match(/<input [^>]+checked="checked"/, result)
1436
- #assert_match(/<input [^>]+disabled="disabled"/, result)
1437
- #refute_match(/<col [^>]+[^\/]>/, result)
1438
- #refute_match(/<[bh]r>/, result)
1439
- #assert_match(/video [^>]+loop="loop"/, result)
1440
- #assert_match(/video [^>]+autoplay="autoplay"/, result)
1441
- #assert_match(/video [^>]+controls="controls"/, result)
1442
- #assert_match(/audio [^>]+loop="loop"/, result)
1443
- #assert_match(/audio [^>]+autoplay="autoplay"/, result)
1444
- #assert_match(/audio [^>]+controls="controls"/, result)
1445
- #assert_match(/iframe [^>]+webkitallowfullscreen="webkitallowfullscreen"/i, result)
1446
- #assert_match(/iframe [^>]+mozallowfullscreen="mozallowfullscreen"/i, result)
1447
- #assert_match(/iframe [^>]+allowfullscreen="allowfullscreen"/i, result)
1448
1659
  end
1449
1660
 
1450
1661
  test 'xhtml backend should emit elements in proper namespace' do
@@ -1823,6 +2034,28 @@ asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
1823
2034
  assert_xpath '//h2[text()="NAME"]/following-sibling::*[@class="sectionbody"]/p[text()="asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats"]', output, 1
1824
2035
  assert_xpath '//*[@id="content"]/*[@class="sect1"]/h2[text()="SYNOPSIS"]', output, 1
1825
2036
  end
2037
+
2038
+ test 'should output special header block in embeddable HTML for manpage doctype' do
2039
+ input = <<-EOS
2040
+ = asciidoctor(1)
2041
+ :doctype: manpage
2042
+ :showtitle:
2043
+
2044
+ == NAME
2045
+
2046
+ asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
2047
+
2048
+ == SYNOPSIS
2049
+
2050
+ *asciidoctor* ['OPTION']... 'FILE'..
2051
+ EOS
2052
+
2053
+ output = render_string input, :header_footer => false
2054
+ assert_xpath '/h1[text()="asciidoctor(1) Manual Page"]', output, 1
2055
+ assert_xpath '/h1/following-sibling::h2[text()="NAME"]', output, 1
2056
+ assert_xpath '/h2[text()="NAME"]/following-sibling::*[@class="sectionbody"]', output, 1
2057
+ assert_xpath '/h2[text()="NAME"]/following-sibling::*[@class="sectionbody"]/p[text()="asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats"]', output, 1
2058
+ end
1826
2059
  end
1827
2060
 
1828
2061
  context 'Secure Asset Path' do
@@ -1859,4 +2092,25 @@ text
1859
2092
  assert_match(/missing converter for backend 'unknownBackend'/, exception.message)
1860
2093
  end
1861
2094
  end
2095
+
2096
+ context 'Timing report' do
2097
+ test 'print_report does not lose precision' do
2098
+ timings = Asciidoctor::Timings.new
2099
+ log = timings.instance_variable_get(:@log)
2100
+ log[:read] = 0.00001
2101
+ log[:parse] = 0.00003
2102
+ log[:convert] = 0.00005
2103
+ timings.print_report(sink = StringIO.new)
2104
+ expect = ['0.00004', '0.00005', '0.00009']
2105
+ result = sink.string.split("\n").map {|l| l.sub(/.*:\s*([\d.]+)/, '\1') }
2106
+ assert_equal expect, result
2107
+ end
2108
+
2109
+ test 'print_report should print 0 for untimed phases' do
2110
+ Asciidoctor::Timings.new.print_report(sink = StringIO.new)
2111
+ expect = [].fill('0.00000', 0..2)
2112
+ result = sink.string.split("\n").map {|l| l.sub(/.*:\s*([\d.]+)/, '\1') }
2113
+ assert_equal expect, result
2114
+ end
2115
+ end
1862
2116
  end