capybara 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +42 -0
  3. data/README.rdoc +208 -0
  4. data/Rakefile +33 -0
  5. data/examples/webcat.rb +36 -0
  6. data/lib/capybara.rb +30 -0
  7. data/lib/capybara/core_ext/tcp_socket.rb +26 -0
  8. data/lib/capybara/cucumber.rb +32 -0
  9. data/lib/capybara/driver/culerity_driver.rb +83 -0
  10. data/lib/capybara/driver/firewatir_driver.rb +66 -0
  11. data/lib/capybara/driver/rack_test_driver.rb +151 -0
  12. data/lib/capybara/driver/safariwatir_driver.rb +67 -0
  13. data/lib/capybara/driver/selenium_driver.rb +93 -0
  14. data/lib/capybara/dsl.rb +58 -0
  15. data/lib/capybara/node.rb +36 -0
  16. data/lib/capybara/rails.rb +11 -0
  17. data/lib/capybara/save_and_open_page.rb +25 -0
  18. data/lib/capybara/server.rb +53 -0
  19. data/lib/capybara/session.rb +190 -0
  20. data/script/console +10 -0
  21. data/script/destroy +14 -0
  22. data/script/generate +14 -0
  23. data/spec/driver/culerity_driver_spec.rb +10 -0
  24. data/spec/driver/firewatir_driver_spec.rb +10 -0
  25. data/spec/driver/rack_test_driver_spec.rb +9 -0
  26. data/spec/driver/safariwarit_driver_spec.rb +10 -0
  27. data/spec/driver/selenium_driver_spec.rb +10 -0
  28. data/spec/drivers_spec.rb +72 -0
  29. data/spec/dsl_spec.rb +139 -0
  30. data/spec/fixtures/test_file.txt +1 -0
  31. data/spec/public/jquery.js +19 -0
  32. data/spec/session/culerity_session_spec.rb +23 -0
  33. data/spec/session/rack_test_session_spec.rb +23 -0
  34. data/spec/session/selenium_session_spec.rb +23 -0
  35. data/spec/session_spec.rb +611 -0
  36. data/spec/spec_helper.rb +10 -0
  37. data/spec/test_app.rb +67 -0
  38. data/spec/views/form.erb +117 -0
  39. data/spec/views/with_html.erb +19 -0
  40. data/spec/views/with_js.erb +20 -0
  41. data/spec/views/with_scope.erb +36 -0
  42. data/spec/views/with_simple_html.erb +1 -0
  43. metadata +164 -0
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2009-11-04
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -0,0 +1,42 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ examples/webcat.rb
6
+ lib/capybara.rb
7
+ lib/capybara/core_ext/tcp_socket.rb
8
+ lib/capybara/cucumber.rb
9
+ lib/capybara/driver/culerity_driver.rb
10
+ lib/capybara/driver/firewatir_driver.rb
11
+ lib/capybara/driver/rack_test_driver.rb
12
+ lib/capybara/driver/safariwatir_driver.rb
13
+ lib/capybara/driver/selenium_driver.rb
14
+ lib/capybara/dsl.rb
15
+ lib/capybara/node.rb
16
+ lib/capybara/rails.rb
17
+ lib/capybara/save_and_open_page.rb
18
+ lib/capybara/server.rb
19
+ lib/capybara/session.rb
20
+ script/console
21
+ script/destroy
22
+ script/generate
23
+ spec/driver/culerity_driver_spec.rb
24
+ spec/driver/firewatir_driver_spec.rb
25
+ spec/driver/rack_test_driver_spec.rb
26
+ spec/driver/safariwarit_driver_spec.rb
27
+ spec/driver/selenium_driver_spec.rb
28
+ spec/drivers_spec.rb
29
+ spec/dsl_spec.rb
30
+ spec/fixtures/test_file.txt
31
+ spec/public/jquery.js
32
+ spec/session/culerity_session_spec.rb
33
+ spec/session/rack_test_session_spec.rb
34
+ spec/session/selenium_session_spec.rb
35
+ spec/session_spec.rb
36
+ spec/spec_helper.rb
37
+ spec/test_app.rb
38
+ spec/views/form.erb
39
+ spec/views/with_html.erb
40
+ spec/views/with_js.erb
41
+ spec/views/with_scope.erb
42
+ spec/views/with_simple_html.erb
@@ -0,0 +1,208 @@
1
+ = capybara
2
+
3
+ * http://github.com/jnicklas/capybara
4
+
5
+ == Description:
6
+
7
+ Capybara aims to simplify the process of integration testing Rack applications,
8
+ such as Rails, Sinatra or Merb. It is inspired by and aims to replace Webrat as
9
+ a DSL for interacting with a webapplication. It is agnostic about the driver
10
+ running your tests and currently comes bundled with rack-test, Culerity and
11
+ Selenium support built in.
12
+
13
+ == Disclaimer:
14
+
15
+ Capybara is alpha level software, don't use it unless you're prepared to get
16
+ your hands dirty.
17
+
18
+ == Using Capybara with Cucumber
19
+
20
+ Capybara is built to work nicely with Cucumber. The API is very similar to
21
+ Webrat, so if you know Webrat you should feel right at home. Remove any
22
+ references to Webrat from your <tt>env.rb</tt>, if you're using Rails, make sure to set
23
+
24
+ Cucumber::Rails::World.use_transactional_fixtures = false
25
+
26
+ Capybara uses DatabaseCleaner to truncate the database. Require Capybara in your
27
+ env.rb. For Rails do this:
28
+
29
+ require 'capybara/rails'
30
+ require 'capybara/cucumber'
31
+
32
+ For other frameworks, you'll need to set the Rack app manually:
33
+
34
+ require 'capybara/cucumber'
35
+ Capybara.app = MyRackApp
36
+
37
+ Now you can use it in your steps:
38
+
39
+ When /I sign in/ do
40
+ within("//form[@id='session']") do
41
+ fill_in 'Login', :with => 'user@example.com'
42
+ fill_in 'Password', :with => 'password'
43
+ end
44
+ click_link 'Sign in'
45
+ end
46
+
47
+ == Default and current driver
48
+
49
+ You can set up a default driver for your features. For example if you'd prefer
50
+ to run Selenium, you could do:
51
+
52
+ require 'capybara/rails'
53
+ require 'capybara/cucumber'
54
+ Capybara.default_driver = :selenium
55
+
56
+ You can change the driver temporarily:
57
+
58
+ Capybara.current_driver = :culerity
59
+ Capybara.use_default_driver
60
+
61
+ == Cucumber and Tags
62
+
63
+ Capybara sets up some [tags](http://wiki.github.com/aslakhellesoy/cucumber/tags)
64
+ for you to use in Cucumber. Often you'll want to use run only some scenarios
65
+ with a driver that supports JavaScript, Capybara makes this easy: simply tag the
66
+ scenario (or feature) with <tt>@javascript</tt>:
67
+
68
+ @javascript
69
+ Scenario: do something AJAXy
70
+ When I click the AJAX link
71
+ ...
72
+
73
+ You can change which driver Capybara uses for JavaScript:
74
+
75
+ Capybara.javascript_driver = :culerity
76
+
77
+ There are also explicit <tt>@selenium</tt>, <tt>@culerity</tt> and <tt>@rack_test</tt> tags set up
78
+ for you.
79
+
80
+ == The API
81
+
82
+ Navigation:
83
+
84
+ visit – The only way to get to anywhere.
85
+
86
+ Scoping:
87
+
88
+ within – Takes a block which executes in the given scope
89
+
90
+ Interaction:
91
+
92
+ click_link
93
+ click_button
94
+ fill_in – Currently broken with password fields under Culerity
95
+ choose
96
+ check
97
+ uncheck – Currently broken under Culerity
98
+ attach_file
99
+ select
100
+
101
+ Querying:
102
+
103
+ body
104
+ has_xpath? – Checks if given XPath exists, takes text and count options
105
+ has_css? – Checks if given CSS exists, takes text and count options
106
+ has_content? – Checks if the given content is on the page
107
+ find_field
108
+ find_link
109
+ find_button
110
+ field_labeled
111
+
112
+ Debugging:
113
+
114
+ save_and_open_page
115
+
116
+ == Using the DSL outside cucumber
117
+
118
+ You can mix the DSL into any context, for example you could use it in RSpec
119
+ examples. Just load the dsl and include it anywhere:
120
+
121
+ require 'capybara'
122
+ require 'capybara/dsl'
123
+
124
+ include Capybara
125
+ Capybara.default_driver = :culerity
126
+
127
+ within("//form[@id='session']") do
128
+ fill_in 'Login', :with => 'user@example.com'
129
+ fill_in 'Password', :with => 'password'
130
+ end
131
+ click_link 'Sign in'
132
+
133
+ == Using the sessions manually
134
+
135
+ For ultimate control, you can instantiate and use a session manually.
136
+
137
+ require 'capybara'
138
+
139
+ session = Capybara::Session.new(:culerity, my_rack_app)
140
+ session.within("//form[@id='session']") do
141
+ session.fill_in 'Login', :with => 'user@example.com'
142
+ session.fill_in 'Password', :with => 'password'
143
+ end
144
+ session.click_link 'Sign in'
145
+
146
+ == Install:
147
+
148
+ Capybara is hosted on Gemcutter, install it with:
149
+
150
+ sudo gem install capybara
151
+
152
+ == Gotchas:
153
+
154
+ * Everything is *case sensitive*. Capybara heavily relies on XPath, which
155
+ doesn't support case insensitive searches.
156
+
157
+ * The <tt>have_tag</tt> and <tt>have_text</tt> matchers in RSpec-Rails are not
158
+ supported. You should use <tt>page.should have_css('#header p')</tt>,
159
+ <tt>page.should have_xpath('//ul/li')</tt> and <tt>page.should
160
+ have_content('Monkey')</tt> instead.
161
+
162
+ * Unchecking checkboxes and filling in password fields is currently broken under
163
+ Culerity.
164
+
165
+ * Domain names (including subdomains) don't work under rack-test. Since it's a
166
+ pain to set up subdomains for the other drivers anyway, you should consider an
167
+ alternate solution. You might use
168
+ [default_url_options](https://gist.github.com/643a758320a2926bd2ed) in Rails
169
+ for example.
170
+
171
+ * The <tt>set_hidden_field</tt> method from Webrat is not implemented, since it doesn't
172
+ work in any of the browser based drivers (Culerity, Selenium)
173
+
174
+ * Access to session, request and response from the test is not possible. Maybe
175
+ we'll do response headers at some point in the future, but the others really
176
+ shouldn't be touched in an integration test anyway.
177
+
178
+ * Access to Rails specific stuff (such as <tt>controller</tt>) is unavailable,
179
+ since we're not using Rails' integration testing.
180
+
181
+ * <tt><a href="#"></tt> Will cause problems under rack-test, please do
182
+ <tt><a href="/same/url#"></tt> instead. You can achieve this in Rails with
183
+ <tt>link_to('foo', :anchor => '')</tt>
184
+
185
+ == License:
186
+
187
+ (The MIT License)
188
+
189
+ Copyright (c) 2009 Jonas Nicklas
190
+
191
+ Permission is hereby granted, free of charge, to any person obtaining
192
+ a copy of this software and associated documentation files (the
193
+ 'Software'), to deal in the Software without restriction, including
194
+ without limitation the rights to use, copy, modify, merge, publish,
195
+ distribute, sublicense, and/or sell copies of the Software, and to
196
+ permit persons to whom the Software is furnished to do so, subject to
197
+ the following conditions:
198
+
199
+ The above copyright notice and this permission notice shall be
200
+ included in all copies or substantial portions of the Software.
201
+
202
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
203
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
204
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
205
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
206
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
207
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
208
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/capybara'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'capybara' do
14
+ self.developer 'Jonas Nicklas', 'jonas.nicklas@gmail.com'
15
+ self.rubyforge_name = self.name # TODO this is default value
16
+ self.readme_file = 'README.rdoc'
17
+ self.extra_rdoc_files = ['README.rdoc']
18
+
19
+ self.extra_deps = [
20
+ ['nokogiri', '>= 1.3.3'],
21
+ ['culerity', '>= 0.2.3'],
22
+ ['selenium-webdriver', '>= 0.0.3'],
23
+ ['rack', '>= 1.0.0'],
24
+ ['database_cleaner', '>= 0.2.3']
25
+ ]
26
+ end
27
+
28
+ require 'newgem/tasks'
29
+ Dir['tasks/**/*.rake'].each { |t| load t }
30
+
31
+ # TODO - want other tests/tasks run by default? Add them to the list
32
+ # remove_task :default
33
+ # task :default => [:spec, :features]
@@ -0,0 +1,36 @@
1
+ session = Capybara::Session.new('http://localhost:3000')
2
+
3
+ session.visit '/'
4
+
5
+ session.driver.trigger :click, '//div[@id=foo]//a'
6
+ session.driver.trigger :mouseover, '#foo a.bar' # will be ignored by drivers who do not support it
7
+
8
+ nodelist = session.find 'li#foo a'
9
+ nodelist.empty?
10
+ nodelist.first.tag_name # => 'a'
11
+ nodelist.first.text # => 'a cute link'
12
+ nodelist.first.html # => 'a <em>cute</em> link'
13
+ nodelist.first.attributes # => { :href => '/blah' }
14
+ nodelist.first.trigger :click
15
+
16
+ session.request.url # => '/blah'
17
+ session.response.ok? # => true
18
+
19
+ # fancy stuff, just builds on the stuff above!
20
+
21
+ session.click_link 'a cute link'
22
+ session.click_button 'an awesome button'
23
+ session.within '#foo' do
24
+ click_link 'a cute link'
25
+ end
26
+ session.fill_in 'foo', :with => 'bar'
27
+ session.choose 'Monkey'
28
+ session.check 'I am awesome'
29
+ session.wait_for '#fooo"
30
+
31
+ # In cuke:
32
+
33
+ When 'I am awesome' do
34
+ page.check 'I am awesome'
35
+ page.click_button 'FooBar'
36
+ end
@@ -0,0 +1,30 @@
1
+ require 'nokogiri'
2
+
3
+ module Capybara
4
+ VERSION = '0.1'
5
+
6
+ class CapybaraError < StandardError; end
7
+ class DriverNotFoundError < CapybaraError; end
8
+ class ElementNotFound < CapybaraError; end
9
+
10
+ class << self
11
+ attr_accessor :debug, :asset_root
12
+
13
+ def log(message)
14
+ puts "[capybara] #{message}" if debug
15
+ true
16
+ end
17
+ end
18
+
19
+ autoload :Server, 'capybara/server'
20
+ autoload :Session, 'capybara/session'
21
+ autoload :Node, 'capybara/node'
22
+
23
+ module Driver
24
+ autoload :RackTest, 'capybara/driver/rack_test_driver'
25
+ autoload :Culerity, 'capybara/driver/culerity_driver'
26
+ autoload :SafariWatir, 'capybara/driver/safariwatir_driver'
27
+ autoload :FireWatir, 'capybara/driver/firewatir_driver'
28
+ autoload :Selenium, 'capybara/driver/selenium_driver'
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ class TCPSocket
2
+ def self.wait_for_service_with_timeout(options)
3
+ start_time = Time.now
4
+
5
+ until listening_service?(options)
6
+ verbose_wait
7
+
8
+ if options[:timeout] && (Time.now > start_time + options[:timeout])
9
+ raise SocketError.new("Socket did not open within #{options[:timeout]} seconds")
10
+ end
11
+ end
12
+ end
13
+
14
+ def self.wait_for_service_termination_with_timeout(options)
15
+ start_time = Time.now
16
+
17
+ while listening_service?(options)
18
+ verbose_wait
19
+
20
+ if options[:timeout] && (Time.now > start_time + options[:timeout])
21
+ raise SocketError.new("Socket did not terminate within #{options[:timeout]} seconds")
22
+ end
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,32 @@
1
+ require 'capybara'
2
+ require 'capybara/dsl'
3
+
4
+ World(Capybara)
5
+
6
+ After do
7
+ Capybara.reset_sessions!
8
+ end
9
+
10
+ require 'database_cleaner'
11
+ require 'database_cleaner/cucumber'
12
+ DatabaseCleaner.strategy = :truncation
13
+
14
+ Before('@javascript') do
15
+ Capybara.current_driver = Capybara.javascript_driver
16
+ end
17
+
18
+ Before('@selenium') do
19
+ Capybara.current_driver = :selenium
20
+ end
21
+
22
+ Before('@culerity') do
23
+ Capybara.current_driver = :culerity
24
+ end
25
+
26
+ Before('@rack_test') do
27
+ Capybara.current_driver = :rack_test
28
+ end
29
+
30
+ After do
31
+ Capybara.use_default_driver
32
+ end
@@ -0,0 +1,83 @@
1
+ require 'culerity'
2
+
3
+ class Capybara::Driver::Culerity
4
+ class Node < Capybara::Node
5
+ def text
6
+ node.text
7
+ end
8
+
9
+ def [](name)
10
+ value = if name.to_sym == :class
11
+ node.class_name
12
+ else
13
+ node.send(name.to_sym)
14
+ end
15
+ return value if value and not value.empty?
16
+ end
17
+
18
+ def set(value)
19
+ node.set(value.to_s)
20
+ end
21
+
22
+ def select(option)
23
+ node.select(option)
24
+ end
25
+
26
+ def click
27
+ node.click
28
+ end
29
+
30
+ def tag_name
31
+ # FIXME: this might be the dumbest way ever of getting the tag name
32
+ # there has to be something better...
33
+ node.to_xml[/^\s*<([a-z0-9\-\:]+)/, 1]
34
+ end
35
+ end
36
+
37
+ attr_reader :app, :rack_server
38
+
39
+ def self.server
40
+ unless @_server
41
+ @_server = ::Culerity::run_server
42
+ at_exit do
43
+ @_server.close
44
+ end
45
+ end
46
+ @_server
47
+ end
48
+
49
+ def initialize(app)
50
+ @app = app
51
+ @rack_server = Capybara::Server.new(@app)
52
+ @rack_server.boot
53
+ end
54
+
55
+ def visit(path)
56
+ browser.goto(url(path))
57
+ end
58
+
59
+ def body
60
+ browser.html
61
+ end
62
+
63
+ def find(selector)
64
+ browser.elements_by_xpath(selector).map { |node| Node.new(self, node) }
65
+ end
66
+
67
+ private
68
+
69
+ def url(path)
70
+ rack_server.url(path)
71
+ end
72
+
73
+ def browser
74
+ unless @_browser
75
+ @_browser = ::Culerity::RemoteBrowserProxy.new self.class.server, {:browser => :firefox, :log_level => :off}
76
+ at_exit do
77
+ @_browser.exit
78
+ end
79
+ end
80
+ @_browser
81
+ end
82
+
83
+ end