puppeteer-ruby 0.0.4 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +30 -0
  3. data/.rubocop.yml +4 -5
  4. data/README.md +3 -2
  5. data/docs/Puppeteer.html +2020 -0
  6. data/docs/Puppeteer/AsyncAwaitBehavior.html +105 -0
  7. data/docs/Puppeteer/Browser.html +2148 -0
  8. data/docs/Puppeteer/BrowserContext.html +809 -0
  9. data/docs/Puppeteer/BrowserFetcher.html +214 -0
  10. data/docs/Puppeteer/BrowserRunner.html +914 -0
  11. data/docs/Puppeteer/BrowserRunner/BrowserProcess.html +477 -0
  12. data/docs/Puppeteer/CDPSession.html +813 -0
  13. data/docs/Puppeteer/CDPSession/Error.html +124 -0
  14. data/docs/Puppeteer/ConcurrentRubyUtils.html +430 -0
  15. data/docs/Puppeteer/Connection.html +960 -0
  16. data/docs/Puppeteer/Connection/MessageCallback.html +434 -0
  17. data/docs/Puppeteer/Connection/ProtocolError.html +216 -0
  18. data/docs/Puppeteer/Connection/RequestDebugPrinter.html +217 -0
  19. data/docs/Puppeteer/Connection/ResponseDebugPrinter.html +244 -0
  20. data/docs/Puppeteer/ConsoleMessage.html +565 -0
  21. data/docs/Puppeteer/ConsoleMessage/Location.html +433 -0
  22. data/docs/Puppeteer/DOMWorld.html +2219 -0
  23. data/docs/Puppeteer/DOMWorld/DetachedError.html +124 -0
  24. data/docs/Puppeteer/DOMWorld/DocumentEvaluationError.html +124 -0
  25. data/docs/Puppeteer/DebugPrint.html +233 -0
  26. data/docs/Puppeteer/Device.html +470 -0
  27. data/docs/Puppeteer/Devices.html +139 -0
  28. data/docs/Puppeteer/ElementHandle.html +2542 -0
  29. data/docs/Puppeteer/ElementHandle/ElementNotFoundError.html +206 -0
  30. data/docs/Puppeteer/ElementHandle/ElementNotVisibleError.html +206 -0
  31. data/docs/Puppeteer/ElementHandle/Point.html +492 -0
  32. data/docs/Puppeteer/ElementHandle/ScrollIntoViewError.html +124 -0
  33. data/docs/Puppeteer/EmulationManager.html +454 -0
  34. data/docs/Puppeteer/EventCallbackable.html +433 -0
  35. data/docs/Puppeteer/EventCallbackable/EventListeners.html +435 -0
  36. data/docs/Puppeteer/ExecutionContext.html +998 -0
  37. data/docs/Puppeteer/ExecutionContext/EvaluationError.html +124 -0
  38. data/docs/Puppeteer/ExecutionContext/JavaScriptExpression.html +357 -0
  39. data/docs/Puppeteer/ExecutionContext/JavaScriptFunction.html +389 -0
  40. data/docs/Puppeteer/FileChooser.html +455 -0
  41. data/docs/Puppeteer/Frame.html +3677 -0
  42. data/docs/Puppeteer/FrameManager.html +2410 -0
  43. data/docs/Puppeteer/FrameManager/NavigationError.html +124 -0
  44. data/docs/Puppeteer/IfPresent.html +222 -0
  45. data/docs/Puppeteer/JSHandle.html +1352 -0
  46. data/docs/Puppeteer/Keyboard.html +1557 -0
  47. data/docs/Puppeteer/Keyboard/KeyDefinition.html +831 -0
  48. data/docs/Puppeteer/Keyboard/KeyDescription.html +603 -0
  49. data/docs/Puppeteer/Launcher.html +237 -0
  50. data/docs/Puppeteer/Launcher/Base.html +385 -0
  51. data/docs/Puppeteer/Launcher/Base/ExecutablePathNotFound.html +124 -0
  52. data/docs/Puppeteer/Launcher/BrowserOptions.html +441 -0
  53. data/docs/Puppeteer/Launcher/Chrome.html +669 -0
  54. data/docs/Puppeteer/Launcher/Chrome/DefaultArgs.html +382 -0
  55. data/docs/Puppeteer/Launcher/ChromeArgOptions.html +531 -0
  56. data/docs/Puppeteer/Launcher/LaunchOptions.html +893 -0
  57. data/docs/Puppeteer/LifecycleWatcher.html +834 -0
  58. data/docs/Puppeteer/LifecycleWatcher/ExpectedLifecycle.html +363 -0
  59. data/docs/Puppeteer/LifecycleWatcher/FrameDetachedError.html +206 -0
  60. data/docs/Puppeteer/LifecycleWatcher/TerminatedError.html +124 -0
  61. data/docs/Puppeteer/Mouse.html +1105 -0
  62. data/docs/Puppeteer/Mouse/Button.html +136 -0
  63. data/docs/Puppeteer/NetworkManager.html +901 -0
  64. data/docs/Puppeteer/NetworkManager/Credentials.html +385 -0
  65. data/docs/Puppeteer/Page.html +5970 -0
  66. data/docs/Puppeteer/Page/FileChooserTimeoutError.html +206 -0
  67. data/docs/Puppeteer/Page/ScreenshotOptions.html +845 -0
  68. data/docs/Puppeteer/Page/ScriptTag.html +555 -0
  69. data/docs/Puppeteer/Page/StyleTag.html +448 -0
  70. data/docs/Puppeteer/Page/TargetCrashedError.html +124 -0
  71. data/docs/Puppeteer/RemoteObject.html +1087 -0
  72. data/docs/Puppeteer/Target.html +1336 -0
  73. data/docs/Puppeteer/Target/InitializeFailure.html +124 -0
  74. data/docs/Puppeteer/Target/TargetInfo.html +729 -0
  75. data/docs/Puppeteer/TimeoutError.html +135 -0
  76. data/docs/Puppeteer/TimeoutSettings.html +496 -0
  77. data/docs/Puppeteer/TouchScreen.html +464 -0
  78. data/docs/Puppeteer/Viewport.html +837 -0
  79. data/docs/Puppeteer/WaitTask.html +637 -0
  80. data/docs/Puppeteer/WaitTask/TerminatedError.html +124 -0
  81. data/docs/Puppeteer/WaitTask/TimeoutError.html +206 -0
  82. data/docs/Puppeteer/WebSocket.html +673 -0
  83. data/docs/Puppeteer/WebSocket/DriverImpl.html +412 -0
  84. data/docs/Puppeteer/WebSocketTransport.html +600 -0
  85. data/docs/Puppeteer/WebSocktTransportError.html +124 -0
  86. data/docs/_index.html +823 -0
  87. data/docs/class_list.html +51 -0
  88. data/docs/css/common.css +1 -0
  89. data/docs/css/full_list.css +58 -0
  90. data/docs/css/style.css +496 -0
  91. data/docs/file.README.html +123 -0
  92. data/docs/file_list.html +56 -0
  93. data/docs/frames.html +17 -0
  94. data/docs/index.html +123 -0
  95. data/docs/js/app.js +314 -0
  96. data/docs/js/full_list.js +216 -0
  97. data/docs/js/jquery.js +4 -0
  98. data/docs/method_list.html +4075 -0
  99. data/docs/top-level-namespace.html +126 -0
  100. data/lib/puppeteer.rb +16 -8
  101. data/lib/puppeteer/async_await_behavior.rb +6 -0
  102. data/lib/puppeteer/browser.rb +25 -6
  103. data/lib/puppeteer/browser_runner.rb +1 -1
  104. data/lib/puppeteer/cdp_session.rb +33 -11
  105. data/lib/puppeteer/connection.rb +1 -1
  106. data/lib/puppeteer/dom_world.rb +121 -104
  107. data/lib/puppeteer/element_handle.rb +186 -224
  108. data/lib/puppeteer/element_handle/bounding_box.rb +12 -0
  109. data/lib/puppeteer/element_handle/box_model.rb +19 -0
  110. data/lib/puppeteer/element_handle/point.rb +26 -0
  111. data/lib/puppeteer/errors.rb +1 -3
  112. data/lib/puppeteer/execution_context.rb +36 -17
  113. data/lib/puppeteer/file_chooser.rb +29 -0
  114. data/lib/puppeteer/frame.rb +17 -11
  115. data/lib/puppeteer/frame_manager.rb +1 -3
  116. data/lib/puppeteer/js_handle.rb +3 -2
  117. data/lib/puppeteer/launcher.rb +0 -1
  118. data/lib/puppeteer/launcher/chrome.rb +48 -2
  119. data/lib/puppeteer/lifecycle_watcher.rb +3 -3
  120. data/lib/puppeteer/page.rb +121 -68
  121. data/lib/puppeteer/remote_object.rb +15 -1
  122. data/lib/puppeteer/target.rb +24 -24
  123. data/lib/puppeteer/version.rb +1 -1
  124. data/lib/puppeteer/viewport.rb +18 -0
  125. data/lib/puppeteer/wait_task.rb +183 -1
  126. data/lib/puppeteer/web_socket.rb +3 -1
  127. data/lib/puppeteer/web_socket_transport.rb +1 -1
  128. data/puppeteer-ruby.gemspec +4 -1
  129. metadata +145 -4
