capybara 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +39 -0
- data/README.rdoc +181 -57
- data/Rakefile +7 -16
- data/config.ru +6 -0
- data/lib/capybara.rb +19 -9
- data/lib/capybara/cucumber.rb +4 -0
- data/lib/capybara/driver/base.rb +25 -8
- data/lib/capybara/driver/celerity_driver.rb +108 -0
- data/lib/capybara/driver/culerity_driver.rb +1 -70
- data/lib/capybara/driver/rack_test_driver.rb +57 -25
- data/lib/capybara/driver/selenium_driver.rb +28 -10
- data/lib/capybara/dsl.rb +7 -4
- data/lib/capybara/node.rb +50 -29
- data/lib/capybara/rails.rb +1 -1
- data/lib/capybara/save_and_open_page.rb +1 -1
- data/lib/capybara/searchable.rb +45 -0
- data/lib/capybara/server.rb +11 -4
- data/lib/capybara/session.rb +94 -94
- data/lib/capybara/wait_until.rb +23 -0
- data/lib/capybara/xpath.rb +55 -41
- data/spec/capybara_spec.rb +18 -0
- data/spec/driver/celerity_driver_spec.rb +17 -0
- data/spec/driver/culerity_driver_spec.rb +3 -0
- data/spec/driver/rack_test_driver_spec.rb +3 -0
- data/spec/driver/remote_culerity_driver_spec.rb +19 -0
- data/spec/driver/remote_selenium_driver_spec.rb +18 -0
- data/spec/driver/selenium_driver_spec.rb +2 -0
- data/spec/drivers_spec.rb +51 -5
- data/spec/dsl/all_spec.rb +38 -0
- data/spec/dsl/attach_file_spec.rb +66 -0
- data/spec/dsl/check_spec.rb +28 -0
- data/spec/dsl/choose_spec.rb +28 -0
- data/spec/dsl/click_button_spec.rb +183 -0
- data/spec/dsl/click_link_spec.rb +88 -0
- data/spec/dsl/click_spec.rb +26 -0
- data/spec/dsl/current_url_spec.rb +10 -0
- data/spec/dsl/fill_in_spec.rb +83 -0
- data/spec/dsl/find_button_spec.rb +18 -0
- data/spec/dsl/find_field_spec.rb +24 -0
- data/spec/dsl/find_link_spec.rb +19 -0
- data/spec/dsl/find_spec.rb +36 -0
- data/spec/dsl/has_content_spec.rb +103 -0
- data/spec/dsl/has_css_spec.rb +109 -0
- data/spec/dsl/has_xpath_spec.rb +115 -0
- data/spec/dsl/locate_spec.rb +38 -0
- data/spec/dsl/select_spec.rb +27 -0
- data/spec/dsl/uncheck_spec.rb +29 -0
- data/spec/dsl/within_spec.rb +145 -0
- data/spec/fixtures/capybara.jpg +0 -0
- data/spec/public/test.js +27 -0
- data/spec/searchable_spec.rb +61 -0
- data/spec/server_spec.rb +47 -0
- data/spec/session/celerity_session_spec.rb +27 -0
- data/spec/session/culerity_session_spec.rb +1 -0
- data/spec/session/rack_test_session_spec.rb +1 -0
- data/spec/session/selenium_session_spec.rb +1 -0
- data/spec/session_spec.rb +32 -903
- data/spec/session_with_headers_support_spec.rb +13 -0
- data/spec/session_with_javascript_support_spec.rb +165 -0
- data/spec/session_without_headers_support_spec.rb +15 -0
- data/spec/session_without_javascript_support_spec.rb +15 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/test_app.rb +9 -1
- data/spec/views/form.erb +38 -0
- data/spec/views/postback.erb +13 -0
- data/spec/views/with_html.erb +13 -0
- data/spec/views/with_js.erb +9 -24
- data/spec/views/with_simple_html.erb +1 -1
- data/spec/wait_until_spec.rb +28 -0
- data/spec/xpath_spec.rb +34 -3
- metadata +54 -5
data/lib/capybara/dsl.rb
CHANGED
@@ -47,10 +47,13 @@ module Capybara
|
|
47
47
|
end
|
48
48
|
|
49
49
|
SESSION_METHODS = [
|
50
|
-
:visit, :body, :click_link, :click_button, :drag, :fill_in,
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
50
|
+
:visit, :current_url, :body, :click_link, :click_button, :drag, :fill_in,
|
51
|
+
:choose, :has_xpath?, :has_no_xpath?, :has_css?, :has_no_css?,
|
52
|
+
:check, :uncheck, :attach_file, :select, :source,
|
53
|
+
:has_content?, :has_no_content?, :within, :within_fieldset, :within_table,
|
54
|
+
:save_and_open_page, :find, :find_field, :find_link, :find_button,
|
55
|
+
:field_labeled, :all, :locate, :evaluate_script,
|
56
|
+
:click, :wait_until
|
54
57
|
]
|
55
58
|
SESSION_METHODS.each do |method|
|
56
59
|
class_eval <<-RUBY, __FILE__, __LINE__+1
|
data/lib/capybara/node.rb
CHANGED
@@ -1,40 +1,61 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module Capybara
|
2
|
+
class Node
|
3
|
+
include Searchable
|
3
4
|
|
4
|
-
|
5
|
-
@driver = driver
|
6
|
-
@node = node
|
7
|
-
end
|
5
|
+
attr_reader :driver, :node
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
def initialize(driver, node)
|
8
|
+
@driver = driver
|
9
|
+
@node = node
|
10
|
+
end
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
def text
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
def [](name)
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
def value
|
21
|
+
self[:value]
|
22
|
+
end
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
def set(value)
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
def select(option)
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
def click
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def drag_to(element)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
def tag_name
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
|
44
|
+
def visible?
|
45
|
+
raise NotImplementedError
|
46
|
+
end
|
47
|
+
|
48
|
+
def path
|
49
|
+
raise NotSupportedByDriverError
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def all_unfiltered(locator)
|
55
|
+
nodes = XPath.wrap(locator).scope(path).paths.map do |path|
|
56
|
+
driver.find(path)
|
57
|
+
end.flatten
|
58
|
+
end
|
36
59
|
|
37
|
-
def tag_name
|
38
|
-
raise "Not implemented"
|
39
60
|
end
|
40
61
|
end
|
data/lib/capybara/rails.rb
CHANGED
@@ -23,7 +23,7 @@ module Capybara
|
|
23
23
|
|
24
24
|
def rewrite_css_and_image_references(response_html) # :nodoc:
|
25
25
|
return response_html unless Capybara.asset_root
|
26
|
-
response_html.gsub(/("|')\/(stylesheets|images)/, '\1' + Capybara.asset_root + '/\2')
|
26
|
+
response_html.gsub(/("|')\/(stylesheets|images|javascripts)/, '\1' + Capybara.asset_root + '/\2')
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Capybara
|
2
|
+
module Searchable
|
3
|
+
def find(locator, options = {})
|
4
|
+
all(locator, options).first
|
5
|
+
end
|
6
|
+
|
7
|
+
def find_field(locator)
|
8
|
+
find(XPath.field(locator))
|
9
|
+
end
|
10
|
+
alias_method :field_labeled, :find_field
|
11
|
+
|
12
|
+
def find_link(locator)
|
13
|
+
find(XPath.link(locator))
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_button(locator)
|
17
|
+
find(XPath.button(locator))
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_by_id(id)
|
21
|
+
find(Xpath.for_css("##{id}"))
|
22
|
+
end
|
23
|
+
|
24
|
+
def all(locator, options = {})
|
25
|
+
results = all_unfiltered(locator)
|
26
|
+
|
27
|
+
if options[:text]
|
28
|
+
results = results.select { |n| n.text.match(options[:text]) }
|
29
|
+
end
|
30
|
+
|
31
|
+
if options[:visible] == true
|
32
|
+
results.reject! { |n| !n.visible? }
|
33
|
+
end
|
34
|
+
|
35
|
+
results
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def all_unfiltered(locator)
|
41
|
+
raise "Must be overridden"
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
data/lib/capybara/server.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'net/http'
|
3
3
|
require 'rack'
|
4
|
-
|
4
|
+
begin
|
5
|
+
require 'rack/handler/mongrel'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rack/handler/webrick'
|
8
|
+
end
|
5
9
|
|
6
10
|
class Capybara::Server
|
7
11
|
class Identify
|
@@ -10,7 +14,7 @@ class Capybara::Server
|
|
10
14
|
end
|
11
15
|
|
12
16
|
def call(env)
|
13
|
-
if env["
|
17
|
+
if env["PATH_INFO"] == "/__identify__"
|
14
18
|
[200, {}, @app.object_id.to_s]
|
15
19
|
else
|
16
20
|
@app.call(env)
|
@@ -18,7 +22,6 @@ class Capybara::Server
|
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
21
|
-
|
22
25
|
attr_reader :app, :port
|
23
26
|
|
24
27
|
def initialize(app)
|
@@ -48,7 +51,11 @@ private
|
|
48
51
|
|
49
52
|
Timeout.timeout(10) do
|
50
53
|
Thread.new do
|
51
|
-
Rack::Handler::Mongrel
|
54
|
+
if defined?(Rack::Handler::Mongrel)
|
55
|
+
Rack::Handler::Mongrel.run(Identify.new(@app), :Port => port)
|
56
|
+
else
|
57
|
+
Rack::Handler::WEBrick.run(Identify.new(@app), :Port => port, :AccessLog => [])
|
58
|
+
end
|
52
59
|
end
|
53
60
|
Capybara.log "checking if application has booted"
|
54
61
|
|
data/lib/capybara/session.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
require 'capybara/wait_until'
|
2
|
+
|
1
3
|
module Capybara
|
2
4
|
class Session
|
5
|
+
include Searchable
|
3
6
|
|
4
7
|
attr_reader :mode, :app
|
5
8
|
|
@@ -12,109 +15,92 @@ module Capybara
|
|
12
15
|
@driver ||= case mode
|
13
16
|
when :rack_test
|
14
17
|
Capybara::Driver::RackTest.new(app)
|
15
|
-
when :culerity
|
16
|
-
Capybara::Driver::Culerity.new(app)
|
17
18
|
when :selenium
|
18
19
|
Capybara::Driver::Selenium.new(app)
|
20
|
+
when :celerity
|
21
|
+
Capybara::Driver::Celerity.new(app)
|
22
|
+
when :culerity
|
23
|
+
Capybara::Driver::Culerity.new(app)
|
19
24
|
else
|
20
25
|
raise Capybara::DriverNotFoundError, "no driver called #{mode} was found"
|
21
26
|
end
|
22
27
|
end
|
23
28
|
|
29
|
+
def current_url
|
30
|
+
driver.current_url
|
31
|
+
end
|
32
|
+
|
33
|
+
def response_headers
|
34
|
+
driver.response_headers
|
35
|
+
end
|
36
|
+
|
24
37
|
def visit(path)
|
25
38
|
driver.visit(path)
|
26
39
|
end
|
27
40
|
|
28
41
|
def click(locator)
|
29
|
-
|
30
|
-
|
31
|
-
link.click
|
42
|
+
msg = "no link or button '#{locator}' found"
|
43
|
+
locate(XPath.link(locator).button(locator), msg).click
|
32
44
|
end
|
33
45
|
|
34
46
|
def click_link(locator)
|
35
|
-
|
36
|
-
|
37
|
-
link.click
|
47
|
+
msg = "no link with title, id or text '#{locator}' found"
|
48
|
+
locate(XPath.link(locator), msg).click
|
38
49
|
end
|
39
50
|
|
40
51
|
def click_button(locator)
|
41
|
-
|
42
|
-
|
43
|
-
button.click
|
52
|
+
msg = "no button with value or id or text '#{locator}' found"
|
53
|
+
locate(XPath.button(locator)).click
|
44
54
|
end
|
45
55
|
|
46
56
|
def drag(source_locator, target_locator)
|
47
|
-
source =
|
48
|
-
|
49
|
-
target = find(target_locator)
|
50
|
-
raise Capybara::ElementNotFound, "drag target '#{target_locator}' not found on page" unless target
|
57
|
+
source = locate(source_locator, "drag source '#{source_locator}' not found on page")
|
58
|
+
target = locate(target_locator, "drag target '#{target_locator}' not found on page")
|
51
59
|
source.drag_to(target)
|
52
60
|
end
|
53
61
|
|
54
62
|
def fill_in(locator, options={})
|
55
|
-
|
56
|
-
|
57
|
-
field.set(options[:with])
|
63
|
+
msg = "cannot fill in, no text field, text area or password field with id, name, or label '#{locator}' found"
|
64
|
+
locate(XPath.fillable_field(locator), msg).set(options[:with])
|
58
65
|
end
|
59
66
|
|
60
67
|
def choose(locator)
|
61
|
-
|
62
|
-
|
63
|
-
field.set(true)
|
68
|
+
msg = "cannot choose field, no radio button with id, name, or label '#{locator}' found"
|
69
|
+
locate(XPath.radio_button(locator), msg).set(true)
|
64
70
|
end
|
65
71
|
|
66
72
|
def check(locator)
|
67
|
-
|
68
|
-
|
69
|
-
field.set(true)
|
73
|
+
msg = "cannot check field, no checkbox with id, name, or label '#{locator}' found"
|
74
|
+
locate(XPath.checkbox(locator), msg).set(true)
|
70
75
|
end
|
71
76
|
|
72
77
|
def uncheck(locator)
|
73
|
-
|
74
|
-
|
75
|
-
field.set(false)
|
78
|
+
msg = "cannot uncheck field, no checkbox with id, name, or label '#{locator}' found"
|
79
|
+
locate(XPath.checkbox(locator), msg).set(false)
|
76
80
|
end
|
77
81
|
|
78
82
|
def select(value, options={})
|
79
|
-
|
80
|
-
|
81
|
-
field.select(value)
|
83
|
+
msg = "cannot select option, no select box with id, name, or label '#{options[:from]}' found"
|
84
|
+
locate(XPath.select(options[:from]), msg).select(value)
|
82
85
|
end
|
83
86
|
|
84
87
|
def attach_file(locator, path)
|
85
|
-
|
86
|
-
|
87
|
-
field.set(path)
|
88
|
+
msg = "cannot attach file, no file field with id, name, or label '#{locator}' found"
|
89
|
+
locate(XPath.file_field(locator), msg).set(path)
|
88
90
|
end
|
89
91
|
|
90
92
|
def body
|
91
93
|
driver.body
|
92
94
|
end
|
93
95
|
|
94
|
-
def
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
def has_xpath?(path, options={})
|
99
|
-
results = all(path)
|
100
|
-
if options[:text]
|
101
|
-
results = filter_by_text(results, options[:text])
|
102
|
-
end
|
103
|
-
if options[:count]
|
104
|
-
results.size == options[:count]
|
105
|
-
else
|
106
|
-
results.size > 0
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def has_css?(path, options={})
|
111
|
-
has_xpath?(css_to_xpath(path), options)
|
96
|
+
def source
|
97
|
+
driver.source
|
112
98
|
end
|
113
99
|
|
114
100
|
def within(kind, scope=nil)
|
115
101
|
kind, scope = Capybara.default_selector, kind unless scope
|
116
|
-
scope =
|
117
|
-
|
102
|
+
scope = XPath.from_css(scope) if kind == :css
|
103
|
+
locate(scope, "scope '#{scope}' not found on page")
|
118
104
|
scopes.push(scope)
|
119
105
|
yield
|
120
106
|
scopes.pop
|
@@ -132,67 +118,81 @@ module Capybara
|
|
132
118
|
end
|
133
119
|
end
|
134
120
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
121
|
+
def has_xpath?(path, options={})
|
122
|
+
wait_conditionally_until do
|
123
|
+
results = all(path, options)
|
124
|
+
|
125
|
+
if options[:count]
|
126
|
+
results.size == options[:count]
|
127
|
+
else
|
128
|
+
results.size > 0
|
129
|
+
end
|
130
|
+
end
|
131
|
+
rescue Capybara::TimeoutError
|
132
|
+
return false
|
138
133
|
end
|
139
134
|
|
140
|
-
def
|
141
|
-
|
142
|
-
|
143
|
-
|
135
|
+
def has_no_xpath?(path, options={})
|
136
|
+
wait_conditionally_until do
|
137
|
+
results = all(path, options)
|
138
|
+
|
139
|
+
if options[:count]
|
140
|
+
results.size != options[:count]
|
141
|
+
else
|
142
|
+
results.empty?
|
143
|
+
end
|
144
|
+
end
|
145
|
+
rescue Capybara::TimeoutError
|
146
|
+
return false
|
144
147
|
end
|
145
148
|
|
146
|
-
def
|
147
|
-
|
149
|
+
def has_css?(path, options={})
|
150
|
+
has_xpath?(XPath.from_css(path), options)
|
148
151
|
end
|
149
|
-
|
150
|
-
def
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
152
|
+
|
153
|
+
def has_no_css?(path, options={})
|
154
|
+
has_no_xpath?(XPath.from_css(path), options)
|
155
|
+
end
|
156
|
+
|
157
|
+
def has_content?(content)
|
158
|
+
has_xpath?(XPath.content(content))
|
159
|
+
end
|
160
|
+
|
161
|
+
def has_no_content?(content)
|
162
|
+
has_no_xpath?(XPath.content(content))
|
158
163
|
end
|
159
164
|
|
160
|
-
def
|
161
|
-
|
165
|
+
def save_and_open_page
|
166
|
+
require 'capybara/save_and_open_page'
|
167
|
+
Capybara::SaveAndOpenPage.save_and_open_page(body)
|
162
168
|
end
|
163
|
-
alias_method :field_labeled, :find_field
|
164
169
|
|
165
|
-
|
166
|
-
|
170
|
+
#return node identified by locator or raise ElementNotFound(using desc)
|
171
|
+
def locate(locator, fail_msg = nil)
|
172
|
+
node = wait_conditionally_until { find(locator) }
|
173
|
+
ensure
|
174
|
+
raise Capybara::ElementNotFound, fail_msg || "Unable to locate '#{locator}'" unless node
|
175
|
+
return node
|
167
176
|
end
|
168
177
|
|
169
|
-
def
|
170
|
-
|
178
|
+
def wait_until(timeout = Capybara.default_wait_time)
|
179
|
+
WaitUntil.timeout(timeout) { yield }
|
171
180
|
end
|
172
181
|
|
173
182
|
def evaluate_script(script)
|
174
|
-
|
175
|
-
driver.evaluate_script(script)
|
176
|
-
rescue NoMethodError
|
177
|
-
raise NotSupportedByDriverError
|
178
|
-
end
|
183
|
+
driver.evaluate_script(script)
|
179
184
|
end
|
180
185
|
|
181
186
|
private
|
182
187
|
|
183
|
-
def
|
184
|
-
|
188
|
+
def wait_conditionally_until
|
189
|
+
if driver.wait? then wait_until { yield } else yield end
|
185
190
|
end
|
186
191
|
|
187
|
-
def
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
node.text.include?(text)
|
192
|
-
when Regexp
|
193
|
-
node.text =~ text
|
194
|
-
end
|
195
|
-
end
|
192
|
+
def all_unfiltered(locator)
|
193
|
+
XPath.wrap(locator).scope(current_scope).paths.map do |path|
|
194
|
+
driver.find(path)
|
195
|
+
end.flatten
|
196
196
|
end
|
197
197
|
|
198
198
|
def current_scope
|