tarantula 0.3.3 → 0.4.0

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.
Files changed (74) hide show
  1. data/.autotest +14 -0
  2. data/.gitignore +9 -0
  3. data/.rvmrc +1 -0
  4. data/DSL_EXAMPLES.md +120 -0
  5. data/Gemfile +2 -0
  6. data/Gemfile.lock +108 -0
  7. data/{MIT-LICENSE → LICENSE} +0 -0
  8. data/README.rdoc +3 -28
  9. data/Rakefile +27 -59
  10. data/lib/relevance/core_extensions/ellipsize.rb +23 -19
  11. data/lib/relevance/core_extensions/file.rb +10 -4
  12. data/lib/relevance/core_extensions/response.rb +9 -6
  13. data/lib/relevance/core_extensions/test_case.rb +14 -12
  14. data/lib/relevance/tarantula.rb +24 -25
  15. data/lib/relevance/tarantula/attack.rb +19 -15
  16. data/lib/relevance/tarantula/attack_handler.rb +32 -26
  17. data/lib/relevance/tarantula/basic_attack.rb +36 -32
  18. data/lib/relevance/tarantula/crawler.rb +222 -216
  19. data/lib/relevance/tarantula/form.rb +27 -21
  20. data/lib/relevance/tarantula/form_submission.rb +79 -73
  21. data/lib/relevance/tarantula/html_document_handler.rb +37 -31
  22. data/lib/relevance/tarantula/html_report_helper.rb +36 -29
  23. data/lib/relevance/tarantula/html_reporter.rb +105 -99
  24. data/lib/relevance/tarantula/invalid_html_handler.rb +21 -15
  25. data/lib/relevance/tarantula/io_reporter.rb +37 -31
  26. data/lib/relevance/tarantula/link.rb +97 -73
  27. data/lib/relevance/tarantula/log_grabber.rb +20 -14
  28. data/lib/relevance/tarantula/rails_integration_proxy.rb +64 -58
  29. data/lib/relevance/tarantula/response.rb +16 -10
  30. data/lib/relevance/tarantula/result.rb +69 -63
  31. data/lib/relevance/tarantula/tidy_handler.rb +22 -17
  32. data/lib/relevance/tarantula/transform.rb +18 -14
  33. data/lib/relevance/tarantula/version.rb +5 -0
  34. data/{tasks → lib/relevance/tasks}/tarantula_tasks.rake +1 -1
  35. data/lib/tarantula-rails3.rb +9 -0
  36. data/{examples/relevance/core_extensions/ellipsize_example.rb → spec/relevance/core_extensions/ellipsize_spec.rb} +2 -2
  37. data/{examples/relevance/core_extensions/file_example.rb → spec/relevance/core_extensions/file_spec.rb} +2 -2
  38. data/{examples/relevance/core_extensions/response_example.rb → spec/relevance/core_extensions/response_spec.rb} +2 -2
  39. data/{examples/relevance/core_extensions/test_case_example.rb → spec/relevance/core_extensions/test_case_spec.rb} +1 -1
  40. data/{examples/relevance/tarantula/attack_handler_example.rb → spec/relevance/tarantula/attack_handler_spec.rb} +1 -1
  41. data/{examples/relevance/tarantula/basic_attack_example.rb → spec/relevance/tarantula/basic_attack_spec.rb} +2 -2
  42. data/{examples/relevance/tarantula/crawler_example.rb → spec/relevance/tarantula/crawler_spec.rb} +2 -2
  43. data/{examples/relevance/tarantula/form_example.rb → spec/relevance/tarantula/form_spec.rb} +2 -2
  44. data/{examples/relevance/tarantula/form_submission_example.rb → spec/relevance/tarantula/form_submission_spec.rb} +3 -3
  45. data/{examples/relevance/tarantula/html_document_handler_example.rb → spec/relevance/tarantula/html_document_handler_spec.rb} +1 -1
  46. data/{examples/relevance/tarantula/html_report_helper_example.rb → spec/relevance/tarantula/html_report_helper_spec.rb} +1 -1
  47. data/{examples/relevance/tarantula/html_reporter_example.rb → spec/relevance/tarantula/html_reporter_spec.rb} +1 -1
  48. data/{examples/relevance/tarantula/invalid_html_handler_example.rb → spec/relevance/tarantula/invalid_html_handler_spec.rb} +1 -1
  49. data/{examples/relevance/tarantula/io_reporter_example.rb → spec/relevance/tarantula/io_reporter_spec.rb} +1 -1
  50. data/{examples/relevance/tarantula/link_example.rb → spec/relevance/tarantula/link_spec.rb} +5 -5
  51. data/{examples/relevance/tarantula/log_grabber_example.rb → spec/relevance/tarantula/log_grabber_spec.rb} +1 -1
  52. data/{examples/relevance/tarantula/rails_integration_proxy_example.rb → spec/relevance/tarantula/rails_integration_proxy_spec.rb} +1 -1
  53. data/{examples/relevance/tarantula/result_example.rb → spec/relevance/tarantula/result_spec.rb} +1 -1
  54. data/{examples/relevance/tarantula/tidy_handler_example.rb → spec/relevance/tarantula/tidy_handler_spec.rb} +1 -1
  55. data/{examples/relevance/tarantula/transform_example.rb → spec/relevance/tarantula/transform_spec.rb} +2 -2
  56. data/{examples/relevance/tarantula_example.rb → spec/relevance/tarantula_spec.rb} +1 -1
  57. data/{examples/example_helper.rb → spec/spec_helper.rb} +6 -14
  58. data/tarantula.gemspec +31 -0
  59. data/template/tarantula_test.rb +1 -1
  60. data/vendor/xss-shield/MIT-LICENSE +20 -0
  61. data/vendor/xss-shield/README +76 -0
  62. data/vendor/xss-shield/init.rb +16 -0
  63. data/vendor/xss-shield/lib/xss_shield.rb +6 -0
  64. data/vendor/xss-shield/lib/xss_shield/erb_hacks.rb +111 -0
  65. data/vendor/xss-shield/lib/xss_shield/haml_hacks.rb +42 -0
  66. data/vendor/xss-shield/lib/xss_shield/safe_string.rb +47 -0
  67. data/vendor/xss-shield/lib/xss_shield/secure_helpers.rb +40 -0
  68. data/vendor/xss-shield/test/test_actionview_integration.rb +40 -0
  69. data/vendor/xss-shield/test/test_erb.rb +44 -0
  70. data/vendor/xss-shield/test/test_haml.rb +43 -0
  71. data/vendor/xss-shield/test/test_helpers.rb +25 -0
  72. data/vendor/xss-shield/test/test_safe_string.rb +55 -0
  73. metadata +170 -99
  74. data/VERSION.yml +0 -4
