2017-04-22 06:27:44 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace SilverStripe\BehatExtension\Utility;
|
|
|
|
|
2018-10-15 00:26:34 +02:00
|
|
|
use Behat\Behat\Hook\Scope\ScenarioScope;
|
2017-08-02 01:48:53 +02:00
|
|
|
use Behat\Behat\Hook\Scope\StepScope;
|
2017-04-22 06:27:44 +02:00
|
|
|
use Behat\Gherkin\Node\FeatureNode;
|
|
|
|
use Behat\Gherkin\Node\NodeInterface;
|
|
|
|
use Behat\Gherkin\Node\ScenarioInterface;
|
2018-10-15 00:26:34 +02:00
|
|
|
use Behat\Gherkin\Node\TaggedNodeInterface;
|
2017-04-22 06:27:44 +02:00
|
|
|
use \Exception;
|
2018-10-15 00:26:34 +02:00
|
|
|
use InvalidArgumentException;
|
2017-04-22 06:27:44 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Helpers for working with steps
|
|
|
|
*
|
|
|
|
* Note: Add `@retry` to any feature / scenario to make it retryable
|
|
|
|
*/
|
|
|
|
trait StepHelper
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Get scenario from step in a feature node
|
|
|
|
* See https://github.com/Behat/Behat/issues/653
|
|
|
|
*
|
|
|
|
* @param FeatureNode $feature
|
|
|
|
* @param NodeInterface $step
|
|
|
|
* @return ScenarioInterface
|
|
|
|
*/
|
|
|
|
protected function getStepScenario(FeatureNode $feature, NodeInterface $step)
|
|
|
|
{
|
|
|
|
$scenario = null;
|
|
|
|
foreach ($feature->getScenarios() as $nextScenario) {
|
|
|
|
if ($nextScenario->getLine() > $step->getLine()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
$scenario = $nextScenario;
|
|
|
|
}
|
|
|
|
return $scenario;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retry until no exceptions are thrown, or until
|
|
|
|
* $timeout seconds are reached.
|
|
|
|
*
|
|
|
|
* If timeout reached, re-throws the first exception.
|
|
|
|
*
|
|
|
|
* @param callable $callback
|
|
|
|
* @param int $timeout
|
|
|
|
* @return mixed
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
protected function retryThrowable($callback, $timeout = 3)
|
|
|
|
{
|
|
|
|
$firstEx = null;
|
|
|
|
do {
|
|
|
|
try {
|
|
|
|
return call_user_func($callback);
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
if (!$firstEx) {
|
|
|
|
$firstEx = $ex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sleep(1);
|
|
|
|
} while (--$timeout >= 0);
|
|
|
|
throw $firstEx;
|
|
|
|
}
|
2017-08-02 01:48:53 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a step has a given tag
|
|
|
|
*
|
2018-10-15 00:26:34 +02:00
|
|
|
* @param StepScope|ScenarioScope $event
|
2017-08-02 01:48:53 +02:00
|
|
|
* @param string $tag
|
|
|
|
* @return bool
|
|
|
|
*/
|
2018-10-15 00:26:34 +02:00
|
|
|
protected function stepHasTag($event, $tag)
|
2017-08-02 01:48:53 +02:00
|
|
|
{
|
2018-10-15 00:26:34 +02:00
|
|
|
$checks = [];
|
|
|
|
if ($event instanceof StepScope) {
|
|
|
|
$checks[] = $feature = $event->getFeature();
|
|
|
|
$checks[] = $this->getStepScenario($feature, $event->getStep());
|
|
|
|
} elseif ($event instanceof ScenarioScope) {
|
|
|
|
$checks[] = $event->getFeature();
|
|
|
|
$checks[] = $event->getScenario();
|
|
|
|
} else {
|
|
|
|
throw new InvalidArgumentException(sprintf(
|
|
|
|
'%s expected an instance of either %s or %s. Got %s instead',
|
|
|
|
__METHOD__,
|
|
|
|
StepScope::class,
|
|
|
|
ScenarioScope::class,
|
|
|
|
is_object($event) ? sprintf('an instance of %s', get_class($event)) : gettype($event)
|
|
|
|
));
|
2017-08-02 01:48:53 +02:00
|
|
|
}
|
2018-10-15 00:26:34 +02:00
|
|
|
|
|
|
|
foreach ($checks as $check) {
|
|
|
|
if ($check instanceof TaggedNodeInterface && $check->hasTag($tag)) {
|
|
|
|
return true;
|
|
|
|
}
|
2017-08-02 01:48:53 +02:00
|
|
|
}
|
2018-10-15 00:26:34 +02:00
|
|
|
|
2017-08-02 01:48:53 +02:00
|
|
|
return false;
|
|
|
|
}
|
2017-04-22 06:27:44 +02:00
|
|
|
}
|