puppeteer-ruby 0.0.10 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) 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 +37 -13
  7. data/lib/puppeteer/browser.rb +39 -26
  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 +16 -16
  11. data/lib/puppeteer/define_async_method.rb +23 -0
  12. data/lib/puppeteer/dom_world.rb +58 -71
  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 +38 -3
  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 +8 -33
  24. data/lib/puppeteer/page.rb +132 -121
  25. data/lib/puppeteer/page/pdf_options.rb +166 -0
  26. data/lib/puppeteer/remote_object.rb +2 -5
  27. data/lib/puppeteer/target.rb +1 -1
  28. data/lib/puppeteer/touch_screen.rb +2 -7
  29. data/lib/puppeteer/version.rb +1 -1
  30. data/lib/puppeteer/wait_task.rb +2 -4
  31. data/lib/puppeteer/web_socket_transport.rb +8 -8
  32. data/puppeteer-ruby.gemspec +1 -1
  33. data/puppeteer-ruby.png +0 -0
  34. metadata +9 -102
  35. data/Dockerfile +0 -6
  36. data/docker-compose.yml +0 -15
  37. data/docs/Puppeteer.html +0 -2020
  38. data/docs/Puppeteer/AsyncAwaitBehavior.html +0 -105
  39. data/docs/Puppeteer/Browser.html +0 -2148
  40. data/docs/Puppeteer/BrowserContext.html +0 -809
  41. data/docs/Puppeteer/BrowserFetcher.html +0 -214
  42. data/docs/Puppeteer/BrowserRunner.html +0 -914
  43. data/docs/Puppeteer/BrowserRunner/BrowserProcess.html +0 -477
  44. data/docs/Puppeteer/CDPSession.html +0 -813
  45. data/docs/Puppeteer/CDPSession/Error.html +0 -124
  46. data/docs/Puppeteer/ConcurrentRubyUtils.html +0 -430
  47. data/docs/Puppeteer/Connection.html +0 -960
  48. data/docs/Puppeteer/Connection/MessageCallback.html +0 -434
  49. data/docs/Puppeteer/Connection/ProtocolError.html +0 -216
  50. data/docs/Puppeteer/Connection/RequestDebugPrinter.html +0 -217
  51. data/docs/Puppeteer/Connection/ResponseDebugPrinter.html +0 -244
  52. data/docs/Puppeteer/ConsoleMessage.html +0 -565
  53. data/docs/Puppeteer/ConsoleMessage/Location.html +0 -433
  54. data/docs/Puppeteer/DOMWorld.html +0 -2219
  55. data/docs/Puppeteer/DOMWorld/DetachedError.html +0 -124
  56. data/docs/Puppeteer/DOMWorld/DocumentEvaluationError.html +0 -124
  57. data/docs/Puppeteer/DebugPrint.html +0 -233
  58. data/docs/Puppeteer/Device.html +0 -470
  59. data/docs/Puppeteer/Devices.html +0 -139
  60. data/docs/Puppeteer/ElementHandle.html +0 -2542
  61. data/docs/Puppeteer/ElementHandle/ElementNotFoundError.html +0 -206
  62. data/docs/Puppeteer/ElementHandle/ElementNotVisibleError.html +0 -206
  63. data/docs/Puppeteer/ElementHandle/Point.html +0 -492
  64. data/docs/Puppeteer/ElementHandle/ScrollIntoViewError.html +0 -124
  65. data/docs/Puppeteer/EmulationManager.html +0 -454
  66. data/docs/Puppeteer/EventCallbackable.html +0 -433
  67. data/docs/Puppeteer/EventCallbackable/EventListeners.html +0 -435
  68. data/docs/Puppeteer/ExecutionContext.html +0 -998
  69. data/docs/Puppeteer/ExecutionContext/EvaluationError.html +0 -124
  70. data/docs/Puppeteer/ExecutionContext/JavaScriptExpression.html +0 -357
  71. data/docs/Puppeteer/ExecutionContext/JavaScriptFunction.html +0 -389
  72. data/docs/Puppeteer/FileChooser.html +0 -455
  73. data/docs/Puppeteer/Frame.html +0 -3677
  74. data/docs/Puppeteer/FrameManager.html +0 -2410
  75. data/docs/Puppeteer/FrameManager/NavigationError.html +0 -124
  76. data/docs/Puppeteer/IfPresent.html +0 -222
  77. data/docs/Puppeteer/JSHandle.html +0 -1352
  78. data/docs/Puppeteer/Keyboard.html +0 -1557
  79. data/docs/Puppeteer/Keyboard/KeyDefinition.html +0 -831
  80. data/docs/Puppeteer/Keyboard/KeyDescription.html +0 -603
  81. data/docs/Puppeteer/Launcher.html +0 -237
  82. data/docs/Puppeteer/Launcher/Base.html +0 -385
  83. data/docs/Puppeteer/Launcher/Base/ExecutablePathNotFound.html +0 -124
  84. data/docs/Puppeteer/Launcher/BrowserOptions.html +0 -441
  85. data/docs/Puppeteer/Launcher/Chrome.html +0 -669
  86. data/docs/Puppeteer/Launcher/Chrome/DefaultArgs.html +0 -382
  87. data/docs/Puppeteer/Launcher/ChromeArgOptions.html +0 -531
  88. data/docs/Puppeteer/Launcher/LaunchOptions.html +0 -893
  89. data/docs/Puppeteer/LifecycleWatcher.html +0 -834
  90. data/docs/Puppeteer/LifecycleWatcher/ExpectedLifecycle.html +0 -363
  91. data/docs/Puppeteer/LifecycleWatcher/FrameDetachedError.html +0 -206
  92. data/docs/Puppeteer/LifecycleWatcher/TerminatedError.html +0 -124
  93. data/docs/Puppeteer/Mouse.html +0 -1105
  94. data/docs/Puppeteer/Mouse/Button.html +0 -136
  95. data/docs/Puppeteer/NetworkManager.html +0 -901
  96. data/docs/Puppeteer/NetworkManager/Credentials.html +0 -385
  97. data/docs/Puppeteer/Page.html +0 -5970
  98. data/docs/Puppeteer/Page/FileChooserTimeoutError.html +0 -206
  99. data/docs/Puppeteer/Page/ScreenshotOptions.html +0 -845
  100. data/docs/Puppeteer/Page/ScriptTag.html +0 -555
  101. data/docs/Puppeteer/Page/StyleTag.html +0 -448
  102. data/docs/Puppeteer/Page/TargetCrashedError.html +0 -124
  103. data/docs/Puppeteer/RemoteObject.html +0 -1087
  104. data/docs/Puppeteer/Target.html +0 -1336
  105. data/docs/Puppeteer/Target/InitializeFailure.html +0 -124
  106. data/docs/Puppeteer/Target/TargetInfo.html +0 -729
  107. data/docs/Puppeteer/TimeoutError.html +0 -135
  108. data/docs/Puppeteer/TimeoutSettings.html +0 -496
  109. data/docs/Puppeteer/TouchScreen.html +0 -464
  110. data/docs/Puppeteer/Viewport.html +0 -837
  111. data/docs/Puppeteer/WaitTask.html +0 -637
  112. data/docs/Puppeteer/WaitTask/TerminatedError.html +0 -124
  113. data/docs/Puppeteer/WaitTask/TimeoutError.html +0 -206
  114. data/docs/Puppeteer/WebSocket.html +0 -673
  115. data/docs/Puppeteer/WebSocket/DriverImpl.html +0 -412
  116. data/docs/Puppeteer/WebSocketTransport.html +0 -600
  117. data/docs/Puppeteer/WebSocktTransportError.html +0 -124
  118. data/docs/_index.html +0 -823
  119. data/docs/class_list.html +0 -51
  120. data/docs/css/common.css +0 -1
  121. data/docs/css/full_list.css +0 -58
  122. data/docs/css/style.css +0 -496
  123. data/docs/file.README.html +0 -123
  124. data/docs/file_list.html +0 -56
  125. data/docs/frames.html +0 -17
  126. data/docs/index.html +0 -123
  127. data/docs/js/app.js +0 -314
  128. data/docs/js/full_list.js +0 -216
  129. data/docs/js/jquery.js +0 -4
  130. data/docs/method_list.html +0 -4075
  131. data/docs/top-level-namespace.html +0 -126
  132. 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
