Passed
Pull Request — master (#330)
by
unknown
24:48
created

Test/WebTestCase.php (10 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\HttpKernel\KernelInterface;
25
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
26
use Symfony\Component\Security\Core\User\UserInterface;
27
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
28
use Symfony\Component\DependencyInjection\ContainerInterface;
29
use Symfony\Component\HttpFoundation\Session\Session;
30
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
31
use Symfony\Bridge\Doctrine\ManagerRegistry;
32
use Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader;
33
use Doctrine\Common\Persistence\ObjectManager;
34
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
35
use Doctrine\Common\DataFixtures\Executor\AbstractExecutor;
36
use Doctrine\Common\DataFixtures\ProxyReferenceRepository;
37
use Doctrine\DBAL\Driver\PDOSqlite\Driver as SqliteDriver;
38
use Doctrine\DBAL\Platforms\MySqlPlatform;
39
use Doctrine\ORM\EntityManager;
40
use Doctrine\ORM\Tools\SchemaTool;
41
use Nelmio\Alice\Fixtures;
42
use Liip\FunctionalTestBundle\Utils\HttpAssertions;
43
44
/**
45
 * @author Lea Haensenberger
46
 * @author Lukas Kahwe Smith <[email protected]>
47
 * @author Benjamin Eberlei <[email protected]>
48
 */
49
abstract class WebTestCase extends BaseWebTestCase
50
{
51
    protected $environment = 'test';
52
    protected $containers;
53
    protected $kernelDir;
54
    // 5 * 1024 * 1024 KB
55
    protected $maxMemory = 5242880;
56
57
    // RUN COMMAND
58
    protected $verbosityLevel;
59
    protected $decorated;
60
61
    /**
62
     * @var array
63
     */
64
    private $firewallLogins = array();
65
66
    /**
67
     * @var array
68
     */
69
    private $excludedDoctrineTables = array();
70
71
    /**
72
     * @var array
73
     */
74
    private static $cachedMetadatas = array();
75
76
    protected static function getKernelClass()
77
    {
78
        $dir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : static::getPhpUnitXmlDir();
79
80
        list($appname) = explode('\\', get_called_class());
81
82
        $class = $appname.'Kernel';
83
        $file = $dir.'/'.strtolower($appname).'/'.$class.'.php';
84
        if (!file_exists($file)) {
85
            return parent::getKernelClass();
86
        }
87
        require_once $file;
88
89
        return $class;
90
    }
91
92
    /**
93
     * Creates a mock object of a service identified by its id.
94
     *
95
     * @param string $id
96
     *
97
     * @return \PHPUnit_Framework_MockObject_MockBuilder
98
     */
99
    protected function getServiceMockBuilder($id)
100
    {
101
        $service = $this->getContainer()->get($id);
102
        $class = get_class($service);
103
104
        return $this->getMockBuilder($class)->disableOriginalConstructor();
105
    }
106
107
    /**
108
     * Builds up the environment to run the given command.
109
     *
110
     * @param string $name
111
     * @param array  $params
112
     * @param bool   $reuseKernel
113
     *
114
     * @return string
115
     */
116 12
    protected function runCommand($name, array $params = array(), $reuseKernel = false)
117
    {
118 12
        array_unshift($params, $name);
119
120 12
        if (!$reuseKernel) {
121 12
            if (null !== static::$kernel) {
122 9
                static::$kernel->shutdown();
123 9
            }
124
125 12
            $kernel = static::$kernel = $this->createKernel(array('environment' => $this->environment));
126 12
            $kernel->boot();
127 12
        } else {
128 2
            $kernel = $this->getContainer()->get('kernel');
129
        }
130
131 12
        $application = $this->createApplication($kernel);
132
133
        // @codeCoverageIgnoreStart
134
        if ('203' === substr(Kernel::VERSION_ID, 0, 3)) {
135
            $params = $this->configureVerbosityForSymfony203($params);
136
        }
137
        // @codeCoverageIgnoreEnd
138
139 12
        $input = new ArrayInput($params);
140 12
        $input->setInteractive(false);
141
142 12
        $fp = fopen('php://temp/maxmemory:'.$this->maxMemory, 'r+');
143 12
        $output = new StreamOutput($fp, $this->getVerbosityLevel(), $this->getDecorated());
144
145 11
        $application->run($input, $output);
146
147 11
        rewind($fp);
148
149 11
        return stream_get_contents($fp);
150
    }
151
152
    /**
153
     * @param KernelInterface $kernel
154
     * @return Application
155
     */
156 12
    protected function createApplication(KernelInterface $kernel)
157
    {
158 12
        $application = new Application($kernel);
159 12
        $application->setAutoExit(false);
160
161 12
        return $application;
162
    }
163
164
    /**
165
     * Retrieves the output verbosity level.
166
     *
167
     * @see Symfony\Component\Console\Output\OutputInterface for available levels
168
     *
169
     * @return int
170
     *
171
     * @throws \OutOfBoundsException If the set value isn't accepted
172
     */
173 12
    protected function getVerbosityLevel()
174
    {
175
        // If `null`, is not yet set
176 12
        if (null === $this->verbosityLevel) {
177
            // Set the global verbosity level that is set as NORMAL by the TreeBuilder in Configuration
178 6
            $level = strtoupper($this->getContainer()->getParameter('liip_functional_test.command_verbosity'));
179 6
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
180
181 6
            $this->verbosityLevel = constant($verbosity);
182 6
        }
183
184
        // If string, it is set by the developer, so check that the value is an accepted one
185 12
        if (is_string($this->verbosityLevel)) {
186 6
            $level = strtoupper($this->verbosityLevel);
187 6
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
188
189 6
            if (!defined($verbosity)) {
190 1
                throw new \OutOfBoundsException(
191 1
                    sprintf('The set value "%s" for verbosityLevel is not valid. Accepted are: "quiet", "normal", "verbose", "very_verbose" and "debug".', $level)
192 1
                );
193
            }
194
195 5
            $this->verbosityLevel = constant($verbosity);
196 5
        }
197
198 11
        return $this->verbosityLevel;
199
    }
200
201
    /**
202
     * In Symfony 2.3.* the verbosity level has to be set through {Symfony\Component\Console\Input\ArrayInput} and not
203
     * in {Symfony\Component\Console\Output\OutputInterface}.
204
     *
205
     * This method builds $params to be passed to {Symfony\Component\Console\Input\ArrayInput}.
206
     *
207
     * @codeCoverageIgnore
208
     *
209
     * @param array $params
210
     *
211
     * @return array
212
     */
213
    private function configureVerbosityForSymfony203(array $params)
214
    {
215
        switch ($this->getVerbosityLevel()) {
216
            case OutputInterface::VERBOSITY_QUIET:
217
                $params['-q'] = '-q';
218
                break;
219
220
            case OutputInterface::VERBOSITY_VERBOSE:
221
                $params['-v'] = '';
222
                break;
223
224
            case OutputInterface::VERBOSITY_VERY_VERBOSE:
225
                $params['-vv'] = '';
226
                break;
227
228
            case OutputInterface::VERBOSITY_DEBUG:
229
                $params['-vvv'] = '';
230
                break;
231
        }
232
233
        return $params;
234
    }
235
236 6
    public function setVerbosityLevel($level)
237
    {
238 6
        $this->verbosityLevel = $level;
239 6
    }
240
241
    /**
242
     * Retrieves the flag indicating if the output should be decorated or not.
243
     *
244
     * @return bool
245
     */
246 11
    protected function getDecorated()
247
    {
248 11
        if (null === $this->decorated) {
249
            // Set the global decoration flag that is set to `true` by the TreeBuilder in Configuration
250 5
            $this->decorated = $this->getContainer()->getParameter('liip_functional_test.command_decoration');
251 5
        }
252
253
        // Check the local decorated flag
254 11
        if (false === is_bool($this->decorated)) {
255
            throw new \OutOfBoundsException(
256
                sprintf('`WebTestCase::decorated` has to be `bool`. "%s" given.', gettype($this->decorated))
257
            );
258
        }
259
260 11
        return $this->decorated;
261
    }
262
263 6
    public function isDecorated($decorated)
264
    {
265 6
        $this->decorated = $decorated;
266 6
    }
267
268
    /**
269
     * Get an instance of the dependency injection container.
270
     * (this creates a kernel *without* parameters).
271
     *
272
     * @return ContainerInterface
273
     */
274 50
    protected function getContainer()
0 ignored issues
show
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...
275
    {
276 50
        if (!empty($this->kernelDir)) {
277
            $tmpKernelDir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : null;
278
            $_SERVER['KERNEL_DIR'] = getcwd().$this->kernelDir;
279
        }
280
281 50
        $cacheKey = $this->kernelDir.'|'.$this->environment;
282 50
        if (empty($this->containers[$cacheKey])) {
283
            $options = array(
284 49
                'environment' => $this->environment,
285 49
            );
286 49
            $kernel = $this->createKernel($options);
287 49
            $kernel->boot();
288
289 49
            $this->containers[$cacheKey] = $kernel->getContainer();
290 49
        }
291
292 50
        if (isset($tmpKernelDir)) {
293
            $_SERVER['KERNEL_DIR'] = $tmpKernelDir;
294
        }
295
296 50
        return $this->containers[$cacheKey];
297
    }
298
299
    /**
300
     * This function finds the time when the data blocks of a class definition
301
     * file were being written to, that is, the time when the content of the
302
     * file was changed.
303
     *
304
     * @param string $class The fully qualified class name of the fixture class to
305
     *                      check modification date on
306
     *
307
     * @return \DateTime|null
308
     */
309 3
    protected function getFixtureLastModified($class)
310
    {
311 3
        $lastModifiedDateTime = null;
312
313 3
        $reflClass = new \ReflectionClass($class);
314 3
        $classFileName = $reflClass->getFileName();
315
316 3
        if (file_exists($classFileName)) {
317 3
            $lastModifiedDateTime = new \DateTime();
318 3
            $lastModifiedDateTime->setTimestamp(filemtime($classFileName));
319 3
        }
320
321 3
        return $lastModifiedDateTime;
322
    }
323
324
    /**
325
     * Determine if the Fixtures that define a database backup have been
326
     * modified since the backup was made.
327
     *
328
     * @param array  $classNames The fixture classnames to check
329
     * @param string $backup     The fixture backup SQLite database file path
330
     *
331
     * @return bool TRUE if the backup was made since the modifications to the
332
     *              fixtures; FALSE otherwise
333
     */
334 7
    protected function isBackupUpToDate(array $classNames, $backup)
335
    {
336 7
        $backupLastModifiedDateTime = new \DateTime();
337 7
        $backupLastModifiedDateTime->setTimestamp(filemtime($backup));
338
339
        /** @var \Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader $loader */
340 7
        $loader = $this->getFixtureLoader($this->getContainer(), $classNames);
341
342
        // Use loader in order to fetch all the dependencies fixtures.
343 7
        foreach ($loader->getFixtures() as $className) {
344 3
            $fixtureLastModifiedDateTime = $this->getFixtureLastModified($className);
345 3
            if ($backupLastModifiedDateTime < $fixtureLastModifiedDateTime) {
346 1
                return false;
347
            }
348 7
        }
349
350 7
        return true;
351
    }
352
353
    /**
354
     * Set the database to the provided fixtures.
355
     *
356
     * Drops the current database and then loads fixtures using the specified
357
     * classes. The parameter is a list of fully qualified class names of
358
     * classes that implement Doctrine\Common\DataFixtures\FixtureInterface
359
     * so that they can be loaded by the DataFixtures Loader::addFixture
360
     *
361
     * When using SQLite this method will automatically make a copy of the
362
     * loaded schema and fixtures which will be restored automatically in
363
     * case the same fixture classes are to be loaded again. Caveat: changes
364
     * to references and/or identities may go undetected.
365
     *
366
     * Depends on the doctrine data-fixtures library being available in the
367
     * class path.
368
     *
369
     * @param array  $classNames   List of fully qualified class names of fixtures to load
370
     * @param string $omName       The name of object manager to use
371
     * @param string $registryName The service id of manager registry to use
372
     * @param int    $purgeMode    Sets the ORM purge mode
373
     *
374
     * @return null|AbstractExecutor
375
     */
376 36
    protected function loadFixtures(array $classNames, $omName = null, $registryName = 'doctrine', $purgeMode = null)
377
    {
378 36
        $container = $this->getContainer();
379
        /** @var ManagerRegistry $registry */
380 36
        $registry = $container->get($registryName);
381
        /** @var ObjectManager $om */
382 36
        $om = $registry->getManager($omName);
383 36
        $type = $registry->getName();
384
385 36
        $executorClass = 'PHPCR' === $type && class_exists('Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor')
386 36
            ? 'Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor'
387 36
            : 'Doctrine\\Common\\DataFixtures\\Executor\\'.$type.'Executor';
388 36
        $referenceRepository = new ProxyReferenceRepository($om);
389 36
        $cacheDriver = $om->getMetadataFactory()->getCacheDriver();
390
391 36
        if ($cacheDriver) {
392 36
            $cacheDriver->deleteAll();
393 36
        }
394
395 36
        if ('ORM' === $type) {
396 35
            $connection = $om->getConnection();
397 35
            if ($connection->getDriver() instanceof SqliteDriver) {
398 30
                $params = $connection->getParams();
399 30
                if (isset($params['master'])) {
400
                    $params = $params['master'];
401
                }
402
403 30
                $name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false);
404 30
                if (!$name) {
405
                    throw new \InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped.");
406
                }
407
408 30
                if (!isset(self::$cachedMetadatas[$omName])) {
409 10
                    self::$cachedMetadatas[$omName] = $om->getMetadataFactory()->getAllMetadata();
410 10
                    usort(self::$cachedMetadatas[$omName], function ($a, $b) {
411
                        return strcmp($a->name, $b->name);
412 10
                    });
413 10
                }
414 30
                $metadatas = self::$cachedMetadatas[$omName];
415
416 30
                if ($container->getParameter('liip_functional_test.cache_sqlite_db')) {
417 9
                    $backup = $container->getParameter('kernel.cache_dir').'/test_'.md5(serialize($metadatas).serialize($classNames)).'.db';
418 9
                    if (file_exists($backup) && file_exists($backup.'.ser') && $this->isBackupUpToDate($classNames, $backup)) {
419 7
                        $connection = $this->getContainer()->get('doctrine.orm.entity_manager')->getConnection();
420 7
                        if (null !== $connection) {
421 7
                            $connection->close();
422 7
                        }
423
424 7
                        $om->flush();
425 7
                        $om->clear();
426
427 7
                        $this->preFixtureBackupRestore($om, $referenceRepository, $backup);
428
429 7
                        copy($backup, $name);
430
431 7
                        $executor = new $executorClass($om);
432 7
                        $executor->setReferenceRepository($referenceRepository);
433 7
                        $executor->getReferenceRepository()->load($backup);
434
435 7
                        $this->postFixtureBackupRestore($backup);
436
437 7
                        return $executor;
438
                    }
439 3
                }
440
441
                // TODO: handle case when using persistent connections. Fail loudly?
442 24
                $schemaTool = new SchemaTool($om);
443 24
                $schemaTool->dropDatabase();
444 24
                if (!empty($metadatas)) {
445 24
                    $schemaTool->createSchema($metadatas);
446 24
                }
447 24
                $this->postFixtureSetup();
448
449 24
                $executor = new $executorClass($om);
450 24
                $executor->setReferenceRepository($referenceRepository);
451 24
            }
452 29
        }
453
454 30
        if (empty($executor)) {
455 6
            $purgerClass = 'Doctrine\\Common\\DataFixtures\\Purger\\'.$type.'Purger';
456 6
            if ('PHPCR' === $type) {
457 1
                $purger = new $purgerClass($om);
458 1
                $initManager = $container->has('doctrine_phpcr.initializer_manager')
459 1
                    ? $container->get('doctrine_phpcr.initializer_manager')
460 1
                    : null;
461
462 1
                $executor = new $executorClass($om, $purger, $initManager);
463 1
            } else {
464 5
                if ('ORM' === $type) {
465 5
                    $purger = new $purgerClass(null, $this->excludedDoctrineTables);
466 5
                } else {
467
                    $purger = new $purgerClass();
468
                }
469
470 5
                if (null !== $purgeMode) {
471 2
                    $purger->setPurgeMode($purgeMode);
472 2
                }
473
474 5
                $executor = new $executorClass($om, $purger);
475
            }
476
477 6
            $executor->setReferenceRepository($referenceRepository);
478 6
            $executor->purge();
479 6
        }
480
481 30
        $loader = $this->getFixtureLoader($container, $classNames);
482
483 30
        $executor->execute($loader->getFixtures(), true);
484
485 30
        if (isset($name) && isset($backup)) {
486 3
            $this->preReferenceSave($om, $executor, $backup);
487
488 3
            $executor->getReferenceRepository()->save($backup);
489 3
            copy($name, $backup);
490
491 3
            $this->postReferenceSave($om, $executor, $backup);
492 3
        }
493
494 30
        return $executor;
495
    }
496
497
    /**
498
     * Clean database.
499
     *
500
     * @param ManagerRegistry $registry
501
     * @param EntityManager   $om
502
     * @param null            $omName
503
     * @param string          $registryName
504
     * @param int             $purgeMode
505
     */
506 9
    private function cleanDatabase(ManagerRegistry $registry, EntityManager $om, $omName = null, $registryName = 'doctrine', $purgeMode = null)
507
    {
508 9
        $connection = $om->getConnection();
509
510 9
        $mysql = ($registry->getName() === 'ORM'
511 9
            && $connection->getDatabasePlatform() instanceof MySqlPlatform);
512
513 9
        if ($mysql) {
514 1
            $connection->query('SET FOREIGN_KEY_CHECKS=0');
515 1
        }
516
517 9
        $this->loadFixtures(array(), $omName, $registryName, $purgeMode);
518
519 9
        if ($mysql) {
520 1
            $connection->query('SET FOREIGN_KEY_CHECKS=1');
521 1
        }
522 9
    }
523
524
    /**
525
     * Locate fixture files.
526
     *
527
     * @param array $paths
528
     *
529
     * @return array $files
530
     *
531
     * @throws \InvalidArgumentException if a wrong path is given outside a bundle
532
     */
533 10
    private function locateResources($paths)
534
    {
535 10
        $files = array();
536
537 10
        $kernel = $this->getContainer()->get('kernel');
538
539 10
        foreach ($paths as $path) {
540 10
            if ($path[0] !== '@') {
541 3
                if (!file_exists($path)) {
542 1
                    throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $path));
543
                }
544 2
                $files[] = $path;
545 2
                continue;
546
            }
547
548 7
            $files[] = $kernel->locateResource($path);
549 8
        }
550
551 8
        return $files;
552
    }
