html-pipeline-1.11.0/0000755000076400007640000000000012545202654013402 5ustar pravipravihtml-pipeline-1.11.0/LICENSE0000644000076400007640000000207412545202654014412 0ustar pravipraviCopyright (c) 2012 GitHub Inc. and Jerry Cheung MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.html-pipeline-1.11.0/CONTRIBUTING.md0000644000076400007640000000300512545202654015631 0ustar pravipravi# Contributing Thanks for using and improving `HTML::Pipeline`! - [Submitting a New Issue](#submitting-a-new-issue) - [Sending a Pull Request](#sending-a-pull-request) ## Submitting a New Issue If there's an idea you'd like to propose, or a design change, feel free to file a new issue. If you have an implementation question or believe you've found a bug, please provide as many details as possible: - Input document - Output HTML document - the exact `HTML::Pipeline` code you are using - output of the following from your project ``` ruby -v bundle exec nokogiri -v ``` ## Sending a Pull Request [Pull requests][pr] are always welcome! Check out [the project's issues list][issues] for ideas on what could be improved. Before sending, please add tests and ensure the test suite passes. ### Running the Tests To run the full suite: `bundle exec rake` To run a specific test file: `bundle exec ruby -Itest test/html/pipeline_test.rb` To run a specific test: `bundle exec ruby -Itest test/html/pipeline/markdown_filter_test.rb -n test_disabling_gfm` To run the full suite with all [supported rubies][travisyaml] in bash: ```bash rubies=(ree-1.8.7-2011.03 1.9.2-p290 1.9.3-p429 2.0.0-p247) for r in ${rubies[*]} do rbenv local $r # switch to your version manager of choice bundle install bundle exec rake done ``` [issues]: https://github.com/jch/html-pipeline/issues [pr]: https://help.github.com/articles/using-pull-requests [travisyaml]: https://github.com/jch/html-pipeline/blob/master/.travis.yml html-pipeline-1.11.0/.travis.yml0000644000076400007640000000036412545202654015516 0ustar pravipravilanguage: ruby before_install: - sudo apt-get update -qq - sudo apt-get install -qq libicu-dev script: "bundle exec rake" rvm: - 1.9.2 - 1.9.3 - 2.0.0 - 2.1.1 - ree matrix: fast_finish: true allow_failures: - rvm: ree html-pipeline-1.11.0/bin/0000755000076400007640000000000012545202654014152 5ustar pravipravihtml-pipeline-1.11.0/bin/html-pipeline0000755000076400007640000000333512545202654016653 0ustar pravipravi#!/usr/bin/env ruby require 'html/pipeline' require 'optparse' # Accept "help", too ARGV.map!{|a| a == "help" ? "--help" : a } OptionParser.new do |opts| opts.banner = <<-HELP.gsub(/^ /, '') Usage: html-pipeline [-h] [-f] html-pipeline [FILTER [FILTER [...]]] < file.md cat file.md | html-pipeline [FILTER [FILTER [...]]] HELP opts.separator "Options:" opts.on("-f", "--filters", "List the available filters") do filters = HTML::Pipeline.constants.grep(/\w+Filter$/). map{|f| f.to_s.gsub(/Filter$/,'') } # Text filter doesn't work, no call method filters -= ["Text"] abort <<-HELP.gsub(/^ /, '') Available filters: #{filters.join("\n ")} HELP end end.parse! # Default to a GitHub-ish pipeline if ARGV.empty? filters = [ HTML::Pipeline::MarkdownFilter, HTML::Pipeline::SanitizationFilter, HTML::Pipeline::ImageMaxWidthFilter, HTML::Pipeline::EmojiFilter, HTML::Pipeline::AutolinkFilter, HTML::Pipeline::TableOfContentsFilter, ] # Add syntax highlighting if linguist is present begin require 'linguist' filters << HTML::Pipeline::SyntaxHighlightFilter rescue LoadError end else def filter_named(name) case name when "Text" raise NameError # Text filter doesn't work, no call method end HTML::Pipeline.const_get("#{name}Filter") rescue NameError => e abort "Unknown filter '#{name}'. List filters with the -f option." end filters = [] until ARGV.empty? name = ARGV.shift filters << filter_named(name) end end context = { :asset_root => "/assets", :base_url => "/", :gfm => true } puts HTML::Pipeline.new(filters, context).call(ARGF.read)[:output] html-pipeline-1.11.0/script/0000755000076400007640000000000012545202654014706 5ustar pravipravihtml-pipeline-1.11.0/script/release0000755000076400007640000000061612545202654016257 0ustar pravipravi#!/usr/bin/env bash # Usage: script/release # Build the package, tag a commit, push it to origin, and then release the # package publicly. set -e version="$(script/package | grep Version: | awk '{print $2}')" [ -n "$version" ] || exit 1 echo $version git commit --allow-empty -a -m "Release $version" git tag "v$version" git push origin git push origin "v$version" gem push pkg/*-${version}.gem html-pipeline-1.11.0/script/package0000755000076400007640000000023012545202654016222 0ustar pravipravi#!/usr/bin/env bash # Usage: script/gem # Updates the gemspec and builds a new gem in the pkg directory. mkdir -p pkg gem build *.gemspec mv *.gem pkg html-pipeline-1.11.0/html-pipeline.gemspec0000644000076400007640000000212112545202654017512 0ustar pravipravi# -*- encoding: utf-8 -*- require File.expand_path("../lib/html/pipeline/version", __FILE__) Gem::Specification.new do |gem| gem.name = "html-pipeline" gem.version = HTML::Pipeline::VERSION gem.license = "MIT" gem.authors = ["Ryan Tomayko", "Jerry Cheung"] gem.email = ["ryan@github.com", "jerry@github.com"] gem.description = %q{GitHub HTML processing filters and utilities} gem.summary = %q{Helpers for processing content through a chain of filters} gem.homepage = "https://github.com/jch/html-pipeline" gem.files = `git ls-files`.split $/ gem.test_files = gem.files.grep(%r{^test}) gem.require_paths = ["lib"] gem.add_dependency "nokogiri", "~> 1.4" gem.add_dependency "activesupport", ">= 2" gem.post_install_message = <This is great:

