playwright-ruby-client 0.2.1 → 0.5.5
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 +17 -5
- data/docs/api_coverage.md +116 -74
- data/lib/playwright.rb +48 -9
- data/lib/playwright/channel.rb +12 -2
- data/lib/playwright/channel_owners/artifact.rb +30 -0
- data/lib/playwright/channel_owners/binding_call.rb +3 -0
- data/lib/playwright/channel_owners/browser.rb +21 -0
- data/lib/playwright/channel_owners/browser_context.rb +154 -3
- data/lib/playwright/channel_owners/browser_type.rb +28 -0
- data/lib/playwright/channel_owners/dialog.rb +28 -0
- data/lib/playwright/channel_owners/element_handle.rb +7 -7
- data/lib/playwright/channel_owners/frame.rb +26 -5
- data/lib/playwright/channel_owners/js_handle.rb +2 -2
- data/lib/playwright/channel_owners/page.rb +125 -25
- data/lib/playwright/channel_owners/playwright.rb +24 -27
- data/lib/playwright/channel_owners/request.rb +26 -2
- data/lib/playwright/channel_owners/response.rb +60 -0
- data/lib/playwright/channel_owners/route.rb +78 -0
- data/lib/playwright/channel_owners/selectors.rb +19 -1
- data/lib/playwright/channel_owners/stream.rb +15 -0
- data/lib/playwright/connection.rb +11 -32
- data/lib/playwright/download.rb +27 -0
- data/lib/playwright/errors.rb +6 -0
- data/lib/playwright/events.rb +2 -5
- data/lib/playwright/keyboard_impl.rb +1 -1
- data/lib/playwright/mouse_impl.rb +41 -0
- data/lib/playwright/playwright_api.rb +3 -1
- data/lib/playwright/route_handler_entry.rb +28 -0
- data/lib/playwright/select_option_values.rb +31 -22
- data/lib/playwright/transport.rb +29 -7
- data/lib/playwright/url_matcher.rb +1 -1
- data/lib/playwright/utils.rb +9 -0
- data/lib/playwright/version.rb +1 -1
- data/lib/playwright/video.rb +51 -0
- data/lib/playwright/wait_helper.rb +2 -2
- data/lib/playwright_api/accessibility.rb +39 -1
- data/lib/playwright_api/android.rb +74 -2
- data/lib/playwright_api/android_device.rb +141 -23
- data/lib/playwright_api/android_input.rb +17 -13
- data/lib/playwright_api/android_socket.rb +16 -0
- data/lib/playwright_api/android_web_view.rb +21 -0
- data/lib/playwright_api/browser.rb +77 -2
- data/lib/playwright_api/browser_context.rb +178 -25
- data/lib/playwright_api/browser_type.rb +40 -9
- data/lib/playwright_api/dialog.rb +54 -7
- data/lib/playwright_api/element_handle.rb +105 -31
- data/lib/playwright_api/file_chooser.rb +6 -1
- data/lib/playwright_api/frame.rb +229 -36
- data/lib/playwright_api/js_handle.rb +23 -0
- data/lib/playwright_api/keyboard.rb +48 -1
- data/lib/playwright_api/mouse.rb +26 -5
- data/lib/playwright_api/page.rb +491 -81
- data/lib/playwright_api/playwright.rb +21 -4
- data/lib/playwright_api/request.rb +30 -2
- data/lib/playwright_api/response.rb +21 -11
- data/lib/playwright_api/route.rb +51 -5
- data/lib/playwright_api/selectors.rb +27 -1
- data/lib/playwright_api/touchscreen.rb +1 -1
- data/lib/playwright_api/worker.rb +25 -1
- data/playwright.gemspec +4 -2
- metadata +42 -14
- data/lib/playwright/channel_owners/chromium_browser.rb +0 -8
- data/lib/playwright/channel_owners/chromium_browser_context.rb +0 -8
- data/lib/playwright/channel_owners/download.rb +0 -27
- data/lib/playwright/channel_owners/firefox_browser.rb +0 -8
- data/lib/playwright/channel_owners/webkit_browser.rb +0 -8
- data/lib/playwright_api/binding_call.rb +0 -27
- data/lib/playwright_api/chromium_browser_context.rb +0 -59
- data/lib/playwright_api/download.rb +0 -95
- data/lib/playwright_api/video.rb +0 -24
@@ -0,0 +1,30 @@
|
|
1
|
+
module Playwright
|
2
|
+
define_channel_owner :Artifact do
|
3
|
+
private def after_initialize
|
4
|
+
@is_remote = false
|
5
|
+
@absolute_path = @initializer['absolutePath']
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :absolute_path
|
9
|
+
|
10
|
+
def path_after_finished
|
11
|
+
if @is_remote
|
12
|
+
raise "Path is not available when using browser_type.connect(). Use save_as() to save a local copy."
|
13
|
+
end
|
14
|
+
@channel.send_message_to_server('pathAfterFinished')
|
15
|
+
end
|
16
|
+
|
17
|
+
def save_as(path)
|
18
|
+
stream = ChannelOwners::Stream.from(@channel.send_message_to_server('saveAsStream'))
|
19
|
+
stream.save_as(path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def failure
|
23
|
+
@channel.send_message_to_server('failure')
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete
|
27
|
+
@channel.send_message_to_server('delete')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -59,12 +59,33 @@ module Playwright
|
|
59
59
|
@initializer['version']
|
60
60
|
end
|
61
61
|
|
62
|
+
def start_tracing(page: nil, categories: nil, path: nil, screenshots: nil)
|
63
|
+
params = {
|
64
|
+
page: page&.channel,
|
65
|
+
categories: categories,
|
66
|
+
path: path,
|
67
|
+
screenshots: screenshots,
|
68
|
+
}.compact
|
69
|
+
|
70
|
+
@channel.send_message_to_server('startTracing', params)
|
71
|
+
end
|
72
|
+
|
73
|
+
def stop_tracing
|
74
|
+
encoded_binary = @channel.send_message_to_server("stopTracing")
|
75
|
+
return Base64.strict_decode64(encoded_binary)
|
76
|
+
end
|
77
|
+
|
62
78
|
private def on_close(_ = {})
|
63
79
|
@connected = false
|
64
80
|
emit(Events::Browser::Disconnected)
|
65
81
|
@closed_or_closing = false
|
66
82
|
end
|
67
83
|
|
84
|
+
# called from BrowserType#connectOverCDP
|
85
|
+
private def add_context(context)
|
86
|
+
@contexts << context
|
87
|
+
end
|
88
|
+
|
68
89
|
# called from BrowserContext#on_close with send(:remove_context), so keep private.
|
69
90
|
private def remove_context(context)
|
70
91
|
@contexts.delete(context)
|
@@ -2,11 +2,16 @@ module Playwright
|
|
2
2
|
# @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_browser_context.py
|
3
3
|
define_channel_owner :BrowserContext do
|
4
4
|
include Utils::Errors::SafeCloseError
|
5
|
-
|
5
|
+
attr_accessor :browser
|
6
|
+
attr_writer :owner_page, :options
|
6
7
|
|
7
8
|
private def after_initialize
|
8
9
|
@pages = Set.new
|
10
|
+
@routes = Set.new
|
11
|
+
@bindings = {}
|
12
|
+
@timeout_settings = TimeoutSettings.new
|
9
13
|
|
14
|
+
@channel.on('bindingCall', ->(params) { on_binding(ChannelOwners::BindingCall.from(params['binding'])) })
|
10
15
|
@channel.once('close', ->(_) { on_close })
|
11
16
|
@channel.on('page', ->(params) { on_page(ChannelOwners::Page.from(params['page']) )})
|
12
17
|
@channel.on('route', ->(params) {
|
@@ -18,11 +23,36 @@ module Playwright
|
|
18
23
|
page.send(:update_browser_context, self)
|
19
24
|
@pages << page
|
20
25
|
emit(Events::BrowserContext::Page, page)
|
26
|
+
page.send(:emit_popup_event_from_browser_context)
|
21
27
|
end
|
22
28
|
|
23
29
|
private def on_route(route, request)
|
24
|
-
#
|
25
|
-
|
30
|
+
# It is not desired to use PlaywrightApi.wrap directly.
|
31
|
+
# However it is a little difficult to define wrapper for `handler` parameter in generate_api.
|
32
|
+
# Just a workaround...
|
33
|
+
wrapped_route = PlaywrightApi.wrap(route)
|
34
|
+
wrapped_request = PlaywrightApi.wrap(request)
|
35
|
+
|
36
|
+
if @routes.none? { |handler_entry| handler_entry.handle(wrapped_route, wrapped_request) }
|
37
|
+
route.continue
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private def on_binding(binding_call)
|
42
|
+
func = @binding[binding_call.name]
|
43
|
+
if func
|
44
|
+
binding_call.call(func)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_default_navigation_timeout(timeout)
|
49
|
+
@timeout_settings.default_navigation_timeout = timeout
|
50
|
+
@channel.send_message_to_server('setDefaultNavigationTimeoutNoReply', timeout: timeout)
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_default_timeout(timeout)
|
54
|
+
@timeout_settings.default_timeout = timeout
|
55
|
+
@channel.send_message_to_server('setDefaultTimeoutNoReply', timeout: timeout)
|
26
56
|
end
|
27
57
|
|
28
58
|
def pages
|
@@ -36,6 +66,111 @@ module Playwright
|
|
36
66
|
ChannelOwners::Page.from(resp)
|
37
67
|
end
|
38
68
|
|
69
|
+
def cookies(urls: nil)
|
70
|
+
target_urls =
|
71
|
+
if urls.nil?
|
72
|
+
[]
|
73
|
+
elsif urls.is_a?(Enumerable)
|
74
|
+
urls
|
75
|
+
else
|
76
|
+
[urls]
|
77
|
+
end
|
78
|
+
@channel.send_message_to_server('cookies', urls: urls)
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_cookies(cookies)
|
82
|
+
@channel.send_message_to_server('addCookies', cookies: cookies)
|
83
|
+
end
|
84
|
+
|
85
|
+
def clear_cookies
|
86
|
+
@channel.send_message_to_server('clearCookies')
|
87
|
+
end
|
88
|
+
|
89
|
+
def grant_permissions(permissions, origin: nil)
|
90
|
+
params = {
|
91
|
+
permissions: permissions,
|
92
|
+
origin: origin,
|
93
|
+
}.compact
|
94
|
+
@channel.send_message_to_server('grantPermissions', params)
|
95
|
+
end
|
96
|
+
|
97
|
+
def clear_permissions
|
98
|
+
@channel.send_message_to_server('clearPermissions')
|
99
|
+
end
|
100
|
+
|
101
|
+
def set_geolocation(geolocation)
|
102
|
+
@channel.send_message_to_server('setGeolocation', geolocation: geolocation)
|
103
|
+
end
|
104
|
+
|
105
|
+
def set_extra_http_headers(headers)
|
106
|
+
@channel.send_message_to_server('setExtraHTTPHeaders',
|
107
|
+
headers: HttpHeaders.new(headers).as_serialized)
|
108
|
+
end
|
109
|
+
|
110
|
+
def set_offline(offline)
|
111
|
+
@channel.send_message_to_server('setOffline', offline: offline)
|
112
|
+
end
|
113
|
+
|
114
|
+
def add_init_script(path: nil, script: nil)
|
115
|
+
source =
|
116
|
+
if path
|
117
|
+
File.read(path)
|
118
|
+
elsif script
|
119
|
+
script
|
120
|
+
else
|
121
|
+
raise ArgumentError.new('Either path or script parameter must be specified')
|
122
|
+
end
|
123
|
+
|
124
|
+
@channel.send_message_to_server('addInitScript', source: script)
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
|
128
|
+
def expose_binding(name, callback, handle: nil)
|
129
|
+
if @pages.any? { |page| page.send(:has_bindings?, name) }
|
130
|
+
raise ArgumentError.new("Function \"#{name}\" has been already registered in one of the pages")
|
131
|
+
end
|
132
|
+
if @bindings.key?(name)
|
133
|
+
raise ArgumentError.new("Function \"#{name}\" has been already registered")
|
134
|
+
end
|
135
|
+
params = {
|
136
|
+
name: name,
|
137
|
+
needsHandle: handle,
|
138
|
+
}.compact
|
139
|
+
@bindings[name] = callback
|
140
|
+
@channel.send_message_to_server('exposeBinding', params)
|
141
|
+
end
|
142
|
+
|
143
|
+
def expose_function(name, callback)
|
144
|
+
expose_binding(name, ->(_source, *args) { callback.call(*args) }, )
|
145
|
+
end
|
146
|
+
|
147
|
+
def route(url, handler)
|
148
|
+
entry = RouteHandlerEntry.new(url, handler)
|
149
|
+
@routes << entry
|
150
|
+
if @routes.count >= 1
|
151
|
+
@channel.send_message_to_server('setNetworkInterceptionEnabled', enabled: true)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def unroute(url, handler: nil)
|
156
|
+
@routes.reject! do |handler_entry|
|
157
|
+
handler_entry.same_value?(url: url, handler: handler)
|
158
|
+
end
|
159
|
+
if @routes.count == 0
|
160
|
+
@channel.send_message_to_server('setNetworkInterceptionEnabled', enabled: false)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def expect_event(event, predicate: nil, timeout: nil, &block)
|
165
|
+
wait_helper = WaitHelper.new
|
166
|
+
wait_helper.reject_on_timeout(timeout || @timeout_settings.timeout, "Timeout while waiting for event \"#{event}\"")
|
167
|
+
wait_helper.wait_for_event(self, event, predicate: predicate)
|
168
|
+
|
169
|
+
block&.call
|
170
|
+
|
171
|
+
wait_helper.promise.value!
|
172
|
+
end
|
173
|
+
|
39
174
|
private def on_close
|
40
175
|
@closed_or_closing = true
|
41
176
|
@browser&.send(:remove_context, self)
|
@@ -51,6 +186,18 @@ module Playwright
|
|
51
186
|
raise unless safe_close_error?(err)
|
52
187
|
end
|
53
188
|
|
189
|
+
def pause
|
190
|
+
@channel.send_message_to_server('pause')
|
191
|
+
end
|
192
|
+
|
193
|
+
def expect_page(predicate: nil, timeout: nil)
|
194
|
+
params = {
|
195
|
+
predicate: predicate,
|
196
|
+
timeout: timeout,
|
197
|
+
}.compact
|
198
|
+
expect_event(Events::BrowserContext::Page, params)
|
199
|
+
end
|
200
|
+
|
54
201
|
# called from Page#on_close with send(:remove_page, page), so keep private
|
55
202
|
private def remove_page(page)
|
56
203
|
@pages.delete(page)
|
@@ -60,5 +207,9 @@ module Playwright
|
|
60
207
|
private def _timeout_settings
|
61
208
|
@timeout_settings
|
62
209
|
end
|
210
|
+
|
211
|
+
private def has_record_video_option?
|
212
|
+
@options.key?(:recordVideo)
|
213
|
+
end
|
63
214
|
end
|
64
215
|
end
|
@@ -22,5 +22,33 @@ module Playwright
|
|
22
22
|
browser
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
def connect_over_cdp(endpointURL, slowMo: nil, timeout: nil, &block)
|
27
|
+
raise 'Connecting over CDP is only supported in Chromium.' unless name == 'chromium'
|
28
|
+
|
29
|
+
params = {
|
30
|
+
sdkLanguage: 'ruby',
|
31
|
+
endpointURL: endpointURL,
|
32
|
+
slowMo: slowMo,
|
33
|
+
timeout: timeout,
|
34
|
+
}.compact
|
35
|
+
result = @channel.send_message_to_server_result('connectOverCDP', params)
|
36
|
+
browser = ChannelOwners::Browser.from(result['browser'])
|
37
|
+
|
38
|
+
if result['defaultContext']
|
39
|
+
context = ChannelOwners::BrowserContext.from(result['defaultContext'])
|
40
|
+
browser.send(:add_context, context)
|
41
|
+
end
|
42
|
+
|
43
|
+
if block
|
44
|
+
begin
|
45
|
+
block.call(browser)
|
46
|
+
ensure
|
47
|
+
browser.close
|
48
|
+
end
|
49
|
+
else
|
50
|
+
browser
|
51
|
+
end
|
52
|
+
end
|
25
53
|
end
|
26
54
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Playwright
|
2
|
+
define_channel_owner :Dialog do
|
3
|
+
def type
|
4
|
+
@initializer['type']
|
5
|
+
end
|
6
|
+
|
7
|
+
def message
|
8
|
+
@initializer['message']
|
9
|
+
end
|
10
|
+
|
11
|
+
def default_value
|
12
|
+
@initializer['defaultValue']
|
13
|
+
end
|
14
|
+
|
15
|
+
def accept(promptText: nil)
|
16
|
+
accept_async(prompt_text: promptText).value!
|
17
|
+
end
|
18
|
+
|
19
|
+
def accept_async(promptText: nil)
|
20
|
+
params = { promptText: promptText }.compact
|
21
|
+
@channel.async_send_message_to_server('accept', params)
|
22
|
+
end
|
23
|
+
|
24
|
+
def dismiss
|
25
|
+
@channel.async_send_message_to_server('dismiss')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -82,7 +82,7 @@ module Playwright
|
|
82
82
|
modifiers: modifiers,
|
83
83
|
position: position,
|
84
84
|
timeout: timeout,
|
85
|
-
}
|
85
|
+
}.compact
|
86
86
|
@channel.send_message_to_server('hover', params)
|
87
87
|
|
88
88
|
nil
|
@@ -149,10 +149,8 @@ module Playwright
|
|
149
149
|
value: value,
|
150
150
|
label: label,
|
151
151
|
).as_params
|
152
|
-
params = base_params
|
152
|
+
params = base_params.merge({ noWaitAfter: noWaitAfter, timeout: timeout }.compact)
|
153
153
|
@channel.send_message_to_server('selectOption', params)
|
154
|
-
|
155
|
-
nil
|
156
154
|
end
|
157
155
|
|
158
156
|
def tap_point(
|
@@ -179,7 +177,7 @@ module Playwright
|
|
179
177
|
value: value,
|
180
178
|
noWaitAfter: noWaitAfter,
|
181
179
|
timeout: timeout,
|
182
|
-
}
|
180
|
+
}.compact
|
183
181
|
@channel.send_message_to_server('fill', params)
|
184
182
|
|
185
183
|
nil
|
@@ -230,10 +228,11 @@ module Playwright
|
|
230
228
|
nil
|
231
229
|
end
|
232
230
|
|
233
|
-
def check(force: nil, noWaitAfter: nil, timeout: nil)
|
231
|
+
def check(force: nil, noWaitAfter: nil, position: nil, timeout: nil)
|
234
232
|
params = {
|
235
233
|
force: force,
|
236
234
|
noWaitAfter: noWaitAfter,
|
235
|
+
position: position,
|
237
236
|
timeout: timeout,
|
238
237
|
}.compact
|
239
238
|
@channel.send_message_to_server('check', params)
|
@@ -241,10 +240,11 @@ module Playwright
|
|
241
240
|
nil
|
242
241
|
end
|
243
242
|
|
244
|
-
def uncheck(force: nil, noWaitAfter: nil, timeout: nil)
|
243
|
+
def uncheck(force: nil, noWaitAfter: nil, position: nil, timeout: nil)
|
245
244
|
params = {
|
246
245
|
force: force,
|
247
246
|
noWaitAfter: noWaitAfter,
|
247
|
+
position: position,
|
248
248
|
timeout: timeout,
|
249
249
|
}.compact
|
250
250
|
@channel.send_message_to_server('uncheck', params)
|
@@ -98,6 +98,15 @@ module Playwright
|
|
98
98
|
request&.response
|
99
99
|
end
|
100
100
|
|
101
|
+
def wait_for_url(url, timeout: nil, waitUntil: nil)
|
102
|
+
matcher = UrlMatcher.new(url)
|
103
|
+
if matcher.match?(@url)
|
104
|
+
wait_for_load_state(state: waitUntil, timeout: timeout)
|
105
|
+
else
|
106
|
+
expect_navigation(timeout: timeout, url: url, waitUntil: waitUntil)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
101
110
|
def wait_for_load_state(state: nil, timeout: nil)
|
102
111
|
option_state = state || 'load'
|
103
112
|
option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
|
@@ -412,10 +421,8 @@ module Playwright
|
|
412
421
|
value: value,
|
413
422
|
label: label,
|
414
423
|
).as_params
|
415
|
-
params = base_params
|
424
|
+
params = base_params.merge({ selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact)
|
416
425
|
@channel.send_message_to_server('selectOption', params)
|
417
|
-
|
418
|
-
nil
|
419
426
|
end
|
420
427
|
|
421
428
|
def set_input_files(selector, files, noWaitAfter: nil, timeout: nil)
|
@@ -464,11 +471,18 @@ module Playwright
|
|
464
471
|
nil
|
465
472
|
end
|
466
473
|
|
467
|
-
def check(
|
474
|
+
def check(
|
475
|
+
selector,
|
476
|
+
force: nil,
|
477
|
+
noWaitAfter: nil,
|
478
|
+
position: nil,
|
479
|
+
timeout: nil)
|
480
|
+
|
468
481
|
params = {
|
469
482
|
selector: selector,
|
470
483
|
force: force,
|
471
484
|
noWaitAfter: noWaitAfter,
|
485
|
+
position: position,
|
472
486
|
timeout: timeout,
|
473
487
|
}.compact
|
474
488
|
@channel.send_message_to_server('check', params)
|
@@ -476,11 +490,18 @@ module Playwright
|
|
476
490
|
nil
|
477
491
|
end
|
478
492
|
|
479
|
-
def uncheck(
|
493
|
+
def uncheck(
|
494
|
+
selector,
|
495
|
+
force: nil,
|
496
|
+
noWaitAfter: nil,
|
497
|
+
position: nil,
|
498
|
+
timeout: nil)
|
499
|
+
|
480
500
|
params = {
|
481
501
|
selector: selector,
|
482
502
|
force: force,
|
483
503
|
noWaitAfter: noWaitAfter,
|
504
|
+
position: position,
|
484
505
|
timeout: timeout,
|
485
506
|
}.compact
|
486
507
|
@channel.send_message_to_server('uncheck', params)
|