puppeteer-ruby 0.0.11 → 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +0 -12
  3. data/.github/workflows/docs.yml +45 -0
  4. data/.github/workflows/reviewdog.yml +15 -0
  5. data/README.md +54 -1
  6. data/lib/puppeteer.rb +39 -13
  7. data/lib/puppeteer/browser.rb +22 -25
  8. data/lib/puppeteer/cdp_session.rb +3 -19
  9. data/lib/puppeteer/concurrent_ruby_utils.rb +12 -2
  10. data/lib/puppeteer/connection.rb +3 -15
  11. data/lib/puppeteer/define_async_method.rb +23 -0
  12. data/lib/puppeteer/dom_world.rb +51 -65
  13. data/lib/puppeteer/element_handle.rb +15 -44
  14. data/lib/puppeteer/emulation_manager.rb +2 -6
  15. data/lib/puppeteer/event_callbackable.rb +11 -0
  16. data/lib/puppeteer/execution_context.rb +1 -6
  17. data/lib/puppeteer/frame.rb +34 -39
  18. data/lib/puppeteer/frame_manager.rb +7 -25
  19. data/lib/puppeteer/js_handle.rb +3 -12
  20. data/lib/puppeteer/keyboard.rb +6 -27
  21. data/lib/puppeteer/launcher.rb +6 -6
  22. data/lib/puppeteer/launcher/chrome.rb +10 -8
  23. data/lib/puppeteer/mouse.rb +5 -25
  24. data/lib/puppeteer/network_manager.rb +163 -5
  25. data/lib/puppeteer/page.rb +230 -177
  26. data/lib/puppeteer/page/pdf_options.rb +166 -0
  27. data/lib/puppeteer/remote_object.rb +2 -5
  28. data/lib/puppeteer/request.rb +336 -0
  29. data/lib/puppeteer/response.rb +113 -0
  30. data/lib/puppeteer/target.rb +1 -1
  31. data/lib/puppeteer/touch_screen.rb +2 -7
  32. data/lib/puppeteer/version.rb +1 -1
  33. data/lib/puppeteer/wait_task.rb +2 -4
  34. data/lib/puppeteer/web_socket_transport.rb +8 -8
  35. data/puppeteer-ruby.gemspec +1 -1
  36. data/puppeteer-ruby.png +0 -0
  37. metadata +11 -105
  38. data/Dockerfile +0 -6
  39. data/docker-compose.yml +0 -15
  40. data/docs/Puppeteer.html +0 -2020
  41. data/docs/Puppeteer/AsyncAwaitBehavior.html +0 -105
  42. data/docs/Puppeteer/Browser.html +0 -2258
  43. data/docs/Puppeteer/BrowserContext.html +0 -809
  44. data/docs/Puppeteer/BrowserFetcher.html +0 -214
  45. data/docs/Puppeteer/BrowserRunner.html +0 -914
  46. data/docs/Puppeteer/BrowserRunner/BrowserProcess.html +0 -477
  47. data/docs/Puppeteer/CDPSession.html +0 -813
  48. data/docs/Puppeteer/CDPSession/Error.html +0 -124
  49. data/docs/Puppeteer/ConcurrentRubyUtils.html +0 -430
  50. data/docs/Puppeteer/Connection.html +0 -964
  51. data/docs/Puppeteer/Connection/MessageCallback.html +0 -434
  52. data/docs/Puppeteer/Connection/ProtocolError.html +0 -216
  53. data/docs/Puppeteer/Connection/RequestDebugPrinter.html +0 -217
  54. data/docs/Puppeteer/Connection/ResponseDebugPrinter.html +0 -244
  55. data/docs/Puppeteer/ConsoleMessage.html +0 -565
  56. data/docs/Puppeteer/ConsoleMessage/Location.html +0 -433
  57. data/docs/Puppeteer/DOMWorld.html +0 -2219
  58. data/docs/Puppeteer/DOMWorld/DetachedError.html +0 -124
  59. data/docs/Puppeteer/DOMWorld/DocumentEvaluationError.html +0 -124
  60. data/docs/Puppeteer/DebugPrint.html +0 -233
  61. data/docs/Puppeteer/Device.html +0 -470
  62. data/docs/Puppeteer/Devices.html +0 -139
  63. data/docs/Puppeteer/ElementHandle.html +0 -2542
  64. data/docs/Puppeteer/ElementHandle/BoundingBox.html +0 -507
  65. data/docs/Puppeteer/ElementHandle/BoxModel.html +0 -404
  66. data/docs/Puppeteer/ElementHandle/ElementNotFoundError.html +0 -206
  67. data/docs/Puppeteer/ElementHandle/ElementNotVisibleError.html +0 -206
  68. data/docs/Puppeteer/ElementHandle/Point.html +0 -492
  69. data/docs/Puppeteer/ElementHandle/ScrollIntoViewError.html +0 -124
  70. data/docs/Puppeteer/EmulationManager.html +0 -454
  71. data/docs/Puppeteer/EventCallbackable.html +0 -433
  72. data/docs/Puppeteer/EventCallbackable/EventListeners.html +0 -435
  73. data/docs/Puppeteer/ExecutionContext.html +0 -998
  74. data/docs/Puppeteer/ExecutionContext/EvaluationError.html +0 -124
  75. data/docs/Puppeteer/ExecutionContext/JavaScriptExpression.html +0 -357
  76. data/docs/Puppeteer/ExecutionContext/JavaScriptFunction.html +0 -389
  77. data/docs/Puppeteer/FileChooser.html +0 -455
  78. data/docs/Puppeteer/Frame.html +0 -3813
  79. data/docs/Puppeteer/FrameManager.html +0 -2410
  80. data/docs/Puppeteer/FrameManager/NavigationError.html +0 -124
  81. data/docs/Puppeteer/IfPresent.html +0 -222
  82. data/docs/Puppeteer/JSHandle.html +0 -1352
  83. data/docs/Puppeteer/Keyboard.html +0 -1557
  84. data/docs/Puppeteer/Keyboard/KeyDefinition.html +0 -831
  85. data/docs/Puppeteer/Keyboard/KeyDescription.html +0 -603
  86. data/docs/Puppeteer/Launcher.html +0 -237
  87. data/docs/Puppeteer/Launcher/Base.html +0 -385
  88. data/docs/Puppeteer/Launcher/Base/ExecutablePathNotFound.html +0 -124
  89. data/docs/Puppeteer/Launcher/BrowserOptions.html +0 -441
  90. data/docs/Puppeteer/Launcher/Chrome.html +0 -669
  91. data/docs/Puppeteer/Launcher/Chrome/DefaultArgs.html +0 -382
  92. data/docs/Puppeteer/Launcher/ChromeArgOptions.html +0 -531
  93. data/docs/Puppeteer/Launcher/LaunchOptions.html +0 -893
  94. data/docs/Puppeteer/LifecycleWatcher.html +0 -834
  95. data/docs/Puppeteer/LifecycleWatcher/ExpectedLifecycle.html +0 -363
  96. data/docs/Puppeteer/LifecycleWatcher/FrameDetachedError.html +0 -206
  97. data/docs/Puppeteer/LifecycleWatcher/TerminatedError.html +0 -124
  98. data/docs/Puppeteer/Mouse.html +0 -1095
  99. data/docs/Puppeteer/Mouse/Button.html +0 -136
  100. data/docs/Puppeteer/NetworkManager.html +0 -901
  101. data/docs/Puppeteer/NetworkManager/Credentials.html +0 -385
  102. data/docs/Puppeteer/Page.html +0 -5970
  103. data/docs/Puppeteer/Page/FileChooserTimeoutError.html +0 -206
  104. data/docs/Puppeteer/Page/ScreenshotOptions.html +0 -845
  105. data/docs/Puppeteer/Page/ScriptTag.html +0 -555
  106. data/docs/Puppeteer/Page/StyleTag.html +0 -448
  107. data/docs/Puppeteer/Page/TargetCrashedError.html +0 -124
  108. data/docs/Puppeteer/RemoteObject.html +0 -1087
  109. data/docs/Puppeteer/Target.html +0 -1336
  110. data/docs/Puppeteer/Target/InitializeFailure.html +0 -124
  111. data/docs/Puppeteer/Target/TargetInfo.html +0 -729
  112. data/docs/Puppeteer/TimeoutError.html +0 -135
  113. data/docs/Puppeteer/TimeoutSettings.html +0 -496
  114. data/docs/Puppeteer/TouchScreen.html +0 -464
  115. data/docs/Puppeteer/Viewport.html +0 -837
  116. data/docs/Puppeteer/WaitTask.html +0 -637
  117. data/docs/Puppeteer/WaitTask/TerminatedError.html +0 -124
  118. data/docs/Puppeteer/WaitTask/TimeoutError.html +0 -206
  119. data/docs/Puppeteer/WebSocket.html +0 -673
  120. data/docs/Puppeteer/WebSocket/DriverImpl.html +0 -412
  121. data/docs/Puppeteer/WebSocket/TransportError.html +0 -124
  122. data/docs/Puppeteer/WebSocketTransport.html +0 -600
  123. data/docs/Puppeteer/WebSocktTransportError.html +0 -124
  124. data/docs/_index.html +0 -816
  125. data/docs/class_list.html +0 -51
  126. data/docs/css/common.css +0 -1
  127. data/docs/css/full_list.css +0 -58
  128. data/docs/css/style.css +0 -496
  129. data/docs/file.README.html +0 -123
  130. data/docs/file_list.html +0 -56
  131. data/docs/frames.html +0 -17
  132. data/docs/index.html +0 -123
  133. data/docs/js/app.js +0 -314
  134. data/docs/js/full_list.js +0 -216
  135. data/docs/js/jquery.js +0 -4
  136. data/docs/method_list.html +0 -4091
  137. data/docs/top-level-namespace.html +0 -126
  138. data/lib/puppeteer/async_await_behavior.rb +0 -38
