Skip to content

Commit

Permalink
implement #wait_for_selector method for browser
Browse files Browse the repository at this point in the history
  • Loading branch information
Mifrill committed Jan 7, 2022
1 parent c58767f commit 83ca882
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 2 deletions.
2 changes: 1 addition & 1 deletion lib/ferrum/browser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Browser
evaluate evaluate_on evaluate_async execute evaluate_func
add_script_tag add_style_tag bypass_csp
on goto position position=
playback_rate playback_rate=] => :page
playback_rate playback_rate= wait_for_selector] => :page
delegate %i[default_user_agent] => :process

attr_reader :client, :process, :contexts, :logger, :js_errors, :pending_connection_errors,
Expand Down
30 changes: 30 additions & 0 deletions lib/ferrum/frame/dom.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,36 @@ def body
evaluate("document.documentElement.outerHTML")
end

def wait_for_selector(css: nil, xpath: nil, timeout: 1000, interval: 100)
tap do
evaluate_func(%(
function(selector, isXpath, timeout, interval) {
var attempts = 0;
var max = timeout / interval;
function waitForSelector(resolve, reject) {
if (attempts > ((max < 1) ? 1 : max)) {
return reject(new Error("Not found element match the selector:" + selector));
}
var element = isXpath
? document.
evaluate(selector, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
: document.querySelector(selector);
if (element !== null) {
return resolve(element);
}
setTimeout(function () {
waitForSelector(resolve, reject);
}, interval);
attempts++;
}
return new Promise(function (resolve, reject) {
waitForSelector(resolve, reject);
});
}
), css || xpath, css.nil? && !xpath.nil?, timeout, interval, awaitPromise: true)
end
end

def xpath(selector, within: nil)
expr = <<~JS
function(selector, within) {
Expand Down
2 changes: 1 addition & 1 deletion lib/ferrum/page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def reset
delegate %i[at_css at_xpath css xpath
current_url current_title url title body doctype content=
execution_id evaluate evaluate_on evaluate_async execute evaluate_func
add_script_tag add_style_tag] => :main_frame
add_script_tag add_style_tag wait_for_selector] => :main_frame

include Animation
include Screenshot
Expand Down
58 changes: 58 additions & 0 deletions spec/browser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,64 @@ module Ferrum
expect(browser.evaluate("window.last_hashchange")).to eq("#foo")
end

context "wait_for_selector" do
before do
browser.go_to("/ferrum/with_js")
end

it "waits for provided css selector" do
expect(
browser.wait_for_selector(css: "div#wait_for_selector").at_css("div#wait_for_selector")
).not_to be_nil
end

it "waits for provided css hidden selector" do
expect(
browser.wait_for_selector(css: "div#wait_for_hidden_selector").at_css("div#wait_for_hidden_selector")
).not_to be_nil
end

it "waits for provided xpath selector" do
expect(
browser.wait_for_selector(xpath: "//div[@id='wait_for_selector']").at_css("div#wait_for_selector")
).not_to be_nil
end

it "waits for provided xpath hidden selector" do
expect(
browser
.wait_for_selector(xpath: "//div[@id='wait_for_hidden_selector']")
.at_css("div#wait_for_hidden_selector")
).not_to be_nil
end

it "raises error when timeout exceed" do
expect do
browser.wait_for_selector(css: "div#wait_for_selector", timeout: 800)
end.to raise_error(Ferrum::JavaScriptError, /Not found element match the selector/)
end

it "raises error when provided invalid css" do
expect do
browser.wait_for_selector(css: "//div[@id='wait_for_selector']")
end.to raise_error(Ferrum::JavaScriptError, /Failed to execute 'querySelector' on 'Document'/)
end

it "raises error when provided invalid xpath" do
expect do
browser.wait_for_selector(xpath: "div#wait_for_selector")
end.to raise_error(Ferrum::JavaScriptError, /Failed to execute 'evaluate' on 'Document'/)
end

it "waits less than provided timeout when node found" do
Timeout.timeout(1) do
expect(
browser.wait_for_selector(css: "div#wait_for_selector", timeout: 2000).at_css("div#wait_for_selector")
).not_to be_nil
end
end
end

context "current_url" do
it "supports whitespace characters" do
browser.go_to("/ferrum/arbitrary_path/200/foo%20bar%20baz")
Expand Down
17 changes: 17 additions & 0 deletions spec/support/views/with_js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@
display: inline;
}
</style>
<script>
$(document).ready(function(){
setTimeout(function(){
const div = document.createElement('div');
div.setAttribute('id', 'wait_for_selector');
document.body.appendChild(div);
}, 900);
});
$(document).ready(function(){
setTimeout(function(){
const div = document.createElement('div');
div.setAttribute('id', 'wait_for_hidden_selector');
div.setAttribute('style', 'display:none;');
document.body.appendChild(div);
}, 900);
});
</script>
</head>

<body>
Expand Down

0 comments on commit 83ca882

Please sign in to comment.