selenium-webdriver 3.4.0 → 3.4.1

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 (44) hide show
  1. data/CHANGES +35 -1
  2. data/Gemfile.lock +54 -0
  3. data/lib/selenium/webdriver.rb +8 -10
  4. data/lib/selenium/webdriver/chrome.rb +2 -1
  5. data/lib/selenium/webdriver/chrome/{bridge.rb → driver.rb} +47 -32
  6. data/lib/selenium/webdriver/chrome/options.rb +170 -0
  7. data/lib/selenium/webdriver/common/driver.rb +20 -39
  8. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +5 -0
  9. data/lib/selenium/webdriver/common/element.rb +5 -1
  10. data/lib/selenium/webdriver/common/logger.rb +10 -0
  11. data/lib/selenium/webdriver/common/w3c_error.rb +2 -2
  12. data/lib/selenium/webdriver/edge.rb +2 -1
  13. data/lib/selenium/webdriver/edge/bridge.rb +2 -91
  14. data/lib/selenium/webdriver/edge/driver.rb +80 -0
  15. data/lib/selenium/webdriver/firefox.rb +6 -3
  16. data/lib/selenium/webdriver/firefox/driver.rb +50 -0
  17. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  18. data/lib/selenium/webdriver/firefox/legacy/driver.rb +81 -0
  19. data/lib/selenium/webdriver/firefox/marionette/driver.rb +101 -0
  20. data/lib/selenium/webdriver/firefox/options.rb +139 -0
  21. data/lib/selenium/webdriver/ie.rb +1 -1
  22. data/lib/selenium/webdriver/ie/{bridge.rb → driver.rb} +13 -13
  23. data/lib/selenium/webdriver/phantomjs.rb +1 -1
  24. data/lib/selenium/webdriver/phantomjs/{bridge.rb → driver.rb} +10 -13
  25. data/lib/selenium/webdriver/remote.rb +11 -13
  26. data/lib/selenium/webdriver/remote/bridge.rb +87 -586
  27. data/lib/selenium/webdriver/remote/capabilities.rb +3 -1
  28. data/lib/selenium/webdriver/remote/driver.rb +44 -0
  29. data/lib/selenium/webdriver/remote/http/default.rb +1 -1
  30. data/lib/selenium/webdriver/remote/oss/bridge.rb +586 -0
  31. data/lib/selenium/webdriver/remote/{commands.rb → oss/commands.rb} +9 -5
  32. data/lib/selenium/webdriver/remote/oss/driver.rb +44 -0
  33. data/lib/selenium/webdriver/remote/w3c/bridge.rb +573 -0
  34. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +266 -0
  35. data/lib/selenium/webdriver/remote/{w3c_commands.rb → w3c/commands.rb} +9 -4
  36. data/lib/selenium/webdriver/remote/w3c/driver.rb +41 -0
  37. data/lib/selenium/webdriver/safari.rb +3 -6
  38. data/lib/selenium/webdriver/safari/{bridge.rb → driver.rb} +14 -6
  39. data/selenium-webdriver.gemspec +1 -3
  40. metadata +29 -45
  41. data/lib/selenium/webdriver/firefox/bridge.rb +0 -70
  42. data/lib/selenium/webdriver/firefox/w3c_bridge.rb +0 -114
  43. data/lib/selenium/webdriver/remote/w3c_bridge.rb +0 -663
  44. data/lib/selenium/webdriver/remote/w3c_capabilities.rb +0 -231
