playwright-ruby-client 0.0.5 → 0.0.6
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/lib/playwright.rb +2 -0
- data/lib/playwright/channel_owners/element_handle.rb +75 -0
- data/lib/playwright/channel_owners/frame.rb +117 -0
- data/lib/playwright/channel_owners/page.rb +68 -11
- data/lib/playwright/channel_owners/request.rb +90 -0
- data/lib/playwright/input_type.rb +19 -0
- data/lib/playwright/input_types/keyboard.rb +32 -0
- data/lib/playwright/input_types/mouse.rb +4 -0
- data/lib/playwright/input_types/touchscreen.rb +4 -0
- data/lib/playwright/javascript/expression.rb +22 -0
- data/lib/playwright/javascript/function.rb +22 -0
- data/lib/playwright/playwright_api.rb +31 -23
- data/lib/playwright/timeout_settings.rb +1 -1
- data/lib/playwright/url_matcher.rb +19 -0
- data/lib/playwright/version.rb +1 -1
- data/lib/playwright_api/accessibility.rb +46 -6
- data/lib/playwright_api/binding_call.rb +6 -6
- data/lib/playwright_api/browser.rb +76 -16
- data/lib/playwright_api/browser_context.rb +284 -30
- data/lib/playwright_api/browser_type.rb +54 -9
- data/lib/playwright_api/cdp_session.rb +23 -1
- data/lib/playwright_api/chromium_browser_context.rb +16 -6
- data/lib/playwright_api/console_message.rb +10 -10
- data/lib/playwright_api/dialog.rb +42 -0
- data/lib/playwright_api/download.rb +19 -4
- data/lib/playwright_api/element_handle.rb +174 -21
- data/lib/playwright_api/file_chooser.rb +8 -0
- data/lib/playwright_api/frame.rb +355 -68
- data/lib/playwright_api/js_handle.rb +45 -9
- data/lib/playwright_api/keyboard.rb +98 -8
- data/lib/playwright_api/mouse.rb +22 -0
- data/lib/playwright_api/page.rb +779 -127
- data/lib/playwright_api/playwright.rb +98 -19
- data/lib/playwright_api/request.rb +70 -23
- data/lib/playwright_api/response.rb +6 -6
- data/lib/playwright_api/route.rb +48 -0
- data/lib/playwright_api/selectors.rb +14 -6
- data/lib/playwright_api/video.rb +8 -0
- data/lib/playwright_api/web_socket.rb +3 -5
- data/lib/playwright_api/worker.rb +12 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 243ef6d83fffde209cb8effe291c49b80fb90f7fd1fab7dacd451a524793f78a
|
4
|
+
data.tar.gz: 877db73a65179e2409423c73aa1fdf72b0da40ebe2d44bf22bce34533c438c98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01ea7a14404c07a54acd5112ad3704ce0d00ccd562061cbf46b0ec190c26ff0df850c613cddde6ff2d922de390063d6398a02a33f44fad88dd6d5d6ab1918893
|
7
|
+
data.tar.gz: 5e965b61082503371431d72a8b243c6212d5d2f3b6a232d62faf6fed2c85a920c041e259ea8f2d646722bf9e14eb4fcefebc9f40e86dca3a8ec2aa931c63d95b
|
data/lib/playwright.rb
CHANGED
@@ -15,9 +15,11 @@ require 'playwright/utils'
|
|
15
15
|
|
16
16
|
require 'playwright/channel'
|
17
17
|
require 'playwright/channel_owner'
|
18
|
+
require 'playwright/input_type'
|
18
19
|
require 'playwright/connection'
|
19
20
|
require 'playwright/timeout_settings'
|
20
21
|
require 'playwright/transport'
|
22
|
+
require 'playwright/url_matcher'
|
21
23
|
require 'playwright/version'
|
22
24
|
require 'playwright/wait_helper'
|
23
25
|
|
@@ -3,6 +3,81 @@ require_relative './js_handle'
|
|
3
3
|
module Playwright
|
4
4
|
module ChannelOwners
|
5
5
|
class ElementHandle < JSHandle
|
6
|
+
def click(
|
7
|
+
button: nil,
|
8
|
+
clickCount: nil,
|
9
|
+
delay: nil,
|
10
|
+
force: nil,
|
11
|
+
modifiers: nil,
|
12
|
+
noWaitAfter: nil,
|
13
|
+
position: nil,
|
14
|
+
timeout: nil)
|
15
|
+
|
16
|
+
params = {
|
17
|
+
button: button,
|
18
|
+
clickCount: clickCount,
|
19
|
+
delay: delay,
|
20
|
+
force: force,
|
21
|
+
modifiers: modifiers,
|
22
|
+
noWaitAfter: noWaitAfter,
|
23
|
+
position: position,
|
24
|
+
timeout: timeout,
|
25
|
+
}.compact
|
26
|
+
@channel.send_message_to_server('click', params)
|
27
|
+
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def type_text(text, delay: nil, noWaitAfter: nil, timeout: nil)
|
32
|
+
params = {
|
33
|
+
text: text,
|
34
|
+
delay: delay,
|
35
|
+
noWaitAfter: noWaitAfter,
|
36
|
+
timeout: timeout,
|
37
|
+
}.compact
|
38
|
+
@channel.send_message_to_server('type', params)
|
39
|
+
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def press(key, delay: nil, noWaitAfter: nil, timeout: nil)
|
44
|
+
params = {
|
45
|
+
key: key,
|
46
|
+
delay: delay,
|
47
|
+
noWaitAfter: noWaitAfter,
|
48
|
+
timeout: timeout,
|
49
|
+
}.compact
|
50
|
+
@channel.send_message_to_server('press', params)
|
51
|
+
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def query_selector(selector)
|
56
|
+
resp = @channel.send_message_to_server('querySelector', selector: selector)
|
57
|
+
ChannelOwners::ElementHandle.from_nullable(resp)
|
58
|
+
end
|
59
|
+
|
60
|
+
def query_selector_all(selector)
|
61
|
+
@channel.send_message_to_server('querySelectorAll', selector: selector).map do |el|
|
62
|
+
ChannelOwners::ElementHandle.from(el)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def eval_on_selector(selector, pageFunction, arg: nil)
|
67
|
+
if JavaScript.function?(pageFunction)
|
68
|
+
JavaScript::Function.new(pageFunction, arg).eval_on_selector(@channel, selector)
|
69
|
+
else
|
70
|
+
JavaScript::Expression.new(pageFunction).eval_on_selector(@channel, selector)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def eval_on_selector_all(selector, pageFunction, arg: nil)
|
75
|
+
if JavaScript.function?(pageFunction)
|
76
|
+
JavaScript::Function.new(pageFunction, arg).eval_on_selector_all(@channel, selector)
|
77
|
+
else
|
78
|
+
JavaScript::Expression.new(pageFunction).eval_on_selector_all(@channel, selector)
|
79
|
+
end
|
80
|
+
end
|
6
81
|
end
|
7
82
|
end
|
8
83
|
end
|
@@ -53,6 +53,69 @@ module Playwright
|
|
53
53
|
ChannelOwners::Response.from_nullable(resp)
|
54
54
|
end
|
55
55
|
|
56
|
+
private def setup_navigation_wait_helper(timeout:)
|
57
|
+
WaitHelper.new.tap do |helper|
|
58
|
+
helper.reject_on_event(@page, Events::Page::Close, AlreadyClosedError.new)
|
59
|
+
helper.reject_on_event(@page, Events::Page::Crash, CrashedError.new)
|
60
|
+
helper.reject_on_event(@page, Events::Page::FrameDetached, FrameAlreadyDetachedError.new)
|
61
|
+
helper.reject_on_timeout(timeout, "Timeout #{timeout}ms exceeded.")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def wait_for_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
|
66
|
+
option_wait_until = waitUntil || 'load'
|
67
|
+
option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
|
68
|
+
time_start = Time.now
|
69
|
+
|
70
|
+
wait_helper = setup_navigation_wait_helper(timeout: option_timeout)
|
71
|
+
|
72
|
+
predicate =
|
73
|
+
if url
|
74
|
+
matcher = UrlMatcher.new(url)
|
75
|
+
->(event) { event['error'] || matcher.match?(event['url']) }
|
76
|
+
else
|
77
|
+
->(_) { true }
|
78
|
+
end
|
79
|
+
|
80
|
+
wait_helper.wait_for_event(@event_emitter, 'navigated', predicate: predicate)
|
81
|
+
|
82
|
+
block&.call
|
83
|
+
|
84
|
+
event = wait_helper.promise.value!
|
85
|
+
if event['error']
|
86
|
+
raise event['error']
|
87
|
+
end
|
88
|
+
|
89
|
+
unless @load_states.include?(option_wait_until)
|
90
|
+
elapsed_time = Time.now - time_start
|
91
|
+
if elapsed_time < option_timeout
|
92
|
+
wait_for_load_state(state: option_wait_until, timeout: option_timeout - elapsed_time)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
request_json = event.dig('newDocument', 'request')
|
97
|
+
request = ChannelOwners::Request.from_nullable(request_json)
|
98
|
+
request&.response
|
99
|
+
end
|
100
|
+
|
101
|
+
def wait_for_load_state(state: nil, timeout: nil)
|
102
|
+
option_state = state || 'load'
|
103
|
+
unless %w(load domcontentloaded networkidle).include?(option_state)
|
104
|
+
raise ArgumentError.new('state: expected one of (load|domcontentloaded|networkidle)')
|
105
|
+
end
|
106
|
+
if @load_states.include?(option_state)
|
107
|
+
return
|
108
|
+
end
|
109
|
+
|
110
|
+
wait_helper = setup_navigation_wait_helper(timeout: timeout)
|
111
|
+
|
112
|
+
predicate = ->(state) { state == option_state }
|
113
|
+
wait_helper.wait_for_event(@event_emitter, 'loadstate', predicate: predicate)
|
114
|
+
wait_helper.promise.value!
|
115
|
+
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
|
56
119
|
def evaluate(pageFunction, arg: nil)
|
57
120
|
if JavaScript.function?(pageFunction)
|
58
121
|
JavaScript::Function.new(pageFunction, arg).evaluate(@channel)
|
@@ -69,6 +132,33 @@ module Playwright
|
|
69
132
|
end
|
70
133
|
end
|
71
134
|
|
135
|
+
def query_selector(selector)
|
136
|
+
resp = @channel.send_message_to_server('querySelector', selector: selector)
|
137
|
+
ChannelOwners::ElementHandle.from_nullable(resp)
|
138
|
+
end
|
139
|
+
|
140
|
+
def query_selector_all(selector)
|
141
|
+
@channel.send_message_to_server('querySelectorAll', selector: selector).map do |el|
|
142
|
+
ChannelOwners::ElementHandle.from(el)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def eval_on_selector(selector, pageFunction, arg: nil)
|
147
|
+
if JavaScript.function?(pageFunction)
|
148
|
+
JavaScript::Function.new(pageFunction, arg).eval_on_selector(@channel, selector)
|
149
|
+
else
|
150
|
+
JavaScript::Expression.new(pageFunction).eval_on_selector(@channel, selector)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def eval_on_selector_all(selector, pageFunction, arg: nil)
|
155
|
+
if JavaScript.function?(pageFunction)
|
156
|
+
JavaScript::Function.new(pageFunction, arg).eval_on_selector_all(@channel, selector)
|
157
|
+
else
|
158
|
+
JavaScript::Expression.new(pageFunction).eval_on_selector_all(@channel, selector)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
72
162
|
def content
|
73
163
|
@channel.send_message_to_server('content')
|
74
164
|
end
|
@@ -85,6 +175,33 @@ module Playwright
|
|
85
175
|
nil
|
86
176
|
end
|
87
177
|
|
178
|
+
def click(
|
179
|
+
selector,
|
180
|
+
button: nil,
|
181
|
+
clickCount: nil,
|
182
|
+
delay: nil,
|
183
|
+
force: nil,
|
184
|
+
modifiers: nil,
|
185
|
+
noWaitAfter: nil,
|
186
|
+
position: nil,
|
187
|
+
timeout: nil)
|
188
|
+
|
189
|
+
params = {
|
190
|
+
selector: selector,
|
191
|
+
button: button,
|
192
|
+
clickCount: clickCount,
|
193
|
+
delay: delay,
|
194
|
+
force: force,
|
195
|
+
modifiers: modifiers,
|
196
|
+
noWaitAfter: noWaitAfter,
|
197
|
+
position: position,
|
198
|
+
timeout: timeout,
|
199
|
+
}.compact
|
200
|
+
@channel.send_message_to_server('click', params)
|
201
|
+
|
202
|
+
nil
|
203
|
+
end
|
204
|
+
|
88
205
|
def focus(selector, timeout: nil)
|
89
206
|
params = { selector: selector, timeout: timeout }.compact
|
90
207
|
@channel.send_message_to_server('focus', params)
|
@@ -10,9 +10,9 @@ module Playwright
|
|
10
10
|
@browser_context = @parent
|
11
11
|
@timeout_settings = TimeoutSettings.new(@browser_context.send(:_timeout_settings))
|
12
12
|
@accessibility = Accessibility.new(@channel)
|
13
|
-
@keyboard = Keyboard.new(@channel)
|
14
|
-
@mouse = Mouse.new(@channel)
|
15
|
-
@touchscreen = Touchscreen.new(@channel)
|
13
|
+
@keyboard = InputTypes::Keyboard.new(@channel)
|
14
|
+
@mouse = InputTypes::Mouse.new(@channel)
|
15
|
+
@touchscreen = InputTypes::Touchscreen.new(@channel)
|
16
16
|
|
17
17
|
@viewport_size = @initializer['viewportSize']
|
18
18
|
@closed = false
|
@@ -103,6 +103,14 @@ module Playwright
|
|
103
103
|
@frames.to_a
|
104
104
|
end
|
105
105
|
|
106
|
+
def query_selector(selector)
|
107
|
+
@main_frame.query_selector(selector)
|
108
|
+
end
|
109
|
+
|
110
|
+
def query_selector_all(selector)
|
111
|
+
@main_frame.query_selector_all(selector)
|
112
|
+
end
|
113
|
+
|
106
114
|
def evaluate(pageFunction, arg: nil)
|
107
115
|
@main_frame.evaluate(pageFunction, arg: arg)
|
108
116
|
end
|
@@ -111,6 +119,14 @@ module Playwright
|
|
111
119
|
@main_frame.evaluate_handle(pageFunction, arg: arg)
|
112
120
|
end
|
113
121
|
|
122
|
+
def eval_on_selector(selector, pageFunction, arg: nil)
|
123
|
+
@main_frame.eval_on_selector(selector, pageFunction, arg: arg)
|
124
|
+
end
|
125
|
+
|
126
|
+
def eval_on_selector_all(selector, pageFunction, arg: nil)
|
127
|
+
@main_frame.eval_on_selector_all(selector, pageFunction, arg: arg)
|
128
|
+
end
|
129
|
+
|
114
130
|
def url
|
115
131
|
@main_frame.url
|
116
132
|
end
|
@@ -177,6 +193,30 @@ module Playwright
|
|
177
193
|
@closed
|
178
194
|
end
|
179
195
|
|
196
|
+
def click(
|
197
|
+
selector,
|
198
|
+
button: nil,
|
199
|
+
clickCount: nil,
|
200
|
+
delay: nil,
|
201
|
+
force: nil,
|
202
|
+
modifiers: nil,
|
203
|
+
noWaitAfter: nil,
|
204
|
+
position: nil,
|
205
|
+
timeout: nil)
|
206
|
+
|
207
|
+
@main_frame.click(
|
208
|
+
selector,
|
209
|
+
button: button,
|
210
|
+
clickCount: clickCount,
|
211
|
+
delay: delay,
|
212
|
+
force: force,
|
213
|
+
modifiers: modifiers,
|
214
|
+
noWaitAfter: noWaitAfter,
|
215
|
+
position: position,
|
216
|
+
timeout: timeout,
|
217
|
+
)
|
218
|
+
end
|
219
|
+
|
180
220
|
def focus(selector, timeout: nil)
|
181
221
|
@main_frame.focus(selector, timeout: timeout)
|
182
222
|
end
|
@@ -213,6 +253,12 @@ module Playwright
|
|
213
253
|
end
|
214
254
|
end
|
215
255
|
|
256
|
+
class FrameAlreadyDetachedError < StandardError
|
257
|
+
def initialize
|
258
|
+
super('Navigating frame was detached!')
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
216
262
|
def wait_for_event(event, optionsOrPredicate: nil, &block)
|
217
263
|
predicate, timeout =
|
218
264
|
case optionsOrPredicate
|
@@ -243,13 +289,20 @@ module Playwright
|
|
243
289
|
wait_helper.promise.value!
|
244
290
|
end
|
245
291
|
|
292
|
+
def wait_for_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
|
293
|
+
@main_frame.wait_for_navigation(
|
294
|
+
timeout: timeout,
|
295
|
+
url: url,
|
296
|
+
waitUntil: waitUntil,
|
297
|
+
&block)
|
298
|
+
end
|
299
|
+
|
246
300
|
def wait_for_request(urlOrPredicate, timeout: nil)
|
247
301
|
predicate =
|
248
302
|
case urlOrPredicate
|
249
|
-
when String
|
250
|
-
|
251
|
-
|
252
|
-
-> (req){ urlOrPredicate.match?(req.url) }
|
303
|
+
when String, Regexp
|
304
|
+
url_matcher = UrlMatcher.new(urlOrPredicate)
|
305
|
+
-> (req){ url_matcher.match?(req.url) }
|
253
306
|
when Proc
|
254
307
|
urlOrPredicate
|
255
308
|
else
|
@@ -262,10 +315,9 @@ module Playwright
|
|
262
315
|
def wait_for_response(urlOrPredicate, timeout: nil)
|
263
316
|
predicate =
|
264
317
|
case urlOrPredicate
|
265
|
-
when String
|
266
|
-
|
267
|
-
|
268
|
-
-> (res){ urlOrPredicate.match?(res.url) }
|
318
|
+
when String, Regexp
|
319
|
+
url_matcher = UrlMatcher.new(urlOrPredicate)
|
320
|
+
-> (req){ url_matcher.match?(req.url) }
|
269
321
|
when Proc
|
270
322
|
urlOrPredicate
|
271
323
|
else
|
@@ -280,5 +332,10 @@ module Playwright
|
|
280
332
|
@browser_context = context
|
281
333
|
@timeout_settings = TimeoutSettings.new(context.send(:_timeout_settings))
|
282
334
|
end
|
335
|
+
|
336
|
+
# called from Frame with send(:timeout_settings)
|
337
|
+
private def timeout_settings
|
338
|
+
@timeout_settings
|
339
|
+
end
|
283
340
|
end
|
284
341
|
end
|
@@ -1,5 +1,95 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
1
3
|
module Playwright
|
2
4
|
# @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_network.py
|
3
5
|
define_channel_owner :Request do
|
6
|
+
def after_initialize
|
7
|
+
@redirected_from = ChannelOwners::Request.from_nullable(@initializer['redirectedFrom'])
|
8
|
+
@redirected_from&.send(:update_redirected_to, self)
|
9
|
+
@timing = {
|
10
|
+
startTime: 0,
|
11
|
+
domainLookupStart: -1,
|
12
|
+
domainLookupEnd: -1,
|
13
|
+
connectStart: -1,
|
14
|
+
secureConnectionStart: -1,
|
15
|
+
connectEnd: -1,
|
16
|
+
requestStart: -1,
|
17
|
+
responseStart: -1,
|
18
|
+
responseEnd: -1,
|
19
|
+
}
|
20
|
+
@headers = parse_headers(@initializer['headers'])
|
21
|
+
end
|
22
|
+
|
23
|
+
def url
|
24
|
+
@initializer['url']
|
25
|
+
end
|
26
|
+
|
27
|
+
def resource_type
|
28
|
+
@initializer['resourceType']
|
29
|
+
end
|
30
|
+
|
31
|
+
def method
|
32
|
+
@initialize['method']
|
33
|
+
end
|
34
|
+
|
35
|
+
def post_data
|
36
|
+
post_data_buffer
|
37
|
+
end
|
38
|
+
|
39
|
+
def post_data_json
|
40
|
+
data = post_data
|
41
|
+
return unless data
|
42
|
+
|
43
|
+
content_type = @headers['content-type']
|
44
|
+
return unless content_type
|
45
|
+
|
46
|
+
if content_type == "application/x-www-form-urlencoded"
|
47
|
+
URI.decode_www_form(data).to_h
|
48
|
+
else
|
49
|
+
JSON.parse(data)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def post_data_buffer
|
54
|
+
base64_content = @initializer['postData']
|
55
|
+
if base64_content
|
56
|
+
Base64.strict_decode64(base64_content)
|
57
|
+
else
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def headers
|
63
|
+
@headers
|
64
|
+
end
|
65
|
+
|
66
|
+
def response
|
67
|
+
resp = @channel.send_message_to_server('response')
|
68
|
+
ChannelOwners::Response.from_nullable(resp)
|
69
|
+
end
|
70
|
+
|
71
|
+
def frame
|
72
|
+
@initializer['frame']
|
73
|
+
end
|
74
|
+
|
75
|
+
def navigation_request?
|
76
|
+
@initializer['isNavigationRequest']
|
77
|
+
end
|
78
|
+
|
79
|
+
def failure
|
80
|
+
@failure_text
|
81
|
+
end
|
82
|
+
|
83
|
+
attr_reader :headers, :redirected_from, :redirected_to, :timing
|
84
|
+
|
85
|
+
private def update_redirected_to(request)
|
86
|
+
@redirected_to = request
|
87
|
+
end
|
88
|
+
|
89
|
+
private def parse_headers(headers)
|
90
|
+
headers.map do |header|
|
91
|
+
[header['name'].downcase, header['value']]
|
92
|
+
end.to_h
|
93
|
+
end
|
4
94
|
end
|
5
95
|
end
|