@@ -1,11 +1,13 @@
1
1
  require 'base64'
2
+ require "stringio"
2
3
 
4
+ require_relative './page/pdf_options'
3
5
  require_relative './page/screenshot_options'
4
6
 
5
7
  class Puppeteer::Page
6
8
  include Puppeteer::EventCallbackable
7
9
  include Puppeteer::IfPresent
8
- using Puppeteer::AsyncAwaitBehavior
10
+ using Puppeteer::DefineAsyncMethod
9
11
 
10
12
  # @param {!Puppeteer.CDPSession} client
11
13
  # @param {!Puppeteer.Target} target
@@ -124,6 +126,45 @@ class Puppeteer::Page
124
126
  )
125
127
  end
126
128
 
129
+ EVENT_MAPPINGS = {
130
+ close: 'Events.Page.Close',
131
+ # console: 'Events.Page.Console',
132
+ # dialog: 'Events.Page.Dialog',
133
+ domcontentloaded: 'Events.Page.DOMContentLoaded',
134
+ # error:
135
+ frameattached: 'Events.Page.FrameAttached',
136
+ framedetached: 'Events.Page.FrameDetached',
137
+ framenavigated: 'Events.Page.FrameNavigated',
138
+ load: 'Events.Page.Load',
139
+ # metrics: 'Events.Page.Metrics',
140
+ # pageerror: 'Events.Page.PageError',
141
+ popup: 'Events.Page.Popup',
142
+ request: 'Events.Page.Request',
143
+ requestfailed: 'Events.Page.RequestFailed',
144
+ requestfinished: 'Events.Page.RequestFinished',
145
+ response: 'Events.Page.Response',
146
+ # workercreated: 'Events.Page.WorkerCreated',
147
+ # workerdestroyed: 'Events.Page.WorkerDestroyed',
148
+ }
149
+
150
+ # @param event_name [Symbol]
151
+ def on(event_name, &block)
152
+ unless EVENT_MAPPINGS.has_key?(event_name.to_sym)
153
+ raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{EVENT_MAPPINGS.keys.join(", ")}")
154
+ end
155
+
156
+ add_event_listener(EVENT_MAPPINGS[event_name.to_sym], &block)
157
+ end
158
+
159
+ # @param event_name [Symbol]
160
+ def once(event_name, &block)
161
+ unless EVENT_MAPPINGS.has_key?(event_name.to_sym)
162
+ raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{EVENT_MAPPINGS.keys.join(", ")}")
163
+ end
164
+
165
+ observe_first(EVENT_MAPPINGS[event_name.to_sym], &block)
166
+ end
167
+
127
168
  def handle_file_chooser(event)
