watir 6.16.2 → 6.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/actions/enable-safari/action.yml +11 -0
- data/.github/actions/install-chrome/action.yml +11 -0
- data/.github/workflows/linux.yml +61 -0
- data/.github/workflows/mac.yml +55 -0
- data/.github/workflows/unit.yml +31 -0
- data/.github/workflows/windows.yml +39 -0
- data/.rubocop.yml +32 -107
- data/.rubocop_todo.yml +36 -0
- data/CHANGES.md +42 -0
- data/Gemfile +3 -1
- data/LICENSE +2 -2
- data/README.md +9 -10
- data/Rakefile +4 -4
- data/lib/watir-webdriver.rb +1 -1
- data/lib/watir.rb +1 -1
- data/lib/watir/adjacent.rb +8 -10
- data/lib/watir/after_hooks.rb +4 -4
- data/lib/watir/alert.rb +1 -0
- data/lib/watir/attribute_helper.rb +2 -0
- data/lib/watir/browser.rb +7 -3
- data/lib/watir/capabilities.rb +9 -6
- data/lib/watir/cookies.rb +3 -1
- data/lib/watir/element_collection.rb +21 -6
- data/lib/watir/elements/element.rb +66 -53
- data/lib/watir/elements/file_field.rb +1 -0
- data/lib/watir/elements/html_elements.rb +0 -1
- data/lib/watir/elements/iframe.rb +4 -3
- data/lib/watir/elements/link.rb +0 -9
- data/lib/watir/elements/radio.rb +1 -1
- data/lib/watir/elements/select.rb +22 -7
- data/lib/watir/generator/base/spec_extractor.rb +4 -4
- data/lib/watir/generator/html/generator.rb +1 -1
- data/lib/watir/has_window.rb +17 -15
- data/lib/watir/js_execution.rb +3 -3
- data/lib/watir/js_snippets.rb +2 -2
- data/lib/watir/legacy_wait.rb +1 -1
- data/lib/watir/locators.rb +1 -3
- data/lib/watir/locators/element/locator.rb +22 -12
- data/lib/watir/locators/element/selector_builder.rb +12 -13
- data/lib/watir/locators/element/selector_builder/xpath.rb +40 -13
- data/lib/watir/locators/text_field/matcher.rb +1 -1
- data/lib/watir/locators/text_field/selector_builder/xpath.rb +3 -1
- data/lib/watir/logger.rb +7 -20
- data/lib/watir/radio_set.rb +2 -2
- data/lib/watir/user_editable.rb +6 -2
- data/lib/watir/version.rb +1 -1
- data/lib/watir/wait.rb +2 -0
- data/lib/watir/wait/timer.rb +1 -1
- data/lib/watir/window.rb +8 -4
- data/lib/watir/window_collection.rb +105 -0
- data/lib/watirspec.rb +2 -1
- data/lib/watirspec/guards.rb +1 -1
- data/lib/watirspec/implementation.rb +3 -5
- data/lib/watirspec/rake_tasks.rb +2 -0
- data/lib/watirspec/runner.rb +5 -1
- data/lib/watirspec/server.rb +1 -1
- data/spec/spec_helper.rb +2 -7
- data/spec/unit/container_spec.rb +1 -1
- data/spec/unit/logger_spec.rb +5 -7
- data/spec/unit/match_elements/element_spec.rb +17 -15
- data/spec/unit/selector_builder/button_spec.rb +16 -15
- data/spec/unit/selector_builder/element_spec.rb +58 -9
- data/spec/unit/selector_builder/text_field_spec.rb +14 -14
- data/spec/unit/unit_helper.rb +2 -4
- data/spec/watirspec/after_hooks_spec.rb +58 -68
- data/spec/watirspec/alert_spec.rb +69 -79
- data/spec/watirspec/browser_spec.rb +51 -48
- data/spec/watirspec/cookies_spec.rb +52 -37
- data/spec/watirspec/drag_and_drop_spec.rb +14 -38
- data/spec/watirspec/elements/button_spec.rb +2 -0
- data/spec/watirspec/elements/buttons_spec.rb +1 -1
- data/spec/watirspec/elements/checkbox_spec.rb +8 -4
- data/spec/watirspec/elements/date_field_spec.rb +18 -9
- data/spec/watirspec/elements/date_time_field_spec.rb +3 -4
- data/spec/watirspec/elements/div_spec.rb +62 -54
- data/spec/watirspec/elements/element_spec.rb +73 -88
- data/spec/watirspec/elements/elements_spec.rb +12 -3
- data/spec/watirspec/elements/filefield_spec.rb +25 -50
- data/spec/watirspec/elements/form_spec.rb +6 -8
- data/spec/watirspec/elements/frame_spec.rb +10 -13
- data/spec/watirspec/elements/iframe_spec.rb +12 -9
- data/spec/watirspec/elements/iframes_spec.rb +2 -2
- data/spec/watirspec/elements/link_spec.rb +23 -12
- data/spec/watirspec/elements/links_spec.rb +11 -3
- data/spec/watirspec/elements/option_spec.rb +15 -17
- data/spec/watirspec/elements/select_list_spec.rb +222 -117
- data/spec/watirspec/elements/text_field_spec.rb +8 -4
- data/spec/watirspec/elements/tr_spec.rb +0 -9
- data/spec/watirspec/html/forms_with_input_elements.html +1 -0
- data/spec/watirspec/html/iframes.html +3 -0
- data/spec/watirspec/html/non_control_elements.html +4 -4
- data/spec/watirspec/html/right_click.html +12 -0
- data/spec/watirspec/html/wait.html +6 -6
- data/spec/watirspec/html/window_switching.html +10 -0
- data/spec/watirspec/legacy_wait_spec.rb +216 -0
- data/spec/watirspec/support/rspec_matchers.rb +17 -13
- data/spec/watirspec/user_editable_spec.rb +1 -1
- data/spec/watirspec/wait_spec.rb +257 -305
- data/spec/watirspec/window_switching_spec.rb +332 -211
- data/spec/watirspec_helper.rb +16 -19
- data/support/doctest_helper.rb +0 -2
- data/watir.gemspec +6 -7
- metadata +36 -26
- data/.travis.yml +0 -84
- data/appveyor.yml +0 -12
- data/lib/watir/elements/area.rb +0 -10
- data/spec/watirspec/relaxed_locate_spec.rb +0 -113
@@ -272,7 +272,6 @@ module Watir
|
|
272
272
|
attribute(String, :form, :form)
|
273
273
|
attribute(String, :label, :label)
|
274
274
|
attribute("Boolean", :defaultselected?, :defaultSelected)
|
275
|
-
attribute("Boolean", :selected?, :selected)
|
276
275
|
attribute(String, :value, :value)
|
277
276
|
attribute(Integer, :index, :index)
|
278
277
|
end
|
@@ -44,8 +44,9 @@ module Watir
|
|
44
44
|
# @see Watir::Browser#execute_script
|
45
45
|
#
|
46
46
|
|
47
|
-
def execute_script(script, *args)
|
47
|
+
def execute_script(script, *args, function_name: nil)
|
48
48
|
args.map! do |e|
|
49
|
+
Watir.logger.info "Executing Script on Frame: #{function_name}" if function_name
|
49
50
|
e.is_a?(Element) ? e.wait_until(&:exists?).wd : e
|
50
51
|
end
|
51
52
|
returned = driver.execute_script(script, *args)
|
@@ -134,8 +135,8 @@ module Watir
|
|
134
135
|
@element
|
135
136
|
end
|
136
137
|
|
137
|
-
def respond_to_missing?(meth, _include_private
|
138
|
-
@driver.respond_to?(meth) || @element.respond_to?(meth) || super
|
138
|
+
def respond_to_missing?(meth, _include_private)
|
139
|
+
@driver.respond_to?(meth) || @element.respond_to?(meth) || super(meth, false)
|
139
140
|
end
|
140
141
|
|
141
142
|
def method_missing(meth, *args, &blk)
|
data/lib/watir/elements/link.rb
CHANGED
@@ -3,13 +3,4 @@ module Watir
|
|
3
3
|
alias link a
|
4
4
|
alias links as
|
5
5
|
end # Container
|
6
|
-
|
7
|
-
class Anchor < HTMLElement
|
8
|
-
#
|
9
|
-
# @todo temporarily add href attribute
|
10
|
-
#
|
11
|
-
# @see https://www.w3.org/Bugs/Public/show_bug.cgi?id=23192
|
12
|
-
#
|
13
|
-
attribute String, :href, :href
|
14
|
-
end # Anchor
|
15
6
|
end # Watir
|
data/lib/watir/elements/radio.rb
CHANGED
@@ -30,8 +30,11 @@ module Watir
|
|
30
30
|
#
|
31
31
|
|
32
32
|
def select(*str_or_rx)
|
33
|
-
|
34
|
-
|
33
|
+
if str_or_rx.size > 1 || str_or_rx.first.is_a?(Array)
|
34
|
+
str_or_rx.flatten.map { |v| select_all_by v }.first
|
35
|
+
else
|
36
|
+
str_or_rx.flatten.map { |v| select_by v }.first
|
37
|
+
end
|
35
38
|
end
|
36
39
|
|
37
40
|
#
|
@@ -43,6 +46,10 @@ module Watir
|
|
43
46
|
#
|
44
47
|
|
45
48
|
def select_all(*str_or_rx)
|
49
|
+
Watir.logger.deprecate('#select_all',
|
50
|
+
'#select with an Array instance',
|
51
|
+
ids: [:select_all])
|
52
|
+
|
46
53
|
results = str_or_rx.flatten.map { |v| select_all_by v }
|
47
54
|
results.first
|
48
55
|
end
|
@@ -55,8 +62,11 @@ module Watir
|
|
55
62
|
#
|
56
63
|
|
57
64
|
def select!(*str_or_rx)
|
58
|
-
|
59
|
-
|
65
|
+
if str_or_rx.size > 1 || str_or_rx.first.is_a?(Array)
|
66
|
+
str_or_rx.flatten.map { |v| select_by! v, :multiple }.first
|
67
|
+
else
|
68
|
+
str_or_rx.flatten.map { |v| select_by! v, :single }.first
|
69
|
+
end
|
60
70
|
end
|
61
71
|
|
62
72
|
#
|
@@ -67,6 +77,10 @@ module Watir
|
|
67
77
|
#
|
68
78
|
|
69
79
|
def select_all!(*str_or_rx)
|
80
|
+
Watir.logger.deprecate('#select_all!',
|
81
|
+
'#select! with an Array instance',
|
82
|
+
ids: [:select_all])
|
83
|
+
|
70
84
|
results = str_or_rx.flatten.map { |v| select_by!(v, :multiple) }
|
71
85
|
results.first
|
72
86
|
end
|
@@ -145,7 +159,8 @@ module Watir
|
|
145
159
|
found = find_options(:value, str_or_rx)
|
146
160
|
|
147
161
|
if found.size > 1
|
148
|
-
Watir.logger.deprecate 'Selecting
|
162
|
+
Watir.logger.deprecate 'Selecting multiple options with #select using a String or Regexp value',
|
163
|
+
'#select with the desired values in an Array instance',
|
149
164
|
ids: [:select_by]
|
150
165
|
end
|
151
166
|
select_matching(found)
|
@@ -170,8 +185,8 @@ module Watir
|
|
170
185
|
str_or_rx.inspect.sub('\\A', '^')
|
171
186
|
.sub('\\Z', '$')
|
172
187
|
.sub('\\z', '$')
|
173
|
-
.sub(%r{
|
174
|
-
.sub(%r{
|
188
|
+
.sub(%r{^/}, '')
|
189
|
+
.sub(%r{/[a-z]*$}, '')
|
175
190
|
.gsub(/\(\?#.+\)/, '')
|
176
191
|
.gsub(/\(\?-\w+:/, '(')
|
177
192
|
else
|
@@ -109,8 +109,8 @@ module Watir
|
|
109
109
|
begin
|
110
110
|
intf = fetch_interface(implementor_name).first
|
111
111
|
intf.implements << fetch_interface(implementee_name).first
|
112
|
-
rescue InterfaceNotFound =>
|
113
|
-
puts
|
112
|
+
rescue InterfaceNotFound => e
|
113
|
+
puts e.message
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
@@ -123,8 +123,8 @@ module Watir
|
|
123
123
|
begin
|
124
124
|
intf = fetch_interface(includer_name).first
|
125
125
|
intf.includes << fetch_interface(includee_name).first
|
126
|
-
rescue InterfaceNotFound =>
|
127
|
-
puts
|
126
|
+
rescue InterfaceNotFound => e
|
127
|
+
puts e.message
|
128
128
|
end
|
129
129
|
end
|
130
130
|
end
|
@@ -14,7 +14,7 @@ module Watir
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def ignored_attributes
|
17
|
-
%w[cells elements hash rows span text size selected? style width height tHead tFoot link options]
|
17
|
+
%w[cells elements hash rows span text size selected? style width height tHead tFoot link options selected]
|
18
18
|
end
|
19
19
|
|
20
20
|
def generator_implementation
|
data/lib/watir/has_window.rb
CHANGED
@@ -10,13 +10,7 @@ module Watir
|
|
10
10
|
#
|
11
11
|
|
12
12
|
def windows(*args)
|
13
|
-
|
14
|
-
|
15
|
-
if args.empty?
|
16
|
-
all
|
17
|
-
else
|
18
|
-
filter_windows extract_selector(args), all
|
19
|
-
end
|
13
|
+
WindowCollection.new self, extract_selector(args)
|
20
14
|
end
|
21
15
|
|
22
16
|
#
|
@@ -51,16 +45,24 @@ module Watir
|
|
51
45
|
@original_window ||= window
|
52
46
|
end
|
53
47
|
|
54
|
-
|
48
|
+
#
|
49
|
+
# Waits for and returns second window if present
|
50
|
+
# See Window#use
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# browser.switch_window
|
54
|
+
#
|
55
|
+
# @return [Window]
|
56
|
+
#
|
55
57
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
58
|
+
def switch_window
|
59
|
+
current_window = window
|
60
|
+
wins = windows
|
61
|
+
wait_until { (wins = windows) && wins.size > 1 } if wins.size == 1
|
62
|
+
raise StandardError, 'Unable to determine which window to switch to' if wins.size > 2
|
60
63
|
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
+
wins.find { |w| w != current_window }.use
|
65
|
+
window
|
64
66
|
end
|
65
67
|
end # HasWindow
|
66
68
|
end # Watir
|
data/lib/watir/js_execution.rb
CHANGED
@@ -3,8 +3,8 @@ module Watir
|
|
3
3
|
#
|
4
4
|
# Delegates script execution to Browser or IFrame.
|
5
5
|
#
|
6
|
-
def execute_script(script, *args)
|
7
|
-
@query_scope.execute_script(script, *args)
|
6
|
+
def execute_script(script, *args, function_name: nil)
|
7
|
+
@query_scope.execute_script(script, *args, function_name: function_name)
|
8
8
|
end
|
9
9
|
|
10
10
|
#
|
@@ -54,7 +54,7 @@ module Watir
|
|
54
54
|
long: {flashes: 5, delay: 0.5},
|
55
55
|
rainbow: {flashes: 5, color: %w[red orange yellow green blue indigo violet]}
|
56
56
|
}
|
57
|
-
return flash(presets[preset]) unless presets[preset].nil?
|
57
|
+
return flash(**presets[preset]) unless presets[preset].nil?
|
58
58
|
|
59
59
|
background_color = original_color = style('background-color')
|
60
60
|
background_color = 'white' if background_color.empty?
|
data/lib/watir/js_snippets.rb
CHANGED
@@ -6,11 +6,11 @@ module Watir
|
|
6
6
|
|
7
7
|
def execute_js(function_name, *arguments)
|
8
8
|
file = File.expand_path("../js_snippets/#{function_name}.js", __FILE__)
|
9
|
-
raise Exception::Error, "Can not
|
9
|
+
raise Exception::Error, "Can not execute script as #{function_name}.js does not exist" unless File.exist?(file)
|
10
10
|
|
11
11
|
js = File.read(file)
|
12
12
|
script = "return (#{js}).apply(null, arguments)"
|
13
|
-
@query_scope.execute_script(script, *arguments)
|
13
|
+
@query_scope.execute_script(script, *arguments, function_name: function_name)
|
14
14
|
end
|
15
15
|
end # JSSnippets
|
16
16
|
end # Watir
|
data/lib/watir/legacy_wait.rb
CHANGED
data/lib/watir/locators.rb
CHANGED
@@ -15,7 +15,8 @@ module Watir
|
|
15
15
|
def locate(built)
|
16
16
|
@built = built.dup
|
17
17
|
@driver_scope = locator_scope.wd
|
18
|
-
|
18
|
+
@filter = :first
|
19
|
+
matching_elements
|
19
20
|
rescue Selenium::WebDriver::Error::NoSuchElementError
|
20
21
|
nil
|
21
22
|
end
|
@@ -23,37 +24,46 @@ module Watir
|
|
23
24
|
def locate_all(built)
|
24
25
|
@built = built.dup
|
25
26
|
@driver_scope = locator_scope.wd
|
26
|
-
|
27
|
+
@filter = :all
|
27
28
|
|
28
|
-
[matching_elements
|
29
|
+
return [matching_elements].flatten unless @built.key?(:index)
|
30
|
+
|
31
|
+
raise ArgumentError, "can't locate all elements by :index"
|
29
32
|
end
|
30
33
|
|
31
34
|
private
|
32
35
|
|
33
|
-
def matching_elements
|
34
|
-
return locate_element(
|
35
|
-
|
36
|
-
wd_locator_key = (Watir::Locators::W3C_FINDERS & built.keys).first
|
37
|
-
wd_locator = built.select { |k, _v| wd_locator_key == k }
|
38
|
-
match_values = built.reject { |k, _v| wd_locator_key == k }
|
36
|
+
def matching_elements
|
37
|
+
return locate_element(*@built.to_a.flatten) if @built.size == 1 && @filter == :first
|
39
38
|
|
40
39
|
# TODO: Wrap this to continue trying until default timeout
|
41
40
|
retries = 0
|
42
41
|
begin
|
43
42
|
elements = locate_elements(*wd_locator.to_a.flatten)
|
44
43
|
|
45
|
-
element_matcher.match(elements, match_values, filter)
|
44
|
+
element_matcher.match(elements, match_values, @filter)
|
46
45
|
rescue Selenium::WebDriver::Error::StaleElementReferenceError
|
47
46
|
retries += 1
|
48
47
|
sleep 0.5
|
49
48
|
retry unless retries > 2
|
50
|
-
target = filter == :all ? 'element collection' : 'element'
|
49
|
+
target = @filter == :all ? 'element collection' : 'element'
|
51
50
|
raise LocatorException, "Unable to locate #{target} from #{@selector} due to changing page"
|
52
51
|
end
|
53
52
|
end
|
54
53
|
|
54
|
+
def wd_locator
|
55
|
+
# SelectorBuilder only allows one of these
|
56
|
+
wd_locator_key = (Watir::Locators::W3C_FINDERS & @built.keys).first
|
57
|
+
@wd_locator ||= @built.select { |k, _v| wd_locator_key == k }
|
58
|
+
end
|
59
|
+
|
60
|
+
def match_values
|
61
|
+
@match_values ||= @built.reject { |k, _v| wd_locator.keys.first == k }
|
62
|
+
end
|
63
|
+
|
55
64
|
def locator_scope
|
56
|
-
@built.delete(:scope)
|
65
|
+
scope = @built.delete(:scope)
|
66
|
+
@locator_scope ||= scope || @query_scope.browser
|
57
67
|
end
|
58
68
|
|
59
69
|
def locate_element(how, what, scope = driver_scope)
|
@@ -6,11 +6,10 @@ module Watir
|
|
6
6
|
attr_reader :custom_attributes, :built
|
7
7
|
|
8
8
|
WILDCARD_ATTRIBUTE = /^(aria|data)_(.+)$/.freeze
|
9
|
-
INTEGER_CLASS = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.4') ? Fixnum : Integer
|
10
9
|
VALID_WHATS = Hash.new([String, Regexp, TrueClass, FalseClass]).merge(adjacent: [::Symbol],
|
11
10
|
xpath: [String],
|
12
11
|
css: [String],
|
13
|
-
index: [
|
12
|
+
index: [Integer],
|
14
13
|
visible: [TrueClass, FalseClass],
|
15
14
|
tag_name: [String, Regexp, ::Symbol],
|
16
15
|
visible_text: [String, Regexp],
|
@@ -53,11 +52,7 @@ module Watir
|
|
53
52
|
if @selector.key?(:class) || @selector.key?(:class_name)
|
54
53
|
classes = ([@selector[:class]].flatten + [@selector.delete(:class_name)].flatten).compact
|
55
54
|
|
56
|
-
classes
|
57
|
-
next unless class_name.is_a?(String) && class_name.strip.include?(' ')
|
58
|
-
|
59
|
-
deprecate_class_array(class_name)
|
60
|
-
end
|
55
|
+
deprecate_class_array(classes)
|
61
56
|
|
62
57
|
@selector[:class] = classes
|
63
58
|
end
|
@@ -83,11 +78,15 @@ module Watir
|
|
83
78
|
scope_invalid_locators.empty?
|
84
79
|
end
|
85
80
|
|
86
|
-
def deprecate_class_array(
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
81
|
+
def deprecate_class_array(class_array)
|
82
|
+
class_array.each do |class_name|
|
83
|
+
next unless class_name.is_a?(String) && class_name.strip.include?(' ')
|
84
|
+
|
85
|
+
dep = "Using the :class locator to locate multiple classes with a String value (i.e. \"#{class_name}\")"
|
86
|
+
Watir.logger.deprecate dep,
|
87
|
+
"Array (e.g. #{class_name.split})",
|
88
|
+
ids: [:class_array]
|
89
|
+
end
|
91
90
|
end
|
92
91
|
|
93
92
|
def check_type(how, what)
|
@@ -143,7 +142,7 @@ module Watir
|
|
143
142
|
|
144
143
|
# Extensions implement this method when creating a different selector builder
|
145
144
|
def implementation_class
|
146
|
-
|
145
|
+
Watir.const_get("#{self.class.name}::XPath")
|
147
146
|
end
|
148
147
|
|
149
148
|
def build_wd_selector(selector)
|
@@ -12,6 +12,7 @@ module Watir
|
|
12
12
|
|
13
13
|
def build(selector)
|
14
14
|
@selector = selector
|
15
|
+
@valid_attributes = build_valid_attributes
|
15
16
|
|
16
17
|
@built = (@selector.keys & CAN_NOT_BUILD).each_with_object({}) do |key, hash|
|
17
18
|
hash[key] = @selector.delete(key)
|
@@ -45,9 +46,10 @@ module Watir
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def predicate_expression(key, val)
|
48
|
-
|
49
|
+
case val
|
50
|
+
when true
|
49
51
|
attribute_presence(key)
|
50
|
-
|
52
|
+
when false
|
51
53
|
attribute_absence(key)
|
52
54
|
else
|
53
55
|
equal_pair(key, val)
|
@@ -55,11 +57,9 @@ module Watir
|
|
55
57
|
end
|
56
58
|
|
57
59
|
def predicate_conversion(key, regexp)
|
58
|
-
|
59
|
-
# https://github.com/watir/watir/issues/72
|
60
|
-
downcase = key == :type || regexp.casefold?
|
60
|
+
downcase = case_insensitive_attribute?(key) || regexp.casefold?
|
61
61
|
|
62
|
-
lhs = lhs_for(key, downcase)
|
62
|
+
lhs = lhs_for(key, downcase: downcase)
|
63
63
|
|
64
64
|
results = RegexpDisassembler.new(regexp).substrings
|
65
65
|
|
@@ -72,8 +72,10 @@ module Watir
|
|
72
72
|
|
73
73
|
add_to_matching(key, regexp, results)
|
74
74
|
|
75
|
-
results.map { |
|
76
|
-
"
|
75
|
+
results.map { |rhs|
|
76
|
+
rhs = "'#{rhs}'"
|
77
|
+
rhs = XpathSupport.downcase(rhs) if downcase
|
78
|
+
"contains(#{lhs}, #{rhs})"
|
77
79
|
}.flatten.compact.join(' and ')
|
78
80
|
end
|
79
81
|
|
@@ -200,7 +202,7 @@ module Watir
|
|
200
202
|
regexp.casefold? ? !results.first.casecmp(regexp.source).zero? : results.first != regexp.source
|
201
203
|
end
|
202
204
|
|
203
|
-
def lhs_for(key, downcase
|
205
|
+
def lhs_for(key, downcase: false)
|
204
206
|
case key
|
205
207
|
when String
|
206
208
|
"@#{key}"
|
@@ -211,7 +213,7 @@ module Watir
|
|
211
213
|
when :text
|
212
214
|
'normalize-space()'
|
213
215
|
when :contains_text
|
214
|
-
'
|
216
|
+
'normalize-space()'
|
215
217
|
when ::Symbol
|
216
218
|
lhs = "@#{key.to_s.tr('_', '-')}"
|
217
219
|
downcase ? XpathSupport.downcase(lhs) : lhs
|
@@ -221,11 +223,11 @@ module Watir
|
|
221
223
|
end
|
222
224
|
|
223
225
|
def attribute_presence(attribute)
|
224
|
-
lhs_for(attribute
|
226
|
+
lhs_for(attribute)
|
225
227
|
end
|
226
228
|
|
227
229
|
def attribute_absence(attribute)
|
228
|
-
lhs = lhs_for(attribute
|
230
|
+
lhs = lhs_for(attribute)
|
229
231
|
"not(#{lhs})"
|
230
232
|
end
|
231
233
|
|
@@ -236,9 +238,34 @@ module Watir
|
|
236
238
|
|
237
239
|
negate_xpath ? "not(#{expression})" : expression
|
238
240
|
else
|
239
|
-
|
241
|
+
downcase = case_insensitive_attribute?(key)
|
242
|
+
|
243
|
+
lhs = lhs_for(key, downcase: downcase)
|
244
|
+
rhs = XpathSupport.escape(value)
|
245
|
+
rhs = XpathSupport.downcase(rhs) if downcase
|
246
|
+
|
247
|
+
"#{lhs}=#{rhs}"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def build_valid_attributes
|
252
|
+
tag_name = @selector[:tag_name]
|
253
|
+
if tag_name.is_a?(String) && Watir.tag_to_class[tag_name.to_sym]
|
254
|
+
Watir.tag_to_class[tag_name.to_sym].attribute_list
|
255
|
+
else
|
256
|
+
Watir::HTMLElement.attribute_list
|
240
257
|
end
|
241
258
|
end
|
259
|
+
|
260
|
+
def case_insensitive_attribute?(attribute)
|
261
|
+
# type attributes can be upper case - downcase them
|
262
|
+
# https://github.com/watir/watir/issues/72
|
263
|
+
return true if attribute == :type
|
264
|
+
return true if Watir::Element::CASE_INSENSITIVE_ATTRIBUTES.include?(attribute) &&
|
265
|
+
@valid_attributes.include?(attribute)
|
266
|
+
|
267
|
+
false
|
268
|
+
end
|
242
269
|
end
|
243
270
|
end
|
244
271
|
end
|