@@ -0,0 +1,12 @@
1
+ class Puppeteer::ElementHandle < Puppeteer::JSHandle
2
+ class BoundingBox
3
+ def initialize(x:, y:, width:, height:)
4
+ @x = x
5
+ @y = y
6
+ @width = width
7
+ @height = height
8
+ end
9
+
10
+ attr_reader :x, :y, :width, :height
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ class Puppeteer::ElementHandle < Puppeteer::JSHandle
2
+ class BoxModel
3
+ QUAD_ATTRIBUTE_NAMES = %i(content padding border margin)
4
+ # @param result [Hash]
5
+ def initialize(result_model)
6
+ QUAD_ATTRIBUTE_NAMES.each do |attr_name|
7
+ quad = result_model[attr_name.to_s]
8
+ instance_variable_set(
9
+ :"@#{attr_name}",
10
+ quad.each_slice(2).map { |x, y| Point.new(x: x, y: y) },
11
+ )
12
+ end
13
+ @width = result_model['width']
14
+ @height = result_model['height']
15
+ end
16
+ attr_reader(*QUAD_ATTRIBUTE_NAMES)
17
+ attr_reader :width, :height
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ class Puppeteer::ElementHandle < Puppeteer::JSHandle
2
+ # A class to represent (x, y)-coordinates
3
+ # supporting + and / operators.
4
+ class Point
5
+ def initialize(x:, y:)
6
+ @x = x
7
+ @y = y
8
+ end
9
+
10
+ def +(other)
11
+ Point.new(
12
+ x: @x + other.x,
13
+ y: @y + other.y,
14
+ )
15
+ end
16
+
17
+ def /(num)
18
+ Point.new(
19
+ x: @x / num,
20
+ y: @y / num,
21
+ )
22
+ end
23
+
24
+ attr_reader :x, :y
25
+ end
26
+ end
@@ -1,4 +1,2 @@
1
- # ref: https://github.com/puppeteer/puppeteer/blob/master/lib/Errors.js
1
+ # ref: https://github.com/puppeteer/puppeteer/blob/master/src/Errors.ts
2
2
  class Puppeteer::TimeoutError < StandardError; end
