##// END OF EJS Templates
Display the link name when external links are used in the toc macro. #5445...
Eric Davis -
r3672:b53de502ab60
parent child
Show More
@@ -1,158 +1,161
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'redcloth3'
18 require 'redcloth3'
19
19
20 module Redmine
20 module Redmine
21 module WikiFormatting
21 module WikiFormatting
22 module Textile
22 module Textile
23 class Formatter < RedCloth3
23 class Formatter < RedCloth3
24 include ActionView::Helpers::TagHelper
24 include ActionView::Helpers::TagHelper
25
25
26 # auto_link rule after textile rules so that it doesn't break !image_url! tags
26 # auto_link rule after textile rules so that it doesn't break !image_url! tags
27 RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc]
27 RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc]
28
28
29 def initialize(*args)
29 def initialize(*args)
30 super
30 super
31 self.hard_breaks=true
31 self.hard_breaks=true
32 self.no_span_caps=true
32 self.no_span_caps=true
33 self.filter_styles=true
33 self.filter_styles=true
34 end
34 end
35
35
36 def to_html(*rules)
36 def to_html(*rules)
37 @toc = []
37 @toc = []
38 super(*RULES).to_s
38 super(*RULES).to_s
39 end
39 end
40
40
41 private
41 private
42
42
43 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
43 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
44 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
44 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
45 def hard_break( text )
45 def hard_break( text )
46 text.gsub!( /(.)\n(?!\n|\Z|>| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
46 text.gsub!( /(.)\n(?!\n|\Z|>| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
47 end
47 end
48
48
49 # Patch to add code highlighting support to RedCloth
49 # Patch to add code highlighting support to RedCloth
50 def smooth_offtags( text )
50 def smooth_offtags( text )
51 unless @pre_list.empty?
51 unless @pre_list.empty?
52 ## replace <pre> content
52 ## replace <pre> content
53 text.gsub!(/<redpre#(\d+)>/) do
53 text.gsub!(/<redpre#(\d+)>/) do
54 content = @pre_list[$1.to_i]
54 content = @pre_list[$1.to_i]
55 if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
55 if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
56 content = "<code class=\"#{$1} syntaxhl\">" +
56 content = "<code class=\"#{$1} syntaxhl\">" +
57 Redmine::SyntaxHighlighting.highlight_by_language($2, $1)
57 Redmine::SyntaxHighlighting.highlight_by_language($2, $1)
58 end
58 end
59 content
59 content
60 end
60 end
61 end
61 end
62 end
62 end
63
63
64 # Patch to add 'table of content' support to RedCloth
64 # Patch to add 'table of content' support to RedCloth
65 def textile_p_withtoc(tag, atts, cite, content)
65 def textile_p_withtoc(tag, atts, cite, content)
66 # removes wiki links from the item
66 # removes wiki links from the item
67 toc_item = content.gsub(/(\[\[([^\]\|]*)(\|([^\]]*))?\]\])/) { $4 || $2 }
67 toc_item = content.gsub(/(\[\[([^\]\|]*)(\|([^\]]*))?\]\])/) { $4 || $2 }
68 # sanitizes titles from links
69 # see redcloth3.rb, same as "#{pre}#{text}#{post}"
70 toc_item.gsub!(LINK_RE) { $2+$4+$9 }
68 # removes styles
71 # removes styles
69 # eg. %{color:red}Triggers% => Triggers
72 # eg. %{color:red}Triggers% => Triggers
70 toc_item.gsub! %r[%\{[^\}]*\}([^%]+)%], '\\1'
73 toc_item.gsub! %r[%\{[^\}]*\}([^%]+)%], '\\1'
71
74
72 # replaces non word caracters by dashes
75 # replaces non word caracters by dashes
73 anchor = toc_item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
76 anchor = toc_item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
74
77
75 unless anchor.blank?
78 unless anchor.blank?
76 if tag =~ /^h(\d)$/
79 if tag =~ /^h(\d)$/
77 @toc << [$1.to_i, anchor, toc_item]
80 @toc << [$1.to_i, anchor, toc_item]
78 end
81 end
79 atts << " id=\"#{anchor}\""
82 atts << " id=\"#{anchor}\""
80 content = content + "<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a>"
83 content = content + "<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a>"
81 end
84 end
82 textile_p(tag, atts, cite, content)
85 textile_p(tag, atts, cite, content)
83 end
86 end
84
87
85 alias :textile_h1 :textile_p_withtoc
88 alias :textile_h1 :textile_p_withtoc
86 alias :textile_h2 :textile_p_withtoc
89 alias :textile_h2 :textile_p_withtoc
87 alias :textile_h3 :textile_p_withtoc
90 alias :textile_h3 :textile_p_withtoc
88
91
89 def inline_toc(text)
92 def inline_toc(text)
90 text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
93 text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
91 div_class = 'toc'
94 div_class = 'toc'
92 div_class << ' right' if $1 == '>'
95 div_class << ' right' if $1 == '>'
93 div_class << ' left' if $1 == '<'
96 div_class << ' left' if $1 == '<'
94 out = "<ul class=\"#{div_class}\">"
97 out = "<ul class=\"#{div_class}\">"
95 @toc.each do |heading|
98 @toc.each do |heading|
96 level, anchor, toc_item = heading
99 level, anchor, toc_item = heading
97 out << "<li class=\"heading#{level}\"><a href=\"##{anchor}\">#{toc_item}</a></li>\n"
100 out << "<li class=\"heading#{level}\"><a href=\"##{anchor}\">#{toc_item}</a></li>\n"
98 end
101 end
99 out << '</ul>'
102 out << '</ul>'
100 out
103 out
101 end
104 end
102 end
105 end
103
106
104 AUTO_LINK_RE = %r{
107 AUTO_LINK_RE = %r{
105 ( # leading text
108 ( # leading text
106 <\w+.*?>| # leading HTML tag, or
109 <\w+.*?>| # leading HTML tag, or
107 [^=<>!:'"/]| # leading punctuation, or
110 [^=<>!:'"/]| # leading punctuation, or
108 ^ # beginning of line
111 ^ # beginning of line
109 )
112 )
110 (
113 (
111 (?:https?://)| # protocol spec, or
114 (?:https?://)| # protocol spec, or
112 (?:s?ftps?://)|
115 (?:s?ftps?://)|
113 (?:www\.) # www.*
116 (?:www\.) # www.*
114 )
117 )
115 (
118 (
116 (\S+?) # url
119 (\S+?) # url
117 (\/)? # slash
120 (\/)? # slash
118 )
121 )
119 ([^\w\=\/;\(\)]*?) # post
122 ([^\w\=\/;\(\)]*?) # post
120 (?=<|\s|$)
123 (?=<|\s|$)
121 }x unless const_defined?(:AUTO_LINK_RE)
124 }x unless const_defined?(:AUTO_LINK_RE)
122
125
123 # Turns all urls into clickable links (code from Rails).
126 # Turns all urls into clickable links (code from Rails).
124 def inline_auto_link(text)
127 def inline_auto_link(text)
125 text.gsub!(AUTO_LINK_RE) do
128 text.gsub!(AUTO_LINK_RE) do
126 all, leading, proto, url, post = $&, $1, $2, $3, $6
129 all, leading, proto, url, post = $&, $1, $2, $3, $6
127 if leading =~ /<a\s/i || leading =~ /![<>=]?/
130 if leading =~ /<a\s/i || leading =~ /![<>=]?/
128 # don't replace URL's that are already linked
131 # don't replace URL's that are already linked
129 # and URL's prefixed with ! !> !< != (textile images)
132 # and URL's prefixed with ! !> !< != (textile images)
130 all
133 all
131 else
134 else
132 # Idea below : an URL with unbalanced parethesis and
135 # Idea below : an URL with unbalanced parethesis and
133 # ending by ')' is put into external parenthesis
136 # ending by ')' is put into external parenthesis
134 if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
137 if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
135 url=url[0..-2] # discard closing parenth from url
138 url=url[0..-2] # discard closing parenth from url
136 post = ")"+post # add closing parenth to post
139 post = ")"+post # add closing parenth to post
137 end
140 end
138 tag = content_tag('a', proto + url, :href => "#{proto=="www."?"http://www.":proto}#{url}", :class => 'external')
141 tag = content_tag('a', proto + url, :href => "#{proto=="www."?"http://www.":proto}#{url}", :class => 'external')
139 %(#{leading}#{tag}#{post})
142 %(#{leading}#{tag}#{post})
140 end
143 end
141 end
144 end
142 end
145 end
143
146
144 # Turns all email addresses into clickable links (code from Rails).
147 # Turns all email addresses into clickable links (code from Rails).
145 def inline_auto_mailto(text)
148 def inline_auto_mailto(text)
146 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
149 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
147 mail = $1
150 mail = $1
148 if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
151 if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
149 mail
152 mail
150 else
153 else
151 content_tag('a', mail, :href => "mailto:#{mail}", :class => "email")
154 content_tag('a', mail, :href => "mailto:#{mail}", :class => "email")
152 end
155 end
153 end
156 end
154 end
157 end
155 end
158 end
156 end
159 end
157 end
160 end
158 end
161 end
@@ -1,594 +1,596
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.dirname(__FILE__) + '/../../test_helper'
18 require File.dirname(__FILE__) + '/../../test_helper'
19
19
20 class ApplicationHelperTest < HelperTestCase
20 class ApplicationHelperTest < HelperTestCase
21 include ApplicationHelper
21 include ApplicationHelper
22 include ActionView::Helpers::TextHelper
22 include ActionView::Helpers::TextHelper
23 include ActionView::Helpers::DateHelper
23 include ActionView::Helpers::DateHelper
24
24
25 fixtures :projects, :roles, :enabled_modules, :users,
25 fixtures :projects, :roles, :enabled_modules, :users,
26 :repositories, :changesets,
26 :repositories, :changesets,
27 :trackers, :issue_statuses, :issues, :versions, :documents,
27 :trackers, :issue_statuses, :issues, :versions, :documents,
28 :wikis, :wiki_pages, :wiki_contents,
28 :wikis, :wiki_pages, :wiki_contents,
29 :boards, :messages,
29 :boards, :messages,
30 :attachments,
30 :attachments,
31 :enumerations
31 :enumerations
32
32
33 def setup
33 def setup
34 super
34 super
35 end
35 end
36
36
37 def test_auto_links
37 def test_auto_links
38 to_test = {
38 to_test = {
39 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
39 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
40 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
40 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
41 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
41 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
42 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
42 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
43 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
43 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
44 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
44 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
45 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.',
45 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.',
46 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
46 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
47 '(see inline link : http://www.foo.bar/Test_(foobar))' => '(see inline link : <a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>)',
47 '(see inline link : http://www.foo.bar/Test_(foobar))' => '(see inline link : <a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>)',
48 '(see inline link : http://www.foo.bar/Test)' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>)',
48 '(see inline link : http://www.foo.bar/Test)' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>)',
49 '(see inline link : http://www.foo.bar/Test).' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>).',
49 '(see inline link : http://www.foo.bar/Test).' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>).',
50 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
50 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
51 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
51 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
52 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
52 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
53 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
53 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
54 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&#38;t=z&#38;s=">http://foo.bar/page?p=1&#38;t=z&#38;s=</a>',
54 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&#38;t=z&#38;s=">http://foo.bar/page?p=1&#38;t=z&#38;s=</a>',
55 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
55 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
56 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
56 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
57 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
57 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
58 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
58 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
59 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
59 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
60 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
60 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
61 # two exclamation marks
61 # two exclamation marks
62 'http://example.net/path!602815048C7B5C20!302.html' => '<a class="external" href="http://example.net/path!602815048C7B5C20!302.html">http://example.net/path!602815048C7B5C20!302.html</a>',
62 'http://example.net/path!602815048C7B5C20!302.html' => '<a class="external" href="http://example.net/path!602815048C7B5C20!302.html">http://example.net/path!602815048C7B5C20!302.html</a>',
63 # escaping
63 # escaping
64 'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo"bar</a>',
64 'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo"bar</a>',
65 }
65 }
66 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
66 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
67 end
67 end
68
68
69 def test_auto_mailto
69 def test_auto_mailto
70 assert_equal '<p><a class="email" href="mailto:test@foo.bar">test@foo.bar</a></p>',
70 assert_equal '<p><a class="email" href="mailto:test@foo.bar">test@foo.bar</a></p>',
71 textilizable('test@foo.bar')
71 textilizable('test@foo.bar')
72 end
72 end
73
73
74 def test_inline_images
74 def test_inline_images
75 to_test = {
75 to_test = {
76 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
76 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
77 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
77 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
78 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
78 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
79 # inline styles should be stripped
79 # inline styles should be stripped
80 'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" alt="" />',
80 'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" alt="" />',
81 'with title !http://foo.bar/image.jpg(This is a title)!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a title" alt="This is a title" />',
81 'with title !http://foo.bar/image.jpg(This is a title)!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a title" alt="This is a title" />',
82 'with title !http://foo.bar/image.jpg(This is a double-quoted "title")!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a double-quoted &quot;title&quot;" alt="This is a double-quoted &quot;title&quot;" />',
82 'with title !http://foo.bar/image.jpg(This is a double-quoted "title")!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a double-quoted &quot;title&quot;" alt="This is a double-quoted &quot;title&quot;" />',
83 }
83 }
84 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
84 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
85 end
85 end
86
86
87 def test_inline_images_inside_tags
87 def test_inline_images_inside_tags
88 raw = <<-RAW
88 raw = <<-RAW
89 h1. !foo.png! Heading
89 h1. !foo.png! Heading
90
90
91 Centered image:
91 Centered image:
92
92
93 p=. !bar.gif!
93 p=. !bar.gif!
94 RAW
94 RAW
95
95
96 assert textilizable(raw).include?('<img src="foo.png" alt="" />')
96 assert textilizable(raw).include?('<img src="foo.png" alt="" />')
97 assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
97 assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
98 end
98 end
99
99
100 def test_acronyms
100 def test_acronyms
101 to_test = {
101 to_test = {
102 'this is an acronym: GPL(General Public License)' => 'this is an acronym: <acronym title="General Public License">GPL</acronym>',
102 'this is an acronym: GPL(General Public License)' => 'this is an acronym: <acronym title="General Public License">GPL</acronym>',
103 'GPL(This is a double-quoted "title")' => '<acronym title="This is a double-quoted &quot;title&quot;">GPL</acronym>',
103 'GPL(This is a double-quoted "title")' => '<acronym title="This is a double-quoted &quot;title&quot;">GPL</acronym>',
104 }
104 }
105 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
105 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
106
106
107 end
107 end
108
108
109 def test_attached_images
109 def test_attached_images
110 to_test = {
110 to_test = {
111 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
111 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
112 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
112 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
113 'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
113 'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
114 'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
114 'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
115 # link image
115 # link image
116 '!logo.gif!:http://foo.bar/' => '<a href="http://foo.bar/"><img src="/attachments/download/3" title="This is a logo" alt="This is a logo" /></a>',
116 '!logo.gif!:http://foo.bar/' => '<a href="http://foo.bar/"><img src="/attachments/download/3" title="This is a logo" alt="This is a logo" /></a>',
117 }
117 }
118 attachments = Attachment.find(:all)
118 attachments = Attachment.find(:all)
119 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
119 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
120 end
120 end
121
121
122 def test_textile_external_links
122 def test_textile_external_links
123 to_test = {
123 to_test = {
124 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
124 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
125 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
125 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
126 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
126 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
127 '"link (Link title with "double-quotes")":http://foo.bar' => '<a href="http://foo.bar" title="Link title with &quot;double-quotes&quot;" class="external">link</a>',
127 '"link (Link title with "double-quotes")":http://foo.bar' => '<a href="http://foo.bar" title="Link title with &quot;double-quotes&quot;" class="external">link</a>',
128 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
128 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
129 # no multiline link text
129 # no multiline link text
130 "This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />and another on a second line\":test",
130 "This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />and another on a second line\":test",
131 # mailto link
131 # mailto link
132 "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
132 "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
133 # two exclamation marks
133 # two exclamation marks
134 '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
134 '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
135 # escaping
135 # escaping
136 '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
136 '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
137 }
137 }
138 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
138 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
139 end
139 end
140
140
141 def test_redmine_links
141 def test_redmine_links
142 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
142 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
143 :class => 'issue status-1 priority-1 overdue', :title => 'Error 281 when updating a recipe (New)')
143 :class => 'issue status-1 priority-1 overdue', :title => 'Error 281 when updating a recipe (New)')
144
144
145 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
145 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
146 :class => 'changeset', :title => 'My very first commit')
146 :class => 'changeset', :title => 'My very first commit')
147 changeset_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
147 changeset_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
148 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
148 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
149
149
150 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
150 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
151 :class => 'document')
151 :class => 'document')
152
152
153 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
153 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
154 :class => 'version')
154 :class => 'version')
155
155
156 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
156 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
157
157
158 project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
158 project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
159
159
160 source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}
160 source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}
161 source_url_with_ext = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file.ext']}
161 source_url_with_ext = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file.ext']}
162
162
163 to_test = {
163 to_test = {
164 # tickets
164 # tickets
165 '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
165 '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
166 # changesets
166 # changesets
167 'r1' => changeset_link,
167 'r1' => changeset_link,
168 'r1.' => "#{changeset_link}.",
168 'r1.' => "#{changeset_link}.",
169 'r1, r2' => "#{changeset_link}, #{changeset_link2}",
169 'r1, r2' => "#{changeset_link}, #{changeset_link2}",
170 'r1,r2' => "#{changeset_link},#{changeset_link2}",
170 'r1,r2' => "#{changeset_link},#{changeset_link2}",
171 # documents
171 # documents
172 'document#1' => document_link,
172 'document#1' => document_link,
173 'document:"Test document"' => document_link,
173 'document:"Test document"' => document_link,
174 # versions
174 # versions
175 'version#2' => version_link,
175 'version#2' => version_link,
176 'version:1.0' => version_link,
176 'version:1.0' => version_link,
177 'version:"1.0"' => version_link,
177 'version:"1.0"' => version_link,
178 # source
178 # source
179 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
179 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
180 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
180 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
181 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
181 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
182 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
182 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
183 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
183 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
184 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
184 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
185 'source:/some/file@52' => link_to('source:/some/file@52', source_url.merge(:rev => 52), :class => 'source'),
185 'source:/some/file@52' => link_to('source:/some/file@52', source_url.merge(:rev => 52), :class => 'source'),
186 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_ext.merge(:rev => 52), :class => 'source'),
186 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_ext.merge(:rev => 52), :class => 'source'),
187 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'),
187 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'),
188 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'),
188 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'),
189 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'),
189 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'),
190 'export:/some/file' => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'),
190 'export:/some/file' => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'),
191 # message
191 # message
192 'message#4' => link_to('Post 2', message_url, :class => 'message'),
192 'message#4' => link_to('Post 2', message_url, :class => 'message'),
193 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5'), :class => 'message'),
193 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5'), :class => 'message'),
194 # project
194 # project
195 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
195 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
196 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
196 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
197 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
197 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
198 # escaping
198 # escaping
199 '!#3.' => '#3.',
199 '!#3.' => '#3.',
200 '!r1' => 'r1',
200 '!r1' => 'r1',
201 '!document#1' => 'document#1',
201 '!document#1' => 'document#1',
202 '!document:"Test document"' => 'document:"Test document"',
202 '!document:"Test document"' => 'document:"Test document"',
203 '!version#2' => 'version#2',
203 '!version#2' => 'version#2',
204 '!version:1.0' => 'version:1.0',
204 '!version:1.0' => 'version:1.0',
205 '!version:"1.0"' => 'version:"1.0"',
205 '!version:"1.0"' => 'version:"1.0"',
206 '!source:/some/file' => 'source:/some/file',
206 '!source:/some/file' => 'source:/some/file',
207 # not found
207 # not found
208 '#0123456789' => '#0123456789',
208 '#0123456789' => '#0123456789',
209 # invalid expressions
209 # invalid expressions
210 'source:' => 'source:',
210 'source:' => 'source:',
211 # url hash
211 # url hash
212 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
212 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
213 }
213 }
214 @project = Project.find(1)
214 @project = Project.find(1)
215 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
215 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
216 end
216 end
217
217
218 def test_attachment_links
218 def test_attachment_links
219 attachment_link = link_to('error281.txt', {:controller => 'attachments', :action => 'download', :id => '1'}, :class => 'attachment')
219 attachment_link = link_to('error281.txt', {:controller => 'attachments', :action => 'download', :id => '1'}, :class => 'attachment')
220 to_test = {
220 to_test = {
221 'attachment:error281.txt' => attachment_link
221 'attachment:error281.txt' => attachment_link
222 }
222 }
223 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => Issue.find(3).attachments), "#{text} failed" }
223 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => Issue.find(3).attachments), "#{text} failed" }
224 end
224 end
225
225
226 def test_wiki_links
226 def test_wiki_links
227 to_test = {
227 to_test = {
228 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
228 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
229 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
229 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
230 # link with anchor
230 # link with anchor
231 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
231 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
232 '[[Another page#anchor|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page#anchor" class="wiki-page">Page</a>',
232 '[[Another page#anchor|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page#anchor" class="wiki-page">Page</a>',
233 # page that doesn't exist
233 # page that doesn't exist
234 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
234 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
235 '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">404</a>',
235 '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">404</a>',
236 # link to another project wiki
236 # link to another project wiki
237 '[[onlinestore:]]' => '<a href="/projects/onlinestore/wiki/" class="wiki-page">onlinestore</a>',
237 '[[onlinestore:]]' => '<a href="/projects/onlinestore/wiki/" class="wiki-page">onlinestore</a>',
238 '[[onlinestore:|Wiki]]' => '<a href="/projects/onlinestore/wiki/" class="wiki-page">Wiki</a>',
238 '[[onlinestore:|Wiki]]' => '<a href="/projects/onlinestore/wiki/" class="wiki-page">Wiki</a>',
239 '[[onlinestore:Start page]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Start page</a>',
239 '[[onlinestore:Start page]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Start page</a>',
240 '[[onlinestore:Start page|Text]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Text</a>',
240 '[[onlinestore:Start page|Text]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Text</a>',
241 '[[onlinestore:Unknown page]]' => '<a href="/projects/onlinestore/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
241 '[[onlinestore:Unknown page]]' => '<a href="/projects/onlinestore/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
242 # striked through link
242 # striked through link
243 '-[[Another page|Page]]-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a></del>',
243 '-[[Another page|Page]]-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a></del>',
244 '-[[Another page|Page]] link-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a> link</del>',
244 '-[[Another page|Page]] link-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a> link</del>',
245 # escaping
245 # escaping
246 '![[Another page|Page]]' => '[[Another page|Page]]',
246 '![[Another page|Page]]' => '[[Another page|Page]]',
247 # project does not exist
247 # project does not exist
248 '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
248 '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
249 '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
249 '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
250 }
250 }
251 @project = Project.find(1)
251 @project = Project.find(1)
252 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
252 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
253 end
253 end
254
254
255 def test_html_tags
255 def test_html_tags
256 to_test = {
256 to_test = {
257 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
257 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
258 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
258 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
259 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
259 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
260 # do not escape pre/code tags
260 # do not escape pre/code tags
261 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
261 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
262 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
262 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
263 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
263 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
264 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
264 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
265 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
265 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
266 # remove attributes except class
266 # remove attributes except class
267 "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
267 "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
268 "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
268 "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
269 }
269 }
270 to_test.each { |text, result| assert_equal result, textilizable(text) }
270 to_test.each { |text, result| assert_equal result, textilizable(text) }
271 end
271 end
272
272
273 def test_allowed_html_tags
273 def test_allowed_html_tags
274 to_test = {
274 to_test = {
275 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
275 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
276 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
276 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
277 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
277 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
278 }
278 }
279 to_test.each { |text, result| assert_equal result, textilizable(text) }
279 to_test.each { |text, result| assert_equal result, textilizable(text) }
280 end
280 end
281
281
282 def test_pre_tags
282 def test_pre_tags
283 raw = <<-RAW
283 raw = <<-RAW
284 Before
284 Before
285
285
286 <pre>
286 <pre>
287 <prepared-statement-cache-size>32</prepared-statement-cache-size>
287 <prepared-statement-cache-size>32</prepared-statement-cache-size>
288 </pre>
288 </pre>
289
289
290 After
290 After
291 RAW
291 RAW
292
292
293 expected = <<-EXPECTED
293 expected = <<-EXPECTED
294 <p>Before</p>
294 <p>Before</p>
295 <pre>
295 <pre>
296 &lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
296 &lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
297 </pre>
297 </pre>
298 <p>After</p>
298 <p>After</p>
299 EXPECTED
299 EXPECTED
300
300
301 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
301 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
302 end
302 end
303
303
304 def test_pre_content_should_not_parse_wiki_and_redmine_links
304 def test_pre_content_should_not_parse_wiki_and_redmine_links
305 raw = <<-RAW
305 raw = <<-RAW
306 [[CookBook documentation]]
306 [[CookBook documentation]]
307
307
308 #1
308 #1
309
309
310 <pre>
310 <pre>
311 [[CookBook documentation]]
311 [[CookBook documentation]]
312
312
313 #1
313 #1
314 </pre>
314 </pre>
315 RAW
315 RAW
316
316
317 expected = <<-EXPECTED
317 expected = <<-EXPECTED
318 <p><a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a></p>
318 <p><a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a></p>
319 <p><a href="/issues/1" class="issue status-1 priority-1" title="Can't print recipes (New)">#1</a></p>
319 <p><a href="/issues/1" class="issue status-1 priority-1" title="Can't print recipes (New)">#1</a></p>
320 <pre>
320 <pre>
321 [[CookBook documentation]]
321 [[CookBook documentation]]
322
322
323 #1
323 #1
324 </pre>
324 </pre>
325 EXPECTED
325 EXPECTED
326
326
327 @project = Project.find(1)
327 @project = Project.find(1)
328 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
328 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
329 end
329 end
330
330
331 def test_non_closing_pre_blocks_should_be_closed
331 def test_non_closing_pre_blocks_should_be_closed
332 raw = <<-RAW
332 raw = <<-RAW
333 <pre><code>
333 <pre><code>
334 RAW
334 RAW
335
335
336 expected = <<-EXPECTED
336 expected = <<-EXPECTED
337 <pre><code>
337 <pre><code>
338 </code></pre>
338 </code></pre>
339 EXPECTED
339 EXPECTED
340
340
341 @project = Project.find(1)
341 @project = Project.find(1)
342 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
342 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
343 end
343 end
344
344
345 def test_syntax_highlight
345 def test_syntax_highlight
346 raw = <<-RAW
346 raw = <<-RAW
347 <pre><code class="ruby">
347 <pre><code class="ruby">
348 # Some ruby code here
348 # Some ruby code here
349 </code></pre>
349 </code></pre>
350 RAW
350 RAW
351
351
352 expected = <<-EXPECTED
352 expected = <<-EXPECTED
353 <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="no">1</span> <span class="c"># Some ruby code here</span></span>
353 <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="no">1</span> <span class="c"># Some ruby code here</span></span>
354 </code></pre>
354 </code></pre>
355 EXPECTED
355 EXPECTED
356
356
357 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
357 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
358 end
358 end
359
359
360 def test_wiki_links_in_tables
360 def test_wiki_links_in_tables
361 to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
361 to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
362 '<tr><td><a href="/projects/ecookbook/wiki/Page" class="wiki-page new">Link title</a></td>' +
362 '<tr><td><a href="/projects/ecookbook/wiki/Page" class="wiki-page new">Link title</a></td>' +
363 '<td><a href="/projects/ecookbook/wiki/Other_Page" class="wiki-page new">Other title</a></td>' +
363 '<td><a href="/projects/ecookbook/wiki/Other_Page" class="wiki-page new">Other title</a></td>' +
364 '</tr><tr><td>Cell 21</td><td><a href="/projects/ecookbook/wiki/Last_page" class="wiki-page new">Last page</a></td></tr>'
364 '</tr><tr><td>Cell 21</td><td><a href="/projects/ecookbook/wiki/Last_page" class="wiki-page new">Last page</a></td></tr>'
365 }
365 }
366 @project = Project.find(1)
366 @project = Project.find(1)
367 to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
367 to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
368 end
368 end
369
369
370 def test_text_formatting
370 def test_text_formatting
371 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
371 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
372 '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
372 '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
373 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
373 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
374 'a H *umane* W *eb* T *ext* G *enerator*' => 'a H <strong>umane</strong> W <strong>eb</strong> T <strong>ext</strong> G <strong>enerator</strong>',
374 'a H *umane* W *eb* T *ext* G *enerator*' => 'a H <strong>umane</strong> W <strong>eb</strong> T <strong>ext</strong> G <strong>enerator</strong>',
375 'a *H* umane *W* eb *T* ext *G* enerator' => 'a <strong>H</strong> umane <strong>W</strong> eb <strong>T</strong> ext <strong>G</strong> enerator',
375 'a *H* umane *W* eb *T* ext *G* enerator' => 'a <strong>H</strong> umane <strong>W</strong> eb <strong>T</strong> ext <strong>G</strong> enerator',
376 }
376 }
377 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
377 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
378 end
378 end
379
379
380 def test_wiki_horizontal_rule
380 def test_wiki_horizontal_rule
381 assert_equal '<hr />', textilizable('---')
381 assert_equal '<hr />', textilizable('---')
382 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
382 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
383 end
383 end
384
384
385 def test_acronym
385 def test_acronym
386 assert_equal '<p>This is an acronym: <acronym title="American Civil Liberties Union">ACLU</acronym>.</p>',
386 assert_equal '<p>This is an acronym: <acronym title="American Civil Liberties Union">ACLU</acronym>.</p>',
387 textilizable('This is an acronym: ACLU(American Civil Liberties Union).')
387 textilizable('This is an acronym: ACLU(American Civil Liberties Union).')
388 end
388 end
389
389
390 def test_footnotes
390 def test_footnotes
391 raw = <<-RAW
391 raw = <<-RAW
392 This is some text[1].
392 This is some text[1].
393
393
394 fn1. This is the foot note
394 fn1. This is the foot note
395 RAW
395 RAW
396
396
397 expected = <<-EXPECTED
397 expected = <<-EXPECTED
398 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
398 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
399 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
399 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
400 EXPECTED
400 EXPECTED
401
401
402 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
402 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
403 end
403 end
404
404
405 def test_table_of_content
405 def test_table_of_content
406 raw = <<-RAW
406 raw = <<-RAW
407 {{toc}}
407 {{toc}}
408
408
409 h1. Title
409 h1. Title
410
410
411 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
411 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
412
412
413 h2. Subtitle with a [[Wiki]] link
413 h2. Subtitle with a [[Wiki]] link
414
414
415 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
415 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
416
416
417 h2. Subtitle with [[Wiki|another Wiki]] link
417 h2. Subtitle with [[Wiki|another Wiki]] link
418
418
419 h2. Subtitle with %{color:red}red text%
419 h2. Subtitle with %{color:red}red text%
420
420
421 h1. Another title
421 h1. Another title
422
422
423 h2. An "Internet link":http://www.redmine.org/ inside subtitle
423 RAW
424 RAW
424
425
425 expected = '<ul class="toc">' +
426 expected = '<ul class="toc">' +
426 '<li class="heading1"><a href="#Title">Title</a></li>' +
427 '<li class="heading1"><a href="#Title">Title</a></li>' +
427 '<li class="heading2"><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
428 '<li class="heading2"><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
428 '<li class="heading2"><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
429 '<li class="heading2"><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
429 '<li class="heading2"><a href="#Subtitle-with-red-text">Subtitle with red text</a></li>' +
430 '<li class="heading2"><a href="#Subtitle-with-red-text">Subtitle with red text</a></li>' +
430 '<li class="heading1"><a href="#Another-title">Another title</a></li>' +
431 '<li class="heading1"><a href="#Another-title">Another title</a></li>' +
432 '<li class="heading2"><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
431 '</ul>'
433 '</ul>'
432
434
433 assert textilizable(raw).gsub("\n", "").include?(expected)
435 assert textilizable(raw).gsub("\n", "").include?(expected)
434 end
436 end
435
437
436 def test_blockquote
438 def test_blockquote
437 # orig raw text
439 # orig raw text
438 raw = <<-RAW
440 raw = <<-RAW
439 John said:
441 John said:
440 > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
442 > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
441 > Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
443 > Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
442 > * Donec odio lorem,
444 > * Donec odio lorem,
443 > * sagittis ac,
445 > * sagittis ac,
444 > * malesuada in,
446 > * malesuada in,
445 > * adipiscing eu, dolor.
447 > * adipiscing eu, dolor.
446 >
448 >
447 > >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
449 > >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
448 > Proin a tellus. Nam vel neque.
450 > Proin a tellus. Nam vel neque.
449
451
450 He's right.
452 He's right.
451 RAW
453 RAW
452
454
453 # expected html
455 # expected html
454 expected = <<-EXPECTED
456 expected = <<-EXPECTED
455 <p>John said:</p>
457 <p>John said:</p>
456 <blockquote>
458 <blockquote>
457 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
459 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
458 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
460 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
459 <ul>
461 <ul>
460 <li>Donec odio lorem,</li>
462 <li>Donec odio lorem,</li>
461 <li>sagittis ac,</li>
463 <li>sagittis ac,</li>
462 <li>malesuada in,</li>
464 <li>malesuada in,</li>
463 <li>adipiscing eu, dolor.</li>
465 <li>adipiscing eu, dolor.</li>
464 </ul>
466 </ul>
465 <blockquote>
467 <blockquote>
466 <p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
468 <p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
467 </blockquote>
469 </blockquote>
468 <p>Proin a tellus. Nam vel neque.</p>
470 <p>Proin a tellus. Nam vel neque.</p>
469 </blockquote>
471 </blockquote>
470 <p>He's right.</p>
472 <p>He's right.</p>
471 EXPECTED
473 EXPECTED
472
474
473 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
475 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
474 end
476 end
475
477
476 def test_table
478 def test_table
477 raw = <<-RAW
479 raw = <<-RAW
478 This is a table with empty cells:
480 This is a table with empty cells:
479
481
480 |cell11|cell12||
482 |cell11|cell12||
481 |cell21||cell23|
483 |cell21||cell23|
482 |cell31|cell32|cell33|
484 |cell31|cell32|cell33|
483 RAW
485 RAW
484
486
485 expected = <<-EXPECTED
487 expected = <<-EXPECTED
486 <p>This is a table with empty cells:</p>
488 <p>This is a table with empty cells:</p>
487
489
488 <table>
490 <table>
489 <tr><td>cell11</td><td>cell12</td><td></td></tr>
491 <tr><td>cell11</td><td>cell12</td><td></td></tr>
490 <tr><td>cell21</td><td></td><td>cell23</td></tr>
492 <tr><td>cell21</td><td></td><td>cell23</td></tr>
491 <tr><td>cell31</td><td>cell32</td><td>cell33</td></tr>
493 <tr><td>cell31</td><td>cell32</td><td>cell33</td></tr>
492 </table>
494 </table>
493 EXPECTED
495 EXPECTED
494
496
495 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
497 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
496 end
498 end
497
499
498 def test_table_with_line_breaks
500 def test_table_with_line_breaks
499 raw = <<-RAW
501 raw = <<-RAW
500 This is a table with line breaks:
502 This is a table with line breaks:
501
503
502 |cell11
504 |cell11
503 continued|cell12||
505 continued|cell12||
504 |-cell21-||cell23
506 |-cell21-||cell23
505 cell23 line2
507 cell23 line2
506 cell23 *line3*|
508 cell23 *line3*|
507 |cell31|cell32
509 |cell31|cell32
508 cell32 line2|cell33|
510 cell32 line2|cell33|
509
511
510 RAW
512 RAW
511
513
512 expected = <<-EXPECTED
514 expected = <<-EXPECTED
513 <p>This is a table with line breaks:</p>
515 <p>This is a table with line breaks:</p>
514
516
515 <table>
517 <table>
516 <tr>
518 <tr>
517 <td>cell11<br />continued</td>
519 <td>cell11<br />continued</td>
518 <td>cell12</td>
520 <td>cell12</td>
519 <td></td>
521 <td></td>
520 </tr>
522 </tr>
521 <tr>
523 <tr>
522 <td><del>cell21</del></td>
524 <td><del>cell21</del></td>
523 <td></td>
525 <td></td>
524 <td>cell23<br/>cell23 line2<br/>cell23 <strong>line3</strong></td>
526 <td>cell23<br/>cell23 line2<br/>cell23 <strong>line3</strong></td>
525 </tr>
527 </tr>
526 <tr>
528 <tr>
527 <td>cell31</td>
529 <td>cell31</td>
528 <td>cell32<br/>cell32 line2</td>
530 <td>cell32<br/>cell32 line2</td>
529 <td>cell33</td>
531 <td>cell33</td>
530 </tr>
532 </tr>
531 </table>
533 </table>
532 EXPECTED
534 EXPECTED
533
535
534 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
536 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
535 end
537 end
536
538
537 def test_textile_should_not_mangle_brackets
539 def test_textile_should_not_mangle_brackets
538 assert_equal '<p>[msg1][msg2]</p>', textilizable('[msg1][msg2]')
540 assert_equal '<p>[msg1][msg2]</p>', textilizable('[msg1][msg2]')
539 end
541 end
540
542
541 def test_default_formatter
543 def test_default_formatter
542 Setting.text_formatting = 'unknown'
544 Setting.text_formatting = 'unknown'
543 text = 'a *link*: http://www.example.net/'
545 text = 'a *link*: http://www.example.net/'
544 assert_equal '<p>a *link*: <a href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
546 assert_equal '<p>a *link*: <a href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
545 Setting.text_formatting = 'textile'
547 Setting.text_formatting = 'textile'
546 end
548 end
547
549
548 def test_due_date_distance_in_words
550 def test_due_date_distance_in_words
549 to_test = { Date.today => 'Due in 0 days',
551 to_test = { Date.today => 'Due in 0 days',
550 Date.today + 1 => 'Due in 1 day',
552 Date.today + 1 => 'Due in 1 day',
551 Date.today + 100 => 'Due in about 3 months',
553 Date.today + 100 => 'Due in about 3 months',
552 Date.today + 20000 => 'Due in over 54 years',
554 Date.today + 20000 => 'Due in over 54 years',
553 Date.today - 1 => '1 day late',
555 Date.today - 1 => '1 day late',
554 Date.today - 100 => 'about 3 months late',
556 Date.today - 100 => 'about 3 months late',
555 Date.today - 20000 => 'over 54 years late',
557 Date.today - 20000 => 'over 54 years late',
556 }
558 }
557 to_test.each do |date, expected|
559 to_test.each do |date, expected|
558 assert_equal expected, due_date_distance_in_words(date)
560 assert_equal expected, due_date_distance_in_words(date)
559 end
561 end
560 end
562 end
561
563
562 def test_avatar
564 def test_avatar
563 # turn on avatars
565 # turn on avatars
564 Setting.gravatar_enabled = '1'
566 Setting.gravatar_enabled = '1'
565 assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
567 assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
566 assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
568 assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
567 assert_nil avatar('jsmith')
569 assert_nil avatar('jsmith')
568 assert_nil avatar(nil)
570 assert_nil avatar(nil)
569
571
570 # turn off avatars
572 # turn off avatars
571 Setting.gravatar_enabled = '0'
573 Setting.gravatar_enabled = '0'
572 assert_nil avatar(User.find_by_mail('jsmith@somenet.foo'))
574 assert_nil avatar(User.find_by_mail('jsmith@somenet.foo'))
573 end
575 end
574
576
575 def test_link_to_user
577 def test_link_to_user
576 user = User.find(2)
578 user = User.find(2)
577 t = link_to_user(user)
579 t = link_to_user(user)
578 assert_equal "<a href=\"/users/2\">#{ user.name }</a>", t
580 assert_equal "<a href=\"/users/2\">#{ user.name }</a>", t
579 end
581 end
580
582
581 def test_link_to_user_should_not_link_to_locked_user
583 def test_link_to_user_should_not_link_to_locked_user
582 user = User.find(5)
584 user = User.find(5)
583 assert user.locked?
585 assert user.locked?
584 t = link_to_user(user)
586 t = link_to_user(user)
585 assert_equal user.name, t
587 assert_equal user.name, t
586 end
588 end
587
589
588 def test_link_to_user_should_not_link_to_anonymous
590 def test_link_to_user_should_not_link_to_anonymous
589 user = User.anonymous
591 user = User.anonymous
590 assert user.anonymous?
592 assert user.anonymous?
591 t = link_to_user(user)
593 t = link_to_user(user)
592 assert_equal ::I18n.t(:label_user_anonymous), t
594 assert_equal ::I18n.t(:label_user_anonymous), t
593 end
595 end
594 end
596 end
General Comments 0
You need to be logged in to leave comments. Login now