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