playwright-ruby-client 0.0.6 → 0.2.0

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +117 -5
  3. data/docs/api_coverage.md +359 -0
  4. data/lib/playwright.rb +6 -2
  5. data/lib/playwright/android_input_impl.rb +23 -0
  6. data/lib/playwright/api_implementation.rb +18 -0
  7. data/lib/playwright/channel.rb +7 -0
  8. data/lib/playwright/channel_owner.rb +6 -7
  9. data/lib/playwright/channel_owners/android.rb +10 -1
  10. data/lib/playwright/channel_owners/android_device.rb +163 -0
  11. data/lib/playwright/channel_owners/browser.rb +14 -14
  12. data/lib/playwright/channel_owners/browser_context.rb +10 -2
  13. data/lib/playwright/channel_owners/download.rb +27 -0
  14. data/lib/playwright/channel_owners/element_handle.rb +243 -1
  15. data/lib/playwright/channel_owners/frame.rb +269 -22
  16. data/lib/playwright/channel_owners/js_handle.rb +51 -0
  17. data/lib/playwright/channel_owners/page.rb +379 -34
  18. data/lib/playwright/channel_owners/request.rb +9 -1
  19. data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
  20. data/lib/playwright/connection.rb +9 -6
  21. data/lib/playwright/errors.rb +2 -2
  22. data/lib/playwright/event_emitter.rb +8 -1
  23. data/lib/playwright/event_emitter_proxy.rb +49 -0
  24. data/lib/playwright/file_chooser_impl.rb +23 -0
  25. data/lib/playwright/http_headers.rb +20 -0
  26. data/lib/playwright/input_files.rb +42 -0
  27. data/lib/playwright/javascript/expression.rb +15 -0
  28. data/lib/playwright/javascript/function.rb +15 -0
  29. data/lib/playwright/javascript/value_parser.rb +1 -1
  30. data/lib/playwright/javascript/value_serializer.rb +11 -11
  31. data/lib/playwright/{input_types/keyboard.rb → keyboard_impl.rb} +6 -2
  32. data/lib/playwright/mouse_impl.rb +7 -0
  33. data/lib/playwright/playwright_api.rb +66 -19
  34. data/lib/playwright/select_option_values.rb +42 -0
  35. data/lib/playwright/timeout_settings.rb +1 -1
  36. data/lib/playwright/touchscreen_impl.rb +7 -0
  37. data/lib/playwright/utils.rb +18 -0
  38. data/lib/playwright/version.rb +1 -1
  39. data/lib/playwright/wait_helper.rb +1 -1
  40. data/lib/playwright_api/android.rb +32 -0
  41. data/lib/playwright_api/android_device.rb +77 -0
  42. data/lib/playwright_api/android_input.rb +25 -0
  43. data/lib/playwright_api/binding_call.rb +10 -6
  44. data/lib/playwright_api/browser.rb +22 -22
  45. data/lib/playwright_api/browser_context.rb +31 -22
  46. data/lib/playwright_api/browser_type.rb +16 -56
  47. data/lib/playwright_api/chromium_browser_context.rb +10 -8
  48. data/lib/playwright_api/console_message.rb +9 -10
  49. data/lib/playwright_api/dialog.rb +7 -3
  50. data/lib/playwright_api/download.rb +28 -11
  51. data/lib/playwright_api/element_handle.rb +139 -127
  52. data/lib/playwright_api/file_chooser.rb +17 -9
  53. data/lib/playwright_api/frame.rb +148 -148
  54. data/lib/playwright_api/js_handle.rb +26 -22
  55. data/lib/playwright_api/keyboard.rb +6 -6
  56. data/lib/playwright_api/page.rb +215 -179
  57. data/lib/playwright_api/playwright.rb +34 -46
  58. data/lib/playwright_api/request.rb +7 -8
  59. data/lib/playwright_api/response.rb +10 -6
  60. data/lib/playwright_api/selectors.rb +13 -9
  61. data/lib/playwright_api/web_socket.rb +10 -1
  62. data/lib/playwright_api/worker.rb +13 -13
  63. data/playwright.gemspec +1 -0
  64. metadata +32 -6
  65. data/lib/playwright/input_type.rb +0 -19
  66. data/lib/playwright/input_types/mouse.rb +0 -4
  67. data/lib/playwright/input_types/touchscreen.rb +0 -4
@@ -1,7 +1,7 @@
1
1
  module Playwright
2
2
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_frame.py
3
3
  define_channel_owner :Frame do
4
- def after_initialize
4
+ private def after_initialize
5
5
  if @initializer['parentFrame']
6
6
  @parent_frame = ChannelOwners::Frame.from(@initializer['parentFrame'])
7
7
  @parent_frame.send(:append_child_frame_from_child, self)
@@ -62,7 +62,7 @@ module Playwright
62
62
  end
63
63
  end
64
64
 
65
- def wait_for_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
65
+ def expect_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
66
66
  option_wait_until = waitUntil || 'load'
67
67
  option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
68
68
  time_start = Time.now
@@ -100,6 +100,7 @@ module Playwright
100
100
 