3
-
4
- class Puppeteer::WebSocktTransportError < StandardError; end
@@ -122,7 +122,7 @@ class Puppeteer::ExecutionContext
122
122
  remote_object = Puppeteer::RemoteObject.new(result['result'])
123
123
 
124
124
  if exception_details
125
- raise EvaluationError.new("Evaluation failed: #{exceptionDetails}")
125
+ raise EvaluationError.new("Evaluation failed: #{exception_details}")
126
126
  end
127
127
 
128
128
  if @return_by_value
@@ -216,20 +216,39 @@ class Puppeteer::ExecutionContext
216
216
  # return createJSHandle(this, response.objects);
217
217
  # }
218
218
 
219
- # /**
220
- # * @param {Puppeteer.ElementHandle} elementHandle
221
- # * @return {Promise<Puppeteer.ElementHandle>}
222
- # */
223
- # async _adoptElementHandle(elementHandle) {
224
- # assert(elementHandle.executionContext() !== this, 'Cannot adopt handle that already belongs to this execution context');
225
- # assert(this._world, 'Cannot adopt handle without DOMWorld');
226
- # const nodeInfo = await this._client.send('DOM.describeNode', {
227
- # objectId: elementHandle._remoteObject.objectId,
228
- # });
229
- # const {object} = await this._client.send('DOM.resolveNode', {
230
- # backendNodeId: nodeInfo.node.backendNodeId,
231
- # executionContextId: this._contextId,
232
- # });
233
- # return /** @type {Puppeteer.ElementHandle}*/(createJSHandle(this, object));
234
- # }
219
+ # @param backend_node_id [Integer]
220
+ # @return [Puppeteer::ElementHandle]
221
+ def adopt_backend_node_id(backend_node_id)
222
+ response = @client.send_message('DOM.resolveNode',
223
+ backendNodeId: backend_node_id,
224
+ executionContextId: @context_id,
225
+ )
226
+ Puppeteer::JSHandle.create(
227
+ context: self,
228
+ remote_object: Puppeteer::RemoteObject.new(response["object"]),
229
+ )
230
+ end
231
+
232
+ # @param element_handle [Puppeteer::ElementHandle]
233
+ # @return [Puppeteer::ElementHandle]
234
+ def adopt_element_handle(element_handle)
235
+ if element_handle.execution_context == self
236
+ raise ArgumentError.new('Cannot adopt handle that already belongs to this execution context')
237
+ end
238
+
239
+ unless @world
240
+ raise 'Cannot adopt handle without DOMWorld'
241
+ end
242
+
243
+ node_info = element_handle.remote_object.node_info(@client)
244
+ response = @client.send_message('DOM.resolveNode',
245
+ backendNodeId: node_info["node"]["backendNodeId"],
246
+ executionContextId: @context_id,
247
+ )
248
+
249
+ Puppeteer::JSHandle.create(
250
+ context: self,
251
+ remote_object: Puppeteer::RemoteObject.new(response["object"]),
252
+ )
253
+ end
235
254
  end
