puppeteer-ruby 0.0.3 → 0.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 499744d122eeb8736a1466f17cf9ee09235f488fb19a95d3902da5529406dab0
4
- data.tar.gz: f6b27f599887d385c46914e1b255f9738fd7f946a0ff0a4f83a04075e50e092c
3
+ metadata.gz: 2764adee69056c36a0112866aa1f35a97ccb655ed81bc2977be9707efed7d121
4
+ data.tar.gz: 8919e7f0228b6dff08a1ee13906c97ad2d08552b1fd7652bc094b506bc9ac4e5
5
5
  SHA512:
6
- metadata.gz: 54ece5985462bc3f4f30c1a803fd43a5975845911657431aa33d78d70fba6e242d447869a03b3ad6cf925e0e94315db2df62b4d9d74d8b00e381b8d95326e311
7
- data.tar.gz: d7d3ab0af4b1bd69588b3998d0a47031d04d80fe3d0b7b55ec1f4b1a9b3ccd683c857faf06e103b972b9b59d63bda6fdc3e984f410720eeae17922176d518561
6
+ metadata.gz: c142553b4316d8cf7cf541e41283f01c982fc2f0ece3ffb1f87c258130035e3894062831364284fdfaa6c97771029687232955f7a4e64cb24e8d876aee36f08d
7
+ data.tar.gz: 5d59f78bb651c955617b0a0fee01b02b9e5aa4907d46d15cc79acf559db8f44d1a2a96ecd76571d3431737bfa8b9fd8a24a9a2ad80c5c8c0cc9754c42d1b75af
data/.github/stale.yml ADDED
@@ -0,0 +1,16 @@
1
+ # Number of days of inactivity before an issue becomes stale
2
+ daysUntilStale: 14
3
+ # Number of days of inactivity before a stale issue is closed
4
+ daysUntilClose: 7
5
+ # Issues with these labels will never be considered stale
6
+ exemptLabels:
7
+ - security
8
+ # Label to use when marking an issue as stale
9
+ staleLabel: inactive
10
+ # Comment to post when marking an issue as stale. Set to `false` to disable
11
+ markComment: >
12
+ This issue has been automatically marked as stale because it has not had
13
+ recent activity. It will be closed if no further activity occurs. Thank you
14
+ for your contributions.
15
+ # Comment to post when closing a stale issue. Set to `false` to disable
16
+ closeComment: false
data/README.md CHANGED
@@ -46,6 +46,8 @@ end
46
46
  ![puppeteer-ruby](https://user-images.githubusercontent.com/11763113/78505735-6e7f3000-77b0-11ea-9c82-9016828dd2a9.gif)
47
47
 
48
48
 
49
+ More usage examples can be found [here](https://github.com/YusukeIwaki/puppeteer-ruby-example)
50
+
49
51
  ## Contributing
50
52
 
51
53
  Bug reports and pull requests are welcome on GitHub at https://github.com/YusukeIwaki/puppeteer-ruby.
@@ -44,8 +44,8 @@ class Puppeteer::CDPSession
44
44
  callback.reject(
45
45
  Puppeteer::Connection::ProtocolError.new(
46
46
  method: callback.method,
47
- error_message: response['error']['message'],
48
- error_data: response['error']['data']))
47
+ error_message: message['error']['message'],
48
+ error_data: message['error']['data']))
49
49
  else
50
50
  callback.resolve(message['result'])
51
51
  end
@@ -12,6 +12,7 @@ class Puppeteer::DOMWorld
12
12
  @frame = frame
13
13
  @timeout_settings = timeout_settings
14
14
  @context_promise = resolvable_future
15
+ @pending_destroy = []
15
16
  @wait_tasks = Set.new
16
17
  @detached = false
17
18
  end
@@ -29,20 +30,24 @@ class Puppeteer::DOMWorld
29
30
 
30
31
  if context
31
32
  if @context_promise.fulfilled?
33
+ @pending_destroy << context._context_id
32
34
  @document = nil
33
35
  @context_promise = resolvable_future
34
- @pending_destroy_exists = true
35
36
  end
