Skip to content

Commit eeb553d

Browse files
authored
Merge pull request #175 from rubycdp/refactor
* Introduce wait_for_stop_moving and moving?
2 parents f308536 + 9475db8 commit eeb553d

File tree

6 files changed

+80
-33
lines changed

6 files changed

+80
-33
lines changed

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Web design by [Evrone](https://evrone.com/), what else
5050
* [Frame](https://github.com/rubycdp/ferrum#frame)
5151
* [Dialog](https://github.com/rubycdp/ferrum#dialog)
5252
* [Animation](https://github.com/rubycdp/ferrum#animation)
53+
* [Node](https://github.com/rubycdp/ferrum#node)
5354
* [Thread safety](https://github.com/rubycdp/ferrum#thread-safety)
5455
* [Development](https://github.com/rubycdp/ferrum#development)
5556
* [Contributing](https://github.com/rubycdp/ferrum#contributing)
@@ -979,6 +980,7 @@ end
979980
browser.go_to("https://google.com")
980981
```
981982

983+
982984
## Animation
983985

984986
You can slow down or speed up CSS animations.
@@ -1002,6 +1004,32 @@ browser.playback_rate # => 2000
10021004
```
10031005

10041006

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+
10051033
## Thread safety ##
10061034

10071035
Ferrum is fully thread-safe. You can create one browser or a few as you wish and

lib/ferrum.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def initialize(message = "Browser is dead or given window is closed")
5757
end
5858
end
5959

60-
class NodeIsMovingError < Error
60+
class NodeMovingError < Error
6161
def initialize(node, prev, current)
6262
@node, @prev, @current = node, prev, current
6363
super(message)
@@ -70,6 +70,12 @@ def message
7070
end
7171
end
7272

73+
class CoordinatesNotFoundError < Error
74+
def initialize(message = "Could not compute content quads")
75+
super
76+
end
77+
end
78+
7379
class BrowserError < Error
7480
attr_reader :response
7581

lib/ferrum/browser/client.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ def raise_browser_error(error)
9090
raise NoExecutionContextError.new(error)
9191
when "No target with given id found"
9292
raise NoSuchPageError
93+
when /Could not compute content quads/
94+
raise CoordinatesNotFoundError
9395
else
9496
raise BrowserError.new(error)
9597
end

lib/ferrum/node.rb

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
module Ferrum
44
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
77

88
attr_reader :page, :target_id, :node_id, :description, :tag_name
99

@@ -37,6 +37,19 @@ def focusable?
3737
e.message == "Element is not focusable" ? false : raise
3838
end
3939

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+
4053
def blur
4154
tap { evaluate("this.blur()") }
4255
end
@@ -131,44 +144,36 @@ def inspect
131144
end
132145

133146
def find_position(x: nil, y: nil, position: :top)
134-
prev = get_content_quads
135-
136-
# FIXME: Case when a few quads returned
137-
points = Ferrum.with_attempts(errors: NodeIsMovingError, max: MOVING_ATTEMPTS, wait: 0) do
138-
sleep(MOVING_WAIT)
139-
current = get_content_quads
140-
141-
if current != prev
142-
error = NodeIsMovingError.new(self, prev, current)
143-
prev = current
144-
raise(error)
145-
end
146-
147-
current
148-
end.map { |q| to_points(q) }.first
149-
147+
points = wait_for_stop_moving.map { |q| to_points(q) }.first
150148
get_position(points, x, y, position)
151-
rescue Ferrum::BrowserError => e
152-
return raise unless e.message&.include?("Could not compute content quads")
153-
154-
find_position_via_js
149+
rescue CoordinatesNotFoundError
150+
x, y = get_bounding_rect_coordinates
151+
raise if x == 0 && y == 0
152+
[x, y]
155153
end
156154

157155
private
158156

159-
def find_position_via_js
160-
[
161-
evaluate("this.getBoundingClientRect().left + window.pageXOffset + (this.offsetWidth / 2)"), # x
162-
evaluate("this.getBoundingClientRect().top + window.pageYOffset + (this.offsetHeight / 2)") # y
163-
]
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
164162
end
165163

166164
def get_content_quads
167165
quads = page.command("DOM.getContentQuads", nodeId: node_id)["quads"]
168-
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
169167
quads
170168
end
171169

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+
172177
def get_position(points, offset_x, offset_y, position)
173178
x = y = nil
174179

spec/browser_spec.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,9 @@ module Ferrum
508508
it "does not run into content quads error" do
509509
browser.go_to("/ferrum/index")
510510

511-
allow_any_instance_of(Node).to receive(:get_content_quads).and_raise(Ferrum::BrowserError, "message" => "Could not compute content quads")
511+
allow_any_instance_of(Node).to receive(:get_content_quads)
512+
.and_raise(Ferrum::CoordinatesNotFoundError,
513+
"Could not compute content quads")
512514

513515
browser.at_xpath("//a[text() = 'JS redirect']").click
514516
expect(browser.body).to include("Hello world")

spec/node_spec.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,20 @@ module Ferrum
1717
expect { node.text }.to raise_error(Ferrum::NodeNotFoundError)
1818
end
1919

20-
it "raises an error if the element is not visible", skip: true do
20+
it "raises an error if the element is not visible" do
2121
browser.go_to("/ferrum/index")
22+
2223
browser.execute <<~JS
2324
document.querySelector("a[href=js_redirect]").style.display = "none"
2425
JS
26+
27+
sleep 0.2 # Wait for node to disappear
28+
2529
expect {
2630
browser.at_xpath("//a[text()='JS redirect']").click
2731
}.to raise_error(
28-
Ferrum::BrowserError,
29-
"Could not compute content quads."
32+
Ferrum::CoordinatesNotFoundError,
33+
"Could not compute content quads"
3034
)
3135
end
3236

0 commit comments

Comments
 (0)