capybara 3.20.1 → 3.22.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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/History.md +35 -0
  4. data/README.md +16 -11
  5. data/lib/capybara/driver/node.rb +6 -2
  6. data/lib/capybara/helpers.rb +1 -1
  7. data/lib/capybara/minitest/spec.rb +20 -7
  8. data/lib/capybara/minitest.rb +18 -5
  9. data/lib/capybara/node/actions.rb +36 -29
  10. data/lib/capybara/node/element.rb +123 -82
  11. data/lib/capybara/node/finders.rb +42 -53
  12. data/lib/capybara/node/matchers.rb +161 -72
  13. data/lib/capybara/queries/ancestor_query.rb +9 -7
  14. data/lib/capybara/queries/sibling_query.rb +11 -4
  15. data/lib/capybara/result.rb +2 -0
  16. data/lib/capybara/rspec/matchers/have_ancestor.rb +30 -0
  17. data/lib/capybara/rspec/matchers/have_sibling.rb +30 -0
  18. data/lib/capybara/rspec/matchers.rb +16 -1
  19. data/lib/capybara/selector/definition/checkbox.rb +8 -5
  20. data/lib/capybara/selector/definition/css.rb +4 -1
  21. data/lib/capybara/selector/definition/radio_button.rb +8 -5
  22. data/lib/capybara/selector/filter_set.rb +4 -2
  23. data/lib/capybara/selector/regexp_disassembler.rb +2 -2
  24. data/lib/capybara/selector.rb +153 -171
  25. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +35 -1
  26. data/lib/capybara/selenium/extensions/html5_drag.rb +63 -0
  27. data/lib/capybara/selenium/node.rb +4 -0
  28. data/lib/capybara/selenium/nodes/chrome_node.rb +4 -0
  29. data/lib/capybara/selenium/nodes/firefox_node.rb +4 -13
  30. data/lib/capybara/selenium/nodes/ie_node.rb +14 -3
  31. data/lib/capybara/selenium/nodes/safari_node.rb +0 -13
  32. data/lib/capybara/session/config.rb +1 -1
  33. data/lib/capybara/session.rb +74 -71
  34. data/lib/capybara/spec/public/test.js +29 -3
  35. data/lib/capybara/spec/session/attach_file_spec.rb +13 -5
  36. data/lib/capybara/spec/session/check_spec.rb +6 -0
  37. data/lib/capybara/spec/session/choose_spec.rb +6 -0
  38. data/lib/capybara/spec/session/has_ancestor_spec.rb +44 -0
  39. data/lib/capybara/spec/session/has_css_spec.rb +4 -3
  40. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  41. data/lib/capybara/spec/session/node_spec.rb +85 -0
  42. data/lib/capybara/spec/session/select_spec.rb +5 -5
  43. data/lib/capybara/spec/spec_helper.rb +4 -0
  44. data/lib/capybara/spec/views/form.erb +1 -1
  45. data/lib/capybara/version.rb +1 -1
  46. data/lib/capybara/window.rb +10 -10
  47. data/lib/capybara.rb +42 -36
  48. data/spec/minitest_spec.rb +11 -1
  49. data/spec/minitest_spec_spec.rb +11 -1
  50. data/spec/regexp_dissassembler_spec.rb +1 -1
  51. data/spec/selenium_spec_firefox.rb +2 -0
  52. data/spec/selenium_spec_ie.rb +13 -7
  53. data/spec/selenium_spec_safari.rb +2 -0
  54. data/spec/shared_selenium_session.rb +1 -51
  55. metadata +23 -18
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/rspec/matchers/base'
4
+ require 'capybara/rspec/matchers/count_sugar'
5
+
6
+ module Capybara
7
+ module RSpecMatchers
8
+ module Matchers
9
+ class HaveSibling < WrappedElementMatcher
10
+ include CountSugar
11
+
12
+ def element_matches?(el)
13
+ el.assert_sibling(*@args, &@filter_block)
14
+ end
15
+
16
+ def element_does_not_match?(el)
17
+ el.assert_no_sibling(*@args, &@filter_block)
18
+ end
19
+
20
+ def description
21
+ "have sibling #{query.description}"
22
+ end
23
+
24
+ def query
25
+ @query ||= Capybara::Queries::SiblingQuery.new(*session_query_args, &@filter_block)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'capybara/rspec/matchers/have_selector'
4
+ require 'capybara/rspec/matchers/have_ancestor'
5
+ require 'capybara/rspec/matchers/have_sibling'
4
6
  require 'capybara/rspec/matchers/match_selector'
5
7
  require 'capybara/rspec/matchers/have_current_path'
6
8
  require 'capybara/rspec/matchers/match_style'
@@ -138,7 +140,8 @@ module Capybara
138
140
  end
139
141
 
140
142
  %w[selector css xpath text title current_path link button
141
- field checked_field unchecked_field select table].each do |matcher_type|
143
+ field checked_field unchecked_field select table
144
+ sibling ancestor].each do |matcher_type|
142
145
  define_method "have_no_#{matcher_type}" do |*args, &optional_filter_block|
143
146
  Matchers::NegatedMatcher.new(send("have_#{matcher_type}", *args, &optional_filter_block))
144
147
  end
@@ -151,6 +154,18 @@ module Capybara
151
154
  end
152
155
  end
153
156
 
157
+ # RSpec matcher for whether sibling element(s) matching a given selector exist
158
+ # See {Capybara::Node::Matcher#assert_sibling}
159
+ def have_sibling(*args, &optional_filter_block)
160
+ Matchers::HaveSibling.new(*args, &optional_filter_block)
161
+ end
162
+
163
+ # RSpec matcher for whether ancestor element(s) matching a given selector exist
164
+ # See {Capybara::Node::Matcher#assert_ancestor}
165
+ def have_ancestor(*args, &optional_filter_block)
166
+ Matchers::HaveAncestor.new(*args, &optional_filter_block)
167
+ end
168
+
154
169
  ##
155
170
  # Wait for window to become closed.
156
171
  # @example
@@ -10,14 +10,17 @@ Capybara.add_selector(:checkbox, locator_type: [String, Symbol]) do
10
10
 
11
11
  filter_set(:_field, %i[checked unchecked disabled name])
12
12
 
13
- node_filter(:option) do |node, value|
13
+ node_filter(%i[option with]) do |node, value|
14
14
  val = node.value
15
- (val == value.to_s).tap do |res|
16
- add_error("Expected option value to be #{value.inspect} but it was #{val.inspect}") unless res
15
+ (value.is_a?(Regexp) ? value.match?(val) : val == value.to_s).tap do |res|
16
+ add_error("Expected value to be #{value.inspect} but it was #{val.inspect}") unless res
17
17
  end
18
18
  end
19
19
 
20
- describe_node_filters do |option: nil, **|
21
- " with value #{option.inspect}" if option
20
+ describe_node_filters do |option: nil, with: nil, **|
21
+ desc = +''
22
+ desc << " with value #{option.inspect}" if option
23
+ desc << " with value #{with.inspec}" if with
24
+ desc
22
25
  end
23
26
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Capybara.add_selector(:css, locator_type: [String, Symbol], raw_locator: true) do
4
- css { |css| css }
4
+ css do |css|
5
+ warn "DEPRECATED: Passing a symbol (#{css.inspect}) as the CSS locator is deprecated - please pass a string instead." if css.is_a? Symbol
6
+ css
7
+ end
5
8
  end
@@ -11,14 +11,17 @@ Capybara.add_selector(:radio_button, locator_type: [String, Symbol]) do
11
11
 
12
12
  filter_set(:_field, %i[checked unchecked disabled name])
13
13
 
14
- node_filter(:option) do |node, value|
14
+ node_filter(%i[option with]) do |node, value|
15
15
  val = node.value
16
- (val == value.to_s).tap do |res|
17
- add_error("Expected option value to be #{value.inspect} but it was #{val.inspect}") unless res
16
+ (value.is_a?(Regexp) ? value.match?(val) : val == value.to_s).tap do |res|
17
+ add_error("Expected value to be #{value.inspect} but it was #{val.inspect}") unless res
18
18
  end
19
19
  end
20
20
 
21
- describe_node_filters do |option: nil, **|
22
- " with value #{option.inspect}" if option
21
+ describe_node_filters do |option: nil, with: nil, **|
22
+ desc = +''
23
+ desc << " with value #{option.inspect}" if option
24
+ desc << " with value #{with.inspec}" if with
25
+ desc
23
26
  end
24
27
  end
@@ -15,8 +15,10 @@ module Capybara
15
15
  instance_eval(&block)
16
16
  end
17
17
 
18
- def node_filter(name, *types_and_options, &block)
19
- add_filter(name, Filters::NodeFilter, *types_and_options, &block)
18
+ def node_filter(names, *types_and_options, &block)
19
+ Array(names).each do |name|
20
+ add_filter(name, Filters::NodeFilter, *types_and_options, &block)
21
+ end
20
22
  end
21
23
  alias_method :filter, :node_filter
22
24
 
@@ -166,11 +166,11 @@ module Capybara
166
166
  end
167
167
 
168
168
  def min_repeat
169
- @exp.quantifier&.min || 1
169
+ @exp.repetitions.begin
170
170
  end
171
171
 
172
172
  def max_repeat
173
- @exp.quantifier&.max || 1
173
+ @exp.repetitions.end
174
174
  end
175
175
 
176
176
  def fixed_repeat?
@@ -4,179 +4,161 @@ require 'capybara/selector/xpath_extensions'
4
4
  require 'capybara/selector/selector'
5
5
  require 'capybara/selector/definition'
6
6
 
7
- # rubocop:disable Style/AsciiComments
8
- #
9
- # ## Built-in Selectors
10
- #
11
- # * **:xpath** - Select elements by XPath expression
12
- # * Locator: An XPath expression
13
- #
14
- # * **:css** - Select elements by CSS selector
15
- # * Locator: A CSS selector
16
- #
17
- # * **:id** - Select element by id
18
- # * Locator: (String, Regexp, XPath::Expression) The id of the element to match
19
- #
20
- # * **:field** - Select field elements (input [not of type submit, image, or hidden], textarea, select)
21
- # * Locator: Matches against the id, Capybara.test_id attribute, name, or placeholder
22
- # * Filters:
23
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
24
- # * :name (String) Matches the name attribute
25
- # * :placeholder (String) Matches the placeholder attribute
26
- # * :type (String) — Matches the type attribute of the field or element type for 'textarea' and 'select'
27
- # * :readonly (Boolean)
28
- # * :with (String) — Matches the current value of the field
29
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
30
- # * :checked (Boolean) — Match checked fields?
31
- # * :unchecked (Boolean) — Match unchecked fields?
32
- # * :disabled (Boolean) — Match disabled field?
33
- # * :multiple (Boolean) — Match fields that accept multiple values
34
- # * :style (String, Regexp, Hash)
35
- #
36
- # * **:fieldset** - Select fieldset elements
37
- # * Locator: Matches id or contents of wrapped legend
38
- # * Filters:
39
- # * :id (String, Regexp, XPath::Expression) — Matches id attribute
40
- # * :legend (String) — Matches contents of wrapped legend
41
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
42
- # * :style (String, Regexp, Hash)
43
- #
44
- # * **:link** - Find links ( <a> elements with an href attribute )
45
- # * Locator: Matches the id or title attributes, or the string content of the link, or the alt attribute of a contained img element
46
- # * Filters:
47
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
48
- # * :title (String) — Matches the title attribute
49
- # * :alt (String) — Matches the alt attribute of a contained img element
50
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
51
- # * :href (String, Regexp, nil, false) — Matches the normalized href of the link, if nil will find <a> elements with no href attribute, if false ignores href
52
- # * :style (String, Regexp, Hash)
53
- #
54
- # * **:button** - Find buttons ( input [of type submit, reset, image, button] or button elements )
55
- # * Locator: Matches the id, Capybara.test_id attribute, name, 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
56
- # * Filters:
57
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
7
+ #
8
+ # All Selectors below support the listed selector specific filters in addition to the following system-wide filters
9
+ # * :id (String, Regexp, XPath::Expression) - Matches the id attribute
10
+ # * :class (String, Array<String>, Regexp, XPath::Expression) - Matches the class(es) provided
11
+ # * :style (String, Regexp, Hash<String,String>) - Match on elements style
12
+ #
13
+ # ### Built-in Selectors
14
+ #
15
+ # * **:xpath** - Select elements by XPath expression
16
+ # * Locator: An XPath expression
17
+ #
18
+ # * **:css** - Select elements by CSS selector
19
+ # * Locator: A CSS selector
20
+ #
21
+ # * **:id** - Select element by id
22
+ # * Locator: (String, Regexp, XPath::Expression) The id of the element to match
23
+ #
24
+ # * **:field** - Select field elements (input [not of type submit, image, or hidden], textarea, select)
25
+ # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name, or placeholder
26
+ # * Filters:
58
27
  # * :name (String) - Matches the name attribute
59
- # * :title (String) Matches the title attribute
60
- # * :class (String, Array<String>, Regexp, XPath::Expression) Matches the class(es) provided
61
- # * :value (String) Matches the value of an input button
62
- # * :type
63
- # * :style (String, Regexp, Hash)
64
- #
65
- # * **:link_or_button** - Find links or buttons
66
- # * Locator: See :link and :button selectors
67
- #
68
- # * **:fillable_field** - Find text fillable fields ( textarea, input [not of type submit, image, radio, checkbox, hidden, file] )
69
- # * Locator: Matches against the id, Capybara.test_id attribute, name, or placeholder
70
- # * Filters:
71
- # * :id (String, Regexp, XPath::Expression) Matches the id attribute
72
- # * :name (String) Matches the name attribute
73
- # * :placeholder (String) — Matches the placeholder attribute
74
- # * :with (String) Matches the current value of the field
75
- # * :type (String) Matches the type attribute of the field or element type for 'textarea'
76
- # * :class (String, Array<String>, Regexp, XPath::Expression) Matches the class(es) provided
77
- # * :disabled (Boolean) — Match disabled field?
78
- # * :multiple (Boolean) Match fields that accept multiple values
79
- # * :style (String, Regexp, Hash)
80
- #
81
- # * **:radio_button** - Find radio buttons
82
- # * Locator: Match id, Capybara.test_id attribute, name, or associated label text
83
- # * Filters:
84
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
85
- # * :name (String) Matches the name attribute
86
- # * :class (String, Array<String>, Regexp, XPath::Expression) Matches the class(es) provided
87
- # * :checked (Boolean) Match checked fields?
88
- # * :unchecked (Boolean) Match unchecked fields?
89
- # * :disabled (Boolean) Match disabled field?
90
- # * :option (String) — Match the value
91
- # * :style (String, Regexp, Hash)
92
- #
93
- # * **:checkbox** - Find checkboxes
94
- # * Locator: Match id, Capybara.test_id attribute, name, or associated label text
95
- # * Filters:
96
- # * *:id (String, Regexp, XPath::Expression) Matches the id attribute
97
- # * *:name (String) Matches the name attribute
98
- # * *:class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
99
- # * *:checked (Boolean) Match checked fields?
100
- # * *:unchecked (Boolean) Match unchecked fields?
101
- # * *:disabled (Boolean) Match disabled field?
102
- # * *:option (String) Match the value
103
- # * :style (String, Regexp, Hash)
104
- #
105
- # * **:select** - Find select elements
106
- # * Locator: Match id, Capybara.test_id attribute, name, placeholder, or associated label text
107
- # * Filters:
108
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
109
- # * :name (String) Matches the name attribute
110
- # * :placeholder (String) Matches the placeholder attribute
111
- # * :class (String, Array<String>, Regexp, XPath::Expression) Matches the class(es) provided
112
- # * :disabled (Boolean) Match disabled field?
113
- # * :multiple (Boolean) Match fields that accept multiple values
114
- # * :options (Array<String>) Exact match options
115
- # * :with_options (Array<String>) — Partial match options
116
- # * :selected (String, Array<String>) — Match the selection(s)
117
- # * :with_selected (String, Array<String>) Partial match the selection(s)
118
- # * :style (String, Regexp, Hash)
119
- #
120
- # * **:option** - Find option elements
121
- # * Locator: Match text of option
122
- # * Filters:
123
- # * :disabled (Boolean) Match disabled option
124
- # * :selected (Boolean) Match selected option
125
- #
126
- # * **:datalist_input**
127
- # * Locator:
128
- # * Filters:
129
- # * :disabled
130
- # * :name
131
- # * :placeholder
132
- #
133
- # * **:datalist_option**
134
- # * Locator:
135
- #
136
- # * **:file_field** - Find file input elements
137
- # * Locator: Match id, Capybara.test_id attribute, name, or associated label text
138
- # * Filters:
139
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
140
- # * :name (String) — Matches the name attribute
141
- # * :class (String, Array<String>, Regexp, XPath::Expression) Matches the class(es) provided
142
- # * :disabled (Boolean) Match disabled field?
143
- # * :multiple (Boolean) — Match field that accepts multiple values
144
- # * :style (String, Regexp, Hash)
145
- #
146
- # * **:label** - Find label elements
147
- # * Locator: Match id or text contents
148
- # * Filters:
149
- # * :for (Element, String, Regexp) The element or id of the element associated with the label
150
- #
151
- # * **:table** - Find table elements
152
- # * Locator: id or caption text of table
153
- # * Filters:
154
- # * :id (String, Regexp, XPath::Expression) Match id attribute of table
155
- # * :caption (String) — Match text of associated caption
156
- # * :class ((String, Array<String>, Regexp, XPath::Expression) Matches the class(es) provided
157
- # * :style (String, Regexp, Hash)
158
- # * :with_rows (Array<Array<String>>, Array<Hash<String, String>>) - Partial match <td> data - visibility of <td> elements is not considered
159
- # * :rows (Array<Array<String>>) — Match all <td>s - visibility of <td> elements is not considered
160
- # * :with_cols (Array<Array<String>>, Array<Hash<String, String>>) - Partial match <td> data - visibility of <td> elements is not considered
161
- # * :cols (Array<Array<String>>) — Match all <td>s - visibility of <td> elements is not considered
162
- #
163
- # * **:table_row** - Find table row
164
- # * Locator: Array<String>, Hash<String,String> table row <td> contents - visibility of <td> elements is not considered
165
- #
166
- # * **:frame** - Find frame/iframe elements
167
- # * Locator: Match id or name
168
- # * Filters:
169
- # * :id (String, Regexp, XPath::Expression) — Match id attribute
170
- # * :name (String) Match name attribute
171
- # * :class (String, Array<String>, Regexp, XPath::Expression) Matches the class(es) provided
172
- # * :style (String, Regexp, Hash)
173
- #
174
- # * **:element**
175
- # * Locator: Type of element ('div', 'a', etc) - if not specified defaults to '*'
176
- # * Filters: Matches on any element attribute
177
- class Capybara::Selector; end
28
+ # * :placeholder (String) - Matches the placeholder attribute
29
+ # * :type (String) - Matches the type attribute of the field or element type for 'textarea' and 'select'
30
+ # * :readonly (Boolean) - Match on the element being readonly
31
+ # * :with (String, Regexp) - Matches the current value of the field
32
+ # * :checked (Boolean) - Match checked fields?
33
+ # * :unchecked (Boolean) - Match unchecked fields?
34
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
35
+ # * :multiple (Boolean) - Match fields that accept multiple values
36
+ #
37
+ # * **:fieldset** - Select fieldset elements
38
+ # * Locator: Matches id, {Capybara.configure test_id}, or contents of wrapped legend
39
+ # * Filters:
40
+ # * :legend (String) - Matches contents of wrapped legend
41
+ # * :disabled (Boolean) - Match disabled fieldset?
42
+ #
43
+ # * **:link** - Find links (`<a>` elements with an href attribute)
44
+ # * Locator: Matches the id, {Capybara.configure test_id}, or title attributes, or the string content of the link, or the alt attribute of a contained img element.
45
+ # By default this selector requires a link to have an href attribute.
46
+ # * Filters:
47
+ # * :title (String) - Matches the title attribute
48
+ # * :alt (String) - Matches the alt attribute of a contained img element
49
+ # * :href (String, Regexp, nil, false) - Matches the normalized href of the link, if nil will find `<a>` elements with no href attribute, if false ignores href presence
50
+ #
51
+ # * **:button** - Find buttons ( input [of type submit, reset, image, button] or button elements )
52
+ # * Locator: Matches the id, {Capybara.configure test_id} attribute, name, 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
53
+ # * Filters:
54
+ # * :name (String) - Matches the name attribute
55
+ # * :title (String) - Matches the title attribute
56
+ # * :value (String) - Matches the value of an input button
57
+ # * :type (String) - Matches the type attribute
58
+ # * :disabled (Boolean, :all) - Match disabled buttons (Default: false)
59
+ #
60
+ # * **:link_or_button** - Find links or buttons
61
+ # * Locator: See :link and :button selectors
62
+ # * Filters:
63
+ # * :disabled (Boolean, :all) - Match disabled buttons? (Default: false)
64
+ #
65
+ # * **:fillable_field** - Find text fillable fields ( textarea, input [not of type submit, image, radio, checkbox, hidden, file] )
66
+ # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name, or placeholder
67
+ # * Filters:
68
+ # * :name (String) - Matches the name attribute
69
+ # * :placeholder (String) - Matches the placeholder attribute
70
+ # * :with (String, Regexp) - Matches the current value of the field
71
+ # * :type (String) - Matches the type attribute of the field or element type for 'textarea'
72
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
73
+ # * :multiple (Boolean) - Match fields that accept multiple values
74
+ #
75
+ # * **:radio_button** - Find radio buttons
76
+ # * Locator: Match id, {Capybara.configure test_id} attribute, name, or associated label text
77
+ # * Filters:
78
+ # * :name (String) - Matches the name attribute
79
+ # * :checked (Boolean) - Match checked fields?
80
+ # * :unchecked (Boolean) - Match unchecked fields?
81
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
82
+ # * :option (String, Regexp) - Match the current value
83
+ # * :with - Alias of :option
84
+ #
85
+ # * **:checkbox** - Find checkboxes
86
+ # * Locator: Match id, {Capybara.configure test_id} attribute, name, or associated label text
87
+ # * Filters:
88
+ # * :name (String) - Matches the name attribute
89
+ # * :checked (Boolean) - Match checked fields?
90
+ # * :unchecked (Boolean) - Match unchecked fields?
91
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
92
+ # * :option (String, Regexp) - Match the current value
93
+ # * :with - Alias of :option
94
+ #
95
+ # * **:select** - Find select elements
96
+ # * Locator: Match id, {Capybara.configure test_id} attribute, name, placeholder, or associated label text
97
+ # * Filters:
98
+ # * :name (String) - Matches the name attribute
99
+ # * :placeholder (String) - Matches the placeholder attribute
100
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
101
+ # * :multiple (Boolean) - Match fields that accept multiple values
102
+ # * :options (Array<String>) - Exact match options
103
+ # * :with_options (Array<String>) - Partial match options
104
+ # * :selected (String, Array<String>) - Match the selection(s)
105
+ # * :with_selected (String, Array<String>) - Partial match the selection(s)
106
+ #
107
+ # * **:option** - Find option elements
108
+ # * Locator: Match text of option
109
+ # * Filters:
110
+ # * :disabled (Boolean) - Match disabled option
111
+ # * :selected (Boolean) - Match selected option
112
+ #
113
+ # * **:datalist_input** - Find input field with datalist completion
114
+ # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name, or placeholder
115
+ # * Filters:
116
+ # * :name (String) - Matches the name attribute
117
+ # * :placeholder (String) - Matches the placeholder attribute
118
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
119
+ # * :options (Array<String>) - Exact match options
120
+ # * :with_options (Array<String>) - Partial match options
121
+ #
122
+ # * **:datalist_option** - Find datalist option
123
+ # * Locator: Match text or value of option
124
+ # * Filters:
125
+ # * :disabled (Boolean) - Match disabled option
126
+ #
127
+ # * **:file_field** - Find file input elements
128
+ # * Locator: Match id, {Capybara.configure test_id} attribute, name, or associated label text
129
+ # * Filters:
130
+ # * :name (String) - Matches the name attribute
131
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
132
+ # * :multiple (Boolean) - Match field that accepts multiple values
133
+ #
134
+ # * **:label** - Find label elements
135
+ # * Locator: Match id, {Capybara.configure test_id}, or text contents
136
+ # * Filters:
137
+ # * :for (Element, String, Regexp) - The element or id of the element associated with the label
138
+ #
139
+ # * **:table** - Find table elements
140
+ # * Locator: id, {Capybara.configure test_id}, or caption text of table
141
+ # * Filters:
142
+ # * :caption (String) - Match text of associated caption
143
+ # * :with_rows (Array<Array<String>>, Array<Hash<String, String>>) - Partial match `<td>` data - visibility of `<td>` elements is not considered
144
+ # * :rows (Array<Array<String>>) - Match all `<td>`s - visibility of `<td>` elements is not considered
145
+ # * :with_cols (Array<Array<String>>, Array<Hash<String, String>>) - Partial match `<td>` data - visibility of `<td>` elements is not considered
146
+ # * :cols (Array<Array<String>>) - Match all `<td>`s - visibility of `<td>` elements is not considered
147
+ #
148
+ # * **:table_row** - Find table row
149
+ # * Locator: Array<String>, Hash<String,String> table row `<td>` contents - visibility of `<td>` elements is not considered
150
+ #
151
+ # * **:frame** - Find frame/iframe elements
152
+ # * Locator: Match id or name
153
+ # * Filters:
154
+ # * :name (String) - Match name attribute
155
+ #
156
+ # * **:element**
157
+ # * Locator: Type of element ('div', 'a', etc) - if not specified defaults to '*'
158
+ # * Filters:
159
+ # * :&lt;any> (String, Regexp) - Match on any specified element attribute
178
160
  #
