MinkExtension::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 9
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the Behat MinkExtension.
5
 * (c) Konstantin Kudryashov <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Behat\MinkExtension\ServiceContainer;
12
13
use Behat\Behat\Context\ServiceContainer\ContextExtension;
14
use Behat\MinkExtension\ServiceContainer\Driver\AppiumFactory;
15
use Behat\MinkExtension\ServiceContainer\Driver\BrowserStackFactory;
16
use Behat\MinkExtension\ServiceContainer\Driver\DriverFactory;
17
use Behat\MinkExtension\ServiceContainer\Driver\GoutteFactory;
18
use Behat\MinkExtension\ServiceContainer\Driver\SahiFactory;
19
use Behat\MinkExtension\ServiceContainer\Driver\SauceLabsFactory;
20
use Behat\MinkExtension\ServiceContainer\Driver\Selenium2Factory;
21
use Behat\MinkExtension\ServiceContainer\Driver\SeleniumFactory;
22
use Behat\MinkExtension\ServiceContainer\Driver\ZombieFactory;
23
use Behat\Testwork\EventDispatcher\ServiceContainer\EventDispatcherExtension;
24
use Behat\Testwork\ServiceContainer\Exception\ProcessingException;
25
use Behat\Testwork\ServiceContainer\Extension as ExtensionInterface;
26
use Behat\Testwork\ServiceContainer\ExtensionManager;
27
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
28
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
29
use Symfony\Component\DependencyInjection\ContainerBuilder;
30
use Symfony\Component\DependencyInjection\Definition;
31
use Symfony\Component\DependencyInjection\Reference;
32
33
/**
34
 * Mink extension for Behat class.
35
 *
36
 * @author Konstantin Kudryashov <[email protected]>
37
 * @author Christophe Coevoet <[email protected]>
38
 */