@@ -1,23 +1,29 @@
1
- class Relevance::Tarantula::Form
2
- extend Forwardable
3
- def_delegators("@tag", :search)
4
-
5
- attr_accessor :crawler, :referrer
6
-
7
- def initialize(tag, crawler, referrer)
8
- @tag, @crawler, @referrer = tag, crawler, referrer
9
- end
10
-
11
- def action
12
- @tag['action'].downcase
13
- end
14
-
15
- def method
16
- (rails_method_hack or @tag['method'] or 'get').downcase
17
- end
18
-
19
- def rails_method_hack
20
- (tag = @tag.at('input[@name="_method"]')) && tag["value"]
21
- end
1
+ module Relevance
2
+ module Tarantula
3
+
4
+ class Form
5
+ extend Forwardable
6
+ def_delegators("@tag", :search)
7
+
8
+ attr_accessor :crawler, :referrer
22
9
 
10
+ def initialize(tag, crawler, referrer)
11
+ @tag, @crawler, @referrer = tag, crawler, referrer
12
+ end
13
+
14
+ def action
15
+ @tag['action'].downcase
16
+ end
17
+
18
+ def method
19
+ (rails_method_hack or @tag['method'] or 'get').downcase
20
+ end
21
+
22
+ def rails_method_hack
23
+ (tag = @tag.at('input[@name="_method"]')) && tag["value"]
24
+ end
25
+
26
+ end
27
+
28
+ end
23
29
  end
