Completed
Pull Request — master (#293)
by Lukas Kahwe
13:32 queued 08:21
created

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