128
169
  return if @file_chooser_interceptors.empty?
129
170
 
@@ -166,12 +207,7 @@ class Puppeteer::Page
166
207
  end
167
208
  end
168
209
 
169
- # @param timeout [Integer]
170
- # @return [Future<Puppeteer::FileChooser>]
171
- async def async_wait_for_file_chooser(timeout: nil)
172
- wait_for_file_chooser(timeout: timeout)
173
- end
174
-
210
+ define_async_method :async_wait_for_file_chooser
175
211
 
176
212
  # /**
177
213
  # * @param {!{longitude: number, latitude: number, accuracy: (number|undefined)}} options
@@ -266,6 +302,8 @@ class Puppeteer::Page
266
302
  main_frame.S(selector)
267
303
  end
268
304
 
305
+ define_async_method :async_S
306
+
269
307
  # `$$()` in JavaScript. $ is not allowed to use as a method name in Ruby.
270
308
  # @param {string} selector
271
309
  # @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
@@ -273,6 +311,8 @@ class Puppeteer::Page
273
311
  main_frame.SS(selector)
274
312
  end
275
313
 
314
+ define_async_method :async_SS
315
+
276
316
  # @param {Function|string} pageFunction
277
317
  # @param {!Array<*>} args
278
318
  # @return {!Promise<!Puppeteer.JSHandle>}
