tarantula-rails3 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +49 -0
 - data/LICENSE +20 -0
 - data/README.rdoc +161 -0
 - data/Rakefile +83 -0
 - data/VERSION.yml +4 -0
 - data/examples/example_helper.rb +57 -0
 - data/examples/relevance/core_extensions/ellipsize_example.rb +19 -0
 - data/examples/relevance/core_extensions/file_example.rb +8 -0
 - data/examples/relevance/core_extensions/response_example.rb +29 -0
 - data/examples/relevance/core_extensions/test_case_example.rb +20 -0
 - data/examples/relevance/tarantula/attack_handler_example.rb +29 -0
 - data/examples/relevance/tarantula/basic_attack_example.rb +12 -0
 - data/examples/relevance/tarantula/crawler_example.rb +375 -0
 - data/examples/relevance/tarantula/form_example.rb +50 -0
 - data/examples/relevance/tarantula/form_submission_example.rb +171 -0
 - data/examples/relevance/tarantula/html_document_handler_example.rb +43 -0
 - data/examples/relevance/tarantula/html_report_helper_example.rb +46 -0
 - data/examples/relevance/tarantula/html_reporter_example.rb +82 -0
 - data/examples/relevance/tarantula/invalid_html_handler_example.rb +33 -0
 - data/examples/relevance/tarantula/io_reporter_example.rb +11 -0
 - data/examples/relevance/tarantula/link_example.rb +84 -0
 - data/examples/relevance/tarantula/log_grabber_example.rb +26 -0
 - data/examples/relevance/tarantula/rails_integration_proxy_example.rb +88 -0
 - data/examples/relevance/tarantula/result_example.rb +85 -0
 - data/examples/relevance/tarantula/tidy_handler_example.rb +58 -0
 - data/examples/relevance/tarantula/transform_example.rb +20 -0
 - data/examples/relevance/tarantula_example.rb +23 -0
 - data/laf/images/header_bg.jpg +0 -0
 - data/laf/images/logo.png +0 -0
 - data/laf/images/tagline.png +0 -0
 - data/laf/javascripts/jquery-1.2.3.js +3408 -0
 - data/laf/javascripts/jquery-ui-tabs.js +890 -0
 - data/laf/javascripts/jquery.tablesorter.js +861 -0
 - data/laf/javascripts/tarantula.js +10 -0
 - data/laf/stylesheets/tarantula.css +346 -0
 - data/lib/relevance/core_extensions/ellipsize.rb +34 -0
 - data/lib/relevance/core_extensions/file.rb +9 -0
 - data/lib/relevance/core_extensions/metaclass.rb +78 -0
 - data/lib/relevance/core_extensions/response.rb +9 -0
 - data/lib/relevance/core_extensions/string_chars_fix.rb +11 -0
 - data/lib/relevance/core_extensions/test_case.rb +19 -0
 - data/lib/relevance/tarantula.rb +58 -0
 - data/lib/relevance/tarantula/attack.rb +18 -0
 - data/lib/relevance/tarantula/attack_handler.rb +37 -0
 - data/lib/relevance/tarantula/basic_attack.rb +40 -0
 - data/lib/relevance/tarantula/crawler.rb +254 -0
 - data/lib/relevance/tarantula/detail.html.erb +81 -0
 - data/lib/relevance/tarantula/form.rb +23 -0
 - data/lib/relevance/tarantula/form_submission.rb +88 -0
 - data/lib/relevance/tarantula/html_document_handler.rb +36 -0
 - data/lib/relevance/tarantula/html_report_helper.rb +39 -0
 - data/lib/relevance/tarantula/html_reporter.rb +105 -0
 - data/lib/relevance/tarantula/index.html.erb +37 -0
 - data/lib/relevance/tarantula/invalid_html_handler.rb +18 -0
 - data/lib/relevance/tarantula/io_reporter.rb +34 -0
 - data/lib/relevance/tarantula/link.rb +94 -0
 - data/lib/relevance/tarantula/log_grabber.rb +16 -0
 - data/lib/relevance/tarantula/rails_integration_proxy.rb +68 -0
 - data/lib/relevance/tarantula/recording.rb +12 -0
 - data/lib/relevance/tarantula/response.rb +13 -0
 - data/lib/relevance/tarantula/result.rb +77 -0
 - data/lib/relevance/tarantula/test_report.html.erb +32 -0
 - data/lib/relevance/tarantula/tidy_handler.rb +32 -0
 - data/lib/relevance/tarantula/transform.rb +17 -0
 - data/lib/relevance/tasks/tarantula_tasks.rake +42 -0
 - data/lib/tarantula-rails3.rb +9 -0
 - data/template/tarantula_test.rb +22 -0
 - metadata +164 -0
 