@@ -0,0 +1,29 @@
1
+ class Puppeteer::FileChooser
2
+ # @param element [Puppeteer::ElementHandle]
3
+ # @param event [Hash]
4
+ def initialize(element, event)
5
+ @element = element
6
+ @multiple = event['mode'] != 'selectSingle'
7
+ @handled = false
8
+ end
9
+
10
+ def multiple?
11
+ @multiple
12
+ end
13
+
14
+ # @param file_paths [Array<String>]
15
+ def accept(file_paths)
16
+ if @handled
17
+ raise 'Cannot accept FileChooser which is already handled!'
18
+ end
19
+ @handled = true
20
+ @element.upload_file(*file_paths)
21
+ end
22
+
23
+ def cancel
24
+ if @handled
25
+ raise 'Cannot cancel FileChooser which is already handled!'
26
+ end
27
+ @handled = true
28
+ end
29
+ end
@@ -173,11 +173,11 @@ class Puppeteer::Frame
173
173
  @secondary_world.tap(selector)
174
174
  end
175
175
 
176
- # @param {string} selector
177
- # @param {string} text
178
- # @param {{delay: (number|undefined)}=} options
179
- def type(selector, text, delay: nil)
180
- @main_world.type(selector, text, delay: delay)
176
+ # @param selector [String]
177
+ # @param text [String]
178
+ # @param delay [Number]
179
+ def type_text(selector, text, delay: nil)
180
+ @main_world.type_text(selector, text, delay: delay)
181
181
  end
182
182
 
183
183
  # /**
@@ -202,9 +202,10 @@ class Puppeteer::Frame
202
202
  # return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout)));
203
203
  # }
204
204
 
205
- # @param {string} selector
206
- # @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
207
- # @return {!Promise<?Puppeteer.ElementHandle>}
205
+ # @param selector [String]
206
+ # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
207
+ # @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
208
+ # @param timeout [Integer]
208
209
  def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
209
210
  handle = @secondary_world.wait_for_selector(selector, visible: visible, hidden: hidden, timeout: timeout)
210
211
  if !handle
@@ -216,9 +217,10 @@ class Puppeteer::Frame
216
217
  result
217
218
  end
218
219
 
219
- # @param {string} xpath
220
- # @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
221
- # @return {!Promise<?Puppeteer.ElementHandle>}
220
+ # @param xpath [String]
221
+ # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
222
+ # @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
223
+ # @param timeout [Integer]
222
224
  def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
223
225
  handle = @secondary_world.wait_for_xpath(xpath, visible: visible, hidden: hidden, timeout: timeout)
224
226
  if !handle
