capybara 2.6.2 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|