553
554
    /**
555
     * @param array  $paths        Either symfony resource locators (@ BundleName/etc) or actual file paths
556
     * @param bool   $append
557
     * @param null   $omName
558
     * @param string $registryName
559
     * @param int    $purgeMode
560
     *
561
     * @return array
562
     *
563
     * @throws \BadMethodCallException
564
     */
565 10
    public function loadFixtureFiles(array $paths = array(), $append = false, $omName = null, $registryName = 'doctrine', $purgeMode = null)
566
    {
567 10
        if (!class_exists('Nelmio\Alice\Fixtures')) {
568
            // This class is available during tests, no exception will be thrown.
569
            // @codeCoverageIgnoreStart
570
            throw new \BadMethodCallException('nelmio/alice should be installed to use this method.');
571
            // @codeCoverageIgnoreEnd
572
        }
573
574
        /** @var ContainerInterface $container */
575 10
        $container = $this->getContainer();
576
577
        /** @var ManagerRegistry $registry */
578 10
        $registry = $container->get($registryName);
579
580
        /** @var EntityManager $om */
581 10
        $om = $registry->getManager($omName);
582
583 10
        if ($append === false) {
584 9
            $this->cleanDatabase($registry, $om, $omName, $registryName, $purgeMode);
585 9
        }
586
587 10
        $files = $this->locateResources($paths);
588
589
        // Check if the Hautelook AliceBundle is registered and if yes, use it instead of Nelmio Alice
590 8
        $hautelookLoaderServiceName = 'hautelook_alice.fixtures.loader';
591 8
        if ($container->has($hautelookLoaderServiceName)) {
592 3
            $loaderService = $container->get($hautelookLoaderServiceName);
593 3
            $persisterClass = class_exists('Nelmio\Alice\ORM\Doctrine') ?
594 3
                'Nelmio\Alice\ORM\Doctrine' :
595 3
                'Nelmio\Alice\Persister\Doctrine';
596
597 3
            return $loaderService->load(new $persisterClass($om), $files);
598
        }
599
600 5
        return Fixtures::load($files, $om);
601
    }
