selenium-webdriver 0.1.4 → 0.2.0.dev

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,5 +1,12 @@
1
+ 0.2.0 (???)
2
+ ==================
3
+
4
+ * Update Ruby bindings to use the new Chrome driver (http://code.google.com/p/selenium/wiki/ChromeDriver).
5
+ * Add deprecation warning for WebDriver::Element#value.
6
+ * Change the default timeout for Wait instances to 5 seconds.
7
+
1
8
  0.1.4 (2011-03-21)
2
- ===========
9
+ ==================
3
10
 
4
11
  * Support for Firefox 4.
5
12
  * Search PATH for Firefox / Chrome on OS X as well.
@@ -1,17 +1,15 @@
1
- require "selenium/webdriver/chrome/launcher"
2
- require "selenium/webdriver/chrome/command_executor"
1
+ require 'net/http'
2
+
3
+ require "selenium/webdriver/chrome/service"
3
4
  require "selenium/webdriver/chrome/bridge"
4
5
 
5
- require "fileutils"
6
- require "thread"
7
- require "socket"
8
6
 
9
7
  module Selenium
10
8
  module WebDriver
11
9
 
12
10
  module Chrome
13
11
  def self.path=(path)
14
- Launcher.binary_path = path
12
+ Service.executable_path = path
15
13
  end
16
14
  end
17
15
 
@@ -6,13 +6,24 @@ module Selenium
6
6
  class Bridge < Remote::Bridge
7
7
 
8
8
  def initialize(opts = {})
9
- @launcher = Launcher.new(
10
- :default_profile => opts[:default_profile],
11
- :secure_ssl => opts[:secure_ssl]
12
- )
9
+ # TODO: pass options to Chrome::Service
10
+ http_client = opts.delete(:http_client)
13
11
 
14
- @executor = CommandExecutor.new
15
- @launcher.launch(@executor.uri)
12
+ unless opts.empty?
13
+ raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
14
+ end
15
+
16
+ @service = Service.default_service
17
+ @service.start
18
+
19
+ remote_opts = {
20
+ :url => @service.uri,
21
+ :desired_capabilities => :chrome
22
+ }
23
+
24
+ remote_opts.merge!(:http_client => http_client) if http_client
25
+
26
+ super(remote_opts)
16
27
  end
17
28
 
18
29
  def browser
@@ -20,7 +31,7 @@ module Selenium
20
31
  end
21
32
 
22
33
  def driver_extensions
23
- [DriverExtensions::TakesScreenshot]
34
+ []
24
35
  end
25
36
 
26
37
  def capabilities
@@ -28,81 +39,9 @@ module Selenium
28
39
  end
29
40
 
30
41
  def quit
31
- begin
32
- super
33
- rescue IOError
34
- end
35
-
36
- @executor.close
37
- @launcher.quit
38
- end
39
-
40
- def getAllCookies
41
- execute :getCookies
42
- end
43
-
44
- def deleteCookie(name)
45
- execute :deleteCookie, :name => name
46
- end
47
-
48
- def setImplicitWaitTimeout(milliseconds)
49
- execute :implicitlyWait, :ms => milliseconds
50
- end
51
-
52
- def elementEquals(element, other)
53
- element.ref == other.ref
54
- end
55
-
56
- %w[acceptAlert dismissAlert setAlertValue getAlertText].each do |m|
57
- define_method(m) { |*args| raise NotImplementedError }
58
- end
59
-
60
- private
61
-
62
- def execute(command_name, opts = {}, args = nil)
63
- command = {:request => command_name}.merge(opts)
64
- command.merge!(args) if args
65
-
66
- command = camel_case_keys_in(command)
67
-
68
- puts "--> #{command.inspect}" if $DEBUG
69
- resp = raw_execute command
70
- puts "<-- #{resp.inspect}" if $DEBUG
71
-
72
- code = resp['status']
73
- if e = Error.for_code(code)
74
- msg = resp['value']['message'] if resp['value']
75
- msg ||= "unknown exception for #{command.inspect}"
76
- msg << " (#{code})"
77
-
78
- raise e, msg
79
- end
80
-
81
- resp['value']
82
- end
83
-
84
- def raw_execute(command)
85
- @executor.execute command
86
- end
87
-
88
- #
89
- # TODO(jari): fix this in the remote driver
90
- #
91
-
92
- def camel_case(string)
93
- parts = string.split('_')
94
- parts[1..-1].map { |e| e.capitalize! }
95
- parts.join
96
- end
97
-
98
- def camel_case_keys_in(hash)
99
- h = {}
100
-
101
- hash.each do |key, value|
102
- h[camel_case(key.to_s)] = value
103
- end
104
-
105
- h
42
+ super
43
+ ensure
44
+ @service.stop
106
45
  end
107
46
 
108
47
  end # Bridge
@@ -0,0 +1,62 @@
1
+ module Selenium
2
+ module WebDriver
3
+ module Chrome
4
+
5
+ #
6
+ # @api private
7
+ #
8
+
9
+ class Service
10
+ START_TIMEOUT = 20
11
+ STOP_TIMEOUT = 5
12
+ MISSING_TEXT = "Unable to find the chromedriver executable. Please download the server from http://code.google.com/p/selenium/downloads/list and place it somewhere on your PATH. More info at http://code.google.com/p/selenium/wiki/ChromeDriver."
13
+
14
+ attr_reader :uri
15
+
16
+ def self.executable_path
17
+ @executable_path ||= (
18
+ Platform.find_binary "chromedriver" or raise Error::WebDriverError, MISSING_TEXT
19
+ )
20
+ end
21
+
22
+ def self.executable_path=(path)
23
+ Platform.assert_executable path
24
+ @executable_path = path
25
+ end
26
+
27
+ def self.default_service
28
+ new executable_path, PortProber.random
29
+ end
30
+
31
+ def initialize(executable_path, port)
32
+ @uri = URI.parse "http://#{Platform.localhost}:#{port}"
33
+ server_command = [executable_path, "--port=#{port}"]
34
+
35
+ @process = ChildProcess.build(*server_command)
36
+ @socket_poller = SocketPoller.new Platform.localhost, port, START_TIMEOUT
37
+
38
+ @process.io.inherit! if $DEBUG == true
39
+ end
40
+
41
+ def start
42
+ @process.start
43
+
44
+ unless @socket_poller.connected?
45
+ raise Error::WebDriverError, "unable to connect to chromedriver #{@uri}"
46
+ end
47
+ end
48
+
49
+ def stop
50
+ return if @process.nil? || @process.exited?
51
+
52
+ Net::HTTP.get uri.host, '/shutdown', uri.port
53
+ @process.poll_for_exit STOP_TIMEOUT
54
+ rescue ChildProcess::TimeoutError
55
+ # ok, force quit
56
+ @process.stop STOP_TIMEOUT
57
+ end
58
+ end # Service
59
+
60
+ end # Chrome
61
+ end # WebDriver
62
+ end # Service
@@ -5,6 +5,7 @@ require "selenium/webdriver/common/platform"
5
5
  require "selenium/webdriver/common/proxy"
6
6
  require "selenium/webdriver/common/file_reaper"
7
7
  require "selenium/webdriver/common/socket_poller"
8
+ require "selenium/webdriver/common/port_prober"
8
9
  require "selenium/webdriver/common/zipper"
9
10
  require "selenium/webdriver/common/wait"
10
11
  require "selenium/webdriver/common/alert"
@@ -53,6 +53,7 @@ module Selenium
53
53
  #
54
54
 
55
55
  def value
56
+ warn "#{self.class}#value is deprecated, please use #{self.class}#attribute('value')"
56
57
  bridge.getElementValue @id
57
58
  end
58
59
 
@@ -0,0 +1,25 @@
1
+ module Selenium
2
+ module WebDriver
3
+ class PortProber
4
+ def self.above(port)
5
+ port += 1 until free? port
6
+ port
7
+ end
8
+
9
+ def self.random
10
+ server = TCPServer.new(Platform.localhost, 0)
11
+ port = server.addr[1]
12
+ server.close
13
+
14
+ port
15
+ end
16
+
17
+ def self.free?(port)
18
+ TCPServer.new(Platform.localhost, port).close
19
+ true
20
+ rescue SocketError, Errno::EADDRINUSE
21
+ false
22
+ end
23
+ end # PortProber
24
+ end # WebDriver
25
+ end # Selenium
@@ -2,14 +2,14 @@ module Selenium
2
2
  module WebDriver
3
3
  class Wait
4
4
 
5
- DEFAULT_TIMEOUT = 30
5
+ DEFAULT_TIMEOUT = 5
6
6
  DEFAULT_INTERVAL = 0.5
7
7
 
8
8
  #
9
9
  # Create a new Wait instance
10
10
  #
11
11
  # @param [Hash] opts Options for this instance
12
- # @option opts [Numeric] :timeout (30) Seconds to wait before timing out.
12
+ # @option opts [Numeric] :timeout (5) Seconds to wait before timing out.
13
13
  # @option opts [Numeric] :interval (0.5) Seconds to sleep between polls.
14
14
  # @option opts [String] :message Exception mesage if timed out.
15
15
 
@@ -11,7 +11,8 @@ module Selenium
11
11
  ["#{WebDriver.root}/selenium/webdriver/firefox/native/linux/x86/#{NO_FOCUS_LIBRARY_NAME}", "x86/#{NO_FOCUS_LIBRARY_NAME}"],
12
12
  ]