@@ -248,6 +250,10 @@ class Puppeteer::Frame
248
250
  # TODO(lushnikov): remove this once requestInterception has loaderId exposed.
249
251
  @navigation_url = frame_payload['url']
250
252
  @url = frame_payload['url']
253
+
254
+ # Ensure loaderId updated.
255
+ # The order of [Page.lifecycleEvent name="init"] and [Page.frameNavigated] is random... for some reason...
256
+ @loader_id = frame_payload['loaderId']
251
257
  end
252
258
 
253
259
  # @param url [String]
@@ -219,7 +219,7 @@ class Puppeteer::FrameManager
219
219
 
220
220
  # @param frame_payload [Hash]
221
221
  def handle_frame_navigated(frame_payload)
222
- is_main_frame = !frame_payload['parent_id']
222
+ is_main_frame = !frame_payload['parentId']
223
223
  frame =
224
224
  if is_main_frame
225
225
  @main_frame
@@ -285,8 +285,6 @@ class Puppeteer::FrameManager
285
285
  frame.navigated_within_document(url)
286
286
  emit_event 'Events.FrameManager.FrameNavigatedWithinDocument', frame
287
287
  emit_event 'Events.FrameManager.FrameNavigated', frame
288
- handle_frame_manager_frame_navigated_within_document(frame)
289
- handle_frame_manager_frame_navigated(frame)
290
288
  end
291
289
 
292
290
  # @param frame_id [String]
@@ -89,8 +89,9 @@ class Puppeteer::JSHandle
89
89
  response['result'].each_with_object({}) do |prop, h|
90
90
  next unless prop['enumerable']
91
91
  h[prop['name']] = Puppeteer::JSHandle.create(
92
- context: @context,
93
- remote_object: Puppeteer::RemoteObject.new(prop['value']))
92
+ context: @context,
93
+ remote_object: Puppeteer::RemoteObject.new(prop['value']),
94
+ )
94
95
  end
95
96
  end
96
97
 
@@ -6,7 +6,6 @@ require_relative './launcher/launch_options'
6
6
 
7
7
  # https://github.com/puppeteer/puppeteer/blob/master/lib/Launcher.js
8
8
  module Puppeteer::Launcher
9
-
10
9
  # @param {string} projectRoot
11
10
  # @param {string} preferredRevision
12
11
  # @param {boolean} isPuppeteerCore
@@ -149,8 +149,54 @@ module Puppeteer::Launcher
149
149
 
150
150
  # @param {!(Launcher.BrowserOptions & {browserWSEndpoint?: string, browserURL?: string, transport?: !Puppeteer.ConnectionTransport})} options
151
151
  # @return {!Promise<!Browser>}
152
- def connect(options)
153
- raise NotImplementedError.new('Puppeteer.connect is not implemented yet')
152
+ def connect(options = {})
153
+ @browser_options = BrowserOptions.new(options)
154
+ browser_ws_endpoint = options[:browser_ws_endpoint]
155
+ browser_url = options[:browser_url]
156
+ transport = options[:transport]
157
+
158
+ connection =
159
+ if browser_ws_endpoint && browser_url.nil? && transport.nil?
160
+ connect_with_browser_ws_endpoint(browser_ws_endpoint)
161
+ elsif browser_ws_endpoint.nil? && browser_url && transport.nil?
162
+ connect_with_browser_url(browser_url)
163
+ elsif browser_ws_endpoint.nil? && browser_url.nil? && transport
164
+ connect_with_transport(transport)
165
+ else
166
+ raise ArgumentError.new("Exactly one of browserWSEndpoint, browserURL or transport must be passed to puppeteer.connect")
167
+ end
168
+
169
+ result = connection.send_message('Target.getBrowserContexts')
170
+ browser_context_ids = result['browserContextIds']
171
+
172
+ Puppeteer::Browser.create(
173
+ connection: connection,
174
+ context_ids: browser_context_ids,
175
+ ignore_https_errors: @browser_options.ignore_https_errors?,
176
+ default_viewport: @browser_options.default_viewport,
177
+ process: nil,
178
+ close_callback: -> { connection.send_message('Browser.close') },
179
+ )
180
+ end
181
+
182
+ # @return [Puppeteer::Connection]
183
+ private def connect_with_browser_ws_endpoint(browser_ws_endpoint)
184
+ transport = Puppeteer::WebSocketTransport.create(browser_ws_endpoint)
185
+ Puppeteer::Connection.new(browser_ws_endpoint, transport, @browser_options.slow_mo)
186
+ end
187
+
188
+ # @return [Puppeteer::Connection]
189
+ private def connect_with_browser_url(browser_url)
190
+ raise NotImplementedError.new('Puppeteer#connect with browserUrl is not implemented yet.')
191
+ # const connectionURL = await getWSEndpoint(browserURL);
192
+ # const connectionTransport = await WebSocketTransport.create(
193
+ # connectionURL
194
+ # );
195
+ end
196
+
197
+ # @return [Puppeteer::Connection]
198
+ private def connect_with_transport(transport)
199
+ Puppeteer::Connection.new('', transport, @browser_options.slow_mo)
154
200
  end