@@ -651,15 +684,10 @@ class Puppeteer::Page
651
684
  # @param timeout [number|nil]
652
685
  # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
653
686
  private def wait_for_navigation(timeout: nil, wait_until: nil)
654
- main_frame.wait_for_navigation(timeout: timeout, wait_until: wait_until)
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)
662
- end
690
+ define_async_method :async_wait_for_navigation
663
691
 
664
692
  # /**
665
693
  # * @param {(string|Function)} urlOrPredicate
@@ -792,6 +820,8 @@ class Puppeteer::Page
792
820
  main_frame.evaluate(page_function, *args)
793
821
  end
794
822
 
823
+ define_async_method :async_evaluate
824
+
795
825
  # /**
796
826
  # * @param {Function|string} pageFunction
797
827
  # * @param {!Array<*>} args
@@ -806,9 +836,9 @@ class Puppeteer::Page
806
836
  @frame_manager.network_manager.cache_enabled = enabled
807
837
  end
808
838
 
809
- # @return {!Promise<string>}
839
+ # @return [String]
810
840
  def title
811
- @title
841
+ main_frame.title
812
842
  end
813
843
 
814
844
  # /**
@@ -888,71 +918,66 @@ class Puppeteer::Page
888
918
  buffer
889
919
  end
890
920
 
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;
921
+ class ProtocolStreamReader
922
+ def initialize(client:, handle:, path:)
923
+ @client = client
924
+ @handle = handle
925
+ @path = path
926
+ end
908
927
 
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
- # }
928
+ def read
929
+ out = StringIO.new
930
+ File.open(@path, 'w') do |file|
931
+ eof = false
932
+ until eof
933
+ response = @client.send_message('IO.read', handle: @handle)
934
+ eof = response['eof']
935
+ data =
936
+ if response['base64Encoded']
937
+ Base64.decode64(response['data'])
938
+ else
939
+ response['data']
940
+ end
941
+ out.write(data)
942
+ file.write(data)
943
+ end
944
+ end
945
+ @client.send_message('IO.close', handle: @handle)
946
+ out.read
947
+ end
948
+ end
920
949
 
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
- # }
950
+ class PrintToPdfIsNotImplementedError < StandardError
951
+ def initialize
952
+ super('pdf() is only available in headless mode. See https://github.com/puppeteer/puppeteer/issues/1829')
953
+ end
954
+ end
945
955
 
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
- # }
956
+ # @return [String]
957
+ def pdf(options = {})
958
+ pdf_options = PDFOptions.new(options)
959
+ result = @client.send_message('Page.printToPDF', pdf_options.page_print_args)
960
+ ProtocolStreamReader.new(client: @client, handle: result['stream'], path: pdf_options.path).read
961
+ rescue => err
962
+ if err.message.include?('PrintToPDF is not implemented')
963
+ raise PrintToPdfIsNotImplementedError.new
964
+ else
965
+ raise
966
+ end
967
+ end
968
+
969
+ # @param run_before_unload [Boolean]
970
+ def close(run_before_unload: false)
971
+ unless @client.connection
972
+ raise 'Protocol error: Connection closed. Most likely the page has been closed.'
973
+ end
974
+
975
+ if run_before_unload
976
+ @client.send_message('Page.close')
977
+ else
978
+ @client.connection.send_message('Target.closeTarget', targetId: @target.target_id)
979
+ await @target.is_closed_promise
980
+ end
956
981
  end
