playwright-ruby-client 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +26 -0
- data/documentation/docs/api/browser.md +18 -2
- data/documentation/docs/api/browser_context.md +10 -0
- data/documentation/docs/api/browser_type.md +1 -0
- data/documentation/docs/api/cdp_session.md +41 -1
- data/documentation/docs/api/download.md +97 -0
- data/documentation/docs/api/element_handle.md +38 -4
- data/documentation/docs/api/experimental/android_device.md +1 -0
- data/documentation/docs/api/frame.md +78 -17
- data/documentation/docs/api/keyboard.md +11 -20
- data/documentation/docs/api/locator.md +650 -0
- data/documentation/docs/api/page.md +107 -19
- data/documentation/docs/api/response.md +16 -0
- data/documentation/docs/article/guides/inspector.md +31 -0
- data/documentation/docs/article/guides/playwright_on_alpine_linux.md +91 -0
- data/documentation/docs/article/guides/rails_integration.md +1 -1
- data/documentation/docs/article/guides/semi_automation.md +5 -1
- data/documentation/docs/include/api_coverage.md +70 -7
- data/lib/playwright.rb +36 -4
- data/lib/playwright/channel_owners/artifact.rb +4 -0
- data/lib/playwright/channel_owners/browser.rb +5 -0
- data/lib/playwright/channel_owners/browser_context.rb +37 -3
- data/lib/playwright/channel_owners/cdp_session.rb +19 -0
- data/lib/playwright/channel_owners/element_handle.rb +11 -4
- data/lib/playwright/channel_owners/frame.rb +103 -34
- data/lib/playwright/channel_owners/page.rb +140 -53
- data/lib/playwright/channel_owners/response.rb +9 -1
- data/lib/playwright/connection.rb +2 -4
- data/lib/playwright/{download.rb → download_impl.rb} +5 -1
- data/lib/playwright/javascript/expression.rb +5 -4
- data/lib/playwright/locator_impl.rb +314 -0
- data/lib/playwright/route_handler_entry.rb +3 -2
- data/lib/playwright/timeout_settings.rb +4 -4
- data/lib/playwright/transport.rb +0 -1
- data/lib/playwright/url_matcher.rb +12 -2
- data/lib/playwright/version.rb +2 -2
- data/lib/playwright/web_socket_client.rb +164 -0
- data/lib/playwright/web_socket_transport.rb +104 -0
- data/lib/playwright_api/android.rb +6 -6
- data/lib/playwright_api/android_device.rb +10 -9
- data/lib/playwright_api/browser.rb +17 -11
- data/lib/playwright_api/browser_context.rb +14 -9
- data/lib/playwright_api/browser_type.rb +8 -7
- data/lib/playwright_api/cdp_session.rb +30 -8
- data/lib/playwright_api/console_message.rb +6 -6
- data/lib/playwright_api/dialog.rb +6 -6
- data/lib/playwright_api/download.rb +70 -0
- data/lib/playwright_api/element_handle.rb +44 -24
- data/lib/playwright_api/frame.rb +100 -49
- data/lib/playwright_api/js_handle.rb +6 -6
- data/lib/playwright_api/locator.rb +509 -0
- data/lib/playwright_api/page.rb +110 -57
- data/lib/playwright_api/playwright.rb +6 -6
- data/lib/playwright_api/request.rb +6 -6
- data/lib/playwright_api/response.rb +15 -10
- data/lib/playwright_api/route.rb +6 -6
- data/lib/playwright_api/selectors.rb +6 -6
- data/lib/playwright_api/web_socket.rb +6 -6
- data/lib/playwright_api/worker.rb +6 -6
- metadata +15 -5
@@ -24,13 +24,14 @@ module Playwright
|
|
24
24
|
::Playwright::ChannelOwner.from(resp)
|
25
25
|
end
|
26
26
|
|
27
|
-
def eval_on_selector(channel, selector)
|
28
|
-
|
29
|
-
'evalOnSelector',
|
27
|
+
def eval_on_selector(channel, selector, strict: nil)
|
28
|
+
params = {
|
30
29
|
selector: selector,
|
31
30
|
expression: @expression,
|
32
31
|
arg: @serialized_arg,
|
33
|
-
|
32
|
+
}
|
33
|
+
params[:strict] = strict if strict
|
34
|
+
value = channel.send_message_to_server('evalOnSelector', params)
|
34
35
|
ValueParser.new(value).parse
|
35
36
|
end
|
36
37
|
|
@@ -0,0 +1,314 @@
|
|
1
|
+
module Playwright
|
2
|
+
define_api_implementation :LocatorImpl do
|
3
|
+
def initialize(frame:, timeout_settings:, selector:)
|
4
|
+
@frame = frame
|
5
|
+
@timeout_settings = timeout_settings
|
6
|
+
@selector = selector
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
"Locator@#{@selector}"
|
11
|
+
end
|
12
|
+
|
13
|
+
private def with_element(timeout: nil, &block)
|
14
|
+
start_time = Time.now
|
15
|
+
|
16
|
+
handle = @frame.wait_for_selector(@selector, strict: true, state: 'attached', timeout: timeout)
|
17
|
+
unless handle
|
18
|
+
raise "Could not resolve #{@selector} to DOM Element"
|
19
|
+
end
|
20
|
+
|
21
|
+
call_options = {}
|
22
|
+
if timeout
|
23
|
+
call_options[:timeout] = (timeout - (Time.now - start_time) * 1000).to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
block.call(handle, call_options)
|
28
|
+
ensure
|
29
|
+
handle.dispose
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def bounding_box(timeout: nil)
|
34
|
+
with_element(timeout: timeout) do |handle|
|
35
|
+
handle.bounding_box
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def check(
|
40
|
+
force: nil,
|
41
|
+
noWaitAfter: nil,
|
42
|
+
position: nil,
|
43
|
+
timeout: nil,
|
44
|
+
trial: nil)
|
45
|
+
|
46
|
+
@frame.check(@selector,
|
47
|
+
strict: true,
|
48
|
+
force: force,
|
49
|
+
noWaitAfter: noWaitAfter,
|
50
|
+
position: position,
|
51
|
+
timeout: timeout,
|
52
|
+
trial: trial)
|
53
|
+
end
|
54
|
+
|
55
|
+
def click(
|
56
|
+
button: nil,
|
57
|
+
clickCount: nil,
|
58
|
+
delay: nil,
|
59
|
+
force: nil,
|
60
|
+
modifiers: nil,
|
61
|
+
noWaitAfter: nil,
|
62
|
+
position: nil,
|
63
|
+
timeout: nil,
|
64
|
+
trial: nil)
|
65
|
+
|
66
|
+
@frame.click(@selector,
|
67
|
+
strict: true,
|
68
|
+
button: button,
|
69
|
+
clickCount: clickCount,
|
70
|
+
delay: delay,
|
71
|
+
force: force,
|
72
|
+
modifiers: modifiers,
|
73
|
+
noWaitAfter: noWaitAfter,
|
74
|
+
position: position,
|
75
|
+
timeout: timeout,
|
76
|
+
trial: trial)
|
77
|
+
end
|
78
|
+
|
79
|
+
def dblclick(
|
80
|
+
button: nil,
|
81
|
+
delay: nil,
|
82
|
+
force: nil,
|
83
|
+
modifiers: nil,
|
84
|
+
noWaitAfter: nil,
|
85
|
+
position: nil,
|
86
|
+
timeout: nil,
|
87
|
+
trial: nil)
|
88
|
+
|
89
|
+
@frame.dblclick(@selector,
|
90
|
+
strict: true,
|
91
|
+
button: button,
|
92
|
+
delay: delay,
|
93
|
+
force: force,
|
94
|
+
modifiers: modifiers,
|
95
|
+
noWaitAfter: noWaitAfter,
|
96
|
+
position: position,
|
97
|
+
timeout: timeout,
|
98
|
+
trial: trial)
|
99
|
+
end
|
100
|
+
|
101
|
+
def dispatch_event(type, eventInit: nil, timeout: nil)
|
102
|
+
@frame.dispatch_event(@selector, type, strict: true, eventInit: eventInit, timeout: timeout)
|
103
|
+
end
|
104
|
+
|
105
|
+
def evaluate(expression, arg: nil, timeout: nil)
|
106
|
+
with_element(timeout: timeout) do |handle|
|
107
|
+
handle.evaluate(expression, arg: arg)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def evaluate_all(expression, arg: nil)
|
112
|
+
@frame.eval_on_selector_all(@selector, expression, arg: arg)
|
113
|
+
end
|
114
|
+
|
115
|
+
def evaluate_handle(expression, arg: nil, timeout: nil)
|
116
|
+
with_element(timeout: timeout) do |handle|
|
117
|
+
handle.evaluate_handle(expression, arg: arg)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def fill(value, force: nil, noWaitAfter: nil, timeout: nil)
|
122
|
+
@frame.fill(@selector, value, strict: true, force: force, noWaitAfter: noWaitAfter, timeout: timeout)
|
123
|
+
end
|
124
|
+
|
125
|
+
def locator(selector)
|
126
|
+
LocatorImpl.new(
|
127
|
+
frame: @frame,
|
128
|
+
timeout_settings: @timeout_settings,
|
129
|
+
selector: "#{@selector} >> #{selector}",
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
def element_handle(timeout: nil)
|
134
|
+
@frame.wait_for_selector(@selector, strict: true, state: 'attached', timeout: timeout)
|
135
|
+
end
|
136
|
+
|
137
|
+
def element_handles
|
138
|
+
@frame.query_selector_all(@selector)
|
139
|
+
end
|
140
|
+
|
141
|
+
def first
|
142
|
+
LocatorImpl.new(
|
143
|
+
frame: @frame,
|
144
|
+
timeout_settings: @timeout_settings,
|
145
|
+
selector: "#{@selector} >> _nth=first",
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
def last
|
150
|
+
LocatorImpl.new(
|
151
|
+
frame: @frame,
|
152
|
+
timeout_settings: @timeout_settings,
|
153
|
+
selector: "#{@selector} >> _nth=last",
|
154
|
+
)
|
155
|
+
end
|
156
|
+
|
157
|
+
def nth(index)
|
158
|
+
LocatorImpl.new(
|
159
|
+
frame: @frame,
|
160
|
+
timeout_settings: @timeout_settings,
|
161
|
+
selector: "#{@selector} >> _nth=#{index}",
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
def focus(timeout: nil)
|
166
|
+
@frame.focus(@selector, strict: true, timeout: timeout)
|
167
|
+
end
|
168
|
+
|
169
|
+
def count
|
170
|
+
@frame.eval_on_selector_all(@selector, 'ee => ee.length')
|
171
|
+
end
|
172
|
+
|
173
|
+
def get_attribute(name, timeout: nil)
|
174
|
+
@frame.get_attribute(@selector, name, strict: true, timeout: timeout)
|
175
|
+
end
|
176
|
+
|
177
|
+
def hover(
|
178
|
+
force: nil,
|
179
|
+
modifiers: nil,
|
180
|
+
position: nil,
|
181
|
+
timeout: nil,
|
182
|
+
trial: nil)
|
183
|
+
@frame.hover(@selector,
|
184
|
+
strict: true,
|
185
|
+
force: force,
|
186
|
+
modifiers: modifiers,
|
187
|
+
position: position,
|
188
|
+
timeout: timeout,
|
189
|
+
trial: trial)
|
190
|
+
end
|
191
|
+
|
192
|
+
def inner_html(timeout: nil)
|
193
|
+
@frame.inner_html(@selector, strict: true, timeout: timeout)
|
194
|
+
end
|
195
|
+
|
196
|
+
def inner_text(timeout: nil)
|
197
|
+
@frame.inner_text(@selector, strict: true, timeout: timeout)
|
198
|
+
end
|
199
|
+
|
200
|
+
def input_value(timeout: nil)
|
201
|
+
@frame.input_value(@selector, strict: true, timeout: timeout)
|
202
|
+
end
|
203
|
+
|
204
|
+
%i[checked? disabled? editable? enabled? hidden? visible?].each do |method_name|
|
205
|
+
define_method(method_name) do |timeout: nil|
|
206
|
+
@frame.public_send(method_name, @selector, strict: true, timeout: timeout)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def press(key, delay: nil, noWaitAfter: nil, timeout: nil)
|
211
|
+
@frame.press(@selector, key, strict: true, noWaitAfter: noWaitAfter, timeout: timeout)
|
212
|
+
end
|
213
|
+
|
214
|
+
def screenshot(
|
215
|
+
omitBackground: nil,
|
216
|
+
path: nil,
|
217
|
+
quality: nil,
|
218
|
+
timeout: nil,
|
219
|
+
type: nil)
|
220
|
+
with_element(timeout: timeout) do |handle, options|
|
221
|
+
handle.screenshot(
|
222
|
+
omitBackground: omitBackground,
|
223
|
+
path: path,
|
224
|
+
quality: quality,
|
225
|
+
timeout: options[:timeout],
|
226
|
+
type: type)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def scroll_into_view_if_needed(timeout: nil)
|
231
|
+
with_element(timeout: timeout) do |handle, options|
|
232
|
+
handle.scroll_into_view_if_needed(timeout: options[:timeout])
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def select_option(
|
237
|
+
element: nil,
|
238
|
+
index: nil,
|
239
|
+
value: nil,
|
240
|
+
label: nil,
|
241
|
+
force: nil,
|
242
|
+
noWaitAfter: nil,
|
243
|
+
timeout: nil)
|
244
|
+
|
245
|
+
@frame.select_option(@selector,
|
246
|
+
strict: true,
|
247
|
+
element: element,
|
248
|
+
index: index,
|
249
|
+
value: value,
|
250
|
+
label: label,
|
251
|
+
force: force,
|
252
|
+
noWaitAfter: noWaitAfter,
|
253
|
+
timeout: timeout)
|
254
|
+
end
|
255
|
+
|
256
|
+
def select_text(force: nil, timeout: nil)
|
257
|
+
with_element(timeout: timeout) do |handle, options|
|
258
|
+
handle.select_text(force: force, timeout: options[:timeout])
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def set_input_files(files, noWaitAfter: nil, timeout: nil)
|
263
|
+
@frame.set_input_files(@selector, files, strict: true, noWaitAfter: noWaitAfter, timeout: timeout)
|
264
|
+
end
|
265
|
+
|
266
|
+
def tap_point(
|
267
|
+
force: nil,
|
268
|
+
modifiers: nil,
|
269
|
+
noWaitAfter: nil,
|
270
|
+
position: nil,
|
271
|
+
timeout: nil,
|
272
|
+
trial: nil)
|
273
|
+
@frame.tap_point(@selector,
|
274
|
+
strict: true,
|
275
|
+
force: force,
|
276
|
+
modifiers: modifiers,
|
277
|
+
noWaitAfter: noWaitAfter,
|
278
|
+
position: position,
|
279
|
+
timeout: timeout,
|
280
|
+
trial: trial)
|
281
|
+
end
|
282
|
+
|
283
|
+
def text_content(timeout: nil)
|
284
|
+
@frame.text_content(@selector, strict: true, timeout: timeout)
|
285
|
+
end
|
286
|
+
|
287
|
+
def type(text, delay: nil, noWaitAfter: nil, timeout: nil)
|
288
|
+
@frame.type(@selector, text, strict: true, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout)
|
289
|
+
end
|
290
|
+
|
291
|
+
def uncheck(
|
292
|
+
force: nil,
|
293
|
+
noWaitAfter: nil,
|
294
|
+
position: nil,
|
295
|
+
timeout: nil,
|
296
|
+
trial: nil)
|
297
|
+
@frame.uncheck(@selector,
|
298
|
+
strict: true,
|
299
|
+
force: force,
|
300
|
+
noWaitAfter: noWaitAfter,
|
301
|
+
position: position,
|
302
|
+
timeout: timeout,
|
303
|
+
trial: trial)
|
304
|
+
end
|
305
|
+
|
306
|
+
def all_inner_texts
|
307
|
+
@frame.eval_on_selector_all(@selector, 'ee => ee.map(e => e.innerText)')
|
308
|
+
end
|
309
|
+
|
310
|
+
def all_text_contents
|
311
|
+
@frame.eval_on_selector_all(@selector, "ee => ee.map(e => e.textContent || '')")
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module Playwright
|
2
2
|
class RouteHandlerEntry
|
3
3
|
# @param url [String]
|
4
|
+
# @param base_url [String|nil]
|
4
5
|
# @param handler [Proc]
|
5
|
-
def initialize(url, handler)
|
6
|
+
def initialize(url, base_url, handler)
|
6
7
|
@url_value = url
|
7
|
-
@url_matcher = UrlMatcher.new(url)
|
8
|
+
@url_matcher = UrlMatcher.new(url, base_url: base_url)
|
8
9
|
@handler = handler
|
9
10
|
end
|
10
11
|
|
@@ -8,12 +8,12 @@ module Playwright
|
|
8
8
|
|
9
9
|
attr_writer :default_timeout, :default_navigation_timeout
|
10
10
|
|
11
|
-
def navigation_timeout
|
12
|
-
@default_navigation_timeout || @default_timeout || @parent&.navigation_timeout || DEFAULT_TIMEOUT
|
11
|
+
def navigation_timeout(timeout_override = nil)
|
12
|
+
timeout_override || @default_navigation_timeout || @default_timeout || @parent&.navigation_timeout || DEFAULT_TIMEOUT
|
13
13
|
end
|
14
14
|
|
15
|
-
def timeout
|
16
|
-
@default_timeout || @parent&.timeout || DEFAULT_TIMEOUT
|
15
|
+
def timeout(timeout_override = nil)
|
16
|
+
timeout_override || @default_timeout || @parent&.timeout || DEFAULT_TIMEOUT
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
data/lib/playwright/transport.rb
CHANGED
@@ -8,7 +8,6 @@ module Playwright
|
|
8
8
|
# ref: https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_transport.py
|
9
9
|
class Transport
|
10
10
|
# @param playwright_cli_executable_path [String] path to playwright-cli.
|
11
|
-
# @param debug [Boolean]
|
12
11
|
def initialize(playwright_cli_executable_path:)
|
13
12
|
@driver_executable_path = playwright_cli_executable_path
|
14
13
|
@debug = ENV['DEBUG'].to_s == 'true' || ENV['DEBUG'].to_s == '1'
|
@@ -1,19 +1,29 @@
|
|
1
1
|
module Playwright
|
2
2
|
class UrlMatcher
|
3
3
|
# @param url [String|Regexp]
|
4
|
-
|
4
|
+
# @param base_url [String|nil]
|
5
|
+
def initialize(url, base_url:)
|
5
6
|
@url = url
|
7
|
+
@base_url = base_url
|
6
8
|
end
|
7
9
|
|
8
10
|
def match?(target_url)
|
9
11
|
case @url
|
10
12
|
when String
|
11
|
-
|
13
|
+
joined_url == target_url || File.fnmatch?(@url, target_url)
|
12
14
|
when Regexp
|
13
15
|
@url.match?(target_url)
|
14
16
|
else
|
15
17
|
false
|
16
18
|
end
|
17
19
|
end
|
20
|
+
|
21
|
+
private def joined_url
|
22
|
+
if @base_url && !@url.start_with?('*')
|
23
|
+
URI.join(@base_url, @url).to_s
|
24
|
+
else
|
25
|
+
@url
|
26
|
+
end
|
27
|
+
end
|
18
28
|
end
|
19
29
|
end
|
data/lib/playwright/version.rb
CHANGED
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'websocket/driver'
|
6
|
+
rescue LoadError
|
7
|
+
raise "websocket-driver is required. Add `gem 'websocket-driver'` to your Gemfile"
|
8
|
+
end
|
9
|
+
|
10
|
+
# ref: https://github.com/rails/rails/blob/master/actioncable/lib/action_cable/connection/client_socket.rb
|
11
|
+
# ref: https://github.com/cavalle/chrome_remote/blob/master/lib/chrome_remote/web_socket_client.rb
|
12
|
+
module Playwright
|
13
|
+
class WebSocketClient
|
14
|
+
class SecureSocketFactory
|
15
|
+
def initialize(host, port)
|
16
|
+
@host = host
|
17
|
+
@port = port || 443
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
tcp_socket = TCPSocket.new(@host, @port)
|
22
|
+
OpenSSL::SSL::SSLSocket.new(tcp_socket).tap(&:connect)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class DriverImpl # providing #url, #write(string)
|
27
|
+
def initialize(url)
|
28
|
+
@url = url
|
29
|
+
|
30
|
+
endpoint = URI.parse(url)
|
31
|
+
@socket =
|
32
|
+
if endpoint.scheme == 'wss'
|
33
|
+
SecureSocketFactory.new(endpoint.host, endpoint.port).create
|
34
|
+
else
|
35
|
+
TCPSocket.new(endpoint.host, endpoint.port)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :url
|
40
|
+
|
41
|
+
def write(data)
|
42
|
+
@socket.write(data)
|
43
|
+
rescue Errno::EPIPE
|
44
|
+
raise EOFError.new('already closed')
|
45
|
+
rescue Errno::ECONNRESET
|
46
|
+
raise EOFError.new('closed by remote')
|
47
|
+
end
|
48
|
+
|
49
|
+
def readpartial(maxlen = 1024)
|
50
|
+
@socket.readpartial(maxlen)
|
51
|
+
rescue Errno::ECONNRESET
|
52
|
+
raise EOFError.new('closed by remote')
|
53
|
+
end
|
54
|
+
|
55
|
+
def disconnect
|
56
|
+
@socket.close
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
STATE_CONNECTING = 0
|
61
|
+
STATE_OPENED = 1
|
62
|
+
STATE_CLOSING = 2
|
63
|
+
STATE_CLOSED = 3
|
64
|
+
|
65
|
+
def initialize(url:, max_payload_size:)
|
66
|
+
@impl = DriverImpl.new(url)
|
67
|
+
@driver = ::WebSocket::Driver.client(@impl, max_length: max_payload_size)
|
68
|
+
|
69
|
+
setup
|
70
|
+
end
|
71
|
+
|
72
|
+
class TransportError < StandardError; end
|
73
|
+
|
74
|
+
private def setup
|
75
|
+
@ready_state = STATE_CONNECTING
|
76
|
+
@driver.on(:open) do
|
77
|
+
@ready_state = STATE_OPENED
|
78
|
+
handle_on_open
|
79
|
+
end
|
80
|
+
@driver.on(:close) do |event|
|
81
|
+
@ready_state = STATE_CLOSED
|
82
|
+
handle_on_close(reason: event.reason, code: event.code)
|
83
|
+
end
|
84
|
+
@driver.on(:error) do |event|
|
85
|
+
if !handle_on_error(error_message: event.message)
|
86
|
+
raise TransportError.new(event.message)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
@driver.on(:message) do |event|
|
90
|
+
handle_on_message(event.data)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private def wait_for_data
|
95
|
+
@driver.parse(@impl.readpartial)
|
96
|
+
end
|
97
|
+
|
98
|
+
def start
|
99
|
+
@driver.start
|
100
|
+
|
101
|
+
Thread.new do
|
102
|
+
wait_for_data until @ready_state >= STATE_CLOSING
|
103
|
+
rescue EOFError
|
104
|
+
# Google Chrome was gone.
|
105
|
+
# We have nothing todo. Just finish polling.
|
106
|
+
if @ready_state < STATE_CLOSING
|
107
|
+
handle_on_close(reason: 'Going Away', code: 1001)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# @param message [String]
|
113
|
+
def send_text(message)
|
114
|
+
return if @ready_state >= STATE_CLOSING
|
115
|
+
@driver.text(message)
|
116
|
+
end
|
117
|
+
|
118
|
+
def close(code: 1000, reason: "")
|
119
|
+
return if @ready_state >= STATE_CLOSING
|
120
|
+
@ready_state = STATE_CLOSING
|
121
|
+
@driver.close(reason, code)
|
122
|
+
end
|
123
|
+
|
124
|
+
def on_open(&block)
|
125
|
+
@on_open = block
|
126
|
+
end
|
127
|
+
|
128
|
+
# @param block [Proc(reason: String, code: Numeric)]
|
129
|
+
def on_close(&block)
|
130
|
+
@on_close = block
|
131
|
+
end
|
132
|
+
|
133
|
+
# @param block [Proc(error_message: String)]
|
134
|
+
def on_error(&block)
|
135
|
+
@on_error = block
|
136
|
+
end
|
137
|
+
|
138
|
+
def on_message(&block)
|
139
|
+
@on_message = block
|
140
|
+
end
|
141
|
+
|
142
|
+
private def handle_on_open
|
143
|
+
@on_open&.call
|
144
|
+
end
|
145
|
+
|
146
|
+
private def handle_on_close(reason:, code:)
|
147
|
+
@on_close&.call(reason, code)
|
148
|
+
@impl.disconnect
|
149
|
+
end
|
150
|
+
|
151
|
+
private def handle_on_error(error_message:)
|
152
|
+
return false if @on_error.nil?
|
153
|
+
|
154
|
+
@on_error.call(error_message)
|
155
|
+
true
|
156
|
+
end
|
157
|
+
|
158
|
+
private def handle_on_message(data)
|
159
|
+
return if @ready_state != STATE_OPENED
|
160
|
+
|
161
|
+
@on_message&.call(data)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|