selenium-webdriver 4.20.1 → 4.21.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c32a4152c7c47427939aba71e601004ee0cd8ad0c2454b00b879d0bd84f01a5e
4
- data.tar.gz: d7cf6a825f70df0366b290da8e7fbf36417bcabe055f1835eca9402b6c37c791
3
+ metadata.gz: 4f5e5266f21804cd7520e61a6036171a3bda203460d944ac61cfd324283f72ae
4
+ data.tar.gz: 8a58672a580239e27c909e781b16ff588f216f535dda5e495eff5123224e36b2
5
5
  SHA512:
6
- metadata.gz: 6f121cc65df3f7174419ab881de0f8283e2bbb22698aa7582f019b1a2e3f7aae04376d0be02507ffbc968fe95512c97fb121515a16a9ff62b845a23e939005f7
7
- data.tar.gz: f0b0dfba817532e5cd9ab1ab0874463823434dcdac0f15efe199d400a53539343cdcaafdecf766596dc36cbc52c6e0454784a44fc748c21e6dc380d279bb29e4
6
+ metadata.gz: 78473145c4ce27a7acf22c58e6c204f7beac53b47a3b18139bb895c08c5d1da90ec5ac558bab429f5284604bba717415be0d8606bb298a68278a22a57947a546
7
+ data.tar.gz: b1ae030703351b2b799881b041c9554e0c23c8cfc60a6e8eb32e7f679a7ea23f1c72c7cdbca203b2830439e2488a1ff56c6763df960f8eb873f08356f79ccf91
data/CHANGES CHANGED
@@ -1,3 +1,15 @@
1
+ 4.21.0 (2024-05-16)
2
+ =========================
3
+
4
+ * Add CDP for Chrome 125 and remove 122
5
+ * Initial extensibility points for Appium
6
+ * Support registering extra headers in HTTP client
7
+ * Support overriding User-Agent in HTTP client
8
+ * Support registering extra bridge commands
9
+ * Support overriding default locator conversion
10
+ * Support registering custom finders for SearchContext
11
+ * Support using custom element classes
12
+
1
13
  4.20.1 (2024-04-25)
2
14
  =========================
3
15
 
Binary file
Binary file
Binary file
@@ -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 = ['java'] + properties + args + @additional_args
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)
@@ -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 = FINDERS[how.to_sym]
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 = FINDERS[how.to_sym]
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, Element::ELEMENT_KEY => element)
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
- Element.new self, element_id_from(execute(:get_active_element))
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 = convert_locator(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
- Element.new self, element_id_from(id)
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 = convert_locator(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| Element.new self, element_id_from(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 Element.new(self, element_id) if element_id
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[Element::ELEMENT_KEY]
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 = DEFAULT_HEADERS.dup
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
 
@@ -19,6 +19,6 @@
19
19
 
20
20
  module Selenium
21
21
  module WebDriver
22
- VERSION = '4.20.1'
22
+ VERSION = '4.21.0'
23
23
  end # WebDriver
24
24
  end # Selenium
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.20.1
4
+ version: 4.21.0
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-04-26 00:00:00.000000000 Z
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