asciidoctor 0.0.9 → 0.1.0

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 (42) hide show
  1. data/README.asciidoc +163 -41
  2. data/Rakefile +3 -1
  3. data/asciidoctor.gemspec +13 -5
  4. data/bin/asciidoctor +6 -3
  5. data/bin/asciidoctor-safe +13 -0
  6. data/lib/asciidoctor.rb +237 -26
  7. data/lib/asciidoctor/abstract_node.rb +27 -17
  8. data/lib/asciidoctor/attribute_list.rb +6 -0
  9. data/lib/asciidoctor/backends/base_template.rb +3 -4
  10. data/lib/asciidoctor/backends/docbook45.rb +114 -55
  11. data/lib/asciidoctor/backends/html5.rb +173 -104
  12. data/lib/asciidoctor/cli/invoker.rb +105 -0
  13. data/lib/asciidoctor/cli/options.rb +146 -0
  14. data/lib/asciidoctor/document.rb +135 -35
  15. data/lib/asciidoctor/lexer.rb +86 -33
  16. data/lib/asciidoctor/list_item.rb +2 -2
  17. data/lib/asciidoctor/reader.rb +6 -7
  18. data/lib/asciidoctor/section.rb +17 -5
  19. data/lib/asciidoctor/substituters.rb +216 -97
  20. data/lib/asciidoctor/table.rb +9 -2
  21. data/lib/asciidoctor/version.rb +1 -1
  22. data/man/asciidoctor.1 +212 -0
  23. data/man/asciidoctor.ad +156 -0
  24. data/test/attributes_test.rb +108 -5
  25. data/test/blocks_test.rb +102 -15
  26. data/test/document_test.rb +214 -3
  27. data/test/fixtures/encoding.asciidoc +4 -0
  28. data/test/fixtures/sample.asciidoc +26 -0
  29. data/test/invoker_test.rb +254 -0
  30. data/test/lexer_test.rb +53 -0
  31. data/test/links_test.rb +30 -0
  32. data/test/lists_test.rb +648 -9
  33. data/test/options_test.rb +68 -0
  34. data/test/paragraphs_test.rb +65 -1
  35. data/test/reader_test.rb +18 -4
  36. data/test/{headers_test.rb → sections_test.rb} +237 -0
  37. data/test/substitutions_test.rb +247 -5
  38. data/test/tables_test.rb +22 -4
  39. data/test/test_helper.rb +47 -3
  40. data/test/text_test.rb +20 -4
  41. metadata +34 -6
  42. data/noof.rb +0 -16
@@ -0,0 +1,68 @@
1
+ require 'test_helper'
2
+ require 'asciidoctor/cli/options'
3
+
4
+ context 'Options' do
5
+ test 'should return error code 0 when help flag is present' do
6
+ redirect_streams do |stdout, stderr|
7
+ exitval = Asciidoctor::Cli::Options.parse!(%w(-h))
8
+ assert_equal 0, exitval
9
+ assert_match(/^Usage:/, stdout.string)
10
+ end
11
+ end
12
+
13
+ test 'should return error code 1 when invalid option present' do
14
+ redirect_streams do |stdout, stderr|
15
+ exitval = Asciidoctor::Cli::Options.parse!(%w(--foobar))
16
+ assert_equal 1, exitval
17
+ assert_equal 'asciidoctor: invalid option: --foobar', stderr.string.chomp
18
+ end
19
+ end
20
+
21
+ test 'should return error code 1 when option has invalid argument' do
22
+ redirect_streams do |stdout, stderr|
23
+ exitval = Asciidoctor::Cli::Options.parse!(%w(-b foo input.ad))
24
+ assert_equal 1, exitval
25
+ assert_equal 'asciidoctor: invalid argument: -b foo', stderr.string.chomp
26
+ end
27
+ end
28
+
29
+ test 'should return error code 1 when option is missing required argument' do
30
+ redirect_streams do |stdout, stderr|
31
+ exitval = Asciidoctor::Cli::Options.parse!(%w(-b))
32
+ assert_equal 1, exitval
33
+ assert_equal 'asciidoctor: option missing argument: -b', stderr.string.chomp
34
+ end
35
+ end
36
+
37
+ test 'should emit warning when unparsed options remain' do
38
+ redirect_streams do |stdout, stderr|
39
+ options = Asciidoctor::Cli::Options.parse!(%w(-b docbook extra junk test/fixtures/sample.asciidoc))
40
+ assert options.is_a? Hash
41
+ assert_equal 'asciidoctor: WARNING: extra arguments detected (unparsed arguments: \'extra\', \'junk\')', stderr.string.chomp
42
+ end
43
+ end
44
+
45
+ test 'basic argument assignment' do
46
+ options = Asciidoctor::Cli::Options.parse!(%w(-v -s -d book test/fixtures/sample.asciidoc))
47
+
48
+ assert_equal true, options[:verbose]
49
+ assert_equal false, options[:header_footer]
50
+ assert_equal 'book', options[:attributes]['doctype']
51
+ assert_equal 'test/fixtures/sample.asciidoc', options[:input_file]
52
+ end
53
+
54
+ test 'standard attribute assignment' do
55
+ options = Asciidoctor::Cli::Options.parse!(%w(-a imagesdir=images,icons test/fixtures/sample.asciidoc))
56
+
57
+ assert_equal 'images', options[:attributes]['imagesdir']
58
+ assert_equal '', options[:attributes]['icons']
59
+ end
60
+
61
+ test 'multiple attribute arguments' do
62
+ options = Asciidoctor::Cli::Options.parse!(%w(-a imagesdir=images -a icons test/fixtures/sample.asciidoc))
63
+
64
+ assert_equal 'images', options[:attributes]['imagesdir']
65
+ assert_equal '', options[:attributes]['icons']
66
+ end
67
+
68
+ end
@@ -17,6 +17,70 @@ context "Paragraphs" do
17
17
  rendered = render_string("Title\n=====\n\nPreamble.\n\n== First Section\n\nParagraph 1\n\nParagraph 2\n\n\n== Second Section\n\nLast words")
