puppeteer-ruby 0.0.14 → 0.0.19

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.
@@ -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