101
101
  def wait_for_load_state(state: nil, timeout: nil)
102
102
  option_state = state || 'load'
103
+ option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
103
104
  unless %w(load domcontentloaded networkidle).include?(option_state)
104
105
  raise ArgumentError.new('state: expected one of (load|domcontentloaded|networkidle)')
105
106
  end
@@ -107,7 +108,7 @@ module Playwright
107
108
  return
108
109
  end
109
110
 
110
- wait_helper = setup_navigation_wait_helper(timeout: timeout)
111
+ wait_helper = setup_navigation_wait_helper(timeout: option_timeout)
111
112
 
112
113
  predicate = ->(state) { state == option_state }
113
114
  wait_helper.wait_for_event(@event_emitter, 'loadstate', predicate: predicate)
@@ -143,6 +144,55 @@ module Playwright
143
144
  end
144
145
  end
145
146
 
147
+ def wait_for_selector(selector, state: nil, timeout: nil)
148
+ params = { selector: selector, state: state, timeout: timeout }.compact
149
+ resp = @channel.send_message_to_server('waitForSelector', params)
150
+
151
+ ChannelOwners::ElementHandle.from_nullable(resp)
152
+ end
153
+
154
+ def checked?(selector, timeout: nil)
155
+ params = { selector: selector, timeout: timeout }.compact
156
+ @channel.send_message_to_server('isChecked', params)
157
+ end
158
+
159
+ def disabled?(selector, timeout: nil)
160
+ params = { selector: selector, timeout: timeout }.compact
161
+ @channel.send_message_to_server('isDisabled', params)
162
+ end
163
+
164
+ def editable?(selector, timeout: nil)
165
+ params = { selector: selector, timeout: timeout }.compact
166
+ @channel.send_message_to_server('isEditable', params)
167
+ end
168
+
169
+ def enabled?(selector, timeout: nil)
170
+ params = { selector: selector, timeout: timeout }.compact
171
+ @channel.send_message_to_server('isEnabled', params)
172
+ end
173
+
174
+ def hidden?(selector, timeout: nil)
175
+ params = { selector: selector, timeout: timeout }.compact
176
+ @channel.send_message_to_server('isHidden', params)
177
+ end
178
+
179
+ def visible?(selector, timeout: nil)
180
+ params = { selector: selector, timeout: timeout }.compact
181
+ @channel.send_message_to_server('isVisible', params)
182
+ end
183
+
184
+ def dispatch_event(selector, type, eventInit: nil, timeout: nil)
185
+ params = {
186
+ selector: selector,
187
+ type: type,
188
+ eventInit: JavaScript::ValueSerializer.new(eventInit).serialize,
189
+ timeout: timeout,
190
+ }.compact
191
+ @channel.send_message_to_server('dispatchEvent', params)
192
+
193
+ nil
194
+ end
195
+
146
196
  def eval_on_selector(selector, pageFunction, arg: nil)
147
197
  if JavaScript.function?(pageFunction)
148
198
  JavaScript::Function.new(pageFunction, arg).eval_on_selector(@channel, selector)
@@ -175,6 +225,47 @@ module Playwright
175
225
  nil
176
226
  end
177
227
 
