selenium-webdriver 2.20.0 → 2.21.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,20 @@
1
+ 2.21.0 (2012-04-11)
2
+ ===================
3
+
4
+ * Add Selenium::WebDriver::Window#maximize (#3489)
5
+ * Safari:
6
+ * New driver! See http://code.google.com/p/selenium/wiki/SafariDriver.
7
+ * Firefox:
8
+ * Significant stability improvements.
9
+ * Native events support for Firefox 11
10
+ * Dropped native events support for Firefox 4-9
11
+ * Window maximize implementation.
12
+ * IE:
13
+ * Ignore invisible dialogs (#3360).
14
+ * Window maximize implementation.
15
+ * Android:
16
+ * Accept SSL certificates (#3504).
17
+
1
18
  2.20.0 (2012-02-28)
2
19
  ===================
3
20
 
@@ -101,6 +101,30 @@ module Selenium
101
101
  end
102
102
  end
103
103
 
104
+ #
105
+ # The server port
106
+ #
107
+
108
+ attr_accessor :port
109
+
110
+ #
111
+ # The server timeout
112
+ #
113
+
114
+ attr_accessor :timeout
115
+
116
+ #
117
+ # Whether to launch the server in the background
118
+ #
119
+
120
+ attr_accessor :background
121
+
122
+ #
123
+ # Path to log file, or 'true' for stdout.
124
+ #
125
+
126
+ attr_accessor :log
127
+
104
128
  #
105
129
  # @param [String] jar Path to the server jar.
106
130
  # @param [Hash] opts the options to create the server process with
@@ -190,7 +214,7 @@ module Selenium
190
214
  cp = ChildProcess.build("java", "-jar", @jar, "-port", @port.to_s, *@additional_args)
191
215
  io = cp.io
192
216
 
193
- if @log.kind_of?(String) && !@background
217
+ if @log.kind_of?(String)
194
218
  @log_file = File.open(@log, "w")
195
219
  io.stdout = io.stderr = @log_file
196
220
  elsif @log
@@ -19,6 +19,7 @@ module Selenium
19
19
  autoload :IPhone, 'selenium/webdriver/iphone'
20
20
  autoload :Opera, 'selenium/webdriver/opera'
21
21
  autoload :Remote, 'selenium/webdriver/remote'
22
+ autoload :Safari, 'selenium/webdriver/safari'
22
23
  autoload :Support, 'selenium/webdriver/support'
23
24
 
24
25
  # @api private
@@ -41,6 +41,8 @@ module Selenium
41
41
  IPhone::Bridge.new(opts)
42
42
  when :opera
43
43
  Opera::Bridge.new(opts)
44
+ when :safari
45
+ Safari::Bridge.new(opts)
44
46
  else
45
47
  raise ArgumentError, "unknown driver: #{browser.inspect}"
46
48
  end
@@ -27,17 +27,41 @@ module Selenium
27
27
  end
28
28
 
29
29
  #
30
- # Click the element
30
+ # Click this element. If this causes a new page to load, this method will
31
+ # attempt to block until the page has loaded. At this point, you should
32
+ # discard all references to this element and any further operations
33
+ # performed on this element will raise a StaleElementReferenceError
34
+ # unless you know that the element and the page will still be present. If
35
+ # click() causes a new page to be loaded via an event or is done by
36
+ # sending a native event then the method will *not* wait for it to be
37
+ # loaded and the caller should verify that a new page has been loaded.
31
38
  #
32
-
39
+ # There are some preconditions for an element to be clicked. The element
40
+ # must be visible and it must have a height and width greater then 0.
41
+ #
42
+ # Equivalent to:
43
+ # driver.action.click(element)
44
+ #
45
+ # @example Click on a button
46
+ #
47
+ # driver.find_element(:tag_name, "button").click
48
+ #
49
+ # @raise [StaleElementReferenceError] if the element no longer exists as
50
+ # defined
51
+ #
52
+
33
53
  def click
34
54
  bridge.clickElement @id
35
55
  end
36
56
 
37
57
  #
38
- # Get the tag name of this element
58
+ # Get the tag name of the element.
39
59
  #
40
- # @return [String]
60
+ # @example Get the tagname of an INPUT element(returns "input")
61
+ #
62
+ # driver.find_element(:xpath, "//input").tag_name
63
+ #
64
+ # @return [String] The tag name of this element.
41
65
  #
42
66
 
43
67
  def tag_name
@@ -90,6 +90,14 @@ module Selenium
90
90
  @bridge.setWindowPosition Integer(x), Integer(y)
91
91
  end
92
92
 
93
+ #
94
+ # Maximize the current window
95
+ #
96
+
97
+ def maximize
98
+ @bridge.maximizeWindow
99
+ end
100
+
93
101
 
94
102
  end
95
103
  end
@@ -21,9 +21,7 @@
21
21
  "extensions.logging.enabled": true,
22
22
  "extensions.update.enabled": false,
23
23
  "extensions.update.notifyUser": false,
24
- "focusmanager.testmode": true,
25
24
  "network.manage-offline-status": false,
26
- "network.http.max-connections-per-server": 10,
27
25
  "network.http.phishy-userpass-length": 255,
28
26
  "offline-apps.allow_by_default": true,
29
27
  "prompts.tab_modal.enabled": false,
@@ -51,6 +49,7 @@
51
49
  "dom.max_script_run_time": 30,
52
50
  "dom.report_all_js_exceptions": true,
53
51
  "javascript.options.showInConsole": true,
52
+ "network.http.max-connections-per-server": 10,
54
53
  "webdriver_accept_untrusted_certs": true,
55
54
  "webdriver_assume_untrusted_issuer": true
56
55
  }
@@ -16,25 +16,26 @@ module Selenium
16
16
  timeout = opts.delete(:timeout) { DEFAULT_TIMEOUT }
17
17
  port = opts.delete(:port) { DEFAULT_PORT }
18
18
  http_client = opts.delete(:http_client)
19
+ ignore_mode = opts.delete(:introduce_flakiness_by_ignoring_security_domains)
19
20
 
20
21
  unless opts.empty?
21
22
  raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
22
23
  end
23
24
 
24
25
  @server = Server.new
25
- @port = @server.start Integer(port)
26
+ @port = @server.start Integer(port), timeout
26
27
 
27
- host = Platform.localhost
28
- unless SocketPoller.new(host, @port, timeout).connected?
29
- raise Error::WebDriverError, "unable to connect to IE server within #{timeout} seconds"
28
+ caps = Remote::Capabilities.internet_explorer
29
+ if ignore_mode
30
+ caps['ignoreProtectedModeSettings'] = true
30
31
  end
31
32
 
32
33
  remote_opts = {
33
- :url => "http://#{host}:#{@port}",
34
- :desired_capabilities => :internet_explorer
34
+ :url => @server.uri,
35
+ :desired_capabilities => caps
35
36
  }
36
37
 
37
- remote_opts.merge!(:http_client => http_client) if http_client
38
+ remote_opts[:http_client] = http_client if http_client
38
39
 
39
40
  super(remote_opts)
40
41
  end
@@ -31,10 +31,14 @@ module Selenium
31
31
  # Starts the server, communicating on the specified port, if it is not already running
32
32
  #
33
33
 
34
- def start(start_port)
34
+ def start(start_port, timeout)
35
35
  return port if running?
36
36
  @handle = self.class.start_server(start_port)
37
37
 
38
+ unless SocketPoller.new(Platform.localhost, start_port, timeout).connected?
39
+ raise Error::WebDriverError, "unable to connect to IE server within #{timeout} seconds"
40
+ end
41
+
38
42
  start_port
39
43
  end
40
44
 
@@ -52,6 +56,10 @@ module Selenium
52
56
  self.class.current_port
53
57
  end
54
58
 
59
+ def uri
60
+ "http://#{Platform.localhost}:#{port}"
61
+ end
62
+
55
63
  private
56
64
 
57
65
  def session_count
@@ -104,7 +104,7 @@ module Selenium
104
104
  end
105
105
 
106
106
  def setImplicitWaitTimeout(milliseconds)
107
- execute :setImplicitWaitTimeout, {}, :ms => milliseconds
107
+ execute :implicitlyWait, {}, :ms => milliseconds
108
108
  end
109
109
 
110
110
  def setScriptTimeout(milliseconds)
@@ -217,6 +217,10 @@ module Selenium
217
217
  :height => height
218
218
  end
219
219
 
220
+ def maximizeWindow(handle = :current)
221
+ execute :maximizeWindow, :window_handle => handle
222
+ end
223
+
220
224
  def getWindowSize(handle = :current)
221
225
  data = execute :getWindowSize, :window_handle => handle
222
226
 
@@ -335,11 +339,11 @@ module Selenium
335
339
  end
336
340
 
337
341
  def deleteCookie(name)
338
- execute :deleteCookieNamed, :name => name
342
+ execute :deleteCookie, :name => name
339
343
  end
340
344
 
341
345
  def getAllCookies
342
- execute :getAllCookies
346
+ execute :getCookies
343
347
  end
344
348
 
345
349
  def deleteAllCookies
@@ -562,7 +566,7 @@ module Selenium
562
566
  end
563
567
 
564
568
  #
565
- # executes a command on the remote server via the REST / JSON API.
569
+ # executes a command on the remote server.
566
570
  #
567
571
  #
568
572
  # Returns the 'value' of the returned payload
@@ -573,7 +577,7 @@ module Selenium
573
577
  end
574
578
 
575
579
  #
576
- # executes a command on the remote server via the REST / JSON API.
580
+ # executes a command on the remote server.
577
581
  #
578
582
  # @return [WebDriver::Remote::Response]
579
583
  #
@@ -108,6 +108,15 @@ module Selenium
108
108
  }.merge(opts))