data/CHANGES CHANGED
@@ -1,4 +1,38 @@
1
- 3.4.0 (Unreleased)
1
+ 3.4.1 (2017-06-13)
2
+ ==================
3
+
4
+ Ruby:
5
+ * Implemented a new dual-dialect mechanism for communication with drivers
6
+ (a.k.a. protocol handshake). There shouldn't be any noticeable differences
7
+ but since this is a significant refactoring of internal APIs, some bugs
8
+ could slip into the release.
9
+ * Renamed ElementClickIntercepted to ElementClickInterceptedError.
10
+ * Renamed ElementNotInteractable to ElementNotInteractableError.
11
+ * Deprecated W3CCapabilities in favor of Capabilities (it was meant to be private API).
12
+ * Added a warning when trying to save screenshot without .png extension (thanks @abotalov).
13
+
14
+ IE:
15
+ * Added support for both old IEDriver which uses OSS dialect of JSON wire
16
+ protocol (<= 3.4.0) and new IEDriver which uses W3C dialect (not yet released).
17
+
18
+ Safari:
19
+ * Removed runtime dependencies used for old SafariDriver (u.g. websocket).
20
+
21
+ Chrome:
22
+ * Added new Chrome::Options class that should be used to customize browser
23
+ behavior (command line arguments, extensions, preferences, mobile emulation, etc.).
24
+ The instance of options class can be passed to driver initialization using
25
+ :options key. Old way of passing these customization directly to driver
26
+ initialization is deprecated.
27
+
28
+ Firefox:
29
+ * Added new Firefox::Options class that should be used to customize browser
30
+ behavior (command line arguments, profile, preferences, Firefox binary, etc.).
31
+ The instance of options class can be passed to driver initialization using
32
+ :options key. Old way of passing these customization directly to driver
33
+ initialization is deprecated.
34
+
35
+ 3.4.0 (2017-04-21)
2
36
  ===================
3
37
 
4
38
  Edge:
@@ -0,0 +1,54 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ selenium-webdriver (3.4.1)
5
+ childprocess (~> 0.5)
6
+ rubyzip (~> 1.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.5.1)
12
+ public_suffix (~> 2.0, >= 2.0.2)
13
+ childprocess (0.7.0)
14
+ ffi (~> 1.0, >= 1.0.11)
15
+ crack (0.4.3)
16
+ safe_yaml (~> 1.0.0)
17
+ diff-lcs (1.3)
18
+ ffi (1.9.18)
19
+ hashdiff (0.3.4)
20
+ public_suffix (2.0.5)
21
+ rack (1.6.8)
22
+ rspec (3.6.0)
23
+ rspec-core (~> 3.6.0)
24
+ rspec-expectations (~> 3.6.0)
25
+ rspec-mocks (~> 3.6.0)
26
+ rspec-core (3.6.0)
27
+ rspec-support (~> 3.6.0)
28
+ rspec-expectations (3.6.0)
29
+ diff-lcs (>= 1.2.0, < 2.0)
30
+ rspec-support (~> 3.6.0)
31
+ rspec-mocks (3.6.0)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.6.0)
34
+ rspec-support (3.6.0)
35
+ rubyzip (1.2.1)
36
+ safe_yaml (1.0.4)
37
+ webmock (2.3.2)
38
+ addressable (>= 2.3.6)
39
+ crack (>= 0.3.2)
40
+ hashdiff
41
+ yard (0.8.7.6)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ rack (~> 1.0)
48
+ rspec (~> 3.0)
49
+ selenium-webdriver!
50
+ webmock (~> 2.0)
51
+ yard (~> 0.8.7)
52
+
53
+ BUNDLED WITH
54
+ 1.15.1
@@ -57,19 +57,17 @@ module Selenium
57
57
  # @overload for(browser, opts)
58
58
  # @param [:ie, :internet_explorer, :edge, :remote, :chrome, :firefox, :ff, :phantomjs, :safari] browser The browser to
59
59
  # create the driver for
60
- # @param [Hash] opts Options passed to Bridge.new
60
+ # @param [Hash] opts Options passed to Driver.new
61
61
  #
62
62
  # @return [Driver]
63
63
  #
64
- # @see Selenium::WebDriver::Remote::Bridge
65
- # @see Selenium::WebDriver::Remote::W3CBridge
66
- # @see Selenium::WebDriver::Firefox::Bridge
67
- # @see Selenium::WebDriver::Firefox::W3CBridge
68
- # @see Selenium::WebDriver::IE::Bridge
69
- # @see Selenium::WebDriver::Edge::Bridge
70
- # @see Selenium::WebDriver::Chrome::Bridge
71
- # @see Selenium::WebDriver::PhantomJS::Bridge
72
- # @see Selenium::WebDriver::Safari::Bridge
64
+ # @see Selenium::WebDriver::Remote::Driver
65
+ # @see Selenium::WebDriver::Firefox::Driver
66
+ # @see Selenium::WebDriver::IE::Driver
67
+ # @see Selenium::WebDriver::Edge::Driver
68
+ # @see Selenium::WebDriver::Chrome::Driver
69
+ # @see Selenium::WebDriver::PhantomJS::Driver
70
+ # @see Selenium::WebDriver::Safari::Driver
73
71
  #
74
72
  # @example
75
73
  #
@@ -20,8 +20,9 @@
20
20
  require 'net/http'
21
21
 
22
22
  require 'selenium/webdriver/chrome/service'
23
- require 'selenium/webdriver/chrome/bridge'
23
+ require 'selenium/webdriver/chrome/driver'
24
24
  require 'selenium/webdriver/chrome/profile'
25
+ require 'selenium/webdriver/chrome/options'
25
26
 
26
27
  module Selenium
27
28
  module WebDriver
@@ -20,8 +20,16 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Chrome
23
+
24
+ #
25
+ # Driver implementation for Chrome.
23
26
  # @api private
24
- class Bridge < Remote::Bridge
27
+ #
28
+
29
+ class Driver < WebDriver::Driver
30
+ include DriverExtensions::HasWebStorage
31
+ include DriverExtensions::TakesScreenshot
32
+
25
33
  def initialize(opts = {})
26
34
  opts[:desired_capabilities] = create_capabilities(opts)
27
35
 
@@ -31,16 +39,12 @@ module Selenium
31
39
 
32
40
  opts[:driver_opts] ||= {}
33
41
  if opts.key? :service_log_path
34
- WebDriver.logger.warn <<-DEPRECATE.gsub(/\n +| {2,}/, ' ').freeze
35
- [DEPRECATION] `:service_log_path` is deprecated. Use `driver_opts: {log_path: #{opts[:service_log_path]}}`
36
- DEPRECATE
42
+ WebDriver.logger.deprecate ':service_log_path', "driver_opts: {log_path: '#{opts[:service_log_path]}'}"
37
43
  opts[:driver_opts][:log_path] = opts.delete :service_log_path
38
44
  end
39
45
 
40
46
  if opts.key? :service_args
41
- WebDriver.logger.warn <<-DEPRECATE.gsub(/\n +| {2,}/, ' ').freeze
42
- [DEPRECATION] `:service_args` is deprecated. Pass switches using `driver_opts`
43
- DEPRECATE
47
+ WebDriver.logger.deprecate ':service_args', "driver_opts: {args: #{opts[:service_args]}}"
44
48
  opts[:driver_opts][:args] = opts.delete(:service_args)
45
49
  end
46
50
 
@@ -49,22 +53,15 @@ module Selenium
49
53
  opts[:url] = @service.uri
50
54
  end
51
55
 
52
- super(opts)
56
+ listener = opts.delete(:listener)
57
+ @bridge = Remote::Bridge.handshake(opts)
58
+ super(@bridge, listener: listener)
53
59
  end
54
60
 
55
61
  def browser
56
62
  :chrome
57
63
  end
58
64
 
59
- def driver_extensions
60
- [DriverExtensions::TakesScreenshot,
61
- DriverExtensions::HasWebStorage]
62
- end
63
-
64
- def capabilities
65
- @capabilities ||= Remote::Capabilities.chrome
66
- end
67
-
68
65
  def quit
69
66
  super
70
67
  ensure
@@ -75,34 +72,52 @@ module Selenium
75
72
 
76
73
  def create_capabilities(opts)
77
74
  caps = opts.delete(:desired_capabilities) { Remote::Capabilities.chrome }
75
+ options = opts.delete(:options) { Options.new }
78
76
 
79
- chrome_options = caps['chromeOptions'] || caps[:chrome_options] || {}
80
- chrome_options['binary'] = Chrome.path if Chrome.path
81
- args = opts.delete(:args) || opts.delete(:switches) || []
77
+ args = opts.delete(:args)
78
+ if args
79
+ WebDriver.logger.deprecate ':args', 'Selenium::WebDriver::Chrome::Options#add_argument'
80
+ raise ArgumentError, ':args must be an Array of Strings' unless args.is_a? Array
81
+ args.each { |arg| options.add_argument(arg.to_s) }
82
+ end
83
+
84
+ profile = opts.delete(:profile)
85
+ if profile
86
+ profile = profile.as_json
87
+
88
+ if options.args.none? { |arg| arg =~ /user-data-dir/ }
89
+ options.add_argument("--user-data-dir=#{profile[:directory]}")
90
+ end
82
91
 
83
- unless args.is_a? Array
84
- raise ArgumentError, ':args must be an Array of Strings'
92
+ if profile[:extensions]
93
+ WebDriver.logger.deprecate 'Using Selenium::WebDriver::Chrome::Profile#extensions',
94
+ 'Selenium::WebDriver::Chrome::Options#add_extension'
95
+ profile[:extensions].each do |extension|
96
+ options.add_encoded_extension(extension)
97
+ end
98
+ end
85
99
  end
86
100
 
87
- args.map!(&:to_s)
88
- profile = opts.delete(:profile).as_json if opts.key?(:profile)
101
+ detach = opts.delete(:detach)
102
+ options.add_option(:detach, true) if detach
89
103
 
90
- if profile && args.none? { |arg| arg =~ /user-data-dir/ }
91
- args << "--user-data-dir=#{profile[:directory]}"
104
+ prefs = opts.delete(:prefs)
105
+ if prefs
106
+ WebDriver.logger.deprecate ':prefs', 'Selenium::WebDriver::Chrome::Options#add_preference'
107
+ prefs.each do |key, value|
108
+ options.add_preference(key, value)
109
+ end
92
110
  end
93
- chrome_options['args'] = args unless args.empty?
94
111
 
95
- chrome_options['extensions'] = profile[:extensions] if profile && profile[:extensions]
96
- chrome_options['detach'] = true if opts.delete(:detach)
97
- chrome_options['prefs'] = opts.delete(:prefs) if opts.key?(:prefs)
112
+ options = options.as_json
113
+ caps[:chrome_options] = options unless options.empty?
98
114
 
99
- caps[:chrome_options] = chrome_options unless chrome_options.empty?
100
115
  caps[:proxy] = opts.delete(:proxy) if opts.key?(:proxy)
101
116
  caps[:proxy] ||= opts.delete('proxy') if opts.key?('proxy')
102
117
 
103
118
  caps
104
119
  end
105
- end # Bridge
120
+ end # Driver
106
121
  end # Chrome
107
122
  end # WebDriver
108
123
  end # Selenium
@@ -0,0 +1,170 @@
1
+ # encoding: utf-8
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 Chrome
23
+ class Options
24
+ attr_reader :args, :prefs, :options, :emulation, :extensions, :encoded_extensions
25
+ attr_accessor :binary
26
+
27
+ #
28
+ # Create a new Options instance.
29
+ #
30
+ # @example
31
+ # options = Selenium::WebDriver::Chrome::Options.new(args: ['start-maximized', 'user-data-dir=/tmp/temp_profile'])
32
+ # driver = Selenium::WebDriver.for(:chrome, options: options)
33
+ #
34
+ # @param [Hash] opts the pre-defined options to create the Chrome::Options with
35
+ # @option opts [Array<String>] :args List of command-line arguments to use when starting Chrome
36
+ # @option opts [String] :binary Path to the Chrome executable to use
37
+ # @option opts [Hash] :prefs A hash with each entry consisting of the name of the preference and its value
38
+ # @option opts [Array<String>] :extensions A list of paths to (.crx) Chrome extensions to install on startup
39
+ # @option opts [Hash] :options A hash for raw options
40
+ # @option opts [Hash] :emulation A hash for raw emulation options
41
+ #
42
+
43
+ def initialize(**opts)
44
+ @args = opts.delete(:args) || []
45
+ @binary = opts.delete(:binary) || Chrome.path
46
+ @prefs = opts.delete(:prefs) || {}
47
+ @extensions = opts.delete(:extensions) || []
48
+ @options = opts.delete(:options) || {}
49
+ @emulation = opts.delete(:emulation) || {}
50
+ @encoded_extensions = []
51
+ end
52
+
53
+ #
54
+ # Add an extension by local path.
55
+ #
56
+ # @example
57
+ # options = Selenium::WebDriver::Chrome::Options.new
58
+ # options.add_extension('/path/to/extension.crx')
59
+ #
60
+ # @param [String] path The local path to the .crx file
61
+ #
62
+
63
+ def add_extension(path)
64
+ raise Error::WebDriverError, "could not find extension at #{path.inspect}" unless File.file?(path)
65
+ raise Error::WebDriverError, "file was not an extension #{path.inspect}" unless File.extname(path) == '.crx'
66
+ @extensions << path
67
+ end
68
+
69
+ #
70
+ # Add an extension by Base64-encoded string.
71
+ #
72
+ # @example
73
+ # options = Selenium::WebDriver::Chrome::Options.new
74
+ # options.add_encoded_extension(encoded_string)
75
+ #
76
+ # @param [String] encoded The Base64-encoded string of the .crx file
77
+ #
78
+
79
+ def add_encoded_extension(encoded)
80
+ @encoded_extensions << encoded
81
+ end
82
+
83
+ #
84
+ # Add a command-line argument to use when starting Chrome
85
+ #
86
+ # @example Start Chrome maximized
87
+ # options = Selenium::WebDriver::Chrome::Options.new
88
+ # options.add_argument('start-maximized')
89
+ #
90
+ # @param [String] arg The command-line argument to add
91
+ #
92
+
93
+ def add_argument(arg)
94
+ @args << arg
95
+ end
96
+
97
+ #
98
+ # Add a new option not yet handled by bindings.
99
+ #
100
+ # @example Leave Chrome open when chromedriver is killed
101
+ # options = Selenium::WebDriver::Chrome::Options.new
102
+ # options.add_option(:detach, true)
103
+ #
104
+ # @param [String, Symbol] name Name of the option
105
+ # @param [Boolean, String, Integer] value Value of the option
106
+ #
107
+
108
+ def add_option(name, value)
109
+ @options[name] = value
110
+ end
111
+
112
+ #
113
+ # Add a preference that is only applied to the user profile in use.
114
+ #
115
+ # @example Set the default homepage
116
+ # options = Selenium::WebDriver::Chrome::Options.new
117
+ # options.add_preference('homepage', 'http://www.seleniumhq.com/')
118
+ #
119
+ # @param [String] name Key of the preference
120
+ # @param [Boolean, String, Integer] value Value of the preference
121
+ #
122
+
123
+ def add_preference(name, value)
124
+ prefs[name] = value
125
+ end
126
+
127
+ #
128
+ # Add an emulation device name
129
+ #
130
+ # @example Start Chrome in mobile emulation mode by device name
131
+ # options = Selenium::WebDriver::Chrome::Options.new
132
+ # options.add_emulated_device(device_name: 'iPhone 6')
133
+ #
134
+ # @example Start Chrome in mobile emulation mode by device metrics
135
+ # options = Selenium::WebDriver::Chrome::Options.new
136
+ # options.add_emulated_device(device_metrics: {width: 400, height: 800, pixelRatio: 1, touch: true})
137
+ #
138
+ # @param [String] device_name Name of the device or a hash containing width, height, pixelRatio, touch
139
+ # @param [Hash] device_metrics Hash containing width, height, pixelRatio, touch
140
+ # @param [String] user_agent Full user agent
141
+ #
142
+
143
+ def add_emulation(device_name: nil, device_metrics: nil, user_agent: nil)
144
+ @emulation[:deviceName] = device_name if device_name
145
+ @emulation[:deviceMetrics] = device_metrics if device_metrics
146
+ @emulation[:userAgent] = user_agent if user_agent
147
+ end
148
+
149
+ #
150
+ # @api private
151
+ #
152
+
153
+ def as_json(*)
154
+ extensions = @extensions.map do |crx_path|
155
+ File.open(crx_path, 'rb') { |crx_file| Base64.strict_encode64 crx_file.read }
156
+ end
157
+ extensions.concat(@encoded_extensions)
158
+
159
+ opts = @options
160
+ opts[:binary] = @binary if @binary
161
+ opts[:args] = @args if @args.any?
162
+ opts[:extensions] = extensions if extensions.any?
163
+ opts[:mobileEmulation] = @emulation unless @emulation.empty?
164
+ opts[:prefs] = @prefs unless @prefs.empty?
165
+ opts
166
+ end
167
+ end # Profile
168
+ end # Chrome
169
+ end # WebDriver
170
+ end # Selenium
@@ -41,40 +41,24 @@ module Selenium
41
41
  #
