async-webdriver 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a6324ffc2dfcde7982396b2dbda22df843948de94ba4e61811a777b64156a05
4
- data.tar.gz: 25cc350a2093f0bc1651500139bb654c5ed0af2f8ac553ecbc5d3544691e9999
3
+ metadata.gz: 2885f18a964ec45c687168efd25b89326eb2a7cdf30e2458a8d348a6613d22ff
4
+ data.tar.gz: 58de67040f73f43aa73e2877acab7c7637575721613d63941127bb69a38e3899
5
5
  SHA512:
6
- metadata.gz: 4a39e3446f54eeb665252c3cb7f76bcdf5b6dc8ba0fcc8e67f0570d5e93f783e77e833cb499211562e26f922fbe14f507a6e7645cbe45c5f7c9af921e3c4f9b7
7
- data.tar.gz: 0205a411504dac570476a891f849542d337f9cf1d44b4462811e3bfdc006b24f3eddd338c27822f7119a9467676cad6f17efdb30c57ddd14ceaf4e15537066d6
6
+ metadata.gz: ddfea03fb616984f3f48d2bf6199918119c6750a84e53f012d9d3e3a9c6231508cb911578c19cfd7cd7a88a4fe15965d5a8a8502fd7fc3148af0c8aa409f1fd1
7
+ data.tar.gz: 7a309bf300d64898d622da707e9fa98e37876cd1752e9ce749b45ed74d0648b7d3f5030d3bad9b123f5bfec468faee57271c45e596af7936868acc34c62fa4b4
checksums.yaml.gz.sig CHANGED
Binary file
@@ -20,48 +20,54 @@ module Async
20
20
  # end
21
21
  # ```
22
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
23
+ def path
24
+ @options.fetch(:path, "chromedriver")
30
25
  end
31
26
 
32
27
  # @returns [String] The version of the `chromedriver` executable.
33
28
  def version
34
- ::IO.popen([@path, "--version"]) do |io|
29
+ ::IO.popen([self.path, "--version"]) do |io|
35
30
  return io.read
36
31
  end
37
32
  rescue Errno::ENOENT
38
33
  return nil
39
34
  end
40
35
 
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)
36
+ class Driver < Bridge::Driver
37
+ def initialize(**options)
38
+ super(**options)
39
+ @process_group = nil
40
+ end
51
41
 
52
- super
53
- end
54
-
55
- # Close the driver.
56
- def close
57
- super
42
+ # @returns [Array(String)] The arguments to pass to the `chromedriver` executable.
43
+ def arguments(**options)
44
+ [
45
+ options.fetch(:path, "chromedriver"),
46
+ "--port=#{self.port}",
47
+ ].compact
48
+ end
49
+
50
+ def start
51
+ @process_group = ProcessGroup.spawn(*arguments(**@options))
52
+
53
+ super
54
+ end
58
55
 
59
- if @process
60
- @process.close
61
- @process = nil
56
+ def close
57
+ if @process_group
58
+ @process_group.close
59
+ @process_group = nil
60
+ end
61
+
62
+ super
62
63
  end
63
64
  end
64
65
 
66
+ # Start the driver.
67
+ def start(**options)
68
+ Driver.new(**options).tap(&:start)
69
+ end
70
+
65
71
  # The default capabilities for the Chrome browser which need to be provided when requesting a new session.
66
72
  # @parameter headless [Boolean] Whether to run the browser in headless mode.
67
73
  # @returns [Hash] The default capabilities for the Chrome browser.
@@ -71,7 +77,8 @@ module Async
71
77
  browserName: "chrome",
72
78
  "goog:chromeOptions": {
73
79
  args: [headless ? "--headless" : nil].compact,
74
- }
80
+ },
81
+ webSocketUrl: true,
75
82
  },
76
83
  }
77
84
  end
