selenium-webdriver 0.0.28 → 0.0.29

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 +25 -0
  2. data/lib/selenium/webdriver.rb +6 -29
  3. data/lib/selenium/webdriver/chrome.rb +4 -2
  4. data/lib/selenium/webdriver/chrome/extension.zip +0 -0
  5. data/lib/selenium/webdriver/chrome/launcher.rb +15 -16
  6. data/lib/selenium/webdriver/common.rb +18 -0
  7. data/lib/selenium/webdriver/{bridge_helper.rb → common/bridge_helper.rb} +0 -0
  8. data/lib/selenium/webdriver/{core_ext → common/core_ext}/dir.rb +0 -0
  9. data/lib/selenium/webdriver/{core_ext → common/core_ext}/string.rb +0 -0
  10. data/lib/selenium/webdriver/{driver.rb → common/driver.rb} +19 -7
  11. data/lib/selenium/webdriver/{driver_extensions → common/driver_extensions}/takes_screenshot.rb +2 -2
  12. data/lib/selenium/webdriver/{element.rb → common/element.rb} +30 -3
  13. data/lib/selenium/webdriver/{error.rb → common/error.rb} +0 -0
  14. data/lib/selenium/webdriver/{file_reaper.rb → common/file_reaper.rb} +0 -0
  15. data/lib/selenium/webdriver/{find.rb → common/find.rb} +9 -1
  16. data/lib/selenium/webdriver/{keys.rb → common/keys.rb} +0 -0
  17. data/lib/selenium/webdriver/{navigation.rb → common/navigation.rb} +3 -3
  18. data/lib/selenium/webdriver/{options.rb → common/options.rb} +47 -5
  19. data/lib/selenium/webdriver/{platform.rb → common/platform.rb} +10 -0
  20. data/lib/selenium/webdriver/common/socket_poller.rb +47 -0
  21. data/lib/selenium/webdriver/{target_locator.rb → common/target_locator.rb} +11 -8
  22. data/lib/selenium/webdriver/{timeouts.rb → common/timeouts.rb} +0 -0
  23. data/lib/selenium/webdriver/common/wait.rb +60 -0
  24. data/lib/selenium/webdriver/common/zipper.rb +54 -0
  25. data/lib/selenium/webdriver/firefox.rb +6 -3
  26. data/lib/selenium/webdriver/firefox/binary.rb +46 -43
  27. data/lib/selenium/webdriver/firefox/bridge.rb +2 -10
  28. data/lib/selenium/webdriver/firefox/extension.rb +51 -0
  29. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  30. data/lib/selenium/webdriver/firefox/launcher.rb +25 -69
  31. data/lib/selenium/webdriver/firefox/profile.rb +123 -89
  32. data/lib/selenium/webdriver/firefox/profiles_ini.rb +2 -1
  33. data/lib/selenium/webdriver/firefox/socket_lock.rb +77 -0
  34. data/lib/selenium/webdriver/ie/bridge.rb +25 -38
  35. data/lib/selenium/webdriver/ie/lib.rb +11 -1
  36. data/lib/selenium/webdriver/ie/native/win32/InternetExplorerDriver.dll +0 -0
  37. data/lib/selenium/webdriver/ie/native/x64/InternetExplorerDriver.dll +0 -0
  38. data/lib/selenium/webdriver/ie/util.rb +3 -17
  39. data/lib/selenium/webdriver/remote/bridge.rb +9 -1
  40. data/lib/selenium/webdriver/remote/capabilities.rb +53 -20
  41. data/lib/selenium/webdriver/remote/http/default.rb +2 -2
  42. metadata +52 -31
  43. data/lib/selenium/webdriver/child_process.rb +0 -243
  44. data/lib/selenium/webdriver/zip_helper.rb +0 -27
@@ -1,5 +1,3 @@
1
- require "fcntl"
2
-
3
1
  module Selenium
4
2
  module WebDriver
5
3
  module Firefox