109
109
  end
110
110
 
111
+ def safari(opts = {})
112
+ new({
113
+ :browser_name => "safari",
114
+ :javascript_enabled => true,
115
+ :takes_screenshot => true,
116
+ :css_selectors_enabled => true
117
+ }.merge(opts))
118
+ end
119
+
111
120
  #
112
121
  # @api private
113
122
  #
@@ -37,6 +37,7 @@ class Selenium::WebDriver::Remote::Bridge
37
37
  command :setWindowPosition, :post, "session/:session_id/window/:window_handle/position"
38
38
  command :getWindowSize, :get, "session/:session_id/window/:window_handle/size"
39
39
  command :getWindowPosition, :get, "session/:session_id/window/:window_handle/position"
40
+ command :maximizeWindow, :post, "session/:session_id/window/:window_handle/maximize"
40
41
 
41
42
  #
42
43
  # script execution
@@ -71,16 +72,16 @@ class Selenium::WebDriver::Remote::Bridge
71
72
  # options
72
73
  #
73
74
 
74
- command :getAllCookies, :get, "session/:session_id/cookie"
75
+ command :getCookies, :get, "session/:session_id/cookie"
75
76
  command :addCookie, :post, "session/:session_id/cookie"
76
77
  command :deleteAllCookies, :delete, "session/:session_id/cookie"