@@ -281,6 +321,8 @@ class Puppeteer::Page
281
321
  context.evaluate_handle(page_function, *args)
282
322
  end
283
323
 
324
+ define_async_method :async_evaluate_handle
325
+
284
326
  # @param {!Puppeteer.JSHandle} prototypeHandle
285
327
  # @return {!Promise<!Puppeteer.JSHandle>}
286
328
  def query_objects(prototype_handle)
@@ -296,13 +338,7 @@ class Puppeteer::Page
296
338
  main_frame.Seval(selector, page_function, *args)
297
339
  end
298
340
 
299
- # `$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
300
- # @param selector [String]
301
- # @param page_function [String]
302
- # @return [Future]
303
- async def async_Seval(selector, page_function, *args)
304
- Seval(selector, page_function, *args)
305
- end
341
+ define_async_method :async_Seval
306
342
 
307
343
  # `$$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
308
344
  # @param selector [String]
@@ -312,13 +348,7 @@ class Puppeteer::Page
312
348
  main_frame.SSeval(selector, page_function, *args)
313
349
  end
314
350
 
315
- # `$$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
316
- # @param selector [String]
317
- # @param page_function [String]
318
- # @return [Future]
319
- async def async_SSeval(selector, page_function, *args)
320
- SSeval(selector, page_function, *args)
321
- end
351
+ define_async_method :async_SSeval
322
352
 
323
353
  # `$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.
324
354
  # @param {string} expression
@@ -327,6 +357,8 @@ class Puppeteer::Page
327
357
  main_frame.Sx(expression)
328
358
  end
329
359
 
360
+ define_async_method :async_Sx
361
+
330
362
  # /**
331
363
  # * @param {!Array<string>} urls
332
364
  # * @return {!Promise<!Array<Network.Cookie>>}
@@ -619,13 +651,14 @@ class Puppeteer::Page
619
651
  main_frame.content
620
652
  end
621
653
 
622
- # @param {string} html
623
- # @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
654
+ # @param html [String]
655
+ # @param timeout [Integer]
656
+ # @param wait_until [String|Array<String>]
624
657
  def set_content(html, timeout: nil, wait_until: nil)
625
658
  main_frame.set_content(html, timeout: timeout, wait_until: wait_until)
626
659
  end
627
660
 
628
- # @param {string} html
661
+ # @param html [String]
629
662
  def content=(html)
630
663
  main_frame.set_content(html)
631
664
  end
@@ -654,48 +687,96 @@ class Puppeteer::Page
654
687
  main_frame.send(:wait_for_navigation, timeout: timeout, wait_until: wait_until)
655
688
  end
656
689
 
