capybara 2.5.0 → 2.18.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 +5 -5
- data/.yard/templates_custom/default/class/html/selectors.erb +38 -0
- data/.yard/templates_custom/default/class/html/setup.rb +17 -0
- data/.yard/yard_extensions.rb +78 -0
- data/.yardopts +1 -0
- data/History.md +413 -10
- data/License.txt +1 -1
- data/README.md +237 -130
- data/lib/capybara/config.rb +132 -0
- data/lib/capybara/cucumber.rb +3 -1
- data/lib/capybara/driver/base.rb +27 -6
- data/lib/capybara/driver/node.rb +14 -5
- data/lib/capybara/dsl.rb +2 -3
- data/lib/capybara/helpers.rb +13 -65
- data/lib/capybara/minitest/spec.rb +177 -0
- data/lib/capybara/minitest.rb +278 -0
- data/lib/capybara/node/actions.rb +180 -24
- data/lib/capybara/node/base.rb +17 -5
- data/lib/capybara/node/document.rb +5 -0
- data/lib/capybara/node/document_matchers.rb +15 -14
- data/lib/capybara/node/element.rb +55 -7
- data/lib/capybara/node/finders.rb +179 -67
- data/lib/capybara/node/matchers.rb +301 -105
- data/lib/capybara/node/simple.rb +15 -4
- data/lib/capybara/queries/ancestor_query.rb +25 -0
- data/lib/capybara/queries/base_query.rb +69 -3
- data/lib/capybara/queries/current_path_query.rb +17 -8
- data/lib/capybara/queries/match_query.rb +19 -0
- data/lib/capybara/queries/selector_query.rb +251 -0
- data/lib/capybara/queries/sibling_query.rb +25 -0
- data/lib/capybara/queries/text_query.rb +67 -16
- data/lib/capybara/queries/title_query.rb +4 -2
- data/lib/capybara/query.rb +3 -131
- data/lib/capybara/rack_test/browser.rb +14 -5
- data/lib/capybara/rack_test/css_handlers.rb +1 -0
- data/lib/capybara/rack_test/driver.rb +15 -8
- data/lib/capybara/rack_test/form.rb +34 -12
- data/lib/capybara/rack_test/node.rb +29 -12
- data/lib/capybara/rails.rb +3 -3
- data/lib/capybara/result.rb +104 -9
- data/lib/capybara/rspec/compound.rb +95 -0
- data/lib/capybara/rspec/features.rb +17 -6
- data/lib/capybara/rspec/matcher_proxies.rb +45 -0
- data/lib/capybara/rspec/matchers.rb +199 -80
- data/lib/capybara/rspec.rb +4 -2
- data/lib/capybara/selector/css.rb +30 -0
- data/lib/capybara/selector/filter.rb +20 -0
- data/lib/capybara/selector/filter_set.rb +74 -0
- data/lib/capybara/selector/filters/base.rb +33 -0
- data/lib/capybara/selector/filters/expression_filter.rb +40 -0
- data/lib/capybara/selector/filters/node_filter.rb +27 -0
- data/lib/capybara/selector/selector.rb +276 -0
- data/lib/capybara/selector.rb +452 -157
- data/lib/capybara/selenium/driver.rb +282 -81
- data/lib/capybara/selenium/node.rb +144 -46
- data/lib/capybara/server.rb +59 -16
- data/lib/capybara/session/config.rb +114 -0
- data/lib/capybara/session/matchers.rb +29 -19
- data/lib/capybara/session.rb +378 -143
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- data/lib/capybara/spec/public/jquery-ui.js +13 -791
- data/lib/capybara/spec/public/jquery.js +4 -9045
- data/lib/capybara/spec/public/test.js +45 -11
- data/lib/capybara/spec/session/accept_alert_spec.rb +30 -7
- data/lib/capybara/spec/session/accept_confirm_spec.rb +14 -2
- data/lib/capybara/spec/session/accept_prompt_spec.rb +35 -6
- data/lib/capybara/spec/session/all_spec.rb +45 -32
- data/lib/capybara/spec/session/ancestor_spec.rb +85 -0
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +110 -0
- data/lib/capybara/spec/session/assert_current_path.rb +15 -2
- data/lib/capybara/spec/session/assert_selector.rb +29 -28
- data/lib/capybara/spec/session/assert_text.rb +59 -20
- data/lib/capybara/spec/session/assert_title.rb +25 -11
- data/lib/capybara/spec/session/attach_file_spec.rb +42 -4
- data/lib/capybara/spec/session/body_spec.rb +1 -0
- data/lib/capybara/spec/session/check_spec.rb +90 -14
- data/lib/capybara/spec/session/choose_spec.rb +31 -5
- data/lib/capybara/spec/session/click_button_spec.rb +20 -9
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +15 -9
- data/lib/capybara/spec/session/click_link_spec.rb +39 -15
- data/lib/capybara/spec/session/current_scope_spec.rb +2 -1
- data/lib/capybara/spec/session/current_url_spec.rb +12 -3
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +6 -5
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +4 -3
- data/lib/capybara/spec/session/element/assert_match_selector.rb +36 -0
- data/lib/capybara/spec/session/element/match_css_spec.rb +23 -0
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +23 -0
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +106 -0
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +22 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +23 -1
- data/lib/capybara/spec/session/execute_script_spec.rb +22 -3
- data/lib/capybara/spec/session/fill_in_spec.rb +50 -32
- data/lib/capybara/spec/session/find_button_spec.rb +43 -2
- data/lib/capybara/spec/session/find_by_id_spec.rb +3 -2
- data/lib/capybara/spec/session/find_field_spec.rb +42 -6
- data/lib/capybara/spec/session/find_link_spec.rb +22 -3
- data/lib/capybara/spec/session/find_spec.rb +103 -57
- data/lib/capybara/spec/session/first_spec.rb +34 -18
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +103 -0
- data/lib/capybara/spec/session/{within_frame_spec.rb → frame/within_frame_spec.rb} +44 -2
- data/lib/capybara/spec/session/go_back_spec.rb +2 -1
- data/lib/capybara/spec/session/go_forward_spec.rb +2 -1
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
- data/lib/capybara/spec/session/has_button_spec.rb +17 -8
- data/lib/capybara/spec/session/has_css_spec.rb +85 -73
- data/lib/capybara/spec/session/has_current_path_spec.rb +91 -7
- data/lib/capybara/spec/session/has_field_spec.rb +93 -58
- data/lib/capybara/spec/session/has_link_spec.rb +9 -8
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +76 -0
- data/lib/capybara/spec/session/has_select_spec.rb +159 -59
- data/lib/capybara/spec/session/has_selector_spec.rb +64 -28
- data/lib/capybara/spec/session/has_table_spec.rb +1 -0
- data/lib/capybara/spec/session/has_text_spec.rb +27 -12
- data/lib/capybara/spec/session/has_title_spec.rb +22 -4
- data/lib/capybara/spec/session/has_xpath_spec.rb +32 -29
- data/lib/capybara/spec/session/headers.rb +2 -1
- data/lib/capybara/spec/session/html_spec.rb +4 -3
- data/lib/capybara/spec/session/node_spec.rb +198 -38
- data/lib/capybara/spec/session/refresh_spec.rb +28 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +46 -5
- data/lib/capybara/spec/session/response_code.rb +2 -1
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -5
- 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 +4 -2
- data/lib/capybara/spec/session/select_spec.rb +34 -32
- data/lib/capybara/spec/session/selectors_spec.rb +65 -0
- data/lib/capybara/spec/session/sibling_spec.rb +52 -0
- data/lib/capybara/spec/session/text_spec.rb +4 -4
- data/lib/capybara/spec/session/title_spec.rb +2 -1
- data/lib/capybara/spec/session/uncheck_spec.rb +42 -2
- data/lib/capybara/spec/session/unselect_spec.rb +17 -16
- data/lib/capybara/spec/session/visit_spec.rb +77 -2
- data/lib/capybara/spec/session/window/become_closed_spec.rb +12 -11
- 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 +16 -11
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +7 -4
- data/lib/capybara/spec/session/window/window_spec.rb +36 -29
- data/lib/capybara/spec/session/window/windows_spec.rb +1 -0
- data/lib/capybara/spec/session/window/within_window_spec.rb +31 -7
- data/lib/capybara/spec/session/within_spec.rb +14 -6
- data/lib/capybara/spec/spec_helper.rb +37 -4
- data/lib/capybara/spec/test_app.rb +15 -3
- 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 +169 -9
- data/lib/capybara/spec/views/frame_child.erb +10 -2
- data/lib/capybara/spec/views/frame_one.erb +2 -1
- data/lib/capybara/spec/views/frame_parent.erb +3 -2
- 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/initial_alert.erb +10 -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_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +7 -1
- data/lib/capybara/spec/views/with_html.erb +40 -2
- data/lib/capybara/spec/views/with_html_entities.erb +1 -0
- data/lib/capybara/spec/views/with_js.erb +32 -1
- 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 +14 -0
- data/lib/capybara/spec/views/with_windows.erb +7 -0
- data/lib/capybara/spec/views/within_frames.erb +3 -2
- data/lib/capybara/version.rb +2 -1
- data/lib/capybara/window.rb +20 -3
- data/lib/capybara.rb +189 -93
- data/spec/basic_node_spec.rb +7 -6
- data/spec/capybara_spec.rb +90 -4
- data/spec/dsl_spec.rb +3 -1
- data/spec/filter_set_spec.rb +28 -0
- data/spec/fixtures/capybara.csv +1 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +5 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +5 -1
- data/spec/minitest_spec.rb +130 -0
- data/spec/minitest_spec_spec.rb +135 -0
- data/spec/per_session_config_spec.rb +67 -0
- data/spec/rack_test_spec.rb +50 -7
- data/spec/result_spec.rb +76 -0
- data/spec/rspec/features_spec.rb +21 -8
- data/spec/rspec/scenarios_spec.rb +21 -0
- data/spec/rspec/{matchers_spec.rb → shared_spec_matchers.rb} +160 -54
- data/spec/rspec/views_spec.rb +5 -0
- data/spec/rspec_matchers_spec.rb +46 -0
- data/spec/rspec_spec.rb +79 -1
- data/spec/selector_spec.rb +199 -0
- data/spec/selenium_spec_chrome.rb +54 -9
- data/spec/selenium_spec_firefox.rb +68 -0
- data/spec/selenium_spec_marionette.rb +127 -0
- data/spec/server_spec.rb +102 -14
- data/spec/session_spec.rb +54 -0
- data/spec/shared_selenium_session.rb +215 -0
- data/spec/spec_helper.rb +7 -0
- metadata +140 -15
- data/spec/selenium_spec.rb +0 -128
data/lib/capybara/selector.rb
CHANGED
|
@@ -1,247 +1,542 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
if @options.has_key?(:valid_values) && !Array(@options[:valid_values]).include?(value)
|
|
21
|
-
warn "Invalid value #{value.inspect} passed to filter #{@name}"
|
|
22
|
-
end
|
|
23
|
-
@block.call(node, value)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
attr_reader :name, :custom_filters, :format
|
|
28
|
-
|
|
29
|
-
class << self
|
|
30
|
-
def all
|
|
31
|
-
@selectors ||= {}
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def add(name, &block)
|
|
35
|
-
all[name.to_sym] = Capybara::Selector.new(name.to_sym, &block)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def remove(name)
|
|
39
|
-
all.delete(name.to_sym)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'capybara/selector/selector'
|
|
3
|
+
Capybara::Selector::FilterSet.add(:_field) do
|
|
4
|
+
filter(:checked, :boolean) { |node, value| not(value ^ node.checked?) }
|
|
5
|
+
filter(:unchecked, :boolean) { |node, value| (value ^ node.checked?) }
|
|
6
|
+
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
|
7
|
+
filter(:multiple, :boolean) { |node, value| !(value ^ node.multiple?) }
|
|
42
8
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@custom_filters = {}
|
|
46
|
-
@match = nil
|
|
47
|
-
@label = nil
|
|
48
|
-
@failure_message = nil
|
|
49
|
-
@description = nil
|
|
50
|
-
instance_eval(&block)
|
|
51
|
-
end
|
|
9
|
+
expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name).equals(val)] }
|
|
10
|
+
expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder).equals(val)] }
|
|
52
11
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@css
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def match(&block)
|
|
67
|
-
@match = block if block
|
|
68
|
-
@match
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def label(label=nil)
|
|
72
|
-
@label = label if label
|
|
73
|
-
@label
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def description(options={})
|
|
77
|
-
(@description && @description.call(options)).to_s
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def call(locator)
|
|
81
|
-
if @format==:css
|
|
82
|
-
@css.call(locator)
|
|
83
|
-
else
|
|
84
|
-
@xpath.call(locator)
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def match?(locator)
|
|
89
|
-
@match and @match.call(locator)
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def filter(name, options={}, &block)
|
|
93
|
-
@custom_filters[name] = Filter.new(name, block, options)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def describe &block
|
|
97
|
-
@description = block
|
|
98
|
-
end
|
|
12
|
+
describe do |options|
|
|
13
|
+
desc, states = String.new, []
|
|
14
|
+
states << 'checked' if options[:checked] || (options[:unchecked] == false)
|
|
15
|
+
states << 'not checked' if options[:unchecked] || (options[:checked] == false)
|
|
16
|
+
states << 'disabled' if options[:disabled] == true
|
|
17
|
+
states << 'not disabled' if options[:disabled] == false
|
|
18
|
+
desc << " that is #{states.join(' and ')}" unless states.empty?
|
|
19
|
+
desc << " with the multiple attribute" if options[:multiple] == true
|
|
20
|
+
desc << " without the multiple attribute" if options[:multiple] == false
|
|
21
|
+
desc
|
|
99
22
|
end
|
|
100
23
|
end
|
|
101
24
|
|
|
25
|
+
##
|
|
26
|
+
#
|
|
27
|
+
# Select elements by XPath expression
|
|
28
|
+
#
|
|
29
|
+
# @locator An XPath expression
|
|
30
|
+
#
|
|
102
31
|
Capybara.add_selector(:xpath) do
|
|
103
32
|
xpath { |xpath| xpath }
|
|
104
33
|
end
|
|
105
34
|
|
|
35
|
+
##
|
|
36
|
+
#
|
|
37
|
+
# Select elements by CSS selector
|
|
38
|
+
#
|
|
39
|
+
# @locator A CSS selector
|
|
40
|
+
#
|
|
106
41
|
Capybara.add_selector(:css) do
|
|
107
42
|
css { |css| css }
|
|
108
43
|
end
|
|
109
44
|
|
|
45
|
+
##
|
|
46
|
+
#
|
|
47
|
+
# Select element by id
|
|
48
|
+
#
|
|
49
|
+
# @locator The id of the element to match
|
|
50
|
+
#
|
|
110
51
|
Capybara.add_selector(:id) do
|
|
111
52
|
xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
|
|
112
53
|
end
|
|
113
54
|
|
|
55
|
+
##
|
|
56
|
+
#
|
|
57
|
+
# Select field elements (input [not of type submit, image, or hidden], textarea, select)
|
|
58
|
+
#
|
|
59
|
+
# @locator Matches against the id, name, or placeholder
|
|
60
|
+
# @filter [String] :id Matches the id attribute
|
|
61
|
+
# @filter [String] :name Matches the name attribute
|
|
62
|
+
# @filter [String] :placeholder Matches the placeholder attribute
|
|
63
|
+
# @filter [String] :type Matches the type attribute of the field or element type for 'textarea' and 'select'
|
|
64
|
+
# @filter [Boolean] :readonly
|
|
65
|
+
# @filter [String] :with Matches the current value of the field
|
|
66
|
+
# @filter [String, Array<String>] :class Matches the class(es) provided
|
|
67
|
+
# @filter [Boolean] :checked Match checked fields?
|
|
68
|
+
# @filter [Boolean] :unchecked Match unchecked fields?
|
|
69
|
+
# @filter [Boolean] :disabled Match disabled field?
|
|
70
|
+
# @filter [Boolean] :multiple Match fields that accept multiple values
|
|
114
71
|
Capybara.add_selector(:field) do
|
|
115
|
-
xpath
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
72
|
+
xpath do |locator, options|
|
|
73
|
+
xpath = XPath.descendant(:input, :textarea, :select)[~XPath.attr(:type).one_of('submit', 'image', 'hidden')]
|
|
74
|
+
locate_field(xpath, locator, options)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
expression_filter(:type) do |expr, type|
|
|
78
|
+
type = type.to_s
|
|
122
79
|
if ['textarea', 'select'].include?(type)
|
|
123
|
-
|
|
80
|
+
expr.axis(:self, type.to_sym)
|
|
124
81
|
else
|
|
125
|
-
|
|
82
|
+
expr[XPath.attr(:type).equals(type)]
|
|
126
83
|
end
|
|
127
84
|
end
|
|
85
|
+
|
|
86
|
+
filter_set(:_field) # checked/unchecked/disabled/multiple/name/placeholder
|
|
87
|
+
|
|
88
|
+
filter(:readonly, :boolean) { |node, value| not(value ^ node.readonly?) }
|
|
89
|
+
filter(:with) do |node, with|
|
|
90
|
+
with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
|
|
91
|
+
end
|
|
128
92
|
describe do |options|
|
|
129
|
-
desc
|
|
93
|
+
desc = String.new
|
|
94
|
+
(expression_filters.keys - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.has_key?(ef) }
|
|
130
95
|
desc << " of type #{options[:type].inspect}" if options[:type]
|
|
131
96
|
desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
|
|
132
|
-
states << 'checked' if options[:checked] || (options.has_key?(:unchecked) && !options[:unchecked])
|
|
133
|
-
states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked])
|
|
134
|
-
states << 'disabled' if options[:disabled]
|
|
135
|
-
desc << " that is #{states.join(' and ')}" unless states.empty?
|
|
136
97
|
desc
|
|
137
98
|
end
|
|
138
99
|
end
|
|
139
100
|
|
|
101
|
+
##
|
|
102
|
+
#
|
|
103
|
+
# Select fieldset elements
|
|
104
|
+
#
|
|
105
|
+
# @locator Matches id or contents of wrapped legend
|
|
106
|
+
#
|
|
107
|
+
# @filter [String] :id Matches id attribute
|
|
108
|
+
# @filter [String] :legend Matches contents of wrapped legend
|
|
109
|
+
# @filter [String, Array<String>] :class Matches the class(es) provided
|
|
110
|
+
#
|
|
140
111
|
Capybara.add_selector(:fieldset) do
|
|
141
|
-
xpath
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
filter(:disabled, default: false, boolean: true) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
|
|
148
|
-
describe { |options| " that is disabled" if options[:disabled] }
|
|
112
|
+
xpath(:legend) do |locator, options|
|
|
113
|
+
xpath = XPath.descendant(:fieldset)
|
|
114
|
+
xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.child(:legend)[XPath.string.n.is(locator.to_s)]] unless locator.nil?
|
|
115
|
+
xpath = xpath[XPath.child(:legend)[XPath.string.n.is(options[:legend])]] if options[:legend]
|
|
116
|
+
xpath
|
|
117
|
+
end
|
|
149
118
|
end
|
|
150
119
|
|
|
120
|
+
##
|
|
121
|
+
#
|
|
122
|
+
# Find links ( <a> elements with an href attribute )
|
|
123
|
+
#
|
|
124
|
+
# @locator Matches the id or title attributes, or the string content of the link, or the alt attribute of a contained img element
|
|
125
|
+
#
|
|
126
|
+
# @filter [String] :id Matches the id attribute
|
|
127
|
+
# @filter [String] :title Matches the title attribute
|
|
128
|
+
# @filter [String] :alt Matches the alt attribute of a contained img element
|
|
129
|
+
# @filter [String] :class Matches the class(es) provided
|
|
130
|
+
# @filter [String, Regexp,nil] :href Matches the normalized href of the link, if nil will find <a> elements with no href attribute
|
|
131
|
+
#
|
|
151
132
|
Capybara.add_selector(:link) do
|
|
152
|
-
xpath
|
|
133
|
+
xpath(:title, :alt) do |locator, options={}|
|
|
134
|
+
xpath = XPath.descendant(:a)
|
|
135
|
+
xpath = if options.fetch(:href, true).nil?
|
|
136
|
+
xpath[~XPath.attr(:href)]
|
|
137
|
+
else
|
|
138
|
+
xpath[XPath.attr(:href)]
|
|
139
|
+
end
|
|
140
|
+
unless locator.nil?
|
|
141
|
+
locator = locator.to_s
|
|
142
|
+
matchers = XPath.attr(:id).equals(locator).or(
|
|
143
|
+
XPath.string.n.is(locator)).or(
|
|
144
|
+
XPath.attr(:title).is(locator)).or(
|
|
145
|
+
XPath.descendant(:img)[XPath.attr(:alt).is(locator)])
|
|
146
|
+
matchers = matchers.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
|
147
|
+
xpath = xpath[matchers]
|
|
148
|
+
end
|
|
149
|
+
xpath = [:title].inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
|
150
|
+
xpath = xpath[XPath.descendant(:img)[XPath.attr(:alt).equals(options[:alt])]] if options[:alt]
|
|
151
|
+
xpath
|
|
152
|
+
end
|
|
153
|
+
|
|
153
154
|
filter(:href) do |node, href|
|
|
154
|
-
|
|
155
|
+
case href
|
|
156
|
+
when nil
|
|
157
|
+
true
|
|
158
|
+
when Regexp
|
|
155
159
|
node[:href].match href
|
|
156
160
|
else
|
|
157
161
|
node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href.to_s)], minimum: 0)
|
|
158
162
|
end
|
|
159
163
|
end
|
|
160
|
-
|
|
164
|
+
|
|
165
|
+
describe do |options|
|
|
166
|
+
desc = String.new()
|
|
167
|
+
desc << " with href #{options[:href].inspect}" if options[:href]
|
|
168
|
+
desc << " with no href attribute" if options.fetch(:href, true).nil?
|
|
169
|
+
end
|
|
161
170
|
end
|
|
162
171
|
|
|
172
|
+
##
|
|
173
|
+
#
|
|
174
|
+
# Find buttons ( input [of type submit, reset, image, button] or button elements )
|
|
175
|
+
#
|
|
176
|
+
# @locator Matches the id, value, or title attributes, string content of a button, or the alt attribute of an image type button or of a descendant image of a button
|
|
177
|
+
#
|
|
178
|
+
# @filter [String] :id Matches the id attribute
|
|
179
|
+
# @filter [String] :title Matches the title attribute
|
|
180
|
+
# @filter [String] :class Matches the class(es) provided
|
|
181
|
+
# @filter [String] :value Matches the value of an input button
|
|
182
|
+
#
|
|
163
183
|
Capybara.add_selector(:button) do
|
|
164
|
-
xpath
|
|
165
|
-
|
|
184
|
+
xpath(:value, :title) do |locator, options={}|
|
|
185
|
+
input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
|
|
186
|
+
btn_xpath = XPath.descendant(:button)
|
|
187
|
+
image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).equals('image')]
|
|
188
|
+
|
|
189
|
+
unless locator.nil?
|
|
190
|
+
locator = locator.to_s
|
|
191
|
+
locator_matches = XPath.attr(:id).equals(locator).or XPath.attr(:value).is(locator).or XPath.attr(:title).is(locator)
|
|
192
|
+
locator_matches = locator_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
|
193
|
+
|
|
194
|
+
input_btn_xpath = input_btn_xpath[locator_matches]
|
|
195
|
+
|
|
196
|
+
btn_xpath = btn_xpath[locator_matches.or XPath.string.n.is(locator).or XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
|
197
|
+
|
|
198
|
+
alt_matches = XPath.attr(:alt).is(locator)
|
|
199
|
+
alt_matches = alt_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
|
200
|
+
image_btn_xpath = image_btn_xpath[alt_matches]
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
res_xpath = input_btn_xpath.union(btn_xpath).union(image_btn_xpath)
|
|
204
|
+
|
|
205
|
+
res_xpath = expression_filters.keys.inject(res_xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
|
206
|
+
|
|
207
|
+
res_xpath
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
|
211
|
+
|
|
212
|
+
describe do |options|
|
|
213
|
+
desc = String.new
|
|
214
|
+
desc << " that is disabled" if options[:disabled] == true
|
|
215
|
+
desc << describe_all_expression_filters(options)
|
|
216
|
+
desc
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
##
|
|
221
|
+
#
|
|
222
|
+
# Find links or buttons
|
|
223
|
+
#
|
|
224
|
+
Capybara.add_selector(:link_or_button) do
|
|
225
|
+
label "link or button"
|
|
226
|
+
xpath do |locator, options|
|
|
227
|
+
self.class.all.values_at(:link, :button).map {|selector| selector.xpath.call(locator, options)}.reduce(:union)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
|
|
231
|
+
|
|
166
232
|
describe { |options| " that is disabled" if options[:disabled] }
|
|
167
233
|
end
|
|
168
234
|
|
|
235
|
+
##
|
|
236
|
+
#
|
|
237
|
+
# Find text fillable fields ( textarea, input [not of type submit, image, radio, checkbox, hidden, file] )
|
|
238
|
+
#
|
|
239
|
+
# @locator Matches against the id, name, or placeholder
|
|
240
|
+
# @filter [String] :id Matches the id attribute
|
|
241
|
+
# @filter [String] :name Matches the name attribute
|
|
242
|
+
# @filter [String] :placeholder Matches the placeholder attribute
|
|
243
|
+
# @filter [String] :with Matches the current value of the field
|
|
244
|
+
# @filter [String] :type Matches the type attribute of the field or element type for 'textarea'
|
|
245
|
+
# @filter [String, Array<String>] :class Matches the class(es) provided
|
|
246
|
+
# @filter [Boolean] :disabled Match disabled field?
|
|
247
|
+
# @filter [Boolean] :multiple Match fields that accept multiple values
|
|
248
|
+
#
|
|
169
249
|
Capybara.add_selector(:fillable_field) do
|
|
170
250
|
label "field"
|
|
171
|
-
xpath
|
|
172
|
-
|
|
173
|
-
|
|
251
|
+
xpath do |locator, options|
|
|
252
|
+
xpath = XPath.descendant(:input, :textarea)[~XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
|
|
253
|
+
locate_field(xpath, locator, options)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
expression_filter(:type) do |expr, type|
|
|
257
|
+
type = type.to_s
|
|
258
|
+
if ['textarea'].include?(type)
|
|
259
|
+
expr.axis(:self, type.to_sym)
|
|
260
|
+
else
|
|
261
|
+
expr[XPath.attr(:type).equals(type)]
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
filter_set(:_field, [:disabled, :multiple, :name, :placeholder])
|
|
266
|
+
|
|
267
|
+
filter(:with) do |node, with|
|
|
268
|
+
with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
describe do |options|
|
|
272
|
+
desc = String.new
|
|
273
|
+
desc << describe_all_expression_filters(options)
|
|
274
|
+
desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
|
|
275
|
+
desc
|
|
276
|
+
end
|
|
174
277
|
end
|
|
175
278
|
|
|
279
|
+
##
|
|
280
|
+
#
|
|
281
|
+
# Find radio buttons
|
|
282
|
+
#
|
|
283
|
+
# @locator Match id, name, or associated label text
|
|
284
|
+
# @filter [String] :id Matches the id attribute
|
|
285
|
+
# @filter [String] :name Matches the name attribute
|
|
286
|
+
# @filter [String, Array<String>] :class Matches the class(es) provided
|
|
287
|
+
# @filter [Boolean] :checked Match checked fields?
|
|
288
|
+
# @filter [Boolean] :unchecked Match unchecked fields?
|
|
289
|
+
# @filter [Boolean] :disabled Match disabled field?
|
|
290
|
+
# @filter [String] :option Match the value
|
|
291
|
+
#
|
|
176
292
|
Capybara.add_selector(:radio_button) do
|
|
177
293
|
label "radio button"
|
|
178
|
-
xpath
|
|
179
|
-
|
|
180
|
-
|
|
294
|
+
xpath do |locator, options|
|
|
295
|
+
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('radio')]
|
|
296
|
+
locate_field(xpath, locator, options)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
filter_set(:_field, [:checked, :unchecked, :disabled, :name])
|
|
300
|
+
|
|
181
301
|
filter(:option) { |node, value| node.value == value.to_s }
|
|
182
|
-
|
|
302
|
+
|
|
183
303
|
describe do |options|
|
|
184
|
-
desc
|
|
304
|
+
desc = String.new
|
|
185
305
|
desc << " with value #{options[:option].inspect}" if options[:option]
|
|
186
|
-
|
|
187
|
-
states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked])
|
|
188
|
-
states << 'disabled' if options[:disabled]
|
|
189
|
-
desc << " that is #{states.join(' and ')}" unless states.empty?
|
|
306
|
+
desc << describe_all_expression_filters(options)
|
|
190
307
|
desc
|
|
191
308
|
end
|
|
192
309
|
end
|
|
193
310
|
|
|
311
|
+
##
|
|
312
|
+
#
|
|
313
|
+
# Find checkboxes
|
|
314
|
+
#
|
|
315
|
+
# @locator Match id, name, or associated label text
|
|
316
|
+
# @filter [String] :id Matches the id attribute
|
|
317
|
+
# @filter [String] :name Matches the name attribute
|
|
318
|
+
# @filter [String, Array<String>] :class Matches the class(es) provided
|
|
319
|
+
# @filter [Boolean] :checked Match checked fields?
|
|
320
|
+
# @filter [Boolean] :unchecked Match unchecked fields?
|
|
321
|
+
# @filter [Boolean] :disabled Match disabled field?
|
|
322
|
+
# @filter [String] :option Match the value
|
|
323
|
+
#
|
|
194
324
|
Capybara.add_selector(:checkbox) do
|
|
195
|
-
xpath
|
|
196
|
-
|
|
197
|
-
|
|
325
|
+
xpath do |locator, options|
|
|
326
|
+
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('checkbox')]
|
|
327
|
+
locate_field(xpath, locator, options)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
filter_set(:_field, [:checked, :unchecked, :disabled, :name])
|
|
331
|
+
|
|
198
332
|
filter(:option) { |node, value| node.value == value.to_s }
|
|
199
|
-
|
|
333
|
+
|
|
200
334
|
describe do |options|
|
|
201
|
-
desc
|
|
335
|
+
desc = String.new
|
|
202
336
|
desc << " with value #{options[:option].inspect}" if options[:option]
|
|
203
|
-
|
|
204
|
-
states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked])
|
|
205
|
-
states << 'disabled' if options[:disabled]
|
|
206
|
-
desc << " that is #{states.join(' and ')}" unless states.empty?
|
|
337
|
+
desc << describe_all_expression_filters(options)
|
|
207
338
|
desc
|
|
208
339
|
end
|
|
209
340
|
end
|
|
210
341
|
|
|
342
|
+
##
|
|
343
|
+
#
|
|
344
|
+
# Find select elements
|
|
345
|
+
#
|
|
346
|
+
# @locator Match id, name, placeholder, or associated label text
|
|
347
|
+
# @filter [String] :id Matches the id attribute
|
|
348
|
+
# @filter [String] :name Matches the name attribute
|
|
349
|
+
# @filter [String] :placeholder Matches the placeholder attribute
|
|
350
|
+
# @filter [String, Array<String>] :class Matches the class(es) provided
|
|
351
|
+
# @filter [Boolean] :disabled Match disabled field?
|
|
352
|
+
# @filter [Boolean] :multiple Match fields that accept multiple values
|
|
353
|
+
# @filter [Array<String>] :options Exact match options
|
|
354
|
+
# @filter [Array<String>] :with_options Partial match options
|
|
355
|
+
# @filter [String, Array<String>] :selected Match the selection(s)
|
|
356
|
+
# @filter [String, Array<String>] :with_selected Partial match the selection(s)
|
|
357
|
+
#
|
|
211
358
|
Capybara.add_selector(:select) do
|
|
212
359
|
label "select box"
|
|
213
|
-
xpath
|
|
360
|
+
xpath do |locator, options|
|
|
361
|
+
xpath = XPath.descendant(:select)
|
|
362
|
+
locate_field(xpath, locator, options)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
filter_set(:_field, [:disabled, :multiple, :name, :placeholder])
|
|
366
|
+
|
|
214
367
|
filter(:options) do |node, options|
|
|
215
|
-
|
|
368
|
+
if node.visible?
|
|
369
|
+
actual = node.all(:xpath, './/option').map { |option| option.text }
|
|
370
|
+
else
|
|
371
|
+
actual = node.all(:xpath, './/option', visible: false).map { |option| option.text(:all) }
|
|
372
|
+
end
|
|
216
373
|
options.sort == actual.sort
|
|
217
374
|
end
|
|
218
|
-
|
|
375
|
+
|
|
376
|
+
filter(:with_options) do |node, options|
|
|
377
|
+
finder_settings = { minimum: 0 }
|
|
378
|
+
if !node.visible?
|
|
379
|
+
finder_settings[:visible] = false
|
|
380
|
+
end
|
|
381
|
+
options.all? { |option| node.first(:option, option, finder_settings) }
|
|
382
|
+
end
|
|
383
|
+
|
|
219
384
|
filter(:selected) do |node, selected|
|
|
220
|
-
actual = node.all(:xpath, './/option').select
|
|
221
|
-
|
|
385
|
+
actual = node.all(:xpath, './/option', visible: false).select(&:selected?).map { |option| option.text(:all) }
|
|
386
|
+
Array(selected).sort == actual.sort
|
|
222
387
|
end
|
|
223
|
-
|
|
388
|
+
|
|
389
|
+
filter(:with_selected) do |node, selected|
|
|
390
|
+
actual = node.all(:xpath, './/option', visible: false).select(&:selected?).map { |option| option.text(:all) }
|
|
391
|
+
(Array(selected) - actual).empty?
|
|
392
|
+
end
|
|
393
|
+
|
|
224
394
|
describe do |options|
|
|
225
|
-
desc =
|
|
395
|
+
desc = String.new
|
|
226
396
|
desc << " with options #{options[:options].inspect}" if options[:options]
|
|
227
397
|
desc << " with at least options #{options[:with_options].inspect}" if options[:with_options]
|
|
228
398
|
desc << " with #{options[:selected].inspect} selected" if options[:selected]
|
|
229
|
-
desc << "
|
|
399
|
+
desc << " with at least #{options[:with_selected].inspect} selected" if options[:with_selected]
|
|
400
|
+
desc << describe_all_expression_filters(options)
|
|
230
401
|
desc
|
|
231
402
|
end
|
|
232
403
|
end
|
|
233
404
|
|
|
405
|
+
##
|
|
406
|
+
#
|
|
407
|
+
# Find option elements
|
|
408
|
+
#
|
|
409
|
+
# @locator Match text of option
|
|
410
|
+
# @filter [Boolean] :disabled Match disabled option
|
|
411
|
+
# @filter [Boolean] :selected Match selected option
|
|
412
|
+
#
|
|
234
413
|
Capybara.add_selector(:option) do
|
|
235
|
-
xpath
|
|
414
|
+
xpath do |locator|
|
|
415
|
+
xpath = XPath.descendant(:option)
|
|
416
|
+
xpath = xpath[XPath.string.n.is(locator.to_s)] unless locator.nil?
|
|
417
|
+
xpath
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
filter(:disabled, :boolean) { |node, value| not(value ^ node.disabled?) }
|
|
421
|
+
filter(:selected, :boolean) { |node, value| not(value ^ node.selected?) }
|
|
422
|
+
|
|
423
|
+
describe do |options|
|
|
424
|
+
desc = String.new
|
|
425
|
+
desc << " that is#{' not' unless options[:disabled]} disabled" if options.has_key?(:disabled)
|
|
426
|
+
desc << " that is#{' not' unless options[:selected]} selected" if options.has_key?(:selected)
|
|
427
|
+
desc
|
|
428
|
+
end
|
|
236
429
|
end
|
|
237
430
|
|
|
431
|
+
##
|
|
432
|
+
#
|
|
433
|
+
# Find file input elements
|
|
434
|
+
#
|
|
435
|
+
# @locator Match id, name, or associated label text
|
|
436
|
+
# @filter [String] :id Matches the id attribute
|
|
437
|
+
# @filter [String] :name Matches the name attribute
|
|
438
|
+
# @filter [String, Array<String>] :class Matches the class(es) provided
|
|
439
|
+
# @filter [Boolean] :disabled Match disabled field?
|
|
440
|
+
# @filter [Boolean] :multiple Match field that accepts multiple values
|
|
441
|
+
#
|
|
238
442
|
Capybara.add_selector(:file_field) do
|
|
239
443
|
label "file field"
|
|
240
|
-
xpath
|
|
241
|
-
|
|
242
|
-
|
|
444
|
+
xpath do |locator, options|
|
|
445
|
+
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('file')]
|
|
446
|
+
locate_field(xpath, locator, options)
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
filter_set(:_field, [:disabled, :multiple, :name])
|
|
450
|
+
|
|
451
|
+
describe do |options|
|
|
452
|
+
desc = String.new
|
|
453
|
+
desc << describe_all_expression_filters(options)
|
|
454
|
+
desc
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
##
|
|
459
|
+
#
|
|
460
|
+
# Find label elements
|
|
461
|
+
#
|
|
462
|
+
# @locator Match id or text contents
|
|
463
|
+
# @filter [Element, String] :for The element or id of the element associated with the label
|
|
464
|
+
#
|
|
465
|
+
Capybara.add_selector(:label) do
|
|
466
|
+
label "label"
|
|
467
|
+
xpath(:for) do |locator, options|
|
|
468
|
+
xpath = XPath.descendant(:label)
|
|
469
|
+
xpath = xpath[XPath.string.n.is(locator.to_s).or XPath.attr(:id).equals(locator.to_s)] unless locator.nil?
|
|
470
|
+
if options.has_key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
|
|
471
|
+
xpath = xpath[XPath.attr(:for).equals(options[:for].to_s).or((~XPath.attr(:for)).and(XPath.descendant()[XPath.attr(:id).equals(options[:for].to_s)]))]
|
|
472
|
+
end
|
|
473
|
+
xpath
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
filter(:for) do |node, field_or_value|
|
|
477
|
+
if field_or_value.is_a? Capybara::Node::Element
|
|
478
|
+
if node[:for]
|
|
479
|
+
field_or_value[:id] == node[:for]
|
|
480
|
+
else
|
|
481
|
+
field_or_value.find_xpath('./ancestor::label[1]').include? node.base
|
|
482
|
+
end
|
|
483
|
+
else
|
|
484
|
+
#Non element values were handled through the expression filter
|
|
485
|
+
true
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
describe do |options|
|
|
490
|
+
desc = String.new
|
|
491
|
+
desc << " for #{options[:for]}" if options[:for]
|
|
492
|
+
desc
|
|
493
|
+
end
|
|
243
494
|
end
|
|
244
495
|
|
|
496
|
+
##
|
|
497
|
+
#
|
|
498
|
+
# Find table elements
|
|
499
|
+
#
|
|
500
|
+
# @locator id or caption text of table
|
|
501
|
+
# @filter [String] :id Match id attribute of table
|
|
502
|
+
# @filter [String] :caption Match text of associated caption
|
|
503
|
+
# @filter [String, Array<String>] :class Matches the class(es) provided
|
|
504
|
+
#
|
|
245
505
|
Capybara.add_selector(:table) do
|
|
246
|
-
xpath
|
|
506
|
+
xpath(:caption) do |locator, options|
|
|
507
|
+
xpath = XPath.descendant(:table)
|
|
508
|
+
xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.descendant(:caption).is(locator.to_s)] unless locator.nil?
|
|
509
|
+
xpath = xpath[XPath.descendant(:caption).equals(options[:caption])] if options[:caption]
|
|
510
|
+
xpath
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
describe do |options|
|
|
514
|
+
desc = String.new
|
|
515
|
+
desc << " with caption #{options[:caption]}" if options[:caption]
|
|
516
|
+
desc
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
##
|
|
521
|
+
#
|
|
522
|
+
# Find frame/iframe elements
|
|
523
|
+
#
|
|
524
|
+
# @locator Match id or name
|
|
525
|
+
# @filter [String] :id Match id attribute
|
|
526
|
+
# @filter [String] :name Match name attribute
|
|
527
|
+
# @filter [String, Array<String>] :class Matches the class(es) provided
|
|
528
|
+
#
|
|
529
|
+
Capybara.add_selector(:frame) do
|
|
530
|
+
xpath(:name) do |locator, options|
|
|
531
|
+
xpath = XPath.descendant(:iframe).union(XPath.descendant(:frame))
|
|
532
|
+
xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.attr(:name).equals(locator)] unless locator.nil?
|
|
533
|
+
xpath = expression_filters.keys.inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
|
534
|
+
xpath
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
describe do |options|
|
|
538
|
+
desc = String.new
|
|
539
|
+
desc << " with name #{options[:name]}" if options[:name]
|
|
540
|
+
desc
|
|
541
|
+
end
|
|
247
542
|
end
|