selenium-webdriver 0.0.29 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +15 -1
- data/lib/selenium-client.rb +2 -0
- data/lib/selenium/client.rb +30 -0
- data/lib/selenium/client/base.rb +118 -0
- data/lib/selenium/client/driver.rb +10 -0
- data/lib/selenium/client/errors.rb +9 -0
- data/lib/selenium/client/extensions.rb +118 -0
- data/lib/selenium/client/idiomatic.rb +488 -0
- data/lib/selenium/client/javascript_expression_builder.rb +116 -0
- data/lib/selenium/client/javascript_frameworks/jquery.rb +13 -0
- data/lib/selenium/client/javascript_frameworks/prototype.rb +13 -0
- data/lib/selenium/client/legacy_driver.rb +1711 -0
- data/lib/selenium/client/protocol.rb +104 -0
- data/lib/selenium/client/selenium_helper.rb +34 -0
- data/lib/selenium/rake/server_task.rb +131 -0
- data/lib/selenium/server.rb +114 -0
- data/lib/selenium/webdriver.rb +3 -2
- data/lib/selenium/webdriver/android.rb +9 -0
- data/lib/selenium/webdriver/android/bridge.rb +45 -0
- data/lib/selenium/webdriver/chrome/extension.zip +0 -0
- data/lib/selenium/webdriver/chrome/launcher.rb +6 -3
- data/lib/selenium/webdriver/common.rb +2 -0
- data/lib/selenium/webdriver/common/driver.rb +8 -4
- data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +28 -0
- data/lib/selenium/webdriver/common/file_reaper.rb +10 -0
- data/lib/selenium/webdriver/common/proxy.rb +119 -0
- data/lib/selenium/webdriver/common/socket_poller.rb +27 -9
- data/lib/selenium/webdriver/firefox/binary.rb +8 -5
- data/lib/selenium/webdriver/firefox/bridge.rb +2 -3
- data/lib/selenium/webdriver/firefox/extension.rb +17 -14
- data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
- data/lib/selenium/webdriver/firefox/launcher.rb +8 -2
- data/lib/selenium/webdriver/firefox/profile.rb +45 -1
- data/lib/selenium/webdriver/firefox/socket_lock.rb +1 -5
- data/lib/selenium/webdriver/ie/bridge.rb +3 -3
- 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/iphone.rb +9 -0
- data/lib/selenium/webdriver/iphone/bridge.rb +34 -0
- data/lib/selenium/webdriver/remote/bridge.rb +0 -1
- data/lib/selenium/webdriver/remote/capabilities.rb +46 -13
- data/lib/selenium/webdriver/remote/commands.rb +3 -4
- data/lib/selenium/webdriver/remote/http/common.rb +1 -1
- data/lib/selenium/webdriver/remote/http/default.rb +7 -1
- metadata +42 -7
@@ -0,0 +1,104 @@
|
|
1
|
+
module Selenium
|
2
|
+
module Client
|
3
|
+
|
4
|
+
HTTP_HEADERS = { 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8' }
|
5
|
+
|
6
|
+
# Module in charge of handling Selenium over-the-wire HTTP protocol
|
7
|
+
module Protocol
|
8
|
+
attr_reader :session_id
|
9
|
+
|
10
|
+
def remote_control_command(verb, args=[])
|
11
|
+
timeout(@default_timeout_in_seconds) do
|
12
|
+
status, response = http_post(http_request_for(verb, args))
|
13
|
+
raise CommandError, response unless status == "OK"
|
14
|
+
response[3..-1] # strip "OK," from response
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def string_command(verb, args=[])
|
19
|
+
remote_control_command(verb, args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def string_array_command(verb, args=[])
|
23
|
+
csv = string_command(verb, args)
|
24
|
+
token = ""
|
25
|
+
tokens = []
|
26
|
+
escape = false
|
27
|
+
csv.split(//).each do |letter|
|
28
|
+
if escape
|
29
|
+
token += letter
|
30
|
+
escape = false
|
31
|
+
next
|
32
|
+
end
|
33
|
+
case letter
|
34
|
+
when '\\'
|
35
|
+
escape = true
|
36
|
+
when ','
|
37
|
+
tokens << token
|
38
|
+
token = ""
|
39
|
+
else
|
40
|
+
token += letter
|
41
|
+
end
|
42
|
+
end
|
43
|
+
tokens << token
|
44
|
+
return tokens
|
45
|
+
end
|
46
|
+
|
47
|
+
def number_command(verb, args)
|
48
|
+
string_command verb, args
|
49
|
+
end
|
50
|
+
|
51
|
+
def number_array_command(verb, args)
|
52
|
+
string_array_command verb, args
|
53
|
+
end
|
54
|
+
|
55
|
+
def boolean_command(verb, args=[])
|
56
|
+
parse_boolean_value string_command(verb, args)
|
57
|
+
end
|
58
|
+
|
59
|
+
def boolean_array_command(verb, args)
|
60
|
+
string_array_command(verb, args).collect {|value| parse_boolean_value(value)}
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def parse_boolean_value(value)
|
66
|
+
if ("true" == value)
|
67
|
+
return true
|
68
|
+
elsif ("false" == value)
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
raise ProtocolError, "Invalid Selenese boolean value that is neither 'true' nor 'false': got '#{value}'"
|
72
|
+
end
|
73
|
+
|
74
|
+
def http_request_for(verb, args)
|
75
|
+
data = "cmd=#{CGI::escape(verb)}"
|
76
|
+
args.each_with_index do |arg, index|
|
77
|
+
data << "&#{index.succ}=#{CGI::escape(arg.to_s)}"
|
78
|
+
end
|
79
|
+
data << "&sessionId=#{session_id}" unless session_id.nil?
|
80
|
+
data
|
81
|
+
end
|
82
|
+
|
83
|
+
def http_post(data)
|
84
|
+
start = Time.now
|
85
|
+
called_from = caller.detect{|line| line !~ /(selenium-client|vendor|usr\/lib\/ruby|\(eval\))/i}
|
86
|
+
http = Net::HTTP.new(@host, @port)
|
87
|
+
http.open_timeout = default_timeout_in_seconds
|
88
|
+
http.read_timeout = default_timeout_in_seconds
|
89
|
+
response = http.post('/selenium-server/driver/', data, HTTP_HEADERS)
|
90
|
+
if response.body !~ /^OK/
|
91
|
+
puts "#{start} selenium-client received failure from selenium server:"
|
92
|
+
puts "requested:"
|
93
|
+
puts "\t" + CGI::unescape(data.split('&').join("\n\t"))
|
94
|
+
puts "received:"
|
95
|
+
puts "\t#{response.body.inspect}"
|
96
|
+
puts "\tcalled from #{called_from}"
|
97
|
+
end
|
98
|
+
[ response.body[0..1], response.body ]
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Defines a mixin module that you can use to write Selenium tests
|
2
|
+
# without typing "@selenium." in front of every command. Every
|
3
|
+
# call to a missing method will be automatically sent to the @selenium
|
4
|
+
# object.
|
5
|
+
module Selenium
|
6
|
+
module Client
|
7
|
+
|
8
|
+
module SeleniumHelper
|
9
|
+
|
10
|
+
# Overrides default open method to actually delegates to @selenium
|
11
|
+
def open(url)
|
12
|
+
@selenium.open url
|
13
|
+
end
|
14
|
+
|
15
|
+
# Overrides default type method to actually delegates to @selenium
|
16
|
+
def type(locator, value)
|
17
|
+
@selenium.type locator, value
|
18
|
+
end
|
19
|
+
|
20
|
+
# Overrides default select method to actually delegates to @selenium
|
21
|
+
def select(input_locator, option_locator)
|
22
|
+
@selenium.select input_locator, option_locator
|
23
|
+
end
|
24
|
+
|
25
|
+
# Delegates to @selenium on method missing
|
26
|
+
def method_missing(method_name, *args)
|
27
|
+
return super unless @selenium.respond_to?(method_name)
|
28
|
+
|
29
|
+
@selenium.send(method_name, *args)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'selenium/server'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
module Selenium
|
5
|
+
module Rake
|
6
|
+
|
7
|
+
class MissingJarFileError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
#
|
12
|
+
# Defines rake tasks for starting, stopping and restarting the Selenium server.
|
13
|
+
#
|
14
|
+
# Usage:
|
15
|
+
#
|
16
|
+
# Selenium::Rake::ServerTask.new do |t|
|
17
|
+
# t.jar = "/path/to/selenium-server-standalone.jar"
|
18
|
+
# t.port = 4444
|
19
|
+
# t.opts = %w[-some options]
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# Tasks defined:
|
23
|
+
#
|
24
|
+
# rake selenium:server:start
|
25
|
+
# rake selenium:server:stop
|
26
|
+
# rake selenium:server:restart
|
27
|
+
#
|
28
|
+
|
29
|
+
class ServerTask
|
30
|
+
|
31
|
+
#
|
32
|
+
# Path to the selenium server jar
|
33
|
+
#
|
34
|
+
|
35
|
+
attr_accessor :jar
|
36
|
+
|
37
|
+
#
|
38
|
+
# Port to use for the server.
|
39
|
+
# Default: 4444
|
40
|
+
#
|
41
|
+
#
|
42
|
+
|
43
|
+
attr_accessor :port
|
44
|
+
|
45
|
+
#
|
46
|
+
# Timeout in seconds for the server to start/stop.
|
47
|
+
# Default: 30
|
48
|
+
#
|
49
|
+
|
50
|
+
attr_accessor :timeout
|
51
|
+
|
52
|
+
#
|
53
|
+
# Whether we should detach from the server process.
|
54
|
+
# Default: true
|
55
|
+
#
|
56
|
+
|
57
|
+
attr_accessor :background
|
58
|
+
alias_method :background?, :background
|
59
|
+
|
60
|
+
#
|
61
|
+
# Configure logging. Pass a log file path or a boolean.
|
62
|
+
# Default: true
|
63
|
+
#
|
64
|
+
# true - log to stdout/stderr
|
65
|
+
# false - no logging
|
66
|
+
# String - log to the specified file
|
67
|
+
#
|
68
|
+
|
69
|
+
attr_accessor :log
|
70
|
+
|
71
|
+
#
|
72
|
+
# Add additional options passed to the server jar.
|
73
|
+
#
|
74
|
+
|
75
|
+
attr_accessor :opts
|
76
|
+
|
77
|
+
|
78
|
+
def initialize(prefix = "selenium:server")
|
79
|
+
@jar = nil
|
80
|
+
@prefix = prefix
|
81
|
+
@port = 4444
|
82
|
+
@timeout = 30
|
83
|
+
@background = true
|
84
|
+
@log = true
|
85
|
+
@opts = []
|
86
|
+
|
87
|
+
yield self if block_given?
|
88
|
+
|
89
|
+
unless @jar
|
90
|
+
raise MissingJarFileError, "must provide path to the selenium server jar"
|
91
|
+
end
|
92
|
+
|
93
|
+
@server = Selenium::Server.new(@jar, :port => @port,
|
94
|
+
:timeout => @timeout,
|
95
|
+
:background => @background,
|
96
|
+
:log => @log )
|
97
|
+
|
98
|
+
@server << @opts
|
99
|
+
|
100
|
+
define_start_task
|
101
|
+
define_stop_task
|
102
|
+
define_restart_task
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def define_start_task
|
108
|
+
desc "Start the Selenium server"
|
109
|
+
task "#{@prefix}:start" do
|
110
|
+
@server.start
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def define_stop_task
|
115
|
+
desc 'Stop the Selenium server'
|
116
|
+
task "#{@prefix}:stop" do
|
117
|
+
@server.stop
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def define_restart_task
|
122
|
+
desc 'Restart the Selenium server'
|
123
|
+
task "#{@prefix}:restart" do
|
124
|
+
@server.stop
|
125
|
+
@server.start
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end # ServerTask
|
130
|
+
end # Rake
|
131
|
+
end # Selenium
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require "childprocess"
|
2
|
+
require "selenium/webdriver/common/socket_poller"
|
3
|
+
require "net/http"
|
4
|
+
|
5
|
+
module Selenium
|
6
|
+
|
7
|
+
#
|
8
|
+
# Wraps the remote server jar
|
9
|
+
#
|
10
|
+
|
11
|
+
class Server
|
12
|
+
|
13
|
+
def initialize(jar, opts = {})
|
14
|
+
raise Errno::ENOENT, jar unless File.exist?(jar)
|
15
|
+
|
16
|
+
@jar = jar
|
17
|
+
@host = "127.0.0.1"
|
18
|
+
@port = opts.fetch(:port, 4444)
|
19
|
+
@timeout = opts.fetch(:timeout, 30)
|
20
|
+
@background = opts.fetch(:background, false)
|
21
|
+
@log = opts[:log]
|
22
|
+
|
23
|
+
@additional_args = []
|
24
|
+
end
|
25
|
+
|
26
|
+
def start
|
27
|
+
process.start
|
28
|
+
poll_for_service
|
29
|
+
|
30
|
+
unless @background
|
31
|
+
begin
|
32
|
+
sleep 1 while process.alive?
|
33
|
+
rescue Errno::ECHILD
|
34
|
+
# no longer alive
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def stop
|
40
|
+
begin
|
41
|
+
Net::HTTP.get(@host, "/selenium-server/driver/?cmd=shutDownSeleniumServer", @port)
|
42
|
+
rescue Errno::ECONNREFUSED
|
43
|
+
end
|
44
|
+
|
45
|
+
stop_process if @process
|
46
|
+
poll_for_shutdown
|
47
|
+
|
48
|
+
@log_file.close if @log_file
|
49
|
+
end
|
50
|
+
|
51
|
+
def webdriver_url
|
52
|
+
"http://#{@host}:#{@port}/wd/hub"
|
53
|
+
end
|
54
|
+
|
55
|
+
def <<(arg)
|
56
|
+
if arg.kind_of?(Array)
|
57
|
+
@additional_args += arg
|
58
|
+
else
|
59
|
+
@additional_args << arg.to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def stop_process
|
66
|
+
return unless @process.alive?
|
67
|
+
|
68
|
+
begin
|
69
|
+
@process.poll_for_exit(5)
|
70
|
+
rescue ChildProcess::TimeoutError
|
71
|
+
@process.stop
|
72
|
+
end
|
73
|
+
rescue Errno::ECHILD
|
74
|
+
# already dead
|
75
|
+
ensure
|
76
|
+
@process = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def process
|
80
|
+
@process ||= (
|
81
|
+
cp = ChildProcess.build("java", "-jar", @jar, "-port", @port.to_s, *@additional_args)
|
82
|
+
io = cp.io
|
83
|
+
|
84
|
+
if @log.kind_of?(String) && !@background
|
85
|
+
@log_file = File.open(@log, "w")
|
86
|
+
io.stdout = io.stderr = @log_file
|
87
|
+
elsif @log
|
88
|
+
io.inherit!
|
89
|
+
end
|
90
|
+
|
91
|
+
cp.detach = @background
|
92
|
+
|
93
|
+
cp
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
def poll_for_service
|
98
|
+
unless socket.connected?
|
99
|
+
raise "remote server not launched in #{@timeout} seconds"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def poll_for_shutdown
|
104
|
+
unless socket.closed?
|
105
|
+
raise "remote server not stopped in #{@timeout} seconds"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def socket
|
110
|
+
@socket ||= WebDriver::SocketPoller.new(@host, @port, @timeout)
|
111
|
+
end
|
112
|
+
|
113
|
+
end # Server
|
114
|
+
end # Selenium
|
data/lib/selenium/webdriver.rb
CHANGED
@@ -27,15 +27,16 @@ end
|
|
27
27
|
|
28
28
|
require "selenium/webdriver/common"
|
29
29
|
|
30
|
-
|
31
30
|
module Selenium
|
32
31
|
module WebDriver
|
33
32
|
Point = Struct.new(:x, :y)
|
34
33
|
Dimension = Struct.new(:width, :height)
|
35
34
|
|
35
|
+
autoload :Android, 'selenium/webdriver/android'
|
36
|
+
autoload :Chrome, 'selenium/webdriver/chrome'
|
36
37
|
autoload :IE, 'selenium/webdriver/ie'
|
38
|
+
autoload :IPhone, 'selenium/webdriver/iphone'
|
37
39
|
autoload :Remote, 'selenium/webdriver/remote'
|
38
|
-
autoload :Chrome, 'selenium/webdriver/chrome'
|
39
40
|
autoload :Firefox, 'selenium/webdriver/firefox'
|
40
41
|
|
41
42
|
def self.root
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Selenium
|
2
|
+
module WebDriver
|
3
|
+
module Android
|
4
|
+
class Bridge < Remote::Bridge
|
5
|
+
|
6
|
+
DEFAULT_URL = "http://localhost:8080/hub"
|
7
|
+
|
8
|
+
def initialize(opts = nil)
|
9
|
+
if opts
|
10
|
+
super
|
11
|
+
else
|
12
|
+
super(
|
13
|
+
:url => DEFAULT_URL,
|
14
|
+
:desired_capabilities => capabilities
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def browser
|
20
|
+
:android
|
21
|
+
end
|
22
|
+
|
23
|
+
def driver_extensions
|
24
|
+
[
|
25
|
+
DriverExtensions::TakesScreenshot,
|
26
|
+
DriverExtensions::Rotatable
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
def setScreenOrientation(orientation)
|
31
|
+
execute :setScreenOrientation, {}, :orientation => orientation
|
32
|
+
end
|
33
|
+
|
34
|
+
def getScreenOrientation
|
35
|
+
execute :getScreenOrientation
|
36
|
+
end
|
37
|
+
|
38
|
+
def capabilities
|
39
|
+
@capabilities ||= Remote::Capabilities.android
|
40
|
+
end
|
41
|
+
|
42
|
+
end # Bridge
|
43
|
+
end # Android
|
44
|
+
end # WebDriver
|
45
|
+
end # Selenium
|