puppeteer-ruby 0.0.14 → 0.0.19

Sign up to get free protection for your applications and to get access to all the features.
@@ -94,5 +94,21 @@ class Puppeteer::Mouse
94
94
  )
95
95
  end
96
96
 
97
+ # Dispatches a `mousewheel` event.
98
+ #
99
+ # @param delta_x [Integer]
100
+ # @param delta_y [Integer]
101
+ def wheel(delta_x: 0, delta_y: 0)
102
+ @client.send_message('Input.dispatchMouseEvent',
103
+ type: 'mouseWheel',
104
+ x: @x,
105
+ y: @y,
106
+ deltaX: delta_x,
107
+ deltaY: delta_y,
108
+ modifiers: @keyboard.modifiers,
109
+ pointerType: 'mouse',
110
+ )
111
+ end
112
+
97
113
  define_async_method :async_up
98
114
  end
@@ -1,5 +1,7 @@
1
1
  class Puppeteer::NetworkManager
2
+ include Puppeteer::DebugPrint
2
3
  include Puppeteer::EventCallbackable
4
+ include Puppeteer::IfPresent
3
5
 
4
6
  class Credentials
5
7
  # @param username [String|NilClass]
@@ -23,19 +25,39 @@ class Puppeteer::NetworkManager
23
25
  @request_id_to_request = {}
24
26
 
25
27
  # @type {!Map<string, !Protocol.Network.requestWillBeSentPayload>}
26
- @request_id_to_request_with_be_sent_event
28
+ @request_id_to_request_with_be_sent_event = {}
27
29
 
28
30
  @extra_http_headers = {}
29
31
 
30
32
  @offline = false
31
33
 
32
- # /** @type {!Set<string>} */
33
- # this._attemptedAuthentications = new Set();
34
+ @attempted_authentications = Set.new
34
35
  @user_request_interception_enabled = false
35
36
  @protocol_request_interception_enabled = false
36
37
  @user_cache_disabled = false
37
- # /** @type {!Map<string, string>} */
38
- # this._requestIdToInterceptionId = new Map();
38
+ @request_id_to_interception_id = {}
39
+
40
+ @client.on_event('Fetch.requestPaused') do |event|
41
+ handle_request_paused(event)
42
+ end
43
+ @client.on_event('Fetch.authRequired') do |event|
44
+ handle_auth_required(event)
45
+ end
46
+ @client.on_event('Network.requestWillBeSent') do |event|
47
+ handle_request_will_be_sent(event)
48
+ end
49
+ @client.on_event('Network.requestServedFromCache') do |event|
50
+ handle_request_served_from_cache(event)
51
+ end
52
+ @client.on_event('Network.responseReceived') do |event|
53
+ handle_response_received(event)
54
+ end
55
+ @client.on_event('Network.loadingFinished') do |event|
56
+ handle_loading_finished(event)
57
+ end
58
+ @client.on_event('Network.loadingFailed') do |event|
59
+ handle_loading_failed(event)
60
+ end
39
61
  end
40
62
 
41
63
  def init
@@ -119,4 +141,140 @@ class Puppeteer::NetworkManager
119
141
  cache_disabled = @user_cache_disabled || @protocol_request_interception_enabled
120
142
  @client.send_message('Network.setCacheDisabled', cacheDisabled: cache_disabled)
121
143
  end