some_code(:first)
``` To generate CSS for HTML formatted code, use the [pygments.rb](https://github.com/tmm1/pygments.rb#usage) `#css` method. `pygments.rb` is a dependency of the `SyntaxHighlightFilter`. Some filters take an optional **context** and/or **result** hash. These are used to pass around arguments and metadata between filters in a pipeline. For example, if you don't want to use GitHub formatted Markdown, you can pass an option in the context hash: ```ruby filter = HTML::Pipeline::MarkdownFilter.new("Hi **world**!", :gfm => false) filter.call ``` ### Examples We define different pipelines for different parts of our app. Here are a few paraphrased snippets to get you started: ```ruby # The context hash is how you pass options between different filters. # See individual filter source for explanation of options. context = { :asset_root => "http://your-domain.com/where/your/images/live/icons", :base_url => "http://your-domain.com" } # Pipeline providing sanitization and image hijacking but no mention # related features. SimplePipeline = Pipeline.new [ SanitizationFilter, TableOfContentsFilter, # add 'name' anchors to all headers and generate toc list CamoFilter, ImageMaxWidthFilter, SyntaxHighlightFilter, EmojiFilter, AutolinkFilter ], context # Pipeline used for user provided content on the web MarkdownPipeline = Pipeline.new [ MarkdownFilter, SanitizationFilter, CamoFilter, ImageMaxWidthFilter, HttpsFilter, MentionFilter, EmojiFilter, SyntaxHighlightFilter ], context.merge(:gfm => true) # enable github formatted markdown # Define a pipeline based on another pipeline's filters NonGFMMarkdownPipeline = Pipeline.new(MarkdownPipeline.filters, context.merge(:gfm => false)) # Pipelines aren't limited to the web. You can use them for email # processing also. HtmlEmailPipeline = Pipeline.new [ ImageMaxWidthFilter ], {} # Just emoji. EmojiPipeline = Pipeline.new [ PlainTextInputFilter, EmojiFilter ], context ``` ## Filters * `MentionFilter` - replace `@user` mentions with links * `AbsoluteSourceFilter` - replace relative image urls with fully qualified versions * `AutolinkFilter` - auto_linking urls in HTML * `CamoFilter` - replace http image urls with [camo-fied](https://github.com/atmos/camo) https versions * `EmailReplyFilter` - util filter for working with emails * `EmojiFilter` - everyone loves [emoji](http://www.emoji-cheat-sheet.com/)! * `HttpsFilter` - HTML Filter for replacing http github urls with https versions. * `ImageMaxWidthFilter` - link to full size image for large images * `MarkdownFilter` - convert markdown to html * `PlainTextInputFilter` - html escape text and wrap the result in a div * `SanitizationFilter` - whitelist sanitize user markup * `SyntaxHighlightFilter` - [code syntax highlighter](#syntax-highlighting) * `TextileFilter` - convert textile to html * `TableOfContentsFilter` - anchor headings with name attributes and generate Table of Contents html unordered list linking headings ## Dependencies Filter gem dependencies are not bundled; you must bundle the filter's gem dependencies. The below list details filters with dependencies. For example, `SyntaxHighlightFilter` uses [github-linguist](https://github.com/github/linguist) to detect and highlight languages. For example, to use the `SyntaxHighlightFilter`, add the following to your Gemfile: ```ruby gem 'github-linguist' ``` * `AutolinkFilter` - `rinku` * `EmailReplyFilter` - `escape_utils`, `email_reply_parser` * `EmojiFilter` - `gemoji` * `MarkdownFilter` - `github-markdown` * `PlainTextInputFilter` - `escape_utils` * `SanitizationFilter` - `sanitize` * `SyntaxHighlightFilter` - `github-linguist` * `TextileFilter` - `RedCloth` _Note:_ See [Gemfile](/Gemfile) `:test` block for version requirements. ## Documentation Full reference documentation can be [found here](http://rubydoc.info/gems/html-pipeline/frames). ## Extending To write a custom filter, you need a class with a `call` method that inherits from `HTML::Pipeline::Filter`. For example this filter adds a base url to images that are root relative: ```ruby require 'uri' class RootRelativeFilter < HTML::Pipeline::Filter def call doc.search("img").each do |img| next if img['src'].nil? src = img['src'].strip if src.start_with? '/' img["src"] = URI.join(context[:base_url], src).to_s end end doc end end ``` Now this filter can be used in a pipeline: ```ruby Pipeline.new [ RootRelativeFilter ], { :base_url => 'http://somehost.com' } ``` ### 3rd Party Extensions If you have an idea for a filter, propose it as [an issue](https://github.com/jch/html-pipeline/issues) first. This allows us discuss whether the filter is a common enough use case to belong in this gem, or should be built as an external gem. * [html-pipeline-asciidoc_filter](https://github.com/asciidoctor/html-pipeline-asciidoc_filter) - asciidoc support ## Instrumenting Filters and Pipelines can be set up to be instrumented when called. The pipeline must be setup with an [ActiveSupport::Notifications] (http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) compatible service object and a name. New pipeline objects will default to the `HTML::Pipeline.default_instrumentation_service` object. ``` ruby # the AS::Notifications-compatible service object service = ActiveSupport::Notifications # instrument a specific pipeline pipeline = HTML::Pipeline.new [MarkdownFilter], context pipeline.setup_instrumentation "MarkdownPipeline", service # or set default instrumentation service for all new pipelines HTML::Pipeline.default_instrumentation_service = service pipeline = HTML::Pipeline.new [MarkdownFilter], context pipeline.setup_instrumentation "MarkdownPipeline" ``` Filters are instrumented when they are run through the pipeline. A `call_filter.html_pipeline` event is published once the filter finishes. The `payload` should include the `filter` name. Each filter will trigger its own instrumentation call. ``` ruby service.subscribe "call_filter.html_pipeline" do |event, start, ending, transaction_id, payload| payload[:pipeline] #=> "MarkdownPipeline", set with `setup_instrumentation` payload[:filter] #=> "MarkdownFilter" payload[:context] #=> context Hash payload[:result] #=> instance of result class payload[:result][:output] #=> output HTML String or Nokogiri::DocumentFragment end ``` The full pipeline is also instrumented: ``` ruby service.subscribe "call_pipeline.html_pipeline" do |event, start, ending, transaction_id, payload| payload[:pipeline] #=> "MarkdownPipeline", set with `setup_instrumentation` payload[:filters] #=> ["MarkdownFilter"] payload[:doc] #=> HTML String or Nokogiri::DocumentFragment payload[:context] #=> context Hash payload[:result] #=> instance of result class payload[:result][:output] #=> output HTML String or Nokogiri::DocumentFragment end ``` ## Contributing Please review the [Contributing Guide](https://github.com/jch/html-pipeline/blob/master/CONTRIBUTING.md). 1. [Fork it](https://help.github.com/articles/fork-a-repo) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Added some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new [Pull Request](https://help.github.com/articles/using-pull-requests) To see what has changed in recent versions, see the [CHANGELOG](https://github.com/jch/html-pipeline/blob/master/CHANGELOG.md). ### Contributors Thanks to all of [these contributors](https://github.com/jch/html-pipeline/graphs/contributors). Project is a member of the [OSS Manifesto](http://ossmanifesto.org/). ### Releasing A New Version This section is for gem maintainers to cut a new version of the gem. * update lib/html/pipeline/version.rb to next version number X.X.X following [semver](http://semver.org). * update CHANGELOG.md. Get latest changes with `git log --oneline vLAST_RELEASE..HEAD | grep Merge` * on the master branch, run `script/release` html-pipeline-1.11.0/test/0000755000076400007640000000000012545202654014361 5ustar pravipravihtml-pipeline-1.11.0/test/test_helper.rb0000644000076400007640000000072512545202654017230 0ustar pravipravirequire 'bundler/setup' require 'html/pipeline' require 'minitest/autorun' require 'active_support/core_ext/string' module TestHelpers # Asserts that two html fragments are equivalent. Attribute order # will be ignored. def assert_equal_html(expected, actual) assert_equal Nokogiri::HTML::DocumentFragment.parse(expected).to_hash, Nokogiri::HTML::DocumentFragment.parse(actual).to_hash end end Minitest::Test.send(:include, TestHelpers) html-pipeline-1.11.0/test/html/0000755000076400007640000000000012545202654015325 5ustar pravipravihtml-pipeline-1.11.0/test/html/pipeline/0000755000076400007640000000000012545202654017132 5ustar pravipravihtml-pipeline-1.11.0/test/html/pipeline/toc_filter_test.rb0000644000076400007640000001127112545202654022652 0ustar pravipravi# encoding: utf-8 require "test_helper" class HTML::Pipeline::TableOfContentsFilterTest < Minitest::Test TocFilter = HTML::Pipeline::TableOfContentsFilter TocPipeline = HTML::Pipeline.new [ HTML::Pipeline::TableOfContentsFilter ] def toc result = {} TocPipeline.call(@orig, {}, result) result[:toc] end def test_anchors_are_added_properly orig = %(