@@ -0,0 +1,93 @@
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
+ # Represents an instance of a locally running driver (usually with a process group).
10
+ class Driver
11
+ def initialize(**options)
12
+ @options = options
13
+ @count = 0
14
+ @closed = false
15
+ end
16
+
17
+ def concurrency
18
+ @options.fetch(:concurrency, 128)
19
+ end
20
+
21
+ attr :count
22
+
23
+ # @attribute [Hash] The status of the driver after a connection has been established.
24
+ attr :status
25
+
26
+ def viable?
27
+ !@closed
28
+ end
29
+
30
+ def closed?
31
+ @closed
32
+ end
33
+
34
+ def close
35
+ @closed = true
36
+ end
37
+
38
+ def reusable?
39
+ @options.fetch(:reusable, !@closed)
40
+ end
41
+
42
+ # Generate a port number for the driver to listen on if it was not specified.
43
+ # @returns [Integer] An ephemeral port number.
44
+ def ephemeral_port
45
+ address = ::Addrinfo.tcp("localhost", 0)
46
+
47
+ address.bind do |socket|
48
+ # We assume that it's unlikely the port will be reused any time soon...
49
+ return socket.local_address.ip_port
50
+ end
51
+ end
52
+
53
+ def port
54
+ @port ||= @options.fetch(:port, self.ephemeral_port)
55
+ end
56
+
57
+ def endpoint
58
+ Async::HTTP::Endpoint.parse("http://localhost", port: self.port)
59
+ end
60
+
61
+ def client
62
+ Client.open(self.endpoint)
63
+ end
64
+
65
+ # Start the driver.
66
+ # @parameter retries [Integer] The number of times to retry before giving up.
67
+ def start(retries: 100)
68
+ endpoint = self.endpoint
69
+
70
+ Console.debug(self, "Waiting for driver to start...", endpoint: endpoint)
71
+ count = 0
72
+
73
+ Async::HTTP::Client.open(endpoint) do |client|
74
+ begin
75
+ response = client.get("/status")
76
+ @status = JSON.parse(response.read)["value"]
77
+ Console.debug(self, "Successfully connected to driver.", status: @status)
78
+ rescue Errno::ECONNREFUSED
79
+ if count < retries
80
+ count += 1
81
+ sleep(0.01 * count)
82
+ Console.debug(self, "Driver not ready, retrying...")
83
+ retry
84
+ else
85
+ raise
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -19,46 +19,56 @@ module Async
19
19
  # bridge&.close
20
20
  # end
21
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
22
+ def path
23
+ @options.fetch(:path, "geckodriver")
29
24
  end
30
25
 
31
26
  # @returns [String] The version of the `geckodriver` executable.
32
27
  def version
33
- ::IO.popen([@path, "--version"]) do |io|
28
+ ::IO.popen([self.path, "--version"]) do |io|
34
29
  return io.read
35
30
  end
36
31
  rescue Errno::ENOENT
37
32
  return nil
38
33
  end
39
34
 
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)
35
+ class Driver < Bridge::Driver
36
+ def initialize(**options)
37
+ super(**options)
38
+ @process_group = nil
39
+ end
50
40
 
51
- super
52
- end
53
-
54
- # Close the driver.
55
- def close
56
- super
41
+ def concurrency
42
+ 1
43
+ end
57
44
 
58
- if @process
59
- @process.close
60
- @process = nil
45
+ # @returns [Array(String)] The arguments to pass to the `chromedriver` executable.
46
+ def arguments(**options)
47
+ [
48
+ options.fetch(:path, "geckodriver"),
49
+ "--port", self.port.to_s,
50
+ ].compact
61
51
  end
52
+
53
+ def start
54
+ @process_group = ProcessGroup.spawn(*arguments(**@options))
55
+
56
+ super
57
+ end
58
+
59
+ def close
60
+ if @process_group
61
+ @process_group.close
62
+ @process_group = nil
63
+ end
64
+
65
+ super
66
+ end
67
+ end
68
+
69
+ # Start the driver.
70
+ def start(**options)
71
+ Driver.new(**options).tap(&:start)
62
72
  end
63
73
 
64
74
  # The default capabilities for the Firefox browser which need to be provided when requesting a new session.
@@ -69,7 +79,7 @@ module Async
69
79
  alwaysMatch: {
70
80
  browserName: "firefox",
71
81
  "moz:firefoxOptions": {
72
- "args": [headless ? "-headless" : nil].compact,
82
+ args: [headless ? "-headless" : nil].compact,
73
83
  }
74
84
  }
75
85
  }
@@ -12,23 +12,14 @@ module Async
12
12
  module Bridge
13
13
  # Generic W3C WebDriver implementation.
14
14
  class Generic
15
- # Start the driver and return a new instance.
16
15
  def self.start(**options)
17
- self.new(**options).tap do |bridge|
18
- bridge.start
19
- end
16
+ self.new(**options).start
20
17
  end
21
18
 
22
- # Initialize the driver.
23
- # @parameter port [Integer] The port to listen on.
24
- def initialize(port: nil)
25
- @port = port
26
- @status = nil
19
+ def initialize(**options)
20
+ @options = options
27
21
  end
28
22
 
29
- # @attribute [String] The status of the driver after a connection has been established.
30
- attr :status
31
-
32
23
  # @returns [String | Nil] The version of the driver.