657
- # @param timeout [number|nil]
658
- # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
659
- # @return [Future]
660
- async def async_wait_for_navigation(timeout: nil, wait_until: nil)
661
- wait_for_navigation(timeout: timeout, wait_until: wait_until)
690
+ define_async_method :async_wait_for_navigation
691
+
692
+ private def wait_for_network_manager_event(event_name, predicate:, timeout:)
693
+ option_timeout = timeout || @timeout_settings.timeout
694
+
695
+ @wait_for_network_manager_event_listener_ids ||= {}
696
+ if_present(@wait_for_network_manager_event_listener_ids[event_name]) do |listener_id|
697
+ @frame_manager.network_manager.remove_event_listener(listener_id)
698
+ end
699
+
700
+ promise = resolvable_future
701
+
702
+ @wait_for_network_manager_event_listener_ids[event_name] =
703
+ @frame_manager.network_manager.add_event_listener(event_name) do |event_target|
704
+ if predicate.call(event_target)
705
+ promise.fulfill(nil)
706
+ end
707
+ end
708
+
709
+ begin
710
+ Timeout.timeout(option_timeout / 1000.0) do
711
+ await_any(promise, session_close_promise)
712
+ end
713
+ rescue Timeout::Error
714
+ raise Puppeteer::TimeoutError.new("waiting for #{event_name} failed: timeout #{timeout}ms exceeded")
715
+ ensure
716
+ @frame_manager.network_manager.remove_event_listener(@wait_for_network_manager_event_listener_ids[event_name])
717
+ end
662
718
  end
663
719
 
664
- # /**
665
- # * @param {(string|Function)} urlOrPredicate
666
- # * @param {!{timeout?: number}=} options
667
- # * @return {!Promise<!Puppeteer.Request>}
668
- # */
669
- # async waitForRequest(urlOrPredicate, options = {}) {
670
- # const {
671
- # timeout = this._timeoutSettings.timeout(),
672
- # } = options;
673
- # return helper.waitForEvent(this._frameManager.networkManager(), Events.NetworkManager.Request, request => {
674
- # if (helper.isString(urlOrPredicate))
675
- # return (urlOrPredicate === request.url());
676
- # if (typeof urlOrPredicate === 'function')
677
- # return !!(urlOrPredicate(request));
678
- # return false;
679
- # }, timeout, this._sessionClosePromise());
680
- # }
720
+ private def session_close_promise
721
+ @disconnect_promise ||= resolvable_future do |future|
722
+ @client.observe_first('Events.CDPSession.Disconnected') do
723
+ future.reject(Puppeteer::CDPSession::Error.new('Target Closed'))
724
+ end
725
+ end
726
+ end
681
727
 
682
- # /**
683
- # * @param {(string|Function)} urlOrPredicate
684
- # * @param {!{timeout?: number}=} options
685
- # * @return {!Promise<!Puppeteer.Response>}
686
- # */
687
- # async waitForResponse(urlOrPredicate, options = {}) {
688
- # const {
689
- # timeout = this._timeoutSettings.timeout(),
690
- # } = options;
691
- # return helper.waitForEvent(this._frameManager.networkManager(), Events.NetworkManager.Response, response => {
692
- # if (helper.isString(urlOrPredicate))
693
- # return (urlOrPredicate === response.url());
694
- # if (typeof urlOrPredicate === 'function')
695
- # return !!(urlOrPredicate(response));
696
- # return false;
697
- # }, timeout, this._sessionClosePromise());
698
- # }
728
+ # - Waits until request URL matches
729
+ # `wait_for_request(url: 'https://example.com/awesome')`
730
+ # - Waits until request matches the given predicate
731
+ # `wait_for_request(predicate: -> (req){ req.url.start_with?('https://example.com/search') })`
732
+ #
733
+ # @param url [String]
734
+ # @param predicate [Proc(Puppeteer::Request -> Boolean)]
735
+ private def wait_for_request(url: nil, predicate: nil, timeout: nil)
736
+ if !url && !predicate
737
+ raise ArgumentError.new('url or predicate must be specified')
738
+ end
739
+ if predicate && !predicate.is_a?(Proc)
740
+ raise ArgumentError.new('predicate must be a proc.')
741
+ end
742
+ request_predicate =
743
+ if url
744
+ -> (request) { request.url == url }
745
+ else
746
+ -> (request) { predicate.call(request) }
747
+ end
748
+
749
+ wait_for_network_manager_event('Events.NetworkManager.Request',
750
+ predicate: request_predicate,
751
+ timeout: timeout,
752
+ )
753
+ end
754
+
755
+ define_async_method :async_wait_for_request
756
+
757
+ # @param url [String]
758
+ # @param predicate [Proc(Puppeteer::Request -> Boolean)]
759
+ private def wait_for_response(url: nil, predicate: nil, timeout: nil)
760
+ if !url && !predicate
761
+ raise ArgumentError.new('url or predicate must be specified')
762
+ end
763
+ if predicate && !predicate.is_a?(Proc)
764
+ raise ArgumentError.new('predicate must be a proc.')
765
+ end
766
+ response_predicate =
767
+ if url
768
+ -> (response) { response.url == url }
769
+ else
770
+ -> (response) { predicate.call(response) }
771
+ end
772
+
773
+ wait_for_network_manager_event('Events.NetworkManager.Response',
774
+ predicate: response_predicate,
775
+ timeout: timeout,
776
+ )
777
+ end
778
+
779
+ define_async_method :async_wait_for_response
699
780
 