155
201
 
156
202
  # @return {string}
@@ -73,7 +73,7 @@ class Puppeteer::LifecycleWatcher
73
73
  terminate(TerminatedError.new('Navigation failed because browser has disconnected!'))
74
74
  end
75
75
  @listener_ids['frame_manager'] = [
76
- @frame_manager.add_event_listener('Events.FrameManager.LifecycleEvent') do |frame|
76
+ @frame_manager.add_event_listener('Events.FrameManager.LifecycleEvent') do |_|
77
77
  check_lifecycle_complete
78
78
  end,
79
79
  @frame_manager.add_event_listener('Events.FrameManager.FrameNavigatedWithinDocument', &method(:navigated_within_document)),
@@ -149,10 +149,10 @@ class Puppeteer::LifecycleWatcher
149
149
  if @frame.loader_id == @initial_loader_id && !@has_same_document_navigation
150
150
  return
151
151
  end
152
- if @has_same_document_navigation
152
+ if @has_same_document_navigation && @same_document_navigation_promise.pending?
153
153
  @same_document_navigation_promise.fulfill(true)
154
154
  end
155
- if @frame.loader_id != @initial_loader_id
155
+ if @frame.loader_id != @initial_loader_id && @new_document_navigation_promise.pending?
156
156
  @new_document_navigation_promise.fulfill(true)
157
157
  end
158
158
  end
@@ -48,10 +48,10 @@ class Puppeteer::Page
48
48
  if event['targetInfo']['type'] != 'worker'
49
49
  # If we don't detach from service workers, they will never die.
50
50
  await @client.send_message('Target.detachFromTarget', sessionId: event['sessionId'])
51
- return
51
+ next
52
52
  end
53
53
 
54
- session = Puppeteer::Connection.from_session(@client).session(event['sessionId'])
54
+ session = Puppeteer::Connection.from_session(@client).session(event['sessionId']) # rubocop:disable Lint/UselessAssignment
55
55
  # const worker = new Worker(session, event.targetInfo.url, this._addConsoleMessage.bind(this), this._handleException.bind(this));
56
56
  # this._workers.set(event.sessionId, worker);
57
57
  # this.emit(Events.Page.WorkerCreated, worker);
@@ -59,7 +59,7 @@ class Puppeteer::Page
59
59
  @client.on_event 'Target.detachedFromTarget' do |event|
60
60
  session_id = event['sessionId']
61
61
  worker = @workers[session_id]
62
- return unless worker
62
+ next unless worker
63
63
 
64
64
  emit_event('Events.Page.WorkerDestroyed', worker)
65
65
  @workers.delete(session_id)
@@ -88,8 +88,8 @@ class Puppeteer::Page
88
88
  network_manager.on_event 'Events.NetworkManager.RequestFinished' do |event|
89
89
  emit_event 'Events.Page.RequestFinished', event
90
90
  end
91
- # this._fileChooserInterceptionIsDisabled = false;
92
- # this._fileChooserInterceptors = new Set();
91
+ @file_chooser_interception_is_disabled = false
92
+ @file_chooser_interceptors = Set.new
93
93
 
94
94
  @client.on_event 'Page.domContentEventFired' do |event|
95
95
  emit_event 'Events.Page.DOMContentLoaded'
@@ -106,8 +106,10 @@ class Puppeteer::Page
106
106
  @client.on_event 'Log.entryAdded' do |event|
107
107
  handle_log_entry_added(event)
108
108
  end
109
- # client.on('Page.fileChooserOpened', event => this._onFileChooser(event));
110
- @target.on_close do
109
+ @client.on_event 'Page.fileChooserOpened' do |event|
110
+ handle_file_chooser(event)
111
+ end
112
+ @target.is_closed_promise.then do
111
113
  emit_event 'Events.Page.Close'
112
114
  @closed = true
113
115
  end
@@ -122,41 +124,53 @@ class Puppeteer::Page
122
124
  )
