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.
- data/CHANGES +25 -0
- data/lib/selenium/webdriver.rb +6 -29
- data/lib/selenium/webdriver/chrome.rb +4 -2
- data/lib/selenium/webdriver/chrome/extension.zip +0 -0
- data/lib/selenium/webdriver/chrome/launcher.rb +15 -16
- data/lib/selenium/webdriver/common.rb +18 -0
- data/lib/selenium/webdriver/{bridge_helper.rb → common/bridge_helper.rb} +0 -0
- data/lib/selenium/webdriver/{core_ext → common/core_ext}/dir.rb +0 -0
- data/lib/selenium/webdriver/{core_ext → common/core_ext}/string.rb +0 -0
- data/lib/selenium/webdriver/{driver.rb → common/driver.rb} +19 -7
- data/lib/selenium/webdriver/{driver_extensions → common/driver_extensions}/takes_screenshot.rb +2 -2
- data/lib/selenium/webdriver/{element.rb → common/element.rb} +30 -3
- data/lib/selenium/webdriver/{error.rb → common/error.rb} +0 -0
- data/lib/selenium/webdriver/{file_reaper.rb → common/file_reaper.rb} +0 -0
- data/lib/selenium/webdriver/{find.rb → common/find.rb} +9 -1
- data/lib/selenium/webdriver/{keys.rb → common/keys.rb} +0 -0
- data/lib/selenium/webdriver/{navigation.rb → common/navigation.rb} +3 -3
- data/lib/selenium/webdriver/{options.rb → common/options.rb} +47 -5
- data/lib/selenium/webdriver/{platform.rb → common/platform.rb} +10 -0
- data/lib/selenium/webdriver/common/socket_poller.rb +47 -0
- data/lib/selenium/webdriver/{target_locator.rb → common/target_locator.rb} +11 -8
- data/lib/selenium/webdriver/{timeouts.rb → common/timeouts.rb} +0 -0
- data/lib/selenium/webdriver/common/wait.rb +60 -0
- data/lib/selenium/webdriver/common/zipper.rb +54 -0
- data/lib/selenium/webdriver/firefox.rb +6 -3
- data/lib/selenium/webdriver/firefox/binary.rb +46 -43
- data/lib/selenium/webdriver/firefox/bridge.rb +2 -10
- data/lib/selenium/webdriver/firefox/extension.rb +51 -0
- data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
- data/lib/selenium/webdriver/firefox/launcher.rb +25 -69
- data/lib/selenium/webdriver/firefox/profile.rb +123 -89
- data/lib/selenium/webdriver/firefox/profiles_ini.rb +2 -1
- data/lib/selenium/webdriver/firefox/socket_lock.rb +77 -0
- data/lib/selenium/webdriver/ie/bridge.rb +25 -38
- data/lib/selenium/webdriver/ie/lib.rb +11 -1
- data/lib/selenium/webdriver/ie/native/win32/InternetExplorerDriver.dll +0 -0
- data/lib/selenium/webdriver/ie/native/x64/InternetExplorerDriver.dll +0 -0
- data/lib/selenium/webdriver/ie/util.rb +3 -17
- data/lib/selenium/webdriver/remote/bridge.rb +9 -1
- data/lib/selenium/webdriver/remote/capabilities.rb +53 -20
- data/lib/selenium/webdriver/remote/http/default.rb +2 -2
- metadata +52 -31
- data/lib/selenium/webdriver/child_process.rb +0 -243
- data/lib/selenium/webdriver/zip_helper.rb +0 -27
@@ -91,6 +91,16 @@ module Selenium
|
|
91
91
|
File.chmod 0766, file
|
92
92
|
end
|
93
93
|
|
94
|
+
def assert_executable(path)
|
95
|
+
unless File.file? path
|
96
|
+
raise Error::WebDriverError, "not a file: #{path.inspect}"
|
97
|
+
end
|
98
|
+
|
99
|
+
unless File.executable? path
|
100
|
+
raise Error::WebDriverError, "not executable: #{path.inspect}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
94
104
|
def find_binary(*binary_names)
|
95
105
|
paths = ENV['PATH'].split(File::PATH_SEPARATOR)
|
96
106
|
binary_names.map! { |n| "#{n}.exe" } if win?
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "socket"
|
2
|
+
|
3
|
+
module Selenium
|
4
|
+
module WebDriver
|
5
|
+
class SocketPoller
|
6
|
+
|
7
|
+
def initialize(host, port, timeout = 0, interval = 0.25)
|
8
|
+
@host = host
|
9
|
+
@port = Integer(port)
|
10
|
+
@timeout = Integer(timeout)
|
11
|
+
@interval = interval
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# @return true if the socket can be connected to
|
16
|
+
#
|
17
|
+
|
18
|
+
def success?
|
19
|
+
max_time = Time.now + @timeout
|
20
|
+
|
21
|
+
(
|
22
|
+
return true if can_connect?
|
23
|
+
wait
|
24
|
+
) until Time.now >= max_time
|
25
|
+
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def can_connect?
|
32
|
+
# There's a bug in 1.9.1 on Windows where this will succeed even if no
|
33
|
+
# one is listening. Users who hit that should upgrade their Ruby.
|
34
|
+
TCPSocket.new(@host, @port).close
|
35
|
+
true
|
36
|
+
rescue Errno::ECONNREFUSED, Errno::ENOTCONN, SocketError => e
|
37
|
+
$stderr.puts [@host, @port].inspect if $DEBUG
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
def wait
|
42
|
+
sleep @interval
|
43
|
+
end
|
44
|
+
|
45
|
+
end # SocketPoller
|
46
|
+
end # WebDriver
|
47
|
+
end # Selenium
|
@@ -6,8 +6,8 @@ module Selenium
|
|
6
6
|
# @api private
|
7
7
|
#
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@bridge =
|
9
|
+
def initialize(bridge)
|
10
|
+
@bridge = bridge
|
11
11
|
end
|
12
12
|
|
13
13
|
#
|
@@ -33,15 +33,18 @@ module Selenium
|
|
33
33
|
original = @bridge.getCurrentWindowHandle
|
34
34
|
@bridge.switchToWindow id
|
35
35
|
|
36
|
-
|
36
|
+
begin
|
37
|
+
returned = yield
|
38
|
+
ensure
|
39
|
+
current_handles = @bridge.getWindowHandles
|
37
40
|
|
38
|
-
|
41
|
+
if current_handles.size == 1
|
42
|
+
original = current_handles.shift
|
43
|
+
end
|
39
44
|
|
40
|
-
|
41
|
-
|
45
|
+
@bridge.switchToWindow original
|
46
|
+
returned
|
42
47
|
end
|
43
|
-
|
44
|
-
@bridge.switchToWindow original
|
45
48
|
else
|
46
49
|
@bridge.switchToWindow id
|
47
50
|
end
|
File without changes
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Selenium
|
2
|
+
module WebDriver
|
3
|
+
class Wait
|
4
|
+
|
5
|
+
DEFAULT_TIMEOUT = 30
|
6
|
+
DEFAULT_INTERVAL = 0.5
|
7
|
+
|
8
|
+
#
|
9
|
+
# Create a new Wait instance
|
10
|
+
#
|
11
|
+
# @param [Hash] opts Options for this instance
|
12
|
+
# @option opts [Numeric] :timeout (30) Seconds to wait before timing out.
|
13
|
+
# @option opts [Numeric] :interval (0.5) Seconds to sleep between polls.
|
14
|
+
# @option opts [String] :message Exception mesage if timed out.
|
15
|
+
|
16
|
+
def initialize(opts = {})
|
17
|
+
@timeout = opts.fetch(:timeout, DEFAULT_TIMEOUT)
|
18
|
+
@interval = opts.fetch(:interval, DEFAULT_INTERVAL)
|
19
|
+
@message = opts[:message]
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
#
|
24
|
+
# Wait until the given block returns a true value.
|
25
|
+
#
|
26
|
+
# @raise [Error::TimeOutError]
|
27
|
+
# @return [Object] the result of the block
|
28
|
+
#
|
29
|
+
|
30
|
+
def until(&blk)
|
31
|
+
end_time = Time.now + @timeout
|
32
|
+
last_error = nil
|
33
|
+
|
34
|
+
until Time.now > end_time
|
35
|
+
begin
|
36
|
+
result = yield
|
37
|
+
return result if result
|
38
|
+
rescue Error::NoSuchElementError => last_error
|
39
|
+
# swallowed
|
40
|
+
end
|
41
|
+
|
42
|
+
sleep @interval
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
if @message
|
47
|
+
msg = @message.dup
|
48
|
+
else
|
49
|
+
msg = "timed out after #{@timeout} seconds"
|
50
|
+
end
|
51
|
+
|
52
|
+
msg << " (#{last_error.message})}" if last_error
|
53
|
+
|
54
|
+
raise Error::TimeOutError, msg
|
55
|
+
end
|
56
|
+
|
57
|
+
end # Wait
|
58
|
+
end # WebDriver
|
59
|
+
end # Selenium
|
60
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'zip/zip'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'find'
|
4
|
+
|
5
|
+
module Selenium
|
6
|
+
module WebDriver
|
7
|
+
module Zipper
|
8
|
+
|
9
|
+
EXTENSIONS = %w[.zip .xpi]
|
10
|
+
|
11
|
+
def self.unzip(path)
|
12
|
+
destination = Dir.mktmpdir("unzip")
|
13
|
+
FileReaper << destination
|
14
|
+
|
15
|
+
Zip::ZipFile.open(path) do |zip|
|
16
|
+
zip.each do |entry|
|
17
|
+
to = File.join(destination, entry.name)
|
18
|
+
dirname = File.dirname(to)
|
19
|
+
|
20
|
+
FileUtils.mkdir_p dirname unless File.exist? dirname
|
21
|
+
zip.extract(entry, to)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
destination
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.zip(path)
|
29
|
+
tmp_zip = Tempfile.new("webdriver-zip")
|
30
|
+
|
31
|
+
begin
|
32
|
+
zos = Zip::ZipOutputStream.new(tmp_zip.path)
|
33
|
+
|
34
|
+
::Find.find(path) do |file|
|
35
|
+
next if File.directory?(file)
|
36
|
+
entry = file.sub("#{path}/", '')
|
37
|
+
|
38
|
+
zos.put_next_entry(entry)
|
39
|
+
zos << File.read(file)
|
40
|
+
p :added => file, :as => entry
|
41
|
+
end
|
42
|
+
|
43
|
+
zos.close
|
44
|
+
tmp_zip.rewind
|
45
|
+
|
46
|
+
[tmp_zip.read].pack("m")
|
47
|
+
ensure
|
48
|
+
tmp_zip.close
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end # Zipper
|
53
|
+
end # WebDriver
|
54
|
+
end # Selenium
|
@@ -3,6 +3,8 @@ require "socket"
|
|
3
3
|
require "rexml/document"
|
4
4
|
|
5
5
|
require "selenium/webdriver/firefox/util"
|
6
|
+
require "selenium/webdriver/firefox/extension"
|
7
|
+
require "selenium/webdriver/firefox/socket_lock"
|
6
8
|
require "selenium/webdriver/firefox/binary"
|
7
9
|
require "selenium/webdriver/firefox/profiles_ini"
|
8
10
|
require "selenium/webdriver/firefox/profile"
|
@@ -11,17 +13,18 @@ require "selenium/webdriver/firefox/bridge"
|
|
11
13
|
|
12
14
|
module Selenium
|
13
15
|
module WebDriver
|
14
|
-
|
15
|
-
# @private
|
16
16
|
module Firefox
|
17
17
|
|
18
|
-
DEFAULT_PROFILE_NAME = "WebDriver".freeze
|
19
18
|
DEFAULT_PORT = 7055
|
20
19
|
DEFAULT_ENABLE_NATIVE_EVENTS = Platform.os == :windows
|
21
20
|
DEFAULT_SECURE_SSL = false
|
22
21
|
DEFAULT_ASSUME_UNTRUSTED_ISSUER = true
|
23
22
|
DEFAULT_LOAD_NO_FOCUS_LIB = false
|
24
23
|
|
24
|
+
def self.path=(path)
|
25
|
+
Binary.path = path
|
26
|
+
end
|
27
|
+
|
25
28
|
end
|
26
29
|
end
|
27
30
|
end
|
@@ -6,78 +6,66 @@ module Selenium
|
|
6
6
|
class Binary
|
7
7
|
|
8
8
|
NO_FOCUS_LIBRARY_NAME = "x_ignore_nofocus.so"
|
9
|
-
NO_FOCUS_LIBRARIES
|
9
|
+
NO_FOCUS_LIBRARIES = [
|
10
10
|
["#{WebDriver.root}/selenium/webdriver/firefox/native/linux/amd64/#{NO_FOCUS_LIBRARY_NAME}", "amd64/#{NO_FOCUS_LIBRARY_NAME}"],
|
11
11
|
["#{WebDriver.root}/selenium/webdriver/firefox/native/linux/x86/#{NO_FOCUS_LIBRARY_NAME}", "x86/#{NO_FOCUS_LIBRARY_NAME}"],
|
12
12
|
]
|
13
13
|
|
14
|
-
|
15
|
-
execute("-CreateProfile", name)
|
14
|
+
WAIT_TIMEOUT = 90
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
_, status = wait
|
20
|
-
end
|
21
|
-
|
22
|
-
if status && status.to_i != 0
|
23
|
-
raise Error::WebDriverError, "could not create base profile: (exit status: #{status})"
|
24
|
-
end
|
25
|
-
end
|
16
|
+
def start_with(profile, profile_path, *args)
|
17
|
+
profile_path = profile_path.gsub("/", "\\") if Platform.win?
|
26
18
|
|
27
|
-
|
28
|
-
ENV['XRE_PROFILE_PATH'] = profile.absolute_path
|
19
|
+
ENV['XRE_PROFILE_PATH'] = profile_path
|
29
20
|
ENV['MOZ_NO_REMOTE'] = '1' # able to launch multiple instances
|
30
21
|
|
31
22
|
if Platform.linux? && (profile.native_events? || profile.load_no_focus_lib?)
|
32
|
-
modify_link_library_path
|
23
|
+
modify_link_library_path profile_path
|
33
24
|
end
|
34
25
|
|
26
|
+
ENV['XRE_CONSOLE_LOG'] = profile.log_file if profile.log_file
|
27
|
+
|
35
28
|
execute(*args)
|
36
29
|
cope_with_mac_strangeness(args) if Platform.mac?
|
37
30
|
end
|
38
31
|
|
32
|
+
def quit
|
33
|
+
return unless @process
|
34
|
+
@process.poll_for_exit 5
|
35
|
+
rescue ChildProcess::TimeoutError
|
36
|
+
# ok, force quit
|
37
|
+
@process.stop 5
|
38
|
+
end
|
39
|
+
|
40
|
+
def wait
|
41
|
+
@process.poll_for_exit(WAIT_TIMEOUT) if @process
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
39
46
|
def execute(*extra_args)
|
40
47
|
args = [self.class.path, "-no-remote", "--verbose"] + extra_args
|
41
|
-
@process = ChildProcess.
|
48
|
+
@process = ChildProcess.build(*args).start
|
42
49
|
end
|
43
50
|
|
44
51
|
def cope_with_mac_strangeness(args)
|
45
52
|
sleep 0.3
|
46
53
|
|
47
|
-
if @process.
|
48
|
-
#
|
49
|
-
sleep
|
54
|
+
if @process.crashed?
|
55
|
+
# ok, trying a restart
|
56
|
+
sleep 7
|
50
57
|
execute(*args)
|
51
58
|
end
|
52
59
|
|
53
60
|
# ensure we're ok
|
54
61
|
sleep 0.3
|
55
|
-
if @process.
|
62
|
+
if @process.crashed?
|
56
63
|
raise Error::WebDriverError, "unable to start Firefox cleanly, args: #{args.inspect}"
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
60
|
-
def
|
61
|
-
@process.ensure_death if @process
|
62
|
-
end
|
63
|
-
|
64
|
-
def kill
|
65
|
-
@process.kill if @process
|
66
|
-
end
|
67
|
-
|
68
|
-
def wait
|
69
|
-
@process.wait if @process
|
70
|
-
end
|
71
|
-
|
72
|
-
def pid
|
73
|
-
@process.pid if @process
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
def modify_link_library_path(profile)
|
67
|
+
def modify_link_library_path(profile_path)
|
79
68
|
paths = []
|
80
|
-
profile_path = profile.absolute_path
|
81
69
|
|
82
70
|
NO_FOCUS_LIBRARIES.each do |from, to|
|
83
71
|
dest = File.join(profile_path, to)
|
@@ -94,7 +82,17 @@ module Selenium
|
|
94
82
|
end
|
95
83
|
|
96
84
|
class << self
|
97
|
-
|
85
|
+
|
86
|
+
#
|
87
|
+
# @private
|
88
|
+
#
|
89
|
+
# @see Firefox.path=
|
90
|
+
#
|
91
|
+
|
92
|
+
def path=(path)
|
93
|
+
Platform.assert_executable(path)
|
94
|
+
@path = path
|
95
|
+
end
|
98
96
|
|
99
97
|
def path
|
100
98
|
@path ||= case Platform.os
|
@@ -108,7 +106,7 @@ module Selenium
|
|
108
106
|
raise Error::WebDriverError, "unknown platform: #{Platform.os}"
|
109
107
|
end
|
110
108
|
|
111
|
-
unless File.file?(@path)
|
109
|
+
unless File.file?(@path.to_s)
|
112
110
|
raise Error::WebDriverError, "Could not find Firefox binary (os=#{Platform.os}). Make sure Firefox is installed or set the path manually with #{self}.path="
|
113
111
|
end
|
114
112
|
|
@@ -118,7 +116,7 @@ module Selenium
|
|
118
116
|
private
|
119
117
|
|
120
118
|
def windows_path
|
121
|
-
windows_registry_path ||
|
119
|
+
windows_registry_path || likely_windows_path || Platform.find_binary("firefox")
|
122
120
|
end
|
123
121
|
|
124
122
|
def windows_registry_path
|
@@ -135,6 +133,11 @@ module Selenium
|
|
135
133
|
# older JRuby or IronRuby does not have win32/registry
|
136
134
|
rescue Win32::Registry::Error
|
137
135
|
end
|
136
|
+
|
137
|
+
def likely_windows_path
|
138
|
+
path = "#{ ENV['PROGRAMFILES'] || "\\Program Files" }\\Mozilla Firefox\\firefox.exe"
|
139
|
+
path if File.executable?(path)
|
140
|
+
end
|
138
141
|
end # class << self
|
139
142
|
|
140
143
|
end # Binary
|
@@ -9,8 +9,8 @@ module Selenium
|
|
9
9
|
@binary = Binary.new
|
10
10
|
@launcher = Launcher.new(
|
11
11
|
@binary,
|
12
|
-
opts.delete(:port)
|
13
|
-
opts.delete(:profile)
|
12
|
+
opts.delete(:port) || DEFAULT_PORT,
|
13
|
+
opts.delete(:profile)
|
14
14
|
)
|
15
15
|
|
16
16
|
http_client = opts.delete(:http_client)
|
@@ -50,14 +50,6 @@ module Selenium
|
|
50
50
|
execute :screenshot
|
51
51
|
end
|
52
52
|
|
53
|
-
def findElementByCssSelector(parent, selector)
|
54
|
-
find_element_by 'css selector', selector, parent
|
55
|
-
end
|
56
|
-
|
57
|
-
def findElementsByCssSelector(parent, selector)
|
58
|
-
find_elements_by 'css selector', selector, parent
|
59
|
-
end
|
60
|
-
|
61
53
|
end # Bridge
|
62
54
|
end # Firefox
|
63
55
|
end # WebDriver
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Selenium
|
2
|
+
module WebDriver
|
3
|
+
module Firefox
|
4
|
+
|
5
|
+
# @private
|
6
|
+
class Extension
|
7
|
+
EM_NAMESPACE_URI = "http://www.mozilla.org/2004/em-rdf#" # not used?
|
8
|
+
|
9
|
+
def initialize(path)
|
10
|
+
unless File.exist?(path)
|
11
|
+
raise Error::WebDriverError, "could not find extension at #{path.inspect}"
|
12
|
+
end
|
13
|
+
|
14
|
+
@path = path
|
15
|
+
end
|
16
|
+
|
17
|
+
def write_to(extensions_dir)
|
18
|
+
ext_path = File.join extensions_dir, read_id_from_install_rdf(root)
|
19
|
+
|
20
|
+
FileUtils.rm_rf ext_path
|
21
|
+
FileUtils.mkdir_p File.dirname(ext_path), :mode => 0700
|
22
|
+
FileUtils.cp_r root, ext_path
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def root
|
28
|
+
@root ||= (
|
29
|
+
if File.directory? @path
|
30
|
+
@path
|
31
|
+
else
|
32
|
+
unless Zipper::EXTENSIONS.include? File.extname(@path)
|
33
|
+
raise Error::WebDriverError, "expected #{Zipper::EXTENSIONS.join(" or ")}, got #{@path.inspect}"
|
34
|
+
end
|
35
|
+
|
36
|
+
Zipper.unzip(@path)
|
37
|
+
end
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def read_id_from_install_rdf(directory)
|
42
|
+
rdf_path = File.join(directory, "install.rdf")
|
43
|
+
doc = REXML::Document.new(File.read(rdf_path))
|
44
|
+
|
45
|
+
REXML::XPath.first(doc, "//em:id").text
|
46
|
+
end
|
47
|
+
|
48
|
+
end # Extension
|
49
|
+
end # Firefox
|
50
|
+
end # WebDriver
|
51
|
+
end # Selenium
|