Ice cube

Will swarm on any motherfucker in a blue uniform

) assert_includes TocFilter.call(orig).to_s, 'Dr Dre

Ice Cube

Eazy-E

MC Ren

) assert_includes toc, '"#dr-dre"' assert_includes toc, '"#ice-cube"' assert_includes toc, '"#eazy-e"' assert_includes toc, '"#mc-ren"' end def test_dupe_headers_have_unique_trailing_identifiers orig = %(

Straight Outta Compton

Dopeman

Express Yourself

Dopeman

) result = TocFilter.call(orig).to_s assert_includes result, '"dopeman"' assert_includes result, '"dopeman-1"' end def test_dupe_headers_have_unique_toc_anchors @orig = %(

Straight Outta Compton

Dopeman

Express Yourself

Dopeman

) assert_includes toc, '"#dopeman"' assert_includes toc, '"#dopeman-1"' end def test_all_header_tags_are_found_when_adding_anchors orig = %(

"Funky President" by James Brown

"It's My Thing" by Marva Whitney

"Boogie Back" by Roy Ayers

"Feel Good" by Fancy

"Funky Drummer" by James Brown
"Ruthless Villain" by Eazy-E
"Be Thankful for What You Got" by William DeVaughn) doc = TocFilter.call(orig) assert_equal 6, doc.search('a').size end def test_toc_is_complete @orig = %(

"Funky President" by James Brown

"It's My Thing" by Marva Whitney

"Boogie Back" by Roy Ayers

"Feel Good" by Fancy

"Funky Drummer" by James Brown
"Ruthless Villain" by Eazy-E
"Be Thankful for What You Got" by William DeVaughn) expected = %Q{
} assert_equal expected, toc end if RUBY_VERSION > "1.9" # not sure how to make this work on 1.8.7 def test_anchors_with_utf8_characters orig = %(