602
603
    /**
604
     * Callback function to be executed after Schema creation.
605
     * Use this to execute acl:init or other things necessary.
606
     */
607 24
    protected function postFixtureSetup()
608
    {
609 24
    }
610
611
    /**
612
     * Callback function to be executed after Schema restore.
613
     *
614
     * @return WebTestCase
615
     *
616
     * @deprecated since version 1.8, to be removed in 2.0. Use postFixtureBackupRestore method instead.
617
     */
618 7
    protected function postFixtureRestore()
619
    {
620 7
    }
621
622
    /**
623
     * Callback function to be executed before Schema restore.
624
     *
625
     * @param ObjectManager            $manager             The object manager
626
     * @param ProxyReferenceRepository $referenceRepository The reference repository
627
     *
628
     * @return WebTestCase
629
     *
630
     * @deprecated since version 1.8, to be removed in 2.0. Use preFixtureBackupRestore method instead.
631
     */
632 7
    protected function preFixtureRestore(ObjectManager $manager, ProxyReferenceRepository $referenceRepository)
0 ignored issues
show
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...
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...
633
    {
634 7
    }
635
636
    /**
637
     * Callback function to be executed after Schema restore.
638
     *
639
     * @param string $backupFilePath Path of file used to backup the references of the data fixtures
640
     *
641
     * @return WebTestCase
642
     */
