asciidoctor 0.0.7 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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