@@ -1,88 +1,94 @@
1
- class Relevance::Tarantula::FormSubmission
2
- include Relevance::Tarantula
3
- attr_accessor :method, :action, :data, :attack, :form
1
+ module Relevance
2
+ module Tarantula
4
3
 
5
- class << self
6
- def attacks
7
- # normalize from hash input to Attack
8
- @attacks = @attacks.map do |val|
9
- Hash === val ? Relevance::Tarantula::Attack.new(val) : val
4
+ class FormSubmission
5
+ include Relevance::Tarantula
6
+ attr_accessor :method, :action, :data, :attack, :form
7
+
8
+ class << self
9
+ def attacks
10
+ # normalize from hash input to Attack
11
+ @attacks = @attacks.map do |val|
12
+ Hash === val ? Relevance::Tarantula::Attack.new(val) : val
13
+ end
14
+ @attacks
15
+ end
16
+ def attacks=(atts)
17
+ # normalize from hash input to Attack
18
+ @attacks = atts.map do |val|
19
+ Hash === val ? Relevance::Tarantula::Attack.new(val) : val
20
+ end
21
+ end
10
22
  end
11
- @attacks
12
- end
13
- def attacks=(atts)
14
- # normalize from hash input to Attack
15
- @attacks = atts.map do |val|
16
- Hash === val ? Relevance::Tarantula::Attack.new(val) : val
23
+ @attacks = [Relevance::Tarantula::BasicAttack.new]
24
+
25
+ def initialize(form, attack = Relevance::Tarantula::BasicAttack.new)
26
+ @form = form
27
+ @method = form.method
28
+ @action = form.action
29
+ @attack = attack
30
+ @data = mutate_selects(form).merge(mutate_text_areas(form)).merge(mutate_inputs(form))
17
31
  end
18
- end
19
- end
20
- @attacks = [Relevance::Tarantula::BasicAttack.new]
21
32
 
22
- def initialize(form, attack = Relevance::Tarantula::BasicAttack.new)
23
- @form = form
24
- @method = form.method
25
- @action = form.action
26
- @attack = attack
27
- @data = mutate_selects(form).merge(mutate_text_areas(form)).merge(mutate_inputs(form))
28
- end
29
-
30
- def crawl
31
- begin
32
- response = form.crawler.submit(method, action, data)
33
- log "Response #{response.code} for #{self}"
34
- rescue ActiveRecord::RecordNotFound => e
35
- log "Skipping #{action}, presumed ok that record is missing"
36
- response = Relevance::Tarantula::Response.new(:code => "404", :body => e.message, :content_type => "text/plain")
37
- end
38
- form.crawler.handle_form_results(self, response)
39
- response
40
- end
33
+ def crawl
34
+ begin
35
+ response = form.crawler.submit(method, action, data)
36
+ log "Response #{response.code} for #{self}"
37
+ rescue ActiveRecord::RecordNotFound => e
38
+ log "Skipping #{action}, presumed ok that record is missing"
39
+ response = Relevance::Tarantula::Response.new(:code => "404", :body => e.message, :content_type => "text/plain")
40
+ end
41
+ form.crawler.handle_form_results(self, response)
42
+ response
43
+ end
41
44
 
42
- def self.mutate(form)
43
- attacks.map{|attack| new(form, attack)} if attacks
44
- end
45
+ def self.mutate(form)
46
+ attacks.map{|attack| new(form, attack)} if attacks
47
+ end
45
48
 