643 7
    protected function postFixtureBackupRestore($backupFilePath)
0 ignored issues
show
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...
644
    {
645 7
        $this->postFixtureRestore();
646
647 7
        return $this;
648
    }
649
650
    /**
651
     * Callback function to be executed before Schema restore.
652
     *
653
     * @param ObjectManager            $manager             The object manager
654
     * @param ProxyReferenceRepository $referenceRepository The reference repository
655
     * @param string                   $backupFilePath      Path of file used to backup the references of the data fixtures
656
     *
657
     * @return WebTestCase
658
     */
659 7
    protected function preFixtureBackupRestore(
660
        ObjectManager $manager,
661
        ProxyReferenceRepository $referenceRepository,
662
        $backupFilePath
663
    ) {
664 7
        $this->preFixtureRestore($manager, $referenceRepository);
665
666 7
        return $this;
667
    }
668
669
    /**
670
     * Callback function to be executed after save of references.
671
     *
672
     * @param ObjectManager    $manager        The object manager
673
     * @param AbstractExecutor $executor       Executor of the data fixtures
674
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
675
     *
676
     * @return WebTestCase
677
     */
678 3
    protected function postReferenceSave(ObjectManager $manager, AbstractExecutor $executor, $backupFilePath)
0 ignored issues
show
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...
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...
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...
679
    {
680 3
    }
