jtzemp-tourbus 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/contact_app/README.rdoc +34 -40
- data/examples/contact_app/contact_app.rb +2 -2
- data/examples/contact_app/tours/simple.rb +12 -13
- data/examples/contact_app/tours/tourbus.yml +1 -1
- data/lib/common.rb +0 -1
- data/lib/runner.rb +4 -1
- data/lib/tour.rb +3 -1
- metadata +2 -14
- data/lib/web-sickle/init.rb +0 -17
- data/lib/web-sickle/lib/assertions.rb +0 -51
- data/lib/web-sickle/lib/hash_proxy.rb +0 -9
- data/lib/web-sickle/lib/helpers/asp_net.rb +0 -16
- data/lib/web-sickle/lib/helpers/table_reader.rb +0 -39
- data/lib/web-sickle/lib/make_nokigiri_output_useful.rb +0 -15
- data/lib/web-sickle/lib/web_sickle.rb +0 -227
- data/lib/web-sickle/spec/lib/helpers/table_reader_spec.rb +0 -137
- data/lib/web-sickle/spec/spec_helper.rb +0 -7
- data/lib/web-sickle/spec/spec_helpers/mechanize_mock_helper.rb +0 -12
- data/lib/web-sickle/spec/web_sickle_spec.rb +0 -50
- data/lib/web_sickle_webrat_adapter.rb +0 -40
@@ -75,60 +75,54 @@ particular order.
|
|
75
75
|
|
76
76
|
Right. Let's look test_home first, because it's simpler:
|
77
77
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
assert_page_uri_matches "/contacts"
|
82
|
-
end
|
78
|
+
def test_home
|
79
|
+
visit "/"
|
80
|
+
assert_contain "If you click this"
|
83
81
|
|
84
|
-
|
82
|
+
click_link "Enter Contact"
|
83
|
+
assert_match /\/contacts/, current_page.url
|
84
|
+
end
|
85
|
+
|
86
|
+
+visit+ is a webrat method that you can call inside of your tours. It opens the given path on the
|
85
87
|
host that tourbus is testing.
|
86
88
|
|
89
|
+
+assert_contain+ is also a webrat method that confirms the given string is on the page.
|
90
|
+
|
87
91
|
+click_link+ does what you'd expect. It takes a hash that identifies
|
88
|
-
the link to click.
|
89
|
-
regexp describing its text label. +click_link+ will raise an exception
|
92
|
+
the link to click. +click_link+ will raise an exception
|
90
93
|
if it cannot find the link to click.
|
91
94
|
|
92
|
-
+
|
93
|
-
matches the given
|
94
|
-
would have passed if the regexp matched. *Note:* Strings only match at
|
95
|
-
the /end/ of the uri; simple containment is not enough. Passing
|
96
|
-
"/contacts" works the same as passing %r{/contacts$}.
|
97
|
-
|
98
|
-
Clear as mud? "/contacts" would match
|
99
|
-
http://localhost:4567/users/42/contacts but not
|
100
|
-
http://localhost:4567/contacts/42.
|
95
|
+
+assert_match+ comes from Test::Unit which is used internally to webrat. It will raise an exception unless the uri
|
96
|
+
matches the given regexp.
|
101
97
|
|
98
|
+
So you should be able to use any Webrat locator or matcher, and any of the Test::Unit assertions.
|
102
99
|
|
103
100
|
=== test_contacts
|
104
101
|
|
105
102
|
Okay, let's actually submit a form.
|
106
103
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
assert_page_uri_matches "/contacts"
|
117
|
-
assert_page_body_contains "Tester, Joe"
|
118
|
-
end
|
104
|
+
def test_contacts
|
105
|
+
visit "/contacts"
|
106
|
+
|
107
|
+
fill_in "first_name", :with => "Joe"
|
108
|
+
fill_in "last_name", :with => "Tester"
|
109
|
+
click_button
|
110
|
+
|
111
|
+
assert_contain "Tester, Joe"
|
112
|
+
end
|
119
113
|
|
120
114
|
test_contacts starts by going directly to the contacts app. Note that
|
121
|
-
the leading "/"
|
115
|
+
the leading "/" isn't optional.
|
116
|
+
|
117
|
+
+fill_in+ is a Webrat method that will look for form fields based on ids, label text, and other things.
|
118
|
+
It's matchers are pretty good. Check out Webrat's documentation for more info. In the examples above,
|
119
|
+
we're finding the fields for the first name and last name and putting in "Joe" and "Tester" respectively.
|
120
|
+
+fill_in+ asserts that the fields actually exist and will raise an exception if they don't.
|
122
121
|
|
123
|
-
+
|
124
|
-
submit
|
125
|
-
|
126
|
-
contains some implicit assertions. It actually reads the form looking
|
127
|
-
for the named inputs and will raise an exception if any are missing.
|
128
|
-
This means you cannot use submit_form to do a blind post to a
|
129
|
-
webserver.
|
122
|
+
+click_button+ does what its name implies. It finds the correct form to
|
123
|
+
submit Webrat is smart like that. *Note:* Like +click_link+, +click_button+
|
124
|
+
contains some implicit assertions and will raise an exception if the button doesn't exist.
|
130
125
|
|
131
|
-
+
|
132
|
-
+assert_page_body_contains+ searches the page body for the given text
|
133
|
-
or regexp.
|
126
|
+
+assert_contain+ we've already seen.
|
134
127
|
|
128
|
+
Good luck, and happy touring!
|
@@ -9,7 +9,7 @@ require 'rubygems'
|
|
9
9
|
require 'sinatra'
|
10
10
|
|
11
11
|
get '/' do
|
12
|
-
'<a href="/contacts">Enter Contact</a>
|
12
|
+
%{If you click this, I'll take you to a page where you can enter your contact info: <a href="/contacts">Enter Contact</a>}
|
13
13
|
end
|
14
14
|
|
15
15
|
get '/contacts' do
|
@@ -23,7 +23,7 @@ get '/contacts' do
|
|
23
23
|
<form action="/contacts" method="POST">
|
24
24
|
<p><label for="first_name"><b>First Name:</b></label> <input name="first_name" size="30"></p>
|
25
25
|
<p><label for="last_name"><b>Last Name:</b></label> <input name="last_name" size="30"></p>
|
26
|
-
<input type="submit">
|
26
|
+
<input type="submit" value="Submit">
|
27
27
|
</form>
|
28
28
|
</body>
|
29
29
|
</html>
|
@@ -1,20 +1,19 @@
|
|
1
1
|
class Simple < Tour
|
2
2
|
def test_home
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
visit "/"
|
4
|
+
assert_contain "If you click this"
|
5
|
+
|
6
|
+
click_link "Enter Contact"
|
7
|
+
assert_match /\/contacts/, current_page.url
|
6
8
|
end
|
7
9
|
|
8
10
|
def test_contacts
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
)
|
17
|
-
assert_page_uri_matches "/contacts"
|
18
|
-
assert_page_body_contains "Tester, Joe"
|
11
|
+
visit "/contacts"
|
12
|
+
|
13
|
+
fill_in "first_name", :with => "Joe"
|
14
|
+
fill_in "last_name", :with => "Tester"
|
15
|
+
click_button
|
16
|
+
|
17
|
+
assert_contain "Tester, Joe"
|
19
18
|
end
|
20
19
|
end
|
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
host: localhost
|
2
|
+
host: http://localhost:4567
|
data/lib/common.rb
CHANGED
data/lib/runner.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'monitor'
|
2
2
|
require 'common'
|
3
3
|
|
4
|
+
# The common base class for all exceptions raised by Webrat.
|
5
|
+
class WebratError < StandardError ; end
|
6
|
+
|
4
7
|
class Runner
|
5
8
|
attr_reader :host, :tours, :number, :runner_type, :runner_id
|
6
9
|
|
@@ -31,7 +34,7 @@ class Runner
|
|
31
34
|
tests += 1
|
32
35
|
tour.run_test test
|
33
36
|
passes += 1
|
34
|
-
rescue TourBusException,
|
37
|
+
rescue TourBusException, WebratError => e
|
35
38
|
log("********** FAILURE IN RUN! **********")
|
36
39
|
log e.message
|
37
40
|
e.backtrace.each do |trace|
|
data/lib/tour.rb
CHANGED
@@ -31,6 +31,7 @@ class Tour
|
|
31
31
|
:unchecks,
|
32
32
|
:choose,
|
33
33
|
:chooses,
|
34
|
+
:current_page,
|
34
35
|
:select,
|
35
36
|
:selects,
|
36
37
|
:select_datetime,
|
@@ -62,7 +63,7 @@ class Tour
|
|
62
63
|
:internal_redirect?,
|
63
64
|
:redirected_to,
|
64
65
|
:reload,
|
65
|
-
:response_body,
|
66
|
+
:response_body,
|
66
67
|
:simulate,
|
67
68
|
:visit,
|
68
69
|
:within,
|
@@ -72,6 +73,7 @@ class Tour
|
|
72
73
|
@host, @tours, @number, @tour_id = host, tours, number, tour_id
|
73
74
|
@tour_type = self.send(:class).to_s
|
74
75
|
@webrat_session = Webrat::MechanizeSession.new
|
76
|
+
visit @host if @host
|
75
77
|
end
|
76
78
|
|
77
79
|
# before_tour runs once per tour, before any tests get run
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jtzemp-tourbus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Brady
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-
|
13
|
+
date: 2009-07-25 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -76,18 +76,6 @@ files:
|
|
76
76
|
- lib/tour.rb
|
77
77
|
- lib/tour_bus.rb
|
78
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
79
|
- README.rdoc
|
92
80
|
- MIT-LICENSE
|
93
81
|
has_rdoc: true
|
data/lib/web-sickle/init.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
gem 'mechanize', ">= 0.7.6"
|
3
|
-
gem "hpricot", ">= 0.6"
|
4
|
-
$: << File.join(File.dirname(__FILE__), 'lib')
|
5
|
-
|
6
|
-
require 'hpricot'
|
7
|
-
require 'mechanize'
|
8
|
-
|
9
|
-
WWW::Mechanize.html_parser = Hpricot
|
10
|
-
|
11
|
-
require 'web_sickle'
|
12
|
-
require "assertions"
|
13
|
-
require "hash_proxy"
|
14
|
-
require "helpers/asp_net"
|
15
|
-
require "helpers/table_reader"
|
16
|
-
|
17
|
-
Hpricot.buffer_size = 524288
|
@@ -1,51 +0,0 @@
|
|
1
|
-
class WebSickleAssertionException < Exception; end
|
2
|
-
|
3
|
-
module WebSickle::Assertions
|
4
|
-
def assert_equals(expected, actual, message = nil)
|
5
|
-
unless(expected == actual)
|
6
|
-
report_error <<-EOF
|
7
|
-
Error: Expected
|
8
|
-
#{expected.inspect}, but got
|
9
|
-
#{actual.inspect}
|
10
|
-
#{message}
|
11
|
-
EOF
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def assert_select(selector, message)
|
16
|
-
assert_select_in(@page, selector, message)
|
17
|
-
end
|
18
|
-
|
19
|
-
def assert_no_select(selector, message)
|
20
|
-
assert_no_select_in(@page, selector, message)
|
21
|
-
end
|
22
|
-
|
23
|
-
def assert_select_in(content, selector, message)
|
24
|
-
report_error("Error: Expected selector #{selector.inspect} to find a page element, but didn't. #{message}") if (content / selector).blank?
|
25
|
-
end
|
26
|
-
|
27
|
-
def assert_no_select_in(content, selector, message)
|
28
|
-
report_error("Error: Expected selector #{selector.inspect} to not find a page element, but did. #{message}") unless (content / selector).blank?
|
29
|
-
end
|
30
|
-
|
31
|
-
def assert_contains(left, right, message = nil)
|
32
|
-
(right.is_a?(Array) ? right : [right]).each do | item |
|
33
|
-
report_error("Error: Expected #{left.inspect} to contain #{right.inspect}, but didn't. #{message}") unless left.include?(item)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def assert(passes, message = nil)
|
38
|
-
report_error("Error: expected true, got false. #{message}") unless passes
|
39
|
-
end
|
40
|
-
|
41
|
-
def assert_link_text(link, text)
|
42
|
-
case text
|
43
|
-
when String
|
44
|
-
assert_equals(link.text, text)
|
45
|
-
when Regexp
|
46
|
-
assert(link.text.match(text))
|
47
|
-
else
|
48
|
-
raise ArgumentError, "Don't know how to assert an object like #{text.inspect} - expected: Regexp or String"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module WebSickle::Helpers
|
2
|
-
module AspNet
|
3
|
-
def asp_net_do_postback(options)
|
4
|
-
target_element = case
|
5
|
-
when options[:button]
|
6
|
-
find_button(options[:button])
|
7
|
-
when options[:field]
|
8
|
-
find_field(options[:field])
|
9
|
-
else
|
10
|
-
nil
|
11
|
-
end
|
12
|
-
@form.fields << WWW::Mechanize::Form::Field.new("__EVENTTARGET", target_element ? target_element.name : "") if target_element
|
13
|
-
submit_form_button
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
module WebSickle::Helpers
|
2
|
-
class TableReader
|
3
|
-
attr_reader :headers, :options, :body_rows, :header_row, :extra_rows
|
4
|
-
|
5
|
-
def initialize(element, p_options = {})
|
6
|
-
@options = {
|
7
|
-
:row_selectors => [" > tr", "thead > tr", "tbody > tr"],
|
8
|
-
:header_selector => " > th",
|
9
|
-
:header_proc => lambda { |th| th.inner_text.gsub(/[\n\s]+/, ' ').strip },
|
10
|
-
:body_selector => " > td",
|
11
|
-
:body_proc => lambda { |header, td| td.inner_text.strip },
|
12
|
-
:header_offset => 0,
|
13
|
-
:body_offset => 1
|
14
|
-
}.merge(p_options)
|
15
|
-
@options[:body_range] ||= options[:body_offset]..-1
|
16
|
-
raw_rows = options[:row_selectors].map{|row_selector| element / row_selector}.compact.flatten
|
17
|
-
|
18
|
-
@header_row = raw_rows[options[:header_offset]]
|
19
|
-
@body_rows = raw_rows[options[:body_range]]
|
20
|
-
@extra_rows = (options[:body_range].last+1)==0 ? [] : raw_rows[(options[:body_range].last+1)..-1]
|
21
|
-
|
22
|
-
@headers = (@header_row / options[:header_selector]).map(&options[:header_proc])
|
23
|
-
end
|
24
|
-
|
25
|
-
def rows
|
26
|
-
@rows ||= @body_rows.map do |row|
|
27
|
-
hash = {}
|
28
|
-
data_array = (headers).zip(row / options[:body_selector]).each do |column_name, td|
|
29
|
-
hash[column_name] = options[:body_proc].call(column_name, td)
|
30
|
-
end
|
31
|
-
hash
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def array_to_hash(data, column_names)
|
36
|
-
column_names.inject({}) {|h,column_name| h[column_name] = data[column_names.index(column_name)]; h }
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# Nokogiri::XML::Element.class_eval do
|
2
|
-
# def inspect(indent = "")
|
3
|
-
# breaker = "\n#{indent}"
|
4
|
-
# if children.length == 0
|
5
|
-
# %(#{indent}<#{name}#{breaker} #{attributes.map {|k,v| k + '=' + v.inspect} * "#{breaker} "}/>)
|
6
|
-
# else
|
7
|
-
# %(#{indent}<#{name} #{attributes.map {|k,v| k + '=' + v.inspect} * " "}>\n#{children.map {|c| c.inspect(indent + ' ') rescue c.class} * "\n"}#{breaker}</#{name}>)
|
8
|
-
# end
|
9
|
-
# end
|
10
|
-
# end
|
11
|
-
# Nokogiri::XML::Text.class_eval do
|
12
|
-
# def inspect(indent = "")
|
13
|
-
# "#{indent}#{text.inspect}"
|
14
|
-
# end
|
15
|
-
# end
|
@@ -1,227 +0,0 @@
|
|
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
|
@@ -1,137 +0,0 @@
|
|
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
|
@@ -1,12 +0,0 @@
|
|
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
|
@@ -1,50 +0,0 @@
|
|
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
|
@@ -1,40 +0,0 @@
|
|
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
|