motion-html-pipeline 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +379 -0
- data/lib/motion-html-pipeline.rb +14 -0
- data/lib/motion-html-pipeline/document_fragment.rb +27 -0
- data/lib/motion-html-pipeline/pipeline.rb +153 -0
- data/lib/motion-html-pipeline/pipeline/absolute_source_filter.rb +45 -0
- data/lib/motion-html-pipeline/pipeline/body_content.rb +42 -0
- data/lib/motion-html-pipeline/pipeline/disabled/@mention_filter.rb +140 -0
- data/lib/motion-html-pipeline/pipeline/disabled/autolink_filter.rb +27 -0
- data/lib/motion-html-pipeline/pipeline/disabled/camo_filter.rb +93 -0
- data/lib/motion-html-pipeline/pipeline/disabled/email_reply_filter.rb +66 -0
- data/lib/motion-html-pipeline/pipeline/disabled/emoji_filter.rb +125 -0
- data/lib/motion-html-pipeline/pipeline/disabled/markdown_filter.rb +37 -0
- data/lib/motion-html-pipeline/pipeline/disabled/plain_text_input_filter.rb +13 -0
- data/lib/motion-html-pipeline/pipeline/disabled/sanitization_filter.rb +137 -0
- data/lib/motion-html-pipeline/pipeline/disabled/syntax_highlight_filter.rb +44 -0
- data/lib/motion-html-pipeline/pipeline/disabled/toc_filter.rb +67 -0
- data/lib/motion-html-pipeline/pipeline/filter.rb +163 -0
- data/lib/motion-html-pipeline/pipeline/https_filter.rb +27 -0
- data/lib/motion-html-pipeline/pipeline/image_filter.rb +17 -0
- data/lib/motion-html-pipeline/pipeline/image_max_width_filter.rb +37 -0
- data/lib/motion-html-pipeline/pipeline/text_filter.rb +14 -0
- data/lib/motion-html-pipeline/pipeline/version.rb +5 -0
- data/spec/motion-html-pipeline/_helpers/mock_instumentation_service.rb +19 -0
- data/spec/motion-html-pipeline/pipeline/absolute_source_filter_spec.rb +47 -0
- data/spec/motion-html-pipeline/pipeline/disabled/auto_link_filter_spec.rb +33 -0
- data/spec/motion-html-pipeline/pipeline/disabled/camo_filter_spec.rb +75 -0
- data/spec/motion-html-pipeline/pipeline/disabled/email_reply_filter_spec.rb +64 -0
- data/spec/motion-html-pipeline/pipeline/disabled/emoji_filter_spec.rb +92 -0
- data/spec/motion-html-pipeline/pipeline/disabled/markdown_filter_spec.rb +112 -0
- data/spec/motion-html-pipeline/pipeline/disabled/plain_text_input_filter_spec.rb +20 -0
- data/spec/motion-html-pipeline/pipeline/disabled/sanitization_filter_spec.rb +164 -0
- data/spec/motion-html-pipeline/pipeline/disabled/syntax_highlighting_filter_spec.rb +59 -0
- data/spec/motion-html-pipeline/pipeline/disabled/toc_filter_spec.rb +137 -0
- data/spec/motion-html-pipeline/pipeline/https_filter_spec.rb +52 -0
- data/spec/motion-html-pipeline/pipeline/image_filter_spec.rb +37 -0
- data/spec/motion-html-pipeline/pipeline/image_max_width_filter_spec.rb +57 -0
- data/spec/motion-html-pipeline/pipeline_spec.rb +80 -0
- data/spec/spec_helper.rb +48 -0
- metadata +147 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module MotionHTMLPipeline
|
2
|
+
class Pipeline
|
3
|
+
# HTML Filter for replacing http references to :http_url with https versions.
|
4
|
+
# Subdomain references are not rewritten.
|
5
|
+
#
|
6
|
+
# Context options:
|
7
|
+
# :http_url - The HTTP url to force HTTPS. Falls back to :base_url
|
8
|
+
class HttpsFilter < Filter
|
9
|
+
def call
|
10
|
+
doc.css(%(a[href^="#{http_url}"])).each do |element|
|
11
|
+
element['href'] = element['href'].sub(/^http:/, 'https:')
|
12
|
+
end
|
13
|
+
doc
|
14
|
+
end
|
15
|
+
|
16
|
+
# HTTP url to replace. Falls back to :base_url
|
17
|
+
def http_url
|
18
|
+
context[:http_url] || context[:base_url]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Raise error if :http_url undefined
|
22
|
+
def validate
|
23
|
+
needs :http_url unless http_url
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MotionHTMLPipeline
|
2
|
+
class Pipeline
|
3
|
+
# HTML Filter that converts image's url into <img> tag.
|
4
|
+
# For example, it will convert
|
5
|
+
# http://example.com/test.jpg
|
6
|
+
# into
|
7
|
+
# <img src="http://example.com/test.jpg" alt=""/>.
|
8
|
+
|
9
|
+
class ImageFilter < TextFilter
|
10
|
+
def call
|
11
|
+
@text.gsub(/(https|http)?:\/\/.+\.(jpg|jpeg|bmp|gif|png)(\?\S+)?/i) do |match|
|
12
|
+
%(<img src="#{match}" alt=""/>)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module MotionHTMLPipeline
|
2
|
+
class Pipeline
|
3
|
+
# This filter rewrites image tags with a max-width inline style and also wraps
|
4
|
+
# the image in an <a> tag that causes the full size image to be opened in a
|
5
|
+
# new tab.
|
6
|
+
#
|
7
|
+
# The max-width inline styles are especially useful in HTML email which
|
8
|
+
# don't use a global stylesheets.
|
9
|
+
class ImageMaxWidthFilter < Filter
|
10
|
+
def call
|
11
|
+
doc.css('img').each do |element|
|
12
|
+
# Skip if there's already a style attribute. Not sure how this
|
13
|
+
# would happen but we can reconsider it in the future.
|
14
|
+
next if element['style']
|
15
|
+
|
16
|
+
# Bail out if src doesn't look like a valid http url. trying to avoid weird
|
17
|
+
# js injection via javascript: urls.
|
18
|
+
next if element['src'].to_s.strip =~ /\Ajavascript/i
|
19
|
+
|
20
|
+
element['style'] = 'max-width:100%;'
|
21
|
+
|
22
|
+
link_image element unless has_ancestor?(element, %w[a])
|
23
|
+
end
|
24
|
+
|
25
|
+
doc
|
26
|
+
end
|
27
|
+
|
28
|
+
def link_image(element)
|
29
|
+
link = HTMLElement.alloc.initWithTagName('a', attributes: { href: element['src'], target: '_blank' })
|
30
|
+
link.appendNode(element.cloneNodeDeep(true))
|
31
|
+
|
32
|
+
parent = element.parentNode
|
33
|
+
parent.replaceChildNode(element, withNode: link)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module MotionHTMLPipeline
|
2
|
+
class Pipeline
|
3
|
+
class TextFilter < Filter
|
4
|
+
attr_reader :text
|
5
|
+
|
6
|
+
def initialize(text, context = nil, result = nil)
|
7
|
+
raise TypeError, 'text cannot be HTML' if text.is_a?(DocumentFragment)
|
8
|
+
# Ensure that this is always a string
|
9
|
+
@text = text.respond_to?(:to_str) ? text.to_str : text.to_s
|
10
|
+
super nil, context, result
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class MockedInstrumentationService
|
2
|
+
attr_reader :events
|
3
|
+
def initialize(event = nil, events = [])
|
4
|
+
@events = events
|
5
|
+
subscribe event
|
6
|
+
end
|
7
|
+
|
8
|
+
def instrument(event, payload = nil)
|
9
|
+
payload ||= {}
|
10
|
+
res = yield payload
|
11
|
+
events << [event, payload, res] if @subscribe == event
|
12
|
+
res
|
13
|
+
end
|
14
|
+
|
15
|
+
def subscribe(event)
|
16
|
+
@subscribe = event
|
17
|
+
@events
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
describe 'MotionHTMLPipeline::Pipeline::AbsoluteSourceFilterTest' do
|
2
|
+
AbsoluteSourceFilter = MotionHTMLPipeline::Pipeline::AbsoluteSourceFilter
|
3
|
+
|
4
|
+
before do
|
5
|
+
@image_base_url = 'http://assets.example.com'
|
6
|
+
@image_subpage_url = 'http://blog.example.com/a/post'
|
7
|
+
@options = {
|
8
|
+
image_base_url: @image_base_url,
|
9
|
+
image_subpage_url: @image_subpage_url
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'test_rewrites_root_urls' do
|
14
|
+
orig = %(<p><img src="/img.png"></p>)
|
15
|
+
|
16
|
+
expect("<p><img src=\"#{@image_base_url}/img.png\"></p>")
|
17
|
+
.to eq AbsoluteSourceFilter.call(orig, @options).to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'test_rewrites_relative_urls' do
|
21
|
+
orig = %(<p><img src="post/img.png"></p>)
|
22
|
+
|
23
|
+
expect("<p><img src=\"#{@image_subpage_url}/img.png\"></p>")
|
24
|
+
.to eq AbsoluteSourceFilter.call(orig, @options).to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'test_does_not_rewrite_absolute_urls' do
|
28
|
+
orig = %(<p><img src="http://other.example.com/img.png"></p>)
|
29
|
+
result = AbsoluteSourceFilter.call(orig, @options).to_s
|
30
|
+
|
31
|
+
expect(result).not_to match(/@image_base_url/)
|
32
|
+
expect(result).not_to match(/@@image_subpage_url/)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'test_fails_when_context_is_missing' do
|
36
|
+
expect{ AbsoluteSourceFilter.call('<img src="img.png">', {}) }.to raise_error(RuntimeError)
|
37
|
+
expect{ AbsoluteSourceFilter.call('<img src="/img.png">', {}) }.to raise_error(RuntimeError)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'test_tells_you_where_context_is_required' do
|
41
|
+
expect{ AbsoluteSourceFilter.call('<img src="img.png">', {}) }
|
42
|
+
.to raise_error(RuntimeError, 'MotionHTMLPipeline::Pipeline::AbsoluteSourceFilter')
|
43
|
+
|
44
|
+
expect{ AbsoluteSourceFilter.call('<img src="/img.png">', {}) }
|
45
|
+
.to raise_error(RuntimeError, 'MotionHTMLPipeline::Pipeline::AbsoluteSourceFilter')
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# describe 'MotionHTMLPipeline::Pipeline::AutolinkFilterTest' do
|
2
|
+
# AutolinkFilter = MotionHTMLPipeline::Pipeline::AutolinkFilter
|
3
|
+
#
|
4
|
+
# it '#test_uses_rinku_for_autolinking' do
|
5
|
+
# # just try to parse a complicated piece of HTML
|
6
|
+
# # that Rails auto_link cannot handle
|
7
|
+
# expect('<p>"<a href="http://www.github.com">http://www.github.com</a>"</p>')
|
8
|
+
# .to eq AutolinkFilter.to_html('<p>"http://www.github.com"</p>')
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# def test_autolink_option
|
12
|
+
# assert_equal '<p>"http://www.github.com"</p>',
|
13
|
+
# AutolinkFilter.to_html('<p>"http://www.github.com"</p>', autolink: false)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# def test_autolink_link_attr
|
17
|
+
# assert_equal '<p>"<a href="http://www.github.com" target="_blank">http://www.github.com</a>"</p>',
|
18
|
+
# AutolinkFilter.to_html('<p>"http://www.github.com"</p>', link_attr: 'target="_blank"')
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# def test_autolink_flags
|
22
|
+
# assert_equal '<p>"<a href="http://github">http://github</a>"</p>',
|
23
|
+
# AutolinkFilter.to_html('<p>"http://github"</p>', flags: Rinku::AUTOLINK_SHORT_DOMAINS)
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def test_autolink_skip_tags
|
27
|
+
# assert_equal '<code>"http://github.com"</code>',
|
28
|
+
# AutolinkFilter.to_html('<code>"http://github.com"</code>')
|
29
|
+
#
|
30
|
+
# assert_equal '<code>"<a href="http://github.com">http://github.com</a>"</code>',
|
31
|
+
# AutolinkFilter.to_html('<code>"http://github.com"</code>', skip_tags: %w[kbd script])
|
32
|
+
# end
|
33
|
+
# end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# describe 'MotionHTMLPipeline::Pipeline::CamoFilterTest' do
|
2
|
+
# CamoFilter = MotionHTMLPipeline::Pipeline::CamoFilter
|
3
|
+
#
|
4
|
+
# def setup
|
5
|
+
# @asset_proxy_url = 'https//assets.example.org'
|
6
|
+
# @asset_proxy_secret_key = 'ssssh-secret'
|
7
|
+
# @options = {
|
8
|
+
# asset_proxy: @asset_proxy_url,
|
9
|
+
# asset_proxy_secret_key: @asset_proxy_secret_key,
|
10
|
+
# asset_proxy_whitelist: [/(^|\.)github\.com$/]
|
11
|
+
# }
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# def test_asset_proxy_disabled
|
15
|
+
# orig = %(<p><img src="http://twitter.com/img.png"></p>)
|
16
|
+
# assert_equal orig,
|
17
|
+
# CamoFilter.call(orig, @options.merge(disable_asset_proxy: true)).to_s
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def test_camouflaging_http_image_urls
|
21
|
+
# orig = %(<p><img src="http://twitter.com/img.png"></p>)
|
22
|
+
# assert_equal %(<p><img src="https//assets.example.org/a5ad43494e343b20d745586282be61ff530e6fa0/687474703a2f2f747769747465722e636f6d2f696d672e706e67" data-canonical-src="http://twitter.com/img.png"></p>),
|
23
|
+
# CamoFilter.call(orig, @options).to_s
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def test_doesnt_rewrite_dotcom_image_urls
|
27
|
+
# orig = %(<p><img src="https://github.com/img.png"></p>)
|
28
|
+
# assert_equal orig, CamoFilter.call(orig, @options).to_s
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def test_doesnt_rewrite_dotcom_subdomain_image_urls
|
32
|
+
# orig = %(<p><img src="https://raw.github.com/img.png"></p>)
|
33
|
+
# assert_equal orig, CamoFilter.call(orig, @options).to_s
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# def test_doesnt_rewrite_dotcom_subsubdomain_image_urls
|
37
|
+
# orig = %(<p><img src="https://f.assets.github.com/img.png"></p>)
|
38
|
+
# assert_equal orig, CamoFilter.call(orig, @options).to_s
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def test_camouflaging_github_prefixed_image_urls
|
42
|
+
# orig = %(<p><img src="https://notgithub.com/img.png"></p>)
|
43
|
+
# assert_equal %(<p><img src="https//assets.example.org/5d4a96c69713f850520538e04cb9661035cfb534/68747470733a2f2f6e6f746769746875622e636f6d2f696d672e706e67" data-canonical-src="https://notgithub.com/img.png"></p>),
|
44
|
+
# CamoFilter.call(orig, @options).to_s
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# def test_doesnt_rewrite_absolute_image_urls
|
48
|
+
# orig = %(<p><img src="/img.png"></p>)
|
49
|
+
# assert_equal orig, CamoFilter.call(orig, @options).to_s
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# def test_doesnt_rewrite_relative_image_urls
|
53
|
+
# orig = %(<p><img src="img.png"></p>)
|
54
|
+
# assert_equal orig, CamoFilter.call(orig, @options).to_s
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# def test_camouflaging_https_image_urls
|
58
|
+
# orig = %(<p><img src="https://foo.com/img.png"></p>)
|
59
|
+
# assert_equal %(<p><img src="https//assets.example.org/3c5c6dc74fd6592d2596209dfcb8b7e5461383c8/68747470733a2f2f666f6f2e636f6d2f696d672e706e67" data-canonical-src="https://foo.com/img.png"></p>),
|
60
|
+
# CamoFilter.call(orig, @options).to_s
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# def test_handling_images_with_no_src_attribute
|
64
|
+
# orig = %(<p><img></p>)
|
65
|
+
# assert_equal orig, CamoFilter.call(orig, @options).to_s
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# def test_required_context_validation
|
69
|
+
# exception = assert_raises(ArgumentError) do
|
70
|
+
# CamoFilter.call('', {})
|
71
|
+
# end
|
72
|
+
# assert_match /:asset_proxy[^_]/, exception.message
|
73
|
+
# assert_match /:asset_proxy_secret_key/, exception.message
|
74
|
+
# end
|
75
|
+
# end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# describe 'MotionHTMLPipeline::Pipeline::EmailReplyFilterTest' do
|
2
|
+
# EmailReplyFilter = MotionHTMLPipeline::Pipeline::EmailReplyFilter
|
3
|
+
#
|
4
|
+
# def setup
|
5
|
+
# @body = <<-EMAIL
|
6
|
+
# Hey, don't send email addresses in comments. They aren't filtered.
|
7
|
+
#
|
8
|
+
# > On Mar 5, 2016, at 08:05, Boaty McBoatface <boatymcboatface@example.com> wrote:
|
9
|
+
# >
|
10
|
+
# > Sup. alreadyleaked@example.com
|
11
|
+
# >
|
12
|
+
# > —
|
13
|
+
# > Reply to this email directly or view it on GitHub.
|
14
|
+
# EMAIL
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# def test_doesnt_hide_by_default
|
18
|
+
# filter = EmailReplyFilter.new(@body)
|
19
|
+
# doc = filter.call.to_s
|
20
|
+
# assert_match /alreadyleaked@example.com/, doc
|
21
|
+
# assert_match /boatymcboatface@example.com/, doc
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def test_hides_email_addresses_when_configured
|
25
|
+
# filter = EmailReplyFilter.new(@body, hide_quoted_email_addresses: true)
|
26
|
+
# doc = filter.call.to_s
|
27
|
+
# refute_match /boatymcboatface@example.com/, doc
|
28
|
+
# refute_match /alreadyleaked@example.com/, doc
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def test_preserves_non_email_content_while_filtering
|
32
|
+
# str = <<-EMAIL
|
33
|
+
# > Thank you! I have some thoughts on this pull request.
|
34
|
+
# >
|
35
|
+
# > * acme provides cmake and a wrapper for it. Please use '$(TARGET)-cmake' instead of cmake -DCMAKE_TOOLCHAIN_FILE='$(CMAKE_TOOLCHAIN_FILE)' -DCMAKE_BUILD_TYPE=Release.
|
36
|
+
#
|
37
|
+
# Okay -- I'm afraid I just blindly copied the eigen3.mk file, since that's a library I'm familiar with :-)
|
38
|
+
#
|
39
|
+
# > * Do you need -DCMAKE_SYSTEM_PROCESSOR=x86?
|
40
|
+
#
|
41
|
+
# Yes, this is a bit dumb, but vc checks for that (or amd) to determine that it's not being built on ARM.
|
42
|
+
#
|
43
|
+
# --
|
44
|
+
# Boaty McBoatface | http://example.org
|
45
|
+
# EMAIL
|
46
|
+
#
|
47
|
+
# filter = EmailReplyFilter.new(str, hide_quoted_email_addresses: true)
|
48
|
+
# doc = filter.call.to_s
|
49
|
+
#
|
50
|
+
# expected = <<-EXPECTED
|
51
|
+
# <div class="email-quoted-reply"> Thank you! I have some thoughts on this pull request.
|
52
|
+
#
|
53
|
+
# * acme provides cmake and a wrapper for it. Please use '$(TARGET)-cmake' instead of cmake -DCMAKE_TOOLCHAIN_FILE='$(CMAKE_TOOLCHAIN_FILE)' -DCMAKE_BUILD_TYPE=Release.</div>
|
54
|
+
# <div class="email-fragment">Okay -- I'm afraid I just blindly copied the eigen3.mk file, since that's a library I'm familiar with :-)</div>
|
55
|
+
# <div class="email-quoted-reply"> * Do you need -DCMAKE_SYSTEM_PROCESSOR=x86?</div>
|
56
|
+
# <div class="email-fragment">Yes, this is a bit dumb, but vc checks for that (or amd) to determine that it's not being built on ARM.</div>
|
57
|
+
# <span class="email-hidden-toggle"><a href="#">…</a></span><div class="email-hidden-reply" style="display:none"><div class="email-signature-reply">--
|
58
|
+
# Boaty McBoatface | http://example.org</div>
|
59
|
+
# </div>
|
60
|
+
# EXPECTED
|
61
|
+
#
|
62
|
+
# assert_equal(expected.chomp, doc)
|
63
|
+
# end
|
64
|
+
# end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# describe 'MotionHTMLPipeline::Pipeline::EmojiFilterTest' do
|
2
|
+
# EmojiFilter = MotionHTMLPipeline::Pipeline::EmojiFilter
|
3
|
+
#
|
4
|
+
# def test_emojify
|
5
|
+
# filter = EmojiFilter.new('<p>:shipit:</p>', asset_root: 'https://foo.com')
|
6
|
+
# doc = filter.call
|
7
|
+
# assert_match 'https://foo.com/emoji/shipit.png', doc.search('img').attr('src').value
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# def test_uri_encoding
|
11
|
+
# filter = EmojiFilter.new('<p>:+1:</p>', asset_root: 'https://foo.com')
|
12
|
+
# doc = filter.call
|
13
|
+
# assert_match 'https://foo.com/emoji/unicode/1f44d.png', doc.search('img').attr('src').value
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# def test_required_context_validation
|
17
|
+
# exception = assert_raises(ArgumentError) do
|
18
|
+
# EmojiFilter.call('', {})
|
19
|
+
# end
|
20
|
+
# assert_match /:asset_root/, exception.message
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# def test_custom_asset_path
|
24
|
+
# filter = EmojiFilter.new('<p>:+1:</p>', asset_path: ':file_name', asset_root: 'https://foo.com')
|
25
|
+
# doc = filter.call
|
26
|
+
# assert_match 'https://foo.com/unicode/1f44d.png', doc.search('img').attr('src').value
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# def test_not_emojify_in_code_tags
|
30
|
+
# body = '<code>:shipit:</code>'
|
31
|
+
# filter = EmojiFilter.new(body, asset_root: 'https://foo.com')
|
32
|
+
# doc = filter.call
|
33
|
+
# assert_equal body, doc.to_html
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# def test_not_emojify_in_tt_tags
|
37
|
+
# body = '<tt>:shipit:</tt>'
|
38
|
+
# filter = EmojiFilter.new(body, asset_root: 'https://foo.com')
|
39
|
+
# doc = filter.call
|
40
|
+
# assert_equal body, doc.to_html
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# def test_not_emojify_in_pre_tags
|
44
|
+
# body = '<pre>:shipit:</pre>'
|
45
|
+
# filter = EmojiFilter.new(body, asset_root: 'https://foo.com')
|
46
|
+
# doc = filter.call
|
47
|
+
# assert_equal body, doc.to_html
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# def test_not_emojify_in_custom_single_tag_foo
|
51
|
+
# body = '<foo>:shipit:</foo>'
|
52
|
+
# filter = EmojiFilter.new(body, asset_root: 'https://foo.com', ignored_ancestor_tags: %w[foo])
|
53
|
+
# doc = filter.call
|
54
|
+
# assert_equal body, doc.to_html
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# def test_not_emojify_in_custom_multiple_tags_foo_and_bar
|
58
|
+
# body = '<bar>:shipit:</bar>'
|
59
|
+
# filter = EmojiFilter.new(body, asset_root: 'https://foo.com', ignored_ancestor_tags: %w[foo bar])
|
60
|
+
# doc = filter.call
|
61
|
+
# assert_equal body, doc.to_html
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# def test_img_tag_attributes
|
65
|
+
# body = ':shipit:'
|
66
|
+
# filter = EmojiFilter.new(body, asset_root: 'https://foo.com')
|
67
|
+
# doc = filter.call
|
68
|
+
# assert_equal %(<img class="emoji" title=":shipit:" alt=":shipit:" src="https://foo.com/emoji/shipit.png" height="20" width="20" align="absmiddle">), doc.to_html
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# def test_img_tag_attributes_can_be_customized
|
72
|
+
# body = ':shipit:'
|
73
|
+
# filter = EmojiFilter.new(body, asset_root: 'https://foo.com', img_attrs: Hash('draggable' => 'false', 'height' => nil, 'width' => nil, 'align' => nil))
|
74
|
+
# doc = filter.call
|
75
|
+
# assert_equal %(<img class="emoji" title=":shipit:" alt=":shipit:" src="https://foo.com/emoji/shipit.png" draggable="false">), doc.to_html
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# def test_img_attrs_value_can_accept_proclike_object
|
79
|
+
# remove_colons = ->(name) { name.delete(':') }
|
80
|
+
# body = ':shipit:'
|
81
|
+
# filter = EmojiFilter.new(body, asset_root: 'https://foo.com', img_attrs: Hash('title' => remove_colons))
|
82
|
+
# doc = filter.call
|
83
|
+
# assert_equal %(<img class="emoji" title="shipit" alt=":shipit:" src="https://foo.com/emoji/shipit.png" height="20" width="20" align="absmiddle">), doc.to_html
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# def test_img_attrs_can_accept_symbolized_keys
|
87
|
+
# body = ':shipit:'
|
88
|
+
# filter = EmojiFilter.new(body, asset_root: 'https://foo.com', img_attrs: Hash(draggable: false, height: nil, width: nil, align: nil))
|
89
|
+
# doc = filter.call
|
90
|
+
# assert_equal %(<img class="emoji" title=":shipit:" alt=":shipit:" src="https://foo.com/emoji/shipit.png" draggable="false">), doc.to_html
|
91
|
+
# end
|
92
|
+
# end
|