async-webdriver 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/async/webdriver/bridge/chrome.rb +81 -0
- data/lib/async/webdriver/bridge/firefox.rb +80 -0
- data/lib/async/webdriver/bridge/generic.rb +91 -0
- data/lib/async/webdriver/bridge/pool.rb +99 -0
- data/lib/async/webdriver/bridge/process_group.rb +77 -0
- data/lib/async/webdriver/bridge.rb +30 -0
- data/lib/async/webdriver/client.rb +71 -26
- data/lib/async/webdriver/element.rb +270 -17
- data/lib/async/webdriver/error.rb +214 -0
- data/lib/async/webdriver/locator.rb +127 -0
- data/lib/async/webdriver/request_helper.rb +120 -0
- data/lib/async/webdriver/scope/alerts.rb +40 -0
- data/lib/async/webdriver/scope/cookies.rb +43 -0
- data/lib/async/webdriver/scope/document.rb +41 -0
- data/lib/async/webdriver/scope/elements.rb +111 -0
- data/lib/async/webdriver/scope/fields.rb +66 -0
- data/lib/async/webdriver/scope/frames.rb +33 -0
- data/lib/async/webdriver/scope/navigation.rb +50 -0
- data/lib/async/webdriver/scope/printing.rb +22 -0
- data/lib/async/webdriver/scope/screen_capture.rb +23 -0
- data/lib/async/webdriver/scope/timeouts.rb +63 -0
- data/lib/async/webdriver/scope.rb +15 -0
- data/lib/async/webdriver/session.rb +107 -65
- data/lib/async/webdriver/version.rb +8 -3
- data/lib/async/webdriver/xpath.rb +29 -0
- data/lib/async/webdriver.rb +7 -12
- data/{LICENSE.txt → license.md} +6 -6
- data/readme.md +37 -0
- data.tar.gz.sig +0 -0
- metadata +71 -149
- metadata.gz.sig +0 -0
- data/.gitignore +0 -11
- data/.rspec +0 -3
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -7
- data/Gemfile +0 -6
- data/Gemfile.lock +0 -103
- data/Guardfile +0 -45
- data/README.md +0 -3
- data/Rakefile +0 -6
- data/async-webdriver.gemspec +0 -37
- data/bin/console +0 -12
- data/bin/setup +0 -8
- data/examples/multiple_sessions.rb +0 -29
- data/lib/async/webdriver/connection.rb +0 -69
- data/lib/async/webdriver/connection_path.rb +0 -25
- data/lib/async/webdriver/execute.rb +0 -29
- data/lib/async/webdriver/session_creator.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee23c04114676fa39b6838b560ad843f0d011e8c89ac1de1038c66c2f7bd5e26
|
4
|
+
data.tar.gz: aa95efc428c3cdfcb362c8a9f6fbc80aaa4cec6258630adb2f057479ba051537
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf32864d5c72d2529151f4de5f1f72d6e4a5a3731d0598e49df758d97371f176ec34504d9922d6f56499855c9d21726fb7fba641b08c19f2ae084960a35ec90f
|
7
|
+
data.tar.gz: 67f0fd5bdba22e79cba73c2db37e69da65ddc3dc22a8603584a59d1cae158902ac1ca501a7d80304683e56f831aa621ca852acd3b31079c4c7a2b19d2a36711c
|
checksums.yaml.gz.sig
ADDED
Binary file
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2023, by Samuel Williams.
|
5
|
+
|
6
|
+
require_relative 'generic'
|
7
|
+
require_relative 'process_group'
|
8
|
+
|
9
|
+
module Async
|
10
|
+
module WebDriver
|
11
|
+
module Bridge
|
12
|
+
# A bridge to the Chrome browser using `chromedriver`.
|
13
|
+
#
|
14
|
+
# ``` ruby
|
15
|
+
# begin
|
16
|
+
# bridge = Async::WebDriver::Bridge::Chrome.start
|
17
|
+
# client = Async::WebDriver::Client.open(bridge.endpoint)
|
18
|
+
# ensure
|
19
|
+
# bridge&.close
|
20
|
+
# end
|
21
|
+
# ```
|
22
|
+
class Chrome < Generic
|
23
|
+
# Create a new bridge to Chrome.
|
24
|
+
# @parameter path [String] The path to the `chromedriver` executable.
|
25
|
+
def initialize(path: "chromedriver")
|
26
|
+
super()
|
27
|
+
|
28
|
+
@path = path
|
29
|
+
@process = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# @returns [String] The version of the `chromedriver` executable.
|
33
|
+
def version
|
34
|
+
::IO.popen([@path, "--version"]) do |io|
|
35
|
+
return io.read
|
36
|
+
end
|
37
|
+
rescue Errno::ENOENT
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# @returns [Array(String)] The arguments to pass to the `chromedriver` executable.
|
42
|
+
def arguments
|
43
|
+
[
|
44
|
+
"--port=#{self.port}",
|
45
|
+
].compact
|
46
|
+
end
|
47
|
+
|
48
|
+
# Start the driver.
|
49
|
+
def start
|
50
|
+
@process ||= ProcessGroup.spawn(@path, *arguments)
|
51
|
+
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
# Close the driver.
|
56
|
+
def close
|
57
|
+
super
|
58
|
+
|
59
|
+
if @process
|
60
|
+
@process.close
|
61
|
+
@process = nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# The default capabilities for the Chrome browser which need to be provided when requesting a new session.
|
66
|
+
# @parameter headless [Boolean] Whether to run the browser in headless mode.
|
67
|
+
# @returns [Hash] The default capabilities for the Chrome browser.
|
68
|
+
def default_capabilities(headless: true)
|
69
|
+
{
|
70
|
+
alwaysMatch: {
|
71
|
+
browserName: "chrome",
|
72
|
+
"goog:chromeOptions": {
|
73
|
+
args: [headless ? "--headless" : nil].compact,
|
74
|
+
}
|
75
|
+
},
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2023, by Samuel Williams.
|
5
|
+
|
6
|
+
require_relative 'generic'
|
7
|
+
require_relative 'process_group'
|
8
|
+
|
9
|
+
module Async
|
10
|
+
module WebDriver
|
11
|
+
module Bridge
|
12
|
+
# A bridge to the Firefox browser using `geckodriver`.
|
13
|
+
#
|
14
|
+
# ``` ruby
|
15
|
+
# begin
|
16
|
+
# bridge = Async::WebDriver::Bridge::Firefox.start
|
17
|
+
# client = Async::WebDriver::Client.open(bridge.endpoint)
|
18
|
+
# ensure
|
19
|
+
# bridge&.close
|
20
|
+
# end
|
21
|
+
class Firefox < Generic
|
22
|
+
# Create a new bridge to Firefox.
|
23
|
+
# @parameter path [String] The path to the `geckodriver` executable.
|
24
|
+
def initialize(path: "geckodriver")
|
25
|
+
super()
|
26
|
+
|
27
|
+
@path = path
|
28
|
+
@process = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# @returns [String] The version of the `geckodriver` executable.
|
32
|
+
def version
|
33
|
+
::IO.popen([@path, "--version"]) do |io|
|
34
|
+
return io.read
|
35
|
+
end
|
36
|
+
rescue Errno::ENOENT
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
|
40
|
+
# @returns [Array(String)] The arguments to pass to the `geckodriver` executable.
|
41
|
+
def arguments
|
42
|
+
[
|
43
|
+
"--port", self.port.to_s,
|
44
|
+
].compact
|
45
|
+
end
|
46
|
+
|
47
|
+
# Start the driver.
|
48
|
+
def start
|
49
|
+
@process ||= ProcessGroup.spawn(@path, *arguments)
|
50
|
+
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
# Close the driver.
|
55
|
+
def close
|
56
|
+
super
|
57
|
+
|
58
|
+
if @process
|
59
|
+
@process.close
|
60
|
+
@process = nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# The default capabilities for the Firefox browser which need to be provided when requesting a new session.
|
65
|
+
# @parameter headless [Boolean] Whether to run the browser in headless mode.
|
66
|
+
# @returns [Hash] The default capabilities for the Firefox browser.
|
67
|
+
def default_capabilities(headless: true)
|
68
|
+
{
|
69
|
+
alwaysMatch: {
|
70
|
+
browserName: "firefox",
|
71
|
+
"moz:firefoxOptions": {
|
72
|
+
"args": [headless ? "-headless" : nil].compact,
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2023, by Samuel Williams.
|
5
|
+
|
6
|
+
require 'socket'
|
7
|
+
require 'async/http/endpoint'
|
8
|
+
require 'async/http/client'
|
9
|
+
|
10
|
+
module Async
|
11
|
+
module WebDriver
|
12
|
+
module Bridge
|
13
|
+
# Generic W3C WebDriver implementation.
|
14
|
+
class Generic
|
15
|
+
# Start the driver and return a new instance.
|
16
|
+
def self.start(**options)
|
17
|
+
self.new(**options).tap do |bridge|
|
18
|
+
bridge.start
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Initialize the driver.
|
23
|
+
# @parameter port [Integer] The port to listen on.
|
24
|
+
def initialize(port: nil)
|
25
|
+
@port = port
|
26
|
+
@status = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# @attribute [String] The status of the driver after a connection has been established.
|
30
|
+
attr :status
|
31
|
+
|
32
|
+
# @returns [String | Nil] The version of the driver.
|
33
|
+
def version
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# @returns [Boolean] Is the driver supported/working?
|
38
|
+
def supported?
|
39
|
+
version != nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# Start the driver.
|
43
|
+
# @parameter retries [Integer] The number of times to retry before giving up.
|
44
|
+
def start(retries: 100)
|
45
|
+
Console.debug(self, "Waiting for driver to start...")
|
46
|
+
count = 0
|
47
|
+
|
48
|
+
Async::HTTP::Client.open(endpoint) do |client|
|
49
|
+
begin
|
50
|
+
response = client.get("/status")
|
51
|
+
@status = JSON.parse(response.read)["value"]
|
52
|
+
Console.debug(self, "Successfully connected to driver.", status: @status)
|
53
|
+
rescue Errno::ECONNREFUSED
|
54
|
+
if count < retries
|
55
|
+
count += 1
|
56
|
+
sleep(0.001 * count)
|
57
|
+
Console.debug(self, "Driver not ready, retrying...")
|
58
|
+
retry
|
59
|
+
else
|
60
|
+
raise
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Close the driver and any associated resources.
|
67
|
+
def close
|
68
|
+
end
|
69
|
+
|
70
|
+
# Generate a port number for the driver to listen on if it was not specified.
|
71
|
+
# @returns [Integer] The port the driver is listening on.
|
72
|
+
def port
|
73
|
+
unless @port
|
74
|
+
address = ::Addrinfo.tcp("localhost", 0)
|
75
|
+
address.bind do |socket|
|
76
|
+
# We assume that it's unlikely the port will be reused any time soon...
|
77
|
+
@port = socket.local_address.ip_port
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
return @port
|
82
|
+
end
|
83
|
+
|
84
|
+
# @returns [Async::HTTP::Endpoint] The endpoint the driver is listening on.
|
85
|
+
def endpoint
|
86
|
+
Async::HTTP::Endpoint.parse("http://localhost:#{port}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2023, by Samuel Williams.
|
5
|
+
|
6
|
+
module Async
|
7
|
+
module WebDriver
|
8
|
+
module Bridge
|
9
|
+
# A pool of sessions.
|
10
|
+
#
|
11
|
+
# ``` ruby
|
12
|
+
# begin
|
13
|
+
# bridge = Async::WebDriver::Bridge::Pool.start(Async::WebDriver::Bridge::Chrome.new)
|
14
|
+
# session = bridge.session
|
15
|
+
# ensure
|
16
|
+
# bridge&.close
|
17
|
+
# end
|
18
|
+
# ```
|
19
|
+
class Pool
|
20
|
+
# Create a new session pool and start it.
|
21
|
+
# @parameter bridge [Bridge] The bridge to use to create sessions.
|
22
|
+
# @parameter capabilities [Hash] The capabilities to use when creating sessions.
|
23
|
+
# @returns [Pool] The pool.
|
24
|
+
def self.start(bridge, **options)
|
25
|
+
self.new(bridge, **options).tap do |pool|
|
26
|
+
pool.start
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Initialize the session pool.
|
31
|
+
# @parameter bridge [Bridge] The bridge to use to create sessions.
|
32
|
+
# @parameter capabilities [Hash] The capabilities to use when creating sessions.
|
33
|
+
# @parameter minimum [Integer] The minimum number of sessions to keep open.
|
34
|
+
def initialize(bridge, capabilities: bridge.default_capabilities, minimum: 2)
|
35
|
+
@bridge = bridge
|
36
|
+
@capabilities = capabilities
|
37
|
+
@minimum = minimum
|
38
|
+
|
39
|
+
@thread = nil
|
40
|
+
|
41
|
+
@waiting = Thread::Queue.new
|
42
|
+
@sessions = Thread::Queue.new
|
43
|
+
end
|
44
|
+
|
45
|
+
# Close the session pool.
|
46
|
+
def close
|
47
|
+
if @waiting
|
48
|
+
@waiting.close
|
49
|
+
end
|
50
|
+
|
51
|
+
if @thread
|
52
|
+
@thread.join
|
53
|
+
@thread = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
if @sessions
|
57
|
+
@sessions.close
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private def prepare_session(client)
|
62
|
+
client.post("session", {capabilities: @capabilities})
|
63
|
+
end
|
64
|
+
|
65
|
+
# Start the session pool.
|
66
|
+
def start
|
67
|
+
@thread ||= Thread.new do
|
68
|
+
Sync do
|
69
|
+
@bridge.start
|
70
|
+
|
71
|
+
client = Client.open(@bridge.endpoint)
|
72
|
+
|
73
|
+
@minimum.times do
|
74
|
+
@waiting << true
|
75
|
+
end
|
76
|
+
|
77
|
+
while @waiting.pop
|
78
|
+
session = prepare_session(client)
|
79
|
+
@sessions << session
|
80
|
+
end
|
81
|
+
ensure
|
82
|
+
client&.close
|
83
|
+
@bridge.close
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Open a session.
|
89
|
+
def session(&block)
|
90
|
+
@waiting << true
|
91
|
+
|
92
|
+
reply = @sessions.pop
|
93
|
+
|
94
|
+
Session.open(@bridge.endpoint, reply["sessionId"], reply["capabilities"], &block)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2023, by Samuel Williams.
|
5
|
+
|
6
|
+
module Async
|
7
|
+
module WebDriver
|
8
|
+
module Bridge
|
9
|
+
# A group of processes that are all killed when the group is closed.
|
10
|
+
class ProcessGroup
|
11
|
+
# Spawn a new process group with a given command.
|
12
|
+
# @parameter arguments [Array] The command to execute.
|
13
|
+
def self.spawn(*arguments)
|
14
|
+
# This might be problematic...
|
15
|
+
self.new(
|
16
|
+
::Process.spawn(*arguments, pgroup: true, out: File::NULL, err: File::NULL)
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create a new process group from an existing process id.
|
21
|
+
# @parameter pid [Integer] The process id.
|
22
|
+
def initialize(pid)
|
23
|
+
@pid = pid
|
24
|
+
|
25
|
+
@status_task = Async(transient: true) do
|
26
|
+
@status = ::Process.wait(@pid)
|
27
|
+
|
28
|
+
unless @status.success?
|
29
|
+
Console.error(self, "Process exited unexpectedly: #{@status}")
|
30
|
+
end
|
31
|
+
ensure
|
32
|
+
self.close
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Close the process group.
|
37
|
+
def close
|
38
|
+
if @status_task
|
39
|
+
@status_task.stop
|
40
|
+
@status_task = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
if @pid
|
44
|
+
::Process.kill("INT", -@pid)
|
45
|
+
|
46
|
+
Async do |task|
|
47
|
+
task.with_timeout(1) do
|
48
|
+
::Process.wait(@pid)
|
49
|
+
rescue Errno::ECHILD
|
50
|
+
# Done.
|
51
|
+
rescue Async::TimeoutError
|
52
|
+
Console.info(self, "Killing pid #{@pid}...")
|
53
|
+
::Process.kill("KILL", -@pid)
|
54
|
+
end
|
55
|
+
end.wait
|
56
|
+
|
57
|
+
wait_all(-@pid)
|
58
|
+
@pid = nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
|
64
|
+
# Wait for all processes in the group to exit.
|
65
|
+
def wait_all(pgid)
|
66
|
+
while true
|
67
|
+
pid, status = ::Process.wait2(pgid, ::Process::WNOHANG)
|
68
|
+
|
69
|
+
break unless pid
|
70
|
+
end
|
71
|
+
rescue Errno::ECHILD
|
72
|
+
# Done.
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2023, by Samuel Williams.
|
5
|
+
|
6
|
+
require_relative 'bridge/chrome'
|
7
|
+
require_relative 'bridge/firefox'
|
8
|
+
|
9
|
+
module Async
|
10
|
+
module WebDriver
|
11
|
+
# A bridge is a process that can be used to communicate with a browser.
|
12
|
+
# It is not needed in all cases, but is useful when you want to run integration tests without any external drivers/dependencies.
|
13
|
+
# As starting a bridge can be slow, it is recommended to use a shared bridge when possible.
|
14
|
+
module Bridge
|
15
|
+
ALL = [
|
16
|
+
Bridge::Chrome,
|
17
|
+
Bridge::Firefox,
|
18
|
+
]
|
19
|
+
|
20
|
+
def self.each(&block)
|
21
|
+
return enum_for(:each) unless block_given?
|
22
|
+
|
23
|
+
ALL.each do |klass|
|
24
|
+
next unless klass.new.supported?
|
25
|
+
yield klass
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,30 +1,75 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
class Client
|
6
|
-
def initialize(endpoint:, desired_capabilities: {})
|
7
|
-
@connection = Connection.new endpoint: endpoint
|
8
|
-
@desired_capabilities = desired_capabilities
|
9
|
-
end
|
10
|
-
|
11
|
-
def session
|
12
|
-
SessionCreator.new connection: @connection, desired_capabilities: @desired_capabilities
|
13
|
-
end
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2023, by Samuel Williams.
|
14
5
|
|
15
|
-
|
16
|
-
|
17
|
-
end
|
6
|
+
require_relative 'request_helper'
|
7
|
+
require_relative 'session'
|
18
8
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
9
|
+
module Async
|
10
|
+
module WebDriver
|
11
|
+
# A client for the WebDriver protocol.
|
12
|
+
#
|
13
|
+
# If you have a running web driver server, you can connect to it like so (assuming it is running on port 4444):
|
14
|
+
#
|
15
|
+
# ``` ruby
|
16
|
+
# begin
|
17
|
+
# client = Async::WebDriver::Client.open(Async::HTTP::Endpoint.parse("http://localhost:4444"))
|
18
|
+
# session = client.session
|
19
|
+
# ensure
|
20
|
+
# client&.close
|
21
|
+
# end
|
22
|
+
# ```
|
23
|
+
class Client
|
24
|
+
include RequestHelper
|
25
|
+
|
26
|
+
# Open a new session.
|
27
|
+
# @parameter endpoint [Async::HTTP::Endpoint] The endpoint to connect to.
|
28
|
+
# @yields {|client| ...} The client will be closed automatically if you provide a block.
|
29
|
+
# @parameter client [Client] The client.
|
30
|
+
# @returns [Client] The client if no block is given.
|
31
|
+
def self.open(endpoint, **options)
|
32
|
+
client = self.new(
|
33
|
+
Async::HTTP::Client.open(endpoint, **options)
|
34
|
+
)
|
35
|
+
|
36
|
+
return client unless block_given?
|
37
|
+
|
38
|
+
begin
|
39
|
+
yield client
|
40
|
+
ensure
|
41
|
+
client.close
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Initialize the client.
|
46
|
+
# @parameter delegate [Protocol::HTTP::Middleware] The underlying HTTP client (or wrapper).
|
47
|
+
def initialize(delegate)
|
48
|
+
@delegate = delegate
|
49
|
+
end
|
50
|
+
|
51
|
+
# Close the client.
|
52
|
+
def close
|
53
|
+
@delegate.close
|
54
|
+
end
|
55
|
+
|
56
|
+
# Request a new session.
|
57
|
+
# @returns [Session] The session if no block is given.
|
58
|
+
# @yields {|session| ...} The session will be closed automatically if you provide a block.
|
59
|
+
# @parameter session [Session] The session.
|
60
|
+
def session(capabilities, &block)
|
61
|
+
reply = post("session", {capabilities: capabilities})
|
62
|
+
|
63
|
+
session = Session.new(@delegate, reply["sessionId"], reply["value"])
|
64
|
+
|
65
|
+
return session unless block_given?
|
66
|
+
|
67
|
+
begin
|
68
|
+
yield session
|
69
|
+
ensure
|
70
|
+
session.close
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
30
75
|
end
|