diff --git a/behat.yml.dist b/behat.yml.dist index e6e546c..6f3e7a7 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -6,4 +6,6 @@ default: extensions: Behat\MinkExtension\Extension: base_url: http://en.wikipedia.org/ - goutte: ~ + sessions: + default: + goutte: ~ diff --git a/doc/index.rst b/doc/index.rst index 62d535c..b335a29 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -60,7 +60,9 @@ The easiest way to keep your suite updated is to use `Composer beConstructedWith($mink, array('default_session' => 'goutte', 'javascript_session' => 'selenium2')); + $this->beConstructedWith($mink, 'goutte', 'selenium2'); $event->getFeature()->willReturn($feature); $event->getScenario()->willReturn($scenario); @@ -54,6 +55,15 @@ class SessionsListenerSpec extends ObjectBehavior $this->prepareDefaultMinkSession($event); } + function it_fails_when_the_javascript_session_is_used_but_not_defined($event, $mink, $feature) + { + $this->beConstructedWith($mink, 'goutte', null); + $feature->getTags()->willReturn(array('javascript')); + + $this->shouldThrow(new ProcessingException('The @javascript tag cannot be used without enabling a javascript session')) + ->duringPrepareDefaultMinkSession($event); + } + function it_switches_to_a_named_session($event, $mink, $scenario) { $scenario->getTags()->willReturn(array('mink:test')); diff --git a/spec/Behat/MinkExtension/ServiceContainer/Driver/GoutteFactorySpec.php b/spec/Behat/MinkExtension/ServiceContainer/Driver/GoutteFactorySpec.php new file mode 100644 index 0000000..9b27f62 --- /dev/null +++ b/spec/Behat/MinkExtension/ServiceContainer/Driver/GoutteFactorySpec.php @@ -0,0 +1,24 @@ +shouldHaveType('Behat\MinkExtension\ServiceContainer\Driver\DriverFactory'); + } + + function it_is_named_goutte() + { + $this->getDriverName()->shouldReturn('goutte'); + } + + function it_does_not_support_javascript() + { + $this->supportsJavascript()->shouldBe(false); + } +} diff --git a/spec/Behat/MinkExtension/ServiceContainer/Driver/SahiFactorySpec.php b/spec/Behat/MinkExtension/ServiceContainer/Driver/SahiFactorySpec.php new file mode 100644 index 0000000..62550f2 --- /dev/null +++ b/spec/Behat/MinkExtension/ServiceContainer/Driver/SahiFactorySpec.php @@ -0,0 +1,24 @@ +shouldHaveType('Behat\MinkExtension\ServiceContainer\Driver\DriverFactory'); + } + + function it_is_named_sahi() + { + $this->getDriverName()->shouldReturn('sahi'); + } + + function it_supports_javascript() + { + $this->supportsJavascript()->shouldBe(true); + } +} diff --git a/spec/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactorySpec.php b/spec/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactorySpec.php new file mode 100644 index 0000000..05922c3 --- /dev/null +++ b/spec/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactorySpec.php @@ -0,0 +1,24 @@ +shouldHaveType('Behat\MinkExtension\ServiceContainer\Driver\DriverFactory'); + } + + function it_is_named_saucelabs() + { + $this->getDriverName()->shouldReturn('saucelabs'); + } + + function it_supports_javascript() + { + $this->supportsJavascript()->shouldBe(true); + } +} diff --git a/spec/Behat/MinkExtension/ServiceContainer/Driver/Selenium2FactorySpec.php b/spec/Behat/MinkExtension/ServiceContainer/Driver/Selenium2FactorySpec.php new file mode 100644 index 0000000..60e4199 --- /dev/null +++ b/spec/Behat/MinkExtension/ServiceContainer/Driver/Selenium2FactorySpec.php @@ -0,0 +1,24 @@ +shouldHaveType('Behat\MinkExtension\ServiceContainer\Driver\DriverFactory'); + } + + function it_is_named_selenium2() + { + $this->getDriverName()->shouldReturn('selenium2'); + } + + function it_supports_javascript() + { + $this->supportsJavascript()->shouldBe(true); + } +} diff --git a/spec/Behat/MinkExtension/ServiceContainer/Driver/SeleniumFactorySpec.php b/spec/Behat/MinkExtension/ServiceContainer/Driver/SeleniumFactorySpec.php new file mode 100644 index 0000000..4de8c85 --- /dev/null +++ b/spec/Behat/MinkExtension/ServiceContainer/Driver/SeleniumFactorySpec.php @@ -0,0 +1,24 @@ +shouldHaveType('Behat\MinkExtension\ServiceContainer\Driver\DriverFactory'); + } + + function it_is_named_selenium() + { + $this->getDriverName()->shouldReturn('selenium'); + } + + function it_supports_javascript() + { + $this->supportsJavascript()->shouldBe(true); + } +} diff --git a/spec/Behat/MinkExtension/ServiceContainer/Driver/ZombieFactorySpec.php b/spec/Behat/MinkExtension/ServiceContainer/Driver/ZombieFactorySpec.php new file mode 100644 index 0000000..6f1f219 --- /dev/null +++ b/spec/Behat/MinkExtension/ServiceContainer/Driver/ZombieFactorySpec.php @@ -0,0 +1,24 @@ +shouldHaveType('Behat\MinkExtension\ServiceContainer\Driver\DriverFactory'); + } + + function it_is_named_zombie() + { + $this->getDriverName()->shouldReturn('zombie'); + } + + function it_supports_javascript() + { + $this->supportsJavascript()->shouldBe(true); + } +} diff --git a/src/Behat/MinkExtension/Extension.php b/src/Behat/MinkExtension/Extension.php index aa23317..99261e8 100644 --- a/src/Behat/MinkExtension/Extension.php +++ b/src/Behat/MinkExtension/Extension.php @@ -11,6 +11,13 @@ namespace Behat\MinkExtension; use Behat\Behat\Context\ServiceContainer\ContextExtension; +use Behat\MinkExtension\ServiceContainer\Driver\DriverFactory; +use Behat\MinkExtension\ServiceContainer\Driver\GoutteFactory; +use Behat\MinkExtension\ServiceContainer\Driver\SahiFactory; +use Behat\MinkExtension\ServiceContainer\Driver\SaucelabsFactory; +use Behat\MinkExtension\ServiceContainer\Driver\Selenium2Factory; +use Behat\MinkExtension\ServiceContainer\Driver\SeleniumFactory; +use Behat\MinkExtension\ServiceContainer\Driver\ZombieFactory; use Behat\Testwork\EventDispatcher\ServiceContainer\EventDispatcherExtension; use Behat\Testwork\ServiceContainer\Exception\ProcessingException; use Behat\Testwork\ServiceContainer\Extension as ExtensionInterface; @@ -30,9 +37,28 @@ class Extension implements ExtensionInterface const MINK_ID = 'mink'; const SELECTORS_HANDLER_ID = 'mink.selectors_handler'; - const SESSION_TAG = 'mink.session'; const SELECTOR_TAG = 'mink.selector'; + /** + * @var DriverFactory[] + */ + private $driverFactories = array(); + + public function __construct() + { + $this->registerDriverFactory(new GoutteFactory()); + $this->registerDriverFactory(new SahiFactory()); + $this->registerDriverFactory(new SeleniumFactory()); + $this->registerDriverFactory(new Selenium2Factory()); + $this->registerDriverFactory(new SaucelabsFactory()); + $this->registerDriverFactory(new ZombieFactory()); + } + + public function registerDriverFactory(DriverFactory $driverFactory) + { + $this->driverFactories[$driverFactory->getDriverName()] = $driverFactory; + } + /** * {@inheritDoc} */ @@ -51,41 +77,17 @@ class Extension implements ExtensionInterface $this->loadMink($container); $this->loadContextInitializer($container); $this->loadSelectorsHandler($container); + $this->loadSessions($container, $config); $this->loadSessionsListener($container); if ($config['show_auto']) { $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']); - } + unset($config['sessions']); $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']); } @@ -103,151 +105,38 @@ class Extension implements ExtensionInterface ->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('default_session')->defaultNull()->info('Defaults to the first non-javascript session if any, or the first session otherwise')->end() + ->scalarNode('javascript_session')->defaultNull()->info('Defaults to the first javascript session if any')->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; - - 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('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(); + + /** @var ArrayNodeDefinition $sessionsBuilder */ + $sessionsBuilder = $builder + ->children() + ->arrayNode('sessions') + ->isRequired() + ->requiresAtLeastOneElement() + ->useAttributeAsKey('name') + ->prototype('array') + ; + + foreach ($this->driverFactories as $factory) { + $factoryNode = $sessionsBuilder->children()->arrayNode($factory->getDriverName())->canBeUnset(); + + $factory->configure($factoryNode); + } + + $sessionsBuilder + ->validate() + ->ifTrue(function ($v) {return count($v) > 1;}) + ->thenInvalid('You cannot set multiple driver types for the same session') + ->end() + ->validate() + ->ifTrue(function ($v) {return count($v) === 0;}) + ->thenInvalid('You must set a driver definition for the session.') + ->end() + ; } /** @@ -270,7 +159,6 @@ class Extension implements ExtensionInterface */ public function process(ContainerBuilder $container) { - $this->processSessions($container); $this->processSelectors($container); } @@ -302,11 +190,49 @@ class Extension implements ExtensionInterface $container->setDefinition(self::SELECTOR_TAG . '.named', $namedSelectorDefinition); } + private function loadSessions(ContainerBuilder $container, array $config) + { + $defaultSession = $config['default_session']; + $javascriptSession = $config['javascript_session']; + $javascriptSessions = $nonJavascriptSessions = array(); + + $minkDefinition = $container->getDefinition(self::MINK_ID); + + foreach ($config['sessions'] as $name => $session) { + $driver = key($session); + $factory = $this->driverFactories[$driver]; + + $definition = new Definition('Behat\Mink\Session', array( + $factory->buildDriver($session[$driver]), + new Reference(self::SELECTORS_HANDLER_ID), + )); + $minkDefinition->addMethodCall('registerSession', array($name, $definition)); + + if ($factory->supportsJavascript()) { + $javascriptSessions[] = $name; + } else { + $nonJavascriptSessions[] = $name; + } + } + + if (null === $javascriptSession && !empty($javascriptSessions)) { + $javascriptSession = $javascriptSessions[0]; + } + + if (null === $defaultSession) { + $defaultSession = !empty($nonJavascriptSessions) ? $nonJavascriptSessions[0] : $javascriptSessions[0]; + } + + $container->setParameter('mink.default_session', $defaultSession); + $container->setParameter('mink.javascript_session', $javascriptSession); + } + private function loadSessionsListener(ContainerBuilder $container) { $definition = new Definition('Behat\MinkExtension\Listener\SessionsListener', array( new Reference(self::MINK_ID), - '%mink.parameters%', + '%mink.default_session%', + '%mink.javascript_session%', )); $definition->addTag(EventDispatcherExtension::SUBSCRIBER_TAG, array('priority' => 0)); $container->setDefinition('mink.listener.sessions', $definition); @@ -322,173 +248,6 @@ class Extension implements ExtensionInterface $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.' - ); - } - - $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'); - } - - 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); diff --git a/src/Behat/MinkExtension/Listener/SessionsListener.php b/src/Behat/MinkExtension/Listener/SessionsListener.php index 0a56886..9c9d08a 100644 --- a/src/Behat/MinkExtension/Listener/SessionsListener.php +++ b/src/Behat/MinkExtension/Listener/SessionsListener.php @@ -14,6 +14,7 @@ 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\ServiceContainer\Exception\ProcessingException; use Behat\Testwork\Tester\Event\ExerciseCompleted; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -26,18 +27,21 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; class SessionsListener implements EventSubscriberInterface { private $mink; - private $parameters; + private $defaultSession; + private $javascriptSession; /** * Initializes initializer. * - * @param Mink $mink - * @param array $parameters + * @param Mink $mink + * @param string $defaultSession + * @param string|null $javascriptSession */ - public function __construct(Mink $mink, array $parameters) + public function __construct(Mink $mink, $defaultSession, $javascriptSession) { - $this->mink = $mink; - $this->parameters = $parameters; + $this->mink = $mink; + $this->defaultSession = $defaultSession; + $this->javascriptSession = $javascriptSession; } /** @@ -64,16 +68,22 @@ class SessionsListener implements EventSubscriberInterface * instead of just soft-resetting them * * @param AbstractScenarioTested $event + * + * @throws ProcessingException when the @javascript tag is used without a javascript session */ public function prepareDefaultMinkSession(AbstractScenarioTested $event) { $scenario = $event->getScenario(); $feature = $event->getFeature(); - $session = $this->parameters['default_session']; + $session = $this->defaultSession; foreach (array_merge($feature->getTags(), $scenario->getTags()) as $tag) { if ('javascript' === $tag) { - $session = $this->parameters['javascript_session']; + if (null === $this->javascriptSession) { + throw new ProcessingException('The @javascript tag cannot be used without enabling a javascript session'); + } + + $session = $this->javascriptSession; } elseif (preg_match('/^mink\:(.+)/', $tag, $matches)) { $session = $matches[1]; } diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/DriverFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/DriverFactory.php new file mode 100644 index 0000000..b2adf83 --- /dev/null +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/DriverFactory.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\MinkExtension\ServiceContainer\Driver; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; + +/** + * @author Christophe Coevoet + */ +interface DriverFactory +{ + /** + * Gets the name of the driver being configured. + * + * This will be the key of the configuration for the driver. + * + * @return string + */ + public function getDriverName(); + + /** + * Defines whether a session using this driver is eligible as default javascript session + * + * @return boolean + */ + public function supportsJavascript(); + + /** + * Setups configuration for the driver factory. + * + * @param ArrayNodeDefinition $builder + */ + public function configure(ArrayNodeDefinition $builder); + + /** + * Builds the service definition for the driver. + * + * @param array $config + * + * @return Definition + */ + public function buildDriver(array $config); +} diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/GoutteFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/GoutteFactory.php new file mode 100644 index 0000000..e29b484 --- /dev/null +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/GoutteFactory.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\MinkExtension\ServiceContainer\Driver; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; + +/** + * @author Christophe Coevoet + */ +class GoutteFactory implements DriverFactory +{ + /** + * {@inheritdoc} + */ + public function getDriverName() + { + return 'goutte'; + } + + /** + * {@inheritdoc} + */ + public function supportsJavascript() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function configure(ArrayNodeDefinition $builder) + { + $builder + ->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; + + return $v; + }) + ->end() + ->end() + ->end() + ; + } + + /** + * {@inheritdoc} + */ + public function buildDriver(array $config) + { + if (!class_exists('Behat\Mink\Driver\GoutteDriver')) { + throw new \RuntimeException( + 'Install MinkGoutteDriver in order to use goutte driver.' + ); + } + + $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'], + )), + )); + + return new Definition('Behat\Mink\Driver\GoutteDriver', array( + $clientDefinition, + )); + } +} diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/SahiFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/SahiFactory.php new file mode 100644 index 0000000..860f3cf --- /dev/null +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/SahiFactory.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\MinkExtension\ServiceContainer\Driver; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; + +class SahiFactory implements DriverFactory +{ + /** + * {@inheritdoc} + */ + public function getDriverName() + { + return 'sahi'; + } + + /** + * {@inheritdoc} + */ + public function supportsJavascript() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function configure(ArrayNodeDefinition $builder) + { + $builder + ->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() + ; + } + + /** + * {@inheritdoc} + */ + public function buildDriver(array $config) + { + if (!class_exists('Behat\Mink\Driver\SahiDriver')) { + throw new \RuntimeException( + 'Install MinkSahiDriver in order to use sahi driver.' + ); + } + + return 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'], + )), + )), + )); + } +} diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php new file mode 100644 index 0000000..ace030e --- /dev/null +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\MinkExtension\ServiceContainer\Driver; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; + +class SaucelabsFactory implements DriverFactory +{ + /** + * {@inheritdoc} + */ + public function getDriverName() + { + return 'saucelabs'; + } + + /** + * {@inheritdoc} + */ + public function supportsJavascript() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function configure(ArrayNodeDefinition $builder) + { + $builder + ->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() + ; + } + + /** + * {@inheritdoc} + */ + public function buildDriver(array $config) + { + if (!class_exists('Behat\Mink\Driver\Selenium2Driver')) { + throw new \RuntimeException( + 'Install MinkSelenium2Driver in order to use saucelabs driver.' + ); + } + $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'; + } + + return new Definition('Behat\Mink\Driver\Selenium2Driver', array( + $config['browser'], + $capabilities, + sprintf('%s:%s@%s/wd/hub', $config['username'], $config['access_key'], $host), + )); + } +} diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/Selenium2Factory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/Selenium2Factory.php new file mode 100644 index 0000000..8a0d5b4 --- /dev/null +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/Selenium2Factory.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\MinkExtension\ServiceContainer\Driver; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; + +class Selenium2Factory implements DriverFactory +{ + /** + * {@inheritdoc} + */ + public function getDriverName() + { + return 'selenium2'; + } + + /** + * {@inheritdoc} + */ + public function supportsJavascript() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function configure(ArrayNodeDefinition $builder) + { + $builder + ->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('http://localhost:4444/wd/hub')->end() + ->end() + ; + } + + /** + * {@inheritdoc} + */ + public function buildDriver(array $config) + { + if (!class_exists('Behat\Mink\Driver\Selenium2Driver')) { + throw new \RuntimeException( + 'Install MinkSelenium2Driver in order to use selenium2 driver.' + ); + } + + return new Definition('Behat\Mink\Driver\Selenium2Driver', array( + $config['browser'], + $config['capabilities'], + $config['wd_host'], + )); + } +} diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/SeleniumFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/SeleniumFactory.php new file mode 100644 index 0000000..03fb9a5 --- /dev/null +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/SeleniumFactory.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\MinkExtension\ServiceContainer\Driver; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; + +class SeleniumFactory implements DriverFactory +{ + /** + * {@inheritdoc} + */ + public function getDriverName() + { + return 'selenium'; + } + + /** + * {@inheritdoc} + */ + public function supportsJavascript() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function configure(ArrayNodeDefinition $builder) + { + $builder + ->children() + ->scalarNode('host')->defaultValue('127.0.0.1')->end() + ->scalarNode('port')->defaultValue(4444)->end() + ->scalarNode('browser')->defaultValue('*%mink.browser_name%')->end() + ->end() + ; + } + + /** + * {@inheritdoc} + */ + public function buildDriver(array $config) + { + if (!class_exists('Behat\Mink\Driver\SeleniumDriver')) { + throw new \RuntimeException( + 'Install MinkSeleniumDriver in order to activate selenium session.' + ); + } + + return new Definition('Behat\Mink\Driver\SeleniumDriver', array( + $config['browser'], + '%mink.base_url%', + new Definition('Selenium\Client', array( + $config['host'], + $config['port'], + )), + )); + } +} diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/ZombieFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/ZombieFactory.php new file mode 100644 index 0000000..59cb5df --- /dev/null +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/ZombieFactory.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\MinkExtension\ServiceContainer\Driver; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; + +class ZombieFactory implements DriverFactory +{ + /** + * {@inheritdoc} + */ + public function getDriverName() + { + return 'zombie'; + } + + /** + * {@inheritdoc} + */ + public function supportsJavascript() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function configure(ArrayNodeDefinition $builder) + { + $builder + ->children() + ->scalarNode('host')->defaultValue('127.0.0.1')->end() + ->scalarNode('port')->defaultValue(8124)->end() + ->booleanNode('auto_server')->defaultTrue()->end() + ->scalarNode('node_bin')->defaultValue('node')->end() + ->scalarNode('server_path')->defaultNull()->end() + ->scalarNode('threshold')->defaultValue(2000000)->end() + ->scalarNode('node_modules_path')->defaultValue('')->end() + ->end() + ; + } + + /** + * {@inheritdoc} + */ + public function buildDriver(array $config) + { + if (!class_exists('Behat\Mink\Driver\ZombieDriver')) { + throw new \RuntimeException( + 'Install MinkZombieDriver in order to use zombie driver.' + ); + } + + return 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'], + )); + } +}