144
+
145
+ private def handle_request_will_be_sent(event)
146
+ # Request interception doesn't happen for data URLs with Network Service.
147
+ if @protocol_request_interception_enabled && !event['request']['url'].start_with?('data:')
148
+ request_id = event['requestId']
149
+ interception_id = @request_id_to_interception_id.delete(request_id)
150
+ if interception_id
151
+ handle_request(event, interception_id)
152
+ else
153
+ @request_id_to_request_with_be_sent_event[request_id] = event
154
+ end
155
+ return
156
+ end
157
+ handle_request(event, nil)
158
+ end
159
+
160
+ private def handle_auth_required(event)
161
+ response = 'Default'
162
+ if @attempted_authentications.include?(event['requestId'])
163
+ response = 'CancelAuth'
164
+ elsif @credentials
165
+ response = 'ProvideCredentials'
166
+ @attempted_authentications << event['requestId']
167
+ end
168
+
169
+ username = @credentials&.username
170
+ password = @credentials&.password
171
+
172
+ begin
173
+ @client.send_message('Fetch.continueWithAuth',
174
+ requestId: event['requestId'],
175
+ authChallengeResponse: {
176
+ response: response,
177
+ username: username,
178
+ password: password,
179
+ },
180
+ )
181
+ rescue => err
182
+ debug_puts(err)
183
+ end
184
+ end
185
+
186
+ private def handle_request_paused(event)
187
+ if !@user_request_interception_enabled && @protocol_request_interception_enabled
188
+ begin
189
+ @client.send_message('Fetch.continueRequest', requestId: event['requestId'])
190
+ rescue => err
191
+ debug_puts(err)
192
+ end
193
+ end
194
+
195
+ request_id = event['networkId']
196
+ interception_id = event['requestId']
197
+ if request_id && (request_will_be_sent_event = @request_id_to_request_with_be_sent_event.delete(request_id))
198
+ handle_request(request_will_be_sent_event, interception_id)
199
+ else
200
+ @request_id_to_interception_id[request_id] = interception_id
201
+ end
202
+ end
203
+
204
+ private def handle_request(event, interception_id)
205
+ redirect_chain = []
206
+ if event['redirectResponse']
207
+ if_present(@request_id_to_request[event['requestId']]) do |request|
208
+ handle_request_redirect(request, event['redirectResponse'])
209
+ redirect_chain = request.internal.redirect_chain
210
+ end
211
+ end
212
+ frame = if_present(event['frameId']) { |frame_id| @frame_manager.frame(frame_id) }
213
+ request = Puppeteer::Request.new(@client, frame, interception_id, @user_request_interception_enabled, event, redirect_chain)
214
+ @request_id_to_request[event['requestId']] = request
215
+ emit_event 'Events.NetworkManager.Request', request
216
+ end
217
+
218
+ private def handle_request_served_from_cache(event)
219
+ if_present(@request_id_to_request[event['requestId']]) do |request|
220
+ request.internal.from_memory_cache = true
221
+ end
222
+ end
223
+
224
+ # @param request [Puppeteer::Request]
225
+ # @param response_payload [Hash]
226
+ private def handle_request_redirect(request, response_payload)
227
+ response = Puppeteer::Response.new(@client, request, response_payload)
228
+ request.internal.response = response
229
+ request.internal.redirect_chain << request
230
+ response.internal.body_loaded_promise.reject(Puppeteer::Response::Redirected.new)
231
+ @request_id_to_request.delete(request.internal.request_id)
232
+ @attempted_authentications.delete(request.internal.interception_id)
233
+ emit_event 'Events.NetworkManager.Response', response
234
+ emit_event 'Events.NetworkManager.RequestFinished', request
235
+ end
236
+
237
+ # @param event [Hash]
238
+ private def handle_response_received(event)
239
+ request = @request_id_to_request[event['requestId']]
240
+ # FileUpload sends a response without a matching request.
241
+ return unless request
242
+
243
+ response = Puppeteer::Response.new(@client, request, event['response'])
244
+ request.internal.response = response
245
+ emit_event 'Events.NetworkManager.Response', response
246
+ end
247
+
248
+ private def handle_loading_finished(event)
249
+ request = @request_id_to_request[event['requestId']]
250
+ # For certain requestIds we never receive requestWillBeSent event.
251
+ # @see https://crbug.com/750469
252
+ return unless request
253
+
254
+
255
+ # Under certain conditions we never get the Network.responseReceived
256
+ # event from protocol. @see https://crbug.com/883475
257
+ if_present(request.response) do |response|
258
+ response.internal.body_loaded_promise.fulfill(nil)
259
+ end
260
+
261
+ @request_id_to_request.delete(request.internal.request_id)
262
+ @attempted_authentications.delete(request.internal.interception_id)
263
+ emit_event 'Events.NetworkManager.RequestFinished', request
264
+ end
265
+
266
+ private def handle_loading_failed(event)
267
+ request = @request_id_to_request[event['requestId']]
268
+ # For certain requestIds we never receive requestWillBeSent event.
269
+ # @see https://crbug.com/750469
270
+ return unless request
271
+
272
+ request.internal.failure_text = event['errorText']
273
+ if_present(request.response) do |response|
274
+ response.internal.body_loaded_promise.fulfill(nil)
275
+ end
276
+ @request_id_to_request.delete(request.internal.request_id)
277
+ @attempted_authentications.delete(request.internal.interception_id)
278
+ emit_event 'Events.NetworkManager.RequestFailed', request
279
+ end
122
280
  end
