Files
friends-of-behat-mink-exten…/src/Behat/MinkExtension/Extension.php
2014-04-26 20:23:19 +02:00

303 lines
12 KiB
PHP

<?php
/*
* This file is part of the Behat MinkExtension.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
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;
use Behat\Testwork\ServiceContainer\ExtensionManager;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
/**
* Mink extension for Behat class.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
class Extension implements ExtensionInterface
{
const MINK_ID = 'mink';
const SELECTORS_HANDLER_ID = 'mink.selectors_handler';
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}
*/
public function load(ContainerBuilder $container, array $config)
{
if (isset($config['mink_loader'])) {
$basePath = $container->getParameter('paths.base');
if (file_exists($basePath.DIRECTORY_SEPARATOR.$config['mink_loader'])) {
require($basePath.DIRECTORY_SEPARATOR.$config['mink_loader']);
} else {
require($config['mink_loader']);
}
}
$this->loadMink($container);
$this->loadContextInitializer($container);
$this->loadSelectorsHandler($container);
$this->loadSessions($container, $config);
$this->loadSessionsListener($container);
if ($config['show_auto']) {
$this->loadFailureShowListener($container);
}
unset($config['sessions']);
$container->setParameter('mink.parameters', $config);
$container->setParameter('mink.base_url', $config['base_url']);
$container->setParameter('mink.browser_name', $config['browser_name']);
}
/**
* {@inheritDoc}
*/
public function configure(ArrayNodeDefinition $builder)
{
// Rewrite keys to define a shortcut way without allowing conflicts with real keys
$renamedKeys = array_diff(
array_keys($this->driverFactories),
array('mink_loader', 'base_url', 'files_path', 'show_auto', 'show_cmd', 'show_tmp_dir', 'default_session', 'javascript_session', 'browser_name', 'sessions')
);
$builder
->beforeNormalization()
->always()
->then(function ($v) use ($renamedKeys) {
foreach ($renamedKeys as $driverType) {
if (!array_key_exists($driverType, $v) || isset($v['sessions'][$driverType])) {
continue;
}
$v['sessions'][$driverType][$driverType] = $v[$driverType];
unset($v[$driverType]);
}
return $v;
})
->end()
->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')->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()
->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()
;
}
/**
* {@inheritDoc}
*/
public function getConfigKey()
{
return 'mink';
}
/**
* {@inheritdoc}
*/
public function initialize(ExtensionManager $extensionManager)
{
}
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
$this->processSelectors($container);
}
private function loadMink(ContainerBuilder $container)
{
$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 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];
} elseif (null !== $javascriptSession && !in_array($javascriptSession, $javascriptSessions)) {
throw new InvalidConfigurationException(sprintf(
'The javascript session must be one of the enabled javascript sessions (%s), but got %s',
json_encode($javascriptSessions),
$javascriptSession
));
}
if (null === $defaultSession) {
$defaultSession = !empty($nonJavascriptSessions) ? $nonJavascriptSessions[0] : $javascriptSessions[0];
} elseif (!isset($config['sessions'][$defaultSession])) {
throw new InvalidConfigurationException(sprintf('The default session must be one of the enabled sessions, but got %s', $defaultSession));
}
$container->setParameter('mink.default_session', $defaultSession);
$container->setParameter('mink.javascript_session', $javascriptSession);
$container->setParameter('mink.available_javascript_sessions', $javascriptSessions);
}
private function loadSessionsListener(ContainerBuilder $container)
{
$definition = new Definition('Behat\MinkExtension\Listener\SessionsListener', array(
new Reference(self::MINK_ID),
'%mink.default_session%',
'%mink.javascript_session%',
'%mink.available_javascript_sessions%',
));
$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 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))
);
}
}
}
}