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