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 +4 -4
- data/README.md +84 -0
- data/lib/ferrum.rb +7 -1
- data/lib/ferrum/browser.rb +6 -1
- data/lib/ferrum/browser/client.rb +2 -0
- data/lib/ferrum/browser/options/chrome.rb +1 -0
- data/lib/ferrum/context.rb +7 -3
- data/lib/ferrum/contexts.rb +4 -2
- data/lib/ferrum/node.rb +40 -28
- data/lib/ferrum/page.rb +10 -1
- data/lib/ferrum/page/animation.rb +16 -0
- data/lib/ferrum/page/screenshot.rb +2 -2
- data/lib/ferrum/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c953c12b582e6d81031c8daa598f25d4f8b2dba8e6d736f227b2e8c461a83d41
|
4
|
+
data.tar.gz: 229fe35bc81a133eff2628e1b3e0e0d31d733da2b277e0dbe70c9699e80e2a15
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
|
data/lib/ferrum/browser.rb
CHANGED
@@ -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
|
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
|
@@ -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,
|
data/lib/ferrum/context.rb
CHANGED
@@ -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::
|
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
|
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
|
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
|
data/lib/ferrum/contexts.rb
CHANGED
@@ -7,7 +7,7 @@ module Ferrum
|
|
7
7
|
attr_reader :contexts
|
8
8
|
|
9
9
|
def initialize(browser)
|
10
|
-
@contexts = Concurrent::
|
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
|
-
|
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
|
-
|
6
|
-
|
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
|
-
|
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
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
-
|
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 =
|
117
|
+
dimension = PAPER_FORMATS.fetch(format)
|
118
118
|
options.merge!(paper_width: dimension[:width],
|
119
119
|
paper_height: dimension[:height])
|
120
120
|
end
|
data/lib/ferrum/version.rb
CHANGED
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.
|
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-
|
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:
|