asciidoctor 0.0.7 → 0.0.9

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 (47) hide show
  1. data/Gemfile +2 -0
  2. data/README.asciidoc +35 -26
  3. data/Rakefile +9 -6
  4. data/asciidoctor.gemspec +27 -8
  5. data/bin/asciidoctor +1 -1
  6. data/lib/asciidoctor.rb +351 -63
  7. data/lib/asciidoctor/abstract_block.rb +218 -0
  8. data/lib/asciidoctor/abstract_node.rb +249 -0
  9. data/lib/asciidoctor/attribute_list.rb +211 -0
  10. data/lib/asciidoctor/backends/base_template.rb +99 -0
  11. data/lib/asciidoctor/backends/docbook45.rb +510 -0
  12. data/lib/asciidoctor/backends/html5.rb +585 -0
  13. data/lib/asciidoctor/block.rb +27 -254
  14. data/lib/asciidoctor/callouts.rb +117 -0
  15. data/lib/asciidoctor/debug.rb +7 -4
  16. data/lib/asciidoctor/document.rb +229 -77
  17. data/lib/asciidoctor/inline.rb +29 -0
  18. data/lib/asciidoctor/lexer.rb +1330 -502
  19. data/lib/asciidoctor/list_item.rb +33 -34
  20. data/lib/asciidoctor/reader.rb +305 -142
  21. data/lib/asciidoctor/renderer.rb +115 -19
  22. data/lib/asciidoctor/section.rb +100 -189
  23. data/lib/asciidoctor/substituters.rb +468 -0
  24. data/lib/asciidoctor/table.rb +499 -0
  25. data/lib/asciidoctor/version.rb +1 -1
  26. data/test/attributes_test.rb +301 -87
  27. data/test/blocks_test.rb +568 -0
  28. data/test/document_test.rb +221 -24
  29. data/test/fixtures/dot.gif +0 -0
  30. data/test/fixtures/encoding.asciidoc +1 -0
  31. data/test/fixtures/include-file.asciidoc +1 -0
  32. data/test/fixtures/tip.gif +0 -0
  33. data/test/headers_test.rb +411 -43
  34. data/test/lexer_test.rb +265 -45
  35. data/test/links_test.rb +144 -3
  36. data/test/lists_test.rb +2252 -74
  37. data/test/paragraphs_test.rb +21 -30
  38. data/test/preamble_test.rb +24 -0
  39. data/test/reader_test.rb +248 -12
  40. data/test/renderer_test.rb +22 -0
  41. data/test/substitutions_test.rb +414 -0
  42. data/test/tables_test.rb +484 -0
  43. data/test/test_helper.rb +70 -6
  44. data/test/text_test.rb +30 -6
  45. metadata +64 -10
  46. data/lib/asciidoctor/render_templates.rb +0 -317
  47. data/lib/asciidoctor/string.rb +0 -12
@@ -1,26 +1,27 @@
1
1
  require 'test_helper'
2
2
 
3
3
  context "Paragraphs" do
4
- test "rendered correctly" do
5
- assert_xpath "//p", render_string("Plain text for the win.\n\nYes, plainly."), 2
6
- end
4
+ context 'Normal' do
5
+ test "rendered correctly" do
6
+ assert_xpath "//p", render_string("Plain text for the win.\n\nYes, plainly."), 2
7
+ end
7
8
 
8
- test "with title" do
9
- rendered = render_string(".Titled\nParagraph.\n\nWinning")
10
-
11
- assert_xpath "//div[@class='title']", rendered
12
- assert_xpath "//p", rendered, 2
13
- end
9
+ test "with title" do
10
+ rendered = render_string(".Titled\nParagraph.\n\nWinning")
11
+
12
+ assert_xpath "//div[@class='title']", rendered
13
+ assert_xpath "//p", rendered, 2
14
+ end
14
15
 
15
- test "no duplicate block before next section" do
16
- 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")
17
- assert_xpath '//p[text()="Paragraph 2"]', rendered, 1
16
+ test "no duplicate block before next section" do
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
+ assert_xpath '//p[text()="Paragraph 2"]', rendered, 1
19
+ end
18
20
  end
19
21
 
20
22
  context "code" do
21
23
  test "single-line literal paragraphs" do
22
- output = render_string(" LITERALS\n\n ARE LITERALLY\n\n AWESOMMMME.")
23
- assert_xpath "//pre/tt", render_string(" LITERALS\n\n ARE LITERALLY\n\n AWESOMMMME.")
24
+ assert_xpath "//pre", render_string(" LITERALS\n\n ARE LITERALLY\n\n AWESOMMMME.")
24
25
  end
25
26
 
26
27
  test "multi-line literal paragraph" do
@@ -33,16 +34,16 @@ Install instructions:
33
34
  You're good to go!
34
35
  EOS
35
36
  output = render_string(input)
36
- assert_xpath "//pre/tt", output, 1
37
- assert_match /^gem install asciidoctor/, output, "Indentation should be trimmed from literal block"
37
+ assert_xpath "//pre", output, 1
38
+ assert_match(/^gem install asciidoctor/, output, "Indentation should be trimmed from literal block")
38
39
  end
39
40
 
40
41
  test "listing paragraph" do
41
- assert_xpath "//div[@class='highlight']", render_string("----\nblah blah blah\n----")
42
+ assert_xpath "//pre[@class='highlight']/code", render_string("[source]\n----\nblah blah blah\n----")
42
43
  end
43
44
 
44
45
  test "source code paragraph" do
45
- assert_xpath "//div[@class='highlight']", render_string("[source, perl]\ndie 'zomg perl sucks';")
46
+ assert_xpath "//pre[@class='highlight']/code[@class='perl']", render_string("[source, perl]\ndie 'zomg perl sucks';")
46
47
  end
47
48
  end
48
49
 
@@ -54,10 +55,10 @@ You're good to go!
54
55
  end
55
56
 
56
57
  test "quote block with attribution" do
57
- output = render_string("[quote, A famous person, A famous book]\n____\nFamous quote.\n____")
58
+ output = render_string("[quote, A famous person, A famous book (1999)]\n____\nFamous quote.\n____")
58
59
  assert_xpath '//*[@class = "quoteblock"]', output, 1
59
60
  assert_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]', output, 1
60
- assert_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]/em[text() = "A famous book"]', output, 1
61
+ assert_xpath '//*[@class = "quoteblock"]/*[@class = "attribution"]/em[text() = "A famous book (1999)"]', output, 1
61
62
  # TODO I can't seem to match the attribution (author) w/ xpath
62
63
  end
63
64
 
@@ -140,14 +141,4 @@ Content goes here
140
141
  assert_xpath "//*[@class='sidebarblock']//p", result, 1
141
142
  end
142
143
  end
143
-
144
- context "comments" do
145
- test "line comment" do
146
- assert_no_match /comment/, render_string("first paragraph\n\n//comment\n\nsecond paragraph")
147
- end
148
-
149
- test "comment block" do
150
- assert_no_match /comment/, render_string("first paragraph\n\n////\ncomment\n////\n\nsecond paragraph")
151
- end
152
- end
153
144
  end
@@ -85,4 +85,28 @@ Section paragraph 1.
85
85
  assert_xpath '//h2[@id="_first_section"]/preceding::p', result, 1
86
86
  end
87
87
 