123
125
  end
124
126
 
125
- # /**
126
- # * @param {!Protocol.Page.fileChooserOpenedPayload} event
127
- # */
128
- # async _onFileChooser(event) {
129
- # if (!this._fileChooserInterceptors.size)
130
- # return;
131
- # const frame = this._frameManager.frame(event.frameId);
132
- # const context = await frame.executionContext();
133
- # const element = await context._adoptBackendNodeId(event.backendNodeId);
134
- # const interceptors = Array.from(this._fileChooserInterceptors);
135
- # this._fileChooserInterceptors.clear();
136
- # const fileChooser = new FileChooser(this._client, element, event);
137
- # for (const interceptor of interceptors)
138
- # interceptor.call(null, fileChooser);
139
- # }
127
+ def handle_file_chooser(event)
128
+ return if @file_chooser_interceptors.empty?
129
+
130
+ frame = @frame_manager.frame(event['frameId'])
131
+ context = frame.execution_context
132
+ element = context.adopt_backend_node_id(event['backendNodeId'])
133
+ interceptors = @file_chooser_interceptors.to_a
134
+ @file_chooser_interceptors.clear
135
+ file_chooser = Puppeteer::FileChooser.new(element, event)
136
+ interceptors.each do |promise|
137
+ promise.fulfill(file_chooser)
138
+ end
139
+ end
140
140
 
141
- # /**
142
- # * @param {!{timeout?: number}=} options
143
- # * @return !Promise<!FileChooser>}
144
- # */
145
- # async waitForFileChooser(options = {}) {
146
- # if (!this._fileChooserInterceptors.size)
147
- # await this._client.send('Page.setInterceptFileChooserDialog', {enabled: true});
141
+ class FileChooserTimeoutError < StandardError
142
+ def initialize(timeout:)
143
+ super("waiting for filechooser failed: timeout #{timeout}ms exceeded")
144
+ end
145
+ end
148
146
 
149
- # const {
150
- # timeout = this._timeoutSettings.timeout(),
151
- # } = options;
152
- # let callback;
153
- # const promise = new Promise(x => callback = x);
154
- # this._fileChooserInterceptors.add(callback);
155
- # return helper.waitWithTimeout(promise, 'waiting for file chooser', timeout).catch(e => {
156
- # this._fileChooserInterceptors.delete(callback);
157
- # throw e;
158
- # });
159
- # }
147
+ # @param timeout [Integer]
148
+ # @return [Puppeteer::FileChooser]
149
+ def wait_for_file_chooser(timeout: nil)
150
+ if @file_chooser_interceptors.empty?
151
+ @client.send_message('Page.setInterceptFileChooserDialog', enabled: true)
152
+ end
153
+
154
+ option_timeout = timeout || @timeout_settings.timeout
155
+ promise = resolvable_future
156
+ @file_chooser_interceptors << promise
157
+
158
+ begin
159
+ Timeout.timeout(option_timeout / 1000.0) do
160
+ promise.value!
161
+ end
162
+ rescue Timeout::Error
163
+ raise FileChooserTimeoutError.new(timeout: option_timeout)
164
+ ensure
165
+ @file_chooser_interceptors.delete(promise)
166
+ end
167
+ end
168
+
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
160
174
 
161
175
 
162
176
  # /**
@@ -204,7 +218,9 @@ class Puppeteer::Page
204
218
  end
205
219
  if source != 'worker'
206
220
  console_message_location = Puppeteer::ConsoleMessage::Location.new(
207
- url: url, line_number: line_number)
221
+ url: url,
222
+ line_number: line_number,
223
+ )
208
224
  emit_event('Events.Page.Console',
209
225
  Puppeteer::ConsoleMessage.new(level, text, [], console_message_location))
210
226
  end
@@ -273,23 +289,37 @@ class Puppeteer::Page
273
289
  end
274
290
 