46
- def to_s
47
- "#{action} #{method} #{data.inspect} #{attack.inspect}"
48
- end
49
+ def to_s
50
+ "#{action} #{method} #{data.inspect} #{attack.inspect}"
51
+ end
49
52
 
50
- # a form's signature is what makes it unique (e.g. action + fields)
51
- # used to keep track of which forms we have submitted already
52
- def signature
53
- [action, data.keys.sort, attack.name]
54
- end
53
+ # a form's signature is what makes it unique (e.g. action + fields)
54
+ # used to keep track of which forms we have submitted already
55
+ def signature
56
+ [action, data.keys.sort, attack.name]
57
+ end
55
58
 
56
- def create_random_data_for(form, tag_selector)
57
- form.search(tag_selector).inject({}) do |form_args, input|
58
- # TODO: test
59
- form_args[input['name']] = random_data(input) if input['name']
60
- form_args
61
- end
62
- end
59
+ def create_random_data_for(form, tag_selector)
60
+ form.search(tag_selector).inject({}) do |form_args, input|
61
+ # TODO: test
62
+ form_args[input['name']] = random_data(input) if input['name']
63
+ form_args
64
+ end
65
+ end
63
66
 
64
- def mutate_inputs(form)
65
- create_random_data_for(form, 'input')
66
- end
67
+ def mutate_inputs(form)
68
+ create_random_data_for(form, 'input')
69
+ end
67
70
 
68
- def mutate_text_areas(form)
69
- create_random_data_for(form, 'textarea')
70
- end
71
+ def mutate_text_areas(form)
72
+ create_random_data_for(form, 'textarea')
73
+ end
71
74
 
72
- def mutate_selects(form)
73
- form.search('select').inject({}) do |form_args, select|
74
- options = select.search('option')
75
- option = options.rand
76
- form_args[select['name']] = option['value']
77
- form_args
78
- end
79
- end
75
+ def mutate_selects(form)
76
+ form.search('select').inject({}) do |form_args, select|
77
+ options = select.search('option')
78
+ option = options.sample
79
+ form_args[select['name']] = option['value']
80
+ form_args
81
+ end
82
+ end
80
83
 
81
- def random_data(input)
82
- case input['name']
83
- when /^_method$/ then input['value']
84
- else
85
- attack.input(input)
84
+ def random_data(input)
85
+ case input['name']
86
+ when /^_method$/ then input['value']
87
+ else
88
+ attack.input(input)
89
+ end
90
+ end
86
91
  end
92
+
87
93
  end
88
94
  end
@@ -1,36 +1,42 @@
1
1
  require 'hpricot'
2
2
 
3
- class Relevance::Tarantula::HtmlDocumentHandler
4
- extend Forwardable
5
- def_delegators("@crawler", :queue_link, :queue_form)
6
-
7
- def initialize(crawler)
8
- @crawler = crawler
9
- end
10
- # HTML::Document shouts to stderr when it sees ugly HTML
11
- # We don't want this -- the InvalidHtmlHandler will deal with it
12
- def html_doc_without_stderr_noise(html)
13
- body = nil
14
- Recording.stderr do
15
- body = Hpricot html
16
- end
17
- body
18
- end
19
- def handle(result)
20
- response = result.response
21
- url = result.url
22
- return unless response.html?
23
- body = html_doc_without_stderr_noise(response.body)
24
- body.search('a').each do |tag|
25
- queue_link(tag, url)
26
- end
27
- body.search('link').each do |tag|
28
- queue_link(tag, url)
29
- end
30
- body.search('form').each do |form|
31
- form['action'] = url unless form['action']
32
- queue_form(form, url)
3
+ module Relevance
4
+ module Tarantula
5
+
6
+ class HtmlDocumentHandler
7
+ extend Forwardable
8
+ def_delegators("@crawler", :queue_link, :queue_form)
9
+
10
+ def initialize(crawler)
11
+ @crawler = crawler
12
+ end
13
+ # HTML::Document shouts to stderr when it sees ugly HTML
14
+ # We don't want this -- the InvalidHtmlHandler will deal with it
15
+ def html_doc_without_stderr_noise(html)
16
+ body = nil
17
+ Recording.stderr do
18
+ body = Hpricot html
19
+ end
20
+ body
21
+ end
22
+ def handle(result)
23
+ response = result.response
24
+ url = result.url
25
+ return unless response.html?
26
+ body = html_doc_without_stderr_noise(response.body)
27
+ body.search('a').each do |tag|
28
+ queue_link(tag, url)
29
+ end
30
+ body.search('link').each do |tag|
31
+ queue_link(tag, url)
32
+ end
33
+ body.search('form').each do |form|
34
+ form['action'] = url unless form['action']
35
+ queue_form(form, url)
36
+ end
37
+ nil
38
+ end
33
39
  end
