webrat 0.2.0 → 0.3.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.
- data/History.txt +76 -14
- data/README.txt +40 -36
- data/Rakefile +80 -18
- data/TODO.txt +9 -3
- data/init.rb +1 -1
- data/lib/webrat.rb +30 -5
- data/lib/webrat/core.rb +12 -0
- data/lib/webrat/core/area.rb +44 -0
- data/lib/webrat/core/field.rb +332 -0
- data/lib/webrat/core/flunk.rb +7 -0
- data/lib/webrat/core/form.rb +130 -0
- data/lib/webrat/core/label.rb +18 -0
- data/lib/webrat/core/link.rb +101 -0
- data/lib/webrat/core/locators.rb +92 -0
- data/lib/webrat/core/logging.rb +25 -0
- data/lib/webrat/core/matchers.rb +4 -0
- data/lib/webrat/core/matchers/have_content.rb +94 -0
- data/lib/webrat/core/matchers/have_selector.rb +39 -0
- data/lib/webrat/core/matchers/have_tag.rb +58 -0
- data/lib/webrat/core/matchers/have_xpath.rb +85 -0
- data/lib/webrat/core/methods.rb +44 -0
- data/lib/webrat/core/mime.rb +29 -0
- data/lib/webrat/core/nokogiri.rb +42 -0
- data/lib/webrat/core/scope.rb +208 -0
- data/lib/webrat/core/select_option.rb +29 -0
- data/lib/webrat/core/session.rb +188 -0
- data/lib/webrat/core_extensions/blank.rb +58 -0
- data/lib/webrat/core_extensions/deprecate.rb +8 -0
- data/lib/webrat/core_extensions/detect_mapped.rb +12 -0
- data/lib/webrat/core_extensions/hash_with_indifferent_access.rb +131 -0
- data/lib/webrat/core_extensions/meta_class.rb +6 -0
- data/lib/webrat/core_extensions/nil_to_param.rb +5 -0
- data/lib/webrat/mechanize.rb +28 -0
- data/lib/webrat/merb.rb +75 -0
- data/lib/webrat/rack.rb +24 -0
- data/lib/webrat/rails.rb +102 -0
- data/lib/webrat/rails/redirect_actions.rb +18 -0
- data/lib/webrat/selenium.rb +3 -0
- data/lib/webrat/selenium/location_strategy_javascript/button.js +12 -0
- data/lib/webrat/selenium/location_strategy_javascript/label.js +16 -0
- data/lib/webrat/selenium/location_strategy_javascript/webrat.js +5 -0
- data/lib/webrat/selenium/location_strategy_javascript/webratlink.js +9 -0
- data/lib/webrat/selenium/location_strategy_javascript/webratlinkwithin.js +15 -0
- data/lib/webrat/selenium/location_strategy_javascript/webratselectwithoption.js +5 -0
- data/lib/webrat/selenium/selenium_extensions.js +6 -0
- data/lib/webrat/selenium/selenium_session.rb +137 -0
- data/lib/webrat/sinatra.rb +19 -0
- metadata +66 -52
- data/Manifest.txt +0 -20
- data/lib/webrat/rails_extensions.rb +0 -27
- data/lib/webrat/session.rb +0 -523
- data/test/checks_test.rb +0 -121
- data/test/chooses_test.rb +0 -74
- data/test/clicks_button_test.rb +0 -308
- data/test/clicks_link_test.rb +0 -193
- data/test/fills_in_test.rb +0 -139
- data/test/helper.rb +0 -21
- data/test/reloads_test.rb +0 -26
- data/test/selects_test.rb +0 -93
- data/test/visits_test.rb +0 -31
@@ -0,0 +1,18 @@
|
|
1
|
+
module Webrat
|
2
|
+
class Label #:nodoc:
|
3
|
+
|
4
|
+
def initialize(field, element)
|
5
|
+
@field = field
|
6
|
+
@element = element
|
7
|
+
end
|
8
|
+
|
9
|
+
def matches_text?(label_text)
|
10
|
+
text =~ /^\W*#{Regexp.escape(label_text.to_s)}\b/i
|
11
|
+
end
|
12
|
+
|
13
|
+
def text
|
14
|
+
@element.inner_text
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "webrat/core_extensions/blank"
|
2
|
+
|
3
|
+
module Webrat
|
4
|
+
class Link #:nodoc:
|
5
|
+
|
6
|
+
def initialize(session, element)
|
7
|
+
@session = session
|
8
|
+
@element = element
|
9
|
+
end
|
10
|
+
|
11
|
+
def click(options = {})
|
12
|
+
method = options[:method] || http_method
|
13
|
+
return if href =~ /^#/ && method == :get
|
14
|
+
|
15
|
+
options[:javascript] = true if options[:javascript].nil?
|
16
|
+
|
17
|
+
if options[:javascript]
|
18
|
+
@session.request_page(absolute_href, method, data)
|
19
|
+
else
|
20
|
+
@session.request_page(absolute_href, :get, {})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def matches_text?(link_text)
|
25
|
+
html = text.gsub(' ',' ')
|
26
|
+
|
27
|
+
if link_text.is_a?(Regexp)
|
28
|
+
matcher = link_text
|
29
|
+
else
|
30
|
+
matcher = /#{Regexp.escape(link_text.to_s)}/i
|
31
|
+
end
|
32
|
+
|
33
|
+
html =~ matcher || title =~ matcher
|
34
|
+
end
|
35
|
+
|
36
|
+
def text
|
37
|
+
@element.inner_html
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def data
|
43
|
+
authenticity_token.blank? ? {} : {"authenticity_token" => authenticity_token}
|
44
|
+
end
|
45
|
+
|
46
|
+
def title
|
47
|
+
@element['title']
|
48
|
+
end
|
49
|
+
|
50
|
+
def href
|
51
|
+
@element["href"]
|
52
|
+
end
|
53
|
+
|
54
|
+
def absolute_href
|
55
|
+
if href =~ /^\?/
|
56
|
+
"#{@session.current_url}#{href}"
|
57
|
+
elsif href !~ %r{^https?://www.example.com(/.*)} && (href !~ /^\//)
|
58
|
+
"#{@session.current_url}/#{href}"
|
59
|
+
else
|
60
|
+
href
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def authenticity_token
|
65
|
+
return unless onclick && onclick.include?("s.setAttribute('name', 'authenticity_token');") &&
|
66
|
+
onclick =~ /s\.setAttribute\('value', '([a-f0-9]{40})'\);/
|
67
|
+
$LAST_MATCH_INFO.captures.first
|
68
|
+
end
|
69
|
+
|
70
|
+
def onclick
|
71
|
+
@element["onclick"]
|
72
|
+
end
|
73
|
+
|
74
|
+
def http_method
|
75
|
+
if !onclick.blank? && onclick.include?("f.submit()")
|
76
|
+
http_method_from_js_form
|
77
|
+
else
|
78
|
+
:get
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def http_method_from_js_form
|
83
|
+
if onclick.include?("m.setAttribute('name', '_method')")
|
84
|
+
http_method_from_fake_method_param
|
85
|
+
else
|
86
|
+
:post
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def http_method_from_fake_method_param
|
91
|
+
if onclick.include?("m.setAttribute('value', 'delete')")
|
92
|
+
:delete
|
93
|
+
elsif onclick.include?("m.setAttribute('value', 'put')")
|
94
|
+
:put
|
95
|
+
else
|
96
|
+
raise "No HTTP method for _method param in #{onclick.inspect}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "webrat/core_extensions/detect_mapped"
|
2
|
+
|
3
|
+
module Webrat
|
4
|
+
module Locators
|
5
|
+
|
6
|
+
def field(*args)
|
7
|
+
# This is the default locator strategy
|
8
|
+
find_field_with_id(*args) ||
|
9
|
+
find_field_named(*args) ||
|
10
|
+
field_labeled(*args) ||
|
11
|
+
flunk("Could not find field: #{args.inspect}")
|
12
|
+
end
|
13
|
+
|
14
|
+
def field_labeled(label, *field_types)
|
15
|
+
find_field_labeled(label, *field_types) ||
|
16
|
+
flunk("Could not find field labeled #{label.inspect}")
|
17
|
+
end
|
18
|
+
|
19
|
+
def field_named(name, *field_types)
|
20
|
+
find_field_named(name, *field_types) ||
|
21
|
+
flunk("Could not find field named #{name.inspect}")
|
22
|
+
end
|
23
|
+
|
24
|
+
def field_with_id(id, *field_types)
|
25
|
+
find_field_with_id(id, *field_types) ||
|
26
|
+
flunk("Could not find field with id #{id.inspect}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_field_labeled(label, *field_types) #:nodoc:
|
30
|
+
forms.detect_mapped do |form|
|
31
|
+
form.field_labeled(label, *field_types)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_field_named(name, *field_types) #:nodoc:
|
36
|
+
forms.detect_mapped do |form|
|
37
|
+
form.field_named(name, *field_types)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_field_with_id(id, *field_types) #:nodoc:
|
42
|
+
forms.detect_mapped do |form|
|
43
|
+
form.field_with_id(id, *field_types)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_select_option(option_text, id_or_name_or_label) #:nodoc:
|
48
|
+
if id_or_name_or_label
|
49
|
+
field = field(id_or_name_or_label, SelectField)
|
50
|
+
return field.find_option(option_text)
|
51
|
+
else
|
52
|
+
select_option = forms.detect_mapped do |form|
|
53
|
+
form.find_select_option(option_text)
|
54
|
+
end
|
55
|
+
|
56
|
+
return select_option if select_option
|
57
|
+
end
|
58
|
+
|
59
|
+
flunk("Could not find option #{option_text.inspect}")
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_button(value) #:nodoc:
|
63
|
+
button = forms.detect_mapped do |form|
|
64
|
+
form.find_button(value)
|
65
|
+
end
|
66
|
+
|
67
|
+
if button
|
68
|
+
return button
|
69
|
+
else
|
70
|
+
flunk("Could not find button #{value.inspect}")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def find_area(area_name) #:nodoc:
|
75
|
+
areas.detect { |area| area.matches_text?(area_name) } ||
|
76
|
+
flunk("Could not find area with name #{area_name}")
|
77
|
+
end
|
78
|
+
|
79
|
+
def find_link(text) #:nodoc:
|
80
|
+
matching_links = links.select do |possible_link|
|
81
|
+
possible_link.matches_text?(text)
|
82
|
+
end
|
83
|
+
|
84
|
+
if matching_links.any?
|
85
|
+
matching_links.min { |a, b| a.text.length <=> b.text.length }
|
86
|
+
else
|
87
|
+
flunk("Could not find link with text #{text.inspect}")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Webrat
|
2
|
+
module Logging #:nodoc:
|
3
|
+
|
4
|
+
def warn_log(message) # :nodoc:
|
5
|
+
return unless logger
|
6
|
+
logger.warn message
|
7
|
+
end
|
8
|
+
|
9
|
+
def debug_log(message) # :nodoc:
|
10
|
+
return unless logger
|
11
|
+
logger.debug message
|
12
|
+
end
|
13
|
+
|
14
|
+
def logger # :nodoc:
|
15
|
+
if defined? RAILS_DEFAULT_LOGGER
|
16
|
+
RAILS_DEFAULT_LOGGER
|
17
|
+
elsif defined? Merb
|
18
|
+
Merb.logger
|
19
|
+
else
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Webrat
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class HasContent #:nodoc:
|
5
|
+
def initialize(content)
|
6
|
+
@content = content
|
7
|
+
end
|
8
|
+
|
9
|
+
def matches?(stringlike)
|
10
|
+
if defined?(Nokogiri::XML)
|
11
|
+
matches_nokogiri?(stringlike)
|
12
|
+
else
|
13
|
+
matches_rexml?(stringlike)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def matches_rexml?(stringlike)
|
18
|
+
@document = rexml_document(stringlike)
|
19
|
+
@element = @document.inner_text
|
20
|
+
|
21
|
+
case @content
|
22
|
+
when String
|
23
|
+
@element.include?(@content)
|
24
|
+
when Regexp
|
25
|
+
@element.match(@content)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def matches_nokogiri?(stringlike)
|
30
|
+
@document = Webrat.nokogiri_document(stringlike)
|
31
|
+
@element = @document.inner_text
|
32
|
+
|
33
|
+
case @content
|
34
|
+
when String
|
35
|
+
@element.include?(@content)
|
36
|
+
when Regexp
|
37
|
+
@element.match(@content)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def rexml_document(stringlike)
|
42
|
+
stringlike = stringlike.body.to_s if stringlike.respond_to?(:body)
|
43
|
+
|
44
|
+
case stringlike
|
45
|
+
when REXML::Document
|
46
|
+
stringlike.root
|
47
|
+
when REXML::Node
|
48
|
+
stringlike
|
49
|
+
when StringIO, String
|
50
|
+
begin
|
51
|
+
REXML::Document.new(stringlike.to_s).root
|
52
|
+
rescue REXML::ParseException => e
|
53
|
+
if e.message.include?("second root element")
|
54
|
+
REXML::Document.new("<fake-root-element>#{stringlike}</fake-root-element>").root
|
55
|
+
else
|
56
|
+
raise e
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# ==== Returns
|
63
|
+
# String:: The failure message.
|
64
|
+
def failure_message
|
65
|
+
"expected the following element's content to #{content_message}:\n#{@element}"
|
66
|
+
end
|
67
|
+
|
68
|
+
# ==== Returns
|
69
|
+
# String:: The failure message to be displayed in negative matches.
|
70
|
+
def negative_failure_message
|
71
|
+
"expected the following element's content to not #{content_message}:\n#{@element}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def content_message
|
75
|
+
case @content
|
76
|
+
when String
|
77
|
+
"include \"#{@content}\""
|
78
|
+
when Regexp
|
79
|
+
"match #{@content.inspect}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Matches the contents of an HTML document with
|
85
|
+
# whatever string is supplied
|
86
|
+
#
|
87
|
+
# ---
|
88
|
+
# @api public
|
89
|
+
def contain(content)
|
90
|
+
HasContent.new(content)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Webrat
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class HaveSelector < HaveXpath #:nodoc:
|
5
|
+
|
6
|
+
# ==== Returns
|
7
|
+
# String:: The failure message.
|
8
|
+
def failure_message
|
9
|
+
"expected following text to match selector #{@expected}:\n#{@document}"
|
10
|
+
end
|
11
|
+
|
12
|
+
# ==== Returns
|
13
|
+
# String:: The failure message to be displayed in negative matches.
|
14
|
+
def negative_failure_message
|
15
|
+
"expected following text to not match selector #{@expected}:\n#{@document}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def query
|
19
|
+
Nokogiri::CSS::Parser.parse(*super).map { |ast| ast.to_xpath }
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
# Matches HTML content against a CSS 3 selector.
|
25
|
+
#
|
26
|
+
# ==== Parameters
|
27
|
+
# expected<String>:: The CSS selector to look for.
|
28
|
+
#
|
29
|
+
# ==== Returns
|
30
|
+
# HaveSelector:: A new have selector matcher.
|
31
|
+
# ---
|
32
|
+
# @api public
|
33
|
+
def have_selector(expected)
|
34
|
+
HaveSelector.new(expected)
|
35
|
+
end
|
36
|
+
alias_method :match_selector, :have_selector
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Webrat
|
2
|
+
|
3
|
+
module HaveTagMatcher
|
4
|
+
|
5
|
+
class HaveTag < ::Webrat::Matchers::HaveSelector #:nodoc:
|
6
|
+
|
7
|
+
# ==== Returns
|
8
|
+
# String:: The failure message.
|
9
|
+
def failure_message
|
10
|
+
"expected following output to contain a #{tag_inspect} tag:\n#{@document}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# ==== Returns
|
14
|
+
# String:: The failure message to be displayed in negative matches.
|
15
|
+
def negative_failure_message
|
16
|
+
"expected following output to omit a #{tag_inspect}:\n#{@document}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def tag_inspect
|
20
|
+
options = @expected.last.dup
|
21
|
+
content = options.delete(:content)
|
22
|
+
|
23
|
+
html = "<#{@expected.first}"
|
24
|
+
options.each do |k,v|
|
25
|
+
html << " #{k}='#{v}'"
|
26
|
+
end
|
27
|
+
|
28
|
+
if content
|
29
|
+
html << ">#{content}</#{@expected.first}>"
|
30
|
+
else
|
31
|
+
html << "/>"
|
32
|
+
end
|
33
|
+
|
34
|
+
html
|
35
|
+
end
|
36
|
+
|
37
|
+
def query
|
38
|
+
options = @expected.last.dup
|
39
|
+
selector = @expected.first.to_s
|
40
|
+
|
41
|
+
selector << ":contains('#{options.delete(:content)}')" if options[:content]
|
42
|
+
|
43
|
+
options.each do |key, value|
|
44
|
+
selector << "[#{key}='#{value}']"
|
45
|
+
end
|
46
|
+
|
47
|
+
Nokogiri::CSS::Parser.parse(selector).map { |ast| ast.to_xpath }
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def have_tag(name, attributes = {})
|
53
|
+
HaveTag.new([name, attributes])
|
54
|
+
end
|
55
|
+
alias_method :match_tag, :have_tag
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Webrat
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class HaveXpath #:nodoc:
|
5
|
+
def initialize(expected, &block)
|
6
|
+
@expected = expected
|
7
|
+
@block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(stringlike)
|
11
|
+
if defined?(Nokogiri::XML)
|
12
|
+
matches_nokogiri?(stringlike)
|
13
|
+
else
|
14
|
+
matches_rexml?(stringlike)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def matches_rexml?(stringlike)
|
19
|
+
@document = rexml_document(stringlike)
|
20
|
+
|
21
|
+
query.all? do |q|
|
22
|
+
matched = REXML::XPath.match(@document, q)
|
23
|
+
matched.any? && (!block_given? || matched.all?(&@block))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def matches_nokogiri?(stringlike)
|
28
|
+
@document = Webrat.nokogiri_document(stringlike)
|
29
|
+
@document.xpath(*query).any?
|
30
|
+
end
|
31
|
+
|
32
|
+
def rexml_document(stringlike)
|
33
|
+
stringlike = stringlike.body.to_s if stringlike.respond_to?(:body)
|
34
|
+
|
35
|
+
case stringlike
|
36
|
+
when REXML::Document
|
37
|
+
stringlike.root
|
38
|
+
when REXML::Node
|
39
|
+
stringlike
|
40
|
+
when StringIO, String
|
41
|
+
begin
|
42
|
+
REXML::Document.new(stringlike.to_s).root
|
43
|
+
rescue REXML::ParseException => e
|
44
|
+
if e.message.include?("second root element")
|
45
|
+
REXML::Document.new("<fake-root-element>#{stringlike}</fake-root-element>").root
|
46
|
+
else
|
47
|
+
raise e
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def query
|
54
|
+
[@expected].flatten.compact
|
55
|
+
end
|
56
|
+
|
57
|
+
# ==== Returns
|
58
|
+
# String:: The failure message.
|
59
|
+
def failure_message
|
60
|
+
"expected following text to match xpath #{@expected}:\n#{@document}"
|
61
|
+
end
|
62
|
+
|
63
|
+
# ==== Returns
|
64
|
+
# String:: The failure message to be displayed in negative matches.
|
65
|
+
def negative_failure_message
|
66
|
+
"expected following text to not match xpath #{@expected}:\n#{@document}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Matches HTML content against an XPath query
|
71
|
+
#
|
72
|
+
# ==== Parameters
|
73
|
+
# expected<String>:: The XPath query to look for.
|
74
|
+
#
|
75
|
+
# ==== Returns
|
76
|
+
# HaveXpath:: A new have xpath matcher.
|
77
|
+
# ---
|
78
|
+
# @api public
|
79
|
+
def have_xpath(expected)
|
80
|
+
HaveXpath.new(expected)
|
81
|
+
end
|
82
|
+
alias_method :match_xpath, :have_xpath
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|