]> git.wincent.com - wikitext.git/blob - spec/pre_spec.rb
Handle optional "lang" attribute in PRE_START tags
[wikitext.git] / spec / pre_spec.rb
1 #!/usr/bin/env ruby
2 # encoding: utf-8
3 # Copyright 2007-2009 Wincent Colaiuta. All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are met:
7 #
8 # 1. Redistributions of source code must retain the above copyright notice,
9 #    this list of conditions and the following disclaimer.
10 # 2. Redistributions in binary form must reproduce the above copyright notice,
11 #    this list of conditions and the following disclaimer in the documentation
12 #    and/or other materials provided with the distribution.
13
14 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
18 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 # POSSIBILITY OF SUCH DAMAGE.
25
26 require File.join(File.dirname(__FILE__), 'spec_helper.rb')
27 require 'wikitext'
28
29 describe Wikitext::Parser, 'parsing PRE blocks' do
30   before do
31     @parser = Wikitext::Parser.new
32   end
33
34   it 'should recognize a single-line <pre> block' do
35     @parser.parse(' foo').should == "<pre>foo</pre>\n"
36   end
37
38   it 'should recognize a multiline <pre> block' do
39     @parser.parse(" foo\n bar").should == "<pre>foo\nbar</pre>\n"
40   end
41
42   it 'should handle "empty" lines in the middle of multiline PRE blocks' do
43     input = dedent <<-END
44        foo
45        
46        bar
47     END
48     expected = dedent <<-END
49       <pre>foo
50       
51       bar</pre>
52     END
53     @parser.parse(input).should == expected
54   end
55
56   it 'should render an empty block for an empty PRE block' do
57     @parser.parse(' ').should == "<pre></pre>\n"
58   end
59
60   it 'should sanely handle a leading empty line' do
61     @parser.parse(" \n foo").should == "<pre>\nfoo</pre>\n"
62   end
63
64   it 'should sanely handle a trailing empty line' do
65     @parser.parse(" foo\n \n").should == "<pre>foo\n</pre>\n"
66     @parser.parse(" foo\n ").should == "<pre>foo\n</pre>\n"
67   end
68
69   it 'should allow nesting inside a <blockquote> block' do
70     # nesting inside single blockquotes
71     @parser.parse(">  foo").should == "<blockquote>\n  <pre>foo</pre>\n</blockquote>\n"
72
73     # same, but continued over multiple lines
74     @parser.parse(">  foo\n>  bar").should == "<blockquote>\n  <pre>foo\nbar</pre>\n</blockquote>\n"
75
76     # nesting inside double blockquotes
77     expected = dedent <<-END
78       <blockquote>
79         <blockquote>
80           <pre>foo</pre>
81         </blockquote>
82       </blockquote>
83     END
84     @parser.parse("> >  foo").should == expected
85
86     # same, but continued over multiple lines
87     expected = dedent <<-END
88       <blockquote>
89         <blockquote>
90           <pre>foo
91       bar</pre>
92         </blockquote>
93       </blockquote>
94     END
95     @parser.parse("> >  foo\n> >  bar").should == expected
96   end
97
98   it 'should automatically close preceding blocks at the same depth' do
99     @parser.parse("> foo\n bar").should == "<blockquote>\n  <p>foo</p>\n</blockquote>\n<pre>bar</pre>\n"
100     expected = dedent <<-END
101       <blockquote>
102         <blockquote>
103           <p>foo</p>
104         </blockquote>
105       </blockquote>
106       <pre>bar</pre>
107     END
108     @parser.parse("> > foo\n bar").should == expected
109   end
110
111   it 'should pass <tt> and </tt> tags through without any special meaning' do
112     @parser.parse(' foo <tt>bar</tt>').should == "<pre>foo &lt;tt&gt;bar&lt;/tt&gt;</pre>\n"
113   end
114
115   it 'should pass <em> and </em> tags through without any special meaning' do
116     @parser.parse(" foo ''bar''").should == "<pre>foo ''bar''</pre>\n"
117   end
118
119   it 'should pass <strong> and </strong> tags through without any special meaning' do
120     @parser.parse(" foo '''bar'''").should == "<pre>foo '''bar'''</pre>\n"
121   end
122
123   it 'should pass combined <strong>/<em> and </strong>/</em> tags through without any special meaning' do
124     @parser.parse(" foo '''''bar'''''").should == "<pre>foo '''''bar'''''</pre>\n"
125   end
126
127   it 'should pass named entities through unchanged' do
128     @parser.parse(' &euro;').should == "<pre>&euro;</pre>\n"
129   end
130
131   it 'should pass numeric (decimal) entities through unchanged' do
132     @parser.parse(' &#8364;').should == "<pre>&#8364;</pre>\n"
133   end
134
135   it 'should pass numeric (hexadecimal) entities through unchanged' do
136     @parser.parse(' &#x20ac;').should == "<pre>&#x20ac;</pre>\n"
137     @parser.parse(' &#X20aC;').should == "<pre>&#x20ac;</pre>\n"
138   end
139
140   it 'should convert non-ASCII characters to numeric entities' do
141     @parser.parse(' €').should == "<pre>&#x20ac;</pre>\n"
142   end
143 end
144
145 describe Wikitext::Parser, 'parsing PRE_START/PRE_END blocks' do
146   before do
147     @parser = Wikitext::Parser.new
148   end
149
150   it 'should accept PRE_START/PRE_END as an alternative to the standard syntax' do
151     @parser.parse('<pre>foo</pre>').should == "<pre>foo</pre>\n"
152   end
153
154   it 'should pass through PRE unchanged in PRE_START/PRE_END blocks' do
155     input = dedent <<-END
156       <pre>line 1
157        next line</pre>
158     END
159     expected = dedent <<-END
160       <pre>line 1
161        next line</pre>
162     END
163     @parser.parse(input).should == expected
164   end
165
166   it 'should pass through short BLOCKQUOTE tokens as named entities in PRE_START/PRE_END blocks' do
167     input = dedent <<-END
168       <pre>line 1
169       >next line</pre>
170     END
171     expected = dedent <<-END
172       <pre>line 1
173       &gt;next line</pre>
174     END
175     @parser.parse(input).should == expected
176   end
177
178   it 'should pass through long BLOCKQUOTE tokens as named entities in PRE_START/PRE_END blocks' do
179     input = dedent <<-END
180       <pre>line 1
181       > next line</pre>
182     END
183     expected = dedent <<-END
184       <pre>line 1
185       &gt; next line</pre>
186     END
187     @parser.parse(input).should == expected
188   end
189
190   it 'should pass through EM unchanged in PRE_START/PRE_END blocks' do
191     @parser.parse("<pre>''</pre>").should == "<pre>''</pre>\n"
192   end
193
194   it 'should pass through STRONG unchanged in PRE_START/PRE_END blocks' do
195     @parser.parse("<pre>'''</pre>").should == "<pre>'''</pre>\n"
196   end
197
198   it 'should pass through STRONG_EM unchanged in PRE_START/PRE_END blocks' do
199     @parser.parse("<pre>'''''</pre>").should == "<pre>'''''</pre>\n"
200   end
201
202   it 'should pass through EM_START escaped in PRE_START/PRE_END blocks' do
203     @parser.parse("<pre><em></pre>").should == "<pre>&lt;em&gt;</pre>\n"
204   end
205
206   it 'should pass through EM_END escaped in PRE_START/PRE_END blocks' do
207     @parser.parse("<pre></em></pre>").should == "<pre>&lt;/em&gt;</pre>\n"
208   end
209
210   it 'should pass through STRONG_START escaped in PRE_START/PRE_END blocks' do
211     @parser.parse("<pre><strong></pre>").should == "<pre>&lt;strong&gt;</pre>\n"
212   end
213
214   it 'should pass through STRONG_END escaped in PRE_START/PRE_END blocks' do
215     @parser.parse("<pre></strong></pre>").should == "<pre>&lt;/strong&gt;</pre>\n"
216   end
217
218   it 'should pass through TT unchanged in PRE_START/PRE_END blocks' do
219     @parser.parse("<pre>`</pre>").should == "<pre>`</pre>\n"
220   end
221
222   it 'should pass through TT_START escaped in PRE_START/PRE_END blocks' do
223     @parser.parse("<pre><tt></pre>").should == "<pre>&lt;tt&gt;</pre>\n"
224   end
225
226   it 'should pass through TT_END escaped in PRE_START/PRE_END blocks' do
227     @parser.parse("<pre></tt></pre>").should == "<pre>&lt;/tt&gt;</pre>\n"
228   end
229
230   it 'should pass through UL unchanged in PRE_START/PRE_END blocks' do
231     @parser.parse("<pre>\n#</pre>").should == "<pre>\n#</pre>\n"
232   end
233
234   it 'should pass through OL unchanged in PRE_START/PRE_END blocks' do
235     @parser.parse("<pre>\n*</pre>").should == "<pre>\n*</pre>\n"
236   end
237
238   it 'should ignore PRE_START inside <nowiki> spans' do
239     @parser.parse('<nowiki><pre></nowiki>').should == "<p>&lt;pre&gt;</p>\n"
240   end
241
242   it 'should ignore PRE_END inside <nowiki> spans' do
243     @parser.parse('<nowiki></pre></nowiki>').should == "<p>&lt;/pre&gt;</p>\n"
244   end
245
246   it 'should ignore PRE_START inside standard PRE blocks' do
247     @parser.parse(' <pre>').should == "<pre>&lt;pre&gt;</pre>\n"
248   end
249
250   it 'should ignore PRE_END inside standard PRE blocks' do
251     @parser.parse(' </pre>').should == "<pre>&lt;/pre&gt;</pre>\n"
252   end
253
254   it 'should ignore PRE_START inside already open PRE_START blocks' do
255     @parser.parse('<pre><pre></pre>').should == "<pre>&lt;pre&gt;</pre>\n"
256   end
257
258   it 'should ignore PRE_START inside BLOCKQUOTE blocks' do
259     expected = dedent <<-END
260       <blockquote>
261         <p>&lt;pre&gt;</p>
262       </blockquote>
263     END
264     @parser.parse('> <pre>').should == expected
265   end
266
267   it 'should ignore PRE_END inside BLOCKQUOTE blocks' do
268     expected = dedent <<-END
269       <blockquote>
270         <p>&lt;/pre&gt;</p>
271       </blockquote>
272     END
273     @parser.parse('> </pre>').should == expected
274   end
275
276   it 'should break out of UL blocks on seeing PRE_START' do
277     expected = dedent <<-END
278       <ul>
279         <li>foo</li>
280       </ul>
281       <pre>bar</pre>
282     END
283     @parser.parse('* foo<pre>bar</pre>').should == expected
284   end
285
286   it 'should ignore PRE_END inside UL blocks' do
287     expected = dedent <<-END
288       <ul>
289         <li>&lt;/pre&gt;</li>
290       </ul>
291     END
292     @parser.parse('* </pre>').should == expected
293   end
294
295   it 'should break out of OL blocks on seeing PRE_START' do
296     expected = dedent <<-END
297       <ol>
298         <li>foo</li>
299       </ol>
300       <pre>bar</pre>
301     END
302     @parser.parse('# foo<pre>bar</pre>').should == expected
303   end
304
305   it 'should ignore PRE_END inside OL blocks' do
306     expected = dedent <<-END
307       <ol>
308         <li>&lt;/pre&gt;</li>
309       </ol>
310     END
311     @parser.parse('# </pre>').should == expected
312   end
313
314   it 'should break out of H1 blocks on seeing PRE_START' do
315     expected = dedent <<-END
316       <h1>foo</h1>
317       <pre>bar</pre>
318       <p> =</p>
319     END
320     @parser.parse('= foo<pre>bar</pre> =').should == expected
321   end
322
323   it 'should ignore PRE_END inside H1 blocks' do
324     @parser.parse('= </pre> =').should == "<h1>&lt;/pre&gt;</h1>\n"
325   end
326
327   it 'should break out of H2 blocks on seeing PRE_START' do
328     expected = dedent <<-END
329       <h2>foo</h2>
330       <pre>bar</pre>
331       <p> ==</p>
332     END
333     @parser.parse('== foo<pre>bar</pre> ==').should == expected
334   end
335
336   it 'should ignore PRE_END inside H2 blocks' do
337     @parser.parse('== </pre> ==').should == "<h2>&lt;/pre&gt;</h2>\n"
338   end
339
340   it 'should break out of H3 blocks on seeing PRE_START' do
341     expected = dedent <<-END
342       <h3>foo</h3>
343       <pre>bar</pre>
344       <p> ===</p>
345     END
346     @parser.parse('=== foo<pre>bar</pre> ===').should == expected
347   end
348
349   it 'should ignore PRE_END inside H3 blocks' do
350     @parser.parse('=== </pre> ===').should == "<h3>&lt;/pre&gt;</h3>\n"
351   end
352
353   it 'should break out of H4 blocks on seeing PRE_START' do
354     expected = dedent <<-END
355       <h4>foo</h4>
356       <pre>bar</pre>
357       <p> ====</p>
358     END
359     @parser.parse('==== foo<pre>bar</pre> ====').should == expected
360   end
361
362   it 'should ignore PRE_END inside H4 blocks' do
363     @parser.parse('==== </pre> ====').should == "<h4>&lt;/pre&gt;</h4>\n"
364   end
365
366   it 'should break out of H5 blocks on seeing PRE_START' do
367     expected = dedent <<-END
368       <h5>foo</h5>
369       <pre>bar</pre>
370       <p> =====</p>
371     END
372     @parser.parse('===== foo<pre>bar</pre> =====').should == expected
373   end
374
375   it 'should ignore PRE_END inside H5 blocks' do
376     @parser.parse('===== </pre> =====').should == "<h5>&lt;/pre&gt;</h5>\n"
377   end
378
379   it 'should break out of H6 blocks on seeing PRE_START' do
380     expected = dedent <<-END
381       <h6>foo</h6>
382       <pre>bar</pre>
383       <p> ======</p>
384     END
385     @parser.parse('====== foo<pre>bar</pre> ======').should == expected
386   end
387
388   it 'should ignore PRE_END inside H6 blocks' do
389     @parser.parse('====== </pre> ======').should == "<h6>&lt;/pre&gt;</h6>\n"
390   end
391
392   it 'should start a <pre> block on seeing PRE_START partway through a P block' do
393     # the trailing space after "hello" is preserved just like it would be if the input were "hello " and nothing else
394     expected = dedent <<-END
395       <p>hello </p>
396       <pre>world</pre>
397     END
398     @parser.parse('hello <pre>world</pre>').should == expected
399   end
400
401   it 'should close any open spans while starting a <pre> block on seeing PRE_START partway through a P block' do
402     # ''
403     expected = dedent <<-END
404       <p>hello <em>my </em></p>
405       <pre>world</pre>
406     END
407     @parser.parse("hello ''my <pre>world</pre>").should == expected
408
409     # '''
410     expected = dedent <<-END
411       <p>hello <strong>my </strong></p>
412       <pre>world</pre>
413     END
414     @parser.parse("hello '''my <pre>world</pre>").should == expected
415
416     # '''''
417     expected = dedent <<-END
418       <p>hello <strong><em>my </em></strong></p>
419       <pre>world</pre>
420     END
421     @parser.parse("hello '''''my <pre>world</pre>").should == expected
422
423     # `
424     expected = dedent <<-END
425       <p>hello <tt>my </tt></p>
426       <pre>world</pre>
427     END
428     @parser.parse("hello `my <pre>world</pre>").should == expected
429
430     # <em>
431     expected = dedent <<-END
432       <p>hello <em>my </em></p>
433       <pre>world</pre>
434     END
435     @parser.parse("hello <em>my <pre>world</pre>").should == expected
436
437     # <strong>
438     expected = dedent <<-END
439       <p>hello <strong>my </strong></p>
440       <pre>world</pre>
441     END
442     @parser.parse("hello <strong>my <pre>world</pre>").should == expected
443
444     # <strong><em>
445     expected = dedent <<-END
446       <p>hello <strong><em>my </em></strong></p>
447       <pre>world</pre>
448     END
449     @parser.parse("hello <strong><em>my <pre>world</pre>").should == expected
450
451     # <tt>
452     expected = dedent <<-END
453       <p>hello <tt>my </tt></p>
454       <pre>world</pre>
455     END
456     @parser.parse("hello <tt>my <pre>world</pre>").should == expected
457   end
458
459   it 'should rollback open internal link spans on encountering a PRE_START in the link target' do
460     expected = dedent <<-END
461       <p>[[hello </p>
462       <pre>world</pre>
463       <p>]]</p>
464     END
465     @parser.parse('[[hello <pre>world</pre>]]').should == expected
466   end
467
468   it 'should rollback open internal link spans on encountering a PRE_START in the link text' do
469     expected = dedent <<-END
470       <p>[[hello | there</p>
471       <pre>world</pre>
472       <p>]]</p>
473     END
474     @parser.parse('[[hello | there<pre>world</pre>]]').should == expected
475   end
476
477   it 'should automatically close open PRE_START blocks on hitting the end-of-file' do
478     @parser.parse('<pre>foo').should == "<pre>foo</pre>\n"
479   end
480
481   it 'should handle an optional "lang" attribute' do
482     @parser.parse('<pre lang="ruby">foo</pre>').should == %Q{<pre class="ruby-syntax">foo</pre>\n}
483   end
484
485   it 'should reject excess internal whitespace in PRE_START tags which have a "lang" attribute' do
486     @parser.parse('<pre  lang="ruby">foo</pre>').should == %Q{<p>&lt;pre  lang=&quot;ruby&quot;&gt;foo&lt;/pre&gt;</p>\n}
487     @parser.parse('<pre lang ="ruby">foo</pre>').should == %Q{<p>&lt;pre lang =&quot;ruby&quot;&gt;foo&lt;/pre&gt;</p>\n}
488     @parser.parse('<pre lang= "ruby">foo</pre>').should == %Q{<p>&lt;pre lang= &quot;ruby&quot;&gt;foo&lt;/pre&gt;</p>\n}
489     @parser.parse('<pre lang="ruby" >foo</pre>').should == %Q{<p>&lt;pre lang=&quot;ruby&quot; &gt;foo&lt;/pre&gt;</p>\n}
490   end
491
492   it 'should reject non-alpha characters in "lang" attribute' do
493     @parser.parse('<pre lang="obj-c">foo</pre>').should == %Q{<p>&lt;pre lang=&quot;obj-c&quot;&gt;foo&lt;/pre&gt;</p>\n}
494   end
495
496   it 'should reject empty "lang" attributes' do
497     @parser.parse('<pre lang="">foo</pre>').should == %Q{<p>&lt;pre lang=&quot;&quot;&gt;foo&lt;/pre&gt;</p>\n}
498   end
499 end