selenium-webdriver 3.4.0 → 3.4.1

Sign up to get free protection for your applications and to get access to all the features.
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