To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / .svn / pristine / 73 / 73413db85b6306a4b47ad7eb7dece1c50a1f9330.svn-base @ 1297:0a574315af3e

History | View | Annotate | Download (13.8 KB)

1 1296:038ba2d95de8 Chris
# Redmine - project management software
2
# Copyright (C) 2006-2012  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17
18
require File.expand_path('../../../../../test_helper', __FILE__)
19
require 'digest/md5'
20
21
class Redmine::WikiFormatting::TextileFormatterTest < ActionView::TestCase
22
23
  def setup
24
    @formatter = Redmine::WikiFormatting::Textile::Formatter
25
  end
26
27
  MODIFIERS = {
28
    "*" => 'strong', # bold
29
    "_" => 'em',     # italic
30
    "+" => 'ins',    # underline
31
    "-" => 'del',    # deleted
32
    "^" => 'sup',    # superscript
33
    "~" => 'sub'     # subscript
34
  }
35
36
  def test_modifiers
37
    assert_html_output(
38
      '*bold*'                => '<strong>bold</strong>',
39
      'before *bold*'         => 'before <strong>bold</strong>',
40
      '*bold* after'          => '<strong>bold</strong> after',
41
      '*two words*'           => '<strong>two words</strong>',
42
      '*two*words*'           => '<strong>two*words</strong>',
43
      '*two * words*'         => '<strong>two * words</strong>',
44
      '*two* *words*'         => '<strong>two</strong> <strong>words</strong>',
45
      '*(two)* *(words)*'     => '<strong>(two)</strong> <strong>(words)</strong>',
46
      # with class
47
      '*(foo)two words*'      => '<strong class="foo">two words</strong>'
48
    )
49
  end
50
51
  def test_modifiers_combination
52
    MODIFIERS.each do |m1, tag1|
53
      MODIFIERS.each do |m2, tag2|
54
        next if m1 == m2
55
        text = "#{m2}#{m1}Phrase modifiers#{m1}#{m2}"
56
        html = "<#{tag2}><#{tag1}>Phrase modifiers</#{tag1}></#{tag2}>"
57
        assert_html_output text => html
58
      end
59
    end
60
  end
61
62
  def test_styles
63
    # single style
64
    assert_html_output({
65
      'p{color:red}. text'           => '<p style="color:red;">text</p>',
66
      'p{color:red;}. text'          => '<p style="color:red;">text</p>',
67
      'p{color: red}. text'          => '<p style="color: red;">text</p>',
68
      'p{color:#f00}. text'          => '<p style="color:#f00;">text</p>',
69
      'p{color:#ff0000}. text'       => '<p style="color:#ff0000;">text</p>',
70
      'p{border:10px}. text'         => '<p style="border:10px;">text</p>',
71
      'p{border:10}. text'           => '<p style="border:10;">text</p>',
72
      'p{border:10%}. text'          => '<p style="border:10%;">text</p>',
73
      'p{border:10em}. text'         => '<p style="border:10em;">text</p>',
74
      'p{border:1.5em}. text'        => '<p style="border:1.5em;">text</p>',
75
      'p{border-left:1px}. text'     => '<p style="border-left:1px;">text</p>',
76
      'p{border-right:1px}. text'    => '<p style="border-right:1px;">text</p>',
77
      'p{border-top:1px}. text'      => '<p style="border-top:1px;">text</p>',
78
      'p{border-bottom:1px}. text'   => '<p style="border-bottom:1px;">text</p>',
79
      }, false)
80
81
    # multiple styles
82
    assert_html_output({
83
      'p{color:red; border-top:1px}. text'   => '<p style="color:red;border-top:1px;">text</p>',
84
      'p{color:red ; border-top:1px}. text'  => '<p style="color:red;border-top:1px;">text</p>',
85
      'p{color:red;border-top:1px}. text'    => '<p style="color:red;border-top:1px;">text</p>',
86
      }, false)
87
88
    # styles with multiple values
89
    assert_html_output({
90
      'p{border:1px solid red;}. text'             => '<p style="border:1px solid red;">text</p>',
91
      'p{border-top-left-radius: 10px 5px;}. text' => '<p style="border-top-left-radius: 10px 5px;">text</p>',
92
      }, false)
93
  end
94
95
  def test_invalid_styles_should_be_filtered