33
24
  def version
34
25
  nil
@@ -38,53 +29,6 @@ module Async
38
29
  def supported?
39
30
  version != nil
40
31
  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
32
  end
89
33
  end
90
34
  end
@@ -3,10 +3,15 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2023, by Samuel Williams.
5
5
 
6
+ require 'async/actor'
7
+ require 'async/pool'
8
+
9
+ require_relative '../session'
10
+
6
11
  module Async
7
12
  module WebDriver
8
13
  module Bridge
9
- # A pool of sessions.
14
+ # A pool of sessions, constructed from a bridge, which instantiates drivers as needed. Drivers are capable of supporting 1 ore more sessions.
10
15
  #
11
16
  # ``` ruby
12
17
  # begin
@@ -17,90 +22,139 @@ module Async
17
22
  # end
18
23
  # ```
19
24
  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
25
+ class BridgeController
26
+ def initialize(bridge, capabilities: bridge.default_capabilities)
27
+ @bridge = bridge
28
+ @capabilities = capabilities
29
+ @pool = Async::Pool::Controller.new(self)
30
+ end
31
+
32
+ class SessionCache
33
+ def initialize(driver, capabilities)
34
+ @driver = driver
35
+ @capabilities = capabilities
36
+
37
+ @client = driver.client
38
+ @sessions = []
39
+ end
40
+
41
+ def viable?
42
+ @driver&.viable?
43
+ end
44
+
45
+ def reusable?
46
+ @driver&.reusable?
47
+ end
48
+
49
+ def close
50
+ if @driver
51
+ @driver.close
52
+ @driver = nil
53
+ end
54
+
55
+ if @client
56
+ @client.close
57
+ @client = nil
58
+ end
59
+
60
+ if @sessions
61
+ @sessions = nil
62
+ end
63
+ end
64
+
65
+ def concurrency
66
+ @driver.concurrency
67
+ end
68
+
69
+ def acquire
70
+ if @sessions.empty?
71
+ session = @client.post("session", {capabilities: @capabilities})
72
+ session[:cache] = self
73
+ session[:endpoint] = @driver.endpoint
74
+
75
+ return session
76
+ else
77
+ return @sessions.pop
78
+ end
79
+ end
80
+
81
+ def release(session)
82
+ @sessions.push(session)
83
+ end
84
+ end
85
+
86
+ # Constructor for the pool.
87
+ def call
88
+ SessionCache.new(@bridge.start, @capabilities)
89
+ end
90
+
91
+ def acquire
92
+ session_cache = @pool.acquire
93
+
94
+ return session_cache.acquire
95
+ end
96
+
97
+ def release(session)
98
+ session_cache = session[:cache]
99
+
100
+ session_cache.release(session)
101
+
102
+ @pool.release(session_cache)
103
+ end
104
+
105
+ def retire(session)
106
+ session_cache = session[:cache]
107
+
108
+ session_cache.release(session)
109
+
110
+ @pool.retire(session_cache)
111
+ end
112
+
113
+ def close
114
+ if @pool
115
+ @pool.close
116
+ @pool = nil
117
+ end
27
118
  end
28
119
  end
29
120
 
30
121
  # Initialize the session pool.
31
122
  # @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
123
+ def initialize(...)
124
+ @controller = Async::Actor.new(BridgeController.new(...))
43
125
  end
44
126
 
45
127
  # Close the session pool.
46
128
  def close
47
- if @waiting
48
- @waiting.close
129
+ @controller.close
130
+ end
131
+
132
+ class CachedWrapper < Session
133
+ def pool
134
+ @options[:pool]
49
135
  end
50
136
 
51
- if @thread
52
- @thread.join
53
- @thread = nil
137
+ def payload
138
+ @options[:payload]
54
139
  end
55
140
 
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
141
+ def close
142
+ unless self.pool.reuse(self)
143
+ super
84
144
  end
85
145
  end
86
146
  end
87
147
 
88
148
  # Open a session.
89
149
  def session(&block)
90
- @waiting << true
150
+ payload = @controller.acquire
91
151
 
92
- reply = @sessions.pop
93
-
94
- session = Session.open(@bridge.endpoint, reply["sessionId"], reply["capabilities"])
152
+ session = CachedWrapper.open(payload[:endpoint], payload["sessionId"], payload["capabilities"], pool: self, payload: payload)
95
153
 
96
154
  return session unless block_given?
97
155
 
98
156
  begin
99
157
  yield session