179
- # rubocop:enable Style/AsciiComments
161
+ class Capybara::Selector; end
180
162
 
181
163
  Capybara::Selector::FilterSet.add(:_field) do
182
164
  node_filter(:checked, :boolean) { |node, value| !(value ^ node.checked?) }
@@ -33,11 +33,38 @@ module Capybara::Selenium::Driver::ChromeDriver
33
33
 
34
34
  switch_to_window(window_handles.first)
35
35
  window_handles.slice(1..-1).each { |win| close_window(win) }
36
- super
36
+ return super if chromedriver_version < 73
37
+
38
+ timer = Capybara::Helpers.timer(expire_in: 10)
39
+ begin
40
+ @browser.navigate.to('about:blank')
41
+ clear_storage unless uniform_storage_clear?
42
+ wait_for_empty_page(timer)
43
+ rescue *unhandled_alert_errors
44
+ accept_unhandled_reset_alert
45
+ retry
46
+ end
47
+
48
+ execute_cdp('Storage.clearDataForOrigin', origin: '*', storageTypes: storage_types_to_clear)
37
49
  end
38
50
 
39
51
  private
40
52
 
53
+ def storage_types_to_clear
54
+ types = ['cookies']
55
+ types << 'local_storage' if clear_all_storage?
56
+ types.join(',')
57
+ end
58
+
59
+ def clear_all_storage?
60
+ options.values_at(:clear_session_storage, :clear_local_storage).none? { |s| s == false }
61
+ end
62
+
63
+ def uniform_storage_clear?
64
+ clear = options.values_at(:clear_session_storage, :clear_local_storage)
65
+ clear.all? { |s| s == false } || clear.none? { |s| s == false }
66
+ end
67
+
41
68
  def clear_storage
