Defect #37394 » 37394-v2.patch
Gemfile | ||
---|---|---|
36 | 36 |
gem "html-pipeline", "~> 2.13.2" |
37 | 37 |
gem "sanitize", "~> 6.0" |
38 | 38 | |
39 |
gem "commonmarker", '~> 2.3.0' |
|
40 |
gem 'deckar01-task_list', '2.3.2' |
|
41 | ||
39 | 42 |
# Optional gem for LDAP authentication |
40 | 43 |
group :ldap do |
41 | 44 |
gem 'net-ldap', '~> 0.17.0' |
... | ... | |
46 | 49 |
gem 'mini_magick', '~> 5.2.0' |
47 | 50 |
end |
48 | 51 | |
49 |
# Optional CommonMark support, not for JRuby |
|
50 |
group :common_mark do |
|
51 |
gem "commonmarker", '~> 2.3.0' |
|
52 |
gem 'deckar01-task_list', '2.3.2' |
|
53 |
end |
|
54 | ||
55 | 52 |
# Include database gems for the adapters found in the database |
56 | 53 |
# configuration file |
57 | 54 |
database_file = File.join(File.dirname(__FILE__), "config/database.yml") |
lib/redmine.rb | ||
---|---|---|
24 | 24 |
rescue LoadError |
25 | 25 |
# MiniMagick is not available |
26 | 26 |
end |
27 |
begin |
|
28 |
require 'commonmarker' unless Object.const_defined?(:Commonmarker) |
|
29 |
rescue LoadError |
|
30 |
# CommonMarker is not available |
|
31 |
end |
|
32 | 27 | |
33 | 28 |
module Redmine |
34 | 29 |
end |
lib/redmine/preparation.rb | ||
---|---|---|
408 | 408 | |
409 | 409 |
WikiFormatting.map do |format| |
410 | 410 |
format.register :textile |
411 |
if Object.const_defined?(:Commonmarker) |
|
412 |
format.register :common_mark, label: 'CommonMark Markdown (GitHub Flavored)' |
|
413 |
end |
|
411 |
format.register :common_mark, label: 'CommonMark Markdown (GitHub Flavored)' |
|
414 | 412 |
end |
415 | 413 | |
416 | 414 |
ActionView::Template.register_template_handler :rsb, Views::ApiTemplateHandler |
test/helpers/application_helper_test.rb | ||
---|---|---|
252 | 252 |
end |
253 | 253 | |
254 | 254 |
def test_attached_images_with_markdown_and_non_ascii_filename |
255 |
skip unless Object.const_defined?(:CommonMarker) |
|
256 | ||
257 | 255 |
to_test = { |
258 | 256 |
'CAFÉ.JPG' => 'CAF%C3%89.JPG', |
259 | 257 |
'crème.jpg' => 'cr%C3%A8me.jpg', |
... | ... | |
1658 | 1656 |
end |
1659 | 1657 |
end |
1660 | 1658 | |
1661 |
if Object.const_defined?(:Commonmarker) |
|
1662 |
def test_toc_with_markdown_formatting_should_be_parsed |
|
1663 |
with_settings :text_formatting => 'common_mark' do |
|
1664 |
assert_select_in textilizable("{{toc}}\n\n# Heading"), 'ul.toc li', :text => 'Heading' |
|
1665 |
assert_select_in textilizable("{{<toc}}\n\n# Heading"), 'ul.toc.left li', :text => 'Heading' |
|
1666 |
assert_select_in textilizable("{{>toc}}\n\n# Heading"), 'ul.toc.right li', :text => 'Heading' |
|
1667 |
end |
|
1659 |
def test_toc_with_markdown_formatting_should_be_parsed |
|
1660 |
with_settings :text_formatting => 'common_mark' do |
|
1661 |
assert_select_in textilizable("{{toc}}\n\n# Heading"), 'ul.toc li', :text => 'Heading' |
|
1662 |
assert_select_in textilizable("{{<toc}}\n\n# Heading"), 'ul.toc.left li', :text => 'Heading' |
|
1663 |
assert_select_in textilizable("{{>toc}}\n\n# Heading"), 'ul.toc.right li', :text => 'Heading' |
|
1668 | 1664 |
end |
1669 | 1665 |
end |
1670 | 1666 |
test/unit/lib/redmine/wiki_formatting/common_mark/application_helper_test.rb | ||
---|---|---|
20 | 20 |
require_relative '../../../../../test_helper' |
21 | 21 | |
22 | 22 |
class Redmine::WikiFormatting::CommonMark::ApplicationHelperTest < Redmine::HelperTest |
23 |
if Object.const_defined?(:Commonmarker)
|
|
23 |
include ERB::Util
|
|
24 | 24 | |
25 |
include ERB::Util |
|
26 | ||
27 |
def setup |
|
28 |
super |
|
29 |
set_tmp_attachments_directory |
|
30 |
end |
|
25 |
def setup |
|
26 |
super |
|
27 |
set_tmp_attachments_directory |
|
28 |
end |
|
31 | 29 | |
32 |
def test_attached_images_with_markdown_and_non_ascii_filename |
|
33 |
to_test = { |
|
34 |
'CAFÉ.JPG' => 'CAF%C3%89.JPG', |
|
35 |
'crème.jpg' => 'cr%C3%A8me.jpg', |
|
36 |
} |
|
37 |
with_settings :text_formatting => 'common_mark' do |
|
38 |
to_test.each do |filename, result| |
|
39 |
attachment = Attachment.generate!(:filename => filename) |
|
40 |
assert_include %(<img src="/attachments/download/#{attachment.id}/#{result}" alt="" loading="lazy">), textilizable("", :attachments => [attachment]) |
|
41 |
end |
|
30 |
def test_attached_images_with_markdown_and_non_ascii_filename |
|
31 |
to_test = { |
|
32 |
'CAFÉ.JPG' => 'CAF%C3%89.JPG', |
|
33 |
'crème.jpg' => 'cr%C3%A8me.jpg', |
|
34 |
} |
|
35 |
with_settings :text_formatting => 'common_mark' do |
|
36 |
to_test.each do |filename, result| |
|
37 |
attachment = Attachment.generate!(:filename => filename) |
|
38 |
assert_include %(<img src="/attachments/download/#{attachment.id}/#{result}" alt="" loading="lazy">), textilizable("", :attachments => [attachment]) |
|
42 | 39 |
end |
43 | 40 |
end |
41 |
end |
|
44 | 42 | |
45 |
def test_toc_with_markdown_formatting_should_be_parsed |
|
46 |
with_settings :text_formatting => 'common_mark' do |
|
47 |
assert_select_in textilizable("{{toc}}\n\n# Heading"), 'ul.toc li', :text => 'Heading' |
|
48 |
assert_select_in textilizable("{{<toc}}\n\n# Heading"), 'ul.toc.left li', :text => 'Heading' |
|
49 |
assert_select_in textilizable("{{>toc}}\n\n# Heading"), 'ul.toc.right li', :text => 'Heading' |
|
50 |
end |
|
43 |
def test_toc_with_markdown_formatting_should_be_parsed |
|
44 |
with_settings :text_formatting => 'common_mark' do |
|
45 |
assert_select_in textilizable("{{toc}}\n\n# Heading"), 'ul.toc li', :text => 'Heading' |
|
46 |
assert_select_in textilizable("{{<toc}}\n\n# Heading"), 'ul.toc.left li', :text => 'Heading' |
|
47 |
assert_select_in textilizable("{{>toc}}\n\n# Heading"), 'ul.toc.right li', :text => 'Heading' |
|
51 | 48 |
end |
49 |
end |
|
52 | 50 | |
53 |
def test_attached_image_alt_attribute_with_madkrown
|
|
54 |
attachments = Attachment.all
|
|
55 |
with_settings text_formatting: 'common_mark' do
|
|
56 |
# When alt text is set
|
|
57 |
assert_match %r[<img src=".+?" alt="alt text" loading=".+?">],
|
|
58 |
textilizable('', attachments: attachments)
|
|
51 |
def test_attached_image_alt_attribute_with_madkrown |
|
52 |
attachments = Attachment.all |
|
53 |
with_settings text_formatting: 'common_mark' do |
|
54 |
# When alt text is set |
|
55 |
assert_match %r[<img src=".+?" alt="alt text" loading=".+?">], |
|
56 |
textilizable('', attachments: attachments) |
|
59 | 57 | |
60 |
# When alt text is not set
|
|
61 |
assert_match %r[<img src=".+?" title="This is a logo" alt="This is a logo" loading=".+?">],
|
|
62 |
textilizable('', attachments: attachments)
|
|
58 |
# When alt text is not set |
|
59 |
assert_match %r[<img src=".+?" title="This is a logo" alt="This is a logo" loading=".+?">], |
|
60 |
textilizable('', attachments: attachments) |
|
63 | 61 | |
64 |
# When alt text is not set and the attachment has no description
|
|
65 |
assert_match %r[<img src=".+?" alt="" loading=".+?">],
|
|
66 |
textilizable('', attachments: attachments)
|
|
62 |
# When alt text is not set and the attachment has no description |
|
63 |
assert_match %r[<img src=".+?" alt="" loading=".+?">], |
|
64 |
textilizable('', attachments: attachments) |
|
67 | 65 | |
68 |
# When no matching attachments are found
|
|
69 |
assert_match %r[<img src=".+?" alt="">],
|
|
70 |
textilizable('', attachments: attachments)
|
|
71 |
assert_match %r[<img src=".+?" alt="alt text">],
|
|
72 |
textilizable('', attachments: attachments)
|
|
66 |
# When no matching attachments are found |
|
67 |
assert_match %r[<img src=".+?" alt="">], |
|
68 |
textilizable('', attachments: attachments) |
|
69 |
assert_match %r[<img src=".+?" alt="alt text">], |
|
70 |
textilizable('', attachments: attachments) |
|
73 | 71 | |
74 |
# When no attachment is registered |
|
75 |
assert_match %r[<img src=".+?" alt="">], |
|
76 |
textilizable('', attachments: []) |
|
77 |
assert_match %r[<img src=".+?" alt="alt text">], |
|
78 |
textilizable('', attachments: []) |
|
79 |
end |
|
72 |
# When no attachment is registered |
|
73 |
assert_match %r[<img src=".+?" alt="">], |
|
74 |
textilizable('', attachments: []) |
|
75 |
assert_match %r[<img src=".+?" alt="alt text">], |
|
76 |
textilizable('', attachments: []) |
|
80 | 77 |
end |
81 | 78 |
end |
82 | 79 |
end |
test/unit/lib/redmine/wiki_formatting/common_mark/external_links_filter_test.rb | ||
---|---|---|
18 | 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | 19 | |
20 | 20 |
require_relative '../../../../../test_helper' |
21 |
require 'redmine/wiki_formatting/common_mark/external_links_filter' |
|
21 | 22 | |
22 |
if Object.const_defined?(:Commonmarker) |
|
23 |
require 'redmine/wiki_formatting/common_mark/external_links_filter' |
|
24 | ||
25 |
class Redmine::WikiFormatting::CommonMark::ExternalLinksFilterTest < ActiveSupport::TestCase |
|
26 |
def filter(html) |
|
27 |
Redmine::WikiFormatting::CommonMark::ExternalLinksFilter.to_html(html, @options) |
|
28 |
end |
|
23 |
class Redmine::WikiFormatting::CommonMark::ExternalLinksFilterTest < ActiveSupport::TestCase |
|
24 |
def filter(html) |
|
25 |
Redmine::WikiFormatting::CommonMark::ExternalLinksFilter.to_html(html, @options) |
|
26 |
end |
|
29 | 27 | |
30 |
def setup
|
|
31 |
@options = { }
|
|
32 |
end
|
|
28 |
def setup |
|
29 |
@options = { } |
|
30 |
end |
|
33 | 31 | |
34 |
def test_external_links_should_have_external_css_class
|
|
35 |
assert_equal %(<a href="http://example.net/" class="external">link</a>), filter(%(<a href="http://example.net/">link</a>))
|
|
36 |
end
|
|
32 |
def test_external_links_should_have_external_css_class |
|
33 |
assert_equal %(<a href="http://example.net/" class="external">link</a>), filter(%(<a href="http://example.net/">link</a>)) |
|
34 |
end |
|
37 | 35 | |
38 |
def test_locals_links_should_not_have_external_css_class
|
|
39 |
assert_equal %(<a href="/">home</a>), filter(%(<a href="/">home</a>))
|
|
40 |
assert_equal %(<a href="relative">relative</a>), filter(%(<a href="relative">relative</a>))
|
|
41 |
assert_equal %(<a href="#anchor">anchor</a>), filter(%(<a href="#anchor">anchor</a>))
|
|
42 |
end
|
|
36 |
def test_locals_links_should_not_have_external_css_class |
|
37 |
assert_equal %(<a href="/">home</a>), filter(%(<a href="/">home</a>)) |
|
38 |
assert_equal %(<a href="relative">relative</a>), filter(%(<a href="relative">relative</a>)) |
|
39 |
assert_equal %(<a href="#anchor">anchor</a>), filter(%(<a href="#anchor">anchor</a>)) |
|
40 |
end |
|
43 | 41 | |
44 |
def test_mailto_links_should_have_email_class
|
|
45 |
assert_equal %(<a href="mailto:user@example.org" class="email">user</a>), filter(%(<a href="mailto:user@example.org">user</a>))
|
|
46 |
end
|
|
42 |
def test_mailto_links_should_have_email_class |
|
43 |
assert_equal %(<a href="mailto:user@example.org" class="email">user</a>), filter(%(<a href="mailto:user@example.org">user</a>)) |
|
44 |
end |
|
47 | 45 | |
48 |
def test_malformed_uri_should_not_cause_exception |
|
49 |
assert_nothing_raised do |
|
50 |
filter(%(<a href="http://example.com/foo#bar#">Malformed URI</a>)) |
|
51 |
end |
|
46 |
def test_malformed_uri_should_not_cause_exception |
|
47 |
assert_nothing_raised do |
|
48 |
filter(%(<a href="http://example.com/foo#bar#">Malformed URI</a>)) |
|
52 | 49 |
end |
50 |
end |
|
53 | 51 | |
54 |
def test_external_links_with_target_get_rel_noopener |
|
55 |
assert_equal( |
|
56 |
%(<a target="_blank" href="http://example.net/" class="external" rel="noopener">link</a>), |
|
57 |
filter(%(<a target="_blank" href="http://example.net/">link</a>)) |
|
58 |
) |
|
59 |
assert_equal( |
|
60 |
%(<a target="_blank" href="http://example.net/" rel="nofollow noopener" class="external">link</a>), |
|
61 |
filter(%(<a target="_blank" href="http://example.net/" rel="nofollow">link</a>)) |
|
62 |
) |
|
63 |
end |
|
52 |
def test_external_links_with_target_get_rel_noopener |
|
53 |
assert_equal( |
|
54 |
%(<a target="_blank" href="http://example.net/" class="external" rel="noopener">link</a>), |
|
55 |
filter(%(<a target="_blank" href="http://example.net/">link</a>)) |
|
56 |
) |
|
57 |
assert_equal( |
|
58 |
%(<a target="_blank" href="http://example.net/" rel="nofollow noopener" class="external">link</a>), |
|
59 |
filter(%(<a target="_blank" href="http://example.net/" rel="nofollow">link</a>)) |
|
60 |
) |
|
64 | 61 |
end |
65 | 62 |
end |
test/unit/lib/redmine/wiki_formatting/common_mark/fixup_auto_links_filter_test.rb | ||
---|---|---|
18 | 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | 19 | |
20 | 20 |
require_relative '../../../../../test_helper' |
21 |
require 'redmine/wiki_formatting/common_mark/fixup_auto_links_filter' |
|
21 | 22 | |
22 |
if Object.const_defined?(:Commonmarker) |
|
23 |
require 'redmine/wiki_formatting/common_mark/fixup_auto_links_filter' |
|
24 | ||
25 |
class Redmine::WikiFormatting::CommonMark::FixupAutoLinksFilterTest < ActiveSupport::TestCase |
|
26 |
def filter(html) |
|
27 |
Redmine::WikiFormatting::CommonMark::FixupAutoLinksFilter.to_html(html, @options) |
|
28 |
end |
|
23 |
class Redmine::WikiFormatting::CommonMark::FixupAutoLinksFilterTest < ActiveSupport::TestCase |
|
24 |
def filter(html) |
|
25 |
Redmine::WikiFormatting::CommonMark::FixupAutoLinksFilter.to_html(html, @options) |
|
26 |
end |
|
29 | 27 | |
30 |
def format(markdown)
|
|
31 |
Redmine::WikiFormatting::CommonMark::MarkdownFilter.to_html(markdown, Redmine::WikiFormatting::CommonMark::PIPELINE_CONFIG)
|
|
32 |
end
|
|
28 |
def format(markdown) |
|
29 |
Redmine::WikiFormatting::CommonMark::MarkdownFilter.to_html(markdown, Redmine::WikiFormatting::CommonMark::PIPELINE_CONFIG) |
|
30 |
end |
|
33 | 31 | |
34 |
def setup
|
|
35 |
@options = { }
|
|
36 |
end
|
|
32 |
def setup |
|
33 |
@options = { } |
|
34 |
end |
|
37 | 35 | |
38 |
def test_should_fixup_autolinked_user_references
|
|
39 |
text = "user:user@example.org"
|
|
40 |
assert_equal "<p>#{text}</p>", filter(format(text))
|
|
41 |
text = "@user@example.org"
|
|
42 |
assert_equal "<p>#{text}</p>", filter(format(text))
|
|
43 |
end
|
|
36 |
def test_should_fixup_autolinked_user_references |
|
37 |
text = "user:user@example.org" |
|
38 |
assert_equal "<p>#{text}</p>", filter(format(text)) |
|
39 |
text = "@user@example.org" |
|
40 |
assert_equal "<p>#{text}</p>", filter(format(text)) |
|
41 |
end |
|
44 | 42 | |
45 |
def test_should_fixup_autolinked_hires_files |
|
46 |
text = "printscreen@2x.png" |
|
47 |
assert_equal "<p>#{text}</p>", filter(format(text)) |
|
48 |
end |
|
43 |
def test_should_fixup_autolinked_hires_files |
|
44 |
text = "printscreen@2x.png" |
|
45 |
assert_equal "<p>#{text}</p>", filter(format(text)) |
|
49 | 46 |
end |
50 | 47 |
end |
test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb | ||
---|---|---|
20 | 20 |
require_relative '../../../../../test_helper' |
21 | 21 | |
22 | 22 |
class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase |
23 |
if Object.const_defined?(:Commonmarker) |
|
24 | ||
25 |
def setup |
|
26 |
@formatter = Redmine::WikiFormatting::CommonMark::Formatter |
|
27 |
end |
|
23 |
def setup |
|
24 |
@formatter = Redmine::WikiFormatting::CommonMark::Formatter |
|
25 |
end |
|
28 | 26 | |
29 |
def to_html(text)
|
|
30 |
@formatter.new(text).to_html
|
|
31 |
end
|
|
27 |
def to_html(text) |
|
28 |
@formatter.new(text).to_html |
|
29 |
end |
|
32 | 30 | |
33 |
def test_should_render_hard_breaks
|
|
34 |
html ="<p>foo<br>\nbar</p>"
|
|
35 |
assert_equal html, to_html("foo\\\nbar")
|
|
36 |
assert_equal html, to_html("foo \nbar")
|
|
37 |
end
|
|
31 |
def test_should_render_hard_breaks |
|
32 |
html ="<p>foo<br>\nbar</p>" |
|
33 |
assert_equal html, to_html("foo\\\nbar") |
|
34 |
assert_equal html, to_html("foo \nbar") |
|
35 |
end |
|
38 | 36 | |
39 |
def test_should_render_soft_breaks
|
|
40 |
assert_equal "<p>foo<br>\nbar</p>", to_html("foo\nbar")
|
|
41 |
end
|
|
37 |
def test_should_render_soft_breaks |
|
38 |
assert_equal "<p>foo<br>\nbar</p>", to_html("foo\nbar") |
|
39 |
end |
|
42 | 40 | |
43 |
def test_syntax_error_in_image_reference_should_not_raise_exception
|
|
44 |
assert to_html("!>[](foo.png)")
|
|
45 |
end
|
|
41 |
def test_syntax_error_in_image_reference_should_not_raise_exception |
|
42 |
assert to_html("!>[](foo.png)") |
|
43 |
end |
|
46 | 44 | |
47 |
def test_empty_image_should_not_raise_exception
|
|
48 |
assert to_html("![]()")
|
|
49 |
end
|
|
45 |
def test_empty_image_should_not_raise_exception |
|
46 |
assert to_html("![]()") |
|
47 |
end |
|
50 | 48 | |
51 |
def test_inline_style
|
|
52 |
assert_equal "<p><strong>foo</strong></p>", to_html("**foo**")
|
|
53 |
end
|
|
49 |
def test_inline_style |
|
50 |
assert_equal "<p><strong>foo</strong></p>", to_html("**foo**") |
|
51 |
end |
|
54 | 52 | |
55 |
def test_not_set_intra_emphasis
|
|
56 |
assert_equal "<p>foo_bar_baz</p>", to_html("foo_bar_baz")
|
|
57 |
end
|
|
53 |
def test_not_set_intra_emphasis |
|
54 |
assert_equal "<p>foo_bar_baz</p>", to_html("foo_bar_baz") |
|
55 |
end |
|
58 | 56 | |
59 |
def test_wiki_links_should_be_preserved
|
|
60 |
text = 'This is a wiki link: [[Foo]]'
|
|
61 |
assert_include '[[Foo]]', to_html(text)
|
|
62 |
end
|
|
57 |
def test_wiki_links_should_be_preserved |
|
58 |
text = 'This is a wiki link: [[Foo]]' |
|
59 |
assert_include '[[Foo]]', to_html(text) |
|
60 |
end |
|
63 | 61 | |
64 |
def test_redmine_links_with_double_quotes_should_be_preserved
|
|
65 |
text = 'This is a redmine link: version:"1.0"'
|
|
66 |
assert_include 'version:"1.0"', to_html(text)
|
|
67 |
end
|
|
62 |
def test_redmine_links_with_double_quotes_should_be_preserved |
|
63 |
text = 'This is a redmine link: version:"1.0"' |
|
64 |
assert_include 'version:"1.0"', to_html(text) |
|
65 |
end |
|
68 | 66 | |
69 |
def test_links_by_id_should_be_preserved
|
|
70 |
text = "[project#3]"
|
|
71 |
assert_equal "<p>#{text}</p>", to_html(text)
|
|
72 |
end
|
|
67 |
def test_links_by_id_should_be_preserved |
|
68 |
text = "[project#3]" |
|
69 |
assert_equal "<p>#{text}</p>", to_html(text) |
|
70 |
end |
|
73 | 71 | |
74 |
def test_links_to_users_should_be_preserved
|
|
75 |
text = "[@login]"
|
|
76 |
assert_equal "<p>#{text}</p>", to_html(text)
|
|
77 |
text = "[user:login]"
|
|
78 |
assert_equal "<p>#{text}</p>", to_html(text)
|
|
79 |
text = "user:user@example.org"
|
|
80 |
assert_equal "<p>#{text}</p>", to_html(text)
|
|
81 |
text = "[user:user@example.org]"
|
|
82 |
assert_equal "<p>#{text}</p>", to_html(text)
|
|
83 |
text = "@user@example.org"
|
|
84 |
assert_equal "<p>#{text}</p>", to_html(text)
|
|
85 |
text = "[@user@example.org]"
|
|
86 |
assert_equal "<p>#{text}</p>", to_html(text)
|
|
87 |
end
|
|
72 |
def test_links_to_users_should_be_preserved |
|
73 |
text = "[@login]" |
|
74 |
assert_equal "<p>#{text}</p>", to_html(text) |
|
75 |
text = "[user:login]" |
|
76 |
assert_equal "<p>#{text}</p>", to_html(text) |
|
77 |
text = "user:user@example.org" |
|
78 |
assert_equal "<p>#{text}</p>", to_html(text) |
|
79 |
text = "[user:user@example.org]" |
|
80 |
assert_equal "<p>#{text}</p>", to_html(text) |
|
81 |
text = "@user@example.org" |
|
82 |
assert_equal "<p>#{text}</p>", to_html(text) |
|
83 |
text = "[@user@example.org]" |
|
84 |
assert_equal "<p>#{text}</p>", to_html(text) |
|
85 |
end |
|
88 | 86 | |
89 |
def test_files_with_at_should_not_end_up_as_mailto_links
|
|
90 |
text = "printscreen@2x.png"
|
|
91 |
assert_equal "<p>#{text}</p>", to_html(text)
|
|
92 |
text = "[printscreen@2x.png]"
|
|
93 |
assert_equal "<p>#{text}</p>", to_html(text)
|
|
94 |
end
|
|
87 |
def test_files_with_at_should_not_end_up_as_mailto_links |
|
88 |
text = "printscreen@2x.png" |
|
89 |
assert_equal "<p>#{text}</p>", to_html(text) |
|
90 |
text = "[printscreen@2x.png]" |
|
91 |
assert_equal "<p>#{text}</p>", to_html(text) |
|
92 |
end |
|
95 | 93 | |
96 |
def test_should_support_syntax_highlight |
|
97 |
text = <<~STR |
|
98 |
~~~ruby |
|
99 |
def foo |
|
100 |
end |
|
101 |
~~~ |
|
102 |
STR |
|
103 |
assert_select_in to_html(text), 'pre code.ruby.syntaxhl' do |
|
104 |
assert_select 'span.k', :text => 'def' |
|
105 |
assert_select "[data-language='ruby']" |
|
94 |
def test_should_support_syntax_highlight |
|
95 |
text = <<~STR |
|
96 |
~~~ruby |
|
97 |
def foo |
|
106 | 98 |
end |
99 |
~~~ |
|
100 |
STR |
|
101 |
assert_select_in to_html(text), 'pre code.ruby.syntaxhl' do |
|
102 |
assert_select 'span.k', :text => 'def' |
|
103 |
assert_select "[data-language='ruby']" |
|
107 | 104 |
end |
105 |
end |
|
108 | 106 | |
109 |
def test_should_support_syntax_highlight_for_language_with_special_chars |
|
110 |
text = <<~STR |
|
111 |
~~~c++ |
|
112 |
int main() { |
|
113 |
} |
|
114 |
~~~ |
|
115 |
STR |
|
116 | ||
117 |
assert_select_in to_html(text), 'pre' do |
|
118 |
assert_select 'code[class=?]', "c++ syntaxhl" |
|
119 |
assert_select 'span.kt', :text => 'int' |
|
120 |
assert_select "[data-language=?]", "c++" |
|
121 |
end |
|
107 |
def test_should_support_syntax_highlight_for_language_with_special_chars |
|
108 |
text = <<~STR |
|
109 |
~~~c++ |
|
110 |
int main() { |
|
111 |
} |
|
112 |
~~~ |
|
113 |
STR |
|
114 | ||
115 |
assert_select_in to_html(text), 'pre' do |
|
116 |
assert_select 'code[class=?]', "c++ syntaxhl" |
|
117 |
assert_select 'span.kt', :text => 'int' |
|
118 |
assert_select "[data-language=?]", "c++" |
|
122 | 119 |
end |
120 |
end |
|
123 | 121 | |
124 |
def test_external_links_should_have_external_css_class
|
|
125 |
text = 'This is a [link](http://example.net/)'
|
|
126 |
assert_equal '<p>This is a <a href="http://example.net/" class="external">link</a></p>', to_html(text)
|
|
127 |
end
|
|
122 |
def test_external_links_should_have_external_css_class |
|
123 |
text = 'This is a [link](http://example.net/)' |
|
124 |
assert_equal '<p>This is a <a href="http://example.net/" class="external">link</a></p>', to_html(text) |
|
125 |
end |
|
128 | 126 | |
129 |
def test_locals_links_should_not_have_external_css_class
|
|
130 |
text = 'This is a [link](/issues)'
|
|
131 |
assert_equal '<p>This is a <a href="/issues">link</a></p>', to_html(text)
|
|
132 |
end
|
|
127 |
def test_locals_links_should_not_have_external_css_class |
|
128 |
text = 'This is a [link](/issues)' |
|
129 |
assert_equal '<p>This is a <a href="/issues">link</a></p>', to_html(text) |
|
130 |
end |
|
133 | 131 | |
134 |
def test_markdown_should_not_require_surrounded_empty_line
|
|
135 |
text = <<-STR
|
|
136 |
This is a list: |
|
137 |
* One |
|
138 |
* Two |
|
139 |
STR
|
|
140 |
assert_equal "<p>This is a list:</p>\n<ul>\n<li>One</li>\n<li>Two</li>\n</ul>", to_html(text)
|
|
141 |
end
|
|
132 |
def test_markdown_should_not_require_surrounded_empty_line |
|
133 |
text = <<~STR
|
|
134 |
This is a list:
|
|
135 |
* One
|
|
136 |
* Two
|
|
137 |
STR |
|
138 |
assert_equal "<p>This is a list:</p>\n<ul>\n<li>One</li>\n<li>Two</li>\n</ul>", to_html(text) |
|
139 |
end |
|
142 | 140 | |
143 |
def test_footnotes
|
|
144 |
text = <<~STR
|
|
145 |
This is some text[^1].
|
|
141 |
def test_footnotes |
|
142 |
text = <<~STR |
|
143 |
This is some text[^1]. |
|
146 | 144 | |
147 |
[^1]: This is the foot note
|
|
148 |
STR
|
|
145 |
[^1]: This is the foot note |
|
146 |
STR |
|
149 | 147 | |
150 |
expected = <<~EXPECTED
|
|
151 |
<p>This is some text<sup><a href="#fn-1" id="fnref-1">1</a></sup>.</p>
|
|
152 |
<ol>
|
|
153 |
<li id="fn-1">
|
|
154 |
<p>This is the foot note <a href="#fnref-1" aria-label="Back to reference 1">↩</a></p>
|
|
155 |
</li>
|
|
156 |
</ol>
|
|
157 |
EXPECTED
|
|
148 |
expected = <<~EXPECTED |
|
149 |
<p>This is some text<sup><a href="#fn-1" id="fnref-1">1</a></sup>.</p> |
|
150 |
<ol> |
|
151 |
<li id="fn-1"> |
|
152 |
<p>This is the foot note <a href="#fnref-1" aria-label="Back to reference 1">↩</a></p> |
|
153 |
</li> |
|
154 |
</ol> |
|
155 |
EXPECTED |
|
158 | 156 | |
159 |
assert_equal expected.gsub(%r{[\r\n\t]}, ''), to_html(text).gsub(%r{[\r\n\t]}, '').rstrip |
|
160 |
end |
|
157 |
assert_equal expected.gsub(%r{[\r\n\t]}, ''), to_html(text).gsub(%r{[\r\n\t]}, '').rstrip |
|
158 |
end |
|
159 | ||
160 |
STR_WITH_PRE = [ |
|
161 |
# 0 |
|
162 |
<<~STR.chomp, |
|
163 |
# Title |
|
161 | 164 | |
162 |
STR_WITH_PRE = [ |
|
163 |
# 0 |
|
164 |
<<~STR.chomp, |
|
165 |
# Title |
|
166 |
|
|
167 |
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero. |
|
168 |
STR |
|
169 |
# 1 |
|
170 |
<<~STR.chomp, |
|
171 |
## Heading 2 |
|
172 |
|
|
173 |
~~~ruby |
|
174 |
def foo |
|
175 |
end |
|
176 |
~~~ |
|
177 |
|
|
178 |
Morbi facilisis accumsan orci non pharetra. |
|
179 |
|
|
180 |
~~~ ruby |
|
165 |
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero. |
|
166 |
STR |
|
167 |
# 1 |
|
168 |
<<~STR.chomp, |
|
169 |
## Heading 2 |
|
170 | ||
171 |
~~~ruby |
|
181 | 172 |
def foo |
182 | 173 |
end |
183 |
~~~ |
|
184 |
|
|
185 |
``` |
|
186 |
Pre Content: |
|
187 |
|
|
188 |
## Inside pre |
|
189 |
|
|
190 |
<tag> inside pre block |
|
191 |
|
|
192 |
Morbi facilisis accumsan orci non pharetra. |
|
193 |
``` |
|
194 |
STR |
|
195 |
# 2 |
|
196 |
<<~STR.chomp, |
|
197 |
### Heading 3 |
|
198 |
|
|
199 |
Nulla nunc nisi, egestas in ornare vel, posuere ac libero. |
|
200 |
STR |
|
201 |
] |
|
202 | ||
203 |
def test_get_section_should_ignore_pre_content |
|
204 |
text = STR_WITH_PRE.join("\n\n") |
|
205 | ||
206 |
assert_section_with_hash STR_WITH_PRE[1..2].join("\n\n"), text, 2 |
|
207 |
assert_section_with_hash STR_WITH_PRE[2], text, 3 |
|
208 |
end |
|
174 |
~~~ |
|
209 | 175 | |
210 |
def test_get_section_should_not_recognize_double_hash_issue_reference_as_heading |
|
211 |
text = <<~STR |
|
212 |
## Section A |
|
176 |
Morbi facilisis accumsan orci non pharetra. |
|
213 | 177 | |
214 |
This text is a part of Section A. |
|
178 |
~~~ ruby |
|
179 |
def foo |
|
180 |
end |
|
181 |
~~~ |
|
215 | 182 | |
216 |
##1 : This is an issue reference, not an ATX heading. |
|
183 |
``` |
|
184 |
Pre Content: |
|
217 | 185 | |
218 |
This text is also a part of Section A. |
|
219 |
<!-- Section A ends here --> |
|
220 |
STR |
|
186 |
## Inside pre |
|
221 | 187 | |
222 |
assert_section_with_hash text.chomp, text, 1 |
|
223 |
end |
|
188 |
<tag> inside pre block |
|
224 | 189 | |
225 |
def test_update_section_should_not_escape_pre_content_outside_section |
|
226 |
text = STR_WITH_PRE.join("\n\n") |
|
227 |
replacement = "New text" |
|
190 |
Morbi facilisis accumsan orci non pharetra. |
|
191 |
``` |
|
192 |
STR |
|
193 |
# 2 |
|
194 |
<<~STR.chomp, |
|
195 |
### Heading 3 |
|
228 | 196 | |
229 |
assert_equal [STR_WITH_PRE[0..1], "New text"].flatten.join("\n\n"),
|
|
230 |
@formatter.new(text).update_section(3, replacement)
|
|
231 |
end
|
|
197 |
Nulla nunc nisi, egestas in ornare vel, posuere ac libero.
|
|
198 |
STR
|
|
199 |
]
|
|
232 | 200 | |
233 |
def test_should_emphasize_text |
|
234 |
text = 'This _text_ should be emphasized' |
|
235 |
assert_equal '<p>This <em>text</em> should be emphasized</p>', to_html(text) |
|
236 |
end |
|
201 |
def test_get_section_should_ignore_pre_content |
|
202 |
text = STR_WITH_PRE.join("\n\n") |
|
237 | 203 | |
238 |
def test_should_strike_through_text |
|
239 |
text = 'This ~~text~~ should be striked through' |
|
240 |
assert_equal '<p>This <del>text</del> should be striked through</p>', to_html(text) |
|
241 |
end |
|
204 |
assert_section_with_hash STR_WITH_PRE[1..2].join("\n\n"), text, 2 |
|
205 |
assert_section_with_hash STR_WITH_PRE[2], text, 3 |
|
206 |
end |
|
242 | 207 | |
243 |
def test_should_autolink_urls_and_emails |
|
244 |
[ |
|
245 |
["http://example.org", '<p><a href="http://example.org" class="external">http://example.org</a></p>'], |
|
246 |
["http://www.redmine.org/projects/redmine/issues?utf8=✓", |
|
247 |
'<p><a href="http://www.redmine.org/projects/redmine/issues?utf8=%E2%9C%93" class="external">http://www.redmine.org/projects/redmine/issues?utf8=✓</a></p>'], |
|
248 |
['[Letters](https://yandex.ru/search/?text=кол-во)', '<p><a href="https://yandex.ru/search/?text=%D0%BA%D0%BE%D0%BB-%D0%B2%D0%BE" class="external">Letters</a></p>'], |
|
249 |
["www.example.org", '<p><a href="http://www.example.org" class="external">www.example.org</a></p>'], |
|
250 |
["user@example.org", '<p><a href="mailto:user@example.org" class="email">user@example.org</a></p>'] |
|
251 |
].each do |text, html| |
|
252 |
assert_equal html, to_html(text) |
|
253 |
end |
|
254 |
end |
|
208 |
def test_get_section_should_not_recognize_double_hash_issue_reference_as_heading |
|
209 |
text = <<~STR |
|
210 |
## Section A |
|
211 | ||
212 |
This text is a part of Section A. |
|
213 | ||
214 |
##1 : This is an issue reference, not an ATX heading. |
|
215 | ||
216 |
This text is also a part of Section A. |
|
217 |
<!-- Section A ends here --> |
|
218 |
STR |
|
219 | ||
220 |
assert_section_with_hash text.chomp, text, 1 |
|
221 |
end |
|
255 | 222 | |
256 |
def test_should_support_html_tables |
|
257 |
text = '<table style="background: red"><tr><td>Cell</td></tr></table>' |
|
258 |
assert_equal '<table><tr><td>Cell</td></tr></table>', to_html(text) |
|
223 |
def test_update_section_should_not_escape_pre_content_outside_section |
|
224 |
text = STR_WITH_PRE.join("\n\n") |
|
225 |
replacement = "New text" |
|
226 | ||
227 |
assert_equal [STR_WITH_PRE[0..1], "New text"].flatten.join("\n\n"), |
|
228 |
@formatter.new(text).update_section(3, replacement) |
|
229 |
end |
|
230 | ||
231 |
def test_should_emphasize_text |
|
232 |
text = 'This _text_ should be emphasized' |
|
233 |
assert_equal '<p>This <em>text</em> should be emphasized</p>', to_html(text) |
|
234 |
end |
|
235 | ||
236 |
def test_should_strike_through_text |
|
237 |
text = 'This ~~text~~ should be striked through' |
|
238 |
assert_equal '<p>This <del>text</del> should be striked through</p>', to_html(text) |
|
239 |
end |
|
240 | ||
241 |
def test_should_autolink_urls_and_emails |
|
242 |
[ |
|
243 |
["http://example.org", '<p><a href="http://example.org" class="external">http://example.org</a></p>'], |
|
244 |
["http://www.redmine.org/projects/redmine/issues?utf8=✓", |
|
245 |
'<p><a href="http://www.redmine.org/projects/redmine/issues?utf8=%E2%9C%93" class="external">http://www.redmine.org/projects/redmine/issues?utf8=✓</a></p>'], |
|
246 |
['[Letters](https://yandex.ru/search/?text=кол-во)', '<p><a href="https://yandex.ru/search/?text=%D0%BA%D0%BE%D0%BB-%D0%B2%D0%BE" class="external">Letters</a></p>'], |
|
247 |
["www.example.org", '<p><a href="http://www.example.org" class="external">www.example.org</a></p>'], |
|
248 |
["user@example.org", '<p><a href="mailto:user@example.org" class="email">user@example.org</a></p>'] |
|
249 |
].each do |text, html| |
|
250 |
assert_equal html, to_html(text) |
|
259 | 251 |
end |
252 |
end |
|
260 | 253 | |
261 |
def test_should_remove_unsafe_uris |
|
262 |
[ |
|
263 |
['<img src="data:foobar">', '<img>'], |
|
264 |
['<a href="javascript:bla">click me</a>', '<p><a>click me</a></p>'], |
|
265 |
].each do |text, html| |
|
266 |
assert_equal html, to_html(text) |
|
267 |
end |
|
254 |
def test_should_support_html_tables |
|
255 |
text = '<table style="background: red"><tr><td>Cell</td></tr></table>' |
|
256 |
assert_equal '<table><tr><td>Cell</td></tr></table>', to_html(text) |
|
257 |
end |
|
258 | ||
259 |
def test_should_remove_unsafe_uris |
|
260 |
[ |
|
261 |
['<img src="data:foobar">', '<img>'], |
|
262 |
['<a href="javascript:bla">click me</a>', '<p><a>click me</a></p>'], |
|
263 |
].each do |text, html| |
|
264 |
assert_equal html, to_html(text) |
|
268 | 265 |
end |
266 |
end |
|
269 | 267 | |
270 |
def test_should_escape_unwanted_tags |
|
268 |
def test_should_escape_unwanted_tags |
|
269 |
[ |
|
271 | 270 |
[ |
272 |
[ |
|
273 |
%[<p>sit<br>amet <style>.foo { color: #fff; }</style> <script>alert("hello world");</script></p>], |
|
274 |
%[sit<br/>amet <style>.foo { color: #fff; }</style> <script>alert("hello world");</script>] |
|
275 |
] |
|
276 |
].each do |expected, input| |
|
277 |
assert_equal expected, to_html(input) |
|
278 |
end |
|
271 |
%[<p>sit<br>amet <style>.foo { color: #fff; }</style> <script>alert("hello world");</script></p>], |
|
272 |
%[sit<br/>amet <style>.foo { color: #fff; }</style> <script>alert("hello world");</script>] |
|
273 |
] |
|
274 |
].each do |expected, input| |
|
275 |
assert_equal expected, to_html(input) |
|
279 | 276 |
end |
277 |
end |
|
280 | 278 | |
281 |
def test_should_support_task_list
|
|
282 |
text = <<~STR
|
|
283 |
Task list:
|
|
284 |
* [ ] Task 1
|
|
285 |
* [x] Task 2
|
|
286 |
STR
|
|
287 | ||
288 |
expected = <<~EXPECTED
|
|
289 |
<p>Task list:</p>
|
|
290 |
<ul class="task-list">
|
|
291 |
<li class="task-list-item">
|
|
292 |
<input type="checkbox" class="task-list-item-checkbox" disabled> Task 1
|
|
293 |
</li>
|
|
294 |
<li class="task-list-item">
|
|
295 |
<input type="checkbox" class="task-list-item-checkbox" checked disabled> Task 2</li>
|
|
296 |
</ul>
|
|
297 |
EXPECTED
|
|
298 | ||
299 |
assert_equal expected.gsub(%r{[\r\n\t]}, ''), to_html(text).gsub(%r{[\r\n\t]}, '').rstrip
|
|
300 |
end
|
|
279 |
def test_should_support_task_list |
|
280 |
text = <<~STR |
|
281 |
Task list: |
|
282 |
* [ ] Task 1 |
|
283 |
* [x] Task 2 |
|
284 |
STR |
|
285 | ||
286 |
expected = <<~EXPECTED |
|
287 |
<p>Task list:</p> |
|
288 |
<ul class="task-list"> |
|
289 |
<li class="task-list-item"> |
|
290 |
<input type="checkbox" class="task-list-item-checkbox" disabled> Task 1 |
|
291 |
</li> |
|
292 |
<li class="task-list-item"> |
|
293 |
<input type="checkbox" class="task-list-item-checkbox" checked disabled> Task 2</li> |
|
294 |
</ul> |
|
295 |
EXPECTED |
|
296 | ||
297 |
assert_equal expected.gsub(%r{[\r\n\t]}, ''), to_html(text).gsub(%r{[\r\n\t]}, '').rstrip |
|
298 |
end |
|
301 | 299 | |
302 |
private
|
|
300 |
private |
|
303 | 301 | |
304 |
def assert_section_with_hash(expected, text, index)
|
|
305 |
result = @formatter.new(text).get_section(index)
|
|
302 |
def assert_section_with_hash(expected, text, index) |
|
303 |
result = @formatter.new(text).get_section(index) |
|
306 | 304 | |
307 |
assert_kind_of Array, result |
|
308 |
assert_equal 2, result.size |
|
309 |
assert_equal expected, result.first, "section content did not match" |
|
310 |
assert_equal ActiveSupport::Digest.hexdigest(expected), result.last, "section hash did not match" |
|
311 |
end |
|
305 |
assert_kind_of Array, result |
|
306 |
assert_equal 2, result.size |
|
307 |
assert_equal expected, result.first, "section content did not match" |
|
308 |
assert_equal ActiveSupport::Digest.hexdigest(expected), result.last, "section hash did not match" |
|
312 | 309 |
end |
313 | 310 |
end |
test/unit/lib/redmine/wiki_formatting/common_mark/markdown_filter_test.rb | ||
---|---|---|
18 | 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | 19 | |
20 | 20 |
require_relative '../../../../../test_helper' |
21 |
require 'redmine/wiki_formatting/common_mark/markdown_filter' |
|
21 | 22 | |
22 |
if Object.const_defined?(:Commonmarker) |
|
23 |
require 'redmine/wiki_formatting/common_mark/markdown_filter' |
|
24 | ||
25 |
class Redmine::WikiFormatting::CommonMark::MarkdownFilterTest < ActiveSupport::TestCase |
|
26 |
def filter(markdown) |
|
27 |
Redmine::WikiFormatting::CommonMark::MarkdownFilter.to_html(markdown) |
|
28 |
end |
|
23 |
class Redmine::WikiFormatting::CommonMark::MarkdownFilterTest < ActiveSupport::TestCase |
|
24 |
def filter(markdown) |
|
25 |
Redmine::WikiFormatting::CommonMark::MarkdownFilter.to_html(markdown) |
|
26 |
end |
|
29 | 27 | |
30 |
# just a basic sanity test. more formatting tests in the formatter_test |
|
31 |
def test_should_render_markdown |
|
32 |
assert_equal "<p><strong>bold</strong></p>", filter("**bold**") |
|
33 |
end |
|
28 |
# just a basic sanity test. more formatting tests in the formatter_test |
|
29 |
def test_should_render_markdown |
|
30 |
assert_equal "<p><strong>bold</strong></p>", filter("**bold**") |
|
34 | 31 |
end |
35 | 32 |
end |
test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb | ||
---|---|---|
18 | 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | 19 | |
20 | 20 |
require_relative '../../../../../test_helper' |
21 |
require 'redmine/wiki_formatting/common_mark/sanitization_filter' |
|
21 | 22 | |
22 |
if Object.const_defined?(:Commonmarker) |
|
23 |
require 'redmine/wiki_formatting/common_mark/sanitization_filter' |
|
23 |
class Redmine::WikiFormatting::CommonMark::SanitizationFilterTest < ActiveSupport::TestCase |
|
24 |
def filter(html) |
|
25 |
Redmine::WikiFormatting::CommonMark::SanitizationFilter.to_html(html, @options) |
|
26 |
end |
|
24 | 27 | |
25 |
class Redmine::WikiFormatting::CommonMark::SanitizationFilterTest < ActiveSupport::TestCase |
|
26 |
def filter(html) |
|
27 |
Redmine::WikiFormatting::CommonMark::SanitizationFilter.to_html(html, @options) |
|
28 |
end |
|
28 |
def setup |
|
29 |
@options = { } |
|
30 |
end |
|
29 | 31 | |
30 |
def setup |
|
31 |
@options = { } |
|
32 |
end |
|
32 |
def test_should_filter_tags |
|
33 |
input = %(<textarea>foo</textarea> <blink>dont blink</blink>) |
|
34 |
assert_equal %(foo dont blink), filter(input) |
|
35 |
end |
|
33 | 36 | |
34 |
def test_should_filter_tags
|
|
35 |
input = %(<textarea>foo</textarea> <blink>dont blink</blink>)
|
|
36 |
assert_equal %(foo dont blink), filter(input)
|
|
37 |
end
|
|
37 |
def test_should_sanitize_attributes
|
|
38 |
input = %(<a href="foo" onclick="bar" baz="foo">link</a>)
|
|
39 |
assert_equal %(<a href="foo">link</a>), filter(input)
|
|
40 |
end |
|
38 | 41 | |
39 |
def test_should_sanitize_attributes
|
|
40 |
input = %(<a href="foo" onclick="bar" baz="foo">link</a>)
|
|
41 |
assert_equal %(<a href="foo">link</a>), filter(input)
|
|
42 |
end
|
|
42 |
def test_should_allow_relative_links
|
|
43 |
input = %(<a href="foo/bar">foo/bar</a>)
|
|
44 |
assert_equal input, filter(input)
|
|
45 |
end |
|
43 | 46 | |
44 |
def test_should_allow_relative_links |
|
45 |
input = %(<a href="foo/bar">foo/bar</a>) |
|
46 |
assert_equal input, filter(input) |
|
47 |
end |
|
47 |
def test_should_support_footnotes |
|
48 |
input = %(<a href="#fn-1" id="fnref-1">foo</a>) |
|
49 |
assert_equal input, filter(input) |
|
50 |
input = %(<ol><li id="fn-1">footnote</li></ol>) |
|
51 |
assert_equal input, filter(input) |
|
52 |
end |
|
48 | 53 | |
49 |
def test_should_support_footnotes
|
|
50 |
input = %(<a href="#fn-1" id="fnref-1">foo</a>)
|
|
51 |
assert_equal input, filter(input)
|
|
52 |
input = %(<ol><li id="fn-1">footnote</li></ol>)
|
|
53 |
assert_equal input, filter(input)
|
|
54 |
end
|
|
54 |
def test_should_remove_invalid_ids
|
|
55 |
input = %(<a href="#fn1" id="foo">foo</a>)
|
|
56 |
assert_equal %(<a href="#fn1">foo</a>), filter(input)
|
|
57 |
input = %(<ol><li id="foo">footnote</li></ol>)
|
|
58 |
assert_equal %(<ol><li>footnote</li></ol>), filter(input)
|
|
59 |
end |
|
55 | 60 | |
56 |
def test_should_remove_invalid_ids |
|
57 |
input = %(<a href="#fn1" id="foo">foo</a>) |
|
58 |
assert_equal %(<a href="#fn1">foo</a>), filter(input) |
|
59 |
input = %(<ol><li id="foo">footnote</li></ol>) |
|
60 |
assert_equal %(<ol><li>footnote</li></ol>), filter(input) |
|
61 |
end |
|
61 |
def test_should_allow_class_on_code_only |
|
62 |
input = %(<p class="foo">bar</p>) |
|
63 |
assert_equal %(<p>bar</p>), filter(input) |
|
62 | 64 | |
63 |
def test_should_allow_class_on_code_only |
|
64 |
input = %(<p class="foo">bar</p>) |
|
65 |
assert_equal %(<p>bar</p>), filter(input) |
|
65 |
input = %(<code class="language-ruby">foo</code>) |
|
66 |
assert_equal input, filter(input) |
|
66 | 67 | |
67 |
input = %(<code class="language-ruby">foo</code>) |
|
68 |
assert_equal input, filter(input) |
|
68 |
input = %(<code class="foo">foo</code>) |
|
69 |
assert_equal %(<code>foo</code>), filter(input) |
|
70 |
end |
|
69 | 71 | |
70 |
input = %(<code class="foo">foo</code>) |
|
71 |
assert_equal %(<code>foo</code>), filter(input) |
|
72 |
def test_should_allow_links_with_safe_url_schemes |
|
73 |
%w(http https ftp ssh foo).each do |scheme| |
|
74 |
input = %(<a href="#{scheme}://example.org/">foo</a>) |
|
75 |
assert_equal input, filter(input) |
|
72 | 76 |
end |
77 |
end |
|
73 | 78 | |
74 |
def test_should_allow_links_with_safe_url_schemes |
|
75 |
%w(http https ftp ssh foo).each do |scheme| |
|
76 |
input = %(<a href="#{scheme}://example.org/">foo</a>) |
|
77 |
assert_equal input, filter(input) |
|
78 |
end |
|
79 |
end |
|
79 |
def test_should_allow_mailto_links |
|
80 |
input = %(<a href="mailto:foo@example.org">bar</a>) |
|
81 |
assert_equal input, filter(input) |
|
82 |
end |
|
80 | 83 | |
81 |
def test_should_allow_mailto_links |
|
82 |
input = %(<a href="mailto:foo@example.org">bar</a>) |
|
83 |
assert_equal input, filter(input) |
|
84 |
end |
|
84 |
def test_should_remove_empty_link |
|
85 |
input = %(<a href="">bar</a>) |
|
86 |
assert_equal %(<a>bar</a>), filter(input) |
|
87 |
input = %(<a href=" ">bar</a>) |
|
88 |
assert_equal %(<a>bar</a>), filter(input) |
|
89 |
end |
|
90 | ||
91 |
# samples taken from the Sanitize test suite |
|
92 |
# rubocop:disable Layout/LineLength |
|
93 |
STRINGS = [ |
|
94 |
[ |
|
95 |
'<span style="color: #333; background: url(\'https://example.com/evil.svg\')">hello</span>"', |
|
96 |
'<span style="color: #333; ">hello</span>"' |
|
97 |
], |
|
98 |
[ |
|
99 |
'<img src="photo.jpg" style="min-width: 100px; max-width: 200px; min-height: 100px; max-height: 200px;">', |
|
100 |
'<img src="photo.jpg" style="min-width: 100px; max-width: 200px; min-height: 100px; max-height: 200px;">' |
|
101 |
], |
|
102 |
[ |
|
103 |
'<b>Lo<!-- comment -->rem</b> <a href="pants" title="foo" style="text-decoration: underline;">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br/>amet <style>.foo { color: #fff; }</style> <script>alert("hello world");</script>', |
|
104 |
'<b>Lorem</b> <a href="pants" title="foo">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br>amet .foo { color: #fff; } ' |
|
105 |
], |
|
106 |
[ |
|
107 |
'Lo<!-- comment -->rem</b> <a href=pants title="foo>ipsum <a href="http://foo.com/"><strong>dolor</a></strong> sit<br/>amet <script>alert("hello world");', |
|
108 |
'Lorem <a href="pants" title="foo>ipsum <a href="><strong>dolor</strong></a> sit<br>amet ' |
|
109 |
], |
|
110 |
[ |
|
111 |
'<p>a</p><blockquote>b', |
|
112 |
'<p>a</p><blockquote>b</blockquote>' |
|
113 |
], |
|
114 |
[ |
|
115 |
'<b>Lo<!-- comment -->rem</b> <a href="javascript:pants" title="foo">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br/>amet <<foo>script>alert("hello world");</script>', |
|
116 |
'<b>Lorem</b> <a title="foo">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br>amet <script>alert("hello world");' |
|
117 |
] |
|
118 |
] |
|
119 |
# rubocop:enable Layout/LineLength |
|
85 | 120 | |
86 |
def test_should_remove_empty_link |
|
87 |
input = %(<a href="">bar</a>) |
|
88 |
assert_equal %(<a>bar</a>), filter(input) |
|
89 |
input = %(<a href=" ">bar</a>) |
|
90 |
assert_equal %(<a>bar</a>), filter(input) |
|
121 |
def test_should_sanitize_html_strings |
|
122 |
STRINGS.each do |input, expected| |
|
123 |
assert_equal expected, filter(input) |
|
91 | 124 |
end |
125 |
end |
|
126 | ||
127 |
# samples taken from the Sanitize test suite |
|
128 |
PROTOCOLS = { |
|
129 |
'protocol-based JS injection: simple, no spaces' => [ |
|
130 |
'<a href="javascript:alert(\'XSS\');">foo</a>', |
|
131 |
'<a>foo</a>' |
|
132 |
], |
|
133 | ||
134 |
'protocol-based JS injection: simple, spaces before' => [ |
|
135 |
'<a href="javascript :alert(\'XSS\');">foo</a>', |
|
136 |
'<a>foo</a>' |
|
137 |
], |
|
138 | ||
139 |
'protocol-based JS injection: simple, spaces after' => [ |
|
140 |
'<a href="javascript: alert(\'XSS\');">foo</a>', |
|
141 |
'<a>foo</a>' |
|
142 |
], |
|
143 | ||
144 |
'protocol-based JS injection: simple, spaces before and after' => [ |
|
145 |
'<a href="javascript : alert(\'XSS\');">foo</a>', |
|
146 |
'<a>foo</a>' |
|
147 |
], |
|
148 | ||
149 |
'protocol-based JS injection: preceding colon' => [ |
|
150 |
'<a href=":javascript:alert(\'XSS\');">foo</a>', |
|
151 |
'<a>foo</a>' |
|
152 |
], |
|
153 | ||
154 |
'protocol-based JS injection: UTF-8 encoding' => [ |
|
155 |
'<a href="javascript:">foo</a>', |
|
156 |
'<a>foo</a>' |
|
157 |
], |
|
158 | ||
159 |
'protocol-based JS injection: long UTF-8 encoding' => [ |
|
160 |
'<a href="javascript:">foo</a>', |
|
161 |
'<a>foo</a>' |
|
162 |
], |
|
92 | 163 | |
93 |
# samples taken from the Sanitize test suite |
|
94 | 164 |
# rubocop:disable Layout/LineLength |
95 |
STRINGS = [ |
|
96 |
[ |
|
97 |
'<span style="color: #333; background: url(\'https://example.com/evil.svg\')">hello</span>"', |
|
98 |
'<span style="color: #333; ">hello</span>"' |
|
99 |
], |
|
100 |
[ |
|
101 |
'<img src="photo.jpg" style="min-width: 100px; max-width: 200px; min-height: 100px; max-height: 200px;">', |
|
102 |
'<img src="photo.jpg" style="min-width: 100px; max-width: 200px; min-height: 100px; max-height: 200px;">' |
|
103 |
], |
|
104 |
[ |
|
105 |
'<b>Lo<!-- comment -->rem</b> <a href="pants" title="foo" style="text-decoration: underline;">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br/>amet <style>.foo { color: #fff; }</style> <script>alert("hello world");</script>', |
|
106 |
'<b>Lorem</b> <a href="pants" title="foo">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br>amet .foo { color: #fff; } ' |
|
107 |
], |
|
108 |
[ |
|
109 |
'Lo<!-- comment -->rem</b> <a href=pants title="foo>ipsum <a href="http://foo.com/"><strong>dolor</a></strong> sit<br/>amet <script>alert("hello world");', |
|
110 |
'Lorem <a href="pants" title="foo>ipsum <a href="><strong>dolor</strong></a> sit<br>amet ' |
|
111 |
], |
|
112 |
[ |
|
113 |
'<p>a</p><blockquote>b', |
|
114 |
'<p>a</p><blockquote>b</blockquote>' |
|
115 |
], |
|
116 |
[ |
|
117 |
'<b>Lo<!-- comment -->rem</b> <a href="javascript:pants" title="foo">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br/>amet <<foo>script>alert("hello world");</script>', |
|
118 |
'<b>Lorem</b> <a title="foo">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br>amet <script>alert("hello world");' |
|
119 |
] |
|
120 |
] |
|
165 |
'protocol-based JS injection: long UTF-8 encoding without semicolons' => [ |
|
166 |
'<a href=javascript:alert('XSS')>foo</a>', |
|
167 |
'<a>foo</a>' |
|
168 |
], |
|
121 | 169 |
# rubocop:enable Layout/LineLength |
122 | 170 | |
123 |
def test_should_sanitize_html_strings |
|
124 |
STRINGS.each do |input, expected| |
|
125 |
assert_equal expected, filter(input) |
|
126 |
end |
|
127 |
end |
|
128 | ||
129 |
# samples taken from the Sanitize test suite |
|
130 |
PROTOCOLS = { |
|
131 |
'protocol-based JS injection: simple, no spaces' => [ |
|
132 |
'<a href="javascript:alert(\'XSS\');">foo</a>', |
|
133 |
'<a>foo</a>' |
|
134 |
], |
|
135 | ||
136 |
'protocol-based JS injection: simple, spaces before' => [ |
|
137 |
'<a href="javascript :alert(\'XSS\');">foo</a>', |
|
138 |
'<a>foo</a>' |
|
139 |
], |
|
140 | ||
141 |
'protocol-based JS injection: simple, spaces after' => [ |
|
142 |
'<a href="javascript: alert(\'XSS\');">foo</a>', |
|
143 |
'<a>foo</a>' |
|
144 |
], |
|
145 | ||
146 |
'protocol-based JS injection: simple, spaces before and after' => [ |
|
147 |
'<a href="javascript : alert(\'XSS\');">foo</a>', |
|
148 |
'<a>foo</a>' |
|
149 |
], |
|
150 | ||
151 |
'protocol-based JS injection: preceding colon' => [ |
|
152 |
'<a href=":javascript:alert(\'XSS\');">foo</a>', |
|
153 |
'<a>foo</a>' |
|
154 |
], |
|
155 | ||
156 |
'protocol-based JS injection: UTF-8 encoding' => [ |
|
157 |
'<a href="javascript:">foo</a>', |
|
158 |
'<a>foo</a>' |
|
159 |
], |
|
160 | ||
161 |
'protocol-based JS injection: long UTF-8 encoding' => [ |
|
162 |
'<a href="javascript:">foo</a>', |
|
163 |
'<a>foo</a>' |
|
164 |
], |
|
165 | ||
166 |
# rubocop:disable Layout/LineLength |
|
167 |
'protocol-based JS injection: long UTF-8 encoding without semicolons' => [ |
|
168 |
'<a href=javascript:alert('XSS')>foo</a>', |
|
169 |
'<a>foo</a>' |
|
170 |
], |
|
171 |
# rubocop:enable Layout/LineLength |
|
172 | ||
173 |
'protocol-based JS injection: hex encoding' => [ |
|
174 |
'<a href="javascript:">foo</a>', |
|
175 |
'<a>foo</a>' |
|
176 |
], |
|
177 | ||
178 |
'protocol-based JS injection: long hex encoding' => [ |
|
179 |
'<a href="javascript:">foo</a>', |
|
180 |
'<a>foo</a>' |
|
181 |
], |
|
182 | ||
183 |
'protocol-based JS injection: hex encoding without semicolons' => [ |
|
184 |
'<a href=javascript:alert('XSS')>foo</a>', |
|
185 |
'<a>foo</a>' |
|
186 |
], |
|
187 | ||
188 |
'protocol-based JS injection: null char' => [ |
|
189 |
"<img src=java\0script:alert(\"XSS\")>", |
|
190 |
'<img src="java">' |
|
191 |
# '<img>' |
|
192 |
], |
|
193 | ||
194 |
'protocol-based JS injection: invalid URL char' => [ |
|
195 |
'<img src=java\script:alert("XSS")>', |
|
196 |
'<img>' |
|
197 |
], |
|
198 | ||
199 |
'protocol-based JS injection: spaces and entities' => [ |
|
200 |
'<img src="  javascript:alert(\'XSS\');">', |
|
201 |
'<img src="">' |
|
202 |
# '<img>' |
|
203 |
], |
|
204 | ||
205 |
'protocol whitespace' => [ |
|
206 |
'<a href=" http://example.com/"></a>', |
|
207 |
'<a href="http://example.com/"></a>' |
|
208 |
], |
|
209 | ||
210 |
'data images sources' => [ |
|
211 |
'<img src="data:image/png;base64,foobar">', |
|
212 |
'<img>' |
|
213 |
], |
|
214 | ||
215 |
'data URIs' => [ |
|
216 |
'<a href="data:text/html;base64,foobar">XSS</a>', |
|
217 |
'<a>XSS</a>' |
|
218 |
], |
|
219 | ||
220 |
'vbscript URIs' => [ |
|
221 |
'<a href="vbscript:foobar">XSS</a>', |
|
222 |
'<a>XSS</a>' |
|
223 |
], |
|
224 |
} |
|
225 | ||
226 |
PROTOCOLS.each do |name, strings| |
|
227 |
test "should not allow #{name}" do |
|
228 |
input, expected = *strings |
|
229 |
assert_equal expected, filter(input) |
|
230 |
end |
|
171 |
'protocol-based JS injection: hex encoding' => [ |
|
172 |
'<a href="javascript:">foo</a>', |
|
173 |
'<a>foo</a>' |
|
174 |
], |
|
175 | ||
176 |
'protocol-based JS injection: long hex encoding' => [ |
|
177 |
'<a href="javascript:">foo</a>', |
|
178 |
'<a>foo</a>' |
|
179 |
], |
|
180 | ||
181 |
'protocol-based JS injection: hex encoding without semicolons' => [ |
|
182 |
'<a href=javascript:alert('XSS')>foo</a>', |
|
183 |
'<a>foo</a>' |
|
184 |
], |
|
185 | ||
186 |
'protocol-based JS injection: null char' => [ |
|
187 |
"<img src=java\0script:alert(\"XSS\")>", |
|
188 |
'<img src="java">' |
|
189 |
# '<img>' |
|
190 |
], |
|
191 | ||
192 |
'protocol-based JS injection: invalid URL char' => [ |
|
193 |
'<img src=java\script:alert("XSS")>', |
|
194 |
'<img>' |
|
195 |
], |
|
196 | ||
197 |
'protocol-based JS injection: spaces and entities' => [ |
|
198 |
'<img src="  javascript:alert(\'XSS\');">', |
|
199 |
'<img src="">' |
|
200 |
# '<img>' |
|
201 |
], |
|
202 | ||
203 |
'protocol whitespace' => [ |
|
204 |
'<a href=" http://example.com/"></a>', |
|
205 |
'<a href="http://example.com/"></a>' |
|
206 |
], |
|
207 | ||
208 |
'data images sources' => [ |
|
209 |
'<img src="data:image/png;base64,foobar">', |
|
210 |
'<img>' |
|
211 |
], |
|
212 | ||
213 |
'data URIs' => [ |
|
214 |
'<a href="data:text/html;base64,foobar">XSS</a>', |
|
215 |
'<a>XSS</a>' |
|
216 |
], |
|
217 | ||
218 |
'vbscript URIs' => [ |
|
219 |
'<a href="vbscript:foobar">XSS</a>', |
|
220 |
'<a>XSS</a>' |
|
221 |
], |
|
222 |
} |
|
223 | ||
224 |
PROTOCOLS.each do |name, strings| |
|
225 |
test "should not allow #{name}" do |
|
226 |
input, expected = *strings |
|
227 |
assert_equal expected, filter(input) |
|
231 | 228 |
end |
232 | 229 |
end |
233 | 230 |
end |
test/unit/lib/redmine/wiki_formatting/common_mark/syntax_highlight_filter_test.rb | ||
---|---|---|
18 | 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | 19 | |
20 | 20 |
require_relative '../../../../../test_helper' |
21 |
if Object.const_defined?(:Commonmarker) |
|
22 |
require 'redmine/wiki_formatting/common_mark/syntax_highlight_filter' |
|
21 |
require 'redmine/wiki_formatting/common_mark/syntax_highlight_filter' |
|
23 | 22 | |
24 |
class Redmine::WikiFormatting::CommonMark::SyntaxHighlightFilterTest < ActiveSupport::TestCase
|
|
25 |
def filter(html)
|
|
26 |
Redmine::WikiFormatting::CommonMark::SyntaxHighlightFilter.to_html(html, @options)
|
|
27 |
end
|
|
23 |
class Redmine::WikiFormatting::CommonMark::SyntaxHighlightFilterTest < ActiveSupport::TestCase |
|
24 |
def filter(html) |
|
25 |
Redmine::WikiFormatting::CommonMark::SyntaxHighlightFilter.to_html(html, @options) |
|
26 |
end |
|
28 | 27 | |
29 |
def setup
|
|
30 |
@options = { }
|
|
31 |
end
|
|
28 |
def setup |
|
29 |
@options = { } |
|
30 |
end |
|
32 | 31 | |
33 |
def test_should_highlight_supported_language
|
|
34 |
input = <<~HTML
|
|
35 |
<pre><code class="language-ruby">
|
|
36 |
def foo
|
|
37 |
end
|
|
38 |
</code></pre>
|
|
39 |
HTML
|
|
40 |
expected = <<~HTML
|
|
41 |
<pre><code class="ruby syntaxhl" data-language="ruby">
|
|
42 |
<span class="k">def</span> <span class="nf">foo</span>
|
|
43 |
<span class="k">end</span>
|
|
44 |
</code></pre>
|
|
45 |
HTML
|
|
46 |
assert_equal expected, filter(input)
|
|
47 |
end
|
|
32 |
def test_should_highlight_supported_language |
|
33 |
input = <<~HTML |
|
34 |
<pre><code class="language-ruby"> |
|
35 |
def foo |
|
36 |
end |
|
37 |
</code></pre> |
|
38 |
HTML |
|
39 |
expected = <<~HTML |
|
40 |
<pre><code class="ruby syntaxhl" data-language="ruby"> |
|
41 |
<span class="k">def</span> <span class="nf">foo</span> |
|
42 |
<span class="k">end</span> |
|
43 |
</code></pre> |
|
44 |
HTML |
|
45 |
assert_equal expected, filter(input) |
|
46 |
end |
|
48 | 47 | |
49 |
def test_should_highlight_supported_language_with_special_chars
|
|
50 |
input = <<~HTML
|
|
51 |
<pre><code class="language-c-k&r">
|
|
52 |
int i;
|
|
53 |
</code></pre>
|
|
54 |
HTML
|
|
55 |
expected = <<~HTML
|
|
56 |
<pre><code data-language="c-k&r">
|
|
57 |
int i;
|
|
58 |
</code></pre>
|
|
59 |
HTML
|
|
60 |
assert_equal expected, filter(input)
|
|
61 |
end
|
|
48 |
def test_should_highlight_supported_language_with_special_chars |
|
49 |
input = <<~HTML |
|
50 |
<pre><code class="language-c-k&r"> |
|
51 |
int i; |
|
52 |
</code></pre> |
|
53 |
HTML |
|
54 |
expected = <<~HTML |
|
55 |
<pre><code data-language="c-k&r"> |
|
56 |
int i; |
|
57 |
</code></pre> |
|
58 |
HTML |
|
59 |
assert_equal expected, filter(input) |
|
60 |
end |
|
62 | 61 | |
63 |
def test_should_strip_code_class_and_preserve_data_language_attr_for_unknown_language
|
|
64 |
input = <<~HTML
|
|
65 |
<pre><code class="language-foobar">
|
|
66 |
def foo
|
|
67 |
end
|
|
68 |
</code></pre>
|
|
69 |
HTML
|
|
70 |
expected = <<~HTML
|
|
71 |
<pre><code data-language="foobar">
|
|
72 |
def foo
|
|
73 |
end
|
|
74 |
</code></pre>
|
|
75 |
HTML
|
|
76 |
assert_equal expected, filter(input)
|
|
77 |
end
|
|
62 |
def test_should_strip_code_class_and_preserve_data_language_attr_for_unknown_language |
|
63 |
input = <<~HTML |
|
64 |
<pre><code class="language-foobar"> |
|
65 |
def foo |
|
66 |
end |
|
67 |
</code></pre> |
|
68 |
HTML |
|
69 |
expected = <<~HTML |
|
70 |
<pre><code data-language="foobar"> |
|
71 |
def foo |
|
72 |
end |
|
73 |
</code></pre> |
|
74 |
HTML |
|
75 |
assert_equal expected, filter(input) |
|
76 |
end |
|
78 | 77 | |
79 |
def test_should_ignore_code_without_class |
|
80 |
input = <<~HTML |
|
81 |
<pre><code> |
|
82 |
def foo |
|
83 |
end |
|
84 |
</code></pre> |
|
85 |
HTML |
|
86 |
assert_equal input, filter(input) |
|
87 |
end |
|
78 |
def test_should_ignore_code_without_class |
|
79 |
input = <<~HTML |
|
80 |
<pre><code> |
|
81 |
def foo |
|
82 |
end |
|
83 |
</code></pre> |
|
84 |
HTML |
|
85 |
assert_equal input, filter(input) |
|
88 | 86 |
end |
89 | 87 |
end |
- « Previous
- 1
- 2
- Next »