Skip to content

Commit bc13315

Browse files
committed
implement #wait_for_selector method for browser
1 parent c58767f commit bc13315

File tree

5 files changed

+107
-2
lines changed

5 files changed

+107
-2
lines changed

lib/ferrum/browser.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class Browser
2828
evaluate evaluate_on evaluate_async execute evaluate_func
2929
add_script_tag add_style_tag bypass_csp
3030
on goto position position=
31-
playback_rate playback_rate=] => :page
31+
playback_rate playback_rate= wait_for_selector] => :page
3232
delegate %i[default_user_agent] => :process
3333

3434
attr_reader :client, :process, :contexts, :logger, :js_errors, :pending_connection_errors,

lib/ferrum/frame/dom.rb

+30
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,36 @@ def body
3636
evaluate("document.documentElement.outerHTML")
3737
end
3838

39+
def wait_for_selector(css: nil, xpath: nil, timeout: 1000, interval: 100)
40+
tap do
41+
evaluate_func(%(
42+
function(selector, isXpath, timeout, interval) {
43+
var attempts = 0;
44+
var max = timeout / interval;
45+
function waitForSelector(resolve, reject) {
46+
if (attempts > ((max < 1) ? 1 : max)) {
47+
return reject(new Error("Not found element match the selector:" + selector));
48+
}
49+
var element = isXpath
50+
? document.
51+
evaluate(selector, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
52+
: document.querySelector(selector);
53+
if (element !== null) {
54+
return resolve(element);
55+
}
56+
setTimeout(function () {
57+
waitForSelector(resolve, reject);
58+
}, interval);
59+
attempts++;
60+
}
61+
return new Promise(function (resolve, reject) {
62+
waitForSelector(resolve, reject);
63+
});
64+
}
65+
), css || xpath, css.nil? && !xpath.nil?, timeout, interval, awaitPromise: true)
66+
end
67+
end
68+
3969
def xpath(selector, within: nil)
4070
expr = <<~JS
4171
function(selector, within) {

lib/ferrum/page.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def reset
3434
delegate %i[at_css at_xpath css xpath
3535
current_url current_title url title body doctype content=
3636
execution_id evaluate evaluate_on evaluate_async execute evaluate_func
37-
add_script_tag add_style_tag] => :main_frame
37+
add_script_tag add_style_tag wait_for_selector] => :main_frame
3838

3939
include Animation
4040
include Screenshot

spec/node_spec.rb

+58
Original file line numberDiff line numberDiff line change
@@ -296,5 +296,63 @@ module Ferrum
296296
expect(styles["font-weight"]).to eq("700")
297297
end
298298
end
299+
300+
describe "#wait_for_selector" do
301+
before do
302+
browser.go_to("/ferrum/with_js")
303+
end
304+
305+
it "waits for provided css selector" do
306+
expect(
307+
browser.wait_for_selector(css: "div#wait_for_selector").at_css("div#wait_for_selector")
308+
).not_to be_nil
309+
end
310+
311+
it "waits for provided css hidden selector" do
312+
expect(
313+
browser.wait_for_selector(css: "div#wait_for_hidden_selector").at_css("div#wait_for_hidden_selector")
314+
).not_to be_nil
315+
end
316+
317+
it "waits for provided xpath selector" do
318+
expect(
319+
browser.wait_for_selector(xpath: "//div[@id='wait_for_selector']").at_css("div#wait_for_selector")
320+
).not_to be_nil
321+
end
322+
323+
it "waits for provided xpath hidden selector" do
324+
expect(
325+
browser
326+
.wait_for_selector(xpath: "//div[@id='wait_for_hidden_selector']")
327+
.at_css("div#wait_for_hidden_selector")
328+
).not_to be_nil
329+
end
330+
331+
it "raises error when timeout exceed" do
332+
expect do
333+
browser.wait_for_selector(css: "div#wait_for_selector", timeout: 800)
334+
end.to raise_error(Ferrum::JavaScriptError, /Not found element match the selector/)
335+
end
336+
337+
it "raises error when provided invalid css" do
338+
expect do
339+
browser.wait_for_selector(css: "//div[@id='wait_for_selector']")
340+
end.to raise_error(Ferrum::JavaScriptError, /Failed to execute 'querySelector' on 'Document'/)
341+
end
342+
343+
it "raises error when provided invalid xpath" do
344+
expect do
345+
browser.wait_for_selector(xpath: "div#wait_for_selector")
346+
end.to raise_error(Ferrum::JavaScriptError, /Failed to execute 'evaluate' on 'Document'/)
347+
end
348+
349+
it "waits less than provided timeout when node found" do
350+
Timeout.timeout(1) do
351+
expect(
352+
browser.wait_for_selector(css: "div#wait_for_selector", timeout: 2000).at_css("div#wait_for_selector")
353+
).not_to be_nil
354+
end
355+
end
356+
end
299357
end
300358
end

spec/support/views/with_js.erb

+17
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@
2424
display: inline;
2525
}
2626
</style>
27+
<script>
28+
$(document).ready(function(){
29+
setTimeout(function(){
30+
const div = document.createElement('div');
31+
div.setAttribute('id', 'wait_for_selector');
32+
document.body.appendChild(div);
33+
}, 900);
34+
});
35+
$(document).ready(function(){
36+
setTimeout(function(){
37+
const div = document.createElement('div');
38+
div.setAttribute('id', 'wait_for_hidden_selector');
39+
div.setAttribute('style', 'display:none;');
40+
document.body.appendChild(div);
41+
}, 900);
42+
});
43+
</script>
2744
</head>
2845

2946
<body>

0 commit comments

Comments
 (0)