77
- command :deleteCookieNamed, :delete, "session/:session_id/cookie/:name"
78
+ command :deleteCookie, :delete, "session/:session_id/cookie/:name"
78
79
 
79
80
  #
80
81
  # timeouts
81
82
  #
82
83
 
83
- command :setImplicitWaitTimeout, :post, "session/:session_id/timeouts/implicit_wait"
84
+ command :implicitlyWait, :post, "session/:session_id/timeouts/implicit_wait"
84
85
  command :setScriptTimeout, :post, "session/:session_id/timeouts/async_script"
85
86
  command :setTimeout, :post, "session/:session_id/timeouts"
86
87
 
@@ -0,0 +1,41 @@
1
+ require 'libwebsocket'
2
+
3
+ module Selenium
4
+ module WebDriver
5
+ module Safari
6
+
7
+ class << self
8
+ def path=(path)
9
+ Platform.assert_executable(path)
10
+ @path = path
11
+ end
12
+
13
+ def path
14
+ @path ||= (
15
+ path = case Platform.os
16
+ when :windows
17
+ # TODO: improve this
18
+ File.join(ENV['ProgramFiles'], 'Safari', 'Safari.exe')
19
+ when :macosx
20
+ "/Applications/Safari.app/Contents/MacOS/Safari"
21
+ else
22
+ Platform.find_binary("Safari")
23
+ end
24
+
25
+ unless File.file?(path) && File.executable?(path)
26
+ raise Error::WebDriverError, "unable to find the Safari executable, please set Selenium::WebDriver::Safari.path= or add it to your PATH."
27
+ end
28
+
29
+ path
30
+ )
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+
38
+ require 'selenium/webdriver/safari/browser'
39
+ require 'selenium/webdriver/safari/server'
40
+ require 'selenium/webdriver/safari/bridge'
41
+
@@ -0,0 +1,96 @@
1
+ module Selenium
2
+ module WebDriver
3
+ module Safari
4
+
5
+ class Bridge < Remote::Bridge
6
+ COMMAND_TIMEOUT = 60
7
+
8
+ def initialize(opts = {})
9
+ port = Integer(opts[:port] || PortProber.random)
10
+ timeout = Integer(opts[:timeout] || COMMAND_TIMEOUT)
11
+
12
+ @command_id ||= 0
13
+
14
+ @server = Server.new(port, timeout)
15
+ @server.start
16
+
17
+ @browser = Browser.new
18
+ @browser.start(prepare_connect_file)
19
+
20
+ @server.wait_for_connection
21
+
22
+ super(:desired_capabilities => :safari)
23
+ end
24
+
25
+ def quit
26
+ super
27
+
28
+ @server.stop
29
+ @browser.stop
30
+ end
31
+
32
+ def driver_extensions
33
+ []
34
+ end
35
+
36
+ private
37
+
38
+ def create_session(desired_capabilities)
39
+ resp = raw_execute :newSession, {}, :desiredCapabilities => desired_capabilities
40
+ Remote::Capabilities.json_create resp.fetch('value')
41
+ end
42
+
43
+ def raw_execute(command, opts = {}, command_hash = nil)
44
+ @command_id += 1
45
+
46
+ params = {}
47
+ opts.each do |key, value|
48
+ params[camel_case(key.to_s)] = value
49
+ end
50
+
51
+ params.merge!(command_hash) if command_hash
52
+
53
+ @server.send :id => @command_id.to_s,
54
+ :name => command,
55
+ :parameters => params
56
+
57
+ response = @server.receive
58
+
59
+ status_code = response['status']
60
+ if status_code != 0
61
+ raise Error.for_code(status_code), response['value']['message']
62
+ end
63
+
64
+ if response['id'] != @command_id.to_s
65
+ raise Error::WebDriverError, "response id does not match command id"
66
+ end
67
+
68
+ response
69
+ end
70
+
71
+ def camel_case(str)
72
+ parts = str.split('_')
73
+ parts[1..-1].map { |e| e.capitalize! }
74
+
75
+ parts.join
76
+ end
77
+
78
+ def prepare_connect_file
79
+ # TODO: use tempfile?
80
+ path = File.join(Dir.tmpdir, "safaridriver-#{Time.now.to_i}.html")
81
+
82
+ File.open(path, 'w') do |io|
83
+ io << "<!DOCTYPE html><script>window.location = '#{@server.uri}';</script>"
84
+ end
85
+
86
+ FileReaper << path
87
+ path.gsub! "/", "\\" if Platform.windows?
88
+
89
+ path
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,20 @@
1
+ module Selenium
2
+ module WebDriver
3
+ module Safari
4
+
5
+ class Browser
6
+ def start(*args)
7
+ @process = ChildProcess.new(Safari.path, *args)
8
+ @process.io.inherit! if $DEBUG
9
+ @process.start
10
+ end
11
+
12
+ def stop
13
+ @process.stop if @process
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,138 @@
1
+ module Selenium
2
+ module WebDriver
3
+ module Safari
4
+
5
+ class Server
6
+ def initialize(port, command_timeout)
7
+ @port = port
8
+ @command_timeout = command_timeout
9
+ @frame = LibWebSocket::Frame.new
10
+ end
11
+
12
+ def start
13
+ @server = TCPServer.new(Platform.localhost, @port)
14
+ end
15
+
16
+ def stop
17
+ @server.close if @server && !@server.closed?
18
+ @ws.close if @ws && !@ws.closed?
19
+ end
20
+
21
+ def send(command)
22
+ json = MultiJson.encode(command)
23
+ puts ">>> #{json}" if $DEBUG
24
+
25
+ frame = LibWebSocket::Frame.new(json).to_s
26
+
27
+ @ws.write frame
28
+ @ws.flush
29
+ end
30
+
31
+ def receive
32
+ until msg = @frame.next
33
+ end_time = Time.now + @command_timeout
34
+
35
+ begin
36
+ data = @ws.read_nonblock(1)
37
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
38
+ now = Time.now
39
+ if now >= end_time
40
+ raise Error::TimeOutError, "timed out waiting for Safari to respond"
41
+ end
42
+
43
+ IO.select([@ws], nil, nil, end_time - now)
44
+ retry
45
+ end
46
+
47
+ @frame.append(data)
48
+ end
49
+
50
+ puts "<<< #{msg}" if $DEBUG
51
+
52
+ MultiJson.decode msg
53
+ end
54
+
55
+ def ws_uri
56
+ "ws://#{Platform.localhost}:#{@port}/wd"
57
+ end
58
+
59
+ def uri
60
+ "http://#{Platform.localhost}:#{@port}"
61
+ end
62
+
63
+ def wait_for_connection
64
+ # TODO: timeouts / non-blocking accept
65
+ process_initial_http_request
66
+ process_handshake
67
+ end
68
+
69
+ HEADERS = <<-HEADERS
70
+ HTTP/1.1 200 OK
71
+ Content-Type: text/html; charset=utf-8
72
+ Server: safaridriver-ruby
73
+ HEADERS
74
+
75
+ HEADERS.gsub!("\n", "\r\n")
76
+
77
+ HTML = <<-HTML
78
+ <!DOCTYPE html>
79
+ <h2>SafariDriver requesting connection at %s</h2>
80
+ <script>
81
+ // Must wait for onload so the injected script is loaded by the
82
+ // SafariDriver extension.
83
+ window.onload = function() {
84
+ window.postMessage({
85
+ 'message': 'connect',
86
+ 'source': 'webdriver',
87
+ 'url': '%s'
88
+ }, '*');
89
+ };
90
+ </script>
91
+ HTML
92
+
93
+ def process_initial_http_request
94
+ http = @server.accept
95
+
96
+ req = ''
97
+ until req.include?("\r\n\r\n")
98
+ req << http.read(1)
99
+ end
100
+
101
+ http << HEADERS
102
+ http << "\r\n\r\n"
103
+ http << HTML % [ws_uri, ws_uri]
104
+
105
+ http.close
106
+ end
107
+
108
+ def process_handshake
109
+ @ws = @server.accept
110
+ hs = LibWebSocket::OpeningHandshake::Server.new
111
+
112
+ req = ''
113
+ until hs.done?
114
+ data = @ws.getc || next
115
+ req << data.chr
116
+
117
+ unless hs.parse(data.chr)
118
+ if req.include? "favicon.ico"
119
+ @ws.close
120
+ process_handshake
121
+ return
122
+ else
123
+ raise Error::WebDriverError, "#{hs.error}: #{req}"
124
+ end
125
+ end
126
+ end
127
+
128
+ @ws.write(hs.to_s)
129
+ @ws.flush
130
+
131
+ puts "handshake complete" if $DEBUG
132
+ @server.close
133
+ end
134
+ end
135
+
136
+ end
137
+ end
138
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: selenium-webdriver
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 2.20.0
5
+ version: 2.21.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jari Bakken
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2012-02-28 00:00:00 -08:00
13
+ date: 2012-04-11 00:00:00 +02:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -58,49 +58,60 @@ dependencies:
58
58
  type: :runtime
