webrat 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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