96
    assert_html_output({
97
      'p{invalid}. text'                     => '<p>text</p>',
98
      'p{invalid:red}. text'                 => '<p>text</p>',
99
      'p{color:(red)}. text'                 => '<p>text</p>',
100
      'p{color:red;invalid:blue}. text'      => '<p style="color:red;">text</p>',
101
      'p{invalid:blue;color:red}. text'      => '<p style="color:red;">text</p>',
102
      'p{color:"}. text'                     => '<p>p{color:"}. text</p>',
103
      }, false)
104
  end
105
106
  def test_inline_code
107
    assert_html_output(
108
      'this is @some code@'      => 'this is <code>some code</code>',
109
      '@<Location /redmine>@'    => '<code>&lt;Location /redmine&gt;</code>'
110
    )
111
  end
112
113
  def test_nested_lists
114
    raw = <<-RAW
115
# Item 1
116
# Item 2
117
** Item 2a
118
** Item 2b
119
# Item 3
120
** Item 3a
121
RAW
122
123
    expected = <<-EXPECTED
124
<ol>
125
  <li>Item 1</li>
126
  <li>Item 2
127
    <ul>
128
      <li>Item 2a</li>
129
      <li>Item 2b</li>
130
    </ul>
131
  </li>
132
  <li>Item 3
133
    <ul>
134
      <li>Item 3a</li>
135
    </ul>
136
  </li>
137
</ol>
138
EXPECTED
139
140
    assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
141
  end
142
143
  def test_escaping
144
    assert_html_output(
145
      'this is a <script>'      => 'this is a &lt;script&gt;'
146
    )
147
  end
148
149
  def test_use_of_backslashes_followed_by_numbers_in_headers
150
    assert_html_output({
151
      'h1. 2009\02\09'      => '<h1>2009\02\09</h1>'
152
    }, false)
153
  end
154
155
  def test_double_dashes_should_not_strikethrough
156
    assert_html_output(
157
      'double -- dashes -- test'  => 'double -- dashes -- test',
158
      'double -- *dashes* -- test'  => 'double -- <strong>dashes</strong> -- test'
159
    )
160
  end
161
162
  def test_acronyms
163
    assert_html_output(
164
      'this is an acronym: GPL(General Public License)' => 'this is an acronym: <acronym title="General Public License">GPL</acronym>',
165
      '2 letters JP(Jean-Philippe) acronym' => '2 letters <acronym title="Jean-Philippe">JP</acronym> acronym',
166
      'GPL(This is a double-quoted "title")' => '<acronym title="This is a double-quoted &quot;title&quot;">GPL</acronym>'
167
    )
168
  end
169
170
  def test_blockquote
171
    # orig raw text
172
    raw = <<-RAW
173
John said:
174
> Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
175
> Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
176
> * Donec odio lorem,
177
> * sagittis ac,
178
> * malesuada in,
179
> * adipiscing eu, dolor.
180
>
181
> >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
182
> Proin a tellus. Nam vel neque.
183
184
He's right.
185
RAW
186
187
    # expected html
188
    expected = <<-EXPECTED
189
<p>John said:</p>
190
<blockquote>
191
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.<br />
192
Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
193
<ul>
194
  <li>Donec odio lorem,</li>
195
  <li>sagittis ac,</li>
196
  <li>malesuada in,</li>
197
  <li>adipiscing eu, dolor.</li>
198
</ul>
199
<blockquote>
200
<p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
201
</blockquote>
202
<p>Proin a tellus. Nam vel neque.</p>
203
</blockquote>
204
<p>He's right.</p>
205
EXPECTED
206
207
    assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
208
  end
209
210
  def test_table
211
    raw = <<-RAW
212
This is a table with empty cells:
213
214
|cell11|cell12||
215
|cell21||cell23|
216
|cell31|cell32|cell33|
217
RAW
218
219
    expected = <<-EXPECTED
220
<p>This is a table with empty cells:</p>
221
222
<table>
223
  <tr><td>cell11</td><td>cell12</td><td></td></tr>
224
  <tr><td>cell21</td><td></td><td>cell23</td></tr>
225
  <tr><td>cell31</td><td>cell32</td><td>cell33</td></tr>
226
</table>
227
EXPECTED
228
229
    assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