36
37
  @context_promise.fulfill(context)
37
38
  # for (const waitTask of this._waitTasks)
38
39
  # waitTask.rerun();
39
40
  else
40
- if @pending_destroy_exists
41
- @pending_destroy_exists = false
42
- else
43
- @document = nil
44
- @context_promise = resolvable_future
45
- end
41
+ raise ArgumentError.new("context should now be nil. Use #delete_context for clearing document.")
42
+ end
43
+ end
44
+
45
+ def delete_context(execution_context_id)
46
+ if @pending_destroy.include?(execution_context_id)
47
+ @pending_destroy.delete(execution_context_id)
48
+ else
49
+ @document = nil
50
+ @context_promise = resolvable_future
46
51
  end
47
52
  end
48
53
 
@@ -303,16 +308,15 @@ class Puppeteer::DOMWorld
303
308
  # }
304
309
  # }
305
310
 
306
- # /**
307
- # * @param {string} selector
308
- # * @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options
309
- # */
310
- # async click(selector, options) {
311
- # const handle = await this.$(selector);
312
- # assert(handle, 'No node found for selector: ' + selector);
313
- # await handle.click(options);
314
- # await handle.dispose();
315
- # }
311
+ # @param selector [String]
312
+ # @param delay [Number]
313
+ # @param button [String] "left"|"right"|"middle"
314
+ # @param click_count [Number]
315
+ def click(selector, delay: nil, button: nil, click_count: nil)
316
+ handle = S(selector)
317
+ handle.click(delay: delay, button: button, click_count: click_count)
318
+ handle.dispose
319
+ end
316
320
 
317
321
  # /**
318
322
  # * @param {string} selector
@@ -28,62 +28,89 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
28
28
  end
29
29
  end
30
30
 
31
- # async _scrollIntoViewIfNeeded() {
32
- # const error = await this.evaluate(async(element, pageJavascriptEnabled) => {
33
- # if (!element.isConnected)
34
- # return 'Node is detached from document';
35
- # if (element.nodeType !== Node.ELEMENT_NODE)
36
- # return 'Node is not of type HTMLElement';
37
- # // force-scroll if page's javascript is disabled.
38
- # if (!pageJavascriptEnabled) {
39
- # element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'});
40
- # return false;
41
- # }
42
- # const visibleRatio = await new Promise(resolve => {
43
- # const observer = new IntersectionObserver(entries => {
44
- # resolve(entries[0].intersectionRatio);
45
- # observer.disconnect();
46
- # });
47
- # observer.observe(element);
48
- # });
49
- # if (visibleRatio !== 1.0)
50
- # element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'});
51
- # return false;
52
- # }, this._page._javascriptEnabled);
53
- # if (error)
54
- # throw new Error(error);
55
- # }
31
+ class ScrollIntoViewError < StandardError; end
32
+
33
+ def scroll_into_view_if_needed
34
+ js = <<~JAVASCRIPT
35
+ async(element, pageJavascriptEnabled) => {
36
+ if (!element.isConnected)
37
+ return 'Node is detached from document';
38
+ if (element.nodeType !== Node.ELEMENT_NODE)
39
+ return 'Node is not of type HTMLElement';
40
+ // force-scroll if page's javascript is disabled.
41
+ if (!pageJavascriptEnabled) {
42
+ element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'});
43
+ return false;
44
+ }
45
+ const visibleRatio = await new Promise(resolve => {
46
+ const observer = new IntersectionObserver(entries => {
47
+ resolve(entries[0].intersectionRatio);
48
+ observer.disconnect();
49
+ });
50
+ observer.observe(element);
51
+ });
52
+ if (visibleRatio !== 1.0)
53
+ element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'});
54
+ return false;
55
+ }
56
+ JAVASCRIPT
57
+ error = evaluate(js, @page.javascript_enabled) # returns String or false
58
+ if error
59
+ raise ScrollIntoViewError.new(error)
60
+ end
61
+ end
56
62
 