13
13
 
14
- WAIT_TIMEOUT = 90
14
+ WAIT_TIMEOUT = 90
15
+ QUIT_TIMEOUT = 5
15
16
 
16
17
  def start_with(profile, profile_path, *args)
17
18
  profile_path = profile_path.gsub("/", "\\") if Platform.win?
@@ -32,10 +33,10 @@ module Selenium
32
33
 
33
34
  def quit
34
35
  return unless @process
35
- @process.poll_for_exit 5
36
+ @process.poll_for_exit QUIT_TIMEOUT
36
37
  rescue ChildProcess::TimeoutError
37
38
  # ok, force quit
38
- @process.stop 5
39
+ @process.stop QUIT_TIMEOUT
39
40
  end
40
41
 
41
42
  def wait
@@ -46,13 +46,7 @@ module Selenium
46
46
  end
47
47
 
48
48
  def find_free_port
49
- port = @port
50
-
51
- until free_port?(port)
52
- port += 1
53
- end
54
-
55
- @port = port
49
+ @port = PortProber.above @port
56
50
  end
57
51
 
58
52
  def create_profile
@@ -82,14 +76,6 @@ module Selenium
82
76
  end
83
77
  end
84
78
 
85
- def free_port?(port)
86
- s = TCPServer.new(@host, port)
87
- s.close
88
- true
89
- rescue SocketError, Errno::EADDRINUSE
90
- false
91
- end
92
-
93
79
  def fetch_profile