700
781
  # @param timeout [number|nil]
701
782
  # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
@@ -734,20 +815,19 @@ class Puppeteer::Page
734
815
  @client.send_message('Emulation.setScriptExecutionDisabled', value: !enabled)
735
816
  end
736
817
 
737
- # /**
738
- # * @param {boolean} enabled
739
- # */
740
- # async setBypassCSP(enabled) {
741
- # await this._client.send('Page.setBypassCSP', { enabled });
742
- # }
818
+ # @param enabled [Boolean]
819
+ def bypass_csp=(enabled)
820
+ @client.send_message('Page.setBypassCSP', enabled: enabled)
821
+ end
743
822
 
744
- # /**
745
- # * @param {?string} type
746
- # */
747
- # async emulateMediaType(type) {
748
- # assert(type === 'screen' || type === 'print' || type === null, 'Unsupported media type: ' + type);
749
- # await this._client.send('Emulation.setEmulatedMedia', {media: type || ''});
750
- # }
823
+ # @param media_type [String|Symbol|nil] either of (media, print, nil)
824
+ def emulate_media_type(media_type)
825
+ media_type_str = media_type.to_s
826
+ unless ['screen', 'print', ''].include?(media_type_str)
827
+ raise ArgumentError.new("Unsupported media type: #{media_type}")
828
+ end
829
+ @client.send_message('Emulation.setEmulatedMedia', media: media_type_str)
830
+ end
751
831
 
752
832
  # /**
753
833
  # * @param {?Array<MediaFeature>} features
@@ -767,7 +847,7 @@ class Puppeteer::Page
767
847
 
768
848
  # @param timezone_id [String?]
769
849
  def emulate_timezone(timezone_id)
770
- @client.send_message('Emulation.setTimezoneOverride', timezoneId: timezoneId || '')
850
+ @client.send_message('Emulation.setTimezoneOverride', timezoneId: timezone_id || '')
771
851
  rescue => err
772
852
  if err.message.include?('Invalid timezone')
773
853
  raise ArgumentError.new("Invalid timezone ID: #{timezone_id}")
@@ -792,6 +872,8 @@ class Puppeteer::Page
792
872
  main_frame.evaluate(page_function, *args)
793
873
  end
794
874
 
875
+ define_async_method :async_evaluate
876
+
795
877
  # /**
796
878
  # * @param {Function|string} pageFunction
797
879
  # * @param {!Array<*>} args
@@ -806,9 +888,9 @@ class Puppeteer::Page
806
888
  @frame_manager.network_manager.cache_enabled = enabled
807
889
  end
808
890
 
809
- # @return {!Promise<string>}
891
+ # @return [String]
810
892
  def title
811
- @title
893
+ main_frame.title
812
894
  end
813
895
 
814
896
  # /**
@@ -888,71 +970,66 @@ class Puppeteer::Page
888
970
  buffer
889
971
  end
890
972
 
