revo-webrat 0.7.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 (215) hide show
  1. data/.document +4 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +19 -0
  4. data/History.txt +437 -0
  5. data/MIT-LICENSE.txt +19 -0
  6. data/README.rdoc +85 -0
  7. data/Rakefile +145 -0
  8. data/Thorfile +118 -0
  9. data/install.rb +1 -0
  10. data/lib/webrat.rb +18 -0
  11. data/lib/webrat/adapters/mechanize.rb +78 -0
  12. data/lib/webrat/adapters/merb.rb +11 -0
  13. data/lib/webrat/adapters/rack.rb +25 -0
  14. data/lib/webrat/adapters/rails.rb +73 -0
  15. data/lib/webrat/adapters/sinatra.rb +9 -0
  16. data/lib/webrat/core.rb +13 -0
  17. data/lib/webrat/core/configuration.rb +106 -0
  18. data/lib/webrat/core/elements/area.rb +31 -0
  19. data/lib/webrat/core/elements/element.rb +33 -0
  20. data/lib/webrat/core/elements/field.rb +487 -0
  21. data/lib/webrat/core/elements/form.rb +103 -0
  22. data/lib/webrat/core/elements/label.rb +31 -0
  23. data/lib/webrat/core/elements/link.rb +93 -0
  24. data/lib/webrat/core/elements/select_option.rb +48 -0
  25. data/lib/webrat/core/locators.rb +20 -0
  26. data/lib/webrat/core/locators/area_locator.rb +38 -0
  27. data/lib/webrat/core/locators/button_locator.rb +54 -0
  28. data/lib/webrat/core/locators/field_by_id_locator.rb +37 -0
  29. data/lib/webrat/core/locators/field_labeled_locator.rb +56 -0
  30. data/lib/webrat/core/locators/field_locator.rb +25 -0
  31. data/lib/webrat/core/locators/field_named_locator.rb +41 -0
  32. data/lib/webrat/core/locators/form_locator.rb +19 -0
  33. data/lib/webrat/core/locators/label_locator.rb +34 -0
  34. data/lib/webrat/core/locators/link_locator.rb +74 -0
  35. data/lib/webrat/core/locators/locator.rb +20 -0
  36. data/lib/webrat/core/locators/select_option_locator.rb +59 -0
  37. data/lib/webrat/core/logging.rb +23 -0
  38. data/lib/webrat/core/matchers.rb +4 -0
  39. data/lib/webrat/core/matchers/have_content.rb +68 -0
  40. data/lib/webrat/core/matchers/have_selector.rb +74 -0
  41. data/lib/webrat/core/matchers/have_tag.rb +21 -0
  42. data/lib/webrat/core/matchers/have_xpath.rb +122 -0
  43. data/lib/webrat/core/methods.rb +64 -0
  44. data/lib/webrat/core/mime.rb +18 -0
  45. data/lib/webrat/core/save_and_open_page.rb +28 -0
  46. data/lib/webrat/core/scope.rb +365 -0
  47. data/lib/webrat/core/session.rb +302 -0
  48. data/lib/webrat/core/xml.rb +76 -0
  49. data/lib/webrat/core_extensions/blank.rb +58 -0
  50. data/lib/webrat/core_extensions/deprecate.rb +8 -0
  51. data/lib/webrat/core_extensions/detect_mapped.rb +12 -0
  52. data/lib/webrat/core_extensions/meta_class.rb +6 -0
  53. data/lib/webrat/core_extensions/nil_to_param.rb +5 -0
  54. data/lib/webrat/core_extensions/tcp_socket.rb +27 -0
  55. data/lib/webrat/integrations/merb.rb +10 -0
  56. data/lib/webrat/integrations/rails.rb +25 -0
  57. data/lib/webrat/integrations/rspec-rails.rb +11 -0
  58. data/lib/webrat/integrations/selenium.rb +11 -0
  59. data/lib/webrat/merb.rb +9 -0
  60. data/lib/webrat/rspec-rails.rb +2 -0
  61. data/lib/webrat/selenium.rb +70 -0
  62. data/lib/webrat/selenium/application_server_factory.rb +40 -0
  63. data/lib/webrat/selenium/application_servers.rb +5 -0
  64. data/lib/webrat/selenium/application_servers/base.rb +46 -0
  65. data/lib/webrat/selenium/application_servers/external.rb +26 -0
  66. data/lib/webrat/selenium/application_servers/merb.rb +50 -0
  67. data/lib/webrat/selenium/application_servers/rails.rb +44 -0
  68. data/lib/webrat/selenium/application_servers/sinatra.rb +37 -0
  69. data/lib/webrat/selenium/location_strategy_javascript/button.js +19 -0
  70. data/lib/webrat/selenium/location_strategy_javascript/label.js +42 -0
  71. data/lib/webrat/selenium/location_strategy_javascript/webrat.js +6 -0
  72. data/lib/webrat/selenium/location_strategy_javascript/webratlink.js +32 -0
  73. data/lib/webrat/selenium/location_strategy_javascript/webratlinkwithin.js +15 -0
  74. data/lib/webrat/selenium/location_strategy_javascript/webratselectwithoption.js +5 -0
  75. data/lib/webrat/selenium/matchers.rb +4 -0
  76. data/lib/webrat/selenium/matchers/have_content.rb +78 -0
  77. data/lib/webrat/selenium/matchers/have_selector.rb +57 -0
  78. data/lib/webrat/selenium/matchers/have_tag.rb +72 -0
  79. data/lib/webrat/selenium/matchers/have_xpath.rb +53 -0
  80. data/lib/webrat/selenium/selenium_extensions.js +6 -0
  81. data/lib/webrat/selenium/selenium_rc_server.rb +86 -0
  82. data/lib/webrat/selenium/selenium_session.rb +267 -0
  83. data/lib/webrat/selenium/silence_stream.rb +18 -0
  84. data/revo-webrat.gemspec +351 -0
  85. data/spec/fakes/test_adapter.rb +33 -0
  86. data/spec/integration/mechanize/Rakefile +7 -0
  87. data/spec/integration/mechanize/config.ru +2 -0
  88. data/spec/integration/mechanize/sample_app.rb +20 -0
  89. data/spec/integration/mechanize/spec/mechanize_spec.rb +22 -0
  90. data/spec/integration/mechanize/spec/spec_helper.rb +29 -0
  91. data/spec/integration/merb/.gitignore +21 -0
  92. data/spec/integration/merb/Rakefile +35 -0
  93. data/spec/integration/merb/app/controllers/application.rb +2 -0
  94. data/spec/integration/merb/app/controllers/exceptions.rb +13 -0
  95. data/spec/integration/merb/app/controllers/testing.rb +27 -0
  96. data/spec/integration/merb/app/views/exceptions/not_acceptable.html.erb +63 -0
  97. data/spec/integration/merb/app/views/exceptions/not_found.html.erb +47 -0
  98. data/spec/integration/merb/app/views/layout/application.html.erb +12 -0
  99. data/spec/integration/merb/app/views/testing/show_form.html.erb +27 -0
  100. data/spec/integration/merb/app/views/testing/upload.html.erb +9 -0
  101. data/spec/integration/merb/config/environments/development.rb +15 -0
  102. data/spec/integration/merb/config/environments/rake.rb +11 -0
  103. data/spec/integration/merb/config/environments/test.rb +14 -0
  104. data/spec/integration/merb/config/init.rb +25 -0
  105. data/spec/integration/merb/config/rack.rb +11 -0
  106. data/spec/integration/merb/config/router.rb +34 -0
  107. data/spec/integration/merb/spec/spec.opts +1 -0
  108. data/spec/integration/merb/spec/spec_helper.rb +26 -0
  109. data/spec/integration/merb/spec/webrat_spec.rb +39 -0
  110. data/spec/integration/merb/tasks/merb.thor/app_script.rb +31 -0
  111. data/spec/integration/merb/tasks/merb.thor/common.rb +64 -0
  112. data/spec/integration/merb/tasks/merb.thor/gem_ext.rb +124 -0
  113. data/spec/integration/merb/tasks/merb.thor/main.thor +150 -0
  114. data/spec/integration/merb/tasks/merb.thor/ops.rb +93 -0
  115. data/spec/integration/merb/tasks/merb.thor/utils.rb +40 -0
  116. data/spec/integration/rack/Rakefile +5 -0
  117. data/spec/integration/rack/app.rb +89 -0
  118. data/spec/integration/rack/test/helper.rb +21 -0
  119. data/spec/integration/rack/test/webrat_rack_test.rb +73 -0
  120. data/spec/integration/rails/.gitignore +3 -0
  121. data/spec/integration/rails/Rakefile +30 -0
  122. data/spec/integration/rails/app/controllers/application_controller.rb +15 -0
  123. data/spec/integration/rails/app/controllers/buttons_controller.rb +7 -0
  124. data/spec/integration/rails/app/controllers/fields_controller.rb +4 -0
  125. data/spec/integration/rails/app/controllers/links_controller.rb +7 -0
  126. data/spec/integration/rails/app/controllers/webrat_controller.rb +46 -0
  127. data/spec/integration/rails/app/helpers/buttons_helper.rb +2 -0
  128. data/spec/integration/rails/app/helpers/fields_helper.rb +2 -0
  129. data/spec/integration/rails/app/helpers/links_helper.rb +2 -0
  130. data/spec/integration/rails/app/views/buttons/show.html.erb +9 -0
  131. data/spec/integration/rails/app/views/fields/show.html.erb +9 -0
  132. data/spec/integration/rails/app/views/links/show.html.erb +5 -0
  133. data/spec/integration/rails/app/views/webrat/before_redirect_form.html.erb +4 -0
  134. data/spec/integration/rails/app/views/webrat/buttons.html.erb +9 -0
  135. data/spec/integration/rails/app/views/webrat/form.html.erb +28 -0
  136. data/spec/integration/rails/app/views/webrat/within.html.erb +3 -0
  137. data/spec/integration/rails/config/boot.rb +109 -0
  138. data/spec/integration/rails/config/environment.rb +12 -0
  139. data/spec/integration/rails/config/environments/development.rb +17 -0
  140. data/spec/integration/rails/config/environments/selenium.rb +22 -0
  141. data/spec/integration/rails/config/environments/test.rb +22 -0
  142. data/spec/integration/rails/config/initializers/inflections.rb +10 -0
  143. data/spec/integration/rails/config/initializers/mime_types.rb +5 -0
  144. data/spec/integration/rails/config/initializers/new_rails_defaults.rb +17 -0
  145. data/spec/integration/rails/config/locales/en.yml +5 -0
  146. data/spec/integration/rails/config/routes.rb +19 -0
  147. data/spec/integration/rails/public/404.html +30 -0
  148. data/spec/integration/rails/public/422.html +30 -0
  149. data/spec/integration/rails/public/500.html +33 -0
  150. data/spec/integration/rails/script/about +4 -0
  151. data/spec/integration/rails/script/console +3 -0
  152. data/spec/integration/rails/script/dbconsole +3 -0
  153. data/spec/integration/rails/script/destroy +3 -0
  154. data/spec/integration/rails/script/generate +3 -0
  155. data/spec/integration/rails/script/performance/benchmarker +3 -0
  156. data/spec/integration/rails/script/performance/profiler +3 -0
  157. data/spec/integration/rails/script/performance/request +3 -0
  158. data/spec/integration/rails/script/plugin +3 -0
  159. data/spec/integration/rails/script/process/inspector +3 -0
  160. data/spec/integration/rails/script/process/reaper +3 -0
  161. data/spec/integration/rails/script/process/spawner +3 -0
  162. data/spec/integration/rails/script/runner +3 -0
  163. data/spec/integration/rails/script/server +3 -0
  164. data/spec/integration/rails/test/integration/button_click_test.rb +66 -0
  165. data/spec/integration/rails/test/integration/fill_in_test.rb +24 -0
  166. data/spec/integration/rails/test/integration/link_click_test.rb +27 -0
  167. data/spec/integration/rails/test/integration/webrat_test.rb +123 -0
  168. data/spec/integration/rails/test/test_helper.rb +27 -0
  169. data/spec/integration/sinatra/Rakefile +5 -0
  170. data/spec/integration/sinatra/classic_app.rb +64 -0
  171. data/spec/integration/sinatra/modular_app.rb +16 -0
  172. data/spec/integration/sinatra/test/classic_app_test.rb +37 -0
  173. data/spec/integration/sinatra/test/modular_app_test.rb +18 -0
  174. data/spec/integration/sinatra/test/test_helper.rb +17 -0
  175. data/spec/private/core/configuration_spec.rb +87 -0
  176. data/spec/private/core/field_spec.rb +83 -0
  177. data/spec/private/core/link_spec.rb +24 -0
  178. data/spec/private/core/session_spec.rb +195 -0
  179. data/spec/private/mechanize/mechanize_adapter_spec.rb +73 -0
  180. data/spec/private/nokogiri_spec.rb +77 -0
  181. data/spec/private/rails/attaches_file_spec.rb +81 -0
  182. data/spec/private/rails/rails_adapter_spec.rb +79 -0
  183. data/spec/private/selenium/application_servers/rails_spec.rb +26 -0
  184. data/spec/public/basic_auth_spec.rb +35 -0
  185. data/spec/public/check_spec.rb +191 -0
  186. data/spec/public/choose_spec.rb +118 -0
  187. data/spec/public/click_area_spec.rb +106 -0
  188. data/spec/public/click_button_spec.rb +494 -0
  189. data/spec/public/click_link_spec.rb +532 -0
  190. data/spec/public/fill_in_spec.rb +224 -0
  191. data/spec/public/locators/field_by_xpath_spec.rb +19 -0
  192. data/spec/public/locators/field_labeled_spec.rb +172 -0
  193. data/spec/public/locators/field_with_id_spec.rb +16 -0
  194. data/spec/public/matchers/contain_spec.rb +107 -0
  195. data/spec/public/matchers/have_selector_spec.rb +146 -0
  196. data/spec/public/matchers/have_tag_spec.rb +39 -0
  197. data/spec/public/matchers/have_xpath_spec.rb +136 -0
  198. data/spec/public/reload_spec.rb +10 -0
  199. data/spec/public/save_and_open_spec.rb +49 -0
  200. data/spec/public/select_date_spec.rb +112 -0
  201. data/spec/public/select_datetime_spec.rb +137 -0
  202. data/spec/public/select_spec.rb +455 -0
  203. data/spec/public/select_time_spec.rb +100 -0
  204. data/spec/public/selenium/application_server_factory_spec.rb +49 -0
  205. data/spec/public/selenium/application_servers/external_spec.rb +12 -0
  206. data/spec/public/selenium/selenium_session_spec.rb +37 -0
  207. data/spec/public/set_hidden_field_spec.rb +5 -0
  208. data/spec/public/submit_form_spec.rb +5 -0
  209. data/spec/public/visit_spec.rb +58 -0
  210. data/spec/public/within_spec.rb +177 -0
  211. data/spec/rcov.opts +1 -0
  212. data/spec/spec.opts +2 -0
  213. data/spec/spec_helper.rb +54 -0
  214. data/vendor/selenium-server.jar +0 -0
  215. metadata +392 -0
