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