@@ -10,9 +8,11 @@ module Selenium
10
8
  SOCKET_LOCK_TIMEOUT = 45
11
9
  STABLE_CONNECTION_TIMEOUT = 60
12
10
 
13
- def initialize(binary, port = DEFAULT_PORT, profile = DEFAULT_PROFILE_NAME)
11
+ def initialize(binary, port, profile = nil)
14
12
  @binary = binary
15
- @port = port.to_i
13
+ @port = Integer(port)
14
+
15
+ raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
16
16
 
17
17
  if profile.kind_of? Profile
18
18
  @profile = profile
@@ -21,12 +21,6 @@ module Selenium
21
21
  @profile = nil
22
22
  end
23
23
 
24
- # need to be really specific about what host to use
25
- #
26
- # on os x, "localhost" will resolve to 3 different addresses (see /etc/hosts)
27
- # Ruby will loop over these and happily bind to the same port on each one,
28
- # making it completely unusable for our purposes.
29
- #
30
24
  @host = "127.0.0.1"
31
25
  end
32
26
 
@@ -35,7 +29,7 @@ module Selenium
35
29
  end
36
30
 
37
31
  def launch
38
- with_lock do
32
+ socket_lock.locked do
39
33
  find_free_port
40
34
  create_profile
41
35
  start_silent_and_wait
@@ -46,28 +40,6 @@ module Selenium
46
40
  self
47
41
  end
48
42
 
49
- def with_lock
50
- max_time = Time.now + SOCKET_LOCK_TIMEOUT
51
- locking_port = @port - 1
52
-
53
- until Time.now > max_time
54
- begin
55
- socket_lock = TCPServer.new(@host, locking_port)
56
- # make sure the fd is not inherited by firefox
57
- socket_lock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
58
-
59
- yield
60
- return
61
- rescue SocketError, Errno::EADDRINUSE
62
- sleep 0.1
63
- end
64
- end
65
-
66
- raise Error::WebDriverError, "unable to bind to locking port #{locking_port} within #{SOCKET_LOCK_TIMEOUT} seconds"
67
- ensure
68
- socket_lock.close if socket_lock
69
- end
70
-
71
43
  def find_free_port
72
44
  port = @port
73
45
 
@@ -79,47 +51,29 @@ module Selenium
79
51
  end
80
52
 
81
53
  def create_profile
82
- unless @profile
83
- fetch_profile
84
- if @profile.nil?
85
- raise Error, WebDriverError, "could not find or create profile: #{profile.inspect}"
86
- end
87
- end
54
+ fetch_profile if @profile.nil?
88
55
 
89
- @profile.delete_extensions_cache
56
+ @profile.add_webdriver_extension
90
57
  @profile.port = @port
91
- @profile.add_webdriver_extension(true)
92
- @profile.update_user_prefs
58
+ @profile_dir = @profile.layout_on_disk
93
59
  end
94
60
 
95
61
  def start
96
62
  assert_profile
97
- @binary.start_with @profile
63
+ @binary.start_with @profile, @profile_dir
98
64
  end
99
65
 
100
66
  def start_silent_and_wait
101
67
  assert_profile
102
- @binary.start_with @profile, "--silent"
68
+ @binary.start_with @profile, @profile_dir, "--silent"
103
69
  @binary.wait
104
70
  end
105
71
 
106
72
  def connect_until_stable
107
- max_time = Time.now + STABLE_CONNECTION_TIMEOUT
108
-
109
- until Time.now >= max_time
110
- return if can_connect?
111
- sleep 0.25
73
+ poller = SocketPoller.new(@host, @port, STABLE_CONNECTION_TIMEOUT)
74
+ unless poller.success?
75
+ raise Error::WebDriverError, "unable to obtain stable firefox connection in #{STABLE_CONNECTION_TIMEOUT} seconds"
112
76
  end