@@ -0,0 +1,11 @@
1
+ require "webrat/integrations/merb"
2
+
3
+ module Webrat
4
+ class MerbAdapter < RackAdapter #:nodoc:
5
+ def initialize(context=nil)
6
+ app = context.respond_to?(:app) ?
7
+ context.app : Merb::Rack::Application.new
8
+ super(Rack::Test::Session.new(Rack::MockSession.new(app, "www.example.com")))
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ require "rack/test"
2
+
3
+ module Webrat
4
+ class RackAdapter
5
+ extend Forwardable
6
+
7
+ def_delegators :@session, :get, :post, :put, :delete
8
+
9
+ def initialize(session) #:nodoc:
10
+ @session = session
11
+ end
12
+
13
+ def response_body
14
+ response.body
15
+ end
16
+
17
+ def response_code
18
+ response.status
19
+ end
20
+
21
+ def response
22
+ @session.last_response
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,73 @@
1
+ require "webrat/integrations/rails"
2
+ require "action_controller/record_identifier"
3
+
4
+ module Webrat
5
+ class RailsAdapter #:nodoc:
6
+ include ActionController::RecordIdentifier
7
+
8
+ attr_reader :integration_session
9
+
10
+ def initialize(session)
11
+ @integration_session = session
12
+ end
13
+
14
+ def get(url, data, headers = nil)
15
+ do_request(:get, url, data, headers)
16
+ end
17
+
18
+ def post(url, data, headers = nil)
19
+ do_request(:post, url, data, headers)
20
+ end
21
+
22
+ def put(url, data, headers = nil)
23
+ do_request(:put, url, data, headers)
24
+ end
25
+
26
+ def delete(url, data, headers = nil)
27
+ do_request(:delete, url, data, headers)
28
+ end
29
+
30
+ def response_body
31
+ response.body
32
+ end
33
+
34
+ def response_code
35
+ response.code.to_i
36
+ end
37
+
38
+ def xml_content_type?
39
+ response.headers["Content-Type"].to_s =~ /xml/
40
+ end
41
+
42
+ protected
43
+
44
+ def do_request(http_method, url, data, headers) #:nodoc:
45
+ update_protocol(url)
46
+ integration_session.send(http_method, normalize_url(url), data, headers)
47
+ end
48
+
49
+ # remove protocol, host and anchor
50
+ def normalize_url(href) #:nodoc:
51
+ uri = URI.parse(href)
52
+ normalized_url = []
53
+ normalized_url << "#{uri.scheme}://" if uri.scheme
54
+ normalized_url << uri.host if uri.host
55
+ normalized_url << ":#{uri.port}" if uri.port && ![80,443].include?(uri.port)
56
+ normalized_url << uri.path if uri.path
57
+ normalized_url << "?#{uri.query}" if uri.query
58
+ normalized_url.join
59
+ end
60
+
61
+ def update_protocol(href) #:nodoc:
62
+ if href =~ /^https:/
63
+ integration_session.https!(true)
64
+ elsif href =~ /^http:/
65
+ integration_session.https!(false)
66
+ end
67
+ end
68
+
69
+ def response #:nodoc:
70
+ integration_session.response
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,9 @@
1
+ module Webrat
2
+ class SinatraAdapter < RackAdapter
3
+ def initialize(context)
4
+ app = context.respond_to?(:app) ? context.app : Sinatra::Application
5
+
6
+ super(Rack::Test::Session.new(Rack::MockSession.new(app, "www.example.com")))
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ require "webrat/core/configuration"
2
+ require "webrat/core/xml"
3
+ require "webrat/core/logging"
4
+ require "webrat/core/elements/form"
5
+ require "webrat/core/scope"
6
+ require "webrat/core/elements/link"
7
+ require "webrat/core/elements/area"
8
+ require "webrat/core/elements/label"
9
+ require "webrat/core/elements/select_option"
10
+ require "webrat/core/session"
11
+ require "webrat/core/methods"
12
+ require "webrat/core/matchers"
13
+ require "webrat/core/save_and_open_page"
@@ -0,0 +1,106 @@
1
+ require "webrat/core_extensions/deprecate"
2
+ require "pathname"
3
+
4
+ module Webrat
5
+
6
+ # Configures Webrat. If this is not done, Webrat will be created
7
+ # with all of the default settings.
8
+ def self.configure(configuration = Webrat.configuration)
9
+ yield configuration if block_given?
10
+ @@configuration = configuration
11
+ end
12
+
13
+ def self.configuration # :nodoc:
14
+ @@configuration ||= Webrat::Configuration.new
15
+ end
16
+
17
+ # Webrat can be configured using the Webrat.configure method. For example:
18
+ #
19
+ # Webrat.configure do |config|
20
+ # config.mode = :sinatra
21
+ # end
22
+ class Configuration
23
+
24
+ # Webrat's mode, set automatically when requiring webrat/rails, webrat/merb, etc.
25
+ attr_reader :mode # :nodoc:
26
+
27
+ # Save and open pages with error status codes (500-599) in a browser? Defualts to true.
28
+ attr_writer :open_error_files
29
+
30
+ # Save and open page storage directory, defaults to "tmp" under current directory if exists, otherwise current directory
31
+ attr_accessor :saved_pages_dir
32
+
33
+ # Which rails environment should the selenium tests be run in? Defaults to test.
34
+ attr_accessor :application_environment
35
+ webrat_deprecate :selenium_environment, :application_environment
36
+ webrat_deprecate :selenium_environment=, :application_environment=
37
+
38
+ # Which port is the application running on for selenium testing? Defaults to 3001.
39
+ attr_accessor :application_port
40
+ webrat_deprecate :selenium_port, :application_port
41
+ webrat_deprecate :selenium_port=, :application_port=
42
+
43
+ # Which port should selenium use to access the application. Defaults to application_port
44
+ attr_writer :application_port_for_selenium
45
+
46
+ def application_port_for_selenium
47
+ @application_port_for_selenium || self.application_port
48
+ end
49
+
50
+ # Which underlying app framework we're testing with selenium
51
+ attr_accessor :application_framework
52
+
53
+ # Which server the application is running on for selenium testing? Defaults to localhost
54
+ attr_accessor :application_address
55
+
56
+ # Which server Selenium server is running on. Defaults to nil(server starts in webrat process and runs locally)
57
+ attr_accessor :selenium_server_address
58
+
59
+ # Which server Selenium port is running on. Defaults to 4444
60
+ attr_accessor :selenium_server_port
61
+
62
+ # Set the key that Selenium uses to determine the browser running. Default *firefox
63
+ attr_accessor :selenium_browser_key
64
+
65
+ # Set the timeout for waiting for the browser process to start
66
+ attr_accessor :selenium_browser_startup_timeout
67
+
68
+ # How many redirects to the same URL should be halted as an infinite redirect
69
+ # loop? Defaults to 10
70
+ attr_accessor :infinite_redirect_limit
71
+
72
+ def initialize # :nodoc:
73
+ self.open_error_files = true
74
+ self.application_environment = :test
75
+ self.application_port = 3001
76
+ self.application_address = 'localhost'
77
+ self.application_framework = :rails
78
+ self.selenium_server_port = 4444
79
+ self.infinite_redirect_limit = 10
80
+ self.selenium_browser_key = '*firefox'
81
+ self.selenium_browser_startup_timeout = 5
82
+
83
+ tmp_dir = Pathname.new(Dir.pwd).join("tmp")
84
+ self.saved_pages_dir = tmp_dir.exist? ? tmp_dir : Dir.pwd
85
+ end
86
+
87
+ def open_error_files? #:nodoc:
88
+ @open_error_files ? true : false
89
+ end
90
+
91
+ # Allows setting of webrat's mode, valid modes are:
92
+ # :rails, :selenium, :rack, :sinatra, :mechanize, :merb
93
+ def mode=(mode)
94
+ @mode = mode.to_sym
95
+
96
+ begin
97
+ require("webrat/integrations/#{mode}")
98
+ rescue LoadError
99
+ # Only some modes have integration code that needs to
100
+ # be loaded, so this is OK
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ end
@@ -0,0 +1,31 @@
1
+ require "webrat/core/elements/element"
2
+
3
+ module Webrat
4
+ class Area < Element #:nodoc:
5
+
6
+ def self.xpath_search
7
+ [".//area"]
8
+ end
9
+
10
+ def click(method = nil, options = {})
11
+ @session.request_page(absolute_href, :get, {})
12
+ end
13
+
14
+ protected
15
+
16
+ def href
17
+ @element["href"]
18
+ end
19
+
20
+ def absolute_href
21
+ if href =~ /^\?/
22
+ "#{@session.current_url}#{href}"
23
+ elsif href !~ %r{^https?://[\w|.]+(/.*)} && (href !~ /^\//)
24
+ "#{@session.current_url}/#{href}"
25
+ else
26
+ href
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ module Webrat
2
+
3
+ class Element # :nodoc:
4
+
5
+ def self.load_all(session, dom)
6
+ dom.xpath(*xpath_search).map do |element|
7
+ load(session, element)
8
+ end
9
+ end
10
+
11
+ def self.load(session, element)
12
+ return nil if element.nil?
13
+ session.elements[element.path] ||= self.new(session, element)
14
+ end
15
+
16
+ attr_reader :element
17
+
18
+ def initialize(session, element)
19
+ @session = session
20
+ @element = element
21
+ end
22
+
23
+ def path
24
+ @element.path
25
+ end
26
+
27
+ def inspect
28
+ "#<#{self.class} @element=#{element.inspect}>"
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,487 @@
1
+ require "cgi"
2
+ require "webrat/core_extensions/blank"
3
+ require "webrat/core_extensions/nil_to_param"
4
+
5
+ require "webrat/core/elements/element"
6
+
7
+ module Webrat
8
+ # Raised when Webrat is asked to manipulate a disabled form field
9
+ class DisabledFieldError < WebratError
10
+ end
11
+
12
+ class Field < Element #:nodoc:
13
+ attr_reader :value
14
+
15
+ def self.xpath_search
16
+ [".//button", ".//input", ".//textarea", ".//select"]
17
+ end
18
+
19
+ def self.xpath_search_excluding_hidden
20
+ [".//button", ".//input[ @type != 'hidden']", ".//textarea", ".//select"]
21
+ end
22
+
23
+ def self.field_classes
24
+ @field_classes || []
25
+ end
26
+
27
+ def self.inherited(klass)
28
+ @field_classes ||= []
29
+ @field_classes << klass
30
+ # raise args.inspect
31
+ end
32
+
33
+ def self.load(session, element)
34
+ return nil if element.nil?
35
+ session.elements[element.path] ||= field_class(element).new(session, element)
36
+ end
37
+
38
+ def self.field_class(element)
39
+ case element.name
40
+ when "button" then ButtonField
41
+ when "select"
42
+ if element.attributes["multiple"].nil?
43
+ SelectField
44
+ else
45
+ MultipleSelectField
46
+ end
47
+ when "textarea" then TextareaField
48
+ else
49
+ case element["type"]
50
+ when "checkbox" then CheckboxField
51
+ when "hidden" then HiddenField
52
+ when "radio" then RadioField
53
+ when "password" then PasswordField
54
+ when "file" then FileField
55
+ when "reset" then ResetField
56
+ when "submit" then ButtonField
57
+ when "button" then ButtonField
58
+ when "image" then ButtonField
59
+ else TextField
60
+ end
61
+ end
62
+ end
63
+
64
+ def initialize(*args)
65
+ super
66
+ @value = default_value
67
+ end
68
+
69
+ def label_text
70
+ return nil if labels.empty?
71
+ labels.first.text
72
+ end
73
+
74
+ def id
75
+ @element["id"]
76
+ end
77
+
78
+ def disabled?
79
+ @element.attributes.has_key?("disabled") && @element["disabled"] != 'false'
80
+ end
81
+
82
+ def raise_error_if_disabled
83
+ return unless disabled?
84
+ raise DisabledFieldError.new("Cannot interact with disabled form element (#{self})")
85
+ end
86
+
87
+ def to_param
88
+ return nil if disabled?
89
+
90
+ params = case Webrat.configuration.mode
91
+ when :rails
92
+ parse_rails_request_params("#{name}=#{escaped_value}")
93
+ when :merb
94
+ ::Merb::Parse.query("#{name}=#{escaped_value}")
95
+ else
96
+ { name => [*@value].first.to_s }
97
+ end
98
+
99
+ unescape_params(params)
100
+ end
101
+
102
+ def set(value)
103
+ @value = value
104
+ end
105
+
106
+ def unset
107
+ @value = default_value
108
+ end
109
+
110
+ protected
111
+
112
+ def parse_rails_request_params(params)
113
+ if defined?(ActionController::AbstractRequest)
114
+ ActionController::AbstractRequest.parse_query_parameters(params)
115
+ elsif defined?(ActionController::UrlEncodedPairParser)
116
+ # For Rails > 2.2
117
+ ActionController::UrlEncodedPairParser.parse_query_parameters(params)
118
+ else
119
+ # For Rails > 2.3
120
+ Rack::Utils.parse_nested_query(params)
121
+ end
122
+ end
123
+
124
+ def form
125
+ Form.load(@session, form_element)
126
+ end
127
+
128
+ def form_element
129
+ parent = @element.parent
130
+
131
+ while parent.respond_to?(:parent)
132
+ return parent if parent.name == 'form'
133
+ parent = parent.parent
134
+ end
135
+ end
136
+
137
+ def name
138
+ @element["name"]
139
+ end
140
+
141
+ def escaped_value
142
+ CGI.escape(@value.to_s)
143
+ end
144
+
145
+ # Because we have to escape it before sending it to the above case statement,
146
+ # we have to make sure we unescape each value when it gets back so assertions
147
+ # involving characters like <, >, and & work as expected
148
+ def unescape_params(params)
149
+ case params.class.name
150
+ when 'Hash', 'Mash'
151
+ params.each { |key,value| params[key] = unescape_params(value) }
152
+ params
153
+ when 'Array'
154
+ params.collect { |value| unescape_params(value) }
155
+ else
156
+ CGI.unescapeHTML(params)
157
+ end
158
+ end
159
+
160
+ def labels
161
+ @labels ||= label_elements.map do |element|
162
+ Label.load(@session, element)
163
+ end
164
+ end
165
+
166
+ def label_elements
167
+ return @label_elements unless @label_elements.nil?
168
+ @label_elements = []
169
+
170
+ parent = @element.parent
171
+ while parent.respond_to?(:parent)
172
+ if parent.name == 'label'
173
+ @label_elements.push parent
174
+ break
175
+ end
176
+ parent = parent.parent
177
+ end
178
+
179
+ unless id.blank?
180
+ @label_elements += form.element.xpath(".//label[@for = '#{id}']")
181
+ end
182
+
183
+ @label_elements
184
+ end
185
+
186
+ def default_value
187
+ @element["value"]
188
+ end
189
+
190
+ def replace_param_value(params, oval, nval)
191
+ output = Hash.new
192
+ params.each do |key, value|
193
+ case value
194
+ when Hash
195
+ value = replace_param_value(value, oval, nval)
196
+ when Array
197
+ value = value.map { |o| o == oval ? nval : oval }
198
+ when oval
199
+ value = nval
200
+ end
201
+ output[key] = value
202
+ end
203
+ output
204
+ end
205
+ end
206
+
207
+ class ButtonField < Field #:nodoc:
208
+
209
+ def self.xpath_search
210
+ [".//button", ".//input[@type = 'submit']", ".//input[@type = 'button']", ".//input[@type = 'image']"]
211
+ end
212
+
213
+ def to_param
214
+ return nil if @value.nil?
215
+ super
216
+ end
217
+
218
+ def default_value
219
+ nil
220
+ end
221
+
222
+ def click
223
+ raise_error_if_disabled
224
+ set(@element["value"]) unless @element["name"].blank?
225
+ form.submit
226
+ end
227
+
228
+ end
229
+
230
+ class HiddenField < Field #:nodoc:
231
+
232
+ def self.xpath_search
233
+ ".//input[@type = 'hidden']"
234
+ end
235
+
236
+ def to_param
237
+ if collection_name?
238
+ super
239
+ else
240
+ checkbox_with_same_name = form.field_named(name, CheckboxField)
241
+
242
+ if checkbox_with_same_name.to_param.blank?
243
+ super
244
+ else
245
+ nil
246
+ end
247
+ end
248
+ end
249
+
250
+ protected
251
+
252
+ def collection_name?
253
+ name =~ /\[\]/
254
+ end
255
+
256
+ end
257
+
258
+ class CheckboxField < Field #:nodoc:
259
+
260
+ def self.xpath_search
261
+ ".//input[@type = 'checkbox']"
262
+ end
263
+
264
+ def to_param
265
+ return nil if @value.nil?
266
+ super
267
+ end
268
+
269
+ def check
270
+ raise_error_if_disabled
271
+ set(@element["value"] || "on")
272
+ end
273
+
274
+ def checked?
275
+ @element["checked"] == "checked"
276
+ end
277
+
278
+ def uncheck
279
+ raise_error_if_disabled
280
+ set(nil)
281
+ end
282
+
283
+ protected
284
+
285
+ def default_value
286
+ if @element["checked"] == "checked"
287
+ @element["value"] || "on"
288
+ else
289
+ nil
290
+ end
291
+ end
292
+
293
+ end
294
+
295
+ class PasswordField < Field #:nodoc:
296
+
297
+ def self.xpath_search
298
+ ".//input[@type = 'password']"
299
+ end
300
+
301
+ end
302
+
303
+ class RadioField < Field #:nodoc:
304
+
305
+ def self.xpath_search
306
+ ".//input[@type = 'radio']"
307
+ end
308
+
309
+ def to_param
310
+ return nil if @value.nil?
311
+ super
312
+ end
313
+
314
+ def choose
315
+ raise_error_if_disabled
316
+ other_options.each do |option|
317
+ option.set(nil)
318
+ end
319
+
320
+ set(@element["value"] || "on")
321
+ end
322
+
323
+ def checked?
324
+ @element["checked"] == "checked"
325
+ end
326
+
327
+ protected
328
+
329
+ def other_options
330
+ form.fields.select { |f| f.name == name }
331
+ end
332
+
333
+ def default_value
334
+ if @element["checked"] == "checked"
335
+ @element["value"] || "on"
336
+ else
337
+ nil
338
+ end
339
+ end
340
+
341
+ end
342
+
343
+ class TextareaField < Field #:nodoc:
344
+
345
+ def self.xpath_search
346
+ ".//textarea"
347
+ end
348
+
349
+ protected
350
+
351
+ def default_value
352
+ @element.inner_html
353
+ end
354
+
355
+ end
356
+
357
+ class FileField < Field #:nodoc:
358
+
359
+ def self.xpath_search
360
+ ".//input[@type = 'file']"
361
+ end
362
+
363
+ attr_accessor :content_type
364
+
365
+ def set(value, content_type = nil)
366
+ super(value)
367
+ @content_type = content_type
368
+ end
369
+
370
+ def to_param
371
+ if @value.nil?
372
+ super
373
+ else
374
+ replace_param_value(super, @value, test_uploaded_file)
375
+ end
376
+ end
377
+
378
+ protected
379
+
380
+ def test_uploaded_file
381
+ case Webrat.configuration.mode
382
+ when :rails
383
+ if content_type
384
+ ActionController::TestUploadedFile.new(@value, content_type)
385
+ else
386
+ ActionController::TestUploadedFile.new(@value)
387
+ end
388
+ when :rack, :merb
389
+ Rack::Test::UploadedFile.new(@value, content_type)
390
+ end
391
+ end
392
+
393
+ end
394
+
395
+ class TextField < Field #:nodoc:
396
+ def self.xpath_search
397
+ [".//input[@type = 'text']", ".//input[not(@type)]"]
398
+ end
399
+ end
400
+
401
+ class ResetField < Field #:nodoc:
402
+ def self.xpath_search
403
+ [".//input[@type = 'reset']"]
404
+ end
405
+ end
406
+
407
+ class SelectField < Field #:nodoc:
408
+
409
+ def self.xpath_search
410
+ [".//select[not(@multiple)]"]
411
+ end
412
+
413
+ def options
414
+ @options ||= SelectOption.load_all(@session, @element)
415
+ end
416
+
417
+ def unset(value)
418
+ @value = nil
419
+ end
420
+
421
+ protected
422
+
423
+ def default_value
424
+ selected_options = @element.xpath(".//option[@selected = 'selected']")
425
+ selected_options = @element.xpath(".//option[position() = 1]") if selected_options.empty?
426
+
427
+ selected_options.map do |option|
428
+ return "" if option.nil?
429
+ option["value"] || option.inner_html
430
+ end.uniq.first
431
+ end
432
+
433
+ end
434
+
435
+ class MultipleSelectField < Field #:nodoc:
436
+
437
+ def self.xpath_search
438
+ [".//select[@multiple='multiple']"]
439
+ end
440
+
441
+ def options
442
+ @options ||= SelectOption.load_all(@session, @element)
443
+ end
444
+
445
+ def set(value)
446
+ @value << value
447
+ end
448
+
449
+ def unset(value)
450
+ @value.delete(value)
451
+ end
452
+
453
+ # We have to overide how the uri string is formed when dealing with multiples
454
+ # Where normally a select field might produce name=value with a multiple,
455
+ # we need to form something like name[]=value1&name[]=value2
456
+ def to_param
457
+ return nil if disabled?
458
+
459
+ uri_string = @value.collect {|value| "#{name}=#{CGI.escape(value)}"}.join("&")
460
+ params = case Webrat.configuration.mode
461
+ when :rails
462
+ parse_rails_request_params(uri_string)
463
+ when :merb
464
+ ::Merb::Parse.query(uri_string)
465
+ else
466
+ { name => @value }
467
+ end
468
+
469
+ unescape_params(params)
470
+ end
471
+
472
+ protected
473
+
474
+ # Overwrite SelectField definition because we don't want to select the first option
475
+ # (mutliples don't select the first option unlike their non multiple versions)
476
+ def default_value
477
+ selected_options = @element.xpath(".//option[@selected = 'selected']")
478
+
479
+ selected_options.map do |option|
480
+ return "" if option.nil?
481
+ option["value"] || option.inner_html
482
+ end.uniq
483
+ end
484
+
485
+ end
486
+
487
+ end