asciidoctor 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of asciidoctor might be problematic. Click here for more details.

Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +387 -0
  3. data/README.adoc +358 -348
  4. data/asciidoctor.gemspec +30 -9
  5. data/bin/asciidoctor +3 -0
  6. data/bin/asciidoctor-safe +3 -0
  7. data/compat/asciidoc.conf +76 -4
  8. data/lib/asciidoctor.rb +174 -79
  9. data/lib/asciidoctor/abstract_block.rb +131 -101
  10. data/lib/asciidoctor/abstract_node.rb +108 -26
  11. data/lib/asciidoctor/attribute_list.rb +1 -1
  12. data/lib/asciidoctor/backends/_stylesheets.rb +204 -62
  13. data/lib/asciidoctor/backends/base_template.rb +11 -22
  14. data/lib/asciidoctor/backends/docbook45.rb +158 -163
  15. data/lib/asciidoctor/backends/docbook5.rb +103 -0
  16. data/lib/asciidoctor/backends/html5.rb +662 -445
  17. data/lib/asciidoctor/block.rb +54 -44
  18. data/lib/asciidoctor/cli/invoker.rb +41 -20
  19. data/lib/asciidoctor/cli/options.rb +66 -20
  20. data/lib/asciidoctor/debug.rb +1 -1
  21. data/lib/asciidoctor/document.rb +265 -100
  22. data/lib/asciidoctor/extensions.rb +443 -0
  23. data/lib/asciidoctor/helpers.rb +38 -6
  24. data/lib/asciidoctor/inline.rb +5 -5
  25. data/lib/asciidoctor/lexer.rb +532 -250
  26. data/lib/asciidoctor/{list_item.rb → list.rb} +33 -13
  27. data/lib/asciidoctor/path_resolver.rb +28 -2
  28. data/lib/asciidoctor/reader.rb +814 -455
  29. data/lib/asciidoctor/renderer.rb +128 -42
  30. data/lib/asciidoctor/section.rb +55 -41
  31. data/lib/asciidoctor/substituters.rb +380 -107
  32. data/lib/asciidoctor/table.rb +40 -30
  33. data/lib/asciidoctor/version.rb +1 -1
  34. data/man/asciidoctor.1 +32 -96
  35. data/man/{asciidoctor.ad → asciidoctor.adoc} +57 -48
  36. data/test/attributes_test.rb +200 -27
  37. data/test/blocks_test.rb +361 -22
  38. data/test/document_test.rb +496 -81
  39. data/test/extensions_test.rb +448 -0
  40. data/test/fixtures/basic-docinfo-footer.html +6 -0
  41. data/test/fixtures/basic-docinfo-footer.xml +8 -0
  42. data/test/fixtures/basic-docinfo.xml +3 -3
  43. data/test/fixtures/basic.asciidoc +1 -0
  44. data/test/fixtures/child-include.adoc +5 -0
  45. data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +6 -0
  46. data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +1 -0
  47. data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +3 -0
  48. data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +5 -0
  49. data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +6 -0
  50. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +3 -0
  51. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +5 -0
  52. data/test/fixtures/docinfo-footer.html +1 -0
  53. data/test/fixtures/docinfo-footer.xml +9 -0
  54. data/test/fixtures/docinfo.xml +1 -0
  55. data/test/fixtures/grandchild-include.adoc +3 -0
  56. data/test/fixtures/parent-include-restricted.adoc +5 -0
  57. data/test/fixtures/parent-include.adoc +5 -0
  58. data/test/invoker_test.rb +82 -8
  59. data/test/lexer_test.rb +21 -3
  60. data/test/links_test.rb +34 -2
  61. data/test/lists_test.rb +304 -7
  62. data/test/options_test.rb +19 -3
  63. data/test/paragraphs_test.rb +13 -0
  64. data/test/paths_test.rb +22 -0
  65. data/test/preamble_test.rb +20 -0
  66. data/test/reader_test.rb +1096 -644
  67. data/test/renderer_test.rb +152 -12
  68. data/test/sections_test.rb +417 -76
  69. data/test/substitutions_test.rb +339 -138
  70. data/test/tables_test.rb +109 -4
  71. data/test/test_helper.rb +79 -13
  72. data/test/text_test.rb +111 -11
  73. metadata +54 -18
@@ -66,6 +66,23 @@ context 'Document' do
66
66
  rescue
67
67
  end
68
68
  end
69
+
70
+ test 'toc and numbered should be enabled by default for DocBook backend' do
71
+ doc = Asciidoctor::Document.new [], :backend => 'docbook'
72
+ assert doc.attr?('toc')
73
+ assert doc.attr?('numbered')
74
+ end
75
+
76
+ test 'should be able to disable toc and numbered in document header for DocBook backend' do
77
+ input = <<-EOS
78
+ = Document Title
79
+ :toc!:
80
+ :numbered!:
81
+ EOS
82
+ doc = document_from_string input, :backend => 'docbook'
83
+ assert !doc.attr?('toc')
84
+ assert !doc.attr?('numbered')
85
+ end
69
86
  end