113
-
114
- raise Error::WebDriverError, "unable to obtain stable firefox connection in #{STABLE_CONNECTION_TIMEOUT} seconds"
115
- end
116
-
117
- def can_connect?
118
- TCPSocket.new(@host, @port).close
119
- true
120
- rescue Errno::ECONNREFUSED, Errno::ENOTCONN, SocketError => e
121
- $stderr.puts "#{e.message} for #{@host}:#{@port}" if $DEBUG
122
- false
123
77
  end
124
78
 
125
79
  def free_port?(port)
@@ -131,20 +85,22 @@ module Selenium
131
85
  end
132
86
 
133
87
  def fetch_profile
134
- existing = Profile.from_name @profile_name
135
-
136
- unless existing
137
- @binary.create_base_profile @profile_name
138
- Profile.ini.refresh
139
- existing = Profile.from_name @profile_name
140
- raise Error::WebDriverError, "unable to find or create new profile" unless existing
88
+ if @profile_name
89
+ @profile = Profile.from_name @profile_name
90
+ if @profile.nil?
91
+ raise Error::WebDriverError, "unable to find profile named: #{@profile_name.inspect}"
92
+ end
93
+ else
94
+ @profile = Profile.new
141
95
  end
142
-
143
- @profile = existing
144
96
  end
145
97
 
146
98
  def assert_profile
147
- raise Error::WebDriverError, "must create_profile first" unless @profile
99
+ raise Error::WebDriverError, "must create_profile first" unless @profile && @profile_dir
100
+ end
101
+
102
+ def socket_lock
103
+ @socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
148
104
  end
149
105
 
150
106
  end # Launcher
@@ -5,12 +5,17 @@ module Selenium
5
5
 
6
6
  ANONYMOUS_PROFILE_NAME = "WEBDRIVER_ANONYMOUS_PROFILE"
7
7
  EXTENSION_NAME = "fxdriver@googlecode.com"
8
- EM_NAMESPACE_URI = "http://www.mozilla.org/2004/em-rdf#"
9
8
  WEBDRIVER_EXTENSION_PATH = File.expand_path("#{WebDriver.root}/selenium/webdriver/firefox/extension/webdriver.xpi")
10
-
11
- attr_reader :name, :directory
9
+ WEBDRIVER_PREFS = {
10
+ :native_events => 'webdriver_enable_native_events',
11
+ :untrusted_certs => 'webdriver_accept_untrusted_certs',
12
+ :untrusted_issuer => 'webdriver_assume_untrusted_issuer',
13
+ :port => 'webdriver_firefox_port',
14
+ :log_file => 'webdriver.log.file'
15
+ }
16
+
17
+ attr_reader :name, :log_file
12
18
  attr_writer :secure_ssl, :native_events, :load_no_focus_lib
13
- attr_accessor :port
14
19
 
15
20
  class << self
16
21
  def ini
@@ -34,23 +39,56 @@ module Selenium
34
39
  # driver = Selenium::WebDriver.for :firefox, :profile => profile
35
40
  #
36
41
 
37
- def initialize(directory = nil)
38
- @directory = directory ? create_tmp_copy(directory) : Dir.mktmpdir("webdriver-profile")
42
+ def initialize(model = nil)
43
+ @model = verify_model(model)
44
+
45
+ model_prefs = read_model_prefs
39
46
 
40
- unless File.directory? @directory
41
- raise Error::WebDriverError, "Profile directory does not exist: #{@directory.inspect}"
47
+ if model_prefs.empty?
48
+ @native_events = DEFAULT_ENABLE_NATIVE_EVENTS
49
+ @secure_ssl = DEFAULT_SECURE_SSL
50
+ @untrusted_issuer = DEFAULT_ASSUME_UNTRUSTED_ISSUER
51
+ @load_no_focus_lib = DEFAULT_LOAD_NO_FOCUS_LIB
52
+ else
53
+ @native_events = model_prefs[WEBDRIVER_PREFS[:native_events]] == "true"
54
+ @secure_ssl = model_prefs[WEBDRIVER_PREFS[:untrusted_certs]] != "true" # FIXME: 'untrusted_certs' vs 'secure_ssl'
55
+ @untrusted_issuer = model_prefs[WEBDRIVER_PREFS[:untrusted_issuer]] == "true"
56
+ @load_no_focus_lib = model_prefs[WEBDRIVER_PREFS[:load_no_focus_lib]] == "true" # not stored in profile atm, so will always be false.
42
57
  end