| 
         @@ -0,0 +1,94 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Relevance::Tarantula::Link
         
     | 
| 
      
 2 
     | 
    
         
            +
              include Relevance::Tarantula
         
     | 
| 
      
 3 
     | 
    
         
            +
              
         
     | 
| 
      
 4 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 5 
     | 
    
         
            +
                include ActionView::Helpers::UrlHelper
         
     | 
| 
      
 6 
     | 
    
         
            +
                # method_javascript_function needs this method
         
     | 
| 
      
 7 
     | 
    
         
            +
                def protect_against_forgery?
         
     | 
| 
      
 8 
     | 
    
         
            +
                  false
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
                #fast fix for rails3 
         
     | 
| 
      
 11 
     | 
    
         
            +
            	def method_javascript_function(method, url = '', href = nil)
         
     | 
| 
      
 12 
     | 
    
         
            +
            	  action = (href && url.size > 0) ? "'#{url}'" : 'this.href'
         
     | 
| 
      
 13 
     | 
    
         
            +
            	  submit_function =
         
     | 
| 
      
 14 
     | 
    
         
            +
            		"var f = document.createElement('form'); f.style.display = 'none'; " +
         
     | 
| 
      
 15 
     | 
    
         
            +
            		"this.parentNode.appendChild(f); f.method = 'POST'; f.action = #{action};"
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            	  unless method == :post
         
     | 
| 
      
 18 
     | 
    
         
            +
            		submit_function << "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); "
         
     | 
| 
      
 19 
     | 
    
         
            +
            		submit_function << "m.setAttribute('name', '_method'); m.setAttribute('value', '#{method}'); f.appendChild(m);"
         
     | 
| 
      
 20 
     | 
    
         
            +
            	  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            	  if protect_against_forgery?
         
     | 
| 
      
 23 
     | 
    
         
            +
            		submit_function << "var s = document.createElement('input'); s.setAttribute('type', 'hidden'); "
         
     | 
| 
      
 24 
     | 
    
         
            +
            		submit_function << "s.setAttribute('name', '#{request_forgery_protection_token}'); s.setAttribute('value', '#{escape_javascript form_authenticity_token}'); f.appendChild(s);"
         
     | 
| 
      
 25 
     | 
    
         
            +
            	  end
         
     | 
| 
      
 26 
     | 
    
         
            +
            	  submit_function << "f.submit();"
         
     | 
| 
      
 27 
     | 
    
         
            +
            	end    
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
              
         
     | 
| 
      
 30 
     | 
    
         
            +
              METHOD_REGEXPS = {}
         
     | 
| 
      
 31 
     | 
    
         
            +
              [:put, :delete, :post].each do |m|
         
     | 
| 
      
 32 
     | 
    
         
            +
                # remove submit from the end so we'll match with or without forgery protection
         
     | 
| 
      
 33 
     | 
    
         
            +
                s = method_javascript_function(m).gsub( /f.submit();/, "" )
         
     | 
| 
      
 34 
     | 
    
         
            +
                # don't just match this.href in case a different url was passed originally
         
     | 
| 
      
 35 
     | 
    
         
            +
                s = Regexp.escape(s).gsub( /this.href/, ".*" )
         
     | 
