playwright-ruby-client 0.0.4 → 0.0.9

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