42
69
  # Chrome errors if attempt to clear storage on about:blank
43
70
  # In W3C mode it crashes chromedriver
@@ -69,6 +96,13 @@ private
69
96
  def bridge
70
97
  browser.send(:bridge)
71
98
  end
99
+
100
+ def chromedriver_version
101
+ @chromedriver_version ||= begin
102
+ caps = browser.capabilities
103
+ caps['chrome']&.fetch('chromedriverVersion', nil).to_f
104
+ end
105
+ end
72
106
  end
73
107
 
74
108
  Capybara::Selenium::Driver.register_specialization :chrome, Capybara::Selenium::Driver::ChromeDriver
@@ -22,6 +22,69 @@ class Capybara::Selenium::Node
22
22
  native.property('draggable')
23
23
  end
24
24
 
25
+ def html5_drop(*args)
26
+ if args[0].is_a? String
27
+ input = driver.evaluate_script ATTACH_FILE
28
+ input.set_file(args)
29
+ driver.execute_script DROP_FILE, self, input
30
+ else
31
+ items = args.each_with_object([]) do |arg, arr|
32
+ arg.each_with_object(arr) do |(type, data), arr_|
33
+ arr_ << { type: type, data: data }
34
+ end
35
+ end
36
+ driver.execute_script DROP_STRING, items, self
37
+ end
38
+ end
39
+
40
+ DROP_STRING = <<~JS
41
+ var strings = arguments[0],
42
+ el = arguments[1],
43
+ dt = new DataTransfer(),
44
+ opts = { cancelable: true, bubbles: true, dataTransfer: dt };
45
+ for (var i=0; i < strings.length; i++){
46
+ if (dt.items) {
47
+ dt.items.add(strings[i]['data'], strings[i]['type']);
48
+ } else {
49
+ dt.setData(strings[i]['type'], strings[i]['data']);
50
+ }
51
+ }
52
+ var dropEvent = new DragEvent('drop', opts);
53
+ el.dispatchEvent(dropEvent);
54
+ JS
55
+
56
+ DROP_FILE = <<~JS
57
+ var el = arguments[0],
58
+ input = arguments[1],
59
+ files = input.files,
60
+ dt = new DataTransfer(),
61
+ opts = { cancelable: true, bubbles: true, dataTransfer: dt };
62
+ input.parentElement.removeChild(input);
63
+ if (dt.items){
64
+ for (var i=0; i<files.length; i++){
65
+ dt.items.add(files[i]);
66
+ }
67
+ } else {
68
+ Object.defineProperty(dt, "files", {
69
+ value: files,
70
+ writable: false
71
+ });
72
+ }
73
+ var dropEvent = new DragEvent('drop', opts);
74
+ el.dispatchEvent(dropEvent);
75
+ JS
76
+
77
+ ATTACH_FILE = <<~JS
78
+ (function(){
79
+ var input = document.createElement('INPUT');
80
+ input.type = "file";
81
+ input.id = "_capybara_drop_file";
82
+ input.multiple = true;
83
+ document.body.appendChild(input);
84
+ return input;
85
+ })()
86
+ JS
87
+
25
88
  MOUSEDOWN_TRACKER = <<~JS
26
89
  document.addEventListener('mousedown', ev => {
27
90
  window.capybara_mousedown_prevented = ev.defaultPrevented;
@@ -137,6 +137,10 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
137
137
  element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
138
138
  end
139
139
 
140
+ def drop(*_)
141
+ raise NotImplementedError, 'Out of browser drop emulation is not implemented for the current browser'
142
+ end
143
+
140
144
  def tag_name
141
145
  @tag_name ||= native.tag_name.downcase
142
146
  end