$inputElement = $driver->find_element_by_xpath("//input[\@value='Absenden']");
$inputElement->click();
# wait until page is ready
while (1) {
Time::HiRes::sleep(0.2);
$readyState = $driver->execute_script("return document.readyState;");
print "readyState: $readyState\n";
if ($readyState eq "complete") {
last;
}
}
return document.readyState;" immediately returns "complete" because the old page is still ready and the server has not started to deliver the new page.
Kind Regards,
GeorgThe usual recommendation here is to duplicate the behavior that an actual user would do. To navigate to a new page and interact it with it, a human’s literal steps might be something like: click a button and then wait specifically for the element on the next page that they want to interact with (coincidentally not caring at all whether the browser thinks the page is ready, much to the chagrin of JS developers everywhere :P).
So, here’s one way to wait for an element:
use Selenium::Waiter;
$submit = $driver->find_element( $locator, $type );
$submit->click;
my $elem_on_next_page = wait_until { $driver->find_element( $next_page_elem_locator, $type ) };
There’s additional information available in this blog post.
Separately from this discussion, webdriver already has some logic built in after clicking or submitting a form that tries to determine when the subsequent page is done loading … if the button triggers an actual page load, and not an AJAX call that mutates the DOM. If you’re dealing with AJAX, and you happen to have jQuery available, you can ask jQuery to tell you when it’s done all of it’s AJAX requests and animations, in the hopes that the end of both of those events signals that the page is ready for further input. But, it’s not fool proof either:
my $wait_for_ajax_and_animations = q%
var callback = arguments[arguments.length-1];
if ($.active != 0) {
$( document ).ajaxStop(function() {
$("*").promise().done(function () {
callback("done");
});
});
}
else {
$("*").promise().done(function () {
callback("done");
});
}
%;
my $timeout = 30000; // however long you want to wait.
$driver->set_async_script_timeout($timeout);
$driver->execute_async_script($wait_for_ajax_and_animations);
Good luck!
Eh, you can do some more complicated stuff in the wait_until if you’d like
my $submit = $driver->find_element( $locator, $type );
my $start = time;
$submit->click;
# warning, untested code! but, you get the idea...
my $next_page = wait_until {
my $success_elem = eval {
# find_element can die, and typically Selenium::Waiter takes a
# `die` to mean that we should exit this block and try another
# iteration, so we'd skip out on the rest of this logic
# block. So, since we want to keep doing things, we must
# prevent the `die` from bubbling up with an eval (or
# try/catch, or whatever you like)
$driver->find_element( $next_page_elem_locator, $type )
};
if ($success_elem) {
# we did it, we're on the success next page
return { success => 1, elem => $success_elem };
}
else {
# maybe we're on the failure page?
my $failure_elem = eval {
$driver->find_element( $failure_page_elem_locator, $type )
};
if ($failure_elem) {
# oops, something went wrong and we're on the fail page
return { success => 0 };
}
else {
# we're not on the next page OR the failure page, so let's
# keep trying until our time is up
return;
}
}
};
if ($next_page->{success}) {
say 'victory';
}
else {
say 'defeat took ' . (time - $start) . ' seconds';
}