Skip to content

Commit 83ca882

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

File tree

5 files changed

+107
-2
lines changed

5 files changed

+107
-2
lines changed

lib/ferrum/browser.rb

Lines changed: 1 addition & 1 deletion
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

Lines changed: 30 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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/browser_spec.rb

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,64 @@ module Ferrum
504504
expect(browser.evaluate("window.last_hashchange")).to eq("#foo")
505505
end
506506

507+
context "wait_for_selector" do
508+
before do
509+
browser.go_to("/ferrum/with_js")
510+
end
511+
512+
it "waits for provided css selector" do
513+
expect(
514+
browser.wait_for_selector(css: "div#wait_for_selector").at_css("div#wait_for_selector")
515+
).not_to be_nil
516+
end
517+
518+
it "waits for provided css hidden selector" do
519+
expect(
520+
browser.wait_for_selector(css: "div#wait_for_hidden_selector").at_css("div#wait_for_hidden_selector")
521+
).not_to be_nil
522+
end
523+
524+
it "waits for provided xpath selector" do
525+
expect(
526+
browser.wait_for_selector(xpath: "//div[@id='wait_for_selector']").at_css("div#wait_for_selector")
527+
).not_to be_nil
528+
end
529+
530+
it "waits for provided xpath hidden selector" do
531+
expect(
532+
browser
533+
.wait_for_selector(xpath: "//div[@id='wait_for_hidden_selector']")
534+
.at_css("div#wait_for_hidden_selector")
535+
).not_to be_nil
536+
end
537+
538+
it "raises error when timeout exceed" do
539+
expect do
540+
browser.wait_for_selector(css: "div#wait_for_selector", timeout: 800)
541+
end.to raise_error(Ferrum::JavaScriptError, /Not found element match the selector/)
542+
end
543+
544+
it "raises error when provided invalid css" do
545+
expect do
546+
browser.wait_for_selector(css: "//div[@id='wait_for_selector']")
547+
end.to raise_error(Ferrum::JavaScriptError, /Failed to execute 'querySelector' on 'Document'/)
548+
end
549+
550+
it "raises error when provided invalid xpath" do
551+
expect do
552+
browser.wait_for_selector(xpath: "div#wait_for_selector")
553+
end.to raise_error(Ferrum::JavaScriptError, /Failed to execute 'evaluate' on 'Document'/)
554+
end
555+
556+
it "waits less than provided timeout when node found" do
557+
Timeout.timeout(1) do
558+
expect(
559+
browser.wait_for_selector(css: "div#wait_for_selector", timeout: 2000).at_css("div#wait_for_selector")
560+
).not_to be_nil
561+
end
562+
end
563+
end
564+
507565
context "current_url" do
508566
it "supports whitespace characters" do
509567
browser.go_to("/ferrum/arbitrary_path/200/foo%20bar%20baz")

spec/support/views/with_js.erb

Lines changed: 17 additions & 0 deletions
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)