681
682
    /**
683
     * Callback function to be executed before save of references.
684
     *
685
     * @param ObjectManager    $manager        The object manager
686
     * @param AbstractExecutor $executor       Executor of the data fixtures
687
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
688
     *
689
     * @return WebTestCase
690
     */
691 3
    protected function preReferenceSave(ObjectManager $manager, AbstractExecutor $executor, $backupFilePath)
0 ignored issues
show
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...
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...
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...
692
    {
693 3
    }
694
695
    /**
696
     * Retrieve Doctrine DataFixtures loader.
697
     *
698
     * @param ContainerInterface $container
699
     * @param array              $classNames
700
     *
701
     * @return Loader
702
     */
703 36
    protected function getFixtureLoader(ContainerInterface $container, array $classNames)
704
    {
705 36
        $loaderClass = class_exists('Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader')
706 36
            ? 'Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader'
707 36
            : (class_exists('Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader')
708
                // This class is not available during tests.
709
                // @codeCoverageIgnoreStart
710
                ? 'Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader'
711
                // @codeCoverageIgnoreEnd
712 36
                : 'Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader');
713
714 36
        $loader = new $loaderClass($container);
715
716 36
        foreach ($classNames as $className) {
717 11
            $this->loadFixtureClass($loader, $className);
718 36
        }
719
720 36
        return $loader;
721
    }