57
- # /**
58
- # * @return {!Promise<!{x: number, y: number}>}
59
- # */
60
- # async _clickablePoint() {
61
- # const [result, layoutMetrics] = await Promise.all([
62
- # this._client.send('DOM.getContentQuads', {
63
- # objectId: this._remoteObject.objectId
64
- # }).catch(debugError),
65
- # this._client.send('Page.getLayoutMetrics'),
66
- # ]);
67
- # if (!result || !result.quads.length)
68
- # throw new Error('Node is either not visible or not an HTMLElement');
69
- # // Filter out quads that have too small area to click into.
70
- # const {clientWidth, clientHeight} = layoutMetrics.layoutViewport;
71
- # const quads = result.quads.map(quad => this._fromProtocolQuad(quad)).map(quad => this._intersectQuadWithViewport(quad, clientWidth, clientHeight)).filter(quad => computeQuadArea(quad) > 1);
72
- # if (!quads.length)
73
- # throw new Error('Node is either not visible or not an HTMLElement');
74
- # // Return the middle point of the first quad.
75
- # const quad = quads[0];
76
- # let x = 0;
77
- # let y = 0;
78
- # for (const point of quad) {
79
- # x += point.x;
80
- # y += point.y;
81
- # }
82
- # return {
83
- # x: x / 4,
84
- # y: y / 4
85
- # };
86
- # }
63
+ class Point
64
+ def initialize(x:, y:)
65
+ @x = x
66
+ @y = y
67
+ end
68
+
69
+ def +(other)
70
+ Point.new(
71
+ x: @x + other.x,
72
+ y: @y + other.y,
73
+ )
74
+ end
75
+
76
+ def /(num)
77
+ Point.new(
78
+ x: @x / num,
79
+ y: @y / num,
80
+ )
81
+ end
82
+
83
+ attr_reader :x, :y
84
+ end
85
+
86
+ class ElementNotVisibleError < StandardError
87
+ def initialize
88
+ super("Node is either not visible or not an HTMLElement")
89
+ end
90
+ end
91
+
92
+ def clickable_point
93
+ result = @remote_object.content_quads(@client)
94
+ if !result || result["quads"].empty?
95
+ raise ElementNotVisibleError.new
96
+ end
97
+
98
+ # Filter out quads that have too small area to click into.
99
+ layout_metrics = @client.send_message('Page.getLayoutMetrics')
100
+ client_width = layout_metrics["layoutViewport"]["clientWidth"]
101
+ client_height = layout_metrics["layoutViewport"]["clientHeight"]
102
+
103
+ quads = result["quads"].
104
+ map { |quad| from_protocol_quad(quad) }.
105
+ map { |quad| intersect_quad_with_viewport(quad, client_width, client_height) }.
106
+ select { |quad| compute_quad_area(quad) > 1 }
107
+ if quads.empty?
108
+ raise ElementNotVisibleError.new
109
+ end
110
+
111
+ # Return the middle point of the first quad.
112
+ quads.first.reduce(:+) / 4
113
+ end
87
114
 
88
115
  # /**
89
116
  # * @return {!Promise<void|Protocol.DOM.getBoxModelReturnValue>}
@@ -94,31 +121,26 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
94
121
  # }).catch(error => debugError(error));
95
122
  # }
96
123
 
97
- # /**
98
- # * @param {!Array<number>} quad
99
- # * @return {!Array<{x: number, y: number}>}
100
- # */
101
- # _fromProtocolQuad(quad) {
102
- # return [
103
- # {x: quad[0], y: quad[1]},
104
- # {x: quad[2], y: quad[3]},
105
- # {x: quad[4], y: quad[5]},
106
- # {x: quad[6], y: quad[7]}
107
- # ];
108
- # }
124
+ # @param quad [Array<number>]
125
+ # @return [Array<Point>]
126
+ private def from_protocol_quad(quad)
127
+ quad.each_slice(2).map do |x, y|
128
+ Point.new(x: x, y: y)
129
+ end
130
+ end
109
131
 
