bjeanes-capybara 0.3.1

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