18
18
  assert_xpath '//p[text()="Paragraph 2"]', rendered, 1
19
19
  end
20
+
21
+ test 'does not treat wrapped line as a list item' do
22
+ input = <<-EOS
23
+ paragraph
24
+ . wrapped line
25
+ EOS
26
+
27
+ output = render_embedded_string input
28
+ assert_css 'p', output, 1
29
+ assert_xpath %(//p[text()="paragraph\n. wrapped line"]), output, 1
30
+ end
31
+
32
+ test 'does not treat wrapped line as a block title' do
33
+ input = <<-EOS
34
+ paragraph
35
+ .wrapped line
36
+ EOS
37
+
38
+ output = render_embedded_string input
39
+ assert_css 'p', output, 1
40
+ assert_xpath %(//p[text()="paragraph\n.wrapped line"]), output, 1
41
+ end
42
+
43
+ test 'expands index term macros in DocBook backend' do
44
+ input = <<-EOS
45
+ Here is an index entry for ((tigers)).
46
+ indexterm:[Big cats,Tigers,Siberian Tiger]
47
+ Here is an index entry for indexterm2:[Linux].
48
+ (((Operating Systems,Linux,Fedora)))
49
+ Note that multi-entry terms generate separate index entries.
50
+ EOS
51
+
52
+ output = render_embedded_string input, :attributes => {'backend' => 'docbook45'}
53
+ assert_xpath '/simpara', output, 1
54
+ term1 = (xmlnodes_at_xpath '(//indexterm)[1]', output, 1).first
55
+ assert_equal '<indexterm><primary>tigers</primary></indexterm>', term1.to_s
56
+ assert term1.next.content.start_with?('tigers')
57
+
58
+ term2 = (xmlnodes_at_xpath '(//indexterm)[2]', output, 1).first
59
+ term2_elements = term2.elements
60
+ assert_equal 3, term2_elements.size
61
+ assert_equal '<primary>Big cats</primary>', term2_elements[0].to_s
62
+ assert_equal '<secondary>Tigers</secondary>', term2_elements[1].to_s
63
+ assert_equal '<tertiary>Siberian Tiger</tertiary>', term2_elements[2].to_s
64
+
65
+ term3 = (xmlnodes_at_xpath '(//indexterm)[3]', output, 1).first
66
+ term3_elements = term3.elements
67
+ assert_equal 2, term3_elements.size
68
+ assert_equal '<primary>Tigers</primary>', term3_elements[0].to_s
69
+ assert_equal '<secondary>Siberian Tiger</secondary>', term3_elements[1].to_s
70
+
71
+ term4 = (xmlnodes_at_xpath '(//indexterm)[4]', output, 1).first
72
+ term4_elements = term4.elements
73
+ assert_equal 1, term4_elements.size
74
+ assert_equal '<primary>Siberian Tiger</primary>', term4_elements[0].to_s
75
+
76
+ term5 = (xmlnodes_at_xpath '(//indexterm)[5]', output, 1).first
77
+ assert_equal '<indexterm><primary>Linux</primary></indexterm>', term5.to_s
78
+ assert term5.next.content.start_with?('Linux')
79
+
80
+ assert_xpath '(//indexterm)[6]/*', output, 3
81
+ assert_xpath '(//indexterm)[7]/*', output, 2
82
+ assert_xpath '(//indexterm)[8]/*', output, 1
83
+ end
20
84
  end
21
85
 
22
86
  context "code" do
@@ -58,7 +122,7 @@ You're good to go!
58
122
  output = render_string("[quote, A famous person, A famous book (1999)]\n____\nFamous quote.\n____")
59
123
  assert_xpath '//*[@class = "quoteblock"]', output, 1
60
124
  assert_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]', output, 1
61
- assert_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]/em[text() = "A famous book (1999)"]', output, 1
125
+ assert_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]/cite[text() = "A famous book (1999)"]', output, 1
62
126
  # TODO I can't seem to match the attribution (author) w/ xpath