94
80
  if @profile_name
95
81
  @profile = Profile.from_name @profile_name
@@ -59,7 +59,7 @@ module Selenium
59
59
  desired_capabilities = Capabilities.send(desired_capabilities)
60
60
  end
61
61
 
62
- uri = URI.parse(url)
62
+ uri = url.kind_of?(URI) ? url : URI.parse(url)
63
63
  uri.path += "/" unless uri.path =~ /\/$/
64
64
 
65
65
  http_client.server_url = uri
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: selenium-webdriver
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.1.4
4
+ prerelease: 6
5
+ version: 0.2.0.dev
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: 2011-03-21 00:00:00 +01:00
13
+ date: 2011-04-06 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -116,9 +116,7 @@ files:
116
116
  - lib/selenium/webdriver/android/bridge.rb
117
117
  - lib/selenium/webdriver/android.rb
118
118
  - lib/selenium/webdriver/chrome/bridge.rb
119
- - lib/selenium/webdriver/chrome/command_executor.rb
120
- - lib/selenium/webdriver/chrome/extension.zip
121
- - lib/selenium/webdriver/chrome/launcher.rb
119
+ - lib/selenium/webdriver/chrome/service.rb
122
120
  - lib/selenium/webdriver/chrome.rb
123
121
  - lib/selenium/webdriver/common/action_builder.rb
124
122
  - lib/selenium/webdriver/common/alert.rb
@@ -138,6 +136,7 @@ files:
138
136
  - lib/selenium/webdriver/common/navigation.rb
139
137
  - lib/selenium/webdriver/common/options.rb
140
138
  - lib/selenium/webdriver/common/platform.rb
139
+ - lib/selenium/webdriver/common/port_prober.rb
141
140
  - lib/selenium/webdriver/common/proxy.rb
142
141
  - lib/selenium/webdriver/common/search_context.rb
143
142
  - lib/selenium/webdriver/common/socket_poller.rb
@@ -198,9 +197,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
198
197
  required_rubygems_version: !ruby/object:Gem::Requirement
