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