日本語

Русский\n日本語

", rendered_h1s[0] assert_equal "

\nРусский

", rendered_h1s[1] end def test_toc_with_utf8_characters @orig = %(

日本語

Русский\n
  • 日本語
  • \n
  • Русский
  • \n} assert_equal expected, rendered_toc end end end html-pipeline-1.11.0/test/html/pipeline/sanitization_filter_test.rb0000644000076400007640000001101112545202654024571 0ustar pravipravirequire "test_helper" class HTML::Pipeline::SanitizationFilterTest < Minitest::Test SanitizationFilter = HTML::Pipeline::SanitizationFilter def test_removing_script_tags orig = %(

    ) html = SanitizationFilter.call(orig).to_s refute_match /script/, html end def test_removing_style_tags orig = %(

    ) html = SanitizationFilter.call(orig).to_s refute_match /style/, html end def test_removing_style_attributes orig = %(

    YO DAWG

    ) html = SanitizationFilter.call(orig).to_s refute_match /font-size/, html refute_match /style/, html end def test_removing_script_event_handler_attributes orig = %(YO DAWG) html = SanitizationFilter.call(orig).to_s refute_match /javscript/, html refute_match /onclick/, html end def test_sanitizes_li_elements_not_contained_in_ul_or_ol stuff = "a\n
  • b
  • \nc" html = SanitizationFilter.call(stuff).to_s assert_equal "a\nb\nc", html end def test_does_not_sanitize_li_elements_contained_in_ul_or_ol stuff = "a\n\nc" assert_equal stuff, SanitizationFilter.call(stuff).to_s end def test_github_specific_protocols_are_not_removed stuff = 'Spill this yo and so on' assert_equal stuff, SanitizationFilter.call(stuff).to_s end def test_unknown_schemes_are_removed stuff = 'Wat is this' html = SanitizationFilter.call(stuff).to_s assert_equal 'Wat is this', html end def test_standard_schemes_are_removed_if_not_specified_in_anchor_schemes stuff = 'No href for you' filter = SanitizationFilter.new(stuff, {:anchor_schemes => []}) html = filter.call.to_s assert_equal 'No href for you', html end def test_custom_anchor_schemes_are_not_removed stuff = 'Wat is this' filter = SanitizationFilter.new(stuff, {:anchor_schemes => ['something-weird']}) html = filter.call.to_s assert_equal stuff, html end def test_anchor_schemes_are_merged_with_other_anchor_restrictions stuff = 'Wat is this' whitelist = { :elements => ['a'], :attributes => {'a' => ['href', 'ping']}, :protocols => {'a' => {'ping' => ['http']}} } filter = SanitizationFilter.new(stuff, {:whitelist => whitelist, :anchor_schemes => ['something-weird']}) html = filter.call.to_s assert_equal 'Wat is this', html end def test_uses_anchor_schemes_from_whitelist_when_not_separately_specified stuff = 'Wat is this' whitelist = { :elements => ['a'], :attributes => {'a' => ['href']}, :protocols => {'a' => {'href' => ['something-weird']}} } filter = SanitizationFilter.new(stuff, {:whitelist => whitelist}) html = filter.call.to_s assert_equal stuff, html end def test_whitelist_contains_default_anchor_schemes assert_equal SanitizationFilter::WHITELIST[:protocols]['a']['href'], ['http', 'https', 'mailto', :relative, 'github-windows', 'github-mac'] end def test_whitelist_from_full_constant stuff = 'Wat is this' filter = SanitizationFilter.new(stuff, :whitelist => SanitizationFilter::FULL) html = filter.call.to_s assert_equal 'Wat is this', html end def test_exports_default_anchor_schemes assert_equal SanitizationFilter::ANCHOR_SCHEMES, ['http', 'https', 'mailto', :relative, 'github-windows', 'github-mac'] end def test_script_contents_are_removed orig = '' assert_equal "", SanitizationFilter.call(orig).to_s end def test_table_rows_and_cells_removed_if_not_in_table orig = %(FooBar) assert_equal 'FooBar', SanitizationFilter.call(orig).to_s end def test_table_sections_removed_if_not_in_table orig = %(Foo) assert_equal 'Foo', SanitizationFilter.call(orig).to_s end def test_table_sections_are_not_removed orig = %(
    Column 1
    Sum
    1
    ) assert_equal orig, SanitizationFilter.call(orig).to_s end end html-pipeline-1.11.0/test/html/pipeline/https_filter_test.rb0000644000076400007640000000301512545202654023224 0ustar pravipravirequire "test_helper" HttpsFilter = HTML::Pipeline::HttpsFilter class HTML::Pipeline::AutolinkFilterTest < Minitest::Test def filter(html) HttpsFilter.to_html(html, @options) end def setup @options = {:base_url => "http://github.com"} end def test_http assert_equal %(github.com), filter(%(github.com)) end def test_https assert_equal %(github.com), filter(%(github.com)) end def test_subdomain assert_equal %(github.com), filter(%(github.com)) end def test_other assert_equal %(github.io), filter(%(github.io)) end def test_uses_http_url_over_base_url @options = {:http_url => "http://github.com", :base_url => "https://github.com"} assert_equal %(github.com), filter(%(github.com)) end def test_only_http_url @options = {:http_url => "http://github.com"} assert_equal %(github.com), filter(%(github.com)) end def test_validates_http_url @options.clear exception = assert_raises(ArgumentError) { filter("") } assert_match "HTML::Pipeline::HttpsFilter: :http_url", exception.message end end html-pipeline-1.11.0/test/html/pipeline/emoji_filter_test.rb0000644000076400007640000000231312545202654023165 0ustar pravipravirequire 'test_helper' class HTML::Pipeline::EmojiFilterTest < Minitest::Test EmojiFilter = HTML::Pipeline::EmojiFilter def test_emojify filter = EmojiFilter.new("

    :shipit:

    ", {:asset_root => 'https://foo.com'}) doc = filter.call assert_match "https://foo.com/emoji/shipit.png", doc.search('img').attr('src').value end def test_emojify_on_string filter = EmojiFilter.new(":shipit:", {:asset_root => 'https://foo.com'}) doc = filter.call assert_match "https://foo.com/emoji/shipit.png", doc.search('img').attr('src').value end def test_uri_encoding filter = EmojiFilter.new("

    :+1:

    ", {:asset_root => 'https://foo.com'}) doc = filter.call assert_match "https://foo.com/emoji/%2B1.png", doc.search('img').attr('src').value end def test_required_context_validation exception = assert_raises(ArgumentError) { EmojiFilter.call("", {}) } assert_match /:asset_root/, exception.message end def test_custom_asset_path filter = EmojiFilter.new("

    :+1:

    ", {:asset_path => ':file_name', :asset_root => 'https://foo.com'}) doc = filter.call assert_match "https://foo.com/%2B1.png", doc.search('img').attr('src').value end end html-pipeline-1.11.0/test/html/pipeline/absolute_source_filter_test.rb0000644000076400007640000000334412545202654025265 0ustar pravipravirequire "test_helper" class HTML::Pipeline::AbsoluteSourceFilterTest < Minitest::Test AbsoluteSourceFilter = HTML::Pipeline::AbsoluteSourceFilter def setup @image_base_url = 'http://assets.example.com' @image_subpage_url = 'http://blog.example.com/a/post' @options = { :image_base_url => @image_base_url, :image_subpage_url => @image_subpage_url } end def test_rewrites_root_urls orig = %(

    ) assert_equal "

    ", AbsoluteSourceFilter.call(orig, @options).to_s end def test_rewrites_relative_urls orig = %(

    ) assert_equal "

    ", AbsoluteSourceFilter.call(orig, @options).to_s end def test_does_not_rewrite_absolute_urls orig = %(

    ) result = AbsoluteSourceFilter.call(orig, @options).to_s refute_match /@image_base_url/, result refute_match /@image_subpage_url/, result end def test_fails_when_context_is_missing assert_raises RuntimeError do AbsoluteSourceFilter.call("", {}) end assert_raises RuntimeError do AbsoluteSourceFilter.call("", {}) end end def test_tells_you_where_context_is_required exception = assert_raises(RuntimeError) { AbsoluteSourceFilter.call("", {}) } assert_match 'HTML::Pipeline::AbsoluteSourceFilter', exception.message exception = assert_raises(RuntimeError) { AbsoluteSourceFilter.call("", {}) } assert_match 'HTML::Pipeline::AbsoluteSourceFilter', exception.message end end html-pipeline-1.11.0/test/html/pipeline/mention_filter_test.rb0000644000076400007640000001136612545202654023543 0ustar pravipravirequire "test_helper" class HTML::Pipeline::MentionFilterTest < Minitest::Test def filter(html, base_url='/', info_url=nil) HTML::Pipeline::MentionFilter.call(html, :base_url => base_url, :info_url => info_url) end def test_filtering_a_documentfragment body = "

    @kneath: check it out.

    " doc = Nokogiri::HTML::DocumentFragment.parse(body) res = filter(doc, '/') assert_same doc, res link = "@kneath" assert_equal "

    #{link}: check it out.

    ", res.to_html end def test_filtering_plain_text body = "

    @kneath: check it out.

    " res = filter(body, '/') link = "@kneath" assert_equal "

    #{link}: check it out.

    ", res.to_html end def test_not_replacing_mentions_in_pre_tags body = "
    @kneath: okay
    " assert_equal body, filter(body).to_html end def test_not_replacing_mentions_in_code_tags body = "

    @kneath: okay

    " assert_equal body, filter(body).to_html end def test_not_replacing_mentions_in_style_tags body = "" assert_equal body, filter(body).to_html end def test_not_replacing_mentions_in_links body = "

    @kneath okay

    " assert_equal body, filter(body).to_html end def test_entity_encoding_and_whatnot body = "

    @kneath what's up

    " link = "@kneath" assert_equal "

    #{link} what's up

    ", filter(body, '/').to_html end def test_html_injection body = "

    @kneath <script>alert(0)</script>

    " link = "@kneath" assert_equal "

    #{link} <script>alert(0)</script>

    ", filter(body, '/').to_html end def test_links_to_nothing_when_no_info_url_given body = "

    How do I @mention someone?

    " assert_equal "

    How do I @mention someone?

    ", filter(body, '/').to_html end def test_links_to_more_info_when_info_url_given body = "

    How do I @mention someone?

    " link = "@mention" assert_equal "

    How do I #{link} someone?

    ", filter(body, '/', 'https://github.com/blog/821').to_html end MarkdownPipeline = HTML::Pipeline.new [ HTML::Pipeline::MarkdownFilter, HTML::Pipeline::MentionFilter ] def mentioned_usernames result = {} MarkdownPipeline.call(@body, {}, result) result[:mentioned_usernames] end def test_matches_usernames_in_body @body = "@test how are you?" assert_equal %w[test], mentioned_usernames end def test_matches_usernames_with_dashes @body = "hi @some-user" assert_equal %w[some-user], mentioned_usernames end def test_matches_usernames_followed_by_a_single_dot @body = "okay @some-user." assert_equal %w[some-user], mentioned_usernames end def test_matches_usernames_followed_by_multiple_dots @body = "okay @some-user..." assert_equal %w[some-user], mentioned_usernames end def test_does_not_match_email_addresses @body = "aman@tmm1.net" assert_equal [], mentioned_usernames end def test_does_not_match_domain_name_looking_things @body = "we need a @github.com email" assert_equal [], mentioned_usernames end def test_does_not_match_organization_team_mentions @body = "we need to @github/enterprise know" assert_equal [], mentioned_usernames end def test_matches_colon_suffixed_names @body = "@tmm1: what do you think?" assert_equal %w[tmm1], mentioned_usernames end def test_matches_list_of_names @body = "@defunkt @atmos @kneath" assert_equal %w[defunkt atmos kneath], mentioned_usernames end def test_matches_list_of_names_with_commas @body = "/cc @defunkt, @atmos, @kneath" assert_equal %w[defunkt atmos kneath], mentioned_usernames end def test_matches_inside_brackets @body = "(@mislav) and [@rtomayko]" assert_equal %w[mislav rtomayko], mentioned_usernames end def test_doesnt_ignore_invalid_users @body = "@defunkt @mojombo and @somedude" assert_equal ['defunkt', 'mojombo', 'somedude'], mentioned_usernames end def test_returns_distinct_set @body = "/cc @defunkt, @atmos, @kneath, @defunkt, @defunkt" assert_equal %w[defunkt atmos kneath], mentioned_usernames end def test_does_not_match_inline_code_block_with_multiple_code_blocks @body = "something\n\n`/cc @defunkt @atmos @kneath` `/cc @atmos/atmos`" assert_equal %w[], mentioned_usernames end def test_mention_at_end_of_parenthetical_sentence @body = "(We're talking 'bout @ymendel.)" assert_equal %w[ymendel], mentioned_usernames end end html-pipeline-1.11.0/test/html/pipeline/image_max_width_filter_test.rb0000644000076400007640000000356212545202654025217 0ustar pravipravirequire "test_helper" class HTML::Pipeline::ImageMaxWidthFilterTest < Minitest::Test def filter(html) HTML::Pipeline::ImageMaxWidthFilter.call(html) end def test_rewrites_image_style_tags body = "

    Screenshot:

    " doc = Nokogiri::HTML::DocumentFragment.parse(body) res = filter(doc) assert_equal_html %q(

    Screenshot:

    ), res.to_html end def test_leaves_existing_image_style_tags_alone body = "

    " doc = Nokogiri::HTML::DocumentFragment.parse(body) res = filter(doc) assert_equal_html '

    ', res.to_html end def test_links_to_image body = "

    Screenshot:

    " doc = Nokogiri::HTML::DocumentFragment.parse(body) res = filter(doc) assert_equal_html '

    Screenshot:

    ', res.to_html end def test_doesnt_link_to_image_when_already_linked body = "

    Screenshot:

    " doc = Nokogiri::HTML::DocumentFragment.parse(body) res = filter(doc) assert_equal_html %q(

    Screenshot:

    ), res.to_html end def test_doesnt_screw_up_inlined_images body = "

    Screenshot , yes, this is a screenshot indeed.

    " doc = Nokogiri::HTML::DocumentFragment.parse(body) assert_equal_html %q(

    Screenshot , yes, this is a screenshot indeed.

    ), filter(doc).to_html end end html-pipeline-1.11.0/test/html/pipeline/plain_text_input_filter_test.rb0000644000076400007640000000130512545202654025450 0ustar pravipravirequire "test_helper" class HTML::Pipeline::PlainTextInputFilterTest < Minitest::Test PlainTextInputFilter = HTML::Pipeline::PlainTextInputFilter def test_fails_when_given_a_documentfragment body = "

    heyo

    " doc = Nokogiri::HTML::DocumentFragment.parse(body) assert_raises(TypeError) { PlainTextInputFilter.call(doc, {}) } end def test_wraps_input_in_a_div_element doc = PlainTextInputFilter.call("howdy pahtner", {}) assert_equal "
    howdy pahtner
    ", doc.to_s end def test_html_escapes_plain_text_input doc = PlainTextInputFilter.call("See: ", {}) assert_equal "
    See: <http://example.org>
    ", doc.to_s end end html-pipeline-1.11.0/test/html/pipeline/camo_filter_test.rb0000644000076400007640000000562712545202654023014 0ustar pravipravirequire "test_helper" class HTML::Pipeline::CamoFilterTest < Minitest::Test CamoFilter = HTML::Pipeline::CamoFilter def setup @asset_proxy_url = 'https//assets.example.org' @asset_proxy_secret_key = 'ssssh-secret' @options = { :asset_proxy => @asset_proxy_url, :asset_proxy_secret_key => @asset_proxy_secret_key, :asset_proxy_whitelist => [/(^|\.)github\.com$/] } end def test_asset_proxy_disabled orig = %(

    ) assert_equal orig, CamoFilter.call(orig, @options.merge(:disable_asset_proxy => true)).to_s end def test_camouflaging_http_image_urls orig = %(

    ) assert_equal %(

    ), CamoFilter.call(orig, @options).to_s end def test_doesnt_rewrite_dotcom_image_urls orig = %(

    ) assert_equal orig, CamoFilter.call(orig, @options).to_s end def test_doesnt_rewrite_dotcom_subdomain_image_urls orig = %(

    ) assert_equal orig, CamoFilter.call(orig, @options).to_s end def test_doesnt_rewrite_dotcom_subsubdomain_image_urls orig = %(

    ) assert_equal orig, CamoFilter.call(orig, @options).to_s end def test_camouflaging_github_prefixed_image_urls orig = %(

    ) assert_equal %(

    ), CamoFilter.call(orig, @options).to_s end def test_doesnt_rewrite_absolute_image_urls orig = %(

    ) assert_equal orig, CamoFilter.call(orig, @options).to_s end def test_doesnt_rewrite_relative_image_urls orig = %(

    ) assert_equal orig, CamoFilter.call(orig, @options).to_s end def test_camouflaging_https_image_urls orig = %(

    ) assert_equal %(

    ), CamoFilter.call(orig, @options).to_s end def test_handling_images_with_no_src_attribute orig = %(

    ) assert_equal orig, CamoFilter.call(orig, @options).to_s end def test_required_context_validation exception = assert_raises(ArgumentError) { CamoFilter.call("", {}) } assert_match /:asset_proxy[^_]/, exception.message assert_match /:asset_proxy_secret_key/, exception.message end end html-pipeline-1.11.0/test/html/pipeline/autolink_filter_test.rb0000644000076400007640000000252112545202654023711 0ustar pravipravirequire "test_helper" AutolinkFilter = HTML::Pipeline::AutolinkFilter class HTML::Pipeline::AutolinkFilterTest < Minitest::Test def test_uses_rinku_for_autolinking # just try to parse a complicated piece of HTML # that Rails auto_link cannot handle assert_equal '

    "http://www.github.com"

    ', AutolinkFilter.to_html('

    "http://www.github.com"

    ') end def test_autolink_option assert_equal '

    "http://www.github.com"

    ', AutolinkFilter.to_html('

    "http://www.github.com"

    ', :autolink => false) end def test_autolink_link_attr assert_equal '

    "http://www.github.com"

    ', AutolinkFilter.to_html('

    "http://www.github.com"

    ', :link_attr => 'target="_blank"') end def test_autolink_flags assert_equal '

    "http://github"

    ', AutolinkFilter.to_html('

    "http://github"

    ', :flags => Rinku::AUTOLINK_SHORT_DOMAINS) end def test_autolink_skip_tags assert_equal '"http://github.com"', AutolinkFilter.to_html('"http://github.com"') assert_equal '"http://github.com"', AutolinkFilter.to_html('"http://github.com"', :skip_tags => %w(kbd script)) end end html-pipeline-1.11.0/test/html/pipeline/syntax_highlight_filter_test.rb0000644000076400007640000000120112545202654025432 0ustar pravipravirequire "test_helper" SyntaxHighlightFilter = HTML::Pipeline::SyntaxHighlightFilter class HTML::Pipeline::SyntaxHighlightFilterTest < Minitest::Test def test_highlight_default filter = SyntaxHighlightFilter.new \ "
    hello
    ", :highlight => "coffeescript" doc = filter.call assert !doc.css(".highlight-coffeescript").empty? end def test_highlight_default_will_not_override filter = SyntaxHighlightFilter.new \ "
    hello
    ", :highlight => "coffeescript" doc = filter.call assert doc.css(".highlight-coffeescript").empty? assert !doc.css(".highlight-c").empty? end end html-pipeline-1.11.0/test/html/pipeline/markdown_filter_test.rb0000644000076400007640000000563112545202654023712 0ustar pravipravirequire "test_helper" MarkdownFilter = HTML::Pipeline::MarkdownFilter class HTML::Pipeline::MarkdownFilterTest < Minitest::Test def setup @haiku = "Pointing at the moon\n" + "Reminded of simple things\n" + "Moments matter most" @links = "See http://example.org/ for more info" @code = "```\n" + "def hello()" + " 'world'" + "end" + "```" end def test_fails_when_given_a_documentfragment body = "

    heyo

    " doc = HTML::Pipeline.parse(body) assert_raises(TypeError) { MarkdownFilter.call(doc, {}) } end def test_gfm_enabled_by_default doc = MarkdownFilter.to_document(@haiku, {}) assert doc.kind_of?(HTML::Pipeline::DocumentFragment) assert_equal 2, doc.search('br').size end def test_disabling_gfm doc = MarkdownFilter.to_document(@haiku, :gfm => false) assert doc.kind_of?(HTML::Pipeline::DocumentFragment) assert_equal 0, doc.search('br').size end def test_fenced_code_blocks doc = MarkdownFilter.to_document(@code) assert doc.kind_of?(HTML::Pipeline::DocumentFragment) assert_equal 1, doc.search('pre').size end def test_fenced_code_blocks_with_language doc = MarkdownFilter.to_document(@code.sub("```", "``` ruby")) assert doc.kind_of?(HTML::Pipeline::DocumentFragment) assert_equal 1, doc.search('pre').size assert_equal 'ruby', doc.search('pre').first['lang'] end end class GFMTest < Minitest::Test def gfm(text) MarkdownFilter.call(text, :gfm => true) end def test_not_touch_single_underscores_inside_words assert_equal "

    foo_bar

    ", gfm("foo_bar") end def test_not_touch_underscores_in_code_blocks assert_equal "
    foo_bar_baz\n
    ", gfm(" foo_bar_baz") end def test_not_touch_underscores_in_pre_blocks assert_equal "
    \nfoo_bar_baz\n
    ", gfm("
    \nfoo_bar_baz\n
    ") end def test_not_touch_two_or_more_underscores_inside_words assert_equal "

    foo_bar_baz

    ", gfm("foo_bar_baz") end def test_turn_newlines_into_br_tags_in_simple_cases assert_equal "

    foo
    \nbar

    ", gfm("foo\nbar") end def test_convert_newlines_in_all_groups assert_equal "

    apple
    \npear
    \norange

    \n\n" + "

    ruby
    \npython
    \nerlang

    ", gfm("apple\npear\norange\n\nruby\npython\nerlang") end def test_convert_newlines_in_even_long_groups assert_equal "

    apple
    \npear
    \norange
    \nbanana

    \n\n" + "

    ruby
    \npython
    \nerlang

    ", gfm("apple\npear\norange\nbanana\n\nruby\npython\nerlang") end def test_not_convert_newlines_in_lists assert_equal "

    foo

    \n\n

    bar

    ", gfm("# foo\n# bar") assert_equal "
      \n
    • foo
    • \n
    • bar
    • \n
    ", gfm("* foo\n* bar") end end html-pipeline-1.11.0/test/html/pipeline_test.rb0000644000076400007640000000444512545202654020525 0ustar pravipravirequire "test_helper" require "helpers/mocked_instrumentation_service" class HTML::PipelineTest < Minitest::Test Pipeline = HTML::Pipeline class TestFilter def self.call(input, context, result) input.reverse end end def setup @context = {} @result_class = Hash @pipeline = Pipeline.new [TestFilter], @context, @result_class end def test_filter_instrumentation service = MockedInstrumentationService.new events = service.subscribe "call_filter.html_pipeline" @pipeline.instrumentation_service = service filter(body = "hello") event, payload, res = events.pop assert event, "event expected" assert_equal "call_filter.html_pipeline", event assert_equal TestFilter.name, payload[:filter] assert_equal @pipeline.class.name, payload[:pipeline] assert_equal body.reverse, payload[:result][:output] end def test_pipeline_instrumentation service = MockedInstrumentationService.new events = service.subscribe "call_pipeline.html_pipeline" @pipeline.instrumentation_service = service filter(body = "hello") event, payload, res = events.pop assert event, "event expected" assert_equal "call_pipeline.html_pipeline", event assert_equal @pipeline.filters.map(&:name), payload[:filters] assert_equal @pipeline.class.name, payload[:pipeline] assert_equal body.reverse, payload[:result][:output] end def test_default_instrumentation_service service = 'default' Pipeline.default_instrumentation_service = service pipeline = Pipeline.new [], @context, @result_class assert_equal service, pipeline.instrumentation_service ensure Pipeline.default_instrumentation_service = nil end def test_setup_instrumentation assert_nil @pipeline.instrumentation_service service = MockedInstrumentationService.new events = service.subscribe "call_pipeline.html_pipeline" @pipeline.setup_instrumentation name = 'foo', service assert_equal service, @pipeline.instrumentation_service assert_equal name, @pipeline.instrumentation_name filter(body = 'foo') event, payload, res = events.pop assert event, "expected event" assert_equal name, payload[:pipeline] assert_equal body.reverse, payload[:result][:output] end def filter(input) @pipeline.call(input) end end html-pipeline-1.11.0/test/helpers/0000755000076400007640000000000012545202654016023 5ustar pravipravihtml-pipeline-1.11.0/test/helpers/mocked_instrumentation_service.rb0000644000076400007640000000056212545202654024660 0ustar pravipraviclass MockedInstrumentationService attr_reader :events def initialize(event = nil, events = []) @events = events subscribe event end def instrument(event, payload = nil) payload ||= {} res = yield payload events << [event, payload, res] if @subscribe == event res end def subscribe(event) @subscribe = event @events end end html-pipeline-1.11.0/CHANGELOG.md0000644000076400007640000000763412545202654015225 0ustar pravipravi# CHANGELOG ## 1.11.0 * Search for text nodes on DocumentFragments without root tags #146 Razer6 * Don't filter @mentions in