891
- # /**
892
- # * @param {!PDFOptions=} options
893
- # * @return {!Promise<!Buffer>}
894
- # */
895
- # async pdf(options = {}) {
896
- # const {
897
- # scale = 1,
898
- # displayHeaderFooter = false,
899
- # headerTemplate = '',
900
- # footerTemplate = '',
901
- # printBackground = false,
902
- # landscape = false,
903
- # pageRanges = '',
904
- # preferCSSPageSize = false,
905
- # margin = {},
906
- # path = null
907
- # } = options;
908
-
909
- # let paperWidth = 8.5;
910
- # let paperHeight = 11;
911
- # if (options.format) {
912
- # const format = Page.PaperFormats[options.format.toLowerCase()];
913
- # assert(format, 'Unknown paper format: ' + options.format);
914
- # paperWidth = format.width;
915
- # paperHeight = format.height;
916
- # } else {
917
- # paperWidth = convertPrintParameterToInches(options.width) || paperWidth;
918
- # paperHeight = convertPrintParameterToInches(options.height) || paperHeight;
919
- # }
973
+ class ProtocolStreamReader
974
+ def initialize(client:, handle:, path:)
975
+ @client = client
976
+ @handle = handle
977
+ @path = path
978
+ end
920
979
 
921
- # const marginTop = convertPrintParameterToInches(margin.top) || 0;
922
- # const marginLeft = convertPrintParameterToInches(margin.left) || 0;
923
- # const marginBottom = convertPrintParameterToInches(margin.bottom) || 0;
924
- # const marginRight = convertPrintParameterToInches(margin.right) || 0;
925
-
926
- # const result = await this._client.send('Page.printToPDF', {
927
- # transferMode: 'ReturnAsStream',
928
- # landscape,
929
- # displayHeaderFooter,
930
- # headerTemplate,
931
- # footerTemplate,
932
- # printBackground,
933
- # scale,
934
- # paperWidth,
935
- # paperHeight,
936
- # marginTop,
937
- # marginBottom,
938
- # marginLeft,
939
- # marginRight,
940
- # pageRanges,
941
- # preferCSSPageSize
942
- # });
943
- # return await helper.readProtocolStream(this._client, result.stream, path);
944
- # }
980
+ def read
981
+ out = StringIO.new
982
+ File.open(@path, 'w') do |file|
983
+ eof = false
984
+ until eof
985
+ response = @client.send_message('IO.read', handle: @handle)
986
+ eof = response['eof']
987
+ data =
988
+ if response['base64Encoded']
989
+ Base64.decode64(response['data'])
990
+ else
991
+ response['data']
992
+ end
993
+ out.write(data)
994
+ file.write(data)
995
+ end
996
+ end
997
+ @client.send_message('IO.close', handle: @handle)
998
+ out.read
999
+ end
1000
+ end
1001
+
1002
+ class PrintToPdfIsNotImplementedError < StandardError
1003
+ def initialize
1004
+ super('pdf() is only available in headless mode. See https://github.com/puppeteer/puppeteer/issues/1829')
1005
+ end
1006
+ end
1007
+
1008
+ # @return [String]
1009
+ def pdf(options = {})
1010
+ pdf_options = PDFOptions.new(options)
1011
+ result = @client.send_message('Page.printToPDF', pdf_options.page_print_args)
1012
+ ProtocolStreamReader.new(client: @client, handle: result['stream'], path: pdf_options.path).read
1013
+ rescue => err
1014
+ if err.message.include?('PrintToPDF is not implemented')
1015
+ raise PrintToPdfIsNotImplementedError.new
1016
+ else
1017
+ raise
1018
+ end
1019
+ end
945
1020
 
946
- # @param {!{runBeforeUnload: (boolean|undefined)}=} options
947
- def close
948
- # assert(!!this._client._connection, 'Protocol error: Connection closed. Most likely the page has been closed.');
949
- # const runBeforeUnload = !!options.runBeforeUnload;
950
- # if (runBeforeUnload) {
951
- # await this._client.send('Page.close');
952
- # } else {
953
- # await this._client._connection.send('Target.closeTarget', { targetId: this._target._targetId });
954
- # await this._target._isClosedPromise;
955
- # }
1021
+ # @param run_before_unload [Boolean]
1022
+ def close(run_before_unload: false)
1023
+ unless @client.connection
1024
+ raise 'Protocol error: Connection closed. Most likely the page has been closed.'
1025
+ end
1026
+
1027
+ if run_before_unload
1028
+ @client.send_message('Page.close')
1029
+ else
1030
+ @client.connection.send_message('Target.closeTarget', targetId: @target.target_id)
1031
+ await @target.is_closed_promise
1032
+ end
956
1033
  end
