Wait for a page to load after click() or submit()

1,085 views
Skip to first unread message

Georg Horn

unread,
Dec 13, 2016, 11:26:32 AM12/13/16
to Selenium Remote Driver
Hello,

i am looking for a way to wait until the page has loaded after a call to $elem->click() oder $elem->submit().

Here: http://stackoverflow.com/questions/15122864/selenium-wait-until-document-is-ready/41125232#41125232
several solutions are provided but none of them in perl using Selenium::Remote::Driver.

The following does not work:

$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;
   
}
}



the call to 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,
Georg

Daniel Gempesaw

unread,
Dec 13, 2016, 11:42:18 AM12/13/16
to Selenium Remote Driver

The 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!

Georg Horn

unread,
Dec 13, 2016, 12:52:40 PM12/13/16
to Selenium Remote Driver
Hello Daniel,

thanks for the hint, i didn´t know about Selenium::Waiter. Your suggestion works if the application that i am testing behaves as expected and returns a page that contains the desired element. But if the webserver returns an unexpected page this will wait until the timeout occurs and i would rather know how long it took to display even the unexpected page...

Daniel Gempesaw

unread,
Dec 14, 2016, 4:43:36 PM12/14/16
to Selenium Remote Driver

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';
}
Reply all
Reply to author
Forward
0 new messages