275
291
  # `$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
276
- # @param {string} selector
277
- # @param {Function|string} pageFunction
278
- # @param {!Array<*>} args
279
- # @return {!Promise<(!Object|undefined)>}
292
+ # @param selector [String]
293
+ # @param page_function [String]
294
+ # @return [Object]
280
295
  def Seval(selector, page_function, *args)
281
296
  main_frame.Seval(selector, page_function, *args)
282
297
  end
283
298
 
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
306
+
284
307
  # `$$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
285
- # @param {string} selector
286
- # @param {Function|string} pageFunction
287
- # @param {!Array<*>} args
288
- # @return {!Promise<(!Object|undefined)>}
308
+ # @param selector [String]
309
+ # @param page_function [String]
310
+ # @return [Object]
289
311
  def SSeval(selector, page_function, *args)
290
312
  main_frame.SSeval(selector, page_function, *args)
291
313
  end
292
314
 
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
322
+
293
323
  # `$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.
294
324
  # @param {string} expression
295
325
  # @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
@@ -608,14 +638,14 @@ class Puppeteer::Page
608
638
  main_frame.goto(url, referer: referer, timeout: timeout, wait_until: wait_until)
609
639
  end
610
640
 
611
- # @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
612
- # @return {!Promise<?Puppeteer.Response>}
641
+ # @param timeout [number|nil]
642
+ # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
643
+ # @return [Puppeteer::Response]
613
644
  def reload(timeout: nil, wait_until: nil)
614
- # const [response] = await Promise.all([
615
- # this.waitForNavigation(options),
616
- # this._client.send('Page.reload')
617
- # ]);
618
- # return response;
645
+ await_all(
646
+ async_wait_for_navigation(timeout: timeout, wait_until: wait_until),
647
+ @client.async_send_message('Page.reload'),
648
+ ).first
619
649
  end
620
650
 
621
651
  # @param timeout [number|nil]
@@ -966,16 +996,21 @@ class Puppeteer::Page
966
996
  main_frame.select(selector, *values)
967
997
  end
968
998
 
969
- # @param {string} selector
999
+ # @param selector [String]
970
1000
  def tap(selector)
971
1001
  main_frame.tap(selector)
972
1002
  end
973
1003
 
974
- # @param {string} selector
975
- # @param {string} text
976
- # @param {{delay: (number|undefined)}=} options
977
- def type(selector, text, delay: nil)
978
- main_frame.type(selector, text, delay: delay)
1004
+ # @param selector [String]
1005
+ async def async_tap(selector)
1006
+ tap(selector)
1007
+ end
1008
+
1009
+ # @param selector [String]
1010
+ # @param text [String]
1011
+ # @param delay [Number]
1012
+ def type_text(selector, text, delay: nil)
1013
+ main_frame.type_text(selector, text, delay: delay)
979
1014
  end
980
1015
 
981
1016
  # /**
@@ -988,20 +1023,38 @@ class Puppeteer::Page
988
1023
  # return this.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, ...args);
989
1024
  # }
990
1025
 
991
- # @param {string} selector
992
- # @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
993
- # @return {!Promise<?Puppeteer.ElementHandle>}
1026
+ # @param selector [String]
1027
+ # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
1028
+ # @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
1029
+ # @param timeout [Integer]
994
1030
  def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
995
1031
  main_frame.wait_for_selector(selector, visible: visible, hidden: hidden, timeout: timeout)
996
1032
  end
997
1033
 
998
- # @param {string} xpath
999
- # @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
1000
- # @return {!Promise<?Puppeteer.ElementHandle>}
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
1041
+
1042
+ # @param xpath [String]
1043
+ # @param visible [Boolean] Wait for element visible (not 'display: none' nor 'visibility: hidden') on true. default to false.
1044
+ # @param hidden [Boolean] Wait for element invisible ('display: none' nor 'visibility: hidden') on true. default to false.
1045
+ # @param timeout [Integer]
1001
1046
  def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
1002
1047
  main_frame.wait_for_xpath(xpath, visible: visible, hidden: hidden, timeout: timeout)
1003
1048
  end
1004
1049
 
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
1057
+
1005
1058
  # @param {Function|string} pageFunction
1006
1059
  # @param {!{polling?: string|number, timeout?: number}=} options
1007
1060
  # @param {!Array<*>} args