722
723
    /**
724
     * Load a data fixture class.
725
     *
726
     * @param Loader $loader
727
     * @param string $className
728
     */
729 11
    protected function loadFixtureClass($loader, $className)
730
    {
731 11
        $fixture = new $className();
732
733 11
        if ($loader->hasFixture($fixture)) {
734 2
            unset($fixture);
735
736 2
            return;
737
        }
738
739 11
        $loader->addFixture($fixture);
740
741 11
        if ($fixture instanceof DependentFixtureInterface) {
742 2
            foreach ($fixture->getDependencies() as $dependency) {
743 2
                $this->loadFixtureClass($loader, $dependency);
744 2
            }
745 2
        }
746 11
    }
747
748
    /**
749
     * Creates an instance of a lightweight Http client.
750
     *
751
     * If $authentication is set to 'true' it will use the content of
752
     * 'liip_functional_test.authentication' to log in.
753
     *
754
     * $params can be used to pass headers to the client, note that they have
755
     * to follow the naming format used in $_SERVER.
756
     * Example: 'HTTP_X_REQUESTED_WITH' instead of 'X-Requested-With'
757
     *
758
     * @param bool|array $authentication
759
     * @param array      $params
760
     *
761
     * @return Client
762
     */
763 55
    protected function makeClient($authentication = false, array $params = array())