@@ -1,5 +1,7 @@
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
@@ -264,7 +266,13 @@ class Puppeteer::Page
264
266
  @frame_manager.main_frame
265
267
  end
266
268
 
267
- attr_reader :keyboard, :touch_screen, :coverage, :accessibility
269
+ attr_reader :touch_screen, :coverage, :accessibility
270
+
271
+ def keyboard(&block)
272
+ @keyboard.instance_eval(&block) unless block.nil?
273
+
274
+ @keyboard
275
+ end
268
276
 
269
277
  def frames
270
278
  @frame_manager.frames
@@ -649,13 +657,14 @@ class Puppeteer::Page
649
657
  main_frame.content
650
658
  end
651
659
 
652
- # @param {string} html
653
- # @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
660
+ # @param html [String]
661
+ # @param timeout [Integer]
662
+ # @param wait_until [String|Array<String>]
654
663
  def set_content(html, timeout: nil, wait_until: nil)
655
664
  main_frame.set_content(html, timeout: timeout, wait_until: wait_until)
656
665
  end
657
666
 
658
- # @param {string} html
667
+ # @param html [String]
659
668
  def content=(html)
660
669
  main_frame.set_content(html)
661
670
  end
@@ -678,49 +687,111 @@ class Puppeteer::Page
678
687
  ).first
679
688
  end
680
689
 
681
- # @param timeout [number|nil]
682
- # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
683
690
  private def wait_for_navigation(timeout: nil, wait_until: nil)
684
691
  main_frame.send(:wait_for_navigation, timeout: timeout, wait_until: wait_until)
685
692
  end
686
693
 
694
+ # @!method async_wait_for_navigation(timeout: nil, wait_until: nil)
695
+ #
696
+ # @param timeout [number|nil]
697
+ # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
687
698
  define_async_method :async_wait_for_navigation
688
699
 
689
- # /**
690
- # * @param {(string|Function)} urlOrPredicate
691
- # * @param {!{timeout?: number}=} options
692
- # * @return {!Promise<!Puppeteer.Request>}
693
- # */
694
- # async waitForRequest(urlOrPredicate, options = {}) {
695
- # const {
696
- # timeout = this._timeoutSettings.timeout(),
697
- # } = options;
698
- # return helper.waitForEvent(this._frameManager.networkManager(), Events.NetworkManager.Request, request => {
699
- # if (helper.isString(urlOrPredicate))
700
- # return (urlOrPredicate === request.url());
701
- # if (typeof urlOrPredicate === 'function')
702
- # return !!(urlOrPredicate(request));
703
- # return false;
704
- # }, timeout, this._sessionClosePromise());
705
- # }
700
+ private def wait_for_network_manager_event(event_name, predicate:, timeout:)
701
+ option_timeout = timeout || @timeout_settings.timeout
706
702
 
