From c6efdf6940e8a755a31286258b80d26a8077a78e Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sat, 26 Apr 2014 22:52:05 +0200 Subject: [PATCH 1/5] Refactored the handling of Selenium2 capabilities Saucelabs-specific capabilities are removed from the Selenium2Factory in favor of the SaucelabsFactory. The SaucelabsFactory now supports all normal capabilities (inherited from the Selenium2Factory) and the list of Saucelabs-specific ones is completed. --- .../Driver/SaucelabsFactory.php | 64 ++++---- .../Driver/Selenium2Factory.php | 140 +++++++++--------- 2 files changed, 102 insertions(+), 102 deletions(-) diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php index ace030e..21572af 100644 --- a/src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php @@ -11,9 +11,8 @@ namespace Behat\MinkExtension\ServiceContainer\Driver; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; -use Symfony\Component\DependencyInjection\Definition; -class SaucelabsFactory implements DriverFactory +class SaucelabsFactory extends Selenium2Factory { /** * {@inheritdoc} @@ -23,14 +22,6 @@ class SaucelabsFactory implements DriverFactory return 'saucelabs'; } - /** - * {@inheritdoc} - */ - public function supportsJavascript() - { - return true; - } - /** * {@inheritdoc} */ @@ -42,19 +33,7 @@ class SaucelabsFactory implements DriverFactory ->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() + ->append($this->getCapabilitiesNode()) ->end() ; } @@ -64,11 +43,6 @@ class SaucelabsFactory implements DriverFactory */ 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()); @@ -83,10 +57,34 @@ class SaucelabsFactory implements DriverFactory $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), - )); + $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('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() + ->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/Selenium2Factory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/Selenium2Factory.php index 8a0d5b4..b7a3e80 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,9 +51,10 @@ 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() + )); } return new Definition('Behat\Mink\Driver\Selenium2Driver', array( @@ -127,4 +63,70 @@ class Selenium2Factory implements DriverFactory $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() + ->end(); + + return $node; + } } From 1843188aea1e595d2545421cbb053e92fcc7648c Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sat, 26 Apr 2014 23:03:50 +0200 Subject: [PATCH 2/5] Added a way to specify custom capabilities for Selenium2 This allows configuring capabilities which are specific to other drivers or custom implementations. Fixes #129 Fixes #125 Refs #135 --- .../ServiceContainer/Driver/Selenium2Factory.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/Selenium2Factory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/Selenium2Factory.php index b7a3e80..74be082 100644 --- a/src/Behat/MinkExtension/ServiceContainer/Driver/Selenium2Factory.php +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/Selenium2Factory.php @@ -57,9 +57,12 @@ class Selenium2Factory implements DriverFactory )); } + $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'], )); } @@ -125,6 +128,12 @@ class Selenium2Factory implements DriverFactory ->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; From 3b00c091e9a7169e4d2cda491f80e8071bb2c93a Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sat, 26 Apr 2014 23:30:25 +0200 Subject: [PATCH 3/5] Added a factory configuring the Selenium2Driver for BrowserStack Closes #103 Replaces #133 --- .../Driver/BrowserStackFactorySpec.php | 24 ++++++ src/Behat/MinkExtension/Extension.php | 2 + .../Driver/BrowserStackFactory.php | 80 +++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 spec/Behat/MinkExtension/ServiceContainer/Driver/BrowserStackFactorySpec.php create mode 100644 src/Behat/MinkExtension/ServiceContainer/Driver/BrowserStackFactory.php 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/src/Behat/MinkExtension/Extension.php b/src/Behat/MinkExtension/Extension.php index 41711c9..80d5da9 100644 --- a/src/Behat/MinkExtension/Extension.php +++ b/src/Behat/MinkExtension/Extension.php @@ -11,6 +11,7 @@ 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; @@ -52,6 +53,7 @@ class Extension implements ExtensionInterface $this->registerDriverFactory(new SeleniumFactory()); $this->registerDriverFactory(new Selenium2Factory()); $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; + } +} From 82fb06bb57f2ce3428f6696d58a53de699ea685d Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sat, 26 Apr 2014 23:34:44 +0200 Subject: [PATCH 4/5] Renamed the SauceLabsFactory for consistent casing --- doc/index.rst | 17 +++++++++++++++-- ...FactorySpec.php => SauceLabsFactorySpec.php} | 6 +++--- src/Behat/MinkExtension/Extension.php | 4 ++-- ...aucelabsFactory.php => SauceLabsFactory.php} | 4 ++-- 4 files changed, 22 insertions(+), 9 deletions(-) rename spec/Behat/MinkExtension/ServiceContainer/Driver/{SaucelabsFactorySpec.php => SauceLabsFactorySpec.php} (70%) rename src/Behat/MinkExtension/ServiceContainer/Driver/{SaucelabsFactory.php => SauceLabsFactory.php} (97%) 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/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 80d5da9..fcb52ab 100644 --- a/src/Behat/MinkExtension/Extension.php +++ b/src/Behat/MinkExtension/Extension.php @@ -15,7 +15,7 @@ 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; @@ -52,7 +52,7 @@ 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/SaucelabsFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactory.php similarity index 97% rename from src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php rename to src/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactory.php index 21572af..b3a4337 100644 --- a/src/Behat/MinkExtension/ServiceContainer/Driver/SaucelabsFactory.php +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactory.php @@ -12,14 +12,14 @@ namespace Behat\MinkExtension\ServiceContainer\Driver; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; -class SaucelabsFactory extends Selenium2Factory +class SauceLabsFactory extends Selenium2Factory { /** * {@inheritdoc} */ public function getDriverName() { - return 'saucelabs'; + return 'sauce_labs'; } /** From 6b02ddf2747612a48f7d3861d5bb0367d8923299 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sat, 26 Apr 2014 23:45:31 +0200 Subject: [PATCH 5/5] Added some missing SauceLabs capabilities Closes #126 --- .../ServiceContainer/Driver/SauceLabsFactory.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactory.php b/src/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactory.php index b3a4337..829f4b7 100644 --- a/src/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactory.php +++ b/src/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactory.php @@ -73,11 +73,22 @@ class SauceLabsFactory extends Selenium2Factory ->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()