capybara 2.6.2 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +40 -15
- data/README.md +45 -38
- data/lib/capybara.rb +86 -10
- data/lib/capybara/cucumber.rb +1 -0
- data/lib/capybara/driver/base.rb +5 -2
- data/lib/capybara/driver/node.rb +1 -0
- data/lib/capybara/dsl.rb +1 -0
- data/lib/capybara/helpers.rb +3 -2
- data/lib/capybara/node/actions.rb +6 -0
- data/lib/capybara/node/base.rb +1 -0
- data/lib/capybara/node/document.rb +1 -0
- data/lib/capybara/node/document_matchers.rb +1 -0
- data/lib/capybara/node/element.rb +1 -0
- data/lib/capybara/node/finders.rb +3 -2
- data/lib/capybara/node/matchers.rb +72 -2
- data/lib/capybara/node/simple.rb +6 -3
- data/lib/capybara/queries/base_query.rb +1 -0
- data/lib/capybara/queries/current_path_query.rb +1 -0
- data/lib/capybara/queries/match_query.rb +21 -0
- data/lib/capybara/queries/selector_query.rb +138 -0
- data/lib/capybara/queries/text_query.rb +1 -0
- data/lib/capybara/queries/title_query.rb +1 -0
- data/lib/capybara/query.rb +3 -131
- data/lib/capybara/rack_test/browser.rb +1 -0
- data/lib/capybara/rack_test/css_handlers.rb +1 -0
- data/lib/capybara/rack_test/driver.rb +6 -4
- data/lib/capybara/rack_test/form.rb +1 -0
- data/lib/capybara/rack_test/node.rb +1 -0
- data/lib/capybara/rails.rb +2 -1
- data/lib/capybara/result.rb +1 -0
- data/lib/capybara/rspec.rb +1 -0
- data/lib/capybara/rspec/features.rb +1 -0
- data/lib/capybara/rspec/matchers.rb +54 -3
- data/lib/capybara/selector.rb +175 -75
- data/lib/capybara/selector/filter.rb +48 -0
- data/lib/capybara/selenium/driver.rb +24 -7
- data/lib/capybara/selenium/node.rb +1 -0
- data/lib/capybara/server.rb +38 -6
- data/lib/capybara/session.rb +30 -14
- data/lib/capybara/session/matchers.rb +2 -1
- data/lib/capybara/spec/public/test.js +6 -0
- data/lib/capybara/spec/session/accept_alert_spec.rb +2 -1
- data/lib/capybara/spec/session/accept_confirm_spec.rb +2 -1
- data/lib/capybara/spec/session/accept_prompt_spec.rb +2 -1
- data/lib/capybara/spec/session/all_spec.rb +1 -0
- data/lib/capybara/spec/session/assert_current_path.rb +1 -0
- data/lib/capybara/spec/session/assert_selector.rb +1 -0
- data/lib/capybara/spec/session/assert_text.rb +1 -0
- data/lib/capybara/spec/session/assert_title.rb +1 -0
- data/lib/capybara/spec/session/attach_file_spec.rb +1 -0
- data/lib/capybara/spec/session/body_spec.rb +1 -0
- data/lib/capybara/spec/session/check_spec.rb +1 -0
- data/lib/capybara/spec/session/choose_spec.rb +1 -0
- data/lib/capybara/spec/session/click_button_spec.rb +1 -0
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +1 -0
- data/lib/capybara/spec/session/click_link_spec.rb +1 -0
- data/lib/capybara/spec/session/current_scope_spec.rb +2 -1
- data/lib/capybara/spec/session/current_url_spec.rb +1 -0
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +2 -1
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -1
- data/lib/capybara/spec/session/element/assert_match_selector.rb +31 -0
- data/lib/capybara/spec/session/element/match_css_spec.rb +17 -0
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +23 -0
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +63 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +1 -0
- data/lib/capybara/spec/session/execute_script_spec.rb +1 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +1 -0
- data/lib/capybara/spec/session/find_button_spec.rb +1 -0
- data/lib/capybara/spec/session/find_by_id_spec.rb +1 -0
- data/lib/capybara/spec/session/find_field_spec.rb +1 -0
- data/lib/capybara/spec/session/find_link_spec.rb +1 -0
- data/lib/capybara/spec/session/find_spec.rb +1 -0
- data/lib/capybara/spec/session/first_spec.rb +1 -0
- data/lib/capybara/spec/session/go_back_spec.rb +1 -0
- data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
- data/lib/capybara/spec/session/has_button_spec.rb +1 -0
- data/lib/capybara/spec/session/has_css_spec.rb +1 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +1 -0
- data/lib/capybara/spec/session/has_field_spec.rb +13 -0
- data/lib/capybara/spec/session/has_link_spec.rb +1 -0
- data/lib/capybara/spec/session/has_select_spec.rb +19 -0
- data/lib/capybara/spec/session/has_selector_spec.rb +6 -0
- data/lib/capybara/spec/session/has_table_spec.rb +1 -0
- data/lib/capybara/spec/session/has_text_spec.rb +1 -0
- data/lib/capybara/spec/session/has_title_spec.rb +1 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
- data/lib/capybara/spec/session/headers.rb +1 -0
- data/lib/capybara/spec/session/html_spec.rb +1 -0
- data/lib/capybara/spec/session/node_spec.rb +29 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +35 -1
- data/lib/capybara/spec/session/response_code.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +3 -2
- data/lib/capybara/spec/session/save_page_spec.rb +34 -2
- data/lib/capybara/spec/session/save_screenshot_spec.rb +31 -1
- data/lib/capybara/spec/session/screenshot_spec.rb +3 -1
- data/lib/capybara/spec/session/select_spec.rb +1 -0
- data/lib/capybara/spec/session/selectors_spec.rb +35 -0
- data/lib/capybara/spec/session/text_spec.rb +1 -0
- data/lib/capybara/spec/session/title_spec.rb +2 -1
- data/lib/capybara/spec/session/uncheck_spec.rb +1 -0
- data/lib/capybara/spec/session/unselect_spec.rb +1 -0
- data/lib/capybara/spec/session/visit_spec.rb +1 -0
- data/lib/capybara/spec/session/window/become_closed_spec.rb +1 -0
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -0
- data/lib/capybara/spec/session/window/window_spec.rb +1 -8
- data/lib/capybara/spec/session/window/windows_spec.rb +1 -0
- data/lib/capybara/spec/session/window/within_window_spec.rb +2 -3
- data/lib/capybara/spec/session/within_frame_spec.rb +1 -0
- data/lib/capybara/spec/session/within_spec.rb +1 -0
- data/lib/capybara/spec/spec_helper.rb +2 -0
- data/lib/capybara/spec/test_app.rb +1 -0
- data/lib/capybara/spec/views/buttons.erb +1 -0
- data/lib/capybara/spec/views/fieldsets.erb +2 -1
- data/lib/capybara/spec/views/form.erb +24 -0
- data/lib/capybara/spec/views/frame_child.erb +2 -1
- data/lib/capybara/spec/views/frame_one.erb +2 -1
- data/lib/capybara/spec/views/frame_parent.erb +2 -1
- data/lib/capybara/spec/views/frame_two.erb +2 -1
- data/lib/capybara/spec/views/header_links.erb +1 -0
- data/lib/capybara/spec/views/host_links.erb +1 -0
- data/lib/capybara/spec/views/path.erb +1 -0
- data/lib/capybara/spec/views/popup_one.erb +1 -0
- data/lib/capybara/spec/views/popup_two.erb +1 -0
- data/lib/capybara/spec/views/postback.erb +2 -1
- data/lib/capybara/spec/views/tables.erb +1 -0
- data/lib/capybara/spec/views/with_base_tag.erb +1 -0
- data/lib/capybara/spec/views/with_count.erb +2 -1
- data/lib/capybara/spec/views/with_hover.erb +2 -1
- data/lib/capybara/spec/views/with_html.erb +3 -0
- data/lib/capybara/spec/views/with_html_entities.erb +1 -0
- data/lib/capybara/spec/views/with_js.erb +5 -0
- data/lib/capybara/spec/views/with_scope.erb +1 -0
- data/lib/capybara/spec/views/with_simple_html.erb +2 -1
- data/lib/capybara/spec/views/with_slow_unload.erb +17 -0
- data/lib/capybara/spec/views/with_title.erb +2 -1
- data/lib/capybara/spec/views/with_unload_alert.erb +12 -0
- data/lib/capybara/spec/views/with_windows.erb +1 -0
- data/lib/capybara/spec/views/within_frames.erb +2 -1
- data/lib/capybara/version.rb +2 -1
- data/lib/capybara/window.rb +1 -0
- data/spec/basic_node_spec.rb +1 -0
- data/spec/capybara_spec.rb +28 -1
- data/spec/dsl_spec.rb +1 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +1 -0
- data/spec/fixtures/selenium_driver_rspec_success.rb +1 -0
- data/spec/rack_test_spec.rb +1 -0
- data/spec/result_spec.rb +1 -0
- data/spec/rspec/features_spec.rb +1 -0
- data/spec/rspec/matchers_spec.rb +1 -0
- data/spec/rspec/scenarios_spec.rb +1 -0
- data/spec/rspec/views_spec.rb +1 -0
- data/spec/rspec_spec.rb +1 -0
- data/spec/selector_spec.rb +55 -1
- data/spec/selenium_spec.rb +6 -2
- data/spec/selenium_spec_chrome.rb +1 -0
- data/spec/server_spec.rb +101 -13
- data/spec/spec_helper.rb +1 -0
- metadata +16 -6
data/lib/capybara/query.rb
CHANGED
@@ -1,135 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'capybara/queries/selector_query'
|
1
3
|
module Capybara
|
2
4
|
# @deprecated This class and its methods are not supposed to be used by users of Capybara's public API.
|
3
5
|
# It may be removed in future versions of Capybara.
|
4
|
-
|
5
|
-
attr_accessor :selector, :locator, :options, :expression, :find, :negative
|
6
|
-
|
7
|
-
VALID_KEYS = [:text, :visible, :between, :count, :maximum, :minimum, :exact, :match, :wait]
|
8
|
-
VALID_MATCH = [:first, :smart, :prefer_exact, :one]
|
9
|
-
|
10
|
-
def initialize(*args)
|
11
|
-
@options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
12
|
-
|
13
|
-
if args[0].is_a?(Symbol)
|
14
|
-
@selector = Selector.all[args[0]]
|
15
|
-
@locator = args[1]
|
16
|
-
else
|
17
|
-
@selector = Selector.all.values.find { |s| s.match?(args[0]) }
|
18
|
-
@locator = args[0]
|
19
|
-
end
|
20
|
-
@selector ||= Selector.all[Capybara.default_selector]
|
21
|
-
|
22
|
-
# for compatibility with Capybara 2.0
|
23
|
-
if Capybara.exact_options and @selector == Selector.all[:option]
|
24
|
-
@options[:exact] = true
|
25
|
-
end
|
26
|
-
|
27
|
-
@expression = @selector.call(@locator)
|
28
|
-
assert_valid_keys
|
29
|
-
end
|
30
|
-
|
31
|
-
def name; selector.name; end
|
32
|
-
def label; selector.label or selector.name; end
|
33
|
-
|
34
|
-
def description
|
35
|
-
@description = "#{label} #{locator.inspect}"
|
36
|
-
@description << " with text #{options[:text].inspect}" if options[:text]
|
37
|
-
@description << selector.description(options)
|
38
|
-
@description
|
39
|
-
end
|
40
|
-
|
41
|
-
def matches_filters?(node)
|
42
|
-
if options[:text]
|
43
|
-
regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text].to_s)
|
44
|
-
return false if not node.text(visible).match(regexp)
|
45
|
-
end
|
46
|
-
case visible
|
47
|
-
when :visible then return false unless node.visible?
|
48
|
-
when :hidden then return false if node.visible?
|
49
|
-
end
|
50
|
-
selector.custom_filters.each do |name, filter|
|
51
|
-
if options.has_key?(name)
|
52
|
-
return false unless filter.matches?(node, options[name])
|
53
|
-
elsif filter.default?
|
54
|
-
return false unless filter.matches?(node, filter.default)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def visible
|
60
|
-
if options.has_key?(:visible)
|
61
|
-
case @options[:visible]
|
62
|
-
when true then :visible
|
63
|
-
when false then :all
|
64
|
-
else @options[:visible]
|
65
|
-
end
|
66
|
-
else
|
67
|
-
if Capybara.ignore_hidden_elements
|
68
|
-
:visible
|
69
|
-
else
|
70
|
-
:all
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def exact?
|
76
|
-
if options.has_key?(:exact)
|
77
|
-
@options[:exact]
|
78
|
-
else
|
79
|
-
Capybara.exact
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def match
|
84
|
-
if options.has_key?(:match)
|
85
|
-
@options[:match]
|
86
|
-
else
|
87
|
-
Capybara.match
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def xpath(exact=nil)
|
92
|
-
exact = self.exact? if exact == nil
|
93
|
-
if @expression.respond_to?(:to_xpath) and exact
|
94
|
-
@expression.to_xpath(:exact)
|
95
|
-
else
|
96
|
-
@expression.to_s
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def css
|
101
|
-
@expression
|
102
|
-
end
|
103
|
-
|
104
|
-
# @api private
|
105
|
-
def resolve_for(node, exact = nil)
|
106
|
-
node.synchronize do
|
107
|
-
children = if selector.format == :css
|
108
|
-
node.find_css(self.css)
|
109
|
-
else
|
110
|
-
node.find_xpath(self.xpath(exact))
|
111
|
-
end.map do |child|
|
112
|
-
if node.is_a?(Capybara::Node::Base)
|
113
|
-
Capybara::Node::Element.new(node.session, child, node, self)
|
114
|
-
else
|
115
|
-
Capybara::Node::Simple.new(child)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
Capybara::Result.new(children, self)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
private
|
123
|
-
|
124
|
-
def valid_keys
|
125
|
-
COUNT_KEYS + [:text, :visible, :exact, :match, :wait] + @selector.custom_filters.keys
|
126
|
-
end
|
127
|
-
|
128
|
-
def assert_valid_keys
|
129
|
-
super
|
130
|
-
unless VALID_MATCH.include?(match)
|
131
|
-
raise ArgumentError, "invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(", ")}"
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
6
|
+
Query = Queries::SelectorQuery
|
135
7
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'rack/test'
|
2
3
|
require 'rack/utils'
|
3
4
|
require 'mime/types'
|
@@ -65,11 +66,11 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
65
66
|
def find_xpath(selector)
|
66
67
|
browser.find(:xpath, selector)
|
67
68
|
end
|
68
|
-
|
69
|
+
|
69
70
|
def find_css(selector)
|
70
71
|
browser.find(:css,selector)
|
71
72
|
end
|
72
|
-
|
73
|
+
|
73
74
|
def html
|
74
75
|
browser.html
|
75
76
|
end
|
@@ -77,7 +78,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
77
78
|
def dom
|
78
79
|
browser.dom
|
79
80
|
end
|
80
|
-
|
81
|
+
|
81
82
|
def title
|
82
83
|
browser.title
|
83
84
|
end
|
@@ -86,8 +87,9 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
86
87
|
@browser = nil
|
87
88
|
end
|
88
89
|
|
90
|
+
# @deprecated This method is being removed
|
89
91
|
def browser_initialized?
|
90
|
-
!@browser.nil?
|
92
|
+
super && !@browser.nil?
|
91
93
|
end
|
92
94
|
|
93
95
|
def get(*args, &block); browser.get(*args, &block); end
|
data/lib/capybara/rails.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'capybara/dsl'
|
2
3
|
|
3
4
|
Capybara.app = Rack::Builder.new do
|
@@ -11,7 +12,7 @@ Capybara.app = Rack::Builder.new do
|
|
11
12
|
end
|
12
13
|
end.to_app
|
13
14
|
|
14
|
-
Capybara.
|
15
|
+
Capybara.save_path = Rails.root.join('tmp/capybara')
|
15
16
|
|
16
17
|
# Override default rack_test driver to respect data-method attributes.
|
17
18
|
Capybara.register_driver :rack_test do |app|
|
data/lib/capybara/result.rb
CHANGED
data/lib/capybara/rspec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Capybara
|
2
3
|
module RSpecMatchers
|
3
4
|
class Matcher
|
4
|
-
include ::RSpec::Matchers::Composable if defined?(::RSpec::Expectations::Version) && RSpec::Expectations::Version::STRING
|
5
|
+
include ::RSpec::Matchers::Composable if defined?(::RSpec::Expectations::Version) && (Gem::Version.new(RSpec::Expectations::Version::STRING) >= Gem::Version.new('3.0'))
|
5
6
|
|
6
7
|
def wrap(actual)
|
7
8
|
if actual.respond_to?("has_selector?")
|
@@ -38,7 +39,7 @@ module Capybara
|
|
38
39
|
end
|
39
40
|
|
40
41
|
def query
|
41
|
-
@query ||= Capybara::
|
42
|
+
@query ||= Capybara::Queries::SelectorQuery.new(*@args)
|
42
43
|
end
|
43
44
|
|
44
45
|
# RSpec 2 compatibility:
|
@@ -160,7 +161,7 @@ module Capybara
|
|
160
161
|
|
161
162
|
class BecomeClosed
|
162
163
|
def initialize(options)
|
163
|
-
@wait_time = Capybara::
|
164
|
+
@wait_time = Capybara::Queries::SelectorQuery.new(options).wait
|
164
165
|
end
|
165
166
|
|
166
167
|
def matches?(window)
|
@@ -186,18 +187,68 @@ module Capybara
|
|
186
187
|
alias_method :failure_message_for_should_not, :failure_message_when_negated
|
187
188
|
end
|
188
189
|
|
190
|
+
class MatchSelector < Matcher
|
191
|
+
attr_reader :failure_message, :failure_message_when_negated
|
192
|
+
|
193
|
+
def initialize(*args)
|
194
|
+
@args = args
|
195
|
+
end
|
196
|
+
|
197
|
+
def matches?(actual)
|
198
|
+
actual.assert_matches_selector(*@args)
|
199
|
+
rescue Capybara::ExpectationNotMet => e
|
200
|
+
@failure_message = e.message
|
201
|
+
return false
|
202
|
+
end
|
203
|
+
|
204
|
+
def does_not_match?(actual)
|
205
|
+
actual.assert_not_matches_selector(*@args)
|
206
|
+
rescue Capybara::ExpectationNotMet => e
|
207
|
+
@failure_message_when_negated = e.message
|
208
|
+
return false
|
209
|
+
end
|
210
|
+
|
211
|
+
def description
|
212
|
+
"match #{query.description}"
|
213
|
+
end
|
214
|
+
|
215
|
+
def query
|
216
|
+
@query ||= Capybara::Queries::MatchQuery.new(*@args)
|
217
|
+
end
|
218
|
+
|
219
|
+
# RSpec 2 compatibility:
|
220
|
+
alias_method :failure_message_for_should, :failure_message
|
221
|
+
alias_method :failure_message_for_should_not, :failure_message_when_negated
|
222
|
+
end
|
223
|
+
|
189
224
|
def have_selector(*args)
|
190
225
|
HaveSelector.new(*args)
|
191
226
|
end
|
192
227
|
|
228
|
+
def match_selector(*args)
|
229
|
+
MatchSelector.new(*args)
|
230
|
+
end
|
231
|
+
# defined_negated_matcher was added in RSpec 3.1 - it's syntactic sugar only since a user can do
|
232
|
+
# expect(page).not_to match_selector, so not sure we really need to support not_match_selector for prior to RSpec 3.1
|
233
|
+
::RSpec::Matchers.define_negated_matcher :not_match_selector, :match_selector if defined?(::RSpec::Expectations::Version) && (Gem::Version.new(RSpec::Expectations::Version::STRING) >= Gem::Version.new('3.1'))
|
234
|
+
|
235
|
+
|
193
236
|
def have_xpath(xpath, options={})
|
194
237
|
HaveSelector.new(:xpath, xpath, options)
|
195
238
|
end
|
196
239
|
|
240
|
+
def match_xpath(xpath, options={})
|
241
|
+
MatchSelector.new(:xpath, xpath, options)
|
242
|
+
end
|
243
|
+
|
197
244
|
def have_css(css, options={})
|
198
245
|
HaveSelector.new(:css, css, options)
|
199
246
|
end
|
200
247
|
|
248
|
+
def match_css(css, options={})
|
249
|
+
MatchSelector.new(:css, css, options)
|
250
|
+
end
|
251
|
+
|
201
252
|
def have_text(*args)
|
202
253
|
HaveText.new(*args)
|
203
254
|
end
|
data/lib/capybara/selector.rb
CHANGED
@@ -1,42 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'capybara/selector/filter'
|
3
|
+
|
1
4
|
module Capybara
|
2
5
|
class Selector
|
3
|
-
class Filter
|
4
|
-
def initialize(name, block, options={})
|
5
|
-
@name = name
|
6
|
-
@block = block
|
7
|
-
@options = options
|
8
|
-
@options[:valid_values] = [true,false] if options[:boolean]
|
9
|
-
end
|
10
|
-
|
11
|
-
def default?
|
12
|
-
@options.has_key?(:default)
|
13
|
-
end
|
14
|
-
|
15
|
-
def default
|
16
|
-
@options[:default]
|
17
|
-
end
|
18
|
-
|
19
|
-
def matches?(node, value)
|
20
|
-
return true if skip?(value)
|
21
|
-
|
22
|
-
if @options.has_key?(:valid_values) && !Array(@options[:valid_values]).include?(value)
|
23
|
-
msg = "Invalid value #{value.inspect} passed to filter #{@name} - "
|
24
|
-
if default?
|
25
|
-
warn msg + "defaulting to #{default}"
|
26
|
-
value = default
|
27
|
-
else
|
28
|
-
warn msg + "skipping"
|
29
|
-
return true
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
@block.call(node, value)
|
34
|
-
end
|
35
|
-
|
36
|
-
def skip?(value)
|
37
|
-
@options.has_key?(:skip_if) && value == @options[:skip_if]
|
38
|
-
end
|
39
|
-
end
|
40
6
|
|
41
7
|
attr_reader :name, :custom_filters, :format
|
42
8
|
|
@@ -65,24 +31,19 @@ module Capybara
|
|
65
31
|
@label = nil
|
66
32
|
@failure_message = nil
|
67
33
|
@description = nil
|
34
|
+
@format = nil
|
35
|
+
@expression = nil
|
68
36
|
instance_eval(&block)
|
69
37
|
end
|
70
38
|
|
71
39
|
def xpath(&block)
|
72
|
-
if block
|
73
|
-
|
74
|
-
@xpath, @css = block, nil
|
75
|
-
end
|
76
|
-
@xpath
|
40
|
+
@format, @expression = :xpath, block if block
|
41
|
+
format == :xpath ? @expression : nil
|
77
42
|
end
|
78
43
|
|
79
|
-
# Same as xpath, but wrap in XPath.css().
|
80
44
|
def css(&block)
|
81
|
-
if block
|
82
|
-
|
83
|
-
@css, @xpath = block, nil
|
84
|
-
end
|
85
|
-
@css
|
45
|
+
@format, @expression = :css, block if block
|
46
|
+
format == :css ? @expression : nil
|
86
47
|
end
|
87
48
|
|
88
49
|
def match(&block)
|
@@ -100,10 +61,10 @@ module Capybara
|
|
100
61
|
end
|
101
62
|
|
102
63
|
def call(locator)
|
103
|
-
if
|
104
|
-
@
|
64
|
+
if format
|
65
|
+
@expression.call(locator)
|
105
66
|
else
|
106
|
-
|
67
|
+
warn "Selector has no format"
|
107
68
|
end
|
108
69
|
end
|
109
70
|
|
@@ -118,6 +79,17 @@ module Capybara
|
|
118
79
|
def describe &block
|
119
80
|
@description = block
|
120
81
|
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def locate_field(xpath, locator)
|
86
|
+
locate_field = xpath[XPath.attr(:id).equals(locator) |
|
87
|
+
XPath.attr(:name).equals(locator) |
|
88
|
+
XPath.attr(:placeholder).equals(locator) |
|
89
|
+
XPath.attr(:id).equals(XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for))]
|
90
|
+
locate_field += XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath)
|
91
|
+
locate_field
|
92
|
+
end
|
121
93
|
end
|
122
94
|
end
|
123
95
|
|
@@ -134,7 +106,11 @@ Capybara.add_selector(:id) do
|
|
134
106
|
end
|
135
107
|
|
136
108
|
Capybara.add_selector(:field) do
|
137
|
-
xpath
|
109
|
+
xpath do |locator|
|
110
|
+
xpath = XPath.descendant(:input, :textarea, :select)[~XPath.attr(:type).one_of('submit', 'image', 'hidden')]
|
111
|
+
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
112
|
+
xpath
|
113
|
+
end
|
138
114
|
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
|
139
115
|
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
|
140
116
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
@@ -147,31 +123,42 @@ Capybara.add_selector(:field) do
|
|
147
123
|
node[:type] == type
|
148
124
|
end
|
149
125
|
end
|
126
|
+
filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) }
|
150
127
|
describe do |options|
|
151
|
-
desc, states =
|
128
|
+
desc, states = String.new, []
|
152
129
|
desc << " of type #{options[:type].inspect}" if options[:type]
|
153
130
|
desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
|
154
131
|
states << 'checked' if options[:checked] || (options.has_key?(:unchecked) && !options[:unchecked])
|
155
132
|
states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked])
|
156
133
|
states << 'disabled' if options[:disabled] == true
|
157
134
|
desc << " that is #{states.join(' and ')}" unless states.empty?
|
135
|
+
desc << " with the multiple attribute" if options[:multiple] == true
|
136
|
+
desc << " without the multiple attribute" if options[:multiple] === false
|
158
137
|
desc
|
159
138
|
end
|
160
139
|
end
|
161
140
|
|
162
141
|
Capybara.add_selector(:fieldset) do
|
163
|
-
xpath
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
xpath { |locator| XPath::HTML.link_or_button(locator) }
|
169
|
-
filter(:disabled, default: false, boolean: true) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
|
170
|
-
describe { |options| " that is disabled" if options[:disabled] }
|
142
|
+
xpath do |locator|
|
143
|
+
xpath = XPath.descendant(:fieldset)
|
144
|
+
xpath = xpath[XPath.attr(:id).equals(locator.to_s) | XPath.child(:legend)[XPath.string.n.is(locator.to_s)]] unless locator.nil?
|
145
|
+
xpath
|
146
|
+
end
|
171
147
|
end
|
172
148
|
|
173
149
|
Capybara.add_selector(:link) do
|
174
|
-
xpath
|
150
|
+
xpath do |locator|
|
151
|
+
xpath = XPath.descendant(:a)[XPath.attr(:href)]
|
152
|
+
unless locator.nil?
|
153
|
+
locator = locator.to_s
|
154
|
+
xpath = xpath[XPath.attr(:id).equals(locator) |
|
155
|
+
XPath.string.n.is(locator) |
|
156
|
+
XPath.attr(:title).is(locator) |
|
157
|
+
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
158
|
+
end
|
159
|
+
xpath
|
160
|
+
end
|
161
|
+
|
175
162
|
filter(:href) do |node, href|
|
176
163
|
if href.is_a? Regexp
|
177
164
|
node[:href].match href
|
@@ -179,31 +166,77 @@ Capybara.add_selector(:link) do
|
|
179
166
|
node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href.to_s)], minimum: 0)
|
180
167
|
end
|
181
168
|
end
|
169
|
+
|
182
170
|
describe { |options| " with href #{options[:href].inspect}" if options[:href] }
|
183
171
|
end
|
184
172
|
|
185
173
|
Capybara.add_selector(:button) do
|
186
|
-
xpath
|
174
|
+
xpath do |locator|
|
175
|
+
input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
|
176
|
+
btn_xpath = XPath.descendant(:button)
|
177
|
+
image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).equals('image')]
|
178
|
+
|
179
|
+
unless locator.nil?
|
180
|
+
locator = locator.to_s
|
181
|
+
input_btn_xpath = input_btn_xpath[XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)]
|
182
|
+
btn_xpath = btn_xpath[XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.string.n.is(locator) | XPath.attr(:title).is(locator)]
|
183
|
+
image_btn_xpath = image_btn_xpath[XPath.attr(:alt).is(locator)]
|
184
|
+
end
|
185
|
+
|
186
|
+
input_btn_xpath + btn_xpath + image_btn_xpath
|
187
|
+
end
|
188
|
+
|
187
189
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
190
|
+
|
188
191
|
describe { |options| " that is disabled" if options[:disabled] == true }
|
189
192
|
end
|
190
193
|
|
194
|
+
Capybara.add_selector(:link_or_button) do
|
195
|
+
label "link or button"
|
196
|
+
xpath do |locator|
|
197
|
+
self.class.all.values_at(:link, :button).map {|selector| selector.xpath.call(locator)}.reduce(:+)
|
198
|
+
end
|
199
|
+
|
200
|
+
filter(:disabled, default: false, boolean: true) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
|
201
|
+
|
202
|
+
describe { |options| " that is disabled" if options[:disabled] }
|
203
|
+
end
|
204
|
+
|
191
205
|
Capybara.add_selector(:fillable_field) do
|
192
206
|
label "field"
|
193
|
-
xpath
|
207
|
+
xpath do |locator|
|
208
|
+
xpath = XPath.descendant(:input, :textarea)[~XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
|
209
|
+
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
210
|
+
xpath
|
211
|
+
end
|
212
|
+
|
194
213
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
195
|
-
|
214
|
+
filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) }
|
215
|
+
|
216
|
+
describe do |options|
|
217
|
+
desc = String.new
|
218
|
+
desc << " that is disabled" if options[:disabled] == true
|
219
|
+
desc << " with the multiple attribute" if options[:multiple] == true
|
220
|
+
desc << " without the multiple attribute" if options[:multiple] === false
|
221
|
+
desc
|
222
|
+
end
|
196
223
|
end
|
197
224
|
|
198
225
|
Capybara.add_selector(:radio_button) do
|
199
226
|
label "radio button"
|
200
|
-
xpath
|
227
|
+
xpath do |locator|
|
228
|
+
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('radio')]
|
229
|
+
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
230
|
+
xpath
|
231
|
+
end
|
232
|
+
|
201
233
|
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
|
202
234
|
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
|
203
235
|
filter(:option) { |node, value| node.value == value.to_s }
|
204
236
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
237
|
+
|
205
238
|
describe do |options|
|
206
|
-
desc, states =
|
239
|
+
desc, states = String.new, []
|
207
240
|
desc << " with value #{options[:option].inspect}" if options[:option]
|
208
241
|
states << 'checked' if options[:checked] || (options.has_key?(:unchecked) && !options[:unchecked])
|
209
242
|
states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked])
|
@@ -214,13 +247,19 @@ Capybara.add_selector(:radio_button) do
|
|
214
247
|
end
|
215
248
|
|
216
249
|
Capybara.add_selector(:checkbox) do
|
217
|
-
xpath
|
250
|
+
xpath do |locator|
|
251
|
+
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('checkbox')]
|
252
|
+
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
253
|
+
xpath
|
254
|
+
end
|
255
|
+
|
218
256
|
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
|
219
257
|
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
|
220
258
|
filter(:option) { |node, value| node.value == value.to_s }
|
221
259
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
260
|
+
|
222
261
|
describe do |options|
|
223
|
-
desc, states =
|
262
|
+
desc, states = String.new, []
|
224
263
|
desc << " with value #{options[:option].inspect}" if options[:option]
|
225
264
|
states << 'checked' if options[:checked] || (options.has_key?(:unchecked) && !options[:unchecked])
|
226
265
|
states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked])
|
@@ -232,7 +271,12 @@ end
|
|
232
271
|
|
233
272
|
Capybara.add_selector(:select) do
|
234
273
|
label "select box"
|
235
|
-
xpath
|
274
|
+
xpath do |locator|
|
275
|
+
xpath = XPath.descendant(:select)
|
276
|
+
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
277
|
+
xpath
|
278
|
+
end
|
279
|
+
|
236
280
|
filter(:options) do |node, options|
|
237
281
|
if node.visible?
|
238
282
|
actual = node.all(:xpath, './/option').map { |option| option.text }
|
@@ -253,27 +297,83 @@ Capybara.add_selector(:select) do
|
|
253
297
|
[selected].flatten.sort == actual.sort
|
254
298
|
end
|
255
299
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
300
|
+
filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) }
|
301
|
+
|
256
302
|
describe do |options|
|
257
|
-
desc =
|
303
|
+
desc = String.new
|
258
304
|
desc << " with options #{options[:options].inspect}" if options[:options]
|
259
305
|
desc << " with at least options #{options[:with_options].inspect}" if options[:with_options]
|
260
306
|
desc << " with #{options[:selected].inspect} selected" if options[:selected]
|
261
307
|
desc << " that is disabled" if options[:disabled] == true
|
308
|
+
desc << " that allows multiple selection" if options[:multiple] == true
|
309
|
+
desc << " that only allows single selection" if options[:multiple] === false
|
262
310
|
desc
|
263
311
|
end
|
264
312
|
end
|
265
313
|
|
266
314
|
Capybara.add_selector(:option) do
|
267
|
-
xpath
|
315
|
+
xpath do |locator|
|
316
|
+
xpath = XPath.descendant(:option)
|
317
|
+
xpath = xpath[XPath.string.n.is(locator.to_s)] unless locator.nil?
|
318
|
+
xpath
|
319
|
+
end
|
320
|
+
|
321
|
+
filter(:disabled, boolean: true) { |node, value| not(value ^ node.disabled?) }
|
322
|
+
filter(:selected, boolean: true) { |node, value| not(value ^ node.selected?) }
|
323
|
+
|
324
|
+
describe do |options|
|
325
|
+
desc = String.new
|
326
|
+
desc << " that is#{' not' unless options[:disabled]} disabled" if options.has_key?(:disabled)
|
327
|
+
desc << " that is#{' not' unless options[:selected]} selected" if options.has_key?(:selected)
|
328
|
+
desc
|
329
|
+
end
|
268
330
|
end
|
269
331
|
|
270
332
|
Capybara.add_selector(:file_field) do
|
271
333
|
label "file field"
|
272
|
-
xpath
|
334
|
+
xpath do |locator|
|
335
|
+
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('file')]
|
336
|
+
xpath = locate_field(xpath, locator.to_s) unless locator.nil?
|
337
|
+
xpath
|
338
|
+
end
|
339
|
+
|
273
340
|
filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
274
|
-
|
341
|
+
filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) }
|
342
|
+
|
343
|
+
describe do |options|
|
344
|
+
desc = String.new
|
345
|
+
desc << " that is disabled" if options[:disabled] == true
|
346
|
+
desc << " that allows multiple files" if options[:multiple] == true
|
347
|
+
desc << " that only allows a single file" if options[:multiple] === false
|
348
|
+
desc
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
Capybara.add_selector(:label) do
|
353
|
+
label "label"
|
354
|
+
xpath do |locator|
|
355
|
+
xpath = XPath.descendant(:label)
|
356
|
+
xpath = xpath[XPath.string.n.is(locator.to_s) | XPath.attr(:id).equals(locator.to_s)] unless locator.nil?
|
357
|
+
xpath
|
358
|
+
end
|
359
|
+
|
360
|
+
filter(:for) do |node, field_or_value|
|
361
|
+
if field_or_value.is_a? Capybara::Node::Element
|
362
|
+
if field_or_value[:id] && (field_or_value[:id] == node[:for])
|
363
|
+
true
|
364
|
+
else
|
365
|
+
field_or_value.find_xpath('./ancestor::label[1]').include? node.base
|
366
|
+
end
|
367
|
+
else
|
368
|
+
node[:for] == field_or_value.to_s
|
369
|
+
end
|
370
|
+
end
|
275
371
|
end
|
276
372
|
|
277
373
|
Capybara.add_selector(:table) do
|
278
|
-
xpath
|
374
|
+
xpath do |locator|
|
375
|
+
xpath = XPath.descendant(:table)
|
376
|
+
xpath = xpath[XPath.attr(:id).equals(locator.to_s) | XPath.descendant(:caption).is(locator.to_s)] unless locator.nil?
|
377
|
+
xpath
|
378
|
+
end
|
279
379
|
end
|