110
- # /**
111
- # * @param {!Array<{x: number, y: number}>} quad
112
- # * @param {number} width
113
- # * @param {number} height
114
- # * @return {!Array<{x: number, y: number}>}
115
- # */
116
- # _intersectQuadWithViewport(quad, width, height) {
117
- # return quad.map(point => ({
118
- # x: Math.min(Math.max(point.x, 0), width),
119
- # y: Math.min(Math.max(point.y, 0), height),
120
- # }));
121
- # }
132
+ # @param quad [Array<Point>]
133
+ # @param width [number]
134
+ # @param height [number]
135
+ # @return [Array<Point>]
136
+ private def intersect_quad_with_viewport(quad, width, height)
137
+ quad.map do |point|
138
+ Point.new(
139
+ x: [[point.x, 0].max, width].min,
140
+ y: [[point.y, 0].max, height].min,
141
+ )
142
+ end
143
+ end
122
144
 
123
145
  # async hover() {
124
146
  # await this._scrollIntoViewIfNeeded();
@@ -126,6 +148,15 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
126
148
  # await this._page.mouse.move(x, y);
127
149
  # }
128
150
 
151
+ # @param delay [Number]
152
+ # @param button [String] "left"|"right"|"middle"
153
+ # @param click_count [Number]
154
+ def click(delay: nil, button: nil, click_count: nil)
155
+ scroll_into_view_if_needed
156
+ point = clickable_point
157
+ @page.mouse.click(point.x, point.y, delay: delay, button: button, click_count: click_count)
158
+ end
159
+
129
160
  # /**
130
161
  # * @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options
131
162
  # */
@@ -430,4 +461,11 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
430
461
  # return visibleRatio > 0;
431
462
  # });
432
463
  # }
464
+
465
+ # @param quad [Array<Point>]
466
+ private def compute_quad_area(quad)
467
+ # Compute sum of all directed areas of adjacent triangles
468
+ # https://en.wikipedia.org/wiki/Polygon#Simple_polygons
469
+ quad.zip(quad.rotate).map { |p1, p2| (p1.x * p2.y - p2.x * p1.y) / 2 }.reduce(:+).abs
470
+ end
433
471
  end
@@ -16,6 +16,11 @@ class Puppeteer::ExecutionContext
16
16
 
17
17
  attr_reader :client, :world
18
18
 
19
+ # only used in DomWorld#delete_context
20
+ def _context_id
21
+ @context_id
22
+ end
23
+
19
24
  # @return [Puppeteer::Frame]
20
25
  def frame
21
26
  if_present(@world) do |world|
@@ -143,8 +143,10 @@ class Puppeteer::Frame
143
143
  @main_world.add_style_tag(style_tag)
144
144
  end
145
145
 
146
- # @param {string} selector
147
- # @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options
146
+ # @param selector [String]
147
+ # @param delay [Number]
148
+ # @param button [String] "left"|"right"|"middle"
149
+ # @param click_count [Number]
148
150
  def click(selector, delay: nil, button: nil, click_count: nil)
149
151
  @secondary_world.click(selector, delay: delay, button: button, click_count: click_count)
150
152
  end
@@ -268,8 +270,8 @@ class Puppeteer::Frame
268
270
 
269
271
  def detach
270
272
  @detached = true
271
- # this._mainWorld._detach();
272
- # this._secondaryWorld._detach();
273
+ @main_world.detach
274
+ @secondary_world.detach
273
275
  if @parent_frame
274
276
  @parent_frame._child_frames.delete(self)
275
277
  end
@@ -206,12 +206,12 @@ class Puppeteer::FrameManager
206
206
  # @param {string} frameId
207
207
  # @param {?string} parentFrameId
208
208
  def handle_frame_attached(frame_id, parent_frame_id)
209
- return if @frames.has_key?[frame_id]
209
+ return if @frames.has_key?(frame_id)
210
210
  if !parent_frame_id
211
211
  raise ArgymentError.new('parent_frame_id must not be nil')
212
212
  end
213
213
  parent_frame = @frames[parent_frame_id]
214
- frame = Frame.new(self, @client, parent_frame, frame_id)
214
+ frame = Puppeteer::Frame.new(self, @client, parent_frame, frame_id)
215
215
  @frames[frame_id] = frame