| 
      
 36 
     | 
    
         
            +
                METHOD_REGEXPS[m] = /#{s}/
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
              
         
     | 
| 
      
 39 
     | 
    
         
            +
              attr_accessor :href, :crawler, :referrer
         
     | 
| 
      
 40 
     | 
    
         
            +
              
         
     | 
| 
      
 41 
     | 
    
         
            +
              def initialize(link, crawler, referrer)
         
     | 
| 
      
 42 
     | 
    
         
            +
                @crawler, @referrer = crawler, referrer
         
     | 
| 
      
 43 
     | 
    
         
            +
                
         
     | 
| 
      
 44 
     | 
    
         
            +
                if String === link || link.nil?
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @href = transform_url(link)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @method = :get
         
     | 
| 
      
 47 
     | 
    
         
            +
                else # should be a tag
         
     | 
| 
      
 48 
     | 
    
         
            +
                  @href = link['href'] ? transform_url(link['href'].downcase) : nil
         
     | 
| 
      
 49 
     | 
    
         
            +
                  @tag = link
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
              
         
     | 
| 
      
 53 
     | 
    
         
            +
              def crawl
         
     | 
| 
      
 54 
     | 
    
         
            +
                response = crawler.follow(method, href)
         
     | 
| 
      
 55 
     | 
    
         
            +
                log "Response #{response.code} for #{self}"
         
     | 
| 
      
 56 
     | 
    
         
            +
                crawler.handle_link_results(self, make_result(response))
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
              
         
     | 
| 
      
 59 
     | 
    
         
            +
              def make_result(response)
         
     | 
| 
      
 60 
     | 
    
         
            +
                crawler.make_result(:method    => method,
         
     | 
| 
      
 61 
     | 
    
         
            +
                                    :url       => href,
         
     | 
| 
      
 62 
     | 
    
         
            +
                                    :response  => response,
         
     | 
| 
      
 63 
     | 
    
         
            +
                                    :referrer  => referrer)
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
              
         
     | 
| 
      
 66 
     | 
    
         
            +
              def method
         
     | 
| 
      
 67 
     | 
    
         
            +
                @method ||= begin
         
     | 
| 
      
 68 
     | 
    
         
            +
                  (@tag &&
         
     | 
| 
      
 69 
     | 
    
         
            +
                   [:put, :delete, :post].detect do |m| # post should be last since it's least specific
         
     | 
| 
      
 70 
     | 
    
         
            +
                     @tag['onclick'] =~ METHOD_REGEXPS[m]
         
     | 
| 
      
 71 
     | 
    
         
            +
                   end) ||
         
     | 
| 
      
 72 
     | 
    
         
            +
                  :get
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
              
         
     | 
| 
      
 76 
     | 
    
         
            +
              def transform_url(link)
         
     | 
| 
      
 77 
     | 
    
         
            +
                crawler.transform_url(link)
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
              
         
     | 
| 
      
 80 
     | 
    
         
            +
              def ==(obj)
         
     | 
| 
      
 81 
     | 
    
         
            +
                obj.respond_to?(:href) && obj.respond_to?(:method) &&
         
     | 
| 
      
 82 
     | 
    
         
            +
                  self.href.to_s == obj.href.to_s && self.method.to_s == obj.method.to_s
         
     | 
| 
      
 83 
     | 
    
         
            +
              end
         
     | 
| 
      
 84 
     | 
    
         
            +
              alias :eql? :==
         
     | 
| 
      
 85 
     | 
    
         
            +
              
         
     | 
| 
      
 86 
     | 
    
         
            +
              def hash
         
     | 
| 
      
 87 
     | 
    
         
            +
                to_s.hash
         
     | 
| 
      
 88 
     | 
    
         
            +
              end
         
     | 
| 
      
 89 
     | 
    
         
            +
              
         
     | 
| 
      
 90 
     | 
    
         
            +
              def to_s
         
     | 
| 
      
 91 
     | 
    
         
            +
                "<Relevance::Tarantula::Link href=#{href}, method=#{method}>"
         
     | 