228
+ def name
229
+ @name || ''
230
+ end
231
+
232
+ def url
233
+ @url || ''
234
+ end
235
+
236
+ def child_frames
237
+ @child_frames.to_a
238
+ end
239
+
240
+ def detached?
241
+ @detached
242
+ end
243
+
244
+ def add_script_tag(content: nil, path: nil, type: nil, url: nil)
245
+ params = {
246
+ content: content,
247
+ type: type,
248
+ url: url,
249
+ }.compact
250
+ if path
251
+ params[:content] = "#{File.read(path)}\n//# sourceURL=#{path}"
252
+ end
253
+ resp = @channel.send_message_to_server('addScriptTag', params)
254
+ ChannelOwners::ElementHandle.from(resp)
255
+ end
256
+
257
+ def add_style_tag(content: nil, path: nil, url: nil)
258
+ params = {
259
+ content: content,
260
+ url: url,
261
+ }.compact
262
+ if path
263
+ params[:content] = "#{File.read(path)}\n/*# sourceURL=#{path}*/"
264
+ end
265
+ resp = @channel.send_message_to_server('addStyleTag', params)
266
+ ChannelOwners::ElementHandle.from(resp)
267
+ end
268
+
178
269
  def click(
179
270
  selector,
180
271
  button: nil,
@@ -202,18 +293,145 @@ module Playwright
202
293
  nil
203
294
  end
204
295
 
296
+ def dblclick(
297
+ selector,
298
+ button: nil,
299
+ delay: nil,
300
+ force: nil,
301
+ modifiers: nil,
302
+ noWaitAfter: nil,
303
+ position: nil,
304
+ timeout: nil)
305
+
306
+ params = {
307
+ selector: selector,
308
+ button: button,
309
+ delay: delay,
310
+ force: force,
311
+ modifiers: modifiers,
312
+ noWaitAfter: noWaitAfter,
313
+ position: position,
314
+ timeout: timeout,
315
+ }.compact
316
+ @channel.send_message_to_server('dblclick', params)
317
+
318
+ nil
319
+ end
320
+
321
+ def tap_point(
322
+ selector,
323
+ force: nil,
324
+ modifiers: nil,
325
+ noWaitAfter: nil,
326
+ position: nil,
327
+ timeout: nil)
328
+ params = {
329
+ selector: selector,
330
+ force: force,
331
+ modifiers: modifiers,
332
+ noWaitAfter: noWaitAfter,
333
+ position: position,
334
+ timeout: timeout,
335
+ }.compact
336
+ @channel.send_message_to_server('tap', params)
337
+
338
+ nil
339
+ end
340
+
341
+ def fill(selector, value, noWaitAfter: nil, timeout: nil)
342
+ params = {
343
+ selector: selector,
344
+ value: value,
345
+ noWaitAfter: noWaitAfter,
346
+ timeout: timeout,
347
+ }.compact
348
+ @channel.send_message_to_server('fill', params)
349
+
350
+ nil
351
+ end
352
+
205
353
  def focus(selector, timeout: nil)
206
354
  params = { selector: selector, timeout: timeout }.compact
207
355
  @channel.send_message_to_server('focus', params)
208
356
  nil
209
357
  end
210
358
 
211
- def type_text(
212
- selector,
213
- text,
214
- delay: nil,
215
- noWaitAfter: nil,
216
- timeout: nil)
359
+ def text_content(selector, timeout: nil)
360
+ params = { selector: selector, timeout: timeout }.compact
361
+ @channel.send_message_to_server('textContent', params)
362
+ end
363
+
364
+ def inner_text(selector, timeout: nil)
365
+ params = { selector: selector, timeout: timeout }.compact
366
+ @channel.send_message_to_server('innerText', params)
367
+ end
368
+
369
+ def inner_html(selector, timeout: nil)
370
+ params = { selector: selector, timeout: timeout }.compact
371
+ @channel.send_message_to_server('innerHTML', params)
372
+ end
373
+
374
+ def get_attribute(selector, name, timeout: nil)
375
+ params = {
376
+ selector: selector,
377
+ name: name,
378
+ timeout: timeout,
379
+ }.compact
380
+ @channel.send_message_to_server('getAttribute', params)
381
+ end
382
+
383
+ def hover(
384
+ selector,
385
+ force: nil,
386
+ modifiers: nil,
387
+ position: nil,
388
+ timeout: nil)
389
+ params = {
390
+ selector: selector,
391
+ force: force,
392
+ modifiers: modifiers,
393
+ position: position,
394
+ timeout: timeout,
395
+ }.compact
396
+ @channel.send_message_to_server('hover', params)
397
+
398
+ nil
399
+ end
400
+
401
+ def select_option(
402
+ selector,
403
+ element: nil,
404
+ index: nil,
405
+ value: nil,
406
+ label: nil,
407
+ noWaitAfter: nil,
408
+ timeout: nil)
409
+ base_params = SelectOptionValues.new(
410
+ element: element,
411
+ index: index,
412
+ value: value,
413
+ label: label,
414
+ ).as_params
415
+ params = base_params + { selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact
416
+ @channel.send_message_to_server('selectOption', params)
417
+
418
+ nil
419
+ end
420
+
421
+ def set_input_files(selector, files, noWaitAfter: nil, timeout: nil)
422
+ file_payloads = InputFiles.new(files).as_params
423
+ params = { files: file_payloads, selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact
424
+ @channel.send_message_to_server('setInputFiles', params)
425
+
426
+ nil
427
+ end
428
+
429
+ def type(
430
+ selector,
431
+ text,
432
+ delay: nil,
433
+ noWaitAfter: nil,
434
+ timeout: nil)
217
435
 
218
436
  params = {
219
437
  selector: selector,
@@ -222,16 +440,17 @@ module Playwright
222
440
  noWaitAfter: noWaitAfter,
223
441
  timeout: timeout,
224
442
  }.compact
225
-
226
443
  @channel.send_message_to_server('type', params)
444
+
445
+ nil
227
446
  end
228
447
 
229
448
  def press(
230
- selector,
231
- key,
232
- delay: nil,
233
- noWaitAfter: nil,
234
- timeout: nil)
449
+ selector,
450
+ key,
451
+ delay: nil,
452
+ noWaitAfter: nil,
453
+ timeout: nil)
235
454
 
236
455
  params = {
237
456
  selector: selector,
@@ -240,20 +459,48 @@ module Playwright
240
459
  noWaitAfter: noWaitAfter,
241
460
  timeout: timeout,
242
461
  }.compact
243
-
244
462
  @channel.send_message_to_server('press', params)
463
+
464
+ nil
245
465
  end
246
466
 
247
- def name
248
- @name || ''
467
+ def check(selector, force: nil, noWaitAfter: nil, timeout: nil)
468
+ params = {
469
+ selector: selector,
470
+ force: force,
471
+ noWaitAfter: noWaitAfter,
472
+ timeout: timeout,
473
+ }.compact
474
+ @channel.send_message_to_server('check', params)
475
+
476
+ nil
249
477
  end
250
478
 
251
- def url
252
- @url || ''
479
+ def uncheck(selector, force: nil, noWaitAfter: nil, timeout: nil)
480
+ params = {
481
+ selector: selector,
482
+ force: force,
483
+ noWaitAfter: noWaitAfter,
484
+ timeout: timeout,
485
+ }.compact
486
+ @channel.send_message_to_server('uncheck', params)
487
+
488
+ nil
253
489
  end
254
490
 
255
- def child_frames
256
- @child_frames.to_a
491
+ def wait_for_function(pageFunction, arg: nil, polling: nil, timeout: nil)
492
+ if polling.is_a?(String) && polling != 'raf'
493
+ raise ArgumentError.new("Unknown polling option: #{polling}")
494
+ end
495
+
496
+ function_or_expression =
497
+ if JavaScript.function?(pageFunction)
498
+ JavaScript::Function.new(pageFunction, arg)
499
+ else
500
+ JavaScript::Expression.new(pageFunction)
501
+ end
502
+
503
+ function_or_expression.wait_for_function(@channel, polling: polling, timeout: timeout)
257
504
  end
258
505
 
259
506
  def title
@@ -1,4 +1,55 @@
1
1
  module Playwright
2
2
  define_channel_owner :JSHandle do
3
+ private def after_initialize
4
+ @preview = @initializer['preview']
5
+ @channel.on('previewUpdated', method(:on_preview_updated))
6
+ end
7
+
8
+ def to_s
9
+ @preview
10
+ end
11
+
12
+ private def on_preview_updated(preview)
13
+ @preview = preview
14
+ end
15
+
16
+ def evaluate(pageFunction, arg: nil)
17
+ if JavaScript.function?(pageFunction)
18
+ JavaScript::Function.new(pageFunction, arg).evaluate(@channel)
19
+ else
20
+ JavaScript::Expression.new(pageFunction).evaluate(@channel)
21
+ end
22
+ end
23
+
24
+ def evaluate_handle(pageFunction, arg: nil)
25
+ if JavaScript.function?(pageFunction)
26
+ JavaScript::Function.new(pageFunction, arg).evaluate_handle(@channel)
27
+ else
28
+ JavaScript::Expression.new(pageFunction).evaluate_handle(@channel)
29
+ end
30
+ end
31
+
32
+ def get_properties
33
+ resp = @channel.send_message_to_server('getPropertyList')
34
+ resp.map { |prop| [prop['name'], ChannelOwner.from(prop['value'])] }.to_h
35
+ end
36
+
37
+ def get_property(name)
38
+ resp = @channel.send_message_to_server('getProperty', name: name)
39
+ ChannelOwner.from(resp)
40
+ end
41
+
42
+ def as_element
43
+ nil
44
+ end
45
+
46
+ def dispose
47
+ @channel.send_message_to_server('dispose')
48
+ end
49
+
50
+ def json_value
51
+ value = @channel.send_message_to_server('jsonValue')
52
+ JavaScript::ValueParser.new(value).parse
53
+ end
3
54
  end
4
55
  end
@@ -6,13 +6,13 @@ module Playwright
6
6
  include Utils::Errors::SafeCloseError
7
7
  attr_writer :owned_context
8
8
 
9
- def after_initialize
9
+ private def after_initialize
10
10
  @browser_context = @parent
11
11
  @timeout_settings = TimeoutSettings.new(@browser_context.send(:_timeout_settings))
12
12
  @accessibility = Accessibility.new(@channel)
13
- @keyboard = InputTypes::Keyboard.new(@channel)
14
- @mouse = InputTypes::Mouse.new(@channel)
15
- @touchscreen = InputTypes::Touchscreen.new(@channel)
13
+ @keyboard = KeyboardImpl.new(@channel)
14
+ @mouse = MouseImpl.new(@channel)
15
+ @touchscreen = TouchscreenImpl.new(@channel)
16
16
 
17
17
  @viewport_size = @initializer['viewportSize']
18
18
  @closed = false
@@ -26,7 +26,19 @@ module Playwright
26
26
  console_message = ChannelOwners::ConsoleMessage.from(params['message'])
27
27
  emit(Events::Page::Console, console_message)
28
28
  })