707
- # /**
708
- # * @param {(string|Function)} urlOrPredicate
709
- # * @param {!{timeout?: number}=} options
710
- # * @return {!Promise<!Puppeteer.Response>}
711
- # */
712
- # async waitForResponse(urlOrPredicate, options = {}) {
713
- # const {
714
- # timeout = this._timeoutSettings.timeout(),
715
- # } = options;
716
- # return helper.waitForEvent(this._frameManager.networkManager(), Events.NetworkManager.Response, response => {
717
- # if (helper.isString(urlOrPredicate))
718
- # return (urlOrPredicate === response.url());
719
- # if (typeof urlOrPredicate === 'function')
720
- # return !!(urlOrPredicate(response));
721
- # return false;
722
- # }, timeout, this._sessionClosePromise());
723
- # }
703
+ @wait_for_network_manager_event_listener_ids ||= {}
704
+ if_present(@wait_for_network_manager_event_listener_ids[event_name]) do |listener_id|
705
+ @frame_manager.network_manager.remove_event_listener(listener_id)
706
+ end
707
+
708
+ promise = resolvable_future
709
+
710
+ @wait_for_network_manager_event_listener_ids[event_name] =
711
+ @frame_manager.network_manager.add_event_listener(event_name) do |event_target|
712
+ if predicate.call(event_target)
713
+ promise.fulfill(nil)
714
+ end
715
+ end
716
+
717
+ begin
718
+ Timeout.timeout(option_timeout / 1000.0) do
719
+ await_any(promise, session_close_promise)
720
+ end
721
+ rescue Timeout::Error
722
+ raise Puppeteer::TimeoutError.new("waiting for #{event_name} failed: timeout #{timeout}ms exceeded")
723
+ ensure
724
+ @frame_manager.network_manager.remove_event_listener(@wait_for_network_manager_event_listener_ids[event_name])
725
+ end
726
+ end
727
+
728
+ private def session_close_promise
729
+ @disconnect_promise ||= resolvable_future do |future|
730
+ @client.observe_first('Events.CDPSession.Disconnected') do
731
+ future.reject(Puppeteer::CDPSession::Error.new('Target Closed'))
732
+ end
733
+ end
734
+ end
735
+
736
+ private def wait_for_request(url: nil, predicate: nil, timeout: nil)
737
+ if !url && !predicate
738
+ raise ArgumentError.new('url or predicate must be specified')
739
+ end
740
+ if predicate && !predicate.is_a?(Proc)
741
+ raise ArgumentError.new('predicate must be a proc.')
742
+ end
743
+ request_predicate =
744
+ if url
745
+ -> (request) { request.url == url }
746
+ else
747
+ -> (request) { predicate.call(request) }
748
+ end
749
+
750
+ wait_for_network_manager_event('Events.NetworkManager.Request',
751
+ predicate: request_predicate,
752
+ timeout: timeout,
753
+ )
754
+ end
755
+
756
+ # @!method async_wait_for_request(url: nil, predicate: nil, timeout: nil)
757
+ #
758
+ # Waits until request URL matches or request matches the given predicate.
759
+ #
760
+ # Waits until request URL matches
761
+ # wait_for_request(url: 'https://example.com/awesome')
762
+ #
763
+ # Waits until request matches the given predicate
764
+ # wait_for_request(predicate: -> (req){ req.url.start_with?('https://example.com/search') })
765
+ #
766
+ # @param url [String]
767
+ # @param predicate [Proc(Puppeteer::Request -> Boolean)]
768
+ define_async_method :async_wait_for_request
769
+
770
+ private def wait_for_response(url: nil, predicate: nil, timeout: nil)
771
+ if !url && !predicate
772
+ raise ArgumentError.new('url or predicate must be specified')
773
+ end
774
+ if predicate && !predicate.is_a?(Proc)
775
+ raise ArgumentError.new('predicate must be a proc.')
776
+ end
777
+ response_predicate =
778
+ if url
779
+ -> (response) { response.url == url }
780
+ else
781
+ -> (response) { predicate.call(response) }
782
+ end
783
+
784
+ wait_for_network_manager_event('Events.NetworkManager.Response',
785
+ predicate: response_predicate,
786
+ timeout: timeout,
787
+ )
788
+ end
789
+
790
+ # @!method async_wait_for_response(url: nil, predicate: nil, timeout: nil)
791
+ #
792
+ # @param url [String]
793
+ # @param predicate [Proc(Puppeteer::Request -> Boolean)]
794
+ define_async_method :async_wait_for_response
724
795
 
725
796
  # @param timeout [number|nil]
726
797
  # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
@@ -759,20 +830,19 @@ class Puppeteer::Page
759
830
  @client.send_message('Emulation.setScriptExecutionDisabled', value: !enabled)
760
831
  end
761
832
 
762
- # /**
763
- # * @param {boolean} enabled
764
- # */
765
- # async setBypassCSP(enabled) {
766
- # await this._client.send('Page.setBypassCSP', { enabled });
767
- # }
833
+ # @param enabled [Boolean]
834
+ def bypass_csp=(enabled)
835
+ @client.send_message('Page.setBypassCSP', enabled: enabled)
836
+ end
768
837
 