39
class MinkExtension implements ExtensionInterface
40
{
41
    const MINK_ID = 'mink';
42
    const SELECTORS_HANDLER_ID = 'mink.selectors_handler';
43
44
    const SELECTOR_TAG = 'mink.selector';
45
46
    /**
47
     * @var DriverFactory[]
48
     */
49
    private $driverFactories = array();
50
51
    public function __construct()
52
    {
53
        $this->registerDriverFactory(new GoutteFactory());
54
        $this->registerDriverFactory(new SahiFactory());
55
        $this->registerDriverFactory(new SeleniumFactory());
56
        $this->registerDriverFactory(new Selenium2Factory());
57
        $this->registerDriverFactory(new SauceLabsFactory());
58
        $this->registerDriverFactory(new BrowserStackFactory());
59
        $this->registerDriverFactory(new ZombieFactory());
60
        $this->registerDriverFactory(new AppiumFactory());
61
    }
62
63
    public function registerDriverFactory(DriverFactory $driverFactory)
64
    {
65
        $this->driverFactories[$driverFactory->getDriverName()] = $driverFactory;
66
    }
67
68
    /**
69
     * {@inheritDoc}
70
     */
71
    public function load(ContainerBuilder $container, array $config)
72
    {
73
        if (isset($config['mink_loader'])) {
74
            $basePath = $container->getParameter('paths.base');
75
76
            if (file_exists($basePath.DIRECTORY_SEPARATOR.$config['mink_loader'])) {
77
                require($basePath.DIRECTORY_SEPARATOR.$config['mink_loader']);
78
            } else {
79
                require($config['mink_loader']);
80
            }
81
        }
82
83
        $this->loadMink($container);
84
        $this->loadContextInitializer($container);
85
        $this->loadSelectorsHandler($container);
86
        $this->loadSessions($container, $config);
87
        $this->loadSessionsListener($container);
88
89
        if ($config['show_auto']) {
90
            $this->loadFailureShowListener($container);
91
        }
92
93
        unset($config['sessions']);
94
95
        $container->setParameter('mink.parameters', $config);
96
        $container->setParameter('mink.base_url', $config['base_url']);
97
        $container->setParameter('mink.browser_name', $config['browser_name']);
98
    }
99
100
    /**
101
     * {@inheritDoc}
102
     */
103
    public function configure(ArrayNodeDefinition $builder)
104
    {
105
        // Rewrite keys to define a shortcut way without allowing conflicts with real keys
106
        $renamedKeys = array_diff(
107
            array_keys($this->driverFactories),
108
            array('mink_loader', 'base_url', 'files_path', 'show_auto', 'show_cmd', 'show_tmp_dir', 'default_session', 'javascript_session', 'browser_name', 'sessions')
109
        );
110
111
        $builder
112
            ->beforeNormalization()
113
                ->always()
114
                ->then(function ($v) use ($renamedKeys) {
115
                    foreach ($renamedKeys as $driverType) {
116
                        if (!array_key_exists($driverType, $v) || isset($v['sessions'][$driverType])) {
117
                            continue;
118
                        }
119
120
                        $v['sessions'][$driverType][$driverType] = $v[$driverType];
121
                        unset($v[$driverType]);
122
                    }
123
124
                    return $v;
125
                })
126
            ->end()
127
            ->addDefaultsIfNotSet()
128
            ->children()
129
                ->scalarNode('mink_loader')->defaultNull()->end()
130
                ->scalarNode('base_url')->defaultNull()->end()
131
                ->scalarNode('files_path')->defaultNull()->end()
132
                ->booleanNode('show_auto')->defaultFalse()->end()
133
                ->scalarNode('show_cmd')->defaultNull()->end()
134
                ->scalarNode('show_tmp_dir')->defaultValue(sys_get_temp_dir())->end()
135
                ->scalarNode('default_session')->defaultNull()->info('Defaults to the first non-javascript session if any, or the first session otherwise')->end()
136
                ->scalarNode('javascript_session')->defaultNull()->info('Defaults to the first javascript session if any')->end()
137
                ->scalarNode('browser_name')->defaultValue('firefox')->end()
138
            ->end()
139
        ->end();
140
141
        /** @var ArrayNodeDefinition $sessionsBuilder */
142
        $sessionsBuilder = $builder
143
            ->children()
144
                ->arrayNode('sessions')
145
                    ->isRequired()
146
                    ->requiresAtLeastOneElement()
147
                    ->useAttributeAsKey('name')
148
                    ->prototype('array')
149
        ;
150
151
        foreach ($this->driverFactories as $factory) {
152
            $factoryNode = $sessionsBuilder->children()->arrayNode($factory->getDriverName())->canBeUnset();
153
154
            $factory->configure($factoryNode);
155
        }
156
157
        $sessionsBuilder
158
            ->validate()
159
                ->ifTrue(function ($v) {return count($v) > 1;})
160
                ->thenInvalid('You cannot set multiple driver types for the same session')
161
            ->end()
162
            ->validate()
163
                ->ifTrue(function ($v) {return count($v) === 0;})
164
                ->thenInvalid('You must set a driver definition for the session.')
165
            ->end()
166
        ;
167
    }
168
169
    /**
170
     * {@inheritDoc}
171
     */
172
    public function getConfigKey()
173
    {
174
        return 'mink';
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180
    public function initialize(ExtensionManager $extensionManager)
181
    {
182
    }
183
184
    /**
185
     * {@inheritDoc}
186
     */
187
    public function process(ContainerBuilder $container)
188
    {
189
        $this->processSelectors($container);
190
    }
191
192
    private function loadMink(ContainerBuilder $container)
193
    {
194
        $container->setDefinition(self::MINK_ID, new Definition('Behat\Mink\Mink'));
195
    }
196
197 View Code Duplication
    private function loadContextInitializer(ContainerBuilder $container)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
198
    {
199
        $definition = new Definition('Behat\MinkExtension\Context\Initializer\MinkAwareInitializer', array(
200
            new Reference(self::MINK_ID),
201
            '%mink.parameters%',
202
        ));
203
        $definition->addTag(ContextExtension::INITIALIZER_TAG, array('priority' => 0));
204
        $container->setDefinition('mink.context_initializer', $definition);
205
    }
206
207
    private function loadSelectorsHandler(ContainerBuilder $container)
208
    {
209
        $container->setDefinition(self::SELECTORS_HANDLER_ID, new Definition('Behat\Mink\Selector\SelectorsHandler'));
210
211
        $cssSelectorDefinition = new Definition('Behat\Mink\Selector\CssSelector');
212
        $cssSelectorDefinition->addTag(self::SELECTOR_TAG, array('alias' => 'css'));
213
        $container->setDefinition(self::SELECTOR_TAG . '.css', $cssSelectorDefinition);
214
215
        $namedSelectorDefinition = new Definition('Behat\Mink\Selector\NamedSelector');
216
        $namedSelectorDefinition->addTag(self::SELECTOR_TAG, array('alias' => 'named'));
217
        $container->setDefinition(self::SELECTOR_TAG . '.named', $namedSelectorDefinition);
218
    }
219
220
    private function loadSessions(ContainerBuilder $container, array $config)
221
    {
222
        $defaultSession = $config['default_session'];
223
        $javascriptSession = $config['javascript_session'];
224
        $javascriptSessions = $nonJavascriptSessions = array();
225
226
        $minkDefinition = $container->getDefinition(self::MINK_ID);
227
228
        foreach ($config['sessions'] as $name => $session) {
229
            $driver = key($session);
230
            $factory = $this->driverFactories[$driver];
231
232
            $definition = new Definition('Behat\Mink\Session', array(
233
                $factory->buildDriver($session[$driver]),
234
                new Reference(self::SELECTORS_HANDLER_ID),
235
            ));
236
            $minkDefinition->addMethodCall('registerSession', array($name, $definition));
237
238
            if ($factory->supportsJavascript()) {
239
                $javascriptSessions[] = $name;
240
            } else {
241
                $nonJavascriptSessions[] = $name;
242
            }
243
        }
244
245
        if (null === $javascriptSession && !empty($javascriptSessions)) {
246
            $javascriptSession = $javascriptSessions[0];
247
        } elseif (null !== $javascriptSession && !in_array($javascriptSession, $javascriptSessions)) {
248
            throw new InvalidConfigurationException(sprintf(
249
                'The javascript session must be one of the enabled javascript sessions (%s), but got %s',
250
                json_encode($javascriptSessions),
251
                $javascriptSession
252
            ));
253
        }
254
255
        if (null === $defaultSession) {
256
            $defaultSession = !empty($nonJavascriptSessions) ? $nonJavascriptSessions[0] : $javascriptSessions[0];
257
        } elseif (!isset($config['sessions'][$defaultSession])) {
258
            throw new InvalidConfigurationException(sprintf('The default session must be one of the enabled sessions, but got %s', $defaultSession));
259
        }
260
261
        $container->setParameter('mink.default_session', $defaultSession);
262
        $container->setParameter('mink.javascript_session', $javascriptSession);
263
        $container->setParameter('mink.available_javascript_sessions', $javascriptSessions);
264
    }
265
266 View Code Duplication
    private function loadSessionsListener(ContainerBuilder $container)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267
    {
268
        $definition = new Definition('Behat\MinkExtension\Listener\SessionsListener', array(
269
            new Reference(self::MINK_ID),
270
            '%mink.default_session%',
271
            '%mink.javascript_session%',
272
            '%mink.available_javascript_sessions%',
273
        ));
274
        $definition->addTag(EventDispatcherExtension::SUBSCRIBER_TAG, array('priority' => 0));
275
        $container->setDefinition('mink.listener.sessions', $definition);
276
    }
277
278 View Code Duplication
    private function loadFailureShowListener(ContainerBuilder $container)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
279
    {
280
        $definition = new Definition('Behat\MinkExtension\Listener\FailureShowListener', array(
281
            new Reference(self::MINK_ID),
282
            '%mink.parameters%',
283
        ));
284
        $definition->addTag(EventDispatcherExtension::SUBSCRIBER_TAG, array('priority' => 0));
285
        $container->setDefinition('mink.listener.failure_show', $definition);
286
    }
287
288
    private function processSelectors(ContainerBuilder $container)
289
    {
290
        $handlerDefinition = $container->getDefinition(self::SELECTORS_HANDLER_ID);
291
292
        foreach ($container->findTaggedServiceIds(self::SELECTOR_TAG) as $id => $tags) {
293
            foreach ($tags as $tag) {
294
                if (!isset($tag['alias'])) {
295
                    throw new ProcessingException(sprintf(
296
                        'All `%s` tags should have an `alias` attribute, but `%s` service has none.',
297
                        $tag,
298
                        $id
299
                    ));
300
                }
301
                $handlerDefinition->addMethodCall(
302
                    'registerSelector', array($tag['alias'], new Reference($id))
303
                );
304
            }
305
        }
306
    }
307
}
308