├── composer.json ├── readme.md └── src ├── Bootstrap.php ├── ContainerFactory.php ├── DatabasesRegistry.php ├── Mocks ├── ApplicationRequestMock.php ├── ControlMock.php ├── DoctrineConnectionMock.php ├── HttpRequestMock.php ├── NetteDatabaseConnectionMock.php └── PresenterMock.php ├── Providers └── IDatabaseProvider.php ├── Runner.php ├── Testbench.php ├── TestbenchExtension.php ├── Traits ├── TCompiledContainer.php ├── TComponent.php ├── TDoctrine.php ├── TNetteDatabase.php └── TPresenter.php └── run-tests /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mrtnzlml/testbench", 3 | "description": "Simple integration testing tool for Nette Framework applications", 4 | "type": "library", 5 | "license": [ 6 | "BSD-3-Clause", 7 | "GPL-2.0", 8 | "GPL-3.0" 9 | ], 10 | "authors": [ 11 | { 12 | "name": "Martin Zlámal", 13 | "homepage": "http://zlml.cz/" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.6.0", 18 | "kdyby/fake-session": "^2.0", 19 | "nette/application": "^2.4 || ^v3.0", 20 | "nette/bootstrap": "^2.4 || ^v3.0", 21 | "nette/di": "^2.4 || ^v3.0", 22 | "nette/safe-stream": "^2.3", 23 | "nette/security": "^2.4 || ^v3.0", 24 | "nette/tester": "^2.0", 25 | "nette/utils": "^2.4 || ^v3.0" 26 | }, 27 | "require-dev": { 28 | "latte/latte": "^2.4", 29 | "nette/forms": "^2.4 || ^v3.0", 30 | "nette/robot-loader": "^2.4 || ^v3.0", 31 | "tracy/tracy": "^2.4", 32 | "kdyby/doctrine": "^3.1", 33 | "kdyby/doctrine-cache": "^2.5", 34 | "kdyby/doctrine-dbal-batchimport": "dev-master", 35 | "kdyby/events": "^3.0", 36 | "nette/database": "^2.4 || ^v3.0", 37 | "zenify/doctrine-migrations": "^2.3" 38 | }, 39 | "autoload": { 40 | "psr-4": { 41 | "Testbench\\": [ 42 | "src/", 43 | "src/Traits/" 44 | ] 45 | }, 46 | "files": [ 47 | "src/Testbench.php" 48 | ] 49 | }, 50 | "autoload-dev": { 51 | "classmap": [ 52 | "tests/" 53 | ] 54 | }, 55 | "bin": [ 56 | "src/run-tests" 57 | ], 58 | "suggest": { 59 | "kdyby/doctrine": "Allows enhanced database tests using Doctrine", 60 | "kdyby/doctrine-dbal-batchimport": "Allows SQL scripts import for Doctrine (required with kdyby/doctrine)", 61 | "nette/database": "Allows enhanced database tests using Nette\\Database", 62 | "zenify/doctrine-migrations": "Migrate database to the fresh state" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | > At this moment I don't have time, energy and money to maintain this project. But it's a shame so if you depend on this project and you want to become a sponsor or develop it further please don't hesitate to contact me. Otherwise, I am not able to guarantee bright future of this repo... :) 2 | 3 | [![Build Status](https://travis-ci.org/mrtnzlml/testbench.svg?branch=master)](https://travis-ci.org/mrtnzlml/testbench) 4 | 5 | Tested against PHP 5.6 and 7.0. Please read [this wiki](https://github.com/mrtnzlml/testbench/wiki). 6 | 7 | Heavily inspired by these GitHub projects: 8 | - [Kdyby/TesterExtras](https://github.com/Kdyby/TesterExtras), [Librette](https://github.com/librette) tests, [Nette](https://github.com/nette) tests and [damejidlo/modular-testcase](https://github.com/damejidlo/modular-testcase) 9 | 10 | And article(s): 11 | - [Bootstrap your integration testing database](https://jiripudil.cz/blog/bootstrap-your-integration-testing-database) (Jiří Pudil) 12 | - [Testování presenterů v Nette](http://zlml.cz/testovani-presenteru-v-nette) (me) 13 | 14 | Simple test bench for Nette Framework projects 15 | ---------------------------------------------- 16 | Write integration tests as simple as possible. This project helps you to write tests very quickly. DRY! The main goal of this project is to make testing very simple for everyone and help with the difficult start. 17 | 18 | You can find few examples in this readme or take a look to the `tests` folder in this project. 19 | 20 | Installation 21 | ------------ 22 | ``` 23 | $ composer require mrtnzlml/testbench 24 | ``` 25 | 26 | Testbench itself doesn't need database. But if you want to use `Testbench\TDoctrine` or `Testbench\TNetteDatabase` trait you have to setup database for the first connection. You should use standard database configuration - nothing special. You can see example in `tests/tests.local.neon.dist` file. Empty database is good enough for Testbench. Supported databases are: 27 | 28 | - Doctrine 2 29 | - Nette\Database 30 | 31 | For another databases please send PR or open issue. 32 | 33 | Minimal code 34 | ------------ 35 | At first you need classic bootstrap file (just example, DIY): 36 | 37 | ```php 38 | createRobotLoader()->addDirectory([ 44 | __DIR__ . '/../app', 45 | ])->register(); 46 | 47 | $configurator->addParameters([ 48 | 'appDir' => __DIR__ . '/../app', 49 | ]); 50 | 51 | $configurator->addConfig(__DIR__ . '/../app/config/config.neon'); 52 | $configurator->addConfig(__DIR__ . '/tests.neon'); 53 | }); 54 | ``` 55 | 56 | It's important, that we are not creating dependency injection container here. You can use [autoload](https://getcomposer.org/doc/04-schema.md#autoload) from composer if you don't want to use robot loader. 57 | You should also create config file e.g. `tests.neon`. This file is needed only for database tests at this moment. In this file you should configure your project before tests: 58 | 59 | ```neon 60 | testbench: 61 | sqls: #what should be loaded after empty database creation 62 | - %appDir%/../sqls/1.sql 63 | - %appDir%/../sqls/2.sql 64 | ``` 65 | 66 | And you are ready to go: 67 | 68 | ```php 69 | checkAction('Homepage:default'); 84 | } 85 | 86 | public function testRenderDefaultModule() 87 | { 88 | $this->checkAction('Module:Homepage:default'); 89 | } 90 | 91 | } 92 | 93 | (new HomepagePresenterTest())->run(); 94 | ``` 95 | 96 | You can easily write cover with tests UI\Controls, restricted areas, forms, signals, redirects, ... 97 | 98 | Please read [this article](http://zlml.cz/jednoduche-testovani-pro-uplne-kazdeho). 99 | 100 | Give it a shot! 101 | ----------- 102 | Look at the tests in this project. You'll see how to use it properly. There are examples in `tests` folder or in the wiki. Learn how to use these traits: 103 | 104 | - [Testbench\TCompiledContainer](https://github.com/mrtnzlml/testbench/wiki/Testbench%5CTCompiledContainer) 105 | - [Testbench\TComponent](https://github.com/mrtnzlml/testbench/wiki/Testbench%5CTComponent) 106 | - [Testbench\TDoctrine](https://github.com/mrtnzlml/testbench/wiki/Testbench%5CTDoctrine) 107 | - [Testbench\TNetteDatabase](https://github.com/mrtnzlml/testbench/wiki/Testbench%5CTNetteDatabase) 108 | - [Testbench\TPresenter](https://github.com/mrtnzlml/testbench/wiki/Testbench%5CTPresenter) 109 | -------------------------------------------------------------------------------- /src/Bootstrap.php: -------------------------------------------------------------------------------- 1 | 0) { //\Tester\Environment::setup already called 24 | \Tester\Environment::setup(); 25 | } 26 | date_default_timezone_set('Europe/Prague'); 27 | 28 | if (class_exists('Tracy\Debugger')) { 29 | \Tracy\Debugger::$logDirectory = self::$tempDir; 30 | } 31 | 32 | $_ENV = $_GET = $_POST = $_FILES = []; 33 | 34 | $_SERVER['HTTP_USER_AGENT'] = 'Awesome Browser'; 35 | $_SERVER['REMOTE_ADDR'] = '11.22.33.44'; 36 | $_SERVER['HTTP_HOST'] = $_SERVER['SERVER_NAME'] = 'test.bench'; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/ContainerFactory.php: -------------------------------------------------------------------------------- 1 | addParameters($config); 27 | 28 | $configurator->onCompile[] = function (\Nette\Configurator $configurator, \Nette\DI\Compiler $compiler) use ($config) { 29 | $compiler->addConfig($config); 30 | $compiler->addExtension('testbench', new \Testbench\TestbenchExtension); 31 | self::registerAdditionalExtension($compiler, 'fakeSession', new \Kdyby\FakeSession\DI\FakeSessionExtension); 32 | if (class_exists('Kdyby\Console\DI\ConsoleExtension')) { 33 | self::registerAdditionalExtension($compiler, 'console', new \Kdyby\Console\DI\ConsoleExtension); 34 | } 35 | }; 36 | 37 | $configurator->setTempDirectory(\Testbench\Bootstrap::$tempDir); // shared container for performance purposes 38 | $configurator->setDebugMode(FALSE); 39 | 40 | if (is_callable(\Testbench\Bootstrap::$onBeforeContainerCreate)) { 41 | call_user_func_array(\Testbench\Bootstrap::$onBeforeContainerCreate, [$configurator]); 42 | } 43 | 44 | self::$container = $configurator->createContainer(); 45 | } 46 | return self::$container; 47 | } 48 | 49 | /** 50 | * Register extension if not registered by user. 51 | */ 52 | private static function registerAdditionalExtension(\Nette\DI\Compiler $compiler, $name, $newExtension) 53 | { 54 | $extensions = []; 55 | $config = $compiler->getConfig(); 56 | foreach (isset($config['extensions']) ? $config['extensions'] : [] as $extension) { 57 | if (is_string($extension)) { 58 | $extensions[] = $extension; 59 | } elseif ($extension instanceof \Nette\DI\Statement) { 60 | $extensions[] = $extension->getEntity(); 61 | } 62 | } 63 | if (!in_array(get_class($newExtension), $extensions)) { 64 | $compiler->addExtension($name, $newExtension); 65 | } 66 | } 67 | 68 | final public function __clone() 69 | { 70 | throw new \Exception('Clone is not allowed'); 71 | } 72 | 73 | final public function __wakeup() 74 | { 75 | throw new \Exception('Unserialization is not allowed'); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/DatabasesRegistry.php: -------------------------------------------------------------------------------- 1 | dataFile = 'nette.safe://' . \Testbench\Bootstrap::$tempDir . '/../databases.testbench'; 13 | } 14 | 15 | /** 16 | * @return TRUE if registration successful or FALSE if database record already exists 17 | */ 18 | public function registerDatabase($databaseName) 19 | { 20 | if (file_exists($this->dataFile)) { 21 | $data = file_get_contents($this->dataFile); 22 | } else { 23 | $data = ''; 24 | } 25 | 26 | if (!preg_match('~' . $databaseName . '~', $data)) { //database doesn't exist in log file 27 | $handle = fopen($this->dataFile, 'a+'); 28 | fwrite($handle, $databaseName . "\n"); 29 | fclose($handle); 30 | 31 | return TRUE; 32 | } else { //database already exists in log file 33 | return FALSE; 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/Mocks/ApplicationRequestMock.php: -------------------------------------------------------------------------------- 1 | onConnect($this); 24 | } 25 | } 26 | 27 | public function __construct( 28 | array $params, 29 | DBAL\Driver $driver, 30 | DBAL\Configuration $config = NULL, 31 | Common\EventManager $eventManager = NULL 32 | ) { 33 | $container = \Testbench\ContainerFactory::create(FALSE); 34 | $this->onConnect[] = function (DoctrineConnectionMock $connection) use ($container) { 35 | if ($this->__testbench_databaseName !== NULL) { //already initialized (needed for pgsql) 36 | return; 37 | } 38 | try { 39 | $config = $container->parameters['testbench']; 40 | if ($config['shareDatabase'] === TRUE) { 41 | $registry = new \Testbench\DatabasesRegistry; 42 | $dbName = $container->parameters['testbench']['dbprefix'] . getenv(\Tester\Environment::THREAD); 43 | if ($registry->registerDatabase($dbName)) { 44 | $this->__testbench_database_setup($connection, $container, TRUE); 45 | } else { 46 | $this->__testbench_databaseName = $dbName; 47 | $this->__testbench_database_change($connection, $container); 48 | } 49 | } else { // always create new test database 50 | $this->__testbench_database_setup($connection, $container); 51 | } 52 | } catch (\Exception $e) { 53 | \Tester\Assert::fail($e->getMessage()); 54 | } 55 | }; 56 | parent::__construct($params, $driver, $config, $eventManager); 57 | } 58 | 59 | /** @internal */ 60 | public function __testbench_database_setup($connection, \Nette\DI\Container $container, $persistent = FALSE) 61 | { 62 | $config = $container->parameters['testbench']; 63 | $this->__testbench_databaseName = $config['dbprefix'] . getenv(\Tester\Environment::THREAD); 64 | 65 | $this->__testbench_database_drop($connection, $container); 66 | $this->__testbench_database_create($connection, $container); 67 | 68 | foreach ($config['sqls'] as $file) { 69 | \Kdyby\Doctrine\Dbal\BatchImport\Helpers::loadFromFile($connection, $file); 70 | } 71 | 72 | if ($config['migrations'] === TRUE) { 73 | if (class_exists(\Zenify\DoctrineMigrations\Configuration\Configuration::class)) { 74 | /** @var \Zenify\DoctrineMigrations\Configuration\Configuration $migrationsConfig */ 75 | $migrationsConfig = $container->getByType(\Zenify\DoctrineMigrations\Configuration\Configuration::class); 76 | $migrationsConfig->__construct($container, $connection); 77 | $migrationsConfig->registerMigrationsFromDirectory($migrationsConfig->getMigrationsDirectory()); 78 | $migration = new \Doctrine\DBAL\Migrations\Migration($migrationsConfig); 79 | $migration->migrate($migrationsConfig->getLatestVersion()); 80 | } 81 | } 82 | 83 | if ($persistent === FALSE) { 84 | register_shutdown_function(function () use ($connection, $container) { 85 | $this->__testbench_database_drop($connection, $container); 86 | }); 87 | } 88 | } 89 | 90 | /** 91 | * @internal 92 | * 93 | * @param $connection \Kdyby\Doctrine\Connection 94 | */ 95 | public function __testbench_database_create($connection, \Nette\DI\Container $container) 96 | { 97 | $connection->exec("CREATE DATABASE {$this->__testbench_databaseName}"); 98 | $this->__testbench_database_change($connection, $container); 99 | } 100 | 101 | /** 102 | * @internal 103 | * 104 | * @param $connection \Kdyby\Doctrine\Connection 105 | */ 106 | public function __testbench_database_change($connection, \Nette\DI\Container $container) 107 | { 108 | if ($connection->getDatabasePlatform() instanceof MySqlPlatform) { 109 | $connection->exec("USE {$this->__testbench_databaseName}"); 110 | } else { 111 | $this->__testbench_database_connect($connection, $container, $this->__testbench_databaseName); 112 | } 113 | } 114 | 115 | /** 116 | * @internal 117 | * 118 | * @param $connection \Kdyby\Doctrine\Connection 119 | */ 120 | public function __testbench_database_drop($connection, \Nette\DI\Container $container) 121 | { 122 | if (!$connection->getDatabasePlatform() instanceof MySqlPlatform) { 123 | $this->__testbench_database_connect($connection, $container); 124 | } 125 | $connection->exec("DROP DATABASE IF EXISTS {$this->__testbench_databaseName}"); 126 | } 127 | 128 | /** 129 | * @internal 130 | * 131 | * @param $connection \Kdyby\Doctrine\Connection 132 | */ 133 | public function __testbench_database_connect($connection, \Nette\DI\Container $container, $databaseName = NULL) 134 | { 135 | //connect to an existing database other than $this->_databaseName 136 | if ($databaseName === NULL) { 137 | $dbname = $container->parameters['testbench']['dbname']; 138 | if ($dbname) { 139 | $databaseName = $dbname; 140 | } elseif ($connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { 141 | $databaseName = 'postgres'; 142 | } else { 143 | throw new \LogicException('You should setup existing database name using testbench:dbname option.'); 144 | } 145 | } 146 | 147 | $connection->close(); 148 | $connection->__construct( 149 | ['dbname' => $databaseName] + $connection->getParams(), 150 | $connection->getDriver(), 151 | $connection->getConfiguration(), 152 | $connection->getEventManager() 153 | ); 154 | $connection->connect(); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/Mocks/HttpRequestMock.php: -------------------------------------------------------------------------------- 1 | setQuery($query); 25 | } 26 | parent::__construct( 27 | $url, 28 | NULL, //deprecated 29 | $post, 30 | $files, 31 | $cookies, 32 | $headers, 33 | $method, 34 | $remoteAddress, 35 | $remoteHost, 36 | $rawBodyCallback 37 | ); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Mocks/NetteDatabaseConnectionMock.php: -------------------------------------------------------------------------------- 1 | onConnect[] = function (NetteDatabaseConnectionMock $connection) use ($container) { 20 | if ($this->__testbench_databaseName !== NULL) { //already initialized (needed for pgsql) 21 | return; 22 | } 23 | try { 24 | $config = $container->parameters['testbench']; 25 | if ($config['shareDatabase'] === TRUE) { 26 | $registry = new \Testbench\DatabasesRegistry; 27 | $dbName = $container->parameters['testbench']['dbprefix'] . getenv(\Tester\Environment::THREAD); 28 | if ($registry->registerDatabase($dbName)) { 29 | $this->__testbench_database_setup($connection, $container, TRUE); 30 | } else { 31 | $this->__testbench_databaseName = $dbName; 32 | $this->__testbench_database_change($connection, $container); 33 | } 34 | } else { // always create new test database 35 | $this->__testbench_database_setup($connection, $container); 36 | } 37 | } catch (\Exception $e) { 38 | \Tester\Assert::fail($e->getMessage()); 39 | } 40 | }; 41 | parent::__construct($dsn, $user, $password, $options); 42 | } 43 | 44 | /** @internal */ 45 | public function __testbench_database_setup($connection, \Nette\DI\Container $container, $persistent = FALSE) 46 | { 47 | $config = $container->parameters['testbench']; 48 | $this->__testbench_databaseName = $config['dbprefix'] . getenv(\Tester\Environment::THREAD); 49 | 50 | $this->__testbench_database_drop($connection, $container); 51 | $this->__testbench_database_create($connection, $container); 52 | 53 | foreach ($config['sqls'] as $file) { 54 | \Nette\Database\Helpers::loadFromFile($connection, $file); 55 | } 56 | 57 | if ($persistent === FALSE) { 58 | register_shutdown_function(function () use ($connection, $container) { 59 | $this->__testbench_database_drop($connection, $container); 60 | }); 61 | } 62 | } 63 | 64 | /** 65 | * @internal 66 | * 67 | * @param $connection \Nette\Database\Connection 68 | */ 69 | public function __testbench_database_create($connection, \Nette\DI\Container $container) 70 | { 71 | $connection->query("CREATE DATABASE {$this->__testbench_databaseName}"); 72 | $this->__testbench_database_change($connection, $container); 73 | } 74 | 75 | /** 76 | * @internal 77 | * 78 | * @param $connection \Nette\Database\Connection 79 | */ 80 | public function __testbench_database_change($connection, \Nette\DI\Container $container) 81 | { 82 | if ($connection->getSupplementalDriver() instanceof MySqlDriver) { 83 | $connection->query("USE {$this->__testbench_databaseName}"); 84 | } else { 85 | $this->__testbench_database_connect($connection, $container, $this->__testbench_databaseName); 86 | } 87 | } 88 | 89 | /** 90 | * @internal 91 | * 92 | * @param $connection \Nette\Database\Connection 93 | */ 94 | public function __testbench_database_drop($connection, \Nette\DI\Container $container) 95 | { 96 | if (!$connection->getSupplementalDriver() instanceof MySqlDriver) { 97 | $this->__testbench_database_connect($connection, $container); 98 | } 99 | $connection->query("DROP DATABASE IF EXISTS {$this->__testbench_databaseName}"); 100 | } 101 | 102 | /** 103 | * @internal 104 | * 105 | * @param $connection \Nette\Database\Connection 106 | */ 107 | public function __testbench_database_connect($connection, \Nette\DI\Container $container, $databaseName = NULL) 108 | { 109 | //connect to an existing database other than $this->_databaseName 110 | if ($databaseName === NULL) { 111 | $dbname = $container->parameters['testbench']['dbname']; 112 | if ($dbname) { 113 | $databaseName = $dbname; 114 | } elseif ($connection->getSupplementalDriver() instanceof PgSqlDriver) { 115 | $databaseName = 'postgres'; 116 | } else { 117 | throw new \LogicException('You should setup existing database name using testbench:dbname option.'); 118 | } 119 | } 120 | 121 | $dsn = preg_replace('~dbname=[a-z0-9_-]+~i', "dbname=$databaseName", $connection->getDsn()); 122 | 123 | $dbr = (new \Nette\Reflection\ClassType($connection))->getParentClass(); //:-( 124 | $params = $dbr->getProperty('params'); 125 | $params->setAccessible(TRUE); 126 | $params = $params->getValue($connection); 127 | 128 | $options = $dbr->getProperty('options'); 129 | $options->setAccessible(TRUE); 130 | $options = $options->getValue($connection); 131 | 132 | $connection->disconnect(); 133 | $connection->__construct($dsn, $params[1], $params[2], $options); 134 | $connection->connect(); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/Mocks/PresenterMock.php: -------------------------------------------------------------------------------- 1 | autoCanonicalize = FALSE; 17 | return parent::run($request); 18 | } 19 | 20 | public function startup() 21 | { 22 | if ($this->getParameter('__terminate') === TRUE) { 23 | $this->terminate(); 24 | } 25 | parent::startup(); 26 | $this->onStartup($this); 27 | } 28 | 29 | public function afterRender() 30 | { 31 | $this->terminate(); 32 | } 33 | 34 | public function isAjax() 35 | { 36 | return FALSE; 37 | } 38 | 39 | public function link($destination, $args = []) 40 | { 41 | if (!is_array($args)) { 42 | $args = array_slice(func_get_args(), 1); 43 | } 44 | $params = urldecode(http_build_query($args, NULL, ', ')); 45 | $params = $params ? "($params)" : ''; 46 | return "plink|$destination$params"; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/Providers/IDatabaseProvider.php: -------------------------------------------------------------------------------- 1 | getNextValue(); 26 | } else { 27 | unset($parameters[$arg]); 28 | $parameters[$arg][] = $previousValue; 29 | $parameters[$arg][] = $args->getNextValue(); 30 | } 31 | } else { 32 | $parameters[$arg] = $args->getNextValue(); 33 | } 34 | $args->next(); 35 | } else { //environment variables or $pathToTests 36 | if (preg_match('~[a-z0-9_]+=[a-z0-9_]+~i', $arg)) { //linux environment variable 37 | $environmentVariables[] = $arg; 38 | } else { 39 | $pathToTests = $arg; 40 | } 41 | } 42 | } 43 | 44 | //Specify PHP interpreter to run 45 | if (!array_key_exists('-p', $parameters)) { 46 | $parameters['-p'] = 'php'; 47 | } 48 | 49 | //Show information about skipped tests 50 | if (!array_key_exists('-s', $parameters)) { 51 | $parameters['-s'] = TRUE; 52 | } 53 | 54 | //Look for php.ini file 55 | $os = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? 'win' : 'unix'; 56 | $iniFile = $testsDir . "/php-$os.ini"; 57 | if (!array_key_exists('-c', $parameters)) { 58 | if (is_file($iniFile)) { 59 | $parameters['-c'] = $iniFile; 60 | } else { 61 | $parameters['-C'] = TRUE; 62 | } 63 | } 64 | 65 | //Purge temp directory 66 | if (isset($parameters['--temp'])) { 67 | $dir = $parameters['--temp']; 68 | } else { 69 | $dir = $testsDir . '/_temp'; 70 | } 71 | unset($parameters['--temp']); 72 | if (!is_dir($dir)) { 73 | mkdir($dir); 74 | } 75 | $rdi = new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS); 76 | $rii = new \RecursiveIteratorIterator($rdi, \RecursiveIteratorIterator::CHILD_FIRST); 77 | foreach ($rii as $entry) { 78 | if ($entry->isDir()) { 79 | rmdir($entry); 80 | } else { 81 | unlink($entry); 82 | } 83 | } 84 | 85 | //Propagate bootstrap file into ENV 86 | if (isset($parameters['--bootstrap'])) { 87 | $bootstrapFile = realpath($parameters['--bootstrap']); 88 | putenv('BOOTSTRAP=' . $bootstrapFile); 89 | unset($parameters['--bootstrap']); 90 | } 91 | 92 | if ($pathToTests === NULL) { 93 | $pathToTests = $testsDir; 94 | } 95 | 96 | $args = $environmentVariables; 97 | foreach ($parameters as $key => $value) { //return to the Tester format 98 | if ($value === TRUE) { //singles 99 | $args[] = $key; 100 | continue; 101 | } 102 | if (is_array($value)) { 103 | foreach ($value as $v) { 104 | $args[] = $key; 105 | $args[] = $v; 106 | } 107 | } else { 108 | $args[] = $key; 109 | $args[] = $value; 110 | } 111 | } 112 | $args[] = $pathToTests; 113 | return $args; 114 | } 115 | 116 | public function findVendorDirectory() 117 | { 118 | $recursionLimit = 10; 119 | $findVendor = function ($dirName = 'vendor/bin', $dir = __DIR__) use (&$findVendor, &$recursionLimit) { 120 | if (!$recursionLimit--) { 121 | throw new \Exception('Cannot find vendor directory.'); 122 | } 123 | $found = $dir . "/$dirName"; 124 | if (is_dir($found) || is_file($found)) { 125 | return dirname($found); 126 | } 127 | return $findVendor($dirName, dirname($dir)); 128 | }; 129 | return $findVendor(); 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/Testbench.php: -------------------------------------------------------------------------------- 1 | NULL, // custom initial test database name (should not be needed) 10 | 'dbprefix' => '_testbench_', // database prefix for created tests databases 11 | 'migrations' => FALSE, // set TRUE if you want to use Doctrine migrations 12 | 'shareDatabase' => FALSE, // should Testbench always create new databases (FALSE) or use shared databases (TRUE) 13 | 'sqls' => [], // sqls you want to import during new test database creation 14 | 'url' => 'http://test.bench/', // fake URL for HTTP request mock 15 | ]; 16 | 17 | public function loadConfiguration() 18 | { 19 | $builder = $this->compiler->getContainerBuilder(); 20 | $builder->parameters[$this->name] = $this->validateConfig($this->defaults); 21 | 22 | $this->prepareDoctrine(); 23 | $this->prepareNetteDatabase($builder); 24 | //TODO: $builder->addDefinition($this->prefix('applicationRequestMock'))->setClass('Testbench\ApplicationRequestMock'); 25 | } 26 | 27 | public function beforeCompile() 28 | { 29 | $builder = $this->compiler->getContainerBuilder(); 30 | 31 | if ($builder->hasDefinition($this->prefix('presenterMock'))) { //custom testbench.presenterMock implementation 32 | //workaround because of Application\UI\Presenter descendant (presenterMock needs to be reattached) 33 | $mockReplacement = $builder->getDefinition($this->prefix('presenterMock'))->getClass(); 34 | $builder->removeDefinition($this->prefix('presenterMock')); 35 | $builder->addDefinition($this->prefix('presenterMock'))->setClass($mockReplacement); 36 | } else { 37 | $builder->addDefinition($this->prefix('presenterMock'))->setClass('Testbench\Mocks\PresenterMock'); 38 | } 39 | } 40 | 41 | /** 42 | * 'wrapperClass' is not a service! 43 | */ 44 | private function prepareDoctrine() 45 | { 46 | $doctrineConnectionSectionKeys = ['dbname' => NULL, 'driver' => NULL, 'connection' => NULL]; 47 | /** @var \Nette\DI\CompilerExtension $extension */ 48 | foreach ($this->compiler->getExtensions('Kdyby\Doctrine\DI\OrmExtension') as $extension) { 49 | if (array_intersect_key($extension->config, $doctrineConnectionSectionKeys)) { 50 | $extension->config['wrapperClass'] = 'Testbench\Mocks\DoctrineConnectionMock'; 51 | } else { 52 | foreach ($extension->config as $sectionName => $sectionConfig) { 53 | if (is_array($sectionConfig) && array_intersect_key($sectionConfig, $doctrineConnectionSectionKeys)) { 54 | $extension->config[$sectionName]['wrapperClass'] = 'Testbench\Mocks\DoctrineConnectionMock'; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | private function prepareNetteDatabase(\Nette\DI\ContainerBuilder $builder) 62 | { 63 | $ndbConnectionSectionKeys = ['dsn' => NULL, 'user' => NULL, 'password' => NULL]; 64 | /** @var \Nette\DI\CompilerExtension $extension */ 65 | foreach ($this->compiler->getExtensions('Nette\Bridges\DatabaseDI\DatabaseExtension') as $extension) { 66 | if (array_intersect_key($extension->config, $ndbConnectionSectionKeys)) { 67 | $extensionConfig = $extension->config; 68 | $definitionName = $extension->name . '.default.connection'; 69 | $builder->getDefinition($definitionName) 70 | ->setClass('Testbench\Mocks\NetteDatabaseConnectionMock', [ 71 | $extensionConfig['dsn'], 72 | $extensionConfig['user'], 73 | $extensionConfig['password'], 74 | isset($extensionConfig['options']) ? ($extensionConfig['options'] + ['lazy' => TRUE]) : [], 75 | ]); 76 | } else { 77 | foreach ($extension->config as $sectionName => $sectionConfig) { 78 | $definitionName = $extension->name . '.' . $sectionName . '.connection'; 79 | $builder->getDefinition($definitionName) 80 | ->setClass('Testbench\Mocks\NetteDatabaseConnectionMock', [ 81 | $sectionConfig['dsn'], 82 | $sectionConfig['user'], 83 | $sectionConfig['password'], 84 | isset($sectionConfig['options']) ? ($sectionConfig['options'] + ['lazy' => TRUE]) : [], 85 | ]); 86 | } 87 | } 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/Traits/TCompiledContainer.php: -------------------------------------------------------------------------------- 1 | getContainer()->getByType($class); 17 | } 18 | 19 | protected function refreshContainer($config = []) 20 | { 21 | return \Testbench\ContainerFactory::create(TRUE, $config); 22 | } 23 | 24 | protected function changeRunLevel($testSpeed = \Testbench::FINE) 25 | { 26 | if ((int)getenv('RUNLEVEL') < $testSpeed) { 27 | \Tester\Environment::skip( 28 | "Required runlevel '$testSpeed' but current runlevel is '" . (int)getenv('RUNLEVEL') . "' (higher runlevel means slower tests)\n" . 29 | "You can run this test with environment variable: 'RUNLEVEL=$testSpeed vendor/bin/run-tests ...'\n" 30 | ); 31 | } 32 | } 33 | 34 | protected function markTestAsSlow($really = TRUE) 35 | { 36 | $this->changeRunLevel($really ? \Testbench::FINE : \Testbench::QUICK); 37 | } 38 | 39 | protected function markTestAsVerySlow($really = TRUE) 40 | { 41 | $this->changeRunLevel($really ? \Testbench::SLOW : \Testbench::QUICK); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/Traits/TComponent.php: -------------------------------------------------------------------------------- 1 | getName()) { 16 | $name = $component->getReflection()->getShortName(); 17 | if (preg_match('~class@anonymous.*~', $name)) { 18 | $name = md5($name); 19 | } 20 | } 21 | } 22 | if (!$this->__testbench_presenterMock) { 23 | $container = \Testbench\ContainerFactory::create(FALSE); 24 | $this->__testbench_presenterMock = $container->getByType('Testbench\Mocks\PresenterMock'); 25 | $container->callInjects($this->__testbench_presenterMock); 26 | } 27 | $this->__testbench_presenterMock->onStartup[] = function (Mocks\PresenterMock $presenter) use ($component, $name) { 28 | try { 29 | $presenter->removeComponent($component); 30 | } catch (\Nette\InvalidArgumentException $exc) { 31 | } 32 | $presenter->addComponent($component, $name); 33 | }; 34 | $this->__testbench_presenterMock->run(new Mocks\ApplicationRequestMock); 35 | } 36 | 37 | protected function checkRenderOutput(IComponent $control, $expected, array $renderParameters = []) 38 | { 39 | if (!$control->getParent()) { 40 | $this->attachToPresenter($control); 41 | } 42 | ob_start(); 43 | $control->render(...$renderParameters); 44 | if (is_file($expected)) { 45 | \Tester\Assert::matchFile($expected, ob_get_clean()); 46 | } else { 47 | \Tester\Assert::match($expected, ob_get_clean()); 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/Traits/TDoctrine.php: -------------------------------------------------------------------------------- 1 | getByType('Doctrine\DBAL\Connection'); 16 | if (!$connection instanceof Mocks\DoctrineConnectionMock) { 17 | $serviceNames = $container->findByType('Doctrine\DBAL\Connection'); 18 | throw new \LogicException(sprintf( 19 | 'The service %s should be instance of Testbench\Mocks\DoctrineConnectionMock, to allow lazy schema initialization.', 20 | reset($serviceNames) 21 | )); 22 | } 23 | return $container->getByType('Kdyby\Doctrine\EntityManager'); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/Traits/TNetteDatabase.php: -------------------------------------------------------------------------------- 1 | getByType('Nette\Database\Connection'); 13 | if (!$connection instanceof Mocks\NetteDatabaseConnectionMock) { 14 | $serviceNames = $container->findByType('Nette\Database\Connection'); 15 | throw new \LogicException(sprintf( 16 | 'The service %s should be instance of Testbench\Mocks\NetteDatabaseConnectionMock, to allow lazy schema initialization.', 17 | reset($serviceNames) 18 | )); 19 | } 20 | /** @var \Nette\Database\Context $context */ 21 | $context = $container->getByType('Nette\Database\Context'); 22 | return $context; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/Traits/TPresenter.php: -------------------------------------------------------------------------------- 1 | removeService('httpRequest'); 37 | $headers = $this->__testbench_ajaxMode ? ['X-Requested-With' => 'XMLHttpRequest'] : []; 38 | $url = new \Nette\Http\UrlScript($container->parameters['testbench']['url']); 39 | $container->addService('httpRequest', new Mocks\HttpRequestMock($url, $params, $post, [], [], $headers)); 40 | $presenterFactory = $container->getByType('Nette\Application\IPresenterFactory'); 41 | $this->__testbench_presenter = $presenterFactory->createPresenter($presenter); 42 | $this->__testbench_presenter->autoCanonicalize = FALSE; 43 | $this->__testbench_presenter->invalidLinkMode = \Nette\Application\UI\Presenter::INVALID_LINK_EXCEPTION; 44 | 45 | $postCopy = $post; 46 | if (isset($params['do'])) { 47 | foreach ($post as $key => $field) { 48 | if (is_array($field) && array_key_exists(\Nette\Forms\Form::REQUIRED, $field)) { 49 | $post[$key] = $field[0]; 50 | } 51 | } 52 | } 53 | 54 | /** @var \Kdyby\FakeSession\Session $session */ 55 | $session = $this->__testbench_presenter->getSession(); 56 | $session->setFakeId('testbench.fakeId'); 57 | $session->getSection('Nette\Forms\Controls\CsrfProtection')->token = 'testbench.fakeToken'; 58 | $post = $post + ['_token_' => 'goVdCQ1jk0UQuVArz15RzkW6vpDU9YqTRILjE=']; //CSRF magic! ¯\_(ツ)_/¯ 59 | 60 | $request = new Mocks\ApplicationRequestMock( 61 | $presenter, 62 | $post ? 'POST' : 'GET', 63 | ['action' => $action] + $params, 64 | $post 65 | ); 66 | try { 67 | $this->__testbench_httpCode = 200; 68 | $this->__testbench_exception = NULL; 69 | $response = $this->__testbench_presenter->run($request); 70 | 71 | if (isset($params['do'])) { 72 | if (preg_match('~(.+)-submit$~', $params['do'], $matches)) { 73 | /** @var \Nette\Application\UI\Form $form */ 74 | $form = $this->__testbench_presenter->getComponent($matches[1]); 75 | foreach ($form->getControls() as $control) { 76 | if (array_key_exists($control->getName(), $postCopy)) { 77 | $subvalues = $postCopy[$control->getName()]; 78 | $rq = \Nette\Forms\Form::REQUIRED; 79 | if (is_array($subvalues) && array_key_exists($rq, $subvalues) && $subvalues[$rq]) { 80 | if ($control->isRequired() !== TRUE) { 81 | Assert::fail("field '{$control->name}' should be defined as required, but it's not"); 82 | } 83 | } 84 | } 85 | if ($control->hasErrors()) { 86 | $errors = ''; 87 | $counter = 1; 88 | foreach ($control->getErrors() as $error) { 89 | $errors .= " - $error\n"; 90 | $counter++; 91 | } 92 | Assert::fail("field '{$control->name}' returned this error(s):\n$errors"); 93 | } 94 | } 95 | foreach ($form->getErrors() as $error) { 96 | Assert::fail($error); 97 | } 98 | } 99 | } 100 | 101 | return $response; 102 | } catch (\Exception $exc) { 103 | $this->__testbench_exception = $exc; 104 | $this->__testbench_httpCode = $exc->getCode(); 105 | throw $exc; 106 | } 107 | } 108 | 109 | /** 110 | * @param string $destination fully qualified presenter name (module:module:presenter) 111 | * @param array $params provided to the presenter usually via URL 112 | * @param array $post provided to the presenter via POST 113 | * 114 | * @return \Nette\Application\Responses\TextResponse 115 | * @throws \Exception 116 | */ 117 | protected function checkAction($destination, $params = [], $post = []) 118 | { 119 | /** @var \Nette\Application\Responses\TextResponse $response */ 120 | $response = $this->check($destination, $params, $post); 121 | if (!$this->__testbench_exception) { 122 | Assert::same(200, $this->getReturnCode()); 123 | Assert::type('Nette\Application\Responses\TextResponse', $response); 124 | Assert::type('Nette\Application\UI\ITemplate', $response->getSource()); 125 | 126 | $html = (string)$response->getSource(); 127 | //DOMDocument doesn't handle HTML tags inside of script tags very well 128 | $html = preg_replace('~)<[^<]*)*<\/script>~', '', $html); //http://stackoverflow.com/a/6660315/3135248 129 | $dom = @\Tester\DomQuery::fromHtml($html); 130 | Assert::true($dom->has('html'), "missing 'html' tag"); 131 | Assert::true($dom->has('title'), "missing 'title' tag"); 132 | Assert::true($dom->has('body'), "missing 'body' tag"); 133 | } 134 | return $response; 135 | } 136 | 137 | /** 138 | * @param string $destination 139 | * @param string $signal 140 | * @param array $params 141 | * @param array $post 142 | * 143 | * @return \Nette\Application\IResponse 144 | */ 145 | protected function checkSignal($destination, $signal, $params = [], $post = []) 146 | { 147 | return $this->checkRedirect($destination, FALSE, [ 148 | 'do' => $signal, 149 | ] + $params, $post); 150 | } 151 | 152 | protected function checkAjaxSignal($destination, $signal, $params = [], $post = []) 153 | { 154 | $this->__testbench_ajaxMode = TRUE; 155 | $response = $this->check($destination, [ 156 | 'do' => $signal, 157 | ] + $params, $post); 158 | Assert::true($this->__testbench_presenter->isAjax()); 159 | if (!$this->__testbench_exception) { 160 | Assert::same(200, $this->getReturnCode()); 161 | Assert::type('Nette\Application\Responses\JsonResponse', $response); 162 | } 163 | $this->__testbench_ajaxMode = FALSE; 164 | return $response; 165 | } 166 | 167 | /** 168 | * @param string $destination fully qualified presenter name (module:module:presenter) 169 | * @param string $path 170 | * @param array $params provided to the presenter usually via URL 171 | * @param array $post provided to the presenter via POST 172 | * 173 | * @return \Nette\Application\Responses\RedirectResponse 174 | * @throws \Exception 175 | */ 176 | protected function checkRedirect($destination, $path = '/', $params = [], $post = []) 177 | { 178 | /** @var \Nette\Application\Responses\RedirectResponse $response */ 179 | $response = $this->check($destination, $params, $post); 180 | if (!$this->__testbench_exception) { 181 | Assert::same(200, $this->getReturnCode()); 182 | Assert::type('Nette\Application\Responses\RedirectResponse', $response); 183 | Assert::same(302, $response->getCode()); 184 | if ($path) { 185 | if (!\Tester\Assert::isMatching("~^https?://test\.bench{$path}(?(?=\?).+)$~", $response->getUrl())) { 186 | $path = Dumper::color('yellow') . Dumper::toLine($path) . Dumper::color('white'); 187 | $url = Dumper::color('yellow') . Dumper::toLine($response->getUrl()) . Dumper::color('white'); 188 | $originalUrl = new \Nette\Http\Url($response->getUrl()); 189 | Assert::fail( 190 | str_repeat(' ', strlen($originalUrl->getHostUrl()) - 13) // strlen('Failed: path ') = 13 191 | . "path $path doesn't match\n$url\nafter redirect" 192 | ); 193 | } 194 | } 195 | } 196 | return $response; 197 | } 198 | 199 | /** 200 | * @param string $destination fully qualified presenter name (module:module:presenter) 201 | * @param array $params provided to the presenter usually via URL 202 | * @param array $post provided to the presenter via POST 203 | * 204 | * @return \Nette\Application\Responses\JsonResponse 205 | * @throws \Exception 206 | */ 207 | protected function checkJson($destination, $params = [], $post = []) 208 | { 209 | /** @var \Nette\Application\Responses\JsonResponse $response */ 210 | $response = $this->check($destination, $params, $post); 211 | if (!$this->__testbench_exception) { 212 | Assert::same(200, $this->getReturnCode()); 213 | Assert::type('Nette\Application\Responses\JsonResponse', $response); 214 | Assert::same('application/json', $response->getContentType()); 215 | } 216 | return $response; 217 | } 218 | 219 | /** 220 | * @param string $destination fully qualified presenter name (module:module:presenter) 221 | * @param array $scheme what is expected 222 | * @param array $params provided to the presenter usually via URL 223 | * @param array $post provided to the presenter via POST 224 | */ 225 | public function checkJsonScheme($destination, array $scheme, $params = [], $post = []) 226 | { 227 | $response = $this->checkJson($destination, $params, $post); 228 | Assert::same($scheme, $response->getPayload()); 229 | } 230 | 231 | /** 232 | * @param string $destination fully qualified presenter name (module:module:presenter) 233 | * @param string $formName 234 | * @param array $post provided to the presenter via POST 235 | * @param string|boolean $path Path after redirect or FALSE if it's form without redirect 236 | * 237 | * @return \Nette\Application\Responses\RedirectResponse 238 | * @throws \Tester\AssertException 239 | */ 240 | protected function checkForm($destination, $formName, $post = [], $path = '/') 241 | { 242 | if (is_string($path)) { 243 | return $this->checkRedirect($destination, $path, [ 244 | 'do' => $formName . '-submit', 245 | ], $post); 246 | } elseif (is_bool($path)) { 247 | /** @var \Nette\Application\Responses\RedirectResponse $response */ 248 | $response = $this->check($destination, [ 249 | 'do' => $formName . '-submit', 250 | ], $post); 251 | if (!$this->__testbench_exception) { 252 | Assert::same(200, $this->getReturnCode()); 253 | Assert::type('Nette\Application\Responses\TextResponse', $response); 254 | } 255 | return $response; 256 | } else { 257 | \Tester\Assert::fail('Path should be string or boolean (probably FALSE).'); 258 | } 259 | } 260 | 261 | /** 262 | * @param string $destination fully qualified presenter name (module:module:presenter) 263 | * @param $formName 264 | * @param array $post provided to the presenter via POST 265 | * @param string|bool $path 266 | * 267 | * @return \Nette\Application\IResponse 268 | * @throws \Exception 269 | */ 270 | protected function checkAjaxForm($destination, $formName, $post = [], $path = FALSE) 271 | { 272 | if (is_string($path)) { 273 | $this->checkForm($destination, $formName, $post, $path); 274 | Assert::false($this->__testbench_presenter->isAjax()); 275 | } 276 | $this->__testbench_presenter = NULL; //FIXME: not very nice, but performance first 277 | $this->__testbench_ajaxMode = TRUE; 278 | $response = $this->check($destination, [ 279 | 'do' => $formName . '-submit', 280 | ], $post); 281 | Assert::true($this->__testbench_presenter->isAjax()); 282 | if (!$this->__testbench_exception) { 283 | Assert::same(200, $this->getReturnCode()); 284 | Assert::type('Nette\Application\Responses\JsonResponse', $response); 285 | } 286 | $this->__testbench_presenter = NULL; 287 | $this->__testbench_ajaxMode = FALSE; 288 | return $response; 289 | } 290 | 291 | /** 292 | * @param string $destination fully qualified presenter name (module:module:presenter) 293 | * @param array $params provided to the presenter usually via URL 294 | * @param array $post provided to the presenter via POST 295 | * 296 | * @return \Nette\Application\Responses\TextResponse 297 | * @throws \Exception 298 | */ 299 | protected function checkRss($destination, $params = [], $post = []) 300 | { 301 | /** @var \Nette\Application\Responses\TextResponse $response */ 302 | $response = $this->check($destination, $params, $post); 303 | if (!$this->__testbench_exception) { 304 | Assert::same(200, $this->getReturnCode()); 305 | Assert::type('Nette\Application\Responses\TextResponse', $response); 306 | Assert::type('Nette\Application\UI\ITemplate', $response->getSource()); 307 | 308 | $dom = \Tester\DomQuery::fromXml($response->getSource()); 309 | Assert::true($dom->has('rss'), "missing 'rss' element"); 310 | Assert::true($dom->has('channel'), "missing 'channel' element"); 311 | Assert::true($dom->has('title'), "missing 'title' element"); 312 | Assert::true($dom->has('link'), "missing 'link' element"); 313 | Assert::true($dom->has('item'), "missing 'item' element"); 314 | } 315 | return $response; 316 | } 317 | 318 | /** 319 | * @param string $destination fully qualified presenter name (module:module:presenter) 320 | * @param array $params provided to the presenter usually via URL 321 | * @param array $post provided to the presenter via POST 322 | * 323 | * @return \Nette\Application\Responses\TextResponse 324 | * @throws \Exception 325 | */ 326 | protected function checkSitemap($destination, $params = [], $post = []) 327 | { 328 | /** @var \Nette\Application\Responses\TextResponse $response */ 329 | $response = $this->check($destination, $params, $post); 330 | if (!$this->__testbench_exception) { 331 | Assert::same(200, $this->getReturnCode()); 332 | Assert::type('Nette\Application\Responses\TextResponse', $response); 333 | Assert::type('Nette\Application\UI\ITemplate', $response->getSource()); 334 | 335 | $xml = \Tester\DomQuery::fromXml($response->getSource()); 336 | Assert::same('urlset', $xml->getName(), 'root element is'); 337 | $url = $xml->children(); 338 | Assert::same('url', $url->getName(), "child of 'urlset'"); 339 | Assert::same('loc', $url->children()->getName(), "child of 'url'"); 340 | } 341 | return $response; 342 | } 343 | 344 | /** 345 | * @param \Nette\Security\IIdentity|integer $id 346 | * @param array|null $roles 347 | * @param array|null $data 348 | * 349 | * @return \Nette\Security\User 350 | */ 351 | protected function logIn($id = 1, $roles = NULL, $data = NULL) 352 | { 353 | if ($id instanceof \Nette\Security\IIdentity) { 354 | $identity = $id; 355 | } else { 356 | $identity = new \Nette\Security\Identity($id, $roles, $data); 357 | } 358 | /** @var \Nette\Security\User $user */ 359 | $user = \Testbench\ContainerFactory::create(FALSE)->getByType('Nette\Security\User'); 360 | $user->login($identity); 361 | return $user; 362 | } 363 | 364 | /** 365 | * @return \Nette\Security\User 366 | */ 367 | protected function logOut() 368 | { 369 | /** @var \Nette\Security\User $user */ 370 | $user = \Testbench\ContainerFactory::create(FALSE)->getByType('Nette\Security\User'); 371 | $user->logout(); 372 | return $user; 373 | } 374 | 375 | /** 376 | * @return bool 377 | */ 378 | protected function isUserLoggedIn() 379 | { 380 | /** @var \Nette\Security\User $user */ 381 | $user = \Testbench\ContainerFactory::create(FALSE)->getByType('Nette\Security\User'); 382 | return $user->isLoggedIn(); 383 | } 384 | 385 | /** 386 | * @return \Nette\Application\UI\Presenter 387 | */ 388 | protected function getPresenter() 389 | { 390 | return $this->__testbench_presenter; 391 | } 392 | 393 | /** 394 | * @return integer 395 | */ 396 | protected function getReturnCode() 397 | { 398 | return $this->__testbench_httpCode; 399 | } 400 | 401 | /** 402 | * @return \Exception 403 | */ 404 | protected function getException() 405 | { 406 | return $this->__testbench_exception; 407 | } 408 | 409 | } 410 | -------------------------------------------------------------------------------- /src/run-tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | findVendorDirectory(); 7 | $testsDir = dirname($vendorDir) . '/tests'; 8 | 9 | require $vendorDir . '/autoload.php'; 10 | $script = array_shift($_SERVER['argv']); 11 | $_SERVER['argv'] = $runner->prepareArguments($_SERVER['argv'], $testsDir); 12 | array_unshift($_SERVER['argv'], $script); 13 | 14 | echo "TESTBENCH edition\n"; 15 | require $vendorDir . '/nette/tester/src/tester.php'; 16 | --------------------------------------------------------------------------------