playwright-ruby-client 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|