88
+ test 'preamble in book doctype' do
89
+ input = <<-EOS
90
+ Book
91
+ ====
92
+ :doctype: book
93
+
94
+ Back then...
95
+
96
+ = Chapter One
97
+
98
+ It was a dark and stormy night...
99
+
100
+ = Chapter Two
101
+
102
+ They couldn't believe their eyes when...
103
+ EOS
104
+
105
+ d = document_from_string(input)
106
+ assert_equal 'book', d.doctype
107
+ output = d.render
108
+ assert_xpath '//h1', output, 3
109
+ assert_xpath %{//*[@id="preamble"]//p[text() = "Back then#{[8230].pack('U*')}"]}, output, 1
110
+ end
111
+
88
112
  end
@@ -9,7 +9,7 @@ class ReaderTest < Test::Unit::TestCase
9
9
 
10
10
  context "has_lines?" do
11
11
  test "returns false for empty document" do
12
- assert ! Asciidoctor::Reader.new.has_lines?
12
+ assert !Asciidoctor::Reader.new.has_lines?
13
13
  end
14
14
 
15
15
  test "returns true with lines remaining" do
@@ -35,22 +35,258 @@ class ReaderTest < Test::Unit::TestCase
35
35
  second = reader.peek_line
36
36
  assert_equal "foo", second
37
37
  end
38
+
39
+ test "unshift puts line onto Reader instance for the next get_line" do
40
+ reader = Asciidoctor::Reader.new(["foo"])
41
+ reader.unshift("bar")
42
+ assert_equal "bar", reader.get_line
43
+ assert_equal "foo", reader.get_line
44
+ end
45
+ end
46
+
47
+ context "Grab lines" do
48
+ test "Grab until end" do
49
+ input = <<-EOS
50
+ This is one paragraph.
51
+
52
+ This is another paragraph.
53
+ EOS
54
+
55
+ lines = input.lines.entries
56
+ reader = Asciidoctor::Reader.new(lines)
57
+ result = reader.grab_lines_until
58
+ assert_equal 3, result.size
59
+ assert_equal lines, result
60
+ assert !reader.has_lines?
61
+ assert reader.empty?
62
+ end
63
+
64
+ test "Grab until blank line" do
65
+ input = <<-EOS
66
+ This is one paragraph.
67
+
68
+ This is another paragraph.
69
+ EOS
70
+
71
+ lines = input.lines.entries
72
+ reader = Asciidoctor::Reader.new(lines)
73
+ result = reader.grab_lines_until :break_on_blank_lines => true
74
+ assert_equal 1, result.size
75
+ assert_equal lines.first, result.first
76
+ assert_equal lines.last, reader.peek_line
77
+ end
78
+
79
+ test "Grab until blank line preserving last line" do
80
+ input = <<-EOS
81
+ This is one paragraph.
82
+
83
+ This is another paragraph.
84
+ EOS
85
+
86
+ lines = input.lines.entries
87
+ reader = Asciidoctor::Reader.new(lines)
88
+ result = reader.grab_lines_until :break_on_blank_lines => true, :preserve_last_line => true
89
+ assert_equal 1, result.size
90
+ assert_equal lines.first, result.first
91
+ assert_equal "\n", reader.peek_line
92
+ end
93
+
94
+ test "Grab until condition" do
95
+ input = <<-EOS
96
+ --
97
+ This is one paragraph inside the block.
98
+
99
+ This is another paragraph inside the block.
100
+ --
101
+
102
+ This is a paragraph outside the block.
103
+ EOS
104
+
105
+ lines = input.lines.entries
106
+ reader = Asciidoctor::Reader.new(lines)
107
+ reader.get_line
108
+ result = reader.grab_lines_until {|line| line.chomp == '--' }
109
+ assert_equal 3, result.size
110
+ assert_equal lines[1, 3], result
111
+ assert_equal "\n", reader.peek_line
112
+ end
113
+
114
+ test "Grab until condition with last line" do
115
+ input = <<-EOS
116
+ --
117
+ This is one paragraph inside the block.
118
+
119
+ This is another paragraph inside the block.
120
+ --
121
+
122
+ This is a paragraph outside the block.
123
+ EOS
124
+
125
+ lines = input.lines.entries
126
+ reader = Asciidoctor::Reader.new(lines)
127
+ reader.get_line
128
+ result = reader.grab_lines_until(:grab_last_line => true) {|line| line.chomp == '--' }
129
+ assert_equal 4, result.size
130
+ assert_equal lines[1, 4], result
131
+ assert_equal "\n", reader.peek_line
132
+ end
133
+
134
+ test "Grab until condition with last line and preserving last line" do
135
+ input = <<-EOS
136
+ --
137
+ This is one paragraph inside the block.
138
+
139
+ This is another paragraph inside the block.
140
+ --
141
+
142
+ This is a paragraph outside the block.
143
+ EOS
144
+
145
+ lines = input.lines.entries
146
+ reader = Asciidoctor::Reader.new(lines)
147
+ reader.get_line
148
+ result = reader.grab_lines_until(:grab_last_line => true, :preserve_last_line => true) {|line| line.chomp == '--' }
149
+ assert_equal 4, result.size
150
+ assert_equal lines[1, 4], result
151
+ assert_equal "--\n", reader.peek_line
152
+ end
38
153
  end
39
154
 
40
- test "unshift puts line onto Reader instance for the next get_line" do
41
- reader = Asciidoctor::Reader.new(["foo"])
42
- reader.unshift("bar")
43
- assert_equal "bar", reader.get_line
44
- assert_equal "foo", reader.get_line
155
+ context 'Include Macro' do
156
+ test 'include macro is disabled by default' do
157
+ input = <<-EOS
158
+ include::include-file.asciidoc[]
159
+ EOS
160
+ para = block_from_string input, :attributes => { 'include-depth' => 0 }
161
+ assert_equal 1, para.buffer.size
162
+ assert_equal 'include::include-file.asciidoc[]', para.buffer.join
163
+ end
164
+
165
+ test 'include macro is enabled when safe mode is less than SECURE' do
166
+ input = <<-EOS
167
+ include::fixtures/include-file.asciidoc[]
168
+ EOS
169
+
170
+ doc = document_from_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => File.dirname(__FILE__)}
171
+ output = doc.render
172
+ assert_match(/included content/, output)
173
+ end
174
+
175
+ test "block is called to handle an include macro" do
176
+ input = <<-EOS
177
+ first line
178
+
179
+ include::include-file.asciidoc[]
180
+
181
+ last line
182
+ EOS
183
+ doc = Asciidoctor::Document.new [], :safe => Asciidoctor::SafeMode::SAFE
184
+ Asciidoctor::Reader.new(input.lines.entries, doc) {|inc|
185
+ ":file: #{inc}\n\nmiddle line".lines.entries
186
+ }
187
+ assert_equal 'include-file.asciidoc', doc.attributes['file']
188
+ end
189
+
190
+ test 'escaped include macro is left unprocessed' do
191
+ input = <<-EOS
192
+ \\include::include-file.asciidoc[]
193
+ EOS
194
+ para = block_from_string input
195
+ assert_equal 1, para.buffer.size
196
+ assert_equal 'include::include-file.asciidoc[]', para.buffer.join
197
+ end
198
+
199
+ test 'include macro not at start of line is ignored' do
200
+ input = <<-EOS
201
+ include::include-file.asciidoc[]
202
+ EOS
203
+ para = block_from_string input
204
+ assert_equal 1, para.buffer.size
205
+ # NOTE the space gets stripped because the line is treated as an inline literal
206
+ assert_equal :literal, para.context
207
+ assert_equal 'include::include-file.asciidoc[]', para.buffer.join
208
+ end
209
+
210
+ test 'include macro is disabled when include-depth attribute is 0' do
211
+ input = <<-EOS
212
+ include::include-file.asciidoc[]
213
+ EOS
214
+ para = block_from_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => { 'include-depth' => 0 }
215
+ assert_equal 1, para.buffer.size
216
+ assert_equal 'include::include-file.asciidoc[]', para.buffer.join
217
+ end
218
+
219
+ test 'include-depth cannot be set by document' do
220
+ input = <<-EOS
221
+ :include-depth: 1
222
+
223
+ include::include-file.asciidoc[]
224
+ EOS
225
+ para = block_from_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => { 'include-depth' => 0 }
226
+ assert_equal 1, para.buffer.size
227
+ assert_equal 'include::include-file.asciidoc[]', para.buffer.join
228
+ end
45
229
  end
46
230
 
