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