Completed
Pull Request — master (#293)
by Lukas Kahwe
04:41
created

WebTestCase::configureVerbosityForSymfony203()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 5.9256

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 6
cts 9
cp 0.6667
rs 8.6737
c 0
b 0
f 0
cc 5
eloc 15
nc 5
nop 1
crap 5.9256
1
<?php
2
3
/*
4
 * This file is part of the Liip/FunctionalTestBundle
5
 *
6
 * (c) Lukas Kahwe Smith <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Liip\FunctionalTestBundle\Test;
13
14
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase;
15
use Symfony\Bundle\FrameworkBundle\Console\Application;
16
use Symfony\Bundle\FrameworkBundle\Client;
17
use Symfony\Component\Console\Input\ArrayInput;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use Symfony\Component\Console\Output\StreamOutput;
20
use Symfony\Component\DomCrawler\Crawler;
21
use Symfony\Component\BrowserKit\Cookie;
22
use Symfony\Component\HttpKernel\Kernel;
23
use Symfony\Component\HttpFoundation\Response;
24
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
25
use Symfony\Component\Security\Core\User\UserInterface;
26
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
27
use Symfony\Component\DependencyInjection\ContainerInterface;
28
use Symfony\Component\HttpFoundation\Session\Session;
29
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
30
use Symfony\Bridge\Doctrine\ManagerRegistry;
31
use Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader;
32
use Doctrine\Common\Persistence\ObjectManager;
33
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
34
use Doctrine\Common\DataFixtures\Executor\AbstractExecutor;
35
use Doctrine\Common\DataFixtures\ProxyReferenceRepository;
36
use Doctrine\DBAL\Driver\PDOSqlite\Driver as SqliteDriver;
37
use Doctrine\DBAL\Platforms\MySqlPlatform;
38
use Doctrine\ORM\EntityManager;
39
use Doctrine\ORM\Tools\SchemaTool;
40
use Nelmio\Alice\Fixtures;
41
use Liip\FunctionalTestBundle\Utils\HttpAssertions;
42
43
/**
44
 * @author Lea Haensenberger
45
 * @author Lukas Kahwe Smith <[email protected]>
46
 * @author Benjamin Eberlei <[email protected]>
47
 */
48
abstract class WebTestCase extends BaseWebTestCase
49
{
50
    protected $environment = 'test';
51
    protected $containers;
52
    protected $kernelDir;
53
    // 5 * 1024 * 1024 KB
54
    protected $maxMemory = 5242880;
55
56
    // RUN COMMAND
57
    protected $verbosityLevel;
58
    protected $decorated;
59
60
    /**
61
     * @var array
62
     */
63
    private $firewallLogins = array();
64
65
    /**
66
     * @var array
67
     */
68
    private static $cachedMetadatas = array();
69
70
    protected static function getKernelClass()
0 ignored issues
show
Coding Style introduced by
getKernelClass uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
71
    {
72
        $dir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : static::getPhpUnitXmlDir();
73
74
        list($appname) = explode('\\', get_called_class());
75
76
        $class = $appname.'Kernel';
77
        $file = $dir.'/'.strtolower($appname).'/'.$class.'.php';
78
        if (!file_exists($file)) {
79
            return parent::getKernelClass();
80
        }
81
        require_once $file;
82
83
        return $class;
84
    }
85
86
    /**
87
     * Creates a mock object of a service identified by its id.
88
     *
89
     * @param string $id
90
     *
91
     * @return \PHPUnit_Framework_MockObject_MockBuilder
92 2
     */
93
    protected function getServiceMockBuilder($id)
94
    {
95 2
        $service = $this->getContainer()->get($id);
96
        $class = get_class($service);
97
98
        return $this->getMockBuilder($class)->disableOriginalConstructor();
99
    }
100
101
    /**
102
     * Builds up the environment to run the given command.
103
     *
104
     * @param string $name
105
     * @param array  $params
106
     * @param bool   $reuseKernel
107
     *
108
     * @return string
109 12
     */
110
    protected function runCommand($name, array $params = array(), $reuseKernel = false)
111 12
    {
112
        array_unshift($params, $name);
113 12
114 12
        if (!$reuseKernel) {
115 9
            if (null !== static::$kernel) {
116 9
                static::$kernel->shutdown();
117
            }
118 12
119 12
            $kernel = static::$kernel = $this->createKernel(array('environment' => $this->environment));
120 12
            $kernel->boot();
121 2
        } else {
122
            $kernel = $this->getContainer()->get('kernel');
123
        }
124 12
125 12
        $application = new Application($kernel);
126
        $application->setAutoExit(false);
127 12
128 12
        // @codeCoverageIgnoreStart
129
        if ('203' === substr(Kernel::VERSION_ID, 0, 3)) {
130 12
            $params = $this->configureVerbosityForSymfony203($params);
131 12
        }
132
        // @codeCoverageIgnoreEnd
133 11
134
        $input = new ArrayInput($params);
135 11
        $input->setInteractive(false);
136
137 11
        $fp = fopen('php://temp/maxmemory:'.$this->maxMemory, 'r+');
138
        $output = new StreamOutput($fp, $this->getVerbosityLevel(), $this->getDecorated());
139
140
        $application->run($input, $output);
141
142
        rewind($fp);
143
144
        return stream_get_contents($fp);
145
    }
146
147
    /**
148
     * Retrieves the output verbosity level.
149 12
     *
150
     * @see Symfony\Component\Console\Output\OutputInterface for available levels
151
     *
152 12
     * @return int
153
     *
154 6
     * @throws \OutOfBoundsException If the set value isn't accepted
155 6
     */
156
    protected function getVerbosityLevel()
157 6
    {
158 6
        // If `null`, is not yet set
159
        if (null === $this->verbosityLevel) {
160
            // Set the global verbosity level that is set as NORMAL by the TreeBuilder in Configuration
161 12
            $level = strtoupper($this->getContainer()->getParameter('liip_functional_test.command_verbosity'));
162 6
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
163 6
164
            $this->verbosityLevel = constant($verbosity);
165 6
        }
166 1
167 1
        // If string, it is set by the developer, so check that the value is an accepted one
168 1
        if (is_string($this->verbosityLevel)) {
169
            $level = strtoupper($this->verbosityLevel);
170
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
171 5
172 5
            if (!defined($verbosity)) {
173
                throw new \OutOfBoundsException(
174 11
                    sprintf('The set value "%s" for verbosityLevel is not valid. Accepted are: "quiet", "normal", "verbose", "very_verbose" and "debug".', $level)
175
                    );
176
            }
177 6
178
            $this->verbosityLevel = constant($verbosity);
179 6
        }
180 6
181
        return $this->verbosityLevel;
182
    }
183
184
    /**
185
     * In Symfony 2.3.* the verbosity level has to be set through {Symfony\Component\Console\Input\ArrayInput} and not
186
     * in {Symfony\Component\Console\Output\OutputInterface}.
187 11
     *
188
     * This method builds $params to be passed to {Symfony\Component\Console\Input\ArrayInput}.
189 11
     *
190
     * @codeCoverageIgnore
191 5
     *
192 5
     * @param array $params
193
     *
194
     * @return array
195 11
     */
196
    private function configureVerbosityForSymfony203(array $params)
197
    {
198
        switch ($this->getVerbosityLevel()) {
199
            case OutputInterface::VERBOSITY_QUIET:
200
                $params['-q'] = '-q';
201 11
                break;
202
203
            case OutputInterface::VERBOSITY_VERBOSE:
204 6
                $params['-v'] = '';
205
                break;
206 6
207 6
            case OutputInterface::VERBOSITY_VERY_VERBOSE:
208
                $params['-vv'] = '';
209
                break;
210
211
            case OutputInterface::VERBOSITY_DEBUG:
212
                $params['-vvv'] = '';
213
                break;
214
        }
215 45
216
        return $params;
217 45
    }
218
219
    public function setVerbosityLevel($level)
220
    {
221
        $this->verbosityLevel = $level;
222 45
    }
223 45
224
    /**
225 44
     * Retrieves the flag indicating if the output should be decorated or not.
226 44
     *
227 44
     * @return bool
228 44
     */
229
    protected function getDecorated()
230 44
    {
231 44
        if (null === $this->decorated) {
232
            // Set the global decoration flag that is set to `true` by the TreeBuilder in Configuration
233 45
            $this->decorated = $this->getContainer()->getParameter('liip_functional_test.command_decoration');
234
        }
235
236
        // Check the local decorated flag
237 45
        if (false === is_bool($this->decorated)) {
238
            throw new \OutOfBoundsException(
239
                sprintf('`WebTestCase::decorated` has to be `bool`. "%s" given.', gettype($this->decorated))
240
            );
241
        }
242
243
        return $this->decorated;
244
    }
245
246
    public function isDecorated($decorated)
247
    {
248
        $this->decorated = $decorated;
249
    }
250 3
251
    /**
252 3
     * Get an instance of the dependency injection container.
253
     * (this creates a kernel *without* parameters).
254 3
     *
255 3
     * @return ContainerInterface
256
     */
257 3
    protected function getContainer()
0 ignored issues
show
Coding Style introduced by
getContainer uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
258 3
    {
259 3
        if (!empty($this->kernelDir)) {
260 3
            $tmpKernelDir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : null;
261
            $_SERVER['KERNEL_DIR'] = getcwd().$this->kernelDir;
262 3
        }
263
264
        $cacheKey = $this->kernelDir.'|'.$this->environment;
265
        if (empty($this->containers[$cacheKey])) {
266
            $options = array(
267
                'environment' => $this->environment,
268
            );
269
            $kernel = $this->createKernel($options);
270
            $kernel->boot();
271
272
            $this->containers[$cacheKey] = $kernel->getContainer();
273
        }
274
275 7
        if (isset($tmpKernelDir)) {
276
            $_SERVER['KERNEL_DIR'] = $tmpKernelDir;
277 7
        }
278 7
279
        return $this->containers[$cacheKey];
280
    }
281 7
282
    /**
283
     * This function finds the time when the data blocks of a class definition
284 7
     * file were being written to, that is, the time when the content of the
285 3
     * file was changed.
286 3
     *
287 1
     * @param string $class The fully qualified class name of the fixture class to
288
     *                      check modification date on
289 7
     *
290
     * @return \DateTime|null
291 7
     */
292
    protected function getFixtureLastModified($class)
293
    {
294
        $lastModifiedDateTime = null;
295
296
        $reflClass = new \ReflectionClass($class);
297
        $classFileName = $reflClass->getFileName();
298
299
        if (file_exists($classFileName)) {
300
            $lastModifiedDateTime = new \DateTime();
301
            $lastModifiedDateTime->setTimestamp(filemtime($classFileName));
302
        }
303
304
        return $lastModifiedDateTime;
305
    }
306
307
    /**
308
     * Determine if the Fixtures that define a database backup have been
309
     * modified since the backup was made.
310
     *
311
     * @param array  $classNames The fixture classnames to check
312
     * @param string $backup     The fixture backup SQLite database file path
313
     *
314
     * @return bool TRUE if the backup was made since the modifications to the
315
     *              fixtures; FALSE otherwise
316
     */
317 32
    protected function isBackupUpToDate(array $classNames, $backup)
318
    {
319 32
        $backupLastModifiedDateTime = new \DateTime();
320
        $backupLastModifiedDateTime->setTimestamp(filemtime($backup));
321 32
322
        /** @var \Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader $loader */
323 32
        $loader = $this->getFixtureLoader($this->getContainer(), $classNames);
324 32
325
        // Use loader in order to fetch all the dependencies fixtures.
326 32
        foreach ($loader->getFixtures() as $className) {
327 32
            $fixtureLastModifiedDateTime = $this->getFixtureLastModified($className);
328 32
            if ($backupLastModifiedDateTime < $fixtureLastModifiedDateTime) {
329 32
                return false;
330 32
            }
331
        }
332 32
333 32
        return true;
334 32
    }
335
336 32
    /**
337 31
     * Set the database to the provided fixtures.
338 31
     *
339 27
     * Drops the current database and then loads fixtures using the specified
340 27
     * classes. The parameter is a list of fully qualified class names of
341
     * classes that implement Doctrine\Common\DataFixtures\FixtureInterface
342
     * so that they can be loaded by the DataFixtures Loader::addFixture
343
     *
344 27
     * When using SQLite this method will automatically make a copy of the
345 27
     * loaded schema and fixtures which will be restored automatically in
346
     * case the same fixture classes are to be loaded again. Caveat: changes
347
     * to references and/or identities may go undetected.
348
     *
349 27
     * Depends on the doctrine data-fixtures library being available in the
350 10
     * class path.
351 10
     *
352
     * @param array  $classNames   List of fully qualified class names of fixtures to load
353 10
     * @param string $omName       The name of object manager to use
354 10
     * @param string $registryName The service id of manager registry to use
355 27
     * @param int    $purgeMode    Sets the ORM purge mode
356
     *
357 27
     * @return null|AbstractExecutor
358 9
     */
359 9
    protected function loadFixtures(array $classNames, $omName = null, $registryName = 'doctrine', $purgeMode = null)
360 7
    {
361 7
        $container = $this->getContainer();
362
        /** @var ManagerRegistry $registry */
363 7
        $registry = $container->get($registryName);
364
        /** @var ObjectManager $om */
365 7
        $om = $registry->getManager($omName);
366
        $type = $registry->getName();
367 7
368 7
        $executorClass = 'PHPCR' === $type && class_exists('Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor')
369 7
            ? 'Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor'
370
            : 'Doctrine\\Common\\DataFixtures\\Executor\\'.$type.'Executor';
371 7
        $referenceRepository = new ProxyReferenceRepository($om);
372
        $cacheDriver = $om->getMetadataFactory()->getCacheDriver();
373 7
374
        if ($cacheDriver) {
375 3
            $cacheDriver->deleteAll();
376
        }
377
378 21
        if ('ORM' === $type) {
379 21
            $connection = $om->getConnection();
380 21
            if ($connection->getDriver() instanceof SqliteDriver) {
381 21
                $params = $connection->getParams();
382 21
                if (isset($params['master'])) {
383 21
                    $params = $params['master'];
384
                }
385 21
386 21
                $name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false);
387 21
                if (!$name) {
388 25
                    throw new \InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped.");
389
                }
390 26
391 5
                if (!isset(self::$cachedMetadatas[$omName])) {
392 5
                    self::$cachedMetadatas[$omName] = $om->getMetadataFactory()->getAllMetadata();
393 1
                    usort(self::$cachedMetadatas[$omName], function ($a, $b) {
394 1
                        return strcmp($a->name, $b->name);
395 1
                    });
396 1
                }
397
                $metadatas = self::$cachedMetadatas[$omName];
398 1
399 1
                if ($container->getParameter('liip_functional_test.cache_sqlite_db')) {
400 4
                    $backup = $container->getParameter('kernel.cache_dir').'/test_'.md5(serialize($metadatas).serialize($classNames)).'.db';
401 4
                    if (file_exists($backup) && file_exists($backup.'.ser') && $this->isBackupUpToDate($classNames, $backup)) {
402 1
                        $om->flush();
403 1
                        $om->clear();
404
405 4
                        $this->preFixtureRestore($om, $referenceRepository);
406
407
                        copy($backup, $name);
408 5
409 5
                        $executor = new $executorClass($om);
410 5
                        $executor->setReferenceRepository($referenceRepository);
411
                        $executor->getReferenceRepository()->load($backup);
412 26
413
                        $this->postFixtureRestore();
414 26
415
                        return $executor;
416 26
                    }
417 3
                }
418
419 3
                // TODO: handle case when using persistent connections. Fail loudly?
420 3
                $schemaTool = new SchemaTool($om);
0 ignored issues
show
Compatibility introduced by
$om of type object<Doctrine\Common\Persistence\ObjectManager> is not a sub-type of object<Doctrine\ORM\EntityManagerInterface>. It seems like you assume a child interface of the interface Doctrine\Common\Persistence\ObjectManager to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
421
                $schemaTool->dropDatabase();
422 3
                if (!empty($metadatas)) {
423 3
                    $schemaTool->createSchema($metadatas);
424
                }
425 26
                $this->postFixtureSetup();
426
427
                $executor = new $executorClass($om);
428
                $executor->setReferenceRepository($referenceRepository);
429
            }
430
        }
431
432
        if (empty($executor)) {
433
            $purgerClass = 'Doctrine\\Common\\DataFixtures\\Purger\\'.$type.'Purger';
434
            if ('PHPCR' === $type) {
435 6
                $purger = new $purgerClass($om);
436
                $initManager = $container->has('doctrine_phpcr.initializer_manager')
437 6
                    ? $container->get('doctrine_phpcr.initializer_manager')
438
                    : null;
439 6
440 6
                $executor = new $executorClass($om, $purger, $initManager);
441
            } else {
442 6
                $purger = new $purgerClass();
443 1
                if (null !== $purgeMode) {
444 1
                    $purger->setPurgeMode($purgeMode);
445
                }
446 6
447
                $executor = new $executorClass($om, $purger);
448 6
            }
449 1
450 1
            $executor->setReferenceRepository($referenceRepository);
451 6
            $executor->purge();
452
        }
453
454
        $loader = $this->getFixtureLoader($container, $classNames);
455
456
        $executor->execute($loader->getFixtures(), true);
457
458
        if (isset($name) && isset($backup)) {
459
            $this->preReferenceSave($om, $executor, $backup);
460 6
461
            $executor->getReferenceRepository()->save($backup);
462 6
            copy($name, $backup);
463
464 6
            $this->postReferenceSave($om, $executor, $backup);
465
        }
466 6
467 6
        return $executor;
468 1
    }
469 1
470
    /**
471
     * Clean database.
472 5
     *
473 6
     * @param ManagerRegistry $registry
474
     * @param EntityManager   $om
475 6
     * @param null            $omName
476
     */
477
    private function cleanDatabase(ManagerRegistry $registry, EntityManager $om, $omName = null)
478
    {
479
        $connection = $om->getConnection();
480
481
        $mysql = ($registry->getName() === 'ORM'
482
            && $connection->getDatabasePlatform() instanceof MySqlPlatform);
483
484
        if ($mysql) {
485
            $connection->query('SET FOREIGN_KEY_CHECKS=0');
486
        }
487
488 6
        $this->loadFixtures(array(), $omName);
489
490 6
        if ($mysql) {
491
            $connection->query('SET FOREIGN_KEY_CHECKS=1');
492
        }
493
    }
494
495
    /**
496
     * Locate fixture files.
497
     *
498 6
     * @param array $paths
499
     *
500
     * @return array $files
501 6
     */
502
    private function locateResources($paths)
503
    {
504 6
        $files = array();
505
506 6
        $kernel = $this->getContainer()->get('kernel');
507 6
508 6
        foreach ($paths as $path) {
509
            if ($path[0] !== '@' && file_exists($path) === true) {
510 6
                $files[] = $path;
511
                continue;
512
            }
513 6
514 6
            $files[] = $kernel->locateResource($path);
515 3
        }
516 3
517 3
        return $files;
518 3
    }
519
520 3
    /**
521
     * @param array  $paths        Either symfony resource locators (@ BundleName/etc) or actual file paths
522
     * @param bool   $append
523 3
     * @param null   $omName
524
     * @param string $registryName
525
     *
526
     * @return array
527
     *
528
     * @throws \BadMethodCallException
529
     */
530 21
    public function loadFixtureFiles(array $paths = array(), $append = false, $omName = null, $registryName = 'doctrine')
531
    {
532 21
        if (!class_exists('Nelmio\Alice\Fixtures')) {
533
            // This class is available during tests, no exception will be thrown.
534
            // @codeCoverageIgnoreStart
535
            throw new \BadMethodCallException('nelmio/alice should be installed to use this method.');
536
            // @codeCoverageIgnoreEnd
537
        }
538
539 7
        /** @var ContainerInterface $container */
540
        $container = $this->getContainer();
541 7
542
        /** @var ManagerRegistry $registry */
543
        $registry = $container->get($registryName);
544
545
        /** @var EntityManager $om */
546
        $om = $registry->getManager($omName);
547
548
        if ($append === false) {
549
            $this->cleanDatabase($registry, $om, $omName);
550
        }
551 7
552
        $files = $this->locateResources($paths);
553 7
554
        // Check if the Hautelook AliceBundle is registered and if yes, use it instead of Nelmio Alice
555
        $hautelookLoaderServiceName = 'hautelook_alice.fixtures.loader';
556
        if ($container->has($hautelookLoaderServiceName)) {
557
            $loaderService = $container->get($hautelookLoaderServiceName);
558
            $persisterClass = class_exists('Nelmio\Alice\ORM\Doctrine') ?
559
                'Nelmio\Alice\ORM\Doctrine' :
560
                'Nelmio\Alice\Persister\Doctrine';
561
562
            return $loaderService->load(new $persisterClass($om), $files);
563
        }
564 3
565
        return Fixtures::load($files, $om);
566 3
    }
567
568
    /**
569
     * Callback function to be executed after Schema creation.
570
     * Use this to execute acl:init or other things necessary.
571
     */
572
    protected function postFixtureSetup()
573
    {
574
    }
575
576
    /**
577 3
     * Callback function to be executed after Schema restore.
578
     *
579 3
     * @return WebTestCase
580
     */
581
    protected function postFixtureRestore()
582
    {
583
    }
584
585
    /**
586
     * Callback function to be executed before Schema restore.
587
     *
588
     * @param ObjectManager            $manager             The object manager
589 32
     * @param ProxyReferenceRepository $referenceRepository The reference repository
590
     *
591 32
     * @return WebTestCase
592 32
     */
593 32
    protected function preFixtureRestore(ObjectManager $manager, ProxyReferenceRepository $referenceRepository)
0 ignored issues
show
Unused Code introduced by
The parameter $manager is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $referenceRepository is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
594
    {
595
    }
596
597
    /**
598 32
     * Callback function to be executed after save of references.
599
     *
600 32
     * @param ObjectManager    $manager        The object manager
601
     * @param AbstractExecutor $executor       Executor of the data fixtures
602 32
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
603 10
     *
604 32
     * @return WebTestCase
605
     */
606 32
    protected function postReferenceSave(ObjectManager $manager, AbstractExecutor $executor, $backupFilePath)
0 ignored issues
show
Unused Code introduced by
The parameter $manager is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $executor is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $backupFilePath is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
607
    {
608
    }
609
610
    /**
611
     * Callback function to be executed before save of references.
612
     *
613
     * @param ObjectManager    $manager        The object manager
614
     * @param AbstractExecutor $executor       Executor of the data fixtures
615 10
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
616
     *
617 10
     * @return WebTestCase
618
     */
619 10
    protected function preReferenceSave(ObjectManager $manager, AbstractExecutor $executor, $backupFilePath)
0 ignored issues
show
Unused Code introduced by
The parameter $manager is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $executor is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $backupFilePath is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
620 2
    {
621
    }
622 2
623
    /**
624
     * Retrieve Doctrine DataFixtures loader.
625 10
     *
626
     * @param ContainerInterface $container
627 10
     * @param array              $classNames
628 2
     *
629 2
     * @return Loader
630 2
     */
631 2
    protected function getFixtureLoader(ContainerInterface $container, array $classNames)
632 10
    {
633
        $loaderClass = class_exists('Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader')
634
            ? 'Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader'
635
            : (class_exists('Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader')
636
                // This class is not available during tests.
637
                // @codeCoverageIgnoreStart
638
                ? 'Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader'
639
                // @codeCoverageIgnoreEnd
640
                : 'Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader');
641
642
        $loader = new $loaderClass($container);
643
644
        foreach ($classNames as $className) {
645
            $this->loadFixtureClass($loader, $className);
646
        }
647
648
        return $loader;
649 49
    }
650
651 49
    /**
652 2
     * Load a data fixture class.
653
     *
654 1
     * @param Loader $loader
655 1
     * @param string $className
656 1
     */
657 1
    protected function loadFixtureClass($loader, $className)
658 1
    {
659 1
        $fixture = new $className();
660
661 2
        if ($loader->hasFixture($fixture)) {
662 2
            unset($fixture);
663 2
664 2
            return;
665 2
        }
666
667 49
        $loader->addFixture($fixture);
668
669 49
        if ($fixture instanceof DependentFixtureInterface) {
670
            foreach ($fixture->getDependencies() as $dependency) {
671 2
                $this->loadFixtureClass($loader, $dependency);
672
            }
673 2
        }
674
    }
675
676
    /**
677 2
     * Creates an instance of a lightweight Http client.
678
     *
679 2
     * If $authentication is set to 'true' it will use the content of
680 2
     * 'liip_functional_test.authentication' to log in.
681 2
     *
682
     * $params can be used to pass headers to the client, note that they have
683 2
     * to follow the naming format used in $_SERVER.
684
     * Example: 'HTTP_X_REQUESTED_WITH' instead of 'X-Requested-With'
685
     *
686 2
     * @param bool|array $authentication
687 2
     * @param array      $params
688
     *
689
     * @return Client
690
     */
691 2
    protected function makeClient($authentication = false, array $params = array())
692 2
    {
693 2
        if ($authentication) {
694
            if ($authentication === true) {
695
                $authentication = array(
696
                    'username' => $this->getContainer()
697
                        ->getParameter('liip_functional_test.authentication.username'),
698
                    'password' => $this->getContainer()
699
                        ->getParameter('liip_functional_test.authentication.password'),
700 2
                );
701 2
            }
702 2
703
            $params = array_merge($params, array(
704 2
                'PHP_AUTH_USER' => $authentication['username'],
705 2
                'PHP_AUTH_PW' => $authentication['password'],
706
            ));
707 49
        }
708
709
        $client = static::createClient(array('environment' => $this->environment), $params);
710
711
        if ($this->firewallLogins) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->firewallLogins of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
712
            // has to be set otherwise "hasPreviousSession" in Request returns false.
713
            $options = $client->getContainer()->getParameter('session.storage.options');
714
715
            if (!$options || !isset($options['name'])) {
716
                throw new \InvalidArgumentException('Missing session.storage.options#name');
717
            }
718
719
            $session = $client->getContainer()->get('session');
720
            // Since the namespace of the session changed in symfony 2.1, instanceof can be used to check the version.
721
            if ($session instanceof Session) {
722
                $session->setId(uniqid());
723 2
            }
724
725 2
            $client->getCookieJar()->set(new Cookie($options['name'], $session->getId()));
726 2
727 2
            /** @var $user UserInterface */
728 2
            foreach ($this->firewallLogins as $firewallName => $user) {
729 2
                $token = $this->createUserToken($user, $firewallName);
730 2
731
                // BC: security.token_storage is available on Symfony 2.6+
732
                // see http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements
733
                if ($client->getContainer()->has('security.token_storage')) {
734
                    $tokenStorage = $client->getContainer()->get('security.token_storage');
735
                } else {
736
                    // This block will never be reached with Symfony 2.6+
737
                    // @codeCoverageIgnoreStart
738
                    $tokenStorage = $client->getContainer()->get('security.context');
739
                    // @codeCoverageIgnoreEnd
740
                }
741
742 1
                $tokenStorage->setToken($token);
743
                $session->set('_security_'.$firewallName, serialize($token));
744 1
            }
745
746
            $session->save();
747
        }
748
749
        return $client;
750
    }
