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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +17 -5
  3. data/docs/api_coverage.md +116 -74
  4. data/lib/playwright.rb +48 -9
  5. data/lib/playwright/channel.rb +12 -2
  6. data/lib/playwright/channel_owners/artifact.rb +30 -0
  7. data/lib/playwright/channel_owners/binding_call.rb +3 -0
  8. data/lib/playwright/channel_owners/browser.rb +21 -0
  9. data/lib/playwright/channel_owners/browser_context.rb +154 -3
  10. data/lib/playwright/channel_owners/browser_type.rb +28 -0
  11. data/lib/playwright/channel_owners/dialog.rb +28 -0
  12. data/lib/playwright/channel_owners/element_handle.rb +7 -7
  13. data/lib/playwright/channel_owners/frame.rb +26 -5
  14. data/lib/playwright/channel_owners/js_handle.rb +2 -2
  15. data/lib/playwright/channel_owners/page.rb +125 -25
  16. data/lib/playwright/channel_owners/playwright.rb +24 -27
  17. data/lib/playwright/channel_owners/request.rb +26 -2
  18. data/lib/playwright/channel_owners/response.rb +60 -0
  19. data/lib/playwright/channel_owners/route.rb +78 -0
  20. data/lib/playwright/channel_owners/selectors.rb +19 -1
  21. data/lib/playwright/channel_owners/stream.rb +15 -0
  22. data/lib/playwright/connection.rb +11 -32
  23. data/lib/playwright/download.rb +27 -0
  24. data/lib/playwright/errors.rb +6 -0
  25. data/lib/playwright/events.rb +2 -5
  26. data/lib/playwright/keyboard_impl.rb +1 -1
  27. data/lib/playwright/mouse_impl.rb +41 -0
  28. data/lib/playwright/playwright_api.rb +3 -1
  29. data/lib/playwright/route_handler_entry.rb +28 -0
  30. data/lib/playwright/select_option_values.rb +31 -22
  31. data/lib/playwright/transport.rb +29 -7
  32. data/lib/playwright/url_matcher.rb +1 -1
  33. data/lib/playwright/utils.rb +9 -0
  34. data/lib/playwright/version.rb +1 -1
  35. data/lib/playwright/video.rb +51 -0
  36. data/lib/playwright/wait_helper.rb +2 -2
  37. data/lib/playwright_api/accessibility.rb +39 -1
  38. data/lib/playwright_api/android.rb +74 -2
  39. data/lib/playwright_api/android_device.rb +141 -23
  40. data/lib/playwright_api/android_input.rb +17 -13
  41. data/lib/playwright_api/android_socket.rb +16 -0
  42. data/lib/playwright_api/android_web_view.rb +21 -0
  43. data/lib/playwright_api/browser.rb +77 -2
  44. data/lib/playwright_api/browser_context.rb +178 -25
  45. data/lib/playwright_api/browser_type.rb +40 -9
  46. data/lib/playwright_api/dialog.rb +54 -7
  47. data/lib/playwright_api/element_handle.rb +105 -31
  48. data/lib/playwright_api/file_chooser.rb +6 -1
  49. data/lib/playwright_api/frame.rb +229 -36
  50. data/lib/playwright_api/js_handle.rb +23 -0
  51. data/lib/playwright_api/keyboard.rb +48 -1
  52. data/lib/playwright_api/mouse.rb +26 -5
  53. data/lib/playwright_api/page.rb +491 -81
  54. data/lib/playwright_api/playwright.rb +21 -4
  55. data/lib/playwright_api/request.rb +30 -2
  56. data/lib/playwright_api/response.rb +21 -11
  57. data/lib/playwright_api/route.rb +51 -5
  58. data/lib/playwright_api/selectors.rb +27 -1
  59. data/lib/playwright_api/touchscreen.rb +1 -1
  60. data/lib/playwright_api/worker.rb +25 -1
  61. data/playwright.gemspec +4 -2
  62. metadata +42 -14
  63. data/lib/playwright/channel_owners/chromium_browser.rb +0 -8
  64. data/lib/playwright/channel_owners/chromium_browser_context.rb +0 -8
  65. data/lib/playwright/channel_owners/download.rb +0 -27
  66. data/lib/playwright/channel_owners/firefox_browser.rb +0 -8
  67. data/lib/playwright/channel_owners/webkit_browser.rb +0 -8
  68. data/lib/playwright_api/binding_call.rb +0 -27
  69. data/lib/playwright_api/chromium_browser_context.rb +0 -59
  70. data/lib/playwright_api/download.rb +0 -95
  71. 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
@@ -1,4 +1,7 @@
1
1
  module Playwright
2
2
  define_channel_owner :BindingCall do
3
+ def name
4
+ @initializer['name']
5
+ end
3
6
  end
4
7
  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
- attr_writer :browser, :owner_page, :options
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
- # @routes.each ...
25
- route.continue_
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 + { noWaitAfter: noWaitAfter, timeout: timeout }.compact
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 + { selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact
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(selector, force: nil, noWaitAfter: nil, timeout: nil)
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(selector, force: nil, noWaitAfter: nil, timeout: nil)
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)