43
58
 
44
- FileReaper << @directory
59
+ @additional_prefs = {}
60
+ @extensions = {}
61
+ end
45
62
 
46
- # TODO: replace constants with options hash
47
- @port = DEFAULT_PORT
48
- @native_events = DEFAULT_ENABLE_NATIVE_EVENTS
49
- @secure_ssl = DEFAULT_SECURE_SSL
50
- @untrusted_issuer = DEFAULT_ASSUME_UNTRUSTED_ISSUER
51
- @load_no_focus_lib = DEFAULT_LOAD_NO_FOCUS_LIB
63
+ def layout_on_disk
64
+ profile_dir = @model ? create_tmp_copy(@model) : Dir.mktmpdir("webdriver-profile")
65
+ FileReaper << profile_dir
52
66
 
53
- @additional_prefs = {}
67
+ install_extensions(profile_dir)
68
+ delete_lock_files(profile_dir)
69
+ delete_extensions_cache(profile_dir)
70
+ update_user_prefs_in(profile_dir)
71
+
72
+ profile_dir
73
+ end
74
+
75
+ def as_json(opts = nil)
76
+ {'zip' => Zipper.zip(layout_on_disk)}
77
+ end
78
+
79
+ def to_json(*args)
80
+ as_json.to_json(*args)
81
+ end
82
+
83
+ def self.from_json(json)
84
+ zip_file = Tempfile.new("webdriver-profile-duplicate-#{json.hash}")
85
+
86
+ zip_file << JSON.parse(json)['zip'].unpack("m")[0]
87
+ zip_file.close
88
+
89
+ new(Zipper.unzip(zip_file.path))
90
+ ensure
91
+ zip_file.delete if zip_file
54
92
  end
55
93
 
56
94
  #
@@ -75,80 +113,27 @@ module Selenium
75
113
  @additional_prefs[key.to_s] = value
76
114
  end
77
115
 
78
- def absolute_path
79
- if Platform.win?
80
- directory.gsub("/", "\\")
81
- else
82
- directory
83
- end
116
+ def port=(port)
117
+ self[WEBDRIVER_PREFS[:port]] = port
84
118
  end
85
119
 
86
- def update_user_prefs
87
- prefs = current_user_prefs
88
-
89
- prefs.merge! OVERRIDABLE_PREFERENCES
90
- prefs.merge! @additional_prefs
91
- prefs.merge! DEFAULT_PREFERENCES
92
-
93
- prefs['webdriver_firefox_port'] = @port
94
- prefs['webdriver_accept_untrusted_certs'] = !secure_ssl?
95
- prefs['webdriver_enable_native_events'] = native_events?
96
- prefs['webdriver_assume_untrusted_issuer'] = assume_untrusted_certificate_issuer?
97
-
98
- # If the user sets the home page, we should also start up there
99
- prefs["startup.homepage_welcome_url"] = prefs["browser.startup.homepage"]
100
-
101
- write_prefs prefs
120
+ def log_file=(file)
121
+ @log_file = file
122
+ self[WEBDRIVER_PREFS[:log_file]] = file
102
123
  end
103
124
 
104
- def add_webdriver_extension(force_creation = false)
105
- ext_path = File.join(extensions_dir, EXTENSION_NAME)
106
-
107
- if File.exists? ext_path
108
- return unless force_creation
125
+ def add_webdriver_extension
126
+ unless @extensions.has_key?(:webdriver)
127
+ add_extension(WEBDRIVER_EXTENSION_PATH, :webdriver)
109
128
  end
110
-
111
- add_extension WEBDRIVER_EXTENSION_PATH
112
- delete_extensions_cache
113
129
  end