70
87
 
71
88
  context 'Load APIs' do
@@ -182,6 +199,26 @@ preamble
182
199
  doc = Asciidoctor.load('text', :attributes => nil)
183
200
  assert doc.attributes.is_a?(Hash)
184
201
  end
202
+
203
+ test 'should accept attributes if hash like' do
204
+ class Hashish
205
+ def initialize
206
+ @table = {'toc' => ''}
207
+ end
208
+
209
+ def keys
210
+ @table.keys
211
+ end
212
+
213
+ def [](key)
214
+ @table[key]
215
+ end
216
+ end
217
+
218
+ doc = Asciidoctor.load('text', :attributes => Hashish.new)
219
+ assert doc.attributes.is_a?(Hash)
220
+ assert doc.attributes.has_key?('toc')
221
+ end
185
222
  end
186
223
 
187
224
  context 'Render APIs' do
@@ -209,86 +246,25 @@ preamble
209
246
  assert_css '#section-a', output, 1
210
247
  end
211
248
 
212
- test 'should include docinfo files for html backend' do
213
- sample_input_path = fixture_path('basic.asciidoc')
214
-
215
- output = Asciidoctor.render_file(sample_input_path,
216
- :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo' => ''})
217
- assert !output.empty?
218
- assert_css 'script[src="modernizr.js"]', output, 1
219
- assert_css 'meta[http-equiv="imagetoolbar"]', output, 0
220
-
221
- output = Asciidoctor.render_file(sample_input_path,
222
- :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo1' => ''})
223
- assert !output.empty?
224
- assert_css 'script[src="modernizr.js"]', output, 0
225
- assert_css 'meta[http-equiv="imagetoolbar"]', output, 1
226
-
227
- output = Asciidoctor.render_file(sample_input_path,
228
- :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo2' => ''})
229
- assert !output.empty?
230
- assert_css 'script[src="modernizr.js"]', output, 1
231
- assert_css 'meta[http-equiv="imagetoolbar"]', output, 1
232
- end
233
-
234
- test 'should include docinfo files for docbook backend' do
235
- sample_input_path = fixture_path('basic.asciidoc')
236
-
237
- output = Asciidoctor.render_file(sample_input_path,
238
- :header_footer => true, :backend => 'docbook', :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo' => ''})
239
- assert !output.empty?
240
- assert_css 'productname', output, 0
241
- assert_css 'copyright', output, 1
242
-
243
- output = Asciidoctor.render_file(sample_input_path,
244
- :header_footer => true, :backend => 'docbook', :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo1' => ''})
245
- assert !output.empty?
246
- assert_css 'productname', output, 1
247
- assert_css 'copyright', output, 0
248
-
249
- output = Asciidoctor.render_file(sample_input_path,
250
- :header_footer => true, :backend => 'docbook', :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo2' => ''})
251
- assert !output.empty?
252
- assert_css 'productname', output, 1
253
- assert_css 'copyright', output, 1
254
- end
255
-
256
- test 'should not include docinfo files by default' do
249
+ test 'should link to default stylesheet by default when safe mode is SECURE or greater' do
257
250
  sample_input_path = fixture_path('basic.asciidoc')
258
-
259
- output = Asciidoctor.render_file(sample_input_path,
260
- :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER)
261
- assert !output.empty?
262
- assert_css 'script[src="modernizr.js"]', output, 0
263
- assert_css 'meta[http-equiv="imagetoolbar"]', output, 0
264
-
265
- output = Asciidoctor.render_file(sample_input_path,
266
- :header_footer => true, :backend => 'docbook', :safe => Asciidoctor::SafeMode::SERVER)
267
- assert !output.empty?
268
- assert_css 'productname', output, 0
269
- assert_css 'copyright', output, 0
251
+ output = Asciidoctor.render_file(sample_input_path, :header_footer => true)
252
+ assert_css 'html:root > head > link[rel="stylesheet"][href="./asciidoctor.css"]', output, 1
270
253
  end
271
254
 
272
- test 'should not include docinfo files if safe mode is SECURE or greater' do
273
- sample_input_path = fixture_path('basic.asciidoc')
274
-
275
- output = Asciidoctor.render_file(sample_input_path,
276
- :header_footer => true, :attributes => {'docinfo2' => ''})
277
- assert !output.empty?
278
- assert_css 'script[src="modernizr.js"]', output, 0
279
- assert_css 'meta[http-equiv="imagetoolbar"]', output, 0
255
+ test 'should embed default stylesheet by default if SafeMode is less than SECURE' do
256
+ input = <<-EOS
257
+ = Document Title
280
258
 
281
- output = Asciidoctor.render_file(sample_input_path,
282
- :header_footer => true, :backend => 'docbook', :attributes => {'docinfo2' => ''})
283
- assert !output.empty?
284
- assert_css 'productname', output, 0
285
- assert_css 'copyright', output, 0
286
- end
259
+ text
260
+ EOS
287
261
 
288
- test 'should link to default stylesheet by default' do
289
- sample_input_path = fixture_path('basic.asciidoc')
290
- output = Asciidoctor.render_file(sample_input_path, :header_footer => true)
291
- assert_css 'html:root > head > link[rel="stylesheet"][href="./asciidoctor.css"]', output, 1
262
+ output = Asciidoctor.render(input, :safe => Asciidoctor::SafeMode::SERVER, :header_footer => true)
263
+ assert_css 'html:root > head > link[rel="stylesheet"][href="./asciidoctor.css"]', output, 0
264
+ stylenode = xmlnodes_at_css 'html:root > head > style', output, 1
265
+ styles = stylenode.first.content
266
+ assert !styles.nil?
267
+ assert !styles.strip.empty?
292
268
  end
293
269
 
294
270
  test 'should link to default stylesheet by default if linkcss is unset in document' do
@@ -494,6 +470,134 @@ text
494
470
  end
495
471
  end
496
472
 
473
+ context 'Docinfo files' do
474
+ test 'should include docinfo files for html backend' do
475
+ sample_input_path = fixture_path('basic.asciidoc')
476
+
477
+ output = Asciidoctor.render_file(sample_input_path,
478
+ :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo' => ''})
479
+ assert !output.empty?
480
+ assert_css 'script[src="modernizr.js"]', output, 1
481
+ assert_css 'meta[http-equiv="imagetoolbar"]', output, 0
482
+
483
+ output = Asciidoctor.render_file(sample_input_path,
484
+ :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo1' => ''})
485
+ assert !output.empty?
486
+ assert_css 'script[src="modernizr.js"]', output, 0
487
+ assert_css 'meta[http-equiv="imagetoolbar"]', output, 1
488
+
489
+ output = Asciidoctor.render_file(sample_input_path,
490
+ :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo2' => ''})
491
+ assert !output.empty?
492
+ assert_css 'script[src="modernizr.js"]', output, 1
493
+ assert_css 'meta[http-equiv="imagetoolbar"]', output, 1
494
+ end
495
+
496
+ test 'should include docinfo files for docbook backend' do
497
+ sample_input_path = fixture_path('basic.asciidoc')
498
+
499
+ output = Asciidoctor.render_file(sample_input_path,
500
+ :header_footer => true, :backend => 'docbook', :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo' => ''})
501
+ assert !output.empty?
502
+ assert_css 'productname', output, 0
503
+ assert_css 'copyright', output, 1
504
+
505
+ output = Asciidoctor.render_file(sample_input_path,
506
+ :header_footer => true, :backend => 'docbook', :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo1' => ''})
507
+ assert !output.empty?
508
+ assert_css 'productname', output, 1
509
+ assert_css 'edition', output, 1
510
+ assert_xpath '//xmlns:edition[text()="1.0"]', output, 1 # verifies substitutions are performed
511
+ assert_css 'copyright', output, 0
512
+
513
+ output = Asciidoctor.render_file(sample_input_path,
514
+ :header_footer => true, :backend => 'docbook', :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo2' => ''})
515
+ assert !output.empty?
516
+ assert_css 'productname', output, 1
517
+ assert_css 'edition', output, 1
518
+ assert_xpath '//xmlns:edition[text()="1.0"]', output, 1 # verifies substitutions are performed
519
+ assert_css 'copyright', output, 1
520
+ end
521
+
522
+ test 'should include docinfo footer files for html backend' do
523
+ sample_input_path = fixture_path('basic.asciidoc')
524
+
525
+ output = Asciidoctor.render_file(sample_input_path,
526
+ :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo' => ''})
527
+ assert !output.empty?
528
+ assert_css 'body script', output, 1
529
+ assert_css 'a#top', output, 0
530
+
531
+ output = Asciidoctor.render_file(sample_input_path,
532
+ :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo1' => ''})
533
+ assert !output.empty?
534
+ assert_css 'body script', output, 0
535
+ assert_css 'a#top', output, 1
536
+
537
+ output = Asciidoctor.render_file(sample_input_path,
538
+ :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo2' => ''})
539
+ assert !output.empty?
540
+ assert_css 'body script', output, 1
541
+ assert_css 'a#top', output, 1
542
+ end
543
+
544
+ test 'should include docinfo footer files for docbook backend' do
545
+ sample_input_path = fixture_path('basic.asciidoc')
546
+
547
+ output = Asciidoctor.render_file(sample_input_path,
548
+ :header_footer => true, :backend => 'docbook', :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo' => ''})
549
+ assert !output.empty?
550
+ assert_css 'article > revhistory', output, 1
551
+ assert_xpath '/xmlns:article/xmlns:revhistory/xmlns:revision/xmlns:revnumber[text()="1.0"]', output, 1 # verifies substitutions are performed
552
+ assert_css 'glossary#_glossary', output, 0
553
+
554
+ output = Asciidoctor.render_file(sample_input_path,
555
+ :header_footer => true, :backend => 'docbook', :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo1' => ''})
556
+ assert !output.empty?
557
+ assert_css 'article > revhistory', output, 0
558
+ assert_css 'glossary#_glossary', output, 1
559
+
560
+ output = Asciidoctor.render_file(sample_input_path,
561
+ :header_footer => true, :backend => 'docbook', :safe => Asciidoctor::SafeMode::SERVER, :attributes => {'docinfo2' => ''})
562
+ assert !output.empty?
563
+ assert_css 'article > revhistory', output, 1
564
+ assert_xpath '/xmlns:article/xmlns:revhistory/xmlns:revision/xmlns:revnumber[text()="1.0"]', output, 1 # verifies substitutions are performed
565
+ assert_css 'glossary#_glossary', output, 1
566
+ end
567
+
568
+ test 'should not include docinfo files by default' do
569
+ sample_input_path = fixture_path('basic.asciidoc')
570
+
571
+ output = Asciidoctor.render_file(sample_input_path,
572
+ :header_footer => true, :safe => Asciidoctor::SafeMode::SERVER)
573
+ assert !output.empty?
574
+ assert_css 'script[src="modernizr.js"]', output, 0
575
+ assert_css 'meta[http-equiv="imagetoolbar"]', output, 0
576
+
577
+ output = Asciidoctor.render_file(sample_input_path,
578
+ :header_footer => true, :backend => 'docbook', :safe => Asciidoctor::SafeMode::SERVER)
579
+ assert !output.empty?
580
+ assert_css 'productname', output, 0
581
+ assert_css 'copyright', output, 0
582
+ end
583
+
584
+ test 'should not include docinfo files if safe mode is SECURE or greater' do
585
+ sample_input_path = fixture_path('basic.asciidoc')
586
+
587
+ output = Asciidoctor.render_file(sample_input_path,
588
+ :header_footer => true, :attributes => {'docinfo2' => ''})
589
+ assert !output.empty?
590
+ assert_css 'script[src="modernizr.js"]', output, 0
591
+ assert_css 'meta[http-equiv="imagetoolbar"]', output, 0
592
+
593
+ output = Asciidoctor.render_file(sample_input_path,
594
+ :header_footer => true, :backend => 'docbook', :attributes => {'docinfo2' => ''})
595
+ assert !output.empty?
596
+ assert_css 'productname', output, 0
597
+ assert_css 'copyright', output, 0
598
+ end
599
+ end
600
+
497
601
  context 'Renderer' do
498
602
  test 'built-in HTML5 views are registered by default' do
499
603
  doc = document_from_string ''
@@ -507,8 +611,8 @@ text
507
611
  assert !views.nil?
508
612
  assert_equal 36, views.size
509
613
  assert views.has_key? 'document'
510
- assert views['document'].is_a?(Asciidoctor::HTML5::DocumentTemplate)
511
- assert_equal 'ERB', views['document'].eruby.to_s
614
+ assert Asciidoctor.const_defined?(:HTML5)
615
+ assert Asciidoctor::HTML5.const_defined?(:DocumentTemplate)
512
616
  end
513
617
 
514
618
  test 'built-in DocBook45 views are registered when backend is docbook45' do
@@ -523,11 +627,40 @@ text
523
627
  assert !views.nil?
524
628
  assert_equal 36, views.size
525
629
  assert views.has_key? 'document'
526
- assert views['document'].is_a?(Asciidoctor::DocBook45::DocumentTemplate)
630
+ assert Asciidoctor.const_defined?(:DocBook45)
631
+ assert Asciidoctor::DocBook45.const_defined?(:DocumentTemplate)
632
+ end
633
+
634
+ test 'built-in DocBook5 views are registered when backend is docbook5' do
635
+ doc = document_from_string '', :attributes => {'backend' => 'docbook5'}
636
+ renderer = doc.renderer
637
+ assert_equal 'docbook5', doc.attributes['backend']
638
+ assert doc.attributes.has_key? 'backend-docbook5'
639
+ assert_equal 'docbook', doc.attributes['basebackend']
640
+ assert doc.attributes.has_key? 'basebackend-docbook'
641
+ assert !renderer.nil?
642
+ views = renderer.views
643
+ assert !views.nil?
644
+ assert_equal 36, views.size
645
+ assert views.has_key? 'document'
646
+ assert Asciidoctor.const_defined?(:DocBook5)
647
+ assert Asciidoctor::DocBook5.const_defined?(:DocumentTemplate)
648
+ end
649
+
650
+ test 'eRuby implementation should default to ERB' do
651
+ # intentionally use built-in templates for this test
652
+ doc = Asciidoctor::Document.new [], :header_footer => true
653
+ renderer = doc.renderer
654
+ views = renderer.views
655
+ assert !views.nil?
656
+ assert views.has_key? 'document'
657
+ assert views['document'].is_a?(Asciidoctor::HTML5::DocumentTemplate)
527
658
  assert_equal 'ERB', views['document'].eruby.to_s
659
+ assert_equal 'ERB', views['document'].template.class.to_s
528
660
  end
529
661
 
530
662
  test 'can set erubis as eRuby implementation' do
663
+ # intentionally use built-in templates for this test
531
664
  doc = Asciidoctor::Document.new [], :eruby => 'erubis', :header_footer => true
532
665
  assert $LOADED_FEATURES.detect {|p| p == 'erubis.rb' || p.end_with?('/erubis.rb') }.nil?
533
666
  renderer = doc.renderer
@@ -535,6 +668,7 @@ text
535
668
  views = renderer.views
536
669
  assert !views.nil?
537
670
  assert views.has_key? 'document'
671
+ assert views['document'].is_a?(Asciidoctor::HTML5::DocumentTemplate)
538
672
  assert_equal 'Erubis::FastEruby', views['document'].eruby.to_s
539
673
  assert_equal 'Erubis::FastEruby', views['document'].template.class.to_s
540
674
  end
@@ -655,6 +789,20 @@ text
655
789
  assert_css '#header h1', output, 1
656
790
  assert_css '#content h1', output, 0
657
791
  end
792
+
793
+ test 'should sanitize contents of HTML title element' do
794
+ input = <<-EOS
795
+ = *Document* image:logo.png[] _Title_ image:another-logo.png[]
796
+
797
+ content
798
+ EOS
799
+
800
+ output = render_string input
801
+ assert_xpath '/html/head/title[text()="Document Title"]', output, 1
802
+ nodes = xmlnodes_at_xpath('//*[@id="header"]/h1', output, 1)
803
+ assert_equal 1, nodes.size
804
+ 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)
805
+ end
658
806
 
659
807
  test 'should not choke on empty source' do
660
808
  doc = Asciidoctor::Document.new ''
@@ -690,7 +838,7 @@ more info...
690
838
  assert_xpath '//*[@id="header"]/span[@id="revremark"][text() = "See changelog."]', output, 1
691
839
  end
692
840
 
693
- test 'with metadata to DocBook' do
841
+ test 'with metadata to DocBook45' do
694
842
  input = <<-EOS
695
843
  = AsciiDoc
696
844
  Stuart Rackham <founder@asciidoc.org>
@@ -715,6 +863,24 @@ more info...
715
863
  assert_xpath '/article/articleinfo/revhistory/revision/revremark[text() = "See changelog."]', output, 1
716
864
  end
717
865
 
866
+ test 'with metadata to DocBook5' do
867
+ input = <<-EOS
868
+ = AsciiDoc
869
+ Stuart Rackham <founder@asciidoc.org>
870
+
871
+ == Version 8.6.8
872
+
873
+ more info...
874
+ EOS
875
+ output = render_string input, :backend => 'docbook5'
876
+ assert_xpath '/article/info', output, 1
877
+ assert_xpath '/article/info/title[text() = "AsciiDoc"]', output, 1
878
+ assert_xpath '/article/info/author/personname', output, 1
879
+ assert_xpath '/article/info/author/personname/firstname[text() = "Stuart"]', output, 1
880
+ assert_xpath '/article/info/author/personname/surname[text() = "Rackham"]', output, 1
881
+ assert_xpath '/article/info/author/email[text() = "founder@asciidoc.org"]', output, 1
882
+ end
883
+
718
884
  test 'with author defined using attribute entry to DocBook' do
719
885
  input = <<-EOS
720
886
  = Document Title
@@ -732,6 +898,27 @@ content
732
898
  assert_xpath '//articleinfo/authorinitials[text() = "DW"]', output, 1
733
899
  end
734
900
 
901
+ test 'should include multiple authors in HTML output' do
902
+ input = <<-EOS
903
+ = Document Title
904
+ Doc Writer <thedoctor@asciidoc.org>; Junior Writer <junior@asciidoctor.org>
905
+
906
+ content
907
+ EOS
908
+
909
+ output = render_string input
910
+ assert_xpath '//span[@id="author"]', output, 1
911
+ assert_xpath '//span[@id="author"][text()="Doc Writer"]', output, 1
912
+ assert_xpath '//span[@id="email"]', output, 1
913
+ assert_xpath '//span[@id="email"]/a', output, 1
914
+ assert_xpath '//span[@id="email"]/a[@href="mailto:thedoctor@asciidoc.org"][text()="thedoctor@asciidoc.org"]', output, 1
915
+ assert_xpath '//span[@id="author2"]', output, 1
916
+ assert_xpath '//span[@id="author2"][text()="Junior Writer"]', output, 1
917
+ assert_xpath '//span[@id="email2"]', output, 1
918
+ assert_xpath '//span[@id="email2"]/a', output, 1
919
+ assert_xpath '//span[@id="email2"]/a[@href="mailto:junior@asciidoctor.org"][text()="junior@asciidoctor.org"]', output, 1
920
+ end
921
+
735
922
  test 'should create authorgroup in DocBook when multiple authors' do
736
923
  input = <<-EOS
737
924
  = Document Title
@@ -779,6 +966,13 @@ content
779
966
  assert_xpath '//*[@id="preamble"]', result, 1
780
967
  end
781
968
 
969
+ test 'can disable last updated in footer' do
970
+ doc = document_from_string "= Document Title\n\npreamble", :attributes => {'last-update-label!' => ''}
971
+ result = doc.render
972
+ assert_xpath '//*[@id="footer-text"]', result, 1
973
+ assert_xpath '//*[@id="footer-text"][normalize-space(text())=""]', result, 1
974
+ end
975
+
782
976
  test 'no header footer' do
783
977
  doc = document_from_string "= Title\n\npreamble", :header_footer => false
784
978
  assert doc.attr?('embedded')
@@ -790,8 +984,31 @@ content
790
984
  assert_xpath '/*[@id="preamble"]', result, 1
791
985
  end
792
986
 
793
- test 'enable title when no header footer' do
794
- result = render_string("= Title\n\npreamble", :header_footer => false, :attributes => {'notitle!' => ''})
987
+ test 'enable title in embedded document by unassigning notitle attribute' do
988
+ input = <<-EOS
989
+ = Document Title
990
+
991
+ content
992
+ EOS
993
+
994
+ result = render_string input, :header_footer => false, :attributes => {'notitle!' => ''}
995
+ assert_xpath '/html', result, 0
996
+ assert_xpath '/h1', result, 1
997
+ assert_xpath '/*[@id="header"]', result, 0
998
+ assert_xpath '/*[@id="footer"]', result, 0
999
+ assert_xpath '/*[@id="preamble"]', result, 1
1000
+ assert_xpath '(/*)[1]/self::h1', result, 1
1001
+ assert_xpath '(/*)[2]/self::*[@id="preamble"]', result, 1
1002
+ end
1003
+
1004
+ test 'enable title in embedded document by assigning showtitle attribute' do
1005
+ input = <<-EOS
1006
+ = Document Title
1007
+
1008
+ content
1009
+ EOS
1010
+
1011
+ result = render_string input, :header_footer => false, :attributes => {'showtitle' => ''}
795
1012
  assert_xpath '/html', result, 0
796
1013
  assert_xpath '/h1', result, 1
797
1014
  assert_xpath '/*[@id="header"]', result, 0
@@ -900,6 +1117,11 @@ section body
900
1117
  assert_xpath '/article/simpara[text() = "text"]', result, 1
901
1118
  end
902
1119
 
1120
+ test 'docbook45 backend doctype article no xmlns' do
1121
+ result = render_string('text', :keep_namespaces => true, :attributes => {'backend' => 'docbook45', 'doctype' => 'article', 'noxmlns' => ''})
1122
+ assert_no_match(RE_XMLNS_ATTRIBUTE, result)
1123
+ end
1124
+
903
1125
  test 'docbook45 backend doctype book' do
904
1126
  input = <<-EOS
905
1127
  = Title
@@ -926,6 +1148,11 @@ chapter body
926
1148
  assert_xpath '/book/simpara[text() = "text"]', result, 1
927
1149
  end
928
1150
 
1151
+ test 'docbook45 backend doctype book no xmlns' do
1152
+ result = render_string('text', :keep_namespaces => true, :attributes => {'backend' => 'docbook45', 'doctype' => 'book', 'noxmlns' => ''})
1153
+ assert_no_match(RE_XMLNS_ATTRIBUTE, result)
1154
+ end
1155
+
929
1156
  test 'docbook45 backend parses out subtitle' do
930
1157
  input = <<-EOS
931
1158
  = Document Title: Subtitle
@@ -939,6 +1166,64 @@ text
939
1166
  assert_xpath '/book/bookinfo/subtitle[text() = "Subtitle"]', result, 1
940
1167
  end
941
1168
 
1169
+ test 'docbook5 backend doctype article' do
1170
+ input = <<-EOS
1171
+ = Title
1172
+ Author Name
1173
+
1174
+ preamble
1175
+
1176
+ == First Section
1177
+
1178
+ section body
1179
+ EOS
1180
+ result = render_string(input, :keep_namespaces => true, :attributes => {'backend' => 'docbook5'})
1181
+ assert_xpath '/xmlns:article', result, 1
1182
+ doc = xmlnodes_at_xpath('/xmlns:article', result, 1).first
1183
+ assert_equal 'http://docbook.org/ns/docbook', doc.namespaces['xmlns']
1184
+ assert_equal 'http://www.w3.org/1999/xlink', doc.namespaces['xmlns:xlink']
1185
+ assert_xpath '/xmlns:article[@version="5.0"]', result, 1
1186
+ assert_xpath '/xmlns:article/xmlns:info/xmlns:title[text() = "Title"]', result, 1
1187
+ assert_xpath '/xmlns:article/xmlns:simpara[text() = "preamble"]', result, 1
1188
+ assert_xpath '/xmlns:article/xmlns:section', result, 1
1189
+ section = xmlnodes_at_xpath('/xmlns:article/xmlns:section', result, 1).first
1190
+ # nokogiri can't make up its mind
1191
+ id_attr = section.attribute('id') || section.attribute('xml:id')
1192
+ assert_not_nil id_attr
1193
+ assert_not_nil id_attr.namespace
1194
+ assert_equal 'xml', id_attr.namespace.prefix
1195
+ assert_equal '_first_section', id_attr.value
1196
+ end
1197
+
1198
+ test 'docbook5 backend doctype book' do
1199
+ input = <<-EOS
1200
+ = Title
1201
+ Author Name
1202
+
1203
+ preamble
1204
+
1205
+ == First Chapter
1206
+
1207
+ chapter body
1208
+ EOS
1209
+ result = render_string(input, :keep_namespaces => true, :attributes => {'backend' => 'docbook5', 'doctype' => 'book'})
1210
+ assert_xpath '/xmlns:book', result, 1
1211
+ doc = xmlnodes_at_xpath('/xmlns:book', result, 1).first
1212
+ assert_equal 'http://docbook.org/ns/docbook', doc.namespaces['xmlns']
1213
+ assert_equal 'http://www.w3.org/1999/xlink', doc.namespaces['xmlns:xlink']
1214
+ assert_xpath '/xmlns:book[@version="5.0"]', result, 1
1215
+ assert_xpath '/xmlns:book/xmlns:info/xmlns:title[text() = "Title"]', result, 1
1216
+ assert_xpath '/xmlns:book/xmlns:preface/xmlns:simpara[text() = "preamble"]', result, 1
1217
+ assert_xpath '/xmlns:book/xmlns:chapter', result, 1
1218
+ chapter = xmlnodes_at_xpath('/xmlns:book/xmlns:chapter', result, 1).first
1219
+ # nokogiri can't make up its mind
1220
+ id_attr = chapter.attribute('id') || chapter.attribute('xml:id')
1221
+ assert_not_nil id_attr
1222
+ assert_not_nil id_attr.namespace
1223
+ assert_equal 'xml', id_attr.namespace.prefix
1224
+ assert_equal '_first_chapter', id_attr.value
1225
+ end
1226
+
942
1227
  test 'should be able to set backend using :backend option key' do
943
1228
  doc = Asciidoctor::Document.new([], :backend => 'html5')
944
1229
  assert_equal 'html5', doc.attributes['backend']
@@ -998,5 +1283,135 @@ preamble
998
1283
  assert_equal '', doc.attr('toc')
999
1284
  assert_equal 'Dan Allen', doc.attr('author')
1000
1285
  end
1286
+
1287
+ test 'should parse mantitle and manvolnum from document title for manpage doctype' do
1288
+ input = <<-EOS
1289
+ = asciidoctor ( 1 )
1290
+ :doctype: manpage
1291
+
1292
+ == NAME
1293
+
1294
+ asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
1295
+ EOS
1296
+
1297
+ doc = document_from_string input
1298
+ assert_equal 'asciidoctor', doc.attr('mantitle')
1299
+ assert_equal '1', doc.attr('manvolnum')
1300
+ end
1301
+
1302
+ test 'should perform attribute substitution on mantitle in manpage doctype' do
1303
+ input = <<-EOS
1304
+ = {app}(1)
1305
+ :doctype: manpage
1306
+ :app: asciidoctor
1307
+
1308
+ == NAME
1309
+
1310
+ asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
1311
+ EOS
1312
+
1313
+ doc = document_from_string input
1314
+ assert_equal 'asciidoctor', doc.attr('mantitle')
1315
+ end
1316
+
1317
+ test 'should consume name section as manname and manpurpose for manpage doctype' do
1318
+ input = <<-EOS
1319
+ = asciidoctor(1)
1320
+ :doctype: manpage
1321
+
1322
+ == NAME
1323
+
1324
+ asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
1325
+ EOS
1326
+
1327
+ doc = document_from_string input
1328
+ assert_equal 'asciidoctor', doc.attr('manname')
1329
+ assert_equal 'converts AsciiDoc source files to HTML, DocBook and other formats', doc.attr('manpurpose')
1330
+ assert_equal 0, doc.blocks.size
1331
+ end
1332
+
1333
+ test 'should set docname and outfilesuffix from manname and manvolnum for manpage backend and doctype' do
1334
+ input = <<-EOS
1335
+ = asciidoctor(1)
1336
+ :doctype: manpage
1337
+
1338
+ == NAME
1339
+
1340
+ asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
1341
+ EOS
1342
+
1343
+ doc = document_from_string input, :backend => 'manpage'
1344
+ assert_equal 'asciidoctor', doc.attributes['docname']
1345
+ assert_equal '.1', doc.attributes['outfilesuffix']
1346
+ end
1347
+
1348
+ test 'should mark synopsis as special section in manpage doctype' do
1349
+ input = <<-EOS
1350
+ = asciidoctor(1)
1351
+ :doctype: manpage
1352
+
1353
+ == NAME
1354
+
1355
+ asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
1356
+
1357
+ == SYNOPSIS
1358
+
1359
+ *asciidoctor* ['OPTION']... 'FILE'..
1360
+ EOS
1361
+
1362
+ doc = document_from_string input
1363
+ synopsis_section = doc.blocks.first
1364
+ assert_not_nil synopsis_section
1365
+ assert_equal :section, synopsis_section.context
1366
+ assert synopsis_section.special
1367
+ assert_equal 'synopsis', synopsis_section.sectname
1368
+ end
1369
+
1370
+ test 'should output special header block in HTML for manpage doctype' do
1371
+ input = <<-EOS
1372
+ = asciidoctor(1)
1373
+ :doctype: manpage
1374
+
1375
+ == NAME
1376
+
1377
+ asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
1378
+
1379
+ == SYNOPSIS
1380
+
1381
+ *asciidoctor* ['OPTION']... 'FILE'..
1382
+ EOS
1383
+
1384
+ output = render_string input
1385
+ assert_css 'body.manpage', output, 1
1386
+ assert_xpath '//body/*[@id="header"]/h1[text()="asciidoctor(1) Manual Page"]', output, 1
1387
+ assert_xpath '//body/*[@id="header"]/h1/following-sibling::h2[text()="NAME"]', output, 1
1388
+ assert_xpath '//h2[text()="NAME"]/following-sibling::*[@class="sectionbody"]', output, 1
1389
+ assert_xpath '//h2[text()="NAME"]/following-sibling::*[@class="sectionbody"]/p[text()="asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats"]', output, 1
1390
+ assert_xpath '//*[@id="content"]/*[@class="sect1"]/h2[text()="SYNOPSIS"]', output, 1
1391
+ end
1392
+ end
1393
+
1394
+ context 'Secure Asset Path' do
1395
+ test 'allows us to specify a path relative to the current dir' do
1396
+ doc = Asciidoctor::Document.new
1397
+ legit_path = Dir.pwd + '/foo'
1398
+ assert_equal legit_path, doc.normalize_asset_path(legit_path)
1399
+ end
1400
+
1401
+ test 'keeps naughty absolute paths from getting outside' do
1402
+ naughty_path = "#{disk_root}etc/passwd"
1403
+ doc = Asciidoctor::Document.new
1404
+ secure_path = doc.normalize_asset_path(naughty_path)
1405
+ assert naughty_path != secure_path
1406
+ assert_match(/^#{doc.base_dir}/, secure_path)
1407
+ end
1408
+
1409
+ test 'keeps naughty relative paths from getting outside' do
1410
+ naughty_path = 'safe/ok/../../../../../etc/passwd'
1411
+ doc = Asciidoctor::Document.new
1412
+ secure_path = doc.normalize_asset_path(naughty_path)
1413
+ assert naughty_path != secure_path
1414
+ assert_match(/^#{doc.base_dir}/, secure_path)
1415
+ end
1001
1416
  end
1002
1417
  end