bjeanes-capybara 0.3.1

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 (88) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +87 -0
  3. data/README.rdoc +404 -0
  4. data/Rakefile +29 -0
  5. data/config.ru +6 -0
  6. data/lib/capybara.rb +53 -0
  7. data/lib/capybara/cucumber.rb +32 -0
  8. data/lib/capybara/driver/base.rb +37 -0
  9. data/lib/capybara/driver/celerity_driver.rb +135 -0
  10. data/lib/capybara/driver/culerity_driver.rb +25 -0
  11. data/lib/capybara/driver/rack_test_driver.rb +244 -0
  12. data/lib/capybara/driver/selenium_driver.rb +137 -0
  13. data/lib/capybara/dsl.rb +60 -0
  14. data/lib/capybara/node.rb +69 -0
  15. data/lib/capybara/rails.rb +11 -0
  16. data/lib/capybara/save_and_open_page.rb +33 -0
  17. data/lib/capybara/searchable.rb +53 -0
  18. data/lib/capybara/server.rb +111 -0
  19. data/lib/capybara/session.rb +274 -0
  20. data/lib/capybara/wait_until.rb +23 -0
  21. data/lib/capybara/xpath.rb +176 -0
  22. data/script/console +10 -0
  23. data/script/destroy +14 -0
  24. data/script/generate +14 -0
  25. data/spec/capybara_spec.rb +18 -0
  26. data/spec/driver/celerity_driver_spec.rb +17 -0
  27. data/spec/driver/culerity_driver_spec.rb +13 -0
  28. data/spec/driver/rack_test_driver_spec.rb +12 -0
  29. data/spec/driver/remote_culerity_driver_spec.rb +26 -0
  30. data/spec/driver/remote_selenium_driver_spec.rb +18 -0
  31. data/spec/driver/selenium_driver_spec.rb +12 -0
  32. data/spec/drivers_spec.rb +139 -0
  33. data/spec/dsl/all_spec.rb +69 -0
  34. data/spec/dsl/attach_file_spec.rb +64 -0
  35. data/spec/dsl/check_spec.rb +39 -0
  36. data/spec/dsl/choose_spec.rb +26 -0
  37. data/spec/dsl/click_button_spec.rb +218 -0
  38. data/spec/dsl/click_link_spec.rb +108 -0
  39. data/spec/dsl/click_spec.rb +24 -0
  40. data/spec/dsl/current_url_spec.rb +8 -0
  41. data/spec/dsl/fill_in_spec.rb +91 -0
  42. data/spec/dsl/find_button_spec.rb +16 -0
  43. data/spec/dsl/find_by_id_spec.rb +16 -0
  44. data/spec/dsl/find_field_spec.rb +22 -0
  45. data/spec/dsl/find_link_spec.rb +17 -0
  46. data/spec/dsl/find_spec.rb +57 -0
  47. data/spec/dsl/has_button_spec.rb +32 -0
  48. data/spec/dsl/has_content_spec.rb +101 -0
  49. data/spec/dsl/has_css_spec.rb +107 -0
  50. data/spec/dsl/has_field_spec.rb +96 -0
  51. data/spec/dsl/has_link_spec.rb +33 -0
  52. data/spec/dsl/has_xpath_spec.rb +123 -0
  53. data/spec/dsl/locate_spec.rb +59 -0
  54. data/spec/dsl/select_spec.rb +71 -0
  55. data/spec/dsl/uncheck_spec.rb +21 -0
  56. data/spec/dsl/within_spec.rb +153 -0
  57. data/spec/dsl_spec.rb +140 -0
  58. data/spec/fixtures/capybara.jpg +0 -0
  59. data/spec/fixtures/test_file.txt +1 -0
  60. data/spec/public/jquery-ui.js +35 -0
  61. data/spec/public/jquery.js +19 -0
  62. data/spec/public/test.js +30 -0
  63. data/spec/save_and_open_page_spec.rb +43 -0
  64. data/spec/searchable_spec.rb +61 -0
  65. data/spec/server_spec.rb +47 -0
  66. data/spec/session/celerity_session_spec.rb +27 -0
  67. data/spec/session/culerity_session_spec.rb +25 -0
  68. data/spec/session/rack_test_session_spec.rb +25 -0
  69. data/spec/session/selenium_session_spec.rb +25 -0
  70. data/spec/session_spec.rb +77 -0
  71. data/spec/session_with_headers_support_spec.rb +13 -0
  72. data/spec/session_with_javascript_support_spec.rb +182 -0
  73. data/spec/session_without_headers_support_spec.rb +15 -0
  74. data/spec/session_without_javascript_support_spec.rb +15 -0
  75. data/spec/spec_helper.rb +27 -0
  76. data/spec/test_app.rb +71 -0
  77. data/spec/views/buttons.erb +4 -0
  78. data/spec/views/fieldsets.erb +29 -0
  79. data/spec/views/form.erb +226 -0
  80. data/spec/views/postback.erb +13 -0
  81. data/spec/views/tables.erb +122 -0
  82. data/spec/views/with_html.erb +38 -0
  83. data/spec/views/with_js.erb +34 -0
  84. data/spec/views/with_scope.erb +36 -0
  85. data/spec/views/with_simple_html.erb +1 -0
  86. data/spec/wait_until_spec.rb +28 -0
  87. data/spec/xpath_spec.rb +271 -0
  88. metadata +239 -0