114
130
 
115
131
  #
116
- # Aadd the extension (directory, .zip or .xpi) at the given path to the profile.
132
+ # Add the extension (directory, .zip or .xpi) at the given path to the profile.
117
133
  #
118
134
 
119
- def add_extension(path)
120
- unless File.exist?(path)
121
- raise Error::WebDriverError, "could not find extension at #{path.inspect}"
122
- end
123
-
124
- if File.directory? path
125
- root = path
126
- else
127
- unless %w[.zip .xpi].include? File.extname(path)
128
- raise Error::WebDriverError, "expected .zip or .xpi extension, got #{path.inspect}"
129
- end
130
-
131
- root = ZipHelper.unzip(path)
132
- end
133
-
134
- ext_path = File.join extensions_dir, read_id_from_install_rdf(root)
135
-
136
- FileUtils.rm_rf ext_path
137
- FileUtils.mkdir_p File.dirname(ext_path), :mode => 0700
138
- FileUtils.cp_r root, ext_path
139
- end
140
-
141
- def extensions_dir
142
- @extensions_dir ||= File.join(directory, "extensions")
143
- end
144
-
145
- def user_prefs_path
146
- @user_prefs_path ||= File.join(directory, "user.js")
147
- end
148
-
149
- def delete_extensions_cache
150
- cache = File.join(directory, "extensions.cache")
151
- FileUtils.rm_f cache if File.exist?(cache)
135
+ def add_extension(path, name = extension_name_for(path))
136
+ @extensions[name] = Extension.new(path)
152
137
  end
153
138
 
154
139
  def native_events?
@@ -173,11 +158,42 @@ module Selenium
173
158
 
174
159
  private
175
160
 
176
- def read_id_from_install_rdf(directory)
177
- rdf_path = File.join(directory, "install.rdf")
178
- doc = REXML::Document.new(File.read(rdf_path))
161
+ def install_extensions(directory)
162
+ destination = File.join(directory, "extensions")
179
163
 
180
- REXML::XPath.first(doc, "//em:id").text
164
+ @extensions.each do |name, extension|
165
+ p :extension => name if $DEBUG
166
+ extension.write_to(destination)
167
+ end
168
+ end
169
+
170
+ def verify_model(model)
171
+ return unless model
172
+
173
+ raise Errno::ENOENT, model unless File.exist?(model)
174
+ raise Errno::ENOTDIR, model unless File.directory?(model)
175
+
176
+ model
177
+ end
178
+
179
+ def read_model_prefs
180
+ return {} unless @model
181
+
182
+ read_user_prefs(File.join(@model, 'user.js'))
183
+ end
184
+
185
+ def delete_extensions_cache(directory)
186
+ FileUtils.rm_f File.join(directory, "extensions.cache")
187
+ end
188
+
189
+ def delete_lock_files(directory)
190
+ %w[.parentlock parent.lock].each do |name|
191
+ FileUtils.rm_f File.join(directory, name)
192
+ end
193
+ end
194
+
195
+ def extension_name_for(path)
196
+ File.basename(path, File.extname(path))
181
197
  end
182
198
 
183
199
  def create_tmp_copy(directory)
@@ -191,13 +207,30 @@ module Selenium
191
207
  tmp_directory
192
208
  end
193
209
 
210
+ def update_user_prefs_in(directory)
211
+ path = File.join(directory, 'user.js')
212
+ prefs = read_user_prefs(path)
213
+
214
+ prefs.merge! OVERRIDABLE_PREFERENCES
215
+ prefs.merge! @additional_prefs
216
+ prefs.merge! DEFAULT_PREFERENCES
217
+
218
+ prefs[WEBDRIVER_PREFS[:untrusted_certs]] = !secure_ssl?
219
+ prefs[WEBDRIVER_PREFS[:native_events]] = native_events?
220
+ prefs[WEBDRIVER_PREFS[:untrusted_issuer]] = assume_untrusted_certificate_issuer?
221
+
222
+ # If the user sets the home page, we should also start up there
223
+ prefs["startup.homepage_welcome_url"] = prefs["browser.startup.homepage"]
224
+
225
+ write_prefs prefs, path
226
+ end
194
227
 