34
- nil
40
+
35
41
  end
36
42
  end
@@ -1,39 +1,46 @@
1
1
  require "erb"
2
- module Relevance::Tarantula::HtmlReportHelper
3
- include ERB::Util
4
- include Relevance::Tarantula
5
- def wrap_in_line_number_table_row(text, &blk)
6
- x = Builder::XmlMarkup.new
2
+ require "active_support/builder" unless defined?(Builder)
3
+ module Relevance
4
+ module Tarantula
7
5
 
8
- x.tr do
9
- lines = text.split("\n")
10
- x.td(:class => "numbers") do
11
- lines.size.times do |index|
12
- x.span(index+1, :class => "line number")
6
+ module HtmlReportHelper
7
+ include ERB::Util
8
+ include Relevance::Tarantula
9
+ def wrap_in_line_number_table_row(text, &blk)
10
+ x = Builder::XmlMarkup.new
11
+
12
+ x.tr do
13
+ lines = text.split("\n")
14
+ x.td(:class => "numbers") do
15
+ lines.size.times do |index|
16
+ x.span(index+1, :class => "line number")
17
+ end
18
+ end
19
+ x.td(:class => "lines") do
20
+ lines.each do |line|
21
+ x.span(line, :class => "line")
22
+ end
23
+ end
13
24
  end
25
+
26
+ x.target!
27
+ end
28
+
29
+ def textmate_url(file, line_no)
30
+ "txmt://open?url=file://#{File.expand_path(File.join(rails_root,file))}&line_no=#{line_no}"
14
31
  end
15
- x.td(:class => "lines") do
16
- lines.each do |line|
17
- x.span(line, :class => "line")
32
+
33
+ def wrap_stack_trace_line(text)
34
+ if text =~ %r{^\s*(/[^:]+):(\d+):([^:]+)$}
35
+ file = h($1) # .to_s_xss_protected
36
+ line_number = $2
37
+ message = h($3) # .to_s_xss_protected
38
+ "<a href='#{textmate_url(file, line_number)}'>#{file}:#{line_number}</a>:#{message}" # .mark_as_xss_protected
39
+ else
40
+ h(text) # .to_s_xss_protected
18
41
  end
19
42
  end
20
43
  end
21
44
 
22
- x.target!
23
- end
24
-
25
- def textmate_url(file, line_no)
26
- "txmt://open?url=file://#{File.expand_path(File.join(rails_root,file))}&line_no=#{line_no}"
27
- end
28
-
29
- def wrap_stack_trace_line(text)
30
- if text =~ %r{^\s*(/[^:]+):(\d+):([^:]+)$}
31
- file = h($1) # .to_s_xss_protected
32
- line_number = $2
33
- message = h($3) # .to_s_xss_protected
34
- "<a href='#{textmate_url(file, line_number)}'>#{file}:#{line_number}</a>:#{message}" # .mark_as_xss_protected
35
- else
36
- h(text) # .to_s_xss_protected
37
- end
38
45
  end
39
46
  end
