Behat Selenium - 等待滑入的元素
Behat Selenium - Waiting for elements that slide in
我在 Behat 等待元素到 "slide in" 时遇到问题。
在我们的管理面板菜单中,每个主要项目向下滑动以显示一个子菜单,如下所示:
为了清楚起见,我添加了红线来显示菜单,并为子菜单添加了绿线。
问题是,这个菜单 "slides" 中。这是一个可爱的效果,但它破坏了我的测试。 :)
我正在尝试使用 spin function 让 Behat 等到子菜单可见后再尝试点击它。
代码:
/**
* @When I wait for :cssSelector
* @param $cssSelector
* @throws \Exception
*/
public function iWaitFor($cssSelector)
{
$this->spin(function($context) use ($cssSelector) {
/** @var $context FeatureContext */
return !is_null($context->getSession()->getPage()->find('css', $cssSelector));
});
}
/**
* @When I wait for :text to appear
* @Then I should see :text appear
* @param $text
* @throws \Exception
*/
public function iWaitForTextToAppear($text)
{
$this->spin(function($context) use ($text) {
/** @var $context FeatureContext */
return $context->getSession()->getPage()->hasContent($text);
});
}
/**
* @When I wait for :text to disappear
* @Then I should see :text disappear
* @param $text
* @throws \Exception
*/
public function iWaitForTextToDisappear($text)
{
$this->spin(function($context) use ($text) {
/** @var $context FeatureContext */
return !$context->getSession()->getPage()->hasContent($text);
});
}
/**
* 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");
}
以及该功能的一个片段:
@javascript
Scenario Outline: Create a new piece of artwork
Given I am logged in to the CMS as "adminUser"
Then I should see "Artwork"
When I follow "Artwork"
Then I should see "Pieces" appear
When I follow "Pieces"
Then I should see "Manage Artwork Pieces"
然而,虽然这在我的 Vagrant 开发环境中工作得很好,但它在 Jenkins 确认 "Pieces" 出现后尝试 follow "Pieces"
时中断。两者都是 运行 Selenium 和 XVFB 中的 Firefox on headless linux.
这些是我目前使用的版本:
╔═════════════╦══════════════╦════════════╗
║ Environment ║ Development ║ Testing ║
╠═════════════╬══════════════╬════════════╣
║ Linux ║ Ubuntu 14.04 ║ CentOS 6.6 ║
║ PHP ║ 5.5.9 ║ 5.4.38 ║
║ Behat ║ 3.0.x-dev ║ 3.0.x-dev ║
║ Selenium ║ 2.45.0 ║ 2.45.0 ║
║ XVFB ║ 1.15.1 ║ 1.15.0 ║
║ Firefox ║ 36.0.4 ║ 31.5.0 ║
╚═════════════╩══════════════╩════════════╝
我将尝试确保 Firefox 和 Selenium 都尽可能是最新的,看看是否可以解决问题,但是这已经持续了一段时间,所以我不相信。
附录
我错过了实际错误消息的示例:
When I follow "Carousel" # tests/Behat/Cms/Features/1.1.Carousel.feature:11
Offset within element cannot be scrolled into view: (110, 17): http://www.###.co.uk/admin/fp/carousel
Build info: version: '2.44.0', revision: '76d78cf', time: '2014-10-23 20:02:37'
这是直接在Then I should see "Carousel" appear
之后
此外,错误几乎是随机发生的。我需要一种万无一失的方法来确保测试不会继续,直到页面处于允许它继续的状态。我想避免任何类似 "wait" 或任何依赖于 JavaScript 或 Selenium 的东西(以防我切换到另一个驱动程序)。
更新: 将测试机上的 Selenium 更新到 2.45.0,仍然遇到同样的问题。更正了上面Dev Environment上的PHP版本,原来是5.4,实际上是5.5.9。
我相信我已经解决了这个问题,但我不是 100% 确定原因,所以如果我错了请纠正我。
我 猜测 检查页面内容总是 return 正确,无论它是否可见。相反,我转而使用 Mink 提供的断言,这些断言是为每个驱动程序独立实现的。在这种情况下,我们要求 Selenium 考虑元素是否存在。
我最初避免使用断言,因为它们会抛出异常,但是,因为异常是特定的且可知的,我们可以利用它来发挥我们的优势。
这是新代码。
/**
* @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;
});
}
自从 运行 测试套件以来,我已经进行了两次测试,每次检查的变体大约发生 100 次,并且都返回正常。如果有人想提交更好的答案/解释,我会暂时保留这个问题。
我在 Behat 等待元素到 "slide in" 时遇到问题。
在我们的管理面板菜单中,每个主要项目向下滑动以显示一个子菜单,如下所示:
为了清楚起见,我添加了红线来显示菜单,并为子菜单添加了绿线。
问题是,这个菜单 "slides" 中。这是一个可爱的效果,但它破坏了我的测试。 :)
我正在尝试使用 spin function 让 Behat 等到子菜单可见后再尝试点击它。
代码:
/**
* @When I wait for :cssSelector
* @param $cssSelector
* @throws \Exception
*/
public function iWaitFor($cssSelector)
{
$this->spin(function($context) use ($cssSelector) {
/** @var $context FeatureContext */
return !is_null($context->getSession()->getPage()->find('css', $cssSelector));
});
}
/**
* @When I wait for :text to appear
* @Then I should see :text appear
* @param $text
* @throws \Exception
*/
public function iWaitForTextToAppear($text)
{
$this->spin(function($context) use ($text) {
/** @var $context FeatureContext */
return $context->getSession()->getPage()->hasContent($text);
});
}
/**
* @When I wait for :text to disappear
* @Then I should see :text disappear
* @param $text
* @throws \Exception
*/
public function iWaitForTextToDisappear($text)
{
$this->spin(function($context) use ($text) {
/** @var $context FeatureContext */
return !$context->getSession()->getPage()->hasContent($text);
});
}
/**
* 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");
}
以及该功能的一个片段:
@javascript
Scenario Outline: Create a new piece of artwork
Given I am logged in to the CMS as "adminUser"
Then I should see "Artwork"
When I follow "Artwork"
Then I should see "Pieces" appear
When I follow "Pieces"
Then I should see "Manage Artwork Pieces"
然而,虽然这在我的 Vagrant 开发环境中工作得很好,但它在 Jenkins 确认 "Pieces" 出现后尝试 follow "Pieces"
时中断。两者都是 运行 Selenium 和 XVFB 中的 Firefox on headless linux.
这些是我目前使用的版本:
╔═════════════╦══════════════╦════════════╗
║ Environment ║ Development ║ Testing ║
╠═════════════╬══════════════╬════════════╣
║ Linux ║ Ubuntu 14.04 ║ CentOS 6.6 ║
║ PHP ║ 5.5.9 ║ 5.4.38 ║
║ Behat ║ 3.0.x-dev ║ 3.0.x-dev ║
║ Selenium ║ 2.45.0 ║ 2.45.0 ║
║ XVFB ║ 1.15.1 ║ 1.15.0 ║
║ Firefox ║ 36.0.4 ║ 31.5.0 ║
╚═════════════╩══════════════╩════════════╝
我将尝试确保 Firefox 和 Selenium 都尽可能是最新的,看看是否可以解决问题,但是这已经持续了一段时间,所以我不相信。
附录
我错过了实际错误消息的示例:
When I follow "Carousel" # tests/Behat/Cms/Features/1.1.Carousel.feature:11
Offset within element cannot be scrolled into view: (110, 17): http://www.###.co.uk/admin/fp/carousel
Build info: version: '2.44.0', revision: '76d78cf', time: '2014-10-23 20:02:37'
这是直接在Then I should see "Carousel" appear
此外,错误几乎是随机发生的。我需要一种万无一失的方法来确保测试不会继续,直到页面处于允许它继续的状态。我想避免任何类似 "wait" 或任何依赖于 JavaScript 或 Selenium 的东西(以防我切换到另一个驱动程序)。
更新: 将测试机上的 Selenium 更新到 2.45.0,仍然遇到同样的问题。更正了上面Dev Environment上的PHP版本,原来是5.4,实际上是5.5.9。
我相信我已经解决了这个问题,但我不是 100% 确定原因,所以如果我错了请纠正我。
我 猜测 检查页面内容总是 return 正确,无论它是否可见。相反,我转而使用 Mink 提供的断言,这些断言是为每个驱动程序独立实现的。在这种情况下,我们要求 Selenium 考虑元素是否存在。
我最初避免使用断言,因为它们会抛出异常,但是,因为异常是特定的且可知的,我们可以利用它来发挥我们的优势。
这是新代码。
/**
* @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;
});
}
自从 运行 测试套件以来,我已经进行了两次测试,每次检查的变体大约发生 100 次,并且都返回正常。如果有人想提交更好的答案/解释,我会暂时保留这个问题。