motion-html-pipeline 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +379 -0
  3. data/lib/motion-html-pipeline.rb +14 -0
  4. data/lib/motion-html-pipeline/document_fragment.rb +27 -0
  5. data/lib/motion-html-pipeline/pipeline.rb +153 -0
  6. data/lib/motion-html-pipeline/pipeline/absolute_source_filter.rb +45 -0
  7. data/lib/motion-html-pipeline/pipeline/body_content.rb +42 -0
  8. data/lib/motion-html-pipeline/pipeline/disabled/@mention_filter.rb +140 -0
  9. data/lib/motion-html-pipeline/pipeline/disabled/autolink_filter.rb +27 -0
  10. data/lib/motion-html-pipeline/pipeline/disabled/camo_filter.rb +93 -0
  11. data/lib/motion-html-pipeline/pipeline/disabled/email_reply_filter.rb +66 -0
  12. data/lib/motion-html-pipeline/pipeline/disabled/emoji_filter.rb +125 -0
  13. data/lib/motion-html-pipeline/pipeline/disabled/markdown_filter.rb +37 -0
  14. data/lib/motion-html-pipeline/pipeline/disabled/plain_text_input_filter.rb +13 -0
  15. data/lib/motion-html-pipeline/pipeline/disabled/sanitization_filter.rb +137 -0
  16. data/lib/motion-html-pipeline/pipeline/disabled/syntax_highlight_filter.rb +44 -0
  17. data/lib/motion-html-pipeline/pipeline/disabled/toc_filter.rb +67 -0
  18. data/lib/motion-html-pipeline/pipeline/filter.rb +163 -0
  19. data/lib/motion-html-pipeline/pipeline/https_filter.rb +27 -0
  20. data/lib/motion-html-pipeline/pipeline/image_filter.rb +17 -0
  21. data/lib/motion-html-pipeline/pipeline/image_max_width_filter.rb +37 -0
  22. data/lib/motion-html-pipeline/pipeline/text_filter.rb +14 -0
  23. data/lib/motion-html-pipeline/pipeline/version.rb +5 -0
  24. data/spec/motion-html-pipeline/_helpers/mock_instumentation_service.rb +19 -0
  25. data/spec/motion-html-pipeline/pipeline/absolute_source_filter_spec.rb +47 -0
  26. data/spec/motion-html-pipeline/pipeline/disabled/auto_link_filter_spec.rb +33 -0
  27. data/spec/motion-html-pipeline/pipeline/disabled/camo_filter_spec.rb +75 -0
  28. data/spec/motion-html-pipeline/pipeline/disabled/email_reply_filter_spec.rb +64 -0
  29. data/spec/motion-html-pipeline/pipeline/disabled/emoji_filter_spec.rb +92 -0
  30. data/spec/motion-html-pipeline/pipeline/disabled/markdown_filter_spec.rb +112 -0
  31. data/spec/motion-html-pipeline/pipeline/disabled/plain_text_input_filter_spec.rb +20 -0
  32. data/spec/motion-html-pipeline/pipeline/disabled/sanitization_filter_spec.rb +164 -0
  33. data/spec/motion-html-pipeline/pipeline/disabled/syntax_highlighting_filter_spec.rb +59 -0
  34. data/spec/motion-html-pipeline/pipeline/disabled/toc_filter_spec.rb +137 -0
  35. data/spec/motion-html-pipeline/pipeline/https_filter_spec.rb +52 -0
  36. data/spec/motion-html-pipeline/pipeline/image_filter_spec.rb +37 -0
  37. data/spec/motion-html-pipeline/pipeline/image_max_width_filter_spec.rb +57 -0
  38. data/spec/motion-html-pipeline/pipeline_spec.rb +80 -0
  39. data/spec/spec_helper.rb +48 -0
  40. metadata +147 -0
