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.
Files changed (60) hide show
  1. data/History.txt +76 -14
  2. data/README.txt +40 -36
  3. data/Rakefile +80 -18
  4. data/TODO.txt +9 -3
  5. data/init.rb +1 -1
  6. data/lib/webrat.rb +30 -5
  7. data/lib/webrat/core.rb +12 -0
  8. data/lib/webrat/core/area.rb +44 -0
  9. data/lib/webrat/core/field.rb +332 -0
  10. data/lib/webrat/core/flunk.rb +7 -0
  11. data/lib/webrat/core/form.rb +130 -0
  12. data/lib/webrat/core/label.rb +18 -0
  13. data/lib/webrat/core/link.rb +101 -0
  14. data/lib/webrat/core/locators.rb +92 -0
  15. data/lib/webrat/core/logging.rb +25 -0
  16. data/lib/webrat/core/matchers.rb +4 -0
  17. data/lib/webrat/core/matchers/have_content.rb +94 -0
  18. data/lib/webrat/core/matchers/have_selector.rb +39 -0
  19. data/lib/webrat/core/matchers/have_tag.rb +58 -0
  20. data/lib/webrat/core/matchers/have_xpath.rb +85 -0
  21. data/lib/webrat/core/methods.rb +44 -0
  22. data/lib/webrat/core/mime.rb +29 -0
  23. data/lib/webrat/core/nokogiri.rb +42 -0
  24. data/lib/webrat/core/scope.rb +208 -0
  25. data/lib/webrat/core/select_option.rb +29 -0
  26. data/lib/webrat/core/session.rb +188 -0
  27. data/lib/webrat/core_extensions/blank.rb +58 -0
  28. data/lib/webrat/core_extensions/deprecate.rb +8 -0
  29. data/lib/webrat/core_extensions/detect_mapped.rb +12 -0
  30. data/lib/webrat/core_extensions/hash_with_indifferent_access.rb +131 -0
  31. data/lib/webrat/core_extensions/meta_class.rb +6 -0
  32. data/lib/webrat/core_extensions/nil_to_param.rb +5 -0
  33. data/lib/webrat/mechanize.rb +28 -0
  34. data/lib/webrat/merb.rb +75 -0
  35. data/lib/webrat/rack.rb +24 -0
  36. data/lib/webrat/rails.rb +102 -0
  37. data/lib/webrat/rails/redirect_actions.rb +18 -0
  38. data/lib/webrat/selenium.rb +3 -0
  39. data/lib/webrat/selenium/location_strategy_javascript/button.js +12 -0
  40. data/lib/webrat/selenium/location_strategy_javascript/label.js +16 -0
  41. data/lib/webrat/selenium/location_strategy_javascript/webrat.js +5 -0
  42. data/lib/webrat/selenium/location_strategy_javascript/webratlink.js +9 -0
  43. data/lib/webrat/selenium/location_strategy_javascript/webratlinkwithin.js +15 -0
  44. data/lib/webrat/selenium/location_strategy_javascript/webratselectwithoption.js +5 -0
  45. data/lib/webrat/selenium/selenium_extensions.js +6 -0
  46. data/lib/webrat/selenium/selenium_session.rb +137 -0
  47. data/lib/webrat/sinatra.rb +19 -0
  48. metadata +66 -52
  49. data/Manifest.txt +0 -20
  50. data/lib/webrat/rails_extensions.rb +0 -27
  51. data/lib/webrat/session.rb +0 -523
  52. data/test/checks_test.rb +0 -121
  53. data/test/chooses_test.rb +0 -74
  54. data/test/clicks_button_test.rb +0 -308
  55. data/test/clicks_link_test.rb +0 -193
  56. data/test/fills_in_test.rb +0 -139
  57. data/test/helper.rb +0 -21
  58. data/test/reloads_test.rb +0 -26
  59. data/test/selects_test.rb +0 -93
  60. 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,4 @@
1
+ require "webrat/core/matchers/have_xpath"
2
+ require "webrat/core/matchers/have_selector"
3
+ require "webrat/core/matchers/have_tag"
4
+ require "webrat/core/matchers/have_content"
@@ -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