From 9e2c34cbd058d20c8a47f5110dc6a772720a4356 Mon Sep 17 00:00:00 2001 From: everzet Date: Mon, 10 Sep 2012 20:20:10 +0200 Subject: [PATCH] added support for automatic show on failures --- .../Initializer/MinkAwareInitializer.php | 78 +----------- src/Behat/MinkExtension/Extension.php | 7 ++ .../Listener/FailureShowListener.php | 91 ++++++++++++++ .../Listener/SessionsListener.php | 112 ++++++++++++++++++ src/Behat/MinkExtension/services/core.xml | 6 + .../services/failure_show_listener.xml | 19 +++ 6 files changed, 237 insertions(+), 76 deletions(-) create mode 100644 src/Behat/MinkExtension/Listener/FailureShowListener.php create mode 100644 src/Behat/MinkExtension/Listener/SessionsListener.php create mode 100644 src/Behat/MinkExtension/services/failure_show_listener.xml diff --git a/src/Behat/MinkExtension/Context/Initializer/MinkAwareInitializer.php b/src/Behat/MinkExtension/Context/Initializer/MinkAwareInitializer.php index e0c83a7..184b8a0 100644 --- a/src/Behat/MinkExtension/Context/Initializer/MinkAwareInitializer.php +++ b/src/Behat/MinkExtension/Context/Initializer/MinkAwareInitializer.php @@ -2,12 +2,8 @@ namespace Behat\MinkExtension\Context\Initializer; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - use Behat\Behat\Context\Initializer\InitializerInterface, - Behat\Behat\Context\ContextInterface, - Behat\Behat\Event\ScenarioEvent, - Behat\Behat\Event\OutlineEvent; + Behat\Behat\Context\ContextInterface; use Behat\Mink\Mink; @@ -27,7 +23,7 @@ use Behat\MinkExtension\Context\MinkAwareInterface; * * @author Konstantin Kudryashov */ -class MinkAwareInitializer implements InitializerInterface, EventSubscriberInterface +class MinkAwareInitializer implements InitializerInterface { private $mink; private $parameters; @@ -44,33 +40,6 @@ class MinkAwareInitializer implements InitializerInterface, EventSubscriberInter $this->parameters = $parameters; } - /** - * Returns an array of event names this subscriber wants to listen to. - * - * The array keys are event names and the value can be: - * - * * The method name to call (priority defaults to 0) - * * An array composed of the method name to call and the priority - * * An array of arrays composed of the method names to call and respective - * priorities, or 0 if unset - * - * For instance: - * - * * array('eventName' => 'methodName') - * * array('eventName' => array('methodName', $priority)) - * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) - * - * @return array The event names to listen to - */ - public static function getSubscribedEvents() - { - return array( - 'beforeScenario' => array('prepareDefaultMinkSession', 10), - 'beforeOutline' => array('prepareDefaultMinkSession', 10), - 'afterSuite' => array('tearDownMinkSessions', -10) - ); - } - /** * Checks if initializer supports provided context. * @@ -106,47 +75,4 @@ class MinkAwareInitializer implements InitializerInterface, EventSubscriberInter $context->setMink($this->mink); $context->setMinkParameters($this->parameters); } - - /** - * Configures default Mink session before each scenario. - * Configuration is based on provided scenario tags: - * - * `@javascript` tagged scenarios will get `javascript_session` as default session - * `@mink:CUSTOM_NAME tagged scenarios will get `CUSTOM_NAME` as default session - * Other scenarios get `default_session` as default session - * - * `@insulated` tag will cause Mink to stop current sessions before scenario - * instead of just soft-resetting them - * - * @param ScenarioEvent|OutlineEvent $event - */ - public function prepareDefaultMinkSession($event) - { - $scenario = $event instanceof ScenarioEvent ? $event->getScenario() : $event->getOutline(); - $session = $this->parameters['default_session']; - - foreach ($scenario->getTags() as $tag) { - if ('javascript' === $tag) { - $session = $this->parameters['javascript_session']; - } elseif (preg_match('/^mink\:(.+)/', $tag, $matches)) { - $session = $matches[1]; - } - } - - if ($scenario->hasTag('insulated')) { - $this->mink->stopSessions(); - } else { - $this->mink->resetSessions(); - } - - $this->mink->setDefaultSessionName($session); - } - - /** - * Stops all started Mink sessions. - */ - public function tearDownMinkSessions() - { - $this->mink->stopSessions(); - } } diff --git a/src/Behat/MinkExtension/Extension.php b/src/Behat/MinkExtension/Extension.php index ae44fe4..4ac0cae 100644 --- a/src/Behat/MinkExtension/Extension.php +++ b/src/Behat/MinkExtension/Extension.php @@ -114,6 +114,10 @@ class Extension implements ExtensionInterface $minkReflection = new \ReflectionClass('Behat\Mink\Mink'); $minkLibPath = realpath(dirname($minkReflection->getFilename()) . '/../../../'); $container->setParameter('mink.paths.lib', $minkLibPath); + + if ($config['show_auto']) { + $loader->load('failure_show_listener.xml'); + } } /** @@ -136,6 +140,9 @@ class Extension implements ExtensionInterface scalarNode('files_path')-> defaultValue(isset($config['files_path']) ? $config['files_path'] : null)-> end()-> + booleanNode('show_auto')-> + defaultValue(false)-> + end()-> scalarNode('show_cmd')-> defaultValue(isset($config['show_cmd']) ? $config['show_cmd'] : null)-> end()-> diff --git a/src/Behat/MinkExtension/Listener/FailureShowListener.php b/src/Behat/MinkExtension/Listener/FailureShowListener.php new file mode 100644 index 0000000..063d535 --- /dev/null +++ b/src/Behat/MinkExtension/Listener/FailureShowListener.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Failed step response show listener. + * Listens to failed Behat steps and shows last response in a browser. + * + * @author Konstantin Kudryashov + */ +class FailureShowListener implements EventSubscriberInterface +{ + private $mink; + private $parameters; + + /** + * Initializes initializer. + * + * @param Mink $mink + * @param array $parameters + */ + public function __construct(Mink $mink, array $parameters) + { + $this->mink = $mink; + $this->parameters = $parameters; + } + + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) + * + * @return array The event names to listen to + */ + public static function getSubscribedEvents() + { + return array( + 'afterStep' => array('showFailedStepResponse', -10) + ); + } + + /** + * Shows last response of failed step with preconfigured command. + * Configuration is based on `behat.yml`: + * + * `show_auto` enable this listener (default to false) + * `show_cmd` command to run (`open %s` to open default browser on Mac) + * `show_tmp_dir` folder where to store temp files (default is system temp) + * + * @param StepEvent $event + */ + public function showFailedStepResponse($event) + { + if (StepEvent::FAILED !== $event->getResult()) { + return; + } + + if (null === $this->parameters['show_cmd']) { + throw new \RuntimeException('Set "show_cmd" parameter in behat.yml to be able to open page in browser (ex.: "show_cmd: open %s")'); + } + + $filename = rtrim($this->parameters['show_tmp_dir'], DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.uniqid().'.html'; + file_put_contents($filename, $this->mink->getSession()->getPage()->getContent()); + system(sprintf($this->parameters['show_cmd'], escapeshellarg($filename))); + } +} diff --git a/src/Behat/MinkExtension/Listener/SessionsListener.php b/src/Behat/MinkExtension/Listener/SessionsListener.php new file mode 100644 index 0000000..1e3513c --- /dev/null +++ b/src/Behat/MinkExtension/Listener/SessionsListener.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Mink sessions listener. + * Listens Behat events and configures/stops Mink sessions. + * + * @author Konstantin Kudryashov + */ +class SessionsListener implements EventSubscriberInterface +{ + private $mink; + private $parameters; + + /** + * Initializes initializer. + * + * @param Mink $mink + * @param array $parameters + */ + public function __construct(Mink $mink, array $parameters) + { + $this->mink = $mink; + $this->parameters = $parameters; + } + + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) + * + * @return array The event names to listen to + */ + public static function getSubscribedEvents() + { + return array( + 'beforeScenario' => array('prepareDefaultMinkSession', 10), + 'beforeOutline' => array('prepareDefaultMinkSession', 10), + 'afterSuite' => array('tearDownMinkSessions', -10) + ); + } + + /** + * Configures default Mink session before each scenario. + * Configuration is based on provided scenario tags: + * + * `@javascript` tagged scenarios will get `javascript_session` as default session + * `@mink:CUSTOM_NAME tagged scenarios will get `CUSTOM_NAME` as default session + * Other scenarios get `default_session` as default session + * + * `@insulated` tag will cause Mink to stop current sessions before scenario + * instead of just soft-resetting them + * + * @param ScenarioEvent|OutlineEvent $event + */ + public function prepareDefaultMinkSession($event) + { + $scenario = $event instanceof ScenarioEvent ? $event->getScenario() : $event->getOutline(); + $session = $this->parameters['default_session']; + + foreach ($scenario->getTags() as $tag) { + if ('javascript' === $tag) { + $session = $this->parameters['javascript_session']; + } elseif (preg_match('/^mink\:(.+)/', $tag, $matches)) { + $session = $matches[1]; + } + } + + if ($scenario->hasTag('insulated')) { + $this->mink->stopSessions(); + } else { + $this->mink->resetSessions(); + } + + $this->mink->setDefaultSessionName($session); + } + + /** + * Stops all started Mink sessions. + */ + public function tearDownMinkSessions() + { + $this->mink->stopSessions(); + } +} diff --git a/src/Behat/MinkExtension/services/core.xml b/src/Behat/MinkExtension/services/core.xml index 7c73578..3849395 100644 --- a/src/Behat/MinkExtension/services/core.xml +++ b/src/Behat/MinkExtension/services/core.xml @@ -14,6 +14,7 @@ Behat\MinkExtension\Context\ClassGuesser\MinkContextClassGuesser Behat\MinkExtension\Context\Initializer\MinkAwareInitializer + Behat\MinkExtension\Listener\SessionsListener goutte selenium2 @@ -43,6 +44,11 @@ %behat.mink.parameters% + + + + + %behat.mink.parameters% diff --git a/src/Behat/MinkExtension/services/failure_show_listener.xml b/src/Behat/MinkExtension/services/failure_show_listener.xml new file mode 100644 index 0000000..e550353 --- /dev/null +++ b/src/Behat/MinkExtension/services/failure_show_listener.xml @@ -0,0 +1,19 @@ + + + + + Behat\MinkExtension\Listener\FailureShowListener + + + + + + + %behat.mink.parameters% + + + + +