]> git.wincent.com - wikitext.git/blob - spec/internal_link_spec.rb
8aa0c2fcdbe248fad15c3b0a7e29a78c886b14e8
[wikitext.git] / spec / internal_link_spec.rb
1 # encoding: utf-8
2 # Copyright 2007-2010 Wincent Colaiuta. All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are met:
6 #
7 # 1. Redistributions of source code must retain the above copyright notice,
8 #    this list of conditions and the following disclaimer.
9 # 2. Redistributions in binary form must reproduce the above copyright notice,
10 #    this list of conditions and the following disclaimer in the documentation
11 #    and/or other materials provided with the distribution.
12
13 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
17 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23 # POSSIBILITY OF SUCH DAMAGE.
24
25 require 'spec_helper'
26
27 describe Wikitext::Parser, 'internal links (space to underscore off)' do
28   before do
29     @parser = Wikitext::Parser.new :space_to_underscore => false
30   end
31
32   it 'should pass through unexpected link end tokens literally' do
33     @parser.parse('foo ]] bar').should == "<p>foo ]] bar</p>\n"                                     # in plain scope
34     @parser.parse("foo '']]'' bar").should == "<p>foo <em>]]</em> bar</p>\n"                        # in EM scope
35     @parser.parse("foo ''']]''' bar").should == "<p>foo <strong>]]</strong> bar</p>\n"              # in STRONG scope
36     @parser.parse("foo ''''']]''''' bar").should == "<p>foo <strong><em>]]</em></strong> bar</p>\n" # in STRONG_EM scope
37     @parser.parse('foo <tt>]]</tt> bar').should == "<p>foo <code>]]</code> bar</p>\n"               # in TT scope
38     @parser.parse('= foo ]] bar =').should == "<h1>foo ]] bar</h1>\n"                               # in H1 scope
39     @parser.parse('== foo ]] bar ==').should == "<h2>foo ]] bar</h2>\n"                             # in H2 scope
40     @parser.parse('=== foo ]] bar ===').should == "<h3>foo ]] bar</h3>\n"                           # in H3 scope
41     @parser.parse('==== foo ]] bar ====').should == "<h4>foo ]] bar</h4>\n"                         # in H4 scope
42     @parser.parse('===== foo ]] bar =====').should == "<h5>foo ]] bar</h5>\n"                       # in H5 scope
43     @parser.parse('====== foo ]] bar ======').should == "<h6>foo ]] bar</h6>\n"                     # in H6 scope
44     @parser.parse('> ]]').should == "<blockquote>\n  <p>]]</p>\n</blockquote>\n"                    # in BLOCKQUOTE scope
45   end
46
47   it 'should turn single words into links' do
48     @parser.parse('[[foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
49   end
50
51   it 'should turn multiple words into links' do
52     @parser.parse('[[foo bar]]').should == %Q{<p><a href="/wiki/foo%20bar">foo bar</a></p>\n}
53   end
54
55   it 'should trim leading whitespace' do
56     @parser.parse('[[ foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
57     @parser.parse('[[  foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
58     @parser.parse('[[   foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
59     @parser.parse('[[    foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
60   end
61
62   it 'should trim trailing whitespace' do
63     @parser.parse('[[foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
64     @parser.parse('[[foo  ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
65     @parser.parse('[[foo   ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
66     @parser.parse('[[foo    ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}   # was a bug (exception)
67     @parser.parse('[[foo     ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}  # was a bug (crash)
68   end
69
70   it 'should trim leading and trailing whitespace (combined)' do
71     @parser.parse('[[ foo    ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
72     @parser.parse('[[  foo   ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
73     @parser.parse('[[   foo  ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
74     @parser.parse('[[    foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
75   end
76
77   it 'should leave embedded whitespace intact' do
78     @parser.parse('[[ foo bar ]]').should == %Q{<p><a href="/wiki/foo%20bar">foo bar</a></p>\n}
79     @parser.parse('[[foo bar ]]').should == %Q{<p><a href="/wiki/foo%20bar">foo bar</a></p>\n}
80     @parser.parse('[[ foo bar ]]').should == %Q{<p><a href="/wiki/foo%20bar">foo bar</a></p>\n}
81   end
82
83   it 'should encode and sanitize quotes' do
84     # note how percent encoding is used in the href, and named entities in the link text
85     @parser.parse('[[hello "world"]]').should == %Q{<p><a href="/wiki/hello%20%22world%22">hello &quot;world&quot;</a></p>\n}
86   end
87
88   it 'should encode and sanitize ampersands' do
89     @parser.parse('[[a & b]]').should == %Q{<p><a href="/wiki/a%20%26%20b">a &amp; b</a></p>\n}
90   end
91
92   it 'should allow ampersand entities (special exception)' do
93     @parser.parse('[[a &amp; b]]').should == %Q{<p><a href="/wiki/a%20%26%20b">a &amp; b</a></p>\n}
94   end
95
96   it 'should allow quote entities (special exception)' do
97     @parser.parse('[[a &quot; b]]').should == %Q{<p><a href="/wiki/a%20%22%20b">a &quot; b</a></p>\n}
98   end
99
100   it 'should handle mixed scenarios (quotes, ampersands, non-ASCII characers)' do
101     expected = %Q{<p><a href="/wiki/foo%2c%20%22bar%22%20%26%20baz%20%e2%82%ac">foo, &quot;bar&quot; &amp; baz &#x20ac;</a></p>\n}
102     @parser.parse('[[foo, "bar" & baz €]]').should == expected
103   end
104
105   it 'should handle embedded paths' do
106     expected = %Q{<p><a href="/wiki/foo%2fbar">foo/bar</a></p>\n}
107     @parser.parse('[[foo/bar]]').should == expected
108   end
109
110   it 'should handle links in paragraph flows' do
111     expected = %Q{<p>foo <a href="/wiki/bar">bar</a> baz</p>\n}
112     @parser.parse('foo [[bar]] baz').should == expected # was a bug
113   end
114
115   describe '"red link" support' do
116     it 'should accept a Proc object via the optional "link_proc" parameter' do
117       @parser.parse('foo', :link_proc => Proc.new { }).should == %Q{<p>foo</p>\n}
118     end
119
120     it 'should accept a lambda via the optional "link_proc" parameter' do
121       @parser.parse('foo', :link_proc => lambda { }).should == %Q{<p>foo</p>\n}
122     end
123
124     it 'should apply custom link CSS when supplied (Proc object version)' do
125       link_proc = Proc.new { |target| target == 'bar' ? 'redlink' : nil }
126       expected = %Q{<p><a href="/wiki/foo">foo</a> <a href="/wiki/bar" class="redlink">bar</a></p>\n}
127       @parser.parse('[[foo]] [[bar]]', :link_proc => link_proc).should == expected
128     end
129
130     it 'should apply custom link CSS when supplied (lambda version)' do
131       link_proc = lambda { |target| target == 'bar' ? 'redlink' : nil }
132       expected = %Q{<p><a href="/wiki/foo">foo</a> <a href="/wiki/bar" class="redlink">bar</a></p>\n}
133       @parser.parse('[[foo]] [[bar]]', :link_proc => link_proc).should == expected
134     end
135
136     it 'should apply no custom link CSS when supplied nil (Proc object version)' do
137       expected = %Q{<p><a href="/wiki/foo">foo</a></p>\n}
138       @parser.parse('[[foo]]', :link_proc => Proc.new { |target| nil }).should == expected
139     end
140
141     it 'should apply no custom link CSS when supplied nil (lambda version)' do
142       expected = %Q{<p><a href="/wiki/foo">foo</a></p>\n}
143       @parser.parse('[[foo]]', :link_proc => lambda { |target| nil }).should == expected
144     end
145
146     it 'should let exceptions bubble up from the link proc (Proc object version)' do
147       lambda { @parser.parse('[[foo]]', :link_proc => Proc.new { |target| raise 'bar' }) }.should raise_error(RuntimeError, /bar/)
148     end
149
150     it 'should let exceptions bubble up from the link proc (lambda version)' do
151       lambda { @parser.parse('[[foo]]', :link_proc => lambda { |target| raise 'bar' }) }.should raise_error(RuntimeError, /bar/)
152     end
153
154     it 'should complain if the link proc returns a non-stringy object (Proc object version)' do
155       lambda {
156         @parser.parse '[[foo]]', :link_proc => Proc.new { 1 }
157       }.should raise_error(TypeError, /can't convert/)
158     end
159
160     it 'should complain if the link proc returns a non-stringy object (lambda version)' do
161       lambda {
162         @parser.parse '[[foo]]', :link_proc => lambda { 1 }
163       }.should raise_error(TypeError, /can't convert/)
164     end
165
166     # a couple of Ruby's idiosynchrasies: different behaviour of lambdas and Procs
167     it 'should not complain if the Proc object accepts too many arguments' do
168       lambda {
169         @parser.parse '[[foo]]', :link_proc => Proc.new { |a,b| }
170       }.should_not raise_error(ArgumentError, /wrong number/)
171     end
172
173     it 'should complain if the lambda accepts too many arguments' do
174       lambda {
175         @parser.parse '[[foo]]', :link_proc => lambda { |a,b| }
176       }.should raise_error(ArgumentError, /wrong number/)
177     end
178
179     it 'should complain when "return" is used inside a "Proc.new" block' do
180       lambda {
181         @parser.parse '[[foo]]', :link_proc => Proc.new { return 'bar' }
182       }.should raise_error(LocalJumpError)
183     end
184
185     it 'should not complain when "return" is used inside a lambda' do
186       lambda {
187         @parser.parse '[[foo]]', :link_proc => lambda { return 'bar' }
188       }.should_not raise_error(LocalJumpError)
189     end
190
191     it 'should interact correctly with spaces in link targets (Proc object version)' do
192       link_proc = Proc.new { |target| target == 'bar b' ? 'redlink' : nil }
193       expected = %Q{<p><a href="/wiki/foo%20a">foo a</a> <a href="/wiki/bar%20b" class="redlink">bar b</a></p>\n}
194       @parser.parse('[[foo a]] [[bar b]]', :link_proc => link_proc).should == expected
195     end
196
197     it 'should interact correctly with spaces in link targets (lambda version)' do
198       link_proc = lambda { |target| target == 'bar b' ? 'redlink' : nil }
199       expected = %Q{<p><a href="/wiki/foo%20a">foo a</a> <a href="/wiki/bar%20b" class="redlink">bar b</a></p>\n}
200       @parser.parse('[[foo a]] [[bar b]]', :link_proc => link_proc).should == expected
201     end
202
203     it 'should interact correctly with explicit link text (Proc object version)' do
204       link_proc = Proc.new { |target| target == 'bar b' ? 'redlink' : nil }
205       expected = %Q{<p><a href="/wiki/foo%20a">hello</a> <a href="/wiki/bar%20b" class="redlink">world</a></p>\n}
206       @parser.parse('[[foo a|hello]] [[bar b|world]]', :link_proc => link_proc).should == expected
207     end
208
209     it 'should interact correctly with explicit link text (lambda version)' do
210       link_proc = lambda { |target| target == 'bar b' ? 'redlink' : nil }
211       expected = %Q{<p><a href="/wiki/foo%20a">hello</a> <a href="/wiki/bar%20b" class="redlink">world</a></p>\n}
212       @parser.parse('[[foo a|hello]] [[bar b|world]]', :link_proc => link_proc).should == expected
213     end
214
215     it 'should handle link targets with encoded parts (Proc object version)' do
216       link_proc = Proc.new { |target| target == 'información' ? 'redlink' : nil }
217       expected = %Q{<p><a href="/wiki/informaci%c3%b3n" class="redlink">informaci&#x00f3;n</a> <a href="/wiki/bar">bar</a></p>\n}
218       @parser.parse('[[información]] [[bar]]', :link_proc => link_proc).should == expected
219     end
220
221     it 'should handle link targets with encoded parts (lambda version)' do
222       link_proc = lambda { |target| target == 'información' ? 'redlink' : nil }
223       expected = %Q{<p><a href="/wiki/informaci%c3%b3n" class="redlink">informaci&#x00f3;n</a> <a href="/wiki/bar">bar</a></p>\n}
224       @parser.parse('[[información]] [[bar]]', :link_proc => link_proc).should == expected
225     end
226   end
227
228   describe 'custom link text' do
229     it 'should recognize link text placed after the separator' do
230       @parser.parse('[[foo|bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
231     end
232
233     it 'should trim whitespace to the left of the separator' do
234       @parser.parse('[[foo |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
235       @parser.parse('[[foo  |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
236       @parser.parse('[[foo   |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
237       @parser.parse('[[foo    |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
238       @parser.parse('[[foo     |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
239       @parser.parse('[[foo      |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
240     end
241
242     it 'should trim whitespace to the right of the separator' do
243       @parser.parse('[[foo| bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
244       @parser.parse('[[foo|  bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
245       @parser.parse('[[foo|   bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
246       @parser.parse('[[foo|    bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
247       @parser.parse('[[foo|     bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
248       @parser.parse('[[foo|      bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
249     end
250
251     it 'should trim whitespace on both sides of the separator (at the same time)' do
252       @parser.parse('[[foo      | bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
253       @parser.parse('[[foo     |  bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
254       @parser.parse('[[foo    |   bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
255       @parser.parse('[[foo   |    bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
256       @parser.parse('[[foo  |     bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
257       @parser.parse('[[foo |      bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
258     end
259
260     it 'should trim trailing whitespace from the link text' do
261       @parser.parse('[[foo|bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
262       @parser.parse('[[foo|bar  ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
263       @parser.parse('[[foo|bar   ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
264       @parser.parse('[[foo|bar    ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
265       @parser.parse('[[foo|bar     ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
266       @parser.parse('[[foo|bar      ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
267     end
268
269     it 'should trim leading and trailing whitespace from the link text' do
270       @parser.parse('[[foo|      bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
271       @parser.parse('[[foo|     bar  ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
272       @parser.parse('[[foo|    bar   ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
273       @parser.parse('[[foo|   bar    ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
274       @parser.parse('[[foo|  bar     ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
275       @parser.parse('[[foo| bar      ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
276     end
277
278     it 'should treat a separator inside the link text as part of the link text' do
279       @parser.parse('[[foo|bar|baz]]').should == %Q{<p><a href="/wiki/foo">bar|baz</a></p>\n}
280     end
281
282     it 'should treat separators outside of links as normal text' do
283       @parser.parse('foo|bar').should == %Q{<p>foo|bar</p>\n}
284     end
285
286     it 'should allow em markup in the custom link text' do
287       expected = %Q{<p><a href="/wiki/foo">bar <em>baz</em></a></p>\n}
288       @parser.parse("[[foo|bar ''baz'']]").should == expected
289     end
290
291     it 'should automatically close unclosed em markup in the custom link text' do
292       expected = %Q{<p><a href="/wiki/foo">bar <em>baz</em></a></p>\n}
293       @parser.parse("[[foo|bar ''baz]]").should == expected
294     end
295
296     it 'should allow strong markup in the custom link text' do
297       expected = %Q{<p><a href="/wiki/foo">bar <strong>baz</strong></a></p>\n}
298       @parser.parse("[[foo|bar '''baz''']]").should == expected
299     end
300
301     it 'should automatically close unclosed strong markup in the custom link text' do
302       expected = %Q{<p><a href="/wiki/foo">bar <strong>baz</strong></a></p>\n}
303       @parser.parse("[[foo|bar '''baz]]").should == expected
304     end
305
306     it 'should allow strong/em markup in the custom link text' do
307       expected = %Q{<p><a href="/wiki/foo">bar <strong><em>baz</em></strong></a></p>\n}
308       @parser.parse("[[foo|bar '''''baz''''']]").should == expected
309     end
310
311     it 'should automatically close unclosed strong/em markup in the custom link text' do
312       expected = %Q{<p><a href="/wiki/foo">bar <strong><em>baz</em></strong></a></p>\n}
313       @parser.parse("[[foo|bar '''''baz]]").should == expected
314     end
315
316     it 'should allow tt markup in the custom link text' do
317       expected = %Q{<p><a href="/wiki/foo">bar <code>baz</code></a></p>\n}
318       @parser.parse('[[foo|bar <tt>baz</tt>]]').should == expected
319     end
320
321     it 'should automatically close unclosed tt markup in the custom link text' do
322       expected = %Q{<p><a href="/wiki/foo">bar <code>baz</code></a></p>\n}
323       @parser.parse('[[foo|bar <tt>baz]]').should == expected
324     end
325
326     it 'should allow named entities in the custom link text' do
327       expected = %Q{<p><a href="/wiki/foo">bar &copy;</a></p>\n}
328       @parser.parse('[[foo|bar &copy;]]').should == expected
329
330       # explicitly test &quot; because it is tokenized separately from the other named entities
331       expected = %Q{<p><a href="/wiki/foo">bar &quot;</a></p>\n}
332       @parser.parse('[[foo|bar &quot;]]').should == expected
333
334       # explicitly test &amp; because it is tokenized separately from the other named entities
335       expected = %Q{<p><a href="/wiki/foo">bar &amp;</a></p>\n}
336       @parser.parse('[[foo|bar &amp;]]').should == expected
337     end
338
339     it 'should allow decimal entities in the custom link text' do
340       expected = %Q{<p><a href="/wiki/foo">bar &#8364;</a></p>\n}
341       @parser.parse('[[foo|bar &#8364;]]').should == expected
342     end
343
344     it 'should allow hexadecimal entities in the custom link text' do
345       expected = %Q{<p><a href="/wiki/foo">bar &#x20ac;</a></p>\n}
346       @parser.parse('[[foo|bar &#x20ac;]]').should == expected
347     end
348
349     it 'should sanitize non-ASCII characters in the custom link text' do
350       expected = %Q{<p><a href="/wiki/foo">bar &#x20ac;</a></p>\n}
351       @parser.parse('[[foo|bar €]]').should == expected
352     end
353
354     it 'should sanitize characters that have special meaning in HTML in the custom link text' do
355       expected = %Q{<p><a href="/wiki/foo">bar &lt;</a></p>\n}
356       @parser.parse('[[foo|bar <]]').should == expected
357
358       expected = %Q{<p><a href="/wiki/foo">bar &gt;</a></p>\n}
359       @parser.parse('[[foo|bar >]]').should == expected
360
361       expected = %Q{<p><a href="/wiki/foo">bar &amp;</a></p>\n}
362       @parser.parse('[[foo|bar &]]').should == expected
363
364       expected = %Q{<p><a href="/wiki/foo">bar &quot;baz&quot;</a></p>\n}
365       @parser.parse('[[foo|bar "baz"]]').should == expected
366     end
367
368     it 'should allow nowiki markup in the custom link text' do
369       expected = %Q{<p><a href="/wiki/foo">bar [[</a></p>\n}
370       @parser.parse("[[foo|bar <nowiki>[[</nowiki>]]").should == expected
371
372       expected = %Q{<p><a href="/wiki/foo">bar [</a></p>\n}
373       @parser.parse("[[foo|bar <nowiki>[</nowiki>]]").should == expected
374
375       expected = %Q{<p><a href="/wiki/foo">bar ]]</a></p>\n}
376       @parser.parse("[[foo|bar <nowiki>]]</nowiki>]]").should == expected
377
378       expected = %Q{<p><a href="/wiki/foo">bar ]</a></p>\n}
379       @parser.parse("[[foo|bar <nowiki>]</nowiki>]]").should == expected
380     end
381
382     it 'should handle paths in custom link text' do
383       expected = %Q{<p><a href="/wiki/hello%2fworld">foo/bar</a></p>\n}
384       @parser.parse('[[hello/world|foo/bar]]').should == expected
385     end
386   end
387
388   describe 'overriding the link prefix' do
389     it 'should be able to override the link prefix' do
390       @parser.internal_link_prefix = '/custom/'
391       @parser.parse('[[foo]]').should == %Q{<p><a href="/custom/foo">foo</a></p>\n}
392     end
393
394     it 'should interpet a nil link prefix as meaning no prefix' do
395       @parser.internal_link_prefix = nil
396       @parser.parse('[[foo]]').should == %Q{<p><a href="foo">foo</a></p>\n}
397     end
398   end
399
400   # "special links" existed in internal links up to and including wikitext version 1.3.2
401   # from version 1.4.0 onwards this feature was changed to instead work with external links
402   # as such, all of these specs have been updated to make sure that the old behaviour was removed
403   describe 'special links' do
404     it 'should no longer recognize links of the form "bug/10" as special links' do
405       @parser.parse('[[bug/10]]').should    == %Q{<p><a href="/wiki/bug%2f10">bug/10</a></p>\n}
406       @parser.parse('[[issue/25]]').should  == %Q{<p><a href="/wiki/issue%2f25">issue/25</a></p>\n}
407       @parser.parse('[[post/7]]').should    == %Q{<p><a href="/wiki/post%2f7">post/7</a></p>\n}
408     end
409
410     it 'should no longer accept custom link text in conjunction with special links' do
411       @parser.parse('[[bug/10|bug #10]]').should == %Q{<p><a href="/wiki/bug%2f10">bug #10</a></p>\n}
412     end
413
414     it 'should not emit special links regardless of custom internal link prefix overrides' do
415       @parser.internal_link_prefix = '/custom/'
416       @parser.parse('[[bug/10]]').should == %Q{<p><a href="/custom/bug%2f10">bug/10</a></p>\n}
417     end
418
419     it 'should (still) not classify links as special merely because of the presence of a slash' do
420       @parser.parse('[[foo/bar]]').should == %Q{<p><a href="/wiki/foo%2fbar">foo/bar</a></p>\n}
421     end
422
423     it 'should (still) not accept special links which have a leading forward slash' do
424       @parser.parse('[[/bug/10]]').should == %Q{<p><a href="/wiki/%2fbug%2f10">/bug/10</a></p>\n}
425     end
426   end
427
428   describe 'invalid links' do
429     it 'should not allow entities in the link text' do
430       @parser.parse('[[a &euro; b]]').should == "<p>[[a &euro; b]]</p>\n"
431     end
432
433     it 'should not allow URIs in the link text' do
434       expected = %Q{<p>[[hello <a href="http://example.com/" class="external">http://example.com/</a> world]]</p>\n}
435       @parser.parse('[[hello http://example.com/ world]]').should == expected
436     end
437
438     it 'should handle embedded [[ inside links' do
439       # note how first part "[[foo " in itself is invalid and so gets rejected and echoed literally
440       expected = %Q{<p>[[foo <a href="/wiki/bar">bar</a></p>\n}
441       @parser.parse('[[foo [[bar]]').should == expected
442     end
443
444     it 'should handled embedded ]] inside links' do
445       # note how the link gets terminated early and the trailing part is rejected and echoed literally
446       expected = %Q{<p><a href="/wiki/foo">foo</a>bar]]</p>\n}
447       @parser.parse('[[foo ]]bar]]').should == expected
448     end
449
450     it 'should handle embedded [ inside links' do
451       # [ is not allowed at all so the entire link is rendered invalid
452       expected = "<p>[[foo [bar]]</p>\n"
453       @parser.parse('[[foo [bar]]').should == expected
454     end
455
456     it 'should handle embedded ] inside links' do
457       # [ is not allowed at all so the entire link is rendered invalid
458       expected = "<p>[[foo ]bar]]</p>\n"
459       @parser.parse('[[foo ]bar]]').should == expected
460     end
461
462     describe 'unterminated link targets (end-of-file)' do
463       it 'should rollback and show the unterminated link' do
464         @parser.parse('[[foo').should == %Q{<p>[[foo</p>\n}
465       end
466
467       it 'should not trim leading whitespace when rolling back' do
468         @parser.parse('[[ foo').should    == %Q{<p>[[ foo</p>\n}
469         @parser.parse('[[  foo').should   == %Q{<p>[[  foo</p>\n}
470         @parser.parse('[[   foo').should  == %Q{<p>[[   foo</p>\n}
471         @parser.parse('[[    foo').should == %Q{<p>[[    foo</p>\n}
472       end
473
474       it 'should not trim trailing whitespace when rolling back' do
475         @parser.parse('[[foo ').should    == %Q{<p>[[foo </p>\n}
476         @parser.parse('[[foo  ').should   == %Q{<p>[[foo  </p>\n}
477         @parser.parse('[[foo   ').should  == %Q{<p>[[foo   </p>\n}
478         @parser.parse('[[foo    ').should == %Q{<p>[[foo    </p>\n}
479       end
480
481       it 'should not trim leadig and trailing whitespace (combined) when rolling back' do
482         @parser.parse('[[    foo ').should == %Q{<p>[[    foo </p>\n}
483         @parser.parse('[[   foo  ').should == %Q{<p>[[   foo  </p>\n}
484         @parser.parse('[[  foo   ').should == %Q{<p>[[  foo   </p>\n}
485         @parser.parse('[[ foo    ').should == %Q{<p>[[ foo    </p>\n}
486       end
487     end
488
489     describe 'unterminated link targets (end-of-line)' do
490       it 'should rollback and show the unterminated link' do
491         @parser.parse("[[foo\n").should == %Q{<p>[[foo</p>\n}
492       end
493
494       it 'should not trim leading whitespace when rolling back' do
495         @parser.parse("[[ foo\n").should    == %Q{<p>[[ foo</p>\n}
496         @parser.parse("[[  foo\n").should   == %Q{<p>[[  foo</p>\n}
497         @parser.parse("[[   foo\n").should  == %Q{<p>[[   foo</p>\n}
498         @parser.parse("[[    foo\n").should == %Q{<p>[[    foo</p>\n}
499       end
500
501       it 'should not trim trailing whitespace when rolling back' do
502         @parser.parse("[[foo \n").should    == %Q{<p>[[foo </p>\n}
503         @parser.parse("[[foo  \n").should   == %Q{<p>[[foo  </p>\n}
504         @parser.parse("[[foo   \n").should  == %Q{<p>[[foo   </p>\n}
505         @parser.parse("[[foo    \n").should == %Q{<p>[[foo    </p>\n}
506       end
507
508       it 'should not trim leading and trailing whitespace (combined) when rolling back' do
509         @parser.parse("[[ foo    \n").should == %Q{<p>[[ foo    </p>\n}
510         @parser.parse("[[  foo   \n").should == %Q{<p>[[  foo   </p>\n}
511         @parser.parse("[[   foo  \n").should == %Q{<p>[[   foo  </p>\n}
512         @parser.parse("[[    foo \n").should == %Q{<p>[[    foo </p>\n}
513       end
514     end
515
516     describe 'missing link text' do
517       it 'should use link target' do
518         @parser.parse('[[foo|]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
519       end
520     end
521
522     describe 'link cut off at separator (end-of-file)' do
523       it 'should rollback and show the unterminated link' do
524         @parser.parse('[[foo|').should == %Q{<p>[[foo|</p>\n}
525         @parser.parse('[[foo| ').should == %Q{<p>[[foo| </p>\n}
526         @parser.parse('[[foo|  ').should == %Q{<p>[[foo|  </p>\n}
527         @parser.parse('[[foo|   ').should == %Q{<p>[[foo|   </p>\n}
528         @parser.parse('[[foo|    ').should == %Q{<p>[[foo|    </p>\n}
529         @parser.parse('[[foo|     ').should == %Q{<p>[[foo|     </p>\n}
530         @parser.parse('[[foo|      ').should == %Q{<p>[[foo|      </p>\n}
531       end
532     end
533
534     describe 'link cut off at separator (end-of-line)' do
535       it 'should rollback and show the unterminated link' do
536         @parser.parse("[[foo|\n").should == %Q{<p>[[foo|</p>\n}
537         @parser.parse("[[foo| \n").should == %Q{<p>[[foo| </p>\n}
538         @parser.parse("[[foo|  \n").should == %Q{<p>[[foo|  </p>\n}
539         @parser.parse("[[foo|   \n").should == %Q{<p>[[foo|   </p>\n}
540         @parser.parse("[[foo|    \n").should == %Q{<p>[[foo|    </p>\n}
541         @parser.parse("[[foo|     \n").should == %Q{<p>[[foo|     </p>\n}
542         @parser.parse("[[foo|      \n").should == %Q{<p>[[foo|      </p>\n}
543       end
544     end
545
546     describe 'unterminated link text (end-of-file)' do
547       it 'should rollback and show the unterminated link' do
548         @parser.parse('[[foo|hello').should == %Q{<p>[[foo|hello</p>\n}
549         @parser.parse('[[foo|hello ').should == %Q{<p>[[foo|hello </p>\n}
550         @parser.parse('[[foo|hello  ').should == %Q{<p>[[foo|hello  </p>\n}
551         @parser.parse('[[foo|hello   ').should == %Q{<p>[[foo|hello   </p>\n}
552         @parser.parse('[[foo|hello    ').should == %Q{<p>[[foo|hello    </p>\n}
553         @parser.parse('[[foo|hello     ').should == %Q{<p>[[foo|hello     </p>\n}
554         @parser.parse('[[foo|hello      ').should == %Q{<p>[[foo|hello      </p>\n}
555       end
556     end
557
558     describe 'unterminated link text (end-of-line)' do
559       it 'should rollback and show the unterminated link' do
560         @parser.parse("[[foo|hello\n").should == %Q{<p>[[foo|hello</p>\n}
561         @parser.parse("[[foo|hello \n").should == %Q{<p>[[foo|hello </p>\n}
562         @parser.parse("[[foo|hello  \n").should == %Q{<p>[[foo|hello  </p>\n}
563         @parser.parse("[[foo|hello   \n").should == %Q{<p>[[foo|hello   </p>\n}
564         @parser.parse("[[foo|hello    \n").should == %Q{<p>[[foo|hello    </p>\n}
565         @parser.parse("[[foo|hello     \n").should == %Q{<p>[[foo|hello     </p>\n}
566         @parser.parse("[[foo|hello      \n").should == %Q{<p>[[foo|hello      </p>\n}
567       end
568     end
569   end
570 end
571
572 describe Wikitext::Parser, 'internal links (space to underscore on)' do
573   before do
574     @parser = Wikitext::Parser.new
575   end
576
577   it 'should pass through unexpected link end tokens literally' do
578     @parser.parse('foo ]] bar').should == "<p>foo ]] bar</p>\n"                                     # in plain scope
579     @parser.parse("foo '']]'' bar").should == "<p>foo <em>]]</em> bar</p>\n"                        # in EM scope
580     @parser.parse("foo ''']]''' bar").should == "<p>foo <strong>]]</strong> bar</p>\n"              # in STRONG scope
581     @parser.parse("foo ''''']]''''' bar").should == "<p>foo <strong><em>]]</em></strong> bar</p>\n" # in STRONG_EM scope
582     @parser.parse('foo <tt>]]</tt> bar').should == "<p>foo <code>]]</code> bar</p>\n"               # in TT scope
583     @parser.parse('= foo ]] bar =').should == "<h1>foo ]] bar</h1>\n"                               # in H1 scope
584     @parser.parse('== foo ]] bar ==').should == "<h2>foo ]] bar</h2>\n"                             # in H2 scope
585     @parser.parse('=== foo ]] bar ===').should == "<h3>foo ]] bar</h3>\n"                           # in H3 scope
586     @parser.parse('==== foo ]] bar ====').should == "<h4>foo ]] bar</h4>\n"                         # in H4 scope
587     @parser.parse('===== foo ]] bar =====').should == "<h5>foo ]] bar</h5>\n"                       # in H5 scope
588     @parser.parse('====== foo ]] bar ======').should == "<h6>foo ]] bar</h6>\n"                     # in H6 scope
589     @parser.parse('> ]]').should == "<blockquote>\n  <p>]]</p>\n</blockquote>\n"                    # in BLOCKQUOTE scope
590   end
591
592   it 'should turn single words into links' do
593     @parser.parse('[[foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
594   end
595
596   it 'should turn multiple words into links, converting spaces into underscores' do
597     @parser.parse('[[foo bar]]').should == %Q{<p><a href="/wiki/foo_bar">foo bar</a></p>\n}
598   end
599
600   it 'should trim leading whitespace' do
601     @parser.parse('[[ foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
602     @parser.parse('[[  foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
603     @parser.parse('[[   foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
604     @parser.parse('[[    foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
605   end
606
607   it 'should trim trailing whitespace' do
608     @parser.parse('[[foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
609     @parser.parse('[[foo  ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
610     @parser.parse('[[foo   ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
611     @parser.parse('[[foo    ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}   # was a bug (exception)
612     @parser.parse('[[foo     ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}  # was a bug (crash)
613   end
614
615   it 'should trim leading and trailing whitespace (combined)' do
616     @parser.parse('[[ foo    ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
617     @parser.parse('[[  foo   ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
618     @parser.parse('[[   foo  ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
619     @parser.parse('[[    foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
620   end
621
622   it 'should convert embedded whitespace into underscores' do
623     @parser.parse('[[ foo bar ]]').should == %Q{<p><a href="/wiki/foo_bar">foo bar</a></p>\n}
624     @parser.parse('[[foo bar ]]').should == %Q{<p><a href="/wiki/foo_bar">foo bar</a></p>\n}
625     @parser.parse('[[ foo bar ]]').should == %Q{<p><a href="/wiki/foo_bar">foo bar</a></p>\n}
626   end
627
628   it 'should encode and sanitize quotes' do
629     # note how percent encoding is used in the href, and named entities in the link text
630     @parser.parse('[[hello "world"]]').should == %Q{<p><a href="/wiki/hello_%22world%22">hello &quot;world&quot;</a></p>\n}
631   end
632
633   it 'should encode and sanitize ampersands' do
634     @parser.parse('[[a & b]]').should == %Q{<p><a href="/wiki/a_%26_b">a &amp; b</a></p>\n}
635   end
636
637   it 'should allow ampersand entities (special exception)' do
638     @parser.parse('[[a &amp; b]]').should == %Q{<p><a href="/wiki/a_%26_b">a &amp; b</a></p>\n}
639   end
640
641   it 'should allow quote entities (special exception)' do
642     @parser.parse('[[a &quot; b]]').should == %Q{<p><a href="/wiki/a_%22_b">a &quot; b</a></p>\n}
643   end
644
645   it 'should handle mixed scenarios (quotes, ampersands, non-ASCII characers)' do
646     expected = %Q{<p><a href="/wiki/foo%2c_%22bar%22_%26_baz_%e2%82%ac">foo, &quot;bar&quot; &amp; baz &#x20ac;</a></p>\n}
647     @parser.parse('[[foo, "bar" & baz €]]').should == expected
648   end
649
650   it 'should handle links in paragraph flows' do
651     expected = %Q{<p>foo <a href="/wiki/bar">bar</a> baz</p>\n}
652     @parser.parse('foo [[bar]] baz').should == expected # was a bug
653   end
654
655   describe '"red link" support' do
656     it 'should accept a Proc object via the optional "link_proc" parameter' do
657       @parser.parse('foo', :link_proc => Proc.new { }).should == %Q{<p>foo</p>\n}
658     end
659
660     it 'should accept a lambda via the optional "link_proc" parameter' do
661       @parser.parse('foo', :link_proc => lambda { }).should == %Q{<p>foo</p>\n}
662     end
663
664     it 'should apply custom link CSS when supplied (Proc object version)' do
665       link_proc = Proc.new { |target| target == 'bar' ? 'redlink' : nil }
666       expected = %Q{<p><a href="/wiki/foo">foo</a> <a href="/wiki/bar" class="redlink">bar</a></p>\n}
667       @parser.parse('[[foo]] [[bar]]', :link_proc => link_proc).should == expected
668     end
669
670     it 'should apply custom link CSS when supplied (lambda version)' do
671       link_proc = lambda { |target| target == 'bar' ? 'redlink' : nil }
672       expected = %Q{<p><a href="/wiki/foo">foo</a> <a href="/wiki/bar" class="redlink">bar</a></p>\n}
673       @parser.parse('[[foo]] [[bar]]', :link_proc => link_proc).should == expected
674     end
675
676     it 'should apply no custom link CSS when supplied nil (Proc object version)' do
677       expected = %Q{<p><a href="/wiki/foo">foo</a></p>\n}
678       @parser.parse('[[foo]]', :link_proc => Proc.new { |target| nil }).should == expected
679     end
680
681     it 'should apply no custom link CSS when supplied nil (lambda version)' do
682       expected = %Q{<p><a href="/wiki/foo">foo</a></p>\n}
683       @parser.parse('[[foo]]', :link_proc => lambda { |target| nil }).should == expected
684     end
685
686     it 'should let exceptions bubble up from the link proc (Proc object version)' do
687       lambda { @parser.parse('[[foo]]', :link_proc => Proc.new { |target| raise 'bar' }) }.should raise_error(RuntimeError, /bar/)
688     end
689
690     it 'should let exceptions bubble up from the link proc (lambda version)' do
691       lambda { @parser.parse('[[foo]]', :link_proc => lambda { |target| raise 'bar' }) }.should raise_error(RuntimeError, /bar/)
692     end
693
694     it 'should complain if the link proc returns a non-stringy object (Proc object version)' do
695       lambda {
696         @parser.parse '[[foo]]', :link_proc => Proc.new { 1 }
697       }.should raise_error(TypeError, /can't convert/)
698     end
699
700     it 'should complain if the link proc returns a non-stringy object (lambda version)' do
701       lambda {
702         @parser.parse '[[foo]]', :link_proc => lambda { 1 }
703       }.should raise_error(TypeError, /can't convert/)
704     end
705
706     # a couple of Ruby's idiosynchrasies: different behaviour of lambdas and Procs
707     it 'should not complain if the Proc object accepts too many arguments' do
708       lambda {
709         @parser.parse '[[foo]]', :link_proc => Proc.new { |a,b| }
710       }.should_not raise_error(ArgumentError, /wrong number/)
711     end
712
713     it 'should complain if the lambda accepts too many arguments' do
714       lambda {
715         @parser.parse '[[foo]]', :link_proc => lambda { |a,b| }
716       }.should raise_error(ArgumentError, /wrong number/)
717     end
718
719     it 'should complain when "return" is used inside a "Proc.new" block' do
720       lambda {
721         @parser.parse '[[foo]]', :link_proc => Proc.new { return 'bar' }
722       }.should raise_error(LocalJumpError)
723     end
724
725     it 'should not complain when "return" is used inside a lambda' do
726       lambda {
727         @parser.parse '[[foo]]', :link_proc => lambda { return 'bar' }
728       }.should_not raise_error(LocalJumpError)
729     end
730
731     it 'should interact correctly with spaces in link targets (Proc object version)' do
732       link_proc = Proc.new { |target| target == 'bar b' ? 'redlink' : nil }
733       expected = %Q{<p><a href="/wiki/foo_a">foo a</a> <a href="/wiki/bar_b" class="redlink">bar b</a></p>\n}
734       @parser.parse('[[foo a]] [[bar b]]', :link_proc => link_proc).should == expected
735     end
736
737     it 'should interact correctly with spaces in link targets (lambda version)' do
738       link_proc = lambda { |target| target == 'bar b' ? 'redlink' : nil }
739       expected = %Q{<p><a href="/wiki/foo_a">foo a</a> <a href="/wiki/bar_b" class="redlink">bar b</a></p>\n}
740       @parser.parse('[[foo a]] [[bar b]]', :link_proc => link_proc).should == expected
741     end
742
743     it 'should interact correctly with explicit link text (Proc object version)' do
744       link_proc = Proc.new { |target| target == 'bar b' ? 'redlink' : nil }
745       expected = %Q{<p><a href="/wiki/foo_a">hello</a> <a href="/wiki/bar_b" class="redlink">world</a></p>\n}
746       @parser.parse('[[foo a|hello]] [[bar b|world]]', :link_proc => link_proc).should == expected
747     end
748
749     it 'should interact correctly with explicit link text (lambda version)' do
750       link_proc = lambda { |target| target == 'bar b' ? 'redlink' : nil }
751       expected = %Q{<p><a href="/wiki/foo_a">hello</a> <a href="/wiki/bar_b" class="redlink">world</a></p>\n}
752       @parser.parse('[[foo a|hello]] [[bar b|world]]', :link_proc => link_proc).should == expected
753     end
754
755     it 'should handle link targets with encoded parts (Proc object version)' do
756       link_proc = Proc.new { |target| target == 'información' ? 'redlink' : nil }
757       expected = %Q{<p><a href="/wiki/informaci%c3%b3n" class="redlink">informaci&#x00f3;n</a> <a href="/wiki/bar">bar</a></p>\n}
758       @parser.parse('[[información]] [[bar]]', :link_proc => link_proc).should == expected
759     end
760
761     it 'should handle link targets with encoded parts (lambda version)' do
762       link_proc = lambda { |target| target == 'información' ? 'redlink' : nil }
763       expected = %Q{<p><a href="/wiki/informaci%c3%b3n" class="redlink">informaci&#x00f3;n</a> <a href="/wiki/bar">bar</a></p>\n}
764       @parser.parse('[[información]] [[bar]]', :link_proc => link_proc).should == expected
765     end
766   end
767
768   describe 'custom link text' do
769     it 'should recognize link text placed after the separator' do
770       @parser.parse('[[foo|bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
771     end
772
773     it 'should trim whitespace to the left of the separator' do
774       @parser.parse('[[foo |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
775       @parser.parse('[[foo  |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
776       @parser.parse('[[foo   |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
777       @parser.parse('[[foo    |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
778       @parser.parse('[[foo     |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
779       @parser.parse('[[foo      |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
780     end
781
782     it 'should trim whitespace to the right of the separator' do
783       @parser.parse('[[foo| bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
784       @parser.parse('[[foo|  bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
785       @parser.parse('[[foo|   bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
786       @parser.parse('[[foo|    bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
787       @parser.parse('[[foo|     bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
788       @parser.parse('[[foo|      bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
789     end
790
791     it 'should trim whitespace on both sides of the separator (at the same time)' do
792       @parser.parse('[[foo      | bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
793       @parser.parse('[[foo     |  bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
794       @parser.parse('[[foo    |   bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
795       @parser.parse('[[foo   |    bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
796       @parser.parse('[[foo  |     bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
797       @parser.parse('[[foo |      bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
798     end
799
800     it 'should trim trailing whitespace from the link text' do
801       @parser.parse('[[foo|bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
802       @parser.parse('[[foo|bar  ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
803       @parser.parse('[[foo|bar   ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
804       @parser.parse('[[foo|bar    ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
805       @parser.parse('[[foo|bar     ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
806       @parser.parse('[[foo|bar      ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
807     end
808
809     it 'should trim leading and trailing whitespace from the link text' do
810       @parser.parse('[[foo|      bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
811       @parser.parse('[[foo|     bar  ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
812       @parser.parse('[[foo|    bar   ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
813       @parser.parse('[[foo|   bar    ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
814       @parser.parse('[[foo|  bar     ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
815       @parser.parse('[[foo| bar      ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
816     end
817
818     it 'should treat a separator inside the link text as part of the link text' do
819       @parser.parse('[[foo|bar|baz]]').should == %Q{<p><a href="/wiki/foo">bar|baz</a></p>\n}
820     end
821
822     it 'should treat separators outside of links as normal text' do
823       @parser.parse('foo|bar').should == %Q{<p>foo|bar</p>\n}
824     end
825
826     it 'should allow em markup in the custom link text' do
827       expected = %Q{<p><a href="/wiki/foo">bar <em>baz</em></a></p>\n}
828       @parser.parse("[[foo|bar ''baz'']]").should == expected
829     end
830
831     it 'should automatically close unclosed em markup in the custom link text' do
832       expected = %Q{<p><a href="/wiki/foo">bar <em>baz</em></a></p>\n}
833       @parser.parse("[[foo|bar ''baz]]").should == expected
834     end
835
836     it 'should allow strong markup in the custom link text' do
837       expected = %Q{<p><a href="/wiki/foo">bar <strong>baz</strong></a></p>\n}
838       @parser.parse("[[foo|bar '''baz''']]").should == expected
839     end
840
841     it 'should automatically close unclosed strong markup in the custom link text' do
842       expected = %Q{<p><a href="/wiki/foo">bar <strong>baz</strong></a></p>\n}
843       @parser.parse("[[foo|bar '''baz]]").should == expected
844     end
845
846     it 'should allow strong/em markup in the custom link text' do
847       expected = %Q{<p><a href="/wiki/foo">bar <strong><em>baz</em></strong></a></p>\n}
848       @parser.parse("[[foo|bar '''''baz''''']]").should == expected
849     end
850
851     it 'should automatically close unclosed strong/em markup in the custom link text' do
852       expected = %Q{<p><a href="/wiki/foo">bar <strong><em>baz</em></strong></a></p>\n}
853       @parser.parse("[[foo|bar '''''baz]]").should == expected
854     end
855
856     it 'should allow tt markup in the custom link text' do
857       expected = %Q{<p><a href="/wiki/foo">bar <code>baz</code></a></p>\n}
858       @parser.parse('[[foo|bar <tt>baz</tt>]]').should == expected
859     end
860
861     it 'should automatically close unclosed tt markup in the custom link text' do
862       expected = %Q{<p><a href="/wiki/foo">bar <code>baz</code></a></p>\n}
863       @parser.parse('[[foo|bar <tt>baz]]').should == expected
864     end
865
866     it 'should allow named entities in the custom link text' do
867       expected = %Q{<p><a href="/wiki/foo">bar &copy;</a></p>\n}
868       @parser.parse('[[foo|bar &copy;]]').should == expected
869
870       # explicitly test &quot; because it is tokenized separately from the other named entities
871       expected = %Q{<p><a href="/wiki/foo">bar &quot;</a></p>\n}
872       @parser.parse('[[foo|bar &quot;]]').should == expected
873
874       # explicitly test &amp; because it is tokenized separately from the other named entities
875       expected = %Q{<p><a href="/wiki/foo">bar &amp;</a></p>\n}
876       @parser.parse('[[foo|bar &amp;]]').should == expected
877     end
878
879     it 'should allow decimal entities in the custom link text' do
880       expected = %Q{<p><a href="/wiki/foo">bar &#8364;</a></p>\n}
881       @parser.parse('[[foo|bar &#8364;]]').should == expected
882     end
883
884     it 'should allow hexadecimal entities in the custom link text' do
885       expected = %Q{<p><a href="/wiki/foo">bar &#x20ac;</a></p>\n}
886       @parser.parse('[[foo|bar &#x20ac;]]').should == expected
887     end
888
889     it 'should sanitize non-ASCII characters in the custom link text' do
890       expected = %Q{<p><a href="/wiki/foo">bar &#x20ac;</a></p>\n}
891       @parser.parse('[[foo|bar €]]').should == expected
892     end
893
894     it 'should sanitize characters that have special meaning in HTML in the custom link text' do
895       expected = %Q{<p><a href="/wiki/foo">bar &lt;</a></p>\n}
896       @parser.parse('[[foo|bar <]]').should == expected
897
898       expected = %Q{<p><a href="/wiki/foo">bar &gt;</a></p>\n}
899       @parser.parse('[[foo|bar >]]').should == expected
900
901       expected = %Q{<p><a href="/wiki/foo">bar &amp;</a></p>\n}
902       @parser.parse('[[foo|bar &]]').should == expected
903
904       expected = %Q{<p><a href="/wiki/foo">bar &quot;baz&quot;</a></p>\n}
905       @parser.parse('[[foo|bar "baz"]]').should == expected
906     end
907
908     it 'should allow nowiki markup in the custom link text' do
909       expected = %Q{<p><a href="/wiki/foo">bar [[</a></p>\n}
910       @parser.parse("[[foo|bar <nowiki>[[</nowiki>]]").should == expected
911
912       expected = %Q{<p><a href="/wiki/foo">bar [</a></p>\n}
913       @parser.parse("[[foo|bar <nowiki>[</nowiki>]]").should == expected
914
915       expected = %Q{<p><a href="/wiki/foo">bar ]]</a></p>\n}
916       @parser.parse("[[foo|bar <nowiki>]]</nowiki>]]").should == expected
917
918       expected = %Q{<p><a href="/wiki/foo">bar ]</a></p>\n}
919       @parser.parse("[[foo|bar <nowiki>]</nowiki>]]").should == expected
920     end
921   end
922
923   describe 'overriding the link prefix' do
924     it 'should be able to override the link prefix' do
925       @parser.internal_link_prefix = '/custom/'
926       @parser.parse('[[foo]]').should == %Q{<p><a href="/custom/foo">foo</a></p>\n}
927     end
928
929     it 'should interpet a nil link prefix as meaning no prefix' do
930       @parser.internal_link_prefix = nil
931       @parser.parse('[[foo]]').should == %Q{<p><a href="foo">foo</a></p>\n}
932     end
933   end
934
935   # see note above about "special links" being removed from internal links from 1.4.0 onwards
936   describe 'special links' do
937     it 'should no longer recognize links of the form "bug/10" as special links' do
938       @parser.parse('[[bug/10]]').should    == %Q{<p><a href="/wiki/bug%2f10">bug/10</a></p>\n}
939       @parser.parse('[[issue/25]]').should  == %Q{<p><a href="/wiki/issue%2f25">issue/25</a></p>\n}
940       @parser.parse('[[post/7]]').should    == %Q{<p><a href="/wiki/post%2f7">post/7</a></p>\n}
941     end
942
943     it 'should no longer accept custom link text in conjunction with special links' do
944       @parser.parse('[[bug/10|bug #10]]').should == %Q{<p><a href="/wiki/bug%2f10">bug #10</a></p>\n}
945     end
946
947     it 'should not emit special links regardless of custom internal link prefix overrides' do
948       @parser.internal_link_prefix = '/custom/'
949       @parser.parse('[[bug/10]]').should == %Q{<p><a href="/custom/bug%2f10">bug/10</a></p>\n}
950     end
951
952     it 'should (still) not classify links as special merely because of the presence of a slash' do
953       @parser.parse('[[foo/bar]]').should == %Q{<p><a href="/wiki/foo%2fbar">foo/bar</a></p>\n}
954     end
955
956     it 'should (still) not accept special links which have a leading forward slash' do
957       @parser.parse('[[/bug/10]]').should == %Q{<p><a href="/wiki/%2fbug%2f10">/bug/10</a></p>\n}
958     end
959   end
960
961   describe 'invalid links' do
962     it 'should not allow entities in the link text' do
963       @parser.parse('[[a &euro; b]]').should == "<p>[[a &euro; b]]</p>\n"
964     end
965
966     it 'should not allow URIs in the link text' do
967       expected = %Q{<p>[[hello <a href="http://example.com/" class="external">http://example.com/</a> world]]</p>\n}
968       @parser.parse('[[hello http://example.com/ world]]').should == expected
969     end
970
971     it 'should handle embedded [[ inside links' do
972       # note how first part "[[foo " in itself is invalid and so gets rejected and echoed literally
973       expected = %Q{<p>[[foo <a href="/wiki/bar">bar</a></p>\n}
974       @parser.parse('[[foo [[bar]]').should == expected
975     end
976
977     it 'should handled embedded ]] inside links' do
978       # note how the link gets terminated early and the trailing part is rejected and echoed literally
979       expected = %Q{<p><a href="/wiki/foo">foo</a>bar]]</p>\n}
980       @parser.parse('[[foo ]]bar]]').should == expected
981     end
982
983     it 'should handle embedded [ inside links' do
984       # [ is not allowed at all so the entire link is rendered invalid
985       expected = "<p>[[foo [bar]]</p>\n"
986       @parser.parse('[[foo [bar]]').should == expected
987     end
988
989     it 'should handle embedded ] inside links' do
990       # [ is not allowed at all so the entire link is rendered invalid
991       expected = "<p>[[foo ]bar]]</p>\n"
992       @parser.parse('[[foo ]bar]]').should == expected
993     end
994
995     describe 'unterminated link targets (end-of-file)' do
996       it 'should rollback and show the unterminated link' do
997         @parser.parse('[[foo').should == %Q{<p>[[foo</p>\n}
998       end
999
1000       it 'should not trim leading whitespace when rolling back' do
1001         @parser.parse('[[ foo').should    == %Q{<p>[[ foo</p>\n}
1002         @parser.parse('[[  foo').should   == %Q{<p>[[  foo</p>\n}
1003         @parser.parse('[[   foo').should  == %Q{<p>[[   foo</p>\n}
1004         @parser.parse('[[    foo').should == %Q{<p>[[    foo</p>\n}
1005       end
1006
1007       it 'should not trim trailing whitespace when rolling back' do
1008         @parser.parse('[[foo ').should    == %Q{<p>[[foo </p>\n}
1009         @parser.parse('[[foo  ').should   == %Q{<p>[[foo  </p>\n}
1010         @parser.parse('[[foo   ').should  == %Q{<p>[[foo   </p>\n}
1011         @parser.parse('[[foo    ').should == %Q{<p>[[foo    </p>\n}
1012       end
1013
1014       it 'should not trim leadig and trailing whitespace (combined) when rolling back' do
1015         @parser.parse('[[    foo ').should == %Q{<p>[[    foo </p>\n}
1016         @parser.parse('[[   foo  ').should == %Q{<p>[[   foo  </p>\n}
1017         @parser.parse('[[  foo   ').should == %Q{<p>[[  foo   </p>\n}
1018         @parser.parse('[[ foo    ').should == %Q{<p>[[ foo    </p>\n}
1019       end
1020     end
1021
1022     describe 'unterminated link targets (end-of-line)' do
1023       it 'should rollback and show the unterminated link' do
1024         @parser.parse("[[foo\n").should == %Q{<p>[[foo</p>\n}
1025       end
1026
1027       it 'should not trim leading whitespace when rolling back' do
1028         @parser.parse("[[ foo\n").should    == %Q{<p>[[ foo</p>\n}
1029         @parser.parse("[[  foo\n").should   == %Q{<p>[[  foo</p>\n}
1030         @parser.parse("[[   foo\n").should  == %Q{<p>[[   foo</p>\n}
1031         @parser.parse("[[    foo\n").should == %Q{<p>[[    foo</p>\n}
1032       end
1033
1034       it 'should not trim trailing whitespace when rolling back' do
1035         @parser.parse("[[foo \n").should    == %Q{<p>[[foo </p>\n}
1036         @parser.parse("[[foo  \n").should   == %Q{<p>[[foo  </p>\n}
1037         @parser.parse("[[foo   \n").should  == %Q{<p>[[foo   </p>\n}
1038         @parser.parse("[[foo    \n").should == %Q{<p>[[foo    </p>\n}
1039       end
1040
1041       it 'should not trim leading and trailing whitespace (combined) when rolling back' do
1042         @parser.parse("[[ foo    \n").should == %Q{<p>[[ foo    </p>\n}
1043         @parser.parse("[[  foo   \n").should == %Q{<p>[[  foo   </p>\n}
1044         @parser.parse("[[   foo  \n").should == %Q{<p>[[   foo  </p>\n}
1045         @parser.parse("[[    foo \n").should == %Q{<p>[[    foo </p>\n}
1046       end
1047     end
1048
1049     describe 'missing link text' do
1050       it 'should use link target (zero-width link text)' do
1051         @parser.parse('[[foo|]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
1052       end
1053
1054       # was a bug in version <= 1.6
1055       # emitted: <p><a href="/wiki/foo"></a></p>\n
1056       it 'should use link target (blank link text)' do
1057         @parser.parse('[[foo| ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
1058       end
1059     end
1060
1061     describe 'link cut off at separator (end-of-file)' do
1062       it 'should rollback and show the unterminated link' do
1063         @parser.parse('[[foo|').should == %Q{<p>[[foo|</p>\n}
1064         @parser.parse('[[foo| ').should == %Q{<p>[[foo| </p>\n}
1065         @parser.parse('[[foo|  ').should == %Q{<p>[[foo|  </p>\n}
1066         @parser.parse('[[foo|   ').should == %Q{<p>[[foo|   </p>\n}
1067         @parser.parse('[[foo|    ').should == %Q{<p>[[foo|    </p>\n}
1068         @parser.parse('[[foo|     ').should == %Q{<p>[[foo|     </p>\n}
1069         @parser.parse('[[foo|      ').should == %Q{<p>[[foo|      </p>\n}
1070       end
1071     end
1072
1073     describe 'link cut off at separator (end-of-line)' do
1074       it 'should rollback and show the unterminated link' do
1075         @parser.parse("[[foo|\n").should == %Q{<p>[[foo|</p>\n}
1076         @parser.parse("[[foo| \n").should == %Q{<p>[[foo| </p>\n}
1077         @parser.parse("[[foo|  \n").should == %Q{<p>[[foo|  </p>\n}
1078         @parser.parse("[[foo|   \n").should == %Q{<p>[[foo|   </p>\n}
1079         @parser.parse("[[foo|    \n").should == %Q{<p>[[foo|    </p>\n}
1080         @parser.parse("[[foo|     \n").should == %Q{<p>[[foo|     </p>\n}
1081         @parser.parse("[[foo|      \n").should == %Q{<p>[[foo|      </p>\n}
1082       end
1083     end
1084
1085     describe 'unterminated link text (end-of-file)' do
1086       it 'should rollback and show the unterminated link' do
1087         @parser.parse('[[foo|hello').should == %Q{<p>[[foo|hello</p>\n}
1088         @parser.parse('[[foo|hello ').should == %Q{<p>[[foo|hello </p>\n}
1089         @parser.parse('[[foo|hello  ').should == %Q{<p>[[foo|hello  </p>\n}
1090         @parser.parse('[[foo|hello   ').should == %Q{<p>[[foo|hello   </p>\n}
1091         @parser.parse('[[foo|hello    ').should == %Q{<p>[[foo|hello    </p>\n}
1092         @parser.parse('[[foo|hello     ').should == %Q{<p>[[foo|hello     </p>\n}
1093         @parser.parse('[[foo|hello      ').should == %Q{<p>[[foo|hello      </p>\n}
1094       end
1095     end
1096
1097     describe 'unterminated link text (end-of-line)' do
1098       it 'should rollback and show the unterminated link' do
1099         @parser.parse("[[foo|hello\n").should == %Q{<p>[[foo|hello</p>\n}
1100         @parser.parse("[[foo|hello \n").should == %Q{<p>[[foo|hello </p>\n}
1101         @parser.parse("[[foo|hello  \n").should == %Q{<p>[[foo|hello  </p>\n}
1102         @parser.parse("[[foo|hello   \n").should == %Q{<p>[[foo|hello   </p>\n}
1103         @parser.parse("[[foo|hello    \n").should == %Q{<p>[[foo|hello    </p>\n}
1104         @parser.parse("[[foo|hello     \n").should == %Q{<p>[[foo|hello     </p>\n}
1105         @parser.parse("[[foo|hello      \n").should == %Q{<p>[[foo|hello      </p>\n}
1106       end
1107     end
1108   end
1109 end