jtzemp-tourbus 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +22 -0
- data/README.rdoc +200 -0
- data/bin/tourbus +46 -0
- data/bin/tourwatch +52 -0
- data/examples/contact_app/README.rdoc +134 -0
- data/examples/contact_app/contact_app.rb +36 -0
- data/examples/contact_app/tours/simple.rb +20 -0
- data/examples/contact_app/tours/tourbus.yml +2 -0
- data/lib/common.rb +31 -0
- data/lib/runner.rb +72 -0
- data/lib/tour.rb +145 -0
- data/lib/tour_bus.rb +92 -0
- data/lib/tour_watch.rb +88 -0
- data/lib/web-sickle/init.rb +17 -0
- data/lib/web-sickle/lib/assertions.rb +51 -0
- data/lib/web-sickle/lib/hash_proxy.rb +9 -0
- data/lib/web-sickle/lib/helpers/asp_net.rb +16 -0
- data/lib/web-sickle/lib/helpers/table_reader.rb +39 -0
- data/lib/web-sickle/lib/make_nokigiri_output_useful.rb +15 -0
- data/lib/web-sickle/lib/web_sickle.rb +227 -0
- data/lib/web-sickle/spec/lib/helpers/table_reader_spec.rb +137 -0
- data/lib/web-sickle/spec/spec_helper.rb +7 -0
- data/lib/web-sickle/spec/spec_helpers/mechanize_mock_helper.rb +12 -0
- data/lib/web-sickle/spec/web_sickle_spec.rb +50 -0
- data/lib/web_sickle_webrat_adapter.rb +40 -0
- metadata +125 -0
@@ -0,0 +1,227 @@
|
|
1
|
+
class WebsickleException < Exception; end
|
2
|
+
|
3
|
+
module WebSickle
|
4
|
+
# form_value is used to interface with the current select form
|
5
|
+
attr_reader :form_value
|
6
|
+
attr_accessor :page
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@page = nil
|
10
|
+
@form_value = HashProxy.new(
|
11
|
+
:set => lambda { |identifier, value| set_form_value(identifier, value)},
|
12
|
+
:get => lambda { |identifier| get_form_value(identifier)}
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def click_link(link)
|
17
|
+
set_page(agent.click(find_link(link)))
|
18
|
+
end
|
19
|
+
|
20
|
+
def submit_form(options = {})
|
21
|
+
options[:button] = :first unless options.has_key?(:button)
|
22
|
+
options[:identified_by] ||= :first
|
23
|
+
select_form(options[:identified_by])
|
24
|
+
set_form_values(options[:values]) if options[:values]
|
25
|
+
submit_form_button(options[:button])
|
26
|
+
end
|
27
|
+
|
28
|
+
# select the current form
|
29
|
+
def select_form(identifier = {})
|
30
|
+
identifier = make_identifier(identifier, [:name, :action, :method])
|
31
|
+
@form = find_in_collection(@page.forms, identifier)
|
32
|
+
unless @form
|
33
|
+
valid_forms = @page.forms.map {|f| "name: #{f.name}, method: #{f.method}, action: #{f.action}"} * "\n"
|
34
|
+
report_error("Couldn't find form on page at #{@page.uri} with attributes #{identifier.inspect}. Valid forms on this page are: \n#{valid_forms}")
|
35
|
+
end
|
36
|
+
@form
|
37
|
+
end
|
38
|
+
|
39
|
+
# submits the current form
|
40
|
+
def submit_form_button(button_criteria = nil, options = {})
|
41
|
+
button =
|
42
|
+
case button_criteria
|
43
|
+
when nil
|
44
|
+
nil
|
45
|
+
else
|
46
|
+
find_button(button_criteria)
|
47
|
+
end
|
48
|
+
set_page(agent.submit(@form, button))
|
49
|
+
end
|
50
|
+
|
51
|
+
# sets the given path to the current page, then opens it using our agent
|
52
|
+
def open_page(path, parameters = [], referer = nil)
|
53
|
+
set_page(agent.get(path, parameters, referer))
|
54
|
+
end
|
55
|
+
|
56
|
+
# uses Hpricot style css selectors to find the elements in the current +page+.
|
57
|
+
# Uses Hpricot#/ (or Hpricot#search)
|
58
|
+
def select_element(match)
|
59
|
+
select_element_in(@page, match)
|
60
|
+
end
|
61
|
+
|
62
|
+
# uses Hpricot style css selectors to find the element in the given container. Works with html pages, and file pages that happen to have xml-like content.
|
63
|
+
# throws error if can't find a match
|
64
|
+
def select_element_in(contents, match)
|
65
|
+
result = (contents.respond_to?(:/) ? contents : Hpricot(contents.body)) / match
|
66
|
+
if result.blank?
|
67
|
+
report_error("Tried to find element matching #{match}, but couldn't")
|
68
|
+
else
|
69
|
+
result
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# uses Hpricot style css selectors to find the element. Works with html pages, and file pages that happen to have xml-like content.
|
74
|
+
# throws error if can't find a match
|
75
|
+
# Uses Hpricot#at
|
76
|
+
def detect_element(match)
|
77
|
+
result = (@page.respond_to?(:at) ? @page : Hpricot(@page.body)).at(match)
|
78
|
+
if result.blank?
|
79
|
+
report_error("Tried to find element matching #{match}, but couldn't")
|
80
|
+
else
|
81
|
+
result
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
# our friendly mechinze agent
|
87
|
+
def agent
|
88
|
+
@agent ||= new_mechanize_agent
|
89
|
+
end
|
90
|
+
|
91
|
+
def make_identifier(identifier, valid_keys = nil, default_key = :name)
|
92
|
+
identifier = {default_key => identifier} unless identifier.is_a?(Hash) || identifier.is_a?(Symbol)
|
93
|
+
identifier.assert_valid_keys(valid_keys) if identifier.is_a?(Hash) && valid_keys
|
94
|
+
identifier
|
95
|
+
end
|
96
|
+
|
97
|
+
def find_field(identifier)
|
98
|
+
if @form.nil?
|
99
|
+
report_error("No form is selected when trying to find field by #{identifier.inspect}")
|
100
|
+
return
|
101
|
+
end
|
102
|
+
identifier = make_identifier(identifier, [:name, :value])
|
103
|
+
find_in_collection(@form.radiobuttons + @form.fields + @form.checkboxes + @form.file_uploads, identifier) ||
|
104
|
+
report_error("Tried to find field identified by #{identifier.inspect}, but failed.\nForm fields are: #{(@form.radiobuttons + @form.fields + @form.checkboxes + @form.file_uploads).map{|f| f.name} * ", \n "}")
|
105
|
+
end
|
106
|
+
|
107
|
+
def find_link(identifier)
|
108
|
+
identifier = make_identifier(identifier, [:href, :text], :text)
|
109
|
+
find_in_collection(page.links, identifier) ||
|
110
|
+
report_error("Tried to find link identified by #{identifier.inspect}, but failed.\nValid links are: #{page.links.map{|f| f.inspect} * ", \n "}")
|
111
|
+
end
|
112
|
+
|
113
|
+
# finds a button by parameters. Throws error if not able to find.
|
114
|
+
# example:
|
115
|
+
# find_button("btnSubmit") - finds a button named "btnSubmit"
|
116
|
+
# find_button(:name => "btnSubmit")
|
117
|
+
# find_button(:name => "btnSubmit", :value => /Lucky/) - finds a button named btnSubmit with a value matching /Lucky/
|
118
|
+
def find_button(identifier)
|
119
|
+
identifier = make_identifier(identifier, [:value, :name])
|
120
|
+
find_in_collection(@form.buttons, identifier) ||
|
121
|
+
report_error("Tried to find button identified by #{identifier.inspect}, but failed. Buttons on selected form are: #{@form.buttons.map{|f| f.name} * ','}")
|
122
|
+
end
|
123
|
+
|
124
|
+
# the magic method that powers find_button, find_field. Does not throw an error if not found
|
125
|
+
def find_in_collection(collection, identifier, via = :find)
|
126
|
+
return collection.first if identifier == :first
|
127
|
+
find_all_in_collection(collection, identifier, :find)
|
128
|
+
end
|
129
|
+
|
130
|
+
def find_all_in_collection(collection, identifier, via = :select)
|
131
|
+
return [collection.first] if identifier == :first
|
132
|
+
collection.send(via) do |item|
|
133
|
+
identifier.all? { |k, criteria| is_a_match?(criteria, item.send(k)) }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# sets a form-field's value by identifier. Throw's error if field does not exist
|
138
|
+
def set_form_value(identifier, value)
|
139
|
+
field = find_field(identifier)
|
140
|
+
case field
|
141
|
+
when WWW::Mechanize::Form::CheckBox
|
142
|
+
field.checked = value
|
143
|
+
when WWW::Mechanize::Form::RadioButton
|
144
|
+
radio_collection = find_all_in_collection(@form.radiobuttons, :name => field.name)
|
145
|
+
radio_collection.each { |f|; f.checked = false }
|
146
|
+
finder = (value.is_a?(Hash) || value.is_a?(Symbol)) ? value : {:value => value}
|
147
|
+
find_in_collection(radio_collection, finder).checked = true
|
148
|
+
when WWW::Mechanize::Form::SelectList
|
149
|
+
if value.is_a?(Hash) || value.is_a?(Symbol)
|
150
|
+
field.value = find_in_collection(field.options, value).value
|
151
|
+
else
|
152
|
+
field.value = value
|
153
|
+
end
|
154
|
+
else
|
155
|
+
field.value = value
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def set_form_values(set_pairs = {})
|
160
|
+
flattened_value_hash(set_pairs).each do |identifier, value|
|
161
|
+
set_form_value(identifier, value)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def flattened_value_hash(hash, parents = [])
|
166
|
+
new_hash = {}
|
167
|
+
hash.each do |key, value|
|
168
|
+
if value.is_a?(Hash) && value.keys.first.is_a?(String)
|
169
|
+
new_hash.update(flattened_value_hash(value, [key] + parents))
|
170
|
+
else
|
171
|
+
parents.each { |parent| key = "#{parent}[#{key}]"}
|
172
|
+
new_hash[key] = value
|
173
|
+
end
|
174
|
+
end
|
175
|
+
new_hash
|
176
|
+
end
|
177
|
+
|
178
|
+
# sets a form-field's value by identifier. Throw's error if field does not exist
|
179
|
+
def get_form_value(identifier)
|
180
|
+
field = find_field(identifier)
|
181
|
+
case field
|
182
|
+
when WWW::Mechanize::Form::CheckBox
|
183
|
+
field.checked
|
184
|
+
else
|
185
|
+
field.value
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def format_error(msg)
|
190
|
+
error = "Error encountered: #{msg}."
|
191
|
+
begin
|
192
|
+
error << "\n\nPage URL:#{@page.uri.to_s}" if @page
|
193
|
+
rescue
|
194
|
+
end
|
195
|
+
error
|
196
|
+
end
|
197
|
+
|
198
|
+
def report_error(msg)
|
199
|
+
raise WebsickleException, format_error(msg)
|
200
|
+
nil
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
def set_page(p)
|
205
|
+
@form = nil
|
206
|
+
@page = p
|
207
|
+
end
|
208
|
+
|
209
|
+
def is_a_match?(criteria, value)
|
210
|
+
case criteria
|
211
|
+
when Regexp
|
212
|
+
criteria.match(value)
|
213
|
+
when String
|
214
|
+
criteria == value
|
215
|
+
when Array
|
216
|
+
criteria.include?(value)
|
217
|
+
else
|
218
|
+
criteria.to_s == value.to_s
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def new_mechanize_agent
|
223
|
+
a = WWW::Mechanize.new
|
224
|
+
a.read_timeout = 600 # 10 minutes
|
225
|
+
a
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
describe WebSickle::Helpers::TableReader do
|
4
|
+
describe "Simple example" do
|
5
|
+
before(:each) do
|
6
|
+
@content = <<-EOF
|
7
|
+
<table>
|
8
|
+
<tr>
|
9
|
+
<th>Name</th>
|
10
|
+
<th>Age</th>
|
11
|
+
</tr>
|
12
|
+
<tr>
|
13
|
+
<td>Googly</td>
|
14
|
+
<td>2</td>
|
15
|
+
</tr>
|
16
|
+
</table>
|
17
|
+
EOF
|
18
|
+
h = Hpricot(@content)
|
19
|
+
@table = WebSickle::Helpers::TableReader.new(h / "table")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should extract headers" do
|
23
|
+
@table.headers.should == ["Name", "Age"]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should extract rows" do
|
27
|
+
@table.rows.should == [
|
28
|
+
{"Name" => "Googly", "Age" => "2"}
|
29
|
+
]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
describe "Targetted example" do
|
36
|
+
before(:each) do
|
37
|
+
@content = <<-EOF
|
38
|
+
<table>
|
39
|
+
<thead>
|
40
|
+
<tr>
|
41
|
+
<td colspan='2'>----</td>
|
42
|
+
</tr>
|
43
|
+
<tr>
|
44
|
+
<th><b>Name</b></th>
|
45
|
+
<th><b>Age</b></th>
|
46
|
+
</tr>
|
47
|
+
</thead>
|
48
|
+
<tbody>
|
49
|
+
<tr>
|
50
|
+
<td>Googly</td>
|
51
|
+
<td>2</td>
|
52
|
+
</tr>
|
53
|
+
<tr>
|
54
|
+
<td>Bear</td>
|
55
|
+
<td>3</td>
|
56
|
+
</tr>
|
57
|
+
<tr>
|
58
|
+
<td colspan='2'>Totals!</td>
|
59
|
+
</tr>
|
60
|
+
<tr>
|
61
|
+
<td>---</td>
|
62
|
+
<td>5</td>
|
63
|
+
</tr>
|
64
|
+
</tbody>
|
65
|
+
</table>
|
66
|
+
EOF
|
67
|
+
h = Hpricot(@content)
|
68
|
+
@table = WebSickle::Helpers::TableReader.new(h / " > table",
|
69
|
+
:header_selector => " > th > b",
|
70
|
+
:header_offset => 1,
|
71
|
+
:body_range => 2..-3
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should extract the column headers" do
|
76
|
+
@table.headers.should == ["Name", "Age"]
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should extract the row data for the specified range" do
|
80
|
+
@table.rows.should == [
|
81
|
+
{"Name" => "Googly", "Age" => "2"},
|
82
|
+
{"Name" => "Bear", "Age" => "3"},
|
83
|
+
]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should allow you to check extra rows to assert you didn't chop off too much" do
|
87
|
+
(@table.extra_rows.first / "td").inner_text.should == "Totals!"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
describe "when using procs to extract data" do
|
94
|
+
before(:each) do
|
95
|
+
@content = <<-EOF
|
96
|
+
<table>
|
97
|
+
<tr>
|
98
|
+
<th>Name</th>
|
99
|
+
<th>Age</th>
|
100
|
+
</tr>
|
101
|
+
<tr>
|
102
|
+
<td>Googly</td>
|
103
|
+
<td>2</td>
|
104
|
+
</tr>
|
105
|
+
<tr>
|
106
|
+
<td>Bear</td>
|
107
|
+
<td>3</td>
|
108
|
+
</tr>
|
109
|
+
</table>
|
110
|
+
EOF
|
111
|
+
h = Hpricot(@content)
|
112
|
+
@table = WebSickle::Helpers::TableReader.new(h / " > table",
|
113
|
+
:header_proc => lambda {|th| th.inner_text.downcase.to_sym},
|
114
|
+
:body_proc => lambda {|col_name, td|
|
115
|
+
value = td.inner_text
|
116
|
+
case col_name
|
117
|
+
when :name
|
118
|
+
value.upcase
|
119
|
+
when :age
|
120
|
+
value.to_i
|
121
|
+
end
|
122
|
+
}
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should use the header proc to extract column headers" do
|
127
|
+
@table.headers.should == [:name, :age]
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should use the body proc to format the data" do
|
131
|
+
@table.rows.should == [
|
132
|
+
{:name => "GOOGLY", :age => 2},
|
133
|
+
{:name => "BEAR", :age => 3}
|
134
|
+
]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module MechanizeMockHelper
|
2
|
+
def fixture_file(filename)
|
3
|
+
File.read("#{File.dirname(__FILE__)}/../fixtures/#{filename}")
|
4
|
+
end
|
5
|
+
|
6
|
+
def mechanize_page(path_to_data, options = {})
|
7
|
+
options[:uri] ||= URI.parse("http://url.com/#{path_to_data}")
|
8
|
+
options[:response] ||= {'content-type' => 'text/html'}
|
9
|
+
|
10
|
+
WWW::Mechanize::Page.new(options[:uri], options[:response], fixture_file("/#{path_to_data}"))
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class WebSickleHelper
|
4
|
+
include WebSickle
|
5
|
+
end
|
6
|
+
|
7
|
+
describe WebSickle do
|
8
|
+
include MechanizeMockHelper
|
9
|
+
|
10
|
+
before(:all) do
|
11
|
+
WebSickleHelper.protected_instance_methods.each do |method|
|
12
|
+
WebSickleHelper.send(:public, method)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
before(:each) do
|
17
|
+
@helper = WebSickleHelper.new
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should flatten a value hash" do
|
21
|
+
@helper.flattened_value_hash("contact" => {"first_name" => "bob"}).should == {"contact[first_name]" => "bob"}
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "clicking links" do
|
25
|
+
before(:each) do
|
26
|
+
@helper.stub!(:page).and_return(mechanize_page("linkies.html"))
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should click a link by matching the link text" do
|
30
|
+
@helper.agent.should_receive(:click) do |link|
|
31
|
+
link.text.should include("one")
|
32
|
+
end
|
33
|
+
@helper.click_link(:text => /one/)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should click a link by matching the link href" do
|
37
|
+
@helper.agent.should_receive(:click) do |link|
|
38
|
+
link.href.should include("/two")
|
39
|
+
end
|
40
|
+
@helper.click_link(:href => %r{/two})
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should default matching the link text" do
|
44
|
+
@helper.agent.should_receive(:click) do |link|
|
45
|
+
link.text.should include("Link number one")
|
46
|
+
end
|
47
|
+
@helper.click_link("Link number one")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'webrat'
|
2
|
+
require 'webrat/mechanize'
|
3
|
+
|
4
|
+
module WebSickleWebratAdapter
|
5
|
+
|
6
|
+
def open_page(url)
|
7
|
+
session.visit(url)
|
8
|
+
end
|
9
|
+
|
10
|
+
def click_link(options)
|
11
|
+
raise "This adapter only works with the :text option!" unless options[:text]
|
12
|
+
session.click_link(options[:text])
|
13
|
+
end
|
14
|
+
|
15
|
+
def submit_form_with_options(options_hash)
|
16
|
+
action = options_hash[:identified_by][:action]
|
17
|
+
raise "You must provide an action!" unless action
|
18
|
+
action_regexp = action.is_a?(Regexp) ? action : Regexp.new("#{action}$")
|
19
|
+
form = page.forms.detect {|f| f.action =~ action_regexp }
|
20
|
+
raise "Could not find form with matching action: #{action_regexp}" unless form
|
21
|
+
method = options_hash[:method] || 'post'
|
22
|
+
session.request_page(form.action, method, options_hash[:values])
|
23
|
+
end
|
24
|
+
|
25
|
+
def page
|
26
|
+
session.response
|
27
|
+
end
|
28
|
+
|
29
|
+
# def visit(url)
|
30
|
+
# session.visit(url)
|
31
|
+
# end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def session
|
36
|
+
@session ||= Webrat::MechanizeSession.new
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jtzemp-tourbus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.9
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Brady
|
8
|
+
- James Britt
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-04-17 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: mechanize
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.8.5
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: trollop
|
28
|
+
type: :runtime
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
version:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: faker
|
38
|
+
type: :runtime
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: hpricot
|
48
|
+
type: :runtime
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
description: TourBus, a web stress-testing tool that combines complex 'tour' definitions with scalable concurrent testing
|
57
|
+
email: github@shinybit.com
|
58
|
+
executables:
|
59
|
+
- tourbus
|
60
|
+
- tourwatch
|
61
|
+
extensions: []
|
62
|
+
|
63
|
+
extra_rdoc_files:
|
64
|
+
- README.rdoc
|
65
|
+
- MIT-LICENSE
|
66
|
+
- examples/contact_app/README.rdoc
|
67
|
+
files:
|
68
|
+
- bin/tourbus
|
69
|
+
- bin/tourwatch
|
70
|
+
- examples/contact_app/README.rdoc
|
71
|
+
- examples/contact_app/contact_app.rb
|
72
|
+
- examples/contact_app/tours/simple.rb
|
73
|
+
- examples/contact_app/tours/tourbus.yml
|
74
|
+
- lib/common.rb
|
75
|
+
- lib/runner.rb
|
76
|
+
- lib/tour.rb
|
77
|
+
- lib/tour_bus.rb
|
78
|
+
- lib/tour_watch.rb
|
79
|
+
- lib/web_sickle_webrat_adapter.rb
|
80
|
+
- lib/web-sickle/init.rb
|
81
|
+
- lib/web-sickle/lib/assertions.rb
|
82
|
+
- lib/web-sickle/lib/hash_proxy.rb
|
83
|
+
- lib/web-sickle/lib/helpers/asp_net.rb
|
84
|
+
- lib/web-sickle/lib/helpers/table_reader.rb
|
85
|
+
- lib/web-sickle/lib/make_nokigiri_output_useful.rb
|
86
|
+
- lib/web-sickle/lib/web_sickle.rb
|
87
|
+
- lib/web-sickle/spec/lib/helpers/table_reader_spec.rb
|
88
|
+
- lib/web-sickle/spec/spec_helper.rb
|
89
|
+
- lib/web-sickle/spec/spec_helpers/mechanize_mock_helper.rb
|
90
|
+
- lib/web-sickle/spec/web_sickle_spec.rb
|
91
|
+
- README.rdoc
|
92
|
+
- MIT-LICENSE
|
93
|
+
has_rdoc: true
|
94
|
+
homepage: http://github.com/dbrady/tourbus/
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options:
|
97
|
+
- --line-numbers
|
98
|
+
- --inline-source
|
99
|
+
- --main
|
100
|
+
- README.rdoc
|
101
|
+
- --title
|
102
|
+
- Tourbus - Web Stress Testing in Ruby
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: "0"
|
110
|
+
version:
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: "0"
|
116
|
+
version:
|
117
|
+
requirements: []
|
118
|
+
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 1.2.0
|
121
|
+
signing_key:
|
122
|
+
specification_version: 2
|
123
|
+
summary: TourBus web stress-testing tool
|
124
|
+
test_files: []
|
125
|
+
|