230
  end
231
232
  def test_table_with_line_breaks
233
    raw = <<-RAW
234
This is a table with line breaks:
235
236
|cell11
237
continued|cell12||
238
|-cell21-||cell23
239
cell23 line2
240
cell23 *line3*|
241
|cell31|cell32
242
cell32 line2|cell33|
243
244
RAW
245
246
    expected = <<-EXPECTED
247
<p>This is a table with line breaks:</p>
248
249
<table>
250
  <tr>
251
    <td>cell11<br />continued</td>
252
    <td>cell12</td>
253
    <td></td>
254
  </tr>
255
  <tr>
256
    <td><del>cell21</del></td>
257
    <td></td>
258
    <td>cell23<br/>cell23 line2<br/>cell23 <strong>line3</strong></td>
259
  </tr>
260
  <tr>
261
    <td>cell31</td>
262
    <td>cell32<br/>cell32 line2</td>
263
    <td>cell33</td>
264
  </tr>
265
</table>
266
EXPECTED
267
268
    assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
269
  end
270
271
  def test_textile_should_not_mangle_brackets
272
    assert_equal '<p>[msg1][msg2]</p>', to_html('[msg1][msg2]')
273
  end
274
275
  def test_textile_should_escape_image_urls
276
    # this is onclick="alert('XSS');" in encoded form
277
    raw = '!/images/comment.png"onclick=&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x27;&#x58;&#x53;&#x53;&#x27;&#x29;;&#x22;!'
278
    expected = '<p><img src="/images/comment.png&quot;onclick=&amp;#x61;&amp;#x6c;&amp;#x65;&amp;#x72;&amp;#x74;&amp;#x28;&amp;#x27;&amp;#x58;&amp;#x53;&amp;#x53;&amp;#x27;&amp;#x29;;&amp;#x22;" alt="" /></p>'
279
    assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
280
  end
