如何让behat在填充之前等待元素显示在屏幕上?

如何让behat在填充之前等待元素显示在屏幕上?

问题描述:

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?

当我点击按钮时,会打开一个带有表单的新页面,我需要填写该页面上的字段 。 p>

但是,只要页面开始加载,就会尝试填充尚未加载的字段。 p>

我想在尝试填充字段之前等待显示字段的隐式等待。 p>

  / **  
 * @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-&gt  ;名称); 
} 
  code>  pre> 
 
 

有人可以帮助我吗? p> div>

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.