216
216
 
217
217
  emit_event 'Events.FrameManager.FrameAttached', frame
@@ -332,12 +332,12 @@ class Puppeteer::FrameManager
332
332
  @context_id_to_context.delete(execution_context_id)
333
333
  @context_id_created.delete(execution_context_id)
334
334
  if context.world
335
- context.world.context = nil
335
+ context.world.delete_context(execution_context_id)
336
336
  end
337
337
  end
338
338
 
339
339
  def handle_execution_contexts_cleared
340
- # executionContextCleared is often notified after executionContextCreated.
340
+ # executionContextsCleared is often notified after executionContextCreated.
341
341
  # D, [2020-04-06T01:47:03.101227 #13823] DEBUG -- : RECV << {"method"=>"Runtime.executionContextCreated", "params"=>{"context"=>{"id"=>5, "origin"=>"https://github.com", "name"=>"", "auxData"=>{"isDefault"=>true, "type"=>"default", "frameId"=>"71C347B70848B89DDDEFAA8AB5B0BC92"}}}, "sessionId"=>"53F088EED260C28001D26A019F95D9E3"}
342
342
  # D, [2020-04-06T01:47:03.101439 #13823] DEBUG -- : RECV << {"method"=>"Page.frameNavigated", "params"=>{"frame"=>{"id"=>"71C347B70848B89DDDEFAA8AB5B0BC92", "loaderId"=>"80338225D035AC96BAE8F6D4E81C7D51", "url"=>"https://github.com/search?q=puppeteer", "securityOrigin"=>"https://github.com", "mimeType"=>"text/html"}}, "sessionId"=>"53F088EED260C28001D26A019F95D9E3"}
343
343
  # D, [2020-04-06T01:47:03.101325 #13823] DEBUG -- : RECV << {"method"=>"Target.targetInfoChanged", "params"=>{"targetInfo"=>{"targetId"=>"71C347B70848B89DDDEFAA8AB5B0BC92", "type"=>"page", "title"=>"https://github.com/search?q=puppeteer", "url"=>"https://github.com/search?q=puppeteer", "attached"=>true, "browserContextId"=>"AF37BC660284CE1552B4ECB147BE9305"}}}
@@ -346,9 +346,9 @@ class Puppeteer::FrameManager
346
346
  # To avoid the problem, just skip recent created ids.
347
347
  now = Time.now
348
348
  context_ids_to_skip = @context_id_created.select { |k, v| now - v < 1 }.keys
349
- @context_id_to_context.reject { |k, v| context_ids_to_skip.include?(k) }.each_value do |context|
349
+ @context_id_to_context.reject { |k, v| context_ids_to_skip.include?(k) }.each do |execution_context_id, context|
350
350
  if context.world
351
- context.world.context = nil
351
+ context.world.delete_context(execution_context_id)
352
352
  end
353
353
  end
354
354
  @context_id_to_context.select! { |k, v| context_ids_to_skip.include?(k) }
@@ -116,7 +116,6 @@ class Puppeteer::JSHandle
116
116
  nil
117
117
  end
118
118
 
119
- # @return [Future]
120
119
  def dispose
121
120
  return if @disposed
122
121
 
@@ -178,6 +178,6 @@ class Puppeteer::Keyboard
178
178
  # @param key [String]
179
179
  # @return [Future]
180
180
  async def async_press(key, delay: nil)
181
- press(key, delay)
181
+ press(key, delay: delay)
182
182
  end
183
183
  end
@@ -41,10 +41,10 @@ class Puppeteer::Keyboard
41
41
  'Backspace': KeyDefinition.new({ 'keyCode': 8, 'code': 'Backspace', 'key': 'Backspace' }),
42
42
  'Tab': KeyDefinition.new({ 'keyCode': 9, 'code': 'Tab', 'key': 'Tab' }),
43
43
  'Numpad5': KeyDefinition.new({ 'keyCode': 12, 'shiftKeyCode': 101, 'key': 'Clear', 'code': 'Numpad5', 'shiftKey': '5', 'location': 3 }),