| 
      
 92 
     | 
    
         
            +
              end
         
     | 
| 
      
 93 
     | 
    
         
            +
              
         
     | 
| 
      
 94 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,68 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'test/unit'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Relevance::Tarantula::RailsIntegrationProxy
         
     | 
| 
      
 4 
     | 
    
         
            +
              include Relevance::Tarantula
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend Relevance::Tarantula
         
     | 
| 
      
 6 
     | 
    
         
            +
              extend Forwardable
         
     | 
| 
      
 7 
     | 
    
         
            +
              attr_accessor :integration_test
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def self.rails_integration_test(integration_test, options = {})
         
     | 
| 
      
 10 
     | 
    
         
            +
                t = Crawler.new
         
     | 
| 
      
 11 
     | 
    
         
            +
                t.max_url_length = options[:max_url_length] if options[:max_url_length] 
         
     | 
| 
      
 12 
     | 
    
         
            +
                t.proxy = RailsIntegrationProxy.new(integration_test)    
         
     | 
| 
      
 13 
     | 
    
         
            +
                t.handlers << HtmlDocumentHandler.new(t)
         
     | 
| 
      
 14 
     | 
    
         
            +
                t.handlers << InvalidHtmlHandler.new
         
     | 
| 
      
 15 
     | 
    
         
            +
                t.log_grabber = Relevance::Tarantula::LogGrabber.new(File.join(rails_root, "log/test.log"))
         
     | 
| 
      
 16 
     | 
    
         
            +
                t.skip_uri_patterns << /logout$/
         
     | 