751
752
    /**
753
     * Create User Token.
754 6
     *
755
     * Factory method for creating a User Token object for the firewall based on
756 6
     * the user object provided. By default it will be a Username/Password
757 5
     * Token based on the user's credentials, but may be overridden for custom
758
     * tokens in your applications.
759
     *
760
     * @param UserInterface $user         The user object to base the token off of
761
     * @param string        $firewallName name of the firewall provider to use
762
     *
763
     * @return TokenInterface The token to be used in the security context
764
     */
765
    protected function createUserToken(UserInterface $user, $firewallName)
766
    {
767
        return new UsernamePasswordToken(
768
            $user,
769
            null,
770
            $firewallName,
771 1
            $user->getRoles()
772
        );
773 1
    }
774 1
775
    /**
776 1
     * Extracts the location from the given route.
777 1
     *
778 1
     * @param string $route    The name of the route
779 1
     * @param array  $params   Set of parameters
780
     * @param int    $absolute
781 1
     *
782
     * @return string
783
     */
784
    protected function getUrl($route, $params = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
785
    {
786
        return $this->getContainer()->get('router')->generate($route, $params, $absolute);
787
    }
788
789
    /**
790
     * Checks the success state of a response.
791
     *
792
     * @param Response $response Response object
793
     * @param bool     $success  to define whether the response is expected to be successful
794
     * @param string   $type
795
     */
796 1
    public function isSuccessful(Response $response, $success = true, $type = 'text/html')
797
    {
798 1
        HttpAssertions::isSuccessful($response, $success, $type);
799 1
    }
800
801 1
    /**
802
     * Executes a request on the given url and returns the response contents.
803 1
     *
804
     * This method also asserts the request was successful.
805
     *
806
     * @param string $path           path of the requested page
807
     * @param string $method         The HTTP method to use, defaults to GET
808
     * @param bool   $authentication Whether to use authentication, defaults to false
809
     * @param bool   $success        to define whether the response is expected to be successful
810
     *
811
     * @return string
812 2
     */
813
    public function fetchContent($path, $method = 'GET', $authentication = false, $success = true)
814 2
    {
815
        $client = $this->makeClient($authentication);
816 2
        $client->request($method, $path);
817
818
        $content = $client->getResponse()->getContent();
819
        if (is_bool($success)) {
820
            $this->isSuccessful($client->getResponse(), $success);
0 ignored issues
show
Documentation introduced by
$client->getResponse() is of type object|null, but the function expects a object<Symfony\Component\HttpFoundation\Response>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
821
        }
822
823
        return $content;
824
    }
825
826
    /**
827 11
     * Executes a request on the given url and returns a Crawler object.
828
     *
829 11
     * This method also asserts the request was successful.
830 8
     *
831
     * @param string $path           path of the requested page
832
     * @param string $method         The HTTP method to use, defaults to GET
833
     * @param bool   $authentication Whether to use authentication, defaults to false
834
     * @param bool   $success        Whether the response is expected to be successful
835
     *
836
     * @return Crawler
837
     */
838
    public function fetchCrawler($path, $method = 'GET', $authentication = false, $success = true)
839 2
    {
840
        $client = $this->makeClient($authentication);
841 2
        $crawler = $client->request($method, $path);
842 1
843
        $this->isSuccessful($client->getResponse(), $success);
0 ignored issues
show
Documentation introduced by
$client->getResponse() is of type object|null, but the function expects a object<Symfony\Component\HttpFoundation\Response>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
844
845
        return $crawler;
846
    }
847
848
    /**
849
     * @param UserInterface $user
850
     * @param string        $firewallName
851
     *
852
     * @return WebTestCase
853
     */
854
    public function loginAs(UserInterface $user, $firewallName)
855
    {
856
        $this->firewallLogins[$firewallName] = $user;
857
858
        return $this;
859
    }
860
861
    /**
862
     * Asserts that the HTTP response code of the last request performed by
863
     * $client matches the expected code. If not, raises an error with more
864
     * information.
865
     *
866
     * @param $expectedStatusCode
867
     * @param Client $client
868
     */
869
    public function assertStatusCode($expectedStatusCode, Client $client)
870
    {
871
        HttpAssertions::assertStatusCode($expectedStatusCode, $client);
872
    }
873
874
    /**
875
     * Assert that the last validation errors within $container match the
876
     * expected keys.
877
     *
878
     * @param array              $expected  A flat array of field names
879
     * @param ContainerInterface $container
880
     */
881
    public function assertValidationErrors(array $expected, ContainerInterface $container)
882
    {
883
        HttpAssertions::assertValidationErrors($expected, $container);
884
    }
885
}
886