29
+ @channel.on('crash', ->(_) { emit(Events::Page::Crash) })
30
+ @channel.on('dialog', method(:on_dialog))
29
31
  @channel.on('domcontentloaded', ->(_) { emit(Events::Page::DOMContentLoaded) })
32
+ @channel.on('download', ->(params) {
33
+ emit(Events::Page::Download, ChannelOwners::Download.from(params['download']))
34
+ })
35
+ @channel.on('fileChooser', ->(params) {
36
+ chooser = FileChooserImpl.new(
37
+ page: self,
38
+ element_handle: ChannelOwners::ElementHandle.from(params['element']),
39
+ is_multiple: params['isMultiple'])
40
+ emit(Events::Page::FileChooser, chooser)
41
+ })
30
42
  @channel.on('frameAttached', ->(params) {
31
43
  on_frame_attached(ChannelOwners::Frame.from(params['frame']))
32
44
  })
@@ -34,9 +46,43 @@ module Playwright
34
46
  on_frame_detached(ChannelOwners::Frame.from(params['frame']))
35
47
  })
36
48
  @channel.on('load', ->(_) { emit(Events::Page::Load) })
49
+ @channel.on('pageError', ->(params) {
50
+ emit(Events::Page::PageError, Error.parse(params['error']['error']))
51
+ })
37
52
  @channel.on('popup', ->(params) {
38
53
  emit(Events::Page::Popup, ChannelOwners::Page.from(params['page']))
39
54
  })
