diff --git a/doc/index.rst b/doc/index.rst index dfa5ce0..e3900f7 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -230,7 +230,7 @@ the following parameters to avoid the validation error triggered by Guzzle : my_session: selenium2: ~ -* ``SaucelabsDriver`` - special flavor of the Selenium2Driver configured to use the +* ``SauceLabsDriver`` - special flavor of the Selenium2Driver configured to use the selenium2 hosted installation of saucelabs.com. In order to use it, modify your ``behat.yml`` profile: @@ -241,7 +241,20 @@ the following parameters to avoid the validation error triggered by Guzzle : Behat\MinkExtension\Extension: sessions: my_session: - saucelabs: ~ + sauce_labs: ~ + +* ``BrowserStackDriver`` - special flavor of the Selenium2Driver configured to use the + selenium2 hosted installation of browserstack.com. In order to use it, modify your + ``behat.yml`` profile: + + .. code-block:: yaml + + default: + extensions: + Behat\MinkExtension\Extension: + sessions: + my_session: + browser_stack: ~ * ``SeleniumDriver`` - javascript driver. In order to use it, modify your ``behat.yml`` profile: diff --git a/spec/Behat/MinkExtension/ServiceContainer/Driver/BrowserStackFactorySpec.php b/spec/Behat/MinkExtension/ServiceContainer/Driver/BrowserStackFactorySpec.php new file mode 100644 index 0000000..00922e7 --- /dev/null +++ b/spec/Behat/MinkExtension/ServiceContainer/Driver/BrowserStackFactorySpec.php @@ -0,0 +1,24 @@ +shouldHaveType('Behat\MinkExtension\ServiceContainer\Driver\DriverFactory'); + } + + function it_is_named_browser_stack() + { + $this->getDriverName()->shouldReturn('browser_stack'); + } + + 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 similarity index 70% rename from spec/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactorySpec.php rename to spec/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactorySpec.php index eedfcd3..21bb10c 100644 --- a/spec/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactorySpec.php +++ b/spec/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactorySpec.php @@ -4,16 +4,16 @@ namespace spec\Behat\MinkExtension\ServiceContainer\Driver; use PhpSpec\ObjectBehavior; -class SaucelabsFactorySpec extends ObjectBehavior +class SauceLabsFactorySpec extends ObjectBehavior { function it_is_a_driver_factory() { $this->shouldHaveType('Behat\MinkExtension\ServiceContainer\Driver\DriverFactory'); } - function it_is_named_saucelabs() + function it_is_named_sauce_labs() { - $this->getDriverName()->shouldReturn('saucelabs'); + $this->getDriverName()->shouldReturn('sauce_labs'); } function it_supports_javascript() diff --git a/src/Behat/MinkExtension/Extension.php b/src/Behat/MinkExtension/Extension.php index 41711c9..fcb52ab 100644 --- a/src/Behat/MinkExtension/Extension.php +++ b/src/Behat/MinkExtension/Extension.php @@ -11,10 +11,11 @@ namespace Behat\MinkExtension; use Behat\Behat\Context\ServiceContainer\ContextExtension; +use Behat\MinkExtension\ServiceContainer\Driver\BrowserStackFactory; 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\SauceLabsFactory; use Behat\MinkExtension\ServiceContainer\Driver\Selenium2Factory; use Behat\MinkExtension\ServiceContainer\Driver\SeleniumFactory; use Behat\MinkExtension\ServiceContainer\Driver\ZombieFactory; @@ -51,7 +52,8 @@ class Extension implements ExtensionInterface $this->registerDriverFactory(new SahiFactory()); $this->registerDriverFactory(new SeleniumFactory()); $this->registerDriverFactory(new Selenium2Factory()); - $this->registerDriverFactory(new SaucelabsFactory()); + $this->registerDriverFactory(new SauceLabsFactory()); + $this->registerDriverFactory(new BrowserStackFactory()); $this->registerDriverFactory(new ZombieFactory()); } diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/BrowserStackFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/BrowserStackFactory.php new file mode 100644 index 0000000..3950381 --- /dev/null +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/BrowserStackFactory.php @@ -0,0 +1,80 @@ + + * + * 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; + +class BrowserStackFactory extends Selenium2Factory +{ + /** + * {@inheritdoc} + */ + public function getDriverName() + { + return 'browser_stack'; + } + + /** + * {@inheritdoc} + */ + public function configure(ArrayNodeDefinition $builder) + { + $builder + ->children() + ->scalarNode('username')->defaultValue(getenv('BROWSERSTACK_USERNAME'))->end() + ->scalarNode('access_key')->defaultValue(getenv('BROWSERSTACK_ACCESS_KEY'))->end() + ->scalarNode('browser')->defaultValue('firefox')->end() + ->append($this->getCapabilitiesNode()) + ->end() + ; + } + + /** + * {@inheritdoc} + */ + public function buildDriver(array $config) + { + $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()); + } + + $config['capabilities'] = $capabilities; + $config['wd_host'] = sprintf('%s:%s@hub.browserstack.com/wd/hub', $config['username'], $config['access_key']); + + return parent::buildDriver($config); + } + + protected function getCapabilitiesNode() + { + $node = parent::getCapabilitiesNode(); + + $node + ->children() + ->scalarNode('name')->defaultValue('Behat feature suite')->end() + ->scalarNode('project')->end() + ->scalarNode('resolution')->end() + ->scalarNode('build')->info('will be set automatically based on the TRAVIS_JOB_NUMBER environment variable if available')->end() + ->scalarNode('os')->end() + ->scalarNode('os_version')->end() + ->scalarNode('device')->end() + ->booleanNode('browserstack-debug')->end() + ->booleanNode('browserstack-tunnel')->end() + ->end() + ; + + return $node; + } +} diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactory.php new file mode 100644 index 0000000..829f4b7 --- /dev/null +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactory.php @@ -0,0 +1,101 @@ + + * + * 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; + +class SauceLabsFactory extends Selenium2Factory +{ + /** + * {@inheritdoc} + */ + public function getDriverName() + { + return 'sauce_labs'; + } + + /** + * {@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() + ->append($this->getCapabilitiesNode()) + ->end() + ; + } + + /** + * {@inheritdoc} + */ + public function buildDriver(array $config) + { + $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'; + } + + $config['capabilities'] = $capabilities; + $config['wd_host'] = sprintf('%s:%s@%s/wd/hub', $config['username'], $config['access_key'], $host); + + return parent::buildDriver($config); + } + + protected function getCapabilitiesNode() + { + $node = parent::getCapabilitiesNode(); + + $node + ->children() + ->scalarNode('name')->defaultValue('Behat feature suite')->end() + ->scalarNode('platform')->defaultValue('Linux')->end() + ->scalarNode('selenium-version')->defaultValue('2.31.0')->end() + ->scalarNode('max-duration')->defaultValue('300')->end() + ->scalarNode('command-timeout')->end() + ->scalarNode('idle-timeout')->end() + ->scalarNode('build')->info('will be set automatically based on the TRAVIS_JOB_NUMBER environment variable if available')->end() + ->arrayNode('custom-data') + ->useAttributeAsKey('') + ->prototype('variable')->end() + ->end() + ->scalarNode('screen-resolution')->end() + ->scalarNode('tunnel-identifier')->end() + ->arrayNode('prerun') + ->children() + ->scalarNode('executable')->isRequired()->end() + ->arrayNode('args')->prototype('scalar')->end()->end() + ->booleanNode('background')->defaultFalse()->end() + ->end() + ->end() + ->booleanNode('record-video')->end() + ->booleanNode('record-screenshots')->end() + ->booleanNode('capture-html')->end() + ->booleanNode('disable-popup-handler')->end() + ->end() + ; + + return $node; + } +} diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php deleted file mode 100644 index ace030e..0000000 --- a/src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * 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 index 8a0d5b4..74be082 100644 --- a/src/Behat/MinkExtension/ServiceContainer/Driver/Selenium2Factory.php +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/Selenium2Factory.php @@ -39,72 +39,7 @@ class Selenium2Factory implements DriverFactory $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() + ->append($this->getCapabilitiesNode()) ->scalarNode('wd_host')->defaultValue('http://localhost:4444/wd/hub')->end() ->end() ; @@ -116,15 +51,91 @@ class Selenium2Factory implements DriverFactory public function buildDriver(array $config) { if (!class_exists('Behat\Mink\Driver\Selenium2Driver')) { - throw new \RuntimeException( - 'Install MinkSelenium2Driver in order to use selenium2 driver.' - ); + throw new \RuntimeException(sprintf( + 'Install MinkSelenium2Driver in order to use %s driver.', + $this->getDriverName() + )); } + $extraCapabilities = $config['capabilities']['extra_capabilities']; + unset($config['capabilities']['extra_capabilities']); + return new Definition('Behat\Mink\Driver\Selenium2Driver', array( $config['browser'], - $config['capabilities'], + array_replace($extraCapabilities, $config['capabilities']), $config['wd_host'], )); } + + protected function getCapabilitiesNode() + { + $node = new ArrayNodeDefinition('capabilities'); + + $node + ->addDefaultsIfNotSet() + ->normalizeKeys(false) + ->children() + ->scalarNode('browserName')->defaultValue('firefox')->end() + ->scalarNode('version')->defaultValue('21')->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 feature suite')->end() + ->scalarNode('deviceOrientation')->defaultValue('portrait')->end() + ->scalarNode('deviceType')->defaultValue('tablet')->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() + ->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() + ->arrayNode('extra_capabilities') + ->info('Custom capabilities merged with the known ones') + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->prototype('variable')->end() + ->end() + ->end(); + + return $node; + } }