ferrum 0.10.2 → 0.11

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: a2256dbc8ed2c2fca2e3209c13e29009c69b4c2ccf328e8a73ade73ed69c89f0
4
- data.tar.gz: 8d5604f8d4f8120b699ec3e341c37f77f25e90a794d3206660ecc0465b22e7b1
3
+ metadata.gz: c953c12b582e6d81031c8daa598f25d4f8b2dba8e6d736f227b2e8c461a83d41
4
+ data.tar.gz: 229fe35bc81a133eff2628e1b3e0e0d31d733da2b277e0dbe70c9699e80e2a15
5
5
  SHA512:
6
- metadata.gz: 7aa18fd5a01f387c27ade8ecdf6aa30df8118f4e90badddc242ed979e5be49c890036049f284555a937ccc79699bcbc954d62c077f45ff0770d9eeeec255a6ea
7
- data.tar.gz: 25c4cc92e59a297a344cf85301a21c3f9e634f25ea685eb6c9d2f4ea1896fcba2898dc5f4cfcc8f3af358001a0e451034bb79bf24981e16a17a51cdd3f07d6a3
6
+ metadata.gz: 1f48e9fac5bfbcf9959d855b1b7c7259f97d845e812f3fce6fa59dc6a3e6b5147cced16167c2a3603e964978a9d3b1e4e5b4f2c7b5b686454acaa47d1aadf6d3
7
+ data.tar.gz: aa9f267ea80365e636e8b047bc87e6d5fe6761814cbd1b59b3d14bc3350ccdd9e2199718ba57c774abdf5bc089ef000cdbd55d03d7dbd6158cee4c975ee9d9e0
data/README.md CHANGED
@@ -49,6 +49,8 @@ Web design by [Evrone](https://evrone.com/), what else
49
49
  * [Frames](https://github.com/rubycdp/ferrum#frames)
50
50
  * [Frame](https://github.com/rubycdp/ferrum#frame)
51
51
  * [Dialog](https://github.com/rubycdp/ferrum#dialog)
52
+ * [Animation](https://github.com/rubycdp/ferrum#animation)
53
+ * [Node](https://github.com/rubycdp/ferrum#node)
52
54
  * [Thread safety](https://github.com/rubycdp/ferrum#thread-safety)
53
55
  * [Development](https://github.com/rubycdp/ferrum#development)
54
56
  * [Contributing](https://github.com/rubycdp/ferrum#contributing)
@@ -234,6 +236,25 @@ browser.go_to("https://github.com/")
234
236
  browser.stop
235
237
  ```
236
238
 
239
+ #### position = \*\*options
240
+
241
+ Set the position for the browser window
242
+
243
+ * options `Hash`
244
+ * :left `Integer`
245
+ * :top `Integer`
246
+
247
+ ```ruby
248
+ browser.position = { left: 10, top: 20 }
249
+ ```
250
+
251
+ #### position : `Array<Integer>`
252
+
253
+ Get the position for the browser window
254
+
255
+ ```ruby
256
+ browser.position # => [10, 20]
257
+ ```
237
258
 
238
259
  ## Finders
239
260
 
@@ -740,6 +761,20 @@ simple value.
740
761
  browser.execute(%(1 + 1)) # => true
741
762
  ```
742
763
 
764
+ #### evaluate_on_new_document(expression)
765
+
766
+ Evaluate JavaScript to modify things before a page load
767
+
768
+ * expression `String` should be valid JavaScript
769
+
770
+ ```ruby
771
+ browser.evaluate_on_new_document <<~JS
772
+ Object.defineProperty(navigator, "languages", {
773
+ get: function() { return ["tlh"]; }
774
+ });
775
+ JS
776
+ ```
777
+
743
778
  #### add_script_tag(\*\*options) : `Boolean`
744
779
 
745
780
  * options `Hash`
@@ -946,6 +981,55 @@ browser.go_to("https://google.com")
946
981
  ```
947
982
 
948
983
 
984
+ ## Animation
985
+
986
+ You can slow down or speed up CSS animations.
987
+
988
+ #### playback_rate : `Integer`
989
+
990
+ Returns playback rate for CSS animations, defaults to `1`.
991
+
992
+
993
+ #### playback_rate = value
994
+
995
+ Sets playback rate of CSS animations
996
+
997
+ * value `Integer`
998
+
999
+ ```ruby
1000
+ browser = Ferrum::Browser.new
1001
+ browser.playback_rate = 2000
1002
+ browser.go_to("https://google.com")
1003
+ browser.playback_rate # => 2000
1004
+ ```
1005
+
1006
+
1007
+ ## Node
1008
+
1009
+ #### node? : `Boolean`
1010
+ #### frame_id
1011
+ #### frame
1012
+ #### focus
1013
+ #### focusable?
1014
+ #### moving? : `Boolean`
1015
+ #### wait_for_stop_moving
1016
+ #### blur
1017
+ #### type
1018
+ #### click
1019
+ #### hover
1020
+ #### select_file
1021
+ #### at_xpath
1022
+ #### at_css
1023
+ #### xpath
1024
+ #### css
1025
+ #### text
1026
+ #### inner_text
1027
+ #### value
1028
+ #### property
1029
+ #### attribute
1030
+ #### evaluate
1031
+
1032
+
949
1033
  ## Thread safety ##
950
1034
 
951
1035
  Ferrum is fully thread-safe. You can create one browser or a few as you wish and
data/lib/ferrum.rb CHANGED
@@ -57,7 +57,7 @@ module Ferrum
57
57
  end
58
58
  end
59
59
 
60
- class NodeIsMovingError < Error
60
+ class NodeMovingError < Error
61
61
  def initialize(node, prev, current)
62
62
  @node, @prev, @current = node, prev, current
63
63
  super(message)
@@ -70,6 +70,12 @@ module Ferrum
70
70
  end
71
71
  end
72
72
 
73
+ class CoordinatesNotFoundError < Error
74
+ def initialize(message = "Could not compute content quads")
75
+ super
76
+ end
77
+ end
78
+
73
79
  class BrowserError < Error
74
80
  attr_reader :response
75
81
 
@@ -26,7 +26,8 @@ module Ferrum
26
26
  frames frame_by main_frame
27
27
  evaluate evaluate_on evaluate_async execute evaluate_func
28
28
  add_script_tag add_style_tag bypass_csp
29
- on goto] => :page
29
+ on goto position position=
30
+ playback_rate playback_rate=] => :page
30
31
  delegate %i[default_user_agent] => :process
31
32
 
32
33
  attr_reader :client, :process, :contexts, :logger, :js_errors, :pending_connection_errors,
@@ -77,6 +78,10 @@ module Ferrum
77
78
  end
78
79
  end
79
80
 
81
+ def evaluate_on_new_document(expression)
82
+ extensions << expression
83
+ end
84
+
80
85
  def timeout
81
86
  @timeout || DEFAULT_TIMEOUT
82
87
  end
@@ -90,6 +90,8 @@ module Ferrum
90
90
  raise NoExecutionContextError.new(error)
91
91
  when "No target with given id found"
92
92
  raise NoSuchPageError
93
+ when /Could not compute content quads/
94
+ raise CoordinatesNotFoundError
93
95
  else
94
96
  raise BrowserError.new(error)
95
97
  end
@@ -36,6 +36,7 @@ module Ferrum
36
36
  "metrics-recording-only" => nil,
37
37
  "safebrowsing-disable-auto-update" => nil,
38
38
  "password-store" => "basic",
39
+ "no-startup-window" => nil,
39
40
  # Note: --no-sandbox is not needed if you properly setup a user in the container.
40
41
  # https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
41
42
  # "no-sandbox" => nil,
@@ -10,7 +10,7 @@ module Ferrum
10
10
 
11
11
  def initialize(browser, contexts, id)
12
12
  @browser, @contexts, @id = browser, contexts, id
13
- @targets = Concurrent::Hash.new
13
+ @targets = Concurrent::Map.new
14
14
  @pendings = Concurrent::MVar.new
15
15
  end
16
16
 
@@ -47,14 +47,14 @@ module Ferrum
47
47
  url: "about:blank")
48
48
  target = @pendings.take(@browser.timeout)
49
49
  raise NoSuchTargetError unless target.is_a?(Target)
50
- @targets[target.id] = target
50
+ @targets.put_if_absent(target.id, target)
51
51
  target
52
52
  end
53
53
 
54
54
  def add_target(params)
55
55
  target = Target.new(@browser, params)
56
56
  if target.window?
57
- @targets[target.id] = target
57
+ @targets.put_if_absent(target.id, target)
58
58
  else
59
59
  @pendings.put(target, @browser.timeout)
60
60
  end
@@ -72,6 +72,10 @@ module Ferrum
72
72
  @contexts.dispose(@id)
73
73
  end
74
74
 
75
+ def has_target?(target_id)
76
+ !!@targets[target_id]
77
+ end
78
+
75
79
  def inspect
76
80
  %(#<#{self.class} @id=#{@id.inspect} @targets=#{@targets.inspect} @default_target=#{@default_target.inspect}>)
77
81
  end
@@ -7,7 +7,7 @@ module Ferrum
7
7
  attr_reader :contexts
8
8
 
9
9
  def initialize(browser)
10
- @contexts = Concurrent::Hash.new
10
+ @contexts = Concurrent::Map.new
11
11
  @browser = browser
12
12
  subscribe
13
13
  discover
@@ -18,7 +18,9 @@ module Ferrum
18
18
  end
19
19
 
20
20
  def find_by(target_id:)
21
- @contexts.find { |_, c| c.targets.keys.include?(target_id) }&.last
21
+ context = nil
22
+ @contexts.each_value { |c| context = c if c.has_target?(target_id) }
23
+ context
22
24
  end
23
25
 
24
26
  def create
data/lib/ferrum/node.rb CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Ferrum
4
4
  class Node
5
- MOVING_WAIT = ENV.fetch("FERRUM_NODE_MOVING_WAIT", 0.01).to_f
6
- MOVING_ATTEMPTS = ENV.fetch("FERRUM_NODE_MOVING_ATTEMPTS", 50).to_i
5
+ MOVING_WAIT_DELAY = ENV.fetch("FERRUM_NODE_MOVING_WAIT", 0.01).to_f
6
+ MOVING_WAIT_ATTEMPTS = ENV.fetch("FERRUM_NODE_MOVING_ATTEMPTS", 50).to_i
7
7
 
8
8
  attr_reader :page, :target_id, :node_id, :description, :tag_name
9
9
 
@@ -30,6 +30,26 @@ module Ferrum
30
30
  tap { page.command("DOM.focus", slowmoable: true, nodeId: node_id) }
31
31
  end
32
32
 
33
+ def focusable?
34
+ focus
35
+ true
36
+ rescue BrowserError => e
37
+ e.message == "Element is not focusable" ? false : raise
38
+ end
39
+
40
+ def wait_for_stop_moving(delay: MOVING_WAIT_DELAY, attempts: MOVING_WAIT_ATTEMPTS)
41
+ Ferrum.with_attempts(errors: NodeMovingError, max: attempts, wait: 0) do
42
+ previous, current = get_content_quads_with(delay: delay)
43
+ raise NodeMovingError.new(self, previous, current) if previous != current
44
+ current
45
+ end
46
+ end
47
+
48
+ def moving?(delay: MOVING_WAIT_DELAY)
49
+ previous, current = get_content_quads_with(delay: delay)
50
+ previous == current
51
+ end
52
+
33
53
  def blur
34
54
  tap { evaluate("this.blur()") }
35
55
  end
@@ -124,44 +144,36 @@ module Ferrum
124
144
  end
125
145
 
126
146
  def find_position(x: nil, y: nil, position: :top)
127
- prev = get_content_quads
128
-
129
- # FIXME: Case when a few quads returned
130
- points = Ferrum.with_attempts(errors: NodeIsMovingError, max: MOVING_ATTEMPTS, wait: 0) do
131
- sleep(MOVING_WAIT)
132
- current = get_content_quads
133
-
134
- if current != prev
135
- error = NodeIsMovingError.new(self, prev, current)
136
- prev = current
137
- raise(error)
138
- end
139
-
140
- current
141
- end.map { |q| to_points(q) }.first
142
-
147
+ points = wait_for_stop_moving.map { |q| to_points(q) }.first
143
148
  get_position(points, x, y, position)
144
- rescue Ferrum::BrowserError => e
145
- return raise unless e.message&.include?("Could not compute content quads")
146
-
147
- find_position_via_js
149
+ rescue CoordinatesNotFoundError
150
+ x, y = get_bounding_rect_coordinates
151
+ raise if x == 0 && y == 0
152
+ [x, y]
148
153
  end
149
154
 
150
155
  private
151
156
 
152
- def find_position_via_js
153
- [
154
- evaluate("this.getBoundingClientRect().left + window.pageXOffset + (this.offsetWidth / 2)"), # x
155
- evaluate("this.getBoundingClientRect().top + window.pageYOffset + (this.offsetHeight / 2)") # y
156
- ]
157
+ def get_bounding_rect_coordinates
158
+ evaluate <<~JS
159
+ [this.getBoundingClientRect().left + window.pageXOffset + (this.offsetWidth / 2),
160
+ this.getBoundingClientRect().top + window.pageYOffset + (this.offsetHeight / 2)]
161
+ JS
157
162
  end
158
163
 
159
164
  def get_content_quads
160
165
  quads = page.command("DOM.getContentQuads", nodeId: node_id)["quads"]
161
- raise "Node is either not visible or not an HTMLElement" if quads.size == 0
166
+ raise CoordinatesNotFoundError, "Node is either not visible or not an HTMLElement" if quads.size == 0
162
167
  quads
163
168
  end
164
169
 
170
+ def get_content_quads_with(delay: MOVING_WAIT_DELAY)
171
+ previous = get_content_quads
172
+ sleep(delay)
173
+ current = get_content_quads
174
+ [previous, current]
175
+ end
176
+
165
177
  def get_position(points, offset_x, offset_y, position)
166
178
  x = y = nil
167
179
 
data/lib/ferrum/page.rb CHANGED
@@ -9,6 +9,7 @@ require "ferrum/dialog"
9
9
  require "ferrum/network"
10
10
  require "ferrum/page/frames"
11
11
  require "ferrum/page/screenshot"
12
+ require "ferrum/page/animation"
12
13
  require "ferrum/browser/client"
13
14
 
14
15
  module Ferrum
@@ -35,7 +36,7 @@ module Ferrum
35
36
  execution_id evaluate evaluate_on evaluate_async execute evaluate_func
36
37
  add_script_tag add_style_tag] => :main_frame
37
38
 
38
- include Frames, Screenshot
39
+ include Frames, Screenshot, Animation
39
40
 
40
41
  attr_accessor :referrer
41
42
  attr_reader :target_id, :browser,
@@ -108,6 +109,14 @@ module Ferrum
108
109
  fitWindow: false)
109
110
  end
110
111
 
112
+ def position
113
+ @browser.command("Browser.getWindowBounds", windowId: window_id).fetch("bounds").values_at("left", "top")
114
+ end
115
+
116
+ def position=(left:, top:)
117
+ @browser.command("Browser.setWindowBounds", windowId: window_id, bounds: { left: left, top: top })
118
+ end
119
+
111
120
  def refresh
112
121
  command("Page.reload", wait: timeout, slowmoable: true)
113
122
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ferrum
4
+ class Page
5
+ module Animation
6
+ def playback_rate
7
+ command("Animation.getPlaybackRate")["playbackRate"]
8
+ end
9
+
10
+
11
+ def playback_rate=(value)
12
+ command("Animation.setPlaybackRate", playbackRate: value)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -12,7 +12,7 @@ module Ferrum
12
12
  scale: 1.0
13
13
  }.freeze
14
14
 
15
- PAPEP_FORMATS = {
15
+ PAPER_FORMATS = {
16
16
  letter: { width: 8.50, height: 11.00 },
17
17
  legal: { width: 8.50, height: 14.00 },
18
18
  tabloid: { width: 11.00, height: 17.00 },
@@ -114,7 +114,7 @@ module Ferrum
114
114
  raise ArgumentError, "Specify :format or :paper_width, :paper_height"
115
115
  end
116
116
 
117
- dimension = PAPEP_FORMATS.fetch(format)
117
+ dimension = PAPER_FORMATS.fetch(format)
118
118
  options.merge!(paper_width: dimension[:width],
119
119
  paper_height: dimension[:height])
120
120
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ferrum
4
- VERSION = "0.10.2"
4
+ VERSION = "0.11"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ferrum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.2
4
+ version: '0.11'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Vorotilin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-24 00:00:00.000000000 Z
11
+ date: 2021-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: websocket-driver
@@ -210,6 +210,7 @@ files:
210
210
  - lib/ferrum/network/response.rb
211
211
  - lib/ferrum/node.rb
212
212
  - lib/ferrum/page.rb
213
+ - lib/ferrum/page/animation.rb
213
214
  - lib/ferrum/page/frames.rb
214
215
  - lib/ferrum/page/screenshot.rb
215
216
  - lib/ferrum/rbga.rb
@@ -218,7 +219,12 @@ files:
218
219
  homepage: https://github.com/route/ferrum
219
220
  licenses:
220
221
  - MIT
221
- metadata: {}
222
+ metadata:
223
+ homepage_uri: https://ferrum.rubycdp.com/
224
+ bug_tracker_uri: https://github.com/rubycdp/ferrum/issues
225
+ documentation_uri: https://github.com/rubycdp/ferrum/blob/master/README.md
226
+ changelog_uri: https://github.com/rubycdp/ferrum/blob/master/CHANGELOG.md
227
+ source_code_uri: https://github.com/rubycdp/ferrum
222
228
  post_install_message:
223
229
  rdoc_options: []
224
230
  require_paths: