Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
implement #wait_for_selector method for browser
  • Loading branch information
Mifrill committed Jan 7, 2022
commit 83ca882255aca06d75ef5884d854903cd10be16d
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