100
-
101
- # Try to reuse the session for extreme performance:
102
- reuse(session)
103
- session = nil
104
158
  ensure
105
159
  session&.close
106
160
  end
@@ -109,7 +163,9 @@ module Async
109
163
  def reuse(session)
110
164
  session.reset!
111
165
 
112
- @sessions << {"sessionId" => session.id, "capabilities" => session.capabilities}
166
+ @controller.release(session.payload)
167
+
168
+ return true
113
169
  end
114
170
  end
115
171
  end
@@ -3,6 +3,8 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2023, by Samuel Williams.
5
5
 
6
+ require_relative 'driver'
7
+
6
8
  module Async
7
9
  module WebDriver
8
10
  module Bridge
@@ -72,6 +74,23 @@ module Async
72
74
  # Done.
73
75
  end
74
76
  end
77
+
78
+ class ProcessDriver < Driver
79
+ def initialize(endpoint, process)
80
+ super(endpoint)
81
+
82
+ @process = process
83
+ end
84
+
85
+ def close
86
+ super
87
+
88
+ if @process
89
+ @process.close
90
+ @process = nil
91
+ end
92
+ end
93
+ end
75
94
  end
76
95
  end
77
96
  end
@@ -6,6 +6,8 @@
6
6
  require_relative 'bridge/chrome'
7
7
  require_relative 'bridge/firefox'
8
8
 
9
+ require_relative 'error'
10
+
9
11
  module Async
10
12
  module WebDriver
11
13
  # A bridge is a process that can be used to communicate with a browser.
@@ -25,6 +27,31 @@ module Async
25
27
  yield klass
26
28
  end
27
29
  end
30
+
31
+ # The environment variable used to select a bridge.
32
+ #
33
+ # ```
34
+ # ASYNC_WEBDRIVER_BRIDGE=Chrome
35
+ # ASYNC_WEBDRIVER_BRIDGE=Firefox
36
+ # ```
37
+ ASYNC_WEBDRIVER_BRIDGE = 'ASYNC_WEBDRIVER_BRIDGE'
38
+
39
+ class UnsupportedError < Error
40
+ end
41
+
42
+ # @returns [Bridge] The default bridge to use.
43
+ def self.default(env = ENV)
44
+ if name = env[ASYNC_WEBDRIVER_BRIDGE]
45
+ self.const_get(name).new
46
+ else
47
+ ALL.each do |klass|
48
+ instance = klass.new
49
+ return instance if instance.supported?
50
+ end
51
+
52
+ raise UnsupportedError, "No supported bridge found!"
53
+ end
54
+ end
28
55
  end
29
56
  end
30
57
  end
@@ -60,7 +60,7 @@ module Async
60
60
  def session(capabilities, &block)
61
61
  reply = post("session", {capabilities: capabilities})
62
62
 
63
- session = Session.new(@delegate, reply["sessionId"], reply["value"])
63
+ session = Session.new(@delegate, reply["sessionId"], reply["capabilities"])
64
64
 
65
65
  return session unless block_given?
66
66
 
@@ -7,36 +7,6 @@ require_relative 'version'
7
7
 
8
8
  module Async
9
9
  module WebDriver