55
+ @channel.on('request', ->(params) {
56
+ emit(Events::Page::Request, ChannelOwners::Request.from(params['request']))
57
+ })
58
+ @channel.on('requestFailed', ->(params) {
59
+ on_request_failed(
60
+ ChannelOwners::Request.from(params['request']),
61
+ params['responseEndTiming'],
62
+ params['failureText'],
63
+ )
64
+ })
65
+ @channel.on('requestFinished', ->(params) {
66
+ on_request_finished(
67
+ ChannelOwners::Request.from(params['request']),
68
+ params['responseEndTiming'],
69
+ )
70
+ })
71
+ @channel.on('response', ->(params) {
72
+ emit(Events::Page::Response, ChannelOwners::Response.from(params['response']))
73
+ })
74
+ @channel.on('route', ->(params) {
75
+ on_route(ChannelOwners::Route.from(params['route']), ChannelOwners::Request.from(params['request']))
76
+ })
77
+ @channel.on('video', ->(params) {
78
+ video.send(:update_relative_path, params['relativePath'])
79
+ })
80
+ @channel.on('webSocket', ->(params) {
81
+ emit(Events::Page::WebSocket, ChannelOwners::WebSocket.from(params['webSocket']))
82
+ })
83
+ @channel.on('worker', ->(params) {
84
+ on_worker(ChannelOwners::Worker.from(params['worker']))
85
+ })
40
86
  end
41
87
 
42
88
  attr_reader \
@@ -47,6 +93,17 @@ module Playwright
47
93
  :viewport_size,
48
94
  :main_frame
49
95
 
96
+ private def on_request_failed(request, response_end_timing, failure_text)
97
+ request.send(:update_failure_text, failure_text)
98
+ request.send(:update_response_end_timing, response_end_timing)
99
+ emit(Events::Page::RequestFailed)
100
+ end
101
+
102
+ private def on_request_finished(request, response_end_timing)
103
+ request.send(:update_response_end_timing, response_end_timing)
104
+ emit(Events::Page::RequestFinished)
105
+ end
106
+
50
107
  private def on_frame_attached(frame)
51
108
  frame.send(:update_page_from_page, self)
52
109
  @frames << frame
@@ -59,12 +116,48 @@ module Playwright
59
116
  emit(Events::Page::FrameDetached, frame)
60
117
  end
61
118
 
119
+ private def on_route(route, request)
120
+ # @routes.each ...
121
+ @browser_context.send(:on_route, route, request)
122
+ end
123
+
62
124
  private def on_close
63
125
  @closed = true
64
126
  @browser_context.send(:remove_page, self)
65
127
  emit(Events::Page::Close)
66
128
  end
67
129
 
130
+ private def on_dialog(params)
131
+ dialog = ChannelOwners::Dialog.from(params['dialog'])
132
+ unless emit(Events::Page::Dialog, dialog)
133
+ dialog.dismiss # FIXME: this should be asynchronous
134
+ end
135
+ end
136
+
137
+ # @override
138
+ def on(event, callback)
139
+ if event == Events::Page::FileChooser && listener_count(event) == 0
140
+ @channel.send_no_reply('setFileChooserInterceptedNoReply', intercepted: true)
141
+ end
142
+ super
143
+ end
144
+
145
+ # @override
146
+ def once(event, callback)
147
+ if event == Events::Page::FileChooser && listener_count(event) == 0
148
+ @channel.send_no_reply('setFileChooserInterceptedNoReply', intercepted: true)
149
+ end
150
+ super
151
+ end
152
+
153
+ # @override
154
+ def off(event, callback)
155
+ super
156
+ if event == Events::Page::FileChooser && listener_count(event) == 0
157
+ @channel.send_no_reply('setFileChooserInterceptedNoReply', intercepted: false)
158
+ end
159
+ end
160
+
68
161
  def context
69
162
  @browser_context
70
163
  end
@@ -74,14 +167,7 @@ module Playwright
74
167
  ChannelOwners::Page.from(resp)
75
168
  end
76
169
 
77
- def frame(frameSelector)
78
- name, url =
79
- if frameSelector.is_a?(Hash)
80
- [frameSelector[:name], frameSelector[:url]]
81
- else
82
- [frameSelector, nil]
83
- end
84
-
170
+ def frame(name: nil, url: nil)
85
171
  if name