59
59
  version_requirements: *id004
60
60
  - !ruby/object:Gem::Dependency
61
- name: rspec
61
+ name: libwebsocket
62
62
  prerelease: false
63
63
  requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.1.3
69
+ type: :runtime
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: rspec
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
64
75
  none: false
65
76
  requirements:
66
77
  - - ~>
67
78
  - !ruby/object:Gem::Version
68
79
  version: "2.0"
69
80
  type: :development
70
- version_requirements: *id005
81
+ version_requirements: *id006
71
82
  - !ruby/object:Gem::Dependency
72
83
  name: rack
73
84
  prerelease: false
74
- requirement: &id006 !ruby/object:Gem::Requirement
85
+ requirement: &id007 !ruby/object:Gem::Requirement
75
86
  none: false
76
87
  requirements:
77
88
  - - ~>
78
89
  - !ruby/object:Gem::Version
79
90
  version: "1.0"
80
91
  type: :development
81
- version_requirements: *id006
92
+ version_requirements: *id007
82
93
  - !ruby/object:Gem::Dependency
83
94
  name: ci_reporter
84
95
  prerelease: false
85
- requirement: &id007 !ruby/object:Gem::Requirement
96
+ requirement: &id008 !ruby/object:Gem::Requirement
86
97
  none: false