42
42
 
43
43
  def for(browser, opts = {})
44
- listener = opts.delete(:listener)
45
-
46
- bridge = case browser
47
- when :firefox, :ff
48
- if Remote::W3CCapabilities.w3c?(opts)
49
- if opts[:desired_capabilities].is_a? Remote::Capabilities
50
- opts[:desired_capabilities] = Remote::W3CCapabilities.new(opts[:desired_capabilities].send(:capabilities))
51
- end
52
- Firefox::W3CBridge.new(opts)
53
- else
54
- Firefox::Bridge.new(opts)
55
- end
56
- when :remote
57
- Remote::Bridge.new(opts)
58
- when :ie, :internet_explorer
59
- IE::Bridge.new(opts)
60
- when :chrome
61
- Chrome::Bridge.new(opts)
62
- when :edge
63
- if opts[:desired_capabilities]
64
- opts[:desired_capabilities] = Remote::W3CCapabilities.new(opts[:desired_capabilities].send(:capabilities))
65
- end
66
- Edge::Bridge.new(opts)
67
- when :phantomjs
68
- PhantomJS::Bridge.new(opts)
69
- when :safari
70
- Safari::Bridge.new(opts)
71
- else
72
- raise ArgumentError, "unknown driver: #{browser.inspect}"
73
- end
74
-
75
- bridge = Support::EventFiringBridge.new(bridge, listener) if listener
76
-
77
- new(bridge)
44
+ case browser
45
+ when :chrome
46
+ Chrome::Driver.new(opts)
47
+ when :internet_explorer, :ie
48
+ IE::Driver.new(opts)
49
+ when :safari
50
+ Safari::Driver.new(opts)
51
+ when :phantomjs
52
+ PhantomJS::Driver.new(opts)
53
+ when :firefox, :ff
54
+ Firefox::Driver.new(opts)
55
+ when :edge
56
+ Edge::Driver.new(opts)
57
+ when :remote
58
+ Remote::Driver.new(opts)
59
+ else
60
+ raise ArgumentError, "unknown driver: #{browser.inspect}"
61
+ end
78
62
  end
79
63
  end
80
64
 
@@ -85,12 +69,9 @@ module Selenium
85
69
  # @api private
86
70
  #
87
71
 
88
- def initialize(bridge)
72
+ def initialize(bridge, listener: nil)
89
73
  @bridge = bridge
90
-
91
- # TODO: refactor this away
92
- return if bridge.driver_extensions.empty?
93
- extend(*bridge.driver_extensions)
74
+ @bridge = Support::EventFiringBridge.new(bridge, listener) if listener
94
75
  end
95
76
 
96
77
  def inspect