##// END OF EJS Templates
Turn ftps and sftp proto into links (#1514)....
Jean-Philippe Lang -
r2016:cbacc71dff75
parent child
Show More
@@ -1,183 +1,183
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'redcloth3'
19 19 require 'coderay'
20 20
21 21 module Redmine
22 22 module WikiFormatting
23 23 module Textile
24 24 class Formatter < RedCloth3
25 25
26 26 # auto_link rule after textile rules so that it doesn't break !image_url! tags
27 27 RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros]
28 28
29 29 def initialize(*args)
30 30 super
31 31 self.hard_breaks=true
32 32 self.no_span_caps=true
33 33 end
34 34
35 35 def to_html(*rules, &block)
36 36 @toc = []
37 37 @macros_runner = block
38 38 super(*RULES).to_s
39 39 end
40 40
41 41 private
42 42
43 43 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
44 44 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
45 45 def hard_break( text )
46 46 text.gsub!( /(.)\n(?!\n|\Z|>| *(>? *[#*=]+(\s|$)|[{|]))/, "\\1<br />\n" ) if hard_breaks
47 47 end
48 48
49 49 # Patch to add code highlighting support to RedCloth
50 50 def smooth_offtags( text )
51 51 unless @pre_list.empty?
52 52 ## replace <pre> content
53 53 text.gsub!(/<redpre#(\d+)>/) do
54 54 content = @pre_list[$1.to_i]
55 55 if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
56 56 content = "<code class=\"#{$1} CodeRay\">" +
57 57 CodeRay.scan($2, $1.downcase).html(:escape => false, :line_numbers => :inline)
58 58 end
59 59 content
60 60 end
61 61 end
62 62 end
63 63
64 64 # Patch to add 'table of content' support to RedCloth
65 65 def textile_p_withtoc(tag, atts, cite, content)
66 66 # removes wiki links from the item
67 67 toc_item = content.gsub(/(\[\[|\]\])/, '')
68 68 # removes styles
69 69 # eg. %{color:red}Triggers% => Triggers
70 70 toc_item.gsub! %r[%\{[^\}]*\}([^%]+)%], '\\1'
71 71
72 72 # replaces non word caracters by dashes
73 73 anchor = toc_item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
74 74
75 75 unless anchor.blank?
76 76 if tag =~ /^h(\d)$/
77 77 @toc << [$1.to_i, anchor, toc_item]
78 78 end
79 79 atts << " id=\"#{anchor}\""
80 80 content = content + "<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a>"
81 81 end
82 82 textile_p(tag, atts, cite, content)
83 83 end
84 84
85 85 alias :textile_h1 :textile_p_withtoc
86 86 alias :textile_h2 :textile_p_withtoc
87 87 alias :textile_h3 :textile_p_withtoc
88 88
89 89 def inline_toc(text)
90 90 text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
91 91 div_class = 'toc'
92 92 div_class << ' right' if $1 == '>'
93 93 div_class << ' left' if $1 == '<'
94 94 out = "<ul class=\"#{div_class}\">"
95 95 @toc.each do |heading|
96 96 level, anchor, toc_item = heading
97 97 out << "<li class=\"heading#{level}\"><a href=\"##{anchor}\">#{toc_item}</a></li>\n"
98 98 end
99 99 out << '</ul>'
100 100 out
101 101 end
102 102 end
103 103
104 104 MACROS_RE = /
105 105 (!)? # escaping
106 106 (
107 107 \{\{ # opening tag
108 108 ([\w]+) # macro name
109 109 (\(([^\}]*)\))? # optional arguments
110 110 \}\} # closing tag
111 111 )
112 112 /x unless const_defined?(:MACROS_RE)
113 113
114 114 def inline_macros(text)
115 115 text.gsub!(MACROS_RE) do
116 116 esc, all, macro = $1, $2, $3.downcase
117 117 args = ($5 || '').split(',').each(&:strip)
118 118 if esc.nil?
119 119 begin
120 120 @macros_runner.call(macro, args)
121 121 rescue => e
122 122 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
123 123 end || all
124 124 else
125 125 all
126 126 end
127 127 end
128 128 end
129 129
130 130 AUTO_LINK_RE = %r{
131 131 ( # leading text
132 132 <\w+.*?>| # leading HTML tag, or
133 133 [^=<>!:'"/]| # leading punctuation, or
134 134 ^ # beginning of line
135 135 )
136 136 (
137 137 (?:https?://)| # protocol spec, or
138 (?:ftp://)|
138 (?:s?ftps?://)|
139 139 (?:www\.) # www.*
140 140 )
141 141 (
142 142 (\S+?) # url
143 143 (\/)? # slash
144 144 )
145 145 ([^\w\=\/;\(\)]*?) # post
146 146 (?=<|\s|$)
147 147 }x unless const_defined?(:AUTO_LINK_RE)
148 148
149 149 # Turns all urls into clickable links (code from Rails).
150 150 def inline_auto_link(text)
151 151 text.gsub!(AUTO_LINK_RE) do
152 152 all, leading, proto, url, post = $&, $1, $2, $3, $6
153 153 if leading =~ /<a\s/i || leading =~ /![<>=]?/
154 154 # don't replace URL's that are already linked
155 155 # and URL's prefixed with ! !> !< != (textile images)
156 156 all
157 157 else
158 158 # Idea below : an URL with unbalanced parethesis and
159 159 # ending by ')' is put into external parenthesis
160 160 if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
161 161 url=url[0..-2] # discard closing parenth from url
162 162 post = ")"+post # add closing parenth to post
163 163 end
164 164 %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
165 165 end
166 166 end
167 167 end
168 168
169 169 # Turns all email addresses into clickable links (code from Rails).
170 170 def inline_auto_mailto(text)
171 171 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
172 172 mail = $1
173 173 if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
174 174 mail
175 175 else
176 176 %{<a href="mailto:#{mail}" class="email">#{mail}</a>}
177 177 end
178 178 end
179 179 end
180 180 end
181 181 end
182 182 end
183 183 end
@@ -1,453 +1,456
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../../test_helper'
19 19
20 20 class ApplicationHelperTest < HelperTestCase
21 21 include ApplicationHelper
22 22 include ActionView::Helpers::TextHelper
23 23 fixtures :projects, :roles, :enabled_modules, :users,
24 24 :repositories, :changesets,
25 25 :trackers, :issue_statuses, :issues, :versions, :documents,
26 26 :wikis, :wiki_pages, :wiki_contents,
27 27 :boards, :messages,
28 28 :attachments
29 29
30 30 def setup
31 31 super
32 32 end
33 33
34 34 def test_auto_links
35 35 to_test = {
36 36 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
37 37 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
38 38 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
39 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
39 40 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
40 41 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
41 42 '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>.',
42 43 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
43 44 '(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>)',
44 45 '(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>)',
45 46 '(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>).',
46 47 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
47 48 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
48 49 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
49 50 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
50 51 '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>',
51 52 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
52 53 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
53 54 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
54 55 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
56 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
57 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
55 58 }
56 59 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
57 60 end
58 61
59 62 def test_auto_mailto
60 63 assert_equal '<p><a href="mailto:test@foo.bar" class="email">test@foo.bar</a></p>',
61 64 textilizable('test@foo.bar')
62 65 end
63 66
64 67 def test_inline_images
65 68 to_test = {
66 69 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
67 70 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
68 71 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
69 72 'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height100px;" alt="" />',
70 73 }
71 74 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
72 75 end
73 76
74 77 def test_attached_images
75 78 to_test = {
76 79 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
77 80 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />'
78 81 }
79 82 attachments = Attachment.find(:all)
80 83 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
81 84 end
82 85
83 86 def test_textile_external_links
84 87 to_test = {
85 88 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
86 89 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
87 90 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
88 91 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
89 92 # no multiline link text
90 93 "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 />\nand another on a second line\":test"
91 94 }
92 95 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
93 96 end
94 97
95 98 def test_redmine_links
96 99 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
97 100 :class => 'issue', :title => 'Error 281 when updating a recipe (New)')
98 101
99 102 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
100 103 :class => 'changeset', :title => 'My very first commit')
101 104
102 105 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
103 106 :class => 'document')
104 107
105 108 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
106 109 :class => 'version')
107 110
108 111 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
109 112
110 113 source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}
111 114 source_url_with_ext = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file.ext']}
112 115
113 116 to_test = {
114 117 # tickets
115 118 '#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.",
116 119 # changesets
117 120 'r1' => changeset_link,
118 121 # documents
119 122 'document#1' => document_link,
120 123 'document:"Test document"' => document_link,
121 124 # versions
122 125 'version#2' => version_link,
123 126 'version:1.0' => version_link,
124 127 'version:"1.0"' => version_link,
125 128 # source
126 129 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
127 130 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
128 131 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
129 132 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
130 133 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
131 134 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
132 135 'source:/some/file@52' => link_to('source:/some/file@52', source_url.merge(:rev => 52), :class => 'source'),
133 136 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_ext.merge(:rev => 52), :class => 'source'),
134 137 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'),
135 138 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'),
136 139 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'),
137 140 'export:/some/file' => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'),
138 141 # message
139 142 'message#4' => link_to('Post 2', message_url, :class => 'message'),
140 143 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5'), :class => 'message'),
141 144 # escaping
142 145 '!#3.' => '#3.',
143 146 '!r1' => 'r1',
144 147 '!document#1' => 'document#1',
145 148 '!document:"Test document"' => 'document:"Test document"',
146 149 '!version#2' => 'version#2',
147 150 '!version:1.0' => 'version:1.0',
148 151 '!version:"1.0"' => 'version:"1.0"',
149 152 '!source:/some/file' => 'source:/some/file',
150 153 # invalid expressions
151 154 'source:' => 'source:',
152 155 # url hash
153 156 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
154 157 }
155 158 @project = Project.find(1)
156 159 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
157 160 end
158 161
159 162 def test_wiki_links
160 163 to_test = {
161 164 '[[CookBook documentation]]' => '<a href="/wiki/ecookbook/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
162 165 '[[Another page|Page]]' => '<a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a>',
163 166 # link with anchor
164 167 '[[CookBook documentation#One-section]]' => '<a href="/wiki/ecookbook/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
165 168 '[[Another page#anchor|Page]]' => '<a href="/wiki/ecookbook/Another_page#anchor" class="wiki-page">Page</a>',
166 169 # page that doesn't exist
167 170 '[[Unknown page]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">Unknown page</a>',
168 171 '[[Unknown page|404]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">404</a>',
169 172 # link to another project wiki
170 173 '[[onlinestore:]]' => '<a href="/wiki/onlinestore/" class="wiki-page">onlinestore</a>',
171 174 '[[onlinestore:|Wiki]]' => '<a href="/wiki/onlinestore/" class="wiki-page">Wiki</a>',
172 175 '[[onlinestore:Start page]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Start page</a>',
173 176 '[[onlinestore:Start page|Text]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Text</a>',
174 177 '[[onlinestore:Unknown page]]' => '<a href="/wiki/onlinestore/Unknown_page" class="wiki-page new">Unknown page</a>',
175 178 # striked through link
176 179 '-[[Another page|Page]]-' => '<del><a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a></del>',
177 180 # escaping
178 181 '![[Another page|Page]]' => '[[Another page|Page]]',
179 182 }
180 183 @project = Project.find(1)
181 184 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
182 185 end
183 186
184 187 def test_html_tags
185 188 to_test = {
186 189 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
187 190 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
188 191 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
189 192 # do not escape pre/code tags
190 193 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
191 194 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
192 195 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
193 196 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
194 197 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
195 198 # remove attributes except class
196 199 "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
197 200 "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
198 201 }
199 202 to_test.each { |text, result| assert_equal result, textilizable(text) }
200 203 end
201 204
202 205 def test_allowed_html_tags
203 206 to_test = {
204 207 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
205 208 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
206 209 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
207 210 }
208 211 to_test.each { |text, result| assert_equal result, textilizable(text) }
209 212 end
210 213
211 214 def syntax_highlight
212 215 raw = <<-RAW
213 216 <pre><code class="ruby">
214 217 # Some ruby code here
215 218 </pre></code>
216 219 RAW
217 220
218 221 expected = <<-EXPECTED
219 222 <pre><code class="ruby CodeRay"><span class="no">1</span> <span class="c"># Some ruby code here</span>
220 223 </pre></code>
221 224 EXPECTED
222 225
223 226 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
224 227 end
225 228
226 229 def test_wiki_links_in_tables
227 230 to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
228 231 '<tr><td><a href="/wiki/ecookbook/Page" class="wiki-page new">Link title</a></td>' +
229 232 '<td><a href="/wiki/ecookbook/Other_Page" class="wiki-page new">Other title</a></td>' +
230 233 '</tr><tr><td>Cell 21</td><td><a href="/wiki/ecookbook/Last_page" class="wiki-page new">Last page</a></td></tr>'
231 234 }
232 235 @project = Project.find(1)
233 236 to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
234 237 end
235 238
236 239 def test_text_formatting
237 240 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
238 241 '(_text within parentheses_)' => '(<em>text within parentheses</em>)'
239 242 }
240 243 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
241 244 end
242 245
243 246 def test_wiki_horizontal_rule
244 247 assert_equal '<hr />', textilizable('---')
245 248 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
246 249 end
247 250
248 251 def test_acronym
249 252 assert_equal '<p>This is an acronym: <acronym title="American Civil Liberties Union">ACLU</acronym>.</p>',
250 253 textilizable('This is an acronym: ACLU(American Civil Liberties Union).')
251 254 end
252 255
253 256 def test_footnotes
254 257 raw = <<-RAW
255 258 This is some text[1].
256 259
257 260 fn1. This is the foot note
258 261 RAW
259 262
260 263 expected = <<-EXPECTED
261 264 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
262 265 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
263 266 EXPECTED
264 267
265 268 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
266 269 end
267 270
268 271 def test_table_of_content
269 272 raw = <<-RAW
270 273 {{toc}}
271 274
272 275 h1. Title
273 276
274 277 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
275 278
276 279 h2. Subtitle
277 280
278 281 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
279 282
280 283 h2. Subtitle with %{color:red}red text%
281 284
282 285 h1. Another title
283 286
284 287 RAW
285 288
286 289 expected = '<ul class="toc">' +
287 290 '<li class="heading1"><a href="#Title">Title</a></li>' +
288 291 '<li class="heading2"><a href="#Subtitle">Subtitle</a></li>' +
289 292 '<li class="heading2"><a href="#Subtitle-with-red-text">Subtitle with red text</a></li>' +
290 293 '<li class="heading1"><a href="#Another-title">Another title</a></li>' +
291 294 '</ul>'
292 295
293 296 assert textilizable(raw).gsub("\n", "").include?(expected)
294 297 end
295 298
296 299 def test_blockquote
297 300 # orig raw text
298 301 raw = <<-RAW
299 302 John said:
300 303 > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
301 304 > Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
302 305 > * Donec odio lorem,
303 306 > * sagittis ac,
304 307 > * malesuada in,
305 308 > * adipiscing eu, dolor.
306 309 >
307 310 > >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
308 311 > Proin a tellus. Nam vel neque.
309 312
310 313 He's right.
311 314 RAW
312 315
313 316 # expected html
314 317 expected = <<-EXPECTED
315 318 <p>John said:</p>
316 319 <blockquote>
317 320 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
318 321 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
319 322 <ul>
320 323 <li>Donec odio lorem,</li>
321 324 <li>sagittis ac,</li>
322 325 <li>malesuada in,</li>
323 326 <li>adipiscing eu, dolor.</li>
324 327 </ul>
325 328 <blockquote>
326 329 <p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
327 330 </blockquote>
328 331 <p>Proin a tellus. Nam vel neque.</p>
329 332 </blockquote>
330 333 <p>He's right.</p>
331 334 EXPECTED
332 335
333 336 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
334 337 end
335 338
336 339 def test_table
337 340 raw = <<-RAW
338 341 This is a table with empty cells:
339 342
340 343 |cell11|cell12||
341 344 |cell21||cell23|
342 345 |cell31|cell32|cell33|
343 346 RAW
344 347
345 348 expected = <<-EXPECTED
346 349 <p>This is a table with empty cells:</p>
347 350
348 351 <table>
349 352 <tr><td>cell11</td><td>cell12</td><td></td></tr>
350 353 <tr><td>cell21</td><td></td><td>cell23</td></tr>
351 354 <tr><td>cell31</td><td>cell32</td><td>cell33</td></tr>
352 355 </table>
353 356 EXPECTED
354 357
355 358 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
356 359 end
357 360
358 361 def test_macro_hello_world
359 362 text = "{{hello_world}}"
360 363 assert textilizable(text).match(/Hello world!/)
361 364 # escaping
362 365 text = "!{{hello_world}}"
363 366 assert_equal '<p>{{hello_world}}</p>', textilizable(text)
364 367 end
365 368
366 369 def test_macro_include
367 370 @project = Project.find(1)
368 371 # include a page of the current project wiki
369 372 text = "{{include(Another page)}}"
370 373 assert textilizable(text).match(/This is a link to a ticket/)
371 374
372 375 @project = nil
373 376 # include a page of a specific project wiki
374 377 text = "{{include(ecookbook:Another page)}}"
375 378 assert textilizable(text).match(/This is a link to a ticket/)
376 379
377 380 text = "{{include(ecookbook:)}}"
378 381 assert textilizable(text).match(/CookBook documentation/)
379 382
380 383 text = "{{include(unknowidentifier:somepage)}}"
381 384 assert textilizable(text).match(/Unknow project/)
382 385 end
383 386
384 387 def test_default_formatter
385 388 Setting.text_formatting = 'unknown'
386 389 text = 'a *link*: http://www.example.net/'
387 390 assert_equal '<p>a *link*: <a href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
388 391 Setting.text_formatting = 'textile'
389 392 end
390 393
391 394 def test_date_format_default
392 395 today = Date.today
393 396 Setting.date_format = ''
394 397 assert_equal l_date(today), format_date(today)
395 398 end
396 399
397 400 def test_date_format
398 401 today = Date.today
399 402 Setting.date_format = '%d %m %Y'
400 403 assert_equal today.strftime('%d %m %Y'), format_date(today)
401 404 end
402 405
403 406 def test_time_format_default
404 407 now = Time.now
405 408 Setting.date_format = ''
406 409 Setting.time_format = ''
407 410 assert_equal l_datetime(now), format_time(now)
408 411 assert_equal l_time(now), format_time(now, false)
409 412 end
410 413
411 414 def test_time_format
412 415 now = Time.now
413 416 Setting.date_format = '%d %m %Y'
414 417 Setting.time_format = '%H %M'
415 418 assert_equal now.strftime('%d %m %Y %H %M'), format_time(now)
416 419 assert_equal now.strftime('%H %M'), format_time(now, false)
417 420 end
418 421
419 422 def test_utc_time_format
420 423 now = Time.now.utc
421 424 Setting.date_format = '%d %m %Y'
422 425 Setting.time_format = '%H %M'
423 426 assert_equal Time.now.strftime('%d %m %Y %H %M'), format_time(now)
424 427 assert_equal Time.now.strftime('%H %M'), format_time(now, false)
425 428 end
426 429
427 430 def test_due_date_distance_in_words
428 431 to_test = { Date.today => 'Due in 0 days',
429 432 Date.today + 1 => 'Due in 1 day',
430 433 Date.today + 100 => 'Due in 100 days',
431 434 Date.today + 20000 => 'Due in 20000 days',
432 435 Date.today - 1 => '1 day late',
433 436 Date.today - 100 => '100 days late',
434 437 Date.today - 20000 => '20000 days late',
435 438 }
436 439 to_test.each do |date, expected|
437 440 assert_equal expected, due_date_distance_in_words(date)
438 441 end
439 442 end
440 443
441 444 def test_avatar
442 445 # turn on avatars
443 446 Setting.gravatar_enabled = '1'
444 447 assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
445 448 assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
446 449 assert_nil avatar('jsmith')
447 450 assert_nil avatar(nil)
448 451
449 452 # turn off avatars
450 453 Setting.gravatar_enabled = '0'
451 454 assert_nil avatar(User.find_by_mail('jsmith@somenet.foo'))
452 455 end
453 456 end
General Comments 0
You need to be logged in to leave comments. Login now