44
- 'NumpadEnter': KeyDefinition.new({ 'keyCode': 13, 'code': 'NumpadEnter', 'key': 'Enter', 'text': '\r', 'location': 3 }),
45
- 'Enter': KeyDefinition.new({ 'keyCode': 13, 'code': 'Enter', 'key': 'Enter', 'text': '\r' }),
46
- '\r': KeyDefinition.new({ 'keyCode': 13, 'code': 'Enter', 'key': 'Enter', 'text': '\r' }),
47
- '\n': KeyDefinition.new({ 'keyCode': 13, 'code': 'Enter', 'key': 'Enter', 'text': '\r' }),
44
+ 'NumpadEnter': KeyDefinition.new({ 'keyCode': 13, 'code': 'NumpadEnter', 'key': 'Enter', 'text': "\r", 'location': 3 }),
45
+ 'Enter': KeyDefinition.new({ 'keyCode': 13, 'code': 'Enter', 'key': 'Enter', 'text': "\r" }),
46
+ "\r": KeyDefinition.new({ 'keyCode': 13, 'code': 'Enter', 'key': 'Enter', 'text': "\r" }),
47
+ "\n": KeyDefinition.new({ 'keyCode': 13, 'code': 'Enter', 'key': 'Enter', 'text': "\r" }),
48
48
  'ShiftLeft': KeyDefinition.new({ 'keyCode': 16, 'code': 'ShiftLeft', 'key': 'Shift', 'location': 1 }),
49
49
  'ShiftRight': KeyDefinition.new({ 'keyCode': 16, 'code': 'ShiftRight', 'key': 'Shift', 'location': 2 }),
50
50
  'ControlLeft': KeyDefinition.new({ 'keyCode': 17, 'code': 'ControlLeft', 'key': 'Control', 'location': 1 }),
@@ -50,6 +50,11 @@ class Puppeteer::LifecycleWatcher
50
50
  end
51
51
  end
52
52
 
53
+ class FrameDetachedError < StandardError
54
+ def initialize
55
+ super('Navigating frame was detached')
56
+ end
57
+ end
53
58
  class TerminatedError < StandardError; end
54
59
 
55
60
  # * @param {!Puppeteer.FrameManager} frameManager
@@ -92,7 +97,7 @@ class Puppeteer::LifecycleWatcher
92
97
  # @param frame [Puppeteer::Frame]
93
98
  def handle_frame_detached(frame)
94
99
  if @frame == frame
95
- # this._terminationCallback.call(null, new Error('Navigating frame was detached'));
100
+ @termination_promise.reject(FrameDetachedError.new)
96
101
  return
97
102
  end
98
103
  check_lifecycle_complete
@@ -37,8 +37,8 @@ class Puppeteer::Mouse
37
37
  @client.send_message('Input.dispatchMouseEvent',
38
38
  type: 'mouseMoved',
39
39
  button: @button,
40
- x: from_x + (@x - from_x) * n / steps,
41
- y: from_y + (@y - from_y) * n / steps,
40
+ x: from_x + (@x - from_x) * n / move_steps,
41
+ y: from_y + (@y - from_y) * n / move_steps,
42
42
  modifiers: @keyboard.modifiers,
43
43
  )
44
44
  end
@@ -56,16 +56,19 @@ class Puppeteer::Mouse
56
56
  # @param y [number]
57
57
  # @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options
58
58
  def click(x, y, delay: nil, button: nil, click_count: nil)