281
282
283
  STR_WITHOUT_PRE = [
284
  # 0
285
"h1. Title
286
287
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.",
288
  # 1
289
"h2. Heading 2
290
291
Maecenas sed elit sit amet mi accumsan vestibulum non nec velit. Proin porta tincidunt lorem, consequat rhoncus dolor fermentum in.
292
293
Cras ipsum felis, ultrices at porttitor vel, faucibus eu nunc.",
294
  # 2
295
"h2. Heading 2
296
297
Morbi facilisis accumsan orci non pharetra.
298
299
h3. Heading 3
300
301
Nulla nunc nisi, egestas in ornare vel, posuere ac libero.",
302
  # 3
303
"h3. Heading 3
304
305
Praesent eget turpis nibh, a lacinia nulla.",
306
  # 4
307
"h2. Heading 2
308
309
Ut rhoncus elementum adipiscing."]
310
311
  TEXT_WITHOUT_PRE = STR_WITHOUT_PRE.join("\n\n").freeze
312
313
  def test_get_section_should_return_the_requested_section_and_its_hash
314
    assert_section_with_hash STR_WITHOUT_PRE[1], TEXT_WITHOUT_PRE, 2
315
    assert_section_with_hash STR_WITHOUT_PRE[2..3].join("\n\n"), TEXT_WITHOUT_PRE, 3
316
    assert_section_with_hash STR_WITHOUT_PRE[3], TEXT_WITHOUT_PRE, 5
317
    assert_section_with_hash STR_WITHOUT_PRE[4], TEXT_WITHOUT_PRE, 6
318
319
    assert_section_with_hash '', TEXT_WITHOUT_PRE, 0
320
    assert_section_with_hash '', TEXT_WITHOUT_PRE, 10
321
  end
322
323
  def test_update_section_should_update_the_requested_section
324
    replacement = "New text"
325
326
    assert_equal [STR_WITHOUT_PRE[0], replacement, STR_WITHOUT_PRE[2..4]].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(2, replacement)
327
    assert_equal [STR_WITHOUT_PRE[0..1], replacement, STR_WITHOUT_PRE[4]].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(3, replacement)
328
    assert_equal [STR_WITHOUT_PRE[0..2], replacement, STR_WITHOUT_PRE[4]].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(5, replacement)
329
    assert_equal [STR_WITHOUT_PRE[0..3], replacement].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(6, replacement)
330
331
    assert_equal TEXT_WITHOUT_PRE, @formatter.new(TEXT_WITHOUT_PRE).update_section(0, replacement)
332
    assert_equal TEXT_WITHOUT_PRE, @formatter.new(TEXT_WITHOUT_PRE).update_section(10, replacement)
333
  end
334
335
  def test_update_section_with_hash_should_update_the_requested_section
336
    replacement = "New text"
337
338
    assert_equal [STR_WITHOUT_PRE[0], replacement, STR_WITHOUT_PRE[2..4]].flatten.join("\n\n"),
339
      @formatter.new(TEXT_WITHOUT_PRE).update_section(2, replacement, Digest::MD5.hexdigest(STR_WITHOUT_PRE[1]))
340
  end
341
342
  def test_update_section_with_wrong_hash_should_raise_an_error
343
    assert_raise Redmine::WikiFormatting::StaleSectionError do
344
      @formatter.new(TEXT_WITHOUT_PRE).update_section(2, "New text", Digest::MD5.hexdigest("Old text"))
345
    end
346
  end
347
348
  STR_WITH_PRE = [
349
  # 0
350
"h1. Title
351
352
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.",
353
  # 1
354
"h2. Heading 2
355
356
<pre><code class=\"ruby\">
357
  def foo
358
  end
359
</code></pre>
360
361
<pre><code><pre><code class=\"ruby\">
362
  Place your code here.
363
</code></pre>
364
</code></pre>
365
366
Morbi facilisis accumsan orci non pharetra.
367
368
<pre>
369
Pre Content:
370
371
h2. Inside pre
372
373
<tag> inside pre block
374
375
Morbi facilisis accumsan orci non pharetra.
376
</pre>",
377
  # 2
378
"h3. Heading 3
379
380
Nulla nunc nisi, egestas in ornare vel, posuere ac libero."]
381
382
  def test_get_section_should_ignore_pre_content
383
    text = STR_WITH_PRE.join("\n\n")
384
385
    assert_section_with_hash STR_WITH_PRE[1..2].join("\n\n"), text, 2
386
    assert_section_with_hash STR_WITH_PRE[2], text, 3
387
  end
388
389
  def test_update_section_should_not_escape_pre_content_outside_section
390
    text = STR_WITH_PRE.join("\n\n")
391
    replacement = "New text"
392
393
    assert_equal [STR_WITH_PRE[0..1], "New text"].flatten.join("\n\n"),
394
      @formatter.new(text).update_section(3, replacement)
395
  end
396
397
  def test_get_section_should_support_lines_with_spaces_before_heading
398
    # the lines after Content 2 and Heading 4 contain a space
399
    text = <<-STR
400
h1. Heading 1
401
402
Content 1
403
404
h1. Heading 2
405
406
Content 2
407
408
h1. Heading 3
409
410
Content 3
411
412
h1. Heading 4
413
414
Content 4
415
STR
416
417
    [1, 2, 3, 4].each do |index|
418
      assert_match /\Ah1. Heading #{index}.+Content #{index}/m, @formatter.new(text).get_section(index).first
419
    end
420
  end
421
422
  def test_get_section_should_support_headings_starting_with_a_tab
423
    text = <<-STR
424
h1.\tHeading 1
425
426
Content 1
427
428
h1. Heading 2
429
430
Content 2
431
STR
432
433
    assert_match /\Ah1.\tHeading 1\s+Content 1\z/, @formatter.new(text).get_section(1).first
434
  end
435
436
  private
437
438
  def assert_html_output(to_test, expect_paragraph = true)
439
    to_test.each do |text, expected|
440
      assert_equal(( expect_paragraph ? "<p>#{expected}</p>" : expected ), @formatter.new(text).to_html, "Formatting the following text failed:\n===\n#{text}\n===\n")
441
    end
442
  end
443
444
  def to_html(text)
445
    @formatter.new(text).to_html
446
  end
447
448
  def assert_section_with_hash(expected, text, index)
449
    result = @formatter.new(text).get_section(index)
450
451
    assert_kind_of Array, result
452
    assert_equal 2, result.size
453
    assert_equal expected, result.first, "section content did not match"
454
    assert_equal Digest::MD5.hexdigest(expected), result.last, "section hash did not match"
455
  end
456
end