##// END OF EJS Templates
Restricts characters before auto links (#10277)....
Jean-Philippe Lang -
r11244:5b4a9ac3b3aa
parent child
Show More
@@ -1,173 +1,173
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 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 'digest/md5'
18 require 'digest/md5'
19
19
20 module Redmine
20 module Redmine
21 module WikiFormatting
21 module WikiFormatting
22 class StaleSectionError < Exception; end
22 class StaleSectionError < Exception; end
23
23
24 @@formatters = {}
24 @@formatters = {}
25
25
26 class << self
26 class << self
27 def map
27 def map
28 yield self
28 yield self
29 end
29 end
30
30
31 def register(name, formatter, helper)
31 def register(name, formatter, helper)
32 raise ArgumentError, "format name '#{name}' is already taken" if @@formatters[name.to_s]
32 raise ArgumentError, "format name '#{name}' is already taken" if @@formatters[name.to_s]
33 @@formatters[name.to_s] = {:formatter => formatter, :helper => helper}
33 @@formatters[name.to_s] = {:formatter => formatter, :helper => helper}
34 end
34 end
35
35
36 def formatter
36 def formatter
37 formatter_for(Setting.text_formatting)
37 formatter_for(Setting.text_formatting)
38 end
38 end
39
39
40 def formatter_for(name)
40 def formatter_for(name)
41 entry = @@formatters[name.to_s]
41 entry = @@formatters[name.to_s]
42 (entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter
42 (entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter
43 end
43 end
44
44
45 def helper_for(name)
45 def helper_for(name)
46 entry = @@formatters[name.to_s]
46 entry = @@formatters[name.to_s]
47 (entry && entry[:helper]) || Redmine::WikiFormatting::NullFormatter::Helper
47 (entry && entry[:helper]) || Redmine::WikiFormatting::NullFormatter::Helper
48 end
48 end
49
49
50 def format_names
50 def format_names
51 @@formatters.keys.map
51 @@formatters.keys.map
52 end
52 end
53
53
54 def to_html(format, text, options = {})
54 def to_html(format, text, options = {})
55 text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, text, options[:object], options[:attribute])
55 text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, text, options[:object], options[:attribute])
56 # Text retrieved from the cache store may be frozen
56 # Text retrieved from the cache store may be frozen
57 # We need to dup it so we can do in-place substitutions with gsub!
57 # We need to dup it so we can do in-place substitutions with gsub!
58 cache_store.fetch cache_key do
58 cache_store.fetch cache_key do
59 formatter_for(format).new(text).to_html
59 formatter_for(format).new(text).to_html
60 end.dup
60 end.dup
61 else
61 else
62 formatter_for(format).new(text).to_html
62 formatter_for(format).new(text).to_html
63 end
63 end
64 text
64 text
65 end
65 end
66
66
67 # Returns true if the text formatter supports single section edit
67 # Returns true if the text formatter supports single section edit
68 def supports_section_edit?
68 def supports_section_edit?
69 (formatter.instance_methods & ['update_section', :update_section]).any?
69 (formatter.instance_methods & ['update_section', :update_section]).any?
70 end
70 end
71
71
72 # Returns a cache key for the given text +format+, +text+, +object+ and +attribute+ or nil if no caching should be done
72 # Returns a cache key for the given text +format+, +text+, +object+ and +attribute+ or nil if no caching should be done
73 def cache_key_for(format, text, object, attribute)
73 def cache_key_for(format, text, object, attribute)
74 if object && attribute && !object.new_record? && format.present?
74 if object && attribute && !object.new_record? && format.present?
75 "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{Digest::MD5.hexdigest text}"
75 "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{Digest::MD5.hexdigest text}"
76 end
76 end
77 end
77 end
78
78
79 # Returns the cache store used to cache HTML output
79 # Returns the cache store used to cache HTML output
80 def cache_store
80 def cache_store
81 ActionController::Base.cache_store
81 ActionController::Base.cache_store
82 end
82 end
83 end
83 end
84
84
85 module LinksHelper
85 module LinksHelper
86 AUTO_LINK_RE = %r{
86 AUTO_LINK_RE = %r{
87 ( # leading text
87 ( # leading text
88 <\w+.*?>| # leading HTML tag, or
88 <\w+.*?>| # leading HTML tag, or
89 [^=<>!:'"/]| # leading punctuation, or
89 [\s\(\[,;]| # leading punctuation, or
90 ^ # beginning of line
90 ^ # beginning of line
91 )
91 )
92 (
92 (
93 (?:https?://)| # protocol spec, or
93 (?:https?://)| # protocol spec, or
94 (?:s?ftps?://)|
94 (?:s?ftps?://)|
95 (?:www\.) # www.*
95 (?:www\.) # www.*
96 )
96 )
97 (
97 (
98 ([^<]\S*?) # url
98 ([^<]\S*?) # url
99 (\/)? # slash
99 (\/)? # slash
100 )
100 )
101 ((?:&gt;)?|[^[:alnum:]_\=\/;\(\)]*?) # post
101 ((?:&gt;)?|[^[:alnum:]_\=\/;\(\)]*?) # post
102 (?=<|\s|$)
102 (?=<|\s|$)
103 }x unless const_defined?(:AUTO_LINK_RE)
103 }x unless const_defined?(:AUTO_LINK_RE)
104
104
105 # Destructively remplaces urls into clickable links
105 # Destructively remplaces urls into clickable links
106 def auto_link!(text)
106 def auto_link!(text)
107 text.gsub!(AUTO_LINK_RE) do
107 text.gsub!(AUTO_LINK_RE) do
108 all, leading, proto, url, post = $&, $1, $2, $3, $6
108 all, leading, proto, url, post = $&, $1, $2, $3, $6
109 if leading =~ /<a\s/i || leading =~ /![<>=]?/
109 if leading =~ /<a\s/i || leading =~ /![<>=]?/
110 # don't replace URL's that are already linked
110 # don't replace URL's that are already linked
111 # and URL's prefixed with ! !> !< != (textile images)
111 # and URL's prefixed with ! !> !< != (textile images)
112 all
112 all
113 else
113 else
114 # Idea below : an URL with unbalanced parethesis and
114 # Idea below : an URL with unbalanced parethesis and
115 # ending by ')' is put into external parenthesis
115 # ending by ')' is put into external parenthesis
116 if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
116 if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
117 url=url[0..-2] # discard closing parenth from url
117 url=url[0..-2] # discard closing parenth from url
118 post = ")"+post # add closing parenth to post
118 post = ")"+post # add closing parenth to post
119 end
119 end
120 content = proto + url
120 content = proto + url
121 href = "#{proto=="www."?"http://www.":proto}#{url}"
121 href = "#{proto=="www."?"http://www.":proto}#{url}"
122 %(#{leading}<a class="external" href="#{ERB::Util.html_escape href}">#{ERB::Util.html_escape content}</a>#{post}).html_safe
122 %(#{leading}<a class="external" href="#{ERB::Util.html_escape href}">#{ERB::Util.html_escape content}</a>#{post}).html_safe
123 end
123 end
124 end
124 end
125 end
125 end
126
126
127 # Destructively remplaces email addresses into clickable links
127 # Destructively remplaces email addresses into clickable links
128 def auto_mailto!(text)
128 def auto_mailto!(text)
129 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
129 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
130 mail = $1
130 mail = $1
131 if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
131 if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
132 mail
132 mail
133 else
133 else
134 %(<a class="email" href="mailto:#{ERB::Util.html_escape mail}">#{ERB::Util.html_escape mail}</a>).html_safe
134 %(<a class="email" href="mailto:#{ERB::Util.html_escape mail}">#{ERB::Util.html_escape mail}</a>).html_safe
135 end
135 end
136 end
136 end
137 end
137 end
138 end
138 end
139
139
140 # Default formatter module
140 # Default formatter module
141 module NullFormatter
141 module NullFormatter
142 class Formatter
142 class Formatter
143 include ActionView::Helpers::TagHelper
143 include ActionView::Helpers::TagHelper
144 include ActionView::Helpers::TextHelper
144 include ActionView::Helpers::TextHelper
145 include ActionView::Helpers::UrlHelper
145 include ActionView::Helpers::UrlHelper
146 include Redmine::WikiFormatting::LinksHelper
146 include Redmine::WikiFormatting::LinksHelper
147
147
148 def initialize(text)
148 def initialize(text)
149 @text = text
149 @text = text
150 end
150 end
151
151
152 def to_html(*args)
152 def to_html(*args)
153 t = CGI::escapeHTML(@text)
153 t = CGI::escapeHTML(@text)
154 auto_link!(t)
154 auto_link!(t)
155 auto_mailto!(t)
155 auto_mailto!(t)
156 simple_format(t, {}, :sanitize => false)
156 simple_format(t, {}, :sanitize => false)
157 end
157 end
158 end
158 end
159
159
160 module Helper
160 module Helper
161 def wikitoolbar_for(field_id)
161 def wikitoolbar_for(field_id)
162 end
162 end
163
163
164 def heads_for_wiki_formatter
164 def heads_for_wiki_formatter
165 end
165 end
166
166
167 def initial_page_content(page)
167 def initial_page_content(page)
168 page.pretty_title.to_s
168 page.pretty_title.to_s
169 end
169 end
170 end
170 end
171 end
171 end
172 end
172 end
173 end
173 end
@@ -1,1204 +1,1208
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.expand_path('../../../test_helper', __FILE__)
20 require File.expand_path('../../../test_helper', __FILE__)
21
21
22 class ApplicationHelperTest < ActionView::TestCase
22 class ApplicationHelperTest < ActionView::TestCase
23 include ERB::Util
23 include ERB::Util
24 include Rails.application.routes.url_helpers
24 include Rails.application.routes.url_helpers
25
25
26 fixtures :projects, :roles, :enabled_modules, :users,
26 fixtures :projects, :roles, :enabled_modules, :users,
27 :repositories, :changesets,
27 :repositories, :changesets,
28 :trackers, :issue_statuses, :issues, :versions, :documents,
28 :trackers, :issue_statuses, :issues, :versions, :documents,
29 :wikis, :wiki_pages, :wiki_contents,
29 :wikis, :wiki_pages, :wiki_contents,
30 :boards, :messages, :news,
30 :boards, :messages, :news,
31 :attachments, :enumerations
31 :attachments, :enumerations
32
32
33 def setup
33 def setup
34 super
34 super
35 set_tmp_attachments_directory
35 set_tmp_attachments_directory
36 end
36 end
37
37
38 context "#link_to_if_authorized" do
38 context "#link_to_if_authorized" do
39 context "authorized user" do
39 context "authorized user" do
40 should "be tested"
40 should "be tested"
41 end
41 end
42
42
43 context "unauthorized user" do
43 context "unauthorized user" do
44 should "be tested"
44 should "be tested"
45 end
45 end
46
46
47 should "allow using the :controller and :action for the target link" do
47 should "allow using the :controller and :action for the target link" do
48 User.current = User.find_by_login('admin')
48 User.current = User.find_by_login('admin')
49
49
50 @project = Issue.first.project # Used by helper
50 @project = Issue.first.project # Used by helper
51 response = link_to_if_authorized("By controller/action",
51 response = link_to_if_authorized("By controller/action",
52 {:controller => 'issues', :action => 'edit', :id => Issue.first.id})
52 {:controller => 'issues', :action => 'edit', :id => Issue.first.id})
53 assert_match /href/, response
53 assert_match /href/, response
54 end
54 end
55
55
56 end
56 end
57
57
58 def test_auto_links
58 def test_auto_links
59 to_test = {
59 to_test = {
60 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
60 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
61 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
61 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
62 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
62 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
63 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
63 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
64 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
64 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
65 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
65 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
66 '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>.',
66 '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>.',
67 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
67 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
68 '(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>)',
68 '(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>)',
69 '(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>)',
69 '(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>)',
70 '(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>).',
70 '(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>).',
71 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
71 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
72 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
72 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
73 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
73 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
74 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
74 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
75 '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>',
75 '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>',
76 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
76 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
77 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
77 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
78 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
78 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
79 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
79 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
80 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
80 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
81 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
81 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
82 # two exclamation marks
82 # two exclamation marks
83 '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>',
83 '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>',
84 # escaping
84 # escaping
85 'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo&quot;bar</a>',
85 'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo&quot;bar</a>',
86 # wrap in angle brackets
86 # wrap in angle brackets
87 '<http://foo.bar>' => '&lt;<a class="external" href="http://foo.bar">http://foo.bar</a>&gt;',
87 '<http://foo.bar>' => '&lt;<a class="external" href="http://foo.bar">http://foo.bar</a>&gt;',
88 # invalid urls
88 # invalid urls
89 'http://' => 'http://',
89 'http://' => 'http://',
90 'www.' => 'www.',
90 'www.' => 'www.',
91 'test-www.bar.com' => 'test-www.bar.com',
91 }
92 }
92 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
93 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
93 end
94 end
94
95
95 if 'ruby'.respond_to?(:encoding)
96 if 'ruby'.respond_to?(:encoding)
96 def test_auto_links_with_non_ascii_characters
97 def test_auto_links_with_non_ascii_characters
97 to_test = {
98 to_test = {
98 'http://foo.bar/тСст' => '<a class="external" href="http://foo.bar/тСст">http://foo.bar/тСст</a>'
99 'http://foo.bar/тСст' => '<a class="external" href="http://foo.bar/тСст">http://foo.bar/тСст</a>'
99 }
100 }
100 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
101 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
101 end
102 end
102 else
103 else
103 puts 'Skipping test_auto_links_with_non_ascii_characters, unsupported ruby version'
104 puts 'Skipping test_auto_links_with_non_ascii_characters, unsupported ruby version'
104 end
105 end
105
106
106 def test_auto_mailto
107 def test_auto_mailto
107 assert_equal '<p><a class="email" href="mailto:test@foo.bar">test@foo.bar</a></p>',
108 to_test = {
108 textilizable('test@foo.bar')
109 'test@foo.bar' => '<a class="email" href="mailto:test@foo.bar">test@foo.bar</a>',
110 'test@www.foo.bar' => '<a class="email" href="mailto:test@www.foo.bar">test@www.foo.bar</a>',
111 }
112 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
109 end
113 end
110
114
111 def test_inline_images
115 def test_inline_images
112 to_test = {
116 to_test = {
113 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
117 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
114 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
118 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
115 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
119 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
116 'with style !{width:100px;height:100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height:100px;" alt="" />',
120 'with style !{width:100px;height:100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height:100px;" alt="" />',
117 '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" />',
121 '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" />',
118 '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;" />',
122 '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;" />',
119 }
123 }
120 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) }
121 end
125 end
122
126
123 def test_inline_images_inside_tags
127 def test_inline_images_inside_tags
124 raw = <<-RAW
128 raw = <<-RAW
125 h1. !foo.png! Heading
129 h1. !foo.png! Heading
126
130
127 Centered image:
131 Centered image:
128
132
129 p=. !bar.gif!
133 p=. !bar.gif!
130 RAW
134 RAW
131
135
132 assert textilizable(raw).include?('<img src="foo.png" alt="" />')
136 assert textilizable(raw).include?('<img src="foo.png" alt="" />')
133 assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
137 assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
134 end
138 end
135
139
136 def test_attached_images
140 def test_attached_images
137 to_test = {
141 to_test = {
138 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
142 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
139 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
143 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
140 'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
144 'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
141 'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
145 'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
142 # link image
146 # link image
143 '!logo.gif!:http://foo.bar/' => '<a href="http://foo.bar/"><img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" /></a>',
147 '!logo.gif!:http://foo.bar/' => '<a href="http://foo.bar/"><img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" /></a>',
144 }
148 }
145 attachments = Attachment.all
149 attachments = Attachment.all
146 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
150 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
147 end
151 end
148
152
149 def test_attached_images_filename_extension
153 def test_attached_images_filename_extension
150 set_tmp_attachments_directory
154 set_tmp_attachments_directory
151 a1 = Attachment.new(
155 a1 = Attachment.new(
152 :container => Issue.find(1),
156 :container => Issue.find(1),
153 :file => mock_file_with_options({:original_filename => "testtest.JPG"}),
157 :file => mock_file_with_options({:original_filename => "testtest.JPG"}),
154 :author => User.find(1))
158 :author => User.find(1))
155 assert a1.save
159 assert a1.save
156 assert_equal "testtest.JPG", a1.filename
160 assert_equal "testtest.JPG", a1.filename
157 assert_equal "image/jpeg", a1.content_type
161 assert_equal "image/jpeg", a1.content_type
158 assert a1.image?
162 assert a1.image?
159
163
160 a2 = Attachment.new(
164 a2 = Attachment.new(
161 :container => Issue.find(1),
165 :container => Issue.find(1),
162 :file => mock_file_with_options({:original_filename => "testtest.jpeg"}),
166 :file => mock_file_with_options({:original_filename => "testtest.jpeg"}),
163 :author => User.find(1))
167 :author => User.find(1))
164 assert a2.save
168 assert a2.save
165 assert_equal "testtest.jpeg", a2.filename
169 assert_equal "testtest.jpeg", a2.filename
166 assert_equal "image/jpeg", a2.content_type
170 assert_equal "image/jpeg", a2.content_type
167 assert a2.image?
171 assert a2.image?
168
172
169 a3 = Attachment.new(
173 a3 = Attachment.new(
170 :container => Issue.find(1),
174 :container => Issue.find(1),
171 :file => mock_file_with_options({:original_filename => "testtest.JPE"}),
175 :file => mock_file_with_options({:original_filename => "testtest.JPE"}),
172 :author => User.find(1))
176 :author => User.find(1))
173 assert a3.save
177 assert a3.save
174 assert_equal "testtest.JPE", a3.filename
178 assert_equal "testtest.JPE", a3.filename
175 assert_equal "image/jpeg", a3.content_type
179 assert_equal "image/jpeg", a3.content_type
176 assert a3.image?
180 assert a3.image?
177
181
178 a4 = Attachment.new(
182 a4 = Attachment.new(
179 :container => Issue.find(1),
183 :container => Issue.find(1),
180 :file => mock_file_with_options({:original_filename => "Testtest.BMP"}),
184 :file => mock_file_with_options({:original_filename => "Testtest.BMP"}),
181 :author => User.find(1))
185 :author => User.find(1))
182 assert a4.save
186 assert a4.save
183 assert_equal "Testtest.BMP", a4.filename
187 assert_equal "Testtest.BMP", a4.filename
184 assert_equal "image/x-ms-bmp", a4.content_type
188 assert_equal "image/x-ms-bmp", a4.content_type
185 assert a4.image?
189 assert a4.image?
186
190
187 to_test = {
191 to_test = {
188 'Inline image: !testtest.jpg!' =>
192 'Inline image: !testtest.jpg!' =>
189 'Inline image: <img src="/attachments/download/' + a1.id.to_s + '/testtest.JPG" alt="" />',
193 'Inline image: <img src="/attachments/download/' + a1.id.to_s + '/testtest.JPG" alt="" />',
190 'Inline image: !testtest.jpeg!' =>
194 'Inline image: !testtest.jpeg!' =>
191 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testtest.jpeg" alt="" />',
195 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testtest.jpeg" alt="" />',
192 'Inline image: !testtest.jpe!' =>
196 'Inline image: !testtest.jpe!' =>
193 'Inline image: <img src="/attachments/download/' + a3.id.to_s + '/testtest.JPE" alt="" />',
197 'Inline image: <img src="/attachments/download/' + a3.id.to_s + '/testtest.JPE" alt="" />',
194 'Inline image: !testtest.bmp!' =>
198 'Inline image: !testtest.bmp!' =>
195 'Inline image: <img src="/attachments/download/' + a4.id.to_s + '/Testtest.BMP" alt="" />',
199 'Inline image: <img src="/attachments/download/' + a4.id.to_s + '/Testtest.BMP" alt="" />',
196 }
200 }
197
201
198 attachments = [a1, a2, a3, a4]
202 attachments = [a1, a2, a3, a4]
199 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
203 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
200 end
204 end
201
205
202 def test_attached_images_should_read_later
206 def test_attached_images_should_read_later
203 set_fixtures_attachments_directory
207 set_fixtures_attachments_directory
204 a1 = Attachment.find(16)
208 a1 = Attachment.find(16)
205 assert_equal "testfile.png", a1.filename
209 assert_equal "testfile.png", a1.filename
206 assert a1.readable?
210 assert a1.readable?
207 assert (! a1.visible?(User.anonymous))
211 assert (! a1.visible?(User.anonymous))
208 assert a1.visible?(User.find(2))
212 assert a1.visible?(User.find(2))
209 a2 = Attachment.find(17)
213 a2 = Attachment.find(17)
210 assert_equal "testfile.PNG", a2.filename
214 assert_equal "testfile.PNG", a2.filename
211 assert a2.readable?
215 assert a2.readable?
212 assert (! a2.visible?(User.anonymous))
216 assert (! a2.visible?(User.anonymous))
213 assert a2.visible?(User.find(2))
217 assert a2.visible?(User.find(2))
214 assert a1.created_on < a2.created_on
218 assert a1.created_on < a2.created_on
215
219
216 to_test = {
220 to_test = {
217 'Inline image: !testfile.png!' =>
221 'Inline image: !testfile.png!' =>
218 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
222 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
219 'Inline image: !Testfile.PNG!' =>
223 'Inline image: !Testfile.PNG!' =>
220 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
224 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
221 }
225 }
222 attachments = [a1, a2]
226 attachments = [a1, a2]
223 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
227 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
224 set_tmp_attachments_directory
228 set_tmp_attachments_directory
225 end
229 end
226
230
227 def test_textile_external_links
231 def test_textile_external_links
228 to_test = {
232 to_test = {
229 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
233 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
230 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
234 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
231 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
235 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
232 '"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>',
236 '"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>',
233 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
237 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
234 # no multiline link text
238 # no multiline link text
235 "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",
239 "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",
236 # mailto link
240 # mailto link
237 "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
241 "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
238 # two exclamation marks
242 # two exclamation marks
239 '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
243 '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
240 # escaping
244 # escaping
241 '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
245 '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
242 }
246 }
243 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
247 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
244 end
248 end
245
249
246 if 'ruby'.respond_to?(:encoding)
250 if 'ruby'.respond_to?(:encoding)
247 def test_textile_external_links_with_non_ascii_characters
251 def test_textile_external_links_with_non_ascii_characters
248 to_test = {
252 to_test = {
249 'This is a "link":http://foo.bar/тСст' => 'This is a <a href="http://foo.bar/тСст" class="external">link</a>'
253 'This is a "link":http://foo.bar/тСст' => 'This is a <a href="http://foo.bar/тСст" class="external">link</a>'
250 }
254 }
251 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
255 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
252 end
256 end
253 else
257 else
254 puts 'Skipping test_textile_external_links_with_non_ascii_characters, unsupported ruby version'
258 puts 'Skipping test_textile_external_links_with_non_ascii_characters, unsupported ruby version'
255 end
259 end
256
260
257 def test_redmine_links
261 def test_redmine_links
258 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
262 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
259 :class => 'issue status-1 priority-4 priority-lowest overdue', :title => 'Error 281 when updating a recipe (New)')
263 :class => 'issue status-1 priority-4 priority-lowest overdue', :title => 'Error 281 when updating a recipe (New)')
260 note_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'},
264 note_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'},
261 :class => 'issue status-1 priority-4 priority-lowest overdue', :title => 'Error 281 when updating a recipe (New)')
265 :class => 'issue status-1 priority-4 priority-lowest overdue', :title => 'Error 281 when updating a recipe (New)')
262
266
263 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
267 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
264 :class => 'changeset', :title => 'My very first commit')
268 :class => 'changeset', :title => 'My very first commit')
265 changeset_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
269 changeset_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
266 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
270 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
267
271
268 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
272 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
269 :class => 'document')
273 :class => 'document')
270
274
271 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
275 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
272 :class => 'version')
276 :class => 'version')
273
277
274 board_url = {:controller => 'boards', :action => 'show', :id => 2, :project_id => 'ecookbook'}
278 board_url = {:controller => 'boards', :action => 'show', :id => 2, :project_id => 'ecookbook'}
275
279
276 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
280 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
277
281
278 news_url = {:controller => 'news', :action => 'show', :id => 1}
282 news_url = {:controller => 'news', :action => 'show', :id => 1}
279
283
280 project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
284 project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
281
285
282 source_url = '/projects/ecookbook/repository/entry/some/file'
286 source_url = '/projects/ecookbook/repository/entry/some/file'
283 source_url_with_rev = '/projects/ecookbook/repository/revisions/52/entry/some/file'
287 source_url_with_rev = '/projects/ecookbook/repository/revisions/52/entry/some/file'
284 source_url_with_ext = '/projects/ecookbook/repository/entry/some/file.ext'
288 source_url_with_ext = '/projects/ecookbook/repository/entry/some/file.ext'
285 source_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/entry/some/file.ext'
289 source_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/entry/some/file.ext'
286 source_url_with_branch = '/projects/ecookbook/repository/revisions/branch/entry/some/file'
290 source_url_with_branch = '/projects/ecookbook/repository/revisions/branch/entry/some/file'
287
291
288 export_url = '/projects/ecookbook/repository/raw/some/file'
292 export_url = '/projects/ecookbook/repository/raw/some/file'
289 export_url_with_rev = '/projects/ecookbook/repository/revisions/52/raw/some/file'
293 export_url_with_rev = '/projects/ecookbook/repository/revisions/52/raw/some/file'
290 export_url_with_ext = '/projects/ecookbook/repository/raw/some/file.ext'
294 export_url_with_ext = '/projects/ecookbook/repository/raw/some/file.ext'
291 export_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/raw/some/file.ext'
295 export_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/raw/some/file.ext'
292 export_url_with_branch = '/projects/ecookbook/repository/revisions/branch/raw/some/file'
296 export_url_with_branch = '/projects/ecookbook/repository/revisions/branch/raw/some/file'
293
297
294 to_test = {
298 to_test = {
295 # tickets
299 # tickets
296 '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
300 '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
297 # ticket notes
301 # ticket notes
298 '#3-14' => note_link,
302 '#3-14' => note_link,
299 '#3#note-14' => note_link,
303 '#3#note-14' => note_link,
300 # should not ignore leading zero
304 # should not ignore leading zero
301 '#03' => '#03',
305 '#03' => '#03',
302 # changesets
306 # changesets
303 'r1' => changeset_link,
307 'r1' => changeset_link,
304 'r1.' => "#{changeset_link}.",
308 'r1.' => "#{changeset_link}.",
305 'r1, r2' => "#{changeset_link}, #{changeset_link2}",
309 'r1, r2' => "#{changeset_link}, #{changeset_link2}",
306 'r1,r2' => "#{changeset_link},#{changeset_link2}",
310 'r1,r2' => "#{changeset_link},#{changeset_link2}",
307 # documents
311 # documents
308 'document#1' => document_link,
312 'document#1' => document_link,
309 'document:"Test document"' => document_link,
313 'document:"Test document"' => document_link,
310 # versions
314 # versions
311 'version#2' => version_link,
315 'version#2' => version_link,
312 'version:1.0' => version_link,
316 'version:1.0' => version_link,
313 'version:"1.0"' => version_link,
317 'version:"1.0"' => version_link,
314 # source
318 # source
315 'source:some/file' => link_to('source:some/file', source_url, :class => 'source'),
319 'source:some/file' => link_to('source:some/file', source_url, :class => 'source'),
316 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
320 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
317 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
321 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
318 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
322 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
319 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
323 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
320 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
324 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
321 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
325 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
322 'source:/some/file@52' => link_to('source:/some/file@52', source_url_with_rev, :class => 'source'),
326 'source:/some/file@52' => link_to('source:/some/file@52', source_url_with_rev, :class => 'source'),
323 'source:/some/file@branch' => link_to('source:/some/file@branch', source_url_with_branch, :class => 'source'),
327 'source:/some/file@branch' => link_to('source:/some/file@branch', source_url_with_branch, :class => 'source'),
324 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_rev_and_ext, :class => 'source'),
328 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_rev_and_ext, :class => 'source'),
325 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url + "#L110", :class => 'source'),
329 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url + "#L110", :class => 'source'),
326 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext + "#L110", :class => 'source'),
330 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext + "#L110", :class => 'source'),
327 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url_with_rev + "#L110", :class => 'source'),
331 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url_with_rev + "#L110", :class => 'source'),
328 # export
332 # export
329 'export:/some/file' => link_to('export:/some/file', export_url, :class => 'source download'),
333 'export:/some/file' => link_to('export:/some/file', export_url, :class => 'source download'),
330 'export:/some/file.ext' => link_to('export:/some/file.ext', export_url_with_ext, :class => 'source download'),
334 'export:/some/file.ext' => link_to('export:/some/file.ext', export_url_with_ext, :class => 'source download'),
331 'export:/some/file@52' => link_to('export:/some/file@52', export_url_with_rev, :class => 'source download'),
335 'export:/some/file@52' => link_to('export:/some/file@52', export_url_with_rev, :class => 'source download'),
332 'export:/some/file.ext@52' => link_to('export:/some/file.ext@52', export_url_with_rev_and_ext, :class => 'source download'),
336 'export:/some/file.ext@52' => link_to('export:/some/file.ext@52', export_url_with_rev_and_ext, :class => 'source download'),
333 'export:/some/file@branch' => link_to('export:/some/file@branch', export_url_with_branch, :class => 'source download'),
337 'export:/some/file@branch' => link_to('export:/some/file@branch', export_url_with_branch, :class => 'source download'),
334 # forum
338 # forum
335 'forum#2' => link_to('Discussion', board_url, :class => 'board'),
339 'forum#2' => link_to('Discussion', board_url, :class => 'board'),
336 'forum:Discussion' => link_to('Discussion', board_url, :class => 'board'),
340 'forum:Discussion' => link_to('Discussion', board_url, :class => 'board'),
337 # message
341 # message
338 'message#4' => link_to('Post 2', message_url, :class => 'message'),
342 'message#4' => link_to('Post 2', message_url, :class => 'message'),
339 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'),
343 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'),
340 # news
344 # news
341 'news#1' => link_to('eCookbook first release !', news_url, :class => 'news'),
345 'news#1' => link_to('eCookbook first release !', news_url, :class => 'news'),
342 'news:"eCookbook first release !"' => link_to('eCookbook first release !', news_url, :class => 'news'),
346 'news:"eCookbook first release !"' => link_to('eCookbook first release !', news_url, :class => 'news'),
343 # project
347 # project
344 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
348 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
345 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
349 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
346 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
350 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
347 # not found
351 # not found
348 '#0123456789' => '#0123456789',
352 '#0123456789' => '#0123456789',
349 # invalid expressions
353 # invalid expressions
350 'source:' => 'source:',
354 'source:' => 'source:',
351 # url hash
355 # url hash
352 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
356 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
353 }
357 }
354 @project = Project.find(1)
358 @project = Project.find(1)
355 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
359 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
356 end
360 end
357
361
358 def test_redmine_links_with_a_different_project_before_current_project
362 def test_redmine_links_with_a_different_project_before_current_project
359 vp1 = Version.generate!(:project_id => 1, :name => '1.4.4')
363 vp1 = Version.generate!(:project_id => 1, :name => '1.4.4')
360 vp3 = Version.generate!(:project_id => 3, :name => '1.4.4')
364 vp3 = Version.generate!(:project_id => 3, :name => '1.4.4')
361
365
362 @project = Project.find(3)
366 @project = Project.find(3)
363 assert_equal %(<p><a href="/versions/#{vp1.id}" class="version">1.4.4</a> <a href="/versions/#{vp3.id}" class="version">1.4.4</a></p>),
367 assert_equal %(<p><a href="/versions/#{vp1.id}" class="version">1.4.4</a> <a href="/versions/#{vp3.id}" class="version">1.4.4</a></p>),
364 textilizable("ecookbook:version:1.4.4 version:1.4.4")
368 textilizable("ecookbook:version:1.4.4 version:1.4.4")
365 end
369 end
366
370
367 def test_escaped_redmine_links_should_not_be_parsed
371 def test_escaped_redmine_links_should_not_be_parsed
368 to_test = [
372 to_test = [
369 '#3.',
373 '#3.',
370 '#3-14.',
374 '#3-14.',
371 '#3#-note14.',
375 '#3#-note14.',
372 'r1',
376 'r1',
373 'document#1',
377 'document#1',
374 'document:"Test document"',
378 'document:"Test document"',
375 'version#2',
379 'version#2',
376 'version:1.0',
380 'version:1.0',
377 'version:"1.0"',
381 'version:"1.0"',
378 'source:/some/file'
382 'source:/some/file'
379 ]
383 ]
380 @project = Project.find(1)
384 @project = Project.find(1)
381 to_test.each { |text| assert_equal "<p>#{text}</p>", textilizable("!" + text), "#{text} failed" }
385 to_test.each { |text| assert_equal "<p>#{text}</p>", textilizable("!" + text), "#{text} failed" }
382 end
386 end
383
387
384 def test_cross_project_redmine_links
388 def test_cross_project_redmine_links
385 source_link = link_to('ecookbook:source:/some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']},
389 source_link = link_to('ecookbook:source:/some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']},
386 :class => 'source')
390 :class => 'source')
387
391
388 changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
392 changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
389 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
393 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
390
394
391 to_test = {
395 to_test = {
392 # documents
396 # documents
393 'document:"Test document"' => 'document:"Test document"',
397 'document:"Test document"' => 'document:"Test document"',
394 'ecookbook:document:"Test document"' => '<a href="/documents/1" class="document">Test document</a>',
398 'ecookbook:document:"Test document"' => '<a href="/documents/1" class="document">Test document</a>',
395 'invalid:document:"Test document"' => 'invalid:document:"Test document"',
399 'invalid:document:"Test document"' => 'invalid:document:"Test document"',
396 # versions
400 # versions
397 'version:"1.0"' => 'version:"1.0"',
401 'version:"1.0"' => 'version:"1.0"',
398 'ecookbook:version:"1.0"' => '<a href="/versions/2" class="version">1.0</a>',
402 'ecookbook:version:"1.0"' => '<a href="/versions/2" class="version">1.0</a>',
399 'invalid:version:"1.0"' => 'invalid:version:"1.0"',
403 'invalid:version:"1.0"' => 'invalid:version:"1.0"',
400 # changeset
404 # changeset
401 'r2' => 'r2',
405 'r2' => 'r2',
402 'ecookbook:r2' => changeset_link,
406 'ecookbook:r2' => changeset_link,
403 'invalid:r2' => 'invalid:r2',
407 'invalid:r2' => 'invalid:r2',
404 # source
408 # source
405 'source:/some/file' => 'source:/some/file',
409 'source:/some/file' => 'source:/some/file',
406 'ecookbook:source:/some/file' => source_link,
410 'ecookbook:source:/some/file' => source_link,
407 'invalid:source:/some/file' => 'invalid:source:/some/file',
411 'invalid:source:/some/file' => 'invalid:source:/some/file',
408 }
412 }
409 @project = Project.find(3)
413 @project = Project.find(3)
410 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
414 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
411 end
415 end
412
416
413 def test_multiple_repositories_redmine_links
417 def test_multiple_repositories_redmine_links
414 svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn_repo-1', :url => 'file:///foo/hg')
418 svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn_repo-1', :url => 'file:///foo/hg')
415 Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
419 Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
416 hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
420 hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
417 Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
421 Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
418
422
419 changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
423 changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
420 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
424 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
421 svn_changeset_link = link_to('svn_repo-1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn_repo-1', :rev => 123},
425 svn_changeset_link = link_to('svn_repo-1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn_repo-1', :rev => 123},
422 :class => 'changeset', :title => '')
426 :class => 'changeset', :title => '')
423 hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
427 hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
424 :class => 'changeset', :title => '')
428 :class => 'changeset', :title => '')
425
429
426 source_link = link_to('source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
430 source_link = link_to('source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
427 hg_source_link = link_to('source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
431 hg_source_link = link_to('source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
428
432
429 to_test = {
433 to_test = {
430 'r2' => changeset_link,
434 'r2' => changeset_link,
431 'svn_repo-1|r123' => svn_changeset_link,
435 'svn_repo-1|r123' => svn_changeset_link,
432 'invalid|r123' => 'invalid|r123',
436 'invalid|r123' => 'invalid|r123',
433 'commit:hg1|abcd' => hg_changeset_link,
437 'commit:hg1|abcd' => hg_changeset_link,
434 'commit:invalid|abcd' => 'commit:invalid|abcd',
438 'commit:invalid|abcd' => 'commit:invalid|abcd',
435 # source
439 # source
436 'source:some/file' => source_link,
440 'source:some/file' => source_link,
437 'source:hg1|some/file' => hg_source_link,
441 'source:hg1|some/file' => hg_source_link,
438 'source:invalid|some/file' => 'source:invalid|some/file',
442 'source:invalid|some/file' => 'source:invalid|some/file',
439 }
443 }
440
444
441 @project = Project.find(1)
445 @project = Project.find(1)
442 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
446 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
443 end
447 end
444
448
445 def test_cross_project_multiple_repositories_redmine_links
449 def test_cross_project_multiple_repositories_redmine_links
446 svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg')
450 svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg')
447 Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
451 Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
448 hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
452 hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
449 Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
453 Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
450
454
451 changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
455 changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
452 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
456 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
453 svn_changeset_link = link_to('ecookbook:svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123},
457 svn_changeset_link = link_to('ecookbook:svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123},
454 :class => 'changeset', :title => '')
458 :class => 'changeset', :title => '')
455 hg_changeset_link = link_to('ecookbook:hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
459 hg_changeset_link = link_to('ecookbook:hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
456 :class => 'changeset', :title => '')
460 :class => 'changeset', :title => '')
457
461
458 source_link = link_to('ecookbook:source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
462 source_link = link_to('ecookbook:source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
459 hg_source_link = link_to('ecookbook:source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
463 hg_source_link = link_to('ecookbook:source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
460
464
461 to_test = {
465 to_test = {
462 'ecookbook:r2' => changeset_link,
466 'ecookbook:r2' => changeset_link,
463 'ecookbook:svn1|r123' => svn_changeset_link,
467 'ecookbook:svn1|r123' => svn_changeset_link,
464 'ecookbook:invalid|r123' => 'ecookbook:invalid|r123',
468 'ecookbook:invalid|r123' => 'ecookbook:invalid|r123',
465 'ecookbook:commit:hg1|abcd' => hg_changeset_link,
469 'ecookbook:commit:hg1|abcd' => hg_changeset_link,
466 'ecookbook:commit:invalid|abcd' => 'ecookbook:commit:invalid|abcd',
470 'ecookbook:commit:invalid|abcd' => 'ecookbook:commit:invalid|abcd',
467 'invalid:commit:invalid|abcd' => 'invalid:commit:invalid|abcd',
471 'invalid:commit:invalid|abcd' => 'invalid:commit:invalid|abcd',
468 # source
472 # source
469 'ecookbook:source:some/file' => source_link,
473 'ecookbook:source:some/file' => source_link,
470 'ecookbook:source:hg1|some/file' => hg_source_link,
474 'ecookbook:source:hg1|some/file' => hg_source_link,
471 'ecookbook:source:invalid|some/file' => 'ecookbook:source:invalid|some/file',
475 'ecookbook:source:invalid|some/file' => 'ecookbook:source:invalid|some/file',
472 'invalid:source:invalid|some/file' => 'invalid:source:invalid|some/file',
476 'invalid:source:invalid|some/file' => 'invalid:source:invalid|some/file',
473 }
477 }
474
478
475 @project = Project.find(3)
479 @project = Project.find(3)
476 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
480 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
477 end
481 end
478
482
479 def test_redmine_links_git_commit
483 def test_redmine_links_git_commit
480 changeset_link = link_to('abcd',
484 changeset_link = link_to('abcd',
481 {
485 {
482 :controller => 'repositories',
486 :controller => 'repositories',
483 :action => 'revision',
487 :action => 'revision',
484 :id => 'subproject1',
488 :id => 'subproject1',
485 :rev => 'abcd',
489 :rev => 'abcd',
486 },
490 },
487 :class => 'changeset', :title => 'test commit')
491 :class => 'changeset', :title => 'test commit')
488 to_test = {
492 to_test = {
489 'commit:abcd' => changeset_link,
493 'commit:abcd' => changeset_link,
490 }
494 }
491 @project = Project.find(3)
495 @project = Project.find(3)
492 r = Repository::Git.create!(:project => @project, :url => '/tmp/test/git')
496 r = Repository::Git.create!(:project => @project, :url => '/tmp/test/git')
493 assert r
497 assert r
494 c = Changeset.new(:repository => r,
498 c = Changeset.new(:repository => r,
495 :committed_on => Time.now,
499 :committed_on => Time.now,
496 :revision => 'abcd',
500 :revision => 'abcd',
497 :scmid => 'abcd',
501 :scmid => 'abcd',
498 :comments => 'test commit')
502 :comments => 'test commit')
499 assert( c.save )
503 assert( c.save )
500 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
504 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
501 end
505 end
502
506
503 # TODO: Bazaar commit id contains mail address, so it contains '@' and '_'.
507 # TODO: Bazaar commit id contains mail address, so it contains '@' and '_'.
504 def test_redmine_links_darcs_commit
508 def test_redmine_links_darcs_commit
505 changeset_link = link_to('20080308225258-98289-abcd456efg.gz',
509 changeset_link = link_to('20080308225258-98289-abcd456efg.gz',
506 {
510 {
507 :controller => 'repositories',
511 :controller => 'repositories',
508 :action => 'revision',
512 :action => 'revision',
509 :id => 'subproject1',
513 :id => 'subproject1',
510 :rev => '123',
514 :rev => '123',
511 },
515 },
512 :class => 'changeset', :title => 'test commit')
516 :class => 'changeset', :title => 'test commit')
513 to_test = {
517 to_test = {
514 'commit:20080308225258-98289-abcd456efg.gz' => changeset_link,
518 'commit:20080308225258-98289-abcd456efg.gz' => changeset_link,
515 }
519 }
516 @project = Project.find(3)
520 @project = Project.find(3)
517 r = Repository::Darcs.create!(
521 r = Repository::Darcs.create!(
518 :project => @project, :url => '/tmp/test/darcs',
522 :project => @project, :url => '/tmp/test/darcs',
519 :log_encoding => 'UTF-8')
523 :log_encoding => 'UTF-8')
520 assert r
524 assert r
521 c = Changeset.new(:repository => r,
525 c = Changeset.new(:repository => r,
522 :committed_on => Time.now,
526 :committed_on => Time.now,
523 :revision => '123',
527 :revision => '123',
524 :scmid => '20080308225258-98289-abcd456efg.gz',
528 :scmid => '20080308225258-98289-abcd456efg.gz',
525 :comments => 'test commit')
529 :comments => 'test commit')
526 assert( c.save )
530 assert( c.save )
527 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
531 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
528 end
532 end
529
533
530 def test_redmine_links_mercurial_commit
534 def test_redmine_links_mercurial_commit
531 changeset_link_rev = link_to('r123',
535 changeset_link_rev = link_to('r123',
532 {
536 {
533 :controller => 'repositories',
537 :controller => 'repositories',
534 :action => 'revision',
538 :action => 'revision',
535 :id => 'subproject1',
539 :id => 'subproject1',
536 :rev => '123' ,
540 :rev => '123' ,
537 },
541 },
538 :class => 'changeset', :title => 'test commit')
542 :class => 'changeset', :title => 'test commit')
539 changeset_link_commit = link_to('abcd',
543 changeset_link_commit = link_to('abcd',
540 {
544 {
541 :controller => 'repositories',
545 :controller => 'repositories',
542 :action => 'revision',
546 :action => 'revision',
543 :id => 'subproject1',
547 :id => 'subproject1',
544 :rev => 'abcd' ,
548 :rev => 'abcd' ,
545 },
549 },
546 :class => 'changeset', :title => 'test commit')
550 :class => 'changeset', :title => 'test commit')
547 to_test = {
551 to_test = {
548 'r123' => changeset_link_rev,
552 'r123' => changeset_link_rev,
549 'commit:abcd' => changeset_link_commit,
553 'commit:abcd' => changeset_link_commit,
550 }
554 }
551 @project = Project.find(3)
555 @project = Project.find(3)
552 r = Repository::Mercurial.create!(:project => @project, :url => '/tmp/test')
556 r = Repository::Mercurial.create!(:project => @project, :url => '/tmp/test')
553 assert r
557 assert r
554 c = Changeset.new(:repository => r,
558 c = Changeset.new(:repository => r,
555 :committed_on => Time.now,
559 :committed_on => Time.now,
556 :revision => '123',
560 :revision => '123',
557 :scmid => 'abcd',
561 :scmid => 'abcd',
558 :comments => 'test commit')
562 :comments => 'test commit')
559 assert( c.save )
563 assert( c.save )
560 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
564 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
561 end
565 end
562
566
563 def test_attachment_links
567 def test_attachment_links
564 to_test = {
568 to_test = {
565 'attachment:error281.txt' => '<a href="/attachments/download/1/error281.txt" class="attachment">error281.txt</a>'
569 'attachment:error281.txt' => '<a href="/attachments/download/1/error281.txt" class="attachment">error281.txt</a>'
566 }
570 }
567 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => Issue.find(3).attachments), "#{text} failed" }
571 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => Issue.find(3).attachments), "#{text} failed" }
568 end
572 end
569
573
570 def test_attachment_link_should_link_to_latest_attachment
574 def test_attachment_link_should_link_to_latest_attachment
571 set_tmp_attachments_directory
575 set_tmp_attachments_directory
572 a1 = Attachment.generate!(:filename => "test.txt", :created_on => 1.hour.ago)
576 a1 = Attachment.generate!(:filename => "test.txt", :created_on => 1.hour.ago)
573 a2 = Attachment.generate!(:filename => "test.txt")
577 a2 = Attachment.generate!(:filename => "test.txt")
574
578
575 assert_equal %(<p><a href="/attachments/download/#{a2.id}/test.txt" class="attachment">test.txt</a></p>),
579 assert_equal %(<p><a href="/attachments/download/#{a2.id}/test.txt" class="attachment">test.txt</a></p>),
576 textilizable('attachment:test.txt', :attachments => [a1, a2])
580 textilizable('attachment:test.txt', :attachments => [a1, a2])
577 end
581 end
578
582
579 def test_wiki_links
583 def test_wiki_links
580 to_test = {
584 to_test = {
581 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
585 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
582 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
586 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
583 # title content should be formatted
587 # title content should be formatted
584 '[[Another page|With _styled_ *title*]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">With <em>styled</em> <strong>title</strong></a>',
588 '[[Another page|With _styled_ *title*]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">With <em>styled</em> <strong>title</strong></a>',
585 '[[Another page|With title containing <strong>HTML entities &amp; markups</strong>]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">With title containing &lt;strong&gt;HTML entities &amp; markups&lt;/strong&gt;</a>',
589 '[[Another page|With title containing <strong>HTML entities &amp; markups</strong>]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">With title containing &lt;strong&gt;HTML entities &amp; markups&lt;/strong&gt;</a>',
586 # link with anchor
590 # link with anchor
587 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
591 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
588 '[[Another page#anchor|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page#anchor" class="wiki-page">Page</a>',
592 '[[Another page#anchor|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page#anchor" class="wiki-page">Page</a>',
589 # UTF8 anchor
593 # UTF8 anchor
590 '[[Another_page#ВСст|ВСст]]' => %|<a href="/projects/ecookbook/wiki/Another_page##{CGI.escape 'ВСст'}" class="wiki-page">ВСст</a>|,
594 '[[Another_page#ВСст|ВСст]]' => %|<a href="/projects/ecookbook/wiki/Another_page##{CGI.escape 'ВСст'}" class="wiki-page">ВСст</a>|,
591 # page that doesn't exist
595 # page that doesn't exist
592 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
596 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
593 '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">404</a>',
597 '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">404</a>',
594 # link to another project wiki
598 # link to another project wiki
595 '[[onlinestore:]]' => '<a href="/projects/onlinestore/wiki" class="wiki-page">onlinestore</a>',
599 '[[onlinestore:]]' => '<a href="/projects/onlinestore/wiki" class="wiki-page">onlinestore</a>',
596 '[[onlinestore:|Wiki]]' => '<a href="/projects/onlinestore/wiki" class="wiki-page">Wiki</a>',
600 '[[onlinestore:|Wiki]]' => '<a href="/projects/onlinestore/wiki" class="wiki-page">Wiki</a>',
597 '[[onlinestore:Start page]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Start page</a>',
601 '[[onlinestore:Start page]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Start page</a>',
598 '[[onlinestore:Start page|Text]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Text</a>',
602 '[[onlinestore:Start page|Text]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Text</a>',
599 '[[onlinestore:Unknown page]]' => '<a href="/projects/onlinestore/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
603 '[[onlinestore:Unknown page]]' => '<a href="/projects/onlinestore/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
600 # striked through link
604 # striked through link
601 '-[[Another page|Page]]-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a></del>',
605 '-[[Another page|Page]]-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a></del>',
602 '-[[Another page|Page]] link-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a> link</del>',
606 '-[[Another page|Page]] link-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a> link</del>',
603 # escaping
607 # escaping
604 '![[Another page|Page]]' => '[[Another page|Page]]',
608 '![[Another page|Page]]' => '[[Another page|Page]]',
605 # project does not exist
609 # project does not exist
606 '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
610 '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
607 '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
611 '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
608 }
612 }
609
613
610 @project = Project.find(1)
614 @project = Project.find(1)
611 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
615 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
612 end
616 end
613
617
614 def test_wiki_links_within_local_file_generation_context
618 def test_wiki_links_within_local_file_generation_context
615
619
616 to_test = {
620 to_test = {
617 # link to a page
621 # link to a page
618 '[[CookBook documentation]]' => '<a href="CookBook_documentation.html" class="wiki-page">CookBook documentation</a>',
622 '[[CookBook documentation]]' => '<a href="CookBook_documentation.html" class="wiki-page">CookBook documentation</a>',
619 '[[CookBook documentation|documentation]]' => '<a href="CookBook_documentation.html" class="wiki-page">documentation</a>',
623 '[[CookBook documentation|documentation]]' => '<a href="CookBook_documentation.html" class="wiki-page">documentation</a>',
620 '[[CookBook documentation#One-section]]' => '<a href="CookBook_documentation.html#One-section" class="wiki-page">CookBook documentation</a>',
624 '[[CookBook documentation#One-section]]' => '<a href="CookBook_documentation.html#One-section" class="wiki-page">CookBook documentation</a>',
621 '[[CookBook documentation#One-section|documentation]]' => '<a href="CookBook_documentation.html#One-section" class="wiki-page">documentation</a>',
625 '[[CookBook documentation#One-section|documentation]]' => '<a href="CookBook_documentation.html#One-section" class="wiki-page">documentation</a>',
622 # page that doesn't exist
626 # page that doesn't exist
623 '[[Unknown page]]' => '<a href="Unknown_page.html" class="wiki-page new">Unknown page</a>',
627 '[[Unknown page]]' => '<a href="Unknown_page.html" class="wiki-page new">Unknown page</a>',
624 '[[Unknown page|404]]' => '<a href="Unknown_page.html" class="wiki-page new">404</a>',
628 '[[Unknown page|404]]' => '<a href="Unknown_page.html" class="wiki-page new">404</a>',
625 '[[Unknown page#anchor]]' => '<a href="Unknown_page.html#anchor" class="wiki-page new">Unknown page</a>',
629 '[[Unknown page#anchor]]' => '<a href="Unknown_page.html#anchor" class="wiki-page new">Unknown page</a>',
626 '[[Unknown page#anchor|404]]' => '<a href="Unknown_page.html#anchor" class="wiki-page new">404</a>',
630 '[[Unknown page#anchor|404]]' => '<a href="Unknown_page.html#anchor" class="wiki-page new">404</a>',
627 }
631 }
628
632
629 @project = Project.find(1)
633 @project = Project.find(1)
630
634
631 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :local) }
635 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :local) }
632 end
636 end
633
637
634 def test_wiki_links_within_wiki_page_context
638 def test_wiki_links_within_wiki_page_context
635
639
636 page = WikiPage.find_by_title('Another_page' )
640 page = WikiPage.find_by_title('Another_page' )
637
641
638 to_test = {
642 to_test = {
639 # link to another page
643 # link to another page
640 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
644 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
641 '[[CookBook documentation|documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">documentation</a>',
645 '[[CookBook documentation|documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">documentation</a>',
642 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
646 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
643 '[[CookBook documentation#One-section|documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">documentation</a>',
647 '[[CookBook documentation#One-section|documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">documentation</a>',
644 # link to the current page
648 # link to the current page
645 '[[Another page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Another page</a>',
649 '[[Another page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Another page</a>',
646 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
650 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
647 '[[Another page#anchor]]' => '<a href="#anchor" class="wiki-page">Another page</a>',
651 '[[Another page#anchor]]' => '<a href="#anchor" class="wiki-page">Another page</a>',
648 '[[Another page#anchor|Page]]' => '<a href="#anchor" class="wiki-page">Page</a>',
652 '[[Another page#anchor|Page]]' => '<a href="#anchor" class="wiki-page">Page</a>',
649 # page that doesn't exist
653 # page that doesn't exist
650 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page" class="wiki-page new">Unknown page</a>',
654 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page" class="wiki-page new">Unknown page</a>',
651 '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page" class="wiki-page new">404</a>',
655 '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page" class="wiki-page new">404</a>',
652 '[[Unknown page#anchor]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor" class="wiki-page new">Unknown page</a>',
656 '[[Unknown page#anchor]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor" class="wiki-page new">Unknown page</a>',
653 '[[Unknown page#anchor|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor" class="wiki-page new">404</a>',
657 '[[Unknown page#anchor|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor" class="wiki-page new">404</a>',
654 }
658 }
655
659
656 @project = Project.find(1)
660 @project = Project.find(1)
657
661
658 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(WikiContent.new( :text => text, :page => page ), :text) }
662 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(WikiContent.new( :text => text, :page => page ), :text) }
659 end
663 end
660
664
661 def test_wiki_links_anchor_option_should_prepend_page_title_to_href
665 def test_wiki_links_anchor_option_should_prepend_page_title_to_href
662
666
663 to_test = {
667 to_test = {
664 # link to a page
668 # link to a page
665 '[[CookBook documentation]]' => '<a href="#CookBook_documentation" class="wiki-page">CookBook documentation</a>',
669 '[[CookBook documentation]]' => '<a href="#CookBook_documentation" class="wiki-page">CookBook documentation</a>',
666 '[[CookBook documentation|documentation]]' => '<a href="#CookBook_documentation" class="wiki-page">documentation</a>',
670 '[[CookBook documentation|documentation]]' => '<a href="#CookBook_documentation" class="wiki-page">documentation</a>',
667 '[[CookBook documentation#One-section]]' => '<a href="#CookBook_documentation_One-section" class="wiki-page">CookBook documentation</a>',
671 '[[CookBook documentation#One-section]]' => '<a href="#CookBook_documentation_One-section" class="wiki-page">CookBook documentation</a>',
668 '[[CookBook documentation#One-section|documentation]]' => '<a href="#CookBook_documentation_One-section" class="wiki-page">documentation</a>',
672 '[[CookBook documentation#One-section|documentation]]' => '<a href="#CookBook_documentation_One-section" class="wiki-page">documentation</a>',
669 # page that doesn't exist
673 # page that doesn't exist
670 '[[Unknown page]]' => '<a href="#Unknown_page" class="wiki-page new">Unknown page</a>',
674 '[[Unknown page]]' => '<a href="#Unknown_page" class="wiki-page new">Unknown page</a>',
671 '[[Unknown page|404]]' => '<a href="#Unknown_page" class="wiki-page new">404</a>',
675 '[[Unknown page|404]]' => '<a href="#Unknown_page" class="wiki-page new">404</a>',
672 '[[Unknown page#anchor]]' => '<a href="#Unknown_page_anchor" class="wiki-page new">Unknown page</a>',
676 '[[Unknown page#anchor]]' => '<a href="#Unknown_page_anchor" class="wiki-page new">Unknown page</a>',
673 '[[Unknown page#anchor|404]]' => '<a href="#Unknown_page_anchor" class="wiki-page new">404</a>',
677 '[[Unknown page#anchor|404]]' => '<a href="#Unknown_page_anchor" class="wiki-page new">404</a>',
674 }
678 }
675
679
676 @project = Project.find(1)
680 @project = Project.find(1)
677
681
678 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :anchor) }
682 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :anchor) }
679 end
683 end
680
684
681 def test_html_tags
685 def test_html_tags
682 to_test = {
686 to_test = {
683 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
687 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
684 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
688 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
685 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
689 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
686 # do not escape pre/code tags
690 # do not escape pre/code tags
687 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
691 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
688 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
692 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
689 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
693 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
690 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
694 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
691 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
695 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
692 # remove attributes except class
696 # remove attributes except class
693 "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
697 "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
694 '<pre class="foo">some text</pre>' => '<pre class="foo">some text</pre>',
698 '<pre class="foo">some text</pre>' => '<pre class="foo">some text</pre>',
695 "<pre class='foo bar'>some text</pre>" => "<pre class='foo bar'>some text</pre>",
699 "<pre class='foo bar'>some text</pre>" => "<pre class='foo bar'>some text</pre>",
696 '<pre class="foo bar">some text</pre>' => '<pre class="foo bar">some text</pre>',
700 '<pre class="foo bar">some text</pre>' => '<pre class="foo bar">some text</pre>',
697 "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
701 "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
698 # xss
702 # xss
699 '<pre><code class=""onmouseover="alert(1)">text</code></pre>' => '<pre><code>text</code></pre>',
703 '<pre><code class=""onmouseover="alert(1)">text</code></pre>' => '<pre><code>text</code></pre>',
700 '<pre class=""onmouseover="alert(1)">text</pre>' => '<pre>text</pre>',
704 '<pre class=""onmouseover="alert(1)">text</pre>' => '<pre>text</pre>',
701 }
705 }
702 to_test.each { |text, result| assert_equal result, textilizable(text) }
706 to_test.each { |text, result| assert_equal result, textilizable(text) }
703 end
707 end
704
708
705 def test_allowed_html_tags
709 def test_allowed_html_tags
706 to_test = {
710 to_test = {
707 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
711 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
708 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
712 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
709 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
713 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
710 }
714 }
711 to_test.each { |text, result| assert_equal result, textilizable(text) }
715 to_test.each { |text, result| assert_equal result, textilizable(text) }
712 end
716 end
713
717
714 def test_pre_tags
718 def test_pre_tags
715 raw = <<-RAW
719 raw = <<-RAW
716 Before
720 Before
717
721
718 <pre>
722 <pre>
719 <prepared-statement-cache-size>32</prepared-statement-cache-size>
723 <prepared-statement-cache-size>32</prepared-statement-cache-size>
720 </pre>
724 </pre>
721
725
722 After
726 After
723 RAW
727 RAW
724
728
725 expected = <<-EXPECTED
729 expected = <<-EXPECTED
726 <p>Before</p>
730 <p>Before</p>
727 <pre>
731 <pre>
728 &lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
732 &lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
729 </pre>
733 </pre>
730 <p>After</p>
734 <p>After</p>
731 EXPECTED
735 EXPECTED
732
736
733 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
737 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
734 end
738 end
735
739
736 def test_pre_content_should_not_parse_wiki_and_redmine_links
740 def test_pre_content_should_not_parse_wiki_and_redmine_links
737 raw = <<-RAW
741 raw = <<-RAW
738 [[CookBook documentation]]
742 [[CookBook documentation]]
739
743
740 #1
744 #1
741
745
742 <pre>
746 <pre>
743 [[CookBook documentation]]
747 [[CookBook documentation]]
744
748
745 #1
749 #1
746 </pre>
750 </pre>
747 RAW
751 RAW
748
752
749 expected = <<-EXPECTED
753 expected = <<-EXPECTED
750 <p><a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a></p>
754 <p><a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a></p>
751 <p><a href="/issues/1" class="issue status-1 priority-4 priority-lowest" title="Can&#x27;t print recipes (New)">#1</a></p>
755 <p><a href="/issues/1" class="issue status-1 priority-4 priority-lowest" title="Can&#x27;t print recipes (New)">#1</a></p>
752 <pre>
756 <pre>
753 [[CookBook documentation]]
757 [[CookBook documentation]]
754
758
755 #1
759 #1
756 </pre>
760 </pre>
757 EXPECTED
761 EXPECTED
758
762
759 @project = Project.find(1)
763 @project = Project.find(1)
760 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
764 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
761 end
765 end
762
766
763 def test_non_closing_pre_blocks_should_be_closed
767 def test_non_closing_pre_blocks_should_be_closed
764 raw = <<-RAW
768 raw = <<-RAW
765 <pre><code>
769 <pre><code>
766 RAW
770 RAW
767
771
768 expected = <<-EXPECTED
772 expected = <<-EXPECTED
769 <pre><code>
773 <pre><code>
770 </code></pre>
774 </code></pre>
771 EXPECTED
775 EXPECTED
772
776
773 @project = Project.find(1)
777 @project = Project.find(1)
774 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
778 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
775 end
779 end
776
780
777 def test_syntax_highlight
781 def test_syntax_highlight
778 raw = <<-RAW
782 raw = <<-RAW
779 <pre><code class="ruby">
783 <pre><code class="ruby">
780 # Some ruby code here
784 # Some ruby code here
781 </code></pre>
785 </code></pre>
782 RAW
786 RAW
783
787
784 expected = <<-EXPECTED
788 expected = <<-EXPECTED
785 <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="comment"># Some ruby code here</span></span>
789 <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="comment"># Some ruby code here</span></span>
786 </code></pre>
790 </code></pre>
787 EXPECTED
791 EXPECTED
788
792
789 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
793 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
790 end
794 end
791
795
792 def test_to_path_param
796 def test_to_path_param
793 assert_equal 'test1/test2', to_path_param('test1/test2')
797 assert_equal 'test1/test2', to_path_param('test1/test2')
794 assert_equal 'test1/test2', to_path_param('/test1/test2/')
798 assert_equal 'test1/test2', to_path_param('/test1/test2/')
795 assert_equal 'test1/test2', to_path_param('//test1/test2/')
799 assert_equal 'test1/test2', to_path_param('//test1/test2/')
796 assert_equal nil, to_path_param('/')
800 assert_equal nil, to_path_param('/')
797 end
801 end
798
802
799 def test_wiki_links_in_tables
803 def test_wiki_links_in_tables
800 to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
804 to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
801 '<tr><td><a href="/projects/ecookbook/wiki/Page" class="wiki-page new">Link title</a></td>' +
805 '<tr><td><a href="/projects/ecookbook/wiki/Page" class="wiki-page new">Link title</a></td>' +
802 '<td><a href="/projects/ecookbook/wiki/Other_Page" class="wiki-page new">Other title</a></td>' +
806 '<td><a href="/projects/ecookbook/wiki/Other_Page" class="wiki-page new">Other title</a></td>' +
803 '</tr><tr><td>Cell 21</td><td><a href="/projects/ecookbook/wiki/Last_page" class="wiki-page new">Last page</a></td></tr>'
807 '</tr><tr><td>Cell 21</td><td><a href="/projects/ecookbook/wiki/Last_page" class="wiki-page new">Last page</a></td></tr>'
804 }
808 }
805 @project = Project.find(1)
809 @project = Project.find(1)
806 to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
810 to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
807 end
811 end
808
812
809 def test_text_formatting
813 def test_text_formatting
810 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
814 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
811 '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
815 '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
812 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
816 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
813 '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>',
817 '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>',
814 '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',
818 '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',
815 }
819 }
816 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
820 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
817 end
821 end
818
822
819 def test_wiki_horizontal_rule
823 def test_wiki_horizontal_rule
820 assert_equal '<hr />', textilizable('---')
824 assert_equal '<hr />', textilizable('---')
821 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
825 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
822 end
826 end
823
827
824 def test_footnotes
828 def test_footnotes
825 raw = <<-RAW
829 raw = <<-RAW
826 This is some text[1].
830 This is some text[1].
827
831
828 fn1. This is the foot note
832 fn1. This is the foot note
829 RAW
833 RAW
830
834
831 expected = <<-EXPECTED
835 expected = <<-EXPECTED
832 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
836 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
833 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
837 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
834 EXPECTED
838 EXPECTED
835
839
836 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
840 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
837 end
841 end
838
842
839 def test_headings
843 def test_headings
840 raw = 'h1. Some heading'
844 raw = 'h1. Some heading'
841 expected = %|<a name="Some-heading"></a>\n<h1 >Some heading<a href="#Some-heading" class="wiki-anchor">&para;</a></h1>|
845 expected = %|<a name="Some-heading"></a>\n<h1 >Some heading<a href="#Some-heading" class="wiki-anchor">&para;</a></h1>|
842
846
843 assert_equal expected, textilizable(raw)
847 assert_equal expected, textilizable(raw)
844 end
848 end
845
849
846 def test_headings_with_special_chars
850 def test_headings_with_special_chars
847 # This test makes sure that the generated anchor names match the expected
851 # This test makes sure that the generated anchor names match the expected
848 # ones even if the heading text contains unconventional characters
852 # ones even if the heading text contains unconventional characters
849 raw = 'h1. Some heading related to version 0.5'
853 raw = 'h1. Some heading related to version 0.5'
850 anchor = sanitize_anchor_name("Some-heading-related-to-version-0.5")
854 anchor = sanitize_anchor_name("Some-heading-related-to-version-0.5")
851 expected = %|<a name="#{anchor}"></a>\n<h1 >Some heading related to version 0.5<a href="##{anchor}" class="wiki-anchor">&para;</a></h1>|
855 expected = %|<a name="#{anchor}"></a>\n<h1 >Some heading related to version 0.5<a href="##{anchor}" class="wiki-anchor">&para;</a></h1>|
852
856
853 assert_equal expected, textilizable(raw)
857 assert_equal expected, textilizable(raw)
854 end
858 end
855
859
856 def test_headings_in_wiki_single_page_export_should_be_prepended_with_page_title
860 def test_headings_in_wiki_single_page_export_should_be_prepended_with_page_title
857 page = WikiPage.new( :title => 'Page Title', :wiki_id => 1 )
861 page = WikiPage.new( :title => 'Page Title', :wiki_id => 1 )
858 content = WikiContent.new( :text => 'h1. Some heading', :page => page )
862 content = WikiContent.new( :text => 'h1. Some heading', :page => page )
859
863
860 expected = %|<a name="Page_Title_Some-heading"></a>\n<h1 >Some heading<a href="#Page_Title_Some-heading" class="wiki-anchor">&para;</a></h1>|
864 expected = %|<a name="Page_Title_Some-heading"></a>\n<h1 >Some heading<a href="#Page_Title_Some-heading" class="wiki-anchor">&para;</a></h1>|
861
865
862 assert_equal expected, textilizable(content, :text, :wiki_links => :anchor )
866 assert_equal expected, textilizable(content, :text, :wiki_links => :anchor )
863 end
867 end
864
868
865 def test_table_of_content
869 def test_table_of_content
866 raw = <<-RAW
870 raw = <<-RAW
867 {{toc}}
871 {{toc}}
868
872
869 h1. Title
873 h1. Title
870
874
871 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
875 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
872
876
873 h2. Subtitle with a [[Wiki]] link
877 h2. Subtitle with a [[Wiki]] link
874
878
875 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
879 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
876
880
877 h2. Subtitle with [[Wiki|another Wiki]] link
881 h2. Subtitle with [[Wiki|another Wiki]] link
878
882
879 h2. Subtitle with %{color:red}red text%
883 h2. Subtitle with %{color:red}red text%
880
884
881 <pre>
885 <pre>
882 some code
886 some code
883 </pre>
887 </pre>
884
888
885 h3. Subtitle with *some* _modifiers_
889 h3. Subtitle with *some* _modifiers_
886
890
887 h3. Subtitle with @inline code@
891 h3. Subtitle with @inline code@
888
892
889 h1. Another title
893 h1. Another title
890
894
891 h3. An "Internet link":http://www.redmine.org/ inside subtitle
895 h3. An "Internet link":http://www.redmine.org/ inside subtitle
892
896
893 h2. "Project Name !/attachments/1234/logo_small.gif! !/attachments/5678/logo_2.png!":/projects/projectname/issues
897 h2. "Project Name !/attachments/1234/logo_small.gif! !/attachments/5678/logo_2.png!":/projects/projectname/issues
894
898
895 RAW
899 RAW
896
900
897 expected = '<ul class="toc">' +
901 expected = '<ul class="toc">' +
898 '<li><a href="#Title">Title</a>' +
902 '<li><a href="#Title">Title</a>' +
899 '<ul>' +
903 '<ul>' +
900 '<li><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
904 '<li><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
901 '<li><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
905 '<li><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
902 '<li><a href="#Subtitle-with-red-text">Subtitle with red text</a>' +
906 '<li><a href="#Subtitle-with-red-text">Subtitle with red text</a>' +
903 '<ul>' +
907 '<ul>' +
904 '<li><a href="#Subtitle-with-some-modifiers">Subtitle with some modifiers</a></li>' +
908 '<li><a href="#Subtitle-with-some-modifiers">Subtitle with some modifiers</a></li>' +
905 '<li><a href="#Subtitle-with-inline-code">Subtitle with inline code</a></li>' +
909 '<li><a href="#Subtitle-with-inline-code">Subtitle with inline code</a></li>' +
906 '</ul>' +
910 '</ul>' +
907 '</li>' +
911 '</li>' +
908 '</ul>' +
912 '</ul>' +
909 '</li>' +
913 '</li>' +
910 '<li><a href="#Another-title">Another title</a>' +
914 '<li><a href="#Another-title">Another title</a>' +
911 '<ul>' +
915 '<ul>' +
912 '<li>' +
916 '<li>' +
913 '<ul>' +
917 '<ul>' +
914 '<li><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
918 '<li><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
915 '</ul>' +
919 '</ul>' +
916 '</li>' +
920 '</li>' +
917 '<li><a href="#Project-Name">Project Name</a></li>' +
921 '<li><a href="#Project-Name">Project Name</a></li>' +
918 '</ul>' +
922 '</ul>' +
919 '</li>' +
923 '</li>' +
920 '</ul>'
924 '</ul>'
921
925
922 @project = Project.find(1)
926 @project = Project.find(1)
923 assert textilizable(raw).gsub("\n", "").include?(expected)
927 assert textilizable(raw).gsub("\n", "").include?(expected)
924 end
928 end
925
929
926 def test_table_of_content_should_generate_unique_anchors
930 def test_table_of_content_should_generate_unique_anchors
927 raw = <<-RAW
931 raw = <<-RAW
928 {{toc}}
932 {{toc}}
929
933
930 h1. Title
934 h1. Title
931
935
932 h2. Subtitle
936 h2. Subtitle
933
937
934 h2. Subtitle
938 h2. Subtitle
935 RAW
939 RAW
936
940
937 expected = '<ul class="toc">' +
941 expected = '<ul class="toc">' +
938 '<li><a href="#Title">Title</a>' +
942 '<li><a href="#Title">Title</a>' +
939 '<ul>' +
943 '<ul>' +
940 '<li><a href="#Subtitle">Subtitle</a></li>' +
944 '<li><a href="#Subtitle">Subtitle</a></li>' +
941 '<li><a href="#Subtitle-2">Subtitle</a></li>'
945 '<li><a href="#Subtitle-2">Subtitle</a></li>'
942 '</ul>'
946 '</ul>'
943 '</li>' +
947 '</li>' +
944 '</ul>'
948 '</ul>'
945
949
946 @project = Project.find(1)
950 @project = Project.find(1)
947 result = textilizable(raw).gsub("\n", "")
951 result = textilizable(raw).gsub("\n", "")
948 assert_include expected, result
952 assert_include expected, result
949 assert_include '<a name="Subtitle">', result
953 assert_include '<a name="Subtitle">', result
950 assert_include '<a name="Subtitle-2">', result
954 assert_include '<a name="Subtitle-2">', result
951 end
955 end
952
956
953 def test_table_of_content_should_contain_included_page_headings
957 def test_table_of_content_should_contain_included_page_headings
954 raw = <<-RAW
958 raw = <<-RAW
955 {{toc}}
959 {{toc}}
956
960
957 h1. Included
961 h1. Included
958
962
959 {{include(Child_1)}}
963 {{include(Child_1)}}
960 RAW
964 RAW
961
965
962 expected = '<ul class="toc">' +
966 expected = '<ul class="toc">' +
963 '<li><a href="#Included">Included</a></li>' +
967 '<li><a href="#Included">Included</a></li>' +
964 '<li><a href="#Child-page-1">Child page 1</a></li>' +
968 '<li><a href="#Child-page-1">Child page 1</a></li>' +
965 '</ul>'
969 '</ul>'
966
970
967 @project = Project.find(1)
971 @project = Project.find(1)
968 assert textilizable(raw).gsub("\n", "").include?(expected)
972 assert textilizable(raw).gsub("\n", "").include?(expected)
969 end
973 end
970
974
971 def test_section_edit_links
975 def test_section_edit_links
972 raw = <<-RAW
976 raw = <<-RAW
973 h1. Title
977 h1. Title
974
978
975 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
979 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
976
980
977 h2. Subtitle with a [[Wiki]] link
981 h2. Subtitle with a [[Wiki]] link
978
982
979 h2. Subtitle with *some* _modifiers_
983 h2. Subtitle with *some* _modifiers_
980
984
981 h2. Subtitle with @inline code@
985 h2. Subtitle with @inline code@
982
986
983 <pre>
987 <pre>
984 some code
988 some code
985
989
986 h2. heading inside pre
990 h2. heading inside pre
987
991
988 <h2>html heading inside pre</h2>
992 <h2>html heading inside pre</h2>
989 </pre>
993 </pre>
990
994
991 h2. Subtitle after pre tag
995 h2. Subtitle after pre tag
992 RAW
996 RAW
993
997
994 @project = Project.find(1)
998 @project = Project.find(1)
995 set_language_if_valid 'en'
999 set_language_if_valid 'en'
996 result = textilizable(raw, :edit_section_links => {:controller => 'wiki', :action => 'edit', :project_id => '1', :id => 'Test'}).gsub("\n", "")
1000 result = textilizable(raw, :edit_section_links => {:controller => 'wiki', :action => 'edit', :project_id => '1', :id => 'Test'}).gsub("\n", "")
997
1001
998 # heading that contains inline code
1002 # heading that contains inline code
999 assert_match Regexp.new('<div class="contextual" title="Edit this section">' +
1003 assert_match Regexp.new('<div class="contextual" title="Edit this section">' +
1000 '<a href="/projects/1/wiki/Test/edit\?section=4"><img alt="Edit" src="/images/edit.png(\?\d+)?" /></a></div>' +
1004 '<a href="/projects/1/wiki/Test/edit\?section=4"><img alt="Edit" src="/images/edit.png(\?\d+)?" /></a></div>' +
1001 '<a name="Subtitle-with-inline-code"></a>' +
1005 '<a name="Subtitle-with-inline-code"></a>' +
1002 '<h2 >Subtitle with <code>inline code</code><a href="#Subtitle-with-inline-code" class="wiki-anchor">&para;</a></h2>'),
1006 '<h2 >Subtitle with <code>inline code</code><a href="#Subtitle-with-inline-code" class="wiki-anchor">&para;</a></h2>'),
1003 result
1007 result
1004
1008
1005 # last heading
1009 # last heading
1006 assert_match Regexp.new('<div class="contextual" title="Edit this section">' +
1010 assert_match Regexp.new('<div class="contextual" title="Edit this section">' +
1007 '<a href="/projects/1/wiki/Test/edit\?section=5"><img alt="Edit" src="/images/edit.png(\?\d+)?" /></a></div>' +
1011 '<a href="/projects/1/wiki/Test/edit\?section=5"><img alt="Edit" src="/images/edit.png(\?\d+)?" /></a></div>' +
1008 '<a name="Subtitle-after-pre-tag"></a>' +
1012 '<a name="Subtitle-after-pre-tag"></a>' +
1009 '<h2 >Subtitle after pre tag<a href="#Subtitle-after-pre-tag" class="wiki-anchor">&para;</a></h2>'),
1013 '<h2 >Subtitle after pre tag<a href="#Subtitle-after-pre-tag" class="wiki-anchor">&para;</a></h2>'),
1010 result
1014 result
1011 end
1015 end
1012
1016
1013 def test_default_formatter
1017 def test_default_formatter
1014 with_settings :text_formatting => 'unknown' do
1018 with_settings :text_formatting => 'unknown' do
1015 text = 'a *link*: http://www.example.net/'
1019 text = 'a *link*: http://www.example.net/'
1016 assert_equal '<p>a *link*: <a class="external" href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
1020 assert_equal '<p>a *link*: <a class="external" href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
1017 end
1021 end
1018 end
1022 end
1019
1023
1020 def test_due_date_distance_in_words
1024 def test_due_date_distance_in_words
1021 to_test = { Date.today => 'Due in 0 days',
1025 to_test = { Date.today => 'Due in 0 days',
1022 Date.today + 1 => 'Due in 1 day',
1026 Date.today + 1 => 'Due in 1 day',
1023 Date.today + 100 => 'Due in about 3 months',
1027 Date.today + 100 => 'Due in about 3 months',
1024 Date.today + 20000 => 'Due in over 54 years',
1028 Date.today + 20000 => 'Due in over 54 years',
1025 Date.today - 1 => '1 day late',
1029 Date.today - 1 => '1 day late',
1026 Date.today - 100 => 'about 3 months late',
1030 Date.today - 100 => 'about 3 months late',
1027 Date.today - 20000 => 'over 54 years late',
1031 Date.today - 20000 => 'over 54 years late',
1028 }
1032 }
1029 ::I18n.locale = :en
1033 ::I18n.locale = :en
1030 to_test.each do |date, expected|
1034 to_test.each do |date, expected|
1031 assert_equal expected, due_date_distance_in_words(date)
1035 assert_equal expected, due_date_distance_in_words(date)
1032 end
1036 end
1033 end
1037 end
1034
1038
1035 def test_avatar_enabled
1039 def test_avatar_enabled
1036 with_settings :gravatar_enabled => '1' do
1040 with_settings :gravatar_enabled => '1' do
1037 assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
1041 assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
1038 assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
1042 assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
1039 # Default size is 50
1043 # Default size is 50
1040 assert avatar('jsmith <jsmith@somenet.foo>').include?('size=50')
1044 assert avatar('jsmith <jsmith@somenet.foo>').include?('size=50')
1041 assert avatar('jsmith <jsmith@somenet.foo>', :size => 24).include?('size=24')
1045 assert avatar('jsmith <jsmith@somenet.foo>', :size => 24).include?('size=24')
1042 # Non-avatar options should be considered html options
1046 # Non-avatar options should be considered html options
1043 assert avatar('jsmith <jsmith@somenet.foo>', :title => 'John Smith').include?('title="John Smith"')
1047 assert avatar('jsmith <jsmith@somenet.foo>', :title => 'John Smith').include?('title="John Smith"')
1044 # The default class of the img tag should be gravatar
1048 # The default class of the img tag should be gravatar
1045 assert avatar('jsmith <jsmith@somenet.foo>').include?('class="gravatar"')
1049 assert avatar('jsmith <jsmith@somenet.foo>').include?('class="gravatar"')
1046 assert !avatar('jsmith <jsmith@somenet.foo>', :class => 'picture').include?('class="gravatar"')
1050 assert !avatar('jsmith <jsmith@somenet.foo>', :class => 'picture').include?('class="gravatar"')
1047 assert_nil avatar('jsmith')
1051 assert_nil avatar('jsmith')
1048 assert_nil avatar(nil)
1052 assert_nil avatar(nil)
1049 end
1053 end
1050 end
1054 end
1051
1055
1052 def test_avatar_disabled
1056 def test_avatar_disabled
1053 with_settings :gravatar_enabled => '0' do
1057 with_settings :gravatar_enabled => '0' do
1054 assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo'))
1058 assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo'))
1055 end
1059 end
1056 end
1060 end
1057
1061
1058 def test_link_to_user
1062 def test_link_to_user
1059 user = User.find(2)
1063 user = User.find(2)
1060 assert_equal '<a href="/users/2" class="user active">John Smith</a>', link_to_user(user)
1064 assert_equal '<a href="/users/2" class="user active">John Smith</a>', link_to_user(user)
1061 end
1065 end
1062
1066
1063 def test_link_to_user_should_not_link_to_locked_user
1067 def test_link_to_user_should_not_link_to_locked_user
1064 with_current_user nil do
1068 with_current_user nil do
1065 user = User.find(5)
1069 user = User.find(5)
1066 assert user.locked?
1070 assert user.locked?
1067 assert_equal 'Dave2 Lopper2', link_to_user(user)
1071 assert_equal 'Dave2 Lopper2', link_to_user(user)
1068 end
1072 end
1069 end
1073 end
1070
1074
1071 def test_link_to_user_should_link_to_locked_user_if_current_user_is_admin
1075 def test_link_to_user_should_link_to_locked_user_if_current_user_is_admin
1072 with_current_user User.find(1) do
1076 with_current_user User.find(1) do
1073 user = User.find(5)
1077 user = User.find(5)
1074 assert user.locked?
1078 assert user.locked?
1075 assert_equal '<a href="/users/5" class="user locked">Dave2 Lopper2</a>', link_to_user(user)
1079 assert_equal '<a href="/users/5" class="user locked">Dave2 Lopper2</a>', link_to_user(user)
1076 end
1080 end
1077 end
1081 end
1078
1082
1079 def test_link_to_user_should_not_link_to_anonymous
1083 def test_link_to_user_should_not_link_to_anonymous
1080 user = User.anonymous
1084 user = User.anonymous
1081 assert user.anonymous?
1085 assert user.anonymous?
1082 t = link_to_user(user)
1086 t = link_to_user(user)
1083 assert_equal ::I18n.t(:label_user_anonymous), t
1087 assert_equal ::I18n.t(:label_user_anonymous), t
1084 end
1088 end
1085
1089
1086 def test_link_to_attachment
1090 def test_link_to_attachment
1087 a = Attachment.find(3)
1091 a = Attachment.find(3)
1088 assert_equal '<a href="/attachments/3/logo.gif">logo.gif</a>',
1092 assert_equal '<a href="/attachments/3/logo.gif">logo.gif</a>',
1089 link_to_attachment(a)
1093 link_to_attachment(a)
1090 assert_equal '<a href="/attachments/3/logo.gif">Text</a>',
1094 assert_equal '<a href="/attachments/3/logo.gif">Text</a>',
1091 link_to_attachment(a, :text => 'Text')
1095 link_to_attachment(a, :text => 'Text')
1092 assert_equal '<a href="/attachments/3/logo.gif" class="foo">logo.gif</a>',
1096 assert_equal '<a href="/attachments/3/logo.gif" class="foo">logo.gif</a>',
1093 link_to_attachment(a, :class => 'foo')
1097 link_to_attachment(a, :class => 'foo')
1094 assert_equal '<a href="/attachments/download/3/logo.gif">logo.gif</a>',
1098 assert_equal '<a href="/attachments/download/3/logo.gif">logo.gif</a>',
1095 link_to_attachment(a, :download => true)
1099 link_to_attachment(a, :download => true)
1096 assert_equal '<a href="http://test.host/attachments/3/logo.gif">logo.gif</a>',
1100 assert_equal '<a href="http://test.host/attachments/3/logo.gif">logo.gif</a>',
1097 link_to_attachment(a, :only_path => false)
1101 link_to_attachment(a, :only_path => false)
1098 end
1102 end
1099
1103
1100 def test_thumbnail_tag
1104 def test_thumbnail_tag
1101 a = Attachment.find(3)
1105 a = Attachment.find(3)
1102 assert_equal '<a href="/attachments/3/logo.gif" title="logo.gif"><img alt="3" src="/attachments/thumbnail/3" /></a>',
1106 assert_equal '<a href="/attachments/3/logo.gif" title="logo.gif"><img alt="3" src="/attachments/thumbnail/3" /></a>',
1103 thumbnail_tag(a)
1107 thumbnail_tag(a)
1104 end
1108 end
1105
1109
1106 def test_link_to_project
1110 def test_link_to_project
1107 project = Project.find(1)
1111 project = Project.find(1)
1108 assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
1112 assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
1109 link_to_project(project)
1113 link_to_project(project)
1110 assert_equal %(<a href="/projects/ecookbook/settings">eCookbook</a>),
1114 assert_equal %(<a href="/projects/ecookbook/settings">eCookbook</a>),
1111 link_to_project(project, :action => 'settings')
1115 link_to_project(project, :action => 'settings')
1112 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
1116 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
1113 link_to_project(project, {:only_path => false, :jump => 'blah'})
1117 link_to_project(project, {:only_path => false, :jump => 'blah'})
1114 assert_equal %(<a href="/projects/ecookbook/settings" class="project">eCookbook</a>),
1118 assert_equal %(<a href="/projects/ecookbook/settings" class="project">eCookbook</a>),
1115 link_to_project(project, {:action => 'settings'}, :class => "project")
1119 link_to_project(project, {:action => 'settings'}, :class => "project")
1116 end
1120 end
1117
1121
1118 def test_link_to_project_settings
1122 def test_link_to_project_settings
1119 project = Project.find(1)
1123 project = Project.find(1)
1120 assert_equal '<a href="/projects/ecookbook/settings">eCookbook</a>', link_to_project_settings(project)
1124 assert_equal '<a href="/projects/ecookbook/settings">eCookbook</a>', link_to_project_settings(project)
1121
1125
1122 project.status = Project::STATUS_CLOSED
1126 project.status = Project::STATUS_CLOSED
1123 assert_equal '<a href="/projects/ecookbook">eCookbook</a>', link_to_project_settings(project)
1127 assert_equal '<a href="/projects/ecookbook">eCookbook</a>', link_to_project_settings(project)
1124
1128
1125 project.status = Project::STATUS_ARCHIVED
1129 project.status = Project::STATUS_ARCHIVED
1126 assert_equal 'eCookbook', link_to_project_settings(project)
1130 assert_equal 'eCookbook', link_to_project_settings(project)
1127 end
1131 end
1128
1132
1129 def test_link_to_legacy_project_with_numerical_identifier_should_use_id
1133 def test_link_to_legacy_project_with_numerical_identifier_should_use_id
1130 # numeric identifier are no longer allowed
1134 # numeric identifier are no longer allowed
1131 Project.update_all "identifier=25", "id=1"
1135 Project.update_all "identifier=25", "id=1"
1132
1136
1133 assert_equal '<a href="/projects/1">eCookbook</a>',
1137 assert_equal '<a href="/projects/1">eCookbook</a>',
1134 link_to_project(Project.find(1))
1138 link_to_project(Project.find(1))
1135 end
1139 end
1136
1140
1137 def test_principals_options_for_select_with_users
1141 def test_principals_options_for_select_with_users
1138 User.current = nil
1142 User.current = nil
1139 users = [User.find(2), User.find(4)]
1143 users = [User.find(2), User.find(4)]
1140 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>),
1144 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>),
1141 principals_options_for_select(users)
1145 principals_options_for_select(users)
1142 end
1146 end
1143
1147
1144 def test_principals_options_for_select_with_selected
1148 def test_principals_options_for_select_with_selected
1145 User.current = nil
1149 User.current = nil
1146 users = [User.find(2), User.find(4)]
1150 users = [User.find(2), User.find(4)]
1147 assert_equal %(<option value="2">John Smith</option><option value="4" selected="selected">Robert Hill</option>),
1151 assert_equal %(<option value="2">John Smith</option><option value="4" selected="selected">Robert Hill</option>),
1148 principals_options_for_select(users, User.find(4))
1152 principals_options_for_select(users, User.find(4))
1149 end
1153 end
1150
1154
1151 def test_principals_options_for_select_with_users_and_groups
1155 def test_principals_options_for_select_with_users_and_groups
1152 User.current = nil
1156 User.current = nil
1153 users = [User.find(2), Group.find(11), User.find(4), Group.find(10)]
1157 users = [User.find(2), Group.find(11), User.find(4), Group.find(10)]
1154 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>) +
1158 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>) +
1155 %(<optgroup label="Groups"><option value="10">A Team</option><option value="11">B Team</option></optgroup>),
1159 %(<optgroup label="Groups"><option value="10">A Team</option><option value="11">B Team</option></optgroup>),
1156 principals_options_for_select(users)
1160 principals_options_for_select(users)
1157 end
1161 end
1158
1162
1159 def test_principals_options_for_select_with_empty_collection
1163 def test_principals_options_for_select_with_empty_collection
1160 assert_equal '', principals_options_for_select([])
1164 assert_equal '', principals_options_for_select([])
1161 end
1165 end
1162
1166
1163 def test_principals_options_for_select_should_include_me_option_when_current_user_is_in_collection
1167 def test_principals_options_for_select_should_include_me_option_when_current_user_is_in_collection
1164 users = [User.find(2), User.find(4)]
1168 users = [User.find(2), User.find(4)]
1165 User.current = User.find(4)
1169 User.current = User.find(4)
1166 assert_include '<option value="4">&lt;&lt; me &gt;&gt;</option>', principals_options_for_select(users)
1170 assert_include '<option value="4">&lt;&lt; me &gt;&gt;</option>', principals_options_for_select(users)
1167 end
1171 end
1168
1172
1169 def test_stylesheet_link_tag_should_pick_the_default_stylesheet
1173 def test_stylesheet_link_tag_should_pick_the_default_stylesheet
1170 assert_match 'href="/stylesheets/styles.css"', stylesheet_link_tag("styles")
1174 assert_match 'href="/stylesheets/styles.css"', stylesheet_link_tag("styles")
1171 end
1175 end
1172
1176
1173 def test_stylesheet_link_tag_for_plugin_should_pick_the_plugin_stylesheet
1177 def test_stylesheet_link_tag_for_plugin_should_pick_the_plugin_stylesheet
1174 assert_match 'href="/plugin_assets/foo/stylesheets/styles.css"', stylesheet_link_tag("styles", :plugin => :foo)
1178 assert_match 'href="/plugin_assets/foo/stylesheets/styles.css"', stylesheet_link_tag("styles", :plugin => :foo)
1175 end
1179 end
1176
1180
1177 def test_image_tag_should_pick_the_default_image
1181 def test_image_tag_should_pick_the_default_image
1178 assert_match 'src="/images/image.png"', image_tag("image.png")
1182 assert_match 'src="/images/image.png"', image_tag("image.png")
1179 end
1183 end
1180
1184
1181 def test_image_tag_should_pick_the_theme_image_if_it_exists
1185 def test_image_tag_should_pick_the_theme_image_if_it_exists
1182 theme = Redmine::Themes.themes.last
1186 theme = Redmine::Themes.themes.last
1183 theme.images << 'image.png'
1187 theme.images << 'image.png'
1184
1188
1185 with_settings :ui_theme => theme.id do
1189 with_settings :ui_theme => theme.id do
1186 assert_match %|src="/themes/#{theme.dir}/images/image.png"|, image_tag("image.png")
1190 assert_match %|src="/themes/#{theme.dir}/images/image.png"|, image_tag("image.png")
1187 assert_match %|src="/images/other.png"|, image_tag("other.png")
1191 assert_match %|src="/images/other.png"|, image_tag("other.png")
1188 end
1192 end
1189 ensure
1193 ensure
1190 theme.images.delete 'image.png'
1194 theme.images.delete 'image.png'
1191 end
1195 end
1192
1196
1193 def test_image_tag_sfor_plugin_should_pick_the_plugin_image
1197 def test_image_tag_sfor_plugin_should_pick_the_plugin_image
1194 assert_match 'src="/plugin_assets/foo/images/image.png"', image_tag("image.png", :plugin => :foo)
1198 assert_match 'src="/plugin_assets/foo/images/image.png"', image_tag("image.png", :plugin => :foo)
1195 end
1199 end
1196
1200
1197 def test_javascript_include_tag_should_pick_the_default_javascript
1201 def test_javascript_include_tag_should_pick_the_default_javascript
1198 assert_match 'src="/javascripts/scripts.js"', javascript_include_tag("scripts")
1202 assert_match 'src="/javascripts/scripts.js"', javascript_include_tag("scripts")
1199 end
1203 end
1200
1204
1201 def test_javascript_include_tag_for_plugin_should_pick_the_plugin_javascript
1205 def test_javascript_include_tag_for_plugin_should_pick_the_plugin_javascript
1202 assert_match 'src="/plugin_assets/foo/javascripts/scripts.js"', javascript_include_tag("scripts", :plugin => :foo)
1206 assert_match 'src="/plugin_assets/foo/javascripts/scripts.js"', javascript_include_tag("scripts", :plugin => :foo)
1203 end
1207 end
1204 end
1208 end
General Comments 0
You need to be logged in to leave comments. Login now