playwright-ruby-client 0.2.1 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
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)