199
198
  none: false
200
199
  requirements:
201
- - - ">="
200
+ - - ">"
202
201
  - !ruby/object:Gem::Version
203
- version: "0"
202
+ version: 1.3.1
204
203
  requirements: []
205
204
 
206
205
  rubyforge_project:
@@ -1,126 +0,0 @@
1
- module Selenium
2
- module WebDriver
3
- module Chrome
4
-
5
- # @api private
6
- class CommandExecutor
7
- HTML_TEMPLATE = "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n%s"
8
- JSON_TEMPLATE = "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n%s"
9
-
10
- def initialize
11
- @server = TCPServer.new(localhost, 0)
12
- @queue = Queue.new
13
-
14
- @accepted_any = false
15
- @next_socket = nil
16
- @listening = true
17
-
18
- Thread.new { start_run_loop }
19
- end
20
-
21
- def execute(command)
22
- until accepted_any?
23
- Thread.pass
24
- sleep 0.01
25
- end
26
-
27
- json = command.to_json
28
- data = JSON_TEMPLATE % [json.length, json]
29
-
30
- @next_socket.write data
31
- @next_socket.close
32
-
33
- JSON.parse read_response(@queue.pop)
34
- end
35
-
36
- def close
37
- stop_listening
38
- close_sockets
39
- @server.close unless @server.closed?
40
- rescue IOError
41
- nil
42
- end
43
-
44
- def port
45
- @server.addr[1]
46
- end
47
-
48
- def uri
49
- "http://#{Platform.localhost}:#{port}/chromeCommandExecutor"
50
- end
51
-
52
- private
53
-
54
- def localhost
55
- Platform.ironruby? ? Platform.localhost : "0.0.0.0" # yeah, weird..
56
- end
57
-
58
- def start_run_loop
59
- while(@listening) do
60
- socket = @server.accept
61
-
62
- if socket.read(1) == "G" # initial GET(s)
63
- write_holding_page_to socket
64
- else
65
- if accepted_any?
66
- @queue << socket
67
- else
68
- read_response(socket)
69
- @accepted_any = true
70
- end
71
- end
72
- end
73
- rescue IOError, Errno::EBADF
74
- raise if @listening
75
- end
76
-
77
- def read_response(socket)
78
- result = ''
79
- seen_double_crlf = false
80
-
81
- while line = next_line(socket)
82
- seen_double_crlf = true if line.empty?
83
- result << "#{line}\n" if seen_double_crlf
84
- end
85
-
86
- @next_socket = socket
87
-
88
- result.strip!
89
- end
90
-
91
- def accepted_any?
92
- @accepted_any
93
- end
94
-
95
- def close_sockets
96
- @next_socket.close if @next_socket
97
- @queue.pop.close until @queue.empty?
98
- end
99
-
100
- def stop_listening
101
- @listening = false
102
- end
103
-
104
- def next_line(socket)
105
- return if socket.closed?
106
- input = socket.gets
107
-
108
- raise Error::WebDriverError, "unexpected EOF from Chrome" if input.nil?
109
-
110
- line = input.chomp
111
- return if line == "EOResponse"
112
-
113
- line
114
- end
115
-
116
- def write_holding_page_to(socket)
117
- msg = %[<html><head><script type='text/javascript'>if (window.location.search == '') { setTimeout("window.location = window.location.href + '?reloaded'", 5000); }</script></head><body><p>ChromeDriver server started and connected. Please leave this tab open.</p></body></html>]
118
-
119
- socket.write HTML_TEMPLATE % [msg.length, msg]
120
- socket.close
121
- end
122
-
123
- end # CommandExecutor
124
- end # Chrome
125
- end # WebDriver
126
- end # Selenium
@@ -1,175 +0,0 @@
1
- module Selenium
2
- module WebDriver
3
- module Chrome
4
-
5
- # @api private
6
- class Launcher
7
- include FileUtils
8
-
9
- attr_reader :pid
10
-
11
- def initialize(opts = {})
12
- @default_profile = opts[:default_profile]
13
- @secure_ssl = !!opts[:secure_ssl]
14
- end
15
-
16
- def self.binary_path
17
- @binary_path ||= (
18
- path = possible_paths.find { |f| File.exist?(f) }
19
- path || raise(Error::WebDriverError, "Could not find Chrome binary. Make sure Chrome is installed (OS: #{Platform.os})")
20
- )
21
- end
22
-
23
- #
24
- # @api private
25
- #
26
- # @see Chrome.path=
27
- #
28
-
29
- def self.binary_path=(path)
30
- Platform.assert_executable(path)
31
- @binary_path = path
32
- end
33
-
34
- def launch(server_url)
35
- create_extension
36
- create_profile
37
- launch_chrome server_url
38
-
39
- pid
40
- end
41
-
42
- def quit
43
- @process.stop
44
- end
45
-
46
- private
47
-
48
- def create_extension
49
- # TODO: find a better way to do this
50
- rm_rf tmp_extension_dir
51
- mkdir_p File.dirname(tmp_extension_dir), :mode => 0700
52
- cp_r ext_path, tmp_extension_dir
53
- end
54
-
55
- def create_profile
56
- touch "#{tmp_profile_dir}/First Run"
57
- touch "#{tmp_profile_dir}/First Run Dev"
58
- end
59
-
60
- def launch_chrome(server_url)
61
- path = self.class.binary_path
62
-
63
- args = [
64
- Platform.wrap_in_quotes_if_necessary(path),
65
- "--load-extension=#{Platform.wrap_in_quotes_if_necessary(tmp_extension_dir)}",
66
- "--activate-on-launch",
67
- "--disable-hang-monitor",
68
- "--disable-popup-blocking",
69
- "--disable-prompt-on-repost"
70
- ]
71
-
72
- unless @default_profile
73
- args << "--user-data-dir=#{Platform.wrap_in_quotes_if_necessary(tmp_profile_dir)}"
74
- end
75
-
76
- unless @secure_ssl
77
- args << "--ignore-certificate-errors"
78
- end
79
-
80
- args << server_url
81
-
82
- @process = ChildProcess.build(*args)
83
- @process.io.inherit! if $DEBUG
84
-
85
- @process.start
86
- end
87
-
88
- def ext_path
89
- @ext_path ||= Zipper.unzip("#{WebDriver.root}/selenium/webdriver/chrome/extension.zip")
90
- end
91
-
92
- def tmp_extension_dir
93
- @tmp_extension_dir ||= (
94
- dir = Dir.mktmpdir("webdriver-chrome-extension")
95
- Platform.make_writable(dir)
96
- FileReaper << dir
97
-
98
- dir
99
- )
100
- end
101
-
102
- def tmp_profile_dir
103
- @tmp_profile_dir ||= (
104
- dir = Dir.mktmpdir("webdriver-chrome-profile")
105
- Platform.make_writable(dir)
106
- FileReaper << dir
107
-
108
- dir
109
- )
110
- end
111
-
112
- class << self
113
- def possible_paths
114
- case Platform.os
115
- when :windows
116
- windows_paths
117
- when :macosx
118
- macosx_paths
119
- when :unix, :linux
120
- unix_paths
121
- else
122
- raise "unknown OS: #{Platform.os}"
123
- end
124
- end
125
-
126
- def unix_paths
127
- [
128
- Platform.find_binary("google-chrome"),
129
- Platform.find_binary("chromium"),
130
- Platform.find_binary("chromium-browser"),
131
- "/usr/bin/google-chrome"
132
- ].compact
133
- end
134
-
135
- def macosx_paths
136
- [
137
- "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
138
- "#{Platform.home}/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
139
- Platform.find_binary("Google Chrome")
140
- ]
141
- end
142
-
143
- def windows_paths
144
- paths = [
145
- windows_registry_path,
146
- "#{ENV['USERPROFILE']}\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe",
147
- "#{ENV['USERPROFILE']}\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe",
148
- "#{Platform.home}\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe",
149
- "#{Platform.home}\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe",
150
- ].compact
151
-
152
- paths.map! { |path| Platform.cygwin_path(path) } if Platform.cygwin?
153
-
154
- paths
155
- end
156
-
157
- def windows_registry_path
158
- require "win32/registry"
159
-
160
- reg = Win32::Registry::HKEY_LOCAL_MACHINE.open(
161
- "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe")
162
-
163
- reg[""]
164
- rescue LoadError
165
- # older JRuby and IronRuby does not have win32/registry
166
- nil
167
- rescue Win32::Registry::Error
168
- nil
169
- end
170
- end # class << self
171
-
172
- end # Launcher
173
- end # Chrome
174
- end # WebDriver
175
- end # Selenium