63
127
  end
64
128
 
@@ -231,15 +231,15 @@ include::include-file.asciidoc[]
231
231
  context 'build secure asset path' do
232
232
  test 'allows us to specify a path relative to the current dir' do
233
233
  doc = Asciidoctor::Document.new
234
- reader = Asciidoctor::Reader.new(["foo"], doc)
234
+ Asciidoctor::Reader.new(["foo"], doc)
235
235
  legit_path = Dir.pwd + "/foo"
236
236
  assert_equal legit_path, doc.normalize_asset_path(legit_path)
237
237
  end
238
238
 
239
239
  test "keeps naughty absolute paths from getting outside" do
240
- naughty_path = "/etc/passwd"
240
+ naughty_path = "#{disk_root}etc/passwd"
241
241
  doc = Asciidoctor::Document.new
242
- reader = Asciidoctor::Reader.new(["foo"], doc)
242
+ Asciidoctor::Reader.new(["foo"], doc)
243
243
  secure_path = doc.normalize_asset_path(naughty_path)
244
244
  assert naughty_path != secure_path
245
245
  assert_match(/^#{doc.base_dir}/, secure_path)
@@ -248,7 +248,7 @@ include::include-file.asciidoc[]
248
248
  test "keeps naughty relative paths from getting outside" do
249
249
  naughty_path = "safe/ok/../../../../../etc/passwd"
250
250
  doc = Asciidoctor::Document.new
251
- reader = Asciidoctor::Reader.new(["foo"], doc)
251
+ Asciidoctor::Reader.new(["foo"], doc)
252
252
  secure_path = doc.normalize_asset_path(naughty_path)
253
253
  assert naughty_path != secure_path
254
254
  assert_match(/^#{doc.base_dir}/, secure_path)
@@ -283,6 +283,20 @@ endif::holygrail[]
283
283
  end
284
284
 
285
285
  context 'Text processing' do
286
+ test 'should clean CRLF from end of lines' do
287
+ input = <<-EOS
288
+ source\r
289
+ with\r
290
+ CRLF\r
291
+ endlines\r
292
+ EOS
293
+
294
+ reader = Asciidoctor::Reader.new(input.lines.entries, Asciidoctor::Document.new)
295
+ reader.lines.each do |line|
296
+ assert !line.end_with?("\r\n")
297
+ end
298
+ end
299
+
286
300
  test 'sanitize attribute name' do
287
301
  assert_equal 'foobar', @reader.sanitize_attribute_name("Foo Bar")
288
302
  assert_equal 'foo', @reader.sanitize_attribute_name("foo")
@@ -28,6 +28,16 @@ context 'Sections' do
28
28
  assert_equal 'section_one', sec.id
29
29
  end
30
30
 
31
+ test 'synthetic id separator can be customized' do
32
+ sec = block_from_string(":idseparator: -\n\n== Section One")
33
+ assert_equal '_section-one', sec.id
34
+ end
35
+
36
+ test 'synthetic id separator can be set to blank' do
37
+ sec = block_from_string(":idseparator:\n\n== Section One")
38
+ assert_equal '_sectionone', sec.id
39
+ end
40
+
31
41
  test 'synthetic ids can be disabled' do
32
42
  sec = block_from_string(":sectids!:\n\n== Section One\n")
33
43
  assert sec.id.nil?
@@ -198,6 +208,119 @@ text
198
208
  end
199
209
  end
200
210
 
211
+ context 'Floating Title' do
212
+ test 'should create floating title if style is float' do
213
+ input = <<-EOS
214
+ [float]
215
+ = Plain Ol' Heading
216
+
217
+ not in section
218
+ EOS
219
+
220
+ output = render_embedded_string input
221
+ assert_xpath '/h1[@id="_plain_ol_heading"]', output, 1
222
+ assert_xpath '/h1[@class="float"]', output, 1
223
+ assert_xpath %(/h1[@class="float"][text()="Plain Ol' Heading"]), output, 1
224
+ assert_xpath '/h1/following-sibling::*[@class="paragraph"]', output, 1
225
+ assert_xpath '/h1/following-sibling::*[@class="paragraph"]/p', output, 1
226
+ assert_xpath '/h1/following-sibling::*[@class="paragraph"]/p[text()="not in section"]', output, 1
227
+ end
228
+
229
+ test 'should create floating title if style is discrete' do
230
+ input = <<-EOS
231
+ [discrete]
232
+ === Plain Ol' Heading
233
+
234
+ not in section
235
+ EOS
236
+
237
+ output = render_embedded_string input
238
+ assert_xpath '/h3', output, 1
239
+ assert_xpath '/h3[@id="_plain_ol_heading"]', output, 1
240
+ assert_xpath '/h3[@class="discrete"]', output, 1
241
+ assert_xpath %(/h3[@class="discrete"][text()="Plain Ol' Heading"]), output, 1
242
+ assert_xpath '/h3/following-sibling::*[@class="paragraph"]', output, 1
243
+ assert_xpath '/h3/following-sibling::*[@class="paragraph"]/p', output, 1
244
+ assert_xpath '/h3/following-sibling::*[@class="paragraph"]/p[text()="not in section"]', output, 1
245
+ end
246
+
247
+ test 'floating title should be a block with context floating_title' do
248
+ input = <<-EOS
249
+ [float]
250
+ === Plain Ol' Heading
251
+
252
+ not in section
253
+ EOS
254
+
255
+ doc = document_from_string input
256
+ floatingtitle = doc.blocks.first
257
+ assert floatingtitle.is_a?(Asciidoctor::Block)
258
+ assert !floatingtitle.is_a?(Asciidoctor::Section)
259
+ assert_equal :floating_title, floatingtitle.context
260
+ end
261
+
262
+ test 'should not include floating title in toc' do
263
+ input = <<-EOS
264
+ :toc:
265
+
266
+ == Section One
267
+
268
+ [float]
269
+ === Miss Independent
270
+
271
+ == Section Two
272
+ EOS
273
+
274
+ output = render_string input
275
+ assert_xpath '//*[@id="toc"]', output, 1
276
+ assert_xpath %(//*[@id="toc"]//a[contains(text(), " Section ")]), output, 2
277
+ assert_xpath %(//*[@id="toc"]//a[text()="Miss Independent"]), output, 0
278
+ end
279
+
280
+ test 'should not set id on floating title if sectids attribute is unset' do
281
+ input = <<-EOS
282
+ [float]
283
+ === Plain Ol' Heading
284
+
285
+ not in section
286
+ EOS
287
+
288
+ output = render_embedded_string input, :attributes => {'sectids' => nil}
289
+ assert_xpath '/h3', output, 1
290
+ assert_xpath '/h3[@id="_plain_ol_heading"]', output, 0
291
+ assert_xpath '/h3[@class="float"]', output, 1
292
+ end
293
+
294
+ test 'should use explicit id for floating title if specified' do
295
+ input = <<-EOS
296
+ [[free]]
297
+ [float]
298
+ == Plain Ol' Heading
299
+
300
+ not in section
301
+ EOS
302
+
303
+ output = render_embedded_string input
304
+ assert_xpath '/h2', output, 1
305
+ assert_xpath '/h2[@id="free"]', output, 1
306
+ assert_xpath '/h2[@class="float"]', output, 1
307
+ end
308
+
309
+ test 'should add role to class attribute on floating title' do
310
+ input = <<-EOS
311
+ [float, role="isolated"]
312
+ == Plain Ol' Heading
313
+
314
+ not in section
315
+ EOS
316
+
317
+ output = render_embedded_string input
318
+ assert_xpath '/h2', output, 1
319
+ assert_xpath '/h2[@id="_plain_ol_heading"]', output, 1
320
+ assert_xpath '/h2[@class="float isolated"]', output, 1
321
+ end
322
+ end
323
+
201
324
  context 'Section Numbering' do
202
325
  test 'should create section number with one entry for level 1' do
203
326
  sect1 = Asciidoctor::Section.new(nil)
@@ -306,6 +429,120 @@ paragraph
306
429
  end
307
430
  end
308
431
 
432
+ context 'Special sections' do
433
+ test 'should assign sectname and caption to appendix section' do
434
+ input = <<-EOS
435
+ [appendix]
436
+ == Attribute Options
437
+
438
+ Details
439
+ EOS
440
+
441
+ output = block_from_string input
442
+ assert_equal 'appendix', output.sectname
443
+ assert_equal 'Appendix A: ', output.attr('caption')
444
+ end
445
+
446
+ test 'should render appendix title prefixed with caption' do
447
+ input = <<-EOS
448
+ [appendix]
449
+ == Attribute Options
450
+
451
+ Details
452
+ EOS
453
+
454
+ output = render_embedded_string input
455
+ assert_xpath '//h2[text()="Appendix A: Attribute Options"]', output, 1
456
+ end
457
+
458
+ test 'should increment appendix number for each appendix section' do
459
+ input = <<-EOS
460
+ [appendix]
461
+ == Attribute Options
462
+
463
+ Details
464
+
465
+ [appendix]
466
+ == Migration
467
+
468
+ Details
469
+ EOS
470
+
471
+ output = render_embedded_string input
472
+ assert_xpath '(//h2)[1][text()="Appendix A: Attribute Options"]', output, 1
473
+ assert_xpath '(//h2)[2][text()="Appendix B: Migration"]', output, 1
474
+ end
475
+
476
+ test 'should not number special sections or subsections' do
477
+ input = <<-EOS
478
+ :numbered:
479
+
480
+ == Section One
481
+
482
+ [appendix]
483
+ == Attribute Options
484
+
485
+ Details
486
+
487
+ [appendix]
488
+ == Migration
489
+
490
+ Details
491
+
492
+ === Gotchas
493
+
494
+ Details
495
+
496
+ [glossary]
497
+ == Glossary
498
+
499
+ Terms
500
+ EOS
501
+
502
+ output = render_embedded_string input
503
+ assert_xpath '(//h2)[1][text()="1. Section One"]', output, 1
504
+ assert_xpath '(//h2)[2][text()="Appendix A: Attribute Options"]', output, 1
505
+ assert_xpath '(//h2)[3][text()="Appendix B: Migration"]', output, 1
506
+ assert_xpath '(//h3)[1][text()="Gotchas"]', output, 1
507
+ assert_xpath '(//h2)[4][text()="Glossary"]', output, 1
508
+ end
509
+
510
+ test 'should not number special sections or subsections in toc' do
511
+ input = <<-EOS
512
+ :numbered:
513
+ :toc:
514
+
515
+ == Section One
516
+
517
+ [appendix]
518
+ == Attribute Options
519
+
520
+ Details
521
+
522
+ [appendix]
523
+ == Migration
524
+
525
+ Details
526
+
527
+ === Gotchas
528
+
529
+ Details
530
+
531
+ [glossary]
532
+ == Glossary
533
+
534
+ Terms
535
+ EOS
536
+
537
+ output = render_string input
538
+ assert_xpath '//*[@id="toc"]/ol//li/a[text()="1. Section One"]', output, 1
539
+ assert_xpath '//*[@id="toc"]/ol//li/a[text()="Appendix A: Attribute Options"]', output, 1
540
+ assert_xpath '//*[@id="toc"]/ol//li/a[text()="Appendix B: Migration"]', output, 1
541
+ assert_xpath '//*[@id="toc"]/ol//li/a[text()="Gotchas"]', output, 1
542
+ assert_xpath '//*[@id="toc"]/ol//li/a[text()="Glossary"]', output, 1
543
+ end
544
+ end
545
+
309
546
  context "heading patterns in blocks" do
310
547
  test "should not interpret a listing block as a heading" do
311
548
  input = <<-EOS