86
172
  @frames.find { |f| f.name == name }
87
173
  elsif url
@@ -103,6 +189,16 @@ module Playwright
103
189
  @frames.to_a
104
190
  end
105
191
 
192
+ def set_default_navigation_timeout(timeout)
193
+ @timeout_settings.default_navigation_timeout = timeout
194
+ @channel.send_message_to_server('setDefaultNavigationTimeoutNoReply', timeout: timeout)
195
+ end
196
+
197
+ def set_default_timeout(timeout)
198
+ @timeout_settings.default_timeout = timeout
199
+ @channel.send_message_to_server('setDefaultTimeoutNoReply', timeout: timeout)
200
+ end
201
+
106
202
  def query_selector(selector)
107
203
  @main_frame.query_selector(selector)
108
204
  end
@@ -111,6 +207,38 @@ module Playwright
111
207
  @main_frame.query_selector_all(selector)
112
208
  end
113
209
 
210
+ def wait_for_selector(selector, state: nil, timeout: nil)
211
+ @main_frame.wait_for_selector(selector, state: state, timeout: timeout)
212
+ end
213
+
214
+ def checked?(selector, timeout: nil)
215
+ @main_frame.checked?(selector, timeout: timeout)
216
+ end
217
+
218
+ def disabled?(selector, timeout: nil)
219
+ @main_frame.disabled?(selector, timeout: timeout)
220
+ end
221
+
222
+ def editable?(selector, timeout: nil)
223
+ @main_frame.editable?(selector, timeout: timeout)
224
+ end
225
+
226
+ def enabled?(selector, timeout: nil)
227
+ @main_frame.enabled?(selector, timeout: timeout)
228
+ end
229
+
230
+ def hidden?(selector, timeout: nil)
231
+ @main_frame.hidden?(selector, timeout: timeout)
232
+ end
233
+
234
+ def visible?(selector, timeout: nil)
235
+ @main_frame.visible?(selector, timeout: timeout)
236
+ end
237
+
238
+ def dispatch_event(selector, type, eventInit: nil, timeout: nil)
239
+ @main_frame.dispatch_event(selector, type, eventInit: eventInit, timeout: timeout)
240
+ end
241
+
114
242
  def evaluate(pageFunction, arg: nil)
115
243
  @main_frame.evaluate(pageFunction, arg: arg)
116
244
  end
@@ -127,6 +255,33 @@ module Playwright
127
255
  @main_frame.eval_on_selector_all(selector, pageFunction, arg: arg)
128
256
  end
129
257
 
258
+ def add_script_tag(content: nil, path: nil, type: nil, url: nil)
259
+ @main_frame.add_script_tag(content: content, path: path, type: type, url: url)
260
+ end
261
+
262
+ def add_style_tag(content: nil, path: nil, url: nil)
263
+ @main_frame.add_style_tag(content: content, path: path, url: url)
264
+ end
265
+
266
+ def expose_function(name, callback)
267
+ @channel.send_message_to_server('exposeBinding', name: name)
268
+ @bindings[name] = ->(_source, *args) { callback.call(*args) }
269
+ end
270
+
271
+ def expose_binding(name, callback, handle: nil)
272
+ params = {
273
+ name: name,
274
+ needsHandle: handle,
275
+ }.compact
276
+ @channel.send_message_to_server('exposeBinding', params)
277
+ @bindings[name] = callback
278
+ end
279
+
280
+ def set_extra_http_headers(headers)
281
+ serialized_headers = HttpHeaders.new(headers).as_serialized
282
+ @channel.send_message_to_server('setExtraHTTPHeaders', headers: serialized_headers)
283
+ end
284
+
130
285
  def url
131
286
  @main_frame.url
132
287
  end
@@ -143,12 +298,66 @@ module Playwright
143
298
  @main_frame.goto(url, timeout: timeout, waitUntil: waitUntil, referer: referer)
144
299
  end
145
300
 
301
+ def reload(timeout: nil, waitUntil: nil)
302
+ params = {
303
+ timeout: timeout,
304
+ waitUntil: waitUntil,
305
+ }.compact
306
+ resp = @channel.send_message_to_server('reoad', params)
307
+ ChannelOwners::Response.from_nullable(resp)
308
+ end
309
+
310
+ def wait_for_load_state(state: nil, timeout: nil)
311
+ @main_frame.wait_for_load_state(state: state, timeout: timeout)
312
+ end
313
+
314
+ def go_back(timeout: nil, waitUntil: nil)
315
+ params = { timeout: timeout, waitUntil: waitUntil }.compact
316
+ resp = @channel.send_message_to_server('goBack', params)
317
+ ChannelOwners::Response.from_nullable(resp)
318
+ end
319
+
320
+ def go_forward(timeout: nil, waitUntil: nil)
321
+ params = { timeout: timeout, waitUntil: waitUntil }.compact
322
+ resp = @channel.send_message_to_server('goForward', params)
323
+ ChannelOwners::Response.from_nullable(resp)
324
+ end
325
+
326
+ def emulate_media(colorScheme: nil, media: nil)
327
+ params = {
328
+ colorScheme: colorScheme,
329
+ media: media,
330
+ }.compact
331
+ @channel.send_message_to_server('emulateMedia', params)
332
+
333
+ nil
334
+ end
335
+
146
336
  def set_viewport_size(viewportSize)