957
982
 
958
983
  # @return [boolean]
@@ -970,20 +995,15 @@ class Puppeteer::Page
970
995
  main_frame.click(selector, delay: delay, button: button, click_count: click_count)
971
996
  end
972
997
 
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
998
+ define_async_method :async_click
981
999
 
982
1000
  # @param {string} selector
983
1001
  def focus(selector)
984
1002
  main_frame.focus(selector)
985
1003
  end
986
1004
 
1005
+ define_async_method :async_focus
1006
+
987
1007
  # @param {string} selector
988
1008
  def hover(selector)
989
1009
  main_frame.hover(selector)
@@ -996,15 +1016,14 @@ class Puppeteer::Page
996
1016
  main_frame.select(selector, *values)
997
1017
  end
998
1018
 
1019
+ define_async_method :async_select
1020
+
999
1021
  # @param selector [String]
1000
1022
  def tap(selector)
1001
1023
  main_frame.tap(selector)
1002
1024
  end
1003
1025
 
1004
- # @param selector [String]
1005
- async def async_tap(selector)
1006
- tap(selector)
1007
- end
1026
+ define_async_method :async_tap
1008
1027
 
1009
1028
  # @param selector [String]
1010
1029
  # @param text [String]
@@ -1013,6 +1032,8 @@ class Puppeteer::Page
1013
1032
  main_frame.type_text(selector, text, delay: delay)
