How to get the Mink Selenium 2 Driver to wait for the page to load with Behat

Patrick picture Patrick · Feb 13, 2015 · Viewed 9.1k times · Source

I am in the process of upgrading from the Behat 2.x series to the Behat 3.x series. In the prior version I could load the Selenium 1 driver, which attached to PhantomJS to execute tests. When I did this I was able to hook into a function called waitForPageToLoad().

This function was provided by php-selenium (from Alexandre Salomé). It hooked into selenium and called a driver action by the same name. This worked perfectly for ensuring that Selenium waited for a page to load. At least until a timeout was reached. It made tests to go much faster.

The problem is that the Selenium 1 driver is not compatible with Behat 3.x. It looks like it has been all but abandoned and I don't see that functionality in the Selenium 2 driver for Mink.

Does anyone know of a way to make this work with Behat 3.x and Selenium 2?

Answer

DanielM picture DanielM · Apr 13, 2015

Using Selenium (or any other driver for that matter), I've never had to worry about whether the page has loaded or not, with one exception: if the page finishes loading, then loads more content via AJAX.

To handle this, you can use a spin function as documented in the Behat Manual.

http://docs.behat.org/en/v2.5/cookbook/using_spin_functions.html

The benefits of this are:

  • It doesn't need you to use the selenium driver (for example, you could use PhantomJS if you want speed over looks).
  • It won't break if you stop using jQuery and switch to something else (such as Angular's $httpProvider)

I wouldn't use theirs though, the back trace is broken and who want's to wait a second between checks anyway. :)

Try this:

Assuming you are using the Mink Context (thanks Mick), you can simply check the page every second or so until the desired text has either appeared or dissapeared, or a given timeout has expired in which case we'd assume a fail.

/**
 * @When I wait for :text to appear
 * @Then I should see :text appear
 * @param $text
 * @throws \Exception
 */
public function iWaitForTextToAppear($text)
{
    $this->spin(function(FeatureContext $context) use ($text) {
        try {
            $context->assertPageContainsText($text);
            return true;
        }
        catch(ResponseTextException $e) {
            // NOOP
        }
        return false;
    });
}


/**
 * @When I wait for :text to disappear
 * @Then I should see :text disappear
 * @param $text
 * @throws \Exception
 */
public function iWaitForTextToDisappear($text)
{
    $this->spin(function(FeatureContext $context) use ($text) {
        try {
            $context->assertPageContainsText($text);
        }
        catch(ResponseTextException $e) {
            return true;
        }
        return false;
    });
}

/**
 * Based on Behat's own example
 * @see http://docs.behat.org/en/v2.5/cookbook/using_spin_functions.html#adding-a-timeout
 * @param $lambda
 * @param int $wait
 * @throws \Exception
 */
public function spin($lambda, $wait = 60)
{
    $time = time();
    $stopTime = $time + $wait;
    while (time() < $stopTime)
    {
        try {
            if ($lambda($this)) {
                return;
            }
        } catch (\Exception $e) {
            // do nothing
        }

        usleep(250000);
    }

    throw new \Exception("Spin function timed out after {$wait} seconds");
}