diff --git a/.travis.yml b/.travis.yml index eec5847..32c32a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,16 +8,18 @@ php: env: - SYMFONY_VERSION='2.2.*' - SYMFONY_VERSION='2.3.*' + - SYMFONY_VERSION='2.4.*' - SYMFONY_VERSION='2.4.*@dev' matrix: allow_failures: - - env: "SYMFONY_VERSION='2.4.*@dev'" + - env: "SYMFONY_VERSION='2.5.*@dev'" before_script: - - curl http://getcomposer.org/installer | php - - php composer.phar require --no-update symfony/symfony=$SYMFONY_VERSION - - php composer.phar install --dev --prefer-source + - composer require --no-update symfony/symfony=$SYMFONY_VERSION + - composer install --prefer-source - export PATH=./vendor/bin:$PATH -script: behat -fprogress +script: + - phpspec run -f pretty + - behat -fprogress diff --git a/README.md b/README.md index 1609eab..f5f6d6b 100755 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Status](https://secure.travis-ci.org/Behat/MinkExtension.png?branch=master)](htt Provides integration layer: * Additional services for Behat (`Mink`, `Sessions`, `Drivers`). -* `Behat\MinkExtension\Context\MinkAwareInterface` which provides `Mink` +* `Behat\MinkExtension\Context\MinkAwareContext` which provides `Mink` instance for your contexts or subcontexts. * Base `Behat\MinkExtension\Context\MinkContext` context which provides base step definitions and hooks for your contexts or subcontexts. Or it could be diff --git a/composer.json b/composer.json index eccefa7..70ea417 100644 --- a/composer.json +++ b/composer.json @@ -14,12 +14,13 @@ "require": { "php": ">=5.3.2", - "behat/behat": "~3.0-RC1@dev", + "behat/behat": "~3.0-RC2@dev", "behat/mink": ">=1.4.3,<1.6-dev", "symfony/config": "~2.2" }, "require-dev": { + "phpspec/phpspec": "2.0.*@dev", "behat/mink-goutte-driver": "~1.0" }, diff --git a/doc/index.rst b/doc/index.rst index b7bcd19..62d535c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -9,48 +9,24 @@ and languages provide functional testing tools. Today we’ll talk about how to use Behat for functional testing of web applications. `Mink `_ is a tool exactly for that and this extension provides integration for it. -Basically, MinkExtension is an integration layer between Behat 2.4+ and Mink 1.4+ +Basically, MinkExtension is an integration layer between Behat 3.0+ and Mink 1.4+ and it provides: * Additional services for Behat (``Mink``, ``Sessions``, ``Drivers``). -* ``Behat\MinkExtension\Context\MinkAwareInterface`` which provides ``Mink`` - instance for your contexts or subcontexts. +* ``Behat\MinkExtension\Context\MinkAwareContext`` which provides ``Mink`` + instance for your contexts. * Base ``Behat\MinkExtension\Context\MinkContext`` context which provides base step definitions and hooks for your contexts or subcontexts. Or it could be - even used as subcontext on its own. + even used as context on its own. Installation ------------ This extension requires: -* Behat 2.4+ +* Behat 3.0+ * Mink 1.4+ -Through PHAR -~~~~~~~~~~~~ - -You should first download 3 phar archives: - -* `behat.phar `_ - Behat itself -* `mink.phar `_ - Mink framework -* `mink_extension.phar `_ - - integration extension - -After downloading and placing them into project directory, you need to -activate ``mink_extension.phar`` in your ``behat.yml``: - -.. code-block:: yaml - - # behat.yml - default: - # ... - extensions: - mink_extension.phar: - mink_loader: 'mink-VERSION.phar' - base_url: 'http://example.com' - goutte: ~ - Through Composer ~~~~~~~~~~~~~~~~ @@ -61,10 +37,10 @@ The easiest way to keep your suite updated is to use `Composer useContext('mink', new MinkContext); - } - } + default: + suites: + my_suite: + contexts: + - FeatureContext + - Behat\MinkExtension\Context\MinkContext .. note:: @@ -161,17 +122,10 @@ After installing extension, there would be 6 usage options available for you: It will cause ``RedundantException``. So, you can inherit from ``MinkContext`` only with one of your context/subcontext classes. - .. note:: - - Here, we are also extending our main context from ``RawMinkContext`` class. - This class doesn't provide any definitions or hooks - just helper methods - for you to interact with Mink. It means, that you could extend ``RawMinkContext`` - with as many context classes in your suite as you want. - -5. If you're on the php 5.4+, you can simply use ``Behat\MinkExtension\Context\MinkDictionary`` - trait inside your ``FeatureContext`` or any of its subcontexts. This trait will provide - all the needed methods, hooks and definitions for you to start. You can use this trait **only - once** inside your feature context tree. +4. If you're on the php 5.4+, you can simply use ``Behat\MinkExtension\Context\MinkDictionary`` + trait inside your ``FeatureContext``. This trait will provide all the needed methods, + hooks and definitions for you to start. You can use this trait **only once** inside + your feature context tree, and it cannot be used at the same time than the ``MinkContext``. .. code-block:: php @@ -193,10 +147,9 @@ After installing extension, there would be 6 usage options available for you: } } -6. Implementing ``Behat\MinkExtension\Context\MinkAwareInterface`` with your context or its - subcontexts. +5. Implementing ``Behat\MinkExtension\Context\MinkAwareContext`` with your context. -There's common things between last 5 methods. In each of those, target context will implement +There's common things these methods. In each of those, target context will implement ``setMink(Mink $mink)`` and ``setMinkParameters(array $parameters)`` methods. Those methods would be automatically called **immediately after** each context creation before each scenario. And this ``$mink`` instance will be preconfigured based on the settings you've provided in your @@ -212,7 +165,7 @@ Drivers ~~~~~~~ First of all, there's drivers enabling configuration. MinkExtension comes -with support for 5 drivers out of the box: +with support for 6 drivers out of the box: * ``GoutteDriver`` - default headless driver. It is used by default, which means that if you didn't changed ``default_session`` (another parameter) - you should @@ -226,12 +179,12 @@ with support for 5 drivers out of the box: goutte: ~ .. Tips : HTTPS and self-signed certificate -In case you use Behat/Mink/Goutte to test your application, and want to test an -application secured with HTTPS, but with a self-signed certificate, you can use +In case you use Behat/Mink/Goutte to test your application, and want to test an +application secured with HTTPS, but with a self-signed certificate, you can use the following parameters to avoid the validation error triggered by Guzzle : .. code-block:: yaml - + default: extensions: Behat\MinkExtension\Extension: @@ -252,6 +205,20 @@ the following parameters to avoid the validation error triggered by Guzzle : Behat\MinkExtension\Extension: selenium2: ~ +* ``SaucelabsDriver`` - special flavor of the Selenium2Driver configured to use the + selenium2 hosted installation of saucelabs.com. You could use it by setting it in + ``javascript_session`` to ``saucelabs`` and by marking scenarios as ``@javascript`` + or simply by marking scenarios with ``mink:saucelabs`` (no need to switch + ``javascript_session`` in this case). In order to enable it, modify your ``behat.yml`` + profile: + + .. code-block:: yaml + + default: + extensions: + Behat\MinkExtension\Extension: + saucelabs: ~ + * ``SeleniumDriver`` - another javascript driver. You could use it by setting it in ``javascript_session`` to ``selenium`` and by marking scenarios as ``@javascript`` or simply by marking scenarios with ``mink:selenium`` (no need to switch @@ -300,7 +267,7 @@ the following parameters to avoid the validation error triggered by Guzzle : - GoutteDriver - ``behat/mink-goutte-driver`` - SeleniumDriver - ``behat/mink-selenium-driver`` - - WebDriver - ``behat/mink-selenium2-driver`` + - Selenium2Driver (also used for Saucelabs) - ``behat/mink-selenium2-driver`` - SahiDriver - ``behat/mink-sahi-driver`` - ZombieDriver - ``behat/mink-zombie-driver`` @@ -326,10 +293,16 @@ There's other useful parameters, that you can use to configure your suite: currently opened page into temporary file and opens it with some browser utility (for debugging). This option defines command to be used for opening. For example: ``show_cmd: 'firefox %s'``. -* ``browser_name`` - metaoption, that defines which browser to use for Sahi, +* ``show_tmp_dir`` - the temporary folder used to show the opened page (defaults + to the system temp dir) +* ``show_auto`` - Whether the opened page should be shown automatically when + a step fails. +* ``browser_name`` - meta-option, that defines which browser to use for Sahi, Selenium and Selenium2 drivers. * ``default_session`` - defines default session (driver) to be used for all - untagged scenarios. Could be any enabled driver name. + untagged scenarios. Could be any enabled driver name. Defaults to ``goutte`` * ``javascript_session`` - defines javascript session (driver) (the one, which will be used for ``@javascript`` tagged scenarios). Could be any enabled driver - name. + name. Defaults to ``selenium2`` +* ``mink_loader`` - path to a file loaded to make Mink available (useful when + using the PHAR archive for Mink, useless when using Composer) diff --git a/spec/Behat/MinkExtension/Context/Initializer/MinkAwareInitializerSpec.php b/spec/Behat/MinkExtension/Context/Initializer/MinkAwareInitializerSpec.php new file mode 100644 index 0000000..84b208a --- /dev/null +++ b/spec/Behat/MinkExtension/Context/Initializer/MinkAwareInitializerSpec.php @@ -0,0 +1,33 @@ +beConstructedWith($mink, array('base_url' => 'foo')); + } + + function it_is_a_context_initializer() + { + $this->shouldHaveType('Behat\Behat\Context\Initializer\ContextInitializer'); + } + + function it_does_nothing_for_basic_contexts(Context $context) + { + $this->initializeContext($context); + } + + function it_injects_mink_and_parameters_in_mink_aware_contexts(MinkAwareContext $context, $mink) + { + $context->setMink($mink)->shouldBeCalled(); + $context->setMinkParameters(array('base_url' => 'foo'))->shouldBeCalled(); + $this->initializeContext($context); + } +} diff --git a/spec/Behat/MinkExtension/ExtensionSpec.php b/spec/Behat/MinkExtension/ExtensionSpec.php new file mode 100644 index 0000000..0b38619 --- /dev/null +++ b/spec/Behat/MinkExtension/ExtensionSpec.php @@ -0,0 +1,18 @@ +shouldHaveType('Behat\Testwork\ServiceContainer\Extension'); + } + + function it_is_named_mink() + { + $this->getConfigKey()->shouldReturn('mink'); + } +} diff --git a/spec/Behat/MinkExtension/Listener/SessionsListenerSpec.php b/spec/Behat/MinkExtension/Listener/SessionsListenerSpec.php new file mode 100644 index 0000000..49846ad --- /dev/null +++ b/spec/Behat/MinkExtension/Listener/SessionsListenerSpec.php @@ -0,0 +1,100 @@ +beConstructedWith($mink, array('default_session' => 'goutte', 'javascript_session' => 'selenium2')); + + $event->getFeature()->willReturn($feature); + $event->getScenario()->willReturn($scenario); + + $feature->hasTag('insulated')->willReturn(false); + $feature->getTags()->willReturn(array()); + $scenario->hasTag('insulated')->willReturn(false); + $scenario->getTags()->willReturn(array()); + } + + function it_is_an_event_subscriber() + { + $this->shouldHaveType('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + } + + function it_resets_the_default_session_before_scenarios($event, $mink) + { + $mink->resetSessions()->shouldBeCalled(); + $mink->setDefaultSessionName('goutte')->shouldBeCalled(); + + $this->prepareDefaultMinkSession($event); + } + + function it_switches_to_the_javascript_session_for_tagged_scenarios($event, $mink, $scenario) + { + $scenario->getTags()->willReturn(array('javascript')); + $mink->resetSessions()->shouldBeCalled(); + $mink->setDefaultSessionName('selenium2')->shouldBeCalled(); + + $this->prepareDefaultMinkSession($event); + } + + function it_switches_to_the_javascript_session_for_tagged_features($event, $mink, $feature) + { + $feature->getTags()->willReturn(array('javascript')); + $mink->resetSessions()->shouldBeCalled(); + $mink->setDefaultSessionName('selenium2')->shouldBeCalled(); + + $this->prepareDefaultMinkSession($event); + } + + function it_switches_to_a_named_session($event, $mink, $scenario) + { + $scenario->getTags()->willReturn(array('mink:test')); + $mink->resetSessions()->shouldBeCalled(); + $mink->setDefaultSessionName('test')->shouldBeCalled(); + + $this->prepareDefaultMinkSession($event); + } + + function it_prefers_the_scenario_over_the_feature($event, $mink, $scenario, $feature) + { + $scenario->getTags()->willReturn(array('mink:test')); + $feature->getTags()->willReturn(array('javascript')); + $mink->resetSessions()->shouldBeCalled(); + $mink->setDefaultSessionName('test')->shouldBeCalled(); + + $this->prepareDefaultMinkSession($event); + } + + function it_stops_the_sessions_for_insulated_scenarios($event, $mink, $scenario) + { + $scenario->hasTag('insulated')->willReturn(true); + $mink->stopSessions()->shouldBeCalled(); + $mink->setDefaultSessionName('goutte')->shouldBeCalled(); + + $this->prepareDefaultMinkSession($event); + } + + function it_stops_the_sessions_for_insulated_features($event, $mink, $feature) + { + $feature->hasTag('insulated')->willReturn(true); + $mink->stopSessions()->shouldBeCalled(); + $mink->setDefaultSessionName('goutte')->shouldBeCalled(); + + $this->prepareDefaultMinkSession($event); + } + + function it_stops_the_sessions_at_the_end_of_the_exercise($mink) + { + $mink->stopSessions()->shouldBeCalled(); + + $this->tearDownMinkSessions(); + } +} diff --git a/src/Behat/MinkExtension/Compiler/SelectorsPass.php b/src/Behat/MinkExtension/Compiler/SelectorsPass.php deleted file mode 100644 index b0657b5..0000000 --- a/src/Behat/MinkExtension/Compiler/SelectorsPass.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * Selectors handler compilation pass. Registers all available Mink selector engines. - * - * @author Konstantin Kudryashov - */ -class SelectorsPass implements CompilerPassInterface -{ - /** - * Registers additional Mink selector handlers. - * - * @param ContainerBuilder $container - */ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('behat.mink.selector.handler')) { - return; - } - - $handlerDefinition = $container->getDefinition('behat.mink.selector.handler'); - foreach ($container->findTaggedServiceIds('behat.mink.selector') as $id => $attributes) { - foreach ($attributes as $attribute) { - if (isset($attribute['alias']) && $alias = $attribute['alias']) { - $handlerDefinition->addMethodCall( - 'registerSelector', array($alias, new Reference($id)) - ); - } - } - } - } -} diff --git a/src/Behat/MinkExtension/Compiler/SessionsPass.php b/src/Behat/MinkExtension/Compiler/SessionsPass.php deleted file mode 100644 index 44e1661..0000000 --- a/src/Behat/MinkExtension/Compiler/SessionsPass.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * Behat\Mink container compilation pass. - * Registers all available in controller Mink sessions. - * - * @author Konstantin Kudryashov - */ -class SessionsPass implements CompilerPassInterface -{ - /** - * Registers Mink sessions. - * - * @param ContainerBuilder $container - */ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('behat.mink')) { - return; - } - $minkDefinition = $container->getDefinition('behat.mink'); - - foreach ($container->findTaggedServiceIds('behat.mink.session') as $id => $attributes) { - foreach ($attributes as $attribute) { - if (isset($attribute['alias']) && $name = $attribute['alias']) { - $minkDefinition->addMethodCall( - 'registerSession', array($name, new Reference($id)) - ); - } - } - } - - $minkDefinition->addMethodCall( - 'setDefaultSessionName', array($container->getParameter('behat.mink.default_session')) - ); - } -} diff --git a/src/Behat/MinkExtension/Context/ClassGuesser/MinkContextClassGuesser.php b/src/Behat/MinkExtension/Context/ClassGuesser/MinkContextClassGuesser.php deleted file mode 100644 index da75d34..0000000 --- a/src/Behat/MinkExtension/Context/ClassGuesser/MinkContextClassGuesser.php +++ /dev/null @@ -1,22 +0,0 @@ - + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Behat\MinkExtension\Context\Initializer; use Behat\Behat\Context\Context; use Behat\Behat\Context\Initializer\ContextInitializer; use Behat\Mink\Mink; -use Behat\MinkExtension\Context\MinkAwareInterface; - -/* - * This file is part of the Behat\MinkExtension. - * (c) Konstantin Kudryashov - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ +use Behat\MinkExtension\Context\MinkAwareContext; /** * Mink aware contexts initializer. @@ -40,20 +40,29 @@ class MinkAwareInitializer implements ContextInitializer } /** - * Checks if initializer supports provided context. + * Initializes provided context. + * + * @param Context $context + */ + public function initializeContext(Context $context) + { + if (!$context instanceof MinkAwareContext && !$this->usesMinkDictionary($context)) { + return; + } + + $context->setMink($this->mink); + $context->setMinkParameters($this->parameters); + } + + /** + * Checks whether the context uses the MinkDictionary trait. * * @param Context $context * * @return Boolean */ - public function supportsContext(Context $context) + private function usesMinkDictionary(Context $context) { - // if context/subcontext implements MinkAwareInterface - if ($context instanceof MinkAwareInterface) { - return true; - } - - // if context/subcontext uses MinkDictionary trait $refl = new \ReflectionObject($context); if (method_exists($refl, 'getTraitNames')) { if (in_array('Behat\\MinkExtension\\Context\\MinkDictionary', $refl->getTraitNames())) { @@ -63,15 +72,4 @@ class MinkAwareInitializer implements ContextInitializer return false; } - - /** - * Initializes provided context. - * - * @param Context $context - */ - public function initializeContext(Context $context) - { - $context->setMink($this->mink); - $context->setMinkParameters($this->parameters); - } } diff --git a/src/Behat/MinkExtension/Context/MinkAwareInterface.php b/src/Behat/MinkExtension/Context/MinkAwareContext.php similarity index 84% rename from src/Behat/MinkExtension/Context/MinkAwareInterface.php rename to src/Behat/MinkExtension/Context/MinkAwareContext.php index 47aa810..11aaa4f 100644 --- a/src/Behat/MinkExtension/Context/MinkAwareInterface.php +++ b/src/Behat/MinkExtension/Context/MinkAwareContext.php @@ -1,23 +1,24 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +namespace Behat\MinkExtension\Context; + +use Behat\Behat\Context\Context; +use Behat\Mink\Mink; + /** * Mink aware interface for contexts. * * @author Konstantin Kudryashov */ -interface MinkAwareInterface +interface MinkAwareContext extends Context { /** * Sets Mink instance. diff --git a/src/Behat/MinkExtension/Context/MinkContext.php b/src/Behat/MinkExtension/Context/MinkContext.php index 7f5edb1..c21f4dc 100644 --- a/src/Behat/MinkExtension/Context/MinkContext.php +++ b/src/Behat/MinkExtension/Context/MinkContext.php @@ -1,18 +1,18 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +namespace Behat\MinkExtension\Context; + +use Behat\Behat\Context\TranslatableContext; +use Behat\Gherkin\Node\TableNode; + /** * Mink context for Behat BDD tool. * Provides Mink integration and base step definitions. diff --git a/src/Behat/MinkExtension/Context/MinkDictionary.php b/src/Behat/MinkExtension/Context/MinkDictionary.php index bed0882..7a940d1 100644 --- a/src/Behat/MinkExtension/Context/MinkDictionary.php +++ b/src/Behat/MinkExtension/Context/MinkDictionary.php @@ -1,21 +1,20 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +namespace Behat\MinkExtension\Context; + +use Behat\Gherkin\Node\TableNode; +use Behat\Mink\Mink; +use Behat\Mink\Session; +use Behat\Mink\WebAssert; + /** * Mink steps dictionary for Behat BDD tool. * diff --git a/src/Behat/MinkExtension/Context/RawMinkContext.php b/src/Behat/MinkExtension/Context/RawMinkContext.php index cb23cdb..c4d45ec 100644 --- a/src/Behat/MinkExtension/Context/RawMinkContext.php +++ b/src/Behat/MinkExtension/Context/RawMinkContext.php @@ -1,28 +1,26 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +namespace Behat\MinkExtension\Context; + +use Behat\Mink\Mink; +use Behat\Mink\WebAssert; +use Behat\Mink\Session; + /** * Raw Mink context for Behat BDD tool. * Provides raw Mink integration (without step definitions) and web assertions. * * @author Konstantin Kudryashov */ -class RawMinkContext implements MinkAwareInterface, Context +class RawMinkContext implements MinkAwareContext { private $mink; private $minkParameters; @@ -133,10 +131,10 @@ class RawMinkContext implements MinkAwareInterface, Context /** * Save a screenshot of the current window to the file system. * - * @param string $filename Desired filename, defaults to - * __.png - * @param string $filepath Desired filepath, defaults to - * upload_tmp_dir, falls back to sys_get_temp_dir() + * @param string $filename Desired filename, defaults to + * __.png + * @param string $filepath Desired filepath, defaults to + * upload_tmp_dir, falls back to sys_get_temp_dir() */ public function saveScreenshot($filename = null, $filepath = null) { diff --git a/src/Behat/MinkExtension/Extension.php b/src/Behat/MinkExtension/Extension.php index 92a5480..9eae967 100644 --- a/src/Behat/MinkExtension/Extension.php +++ b/src/Behat/MinkExtension/Extension.php @@ -1,24 +1,23 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Behat\MinkExtension; -use Behat\MinkExtension\Compiler\SelectorsPass; -use Behat\MinkExtension\Compiler\SessionsPass; -use Symfony\Component\Config\FileLocator, - Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition, - Symfony\Component\DependencyInjection\ContainerBuilder, - Symfony\Component\DependencyInjection\Loader\XmlFileLoader; - +use Behat\Behat\Context\ServiceContainer\ContextExtension; +use Behat\Testwork\EventDispatcher\ServiceContainer\EventDispatcherExtension; +use Behat\Testwork\ServiceContainer\Exception\ProcessingException; use Behat\Testwork\ServiceContainer\Extension as BaseExtension; - -/* - * This file is part of the Behat\MinkExtension - * - * (c) Konstantin Kudryashov - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; /** * Mink extension for Behat class. @@ -27,16 +26,19 @@ use Behat\Testwork\ServiceContainer\Extension as BaseExtension; */ class Extension implements BaseExtension { + const MINK_ID = 'mink'; + const SELECTORS_HANDLER_ID = 'mink.selectors_handler'; + + const SESSION_TAG = 'mink.session'; + const SELECTOR_TAG = 'mink.selector'; + /** * {@inheritDoc} */ public function load(ContainerBuilder $container, array $config) { - $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/services')); - $loader->load('core.xml'); - if (isset($config['mink_loader'])) { - $basePath = $container->getParameter('behat.paths.base'); + $basePath = $container->getParameter('paths.base'); if (file_exists($basePath.DIRECTORY_SEPARATOR.$config['mink_loader'])) { require($basePath.DIRECTORY_SEPARATOR.$config['mink_loader']); @@ -45,116 +47,45 @@ class Extension implements BaseExtension } } - if (isset($config['goutte'])) { - if (!class_exists('Behat\\Mink\\Driver\\GoutteDriver')) { - throw new \RuntimeException( - 'Install MinkGoutteDriver in order to activate goutte session.' - ); - } - - $loader->load('sessions/goutte.xml'); - } - if (isset($config['sahi'])) { - if (!class_exists('Behat\\Mink\\Driver\\SahiDriver')) { - throw new \RuntimeException( - 'Install MinkSahiDriver in order to activate sahi session.' - ); - } - - $loader->load('sessions/sahi.xml'); - } - if (isset($config['zombie'])) { - if (!class_exists('Behat\\Mink\\Driver\\ZombieDriver')) { - throw new \RuntimeException( - 'Install MinkZombieDriver in order to activate zombie session.' - ); - } - - $loader->load('sessions/zombie.xml'); - } - if (isset($config['selenium'])) { - if (!class_exists('Behat\\Mink\\Driver\\SeleniumDriver')) { - throw new \RuntimeException( - 'Install MinkSeleniumDriver in order to activate selenium session.' - ); - } - - $loader->load('sessions/selenium.xml'); - } - if (isset($config['selenium2'])) { - if (!class_exists('Behat\\Mink\\Driver\\Selenium2Driver')) { - throw new \RuntimeException( - 'Install MinkSelenium2Driver in order to activate selenium2 session.' - ); - } - - $loader->load('sessions/selenium2.xml'); - } - if (isset($config['saucelabs'])) { - if (!class_exists('Behat\\Mink\\Driver\\Selenium2Driver')) { - throw new \RuntimeException( - 'Install MinkSelenium2Driver in order to activate saucelabs session.' - ); - } - - $loader->load('sessions/saucelabs.xml'); - } - - $minkParameters = array(); - foreach ($config as $ns => $tlValue) { - if (!is_array($tlValue)) { - $minkParameters[$ns] = $tlValue; - } else { - foreach ($tlValue as $name => $value) { - if ('guzzle_parameters' === $name) { - $value['redirect.disable'] = true; - } - - $container->setParameter("behat.mink.$ns.$name", $value); - } - } - } - $container->setParameter('behat.mink.parameters', $minkParameters); - - if (isset($config['saucelabs'])) { - $capabilities = $container->getParameter('behat.mink.saucelabs.capabilities'); - $capabilities['tags'] = array(php_uname('n'), 'PHP '.phpversion()); - - if (getenv('TRAVIS_JOB_NUMBER')) { - $capabilities['tunnel-identifier'] = getenv('TRAVIS_JOB_NUMBER'); - $capabilities['build'] = getenv('TRAVIS_BUILD_NUMBER'); - $capabilities['tags'] = array('Travis-CI', 'PHP '.phpversion()); - } - - $container->setParameter('behat.mink.saucelabs.capabilities', $capabilities); - - $host = 'ondemand.saucelabs.com'; - if ($config['saucelabs']['connect']) { - $host = 'localhost:4445'; - } - - $username = $config['saucelabs']['username']; - $accessKey = $config['saucelabs']['access_key']; - - $container->setParameter('behat.mink.saucelabs.wd_host', sprintf( - '%s:%s@%s/wd/hub', $username, $accessKey, $host - )); - } - - if (isset($config['base_url'])) { - $container->setParameter('behat.mink.base_url', $config['base_url']); - } - $container->setParameter('behat.mink.default_session', $config['default_session']); - $container->setParameter('behat.mink.javascript_session', $config['javascript_session']); - $container->setParameter('behat.mink.browser_name', $config['browser_name']); - - $minkReflection = new \ReflectionClass('Behat\Mink\Mink'); - $minkLibPath = realpath(dirname($minkReflection->getFilename()) . '/../../../'); - $container->setParameter('mink.paths.lib', $minkLibPath); + $this->loadMink($container); + $this->loadContextInitializer($container); + $this->loadSelectorsHandler($container); + $this->loadSessionsListener($container); if ($config['show_auto']) { - $loader->load('failure_show_listener.xml'); + $this->loadFailureShowListener($container); } + + if (isset($config['goutte'])) { + $this->loadGoutteSession($container, $config['goutte']); + unset($config['goutte']); + } + if (isset($config['sahi'])) { + $this->loadSahiSession($container, $config['sahi']); + unset($config['sahi']); + } + if (isset($config['zombie'])) { + $this->loadZombieSession($container, $config['zombie']); + unset($config['zombie']); + } + if (isset($config['selenium'])) { + $this->loadSeleniumSession($container, $config['selenium']); + unset($config['zombie']); + } + if (isset($config['selenium2'])) { + $this->loadSelenium2Session($container, $config['selenium2']); + unset($config['selenium2']); + } + if (isset($config['saucelabs'])) { + $this->loadSaucelabsSession($container, $config['saucelabs']); + unset($config['saucelabs']); + } + + $container->setParameter('mink.parameters', $config); + $container->setParameter('mink.base_url', $config['base_url']); + $container->setParameter('mink.default_session', $config['default_session']); + $container->setParameter('mink.javascript_session', $config['javascript_session']); + $container->setParameter('mink.browser_name', $config['browser_name']); } /** @@ -162,244 +93,160 @@ class Extension implements BaseExtension */ public function configure(ArrayNodeDefinition $builder) { - $config = $this->loadEnvironmentConfiguration(); + $builder + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('mink_loader')->defaultNull()->end() + ->scalarNode('base_url')->defaultNull()->end() + ->scalarNode('files_path')->defaultNull()->end() + ->booleanNode('show_auto')->defaultFalse()->end() + ->scalarNode('show_cmd')->defaultNull()->end() + ->scalarNode('show_tmp_dir')->defaultValue(sys_get_temp_dir())->end() + ->scalarNode('default_session')->defaultValue('goutte')->end() + ->scalarNode('javascript_session')->defaultValue('selenium2')->end() + ->scalarNode('browser_name')->defaultValue('firefox')->end() + ->arrayNode('goutte') + ->children() + ->arrayNode('server_parameters') + ->useAttributeAsKey('key') + ->prototype('variable')->end() + ->end() + ->arrayNode('guzzle_parameters') + ->useAttributeAsKey('key') + ->prototype('variable')->end() + ->validate() + ->always() + ->then(function ($v) { + $v['redirect.disable'] = true; - $builder-> - children()-> - scalarNode('mink_loader')-> - defaultValue(isset($config['mink_loader']) ? $config['mink_loader'] : null)-> - end()-> - scalarNode('base_url')-> - defaultValue(isset($config['base_url']) ? $config['base_url'] : null)-> - end()-> - scalarNode('files_path')-> - defaultValue(isset($config['files_path']) ? $config['files_path'] : null)-> - end()-> - booleanNode('show_auto')-> - defaultValue(isset($config['show_auto']) ? 'true' === $config['show_auto'] : false)-> - end()-> - scalarNode('show_cmd')-> - defaultValue(isset($config['show_cmd']) ? $config['show_cmd'] : null)-> - end()-> - scalarNode('show_tmp_dir')-> - defaultValue(isset($config['show_tmp_dir']) ? $config['show_tmp_dir'] : sys_get_temp_dir())-> - end()-> - scalarNode('default_session')-> - defaultValue(isset($config['default_session']) ? $config['default_session'] : 'goutte')-> - end()-> - scalarNode('javascript_session')-> - defaultValue(isset($config['javascript_session']) ? $config['javascript_session'] : 'selenium2')-> - end()-> - scalarNode('browser_name')-> - defaultValue(isset($config['browser_name']) ? $config['browser_name'] : 'firefox')-> - end()-> - arrayNode('goutte')-> - children()-> - arrayNode('server_parameters')-> - useAttributeAsKey('key')-> - prototype('variable')->end()-> - end()-> - arrayNode('guzzle_parameters')-> - useAttributeAsKey('key')-> - prototype('variable')->end()-> - end()-> - end()-> - end()-> - arrayNode('sahi')-> - children()-> - scalarNode('sid')-> - defaultValue(isset($config['sahi']['sid']) ? $config['sahi']['sid'] : null)-> - end()-> - scalarNode('host')-> - defaultValue('localhost')-> - end()-> - scalarNode('port')-> - defaultValue(isset($config['sahi']['port']) ? $config['sahi']['port'] : 9999)-> - end()-> - scalarNode('browser')-> - defaultValue(isset($config['sahi']['browser']) ? $config['sahi']['browser'] : null)-> - end()-> - scalarNode('limit')-> - defaultValue(isset($config['sahi']['limit']) ? $config['sahi']['limit'] : 600)-> - end()-> - end()-> - end()-> - arrayNode('zombie')-> - children()-> - scalarNode('host')-> - defaultValue(isset($config['zombie']['host']) ? $config['zombie']['host'] : '127.0.0.1')-> - end()-> - scalarNode('port')-> - defaultValue(isset($config['zombie']['port']) ? $config['zombie']['port'] : 8124)-> - end()-> - scalarNode('auto_server')-> - defaultValue(isset($config['zombie']['auto_server']) ? $config['zombie']['auto_server'] : true)-> - end()-> - scalarNode('node_bin')-> - defaultValue(isset($config['zombie']['node_bin']) ? $config['zombie']['node_bin'] : 'node')-> - end()-> - scalarNode('server_path')-> - defaultValue(isset($config['zombie']['server_path']) ? $config['zombie']['server_path'] : null)-> - end()-> - scalarNode('threshold')-> - defaultValue(isset($config['zombie']['threshold']) ? $config['zombie']['threshold'] : 2000000)-> - end()-> - scalarNode('node_modules_path')-> - defaultValue(isset($config['zombie']['node_modules_path']) ? $config['zombie']['node_modules_path'] : '')-> - end()-> - end()-> - end()-> - arrayNode('selenium')-> - children()-> - scalarNode('host')-> - defaultValue(isset($config['selenium']['host']) ? $config['selenium']['host'] : '127.0.0.1')-> - end()-> - scalarNode('port')-> - defaultValue(isset($config['selenium']['port']) ? $config['selenium']['port'] : 4444)-> - end()-> - scalarNode('browser')-> - defaultValue(isset($config['selenium']['browser']) ? $config['selenium']['browser'] : '*%behat.mink.browser_name%')-> - end()-> - end()-> - end()-> - arrayNode('selenium2')-> - children()-> - scalarNode('browser')-> - defaultValue(isset($config['selenium2']['browser']) ? $config['selenium2']['browser'] : '%behat.mink.browser_name%')-> - end()-> - arrayNode('capabilities')-> - normalizeKeys(false)-> - children()-> - scalarNode('browserName')-> - defaultValue(isset($config['selenium2']['capabilities']['browserName']) ? $config['selenium2']['capabilities']['browserName'] : 'firefox')-> - end()-> - scalarNode('version')-> - defaultValue(isset($config['selenium2']['capabilities']['version']) ? $config['selenium2']['capabilities']['version'] : "9")-> - end()-> - scalarNode('platform')-> - defaultValue(isset($config['selenium2']['capabilities']['platform']) ? $config['selenium2']['capabilities']['platform'] : 'ANY')-> - end()-> - scalarNode('browserVersion')-> - defaultValue(isset($config['selenium2']['capabilities']['browserVersion']) ? $config['selenium2']['capabilities']['browserVersion'] : "9")-> - end()-> - scalarNode('browser')-> - defaultValue(isset($config['selenium2']['capabilities']['browser']) ? $config['selenium2']['capabilities']['browser'] : 'firefox')-> - end()-> - scalarNode('ignoreZoomSetting')-> - defaultValue(isset($config['selenium2']['capabilities']['ignoreZoomSetting']) ? $config['selenium2']['capabilities']['ignoreZoomSetting'] : 'false')-> - end()-> - scalarNode('name')-> - defaultValue(isset($config['selenium2']['capabilities']['name']) ? $config['selenium2']['capabilities']['name'] : 'Behat Test')-> - end()-> - scalarNode('deviceOrientation')-> - defaultValue(isset($config['selenium2']['capabilities']['deviceOrientation']) ? $config['selenium2']['capabilities']['deviceOrientation'] : 'portrait')-> - end()-> - scalarNode('deviceType')-> - defaultValue(isset($config['selenium2']['capabilities']['deviceType']) ? $config['selenium2']['capabilities']['deviceType'] : 'tablet')-> - end()-> - scalarNode('selenium-version')-> - defaultValue(isset($config['selenium2']['capabilities']['selenium-version']) ? $config['selenium2']['capabilities']['selenium-version'] : '2.31.0')-> - end()-> - scalarNode('max-duration')-> - defaultValue(isset($config['selenium2']['capabilities']['max-duration']) ? $config['selenium2']['capabilities']['max-duration'] : '300')-> - end()-> - booleanNode('javascriptEnabled')->end()-> - booleanNode('databaseEnabled')->end()-> - booleanNode('locationContextEnabled')->end()-> - booleanNode('applicationCacheEnabled')->end()-> - booleanNode('browserConnectionEnabled')->end()-> - booleanNode('webStorageEnabled')->end()-> - booleanNode('rotatable')->end()-> - booleanNode('acceptSslCerts')->end()-> - booleanNode('nativeEvents')->end()-> - booleanNode('passed')->end()-> - booleanNode('record-video')->end()-> - booleanNode('record-screenshots')->end()-> - booleanNode('capture-html')->end()-> - booleanNode('disable-popup-handler')->end()-> - arrayNode('proxy')-> - children()-> - scalarNode('proxyType')->end()-> - scalarNode('proxyAuthconfigUrl')->end()-> - scalarNode('ftpProxy')->end()-> - scalarNode('httpProxy')->end()-> - scalarNode('sslProxy')->end()-> - end()-> - validate()-> - ifTrue(function ($v) { + return $v; + }) + ->end() + ->end() + ->end() + ->end() + ->arrayNode('sahi') + ->children() + ->scalarNode('sid')->defaultNull()->end() + ->scalarNode('host')->defaultValue('localhost')->end() + ->scalarNode('port')->defaultValue(9999)->end() + ->scalarNode('browser')->defaultNull()->end() + ->scalarNode('limit')->defaultValue(600)->end() + ->end() + ->end() + ->arrayNode('zombie') + ->children() + ->scalarNode('host')->defaultValue('127.0.0.1')->end() + ->scalarNode('port')->defaultValue(8124)->end() + ->booleanNode('auto_server')->defaultValue(true)->end() + ->scalarNode('node_bin')->defaultValue('node')->end() + ->scalarNode('server_path')->defaultNull()->end() + ->scalarNode('threshold')->defaultValue(2000000)->end() + ->scalarNode('node_modules_path')->defaultValue('')->end() + ->end() + ->end() + ->arrayNode('selenium') + ->children() + ->scalarNode('host')->defaultValue('127.0.0.1')->end() + ->scalarNode('port')->defaultValue(4444)->end() + ->scalarNode('browser')->defaultValue('*%mink.browser_name%')->end() + ->end() + ->end() + ->arrayNode('selenium2') + ->children() + ->scalarNode('browser')->defaultValue('%mink.browser_name%')->end() + ->arrayNode('capabilities') + ->addDefaultsIfNotSet() + ->normalizeKeys(false) + ->children() + ->scalarNode('browserName')->defaultValue('firefox')->end() + ->scalarNode('version')->defaultValue('9')->end() + ->scalarNode('platform')->defaultValue('ANY')->end() + ->scalarNode('browserVersion')->defaultValue('9')->end() + ->scalarNode('browser')->defaultValue('firefox')->end() + ->scalarNode('ignoreZoomSetting')->defaultValue('false')->end() + ->scalarNode('name')->defaultValue('Behat Test')->end() + ->scalarNode('deviceOrientation')->defaultValue('portrait')->end() + ->scalarNode('deviceType')->defaultValue('tablet')->end() + ->scalarNode('selenium-version')->defaultValue('2.31.0')->end() + ->scalarNode('max-duration')->defaultValue('300')->end() + ->booleanNode('javascriptEnabled')->end() + ->booleanNode('databaseEnabled')->end() + ->booleanNode('locationContextEnabled')->end() + ->booleanNode('applicationCacheEnabled')->end() + ->booleanNode('browserConnectionEnabled')->end() + ->booleanNode('webStorageEnabled')->end() + ->booleanNode('rotatable')->end() + ->booleanNode('acceptSslCerts')->end() + ->booleanNode('nativeEvents')->end() + ->booleanNode('passed')->end() + ->booleanNode('record-video')->end() + ->booleanNode('record-screenshots')->end() + ->booleanNode('capture-html')->end() + ->booleanNode('disable-popup-handler')->end() + ->arrayNode('proxy') + ->children() + ->scalarNode('proxyType')->end() + ->scalarNode('proxyAuthconfigUrl')->end() + ->scalarNode('ftpProxy')->end() + ->scalarNode('httpProxy')->end() + ->scalarNode('sslProxy')->end() + ->end() + ->validate() + ->ifTrue(function ($v) { return empty($v); - })-> - thenUnset()-> - end()-> - end()-> - arrayNode('firefox')-> - children()-> - scalarNode('profile')-> - validate()-> - ifTrue(function ($v) { - return !file_exists($v); - })-> - thenInvalid('Cannot find profile zip file %s')-> - end()-> - end()-> - scalarNode('binary')->end()-> - end()-> - end()-> - arrayNode('chrome')-> - children()-> - arrayNode('switches')-> - prototype('scalar')->end()-> - end()-> - scalarNode('binary')->end()-> - arrayNode('extensions')-> - prototype('scalar')->end()-> - end()-> - end()-> - end()-> - end()-> - end()-> - scalarNode('wd_host')-> - defaultValue(isset($config['selenium2']['wd_host']) ? $config['selenium2']['wd_host'] : 'http://localhost:4444/wd/hub')-> - end()-> - end()-> - end()-> - arrayNode('saucelabs')-> - children()-> - scalarNode('username')-> - defaultValue(getenv('SAUCE_USERNAME'))-> - end()-> - scalarNode('access_key')-> - defaultValue(getenv('SAUCE_ACCESS_KEY'))-> - end()-> - booleanNode('connect')-> - defaultValue(isset($config['saucelabs']['connect']) ? 'true' === $config['saucelabs']['connect'] : false)-> - end()-> - scalarNode('browser')-> - defaultValue(isset($config['saucelabs']['browser']) ? $config['saucelabs']['browser'] : 'firefox')-> - end()-> - arrayNode('capabilities')-> - children()-> - scalarNode('name')-> - defaultValue(isset($config['saucelabs']['name']) ? $config['saucelabs']['name'] : 'Behat feature suite')-> - end()-> - scalarNode('platform')-> - defaultValue(isset($config['saucelabs']['platform']) ? $config['saucelabs']['platform'] : 'Linux')-> - end()-> - scalarNode('version')-> - defaultValue(isset($config['saucelabs']['version']) ? $config['saucelabs']['version'] : '21')-> - end()-> - scalarNode('deviceType')-> - defaultValue(isset($config['saucelabs']['deviceType']) ? $config['saucelabs']['deviceType'] : null)-> - end()-> - scalarNode('deviceOrientation')-> - defaultValue(isset($config['saucelabs']['deviceOrientation']) ? $config['saucelabs']['deviceOrientation'] : null)-> - end()-> - end()-> - end()-> - end()-> - end()-> - end()-> - end(); + }) + ->thenUnset() + ->end() + ->end() + ->arrayNode('firefox') + ->children() + ->scalarNode('profile') + ->validate() + ->ifTrue(function ($v) { + return !file_exists($v); + }) + ->thenInvalid('Cannot find profile zip file %s') + ->end() + ->end() + ->scalarNode('binary')->end() + ->end() + ->end() + ->arrayNode('chrome') + ->children() + ->arrayNode('switches')->prototype('scalar')->end()->end() + ->scalarNode('binary')->end() + ->arrayNode('extensions')->prototype('scalar')->end()->end() + ->end() + ->end() + ->end() + ->end() + ->scalarNode('wd_host')->defaultValue('http://localhost:4444/wd/hub')->end() + ->end() + ->end() + ->arrayNode('saucelabs') + ->children() + ->scalarNode('username')->defaultValue(getenv('SAUCE_USERNAME'))->end() + ->scalarNode('access_key')->defaultValue(getenv('SAUCE_ACCESS_KEY'))->end() + ->booleanNode('connect')->defaultFalse()->end() + ->scalarNode('browser')->defaultValue('firefox')->end() + ->arrayNode('capabilities') + ->addDefaultsIfNotSet() + ->normalizeKeys(false) + ->children() + ->scalarNode('name')->defaultValue('Behat feature suite')->end() + ->scalarNode('platform')->defaultValue('Linux')->end() + ->scalarNode('version')->defaultValue('21')->end() + ->scalarNode('selenium-version')->defaultValue('2.31.0')->end() + ->scalarNode('max-duration')->defaultValue('300')->end() + ->scalarNode('deviceType')->defaultNull()->end() + ->scalarNode('deviceOrientation')->defaultNull()->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end(); } /** @@ -415,21 +262,242 @@ class Extension implements BaseExtension */ public function process(ContainerBuilder $container) { - $sessionsPass = new SessionsPass(); - $selectorPass = new SelectorsPass(); - - $sessionsPass->process($container); - $selectorPass->process($container); + $this->processSessions($container); + $this->processSelectors($container); } - protected function loadEnvironmentConfiguration() + private function loadMink(ContainerBuilder $container) { - $config = array(); - if ($envConfig = getenv('MINK_EXTENSION_PARAMS')) { - parse_str($envConfig, $config); + $container->setDefinition(self::MINK_ID, new Definition('Behat\Mink\Mink')); + } + + private function loadContextInitializer(ContainerBuilder $container) + { + $definition = new Definition('Behat\MinkExtension\Context\Initializer\MinkAwareInitializer', array( + new Reference(self::MINK_ID), + '%mink.parameters%', + )); + $definition->addTag(ContextExtension::INITIALIZER_TAG, array('priority' => 0)); + $container->setDefinition('mink.context_initializer', $definition); + } + + private function loadSelectorsHandler(ContainerBuilder $container) + { + $container->setDefinition(self::SELECTORS_HANDLER_ID, new Definition('Behat\Mink\Selector\SelectorsHandler')); + + $cssSelectorDefinition = new Definition('Behat\Mink\Selector\CssSelector'); + $cssSelectorDefinition->addTag(self::SELECTOR_TAG, array('alias' => 'css')); + $container->setDefinition(self::SELECTOR_TAG . '.css', $cssSelectorDefinition); + + $namedSelectorDefinition = new Definition('Behat\Mink\Selector\NamedSelector'); + $namedSelectorDefinition->addTag(self::SELECTOR_TAG, array('alias' => 'named')); + $container->setDefinition(self::SELECTOR_TAG . '.named', $namedSelectorDefinition); + } + + private function loadSessionsListener(ContainerBuilder $container) + { + $definition = new Definition('Behat\MinkExtension\Listener\SessionsListener', array( + new Reference(self::MINK_ID), + '%mink.parameters%', + )); + $definition->addTag(EventDispatcherExtension::SUBSCRIBER_TAG, array('priority' => 0)); + $container->setDefinition('mink.listener.sessions', $definition); + } + + private function loadFailureShowListener(ContainerBuilder $container) + { + $definition = new Definition('Behat\MinkExtension\Listener\FailureShowListener', array( + new Reference(self::MINK_ID), + '%mink.parameters%', + )); + $definition->addTag(EventDispatcherExtension::SUBSCRIBER_TAG, array('priority' => 0)); + $container->setDefinition('mink.listener.failure_show', $definition); + } + + private function loadGoutteSession(ContainerBuilder $container, array $config) + { + if (!class_exists('Behat\Mink\Driver\GoutteDriver')) { + throw new \RuntimeException( + 'Install MinkGoutteDriver in order to activate goutte session.' + ); } - return $config; + $clientDefinition = new Definition('Behat\Mink\Driver\Goutte\Client', array( + $config['server_parameters'], + )); + $clientDefinition->addMethodCall('setClient', array( + new Definition('Guzzle\Http\Client', array( + null, + $config['guzzle_parameters'], + )), + )); + + $driverDefinition = new Definition('Behat\Mink\Driver\GoutteDriver', array( + $clientDefinition, + )); + $this->loadSession($container, $driverDefinition, 'goutte'); } -} \ No newline at end of file + private function loadSahiSession(ContainerBuilder $container, array $config) + { + if (!class_exists('Behat\Mink\Driver\SahiDriver')) { + throw new \RuntimeException( + 'Install MinkSahiDriver in order to activate sahi session.' + ); + } + + $driverDefinition = new Definition('Behat\Mink\Driver\SahiDriver', array( + '%mink.browser_name%', + new Definition('Behat\SahiClient\Client', array( + new Definition('Behat\SahiClient\Connection', array( + $config['sid'], + $config['host'], + $config['port'], + $config['browser'], + $config['limit'], + )), + )), + )); + $this->loadSession($container, $driverDefinition, 'sahi'); + } + + private function loadZombieSession(ContainerBuilder $container, array $config) + { + if (!class_exists('Behat\Mink\Driver\ZombieDriver')) { + throw new \RuntimeException( + 'Install MinkZombieDriver in order to activate zombie session.' + ); + } + + $driverDefinition = new Definition('Behat\Mink\Driver\ZombieDriver', array( + new Definition('Behat\Mink\Driver\NodeJS\Server\ZombieServer', array( + $config['host'], + $config['port'], + $config['node_bin'], + $config['server_path'], + $config['threshold'], + $config['node_modules_path'], + )), + new Definition('Behat\Mink\Driver\NodeJS\Connection', array( + $config['host'], + $config['port'], + )), + $config['auto_server'], + )); + $this->loadSession($container, $driverDefinition, 'zombie'); + } + + private function loadSeleniumSession(ContainerBuilder $container, array $config) + { + if (!class_exists('Behat\Mink\Driver\SeleniumDriver')) { + throw new \RuntimeException( + 'Install MinkSeleniumDriver in order to activate selenium session.' + ); + } + + $driverDefinition = new Definition('Behat\Mink\Driver\SeleniumDriver', array( + $config['browser'], + '%mink.base_url%', + new Definition('Selenium\Client', array( + $config['host'], + $config['port'], + )), + )); + $this->loadSession($container, $driverDefinition, 'selenium'); + } + + private function loadSelenium2Session(ContainerBuilder $container, array $config) + { + if (!class_exists('Behat\Mink\Driver\Selenium2Driver')) { + throw new \RuntimeException( + 'Install MinkSelenium2Driver in order to activate selenium2 session.' + ); + } + + $driverDefinition = new Definition('Behat\Mink\Driver\Selenium2Driver', array( + $config['browser'], + $config['capabilities'], + $config['wd_host'], + )); + $this->loadSession($container, $driverDefinition, 'selenium2'); + } + + private function loadSaucelabsSession(ContainerBuilder $container, array $config) + { + if (!class_exists('Behat\Mink\Driver\Selenium2Driver')) { + throw new \RuntimeException( + 'Install MinkSelenium2Driver in order to activate saucelabs session.' + ); + } + $capabilities = $config['capabilities']; + $capabilities['tags'] = array(php_uname('n'), 'PHP '.phpversion()); + + if (getenv('TRAVIS_JOB_NUMBER')) { + $capabilities['tunnel-identifier'] = getenv('TRAVIS_JOB_NUMBER'); + $capabilities['build'] = getenv('TRAVIS_BUILD_NUMBER'); + $capabilities['tags'] = array('Travis-CI', 'PHP '.phpversion()); + } + + $host = 'ondemand.saucelabs.com'; + if ($config['connect']) { + $host = 'localhost:4445'; + } + + $driverDefinition = new Definition('Behat\Mink\Driver\Selenium2Driver', array( + $config['browser'], + $capabilities, + sprintf('%s:%s@%s/wd/hub', $config['username'], $config['access_key'], $host), + )); + $this->loadSession($container, $driverDefinition, 'saucelabs'); + } + + private function loadSession(ContainerBuilder $container, Definition $driverDefinition, $alias) + { + $definition = new Definition('Behat\Mink\Session', array( + $driverDefinition, + new Reference(self::SELECTORS_HANDLER_ID), + )); + $definition->addTag(self::SESSION_TAG, array('alias' => $alias)); + $container->setDefinition(self::SESSION_TAG . '.' . $alias, $definition); + } + + private function processSessions(ContainerBuilder $container) + { + $handlerDefinition = $container->getDefinition(self::MINK_ID); + + foreach ($container->findTaggedServiceIds(self::SESSION_TAG) as $id => $tags) { + foreach ($tags as $tag) { + if (!isset($tag['alias'])) { + throw new ProcessingException(sprintf( + 'All `%s` tags should have an `alias` attribute, but `%s` service has none.', + $tag, + $id + )); + } + $handlerDefinition->addMethodCall( + 'registerSession', array($tag['alias'], new Reference($id)) + ); + } + } + } + + private function processSelectors(ContainerBuilder $container) + { + $handlerDefinition = $container->getDefinition(self::SELECTORS_HANDLER_ID); + + foreach ($container->findTaggedServiceIds(self::SELECTOR_TAG) as $id => $tags) { + foreach ($tags as $tag) { + if (!isset($tag['alias'])) { + throw new ProcessingException(sprintf( + 'All `%s` tags should have an `alias` attribute, but `%s` service has none.', + $tag, + $id + )); + } + $handlerDefinition->addMethodCall( + 'registerSelector', array($tag['alias'], new Reference($id)) + ); + } + } + } +} diff --git a/src/Behat/MinkExtension/Listener/FailureShowListener.php b/src/Behat/MinkExtension/Listener/FailureShowListener.php index 9daae53..92a22f2 100644 --- a/src/Behat/MinkExtension/Listener/FailureShowListener.php +++ b/src/Behat/MinkExtension/Listener/FailureShowListener.php @@ -1,22 +1,21 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +namespace Behat\MinkExtension\Listener; + +use Behat\Behat\Tester\Event\StepTested; +use Behat\Testwork\Tester\Result\TestResult; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Behat\Mink\Mink; +use Behat\Mink\Exception\Exception as MinkException; + /** * Failed step response show listener. * Listens to failed Behat steps and shows last response in a browser. @@ -41,27 +40,12 @@ class FailureShowListener implements EventSubscriberInterface } /** - * 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 + * {@inheritdoc} */ public static function getSubscribedEvents() { return array( - 'afterStep' => array('showFailedStepResponse', -10) + StepTested::AFTER => array('showFailedStepResponse', -10) ); } @@ -73,15 +57,15 @@ class FailureShowListener implements EventSubscriberInterface * `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 + * @param StepTested $event */ - public function showFailedStepResponse($event) + public function showFailedStepResponse(StepTested $event) { - if (StepEvent::FAILED !== $event->getResult()) { + if (TestResult::FAILED !== $event->getResultCode()) { return; } - - if (!$event->getException() instanceof MinkException) { + + if (!$event->getTestResult()->getException() instanceof MinkException) { return; } diff --git a/src/Behat/MinkExtension/Listener/SessionsListener.php b/src/Behat/MinkExtension/Listener/SessionsListener.php index 6812934..0a56886 100644 --- a/src/Behat/MinkExtension/Listener/SessionsListener.php +++ b/src/Behat/MinkExtension/Listener/SessionsListener.php @@ -1,22 +1,22 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +namespace Behat\MinkExtension\Listener; + +use Behat\Behat\Tester\Event\AbstractScenarioTested; +use Behat\Behat\Tester\Event\ExampleTested; +use Behat\Behat\Tester\Event\ScenarioTested; +use Behat\Mink\Mink; +use Behat\Testwork\Tester\Event\ExerciseCompleted; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + /** * Mink sessions listener. * Listens Behat events and configures/stops Mink sessions. @@ -41,29 +41,14 @@ class SessionsListener implements EventSubscriberInterface } /** - * 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 + * {@inheritdoc} */ public static function getSubscribedEvents() { return array( - 'beforeScenario' => array('prepareDefaultMinkSession', 10), - 'beforeOutlineExample' => array('prepareDefaultMinkSession', 10), - 'afterSuite' => array('tearDownMinkSessions', -10) + ScenarioTested::BEFORE => array('prepareDefaultMinkSession', 10), + ExampleTested::BEFORE => array('prepareDefaultMinkSession', 10), + ExerciseCompleted::AFTER => array('tearDownMinkSessions', -10) ); } @@ -78,14 +63,15 @@ class SessionsListener implements EventSubscriberInterface * `@insulated` tag will cause Mink to stop current sessions before scenario * instead of just soft-resetting them * - * @param ScenarioEvent|OutlineExampleEvent $event + * @param AbstractScenarioTested $event */ - public function prepareDefaultMinkSession($event) + public function prepareDefaultMinkSession(AbstractScenarioTested $event) { - $scenario = $event instanceof ScenarioEvent ? $event->getScenario() : $event->getOutline(); + $scenario = $event->getScenario(); + $feature = $event->getFeature(); $session = $this->parameters['default_session']; - foreach ($scenario->getTags() as $tag) { + foreach (array_merge($feature->getTags(), $scenario->getTags()) as $tag) { if ('javascript' === $tag) { $session = $this->parameters['javascript_session']; } elseif (preg_match('/^mink\:(.+)/', $tag, $matches)) { @@ -93,7 +79,7 @@ class SessionsListener implements EventSubscriberInterface } } - if ($scenario->hasTag('insulated')) { + if ($scenario->hasTag('insulated') || $feature->hasTag('insulated')) { $this->mink->stopSessions(); } else { $this->mink->resetSessions(); diff --git a/src/Behat/MinkExtension/services/core.xml b/src/Behat/MinkExtension/services/core.xml deleted file mode 100644 index e22a8a5..0000000 --- a/src/Behat/MinkExtension/services/core.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - null - - Behat\Mink\Mink - Behat\Mink\Session - Behat\Mink\Selector\SelectorsHandler - Behat\Mink\Selector\CssSelector - Behat\Mink\Selector\NamedSelector - - Behat\MinkExtension\Context\ClassGuesser\MinkContextClassGuesser - Behat\MinkExtension\Context\Initializer\MinkAwareInitializer - Behat\MinkExtension\Listener\SessionsListener - - goutte - selenium2 - null - null - null - firefox - - - - - - - - - - - - - - - - - - - - - %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 deleted file mode 100644 index e550353..0000000 --- a/src/Behat/MinkExtension/services/failure_show_listener.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Behat\MinkExtension\Listener\FailureShowListener - - - - - - - %behat.mink.parameters% - - - - - diff --git a/src/Behat/MinkExtension/services/sessions/goutte.xml b/src/Behat/MinkExtension/services/sessions/goutte.xml deleted file mode 100644 index 6f003fc..0000000 --- a/src/Behat/MinkExtension/services/sessions/goutte.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - Behat\Mink\Driver\GoutteDriver - Behat\Mink\Driver\Goutte\Client - Guzzle\Http\Client - - - - - - - - - - - - - - - - - - %behat.mink.goutte.server_parameters% - - - - - - - null - %behat.mink.goutte.guzzle_parameters% - - - - diff --git a/src/Behat/MinkExtension/services/sessions/sahi.xml b/src/Behat/MinkExtension/services/sessions/sahi.xml deleted file mode 100644 index 4fe73af..0000000 --- a/src/Behat/MinkExtension/services/sessions/sahi.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - Behat\Mink\Driver\SahiDriver - Behat\SahiClient\Client - Behat\SahiClient\Connection - - null - localhost - 9999 - null - 600 - - - - - - - - %behat.mink.browser_name% - - - - - - - - - - - %behat.mink.sahi.sid% - %behat.mink.sahi.host% - %behat.mink.sahi.port% - %behat.mink.sahi.browser% - %behat.mink.sahi.limit% - - - - - - diff --git a/src/Behat/MinkExtension/services/sessions/saucelabs.xml b/src/Behat/MinkExtension/services/sessions/saucelabs.xml deleted file mode 100644 index 680e86d..0000000 --- a/src/Behat/MinkExtension/services/sessions/saucelabs.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - Behat\Mink\Driver\Selenium2Driver - - %behat.mink.browser_name% - - Behat feature suite - 21 - Linux - 2.31.0 - 300 - - https://USERNAME:ACCESS_KEY@ondemand.saucelabs.com/wd/hub - - - - - - - - %behat.mink.saucelabs.browser% - %behat.mink.saucelabs.capabilities% - %behat.mink.saucelabs.wd_host% - - - - - - - - diff --git a/src/Behat/MinkExtension/services/sessions/selenium.xml b/src/Behat/MinkExtension/services/sessions/selenium.xml deleted file mode 100644 index 0fe04e7..0000000 --- a/src/Behat/MinkExtension/services/sessions/selenium.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - Behat\Mink\Driver\SeleniumDriver - Selenium\Client - - 127.0.0.1 - 4444 - *%behat.mink.browser_name% - - - - - - - - %behat.mink.selenium.browser% - %behat.mink.base_url% - - - - - - - - - %behat.mink.selenium.host% - %behat.mink.selenium.port% - - - - diff --git a/src/Behat/MinkExtension/services/sessions/selenium2.xml b/src/Behat/MinkExtension/services/sessions/selenium2.xml deleted file mode 100644 index f088b53..0000000 --- a/src/Behat/MinkExtension/services/sessions/selenium2.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Behat\Mink\Driver\Selenium2Driver - - %behat.mink.browser_name% - - firefox - 8 - ANY - 8 - firefox - Behat test - portrait - tablet - 2.31.0 - 300 - - http://localhost:4444/wd/hub - - - - - - - - %behat.mink.selenium2.browser% - %behat.mink.selenium2.capabilities% - %behat.mink.selenium2.wd_host% - - - - - - - - diff --git a/src/Behat/MinkExtension/services/sessions/zombie.xml b/src/Behat/MinkExtension/services/sessions/zombie.xml deleted file mode 100644 index 41d798d..0000000 --- a/src/Behat/MinkExtension/services/sessions/zombie.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - Behat\Mink\Driver\ZombieDriver - Behat\Mink\Driver\NodeJS\Connection - Behat\Mink\Driver\NodeJS\Server\ZombieServer - - 127.0.0.1 - 8124 - true - node - null - 2000000 - - - - - - - - - - - %behat.mink.zombie.auto_server% - - - - - - - - %behat.mink.zombie.host% - %behat.mink.zombie.port% - - - - %behat.mink.zombie.host% - %behat.mink.zombie.port% - %behat.mink.zombie.node_bin% - %behat.mink.zombie.server_path% - %behat.mink.zombie.threshold% - %behat.mink.zombie.node_modules_path% - - - -