| 
      
 17 
     | 
    
         
            +
                t.transform_url_patterns += [
         
     | 
| 
      
 18 
     | 
    
         
            +
                  [/\?\d+$/, ''],                               # strip trailing numbers for assets
         
     | 
| 
      
 19 
     | 
    
         
            +
                  [/^http:\/\/#{integration_test.host}/, '']    # strip full path down to relative
         
     | 
| 
      
 20 
     | 
    
         
            +
                ]
         
     | 
| 
      
 21 
     | 
    
         
            +
                t.test_name = t.proxy.integration_test.method_name
         
     | 
| 
      
 22 
     | 
    
         
            +
                t.reporters << Relevance::Tarantula::HtmlReporter.new(t.report_dir)
         
     | 
| 
      
 23 
     | 
    
         
            +
                t
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              def initialize(integration_test)
         
     | 
| 
      
 27 
     | 
    
         
            +
                @integration_test = integration_test
         
     | 
| 
      
 28 
     | 
    
         
            +
                @integration_test.meta.attr_accessor :response
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
              
         
     | 
| 
      
 31 
     | 
    
         
            +
              [:get, :post, :put, :delete].each do |verb|
         
     | 
| 
      
 32 
     | 
    
         
            +
                define_method(verb) do |url, *args|
         
     | 
| 
      
 33 
     | 
    
         
            +
                  integration_test.send(verb, url, *args)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  response = integration_test.response
         
     | 
| 
      
 35 
     | 
    
         
            +
                  patch_response(url, response)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  response
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              def patch_response(url, response)
         
     | 
| 
      
 41 
     | 
    
         
            +
                if response.code == '404'
         
     | 
| 
      
 42 
     | 
    
         
            +
                  if File.exist?(static_content_path(url))
         
     | 
| 
      
 43 
     | 
    
         
            +
                    case ext = File.extension(url)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    when /html|te?xt|css|js|jpe?g|gif|psd|png|eps|pdf|ico/
         
     | 
| 
      
 45 
     | 
    
         
            +
                      response.body = static_content_file(url)
         
     | 
| 
      
 46 
     | 
    
         
            +
                      response.headers["type"] = "text/#{ext}"  # readable as response.content_type
         
     | 
| 
      
 47 
     | 
    
         
            +
                      response.meta.attr_accessor :code
         
     | 
| 
      
 48 
     | 
    
         
            +
                      response.code = "200"
         
     | 
| 
      
 49 
     | 
    
         
            +
                    else
         
     | 
| 
      
 50 
     | 
    
         
            +
                      log "Skipping unknown type #{url}"
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
                # don't count on metaclass taking block, e.g.
         
     | 
| 
      
 55 
     | 
    
         
            +
                # http://relevancellc.com/2008/2/12/how-should-metaclass-work
         
     | 
| 
      
 56 
     | 
    
         
            +
                response.metaclass.class_eval do
         
     | 
| 
      
 57 
     | 
    
         
            +
                  include Relevance::CoreExtensions::Response
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
              
         
     | 
| 
      
 61 
     | 
    
         
            +
              def static_content_file(url)
         
     | 
| 
      
 62 
     | 
    
         
            +
                File.read(static_content_path(url))
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
              
         
     | 
| 
      
 65 
     | 
    
         
            +
              def static_content_path(url)
         
     | 
| 
      
 66 
     | 
    
         
            +
                File.expand_path(File.join(rails_root, "public", url))
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Used to create a stub response when we didn't get back a real response
         
     | 
| 
      
 2 
     | 
    
         
            +
            class Relevance::Tarantula::Response
         
     | 
| 
      
 3 
     | 
    
         
            +
              HASHABLE_ATTRS = [:code, :body, :content_type]
         
     | 
| 
      
 4 
     | 
    
         
            +
              attr_accessor *HASHABLE_ATTRS
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def initialize(hash)
         
     | 
| 
      
 7 
     | 
    
         
            +
                hash.each do |k,v|
         
     | 
| 
      
 8 
     | 
    
         
            +
                  raise ArgumentError, k unless HASHABLE_ATTRS.member?(k)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  self.instance_variable_set("@#{k}", v)
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
              
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,77 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Relevance::Tarantula::Result
         
     | 
| 
      
 2 
     | 
    
         
            +
              HASHABLE_ATTRS = [:success, :method, :url, :response, :referrer, :data, :description, :log, :test_name]
         
     | 
| 
      
 3 
     | 
    
         
            +
              DEFAULT_LOCALHOST = "http://localhost:3000"
         
     | 
| 
      
 4 
     | 
    
         
            +
              attr_accessor *HASHABLE_ATTRS
         
     | 
| 
      
 5 
     | 
    
         
            +
              include Relevance::Tarantula
         
     | 
| 
      
 6 
     | 
    
         
            +
              include Relevance::Tarantula::HtmlReportHelper
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(hash)
         
     | 
| 
      
 9 
     | 
    
         
            +
                hash.each do |k,v|
         
     | 
| 
      
 10 
     | 
    
         
            +
                  raise ArgumentError, k unless HASHABLE_ATTRS.member?(k)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  self.instance_variable_set("@#{k}", v)
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              def short_description
         
     | 
| 
      
 16 
     | 
    
         
            +
                [method,url].join(" ")
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              def sequence_number
         
     | 
| 
      
 20 
     | 
    
         
            +
                @sequence_number ||= (self.class.next_number += 1)
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              def file_name
         
     | 
| 
      
 24 
     | 
    
         
            +
                "#{sequence_number}.html"
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              def code
         
     | 
| 
      
 28 
     | 
    
         
            +
                response && response.code
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def body
         
     | 
| 
      
 32 
     | 
    
         
            +
                response && response.body
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              def full_url
         
     | 
| 
      
 36 
     | 
    
         
            +
                "#{DEFAULT_LOCALHOST}#{url}"
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              ALLOW_NNN_FOR = /^allow_(\d\d\d)_for$/
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 42 
     | 
    
         
            +
                attr_accessor :next_number
         
     | 
| 
      
 43 
     | 
    
         
            +
                
         
     | 
| 
      
 44 
     | 
    
         
            +
                def handle(result)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  retval = result.dup
         
     | 
| 
      
 46 
     | 
    
         
            +
                  retval.success = successful?(result.response) || can_skip_error?(result)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  retval.description = "Bad HTTP Response" unless retval.success
         
     | 
| 
      
 48 
     | 
    
         
            +
                  retval
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                def success_codes 
         
     | 
| 
      
 52 
     | 
    
         
            +
                  %w{200 201 302 401}
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
                
         
     | 
| 
      
 55 
     | 
    
         
            +
                # allow_errors_for is a hash 
         
     | 
| 
      
 56 
     | 
    
         
            +
                #  k=error code,
         
     | 
| 
      
 57 
     | 
    
         
            +
                #  v=array of matchers for urls that can skip said error
         
     | 
| 
      
 58 
     | 
    
         
            +
                attr_accessor :allow_errors_for
         
     | 
| 
      
 59 
     | 
    
         
            +
                def can_skip_error?(result)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  coll = allow_errors_for[result.code]
         
     | 
| 
      
 61 
     | 
    
         
            +
                  return false unless coll
         
     | 
| 
      
 62 
     | 
    
         
            +
                  coll.any? {|item| item === result.url}
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                def successful?(response)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  success_codes.member?(response.code)
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                def method_missing(meth, *args)  
         
     | 
| 
      
 70 
     | 
    
         
            +
                  super unless ALLOW_NNN_FOR =~ meth.to_s
         
     | 
| 
      
 71 
     | 
    
         
            +
                  (allow_errors_for[$1] ||= []).push(*args)
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
              self.allow_errors_for = {}
         
     | 
| 
      
 76 
     | 
    
         
            +
              self.next_number = 0
         
     | 
| 
      
 77 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <div id="<%= test_name %>">
         
     | 
| 
      
 2 
     | 
    
         
            +
              <% %w{failures successes}.each do |result_type| %>
         
     | 
| 
      
 3 
     | 
    
         
            +
                <table class="list tablesorter" cellspacing="0">
         
     | 
| 
      
 4 
     | 
    
         
            +
                  <caption><%= send(result_type).size %> <%= result_type.capitalize %></caption>
         
     | 
| 
      
 5 
     | 
    
         
            +
                  <thead>
         
     | 
| 
      
 6 
     | 
    
         
            +
                    <tr>
         
     | 
| 
      
 7 
     | 
    
         
            +
                      <th class="sort asc"><span>URL</span><span class="sort"> </span></th>
         
     | 
| 
      
 8 
     | 
    
         
            +
                      <th><span>Action</span><span class="sort"> </span></th>
         
     | 
| 
      
 9 
     | 
    
         
            +
                      <th><span>Response</span><span class="sort"> </span></th>
         
     | 
| 
      
 10 
     | 
    
         
            +
                      <th class="left"><span>Description</span><span class="sort"> </span></th>
         
     | 
| 
      
 11 
     | 
    
         
            +
                      <th><span>Referrer</span><span class="sort"> </span></th>
         
     | 
| 
      
 12 
     | 
    
         
            +
                    </tr>
         
     | 
| 
      
 13 
     | 
    
         
            +
                  </thead>
         
     | 
| 
      
 14 
     | 
    
         
            +
                  <tfoot>
         
     | 
| 
      
 15 
     | 
    
         
            +
                     <tr><td colspan="5"> </td></tr>
         
     | 
| 
      
 16 
     | 
    
         
            +
                  </tfoot>
         
     | 
| 
      
 17 
     | 
    
         
            +
                  
         
     | 
| 
      
 18 
     | 
    
         
            +
                  <tbody>
         
     | 
| 
      
 19 
     | 
    
         
            +
                    <% send(result_type).sort{|x,y| y.code.to_s <=> x.code.to_s}.each_with_index do |result,i| %>
         
     | 
| 
      
 20 
     | 
    
         
            +
                      <tr class="<%= (i%2 == 0) ? 'even' : 'odd' %>">
         
     | 
| 
      
 21 
     | 
    
         
            +
                        <td class="left"><a href="<%= "#{test_name}/#{result.file_name}" %>"><%= result.url.ellipsize(50) %></a></td>
         
     | 
| 
      
 22 
     | 
    
         
            +
                        <td class="method"><%= result.method.to_s.upcase %></td> <!-- TODO Clean up demeter violation -->
         
     | 
| 
      
 23 
     | 
    
         
            +
                        <td><span class="<%= class_for_code(result.code) %>"><%= result.code %></span></td>
         
     | 
| 
      
 24 
     | 
    
         
            +
                        <td class="left"><%= result.description %></td>
         
     | 
| 
      
 25 
     | 
    
         
            +
                        <td class="left"><%= result.referrer.ellipsize(30) %></td>
         
     | 
| 
      
 26 
     | 
    
         
            +
                      </tr>
         
     | 
| 
      
 27 
     | 
    
         
            +
                    <% end %>
         
     | 
| 
      
 28 
     | 
    
         
            +
                  </tbody>
         
     | 
| 
      
 29 
     | 
    
         
            +
                </table>       
         
     | 
| 
      
 30 
     | 
    
         
            +
                <br/>
         
     | 
| 
      
 31 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 32 
     | 
    
         
            +
            </div>
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            begin
         
     | 
| 
      
 3 
     | 
    
         
            +
              gem 'tidy'
         
     | 
| 
      
 4 
     | 
    
         
            +
              require 'tidy'
         
     | 
| 
      
 5 
     | 
    
         
            +
            rescue Gem::LoadError
         
     | 
| 
      
 6 
     | 
    
         
            +
              puts "Tidy gem not available -- 'gem install tidy' to get it."
         
     | 
| 
      
 7 
     | 
    
         
            +
            end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            if defined? Tidy
         
     | 
| 
      
 10 
     | 
    
         
            +
              Tidy.path = ENV['TIDY_PATH'] if ENV['TIDY_PATH']
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              class Relevance::Tarantula::TidyHandler 
         
     | 
| 
      
 13 
     | 
    
         
            +
                include Relevance::Tarantula
         
     | 
| 
      
 14 
     | 
    
         
            +
                def initialize(options = {})
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @options = {:show_warnings=>true}.merge(options)
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
                def handle(result)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  response = result.response
         
     | 
| 
      
 19 
     | 
    
         
            +
                  return unless response.html?
         
     | 
| 
      
 20 
     | 
    
         
            +
                  tidy = Tidy.open(@options) do |tidy|
         
     | 
| 
      
 21 
     | 
    
         
            +
                    xml = tidy.clean(response.body)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    tidy
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  unless tidy.errors.blank?
         
     | 
| 
      
 25 
     | 
    
         
            +
                    error_result = result.dup
         
     | 
| 
      
 26 
     | 
    
         
            +
                    error_result.description = "Bad HTML (Tidy)"
         
     | 
| 
      
 27 
     | 
    
         
            +
                    error_result.data = tidy.errors.inspect
         
     | 
| 
      
 28 
     | 
    
         
            +
                    error_result
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Relevance::Tarantula::Transform 
         
     | 
| 
      
 2 
     | 
    
         
            +
              attr_accessor :from, :to
         
     | 
| 
      
 3 
     | 
    
         
            +
              def initialize(from, to)
         
     | 
| 
      
 4 
     | 
    
         
            +
                @from = from
         
     | 
| 
      
 5 
     | 
    
         
            +
                @to = to
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
              def [](string)
         
     | 
| 
      
 8 
     | 
    
         
            +
                case to
         
     | 
| 
      
 9 
     | 
    
         
            +
                when Proc
         
     | 
| 
      
 10 
     | 
    
         
            +
                  string.gsub(from, &to)
         
     | 
| 
      
 11 
     | 
    
         
            +
                else
         
     | 
| 
      
 12 
     | 
    
         
            +
                  string.gsub(from, to)
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rake'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            namespace :tarantula do
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              desc 'Run tarantula tests.'
         
     | 
| 
      
 6 
     | 
    
         
            +
              task :test do
         
     | 
| 
      
 7 
     | 
    
         
            +
                rm_rf "tmp/tarantula"
         
     | 
| 
      
 8 
     | 
    
         
            +
                Rake::TestTask.new(:tarantula_test) do |t|
         
     | 
| 
      
 9 
     | 
    
         
            +
                  t.libs << 'test'
         
     | 
| 
      
 10 
     | 
    
         
            +
                  t.pattern = 'test/tarantula/**/*_test.rb'
         
     | 
| 
      
 11 
     | 
    
         
            +
                  t.verbose = true
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
                
         
     | 
| 
      
 14 
     | 
    
         
            +
                Rake::Task[:tarantula_test].invoke
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              desc 'Run tarantula tests and open results in your browser.'
         
     | 
| 
      
 18 
     | 
    
         
            +
              task :report do
         
     | 
| 
      
 19 
     | 
    
         
            +
                begin
         
     | 
| 
      
 20 
     | 
    
         
            +
                  Rake::Task['tarantula:test'].invoke
         
     | 
| 
      
 21 
     | 
    
         
            +
                rescue RuntimeError => e
         
     | 
| 
      
 22 
     | 
    
         
            +
                  puts e.message
         
     | 
| 
      
 23 
     | 
    
         
            +
                end    
         
     | 
| 
      
 24 
     | 
    
         
            +
                
         
     | 
| 
      
 25 
     | 
    
         
            +
                Dir.glob("tmp/tarantula/**/index.html") do |file|
         
     | 
| 
      
 26 
     | 
    
         
            +
                  if PLATFORM['darwin']
         
     | 
| 
      
 27 
     | 
    
         
            +
                    system("open #{file}")
         
     | 
| 
      
 28 
     | 
    
         
            +
                  elsif PLATFORM[/linux/]
         
     | 
| 
      
 29 
     | 
    
         
            +
                    system("firefox #{file}")
         
     | 
| 
      
 30 
     | 
    
         
            +
                  else
         
     | 
| 
      
 31 
     | 
    
         
            +
                    puts "You can view tarantula results at #{file}"
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              desc 'Generate a default tarantula test'
         
     | 
| 
      
 37 
     | 
    
         
            +
              task :setup do
         
     | 
| 
      
 38 
     | 
    
         
            +
                mkdir_p "test/tarantula"
         
     | 
| 
      
 39 
     | 
    
         
            +
                template_path = File.expand_path(File.join(File.dirname(__FILE__), "../../..", "template", "tarantula_test.rb"))
         
     | 
| 
      
 40 
     | 
    
         
            +
                cp template_path, "test/tarantula/"
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "test_helper"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "relevance/tarantula"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class TarantulaTest < ActionController::IntegrationTest
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Load enough test data to ensure that there's a link to every page in your
         
     | 
| 
      
 6 
     | 
    
         
            +
              # application. Doing so allows Tarantula to follow those links and crawl 
         
     | 
| 
      
 7 
     | 
    
         
            +
              # every page.  For many applications, you can load a decent data set by
         
     | 
| 
      
 8 
     | 
    
         
            +
              # loading all fixtures.
         
     | 
| 
      
 9 
     | 
    
         
            +
              fixtures :all
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              def test_tarantula
         
     | 
| 
      
 12 
     | 
    
         
            +
                # If your application requires users to log in before accessing certain 
         
     | 
| 
      
 13 
     | 
    
         
            +
                # pages, uncomment the lines below and update them to allow this test to
         
     | 
| 
      
 14 
     | 
    
         
            +
                # log in to your application.  Doing so allows Tarantula to crawl the 
         
     | 
| 
      
 15 
     | 
    
         
            +
                # pages that are only accessible to logged-in users.
         
     | 
| 
      
 16 
     | 
    
         
            +
                # 
         
     | 
| 
      
 17 
     | 
    
         
            +
                #   post '/session', :login => 'quentin', :password => 'monkey'
         
     | 
| 
      
 18 
     | 
    
         
            +
                #   follow_redirect!
         
     | 
| 
      
 19 
     | 
    
         
            +
                
         
     | 
| 
      
 20 
     | 
    
         
            +
                tarantula_crawl(self)
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     |