764
    {
765 55
        if ($authentication) {
766 2
            if ($authentication === true) {
767
                $authentication = array(
768 1
                    'username' => $this->getContainer()
769 1
                        ->getParameter('liip_functional_test.authentication.username'),
770 1
                    'password' => $this->getContainer()
771 1
                        ->getParameter('liip_functional_test.authentication.password'),
772 1
                );
773 1
            }
774
775 2
            $params = array_merge($params, array(
776 2
                'PHP_AUTH_USER' => $authentication['username'],
777 2
                'PHP_AUTH_PW' => $authentication['password'],
778 2
            ));
779 2
        }
780
781 55
        $client = static::createClient(array('environment' => $this->environment), $params);
782
783 55
        if ($this->firewallLogins) {
784
            // has to be set otherwise "hasPreviousSession" in Request returns false.
785 2
            $options = $client->getContainer()->getParameter('session.storage.options');
786
787 2
            if (!$options || !isset($options['name'])) {
788
                throw new \InvalidArgumentException('Missing session.storage.options#name');
789
            }
790
791 2
            $session = $client->getContainer()->get('session');
792
            // Since the namespace of the session changed in symfony 2.1, instanceof can be used to check the version.
793 2
            if ($session instanceof Session) {
794 2
                $session->setId(uniqid());
795 2
            }
796
797 2
            $client->getCookieJar()->set(new Cookie($options['name'], $session->getId()));
798
799
            /** @var $user UserInterface */
800 2
            foreach ($this->firewallLogins as $firewallName => $user) {
801 2
                $token = $this->createUserToken($user, $firewallName);
802
803
                // BC: security.token_storage is available on Symfony 2.6+
804
                // see http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements
805 2
                if ($client->getContainer()->has('security.token_storage')) {
806 2
                    $tokenStorage = $client->getContainer()->get('security.token_storage');
807 2
                } else {
808
                    // This block will never be reached with Symfony 2.6+
809
                    // @codeCoverageIgnoreStart
810
                    $tokenStorage = $client->getContainer()->get('security.context');
811
                    // @codeCoverageIgnoreEnd
812
                }
813
814 2
                $tokenStorage->setToken($token);
815 2
                $session->set('_security_'.$firewallName, serialize($token));
816 2
            }
817
818 2
            $session->save();
819 2
        }
820
821 55
        return $client;
822
    }
823
824
    /**
825
     * Create User Token.
826
     *
827
     * Factory method for creating a User Token object for the firewall based on
828
     * the user object provided. By default it will be a Username/Password
829
     * Token based on the user's credentials, but may be overridden for custom
830
     * tokens in your applications.
831
     *
832
     * @param UserInterface $user         The user object to base the token off of
833
     * @param string        $firewallName name of the firewall provider to use
834
     *
835
     * @return TokenInterface The token to be used in the security context
836
     */