769
- # /**
770
- # * @param {?string} type
771
- # */
772
- # async emulateMediaType(type) {
773
- # assert(type === 'screen' || type === 'print' || type === null, 'Unsupported media type: ' + type);
774
- # await this._client.send('Emulation.setEmulatedMedia', {media: type || ''});
775
- # }
838
+ # @param media_type [String|Symbol|nil] either of (media, print, nil)
839
+ def emulate_media_type(media_type)
840
+ media_type_str = media_type.to_s
841
+ unless ['screen', 'print', ''].include?(media_type_str)
842
+ raise ArgumentError.new("Unsupported media type: #{media_type}")
843
+ end
844
+ @client.send_message('Emulation.setEmulatedMedia', media: media_type_str)
845
+ end
776
846
 
777
847
  # /**
778
848
  # * @param {?Array<MediaFeature>} features
@@ -792,7 +862,7 @@ class Puppeteer::Page
792
862
 
793
863
  # @param timezone_id [String?]
794
864
  def emulate_timezone(timezone_id)
795
- @client.send_message('Emulation.setTimezoneOverride', timezoneId: timezoneId || '')
865
+ @client.send_message('Emulation.setTimezoneOverride', timezoneId: timezone_id || '')
796
866
  rescue => err
797
867
  if err.message.include?('Invalid timezone')
798
868
  raise ArgumentError.new("Invalid timezone ID: #{timezone_id}")
@@ -915,60 +985,53 @@ class Puppeteer::Page
915
985
  buffer
916
986
  end
917
987
 
918
- # /**
919
- # * @param {!PDFOptions=} options
920
- # * @return {!Promise<!Buffer>}
921
- # */
922
- # async pdf(options = {}) {
923
- # const {
924
- # scale = 1,
925
- # displayHeaderFooter = false,
926
- # headerTemplate = '',
927
- # footerTemplate = '',
928
- # printBackground = false,
929
- # landscape = false,
930
- # pageRanges = '',
931
- # preferCSSPageSize = false,
932
- # margin = {},
933
- # path = null
934
- # } = options;
935
-
936
- # let paperWidth = 8.5;
937
- # let paperHeight = 11;
938
- # if (options.format) {
939
- # const format = Page.PaperFormats[options.format.toLowerCase()];
940
- # assert(format, 'Unknown paper format: ' + options.format);
941
- # paperWidth = format.width;
942
- # paperHeight = format.height;
943
- # } else {
944
- # paperWidth = convertPrintParameterToInches(options.width) || paperWidth;
945
- # paperHeight = convertPrintParameterToInches(options.height) || paperHeight;
946
- # }
988
+ class ProtocolStreamReader
989
+ def initialize(client:, handle:, path:)
990
+ @client = client
991
+ @handle = handle
992
+ @path = path
993
+ end
947
994
 
948
- # const marginTop = convertPrintParameterToInches(margin.top) || 0;
949
- # const marginLeft = convertPrintParameterToInches(margin.left) || 0;
950
- # const marginBottom = convertPrintParameterToInches(margin.bottom) || 0;
951
- # const marginRight = convertPrintParameterToInches(margin.right) || 0;
952
-
953
- # const result = await this._client.send('Page.printToPDF', {
954
- # transferMode: 'ReturnAsStream',
955
- # landscape,
956
- # displayHeaderFooter,
957
- # headerTemplate,
958
- # footerTemplate,
959
- # printBackground,
960
- # scale,
961
- # paperWidth,
962
- # paperHeight,
963
- # marginTop,
964
- # marginBottom,
965
- # marginLeft,
966
- # marginRight,
967
- # pageRanges,
968
- # preferCSSPageSize
969
- # });
970
- # return await helper.readProtocolStream(this._client, result.stream, path);
971
- # }
995
+ def read
996
+ out = StringIO.new
997
+ File.open(@path, 'w') do |file|
998
+ eof = false
999
+ until eof
1000
+ response = @client.send_message('IO.read', handle: @handle)
1001
+ eof = response['eof']
1002
+ data =
1003
+ if response['base64Encoded']
1004
+ Base64.decode64(response['data'])
1005
+ else
1006
+ response['data']
1007
+ end
1008
+ out.write(data)
1009
+ file.write(data)
1010
+ end
1011
+ end
1012
+ @client.send_message('IO.close', handle: @handle)
1013
+ out.read
1014
+ end
1015
+ end
1016
+
1017
+ class PrintToPdfIsNotImplementedError < StandardError
1018
+ def initialize
1019
+ super('pdf() is only available in headless mode. See https://github.com/puppeteer/puppeteer/issues/1829')
1020
+ end
1021
+ end
1022
+
1023
+ # @return [String]
1024
+ def pdf(options = {})
1025
+ pdf_options = PDFOptions.new(options)
1026
+ result = @client.send_message('Page.printToPDF', pdf_options.page_print_args)
1027
+ ProtocolStreamReader.new(client: @client, handle: result['stream'], path: pdf_options.path).read
1028
+ rescue => err
1029
+ if err.message.include?('PrintToPDF is not implemented')
1030
+ raise PrintToPdfIsNotImplementedError.new
1031
+ else
1032
+ raise
1033
+ end
1034
+ end
972
1035
 