195
- def current_user_prefs
196
- return {} unless File.exist?(user_prefs_path)
228
+ def read_user_prefs(path)
229
+ return {} unless File.exist?(path)
197
230
 
198
231
  prefs = {}
199
232
 
200
- File.read(user_prefs_path).split("\n").each do |line|
233
+ File.read(path).split("\n").each do |line|
201
234
  if line =~ /user_pref\("([^"]+)"\s*,\s*(.+?)\);/
202
235
  prefs[$1.strip] = $2.strip
203
236
  end
@@ -206,8 +239,8 @@ module Selenium
206
239
  prefs
207
240
  end
208
241
 
209
- def write_prefs(prefs)
210
- File.open(user_prefs_path, "w") { |file|
242
+ def write_prefs(prefs, path)
243
+ File.open(path, "w") { |file|
211
244
  prefs.each do |key, value|
212
245
  p key => value if $DEBUG
213
246
  file.puts %{user_pref("#{key}", #{value});}
@@ -238,6 +271,7 @@ module Selenium
238
271
  "extensions.update.enabled" => 'false',
239
272
  "extensions.update.notifyUser" => 'false',
240
273
  "network.manage-offline-status" => 'false',
274
+ "network.http.max-connections-per-server" => '10',
241
275
  "security.warn_entering_secure" => 'false',
242
276
  "security.warn_submit_insecure" => 'false',
243
277
  "security.warn_entering_secure.show_once" => 'false',
@@ -13,7 +13,8 @@ module Selenium
13
13
  end
14
14
 
15
15
  def [](name)
16
- Profile.new @profile_paths[name]
16
+ path = @profile_paths[name]
17
+ path && Profile.new(path)
17
18
  end
18
19
 
19
20
  def refresh
@@ -0,0 +1,77 @@
1
+ require "fcntl"
2
+
3
+ module Selenium
4
+ module WebDriver
5
+ module Firefox
6
+
7
+ #
8
+ # @private
9
+ #
10
+
11
+ class SocketLock
12
+
13
+ #
14
+ # Need to be really specific about what host to use
15
+ #
16
+ # On os x, "localhost" will resolve to 3 different addresses (see /etc/hosts).
17
+ # Ruby will loop over these and happily bind to the same port on each one,
18
+ # making it completely unusable for our purposes.
19
+ #
20
+
21
+ HOST = "127.0.0.1"
22
+
23
+ def initialize(port, timeout)
24
+ @port = port
25
+ @timeout = timeout
26
+ end
27
+
28
+ def locked(&blk)
29
+ lock
30
+
31
+ begin
32
+ yield
33
+ ensure
34
+ release
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def lock
41
+ max_time = Time.now + @timeout
42
+
43
+ until can_lock? || Time.now >= max_time
44
+ sleep 0.1
45
+ end
46
+
47
+ unless did_lock?
48
+ raise Error::WebDriverError, "unable to bind to locking port #{@port} within #{@timeout} seconds"
49
+ end
50
+ end
51
+
52
+ def release
53
+ @server && @server.close
54
+ end
55
+
56
+ def can_lock?
57
+ @server = TCPServer.new(HOST, @port)
58
+
59
+ # make sure the fd is not inherited by exec()'d processes
60
+ if defined? Fcntl::FD_CLOEXEC
61
+ @server.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
62
+ end
63
+
64
+ true
65
+ rescue SocketError, Errno::EADDRINUSE => ex
66
+ $stderr.puts "#{self}: #{ex.message}" if $DEBUG
67
+ false
68
+ end
69
+
70
+ def did_lock?
71
+ !!@server
72
+ end
73
+
74
+ end # SocketLock
75
+ end # Firefox
76
+ end # WebDriver
77
+ end # Selenium