59
+ # await_all(async_move, async_down, async_up) often breaks the order of CDP commands.
60
+ # D, [2020-04-15T17:09:47.895895 #88683] DEBUG -- : RECV << {"id"=>23, "result"=>{"layoutViewport"=>{"pageX"=>0, "pageY"=>1, "clientWidth"=>375, "clientHeight"=>667}, "visualViewport"=>{"offsetX"=>0, "offsetY"=>0, "pageX"=>0, "pageY"=>1, "clientWidth"=>375, "clientHeight"=>667, "scale"=>1, "zoom"=>1}, "contentSize"=>{"x"=>0, "y"=>0, "width"=>375, "height"=>2007}}, "sessionId"=>"0B09EA5E18DEE403E525B3E7FCD7E225"}
61
+ # D, [2020-04-15T17:09:47.898422 #88683] DEBUG -- : SEND >> {"sessionId":"0B09EA5E18DEE403E525B3E7FCD7E225","method":"Input.dispatchMouseEvent","params":{"type":"mouseReleased","button":"left","x":0,"y":0,"modifiers":0,"clickCount":1},"id":24}
62
+ # D, [2020-04-15T17:09:47.899711 #88683] DEBUG -- : SEND >> {"sessionId":"0B09EA5E18DEE403E525B3E7FCD7E225","method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","button":"left","x":0,"y":0,"modifiers":0,"clickCount":1},"id":25}
63
+ # D, [2020-04-15T17:09:47.900237 #88683] DEBUG -- : SEND >> {"sessionId":"0B09EA5E18DEE403E525B3E7FCD7E225","method":"Input.dispatchMouseEvent","params":{"type":"mouseMoved","button":"left","x":187,"y":283,"modifiers":0},"id":26}
64
+ # So we execute move in advance.
65
+ move(x, y)
59
66
  if delay
60
- await_all(
61
- async_move(x, y),
62
- async_down(button: button, click_count: click_count),
63
- )
67
+ down(button: button, click_count: click_count)
64
68
  sleep(delay / 1000.0)
65
69
  up(button: button, click_count: click_count)
66
70
  else
67
71
  await_all(
68
- async_move(x, y),
69
72
  async_down(button: button, click_count: click_count),
70
73
  async_up(button: button, click_count: click_count),
71
74
  )
@@ -173,7 +173,7 @@ class Puppeteer::Page
173
173
  # await this._client.send('Emulation.setGeolocationOverride', {longitude, latitude, accuracy});
174
174
  # }
175
175
 
176
- attr_reader :target
176
+ attr_reader :javascript_enabled, :target
177
177
 
178
178
  def browser
179
179
  @target.browser
@@ -932,12 +932,23 @@ class Puppeteer::Page
932
932
 
933
933
  attr_reader :mouse
934
934
 
935
- # @param {string} selector
936
- # @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options
935
+ # @param selector [String]
936
+ # @param delay [Number]
937
+ # @param button [String] "left"|"right"|"middle"
938
+ # @param click_count [Number]
937
939
  def click(selector, delay: nil, button: nil, click_count: nil)
938
940
  main_frame.click(selector, delay: delay, button: button, click_count: click_count)
939
941
  end
940
942
 
943
+ # @param selector [String]
944
+ # @param delay [Number]
945
+ # @param button [String] "left"|"right"|"middle"
946
+ # @param click_count [Number]
947
+ # @return [Future]
948
+ async def async_click(selector, delay: nil, button: nil, click_count: nil)
949
+ click(selector, delay: delay, button: button, click_count: click_count)
950
+ end
951
+
941
952
  # @param {string} selector
942
953
  def focus(selector)
943
954
  main_frame.focus(selector)
@@ -57,6 +57,11 @@ class Puppeteer::RemoteObject
57
57
  client.send_message('DOM.describeNode', objectId: @object_id)
58
58
  end
59
59
 
60
+ # used in ElementHandle#clickable_point
61
+ def content_quads(client)
62
+ client.send_message('DOM.getContentQuads', objectId: @object_id)
63
+ end
64
+
60
65
  # helper#valueFromRemoteObject
61
66
  def value
62
67
  if @unserializable_value
@@ -1,3 +1,3 @@
1
1
  class Puppeteer
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppeteer-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-12 00:00:00.000000000 Z
11
+ date: 2020-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -130,6 +130,7 @@ extensions: []
130
130
  extra_rdoc_files: []
131
131
  files:
132
132
  - ".circleci/config.yml"
133
+ - ".github/stale.yml"
133
134
  - ".gitignore"
134
135
  - ".rspec"
135
136
  - ".rubocop.yml"