@@ -0,0 +1,112 @@
1
+ # describe 'MotionHTMLPipeline::Pipeline::MarkdownFilterTest' do
2
+ # MarkdownFilter = MotionHTMLPipeline::Pipeline::MarkdownFilter
3
+ #
4
+ # def setup
5
+ # @haiku =
6
+ # "Pointing at the moon\n" \
7
+ # "Reminded of simple things\n" \
8
+ # 'Moments matter most'
9
+ # @links =
10
+ # 'See http://example.org/ for more info'
11
+ # @code =
12
+ # "```\n" \
13
+ # 'def hello()' \
14
+ # " 'world'" \
15
+ # 'end' \
16
+ # '```'
17
+ # end
18
+ #
19
+ # def test_fails_when_given_a_documentfragment
20
+ # body = '<p>heyo</p>'
21
+ # doc = HTML::Pipeline.parse(body)
22
+ # assert_raises(TypeError) { MarkdownFilter.call(doc, {}) }
23
+ # end
24
+ #
25
+ # def test_gfm_enabled_by_default
26
+ # doc = MarkdownFilter.to_document(@haiku, {})
27
+ # assert doc.is_a?(HTML::Pipeline::DocumentFragment)
28
+ # assert_equal 2, doc.search('br').size
29
+ # end
30
+ #
31
+ # def test_disabling_gfm
32
+ # doc = MarkdownFilter.to_document(@haiku, gfm: false)
33
+ # assert doc.is_a?(HTML::Pipeline::DocumentFragment)
34
+ # assert_equal 0, doc.search('br').size
35
+ # end
36
+ #
37
+ # def test_fenced_code_blocks
38
+ # doc = MarkdownFilter.to_document(@code)
39
+ # assert doc.is_a?(HTML::Pipeline::DocumentFragment)
40
+ # assert_equal 1, doc.search('pre').size
41
+ # end
42
+ #
43
+ # def test_fenced_code_blocks_with_language
44
+ # doc = MarkdownFilter.to_document(@code.sub('```', '``` ruby'))
45
+ # assert doc.is_a?(HTML::Pipeline::DocumentFragment)
46
+ # assert_equal 1, doc.search('pre').size
47
+ # assert_equal 'ruby', doc.search('pre').first['lang']
48
+ # end
49
+ #
50
+ # def test_standard_extensions
51
+ # iframe = "<iframe src='http://www.google.com'></iframe>"
52
+ # iframe_escaped = "&lt;iframe src='http://www.google.com'>&lt;/iframe>"
53
+ # doc = MarkdownFilter.new(iframe, unsafe: true).call
54
+ # assert_equal(doc, iframe_escaped)
55
+ # end
56
+ #
57
+ # def test_changing_extensions
58
+ # iframe = "<iframe src='http://www.google.com'></iframe>"
59
+ # doc = MarkdownFilter.new(iframe, commonmarker_extensions: [], unsafe: true).call
60
+ # assert_equal(doc, iframe)
61
+ # end
62
+ # end
63
+ #
64
+ # class GFMTest < Minitest::Test
65
+ # def gfm(text)
66
+ # MarkdownFilter.call(text, gfm: true, unsafe: true)
67
+ # end
68
+ #
69
+ # def test_not_touch_single_underscores_inside_words
70
+ # assert_equal '<p>foo_bar</p>',
71
+ # gfm('foo_bar')
72
+ # end
73
+ #
74
+ # def test_not_touch_underscores_in_code_blocks
75
+ # assert_equal "<pre><code>foo_bar_baz\n</code></pre>",
76
+ # gfm(' foo_bar_baz')
77
+ # end
78
+ #
79
+ # def test_not_touch_underscores_in_pre_blocks
80
+ # assert_equal "<pre>\nfoo_bar_baz\n</pre>",
81
+ # gfm("<pre>\nfoo_bar_baz\n</pre>")
82
+ # end
83
+ #
84
+ # def test_not_touch_two_or_more_underscores_inside_words
85
+ # assert_equal '<p>foo_bar_baz</p>',
86
+ # gfm('foo_bar_baz')
87
+ # end
88
+ #
89
+ # def test_turn_newlines_into_br_tags_in_simple_cases
90
+ # assert_equal "<p>foo<br />\nbar</p>",
91
+ # gfm("foo\nbar")
92
+ # end
93
+ #
94
+ # def test_convert_newlines_in_all_groups
95
+ # assert_equal "<p>apple<br />\npear<br />\norange</p>\n" \
96
+ # "<p>ruby<br />\npython<br />\nerlang</p>",
97
+ # gfm("apple\npear\norange\n\nruby\npython\nerlang")
98
+ # end
99
+ #
100
+ # def test_convert_newlines_in_even_long_groups
101
+ # assert_equal "<p>apple<br />\npear<br />\norange<br />\nbanana</p>\n" \
102
+ # "<p>ruby<br />\npython<br />\nerlang</p>",
103
+ # gfm("apple\npear\norange\nbanana\n\nruby\npython\nerlang")
104
+ # end
105
+ #
106
+ # def test_not_convert_newlines_in_lists
107
+ # assert_equal "<h1>foo</h1>\n<h1>bar</h1>",
108
+ # gfm("# foo\n# bar")
109
+ # assert_equal "<ul>\n<li>foo</li>\n<li>bar</li>\n</ul>",
110
+ # gfm("* foo\n* bar")
111
+ # end
112
+ # end
@@ -0,0 +1,20 @@
1
+ # describe 'MotionHTMLPipeline::Pipeline::PlainTextInputFilterTest' do
2
+ # PlainTextInputFilter = MotionHTMLPipeline::Pipeline::PlainTextInputFilter
3
+ #
4
+ # def test_fails_when_given_a_documentfragment
5
+ # body = '<p>heyo</p>'
6
+ # doc = Nokogiri::HTML::DocumentFragment.parse(body)
7
+ # assert_raises(TypeError) { PlainTextInputFilter.call(doc, {}) }
8
+ # end
9
+ #
10
+ # def test_wraps_input_in_a_div_element
11
+ # doc = PlainTextInputFilter.call('howdy pahtner', {})
12
+ # assert_equal '<div>howdy pahtner</div>', doc.to_s
13
+ # end
14
+ #
15
+ # def test_html_escapes_plain_text_input
16
+ # doc = PlainTextInputFilter.call('See: <http://example.org>', {})
17
+ # assert_equal '<div>See: &lt;http://example.org&gt;</div>',
18
+ # doc.to_s
19
+ # end
20
+ # end
@@ -0,0 +1,164 @@
1
+ # describe 'MotionHTMLPipeline::Pipeline::SanitizationFilterTest' do
2
+ # SanitizationFilter = MotionHTMLPipeline::Pipeline::SanitizationFilter
3
+ #
4
+ # def test_removing_script_tags
5
+ # orig = %(<p><img src="http://github.com/img.png" /><script></script></p>)
6
+ # html = SanitizationFilter.call(orig).to_s
7
+ # refute_match /script/, html
8
+ # end
9
+ #
10
+ # def test_removing_style_tags
11
+ # orig = %(<p><style>hey now</style></p>)
12
+ # html = SanitizationFilter.call(orig).to_s
13
+ # refute_match /style/, html
14
+ # end
15
+ #
16
+ # def test_removing_style_attributes
17
+ # orig = %(<p style='font-size:1000%'>YO DAWG</p>)
18
+ # html = SanitizationFilter.call(orig).to_s
19
+ # refute_match /font-size/, html
20
+ # refute_match /style/, html
21
+ # end
22
+ #
23
+ # def test_removing_script_event_handler_attributes
24
+ # orig = %(<a onclick='javascript:alert(0)'>YO DAWG</a>)
25
+ # html = SanitizationFilter.call(orig).to_s
26
+ # refute_match /javscript/, html
27
+ # refute_match /onclick/, html
28
+ # end
29
+ #
30
+ # def test_sanitizes_li_elements_not_contained_in_ul_or_ol
31
+ # stuff = "a\n<li>b</li>\nc"
32
+ # html = SanitizationFilter.call(stuff).to_s
33
+ # assert_equal "a\nb\nc", html
34
+ # end
35
+ #
36
+ # def test_does_not_sanitize_li_elements_contained_in_ul_or_ol
37
+ # stuff = "a\n<ul><li>b</li></ul>\nc"
38
+ # assert_equal stuff, SanitizationFilter.call(stuff).to_s
39
+ # end
40
+ #
41
+ # def test_github_specific_protocols_are_not_removed
42
+ # stuff = '<a href="github-windows://spillthelog">Spill this yo</a> and so on'
43
+ # assert_equal stuff, SanitizationFilter.call(stuff).to_s
44
+ # end
45
+ #
46
+ # def test_unknown_schemes_are_removed
47
+ # stuff = '<a href="something-weird://heyyy">Wat</a> is this'
48
+ # html = SanitizationFilter.call(stuff).to_s
49
+ # assert_equal '<a>Wat</a> is this', html
50
+ # end
51
+ #
52
+ # def test_whitelisted_longdesc_schemes_are_allowed
53
+ # stuff = '<img src="./foo.jpg" longdesc="http://longdesc.com">'
54
+ # html = SanitizationFilter.call(stuff).to_s
55
+ # assert_equal '<img src="./foo.jpg" longdesc="http://longdesc.com">', html
56
+ # end
57
+ #
58
+ # def test_weird_longdesc_schemes_are_removed
59
+ # stuff = '<img src="./foo.jpg" longdesc="javascript:alert(1)">'
60
+ # html = SanitizationFilter.call(stuff).to_s
61
+ # assert_equal '<img src="./foo.jpg">', html
62
+ # end
63
+ #
64
+ # def test_standard_schemes_are_removed_if_not_specified_in_anchor_schemes
65
+ # stuff = '<a href="http://www.example.com/">No href for you</a>'
66
+ # filter = SanitizationFilter.new(stuff, anchor_schemes: [])
67
+ # html = filter.call.to_s
68
+ # assert_equal '<a>No href for you</a>', html
69
+ # end
70
+ #
71
+ # def test_custom_anchor_schemes_are_not_removed
72
+ # stuff = '<a href="something-weird://heyyy">Wat</a> is this'
73
+ # filter = SanitizationFilter.new(stuff, anchor_schemes: ['something-weird'])
74
+ # html = filter.call.to_s
75
+ # assert_equal stuff, html
76
+ # end
77
+ #
78
+ # def test_anchor_schemes_are_merged_with_other_anchor_restrictions
79
+ # stuff = '<a href="something-weird://heyyy" ping="more-weird://hiii">Wat</a> is this'
80
+ # whitelist = {
81
+ # elements: ['a'],
82
+ # attributes: { 'a' => %w[href ping] },
83
+ # protocols: { 'a' => { 'ping' => ['http'] } }
84
+ # }
85
+ # filter = SanitizationFilter.new(stuff, whitelist: whitelist, anchor_schemes: ['something-weird'])
86
+ # html = filter.call.to_s
87
+ # assert_equal '<a href="something-weird://heyyy">Wat</a> is this', html
88
+ # end
89
+ #
90
+ # def test_uses_anchor_schemes_from_whitelist_when_not_separately_specified
91
+ # stuff = '<a href="something-weird://heyyy">Wat</a> is this'
92
+ # whitelist = {
93
+ # elements: ['a'],
94
+ # attributes: { 'a' => ['href'] },
95
+ # protocols: { 'a' => { 'href' => ['something-weird'] } }
96
+ # }
97
+ # filter = SanitizationFilter.new(stuff, whitelist: whitelist)
98
+ # html = filter.call.to_s
99
+ # assert_equal stuff, html
100
+ # end
101
+ #
102
+ # def test_whitelist_contains_default_anchor_schemes
103
+ # assert_equal SanitizationFilter::WHITELIST[:protocols]['a']['href'], ['http', 'https', 'mailto', 'xmpp', :relative, 'github-windows', 'github-mac', 'irc', 'ircs']
104
+ # end
105
+ #
106
+ # def test_whitelist_from_full_constant
107
+ # stuff = '<a href="something-weird://heyyy" ping="more-weird://hiii">Wat</a> is this'
108
+ # filter = SanitizationFilter.new(stuff, whitelist: SanitizationFilter::FULL)
109
+ # html = filter.call.to_s
110
+ # assert_equal 'Wat is this', html
111
+ # end
112
+ #
113
+ # def test_exports_default_anchor_schemes
114
+ # assert_equal SanitizationFilter::ANCHOR_SCHEMES, ['http', 'https', 'mailto', 'xmpp', :relative, 'github-windows', 'github-mac', 'irc', 'ircs']
115
+ # end
116
+ #
117
+ # def test_script_contents_are_removed
118
+ # orig = '<script>JavaScript!</script>'
119
+ # assert_equal '', SanitizationFilter.call(orig).to_s
120
+ # end
121
+ #
122
+ # def test_table_rows_and_cells_removed_if_not_in_table
123
+ # orig = %(<tr><td>Foo</td></tr><td>Bar</td>)
124
+ # assert_equal 'FooBar', SanitizationFilter.call(orig).to_s
125
+ # end
126
+ #
127
+ # def test_table_sections_removed_if_not_in_table
128
+ # orig = %(<thead><tr><td>Foo</td></tr></thead>)
129
+ # assert_equal 'Foo', SanitizationFilter.call(orig).to_s
130
+ # end
131
+ #
132
+ # def test_table_sections_are_not_removed
133
+ # orig = %(<table>
134
+ # <thead><tr><th>Column 1</th></tr></thead>
135
+ # <tfoot><tr><td>Sum</td></tr></tfoot>
136
+ # <tbody><tr><td>1</td></tr></tbody>
137
+ # </table>)
138
+ # assert_equal orig, SanitizationFilter.call(orig).to_s
139
+ # end
140
+ #
141
+ # def test_summary_tag_are_not_removed
142
+ # orig = %(<summary>Foo</summary>)
143
+ # assert_equal orig, SanitizationFilter.call(orig).to_s
144
+ # end
145
+ #
146
+ # def test_details_tag_and_open_attribute_are_not_removed
147
+ # orig = %(<details open>Foo</details>)
148
+ # assert_equal orig, SanitizationFilter.call(orig).to_s
149
+ # end
150
+ #
151
+ # def test_nested_details_tag_are_not_removed
152
+ # orig = <<-NESTED
153
+ # <details>
154
+ # <summary>Foo</summary>
155
+ # <details>
156
+ # Bar
157
+ # <summary>Baz</summary>
158
+ # </details>
159
+ # Qux
160
+ # </details>
161
+ # NESTED
162
+ # assert_equal orig, SanitizationFilter.call(orig).to_s
163
+ # end
164
+ # end
@@ -0,0 +1,59 @@
1
+ # describe 'MotionHTMLPipeline::Pipeline::SyntaxHighlightFilterTest' do
2
+ # SyntaxHighlightFilter = MotionHTMLPipeline::Pipeline::SyntaxHighlightFilter
3
+ #
4
+ # def test_highlight_default
5
+ # filter = SyntaxHighlightFilter.new \
6
+ # '<pre>hello</pre>', highlight: 'coffeescript'
7
+ #
8
+ # doc = filter.call
9
+ # assert !doc.css('.highlight-coffeescript').empty?
10
+ # end
11
+ #
12
+ # def test_highlight_default_will_not_override
13
+ # filter = SyntaxHighlightFilter.new \
14
+ # "<pre lang='c'>hello</pre>", highlight: 'coffeescript'
15
+ #
16
+ # doc = filter.call
17
+ # assert doc.css('.highlight-coffeescript').empty?
18
+ # assert !doc.css('.highlight-c').empty?
19
+ # end
20
+ #
21
+ # def test_highlight_does_not_remove_pre_tag
22
+ # filter = SyntaxHighlightFilter.new \
23
+ # "<pre lang='c'>hello</pre>", highlight: 'coffeescript'
24
+ #
25
+ # doc = filter.call
26
+ #
27
+ # assert !doc.css('pre').empty?
28
+ # end
29
+ #
30
+ # def test_highlight_allows_optional_scope
31
+ # filter = SyntaxHighlightFilter.new \
32
+ # "<pre lang='c'>hello</pre>", highlight: 'coffeescript', scope: 'test-scope'
33
+ #
34
+ # doc = filter.call
35
+ #
36
+ # assert !doc.css('pre.test-scope').empty?
37
+ # end
38
+ #
39
+ # def test_highlight_keeps_the_pre_tags_lang
40
+ # filter = SyntaxHighlightFilter.new \
41
+ # "<pre lang='c'>hello</pre>", highlight: 'coffeescript'
42
+ #
43
+ # doc = filter.call
44
+ #
45
+ # assert !doc.css('pre[lang=c]').empty?
46
+ # end
47
+ #
48
+ # def test_highlight_handles_nested_pre_tags
49
+ # inner_code = "<pre>console.log('i am nested!')</pre>"
50
+ # escaped = EscapeUtils.escape_html(inner_code)
51
+ # html = "<pre lang='html'>#{escaped}</pre>"
52
+ # filter = SyntaxHighlightFilter.new html, highlight: 'html'
53
+ #
54
+ # doc = filter.call
55
+ #
56
+ # assert_equal 2, doc.css('span[class=nt]').length
57
+ # assert_equal EscapeUtils.unescape_html(escaped), doc.inner_text
58
+ # end
59
+ # end
@@ -0,0 +1,137 @@
1
+ # describe 'MotionHTMLPipeline::Pipeline::TableOfContentsFilterTest' do
2
+ # TocFilter = MotionHTMLPipeline::Pipeline::TableOfContentsFilter
3
+ #
4
+ # TocPipeline =
5
+ # HTML::Pipeline.new [
6
+ # HTML::Pipeline::TableOfContentsFilter
7
+ # ]
8
+ #
9
+ # def toc
10
+ # result = {}
11
+ # TocPipeline.call(@orig, {}, result)
12
+ # result[:toc]
13
+ # end
14
+ #
15
+ # def test_anchors_are_added_properly
16
+ # orig = %(<h1>Ice cube</h1><p>Will swarm on any motherfucker in a blue uniform</p>)
17
+ # assert_includes TocFilter.call(orig).to_s, '<a id='
18
+ # end
19
+ #
20
+ # def test_custom_anchor_icons_added_properly
21
+ # orig = %(<h1>Ice cube</h1>)
22
+ # expected = %(<h1>\n<a id="ice-cube" class="anchor" href="#ice-cube" aria-hidden="true">#</a>Ice cube</h1>)
23
+ #
24
+ # assert_equal expected, TocFilter.call(orig, anchor_icon: '#').to_s
25
+ # end
26
+ #
27
+ # def test_toc_list_added_properly
28
+ # @orig = %(<h1>Ice cube</h1><p>Will swarm on any motherfucker in a blue uniform</p>)
29
+ # assert_includes toc, %(<ul class="section-nav">\n<li><a href=")
30
+ # end
31
+ #
32
+ # def test_anchors_have_sane_names
33
+ # orig = %(<h1>Dr Dre</h1><h1>Ice Cube</h1><h1>Eazy-E</h1><h1>MC Ren</h1>)
34
+ # result = TocFilter.call(orig).to_s
35
+ #
36
+ # assert_includes result, '"dr-dre"'
37
+ # assert_includes result, '"ice-cube"'
38
+ # assert_includes result, '"eazy-e"'
39
+ # assert_includes result, '"mc-ren"'
40
+ # end
41
+ #
42
+ # def test_anchors_have_aria_hidden
43
+ # orig = '<h1>Straight Outta Compton</h1>'
44
+ # result = TocFilter.call(orig).to_s
45
+ # assert_includes result, 'aria-hidden="true"'
46
+ # end
47
+ #
48
+ # def test_toc_hrefs_have_sane_values
49
+ # @orig = %(<h1>Dr Dre</h1><h1>Ice Cube</h1><h1>Eazy-E</h1><h1>MC Ren</h1>)
50
+ # assert_includes toc, '"#dr-dre"'
51
+ # assert_includes toc, '"#ice-cube"'
52
+ # assert_includes toc, '"#eazy-e"'
53
+ # assert_includes toc, '"#mc-ren"'
54
+ # end
55
+ #
56
+ # def test_dupe_headers_have_unique_trailing_identifiers
57
+ # orig = %(<h1>Straight Outta Compton</h1>
58
+ # <h2>Dopeman</h2>
59
+ # <h3>Express Yourself</h3>
60
+ # <h1>Dopeman</h1>)
61
+ #
62
+ # result = TocFilter.call(orig).to_s
63
+ #
64
+ # assert_includes result, '"dopeman"'
65
+ # assert_includes result, '"dopeman-1"'
66
+ # end
67
+ #
68
+ # def test_dupe_headers_have_unique_toc_anchors
69
+ # @orig = %(<h1>Straight Outta Compton</h1>
70
+ # <h2>Dopeman</h2>
71
+ # <h3>Express Yourself</h3>
72
+ # <h1>Dopeman</h1>)
73
+ #
74
+ # assert_includes toc, '"#dopeman"'
75
+ # assert_includes toc, '"#dopeman-1"'
76
+ # end
77
+ #
78
+ # def test_all_header_tags_are_found_when_adding_anchors
79
+ # orig = %(<h1>"Funky President" by James Brown</h1>
80
+ # <h2>"It's My Thing" by Marva Whitney</h2>
81
+ # <h3>"Boogie Back" by Roy Ayers</h3>
82
+ # <h4>"Feel Good" by Fancy</h4>
83
+ # <h5>"Funky Drummer" by James Brown</h5>
84
+ # <h6>"Ruthless Villain" by Eazy-E</h6>
85
+ # <h7>"Be Thankful for What You Got" by William DeVaughn</h7>)
86
+ #
87
+ # doc = TocFilter.call(orig)
88
+ #
89
+ # assert_equal 6, doc.search('a').size
90
+ # end
91
+ #
92
+ # def test_toc_outputs_escaped_html
93
+ # @orig = %(<h1>&lt;img src="x" onerror="alert(42)"&gt;</h1>)
94
+ #
95
+ # refute_includes toc, %(<img src="x" onerror="alert(42)">)
96
+ # end
97
+ #
98
+ # def test_toc_is_complete
99
+ # @orig = %(<h1>"Funky President" by James Brown</h1>
100
+ # <h2>"It's My Thing" by Marva Whitney</h2>
101
+ # <h3>"Boogie Back" by Roy Ayers</h3>
102
+ # <h4>"Feel Good" by Fancy</h4>
103
+ # <h5>"Funky Drummer" by James Brown</h5>
104
+ # <h6>"Ruthless Villain" by Eazy-E</h6>
105
+ # <h7>"Be Thankful for What You Got" by William DeVaughn</h7>)
106
+ #
107
+ # expected = %(<ul class="section-nav">\n<li><a href="#funky-president-by-james-brown">&quot;Funky President&quot; by James Brown</a></li>\n<li><a href="#its-my-thing-by-marva-whitney">&quot;It&#39;s My Thing&quot; by Marva Whitney</a></li>\n<li><a href="#boogie-back-by-roy-ayers">&quot;Boogie Back&quot; by Roy Ayers</a></li>\n<li><a href="#feel-good-by-fancy">&quot;Feel Good&quot; by Fancy</a></li>\n<li><a href="#funky-drummer-by-james-brown">&quot;Funky Drummer&quot; by James Brown</a></li>\n<li><a href="#ruthless-villain-by-eazy-e">&quot;Ruthless Villain&quot; by Eazy-E</a></li>\n</ul>)
108
+ #
109
+ # assert_equal expected, toc
110
+ # end
111
+ #
112
+ # if RUBY_VERSION > '1.9' # not sure how to make this work on 1.8.7
113
+ #
114
+ # def test_anchors_with_utf8_characters
115
+ # orig = %(<h1>日本語</h1>
116
+ # <h1>Русский</h1)
117
+ #
118
+ # rendered_h1s = TocFilter.call(orig).search('h1').map(&:to_s)
119
+ #
120
+ # assert_equal "<h1>\n<a id=\"日本語\" class=\"anchor\" href=\"#%E6%97%A5%E6%9C%AC%E8%AA%9E\" aria-hidden=\"true\"><span aria-hidden=\"true\" class=\"octicon octicon-link\"></span></a>日本語</h1>",
121
+ # rendered_h1s[0]
122
+ # assert_equal "<h1>\n<a id=\"Русский\" class=\"anchor\" href=\"#%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9\" aria-hidden=\"true\"><span aria-hidden=\"true\" class=\"octicon octicon-link\"></span></a>Русский</h1>",
123
+ # rendered_h1s[1]
124
+ # end
125
+ #
126
+ # def test_toc_with_utf8_characters
127
+ # @orig = %(<h1>日本語</h1>
128
+ # <h1>Русский</h1)
129
+ #
130
+ # rendered_toc = Nokogiri::HTML::DocumentFragment.parse(toc).to_s
131
+ #
132
+ # expected = %(<ul class="section-nav">\n<li><a href="#%E6%97%A5%E6%9C%AC%E8%AA%9E">日本語</a></li>\n<li><a href="#%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9">Русский</a></li>\n</ul>)
133
+ #
134
+ # assert_equal expected, rendered_toc
135
+ # end
136
+ # end
137
+ # end