973
1036
  # @param run_before_unload [Boolean]
974
1037
  def close(run_before_unload: false)
@@ -1023,8 +1086,19 @@ class Puppeteer::Page
1023
1086
  define_async_method :async_select
1024
1087
 
1025
1088
  # @param selector [String]
1026
- def tap(selector)
1027
- main_frame.tap(selector)
1089
+ def tap(selector: nil, &block)
1090
+ # resolves double meaning of tap.
1091
+ if selector.nil? && block
1092
+ # Original usage of Object#tap.
1093
+ #
1094
+ # browser.new_page.tap do |page|
1095
+ # ...
1096
+ # end
1097
+ super(&block)
1098
+ else
1099
+ # Puppeteer's Page#tap.
1100
+ main_frame.tap(selector)
1101
+ end
1028
1102
  end
1029
1103
 
1030
1104
  define_async_method :async_tap
@@ -1038,16 +1112,6 @@ class Puppeteer::Page
1038
1112
 
1039
1113
  define_async_method :async_type_text
1040
1114
 
1041
- # /**
1042
- # * @param {(string|number|Function)} selectorOrFunctionOrTimeout
1043
- # * @param {!{visible?: boolean, hidden?: boolean, timeout?: number, polling?: string|number}=} options
1044
- # * @param {!Array<*>} args
1045
- # * @return {!Promise<!Puppeteer.JSHandle>}
1046
- # */
1047
- # waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) {
1048
- # return this.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, ...args);
1049
- # }
1050
-
1051
1115
  # @param selector [String]
1052
1116
  # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
1053
1117
  # @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
@@ -1058,6 +1122,11 @@ class Puppeteer::Page
1058
1122
 
1059
1123
  define_async_method :async_wait_for_selector
1060
1124
 
1125
+ # @param milliseconds [Integer] the number of milliseconds to wait.
1126
+ def wait_for_timeout(milliseconds)
1127
+ main_frame.wait_for_timeout(milliseconds)
1128
+ end
1129
+
1061
1130
  # @param xpath [String]
1062
1131
  # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
1063
1132
  # @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
@@ -1068,12 +1137,13 @@ class Puppeteer::Page
1068
1137
 
1069
1138
  define_async_method :async_wait_for_xpath
1070
1139
 
1071
- # @param {Function|string} pageFunction
1072
- # @param {!{polling?: string|number, timeout?: number}=} options
1073
- # @param {!Array<*>} args
1074
- # @return {!Promise<!Puppeteer.JSHandle>}
1075
- def wait_for_function(page_function, options = {}, *args)
1076
- main_frame.wait_for_function(page_function, options, *args)
1140
+ # @param page_function [String]
1141
+ # @param args [Integer|Array]
1142
+ # @param polling [String]
1143
+ # @param timeout [Integer]
1144
+ # @return [Puppeteer::JSHandle]
1145
+ def wait_for_function(page_function, args: [], polling: nil, timeout: nil)
1146
+ main_frame.wait_for_function(page_function, args: args, polling: polling, timeout: timeout)
1077
1147
  end
1078
1148
 
1079
1149
  define_async_method :async_wait_for_function