require 'rails_helper' require 'pretty_text' describe PrettyText do before do SiteSetting.enable_markdown_typographer = false end def n(html) html.strip end def cook(*args) PrettyText.cook(*args) end let(:wrapped_image) { "
" } let(:wrapped_image_excerpt) {} describe "Quoting" do describe "with avatar" do let(:default_avatar) { "//test.localhost/uploads/default/avatars/42d/57c/46ce7ee487/{size}.png" } let(:user) { Fabricate(:user) } before do User.stubs(:default_template).returns(default_avatar) end it "do off topic quoting with emoji unescape" do topic = Fabricate(:topic, title: "this is a test topic :slight_smile:") expected = <<~HTML HTML expect(cook("[quote=\"EvilTrout, post:2, topic:#{topic.id}\"]\nddd\n[/quote]", topic_id: 1)).to eq(n(expected)) end it "indifferent about missing quotations" do md = <<~MD [quote=#{user.username}, post:123, topic:456, full:true] ddd [/quote] MD html = <<~HTML HTML expect(PrettyText.cook(md)).to eq(html.strip) end it "indifferent about curlies and no curlies" do md = <<~MD [quote=“#{user.username}, post:123, topic:456, full:true”] ddd [/quote] MD html = <<~HTML HTML expect(PrettyText.cook(md)).to eq(html.strip) end it "trims spaces on quote params" do md = <<~MD [quote="#{user.username}, post:555, topic: 666"] ddd [/quote] MD html = <<~HTML HTML expect(PrettyText.cook(md)).to eq(html.strip) end end describe "with primary user group" do let(:default_avatar) { "//test.localhost/uploads/default/avatars/42d/57c/46ce7ee487/{size}.png" } let(:group) { Fabricate(:group) } let!(:user) { Fabricate(:user, primary_group: group) } before do User.stubs(:default_template).returns(default_avatar) end it "adds primary group class to referenced users quote" do topic = Fabricate(:topic, title: "this is a test topic") expected = <<~HTML HTML expect(cook("[quote=\"#{user.username}, post:2, topic:#{topic.id}\"]\nddd\n[/quote]", topic_id: 1)).to eq(n(expected)) end end it "can handle inline block bbcode" do cooked = PrettyText.cook("[quote]te **s** t[/quote]") html = <<~HTML HTML expect(cooked).to eq(html.strip) end it "handles bbcode edge cases" do expect(PrettyText.cook "[constructor]\ntest").to eq("[constructor]
\ntest
test test
\n@bob
") end it "should handle 3 mentions in a row" do expect(PrettyText.cook('@hello @hello @hello')).to match_html "@hello @hello @hello
" end it "can handle mention edge cases" do expect(PrettyText.cook("hi\n@s")).to eq("hi
\n@s
hi
\n@ss
hi
\n@s.
hi
\n@s.s
hi
\n@.s.s
hi @sam! hi
' expect(PrettyText.cook("hi\n@sam.")).to eq("hi
\n@sam.
hi @#{group.name}! hi
} ) end it "can handle mentions inside a hyperlink" do expect(PrettyText.cook(" @inner ")).to match_html '' end it "can handle mentions inside a hyperlink" do expect(PrettyText.cook("[link @inner](http://site.com)")).to match_html '' end it "can handle a list of mentions" do expect(PrettyText.cook("@a,@b")).to match_html('@a,@b
') end it "should handle group mentions with a hyphen and without" do expect(PrettyText.cook('@hello @hello-hello')).to match_html "@hello @hello-hello
" end it 'should allow for @mentions to have punctuation' do expect(PrettyText.cook("hello @bob's @bob,@bob; @bob\"")).to match_html( "hello @bob's @bob,@bob; @bob\"
" ) end it 'should not treat a medium link as a mention' do expect(PrettyText.cook(". http://test/@sam")).not_to include('mention') end end describe "code fences" do it 'indents code correctly' do code = <<~MD X ``` # x ``` MD cooked = PrettyText.cook(code) html = <<~HTMLX
#
x
HTML
expect(cooked).to eq(html.strip)
end
it "doesn't replace emoji in code blocks with our emoji sets if emoji is enabled" do
expect(PrettyText.cook("```\n💣`\n```\n")).not_to match(/\:bomb\:/)
end
it 'can include code class correctly' do
# keep in mind spaces should be trimmed per spec
expect(PrettyText.cook("``` ruby the mooby\n`````")).to eq('
')
expect(PrettyText.cook("```cpp\ncpp\n```")).to match_html("cpp\n
")
expect(PrettyText.cook("```\ncpp\n```")).to match_html("cpp\n
")
expect(PrettyText.cook("```text\ncpp\n```")).to match_html("cpp\n
")
end
it 'indents code correctly' do
code = "X\n```\n\n #\n x\n```"
cooked = PrettyText.cook(code)
expect(cooked).to match_html("X
\n\n #\n x\n
")
end
it 'does censor code fences' do
begin
['apple', 'banana'].each { |w| Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor]) }
expect(PrettyText.cook("# banana")).not_to include('banana')
ensure
$redis.flushall
end
end
end
describe "rel nofollow" do
before do
SiteSetting.add_rel_nofollow_to_user_content = true
SiteSetting.exclude_rel_nofollow_domains = "foo.com|bar.com"
end
it "should inject nofollow in all user provided links" do
expect(PrettyText.cook('cnn')).to match(/nofollow noopener/)
end
it "should not inject nofollow in all local links" do
expect(PrettyText.cook("cnn") !~ /nofollow/).to eq(true)
end
it "should not inject nofollow in all subdomain links" do
expect(PrettyText.cook("cnn") !~ /nofollow/).to eq(true)
end
it "should inject nofollow in all non subdomain links" do
expect(PrettyText.cook("cnn")).to match(/nofollow/)
end
it "should not inject nofollow for foo.com" do
expect(PrettyText.cook("cnn") !~ /nofollow/).to eq(true)
end
it "should inject nofollow for afoo.com" do
expect(PrettyText.cook("cnn")).to match(/nofollow/)
end
it "should not inject nofollow for bar.foo.com" do
expect(PrettyText.cook("cnn") !~ /nofollow/).to eq(true)
end
it "should not inject nofollow if omit_nofollow option is given" do
expect(PrettyText.cook('cnn', omit_nofollow: true) !~ /nofollow/).to eq(true)
end
end
describe "Excerpt" do
it "sanitizes attempts to inject invalid attributes" do
spinner = "", 100)).to eq("[image]")
end
context 'alt tags' do
it "should keep alt tags" do
expect(PrettyText.excerpt("hello
hello
", 100)).to eq("") end it "should truncate stuff properly" do expect(PrettyText.excerpt("hello world", 5)).to eq("hello…") expect(PrettyText.excerpt("
hello
world
", 6)).to eq("hello w…") end it "should insert a space between to Ps" do expect(PrettyText.excerpt("a
b
", 5)).to eq("a b") end it "should strip quotes" do expect(PrettyText.excerpt("boom", 5)).to eq("boom") end it "should not count the surrounds of a link" do expect(PrettyText.excerpt("cnn", 3)).to match_html "cnn" end it "uses an ellipsis instead of html entities if provided with the option" do expect(PrettyText.excerpt("cnn", 2, text_entities: true)).to match_html "cn..." end it "should truncate links" do expect(PrettyText.excerpt("cnn", 2)).to match_html "cn…" end it "doesn't extract empty quotes as links" do expect(PrettyText.extract_links("\n").to_a).to be_empty end it "doesn't extract links from elided parts" do expect(PrettyText.extract_links("<h3>Hours</h3>
", 100)).to eq("<h3>Hours</h3>")
end
it "should handle nil" do
expect(PrettyText.excerpt(nil, 100)).to eq('')
end
it "handles span excerpt at the beginning of a post" do
expect(PrettyText.excerpt("hi test", 100)).to eq('hi')
post = Fabricate(:post, raw: "hi test")
expect(post.excerpt).to eq("hi")
end
it "ignores max excerpt length if a span excerpt is specified" do
two_hundred = "123456789 " * 20 + "."
text = two_hundred + "#{two_hundred}" + two_hundred
expect(PrettyText.excerpt(text, 100)).to eq(two_hundred)
post = Fabricate(:post, raw: text)
expect(post.excerpt).to eq(two_hundred)
end
it "unescapes html entities when we want text entities" do
expect(PrettyText.excerpt("'", 500, text_entities: true)).to eq("'")
end
it "should have an option to preserve emoji images" do
emoji_image = "@wiseguy, @trollol what do you guys think?
" output = described_class.format_for_email(html, post) expect(output).to eq("@wiseguy, @trollol what do you guys think?
") end it "doesn't change external absolute links" do html = "Check out this guy.
" expect(described_class.format_for_email(html, post)).to eq(html) end it "doesn't change internal absolute links" do html = "Check out this guy.
" expect(described_class.format_for_email(html, post)).to eq(html) end it "can tolerate invalid URLs" do html = "Check out this guy.
" expect { described_class.format_for_email(html, post) }.to_not raise_error end it "doesn't change mailto" do html = "Contact me at this address.
" expect(PrettyText.format_for_email(html, post)).to eq(html) end end it 'Is smart about linebreaks and IMG tags' do raw = <<~MD aa
a
test
test
HTML
expect(PrettyText.cook(raw)).to eq(html.strip)
end
describe 's3_cdn' do
def test_s3_cdn
# add extra img tag to ensure it does not blow up
raw = <<~HTML
:bomb:
") end it "doesn't replace shortcuts if disabled" do SiteSetting.enable_emoji_shortcuts = false expect(PrettyText.cook(":)")).to eq(":)
") end it "does replace shortcuts if enabled" do expect(PrettyText.cook(":)")).to match("smile") end it "replaces skin toned emoji" do expect(PrettyText.cook("hello 👱🏿♀️")).to eq("hello
hello
hello
hello
hello
hello
hello
hello
1 2
" SiteSetting.traditional_markdown_linebreaks = false expect(PrettyText.cook("1\n2")).to match_html "1
\n2
a,,b
™
') SiteSetting.enable_markdown_typographer = false expect(PrettyText.cook('(tm)')).to eq('(tm)
') end it 'handles onebox correctly' do expect(PrettyText.cook("http://a.com\nhttp://b.com").split("onebox").length).to eq(3) expect(PrettyText.cook("http://a.com\n\nhttp://b.com").split("onebox").length).to eq(3) expect(PrettyText.cook("a\nhttp://a.com")).to include('onebox') expect(PrettyText.cook("> http://a.com")).not_to include('onebox') expect(PrettyText.cook("a\nhttp://a.com a")).not_to include('onebox') expect(PrettyText.cook("a\nhttp://a.com\na")).to include('onebox') expect(PrettyText.cook("http://a.com")).to include('onebox') expect(PrettyText.cook("http://a.com ")).to include('onebox') expect(PrettyText.cook("http://a.com a")).not_to include('onebox') expect(PrettyText.cook("- http://a.com")).not_to include('onebox') expect(PrettyText.cook("abc
') expect(PrettyText.cook("a[i]b[/i]c")).to eq('abc
') end it "can handle bbcode after a newline" do # this is not 100% ideal cause we get an extra p here, but this is pretty rare expect(PrettyText.cook("a\n[code]code[/code]")).to eq("a
\ncode
")
# this is fine
expect(PrettyText.cook("a\na[code]code[/code]")).to eq("a
\nacode
Tables | Are | Cool |
---|---|---|
col 3 is | right-aligned | $1600 |
Testing codified **stuff** and `more` stuff
codified\n\n\n **stuff** and `more` stuff
"
expect(cooked).to eq(html)
end
it "support special handling for space in urls" do
cooked = PrettyText.cook "http://testing.com?a%20b"
html = ''
expect(cooked).to eq(html)
end
it "supports onebox for decoded urls" do
cooked = PrettyText.cook "http://testing.com?a%50b"
html = ''
expect(cooked).to eq(html)
end
it "should sanitize the html" do
expect(PrettyText.cook("alert(42)
" end it "should not onebox magically linked urls" do expect(PrettyText.cook('[url]site.com[/url]')).not_to include('onebox') end it "should sanitize the html" do expect(PrettyText.cook("hi
")).to eq "hi
" end it "should strip SCRIPT" do expect(PrettyText.cook("")).to eq "" end it "should allow sanitize bypass" do expect(PrettyText.cook("
Hello #{topic.title}
HTML expect(PrettyText.cook(raw)).to eq(cooked.strip) end it "invalidates the onebox url" do topic = Fabricate(:topic) url = topic.url raw = "Hello #{url}" PrettyText.cook(raw) topic.title = "Updated: #{topic.title}" topic.save cooked = <<~HTMLHello #{topic.title}
HTML expect(PrettyText.cook(raw)).not_to eq(cooked.strip) expect(PrettyText.cook(raw, invalidate_oneboxes: true)).to eq(cooked.strip) expect(PrettyText.cook(raw)).to eq(cooked.strip) end end describe "image decoding" do it "can decode upload:// for default setup" do upload = Fabricate(:upload) raw = <<~RAW ![upload](#{upload.short_url}) - ![upload](#{upload.short_url}) - test - ![upload](#{upload.short_url}) ![upload](#{upload.short_url.gsub!(".png", "")}) RAW cooked = <<~HTMLtest
www.cnn.com test.it http://test.com https://test.ab https://a
HTML expect(cooked).to eq(html.strip) # notice how cnn.com is no longer linked but it is SiteSetting.markdown_linkify_tlds = "not_com|it" cooked = PrettyText.cook(md) html = <<~HTMLwww.cnn.com test.it http://test.com https://test.ab https://a
HTML expect(cooked).to eq(html.strip) # no tlds anymore SiteSetting.markdown_linkify_tlds = "" cooked = PrettyText.cook(md) html = <<~HTMLwww.cnn.com test.it http://test.com https://test.ab https://a
HTML expect(cooked).to eq(html.strip) # lastly ... what about no linkify SiteSetting.enable_markdown_linkify = false cooked = PrettyText.cook(md) html = <<~HTMLwww.cnn.com test.it http://test.com https://test.ab https://a
HTML end it "has a proper data whitlist on div" do cooked = PrettyText.cook("tester
tester