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