When I click on a button, a new page opens with a form and I need to fill a field on that page.
However, as soon as the page starts loading, behat attempts to populate the field that has not yet been loaded.
I would like to put an implicit wait to wait for the field to be displayed before attempting to populate it.
/**
* @Given que preencho corretamente os campos da tela
*/
public function quePreenchoCorretamenteOsCamposDaTela()
{
$faker = Faker\Factory::create();
$this->getPage()->findField('voucher_subject')->setValue($faker->text);
$this->getPage()->findField('voucher_nameRecipient')->setValue($faker->name);
}
Does anyone can help me?
You can use spin function:
trait FeatureContextHelper
{
public function spin (callable $lambda, $wait = 5)
{
$lastErrorMessage = '';
for ($i = 0; $i < $wait; $i++) {
try {
if ($lambda($this)) {
return true;
}
} catch (Exception $e) {
// do nothing
$lastErrorMessage = $e->getMessage();
}
sleep(1);
}
throw new ElementNotVisible('The element is not visible ' . $lastErrorMessage);
}
}
Then in your context:
class FeatureContext extends MinkContext
{
use FeatureContextHelper;
/**
* @Given que preencho corretamente os campos da tela
*/
public function quePreenchoCorretamenteOsCamposDaTela()
{
$this->spin(function ($context) {
$faker = Faker\Factory::create();
$context->getSession()->getPage()->findField('voucher_subject')->setValue($faker->text);
$context->getSession()->getPage()->findField('voucher_nameRecipient')->setValue($faker->name);
return true;
}
}
}
It will try to find the element within 5 seconds and then timeout if it didn't find it. It works for us very well with Selenium2 and Goutte.
If you're using a driver that does only simulate the browser (like BrowserKit or Goutte), behat will have control back only when DOM is correctly composed and ready (and of course no js can be interpreted or executed). If you use something like Selenium2 and the field is built from an asynchronous call (If I understand correctly, this is your case), is up to you to be sure that page is loaded in its entirety. That's because the request has a response and the control is passed back to Behat process.
One possibile solution at this problem is to append a class to the body right before each ajax/async call and strip it out right after every call finished. Then, create a "spinner" function in your behat context to check for the class to be gone.
From my point of view this can be done more elegant now:
$page = $this->getSession()->getPage();
$page->waitFor(5000,
function () use ($page) {
return $page->findField('voucher_subject')->isVisible();
}
);
You can also wrap this inside some private
function.