@@ -1,105 +1,111 @@
1
- class Relevance::Tarantula::HtmlReporter
2
-
3
- include Relevance::Tarantula
4
- attr_accessor :basedir, :results
5
- delegate :successes, :failures, :to => :results
6
-
7
- HtmlResultOverview = Struct.new(:code, :url, :description, :method, :referrer, :file_name)
8
-
9
- def initialize(basedir)
10
- @basedir = basedir
11
- @results = Struct.new(:successes, :failures).new([], [])
12
- FileUtils.mkdir_p(@basedir)
13
- end
14
-
15
- def report(result)
16
- return if result.nil?
17
-
18
- create_detail_report(result)
19
-
20
- collection = result.success ? results.successes : results.failures
21
- collection << HtmlResultOverview.new(
22
- result.code, result.url, result.description, result.method, result.referrer, result.file_name
23
- )
24
- end
1
+ module Relevance
2
+ module Tarantula
25
3
 
26
- def finish_report(test_name)
27
- puts "Writing results to #{basedir}"
28
- copy_styles unless styles_exist?
29
- create_index unless index_exists?
30
- update_index(test_name)
31
- end
32
-
33
- def create_detail_report(result)
34
- template = ERB.new(template("detail.html.erb"))
35
- output(result.file_name, template.result(result.send(:binding)), result.test_name)
36
- end
37
-
38
- def copy_styles
39
- # not using cp_r because it picks up .svn crap
40
- FileUtils.mkdir_p(File.join(basedir, "stylesheets"))
41
- Dir.glob("#{tarantula_home}/laf/stylesheets/*.css").each do |file|
42
- FileUtils.cp(file, File.join(basedir, "stylesheets"))
43
- end
44
- FileUtils.mkdir_p(File.join(basedir, "images"))
45
- Dir.glob("#{tarantula_home}/laf/images/*.{jpg,gif,png}").each do |file|
46
- FileUtils.cp(file, File.join(basedir, "images"))
47
- end
48
- FileUtils.mkdir_p(File.join(basedir, "javascripts"))
49
- Dir.glob("#{tarantula_home}/laf/javascripts/*.js").each do |file|
50
- FileUtils.cp(file, File.join(basedir, "javascripts"))
51
- end
52
- end
53
-
54
- def create_index
55
- template = ERB.new(template("index.html.erb"))
56
- output("index.html", template.result(binding))
57
- end
58
-
59
- def update_index(test_name)
60
- File.open(File.join(basedir, "index.html"), "r+") do |file|
61
- doc = Hpricot file.read
62
- tabs_container = doc.search "#tabs-container ul"
63
- results_container = doc.search "#results-container"
64
- tabs_container.append tab_html(test_name)
65
- results_container.append results_html(test_name)
66
- file.rewind
67
- file.write doc.to_s
68
- end
69
- end
70
-
71
- def index_exists?
72
- File.exists?(File.join(basedir, "index.html"))
73
- end
74
-
75
- def styles_exist?
76
- File.exists?(File.join(basedir, "stylesheets", "tarantula.css"))
77
- end
4
+ class HtmlReporter
5
+
6
+ include Relevance::Tarantula
7
+ attr_accessor :basedir, :results
8
+ delegate :successes, :failures, :to => :results
9
+
10
+ HtmlResultOverview = Struct.new(:code, :url, :description, :method, :referrer, :file_name)
11
+
12
+ def initialize(basedir)
13
+ @basedir = basedir
14
+ @results = Struct.new(:successes, :failures).new([], [])
15
+ FileUtils.mkdir_p(@basedir)
16
+ end
17
+
18
+ def report(result)
19
+ return if result.nil?
20
+
21
+ create_detail_report(result)
22
+
23
+ collection = result.success ? results.successes : results.failures
24
+ collection << HtmlResultOverview.new(
25
+ result.code, result.url, result.description, result.method, result.referrer, result.file_name
26
+ )
27
+ end
28
+
29
+ def finish_report(test_name)
30
+ puts "Writing results to #{basedir}"
31
+ copy_styles unless styles_exist?
32
+ create_index unless index_exists?
33
+ update_index(test_name)
34
+ end
35
+
36
+ def create_detail_report(result)
37
+ template = ERB.new(template("detail.html.erb"))
38
+ output(result.file_name, template.result(result.send(:binding)), result.test_name)
39
+ end
40
+
41
+ def copy_styles
42
+ # not using cp_r because it picks up .svn crap
43
+ FileUtils.mkdir_p(File.join(basedir, "stylesheets"))
44
+ Dir.glob("#{tarantula_home}/laf/stylesheets/*.css").each do |file|
45
+ FileUtils.cp(file, File.join(basedir, "stylesheets"))
46
+ end
47
+ FileUtils.mkdir_p(File.join(basedir, "images"))
48
+ Dir.glob("#{tarantula_home}/laf/images/*.{jpg,gif,png}").each do |file|
49
+ FileUtils.cp(file, File.join(basedir, "images"))
50
+ end
51
+ FileUtils.mkdir_p(File.join(basedir, "javascripts"))
52
+ Dir.glob("#{tarantula_home}/laf/javascripts/*.js").each do |file|
53
+ FileUtils.cp(file, File.join(basedir, "javascripts"))
54
+ end
55
+ end
56
+
57
+ def create_index
58
+ template = ERB.new(template("index.html.erb"))
59
+ output("index.html", template.result(binding))
60
+ end
61
+
62
+ def update_index(test_name)
63
+ File.open(File.join(basedir, "index.html"), "r+") do |file|
64
+ doc = Hpricot file.read
65
+ tabs_container = doc.search "#tabs-container ul"
66
+ results_container = doc.search "#results-container"
67
+ tabs_container.append tab_html(test_name)
68
+ results_container.append results_html(test_name)
69
+ file.rewind
70
+ file.write doc.to_s
71
+ end
72
+ end
73
+
74
+ def index_exists?
75
+ File.exists?(File.join(basedir, "index.html"))
76
+ end
77
+
78
+ def styles_exist?
79
+ File.exists?(File.join(basedir, "stylesheets", "tarantula.css"))
80
+ end
81
+
82
+ def tab_html(test_name)
83
+ "<li><a href='##{test_name}'><span>#{test_name}</span></a></li>"
84
+ end
85
+
86
+ def results_html(test_name)
87
+ template = ERB.new(template("test_report.html.erb"))
88
+ template.result(binding)
89
+ end
90
+
91
+ def template(name)
92
+ File.read(File.join(File.dirname(__FILE__), name))
93
+ end
94
+
95
+ def output(name, body, subdir = '')
96
+ FileUtils.mkdir_p(File.join(basedir, subdir)) unless subdir.empty?
97
+ File.open(File.join(basedir, subdir, name), "w") do |file|
98
+ file.write body
99
+ end
100
+ end
101
+
102
+ # CSS class for HTML status codes
103
+ def class_for_code(code)
104
+ "r#{Integer(code)/100}"
105
+ end
78
106
 
79
- def tab_html(test_name)
80
- "<li><a href='##{test_name}'><span>#{test_name}</span></a></li>"
81
- end
82
107
 
83
- def results_html(test_name)
84
- template = ERB.new(template("test_report.html.erb"))
85
- template.result(binding)
86
- end
87
-
88
- def template(name)
89
- File.read(File.join(File.dirname(__FILE__), name))
90
- end
91
-
92
- def output(name, body, subdir = '')
93
- FileUtils.mkdir_p(File.join(basedir, subdir)) unless subdir.empty?
94
- File.open(File.join(basedir, subdir, name), "w") do |file|
95
- file.write body
96
108
  end
97
- end
98
-
99
- # CSS class for HTML status codes
100
- def class_for_code(code)
101
- "r#{Integer(code)/100}"
109
+
102
110
  end
103
-
104
-
105
111
  end