147
337
  @viewport_size = viewportSize
148
338
  @channel.send_message_to_server('setViewportSize', { viewportSize: viewportSize })
149
339
  nil
150
340
  end
151
341
 
342
+ def bring_to_front
343
+ @channel.send_message_to_server('bringToFront')
344
+ nil
345
+ end
346
+
347
+ def add_init_script(path: nil, script: nil)
348
+ source =
349
+ if path
350
+ File.read(path, 'r')
351
+ elsif script
352
+ script
353
+ else
354
+ raise ArgumentError.new('Either path or script parameter must be specified')
355
+ end
356
+
357
+ @channel.send_message_to_server('addInitScript', source: script)
358
+ nil
359
+ end
360
+
152
361
  def screenshot(
153
362
  path: nil,
154
363
  type: nil,
@@ -167,7 +376,7 @@ module Playwright
167
376
  timeout: timeout,
168
377
  }.compact
169
378
  encoded_binary = @channel.send_message_to_server('screenshot', params)
170
- decoded_binary = Base64.decode64(encoded_binary)
379
+ decoded_binary = Base64.strict_decode64(encoded_binary)
171
380
  if path
172
381
  File.open(path, 'wb') do |f|
173
382
  f.write(decoded_binary)
@@ -217,18 +426,114 @@ module Playwright
217
426
  )
218
427
  end
219
428
 
429
+ def dblclick(
430
+ selector,
431
+ button: nil,
432
+ delay: nil,
433
+ force: nil,
434
+ modifiers: nil,
435
+ noWaitAfter: nil,
436
+ position: nil,
437
+ timeout: nil)
438
+ @main_frame.dblclick(
439
+ selector,
440
+ button: button,
441
+ delay: delay,
442
+ force: force,
443
+ modifiers: modifiers,
444
+ noWaitAfter: noWaitAfter,
445
+ position: position,
446
+ timeout: timeout,
447
+ )
448
+ end
449
+
450
+ def tap_point(
451
+ selector,
452
+ force: nil,
453
+ modifiers: nil,
454
+ noWaitAfter: nil,
455
+ position: nil,
456
+ timeout: nil)
457
+ @main_frame.tap_point(
458
+ selector,
459
+ force: force,
460
+ modifiers: modifiers,
461
+ noWaitAfter: noWaitAfter,
462
+ position: position,
463
+ timeout: timeout,
464
+ )
465
+ end
466
+
467
+ def fill(selector, value, noWaitAfter: nil, timeout: nil)
468
+ @main_frame.fill(selector, value, noWaitAfter: noWaitAfter, timeout: timeout)
469
+ end
470
+
220
471
  def focus(selector, timeout: nil)
221
472
  @main_frame.focus(selector, timeout: timeout)
222
473
  end
223
474
 