837 2
    protected function createUserToken(UserInterface $user, $firewallName)
838
    {
839 2
        return new UsernamePasswordToken(
840 2
            $user,
841 2
            null,
842 2
            $firewallName,
843 2
            $user->getRoles()
844 2
        );
845
    }
846
847
    /**
848
     * Extracts the location from the given route.
849
     *
850
     * @param string $route    The name of the route
851
     * @param array  $params   Set of parameters
852
     * @param int    $absolute
853
     *
854
     * @return string
855
     */
856 1
    protected function getUrl($route, $params = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
857
    {
858 1
        return $this->getContainer()->get('router')->generate($route, $params, $absolute);
859
    }
860
861
    /**
862
     * Checks the success state of a response.
863
     *
864
     * @param Response $response Response object
865
     * @param bool     $success  to define whether the response is expected to be successful
866
     * @param string   $type
867
     */
868 6
    public function isSuccessful(Response $response, $success = true, $type = 'text/html')
869
    {
870 6
        HttpAssertions::isSuccessful($response, $success, $type);
871 5
    }
872
873
    /**
874
     * Executes a request on the given url and returns the response contents.
875
     *
876
     * This method also asserts the request was successful.
877
     *
878
     * @param string $path           path of the requested page
879
     * @param string $method         The HTTP method to use, defaults to GET
880
     * @param bool   $authentication Whether to use authentication, defaults to false
881
     * @param bool   $success        to define whether the response is expected to be successful
882
     *
883
     * @return string
884
     */
885 1
    public function fetchContent($path, $method = 'GET', $authentication = false, $success = true)
886
    {
887 1
        $client = $this->makeClient($authentication);
888 1
        $client->request($method, $path);
889
890 1
        $content = $client->getResponse()->getContent();
891 1
        if (is_bool($success)) {
892 1
            $this->isSuccessful($client->getResponse(), $success);
893 1
        }
894
895 1
        return $content;
896
    }
897
898
    /**
899
     * Executes a request on the given url and returns a Crawler object.
900
     *
901
     * This method also asserts the request was successful.
902
     *
903
     * @param string $path           path of the requested page
904
     * @param string $method         The HTTP method to use, defaults to GET
905
     * @param bool   $authentication Whether to use authentication, defaults to false
906
     * @param bool   $success        Whether the response is expected to be successful
907
     *
908
     * @return Crawler
909
     */
910 1
    public function fetchCrawler($path, $method = 'GET', $authentication = false, $success = true)
911
    {
912 1
        $client = $this->makeClient($authentication);
913 1
        $crawler = $client->request($method, $path);
914
915 1
        $this->isSuccessful($client->getResponse(), $success);
916
917 1
        return $crawler;
918
    }
919
920
    /**
921
     * @param UserInterface $user
922
     * @param string        $firewallName
923
     *
924
     * @return WebTestCase
925
     */
926 2
    public function loginAs(UserInterface $user, $firewallName)
927
    {
928 2
        $this->firewallLogins[$firewallName] = $user;
929
930 2
        return $this;
931
    }
932
933
    /**
934
     * Asserts that the HTTP response code of the last request performed by
935
     * $client matches the expected code. If not, raises an error with more
936
     * information.
937
     *
938
     * @param $expectedStatusCode
939
     * @param Client $client
940
     */
941 12
    public function assertStatusCode($expectedStatusCode, Client $client)
942
    {
943 12
        HttpAssertions::assertStatusCode($expectedStatusCode, $client);
944 9
    }
945
946
    /**
947
     * Assert that the last validation errors within $container match the
948
     * expected keys.
949
     *
950
     * @param array              $expected  A flat array of field names
951
     * @param ContainerInterface $container
952
     */
953 3
    public function assertValidationErrors(array $expected, ContainerInterface $container)
954
    {
955 3
        HttpAssertions::assertValidationErrors($expected, $container);
956 1
    }
957
958
    /**
959
     * @param array $excludedDoctrineTables
960
     */
961 1
    public function setExcludedDoctrineTables($excludedDoctrineTables)
962
    {
963 1
        $this->excludedDoctrineTables = $excludedDoctrineTables;
964 1
    }
965
}
966