selenium-webdriver 4.20.1 → 4.21.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES +17 -0
- data/bin/linux/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/server.rb +2 -1
- data/lib/selenium/webdriver/common/driver_finder.rb +8 -1
- data/lib/selenium/webdriver/common/search_context.rb +10 -2
- data/lib/selenium/webdriver/remote/bridge/locator_converter.rb +76 -0
- data/lib/selenium/webdriver/remote/bridge.rb +32 -46
- data/lib/selenium/webdriver/remote/http/common.rb +21 -3
- data/lib/selenium/webdriver/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b167c7575b53791dfd989aae9c4ba8375661ff7c43fe343c4f4234673f8f6ba0
|
4
|
+
data.tar.gz: cc520cbd925b221a24e38547b15101bbf68812989affaf7435534f96099f7bc4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7313dfdf491d92732485821260da007ad0b63bd5570e9c5623c7fc6dfdba0b14fdddd6293d62c7e30b31610ca986a7c17291174091e1d3ffb7d3dee81caaeffe
|
7
|
+
data.tar.gz: 63a335b0a954e00c75cee80f9c2129360e1542b9d1d7528b461d51b5840935457a88a3528114f29f146949cf273581143dc1eaa8aefd6b479a3c81e0d3d0cc1f
|
data/CHANGES
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
4.21.1 (2024-05-16)
|
2
|
+
=========================
|
3
|
+
|
4
|
+
* Fixed missing Chrome binary error in Rails system tests.
|
5
|
+
|
6
|
+
4.21.0 (2024-05-16)
|
7
|
+
=========================
|
8
|
+
|
9
|
+
* Add CDP for Chrome 125 and remove 122
|
10
|
+
* Initial extensibility points for Appium
|
11
|
+
* Support registering extra headers in HTTP client
|
12
|
+
* Support overriding User-Agent in HTTP client
|
13
|
+
* Support registering extra bridge commands
|
14
|
+
* Support overriding default locator conversion
|
15
|
+
* Support registering custom finders for SearchContext
|
16
|
+
* Support using custom element classes
|
17
|
+
|
1
18
|
4.20.1 (2024-04-25)
|
2
19
|
=========================
|
3
20
|
|
data/bin/linux/selenium-manager
CHANGED
Binary file
|
data/bin/macos/selenium-manager
CHANGED
Binary file
|
Binary file
|
data/lib/selenium/server.rb
CHANGED
@@ -183,6 +183,7 @@ module Selenium
|
|
183
183
|
def initialize(jar, opts = {})
|
184
184
|
raise Errno::ENOENT, jar unless File.exist?(jar)
|
185
185
|
|
186
|
+
@java = opts.fetch(:java, 'java')
|
186
187
|
@jar = jar
|
187
188
|
@host = '127.0.0.1'
|
188
189
|
@role = opts.fetch(:role, 'standalone')
|
@@ -241,7 +242,7 @@ module Selenium
|
|
241
242
|
# extract any additional_args that start with -D as options
|
242
243
|
properties = @additional_args.dup - @additional_args.delete_if { |arg| arg[/^-D/] }
|
243
244
|
args = ['-jar', @jar, @role, '--port', @port.to_s]
|
244
|
-
server_command = [
|
245
|
+
server_command = [@java] + properties + args + @additional_args
|
245
246
|
cp = WebDriver::ChildProcess.build(*server_command)
|
246
247
|
|
247
248
|
if @log.is_a?(String)
|
@@ -59,7 +59,14 @@ module Selenium
|
|
59
59
|
formatted = {driver_path: Platform.cygwin_path(output['driver_path'], only_cygwin: true),
|
60
60
|
browser_path: Platform.cygwin_path(output['browser_path'], only_cygwin: true)}
|
61
61
|
Platform.assert_executable(formatted[:driver_path])
|
62
|
-
|
62
|
+
|
63
|
+
browser_path = formatted[:browser_path]
|
64
|
+
Platform.assert_executable(browser_path)
|
65
|
+
if @options.respond_to?(:binary) && @options.binary.nil?
|
66
|
+
@options.binary = browser_path
|
67
|
+
@options.browser_version = nil
|
68
|
+
end
|
69
|
+
|
63
70
|
formatted
|
64
71
|
end
|
65
72
|
rescue StandardError => e
|
@@ -35,6 +35,14 @@ module Selenium
|
|
35
35
|
xpath: 'xpath'
|
36
36
|
}.freeze
|
37
37
|
|
38
|
+
class << self
|
39
|
+
attr_accessor :extra_finders
|
40
|
+
|
41
|
+
def finders
|
42
|
+
FINDERS.merge(extra_finders || {})
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
38
46
|
#
|
39
47
|
# Find the first element matching the given arguments
|
40
48
|
#
|
@@ -57,7 +65,7 @@ module Selenium
|
|
57
65
|
def find_element(*args)
|
58
66
|
how, what = extract_args(args)
|
59
67
|
|
60
|
-
by =
|
68
|
+
by = SearchContext.finders[how.to_sym]
|
61
69
|
raise ArgumentError, "cannot find element by #{how.inspect}" unless by
|
62
70
|
|
63
71
|
bridge.find_element_by by, what, ref
|
@@ -72,7 +80,7 @@ module Selenium
|
|
72
80
|
def find_elements(*args)
|
73
81
|
how, what = extract_args(args)
|
74
82
|
|
75
|
-
by =
|
83
|
+
by = SearchContext.finders[how.to_sym]
|
76
84
|
raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
|
77
85
|
|
78
86
|
bridge.find_elements_by by, what, ref
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Licensed to the Software Freedom Conservancy (SFC) under one
|
4
|
+
# or more contributor license agreements. See the NOTICE file
|
5
|
+
# distributed with this work for additional information
|
6
|
+
# regarding copyright ownership. The SFC licenses this file
|
7
|
+
# to you under the Apache License, Version 2.0 (the
|
8
|
+
# "License"); you may not use this file except in compliance
|
9
|
+
# with the License. You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing,
|
14
|
+
# software distributed under the License is distributed on an
|
15
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
16
|
+
# KIND, either express or implied. See the License for the
|
17
|
+
# specific language governing permissions and limitations
|
18
|
+
# under the License.
|
19
|
+
|
20
|
+
module Selenium
|
21
|
+
module WebDriver
|
22
|
+
module Remote
|
23
|
+
class Bridge
|
24
|
+
class LocatorConverter
|
25
|
+
ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/
|
26
|
+
UNICODE_CODE_POINT = 30
|
27
|
+
|
28
|
+
#
|
29
|
+
# Converts a locator to a specification compatible one.
|
30
|
+
# @param [String, Symbol] how
|
31
|
+
# @param [String] what
|
32
|
+
#
|
33
|
+
|
34
|
+
def convert(how, what)
|
35
|
+
how = SearchContext.finders[how.to_sym] || how
|
36
|
+
|
37
|
+
case how
|
38
|
+
when 'class name'
|
39
|
+
how = 'css selector'
|
40
|
+
what = ".#{escape_css(what.to_s)}"
|
41
|
+
when 'id'
|
42
|
+
how = 'css selector'
|
43
|
+
what = "##{escape_css(what.to_s)}"
|
44
|
+
when 'name'
|
45
|
+
how = 'css selector'
|
46
|
+
what = "*[name='#{escape_css(what.to_s)}']"
|
47
|
+
end
|
48
|
+
|
49
|
+
if what.is_a?(Hash)
|
50
|
+
what = what.each_with_object({}) do |(h, w), hash|
|
51
|
+
h, w = convert(h.to_s, w)
|
52
|
+
hash[h] = w
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
[how, what]
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
#
|
62
|
+
# Escapes invalid characters in CSS selector.
|
63
|
+
# @see https://mathiasbynens.be/notes/css-escapes
|
64
|
+
#
|
65
|
+
|
66
|
+
def escape_css(string)
|
67
|
+
string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
|
68
|
+
string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..]}" if string[0]&.match?(/[[:digit:]]/)
|
69
|
+
|
70
|
+
string
|
71
|
+
end
|
72
|
+
end # LocatorConverter
|
73
|
+
end # Bridge
|
74
|
+
end # Remote
|
75
|
+
end # WebDriver
|
76
|
+
end # Selenium
|
@@ -22,6 +22,8 @@ module Selenium
|
|
22
22
|
module Remote
|
23
23
|
class Bridge
|
24
24
|
autoload :COMMANDS, 'selenium/webdriver/remote/bridge/commands'
|
25
|
+
autoload :LocatorConverter, 'selenium/webdriver/remote/bridge/locator_converter'
|
26
|
+
|
25
27
|
include Atoms
|
26
28
|
|
27
29
|
PORT = 4444
|
@@ -29,6 +31,25 @@ module Selenium
|
|
29
31
|
attr_accessor :http, :file_detector
|
30
32
|
attr_reader :capabilities
|
31
33
|
|
34
|
+
class << self
|
35
|
+
attr_reader :extra_commands
|
36
|
+
attr_writer :element_class, :locator_converter
|
37
|
+
|
38
|
+
def add_command(name, verb, url, &block)
|
39
|
+
@extra_commands ||= {}
|
40
|
+
@extra_commands[name] = [verb, url]
|
41
|
+
define_method(name, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def locator_converter
|
45
|
+
@locator_converter ||= LocatorConverter.new
|
46
|
+
end
|
47
|
+
|
48
|
+
def element_class
|
49
|
+
@element_class ||= Element
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
32
53
|
#
|
33
54
|
# Initializes the bridge with the given server URL
|
34
55
|
# @param [String, URI] url url for the remote server
|
@@ -43,6 +64,8 @@ module Selenium
|
|
43
64
|
@http = http_client || Http::Default.new
|
44
65
|
@http.server_url = uri
|
45
66
|
@file_detector = nil
|
67
|
+
|
68
|
+
@locator_converter = self.class.locator_converter
|
46
69
|
end
|
47
70
|
|
48
71
|
#
|
@@ -413,7 +436,7 @@ module Selenium
|
|
413
436
|
"e.initEvent('submit', true, true);\n" \
|
414
437
|
"if (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n"
|
415
438
|
|
416
|
-
execute_script(script,
|
439
|
+
execute_script(script, Bridge.element_class::ELEMENT_KEY => element)
|
417
440
|
rescue Error::JavascriptError
|
418
441
|
raise Error::UnsupportedOperationError, 'To submit an element, it must be nested inside a form element'
|
419
442
|
end
|
@@ -500,13 +523,13 @@ module Selenium
|
|
500
523
|
#
|
501
524
|
|
502
525
|
def active_element
|
503
|
-
|
526
|
+
Bridge.element_class.new self, element_id_from(execute(:get_active_element))
|
504
527
|
end
|
505
528
|
|
506
529
|
alias switch_to_active_element active_element
|
507
530
|
|
508
531
|
def find_element_by(how, what, parent_ref = [])
|
509
|
-
how, what =
|
532
|
+
how, what = @locator_converter.convert(how, what)
|
510
533
|
|
511
534
|
return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'
|
512
535
|
|
@@ -520,11 +543,11 @@ module Selenium
|
|
520
543
|
execute :find_element, {}, {using: how, value: what.to_s}
|
521
544
|
end
|
522
545
|
|
523
|
-
|
546
|
+
Bridge.element_class.new self, element_id_from(id)
|
524
547
|
end
|
525
548
|
|
526
549
|
def find_elements_by(how, what, parent_ref = [])
|
527
|
-
how, what =
|
550
|
+
how, what = @locator_converter.convert(how, what)
|
528
551
|
|
529
552
|
return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'
|
530
553
|
|
@@ -538,7 +561,7 @@ module Selenium
|
|
538
561
|
execute :find_elements, {}, {using: how, value: what.to_s}
|
539
562
|
end
|
540
563
|
|
541
|
-
ids.map { |id|
|
564
|
+
ids.map { |id| Bridge.element_class.new self, element_id_from(id) }
|
542
565
|
end
|
543
566
|
|
544
567
|
def shadow_root(element)
|
@@ -612,7 +635,7 @@ module Selenium
|
|
612
635
|
end
|
613
636
|
|
614
637
|
def commands(command)
|
615
|
-
command_list[command]
|
638
|
+
command_list[command] || Bridge.extra_commands[command]
|
616
639
|
end
|
617
640
|
|
618
641
|
def unwrap_script_result(arg)
|
@@ -621,7 +644,7 @@ module Selenium
|
|
621
644
|
arg.map { |e| unwrap_script_result(e) }
|
622
645
|
when Hash
|
623
646
|
element_id = element_id_from(arg)
|
624
|
-
return
|
647
|
+
return Bridge.element_class.new(self, element_id) if element_id
|
625
648
|
|
626
649
|
shadow_root_id = shadow_root_id_from(arg)
|
627
650
|
return ShadowRoot.new self, shadow_root_id if shadow_root_id
|
@@ -633,7 +656,7 @@ module Selenium
|
|
633
656
|
end
|
634
657
|
|
635
658
|
def element_id_from(id)
|
636
|
-
id['ELEMENT'] || id[
|
659
|
+
id['ELEMENT'] || id[Bridge.element_class::ELEMENT_KEY]
|
637
660
|
end
|
638
661
|
|
639
662
|
def shadow_root_id_from(id)
|
@@ -644,43 +667,6 @@ module Selenium
|
|
644
667
|
capabilities = {alwaysMatch: capabilities} if !capabilities['alwaysMatch'] && !capabilities['firstMatch']
|
645
668
|
{capabilities: capabilities}
|
646
669
|
end
|
647
|
-
|
648
|
-
def convert_locator(how, what)
|
649
|
-
how = SearchContext::FINDERS[how.to_sym] || how
|
650
|
-
|
651
|
-
case how
|
652
|
-
when 'class name'
|
653
|
-
how = 'css selector'
|
654
|
-
what = ".#{escape_css(what.to_s)}"
|
655
|
-
when 'id'
|
656
|
-
how = 'css selector'
|
657
|
-
what = "##{escape_css(what.to_s)}"
|
658
|
-
when 'name'
|
659
|
-
how = 'css selector'
|
660
|
-
what = "*[name='#{escape_css(what.to_s)}']"
|
661
|
-
end
|
662
|
-
|
663
|
-
if what.is_a?(Hash)
|
664
|
-
what = what.each_with_object({}) do |(h, w), hash|
|
665
|
-
h, w = convert_locator(h.to_s, w)
|
666
|
-
hash[h] = w
|
667
|
-
end
|
668
|
-
end
|
669
|
-
|
670
|
-
[how, what]
|
671
|
-
end
|
672
|
-
|
673
|
-
ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/
|
674
|
-
UNICODE_CODE_POINT = 30
|
675
|
-
|
676
|
-
# Escapes invalid characters in CSS selector.
|
677
|
-
# @see https://mathiasbynens.be/notes/css-escapes
|
678
|
-
def escape_css(string)
|
679
|
-
string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
|
680
|
-
string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..]}" if string[0]&.match?(/[[:digit:]]/)
|
681
|
-
|
682
|
-
string
|
683
|
-
end
|
684
670
|
end # Bridge
|
685
671
|
end # Remote
|
686
672
|
end # WebDriver
|
@@ -26,10 +26,18 @@ module Selenium
|
|
26
26
|
CONTENT_TYPE = 'application/json'
|
27
27
|
DEFAULT_HEADERS = {
|
28
28
|
'Accept' => CONTENT_TYPE,
|
29
|
-
'Content-Type' => "#{CONTENT_TYPE}; charset=UTF-8"
|
30
|
-
'User-Agent' => "selenium/#{WebDriver::VERSION} (ruby #{Platform.os})"
|
29
|
+
'Content-Type' => "#{CONTENT_TYPE}; charset=UTF-8"
|
31
30
|
}.freeze
|
32
31
|
|
32
|
+
class << self
|
33
|
+
attr_accessor :extra_headers
|
34
|
+
attr_writer :user_agent
|
35
|
+
|
36
|
+
def user_agent
|
37
|
+
@user_agent ||= "selenium/#{WebDriver::VERSION} (ruby #{Platform.os})"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
33
41
|
attr_writer :server_url
|
34
42
|
|
35
43
|
def quit_errors
|
@@ -42,7 +50,7 @@ module Selenium
|
|
42
50
|
|
43
51
|
def call(verb, url, command_hash)
|
44
52
|
url = server_url.merge(url) unless url.is_a?(URI)
|
45
|
-
headers =
|
53
|
+
headers = common_headers.dup
|
46
54
|
headers['Cache-Control'] = 'no-cache' if verb == :get
|
47
55
|
|
48
56
|
if command_hash
|
@@ -61,6 +69,16 @@ module Selenium
|
|
61
69
|
|
62
70
|
private
|
63
71
|
|
72
|
+
def common_headers
|
73
|
+
@common_headers ||= begin
|
74
|
+
headers = DEFAULT_HEADERS.dup
|
75
|
+
headers['User-Agent'] = Common.user_agent
|
76
|
+
headers = headers.merge(Common.extra_headers || {})
|
77
|
+
|
78
|
+
headers
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
64
82
|
def server_url
|
65
83
|
return @server_url if @server_url
|
66
84
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: selenium-webdriver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.21.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Rodionov
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2024-
|
13
|
+
date: 2024-05-16 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: base64
|
@@ -395,6 +395,7 @@ files:
|
|
395
395
|
- lib/selenium/webdriver/remote.rb
|
396
396
|
- lib/selenium/webdriver/remote/bridge.rb
|
397
397
|
- lib/selenium/webdriver/remote/bridge/commands.rb
|
398
|
+
- lib/selenium/webdriver/remote/bridge/locator_converter.rb
|
398
399
|
- lib/selenium/webdriver/remote/capabilities.rb
|
399
400
|
- lib/selenium/webdriver/remote/driver.rb
|
400
401
|
- lib/selenium/webdriver/remote/features.rb
|