47
- def test_grab_lines_until
48
- pending "Not tested yet"
231
+ context 'build secure asset path' do
232
+ test 'allows us to specify a path relative to the current dir' do
233
+ doc = Asciidoctor::Document.new
234
+ reader = Asciidoctor::Reader.new(["foo"], doc)
235
+ legit_path = Dir.pwd + "/foo"
236
+ assert_equal legit_path, doc.normalize_asset_path(legit_path)
237
+ end
238
+
239
+ test "keeps naughty absolute paths from getting outside" do
240
+ naughty_path = "/etc/passwd"
241
+ doc = Asciidoctor::Document.new
242
+ reader = Asciidoctor::Reader.new(["foo"], doc)
243
+ secure_path = doc.normalize_asset_path(naughty_path)
244
+ assert naughty_path != secure_path
245
+ assert_match(/^#{doc.base_dir}/, secure_path)
246
+ end
247
+
248
+ test "keeps naughty relative paths from getting outside" do
249
+ naughty_path = "safe/ok/../../../../../etc/passwd"
250
+ doc = Asciidoctor::Document.new
251
+ reader = Asciidoctor::Reader.new(["foo"], doc)
252
+ secure_path = doc.normalize_asset_path(naughty_path)
253
+ assert naughty_path != secure_path
254
+ assert_match(/^#{doc.base_dir}/, secure_path)
255
+ end
49
256
  end
50
257
 
51
- def test_sanitize_attribute_name
52
- assert_equal 'foobar', @reader.sanitize_attribute_name("Foo Bar")
53
- assert_equal 'foo', @reader.sanitize_attribute_name("foo")
54
- assert_equal 'foo3-bar', @reader.sanitize_attribute_name("Foo 3^ # - Bar[")
258
+ # TODO these tests could be expanded
259
+ context 'Conditional blocks' do
260
+ test 'ifdef with defined attribute includes block' do
261
+ input = <<-EOS
262
+ :holygrail:
263
+
264
+ ifdef::holygrail[]
265
+ There is a holy grail!
266
+ endif::holygrail[]
267
+ EOS
268
+
269
+ reader = Asciidoctor::Reader.new(input.lines.entries, Asciidoctor::Document.new)
270
+ assert_match(/There is a holy grail!/, reader.lines.join)
271
+ end
272
+
273
+ test 'ifndef with undefined attribute includes block' do
274
+ input = <<-EOS
275
+ ifndef::holygrail[]
276
+ Our quest continues to find the holy grail!
277
+ endif::holygrail[]
278
+ EOS
279
+
280
+ reader = Asciidoctor::Reader.new(input.lines.entries, Asciidoctor::Document.new)
281
+ assert_match(/Our quest continues to find the holy grail!/, reader.lines.join)
282
+ end
283
+ end
284
+
285
+ context 'Text processing' do
286
+ test 'sanitize attribute name' do
287
+ assert_equal 'foobar', @reader.sanitize_attribute_name("Foo Bar")
288
+ assert_equal 'foo', @reader.sanitize_attribute_name("foo")
289
+ assert_equal 'foo3-bar', @reader.sanitize_attribute_name("Foo 3^ # - Bar[")
290
+ end
55
291
  end
56
292
  end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ context 'Renderer' do
4
+
5
+ test 'should extract view mapping from built-in template with one segment and backend' do
6
+ view_name, view_backend = Asciidoctor::Renderer.extract_view_mapping('Asciidoctor::HTML5::DocumentTemplate')
7
+ assert_equal 'document', view_name
8
+ assert_equal 'html5', view_backend
9
+ end
10
+
11
+ test 'should extract view mapping from built-in template with two segments and backend' do
12
+ view_name, view_backend = Asciidoctor::Renderer.extract_view_mapping('Asciidoctor::DocBook45::BlockSidebarTemplate')
13
+ assert_equal 'block_sidebar', view_name
14
+ assert_equal 'docbook45', view_backend
15
+ end
16
+
17
+ test 'should extract view mapping from built-in template without backend' do
18
+ view_name, view_backend = Asciidoctor::Renderer.extract_view_mapping('Asciidoctor::DocumentTemplate')
19
+ assert_equal 'document', view_name
20
+ assert view_backend.nil?
21
+ end
22
+ end
@@ -0,0 +1,414 @@
1
+ require 'test_helper'
2
+
3
+ # TODO
4
+ # - test negatives
5
+ # - test role on every quote type
6
+ context 'Substitutions' do
7
+ context 'Dispatcher' do
8
+ test 'apply normal substitutions' do
9
+ para = block_from_string("[blue]'http://asciidoc.org[AsciiDoc]' & [red]*Ruby*\n&#167; Making +++<u>documentation</u>+++ together +\nsince (C) {inception_year}.")
10
+ para.document.attributes['inception_year'] = '2012'
11
+ result = para.apply_normal_subs(para.buffer)
12
+ assert_equal %{<em><span class="blue"><a href="http://asciidoc.org">AsciiDoc</a></span></em> &amp; <strong><span class="red">Ruby</span></strong>\n&#167; Making <u>documentation</u> together<br>\nsince &#169; 2012.}, result
13
+ end
14
+ end
15
+
16
+ context 'Quotes' do
17
+ test 'single-line double-quoted string' do
18
+ para = block_from_string(%q{``a few quoted words''})
19
+ assert_equal '&#8220;a few quoted words&#8221;', para.sub_quotes(para.buffer.join)
20
+ end
21
+
22
+ test 'escaped single-line double-quoted string' do
23
+ para = block_from_string(%q{\``a few quoted words''})
24
+ assert_equal %q(&#8216;`a few quoted words&#8217;'), para.sub_quotes(para.buffer.join)
25
+ end
26
+
27
+ test 'multi-line double-quoted string' do
28
+ para = block_from_string(%Q{``a few\nquoted words''})
29
+ assert_equal "&#8220;a few\nquoted words&#8221;", para.sub_quotes(para.buffer.join)
30
+ end
31
+
32
+ test 'double-quoted string with inline single quote' do
33
+ para = block_from_string(%q{``Here's Johnny!''})
34
+ assert_equal %q{&#8220;Here's Johnny!&#8221;}, para.sub_quotes(para.buffer.join)
35
+ end
36
+
37
+ test 'double-quoted string with inline backquote' do
38
+ para = block_from_string(%q{``Here`s Johnny!''})
39
+ assert_equal %q{&#8220;Here`s Johnny!&#8221;}, para.sub_quotes(para.buffer.join)
40
+ end
41
+
42
+ test 'single-line single-quoted string' do
43
+ para = block_from_string(%q{`a few quoted words'})
44
+ assert_equal '&#8216;a few quoted words&#8217;', para.sub_quotes(para.buffer.join)
45
+ end
46
+
47
+ test 'escaped single-line single-quoted string' do
48
+ para = block_from_string(%q{\`a few quoted words'})
49
+ assert_equal %(`a few quoted words'), para.sub_quotes(para.buffer.join)
50
+ end
51
+
52
+ test 'multi-line single-quoted string' do
53
+ para = block_from_string(%Q{`a few\nquoted words'})
54
+ assert_equal "&#8216;a few\nquoted words&#8217;", para.sub_quotes(para.buffer.join)
55
+ end
56
+
57
+ test 'single-quoted string with inline single quote' do
58
+ para = block_from_string(%q{`That isn't what I did.'})
59
+ assert_equal %q{&#8216;That isn't what I did.&#8217;}, para.sub_quotes(para.buffer.join)
60
+ end
61
+
62
+ test 'single-quoted string with inline backquote' do
63
+ para = block_from_string(%q{`Here`s Johnny!'})
64
+ assert_equal %q{&#8216;Here`s Johnny!&#8217;}, para.sub_quotes(para.buffer.join)
65
+ end
66
+
67
+ test 'single-line constrained unquoted string' do
68
+ para = block_from_string(%q{#a few words#})
69
+ assert_equal 'a few words', para.sub_quotes(para.buffer.join)
70
+ end
71
+
72
+ test 'escaped single-line constrained unquoted string' do
73
+ para = block_from_string(%q{\#a few words#})
74
+ assert_equal '#a few words#', para.sub_quotes(para.buffer.join)
75
+ end
76
+
77
+ test 'multi-line constrained unquoted string' do
78
+ para = block_from_string(%Q{#a few\nwords#})
79
+ assert_equal "a few\nwords", para.sub_quotes(para.buffer.join)
80
+ end
81
+
82
+ test 'single-line unconstrained unquoted string' do
83
+ para = block_from_string(%q{##--anything goes ##})
84
+ assert_equal '--anything goes ', para.sub_quotes(para.buffer.join)
85
+ end
86
+
87
+ test 'escaped single-line unconstrained unquoted string' do
88
+ para = block_from_string(%q{\##--anything goes ##})
89
+ assert_equal '#--anything goes #', para.sub_quotes(para.buffer.join)
90
+ end
91
+
92
+ test 'multi-line unconstrained unquoted string' do
93
+ para = block_from_string(%Q{##--anything\ngoes ##})
94
+ assert_equal "--anything\ngoes ", para.sub_quotes(para.buffer.join)
95
+ end
96
+
97
+ test 'single-line constrained strong string' do
98
+ para = block_from_string(%q{*a few strong words*})
99
+ assert_equal '<strong>a few strong words</strong>', para.sub_quotes(para.buffer.join)
100
+ end
101
+
102
+ test 'escaped single-line constrained strong string' do
103
+ para = block_from_string(%q{\*a few strong words*})
104
+ assert_equal '*a few strong words*', para.sub_quotes(para.buffer.join)
105
+ end
106
+
107
+ test 'multi-line constrained strong string' do
108
+ para = block_from_string(%Q{*a few\nstrong words*})
109
+ assert_equal "<strong>a few\nstrong words</strong>", para.sub_quotes(para.buffer.join)
110
+ end
111
+
112
+ test 'constrained strong string containing an asterisk' do
113
+ para = block_from_string(%q{*bl*ck*-eye})
114
+ assert_equal '<strong>bl*ck</strong>-eye', para.sub_quotes(para.buffer.join)
115
+ end
116
+
117
+ test 'single-line constrained quote variation emphasized string' do
118
+ para = block_from_string(%q{'a few emphasized words'})
119
+ assert_equal '<em>a few emphasized words</em>', para.sub_quotes(para.buffer.join)
120
+ end
121
+
122
+ test 'escaped single-line constrained quote variation emphasized string' do
123
+ para = block_from_string(%q{\'a few emphasized words'})
124
+ assert_equal %q('a few emphasized words'), para.sub_quotes(para.buffer.join)
125
+ end
126
+
127
+ test 'multi-line constrained emphasized quote variation string' do
128
+ para = block_from_string(%Q{'a few\nemphasized words'})
129
+ assert_equal "<em>a few\nemphasized words</em>", para.sub_quotes(para.buffer.join)
130
+ end
131
+
132
+ test 'single-quoted string containing an emphasized phrase' do
133
+ para = block_from_string(%q{`I told him, 'Just go for it!''})
134
+ assert_equal '&#8216;I told him, <em>Just go for it!</em>&#8217;', para.sub_quotes(para.buffer.join)
135
+ end
136
+
137
+ test 'escaped single-quotes inside emphasized words are restored' do
138
+ para = block_from_string(%q{'Here\'s Johnny!'})
139
+ # NOTE the \' is replaced with ' by the :replacements substitution, later in the substitution pipeline
140
+ assert_equal %q{<em>Here\'s Johnny!</em>}, para.sub_quotes(para.buffer.join)
141
+ end
142
+
143
+ test 'single-line constrained emphasized underline variation string' do
144
+ para = block_from_string(%q{_a few emphasized words_})
145
+ assert_equal '<em>a few emphasized words</em>', para.sub_quotes(para.buffer.join)
146
+ end
147
+
148
+ test 'escaped single-line constrained emphasized underline variation string' do
149
+ para = block_from_string(%q{\_a few emphasized words_})
150
+ assert_equal '_a few emphasized words_', para.sub_quotes(para.buffer.join)
151
+ end
152
+
153
+ test 'multi-line constrained emphasized underline variation string' do
154
+ para = block_from_string(%Q{_a few\nemphasized words_})
155
+ assert_equal "<em>a few\nemphasized words</em>", para.sub_quotes(para.buffer.join)
156
+ end
157
+
158
+ test 'single-line constrained monospaced string' do
159
+ para = block_from_string(%q{`a few <\{monospaced\}> words`})
160
+ # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
161
+ assert_equal '<tt>a few &lt;{monospaced}&gt; words</tt>', para.apply_normal_subs(para.buffer)
162
+ end
163
+
164
+ test 'escaped single-line constrained monospaced string' do
165
+ para = block_from_string(%q{\`a few <monospaced> words`})
166
+ # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
167
+ assert_equal '`a few &lt;monospaced&gt; words`', para.apply_normal_subs(para.buffer)
168
+ end
169
+
170
+ test 'multi-line constrained monospaced string' do
171
+ para = block_from_string(%Q{`a few\n<\{monospaced\}> words`})
172
+ # NOTE must use apply_normal_subs because constrained monospaced is handled as a passthrough
173
+ assert_equal "<tt>a few\n&lt;{monospaced}&gt; words</tt>", para.apply_normal_subs(para.buffer)
174
+ end
175
+
176
+ test 'single-line unconstrained strong chars' do
177
+ para = block_from_string(%q{**Git**Hub})
178
+ assert_equal '<strong>Git</strong>Hub', para.sub_quotes(para.buffer.join)
179
+ end
180
+
181
+ test 'escaped single-line unconstrained strong chars' do
182
+ para = block_from_string(%q{\**Git**Hub})
183
+ assert_equal '<strong>*Git</strong>*Hub', para.sub_quotes(para.buffer.join)
184
+ end
185
+
186
+ test 'multi-line unconstrained strong chars' do
187
+ para = block_from_string(%Q{**G\ni\nt\n**Hub})
188
+ assert_equal "<strong>G\ni\nt\n</strong>Hub", para.sub_quotes(para.buffer.join)
189
+ end
190
+
191
+ test 'unconstrained strong chars with inline asterisk' do
192
+ para = block_from_string(%q{**bl*ck**-eye})
193
+ assert_equal '<strong>bl*ck</strong>-eye', para.sub_quotes(para.buffer.join)
194
+ end
195
+
196
+ test 'unconstrained strong chars with role' do
197
+ para = block_from_string(%q{Git[blue]**Hub**})
198
+ assert_equal %q{Git<strong><span class="blue">Hub</span></strong>}, para.sub_quotes(para.buffer.join)
199
+ end
200
+
201
+ # TODO this is not the same result as AsciiDoc, though I don't understand why AsciiDoc gets what it gets
202
+ test 'escaped unconstrained strong chars with role' do
203
+ para = block_from_string(%q{Git\[blue]**Hub**})
204
+ assert_equal %q{Git[blue]<strong>*Hub</strong>*}, para.sub_quotes(para.buffer.join)
205
+ end
206
+
207
+ test 'single-line unconstrained emphasized chars' do
208
+ para = block_from_string(%q{__Git__Hub})
209
+ assert_equal '<em>Git</em>Hub', para.sub_quotes(para.buffer.join)
210
+ end
211
+
212
+ test 'escaped single-line unconstrained emphasized chars' do
213
+ para = block_from_string(%q{\__Git__Hub})
214
+ assert_equal '__Git__Hub', para.sub_quotes(para.buffer.join)
215
+ end
216
+
217
+ test 'multi-line unconstrained emphasized chars' do
218
+ para = block_from_string(%Q{__G\ni\nt\n__Hub})
219
+ assert_equal "<em>G\ni\nt\n</em>Hub", para.sub_quotes(para.buffer.join)
220
+ end
221
+
222
+ test 'unconstrained emphasis chars with role' do
223
+ para = block_from_string(%q{[gray]__Git__Hub})
224
+ assert_equal %q{<em><span class="gray">Git</span></em>Hub}, para.sub_quotes(para.buffer.join)
225
+ end
226
+
227
+ test 'escaped unconstrained emphasis chars with role' do
228
+ para = block_from_string(%q{\[gray]__Git__Hub})
229
+ assert_equal %q{[gray]__Git__Hub}, para.sub_quotes(para.buffer.join)
230
+ end
231
+
232
+ test 'single-line unconstrained monospaced chars' do
233
+ para = block_from_string(%q{Git++Hub++})
234
+ assert_equal 'Git<tt>Hub</tt>', para.sub_quotes(para.buffer.join)
235
+ end
236
+
237
+ test 'escaped single-line unconstrained monospaced chars' do
238
+ para = block_from_string(%q{Git\++Hub++})
239
+ assert_equal 'Git+<tt>Hub</tt>+', para.sub_quotes(para.buffer.join)
240
+ end
241
+
242
+ test 'multi-line unconstrained monospaced chars' do
243
+ para = block_from_string(%Q{Git++\nH\nu\nb++})
244
+ assert_equal "Git<tt>\nH\nu\nb</tt>", para.sub_quotes(para.buffer.join)
245
+ end
246
+
247
+ test 'single-line superscript chars' do
248
+ para = block_from_string(%q{x^2^ = x * x, e = mc^2^, there's a 1^st^ time for everything})
249
+ assert_equal 'x<sup>2</sup> = x * x, e = mc<sup>2</sup>, there\'s a 1<sup>st</sup> time for everything', para.sub_quotes(para.buffer.join)
250
+ end
251
+
252
+ test 'escaped single-line superscript chars' do
253
+ para = block_from_string(%q{x\^2^ = x * x})
254
+ assert_equal 'x^2^ = x * x', para.sub_quotes(para.buffer.join)
255
+ end
256
+
257
+ test 'multi-line superscript chars' do
258
+ para = block_from_string(%Q{x^(n\n+\n1)^})
259
+ assert_equal "x<sup>(n\n+\n1)</sup>", para.sub_quotes(para.buffer.join)
260
+ end
261
+
262
+ test 'single-line subscript chars' do
263
+ para = block_from_string(%q{H~2~O})
264
+ assert_equal 'H<sub>2</sub>O', para.sub_quotes(para.buffer.join)
265
+ end
266
+
267
+ test 'escaped single-line subscript chars' do
268
+ para = block_from_string(%q{H\~2~O})
269
+ assert_equal 'H~2~O', para.sub_quotes(para.buffer.join)
270
+ end
271
+
272
+ test 'multi-line subscript chars' do
273
+ para = block_from_string(%Q{project~ view\non\nGitHub~})
274
+ assert_equal "project<sub> view\non\nGitHub</sub>", para.sub_quotes(para.buffer.join)
275
+ end
276
+ end
277
+
278
+ context 'Macros' do
279
+ test 'a single-line link macro should be interpreted as a link' do
280
+ para = block_from_string('link:/home.html[]')
281
+ assert_equal %q{<a href="/home.html">/home.html</a>}, para.sub_macros(para.buffer.join)
282
+ end
283
+
284
+ test 'a single-line link macro with text should be interpreted as a link' do
285
+ para = block_from_string('link:/home.html[Home]')
286
+ assert_equal %q{<a href="/home.html">Home</a>}, para.sub_macros(para.buffer.join)
287
+ end
288
+
289
+ test 'a single-line raw url should be interpreted as a link' do
290
+ para = block_from_string('http://google.com')
291
+ assert_equal %q{<a href="http://google.com">http://google.com</a>}, para.sub_macros(para.buffer.join)
292
+ end
293
+
294
+ test 'a single-line raw url with text should be interpreted as a link' do
295
+ para = block_from_string('http://google.com[Google]')
296
+ assert_equal %q{<a href="http://google.com">Google</a>}, para.sub_macros(para.buffer.join)
297
+ end
298
+
299
+ test 'a multi-line raw url with text should be interpreted as a link' do
300
+ para = block_from_string("http://google.com[Google\nHomepage]")
301
+ assert_equal %{<a href="http://google.com">Google\nHomepage</a>}, para.sub_macros(para.buffer.join)
302
+ end
303
+
304
+ test 'a multi-line raw url with attribute as text should be interpreted as a link with resolved attribute' do
305
+ para = block_from_string("http://google.com[{google_homepage}]")
306
+ para.document.attributes['google_homepage'] = 'Google Homepage'
307
+ assert_equal %q{<a href="http://google.com">Google Homepage</a>}, para.sub_macros(para.buffer.join)
308
+ end
309
+
310
+ test 'a single-line escaped raw url should not be interpreted as a link' do
311
+ para = block_from_string('\http://google.com')
312
+ assert_equal %q{http://google.com}, para.sub_macros(para.buffer.join)
313
+ end
314
+
315
+ test 'a single-line image macro should be interpreted as an image' do
316
+ para = block_from_string('image:tiger.png[]')
317
+ assert_equal %{<span class="image">\n <img src="tiger.png" alt="tiger">\n</span>}, para.sub_macros(para.buffer.join)
318
+ end
319
+
320
+ test 'a single-line image macro with text should be interpreted as an image with alt text' do
321
+ para = block_from_string('image:tiger.png[Tiger]')
322
+ assert_equal %{<span class="image">\n <img src="tiger.png" alt="Tiger">\n</span>}, para.sub_macros(para.buffer.join)
323
+ end
324
+
325
+ test 'a single-line image macro with text and dimensions should be interpreted as an image with alt text and dimensions' do
326
+ para = block_from_string('image:tiger.png[Tiger, 200, 100]')
327
+ assert_equal %{<span class="image">\n <img src="tiger.png" alt="Tiger" width="200" height="100">\n</span>}, para.sub_macros(para.buffer.join)
328
+ end
329
+
330
+ test 'a single-line image macro with text and link should be interpreted as a linked image with alt text' do
331
+ para = block_from_string('image:tiger.png[Tiger, link="http://en.wikipedia.org/wiki/Tiger"]')
332
+ assert_equal %{<span class="image">\n <a class="image" href="http://en.wikipedia.org/wiki/Tiger"><img src="tiger.png" alt="Tiger"></a>\n</span>}, para.sub_macros(para.buffer.join)
333
+ end
334
+ end
335
+
336
+ context 'Passthroughs' do
337
+ test 'collect inline triple plus passthroughs' do
338
+ para = block_from_string('+++<code>inline code</code>+++')
339
+ result = para.extract_passthroughs(para.buffer.join)
340
+ assert_equal "\x0" + '0' + "\x0", result
341
+ assert_equal 1, para.passthroughs.size
342
+ assert_equal '<code>inline code</code>', para.passthroughs.first[:text]
343
+ assert para.passthroughs.first[:subs].empty?
344
+ end
345
+
346
+ test 'collect multi-line inline triple plus passthroughs' do
347
+ para = block_from_string("+++<code>inline\ncode</code>+++")
348
+ result = para.extract_passthroughs(para.buffer.join)
349
+ assert_equal "\x0" + '0' + "\x0", result
350
+ assert_equal 1, para.passthroughs.size
351
+ assert_equal "<code>inline\ncode</code>", para.passthroughs.first[:text]
352
+ assert para.passthroughs.first[:subs].empty?
353
+ end
354
+
355
+ test 'collect inline double dollar passthroughs' do
356
+ para = block_from_string('$$<code>{code}</code>$$')
357
+ result = para.extract_passthroughs(para.buffer.join)
358
+ assert_equal "\x0" + '0' + "\x0", result
359
+ assert_equal 1, para.passthroughs.size
360
+ assert_equal '<code>{code}</code>', para.passthroughs.first[:text]
361
+ assert_equal [:specialcharacters], para.passthroughs.first[:subs]
362
+ end
363
+
364
+ test 'collect multi-line inline double dollar passthroughs' do
365
+ para = block_from_string("$$<code>\n{code}\n</code>$$")
366
+ result = para.extract_passthroughs(para.buffer.join)
367
+ assert_equal "\x0" + '0' + "\x0", result
368
+ assert_equal 1, para.passthroughs.size
369
+ assert_equal "<code>\n{code}\n</code>", para.passthroughs.first[:text]
370
+ assert_equal [:specialcharacters], para.passthroughs.first[:subs]
371
+ end
372
+
373
+ test 'collect passthroughs from inline pass macro' do
374
+ para = block_from_string(%Q{pass:specialcharacters,quotes[<code>['code'\\]</code>]})
375
+ result = para.extract_passthroughs(para.buffer.join)
376
+ assert_equal "\x0" + '0' + "\x0", result
377
+ assert_equal 1, para.passthroughs.size
378
+ assert_equal %q{<code>['code']</code>}, para.passthroughs.first[:text]
379
+ assert_equal [:specialcharacters, :quotes], para.passthroughs.first[:subs]
380
+ end
381
+
382
+ test 'collect multi-line passthroughs from inline pass macro' do
383
+ para = block_from_string(%Q{pass:specialcharacters,quotes[<code>['more\ncode'\\]</code>]})
384
+ result = para.extract_passthroughs(para.buffer.join)
385
+ assert_equal "\x0" + '0' + "\x0", result
386
+ assert_equal 1, para.passthroughs.size
387
+ assert_equal %Q{<code>['more\ncode']</code>}, para.passthroughs.first[:text]
388
+ assert_equal [:specialcharacters, :quotes], para.passthroughs.first[:subs]
389
+ end
390
+
391
+ test 'restore inline passthroughs without subs' do
392
+ para = block_from_string("\x0" + '0' + "\x0")
393
+ para.passthroughs << {:text => '<code>inline code</code>', :subs => []}
394
+ result = para.restore_passthroughs(para.buffer.join)
395
+ assert_equal '<code>inline code</code>', result
396
+ end
397
+
398
+ # TODO add two entries to ensure index lookup is working correctly (0 indx could be ambiguous)
399
+ test 'restore inline passthroughs with subs' do
400
+ para = block_from_string("\x0" + '0' + "\x0")
401
+ para.passthroughs << {:text => '<code>{code}</code>', :subs => [:specialcharacters]}
402
+ result = para.restore_passthroughs(para.buffer.join)
403
+ assert_equal '&lt;code&gt;{code}&lt;/code&gt;', result
404
+ end
405
+ end
406
+
407
+ context 'Post replacements' do
408
+ test 'line break' do
409
+ para = block_from_string("First line +\nSecond line")
410
+ result = para.apply_subs(para.buffer, :post_replacements)
411
+ assert_equal "First line<br>\n", result.first
412
+ end
413
+ end
414
+ end