1014
1033
  end
1015
1034
 
1035
+ define_async_method :async_type_text
1036
+
1016
1037
  # /**
1017
1038
  # * @param {(string|number|Function)} selectorOrFunctionOrTimeout
1018
1039
  # * @param {!{visible?: boolean, hidden?: boolean, timeout?: number, polling?: string|number}=} options
@@ -1031,13 +1052,7 @@ class Puppeteer::Page
1031
1052
  main_frame.wait_for_selector(selector, visible: visible, hidden: hidden, timeout: timeout)
1032
1053
  end
1033
1054
 
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
1055
+ define_async_method :async_wait_for_selector
1041
1056
 
1042
1057
  # @param xpath [String]
1043
1058
  # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
@@ -1047,13 +1062,7 @@ class Puppeteer::Page
1047
1062
  main_frame.wait_for_xpath(xpath, visible: visible, hidden: hidden, timeout: timeout)
1048
1063
  end
1049
1064
 
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
1065
+ define_async_method :async_wait_for_xpath
1057
1066
 
1058
1067
  # @param {Function|string} pageFunction
1059
1068
  # @param {!{polling?: string|number, timeout?: number}=} options
@@ -1062,4 +1071,6 @@ class Puppeteer::Page
1062
1071
  def wait_for_function(page_function, options = {}, *args)
1063
1072
  main_frame.wait_for_function(page_function, options, *args)
1064
1073
  end
1074
+
1075
+ define_async_method :async_wait_for_function
1065
1076
  end