957
1034
 
958
1035
  # @return [boolean]
@@ -970,20 +1047,15 @@ class Puppeteer::Page
970
1047
  main_frame.click(selector, delay: delay, button: button, click_count: click_count)
971
1048
  end
972
1049
 
973
- # @param selector [String]
974
- # @param delay [Number]
975
- # @param button [String] "left"|"right"|"middle"
976
- # @param click_count [Number]
977
- # @return [Future]
978
- async def async_click(selector, delay: nil, button: nil, click_count: nil)
979
- click(selector, delay: delay, button: button, click_count: click_count)
980
- end
1050
+ define_async_method :async_click
981
1051
 
982
1052
  # @param {string} selector
983
1053
  def focus(selector)
984
1054
  main_frame.focus(selector)
985
1055
  end
986
1056
 
1057
+ define_async_method :async_focus
1058
+
987
1059
  # @param {string} selector
988
1060
  def hover(selector)
989
1061
  main_frame.hover(selector)
@@ -996,15 +1068,14 @@ class Puppeteer::Page
996
1068
  main_frame.select(selector, *values)
997
1069
  end
998
1070
 
1071
+ define_async_method :async_select
1072
+
999
1073
  # @param selector [String]
1000
1074
  def tap(selector)
1001
1075
  main_frame.tap(selector)
1002
1076
  end
1003
1077
 
1004
- # @param selector [String]
1005
- async def async_tap(selector)
1006
- tap(selector)
1007
- end
1078
+ define_async_method :async_tap
1008
1079
 
1009
1080
  # @param selector [String]
1010
1081
  # @param text [String]
@@ -1013,15 +1084,7 @@ class Puppeteer::Page
1013
1084
  main_frame.type_text(selector, text, delay: delay)
1014
1085
  end
1015
1086
 
1016
- # /**
1017
- # * @param {(string|number|Function)} selectorOrFunctionOrTimeout
1018
- # * @param {!{visible?: boolean, hidden?: boolean, timeout?: number, polling?: string|number}=} options
1019
- # * @param {!Array<*>} args
1020
- # * @return {!Promise<!Puppeteer.JSHandle>}
1021
- # */
1022
- # waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) {
1023
- # return this.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, ...args);
1024
- # }
1087
+ define_async_method :async_type_text
1025
1088
 
1026
1089
  # @param selector [String]
1027
1090
  # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
@@ -1031,13 +1094,7 @@ class Puppeteer::Page
1031
1094
  main_frame.wait_for_selector(selector, visible: visible, hidden: hidden, timeout: timeout)
1032
1095
  end
1033
1096
 
1034
- # @param selector [String]
1035
- # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
1036
- # @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
1037
- # @param timeout [Integer]
1038
- async def async_wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
1039
- wait_for_selector(selector, visible: visible, hidden: hidden, timeout: timeout)
1040
- end
1097
+ define_async_method :async_wait_for_selector
1041
1098
 
1042
1099
  # @param xpath [String]
1043
1100
  # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
@@ -1047,13 +1104,7 @@ class Puppeteer::Page
1047
1104
  main_frame.wait_for_xpath(xpath, visible: visible, hidden: hidden, timeout: timeout)
1048
1105
  end
1049
1106
 
1050
- # @param xpath [String]
1051
- # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
1052
- # @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
1053
- # @param timeout [Integer]
1054
- async def async_wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
1055
- wait_for_xpath(xpath, visible: visible, hidden: hidden, timeout: timeout)
1056
- end
1107
+ define_async_method :async_wait_for_xpath
1057
1108
 
1058
1109
  # @param {Function|string} pageFunction
1059
1110
  # @param {!{polling?: string|number, timeout?: number}=} options
@@ -1062,4 +1113,6 @@ class Puppeteer::Page
1062
1113
  def wait_for_function(page_function, options = {}, *args)
1063
1114
  main_frame.wait_for_function(page_function, options, *args)
1064
1115
  end
1116
+
1117
+ define_async_method :async_wait_for_function
1065
1118
  end