10
- # Error Code HTTP Status JSON Error Code Description
11
- # element click intercepted 400 element click intercepted The Element Click command could not be completed because the element receiving the events is obscuring the element that was requested clicked.
12
- # element not interactable 400 element not interactable A command could not be completed because the element is not pointer- or keyboard interactable.
13
- # insecure certificate 400 insecure certificate Navigation caused the user agent to hit a certificate warning, which is usually the result of an expired or invalid TLS certificate.
14
- # invalid argument 400 invalid argument The arguments passed to a command are either invalid or malformed.
15
- # invalid cookie domain 400 invalid cookie domain An illegal attempt was made to set a cookie under a different domain than the current page.
16
- # invalid element state 400 invalid element state A command could not be completed because the element is in an invalid state, e.g. attempting to clear an element that isn’t both editable and resettable.
17
- # invalid selector 400 invalid selector Argument was an invalid selector.
18
- # invalid session id 404 invalid session id Occurs if the given session id is not in the list of active sessions, meaning the session either does not exist or that it’s not active.
19
- # javascript error 500 javascript error An error occurred while executing JavaScript supplied by the user.
20
- # move target out of bounds 500 move target out of bounds The target for mouse interaction is not in the browser’s viewport and cannot be brought into that viewport.
21
- # no such alert 404 no such alert An attempt was made to operate on a modal dialog when one was not open.
22
- # no such cookie 404 no such cookie No cookie matching the given path name was found amongst the associated cookies of the current browsing context’s active document.
23
- # no such element 404 no such element An element could not be located on the page using the given search parameters.
24
- # no such frame 404 no such frame A command to switch to a frame could not be satisfied because the frame could not be found.
25
- # no such window 404 no such window A command to switch to a window could not be satisfied because the window could not be found.
26
- # no such shadow root 404 no such shadow root The element does not have a shadow root.
27
- # script timeout error 500 script timeout A script did not complete before its timeout expired.
28
- # session not created 500 session not created A new session could not be created.
29
- # stale element reference 404 stale element reference A command failed because the referenced element is no longer attached to the DOM.
30
- # detached shadow root 404 detached shadow root A command failed because the referenced shadow root is no longer attached to the DOM.
31
- # timeout 500 timeout An operation did not complete before its timeout expired.
32
- # unable to set cookie 500 unable to set cookie A command to set a cookie’s value could not be satisfied.
33
- # unable to capture screen 500 unable to capture screen A screen capture was made impossible.
34
- # unexpected alert open 500 unexpected alert open A modal dialog was open, blocking this operation.
35
- # unknown command 404 unknown command A command could not be executed because the remote end is not aware of it.
36
- # unknown error 500 unknown error An unknown error occurred in the remote end while processing the command.
37
- # unknown method 405 unknown method The requested command matched a known URL but did not match any method for that URL.
38
- # unsupported operation 500 unsupported operation Indicates that a command that should have executed properly cannot be supported for some reason.
39
-
40
10
  class Error < StandardError
41
11
  end
42
12
 
@@ -30,8 +30,9 @@ module Async
30
30
  # @returns [Session] The session if no block is given.
31
31
  def self.open(endpoint, *arguments, **options)
32
32
  client = self.new(
33
- Async::HTTP::Client.open(endpoint, **options),
34
- *arguments
33
+ Async::HTTP::Client.open(endpoint),
34
+ *arguments,
35
+ **options
35
36
  )
36
37
 
37
38
  return client unless block_given?
@@ -47,10 +48,12 @@ module Async
47
48
  # @parameter delegate [Protocol::HTTP::Middleware] The underlying HTTP client (or wrapper).
48
49
  # @parameter id [String] The session identifier.
49
50
  # @parameter capabilities [Hash] The capabilities of the session.
50
- def initialize(delegate, id, capabilities)
51
+ def initialize(delegate, id, capabilities, **options)
51
52
  @delegate = delegate
52
53
  @id = id
53
54
  @capabilities = capabilities
55
+
56
+ @options = options
54
57
  end
55
58
 
56
59
  def inspect
@@ -131,7 +134,9 @@ module Async
131
134
 
132
135
  # Clear cookies and local storage:
133
136
  self.delete_all_cookies
134
- self.execute("localStorage.clear();")
137
+
138
+ # This does not work consistently:
139
+ # self.execute("localStorage.clear();")
135
140
 
136
141
  # Detach the session instance from the underlying HTTP client:
137
142
  @delegate = nil
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Async
7
7
  module WebDriver
8
- VERSION = "0.3.0"
8
+ VERSION = "0.4.0"
9
9
  end
10
10
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-webdriver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -37,7 +37,7 @@ cert_chain:
37
37
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
38
38
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
39
39
  -----END CERTIFICATE-----
40
- date: 2023-12-04 00:00:00.000000000 Z
40
+ date: 2023-12-08 00:00:00.000000000 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: async-http
@@ -45,14 +45,56 @@ dependencies:
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: '0.42'
48
+ version: '0.61'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
- version: '0.42'
55
+ version: '0.61'
56
+ - !ruby/object:Gem::Dependency
57
+ name: async-websocket
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.25'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.25'
70
+ - !ruby/object:Gem::Dependency
71
+ name: async-actor
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '0.1'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '0.1'
84
+ - !ruby/object:Gem::Dependency
85
+ name: async-pool
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '0.4'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '0.4'
56
98
  description:
57
99
  email:
58
100
  executables: []
@@ -62,6 +104,7 @@ files:
62
104
  - lib/async/webdriver.rb
63
105
  - lib/async/webdriver/bridge.rb
64
106
  - lib/async/webdriver/bridge/chrome.rb
107
+ - lib/async/webdriver/bridge/driver.rb
65
108
  - lib/async/webdriver/bridge/firefox.rb
66
109
  - lib/async/webdriver/bridge/generic.rb
67
110
  - lib/async/webdriver/bridge/pool.rb
metadata.gz.sig CHANGED
Binary file