%PDF- <> %âãÏÓ endobj 2 0 obj <> endobj 3 0 obj <>/ExtGState<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI] >>/Annots[ 28 0 R 29 0 R] /MediaBox[ 0 0 595.5 842.25] /Contents 4 0 R/Group<>/Tabs/S>> endobj ºaâÚÎΞ-ÌE1ÍØÄ÷{òò2ÿ ÛÖ^ÔÀá TÎ{¦?§®¥kuµùÕ5sLOšuY>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<> endobj 2 0 obj<>endobj 2 0 obj<>es 3 0 R>> endobj 2 0 obj<> ox[ 0.000000 0.000000 609.600000 935.600000]/Fi endobj 3 0 obj<> endobj 7 1 obj<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/Subtype/Form>> stream
<?php
namespace Codeception\Module;
use Codeception\Configuration;
use Codeception\Exception\ModuleException;
use Codeception\Lib\Framework;
use Codeception\Exception\ModuleRequireException;
use Codeception\Lib\Connector\Symfony as SymfonyConnector;
use Codeception\Lib\Interfaces\DoctrineProvider;
use Codeception\Lib\Interfaces\PartedModule;
use Symfony\Component\Finder\Finder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Finder\SplFileInfo;
use Symfony\Component\VarDumper\Cloner\Data;
/**
* This module uses Symfony Crawler and HttpKernel to emulate requests and test response.
*
* ## Demo Project
*
* <https://github.com/Codeception/symfony-demo>
*
* ## Config
*
* ### Symfony 4.x
*
* * app_path: 'src' - in Symfony 4 Kernel is located inside `src`
* * environment: 'local' - environment used for load kernel
* * kernel_class: 'App\Kernel' - kernel class name
* * em_service: 'doctrine.orm.entity_manager' - use the stated EntityManager to pair with Doctrine Module.
* * debug: true - turn on/off debug mode
* * cache_router: 'false' - enable router caching between tests in order to [increase performance](http://lakion.com/blog/how-did-we-speed-up-sylius-behat-suite-with-blackfire)
* * rebootable_client: 'true' - reboot client's kernel before each request
*
* #### Example (`functional.suite.yml`) - Symfony 4 Directory Structure
*
* modules:
* enabled:
* - Symfony:
* app_path: 'src'
* environment: 'test'
*
*
* ### Symfony 3.x
*
* * app_path: 'app' - specify custom path to your app dir, where the kernel interface is located.
* * var_path: 'var' - specify custom path to your var dir, where bootstrap cache is located.
* * environment: 'local' - environment used for load kernel
* * kernel_class: 'AppKernel' - kernel class name
* * em_service: 'doctrine.orm.entity_manager' - use the stated EntityManager to pair with Doctrine Module.
* * debug: true - turn on/off debug mode
* * cache_router: 'false' - enable router caching between tests in order to [increase performance](http://lakion.com/blog/how-did-we-speed-up-sylius-behat-suite-with-blackfire)
* * rebootable_client: 'true' - reboot client's kernel before each request
*
* #### Example (`functional.suite.yml`) - Symfony 3 Directory Structure
*
* modules:
* enabled:
* - Symfony:
* app_path: 'app/front'
* var_path: 'var'
* environment: 'local_test'
*
*
* ### Symfony 2.x
*
* * app_path: 'app' - specify custom path to your app dir, where bootstrap cache and kernel interface is located.
* * environment: 'local' - environment used for load kernel
* * kernel_class: 'AppKernel' - kernel class name
* * debug: true - turn on/off debug mode
* * em_service: 'doctrine.orm.entity_manager' - use the stated EntityManager to pair with Doctrine Module.
* * cache_router: 'false' - enable router caching between tests in order to [increase performance](http://lakion.com/blog/how-did-we-speed-up-sylius-behat-suite-with-blackfire)
* * rebootable_client: 'true' - reboot client's kernel before each request
*
* ### Example (`functional.suite.yml`) - Symfony 2.x Directory Structure
*
* ```
* modules:
* - Symfony:
* app_path: 'app/front'
* environment: 'local_test'
* ```
*
* ## Public Properties
*
* * kernel - HttpKernel instance
* * client - current Crawler instance
*
* ## Parts
*
* * services - allows to use Symfony DIC only with WebDriver or PhpBrowser modules.
*
* Usage example:
*
* ```yaml
* actor: AcceptanceTester
* modules:
* enabled:
* - Symfony:
* part: SERVICES
* - Doctrine2:
* depends: Symfony
* - WebDriver:
* url: http://your-url.com
* browser: phantomjs
* ```
*
*/
class Symfony extends Framework implements DoctrineProvider, PartedModule
{
private static $possibleKernelClasses = [
'AppKernel', // Symfony Standard
'App\Kernel', // Symfony Flex
];
/**
* @var \Symfony\Component\HttpKernel\Kernel
*/
public $kernel;
public $config = [
'app_path' => 'app',
'var_path' => 'app',
'kernel_class' => null,
'environment' => 'test',
'debug' => true,
'cache_router' => false,
'em_service' => 'doctrine.orm.entity_manager',
'rebootable_client' => true,
];
/**
* @return array
*/
public function _parts()
{
return ['services'];
}
/**
* @var
*/
protected $kernelClass;
/**
* Services that should be persistent permanently for all tests
*
* @var array
*/
protected $permanentServices = [];
/**
* Services that should be persistent during test execution between kernel reboots
*
* @var array
*/
protected $persistentServices = [];
public function _initialize()
{
$this->initializeSymfonyCache();
$this->kernelClass = $this->getKernelClass();
$maxNestingLevel = 200; // Symfony may have very long nesting level
$xdebugMaxLevelKey = 'xdebug.max_nesting_level';
if (ini_get($xdebugMaxLevelKey) < $maxNestingLevel) {
ini_set($xdebugMaxLevelKey, $maxNestingLevel);
}
$this->kernel = new $this->kernelClass($this->config['environment'], $this->config['debug']);
$this->kernel->boot();
if ($this->config['cache_router'] === true) {
$this->persistService('router', true);
}
}
/**
* Require Symfonys bootstrap.php.cache only for PHP Version < 7
*
* @throws ModuleRequireException
*/
private function initializeSymfonyCache()
{
$cache = Configuration::projectDir() . $this->config['var_path'] . DIRECTORY_SEPARATOR . 'bootstrap.php.cache';
if (PHP_VERSION_ID < 70000 && !file_exists($cache)) {
throw new ModuleRequireException(
__CLASS__,
"Symfony bootstrap file not found in $cache\n \n" .
"Please specify path to bootstrap file using `var_path` config option\n \n" .
"If you are trying to load bootstrap from a Bundle provide path like:\n \n" .
"modules:\n enabled:\n" .
" - Symfony:\n" .
" var_path: '../../app'\n" .
" app_path: '../../app'"
);
}
if (file_exists($cache)) {
require_once $cache;
}
}
/**
* Initialize new client instance before each test
*/
public function _before(\Codeception\TestInterface $test)
{
$this->persistentServices = array_merge($this->persistentServices, $this->permanentServices);
$this->client = new SymfonyConnector($this->kernel, $this->persistentServices, $this->config['rebootable_client']);
}
/**
* Update permanent services after each test
*/
public function _after(\Codeception\TestInterface $test)
{
foreach ($this->permanentServices as $serviceName => $service) {
$this->permanentServices[$serviceName] = $this->grabService($serviceName);
}
parent::_after($test);
}
public function onReconfigure($settings = [])
{
parent::_beforeSuite($settings);
$this->_initialize();
}
/**
* Retrieve Entity Manager.
*
* EM service is retrieved once and then that instance returned on each call
*/
public function _getEntityManager()
{
if ($this->kernel === null) {
$this->fail('Symfony2 platform module is not loaded');
}
if (!isset($this->permanentServices[$this->config['em_service']])) {
// try to persist configured EM
$this->persistService($this->config['em_service'], true);
if ($this->_getContainer()->has('doctrine')) {
$this->persistService('doctrine', true);
}
if ($this->_getContainer()->has('doctrine.orm.default_entity_manager')) {
$this->persistService('doctrine.orm.default_entity_manager', true);
}
if ($this->_getContainer()->has('doctrine.dbal.backend_connection')) {
$this->persistService('doctrine.dbal.backend_connection', true);
}
}
return $this->permanentServices[$this->config['em_service']];
}
/**
* Return container.
*
* @return ContainerInterface
*/
public function _getContainer()
{
return $this->kernel->getContainer();
}
/**
* Attempts to guess the kernel location.
*
* When the Kernel is located, the file is required.
*
* @return string The Kernel class name
*/
protected function getKernelClass()
{
$path = codecept_root_dir() . $this->config['app_path'];
if (!file_exists(codecept_root_dir() . $this->config['app_path'])) {
throw new ModuleRequireException(
__CLASS__,
"Can't load Kernel from $path.\n"
. "Directory does not exists. Use `app_path` parameter to provide valid application path"
);
}
$finder = new Finder();
$finder->name('*Kernel.php')->depth('0')->in($path);
$results = iterator_to_array($finder);
if (!count($results)) {
throw new ModuleRequireException(
__CLASS__,
"File with Kernel class was not found at $path. "
. "Specify directory where file with Kernel class for your application is located with `app_path` parameter."
);
}
if (file_exists(codecept_root_dir() . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php')) {
// ensure autoloader from this dir is loaded
require_once codecept_root_dir() . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
}
$filesRealPath = array_map(function ($file) {
require_once $file;
return $file->getRealPath();
}, $results);
$possibleKernelClasses = $this->getPossibleKernelClasses();
foreach ($possibleKernelClasses as $class) {
if (class_exists($class)) {
$refClass = new \ReflectionClass($class);
if ($file = array_search($refClass->getFileName(), $filesRealPath)) {
return $class;
}
}
}
throw new ModuleRequireException(
__CLASS__,
"Kernel class was not found in $file. "
. "Specify directory where file with Kernel class for your application is located with `app_path` parameter."
);
}
/**
* Get service $serviceName and add it to the lists of persistent services.
* If $isPermanent then service becomes persistent between tests
*
* @param string $serviceName
* @param boolean $isPermanent
*/
public function persistService($serviceName, $isPermanent = false)
{
$service = $this->grabService($serviceName);
$this->persistentServices[$serviceName] = $service;
if ($isPermanent) {
$this->permanentServices[$serviceName] = $service;
}
if ($this->client) {
$this->client->persistentServices[$serviceName] = $service;
}
}
/**
* Remove service $serviceName from the lists of persistent services.
*
* @param string $serviceName
*/
public function unpersistService($serviceName)
{
if (isset($this->persistentServices[$serviceName])) {
unset($this->persistentServices[$serviceName]);
}
if (isset($this->permanentServices[$serviceName])) {
unset($this->permanentServices[$serviceName]);
}
if ($this->client && isset($this->client->persistentServices[$serviceName])) {
unset($this->client->persistentServices[$serviceName]);
}
}
/**
* Invalidate previously cached routes.
*/
public function invalidateCachedRouter()
{
$this->unpersistService('router');
}
/**
* Opens web page using route name and parameters.
*
* ``` php
* <?php
* $I->amOnRoute('posts.create');
* $I->amOnRoute('posts.show', array('id' => 34));
* ?>
* ```
*
* @param $routeName
* @param array $params
*/
public function amOnRoute($routeName, array $params = [])
{
$router = $this->grabService('router');
if (!$router->getRouteCollection()->get($routeName)) {
$this->fail(sprintf('Route with name "%s" does not exists.', $routeName));
}
$url = $router->generate($routeName, $params);
$this->amOnPage($url);
}
/**
* Checks that current url matches route.
*
* ``` php
* <?php
* $I->seeCurrentRouteIs('posts.index');
* $I->seeCurrentRouteIs('posts.show', array('id' => 8));
* ?>
* ```
*
* @param $routeName
* @param array $params
*/
public function seeCurrentRouteIs($routeName, array $params = [])
{
$router = $this->grabService('router');
if (!$router->getRouteCollection()->get($routeName)) {
$this->fail(sprintf('Route with name "%s" does not exists.', $routeName));
}
$uri = explode('?', $this->grabFromCurrentUrl())[0];
try {
$match = $router->match($uri);
} catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
$this->fail(sprintf('The "%s" url does not match with any route', $uri));
}
$expected = array_merge(['_route' => $routeName], $params);
$intersection = array_intersect_assoc($expected, $match);
$this->assertEquals($expected, $intersection);
}
/**
* Checks that current url matches route.
* Unlike seeCurrentRouteIs, this can matches without exact route parameters
*
* ``` php
* <?php
* $I->seeCurrentRouteMatches('my_blog_pages');
* ?>
* ```
*
* @param $routeName
*/
public function seeInCurrentRoute($routeName)
{
$router = $this->grabService('router');
if (!$router->getRouteCollection()->get($routeName)) {
$this->fail(sprintf('Route with name "%s" does not exists.', $routeName));
}
$uri = explode('?', $this->grabFromCurrentUrl())[0];
try {
$matchedRouteName = $router->match($uri)['_route'];
} catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
$this->fail(sprintf('The "%s" url does not match with any route', $uri));
}
$this->assertEquals($matchedRouteName, $routeName);
}
/**
* Checks if the desired number of emails was sent.
* If no argument is provided then at least one email must be sent to satisfy the check.
*
* ``` php
* <?php
* $I->seeEmailIsSent(2);
* ?>
* ```
*
* @param null|int $expectedCount
*/
public function seeEmailIsSent($expectedCount = null)
{
$profile = $this->getProfile();
if (!$profile) {
$this->fail('Emails can\'t be tested without Profiler');
}
if (!$profile->hasCollector('swiftmailer')) {
$this->fail('Emails can\'t be tested without SwiftMailer connector');
}
if (!is_int($expectedCount) && !is_null($expectedCount)) {
$this->fail(sprintf(
'The required number of emails must be either an integer or null. "%s" was provided.',
print_r($expectedCount, true)
));
}
$realCount = $profile->getCollector('swiftmailer')->getMessageCount();
if ($expectedCount === null) {
$this->assertGreaterThan(0, $realCount);
} else {
$this->assertEquals(
$expectedCount,
$realCount,
sprintf(
'Expected number of sent emails was %d, but in reality %d %s sent.',
$expectedCount,
$realCount,
$realCount === 2 ? 'was' : 'were'
)
);
}
}
/**
* Checks that no email was sent. This is an alias for seeEmailIsSent(0).
*
* @part email
*/
public function dontSeeEmailIsSent()
{
$this->seeEmailIsSent(0);
}
/**
* Grabs a service from Symfony DIC container.
* Recommended to use for unit testing.
*
* ``` php
* <?php
* $em = $I->grabServiceFromContainer('doctrine');
* ?>
* ```
*
* @param $service
* @return mixed
* @part services
* @deprecated Use grabService instead
*/
public function grabServiceFromContainer($service)
{
return $this->grabService($service);
}
/**
* Grabs a service from Symfony DIC container.
* Recommended to use for unit testing.
*
* ``` php
* <?php
* $em = $I->grabService('doctrine');
* ?>
* ```
*
* @param $service
* @return mixed
* @part services
*/
public function grabService($service)
{
$container = $this->_getContainer();
if (!$container->has($service)) {
$this->fail("Service $service is not available in container");
}
return $container->get($service);
}
/**
* @return \Symfony\Component\HttpKernel\Profiler\Profile
*/
protected function getProfile()
{
$container = $this->_getContainer();
if (!$container->has('profiler')) {
return null;
}
$profiler = $this->grabService('profiler');
$response = $this->client->getResponse();
if (null === $response) {
$this->fail("You must perform a request before using this method.");
}
return $profiler->loadProfileFromResponse($response);
}
/**
* @param $url
*/
protected function debugResponse($url)
{
parent::debugResponse($url);
if ($profile = $this->getProfile()) {
if ($profile->hasCollector('security')) {
if ($profile->getCollector('security')->isAuthenticated()) {
$roles = $profile->getCollector('security')->getRoles();
if ($roles instanceof Data) {
$roles = $this->extractRawRoles($roles);
}
$this->debugSection(
'User',
$profile->getCollector('security')->getUser()
. ' [' . implode(',', $roles) . ']'
);
} else {
$this->debugSection('User', 'Anonymous');
}
}
if ($profile->hasCollector('swiftmailer')) {
$messages = $profile->getCollector('swiftmailer')->getMessageCount();
if ($messages) {
$this->debugSection('Emails', $messages . ' sent');
}
}
if ($profile->hasCollector('timer')) {
$this->debugSection('Time', $profile->getCollector('timer')->getTime());
}
}
}
/**
* @param Data $data
* @return array
*/
private function extractRawRoles(Data $data)
{
if ($this->dataRevealsValue($data)) {
$roles = $data->getValue();
} else {
$raw = $data->getRawData();
$roles = isset($raw[1]) ? $raw[1] : [];
}
return $roles;
}
/**
* Returns a list of recognized domain names.
*
* @return array
*/
protected function getInternalDomains()
{
$internalDomains = [];
$routes = $this->grabService('router')->getRouteCollection();
/* @var \Symfony\Component\Routing\Route $route */
foreach ($routes as $route) {
if (!is_null($route->getHost())) {
$compiled = $route->compile();
if (!is_null($compiled->getHostRegex())) {
$internalDomains[] = $compiled->getHostRegex();
}
}
}
return array_unique($internalDomains);
}
/**
* Reboot client's kernel.
* Can be used to manually reboot kernel when 'rebootable_client' => false
*
* ``` php
* <?php
* ...
* perform some requests
* ...
* $I->rebootClientKernel();
* ...
* perform other requests
* ...
*
* ?>
* ```
*
*/
public function rebootClientKernel()
{
if ($this->client) {
$this->client->rebootKernel();
}
}
/**
* Public API from Data changed from Symfony 3.2 to 3.3.
*
* @param \Symfony\Component\VarDumper\Cloner\Data $data
*
* @return bool
*/
private function dataRevealsValue(Data $data)
{
return method_exists($data, 'getValue');
}
/**
* Returns list of the possible kernel classes based on the module configuration
*
* @return array
*/
private function getPossibleKernelClasses()
{
if (empty($this->config['kernel_class'])) {
return self::$possibleKernelClasses;
}
if (!is_string($this->config['kernel_class'])) {
throw new ModuleException(
__CLASS__,
"Parameter 'kernel_class' must have 'string' type.\n"
);
}
return [$this->config['kernel_class']];
}
}