@@ -0,0 +1,137 @@
1
+ require 'selenium-webdriver'
2
+
3
+ class Capybara::Driver::Selenium < Capybara::Driver::Base
4
+ class Node < Capybara::Node
5
+ def text
6
+ node.text
7
+ end
8
+
9
+ def [](name)
10
+ if name == :value
11
+ node.value
12
+ else
13
+ node.attribute(name)
14
+ end
15
+ rescue Selenium::WebDriver::Error::WebDriverError
16
+ nil
17
+ end
18
+
19
+ def set(value)
20
+ if tag_name == 'textarea' or (tag_name == 'input' and %w(text password hidden file).include?(type))
21
+ node.clear
22
+ node.send_keys(value.to_s)
23
+ elsif tag_name == 'input' and type == 'radio'
24
+ node.select
25
+ elsif tag_name == 'input' and type == 'checkbox'
26
+ node.toggle
27
+ end
28
+ end
29
+
30
+ def select(option)
31
+ option_node = node.find_element(:xpath, ".//option[text()='#{option}']") || node.find_element(:xpath, ".//option[contains(.,'#{option}')]")
32
+ option_node.select
33
+ rescue
34
+ options = node.find_elements(:xpath, "//option").map { |o| "'#{o.text}'" }.join(', ')
35
+ raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
36
+ end
37
+
38
+ def unselect(option)
39
+ if node['multiple'] != 'multiple'
40
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box."
41
+ end
42
+
43
+ begin
44
+ option_node = node.find_element(:xpath, ".//option[text()='#{option}']") || node.find_element(:xpath, ".//option[contains(.,'#{option}')]")
45
+ option_node.clear
46
+ rescue
47
+ options = node.find_elements(:xpath, "//option").map { |o| "'#{o.text}'" }.join(', ')
48
+ raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
49
+ end
50
+ end
51
+
52
+ def click
53
+ node.click
54
+ end
55
+
56
+ def drag_to(element)
57
+ node.drag_and_drop_on(element.node)
58
+ end
59
+
60
+ def tag_name
61
+ node.tag_name
62
+ end
63
+
64
+ def visible?
65
+ node.displayed? and node.displayed? != "false"
66
+ end
67
+
68
+ def trigger(event)
69
+ end
70
+
71
+ private
72
+
73
+ def type
74
+ self[:type]
75
+ end
76
+
77
+ end
78
+
79
+ attr_reader :app, :rack_server
80
+
81
+ def self.driver
82
+ unless @driver
83
+ @driver = Selenium::WebDriver.for :firefox
84
+ at_exit do
85
+ @driver.quit
86
+ end
87
+ end
88
+ @driver
89
+ end
90
+
91
+ def initialize(app)
92
+ @app = app
93
+ @rack_server = Capybara::Server.new(@app)
94
+ @rack_server.boot if Capybara.run_server
95
+ end
96
+
97
+ def visit(path)
98
+ browser.navigate.to(url(path))
99
+ end
100
+
101
+ def source
102
+ browser.page_source
103
+ end
104
+
105
+ def body
106
+ browser.page_source
107
+ end
108
+
109
+ def current_url
110
+ browser.current_url
111
+ end
112
+
113
+ def find(selector)
114
+ browser.find_elements(:xpath, selector).map { |node| Node.new(self, node) }
115
+ end
116
+
117
+ def wait?; true; end
118
+
119
+ def evaluate_script(script)
120
+ browser.execute_script "return #{script}"
121
+ end
122
+
123
+ def browser
124
+ self.class.driver
125
+ end
126
+
127
+ def cleanup!
128
+ browser.manage.delete_all_cookies
129
+ end
130
+
131
+ private
132
+
133
+ def url(path)
134
+ rack_server.url(path)
135
+ end
136
+
137
+ end
@@ -0,0 +1,60 @@
1
+ require 'capybara'
2
+
3
+ module Capybara
4
+ class << self
5
+ attr_writer :default_driver, :current_driver, :javascript_driver
6
+
7
+ attr_accessor :app
8
+
9
+ def default_driver
10
+ @default_driver || :rack_test
11
+ end
12
+
13
+ def current_driver
14
+ @current_driver || default_driver
15
+ end
16
+ alias_method :mode, :current_driver
17
+
18
+ def javascript_driver
19
+ @javascript_driver || :selenium
20
+ end
21
+
22
+ def use_default_driver
23
+ @current_driver = nil
24
+ end
25
+
26
+ def current_session
27
+ session_pool["#{current_driver}#{app.object_id}"] ||= Capybara::Session.new(current_driver, app)
28
+ end
29
+
30
+ def current_session?
31
+ session_pool.has_key?("#{current_driver}#{app.object_id}")
32
+ end
33
+
34
+ def reset_sessions!
35
+ session_pool.each { |mode, session| session.cleanup! }
36
+ @session_pool = nil
37
+ end
38
+
39
+ private
40
+
41
+ def session_pool
42
+ @session_pool ||= {}
43
+ end
44
+ end
45
+
46
+ extend(self)
47
+
48
+ def page
49
+ Capybara.current_session
50
+ end
51
+
52
+ Session::DSL_METHODS.each do |method|
53
+ class_eval <<-RUBY, __FILE__, __LINE__+1
54
+ def #{method}(*args, &block)
55
+ page.#{method}(*args, &block)
56
+ end
57
+ RUBY
58
+ end
59
+
60
+ end
@@ -0,0 +1,69 @@
1
+ module Capybara
2
+ class Node
3
+ include Searchable
4
+
5
+ attr_reader :driver, :node
6
+
7
+ def initialize(driver, node)
8
+ @driver = driver
9
+ @node = node
10
+ end
11
+
12
+ def text
13
+ raise NotImplementedError
14
+ end
15
+
16
+ def [](name)
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def value
21
+ self[:value]
22
+ end
23
+
24
+ def set(value)
25
+ raise NotImplementedError
26
+ end
27
+
28
+ def select(option)
29
+ raise NotImplementedError
30
+ end
31
+
32
+ def unselect(option)
33
+ raise NotImplementedError
34
+ end
35
+
36
+ def click
37
+ raise NotImplementedError
38
+ end
39
+
40
+ def drag_to(element)
41
+ raise NotImplementedError
42
+ end
43
+
44
+ def tag_name
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def visible?
49
+ raise NotImplementedError
50
+ end
51
+
52
+ def path
53
+ raise NotSupportedByDriverError
54
+ end
55
+
56
+ def trigger(event)
57
+ raise NotSupportedByDriverError
58
+ end
59
+
60
+ private
61
+
62
+ def all_unfiltered(locator)
63
+ nodes = XPath.wrap(locator).scope(path).paths.map do |path|
64
+ driver.find(path)
65
+ end.flatten
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,11 @@
1
+ require 'capybara'
2
+ require 'capybara/dsl'
3
+
4
+ Capybara.app = Rack::Builder.new do
5
+ map "/" do
6
+ use Rails::Rack::Static
7
+ run ActionController::Dispatcher.new
8
+ end
9
+ end.to_app
10
+
11
+ Capybara.asset_root = Rails.root.join('public')
@@ -0,0 +1,33 @@
1
+ module Capybara
2
+ module SaveAndOpenPage
3
+ extend(self)
4
+
5
+ def save_and_open_page(html)
6
+ name="capybara-#{Time.new.strftime("%Y%m%d%H%M%S")}.html"
7
+
8
+ FileUtils.touch(name) unless File.exist?(name)
9
+
10
+ tempfile = File.new(name,'w')
11
+ tempfile.write(rewrite_css_and_image_references(html))
12
+ tempfile.close
13
+
14
+ open_in_browser(tempfile.path)
15
+ end
16
+
17
+ def open_in_browser(path) # :nodoc
18
+ require "launchy"
19
+ Launchy::Browser.run(path)
20
+ rescue LoadError
21
+ warn "Sorry, you need to install launchy to open pages: `gem install launchy`"
22
+ end
23
+
24
+ def rewrite_css_and_image_references(response_html) # :nodoc:
25
+ return response_html unless Capybara.asset_root
26
+ directories = Dir.new(Capybara.asset_root).entries.inject([]) do |list, name|
27
+ list << name if File.directory?(name) and not name.to_s =~ /^\./
28
+ list
29
+ end
30
+ response_html.gsub(/("|')\/(#{directories.join('|')})/, '\1' + Capybara.asset_root.to_s + '/\2')
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ module Capybara
2
+ module Searchable
3
+ def find(*args)
4
+ all(*args).first
5
+ end
6
+
7
+ def find_field(locator)
8
+ find(:xpath, XPath.field(locator))
9
+ end
10
+ alias_method :field_labeled, :find_field
11
+
12
+ def find_link(locator)
13
+ find(:xpath, XPath.link(locator))
14
+ end
15
+
16
+ def find_button(locator)
17
+ find(:xpath, XPath.button(locator))
18
+ end
19
+
20
+ def find_by_id(id)
21
+ find(:css, "##{id}")
22
+ end
23
+
24
+ def all(*args)
25
+ options = if args.last.is_a?(Hash) then args.pop else {} end
26
+ if args[1].nil?
27
+ kind, locator = Capybara.default_selector, args.first
28
+ else
29
+ kind, locator = args
30
+ end
31
+ locator = XPath.from_css(locator) if kind == :css
32
+
33
+ results = all_unfiltered(locator)
34
+
35
+ if options[:text]
36
+ results = results.select { |n| n.text.match(options[:text]) }
37
+ end
38
+
39
+ if options[:visible] or Capybara.ignore_hidden_elements
40
+ results = results.select { |n| n.visible? }
41
+ end
42
+
43
+ results
44
+ end
45
+
46
+ private
47
+
48
+ def all_unfiltered(locator)
49
+ raise "Must be overridden"
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,111 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'rack'
4
+
5
+ class Capybara::Server
6
+ class Identify
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ if env["PATH_INFO"] == "/__identify__"
13
+ [200, {}, @app.object_id.to_s]
14
+ else
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+
20
+ attr_reader :app, :port
21
+
22
+ def initialize(app)
23
+ @app = app
24
+ end
25
+
26
+ def host
27
+ "localhost"
28
+ end
29
+
30
+ def url(path)
31
+ if path =~ /^http/
32
+ path
33
+ else
34
+ (Capybara.app_host || "http://#{host}:#{port}") + path.to_s
35
+ end
36
+ end
37
+
38
+ def responsive?
39
+ is_running_on_port?(port)
40
+ end
41
+
42
+ def handler
43
+ begin
44
+ require 'rack/handler/thin'
45
+ Rack::Handler::Thin
46
+ rescue LoadError
47
+ begin
48
+ require 'rack/handler/mongrel'
49
+ Rack::Handler::Mongrel
50
+ rescue LoadError
51
+ require 'rack/handler/webrick'
52
+ Rack::Handler::WEBrick
53
+ end
54
+ end
55
+ end
56
+
57
+ def boot
58
+ find_available_port
59
+ Capybara.log "application has already booted" and return self if responsive?
60
+ Capybara.log "booting Rack applicartion on port #{port}"
61
+
62
+ Timeout.timeout(10) do
63
+ Thread.new do
64
+ handler.run(Identify.new(@app), :Port => port, :AccessLog => [])
65
+ end
66
+ Capybara.log "checking if application has booted"
67
+
68
+ loop do
69
+ Capybara.log("application has booted") and break if responsive?
70
+ Capybara.log("waiting for application to boot...")
71
+ sleep 0.5
72
+ end
73
+ end
74
+ self
75
+ rescue Timeout::Error
76
+ Capybara.log "Rack application timed out during boot"
77
+ exit
78
+ end
79
+
80
+ private
81
+
82
+ def find_available_port
83
+ @port = 9887
84
+ @port += 1 while is_port_open?(@port) and not is_running_on_port?(@port)
85
+ end
86
+
87
+ def is_running_on_port?(tested_port)
88
+ res = Net::HTTP.start(host, tested_port) { |http| http.get('/__identify__') }
89
+
90
+ if res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
91
+ return res.body == @app.object_id.to_s
92
+ end
93
+ rescue Errno::ECONNREFUSED
94
+ return false
95
+ end
96
+
97
+ def is_port_open?(tested_port)
98
+ Timeout::timeout(1) do
99
+ begin
100
+ s = TCPSocket.new(host, tested_port)
101
+ s.close
102
+ return true
103
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
104
+ return false
105
+ end
106
+ end
107
+ rescue Timeout::Error
108
+ return false
109
+ end
110
+
111
+ end