capybara 0.3.0 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +4 -0
- data/README.rdoc +45 -5
- data/lib/capybara.rb +11 -4
- data/lib/capybara/driver/base.rb +3 -0
- data/lib/capybara/driver/celerity_driver.rb +44 -9
- data/lib/capybara/driver/rack_test_driver.rb +80 -16
- data/lib/capybara/driver/selenium_driver.rb +41 -9
- data/lib/capybara/dsl.rb +4 -10
- data/lib/capybara/node.rb +8 -0
- data/lib/capybara/rails.rb +8 -2
- data/lib/capybara/save_and_open_page.rb +5 -1
- data/lib/capybara/searchable.rb +17 -9
- data/lib/capybara/server.rb +35 -23
- data/lib/capybara/session.rb +91 -22
- data/lib/capybara/xpath.rb +66 -27
- data/spec/driver/celerity_driver_spec.rb +2 -2
- data/spec/driver/culerity_driver_spec.rb +1 -2
- data/spec/driver/rack_test_driver_spec.rb +0 -1
- data/spec/driver/remote_culerity_driver_spec.rb +9 -5
- data/spec/driver/selenium_driver_spec.rb +0 -1
- data/spec/drivers_spec.rb +24 -32
- data/spec/dsl/all_spec.rb +56 -25
- data/spec/dsl/attach_file_spec.rb +49 -51
- data/spec/dsl/check_spec.rb +12 -1
- data/spec/dsl/choose_spec.rb +19 -21
- data/spec/dsl/click_button_spec.rb +140 -87
- data/spec/dsl/click_link_spec.rb +88 -68
- data/spec/dsl/click_spec.rb +20 -22
- data/spec/dsl/current_url_spec.rb +6 -8
- data/spec/dsl/fill_in_spec.rb +75 -67
- data/spec/dsl/find_button_spec.rb +12 -14
- data/spec/dsl/find_by_id_spec.rb +16 -0
- data/spec/dsl/find_field_spec.rb +17 -19
- data/spec/dsl/find_link_spec.rb +13 -15
- data/spec/dsl/find_spec.rb +44 -23
- data/spec/dsl/has_button_spec.rb +32 -0
- data/spec/dsl/has_content_spec.rb +79 -81
- data/spec/dsl/has_css_spec.rb +81 -83
- data/spec/dsl/has_field_spec.rb +96 -0
- data/spec/dsl/has_link_spec.rb +33 -0
- data/spec/dsl/has_xpath_spec.rb +97 -89
- data/spec/dsl/locate_spec.rb +47 -26
- data/spec/dsl/select_spec.rb +61 -17
- data/spec/dsl/uncheck_spec.rb +17 -25
- data/spec/dsl/within_spec.rb +112 -104
- data/spec/public/test.js +3 -0
- data/spec/searchable_spec.rb +1 -1
- data/spec/server_spec.rb +7 -7
- data/spec/session/celerity_session_spec.rb +2 -2
- data/spec/session/culerity_session_spec.rb +1 -1
- data/spec/session_spec.rb +7 -0
- data/spec/session_with_javascript_support_spec.rb +139 -120
- data/spec/spec_helper.rb +7 -2
- data/spec/test_app.rb +8 -4
- data/spec/views/form.erb +50 -2
- data/spec/views/tables.erb +61 -1
- data/spec/views/with_html.erb +8 -2
- data/spec/views/with_js.erb +4 -0
- data/spec/xpath_spec.rb +17 -0
- metadata +6 -2
data/lib/capybara/dsl.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'capybara'
|
2
|
+
|
1
3
|
module Capybara
|
2
4
|
class << self
|
3
5
|
attr_writer :default_driver, :current_driver, :javascript_driver
|
@@ -30,6 +32,7 @@ module Capybara
|
|
30
32
|
end
|
31
33
|
|
32
34
|
def reset_sessions!
|
35
|
+
session_pool.each { |mode, session| session.cleanup! }
|
33
36
|
@session_pool = nil
|
34
37
|
end
|
35
38
|
|
@@ -46,16 +49,7 @@ module Capybara
|
|
46
49
|
Capybara.current_session
|
47
50
|
end
|
48
51
|
|
49
|
-
|
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
|
57
|
-
]
|
58
|
-
SESSION_METHODS.each do |method|
|
52
|
+
Session::DSL_METHODS.each do |method|
|
59
53
|
class_eval <<-RUBY, __FILE__, __LINE__+1
|
60
54
|
def #{method}(*args, &block)
|
61
55
|
page.#{method}(*args, &block)
|
data/lib/capybara/node.rb
CHANGED
@@ -29,6 +29,10 @@ module Capybara
|
|
29
29
|
raise NotImplementedError
|
30
30
|
end
|
31
31
|
|
32
|
+
def unselect(option)
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
32
36
|
def click
|
33
37
|
raise NotImplementedError
|
34
38
|
end
|
@@ -48,6 +52,10 @@ module Capybara
|
|
48
52
|
def path
|
49
53
|
raise NotSupportedByDriverError
|
50
54
|
end
|
55
|
+
|
56
|
+
def trigger(event)
|
57
|
+
raise NotSupportedByDriverError
|
58
|
+
end
|
51
59
|
|
52
60
|
private
|
53
61
|
|
data/lib/capybara/rails.rb
CHANGED
@@ -3,9 +3,15 @@ require 'capybara/dsl'
|
|
3
3
|
|
4
4
|
Capybara.app = Rack::Builder.new do
|
5
5
|
map "/" do
|
6
|
-
|
7
|
-
|
6
|
+
if Rails.version.to_f >= 3.0
|
7
|
+
ActionDispatch::Static
|
8
|
+
run Rails.application
|
9
|
+
else # Rails 2
|
10
|
+
use Rails::Rack::Static
|
11
|
+
run ActionController::Dispatcher.new
|
12
|
+
end
|
8
13
|
end
|
9
14
|
end.to_app
|
10
15
|
|
11
16
|
Capybara.asset_root = Rails.root.join('public')
|
17
|
+
|
@@ -23,7 +23,11 @@ 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
|
-
|
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')
|
27
31
|
end
|
28
32
|
end
|
29
33
|
end
|
data/lib/capybara/searchable.rb
CHANGED
@@ -1,35 +1,43 @@
|
|
1
1
|
module Capybara
|
2
2
|
module Searchable
|
3
|
-
def find(
|
4
|
-
all(
|
3
|
+
def find(*args)
|
4
|
+
all(*args).first
|
5
5
|
end
|
6
6
|
|
7
7
|
def find_field(locator)
|
8
|
-
find(XPath.field(locator))
|
8
|
+
find(:xpath, XPath.field(locator))
|
9
9
|
end
|
10
10
|
alias_method :field_labeled, :find_field
|
11
11
|
|
12
12
|
def find_link(locator)
|
13
|
-
find(XPath.link(locator))
|
13
|
+
find(:xpath, XPath.link(locator))
|
14
14
|
end
|
15
15
|
|
16
16
|
def find_button(locator)
|
17
|
-
find(XPath.button(locator))
|
17
|
+
find(:xpath, XPath.button(locator))
|
18
18
|
end
|
19
19
|
|
20
20
|
def find_by_id(id)
|
21
|
-
find(
|
21
|
+
find(:css, "##{id}")
|
22
22
|
end
|
23
23
|
|
24
|
-
def all(
|
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
|
+
|
25
33
|
results = all_unfiltered(locator)
|
26
34
|
|
27
35
|
if options[:text]
|
28
36
|
results = results.select { |n| n.text.match(options[:text]) }
|
29
37
|
end
|
30
38
|
|
31
|
-
if options[:visible]
|
32
|
-
results.
|
39
|
+
if options[:visible] or Capybara.ignore_hidden_elements
|
40
|
+
results = results.select { |n| n.visible? }
|
33
41
|
end
|
34
42
|
|
35
43
|
results
|
data/lib/capybara/server.rb
CHANGED
@@ -1,11 +1,6 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'net/http'
|
3
3
|
require 'rack'
|
4
|
-
begin
|
5
|
-
require 'rack/handler/mongrel'
|
6
|
-
rescue LoadError
|
7
|
-
require 'rack/handler/webrick'
|
8
|
-
end
|
9
4
|
|
10
5
|
class Capybara::Server
|
11
6
|
class Identify
|
@@ -26,49 +21,66 @@ class Capybara::Server
|
|
26
21
|
|
27
22
|
def initialize(app)
|
28
23
|
@app = app
|
29
|
-
find_available_port
|
30
|
-
boot
|
31
24
|
end
|
32
25
|
|
33
26
|
def host
|
34
|
-
|
27
|
+
"localhost"
|
35
28
|
end
|
36
29
|
|
37
30
|
def url(path)
|
38
|
-
|
39
|
-
|
31
|
+
if path =~ /^http/
|
32
|
+
path
|
33
|
+
else
|
34
|
+
(Capybara.app_host || "http://#{host}:#{port}") + path.to_s
|
35
|
+
end
|
40
36
|
end
|
41
37
|
|
42
38
|
def responsive?
|
43
39
|
is_running_on_port?(port)
|
44
40
|
end
|
45
41
|
|
46
|
-
|
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
|
47
56
|
|
48
57
|
def boot
|
49
|
-
|
58
|
+
find_available_port
|
59
|
+
Capybara.log "application has already booted" and return self if responsive?
|
50
60
|
Capybara.log "booting Rack applicartion on port #{port}"
|
51
61
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
else
|
57
|
-
Rack::Handler::WEBrick.run(Identify.new(@app), :Port => port, :AccessLog => [])
|
58
|
-
end
|
59
|
-
end
|
60
|
-
Capybara.log "checking if application has booted"
|
62
|
+
Thread.new do
|
63
|
+
handler.run(Identify.new(@app), :Port => port, :AccessLog => [])
|
64
|
+
end
|
65
|
+
Capybara.log "checking if application has booted"
|
61
66
|
|
62
|
-
|
63
|
-
|
67
|
+
Capybara::WaitUntil.timeout(10) do
|
68
|
+
if responsive?
|
69
|
+
Capybara.log("application has booted")
|
70
|
+
true
|
71
|
+
else
|
64
72
|
sleep 0.5
|
73
|
+
false
|
65
74
|
end
|
66
75
|
end
|
76
|
+
self
|
67
77
|
rescue Timeout::Error
|
68
78
|
Capybara.log "Rack application timed out during boot"
|
69
79
|
exit
|
70
80
|
end
|
71
81
|
|
82
|
+
private
|
83
|
+
|
72
84
|
def find_available_port
|
73
85
|
@port = 9887
|
74
86
|
@port += 1 while is_port_open?(@port) and not is_running_on_port?(@port)
|
data/lib/capybara/session.rb
CHANGED
@@ -4,6 +4,15 @@ module Capybara
|
|
4
4
|
class Session
|
5
5
|
include Searchable
|
6
6
|
|
7
|
+
DSL_METHODS = [
|
8
|
+
:all, :attach_file, :body, :check, :choose, :click, :click_button, :click_link, :current_url, :drag, :evaluate_script,
|
9
|
+
:field_labeled, :fill_in, :find, :find_button, :find_by_id, :find_field, :find_link, :has_content?, :has_css?,
|
10
|
+
:has_no_content?, :has_no_css?, :has_no_xpath?, :has_xpath?, :locate, :save_and_open_page, :select, :source, :uncheck,
|
11
|
+
:visit, :wait_until, :within, :within_fieldset, :within_table, :has_link?, :has_no_link?, :has_button?, :has_no_button?,
|
12
|
+
:has_field?, :has_no_field?, :has_checked_field?, :has_unchecked_field?, :has_no_table?, :has_table?, :unselect,
|
13
|
+
:has_select?, :has_no_select?
|
14
|
+
]
|
15
|
+
|
7
16
|
attr_reader :mode, :app
|
8
17
|
|
9
18
|
def initialize(mode, app)
|
@@ -26,6 +35,10 @@ module Capybara
|
|
26
35
|
end
|
27
36
|
end
|
28
37
|
|
38
|
+
def cleanup!
|
39
|
+
driver.cleanup!
|
40
|
+
end
|
41
|
+
|
29
42
|
def current_url
|
30
43
|
driver.current_url
|
31
44
|
end
|
@@ -40,53 +53,58 @@ module Capybara
|
|
40
53
|
|
41
54
|
def click(locator)
|
42
55
|
msg = "no link or button '#{locator}' found"
|
43
|
-
locate(XPath.link(locator).button(locator), msg).click
|
56
|
+
locate(:xpath, XPath.link(locator).button(locator), msg).click
|
44
57
|
end
|
45
58
|
|
46
59
|
def click_link(locator)
|
47
60
|
msg = "no link with title, id or text '#{locator}' found"
|
48
|
-
locate(XPath.link(locator), msg).click
|
61
|
+
locate(:xpath, XPath.link(locator), msg).click
|
49
62
|
end
|
50
63
|
|
51
64
|
def click_button(locator)
|
52
65
|
msg = "no button with value or id or text '#{locator}' found"
|
53
|
-
locate(XPath.button(locator)).click
|
66
|
+
locate(:xpath, XPath.button(locator), msg).click
|
54
67
|
end
|
55
68
|
|
56
69
|
def drag(source_locator, target_locator)
|
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")
|
70
|
+
source = locate(:xpath, source_locator, "drag source '#{source_locator}' not found on page")
|
71
|
+
target = locate(:xpath, target_locator, "drag target '#{target_locator}' not found on page")
|
59
72
|
source.drag_to(target)
|
60
73
|
end
|
61
74
|
|
62
75
|
def fill_in(locator, options={})
|
63
76
|
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])
|
77
|
+
locate(:xpath, XPath.fillable_field(locator), msg).set(options[:with])
|
65
78
|
end
|
66
79
|
|
67
80
|
def choose(locator)
|
68
81
|
msg = "cannot choose field, no radio button with id, name, or label '#{locator}' found"
|
69
|
-
locate(XPath.radio_button(locator), msg).set(true)
|
82
|
+
locate(:xpath, XPath.radio_button(locator), msg).set(true)
|
70
83
|
end
|
71
84
|
|
72
85
|
def check(locator)
|
73
86
|
msg = "cannot check field, no checkbox with id, name, or label '#{locator}' found"
|
74
|
-
locate(XPath.checkbox(locator), msg).set(true)
|
87
|
+
locate(:xpath, XPath.checkbox(locator), msg).set(true)
|
75
88
|
end
|
76
89
|
|
77
90
|
def uncheck(locator)
|
78
91
|
msg = "cannot uncheck field, no checkbox with id, name, or label '#{locator}' found"
|
79
|
-
locate(XPath.checkbox(locator), msg).set(false)
|
92
|
+
locate(:xpath, XPath.checkbox(locator), msg).set(false)
|
80
93
|
end
|
81
94
|
|
82
95
|
def select(value, options={})
|
83
96
|
msg = "cannot select option, no select box with id, name, or label '#{options[:from]}' found"
|
84
|
-
locate(XPath.select(options[:from]), msg).select(value)
|
97
|
+
locate(:xpath, XPath.select(options[:from]), msg).select(value)
|
98
|
+
end
|
99
|
+
|
100
|
+
def unselect(value, options={})
|
101
|
+
msg = "cannot unselect option, no select box with id, name, or label '#{options[:from]}' found"
|
102
|
+
locate(:xpath, XPath.select(options[:from]), msg).unselect(value)
|
85
103
|
end
|
86
104
|
|
87
105
|
def attach_file(locator, path)
|
88
106
|
msg = "cannot attach file, no file field with id, name, or label '#{locator}' found"
|
89
|
-
locate(XPath.file_field(locator), msg).set(path)
|
107
|
+
locate(:xpath, XPath.file_field(locator), msg).set(path)
|
90
108
|
end
|
91
109
|
|
92
110
|
def body
|
@@ -100,27 +118,30 @@ module Capybara
|
|
100
118
|
def within(kind, scope=nil)
|
101
119
|
kind, scope = Capybara.default_selector, kind unless scope
|
102
120
|
scope = XPath.from_css(scope) if kind == :css
|
103
|
-
locate(scope, "scope '#{scope}' not found on page")
|
104
|
-
|
105
|
-
|
106
|
-
|
121
|
+
locate(:xpath, scope, "scope '#{scope}' not found on page")
|
122
|
+
begin
|
123
|
+
scopes.push(scope)
|
124
|
+
yield
|
125
|
+
ensure
|
126
|
+
scopes.pop
|
127
|
+
end
|
107
128
|
end
|
108
129
|
|
109
130
|
def within_fieldset(locator)
|
110
|
-
within XPath.fieldset(locator) do
|
131
|
+
within :xpath, XPath.fieldset(locator) do
|
111
132
|
yield
|
112
133
|
end
|
113
134
|
end
|
114
135
|
|
115
136
|
def within_table(locator)
|
116
|
-
within XPath.table(locator) do
|
137
|
+
within :xpath, XPath.table(locator) do
|
117
138
|
yield
|
118
139
|
end
|
119
140
|
end
|
120
141
|
|
121
142
|
def has_xpath?(path, options={})
|
122
143
|
wait_conditionally_until do
|
123
|
-
results = all(path, options)
|
144
|
+
results = all(:xpath, path, options)
|
124
145
|
|
125
146
|
if options[:count]
|
126
147
|
results.size == options[:count]
|
@@ -134,7 +155,7 @@ module Capybara
|
|
134
155
|
|
135
156
|
def has_no_xpath?(path, options={})
|
136
157
|
wait_conditionally_until do
|
137
|
-
results = all(path, options)
|
158
|
+
results = all(:xpath, path, options)
|
138
159
|
|
139
160
|
if options[:count]
|
140
161
|
results.size != options[:count]
|
@@ -162,16 +183,64 @@ module Capybara
|
|
162
183
|
has_no_xpath?(XPath.content(content))
|
163
184
|
end
|
164
185
|
|
186
|
+
def has_link?(locator)
|
187
|
+
has_xpath?(XPath.link(locator))
|
188
|
+
end
|
189
|
+
|
190
|
+
def has_no_link?(locator)
|
191
|
+
has_no_xpath?(XPath.link(locator))
|
192
|
+
end
|
193
|
+
|
194
|
+
def has_button?(locator)
|
195
|
+
has_xpath?(XPath.button(locator))
|
196
|
+
end
|
197
|
+
|
198
|
+
def has_no_button?(locator)
|
199
|
+
has_no_xpath?(XPath.button(locator))
|
200
|
+
end
|
201
|
+
|
202
|
+
def has_field?(locator, options={})
|
203
|
+
has_xpath?(XPath.field(locator, options))
|
204
|
+
end
|
205
|
+
|
206
|
+
def has_no_field?(locator, options={})
|
207
|
+
has_no_xpath?(XPath.field(locator, options))
|
208
|
+
end
|
209
|
+
|
210
|
+
def has_checked_field?(locator)
|
211
|
+
has_xpath?(XPath.field(locator, :checked => true))
|
212
|
+
end
|
213
|
+
|
214
|
+
def has_unchecked_field?(locator)
|
215
|
+
has_xpath?(XPath.field(locator, :unchecked => true))
|
216
|
+
end
|
217
|
+
|
218
|
+
def has_select?(locator, options={})
|
219
|
+
has_xpath?(XPath.select(locator, options))
|
220
|
+
end
|
221
|
+
|
222
|
+
def has_no_select?(locator, options={})
|
223
|
+
has_no_xpath?(XPath.select(locator, options))
|
224
|
+
end
|
225
|
+
|
226
|
+
def has_table?(locator, options={})
|
227
|
+
has_xpath?(XPath.table(locator, options))
|
228
|
+
end
|
229
|
+
|
230
|
+
def has_no_table?(locator, options={})
|
231
|
+
has_no_xpath?(XPath.table(locator, options))
|
232
|
+
end
|
233
|
+
|
165
234
|
def save_and_open_page
|
166
235
|
require 'capybara/save_and_open_page'
|
167
236
|
Capybara::SaveAndOpenPage.save_and_open_page(body)
|
168
237
|
end
|
169
238
|
|
170
239
|
#return node identified by locator or raise ElementNotFound(using desc)
|
171
|
-
def locate(locator, fail_msg = nil)
|
172
|
-
node = wait_conditionally_until { find(locator) }
|
240
|
+
def locate(kind_or_locator, locator=nil, fail_msg = nil)
|
241
|
+
node = wait_conditionally_until { find(kind_or_locator, locator) }
|
173
242
|
ensure
|
174
|
-
raise Capybara::ElementNotFound, fail_msg || "Unable to locate '#{
|
243
|
+
raise Capybara::ElementNotFound, fail_msg || "Unable to locate '#{kind_or_locator}'" unless node
|
175
244
|
return node
|
176
245
|
end
|
177
246
|
|
data/lib/capybara/xpath.rb
CHANGED
@@ -33,13 +33,21 @@ module Capybara
|
|
33
33
|
@paths = paths
|
34
34
|
end
|
35
35
|
|
36
|
-
def field(locator)
|
37
|
-
|
36
|
+
def field(locator, options={})
|
37
|
+
if options[:with]
|
38
|
+
fillable_field(locator, options)
|
39
|
+
else
|
40
|
+
xpath = fillable_field(locator)
|
41
|
+
xpath = xpath.input_field(:file, locator, options)
|
42
|
+
xpath = xpath.checkbox(locator, options)
|
43
|
+
xpath = xpath.radio_button(locator, options)
|
44
|
+
xpath.select(locator, options)
|
45
|
+
end
|
38
46
|
end
|
39
47
|
|
40
|
-
def fillable_field(locator)
|
41
|
-
[:text, :password, :email, :url, :search, :tel, :color].inject(text_area(locator)) do |all, type|
|
42
|
-
all.input_field(type, locator)
|
48
|
+
def fillable_field(locator, options={})
|
49
|
+
[:text, :password, :email, :url, :search, :tel, :color].inject(text_area(locator, options)) do |all, type|
|
50
|
+
all.input_field(type, locator, options)
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
@@ -47,8 +55,16 @@ module Capybara
|
|
47
55
|
append("/descendant-or-self::*[contains(.,#{s(locator)})]")
|
48
56
|
end
|
49
57
|
|
50
|
-
def table(locator)
|
51
|
-
|
58
|
+
def table(locator, options={})
|
59
|
+
conditions = ""
|
60
|
+
if options[:rows]
|
61
|
+
row_conditions = options[:rows].map do |row|
|
62
|
+
row = row.map { |column| "*[self::td or self::th][text()=#{s(column)}]" }.join(sibling)
|
63
|
+
"tr[./#{row}]"
|
64
|
+
end.join(sibling)
|
65
|
+
conditions << "[.//#{row_conditions}]"
|
66
|
+
end
|
67
|
+
append("//table[@id=#{s(locator)} or contains(caption,#{s(locator)})]#{conditions}")
|
52
68
|
end
|
53
69
|
|
54
70
|
def fieldset(locator)
|
@@ -56,27 +72,30 @@ module Capybara
|
|
56
72
|
end
|
57
73
|
|
58
74
|
def link(locator)
|
59
|
-
xpath = append("//a[@href][@id=#{s(locator)} or contains(.,#{s(locator)}) or contains(@title,#{s(locator)})]")
|
60
|
-
xpath.prepend("//a[@href][text()=#{s(locator)} or @title=#{s(locator)}]")
|
75
|
+
xpath = append("//a[@href][@id=#{s(locator)} or contains(.,#{s(locator)}) or contains(@title,#{s(locator)}) or img[contains(@alt,#{s(locator)})]]")
|
76
|
+
xpath.prepend("//a[@href][text()=#{s(locator)} or @title=#{s(locator)} or img[@alt=#{s(locator)}]]")
|
61
77
|
end
|
62
78
|
|
63
79
|
def button(locator)
|
64
|
-
xpath = append("//input[@type='submit' or @type='image'][@id=#{s(locator)} or contains(@value,#{s(locator)})]")
|
80
|
+
xpath = append("//input[@type='submit' or @type='image' or @type='button'][@id=#{s(locator)} or contains(@value,#{s(locator)})]")
|
65
81
|
xpath = xpath.append("//button[@id=#{s(locator)} or contains(@value,#{s(locator)}) or contains(.,#{s(locator)})]")
|
66
|
-
xpath = xpath.prepend("//input[@type='submit' or @type='image'][@value=#{s(locator)}]")
|
82
|
+
xpath = xpath.prepend("//input[@type='submit' or @type='image' or @type='button'][@value=#{s(locator)}]")
|
83
|
+
xpath = xpath.prepend("//input[@type='image'][@alt=#{s(locator)} or contains(@alt,#{s(locator)})]")
|
67
84
|
xpath = xpath.prepend("//button[@value=#{s(locator)} or text()=#{s(locator)}]")
|
68
85
|
end
|
69
86
|
|
70
|
-
def text_area(locator)
|
71
|
-
|
87
|
+
def text_area(locator, options={})
|
88
|
+
options = options.merge(:text => options[:with]) if options.has_key?(:with)
|
89
|
+
add_field(locator, "//textarea", options)
|
72
90
|
end
|
73
91
|
|
74
|
-
def select(locator)
|
75
|
-
add_field(locator, "//select")
|
92
|
+
def select(locator, options={})
|
93
|
+
add_field(locator, "//select", options)
|
76
94
|
end
|
77
95
|
|
78
|
-
def input_field(type, locator)
|
79
|
-
|
96
|
+
def input_field(type, locator, options={})
|
97
|
+
options = options.merge(:value => options[:with]) if options.has_key?(:with)
|
98
|
+
add_field(locator, "//input[@type='#{type}']", options)
|
80
99
|
end
|
81
100
|
|
82
101
|
def scope(scope)
|
@@ -95,12 +114,12 @@ module Capybara
|
|
95
114
|
XPath.new(*[XPath.wrap(path).paths, @paths].flatten)
|
96
115
|
end
|
97
116
|
|
98
|
-
def checkbox(locator)
|
99
|
-
input_field(:checkbox, locator)
|
117
|
+
def checkbox(locator, options={})
|
118
|
+
input_field(:checkbox, locator, options)
|
100
119
|
end
|
101
120
|
|
102
|
-
def radio_button(locator)
|
103
|
-
input_field(:radio, locator)
|
121
|
+
def radio_button(locator, options={})
|
122
|
+
input_field(:radio, locator, options)
|
104
123
|
end
|
105
124
|
|
106
125
|
[:text, :password, :email, :url, :search, :tel, :color, :file].each do |type|
|
@@ -113,12 +132,32 @@ module Capybara
|
|
113
132
|
|
114
133
|
protected
|
115
134
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
135
|
+
# place this between to nodes to indicate that they should be siblings
|
136
|
+
def sibling
|
137
|
+
'/following-sibling::*[1]/self::'
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_field(locator, field, options={})
|
141
|
+
postfix = extract_postfix(options)
|
142
|
+
xpath = append("#{field}[@id=#{s(locator)}]#{postfix}")
|
143
|
+
xpath = xpath.append("#{field}[@name=#{s(locator)}]#{postfix}")
|
144
|
+
xpath = xpath.append("#{field}[@id=//label[contains(.,#{s(locator)})]/@for]#{postfix}")
|
145
|
+
xpath = xpath.append("//label[contains(.,#{s(locator)})]#{field}#{postfix}")
|
146
|
+
xpath.prepend("#{field}[@id=//label[text()=#{s(locator)}]/@for]#{postfix}")
|
147
|
+
end
|
148
|
+
|
149
|
+
def extract_postfix(options)
|
150
|
+
options.inject("") do |postfix, (key, value)|
|
151
|
+
case key
|
152
|
+
when :value then postfix += "[@value=#{s(value)}]"
|
153
|
+
when :text then postfix += "[text()=#{s(value)}]"
|
154
|
+
when :checked then postfix += "[@checked]"
|
155
|
+
when :unchecked then postfix += "[not(@checked)]"
|
156
|
+
when :options then postfix += value.map { |o| "[./option/text()=#{s(o)}]" }.join
|
157
|
+
when :selected then postfix += [value].flatten.map { |o| "[./option[@selected]/text()=#{s(o)}]" }.join
|
158
|
+
end
|
159
|
+
postfix
|
160
|
+
end
|
122
161
|
end
|
123
162
|
|
124
163
|
# Sanitize a String for putting it into an xpath query
|