@@ -0,0 +1,166 @@
1
+ require 'mime/types'
2
+
3
+ class Puppeteer::Page
4
+ # /**
5
+ # * @typedef {Object} PDFOptions
6
+ # * @property {number=} scale
7
+ # * @property {boolean=} displayHeaderFooter
8
+ # * @property {string=} headerTemplate
9
+ # * @property {string=} footerTemplate
10
+ # * @property {boolean=} printBackground
11
+ # * @property {boolean=} landscape
12
+ # * @property {string=} pageRanges
13
+ # * @property {string=} format
14
+ # * @property {string|number=} width
15
+ # * @property {string|number=} height
16
+ # * @property {boolean=} preferCSSPageSize
17
+ # * @property {!{top?: string|number, bottom?: string|number, left?: string|number, right?: string|number}=} margin
18
+ # * @property {string=} path
19
+ # */
20
+ class PDFOptions
21
+ # @params options [Hash]
22
+ def initialize(options)
23
+ unless options[:path]
24
+ # Original puppeteer allows path = nil, however nothing to do without path actually.
25
+ # Also in most case, users forget to specify path parameter. So let's raise ArgumentError.
26
+ raise ArgumentError('"path" parameter must be specified.')
27
+ end
28
+
29
+ @scale = options[:scale]
30
+ @display_header_footer = options[:display_header_footer]
31
+ @header_template = options[:header_template]
32
+ @footer_template = options[:footer_template]
33
+ @print_background = options[:print_background]
34
+ @landscape = options[:landscape]
35
+ @page_ranges = options[:page_ranges]
36
+ @format = options[:format]
37
+ @width = options[:width]
38
+ @height = options[:height]
39
+ @prefer_css_page_size = options[:prefer_css_page_size]
40
+ @margin = Margin.new(options[:margin] || {})
41
+ @path = options[:path]
42
+ end
43
+
44
+ attr_reader :path
45
+
46
+ class PaperSize
47
+ def initialize(width:, height:)
48
+ @width = width
49
+ @height = height
50
+ end
51
+ attr_reader :width, :height
52
+ end
53
+
54
+ PAPER_FORMATS = {
55
+ letter: PaperSize.new(width: 8.5, height: 11),
56
+ legal: PaperSize.new(width: 8.5, height: 14),
57
+ tabloid: PaperSize.new(width: 11, height: 17),
58
+ ledger: PaperSize.new(width: 17, height: 11),
59
+ a0: PaperSize.new(width: 33.1, height: 46.8),
60
+ a1: PaperSize.new(width: 23.4, height: 33.1),
61
+ a2: PaperSize.new(width: 16.54, height: 23.4),
62
+ a3: PaperSize.new(width: 11.7, height: 16.54),
63
+ a4: PaperSize.new(width: 8.27, height: 11.7),
64
+ a5: PaperSize.new(width: 5.83, height: 8.27),
65
+ a6: PaperSize.new(width: 4.13, height: 5.83),
66
+ }
67
+
68
+ UNIT_TO_PIXELS = {
69
+ px: 1,
70
+ in: 96,
71
+ cm: 37.8,
72
+ mm: 3.78,
73
+ }
74
+
75
+ # @param parameter [String|Integer|nil]
76
+ private def convert_print_parameter_to_inches(parameter)
77
+ return nil if parameter.nil?
78
+
79
+ pixels =
80
+ if parameter.is_a?(Numeric)
81
+ parameter.to_i
82
+ elsif parameter.is_a?(String)
83
+ unit = parameter[-2..-1].downcase
84
+ value =
85
+ if UNIT_TO_PIXELS.has_key?(unit)
86
+ parameter[0...-2].to_i
87
+ else
88
+ unit = 'px'
89
+ parameter.to_i
90
+ end
91
+
92
+ value * UNIT_TO_PIXELS[unit]
93
+ else
94
+ raise ArgumentError.new("page.pdf() Cannot handle parameter type: #{parameter.class}")
95
+ end
96
+
97
+ pixels / 96
98
+ end
99
+
100
+ private def paper_size
101
+ @paper_size ||= calc_paper_size
102
+ end
103
+
104
+ # @return [PaperSize]
105
+ private def calc_paper_size
106
+ if @format
107
+ PAPER_FORMATS[@format.downcase] or raise ArgumentError.new("Unknown paper format: #{@format}")
108
+ else
109
+ PaperSize.new(
110
+ width: convert_print_parameter_to_inches(@width) || 8.5,
111
+ height: convert_print_parameter_to_inches(@height) || 11.0,
112
+ )
113
+ end
114
+ end
115
+
116
+ class Margin
117
+ def initialize(options)
118
+ @top = options[:top]
119
+ @bottom = options[:bottom]
120
+ @left = options[:left]
121
+ @right = options[:right]
122
+ end
123
+
124
+ def translate(&block)
125
+ new_margin ={
126
+ top: block.call(@top),
127
+ bottom: block.call(@bottom),
128
+ left: block.call(@left),
129
+ right: block.call(@right),
130
+ }
131
+ Margin.new(new_margin)
132
+ end
133
+ attr_reader :top, :bottom, :left, :right
134
+ end
135
+
136
+ private def margin
137
+ @__margin ||= calc_margin
138
+ end
139
+
140
+ private def calc_margin
141
+ @margin.translate do |value|
142
+ convert_print_parameter_to_inches(value) || 0
143
+ end
144
+ end
145
+
146
+ def page_print_args
147
+ {
148
+ transferMode: 'ReturnAsStream',
149
+ landscape: @landscape || false,
150
+ displayHeaderFooter: @display_header_footer || false,
151
+ headerTemplate: @header_template || '',
152
+ footerTemplate: @footer_template || '',
153
+ printBackground: @print_background || false,
154
+ scale: @scale || 1,
155
+ paperWidth: paper_size.width,
156
+ paperHeight: paper_size.height,
157
+ marginTop: margin.top,
158
+ marginBottom: margin.bottom,
159
+ marginLeft: margin.left,
160
+ marginRight: margin.right,
161
+ pageRanges: @page_ranges || '',
162
+ preferCSSPageSize: @prefer_css_page_size || false,
163
+ }
164
+ end
165
+ end
166
+ end