224
- def type_text(
475
+ def text_content(selector, timeout: nil)
476
+ @main_frame.text_content(selector, timeout: timeout)
477
+ end
478
+
479
+ def inner_text(selector, timeout: nil)
480
+ @main_frame.inner_text(selector, timeout: timeout)
481
+ end
482
+
483
+ def inner_html(selector, timeout: nil)
484
+ @main_frame.inner_html(selector, timeout: timeout)
485
+ end
486
+
487
+ def get_attribute(selector, name, timeout: nil)
488
+ @main_frame.get_attribute(selector, name, timeout: timeout)
489
+ end
490
+
491
+ def hover(
492
+ selector,
493
+ force: nil,
494
+ modifiers: nil,
495
+ position: nil,
496
+ timeout: nil)
497
+ @main_frame.hover(
498
+ selector,
499
+ force: force,
500
+ modifiers: modifiers,
501
+ position: position,
502
+ timeout: timeout,
503
+ )
504
+ end
505
+
506
+ def select_option(
507
+ selector,
508
+ element: nil,
509
+ index: nil,
510
+ value: nil,
511
+ label: nil,
512
+ noWaitAfter: nil,
513
+ timeout: nil)
514
+ @main_frame.select_option(
515
+ selector,
516
+ element: element,
517
+ index: index,
518
+ value: value,
519
+ label: label,
520
+ noWaitAfter: noWaitAfter,
521
+ timeout: timeout,
522
+ )
523
+ end
524
+
525
+ def set_input_files(selector, files, noWaitAfter: nil, timeout: nil)
526
+ @main_frame.set_input_files(selector, files, noWaitAfter: noWaitAfter, timeout: timeout)
527
+ end
528
+
529
+ def type(
225
530
  selector,
226
531
  text,
227
532
  delay: nil,
228
533
  noWaitAfter: nil,
229
534
  timeout: nil)
230
535
 
231
- @main_frame.type_text(selector, text, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout)
536
+ @main_frame.type(selector, text, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout)
232
537
  end
233
538
 
234
539
  def press(
@@ -241,6 +546,57 @@ module Playwright
241
546
  @main_frame.press(selector, key, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout)
242
547
  end
243
548
 
549
+ def check(selector, force: nil, noWaitAfter: nil, timeout: nil)
550
+ @main_frame.check(selector, force: force, noWaitAfter: noWaitAfter, timeout: timeout)
551
+ end
552
+
553
+ def uncheck(selector, force: nil, noWaitAfter: nil, timeout: nil)
554
+ @main_frame.uncheck(selector, force: force, noWaitAfter: noWaitAfter, timeout: timeout)
555
+ end
556
+
557
+ def wait_for_function(pageFunction, arg: nil, polling: nil, timeout: nil)
558
+ @main_frame.wait_for_function(pageFunction, arg: arg, polling: polling, timeout: timeout)
559
+ end
560
+
561
+ def pdf(
562
+ displayHeaderFooter: nil,
563
+ footerTemplate: nil,
564
+ format: nil,
565
+ headerTemplate: nil,
566
+ height: nil,
567
+ landscape: nil,
568
+ margin: nil,
569
+ pageRanges: nil,
570
+ path: nil,
571
+ preferCSSPageSize: nil,
572
+ printBackground: nil,
573
+ scale: nil,
574
+ width: nil)
575
+
576
+ params = {
577
+ displayHeaderFooter: displayHeaderFooter,
578
+ footerTemplate: footerTemplate,
579
+ format: format,
580
+ headerTemplate: headerTemplate,
581
+ height: height,
582
+ landscape: landscape,
583
+ margin: margin,
584
+ pageRanges: pageRanges,
585
+ preferCSSPageSize: preferCSSPageSize,
586
+ printBackground: printBackground,
587
+ scale: scale,
588
+ width: width,
589
+ }.compact
590
+ encoded_binary = @channel.send_message_to_server('pdf', params)
591
+ decoded_binary = Base64.strict_decode64(encoded_binary)
592
+ if path
593
+ File.open(path, 'wb') do |f|
594
+ f.write(decoded_binary)
595
+ end
596
+ end
597
+ decoded_binary
598
+ end
599
+
244
600
  class CrashedError < StandardError
245
601
  def initialize
246
602
  super('Page crashed')
@@ -259,20 +615,9 @@ module Playwright
259
615
  end
260
616
  end
261
617
 
262
- def wait_for_event(event, optionsOrPredicate: nil, &block)
263
- predicate, timeout =
264
- case optionsOrPredicate
265
- when Proc
266
- [optionsOrPredicate, nil]
267
- when Hash
268
- [optionsOrPredicate[:predicate], optionsOrPredicate[:timeout]]
269
- else
270
- [nil, nil]
271
- end
272
- timeout ||= @timeout_settings.timeout
273
-
618
+ def expect_event(event, predicate: nil, timeout: nil, &block)
274
619
  wait_helper = WaitHelper.new
275
- wait_helper.reject_on_timeout(timeout, "Timeout while waiting for event \"#{event}\"")
620
+ wait_helper.reject_on_timeout(timeout || @timeout_settings.timeout, "Timeout while waiting for event \"#{event}\"")
276
621
 
277
622
  unless event == Events::Page::Crash
278
623
  wait_helper.reject_on_event(self, Events::Page::Crash, CrashedError.new)
@@ -289,15 +634,15 @@ module Playwright
289
634
  wait_helper.promise.value!
290
635
  end
291
636
 
292
- def wait_for_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
293
- @main_frame.wait_for_navigation(
637
+ def expect_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
638
+ @main_frame.expect_navigation(
294
639
  timeout: timeout,
295
640
  url: url,
296
641
  waitUntil: waitUntil,
297
642
  &block)
298
643
  end
299
644
 
300
- def wait_for_request(urlOrPredicate, timeout: nil)
645
+ def expect_request(urlOrPredicate, timeout: nil)
301
646
  predicate =
302
647
  case urlOrPredicate
303
648
  when String, Regexp
@@ -309,10 +654,10 @@ module Playwright
309
654
  -> (_) { true }
310
655
  end
311
656
 
312
- wait_for_event(Events::Page::Request, optionsOrPredicate: { predicate: predicate, timeout: timeout})
657
+ expect_event(Events::Page::Request, predicate: predicate, timeout: timeout)
313
658
  end
314
659
 
315
- def wait_for_response(urlOrPredicate, timeout: nil)
660
+ def expect_response(urlOrPredicate, timeout: nil)
316
661
  predicate =
317
662
  case urlOrPredicate
318
663
  when String, Regexp
@@ -324,7 +669,7 @@ module Playwright
324
669
  -> (_) { true }
325
670
  end
326
671
 
327
- wait_for_event(Events::Page::Response, optionsOrPredicate: { predicate: predicate, timeout: timeout})
672
+ expect_event(Events::Page::Response, predicate: predicate, timeout: timeout)
328
673
  end
329
674
 
330
675
  # called from BrowserContext#on_page with send(:update_browser_context, page), so keep private.