87
98
  requirements:
88
99
  - - ~>
89
100
  - !ruby/object:Gem::Version
90
101
  version: 1.6.2
91
102
  type: :development
92
- version_requirements: *id007
103
+ version_requirements: *id008
93
104
  - !ruby/object:Gem::Dependency
94
105
  name: webmock
95
106
  prerelease: false
96
- requirement: &id008 !ruby/object:Gem::Requirement
107
+ requirement: &id009 !ruby/object:Gem::Requirement
97
108
  none: false
98
109
  requirements:
99
110
  - - ~>
100
111
  - !ruby/object:Gem::Version
101
112
  version: 1.7.5
102
113
  type: :development
103
- version_requirements: *id008
114
+ version_requirements: *id009
104
115
  description: WebDriver is a tool for writing automated tests of websites. It aims to mimic the behaviour of a real user, and as such interacts with the HTML of the application.
105
116
  email: jari.bakken@gmail.com
106
117
  executables: []
@@ -135,6 +146,7 @@ files:
135
146
  - lib/selenium/webdriver/iphone.rb
136
147
  - lib/selenium/webdriver/opera.rb
137
148
  - lib/selenium/webdriver/remote.rb
149
+ - lib/selenium/webdriver/safari.rb
138
150
  - lib/selenium/webdriver/support.rb
139
151
  - lib/selenium/webdriver/android/bridge.rb
140
152
  - lib/selenium/webdriver/chrome/bridge.rb
@@ -208,6 +220,9 @@ files:
208
220
  - lib/selenium/webdriver/remote/http/curb.rb
209
221
  - lib/selenium/webdriver/remote/http/default.rb
210
222
  - lib/selenium/webdriver/remote/http/persistent.rb
223
+ - lib/selenium/webdriver/safari/bridge.rb
224
+ - lib/selenium/webdriver/safari/browser.rb
225
+ - lib/selenium/webdriver/safari/server.rb
211
226
  - lib/selenium/webdriver/support/abstract_event_listener.rb
212
227
  - lib/selenium/webdriver/support/block_event_listener.rb
213
228
  - lib/selenium/webdriver/support/event_firing_bridge.rb