init
This commit is contained in:
58
vendor/codeception/base/src/Codeception/Actor.php
vendored
Normal file
58
vendor/codeception/base/src/Codeception/Actor.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace Codeception;
|
||||
|
||||
use Codeception\Lib\Actor\Shared\Comment;
|
||||
use Codeception\Lib\Actor\Shared\Friend;
|
||||
use Codeception\Step\Executor;
|
||||
|
||||
abstract class Actor
|
||||
{
|
||||
use Comment;
|
||||
use Friend;
|
||||
|
||||
/**
|
||||
* @var \Codeception\Scenario
|
||||
*/
|
||||
protected $scenario;
|
||||
|
||||
public function __construct(Scenario $scenario)
|
||||
{
|
||||
$this->scenario = $scenario;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Codeception\Scenario
|
||||
*/
|
||||
protected function getScenario()
|
||||
{
|
||||
return $this->scenario;
|
||||
}
|
||||
|
||||
public function wantToTest($text)
|
||||
{
|
||||
$this->wantTo('test ' . $text);
|
||||
}
|
||||
|
||||
public function wantTo($text)
|
||||
{
|
||||
$this->scenario->setFeature(mb_strtolower($text, 'utf-8'));
|
||||
}
|
||||
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
$class = get_class($this);
|
||||
throw new \RuntimeException("Call to undefined method $class::$method");
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy-execution given anonymous function
|
||||
* @param $callable \Closure
|
||||
* @return $this
|
||||
*/
|
||||
public function execute($callable)
|
||||
{
|
||||
$this->scenario->addStep(new Executor($callable, []));
|
||||
$callable();
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
183
vendor/codeception/base/src/Codeception/Application.php
vendored
Normal file
183
vendor/codeception/base/src/Codeception/Application.php
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace Codeception;
|
||||
|
||||
use Codeception\Exception\ConfigurationException;
|
||||
use Symfony\Component\Console\Application as BaseApplication;
|
||||
use Symfony\Component\Console\Input\InputDefinition;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class Application extends BaseApplication
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ArgvInput
|
||||
*/
|
||||
protected $coreArguments = null;
|
||||
|
||||
/**
|
||||
* Register commands from config file
|
||||
*
|
||||
* extensions:
|
||||
* commands:
|
||||
* - Project\Command\MyCustomCommand
|
||||
*
|
||||
*/
|
||||
public function registerCustomCommands()
|
||||
{
|
||||
try {
|
||||
$this->readCustomCommandsFromConfig();
|
||||
} catch (ConfigurationException $e) {
|
||||
if ($e->getCode() === 404) {
|
||||
return;
|
||||
}
|
||||
$this->renderException($e, new ConsoleOutput());
|
||||
exit(1);
|
||||
} catch (\Exception $e) {
|
||||
$this->renderException($e, new ConsoleOutput());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search custom commands and register them.
|
||||
*
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
protected function readCustomCommandsFromConfig()
|
||||
{
|
||||
$this->getCoreArguments(); // Maybe load outside configfile
|
||||
|
||||
$config = Configuration::config();
|
||||
|
||||
if (empty($config['extensions']['commands'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($config['extensions']['commands'] as $commandClass) {
|
||||
$commandName = $this->getCustomCommandName($commandClass);
|
||||
$this->add(new $commandClass($commandName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and get the name of the command
|
||||
*
|
||||
* @param CustomCommandInterface $commandClass
|
||||
*
|
||||
* @throws ConfigurationException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getCustomCommandName($commandClass)
|
||||
{
|
||||
if (!class_exists($commandClass)) {
|
||||
throw new ConfigurationException("Extension: Command class $commandClass not found");
|
||||
}
|
||||
|
||||
$interfaces = class_implements($commandClass);
|
||||
|
||||
if (!in_array('Codeception\CustomCommandInterface', $interfaces)) {
|
||||
throw new ConfigurationException("Extension: Command {$commandClass} must implement " .
|
||||
"the interface `Codeception\\CustomCommandInterface`");
|
||||
}
|
||||
|
||||
return $commandClass::getCommandName();
|
||||
}
|
||||
|
||||
/**
|
||||
* To cache Class ArgvInput
|
||||
*
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function run(InputInterface $input = null, OutputInterface $output = null)
|
||||
{
|
||||
if ($input === null) {
|
||||
$input = $this->getCoreArguments();
|
||||
}
|
||||
|
||||
if (!ini_get('register_argc_argv') && empty($_SERVER['argv'])) {
|
||||
//register_argc_argv is always off on HHVM, but it has no effect
|
||||
throw new ConfigurationException('register_argc_argv must be set to On for running Codeception');
|
||||
}
|
||||
|
||||
return parent::run($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add global a --config option.
|
||||
*
|
||||
* @return InputDefinition
|
||||
*/
|
||||
protected function getDefaultInputDefinition()
|
||||
{
|
||||
$inputDefinition = parent::getDefaultInputDefinition();
|
||||
$inputDefinition->addOption(
|
||||
new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config')
|
||||
);
|
||||
return $inputDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for --config Option and if found will be loaded
|
||||
*
|
||||
* example:
|
||||
* -c file.yml|dir
|
||||
* -cfile.yml|dir
|
||||
* --config file.yml|dir
|
||||
* --config=file.yml|dir
|
||||
*
|
||||
* @return ArgvInput
|
||||
*/
|
||||
protected function getCoreArguments()
|
||||
{
|
||||
if ($this->coreArguments !== null) {
|
||||
return $this->coreArguments;
|
||||
}
|
||||
|
||||
$argvWithoutConfig = [];
|
||||
if (isset($_SERVER['argv'])) {
|
||||
$argv = $_SERVER['argv'];
|
||||
|
||||
for ($i = 0; $i < count($argv); $i++) {
|
||||
if (preg_match('/^(?:-([^c-]*)?c|--config(?:=|$))(.*)$/', $argv[$i], $match)) {
|
||||
if (!empty($match[2])) { //same index
|
||||
$this->preloadConfiguration($match[2]);
|
||||
} elseif (isset($argv[$i + 1])) { //next index
|
||||
$this->preloadConfiguration($argv[++$i]);
|
||||
}
|
||||
if (!empty($match[1])) {
|
||||
$argvWithoutConfig[] = "-" . $match[1]; //rest commands
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$argvWithoutConfig[] = $argv[$i];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->coreArguments = new ArgvInput($argvWithoutConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre load Configuration, the config option is use.
|
||||
*
|
||||
* @param string $configFile Path to Configuration
|
||||
*
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
protected function preloadConfiguration($configFile)
|
||||
{
|
||||
try {
|
||||
Configuration::config($configFile);
|
||||
} catch (ConfigurationException $e) {
|
||||
if ($e->getCode() == 404) {
|
||||
throw new ConfigurationException("Your configuration file `{$configFile}` could not be found.", 405);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
236
vendor/codeception/base/src/Codeception/Codecept.php
vendored
Normal file
236
vendor/codeception/base/src/Codeception/Codecept.php
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
namespace Codeception;
|
||||
|
||||
use Codeception\Exception\ConfigurationException;
|
||||
use Codeception\Subscriber\ExtensionLoader;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
class Codecept
|
||||
{
|
||||
const VERSION = "2.4.5";
|
||||
|
||||
/**
|
||||
* @var \Codeception\PHPUnit\Runner
|
||||
*/
|
||||
protected $runner;
|
||||
/**
|
||||
* @var \PHPUnit\Framework\TestResult
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
/**
|
||||
* @var \Codeception\CodeCoverage
|
||||
*/
|
||||
protected $coverage;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcher
|
||||
*/
|
||||
protected $dispatcher;
|
||||
|
||||
/**
|
||||
* @var ExtensionLoader
|
||||
*/
|
||||
protected $extensionLoader;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'silent' => false,
|
||||
'debug' => false,
|
||||
'steps' => false,
|
||||
'html' => false,
|
||||
'xml' => false,
|
||||
'json' => false,
|
||||
'tap' => false,
|
||||
'report' => false,
|
||||
'colors' => false,
|
||||
'coverage' => false,
|
||||
'coverage-xml' => false,
|
||||
'coverage-html' => false,
|
||||
'coverage-text' => false,
|
||||
'coverage-crap4j' => false,
|
||||
'coverage-phpunit'=> false,
|
||||
'groups' => null,
|
||||
'excludeGroups' => null,
|
||||
'filter' => null,
|
||||
'env' => null,
|
||||
'fail-fast' => false,
|
||||
'ansi' => true,
|
||||
'verbosity' => 1,
|
||||
'interactive' => true,
|
||||
'no-rebuild' => false,
|
||||
'quiet' => false,
|
||||
];
|
||||
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $extensions = [];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
$this->result = new \PHPUnit\Framework\TestResult;
|
||||
$this->dispatcher = new EventDispatcher();
|
||||
$this->extensionLoader = new ExtensionLoader($this->dispatcher);
|
||||
|
||||
$baseOptions = $this->mergeOptions($options);
|
||||
$this->extensionLoader->bootGlobalExtensions($baseOptions); // extensions may override config
|
||||
|
||||
$this->config = Configuration::config();
|
||||
$this->options = $this->mergeOptions($options); // options updated from config
|
||||
|
||||
$this->registerSubscribers();
|
||||
$this->registerPHPUnitListeners();
|
||||
$this->registerPrinter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges given options with default values and current configuration
|
||||
*
|
||||
* @param array $options options
|
||||
* @return array
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
protected function mergeOptions($options)
|
||||
{
|
||||
$config = Configuration::config();
|
||||
$baseOptions = array_merge($this->options, $config['settings']);
|
||||
return array_merge($baseOptions, $options);
|
||||
}
|
||||
|
||||
protected function registerPHPUnitListeners()
|
||||
{
|
||||
$listener = new PHPUnit\Listener($this->dispatcher);
|
||||
$this->result->addListener($listener);
|
||||
}
|
||||
|
||||
public function registerSubscribers()
|
||||
{
|
||||
// required
|
||||
$this->dispatcher->addSubscriber(new Subscriber\GracefulTermination());
|
||||
$this->dispatcher->addSubscriber(new Subscriber\ErrorHandler());
|
||||
$this->dispatcher->addSubscriber(new Subscriber\Dependencies());
|
||||
$this->dispatcher->addSubscriber(new Subscriber\Bootstrap());
|
||||
$this->dispatcher->addSubscriber(new Subscriber\PrepareTest());
|
||||
$this->dispatcher->addSubscriber(new Subscriber\Module());
|
||||
$this->dispatcher->addSubscriber(new Subscriber\BeforeAfterTest());
|
||||
|
||||
// optional
|
||||
if (!$this->options['no-rebuild']) {
|
||||
$this->dispatcher->addSubscriber(new Subscriber\AutoRebuild());
|
||||
}
|
||||
if (!$this->options['silent']) {
|
||||
$this->dispatcher->addSubscriber(new Subscriber\Console($this->options));
|
||||
}
|
||||
if ($this->options['fail-fast']) {
|
||||
$this->dispatcher->addSubscriber(new Subscriber\FailFast());
|
||||
}
|
||||
|
||||
if ($this->options['coverage']) {
|
||||
$this->dispatcher->addSubscriber(new Coverage\Subscriber\Local($this->options));
|
||||
$this->dispatcher->addSubscriber(new Coverage\Subscriber\LocalServer($this->options));
|
||||
$this->dispatcher->addSubscriber(new Coverage\Subscriber\RemoteServer($this->options));
|
||||
$this->dispatcher->addSubscriber(new Coverage\Subscriber\Printer($this->options));
|
||||
}
|
||||
$this->dispatcher->addSubscriber($this->extensionLoader);
|
||||
$this->extensionLoader->registerGlobalExtensions();
|
||||
}
|
||||
|
||||
public function run($suite, $test = null, array $config = null)
|
||||
{
|
||||
ini_set(
|
||||
'memory_limit',
|
||||
isset($this->config['settings']['memory_limit']) ? $this->config['settings']['memory_limit'] : '1024M'
|
||||
);
|
||||
|
||||
$config = $config ?: Configuration::config();
|
||||
|
||||
$settings = Configuration::suiteSettings($suite, $config);
|
||||
|
||||
$selectedEnvironments = $this->options['env'];
|
||||
$environments = Configuration::suiteEnvironments($suite);
|
||||
|
||||
if (!$selectedEnvironments or empty($environments)) {
|
||||
$this->runSuite($settings, $suite, $test);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (array_unique($selectedEnvironments) as $envList) {
|
||||
$envArray = explode(',', $envList);
|
||||
$config = [];
|
||||
foreach ($envArray as $env) {
|
||||
if (isset($environments[$env])) {
|
||||
$currentEnvironment = isset($config['current_environment']) ? [$config['current_environment']] : [];
|
||||
$config = Configuration::mergeConfigs($config, $environments[$env]);
|
||||
$currentEnvironment[] = $config['current_environment'];
|
||||
$config['current_environment'] = implode(',', $currentEnvironment);
|
||||
}
|
||||
}
|
||||
if (empty($config)) {
|
||||
continue;
|
||||
}
|
||||
$suiteToRun = $suite;
|
||||
if (!empty($envList)) {
|
||||
$suiteToRun .= ' (' . implode(', ', $envArray) . ')';
|
||||
}
|
||||
$this->runSuite($config, $suiteToRun, $test);
|
||||
}
|
||||
}
|
||||
|
||||
public function runSuite($settings, $suite, $test = null)
|
||||
{
|
||||
$suiteManager = new SuiteManager($this->dispatcher, $suite, $settings);
|
||||
$suiteManager->initialize();
|
||||
$suiteManager->loadTests($test);
|
||||
$suiteManager->run($this->runner, $this->result, $this->options);
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public static function versionString()
|
||||
{
|
||||
return 'Codeception PHP Testing Framework v' . self::VERSION;
|
||||
}
|
||||
|
||||
public function printResult()
|
||||
{
|
||||
$result = $this->getResult();
|
||||
$result->flushListeners();
|
||||
|
||||
$printer = $this->runner->getPrinter();
|
||||
$printer->printResult($result);
|
||||
|
||||
$this->dispatcher->dispatch(Events::RESULT_PRINT_AFTER, new Event\PrintResultEvent($result, $printer));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit\Framework\TestResult
|
||||
*/
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EventDispatcher
|
||||
*/
|
||||
public function getDispatcher()
|
||||
{
|
||||
return $this->dispatcher;
|
||||
}
|
||||
|
||||
protected function registerPrinter()
|
||||
{
|
||||
$printer = new PHPUnit\ResultPrinter\UI($this->dispatcher, $this->options);
|
||||
$this->runner = new PHPUnit\Runner();
|
||||
$this->runner->setPrinter($printer);
|
||||
}
|
||||
}
|
||||
57
vendor/codeception/base/src/Codeception/Command/Bootstrap.php
vendored
Normal file
57
vendor/codeception/base/src/Codeception/Command/Bootstrap.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Template\Bootstrap as BootstrapTemplate;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Creates default config, tests directory and sample suites for current project.
|
||||
* Use this command to start building a test suite.
|
||||
*
|
||||
* By default it will create 3 suites **acceptance**, **functional**, and **unit**.
|
||||
*
|
||||
* * `codecept bootstrap` - creates `tests` dir and `codeception.yml` in current dir.
|
||||
* * `codecept bootstrap --empty` - creates `tests` dir without suites
|
||||
* * `codecept bootstrap --namespace Frontend` - creates tests, and use `Frontend` namespace for actor classes and helpers.
|
||||
* * `codecept bootstrap --actor Wizard` - sets actor as Wizard, to have `TestWizard` actor in tests.
|
||||
* * `codecept bootstrap path/to/the/project` - provide different path to a project, where tests should be placed
|
||||
*
|
||||
*/
|
||||
class Bootstrap extends Command
|
||||
{
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition(
|
||||
[
|
||||
new InputArgument('path', InputArgument::OPTIONAL, 'custom installation dir', null),
|
||||
new InputOption(
|
||||
'namespace',
|
||||
'ns',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Namespace to add for actor classes and helpers'
|
||||
),
|
||||
new InputOption('actor', 'a', InputOption::VALUE_OPTIONAL, 'Custom actor instead of Tester'),
|
||||
new InputOption('empty', 'e', InputOption::VALUE_NONE, 'Don\'t create standard suites')
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return "Creates default test suites and generates all required files";
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$bootstrap = new BootstrapTemplate($input, $output);
|
||||
if ($input->getArgument('path')) {
|
||||
$bootstrap->initDir($input->getArgument('path'));
|
||||
}
|
||||
$bootstrap->setup();
|
||||
}
|
||||
}
|
||||
108
vendor/codeception/base/src/Codeception/Command/Build.php
vendored
Normal file
108
vendor/codeception/base/src/Codeception/Command/Build.php
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Lib\Generator\Actions as ActionsGenerator;
|
||||
use Codeception\Lib\Generator\Actor as ActorGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Generates Actor classes (initially Guy classes) from suite configs.
|
||||
* Starting from Codeception 2.0 actor classes are auto-generated. Use this command to generate them manually.
|
||||
*
|
||||
* * `codecept build`
|
||||
* * `codecept build path/to/project`
|
||||
*
|
||||
*/
|
||||
class Build extends Command
|
||||
{
|
||||
use Shared\Config;
|
||||
use Shared\FileSystem;
|
||||
|
||||
protected $inheritedMethodTemplate = ' * @method void %s(%s)';
|
||||
|
||||
/**
|
||||
* @var OutputInterface
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates base classes for all suites';
|
||||
}
|
||||
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->output = $output;
|
||||
$this->buildActorsForConfig();
|
||||
}
|
||||
|
||||
private function buildActor(array $settings)
|
||||
{
|
||||
$actorGenerator = new ActorGenerator($settings);
|
||||
$this->output->writeln(
|
||||
'<info>' . Configuration::config()['namespace'] . '\\' . $actorGenerator->getActorName()
|
||||
. "</info> includes modules: " . implode(', ', $actorGenerator->getModules())
|
||||
);
|
||||
|
||||
$content = $actorGenerator->produce();
|
||||
|
||||
$file = $this->createDirectoryFor(
|
||||
Configuration::supportDir(),
|
||||
$settings['actor']
|
||||
) . $this->getShortClassName($settings['actor']);
|
||||
$file .= '.php';
|
||||
return $this->createFile($file, $content);
|
||||
}
|
||||
|
||||
private function buildActions(array $settings)
|
||||
{
|
||||
$actionsGenerator = new ActionsGenerator($settings);
|
||||
$this->output->writeln(
|
||||
" -> {$settings['actor']}Actions.php generated successfully. "
|
||||
. $actionsGenerator->getNumMethods() . " methods added"
|
||||
);
|
||||
|
||||
$content = $actionsGenerator->produce();
|
||||
|
||||
$file = $this->createDirectoryFor(Configuration::supportDir() . '_generated', $settings['actor']);
|
||||
$file .= $this->getShortClassName($settings['actor']) . 'Actions.php';
|
||||
return $this->createFile($file, $content, true);
|
||||
}
|
||||
|
||||
private function buildSuiteActors()
|
||||
{
|
||||
$suites = $this->getSuites();
|
||||
if (!empty($suites)) {
|
||||
$this->output->writeln("<info>Building Actor classes for suites: " . implode(', ', $suites) . '</info>');
|
||||
}
|
||||
foreach ($suites as $suite) {
|
||||
$settings = $this->getSuiteConfig($suite);
|
||||
if (!$settings['actor']) {
|
||||
continue; // no actor
|
||||
}
|
||||
$this->buildActions($settings);
|
||||
$actorBuilt = $this->buildActor($settings);
|
||||
|
||||
if ($actorBuilt) {
|
||||
$this->output->writeln("{$settings['actor']}.php created.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function buildActorsForConfig($configFile = null)
|
||||
{
|
||||
$config = $this->getGlobalConfig($configFile);
|
||||
|
||||
$dir = Configuration::projectDir();
|
||||
$this->buildSuiteActors();
|
||||
|
||||
foreach ($config['include'] as $subConfig) {
|
||||
$this->output->writeln("\n<comment>Included Configuration: $subConfig</comment>");
|
||||
$this->buildActorsForConfig($dir . DIRECTORY_SEPARATOR . $subConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
vendor/codeception/base/src/Codeception/Command/Clean.php
vendored
Normal file
45
vendor/codeception/base/src/Codeception/Command/Clean.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Util\FileSystem;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Recursively cleans `output` directory and generated code.
|
||||
*
|
||||
* * `codecept clean`
|
||||
*
|
||||
*/
|
||||
class Clean extends Command
|
||||
{
|
||||
use Shared\Config;
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Recursively cleans log and generated code';
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$projectDir = Configuration::projectDir();
|
||||
$this->cleanProjectsRecursively($output, $projectDir);
|
||||
$output->writeln("Done");
|
||||
}
|
||||
|
||||
private function cleanProjectsRecursively(OutputInterface $output, $projectDir)
|
||||
{
|
||||
$logDir = Configuration::logDir();
|
||||
$output->writeln("<info>Cleaning up output " . $logDir . "...</info>");
|
||||
FileSystem::doEmptyDir($logDir);
|
||||
|
||||
$config = Configuration::config($projectDir);
|
||||
$subProjects = $config['include'];
|
||||
foreach ($subProjects as $subProject) {
|
||||
$subProjectDir = $projectDir . $subProject;
|
||||
$this->cleanProjectsRecursively($output, $subProjectDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
84
vendor/codeception/base/src/Codeception/Command/Completion.php
vendored
Normal file
84
vendor/codeception/base/src/Codeception/Command/Completion.php
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
if (!class_exists('Stecman\Component\Symfony\Console\BashCompletion\Completion')) {
|
||||
echo "Please install `stecman/symfony-console-completion\n` to enable auto completion";
|
||||
return;
|
||||
}
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Completion as ConsoleCompletion;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\CompletionCommand;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\CompletionHandler;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Completion\ShellPathCompletion as ShellPathCompletion;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class Completion extends CompletionCommand
|
||||
{
|
||||
protected function configureCompletion(CompletionHandler $handler)
|
||||
{
|
||||
// Can't set for all commands, because it wouldn't work well with generate:suite
|
||||
$suiteCommands = [
|
||||
'run',
|
||||
'config:validate',
|
||||
'console',
|
||||
'dry-run',
|
||||
'generate:cept',
|
||||
'generate:cest',
|
||||
'generate:feature',
|
||||
'generate:phpunit',
|
||||
'generate:scenarios',
|
||||
'generate:stepobject',
|
||||
'generate:test',
|
||||
'gherkin:snippets',
|
||||
'gherkin:steps'
|
||||
];
|
||||
|
||||
foreach ($suiteCommands as $suiteCommand) {
|
||||
$handler->addHandler(new ConsoleCompletion(
|
||||
$suiteCommand,
|
||||
'suite',
|
||||
ConsoleCompletion::TYPE_ARGUMENT,
|
||||
Configuration::suites()
|
||||
));
|
||||
}
|
||||
|
||||
$handler->addHandlers([
|
||||
new ShellPathCompletion(
|
||||
ConsoleCompletion::ALL_COMMANDS,
|
||||
'path',
|
||||
ConsoleCompletion::TYPE_ARGUMENT
|
||||
),
|
||||
new ShellPathCompletion(
|
||||
ConsoleCompletion::ALL_COMMANDS,
|
||||
'test',
|
||||
ConsoleCompletion::TYPE_ARGUMENT
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($input->getOption('generate-hook') && $input->getOption('use-vendor-bin')) {
|
||||
global $argv;
|
||||
$argv[0] = 'vendor/bin/' . basename($argv[0]);
|
||||
}
|
||||
|
||||
parent::execute($input, $output);
|
||||
}
|
||||
|
||||
protected function createDefinition()
|
||||
{
|
||||
$definition = parent::createDefinition();
|
||||
$definition->addOption(new InputOption(
|
||||
'use-vendor-bin',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Use the vendor bin for autocompletion.'
|
||||
));
|
||||
|
||||
return $definition;
|
||||
}
|
||||
}
|
||||
40
vendor/codeception/base/src/Codeception/Command/CompletionFallback.php
vendored
Normal file
40
vendor/codeception/base/src/Codeception/Command/CompletionFallback.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Completion as ConsoleCompletion;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\CompletionCommand;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\CompletionHandler;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Completion\ShellPathCompletion as ShellPathCompletion;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputDefinition;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class CompletionFallback extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('_completion')
|
||||
->setDescription('BASH completion hook.')
|
||||
->setHelp(<<<END
|
||||
To enable BASH completion, install optional stecman/symfony-console-completion first:
|
||||
|
||||
<comment>composer require stecman/symfony-console-completion</comment>
|
||||
|
||||
END
|
||||
);
|
||||
|
||||
// Hide this command from listing if supported
|
||||
// Command::setHidden() was not available before Symfony 3.2.0
|
||||
if (method_exists($this, 'setHidden')) {
|
||||
$this->setHidden(true);
|
||||
}
|
||||
}
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$output->writeln("Install optional <comment>stecman/symfony-console-completion</comment>");
|
||||
}
|
||||
}
|
||||
105
vendor/codeception/base/src/Codeception/Command/ConfigValidate.php
vendored
Normal file
105
vendor/codeception/base/src/Codeception/Command/ConfigValidate.php
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Validates and prints Codeception config.
|
||||
* Use it do debug Yaml configs
|
||||
*
|
||||
* Check config:
|
||||
*
|
||||
* * `codecept config`: check global config
|
||||
* * `codecept config unit`: check suite config
|
||||
*
|
||||
* Load config:
|
||||
*
|
||||
* * `codecept config:validate -c path/to/another/config`: from another dir
|
||||
* * `codecept config:validate -c another_config.yml`: from another config file
|
||||
*
|
||||
* Check overriding config values (like in `run` command)
|
||||
*
|
||||
* * `codecept config:validate -o "settings: shuffle: true"`: enable shuffle
|
||||
* * `codecept config:validate -o "settings: lint: false"`: disable linting
|
||||
* * `codecept config:validate -o "reporters: report: \Custom\Reporter" --report`: use custom reporter
|
||||
*
|
||||
*/
|
||||
class ConfigValidate extends Command
|
||||
{
|
||||
use Shared\Config;
|
||||
use Shared\Style;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition(
|
||||
[
|
||||
new InputArgument('suite', InputArgument::OPTIONAL, 'to show suite configuration'),
|
||||
new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'),
|
||||
new InputOption('override', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Override config values'),
|
||||
]
|
||||
);
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Validates and prints config to screen';
|
||||
}
|
||||
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->addStyles($output);
|
||||
|
||||
if ($suite = $input->getArgument('suite')) {
|
||||
$output->write("Validating <bold>$suite</bold> config... ");
|
||||
$config = $this->getSuiteConfig($suite, $input->getOption('config'));
|
||||
$output->writeln("Ok");
|
||||
$output->writeln("------------------------------\n");
|
||||
$output->writeln("<info>$suite Suite Config</info>:\n");
|
||||
$output->writeln($this->formatOutput($config));
|
||||
return;
|
||||
}
|
||||
|
||||
$output->write("Validating global config... ");
|
||||
$config = $this->getGlobalConfig();
|
||||
$output->writeln($input->getOption('override'));
|
||||
if (count($input->getOption('override'))) {
|
||||
$config = $this->overrideConfig($input->getOption('override'));
|
||||
}
|
||||
$suites = Configuration::suites();
|
||||
$output->writeln("Ok");
|
||||
|
||||
$output->writeln("------------------------------\n");
|
||||
$output->writeln("<info>Codeception Config</info>:\n");
|
||||
$output->writeln($this->formatOutput($config));
|
||||
|
||||
$output->writeln('<info>Directories</info>:');
|
||||
$output->writeln("<comment>codecept_root_dir()</comment> " . codecept_root_dir());
|
||||
$output->writeln("<comment>codecept_output_dir()</comment> " . codecept_output_dir());
|
||||
$output->writeln("<comment>codecept_data_dir()</comment> " . codecept_data_dir());
|
||||
$output->writeln('');
|
||||
|
||||
$output->writeln("<info>Available suites</info>: " . implode(', ', $suites));
|
||||
|
||||
foreach ($suites as $suite) {
|
||||
$output->write("Validating suite <bold>$suite</bold>... ");
|
||||
$this->getSuiteConfig($suite);
|
||||
$output->writeln('Ok');
|
||||
}
|
||||
|
||||
|
||||
$output->writeln("Execute <info>codecept config:validate [<suite>]</info> to see config for a suite");
|
||||
}
|
||||
|
||||
protected function formatOutput($config)
|
||||
{
|
||||
$output = print_r($config, true);
|
||||
return preg_replace('~\[(.*?)\] =>~', "<fg=yellow>$1</fg=yellow> =>", $output);
|
||||
}
|
||||
}
|
||||
159
vendor/codeception/base/src/Codeception/Command/Console.php
vendored
Normal file
159
vendor/codeception/base/src/Codeception/Command/Console.php
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Codecept;
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Event\SuiteEvent;
|
||||
use Codeception\Event\TestEvent;
|
||||
use Codeception\Events;
|
||||
use Codeception\Exception\ConfigurationException;
|
||||
use Codeception\Lib\Console\Output;
|
||||
use Codeception\Scenario;
|
||||
use Codeception\SuiteManager;
|
||||
use Codeception\Test\Cept;
|
||||
use Codeception\Util\Debug;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
/**
|
||||
* Try to execute test commands in run-time. You may try commands before writing the test.
|
||||
*
|
||||
* * `codecept console acceptance` - starts acceptance suite environment. If you use WebDriver you can manipulate browser with Codeception commands.
|
||||
*/
|
||||
class Console extends Command
|
||||
{
|
||||
protected $test;
|
||||
protected $codecept;
|
||||
protected $suite;
|
||||
protected $output;
|
||||
protected $actions = [];
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'suite to be executed'),
|
||||
new InputOption('colors', '', InputOption::VALUE_NONE, 'Use colors in output'),
|
||||
]);
|
||||
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Launches interactive test console';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$suiteName = $input->getArgument('suite');
|
||||
$this->output = $output;
|
||||
|
||||
$config = Configuration::config();
|
||||
$settings = Configuration::suiteSettings($suiteName, $config);
|
||||
|
||||
$options = $input->getOptions();
|
||||
$options['debug'] = true;
|
||||
$options['silent'] = true;
|
||||
$options['interactive'] = false;
|
||||
$options['colors'] = true;
|
||||
|
||||
Debug::setOutput(new Output($options));
|
||||
|
||||
$this->codecept = new Codecept($options);
|
||||
$dispatcher = $this->codecept->getDispatcher();
|
||||
|
||||
$suiteManager = new SuiteManager($dispatcher, $suiteName, $settings);
|
||||
$suiteManager->initialize();
|
||||
$this->suite = $suiteManager->getSuite();
|
||||
$moduleContainer = $suiteManager->getModuleContainer();
|
||||
|
||||
$this->actions = array_keys($moduleContainer->getActions());
|
||||
|
||||
$this->test = new Cept(null, null);
|
||||
$this->test->getMetadata()->setServices([
|
||||
'dispatcher' => $dispatcher,
|
||||
'modules' => $moduleContainer
|
||||
]);
|
||||
|
||||
$scenario = new Scenario($this->test);
|
||||
if (!$settings['actor']) {
|
||||
throw new ConfigurationException("Interactive shell can't be started without an actor");
|
||||
}
|
||||
if (isset($config["namespace"])) {
|
||||
$settings['actor'] = $config["namespace"] .'\\' . $settings['actor'];
|
||||
}
|
||||
$actor = $settings['actor'];
|
||||
$I = new $actor($scenario);
|
||||
|
||||
$this->listenToSignals();
|
||||
|
||||
$output->writeln("<info>Interactive console started for suite $suiteName</info>");
|
||||
$output->writeln("<info>Try Codeception commands without writing a test</info>");
|
||||
$output->writeln("<info>type 'exit' to leave console</info>");
|
||||
$output->writeln("<info>type 'actions' to see all available actions for this suite</info>");
|
||||
|
||||
$suiteEvent = new SuiteEvent($this->suite, $this->codecept->getResult(), $settings);
|
||||
$dispatcher->dispatch(Events::SUITE_BEFORE, $suiteEvent);
|
||||
|
||||
$dispatcher->dispatch(Events::TEST_PARSED, new TestEvent($this->test));
|
||||
$dispatcher->dispatch(Events::TEST_BEFORE, new TestEvent($this->test));
|
||||
|
||||
$output->writeln("\n\n<comment>\$I</comment> = new {$settings['actor']}(\$scenario);");
|
||||
$this->executeCommands($input, $output, $I, $settings['bootstrap']);
|
||||
|
||||
$dispatcher->dispatch(Events::TEST_AFTER, new TestEvent($this->test));
|
||||
$dispatcher->dispatch(Events::SUITE_AFTER, new SuiteEvent($this->suite));
|
||||
|
||||
$output->writeln("<info>Bye-bye!</info>");
|
||||
}
|
||||
|
||||
protected function executeCommands(InputInterface $input, OutputInterface $output, $I, $bootstrap)
|
||||
{
|
||||
$dialog = new QuestionHelper();
|
||||
|
||||
if (file_exists($bootstrap)) {
|
||||
require $bootstrap;
|
||||
}
|
||||
|
||||
do {
|
||||
$question = new Question("<comment>\$I-></comment>");
|
||||
$question->setAutocompleterValues($this->actions);
|
||||
|
||||
$command = $dialog->ask($input, $output, $question);
|
||||
if ($command == 'actions') {
|
||||
$output->writeln("<info>" . implode(' ', $this->actions));
|
||||
continue;
|
||||
}
|
||||
if ($command == 'exit') {
|
||||
return;
|
||||
}
|
||||
if ($command == '') {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$value = eval("return \$I->$command;");
|
||||
if ($value && !is_object($value)) {
|
||||
codecept_debug($value);
|
||||
}
|
||||
} catch (\PHPUnit\Framework\AssertionFailedError $fail) {
|
||||
$output->writeln("<error>fail</error> " . $fail->getMessage());
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln("<error>error</error> " . $e->getMessage());
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
protected function listenToSignals()
|
||||
{
|
||||
if (function_exists('pcntl_signal')) {
|
||||
declare (ticks = 1);
|
||||
pcntl_signal(SIGINT, SIG_IGN);
|
||||
pcntl_signal(SIGTERM, SIG_IGN);
|
||||
}
|
||||
}
|
||||
}
|
||||
134
vendor/codeception/base/src/Codeception/Command/DryRun.php
vendored
Normal file
134
vendor/codeception/base/src/Codeception/Command/DryRun.php
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Event\SuiteEvent;
|
||||
use Codeception\Event\TestEvent;
|
||||
use Codeception\Events;
|
||||
use Codeception\Subscriber\Bootstrap as BootstrapLoader;
|
||||
use Codeception\Subscriber\Console as ConsolePrinter;
|
||||
use Codeception\SuiteManager;
|
||||
use Codeception\Test\Interfaces\ScenarioDriven;
|
||||
use Codeception\Test\Test;
|
||||
use Codeception\Util\Maybe;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
/**
|
||||
* Shows step by step execution process for scenario driven tests without actually running them.
|
||||
*
|
||||
* * `codecept dry-run acceptance`
|
||||
* * `codecept dry-run acceptance MyCest`
|
||||
* * `codecept dry-run acceptance checkout.feature`
|
||||
* * `codecept dry-run tests/acceptance/MyCest.php`
|
||||
*
|
||||
*/
|
||||
class DryRun extends Command
|
||||
{
|
||||
use Shared\Config;
|
||||
use Shared\Style;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition(
|
||||
[
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'suite to scan for feature files'),
|
||||
new InputArgument('test', InputArgument::OPTIONAL, 'tests to be loaded'),
|
||||
]
|
||||
);
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Prints step-by-step scenario-driven test or a feature';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->addStyles($output);
|
||||
$suite = $input->getArgument('suite');
|
||||
$test = $input->getArgument('test');
|
||||
|
||||
$config = $this->getGlobalConfig();
|
||||
if (! Configuration::isEmpty() && ! $test && strpos($suite, $config['paths']['tests']) === 0) {
|
||||
list(, $suite, $test) = $this->matchTestFromFilename($suite, $config['paths']['tests']);
|
||||
}
|
||||
$settings = $this->getSuiteConfig($suite);
|
||||
|
||||
$dispatcher = new EventDispatcher();
|
||||
$dispatcher->addSubscriber(new ConsolePrinter([
|
||||
'colors' => !$input->getOption('no-ansi'),
|
||||
'steps' => true,
|
||||
'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
|
||||
]));
|
||||
$dispatcher->addSubscriber(new BootstrapLoader());
|
||||
|
||||
$suiteManager = new SuiteManager($dispatcher, $suite, $settings);
|
||||
$moduleContainer = $suiteManager->getModuleContainer();
|
||||
foreach (Configuration::modules($settings) as $module) {
|
||||
$moduleContainer->mock($module, new Maybe());
|
||||
}
|
||||
$suiteManager->loadTests($test);
|
||||
$tests = $suiteManager->getSuite()->tests();
|
||||
|
||||
$dispatcher->dispatch(Events::SUITE_INIT, new SuiteEvent($suiteManager->getSuite(), null, $settings));
|
||||
$dispatcher->dispatch(Events::SUITE_BEFORE, new SuiteEvent($suiteManager->getSuite(), null, $settings));
|
||||
foreach ($tests as $test) {
|
||||
if ($test instanceof \PHPUnit\Framework\TestSuite\DataProvider) {
|
||||
foreach ($test as $t) {
|
||||
if ($t instanceof Test) {
|
||||
$this->dryRunTest($output, $dispatcher, $t);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($test instanceof Test and $test instanceof ScenarioDriven) {
|
||||
$this->dryRunTest($output, $dispatcher, $test);
|
||||
}
|
||||
}
|
||||
$dispatcher->dispatch(Events::SUITE_AFTER, new SuiteEvent($suiteManager->getSuite()));
|
||||
}
|
||||
|
||||
|
||||
protected function matchTestFromFilename($filename, $tests_path)
|
||||
{
|
||||
$filename = str_replace(['//', '\/', '\\'], '/', $filename);
|
||||
$res = preg_match("~^$tests_path/(.*?)/(.*)$~", $filename, $matches);
|
||||
if (!$res) {
|
||||
throw new \InvalidArgumentException("Test file can't be matched");
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OutputInterface $output
|
||||
* @param $dispatcher
|
||||
* @param $test
|
||||
*/
|
||||
protected function dryRunTest(OutputInterface $output, EventDispatcher $dispatcher, Test $test)
|
||||
{
|
||||
$dispatcher->dispatch(Events::TEST_START, new TestEvent($test));
|
||||
$dispatcher->dispatch(Events::TEST_BEFORE, new TestEvent($test));
|
||||
try {
|
||||
$test->test();
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
$dispatcher->dispatch(Events::TEST_AFTER, new TestEvent($test));
|
||||
$dispatcher->dispatch(Events::TEST_END, new TestEvent($test));
|
||||
if ($test->getMetadata()->isBlocked()) {
|
||||
$output->writeln('');
|
||||
if ($skip = $test->getMetadata()->getSkip()) {
|
||||
$output->writeln("<warning> SKIPPED </warning>" . $skip);
|
||||
}
|
||||
if ($incomplete = $test->getMetadata()->getIncomplete()) {
|
||||
$output->writeln("<warning> INCOMPLETE </warning>" . $incomplete);
|
||||
}
|
||||
}
|
||||
$output->writeln('');
|
||||
}
|
||||
}
|
||||
55
vendor/codeception/base/src/Codeception/Command/GenerateCept.php
vendored
Normal file
55
vendor/codeception/base/src/Codeception/Command/GenerateCept.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Lib\Generator\Cept;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Generates Cept (scenario-driven test) file:
|
||||
*
|
||||
* * `codecept generate:cept suite Login`
|
||||
* * `codecept g:cept suite subdir/subdir/testnameCept.php`
|
||||
* * `codecept g:cept suite LoginCept -c path/to/project`
|
||||
*
|
||||
*/
|
||||
class GenerateCept extends Command
|
||||
{
|
||||
use Shared\FileSystem;
|
||||
use Shared\Config;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'suite to be tested'),
|
||||
new InputArgument('test', InputArgument::REQUIRED, 'test to be run'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates empty Cept file in suite';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$suite = $input->getArgument('suite');
|
||||
$filename = $input->getArgument('test');
|
||||
|
||||
$config = $this->getSuiteConfig($suite);
|
||||
$this->createDirectoryFor($config['path'], $filename);
|
||||
|
||||
$filename = $this->completeSuffix($filename, 'Cept');
|
||||
$gen = new Cept($config);
|
||||
|
||||
$full_path = rtrim($config['path'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $filename;
|
||||
$res = $this->createFile($full_path, $gen->produce());
|
||||
if (!$res) {
|
||||
$output->writeln("<error>Test $filename already exists</error>");
|
||||
return;
|
||||
}
|
||||
$output->writeln("<info>Test was created in $full_path</info>");
|
||||
}
|
||||
}
|
||||
62
vendor/codeception/base/src/Codeception/Command/GenerateCest.php
vendored
Normal file
62
vendor/codeception/base/src/Codeception/Command/GenerateCest.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Lib\Generator\Cest as CestGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Generates Cest (scenario-driven object-oriented test) file:
|
||||
*
|
||||
* * `codecept generate:cest suite Login`
|
||||
* * `codecept g:cest suite subdir/subdir/testnameCest.php`
|
||||
* * `codecept g:cest suite LoginCest -c path/to/project`
|
||||
* * `codecept g:cest "App\Login"`
|
||||
*
|
||||
*/
|
||||
class GenerateCest extends Command
|
||||
{
|
||||
use Shared\FileSystem;
|
||||
use Shared\Config;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'suite where tests will be put'),
|
||||
new InputArgument('class', InputArgument::REQUIRED, 'test name'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates empty Cest file in suite';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$suite = $input->getArgument('suite');
|
||||
$class = $input->getArgument('class');
|
||||
|
||||
$config = $this->getSuiteConfig($suite);
|
||||
$className = $this->getShortClassName($class);
|
||||
$path = $this->createDirectoryFor($config['path'], $class);
|
||||
|
||||
$filename = $this->completeSuffix($className, 'Cest');
|
||||
$filename = $path . $filename;
|
||||
|
||||
if (file_exists($filename)) {
|
||||
$output->writeln("<error>Test $filename already exists</error>");
|
||||
return;
|
||||
}
|
||||
$gen = new CestGenerator($class, $config);
|
||||
$res = $this->createFile($filename, $gen->produce());
|
||||
if (!$res) {
|
||||
$output->writeln("<error>Test $filename already exists</error>");
|
||||
return;
|
||||
}
|
||||
|
||||
$output->writeln("<info>Test was created in $filename</info>");
|
||||
}
|
||||
}
|
||||
58
vendor/codeception/base/src/Codeception/Command/GenerateEnvironment.php
vendored
Normal file
58
vendor/codeception/base/src/Codeception/Command/GenerateEnvironment.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Exception\ConfigurationException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Generates empty environment configuration file into envs dir:
|
||||
*
|
||||
* * `codecept g:env firefox`
|
||||
*
|
||||
* Required to have `envs` path to be specified in `codeception.yml`
|
||||
*/
|
||||
class GenerateEnvironment extends Command
|
||||
{
|
||||
use Shared\FileSystem;
|
||||
use Shared\Config;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('env', InputArgument::REQUIRED, 'Environment name'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates empty environment config';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$conf = $this->getGlobalConfig();
|
||||
if (!Configuration::envsDir()) {
|
||||
throw new ConfigurationException(
|
||||
"Path for environments configuration is not set.\n"
|
||||
. "Please specify envs path in your `codeception.yml`\n \n"
|
||||
. "envs: tests/_envs"
|
||||
);
|
||||
}
|
||||
$relativePath = $conf['paths']['envs'];
|
||||
$env = $input->getArgument('env');
|
||||
$file = "$env.yml";
|
||||
|
||||
$path = $this->createDirectoryFor($relativePath, $file);
|
||||
$saved = $this->createFile($path . $file, "# `$env` environment config goes here");
|
||||
|
||||
if ($saved) {
|
||||
$output->writeln("<info>$env config was created in $relativePath/$file</info>");
|
||||
} else {
|
||||
$output->writeln("<error>File $relativePath/$file already exists</error>");
|
||||
}
|
||||
}
|
||||
}
|
||||
58
vendor/codeception/base/src/Codeception/Command/GenerateFeature.php
vendored
Normal file
58
vendor/codeception/base/src/Codeception/Command/GenerateFeature.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Lib\Generator\Feature;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Generates Feature file (in Gherkin):
|
||||
*
|
||||
* * `codecept generate:feature suite Login`
|
||||
* * `codecept g:feature suite subdir/subdir/login.feature`
|
||||
* * `codecept g:feature suite login.feature -c path/to/project`
|
||||
*
|
||||
*/
|
||||
class GenerateFeature extends Command
|
||||
{
|
||||
use Shared\FileSystem;
|
||||
use Shared\Config;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'suite to be tested'),
|
||||
new InputArgument('feature', InputArgument::REQUIRED, 'feature to be generated'),
|
||||
new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates empty feature file in suite';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$suite = $input->getArgument('suite');
|
||||
$filename = $input->getArgument('feature');
|
||||
|
||||
$config = $this->getSuiteConfig($suite);
|
||||
$this->createDirectoryFor($config['path'], $filename);
|
||||
|
||||
$gen = new Feature(basename($filename));
|
||||
if (!preg_match('~\.feature$~', $filename)) {
|
||||
$filename .= '.feature';
|
||||
}
|
||||
$full_path = rtrim($config['path'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $filename;
|
||||
$res = $this->createFile($full_path, $gen->produce());
|
||||
if (!$res) {
|
||||
$output->writeln("<error>Feature $filename already exists</error>");
|
||||
return;
|
||||
}
|
||||
$output->writeln("<info>Feature was created in $full_path</info>");
|
||||
}
|
||||
}
|
||||
56
vendor/codeception/base/src/Codeception/Command/GenerateGroup.php
vendored
Normal file
56
vendor/codeception/base/src/Codeception/Command/GenerateGroup.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Lib\Generator\Group as GroupGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Creates empty GroupObject - extension which handles all group events.
|
||||
*
|
||||
* * `codecept g:group Admin`
|
||||
*/
|
||||
class GenerateGroup extends Command
|
||||
{
|
||||
use Shared\FileSystem;
|
||||
use Shared\Config;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('group', InputArgument::REQUIRED, 'Group class name'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates Group subscriber';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$config = $this->getGlobalConfig();
|
||||
$group = $input->getArgument('group');
|
||||
|
||||
$class = ucfirst($group);
|
||||
$path = $this->createDirectoryFor(Configuration::supportDir() . 'Group' . DIRECTORY_SEPARATOR, $class);
|
||||
|
||||
$filename = $path . $class . '.php';
|
||||
|
||||
$gen = new GroupGenerator($config, $group);
|
||||
$res = $this->createFile($filename, $gen->produce());
|
||||
|
||||
if (!$res) {
|
||||
$output->writeln("<error>Group $filename already exists</error>");
|
||||
return;
|
||||
}
|
||||
|
||||
$output->writeln("<info>Group extension was created in $filename</info>");
|
||||
$output->writeln(
|
||||
'To use this group extension, include it to "extensions" option of global Codeception config.'
|
||||
);
|
||||
}
|
||||
}
|
||||
50
vendor/codeception/base/src/Codeception/Command/GenerateHelper.php
vendored
Normal file
50
vendor/codeception/base/src/Codeception/Command/GenerateHelper.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Lib\Generator\Helper;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Creates empty Helper class.
|
||||
*
|
||||
* * `codecept g:helper MyHelper`
|
||||
* * `codecept g:helper "My\Helper"`
|
||||
*
|
||||
*/
|
||||
class GenerateHelper extends Command
|
||||
{
|
||||
use Shared\FileSystem;
|
||||
use Shared\Config;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('name', InputArgument::REQUIRED, 'helper name'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates new helper';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$name = ucfirst($input->getArgument('name'));
|
||||
$config = $this->getGlobalConfig();
|
||||
|
||||
$path = $this->createDirectoryFor(Configuration::supportDir() . 'Helper', $name);
|
||||
$filename = $path . $this->getShortClassName($name) . '.php';
|
||||
|
||||
$res = $this->createFile($filename, (new Helper($name, $config['namespace']))->produce());
|
||||
if ($res) {
|
||||
$output->writeln("<info>Helper $filename created</info>");
|
||||
} else {
|
||||
$output->writeln("<error>Error creating helper $filename</error>");
|
||||
}
|
||||
}
|
||||
}
|
||||
75
vendor/codeception/base/src/Codeception/Command/GeneratePageObject.php
vendored
Normal file
75
vendor/codeception/base/src/Codeception/Command/GeneratePageObject.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Lib\Generator\PageObject as PageObjectGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Generates PageObject. Can be generated either globally, or just for one suite.
|
||||
* If PageObject is generated globally it will act as UIMap, without any logic in it.
|
||||
*
|
||||
* * `codecept g:page Login`
|
||||
* * `codecept g:page Registration`
|
||||
* * `codecept g:page acceptance Login`
|
||||
*/
|
||||
class GeneratePageObject extends Command
|
||||
{
|
||||
use Shared\FileSystem;
|
||||
use Shared\Config;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'Either suite name or page object name)'),
|
||||
new InputArgument('page', InputArgument::OPTIONAL, 'Page name of pageobject to represent'),
|
||||
]);
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates empty PageObject class';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$suite = $input->getArgument('suite');
|
||||
$class = $input->getArgument('page');
|
||||
|
||||
if (!$class) {
|
||||
$class = $suite;
|
||||
$suite = null;
|
||||
}
|
||||
|
||||
$conf = $suite
|
||||
? $this->getSuiteConfig($suite)
|
||||
: $this->getGlobalConfig();
|
||||
|
||||
if ($suite) {
|
||||
$suite = DIRECTORY_SEPARATOR . ucfirst($suite);
|
||||
}
|
||||
|
||||
$path = $this->createDirectoryFor(Configuration::supportDir() . 'Page' . $suite, $class);
|
||||
|
||||
$filename = $path . $this->getShortClassName($class) . '.php';
|
||||
|
||||
$output->writeln($filename);
|
||||
|
||||
$gen = new PageObjectGenerator($conf, ucfirst($suite) . '\\' . $class);
|
||||
$res = $this->createFile($filename, $gen->produce());
|
||||
|
||||
if (!$res) {
|
||||
$output->writeln("<error>PageObject $filename already exists</error>");
|
||||
exit;
|
||||
}
|
||||
$output->writeln("<info>PageObject was created in $filename</info>");
|
||||
}
|
||||
|
||||
protected function pathToPageObject($class, $suite)
|
||||
{
|
||||
}
|
||||
}
|
||||
142
vendor/codeception/base/src/Codeception/Command/GenerateScenarios.php
vendored
Normal file
142
vendor/codeception/base/src/Codeception/Command/GenerateScenarios.php
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Exception\ConfigurationException as ConfigurationException;
|
||||
use Codeception\Test\Cest;
|
||||
use Codeception\Test\Interfaces\ScenarioDriven;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
/**
|
||||
* Generates user-friendly text scenarios from scenario-driven tests (Cest, Cept).
|
||||
*
|
||||
* * `codecept g:scenarios acceptance` - for all acceptance tests
|
||||
* * `codecept g:scenarios acceptance --format html` - in html format
|
||||
* * `codecept g:scenarios acceptance --path doc` - generate scenarios to `doc` dir
|
||||
*/
|
||||
class GenerateScenarios extends Command
|
||||
{
|
||||
use Shared\FileSystem;
|
||||
use Shared\Config;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'suite from which texts should be generated'),
|
||||
new InputOption('path', 'p', InputOption::VALUE_REQUIRED, 'Use specified path as destination instead of default'),
|
||||
new InputOption('single-file', '', InputOption::VALUE_NONE, 'Render all scenarios to only one file'),
|
||||
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Specify output format: html or text (default)', 'text'),
|
||||
]);
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates text representation for all scenarios';
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$suite = $input->getArgument('suite');
|
||||
|
||||
$suiteConf = $this->getSuiteConfig($suite);
|
||||
|
||||
$path = $input->getOption('path')
|
||||
? $input->getOption('path')
|
||||
: Configuration::dataDir() . 'scenarios';
|
||||
|
||||
$format = $input->getOption('format');
|
||||
|
||||
@mkdir($path);
|
||||
|
||||
if (!is_writable($path)) {
|
||||
throw new ConfigurationException(
|
||||
"Path $path is not writable. Please, set valid permissions for folder to store scenarios."
|
||||
);
|
||||
}
|
||||
|
||||
$path = $path . DIRECTORY_SEPARATOR . $suite;
|
||||
if (!$input->getOption('single-file')) {
|
||||
@mkdir($path);
|
||||
}
|
||||
|
||||
$suiteManager = new \Codeception\SuiteManager(new EventDispatcher(), $suite, $suiteConf);
|
||||
|
||||
if ($suiteConf['bootstrap']) {
|
||||
if (file_exists($suiteConf['path'] . $suiteConf['bootstrap'])) {
|
||||
require_once $suiteConf['path'] . $suiteConf['bootstrap'];
|
||||
}
|
||||
}
|
||||
|
||||
$tests = $this->getTests($suiteManager);
|
||||
$scenarios = "";
|
||||
|
||||
foreach ($tests as $test) {
|
||||
if (!($test instanceof ScenarioDriven)) {
|
||||
continue;
|
||||
}
|
||||
$feature = $test->getScenarioText($format);
|
||||
|
||||
$name = $this->underscore(basename($test->getFileName(), '.php'));
|
||||
|
||||
// create separate file for each test in Cest
|
||||
if ($test instanceof Cest && !$input->getOption('single-file')) {
|
||||
$name .= '.' . $this->underscore($test->getTestMethod());
|
||||
}
|
||||
|
||||
if ($input->getOption('single-file')) {
|
||||
$scenarios .= $feature;
|
||||
$output->writeln("* $name rendered");
|
||||
} else {
|
||||
$feature = $this->decorate($feature, $format);
|
||||
$this->createFile($path . DIRECTORY_SEPARATOR . $name . $this->formatExtension($format), $feature, true);
|
||||
$output->writeln("* $name generated");
|
||||
}
|
||||
}
|
||||
|
||||
if ($input->getOption('single-file')) {
|
||||
$this->createFile($path . $this->formatExtension($format), $this->decorate($scenarios, $format), true);
|
||||
}
|
||||
}
|
||||
|
||||
protected function decorate($text, $format)
|
||||
{
|
||||
switch ($format) {
|
||||
case 'text':
|
||||
return $text;
|
||||
case 'html':
|
||||
return "<html><body>$text</body></html>";
|
||||
}
|
||||
}
|
||||
|
||||
protected function getTests($suiteManager)
|
||||
{
|
||||
$suiteManager->loadTests();
|
||||
return $suiteManager->getSuite()->tests();
|
||||
}
|
||||
|
||||
protected function formatExtension($format)
|
||||
{
|
||||
switch ($format) {
|
||||
case 'text':
|
||||
return '.txt';
|
||||
case 'html':
|
||||
return '.html';
|
||||
}
|
||||
}
|
||||
|
||||
private function underscore($name)
|
||||
{
|
||||
$name = preg_replace('/([A-Z]+)([A-Z][a-z])/', '\\1_\\2', $name);
|
||||
$name = preg_replace('/([a-z\d])([A-Z])/', '\\1_\\2', $name);
|
||||
$name = str_replace(['/', '\\'], ['.', '.'], $name);
|
||||
$name = preg_replace('/_Cept$/', '', $name);
|
||||
$name = preg_replace('/_Cest$/', '', $name);
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
75
vendor/codeception/base/src/Codeception/Command/GenerateStepObject.php
vendored
Normal file
75
vendor/codeception/base/src/Codeception/Command/GenerateStepObject.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Lib\Generator\StepObject as StepObjectGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
/**
|
||||
* Generates StepObject class. You will be asked for steps you want to implement.
|
||||
*
|
||||
* * `codecept g:stepobject acceptance AdminSteps`
|
||||
* * `codecept g:stepobject acceptance UserSteps --silent` - skip action questions
|
||||
*
|
||||
*/
|
||||
class GenerateStepObject extends Command
|
||||
{
|
||||
use Shared\FileSystem;
|
||||
use Shared\Config;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'Suite for StepObject'),
|
||||
new InputArgument('step', InputArgument::REQUIRED, 'StepObject name'),
|
||||
new InputOption('silent', '', InputOption::VALUE_NONE, 'skip verification question'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates empty StepObject class';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$suite = $input->getArgument('suite');
|
||||
$step = $input->getArgument('step');
|
||||
$config = $this->getSuiteConfig($suite);
|
||||
|
||||
$class = $this->getShortClassName($step);
|
||||
|
||||
$path = $this->createDirectoryFor(Configuration::supportDir() . 'Step' . DIRECTORY_SEPARATOR . ucfirst($suite), $step);
|
||||
|
||||
$dialog = $this->getHelperSet()->get('question');
|
||||
$filename = $path . $class . '.php';
|
||||
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new Question("Add action to StepObject class (ENTER to exit): ");
|
||||
|
||||
$gen = new StepObjectGenerator($config, ucfirst($suite) . '\\' . $step);
|
||||
|
||||
if (!$input->getOption('silent')) {
|
||||
do {
|
||||
$question = new Question('Add action to StepObject class (ENTER to exit): ', null);
|
||||
$action = $dialog->ask($input, $output, $question);
|
||||
if ($action) {
|
||||
$gen->createAction($action);
|
||||
}
|
||||
} while ($action);
|
||||
}
|
||||
|
||||
$res = $this->createFile($filename, $gen->produce());
|
||||
|
||||
if (!$res) {
|
||||
$output->writeln("<error>StepObject $filename already exists</error>");
|
||||
exit;
|
||||
}
|
||||
$output->writeln("<info>StepObject was created in $filename</info>");
|
||||
}
|
||||
}
|
||||
134
vendor/codeception/base/src/Codeception/Command/GenerateSuite.php
vendored
Normal file
134
vendor/codeception/base/src/Codeception/Command/GenerateSuite.php
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Lib\Generator\Helper;
|
||||
use Codeception\Util\Template;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Codeception\Lib\Generator\Actor as ActorGenerator;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Create new test suite. Requires suite name and actor name
|
||||
*
|
||||
* * ``
|
||||
* * `codecept g:suite api` -> api + ApiTester
|
||||
* * `codecept g:suite integration Code` -> integration + CodeTester
|
||||
* * `codecept g:suite frontend Front` -> frontend + FrontTester
|
||||
*
|
||||
*/
|
||||
class GenerateSuite extends Command
|
||||
{
|
||||
use Shared\FileSystem;
|
||||
use Shared\Config;
|
||||
use Shared\Style;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'suite to be generated'),
|
||||
new InputArgument('actor', InputArgument::OPTIONAL, 'name of new actor class'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates new test suite';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->addStyles($output);
|
||||
$suite = $input->getArgument('suite');
|
||||
$actor = $input->getArgument('actor');
|
||||
|
||||
if ($this->containsInvalidCharacters($suite)) {
|
||||
$output->writeln("<error>Suite name '$suite' contains invalid characters. ([A-Za-z0-9_]).</error>");
|
||||
return;
|
||||
}
|
||||
|
||||
$config = $this->getGlobalConfig();
|
||||
if (!$actor) {
|
||||
$actor = ucfirst($suite) . $config['actor_suffix'];
|
||||
}
|
||||
|
||||
$dir = Configuration::testsDir();
|
||||
if (file_exists($dir . $suite . '.suite.yml')) {
|
||||
throw new \Exception("Suite configuration file '$suite.suite.yml' already exists.");
|
||||
}
|
||||
|
||||
$this->createDirectoryFor($dir . $suite);
|
||||
|
||||
if ($config['settings']['bootstrap']) {
|
||||
// generate bootstrap file
|
||||
$this->createFile(
|
||||
$dir . $suite . DIRECTORY_SEPARATOR . $config['settings']['bootstrap'],
|
||||
"<?php\n",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
$helperName = ucfirst($suite);
|
||||
|
||||
$file = $this->createDirectoryFor(
|
||||
Configuration::supportDir() . "Helper",
|
||||
"$helperName.php"
|
||||
) . "$helperName.php";
|
||||
|
||||
$gen = new Helper($helperName, $config['namespace']);
|
||||
// generate helper
|
||||
$this->createFile(
|
||||
$file,
|
||||
$gen->produce()
|
||||
);
|
||||
|
||||
$output->writeln("Helper <info>" . $gen->getHelperName() . "</info> was created in $file");
|
||||
|
||||
$yamlSuiteConfigTemplate = <<<EOF
|
||||
actor: {{actor}}
|
||||
modules:
|
||||
enabled:
|
||||
- {{helper}}
|
||||
EOF;
|
||||
|
||||
$this->createFile(
|
||||
$dir . $suite . '.suite.yml',
|
||||
$yamlSuiteConfig = (new Template($yamlSuiteConfigTemplate))
|
||||
->place('actor', $actor)
|
||||
->place('helper', $gen->getHelperName())
|
||||
->produce()
|
||||
);
|
||||
|
||||
Configuration::append(Yaml::parse($yamlSuiteConfig));
|
||||
$actorGenerator = new ActorGenerator(Configuration::config());
|
||||
|
||||
$content = $actorGenerator->produce();
|
||||
|
||||
$file = $this->createDirectoryFor(
|
||||
Configuration::supportDir(),
|
||||
$actor
|
||||
) . $this->getShortClassName($actor);
|
||||
$file .= '.php';
|
||||
|
||||
$this->createFile($file, $content);
|
||||
|
||||
$output->writeln("Actor <info>" . $actor . "</info> was created in $file");
|
||||
|
||||
$output->writeln("Suite config <info>$suite.suite.yml</info> was created.");
|
||||
$output->writeln(' ');
|
||||
$output->writeln("Next steps:");
|
||||
$output->writeln("1. Edit <bold>$suite.suite.yml</bold> to enable modules for this suite");
|
||||
$output->writeln("2. Create first test with <bold>generate:cest testName</bold> ( or test|cept) command");
|
||||
$output->writeln("3. Run tests of this suite with <bold>codecept run $suite</bold> command");
|
||||
|
||||
$output->writeln("<info>Suite $suite generated</info>");
|
||||
}
|
||||
|
||||
private function containsInvalidCharacters($suite)
|
||||
{
|
||||
return preg_match('#[^A-Za-z0-9_]#', $suite) ? true : false;
|
||||
}
|
||||
}
|
||||
60
vendor/codeception/base/src/Codeception/Command/GenerateTest.php
vendored
Normal file
60
vendor/codeception/base/src/Codeception/Command/GenerateTest.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Lib\Generator\Test as TestGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Generates skeleton for Unit Test that extends `Codeception\TestCase\Test`.
|
||||
*
|
||||
* * `codecept g:test unit User`
|
||||
* * `codecept g:test unit "App\User"`
|
||||
*/
|
||||
class GenerateTest extends Command
|
||||
{
|
||||
use Shared\FileSystem;
|
||||
use Shared\Config;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition(
|
||||
[
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'suite where tests will be put'),
|
||||
new InputArgument('class', InputArgument::REQUIRED, 'class name'),
|
||||
]
|
||||
);
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Generates empty unit test file in suite';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$suite = $input->getArgument('suite');
|
||||
$class = $input->getArgument('class');
|
||||
|
||||
$config = $this->getSuiteConfig($suite);
|
||||
|
||||
$className = $this->getShortClassName($class);
|
||||
$path = $this->createDirectoryFor($config['path'], $class);
|
||||
|
||||
$filename = $this->completeSuffix($className, 'Test');
|
||||
$filename = $path . $filename;
|
||||
|
||||
$gen = new TestGenerator($config, $class);
|
||||
|
||||
$res = $this->createFile($filename, $gen->produce());
|
||||
|
||||
if (!$res) {
|
||||
$output->writeln("<error>Test $filename already exists</error>");
|
||||
return;
|
||||
}
|
||||
$output->writeln("<info>Test was created in $filename</info>");
|
||||
}
|
||||
}
|
||||
72
vendor/codeception/base/src/Codeception/Command/GherkinSnippets.php
vendored
Normal file
72
vendor/codeception/base/src/Codeception/Command/GherkinSnippets.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Lib\Generator\GherkinSnippets as SnippetsGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Generates code snippets for matched feature files in a suite.
|
||||
* Code snippets are expected to be implemented in Actor or PageObjects
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* * `codecept gherkin:snippets acceptance` - snippets from all feature of acceptance tests
|
||||
* * `codecept gherkin:snippets acceptance/feature/users` - snippets from `feature/users` dir of acceptance tests
|
||||
* * `codecept gherkin:snippets acceptance user_account.feature` - snippets from a single feature file
|
||||
* * `codecept gherkin:snippets acceptance/feature/users/user_accout.feature` - snippets from feature file in a dir
|
||||
*/
|
||||
class GherkinSnippets extends Command
|
||||
{
|
||||
use Shared\Config;
|
||||
use Shared\Style;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition(
|
||||
[
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'suite to scan for feature files'),
|
||||
new InputArgument('test', InputArgument::OPTIONAL, 'test to be scanned'),
|
||||
new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'),
|
||||
]
|
||||
);
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Fetches empty steps from feature files of suite and prints code snippets for them';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->addStyles($output);
|
||||
$suite = $input->getArgument('suite');
|
||||
$test = $input->getArgument('test');
|
||||
$config = $this->getSuiteConfig($suite);
|
||||
|
||||
$generator = new SnippetsGenerator($config, $test);
|
||||
$snippets = $generator->getSnippets();
|
||||
$features = $generator->getFeatures();
|
||||
|
||||
if (empty($snippets)) {
|
||||
$output->writeln("<notice> All Gherkin steps are defined. Exiting... </notice>");
|
||||
return;
|
||||
}
|
||||
$output->writeln("<comment> Snippets found in: </comment>");
|
||||
foreach ($features as $feature) {
|
||||
$output->writeln("<info> - {$feature} </info>");
|
||||
}
|
||||
$output->writeln("<comment> Generated Snippets: </comment>");
|
||||
$output->writeln("<info> ----------------------------------------- </info>");
|
||||
foreach ($snippets as $snippet) {
|
||||
$output->writeln($snippet);
|
||||
}
|
||||
$output->writeln("<info> ----------------------------------------- </info>");
|
||||
$output->writeln(sprintf(' <bold>%d</bold> snippets proposed', count($snippets)));
|
||||
$output->writeln("<notice> Copy generated snippets to {$config['actor']} or a specific Gherkin context </notice>");
|
||||
}
|
||||
}
|
||||
71
vendor/codeception/base/src/Codeception/Command/GherkinSteps.php
vendored
Normal file
71
vendor/codeception/base/src/Codeception/Command/GherkinSteps.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Test\Loader\Gherkin;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Prints all steps from all Gherkin contexts for a specific suite
|
||||
*
|
||||
* ```
|
||||
* codecept gherkin:steps acceptance
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
class GherkinSteps extends Command
|
||||
{
|
||||
use Shared\Config;
|
||||
use Shared\Style;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition(
|
||||
[
|
||||
new InputArgument('suite', InputArgument::REQUIRED, 'suite to scan for feature files'),
|
||||
new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'),
|
||||
]
|
||||
);
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Prints all defined feature steps';
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->addStyles($output);
|
||||
$suite = $input->getArgument('suite');
|
||||
$config = $this->getSuiteConfig($suite);
|
||||
$config['describe_steps'] = true;
|
||||
|
||||
$loader = new Gherkin($config);
|
||||
$steps = $loader->getSteps();
|
||||
|
||||
foreach ($steps as $name => $context) {
|
||||
/** @var $table Table **/
|
||||
$table = new Table($output);
|
||||
$table->setHeaders(['Step', 'Implementation']);
|
||||
$output->writeln("Steps from <bold>$name</bold> context:");
|
||||
|
||||
foreach ($context as $step => $callable) {
|
||||
if (count($callable) < 2) {
|
||||
continue;
|
||||
}
|
||||
$method = $callable[0] . '::' . $callable[1];
|
||||
$table->addRow([$step, $method]);
|
||||
}
|
||||
$table->render();
|
||||
}
|
||||
|
||||
if (!isset($table)) {
|
||||
$output->writeln("No steps are defined, start creating them by running <bold>gherkin:snippets</bold>");
|
||||
}
|
||||
}
|
||||
}
|
||||
55
vendor/codeception/base/src/Codeception/Command/Init.php
vendored
Normal file
55
vendor/codeception/base/src/Codeception/Command/Init.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\InitTemplate;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class Init extends Command
|
||||
{
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition(
|
||||
[
|
||||
new InputArgument('template', InputArgument::REQUIRED, 'Init template for the setup'),
|
||||
new InputOption('path', null, InputOption::VALUE_REQUIRED, 'Change current directory', null),
|
||||
new InputOption('namespace', null, InputOption::VALUE_OPTIONAL, 'Namespace to add for actor classes and helpers\'', null),
|
||||
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return "Creates test suites by a template";
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$template = $input->getArgument('template');
|
||||
|
||||
if (class_exists($template)) {
|
||||
$className = $template;
|
||||
} else {
|
||||
$className = 'Codeception\Template\\' . ucfirst($template);
|
||||
|
||||
if (!class_exists($className)) {
|
||||
throw new \Exception("Template from a $className can't be loaded; Init can't be executed");
|
||||
}
|
||||
}
|
||||
|
||||
$initProcess = new $className($input, $output);
|
||||
if (!$initProcess instanceof InitTemplate) {
|
||||
throw new \Exception("$className is not a valid template");
|
||||
}
|
||||
if ($input->getOption('path')) {
|
||||
$initProcess->initDir($input->getOption('path'));
|
||||
}
|
||||
$initProcess->setup();
|
||||
}
|
||||
}
|
||||
558
vendor/codeception/base/src/Codeception/Command/Run.php
vendored
Normal file
558
vendor/codeception/base/src/Codeception/Command/Run.php
vendored
Normal file
@@ -0,0 +1,558 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Codeception\Codecept;
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Util\PathResolver;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Executes tests.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* * `codecept run acceptance`: run all acceptance tests
|
||||
* * `codecept run tests/acceptance/MyCept.php`: run only MyCept
|
||||
* * `codecept run acceptance MyCept`: same as above
|
||||
* * `codecept run acceptance MyCest:myTestInIt`: run one test from a Cest
|
||||
* * `codecept run acceptance checkout.feature`: run feature-file
|
||||
* * `codecept run acceptance -g slow`: run tests from *slow* group
|
||||
* * `codecept run unit,functional`: run only unit and functional suites
|
||||
*
|
||||
* Verbosity modes:
|
||||
*
|
||||
* * `codecept run -v`:
|
||||
* * `codecept run --steps`: print step-by-step execution
|
||||
* * `codecept run -vv`:
|
||||
* * `codecept run --debug`: print steps and debug information
|
||||
* * `codecept run -vvv`: print internal debug information
|
||||
*
|
||||
* Load config:
|
||||
*
|
||||
* * `codecept run -c path/to/another/config`: from another dir
|
||||
* * `codecept run -c another_config.yml`: from another config file
|
||||
*
|
||||
* Override config values:
|
||||
*
|
||||
* * `codecept run -o "settings: shuffle: true"`: enable shuffle
|
||||
* * `codecept run -o "settings: lint: false"`: disable linting
|
||||
* * `codecept run -o "reporters: report: \Custom\Reporter" --report`: use custom reporter
|
||||
*
|
||||
* Run with specific extension
|
||||
*
|
||||
* * `codecept run --ext Recorder` run with Recorder extension enabled
|
||||
* * `codecept run --ext DotReporter` run with DotReporter printer
|
||||
* * `codecept run --ext "My\Custom\Extension"` run with an extension loaded by class name
|
||||
*
|
||||
* Full reference:
|
||||
* ```
|
||||
* Arguments:
|
||||
* suite suite to be tested
|
||||
* test test to be run
|
||||
*
|
||||
* Options:
|
||||
* -o, --override=OVERRIDE Override config values (multiple values allowed)
|
||||
* --config (-c) Use custom path for config
|
||||
* --report Show output in compact style
|
||||
* --html Generate html with results (default: "report.html")
|
||||
* --xml Generate JUnit XML Log (default: "report.xml")
|
||||
* --tap Generate Tap Log (default: "report.tap.log")
|
||||
* --json Generate Json Log (default: "report.json")
|
||||
* --colors Use colors in output
|
||||
* --no-colors Force no colors in output (useful to override config file)
|
||||
* --silent Only outputs suite names and final results
|
||||
* --steps Show steps in output
|
||||
* --debug (-d) Show debug and scenario output
|
||||
* --coverage Run with code coverage (default: "coverage.serialized")
|
||||
* --coverage-html Generate CodeCoverage HTML report in path (default: "coverage")
|
||||
* --coverage-xml Generate CodeCoverage XML report in file (default: "coverage.xml")
|
||||
* --coverage-text Generate CodeCoverage text report in file (default: "coverage.txt")
|
||||
* --coverage-phpunit Generate CodeCoverage PHPUnit report in file (default: "coverage-phpunit")
|
||||
* --no-exit Don't finish with exit code
|
||||
* --group (-g) Groups of tests to be executed (multiple values allowed)
|
||||
* --skip (-s) Skip selected suites (multiple values allowed)
|
||||
* --skip-group (-x) Skip selected groups (multiple values allowed)
|
||||
* --env Run tests in selected environments. (multiple values allowed, environments can be merged with ',')
|
||||
* --fail-fast (-f) Stop after first failure
|
||||
* --help (-h) Display this help message.
|
||||
* --quiet (-q) Do not output any message.
|
||||
* --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
|
||||
* --version (-V) Display this application version.
|
||||
* --ansi Force ANSI output.
|
||||
* --no-ansi Disable ANSI output.
|
||||
* --no-interaction (-n) Do not ask any interactive question.
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
class Run extends Command
|
||||
{
|
||||
use Shared\Config;
|
||||
/**
|
||||
* @var Codecept
|
||||
*/
|
||||
protected $codecept;
|
||||
|
||||
/**
|
||||
* @var integer of executed suites
|
||||
*/
|
||||
protected $executed = 0;
|
||||
|
||||
/**
|
||||
* @var array of options (command run)
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* @var OutputInterface
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
|
||||
/**
|
||||
* Sets Run arguments
|
||||
* @throws \Symfony\Component\Console\Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDefinition([
|
||||
new InputArgument('suite', InputArgument::OPTIONAL, 'suite to be tested'),
|
||||
new InputArgument('test', InputArgument::OPTIONAL, 'test to be run'),
|
||||
new InputOption('override', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Override config values'),
|
||||
new InputOption('ext', 'e', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Run with extension enabled'),
|
||||
new InputOption('report', '', InputOption::VALUE_NONE, 'Show output in compact style'),
|
||||
new InputOption('html', '', InputOption::VALUE_OPTIONAL, 'Generate html with results', 'report.html'),
|
||||
new InputOption('xml', '', InputOption::VALUE_OPTIONAL, 'Generate JUnit XML Log', 'report.xml'),
|
||||
new InputOption('tap', '', InputOption::VALUE_OPTIONAL, 'Generate Tap Log', 'report.tap.log'),
|
||||
new InputOption('json', '', InputOption::VALUE_OPTIONAL, 'Generate Json Log', 'report.json'),
|
||||
new InputOption('colors', '', InputOption::VALUE_NONE, 'Use colors in output'),
|
||||
new InputOption(
|
||||
'no-colors',
|
||||
'',
|
||||
InputOption::VALUE_NONE,
|
||||
'Force no colors in output (useful to override config file)'
|
||||
),
|
||||
new InputOption('silent', '', InputOption::VALUE_NONE, 'Only outputs suite names and final results'),
|
||||
new InputOption('steps', '', InputOption::VALUE_NONE, 'Show steps in output'),
|
||||
new InputOption('debug', 'd', InputOption::VALUE_NONE, 'Show debug and scenario output'),
|
||||
new InputOption(
|
||||
'coverage',
|
||||
'',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Run with code coverage'
|
||||
),
|
||||
new InputOption(
|
||||
'coverage-html',
|
||||
'',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Generate CodeCoverage HTML report in path'
|
||||
),
|
||||
new InputOption(
|
||||
'coverage-xml',
|
||||
'',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Generate CodeCoverage XML report in file'
|
||||
),
|
||||
new InputOption(
|
||||
'coverage-text',
|
||||
'',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Generate CodeCoverage text report in file'
|
||||
),
|
||||
new InputOption(
|
||||
'coverage-crap4j',
|
||||
'',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Generate CodeCoverage report in Crap4J XML format'
|
||||
),
|
||||
new InputOption(
|
||||
'coverage-phpunit',
|
||||
'',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Generate CodeCoverage PHPUnit report in path'
|
||||
),
|
||||
new InputOption('no-exit', '', InputOption::VALUE_NONE, 'Don\'t finish with exit code'),
|
||||
new InputOption(
|
||||
'group',
|
||||
'g',
|
||||
InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
|
||||
'Groups of tests to be executed'
|
||||
),
|
||||
new InputOption(
|
||||
'skip',
|
||||
's',
|
||||
InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
|
||||
'Skip selected suites'
|
||||
),
|
||||
new InputOption(
|
||||
'skip-group',
|
||||
'x',
|
||||
InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
|
||||
'Skip selected groups'
|
||||
),
|
||||
new InputOption(
|
||||
'env',
|
||||
'',
|
||||
InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
|
||||
'Run tests in selected environments.'
|
||||
),
|
||||
new InputOption('fail-fast', 'f', InputOption::VALUE_NONE, 'Stop after first failure'),
|
||||
new InputOption('no-rebuild', '', InputOption::VALUE_NONE, 'Do not rebuild actor classes on start'),
|
||||
]);
|
||||
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'Runs the test suites';
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes Run
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int|null|void
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->ensureCurlIsAvailable();
|
||||
$this->options = $input->getOptions();
|
||||
$this->output = $output;
|
||||
|
||||
// load config
|
||||
$config = $this->getGlobalConfig();
|
||||
|
||||
// update config from options
|
||||
if (count($this->options['override'])) {
|
||||
$config = $this->overrideConfig($this->options['override']);
|
||||
}
|
||||
if ($this->options['ext']) {
|
||||
$config = $this->enableExtensions($this->options['ext']);
|
||||
}
|
||||
|
||||
if (!$this->options['colors']) {
|
||||
$this->options['colors'] = $config['settings']['colors'];
|
||||
}
|
||||
if (!$this->options['silent']) {
|
||||
$this->output->writeln(
|
||||
Codecept::versionString() . "\nPowered by " . \PHPUnit\Runner\Version::getVersionString()
|
||||
);
|
||||
}
|
||||
if ($this->options['debug']) {
|
||||
$this->output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
|
||||
}
|
||||
|
||||
$userOptions = array_intersect_key($this->options, array_flip($this->passedOptionKeys($input)));
|
||||
$userOptions = array_merge(
|
||||
$userOptions,
|
||||
$this->booleanOptions($input, [
|
||||
'xml' => 'report.xml',
|
||||
'html' => 'report.html',
|
||||
'json' => 'report.json',
|
||||
'tap' => 'report.tap.log',
|
||||
'coverage' => 'coverage.serialized',
|
||||
'coverage-xml' => 'coverage.xml',
|
||||
'coverage-html' => 'coverage',
|
||||
'coverage-text' => 'coverage.txt',
|
||||
'coverage-crap4j' => 'crap4j.xml',
|
||||
'coverage-phpunit' => 'coverage-phpunit'])
|
||||
);
|
||||
$userOptions['verbosity'] = $this->output->getVerbosity();
|
||||
$userOptions['interactive'] = !$input->hasParameterOption(['--no-interaction', '-n']);
|
||||
$userOptions['ansi'] = (!$input->hasParameterOption('--no-ansi') xor $input->hasParameterOption('ansi'));
|
||||
|
||||
if ($this->options['no-colors'] || !$userOptions['ansi']) {
|
||||
$userOptions['colors'] = false;
|
||||
}
|
||||
if ($this->options['group']) {
|
||||
$userOptions['groups'] = $this->options['group'];
|
||||
}
|
||||
if ($this->options['skip-group']) {
|
||||
$userOptions['excludeGroups'] = $this->options['skip-group'];
|
||||
}
|
||||
if ($this->options['report']) {
|
||||
$userOptions['silent'] = true;
|
||||
}
|
||||
if ($this->options['coverage-xml'] or $this->options['coverage-html'] or $this->options['coverage-text'] or $this->options['coverage-crap4j'] or $this->options['coverage-phpunit']) {
|
||||
$this->options['coverage'] = true;
|
||||
}
|
||||
if (!$userOptions['ansi'] && $input->getOption('colors')) {
|
||||
$userOptions['colors'] = true; // turn on colors even in non-ansi mode if strictly passed
|
||||
}
|
||||
|
||||
$suite = $input->getArgument('suite');
|
||||
$test = $input->getArgument('test');
|
||||
|
||||
if ($this->options['group']) {
|
||||
$this->output->writeln(sprintf("[Groups] <info>%s</info> ", implode(', ', $this->options['group'])));
|
||||
}
|
||||
if ($input->getArgument('test')) {
|
||||
$this->options['steps'] = true;
|
||||
}
|
||||
|
||||
if (!$test) {
|
||||
// Check if suite is given and is in an included path
|
||||
if (!empty($suite) && !empty($config['include'])) {
|
||||
$isIncludeTest = false;
|
||||
// Remember original projectDir
|
||||
$projectDir = Configuration::projectDir();
|
||||
|
||||
foreach ($config['include'] as $include) {
|
||||
// Find if the suite begins with an include path
|
||||
if (strpos($suite, $include) === 0) {
|
||||
// Use include config
|
||||
$config = Configuration::config($projectDir.$include);
|
||||
|
||||
if (!isset($config['paths']['tests'])) {
|
||||
throw new \RuntimeException(
|
||||
sprintf("Included '%s' has no tests path configured", $include)
|
||||
);
|
||||
}
|
||||
|
||||
$testsPath = $include . DIRECTORY_SEPARATOR. $config['paths']['tests'];
|
||||
|
||||
try {
|
||||
list(, $suite, $test) = $this->matchTestFromFilename($suite, $testsPath);
|
||||
$isIncludeTest = true;
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
// Incorrect include match, continue trying to find one
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
$result = $this->matchSingleTest($suite, $config);
|
||||
if ($result) {
|
||||
list(, $suite, $test) = $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore main config
|
||||
if (!$isIncludeTest) {
|
||||
$config = Configuration::config($projectDir);
|
||||
}
|
||||
} elseif (!empty($suite)) {
|
||||
$result = $this->matchSingleTest($suite, $config);
|
||||
if ($result) {
|
||||
list(, $suite, $test) = $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($test) {
|
||||
$filter = $this->matchFilteredTestName($test);
|
||||
$userOptions['filter'] = $filter;
|
||||
}
|
||||
|
||||
$this->codecept = new Codecept($userOptions);
|
||||
|
||||
if ($suite and $test) {
|
||||
$this->codecept->run($suite, $test, $config);
|
||||
}
|
||||
|
||||
// Run all tests of given suite or all suites
|
||||
if (!$test) {
|
||||
$suites = $suite ? explode(',', $suite) : Configuration::suites();
|
||||
$this->executed = $this->runSuites($suites, $this->options['skip']);
|
||||
|
||||
if (!empty($config['include']) and !$suite) {
|
||||
$current_dir = Configuration::projectDir();
|
||||
$suites += $config['include'];
|
||||
$this->runIncludedSuites($config['include'], $current_dir);
|
||||
}
|
||||
|
||||
if ($this->executed === 0) {
|
||||
throw new \RuntimeException(
|
||||
sprintf("Suite '%s' could not be found", implode(', ', $suites))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->codecept->printResult();
|
||||
|
||||
if (!$input->getOption('no-exit')) {
|
||||
if (!$this->codecept->getResult()->wasSuccessful()) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function matchSingleTest($suite, $config)
|
||||
{
|
||||
// Workaround when codeception.yml is inside tests directory and tests path is set to "."
|
||||
// @see https://github.com/Codeception/Codeception/issues/4432
|
||||
if (isset($config['paths']['tests']) && $config['paths']['tests'] === '.' && !preg_match('~^\.[/\\\]~', $suite)) {
|
||||
$suite = './' . $suite;
|
||||
}
|
||||
|
||||
// running a single test when suite has a configured path
|
||||
if (isset($config['suites'])) {
|
||||
foreach ($config['suites'] as $s => $suiteConfig) {
|
||||
if (!isset($suiteConfig['path'])) {
|
||||
continue;
|
||||
}
|
||||
$testsPath = $config['paths']['tests'] . DIRECTORY_SEPARATOR . $suiteConfig['path'];
|
||||
if ($suiteConfig['path'] === '.') {
|
||||
$testsPath = $config['paths']['tests'];
|
||||
}
|
||||
if (preg_match("~^$testsPath/(.*?)$~", $suite, $matches)) {
|
||||
$matches[2] = $matches[1];
|
||||
$matches[1] = $s;
|
||||
return $matches;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run single test without included tests
|
||||
if (! Configuration::isEmpty() && strpos($suite, $config['paths']['tests']) === 0) {
|
||||
return $this->matchTestFromFilename($suite, $config['paths']['tests']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs included suites recursively
|
||||
*
|
||||
* @param array $suites
|
||||
* @param string $parent_dir
|
||||
*/
|
||||
protected function runIncludedSuites($suites, $parent_dir)
|
||||
{
|
||||
foreach ($suites as $relativePath) {
|
||||
$current_dir = rtrim($parent_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $relativePath;
|
||||
$config = Configuration::config($current_dir);
|
||||
$suites = Configuration::suites();
|
||||
|
||||
$namespace = $this->currentNamespace();
|
||||
$this->output->writeln(
|
||||
"\n<fg=white;bg=magenta>\n[$namespace]: tests from $current_dir\n</fg=white;bg=magenta>"
|
||||
);
|
||||
|
||||
$this->executed += $this->runSuites($suites, $this->options['skip']);
|
||||
if (!empty($config['include'])) {
|
||||
$this->runIncludedSuites($config['include'], $current_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function currentNamespace()
|
||||
{
|
||||
$config = Configuration::config();
|
||||
if (!$config['namespace']) {
|
||||
throw new \RuntimeException(
|
||||
"Can't include into runner suite without a namespace;\n"
|
||||
. "Please add `namespace` section into included codeception.yml file"
|
||||
);
|
||||
}
|
||||
|
||||
return $config['namespace'];
|
||||
}
|
||||
|
||||
protected function runSuites($suites, $skippedSuites = [])
|
||||
{
|
||||
$executed = 0;
|
||||
foreach ($suites as $suite) {
|
||||
if (in_array($suite, $skippedSuites)) {
|
||||
continue;
|
||||
}
|
||||
if (!in_array($suite, Configuration::suites())) {
|
||||
continue;
|
||||
}
|
||||
$this->codecept->run($suite);
|
||||
$executed++;
|
||||
}
|
||||
|
||||
return $executed;
|
||||
}
|
||||
|
||||
protected function matchTestFromFilename($filename, $testsPath)
|
||||
{
|
||||
$testsPath = str_replace(['//', '\/', '\\'], '/', $testsPath);
|
||||
$filename = str_replace(['//', '\/', '\\'], '/', $filename);
|
||||
$res = preg_match("~^$testsPath/(.*?)(?>/(.*))?$~", $filename, $matches);
|
||||
|
||||
if (!$res) {
|
||||
throw new \InvalidArgumentException("Test file can't be matched");
|
||||
}
|
||||
if (!isset($matches[2])) {
|
||||
$matches[2] = null;
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
private function matchFilteredTestName(&$path)
|
||||
{
|
||||
$test_parts = explode(':', $path, 2);
|
||||
if (count($test_parts) > 1) {
|
||||
list($path, $filter) = $test_parts;
|
||||
// use carat to signify start of string like in normal regex
|
||||
// phpunit --filter matches against the fully qualified method name, so tests actually begin with :
|
||||
$carat_pos = strpos($filter, '^');
|
||||
if ($carat_pos !== false) {
|
||||
$filter = substr_replace($filter, ':', $carat_pos, 1);
|
||||
}
|
||||
return $filter;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function passedOptionKeys(InputInterface $input)
|
||||
{
|
||||
$options = [];
|
||||
$request = (string)$input;
|
||||
$tokens = explode(' ', $request);
|
||||
foreach ($tokens as $token) {
|
||||
$token = preg_replace('~=.*~', '', $token); // strip = from options
|
||||
|
||||
if (empty($token)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token == '--') {
|
||||
break; // there should be no options after ' -- ', only arguments
|
||||
}
|
||||
|
||||
if (substr($token, 0, 2) === '--') {
|
||||
$options[] = substr($token, 2);
|
||||
} elseif ($token[0] === '-') {
|
||||
$shortOption = substr($token, 1);
|
||||
$options[] = $this->getDefinition()->getOptionForShortcut($shortOption)->getName();
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
protected function booleanOptions(InputInterface $input, $options = [])
|
||||
{
|
||||
$values = [];
|
||||
$request = (string)$input;
|
||||
foreach ($options as $option => $defaultValue) {
|
||||
if (strpos($request, "--$option")) {
|
||||
$values[$option] = $input->getOption($option) ? $input->getOption($option) : $defaultValue;
|
||||
} else {
|
||||
$values[$option] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
private function ensureCurlIsAvailable()
|
||||
{
|
||||
if (!extension_loaded('curl')) {
|
||||
throw new \Exception(
|
||||
"Codeception requires CURL extension installed to make tests run\n"
|
||||
. "If you are not sure, how to install CURL, please refer to StackOverflow\n\n"
|
||||
. "Notice: PHP for Apache/Nginx and CLI can have different php.ini files.\n"
|
||||
. "Please make sure that your PHP you run from console has CURL enabled."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
311
vendor/codeception/base/src/Codeception/Command/SelfUpdate.php
vendored
Normal file
311
vendor/codeception/base/src/Codeception/Command/SelfUpdate.php
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
<?php
|
||||
namespace Codeception\Command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Codeception\Codecept;
|
||||
|
||||
/**
|
||||
* Auto-updates phar archive from official site: 'http://codeception.com/codecept.phar' .
|
||||
*
|
||||
* * `php codecept.phar self-update`
|
||||
*
|
||||
* @author Franck Cassedanne <franck@cassedanne.com>
|
||||
*/
|
||||
class SelfUpdate extends Command
|
||||
{
|
||||
/**
|
||||
* Class constants
|
||||
*/
|
||||
const NAME = 'Codeception';
|
||||
const GITHUB_REPO = 'Codeception/Codeception';
|
||||
const PHAR_URL = 'http://codeception.com/releases/%s/codecept.phar';
|
||||
const PHAR_URL_PHP54 = 'http://codeception.com/releases/%s/php54/codecept.phar';
|
||||
|
||||
/**
|
||||
* Holds the current script filename.
|
||||
* @var string
|
||||
*/
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* Holds the live version string.
|
||||
* @var string
|
||||
*/
|
||||
protected $liveVersion;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
if (isset($_SERVER['argv'], $_SERVER['argv'][0])) {
|
||||
$this->filename = $_SERVER['argv'][0];
|
||||
} else {
|
||||
$this->filename = \Phar::running(false);
|
||||
}
|
||||
|
||||
$this
|
||||
// ->setAliases(array('selfupdate'))
|
||||
->setDescription(
|
||||
sprintf(
|
||||
'Upgrade <comment>%s</comment> to the latest version',
|
||||
$this->filename
|
||||
)
|
||||
);
|
||||
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getCurrentVersion()
|
||||
{
|
||||
return Codecept::VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$version = $this->getCurrentVersion();
|
||||
|
||||
$output->writeln(
|
||||
sprintf(
|
||||
'<info>%s</info> version <comment>%s</comment>',
|
||||
self::NAME,
|
||||
$version
|
||||
)
|
||||
);
|
||||
|
||||
$output->writeln("\n<info>Checking for a new version...</info>\n");
|
||||
try {
|
||||
$latestVersion = $this->getLatestStableVersion();
|
||||
if ($this->isOutOfDate($version, $latestVersion)) {
|
||||
$output->writeln(
|
||||
sprintf(
|
||||
'A newer version is available: <comment>%s</comment>',
|
||||
$latestVersion
|
||||
)
|
||||
);
|
||||
if (!$input->getOption('no-interaction')) {
|
||||
$dialog = $this->getHelperSet()->get('question');
|
||||
$question = new ConfirmationQuestion("\n<question>Do you want to update?</question> ", false);
|
||||
if (!$dialog->ask($input, $output, $question)) {
|
||||
$output->writeln("\n<info>Bye-bye!</info>\n");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
$output->writeln("\n<info>Updating...</info>");
|
||||
|
||||
$this->retrievePharFile($latestVersion, $output);
|
||||
} else {
|
||||
$output->writeln('You are already using the latest version.');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln(
|
||||
sprintf(
|
||||
"<error>\n%s\n</error>",
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the provided version is current.
|
||||
*
|
||||
* @param string $version The version number to check.
|
||||
* @param string $latestVersion Latest stable version
|
||||
* @return boolean Returns True if a new version is available.
|
||||
*/
|
||||
private function isOutOfDate($version, $latestVersion)
|
||||
{
|
||||
return -1 != version_compare($version, $latestVersion, '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getLatestStableVersion()
|
||||
{
|
||||
$stableVersions = $this->filterStableVersions(
|
||||
$this->getGithubTags(self::GITHUB_REPO)
|
||||
);
|
||||
|
||||
return array_reduce(
|
||||
$stableVersions,
|
||||
function ($a, $b) {
|
||||
return version_compare($a, $b, '>') ? $a : $b;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $tags
|
||||
* @return array
|
||||
*/
|
||||
private function filterStableVersions($tags)
|
||||
{
|
||||
return array_filter($tags, function ($tag) {
|
||||
return preg_match('/^[0-9]+\.[0-9]+\.[0-9]+$/', $tag);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of tags from a github repo.
|
||||
*
|
||||
* @param string $repo The repository name to check upon.
|
||||
* @return array
|
||||
*/
|
||||
protected function getGithubTags($repo)
|
||||
{
|
||||
$jsonTags = $this->retrieveContentFromUrl(
|
||||
'https://api.github.com/repos/' . $repo . '/tags'
|
||||
);
|
||||
|
||||
return array_map(
|
||||
function ($tag) {
|
||||
return $tag['name'];
|
||||
},
|
||||
json_decode($jsonTags, true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the body-content from the provided URL.
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
* @throws \Exception if status code is above 300
|
||||
*/
|
||||
private function retrieveContentFromUrl($url)
|
||||
{
|
||||
$ctx = $this->prepareContext($url);
|
||||
|
||||
$body = file_get_contents($url, 0, $ctx);
|
||||
|
||||
if (isset($http_response_header)) {
|
||||
$code = substr($http_response_header[0], 9, 3);
|
||||
if (floor($code / 100) > 3) {
|
||||
throw new \Exception($http_response_header[0]);
|
||||
}
|
||||
} else {
|
||||
throw new \Exception('Request failed.');
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add proxy support to context if environment variable was set up
|
||||
*
|
||||
* @param array $opt context options
|
||||
* @param string $url
|
||||
*/
|
||||
private function prepareProxy(&$opt, $url)
|
||||
{
|
||||
$scheme = parse_url($url)['scheme'];
|
||||
if ($scheme === 'http' && (!empty($_SERVER['HTTP_PROXY']) || !empty($_SERVER['http_proxy']))) {
|
||||
$proxy = !empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY'];
|
||||
}
|
||||
|
||||
if ($scheme === 'https' && (!empty($_SERVER['HTTPS_PROXY']) || !empty($_SERVER['https_proxy']))) {
|
||||
$proxy = !empty($_SERVER['https_proxy']) ? $_SERVER['https_proxy'] : $_SERVER['HTTPS_PROXY'];
|
||||
}
|
||||
|
||||
if (!empty($proxy)) {
|
||||
$proxy = str_replace(['http://', 'https://'], ['tcp://', 'ssl://'], $proxy);
|
||||
$opt['http']['proxy'] = $proxy;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Preparing context for request
|
||||
* @param $url
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
private function prepareContext($url)
|
||||
{
|
||||
$opts = [
|
||||
'http' => [
|
||||
'follow_location' => 1,
|
||||
'max_redirects' => 20,
|
||||
'timeout' => 10,
|
||||
'user_agent' => self::NAME
|
||||
]
|
||||
];
|
||||
$this->prepareProxy($opts, $url);
|
||||
return stream_context_create($opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the latest phar file.
|
||||
*
|
||||
* @param string $version
|
||||
* @param OutputInterface $output
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function retrievePharFile($version, OutputInterface $output)
|
||||
{
|
||||
$temp = basename($this->filename, '.phar') . '-temp.phar';
|
||||
|
||||
try {
|
||||
$sourceUrl = $this->getPharUrl($version);
|
||||
if (@copy($sourceUrl, $temp)) {
|
||||
chmod($temp, 0777 & ~umask());
|
||||
|
||||
// test the phar validity
|
||||
$phar = new \Phar($temp);
|
||||
// free the variable to unlock the file
|
||||
unset($phar);
|
||||
rename($temp, $this->filename);
|
||||
} else {
|
||||
throw new \Exception('Request failed.');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if (!$e instanceof \UnexpectedValueException
|
||||
&& !$e instanceof \PharException
|
||||
) {
|
||||
throw $e;
|
||||
}
|
||||
unlink($temp);
|
||||
|
||||
$output->writeln(
|
||||
sprintf(
|
||||
"<error>\nSomething went wrong (%s).\nPlease re-run this again.</error>\n",
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$output->writeln(
|
||||
sprintf(
|
||||
"\n<comment>%s</comment> has been updated.\n",
|
||||
$this->filename
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Phar file URL for specified version
|
||||
*
|
||||
* @param string $version
|
||||
* @return string
|
||||
*/
|
||||
protected function getPharUrl($version)
|
||||
{
|
||||
$sourceUrl = self::PHAR_URL;
|
||||
if (version_compare(PHP_VERSION, '7.0.0', '<')) {
|
||||
$sourceUrl = self::PHAR_URL_PHP54;
|
||||
}
|
||||
|
||||
return sprintf($sourceUrl, $version);
|
||||
}
|
||||
}
|
||||
66
vendor/codeception/base/src/Codeception/Command/Shared/Config.php
vendored
Normal file
66
vendor/codeception/base/src/Codeception/Command/Shared/Config.php
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace Codeception\Command\Shared;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Symfony\Component\Console\Exception\InvalidOptionException;
|
||||
use Symfony\Component\Yaml\Exception\ParseException;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
trait Config
|
||||
{
|
||||
protected function getSuiteConfig($suite)
|
||||
{
|
||||
return Configuration::suiteSettings($suite, $this->getGlobalConfig());
|
||||
}
|
||||
|
||||
protected function getGlobalConfig($conf = null)
|
||||
{
|
||||
return Configuration::config($conf);
|
||||
}
|
||||
|
||||
protected function getSuites($conf = null)
|
||||
{
|
||||
return Configuration::suites();
|
||||
}
|
||||
|
||||
protected function overrideConfig($configOptions)
|
||||
{
|
||||
$updatedConfig = [];
|
||||
foreach ($configOptions as $option) {
|
||||
$keys = explode(': ', $option);
|
||||
if (count($keys) < 2) {
|
||||
throw new \InvalidArgumentException('--config-option should have config passed as "key:value"');
|
||||
}
|
||||
$value = array_pop($keys);
|
||||
$yaml = '';
|
||||
for ($ind = 0; count($keys); $ind += 2) {
|
||||
$yaml .= "\n" . str_repeat(' ', $ind) . array_shift($keys) . ': ';
|
||||
}
|
||||
$yaml .= $value;
|
||||
try {
|
||||
$config = Yaml::parse($yaml);
|
||||
} catch (ParseException $e) {
|
||||
throw new \Codeception\Exception\ParseException("Overridden config can't be parsed: \n$yaml\n" . $e->getParsedLine());
|
||||
}
|
||||
$updatedConfig = array_merge_recursive($updatedConfig, $config);
|
||||
}
|
||||
return Configuration::append($updatedConfig);
|
||||
}
|
||||
|
||||
protected function enableExtensions($extensions)
|
||||
{
|
||||
$config = ['extensions' => ['enabled' => []]];
|
||||
foreach ($extensions as $name) {
|
||||
if (!class_exists($name)) {
|
||||
$className = 'Codeception\\Extension\\' . ucfirst($name);
|
||||
if (!class_exists($className)) {
|
||||
throw new InvalidOptionException("Extension $name can't be loaded (tried by $name and $className)");
|
||||
}
|
||||
$config['extensions']['enabled'][] = $className;
|
||||
continue;
|
||||
}
|
||||
$config['extensions']['enabled'][] = $name;
|
||||
}
|
||||
return Configuration::append($config);
|
||||
}
|
||||
}
|
||||
54
vendor/codeception/base/src/Codeception/Command/Shared/FileSystem.php
vendored
Normal file
54
vendor/codeception/base/src/Codeception/Command/Shared/FileSystem.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace Codeception\Command\Shared;
|
||||
|
||||
use Codeception\Util\Shared\Namespaces;
|
||||
|
||||
trait FileSystem
|
||||
{
|
||||
use Namespaces;
|
||||
|
||||
protected function createDirectoryFor($basePath, $className = '')
|
||||
{
|
||||
$basePath = rtrim($basePath, DIRECTORY_SEPARATOR);
|
||||
if ($className) {
|
||||
$className = str_replace(['/', '\\'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], $className);
|
||||
$path = $basePath . DIRECTORY_SEPARATOR . $className;
|
||||
$basePath = pathinfo($path, PATHINFO_DIRNAME) . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
if (!file_exists($basePath)) {
|
||||
// Second argument should be mode. Well, umask() doesn't seem to return any if not set. Config may fix this.
|
||||
mkdir($basePath, 0775, true); // Third parameter commands to create directories recursively
|
||||
}
|
||||
return $basePath;
|
||||
}
|
||||
|
||||
protected function completeSuffix($filename, $suffix)
|
||||
{
|
||||
if (strpos(strrev($filename), strrev($suffix)) === 0) {
|
||||
$filename .= '.php';
|
||||
}
|
||||
if (strpos(strrev($filename), strrev($suffix . '.php')) !== 0) {
|
||||
$filename .= $suffix . '.php';
|
||||
}
|
||||
if (strpos(strrev($filename), strrev('.php')) !== 0) {
|
||||
$filename .= '.php';
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
protected function removeSuffix($classname, $suffix)
|
||||
{
|
||||
$classname = preg_replace('~\.php$~', '', $classname);
|
||||
return preg_replace("~$suffix$~", '', $classname);
|
||||
}
|
||||
|
||||
protected function createFile($filename, $contents, $force = false, $flags = null)
|
||||
{
|
||||
if (file_exists($filename) && !$force) {
|
||||
return false;
|
||||
}
|
||||
file_put_contents($filename, $contents, $flags);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
16
vendor/codeception/base/src/Codeception/Command/Shared/Style.php
vendored
Normal file
16
vendor/codeception/base/src/Codeception/Command/Shared/Style.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace Codeception\Command\Shared;
|
||||
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
trait Style
|
||||
{
|
||||
public function addStyles(OutputInterface $output)
|
||||
{
|
||||
$output->getFormatter()->setStyle('notice', new OutputFormatterStyle('white', 'green', ['bold']));
|
||||
$output->getFormatter()->setStyle('bold', new OutputFormatterStyle(null, null, ['bold']));
|
||||
$output->getFormatter()->setStyle('warning', new OutputFormatterStyle(null, 'yellow', ['bold']));
|
||||
$output->getFormatter()->setStyle('debug', new OutputFormatterStyle('cyan'));
|
||||
}
|
||||
}
|
||||
767
vendor/codeception/base/src/Codeception/Configuration.php
vendored
Normal file
767
vendor/codeception/base/src/Codeception/Configuration.php
vendored
Normal file
@@ -0,0 +1,767 @@
|
||||
<?php
|
||||
|
||||
namespace Codeception;
|
||||
|
||||
use Codeception\Exception\ConfigurationException;
|
||||
use Codeception\Lib\ParamsLoader;
|
||||
use Codeception\Util\Autoload;
|
||||
use Codeception\Util\Template;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Symfony\Component\Yaml\Exception\ParseException;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class Configuration
|
||||
{
|
||||
protected static $suites = [];
|
||||
|
||||
/**
|
||||
* @var array Current configuration
|
||||
*/
|
||||
protected static $config = null;
|
||||
|
||||
/**
|
||||
* @var array environmental files configuration cache
|
||||
*/
|
||||
protected static $envConfig = [];
|
||||
|
||||
/**
|
||||
* @var string Directory containing main configuration file.
|
||||
* @see self::projectDir()
|
||||
*/
|
||||
protected static $dir = null;
|
||||
|
||||
/**
|
||||
* @var string Current project output directory.
|
||||
*/
|
||||
protected static $outputDir = null;
|
||||
|
||||
/**
|
||||
* @var string Current project data directory. This directory is used to hold
|
||||
* sql dumps and other things needed for current project tests.
|
||||
*/
|
||||
protected static $dataDir = null;
|
||||
|
||||
/**
|
||||
* @var string Directory with test support files like Actors, Helpers, PageObjects, etc
|
||||
*/
|
||||
protected static $supportDir = null;
|
||||
|
||||
/**
|
||||
* @var string Directory containing environment configuration files.
|
||||
*/
|
||||
protected static $envsDir = null;
|
||||
|
||||
/**
|
||||
* @var string Directory containing tests and suites of the current project.
|
||||
*/
|
||||
protected static $testsDir = null;
|
||||
|
||||
public static $lock = false;
|
||||
|
||||
protected static $di;
|
||||
|
||||
/**
|
||||
* @var array Default config
|
||||
*/
|
||||
public static $defaultConfig = [
|
||||
'actor_suffix'=> 'Tester',
|
||||
'namespace' => '',
|
||||
'include' => [],
|
||||
'paths' => [],
|
||||
'extends' => null,
|
||||
'suites' => [],
|
||||
'modules' => [],
|
||||
'extensions' => [
|
||||
'enabled' => [],
|
||||
'config' => [],
|
||||
'commands' => [],
|
||||
],
|
||||
'reporters' => [
|
||||
'xml' => 'Codeception\PHPUnit\Log\JUnit',
|
||||
'html' => 'Codeception\PHPUnit\ResultPrinter\HTML',
|
||||
'report' => 'Codeception\PHPUnit\ResultPrinter\Report',
|
||||
'tap' => 'PHPUnit\Util\Log\TAP',
|
||||
'json' => 'PHPUnit\Util\Log\JSON',
|
||||
],
|
||||
'groups' => [],
|
||||
'settings' => [
|
||||
'colors' => true,
|
||||
'bootstrap' => false,
|
||||
'strict_xml' => false,
|
||||
'lint' => true,
|
||||
'backup_globals' => true,
|
||||
'log_incomplete_skipped' => false,
|
||||
'report_useless_tests' => false,
|
||||
'disallow_test_output' => false,
|
||||
'be_strict_about_changes_to_global_state' => false
|
||||
],
|
||||
'coverage' => [],
|
||||
'params' => [],
|
||||
'gherkin' => []
|
||||
];
|
||||
|
||||
public static $defaultSuiteSettings = [
|
||||
'actor' => null,
|
||||
'class_name' => null, // Codeception <2.3 compatibility
|
||||
'modules' => [
|
||||
'enabled' => [],
|
||||
'config' => [],
|
||||
'depends' => []
|
||||
],
|
||||
'path' => null,
|
||||
'extends' => null,
|
||||
'namespace' => null,
|
||||
'groups' => [],
|
||||
'formats' => [],
|
||||
'shuffle' => false,
|
||||
'extensions' => [ // suite extensions
|
||||
'enabled' => [],
|
||||
'config' => [],
|
||||
],
|
||||
'error_level' => 'E_ALL & ~E_STRICT & ~E_DEPRECATED',
|
||||
];
|
||||
|
||||
protected static $params;
|
||||
|
||||
/**
|
||||
* Loads global config file which is `codeception.yml` by default.
|
||||
* When config is already loaded - returns it.
|
||||
*
|
||||
* @param null $configFile
|
||||
* @return array
|
||||
* @throws Exception\ConfigurationException
|
||||
*/
|
||||
public static function config($configFile = null)
|
||||
{
|
||||
if (!$configFile && self::$config) {
|
||||
return self::$config;
|
||||
}
|
||||
|
||||
if (self::$config && self::$lock) {
|
||||
return self::$config;
|
||||
}
|
||||
|
||||
if ($configFile === null) {
|
||||
$configFile = getcwd() . DIRECTORY_SEPARATOR . 'codeception.yml';
|
||||
}
|
||||
|
||||
if (is_dir($configFile)) {
|
||||
$configFile = $configFile . DIRECTORY_SEPARATOR . 'codeception.yml';
|
||||
}
|
||||
|
||||
$dir = realpath(dirname($configFile));
|
||||
self::$dir = $dir;
|
||||
|
||||
$configDistFile = $dir . DIRECTORY_SEPARATOR . 'codeception.dist.yml';
|
||||
|
||||
if (!(file_exists($configDistFile) || file_exists($configFile))) {
|
||||
throw new ConfigurationException("Configuration file could not be found.\nRun `bootstrap` to initialize Codeception.", 404);
|
||||
}
|
||||
|
||||
// Preload config to retrieve params such that they are applied to codeception config file below
|
||||
$tempConfig = self::$defaultConfig;
|
||||
|
||||
$distConfigContents = "";
|
||||
if (file_exists($configDistFile)) {
|
||||
$distConfigContents = file_get_contents($configDistFile);
|
||||
$tempConfig = self::mergeConfigs($tempConfig, self::getConfFromContents($distConfigContents, $configDistFile));
|
||||
}
|
||||
|
||||
$configContents = "";
|
||||
if (file_exists($configFile)) {
|
||||
$configContents = file_get_contents($configFile);
|
||||
$tempConfig = self::mergeConfigs($tempConfig, self::getConfFromContents($configContents, $configFile));
|
||||
}
|
||||
self::prepareParams($tempConfig);
|
||||
|
||||
// load config using params
|
||||
$config = self::mergeConfigs(self::$defaultConfig, self::getConfFromContents($distConfigContents, $configDistFile));
|
||||
$config = self::mergeConfigs($config, self::getConfFromContents($configContents, $configFile));
|
||||
|
||||
if ($config == self::$defaultConfig) {
|
||||
throw new ConfigurationException("Configuration file is invalid");
|
||||
}
|
||||
|
||||
// we check for the "extends" key in the yml file
|
||||
if (isset($config['extends'])) {
|
||||
// and now we search for the file
|
||||
$presetFilePath = codecept_absolute_path($config['extends']);
|
||||
if (file_exists($presetFilePath)) {
|
||||
// and merge it with our configuration file
|
||||
$config = self::mergeConfigs(self::getConfFromFile($presetFilePath), $config);
|
||||
}
|
||||
}
|
||||
|
||||
self::$config = $config;
|
||||
|
||||
// compatibility with suites created by Codeception < 2.3.0
|
||||
if (!isset($config['paths']['output']) and isset($config['paths']['log'])) {
|
||||
$config['paths']['output'] = $config['paths']['log'];
|
||||
}
|
||||
|
||||
if (isset(self::$config['actor'])) {
|
||||
self::$config['actor_suffix'] = self::$config['actor']; // old compatibility
|
||||
}
|
||||
|
||||
if (!isset($config['paths']['support']) and isset($config['paths']['helpers'])) {
|
||||
$config['paths']['support'] = $config['paths']['helpers'];
|
||||
}
|
||||
|
||||
if (!isset($config['paths']['output'])) {
|
||||
throw new ConfigurationException('Output path is not defined by key "paths: output"');
|
||||
}
|
||||
|
||||
self::$outputDir = $config['paths']['output'];
|
||||
|
||||
// fill up includes with wildcard expansions
|
||||
$config['include'] = self::expandWildcardedIncludes($config['include']);
|
||||
|
||||
// config without tests, for inclusion of other configs
|
||||
if (count($config['include'])) {
|
||||
self::$config = $config;
|
||||
if (!isset($config['paths']['tests'])) {
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($config['paths']['tests'])) {
|
||||
throw new ConfigurationException(
|
||||
'Tests directory is not defined in Codeception config by key "paths: tests:"'
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset($config['paths']['data'])) {
|
||||
throw new ConfigurationException('Data path is not defined Codeception config by key "paths: data"');
|
||||
}
|
||||
|
||||
if (!isset($config['paths']['support'])) {
|
||||
throw new ConfigurationException('Helpers path is not defined by key "paths: support"');
|
||||
}
|
||||
|
||||
self::$dataDir = $config['paths']['data'];
|
||||
self::$supportDir = $config['paths']['support'];
|
||||
self::$testsDir = $config['paths']['tests'];
|
||||
|
||||
if (isset($config['paths']['envs'])) {
|
||||
self::$envsDir = $config['paths']['envs'];
|
||||
}
|
||||
|
||||
Autoload::addNamespace(self::$config['namespace'], self::supportDir());
|
||||
self::loadBootstrap($config['settings']['bootstrap']);
|
||||
self::loadSuites();
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
protected static function loadBootstrap($bootstrap)
|
||||
{
|
||||
if (!$bootstrap) {
|
||||
return;
|
||||
}
|
||||
$bootstrap = self::$dir . DIRECTORY_SEPARATOR . self::$testsDir . DIRECTORY_SEPARATOR . $bootstrap;
|
||||
if (file_exists($bootstrap)) {
|
||||
include_once $bootstrap;
|
||||
}
|
||||
}
|
||||
|
||||
protected static function loadSuites()
|
||||
{
|
||||
$suites = Finder::create()
|
||||
->files()
|
||||
->name('*.{suite,suite.dist}.yml')
|
||||
->in(self::$dir . DIRECTORY_SEPARATOR . self::$testsDir)
|
||||
->depth('< 1')
|
||||
->sortByName();
|
||||
|
||||
self::$suites = [];
|
||||
|
||||
foreach (array_keys(self::$config['suites']) as $suite) {
|
||||
self::$suites[$suite] = $suite;
|
||||
}
|
||||
|
||||
/** @var SplFileInfo $suite */
|
||||
foreach ($suites as $suite) {
|
||||
preg_match('~(.*?)(\.suite|\.suite\.dist)\.yml~', $suite->getFilename(), $matches);
|
||||
self::$suites[$matches[1]] = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns suite configuration. Requires suite name and global config used (Configuration::config)
|
||||
*
|
||||
* @param string $suite
|
||||
* @param array $config
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function suiteSettings($suite, $config)
|
||||
{
|
||||
// cut namespace name from suite name
|
||||
if ($suite != $config['namespace'] && substr($suite, 0, strlen($config['namespace'])) == $config['namespace']) {
|
||||
$suite = substr($suite, strlen($config['namespace']));
|
||||
}
|
||||
|
||||
if (!in_array($suite, self::$suites)) {
|
||||
throw new ConfigurationException("Suite $suite was not loaded");
|
||||
}
|
||||
|
||||
// load global config
|
||||
$globalConf = $config['settings'];
|
||||
foreach (['modules', 'coverage', 'namespace', 'groups', 'env', 'gherkin', 'extensions'] as $key) {
|
||||
if (isset($config[$key])) {
|
||||
$globalConf[$key] = $config[$key];
|
||||
}
|
||||
}
|
||||
$settings = self::mergeConfigs(self::$defaultSuiteSettings, $globalConf);
|
||||
|
||||
// load suite config
|
||||
$settings = self::loadSuiteConfig($suite, $config['paths']['tests'], $settings);
|
||||
// load from environment configs
|
||||
if (isset($config['paths']['envs'])) {
|
||||
$envConf = self::loadEnvConfigs(self::$dir . DIRECTORY_SEPARATOR . $config['paths']['envs']);
|
||||
$settings = self::mergeConfigs($settings, $envConf);
|
||||
}
|
||||
|
||||
if (!$settings['actor']) {
|
||||
// Codeception 2.2 compatibility
|
||||
$settings['actor'] = $settings['class_name'];
|
||||
}
|
||||
|
||||
if (!$settings['path']) {
|
||||
// take a suite path from its name
|
||||
$settings['path'] = $suite;
|
||||
}
|
||||
|
||||
$config['paths']['tests'] = str_replace('/', DIRECTORY_SEPARATOR, $config['paths']['tests']);
|
||||
|
||||
$settings['path'] = self::$dir . DIRECTORY_SEPARATOR . $config['paths']['tests']
|
||||
. DIRECTORY_SEPARATOR . $settings['path'] . DIRECTORY_SEPARATOR;
|
||||
|
||||
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads environments configuration from set directory
|
||||
*
|
||||
* @param string $path path to the directory
|
||||
* @return array
|
||||
*/
|
||||
protected static function loadEnvConfigs($path)
|
||||
{
|
||||
if (isset(self::$envConfig[$path])) {
|
||||
return self::$envConfig[$path];
|
||||
}
|
||||
if (!is_dir($path)) {
|
||||
self::$envConfig[$path] = [];
|
||||
return self::$envConfig[$path];
|
||||
}
|
||||
|
||||
$envFiles = Finder::create()
|
||||
->files()
|
||||
->name('*.yml')
|
||||
->in($path)
|
||||
->depth('< 2');
|
||||
|
||||
$envConfig = [];
|
||||
/** @var SplFileInfo $envFile */
|
||||
foreach ($envFiles as $envFile) {
|
||||
$env = str_replace(['.dist.yml', '.yml'], '', $envFile->getFilename());
|
||||
$envConfig[$env] = [];
|
||||
$envPath = $path;
|
||||
if ($envFile->getRelativePath()) {
|
||||
$envPath .= DIRECTORY_SEPARATOR . $envFile->getRelativePath();
|
||||
}
|
||||
foreach (['.dist.yml', '.yml'] as $suffix) {
|
||||
$envConf = self::getConfFromFile($envPath . DIRECTORY_SEPARATOR . $env . $suffix, null);
|
||||
if ($envConf === null) {
|
||||
continue;
|
||||
}
|
||||
$envConfig[$env] = self::mergeConfigs($envConfig[$env], $envConf);
|
||||
}
|
||||
}
|
||||
|
||||
self::$envConfig[$path] = ['env' => $envConfig];
|
||||
return self::$envConfig[$path];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads configuration from Yaml data
|
||||
*
|
||||
* @param string $contents Yaml config file contents
|
||||
* @param string $filename which is supposed to be loaded
|
||||
* @return array
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
protected static function getConfFromContents($contents, $filename = '(.yml)')
|
||||
{
|
||||
if (self::$params) {
|
||||
$template = new Template($contents, '%', '%');
|
||||
$template->setVars(self::$params);
|
||||
$contents = $template->produce();
|
||||
}
|
||||
|
||||
try {
|
||||
return Yaml::parse($contents);
|
||||
} catch (ParseException $exception) {
|
||||
throw new ConfigurationException(
|
||||
sprintf(
|
||||
"Error loading Yaml config from `%s`\n \n%s\nRead more about Yaml format https://goo.gl/9UPuEC",
|
||||
$filename,
|
||||
$exception->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads configuration from Yaml file or returns given value if the file doesn't exist
|
||||
*
|
||||
* @param string $filename filename
|
||||
* @param mixed $nonExistentValue value used if filename is not found
|
||||
* @return array
|
||||
*/
|
||||
protected static function getConfFromFile($filename, $nonExistentValue = [])
|
||||
{
|
||||
if (file_exists($filename)) {
|
||||
$yaml = file_get_contents($filename);
|
||||
return self::getConfFromContents($yaml, $filename);
|
||||
}
|
||||
return $nonExistentValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all possible suite configurations according environment rules.
|
||||
* Suite configurations will contain `current_environment` key which specifies what environment used.
|
||||
*
|
||||
* @param $suite
|
||||
* @return array
|
||||
*/
|
||||
public static function suiteEnvironments($suite)
|
||||
{
|
||||
$settings = self::suiteSettings($suite, self::config());
|
||||
|
||||
if (!isset($settings['env']) || !is_array($settings['env'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$environments = [];
|
||||
|
||||
foreach ($settings['env'] as $env => $envConfig) {
|
||||
$environments[$env] = $envConfig ? self::mergeConfigs($settings, $envConfig) : $settings;
|
||||
$environments[$env]['current_environment'] = $env;
|
||||
}
|
||||
|
||||
return $environments;
|
||||
}
|
||||
|
||||
public static function suites()
|
||||
{
|
||||
return self::$suites;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of enabled modules according suite config.
|
||||
*
|
||||
* @param array $settings suite settings
|
||||
* @return array
|
||||
*/
|
||||
public static function modules($settings)
|
||||
{
|
||||
return array_filter(
|
||||
array_map(
|
||||
function ($m) {
|
||||
return is_array($m) ? key($m) : $m;
|
||||
},
|
||||
$settings['modules']['enabled'],
|
||||
array_keys($settings['modules']['enabled'])
|
||||
),
|
||||
function ($m) use ($settings) {
|
||||
if (!isset($settings['modules']['disabled'])) {
|
||||
return true;
|
||||
}
|
||||
return !in_array($m, $settings['modules']['disabled']);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static function isExtensionEnabled($extensionName)
|
||||
{
|
||||
return isset(self::$config['extensions'], self::$config['extensions']['enabled'])
|
||||
&& in_array($extensionName, self::$config['extensions']['enabled']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current path to `_data` dir.
|
||||
* Use it to store database fixtures, sql dumps, or other files required by your tests.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function dataDir()
|
||||
{
|
||||
return self::$dir . DIRECTORY_SEPARATOR . self::$dataDir . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current path to `_helpers` dir.
|
||||
* Helpers are custom modules.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function supportDir()
|
||||
{
|
||||
return self::$dir . DIRECTORY_SEPARATOR . self::$supportDir . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns actual path to current `_output` dir.
|
||||
* Use it in Helpers or Groups to save result or temporary files.
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception\ConfigurationException
|
||||
*/
|
||||
public static function outputDir()
|
||||
{
|
||||
if (!self::$outputDir) {
|
||||
throw new ConfigurationException("Path for output not specified. Please, set output path in global config");
|
||||
}
|
||||
|
||||
$dir = self::$outputDir . DIRECTORY_SEPARATOR;
|
||||
if (strcmp(self::$outputDir[0], "/") !== 0) {
|
||||
$dir = self::$dir . DIRECTORY_SEPARATOR . $dir;
|
||||
}
|
||||
|
||||
if (!file_exists($dir)) {
|
||||
@mkdir($dir, 0777, true);
|
||||
}
|
||||
|
||||
if (!is_writable($dir)) {
|
||||
@chmod($dir, 0777);
|
||||
}
|
||||
|
||||
if (!is_writable($dir)) {
|
||||
throw new ConfigurationException(
|
||||
"Path for output is not writable. Please, set appropriate access mode for output path."
|
||||
);
|
||||
}
|
||||
|
||||
return $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility alias to `Configuration::logDir()`
|
||||
* @return string
|
||||
*/
|
||||
public static function logDir()
|
||||
{
|
||||
return self::outputDir();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to the root of your project.
|
||||
* Basically returns path to current `codeception.yml` loaded.
|
||||
* Use this method instead of `__DIR__`, `getcwd()` or anything else.
|
||||
* @return string
|
||||
*/
|
||||
public static function projectDir()
|
||||
{
|
||||
return self::$dir . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns path to tests directory
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function testsDir()
|
||||
{
|
||||
return self::$dir . DIRECTORY_SEPARATOR . self::$testsDir . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current path to `_envs` dir.
|
||||
* Use it to store environment specific configuration.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function envsDir()
|
||||
{
|
||||
if (!self::$envsDir) {
|
||||
return null;
|
||||
}
|
||||
return self::$dir . DIRECTORY_SEPARATOR . self::$envsDir . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a meta-configuration file that just points to other `codeception.yml`?
|
||||
* If so, it may have no tests by itself.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isEmpty()
|
||||
{
|
||||
return !(bool)self::$testsDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds parameters to config
|
||||
*
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
public static function append(array $config = [])
|
||||
{
|
||||
self::$config = self::mergeConfigs(self::$config, $config);
|
||||
|
||||
if (isset(self::$config['paths']['output'])) {
|
||||
self::$outputDir = self::$config['paths']['output'];
|
||||
}
|
||||
if (isset(self::$config['paths']['data'])) {
|
||||
self::$dataDir = self::$config['paths']['data'];
|
||||
}
|
||||
if (isset(self::$config['paths']['support'])) {
|
||||
self::$supportDir = self::$config['paths']['support'];
|
||||
}
|
||||
if (isset(self::$config['paths']['tests'])) {
|
||||
self::$testsDir = self::$config['paths']['tests'];
|
||||
}
|
||||
|
||||
return self::$config;
|
||||
}
|
||||
|
||||
public static function mergeConfigs($a1, $a2)
|
||||
{
|
||||
if (!is_array($a1)) {
|
||||
return $a2;
|
||||
}
|
||||
|
||||
if (!is_array($a2)) {
|
||||
return $a1;
|
||||
}
|
||||
|
||||
$res = [];
|
||||
|
||||
// for sequential arrays
|
||||
if (isset($a1[0], $a2[0])) {
|
||||
return array_merge_recursive($a2, $a1);
|
||||
}
|
||||
|
||||
// for associative arrays
|
||||
foreach ($a2 as $k2 => $v2) {
|
||||
if (!isset($a1[$k2])) { // if no such key
|
||||
$res[$k2] = $v2;
|
||||
unset($a1[$k2]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$res[$k2] = self::mergeConfigs($a1[$k2], $v2);
|
||||
unset($a1[$k2]);
|
||||
}
|
||||
|
||||
foreach ($a1 as $k1 => $v1) { // only single elements here left
|
||||
$res[$k1] = $v1;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads config from *.dist.suite.yml and *.suite.yml
|
||||
*
|
||||
* @param $suite
|
||||
* @param $path
|
||||
* @param $settings
|
||||
* @return array
|
||||
*/
|
||||
protected static function loadSuiteConfig($suite, $path, $settings)
|
||||
{
|
||||
if (isset(self::$config['suites'][$suite])) {
|
||||
// bundled config
|
||||
return self::mergeConfigs($settings, self::$config['suites'][$suite]);
|
||||
}
|
||||
|
||||
$suiteDir = self::$dir . DIRECTORY_SEPARATOR . $path;
|
||||
|
||||
$suiteDistConf = self::getConfFromFile($suiteDir . DIRECTORY_SEPARATOR . "$suite.suite.dist.yml");
|
||||
$suiteConf = self::getConfFromFile($suiteDir . DIRECTORY_SEPARATOR . "$suite.suite.yml");
|
||||
|
||||
// now we check the suite config file, if a extends key is defined
|
||||
if (isset($suiteConf['extends'])) {
|
||||
$presetFilePath = codecept_is_path_absolute($suiteConf['extends'])
|
||||
? $suiteConf['extends'] // If path is absolute – use it
|
||||
: realpath($suiteDir . DIRECTORY_SEPARATOR . $suiteConf['extends']); // Otherwise try to locate it in the suite dir
|
||||
|
||||
if (file_exists($presetFilePath)) {
|
||||
$settings = self::mergeConfigs(self::getConfFromFile($presetFilePath), $settings);
|
||||
}
|
||||
}
|
||||
|
||||
$settings = self::mergeConfigs($settings, $suiteDistConf);
|
||||
$settings = self::mergeConfigs($settings, $suiteConf);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces wildcarded items in include array with real paths.
|
||||
*
|
||||
* @param $includes
|
||||
* @return array
|
||||
*/
|
||||
protected static function expandWildcardedIncludes(array $includes)
|
||||
{
|
||||
if (empty($includes)) {
|
||||
return $includes;
|
||||
}
|
||||
$expandedIncludes = [];
|
||||
foreach ($includes as $include) {
|
||||
$expandedIncludes = array_merge($expandedIncludes, self::expandWildcardsFor($include));
|
||||
}
|
||||
return $expandedIncludes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds config files in given wildcarded include path.
|
||||
* Returns the expanded paths or the original if not a wildcard.
|
||||
*
|
||||
* @param $include
|
||||
* @return array
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
protected static function expandWildcardsFor($include)
|
||||
{
|
||||
if (1 !== preg_match('/[\?\.\*]/', $include)) {
|
||||
return [$include,];
|
||||
}
|
||||
|
||||
try {
|
||||
$configFiles = Finder::create()->files()
|
||||
->name('/codeception(\.dist\.yml|\.yml)/')
|
||||
->in(self::$dir . DIRECTORY_SEPARATOR . $include);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw new ConfigurationException(
|
||||
"Configuration file(s) could not be found in \"$include\"."
|
||||
);
|
||||
}
|
||||
|
||||
$paths = [];
|
||||
foreach ($configFiles as $file) {
|
||||
$paths[] = codecept_relative_path($file->getPath());
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
private static function prepareParams($settings)
|
||||
{
|
||||
self::$params = [];
|
||||
$paramsLoader = new ParamsLoader();
|
||||
|
||||
foreach ($settings['params'] as $paramStorage) {
|
||||
static::$params = array_merge(self::$params, $paramsLoader->load($paramStorage));
|
||||
}
|
||||
}
|
||||
}
|
||||
175
vendor/codeception/base/src/Codeception/Coverage/Filter.php
vendored
Normal file
175
vendor/codeception/base/src/Codeception/Coverage/Filter.php
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
namespace Codeception\Coverage;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Exception\ConfigurationException;
|
||||
use Codeception\Exception\ModuleException;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
class Filter
|
||||
{
|
||||
/**
|
||||
* @var \SebastianBergmann\CodeCoverage\CodeCoverage
|
||||
*/
|
||||
protected $phpCodeCoverage = null;
|
||||
|
||||
/**
|
||||
* @var Filter
|
||||
*/
|
||||
protected static $c3;
|
||||
|
||||
/**
|
||||
* @var \SebastianBergmann\CodeCoverage\Filter
|
||||
*/
|
||||
protected $filter = null;
|
||||
|
||||
public function __construct(\SebastianBergmann\CodeCoverage\CodeCoverage $phpCoverage)
|
||||
{
|
||||
$this->phpCodeCoverage = $phpCoverage
|
||||
? $phpCoverage
|
||||
: new \SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
|
||||
$this->filter = $this->phpCodeCoverage->filter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \SebastianBergmann\CodeCoverage\CodeCoverage $phpCoverage
|
||||
* @return Filter
|
||||
*/
|
||||
public static function setup(\SebastianBergmann\CodeCoverage\CodeCoverage $phpCoverage)
|
||||
{
|
||||
self::$c3 = new self($phpCoverage);
|
||||
return self::$c3;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|\SebastianBergmann\CodeCoverage\CodeCoverage
|
||||
*/
|
||||
public function getPhpCodeCoverage()
|
||||
{
|
||||
return $this->phpCodeCoverage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $config
|
||||
* @return Filter
|
||||
*/
|
||||
public function whiteList($config)
|
||||
{
|
||||
$filter = $this->filter;
|
||||
if (!isset($config['coverage'])) {
|
||||
return $this;
|
||||
}
|
||||
$coverage = $config['coverage'];
|
||||
if (!isset($coverage['whitelist'])) {
|
||||
$coverage['whitelist'] = [];
|
||||
if (isset($coverage['include'])) {
|
||||
$coverage['whitelist']['include'] = $coverage['include'];
|
||||
}
|
||||
if (isset($coverage['exclude'])) {
|
||||
$coverage['whitelist']['exclude'] = $coverage['exclude'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($coverage['whitelist']['include'])) {
|
||||
if (!is_array($coverage['whitelist']['include'])) {
|
||||
throw new ConfigurationException('Error parsing yaml. Config `whitelist: include:` should be an array');
|
||||
}
|
||||
foreach ($coverage['whitelist']['include'] as $fileOrDir) {
|
||||
$finder = strpos($fileOrDir, '*') === false
|
||||
? [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir]
|
||||
: $this->matchWildcardPattern($fileOrDir);
|
||||
|
||||
foreach ($finder as $file) {
|
||||
$filter->addFileToWhitelist($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($coverage['whitelist']['exclude'])) {
|
||||
if (!is_array($coverage['whitelist']['exclude'])) {
|
||||
throw new ConfigurationException('Error parsing yaml. Config `whitelist: exclude:` should be an array');
|
||||
}
|
||||
foreach ($coverage['whitelist']['exclude'] as $fileOrDir) {
|
||||
$finder = strpos($fileOrDir, '*') === false
|
||||
? [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir]
|
||||
: $this->matchWildcardPattern($fileOrDir);
|
||||
|
||||
foreach ($finder as $file) {
|
||||
$filter->removeFileFromWhitelist($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $config
|
||||
* @return Filter
|
||||
*/
|
||||
public function blackList($config)
|
||||
{
|
||||
$filter = $this->filter;
|
||||
if (!isset($config['coverage'])) {
|
||||
return $this;
|
||||
}
|
||||
$coverage = $config['coverage'];
|
||||
if (isset($coverage['blacklist'])) {
|
||||
if (!method_exists($filter, 'addFileToBlacklist')) {
|
||||
throw new ModuleException($this, 'The blacklist functionality has been removed from PHPUnit 5,'
|
||||
. ' please remove blacklist section from configuration.');
|
||||
}
|
||||
|
||||
if (isset($coverage['blacklist']['include'])) {
|
||||
foreach ($coverage['blacklist']['include'] as $fileOrDir) {
|
||||
$finder = strpos($fileOrDir, '*') === false
|
||||
? [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir]
|
||||
: $this->matchWildcardPattern($fileOrDir);
|
||||
|
||||
foreach ($finder as $file) {
|
||||
$filter->addFileToBlacklist($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($coverage['blacklist']['exclude'])) {
|
||||
foreach ($coverage['blacklist']['exclude'] as $fileOrDir) {
|
||||
$finder = strpos($fileOrDir, '*') === false
|
||||
? [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir]
|
||||
: $this->matchWildcardPattern($fileOrDir);
|
||||
|
||||
foreach ($finder as $file) {
|
||||
$filter->removeFileFromBlacklist($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function matchWildcardPattern($pattern)
|
||||
{
|
||||
$finder = Finder::create();
|
||||
$fileOrDir = str_replace('\\', '/', $pattern);
|
||||
$parts = explode('/', $fileOrDir);
|
||||
$file = array_pop($parts);
|
||||
$finder->name($file);
|
||||
if (count($parts)) {
|
||||
$last_path = array_pop($parts);
|
||||
if ($last_path === '*') {
|
||||
$finder->in(Configuration::projectDir() . implode('/', $parts));
|
||||
} else {
|
||||
$finder->in(Configuration::projectDir() . implode('/', $parts) . '/' . $last_path);
|
||||
}
|
||||
}
|
||||
$finder->ignoreVCS(true)->files();
|
||||
return $finder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \SebastianBergmann\CodeCoverage\Filter
|
||||
*/
|
||||
public function getFilter()
|
||||
{
|
||||
return $this->filter;
|
||||
}
|
||||
}
|
||||
47
vendor/codeception/base/src/Codeception/Coverage/Subscriber/Local.php
vendored
Normal file
47
vendor/codeception/base/src/Codeception/Coverage/Subscriber/Local.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace Codeception\Coverage\Subscriber;
|
||||
|
||||
use Codeception\Coverage\SuiteSubscriber;
|
||||
use Codeception\Event\SuiteEvent;
|
||||
use Codeception\Events;
|
||||
use Codeception\Lib\Interfaces\Remote;
|
||||
|
||||
/**
|
||||
* Collects code coverage from unit and functional tests.
|
||||
* Results from all suites are merged.
|
||||
*/
|
||||
class Local extends SuiteSubscriber
|
||||
{
|
||||
public static $events = [
|
||||
Events::SUITE_BEFORE => 'beforeSuite',
|
||||
Events::SUITE_AFTER => 'afterSuite',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var Remote
|
||||
*/
|
||||
protected $module;
|
||||
|
||||
protected function isEnabled()
|
||||
{
|
||||
return $this->module === null and $this->settings['enabled'];
|
||||
}
|
||||
|
||||
public function beforeSuite(SuiteEvent $e)
|
||||
{
|
||||
$this->applySettings($e->getSettings());
|
||||
$this->module = $this->getServerConnectionModule($e->getSuite()->getModules());
|
||||
if (!$this->isEnabled()) {
|
||||
return;
|
||||
}
|
||||
$this->applyFilter($e->getResult());
|
||||
}
|
||||
|
||||
public function afterSuite(SuiteEvent $e)
|
||||
{
|
||||
if (!$this->isEnabled()) {
|
||||
return;
|
||||
}
|
||||
$this->mergeToPrint($e->getResult()->getCodeCoverage());
|
||||
}
|
||||
}
|
||||
238
vendor/codeception/base/src/Codeception/Coverage/Subscriber/LocalServer.php
vendored
Normal file
238
vendor/codeception/base/src/Codeception/Coverage/Subscriber/LocalServer.php
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
namespace Codeception\Coverage\Subscriber;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Coverage\SuiteSubscriber;
|
||||
use Codeception\Event\StepEvent;
|
||||
use Codeception\Event\SuiteEvent;
|
||||
use Codeception\Event\TestEvent;
|
||||
use Codeception\Events;
|
||||
use Codeception\Exception\ModuleException;
|
||||
use Codeception\Exception\RemoteException;
|
||||
|
||||
/**
|
||||
* When collecting code coverage data from local server HTTP requests are sent to c3.php file.
|
||||
* Coverage Collection is started by sending cookies/headers.
|
||||
* Result is taken from the local file and merged with local code coverage results.
|
||||
*
|
||||
* Class LocalServer
|
||||
* @package Codeception\Coverage\Subscriber
|
||||
*/
|
||||
class LocalServer extends SuiteSubscriber
|
||||
{
|
||||
// headers
|
||||
const COVERAGE_HEADER = 'X-Codeception-CodeCoverage';
|
||||
const COVERAGE_HEADER_ERROR = 'X-Codeception-CodeCoverage-Error';
|
||||
const COVERAGE_HEADER_CONFIG = 'X-Codeception-CodeCoverage-Config';
|
||||
const COVERAGE_HEADER_SUITE = 'X-Codeception-CodeCoverage-Suite';
|
||||
|
||||
// cookie names
|
||||
const COVERAGE_COOKIE = 'CODECEPTION_CODECOVERAGE';
|
||||
const COVERAGE_COOKIE_ERROR = 'CODECEPTION_CODECOVERAGE_ERROR';
|
||||
|
||||
protected $suiteName;
|
||||
protected $c3Access = [
|
||||
'http' => [
|
||||
'method' => "GET",
|
||||
'header' => ''
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \Codeception\Lib\Interfaces\Web
|
||||
*/
|
||||
protected $module;
|
||||
|
||||
public static $events = [
|
||||
Events::SUITE_BEFORE => 'beforeSuite',
|
||||
Events::TEST_BEFORE => 'beforeTest',
|
||||
Events::STEP_AFTER => 'afterStep',
|
||||
Events::SUITE_AFTER => 'afterSuite',
|
||||
];
|
||||
|
||||
protected function isEnabled()
|
||||
{
|
||||
return $this->module && !$this->settings['remote'] && $this->settings['enabled'];
|
||||
}
|
||||
|
||||
public function beforeSuite(SuiteEvent $e)
|
||||
{
|
||||
$this->module = $this->getServerConnectionModule($e->getSuite()->getModules());
|
||||
$this->applySettings($e->getSettings());
|
||||
if (!$this->isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->suiteName = $e->getSuite()->getBaseName();
|
||||
|
||||
if ($this->settings['remote_config']) {
|
||||
$this->addC3AccessHeader(self::COVERAGE_HEADER_CONFIG, $this->settings['remote_config']);
|
||||
}
|
||||
|
||||
$knock = $this->c3Request('clear');
|
||||
if ($knock === false) {
|
||||
throw new RemoteException(
|
||||
'
|
||||
CodeCoverage Error.
|
||||
Check the file "c3.php" is included in your application.
|
||||
We tried to access "/c3/report/clear" but this URI was not accessible.
|
||||
You can review actual error messages in c3tmp dir.
|
||||
'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function beforeTest(TestEvent $e)
|
||||
{
|
||||
if (!$this->isEnabled()) {
|
||||
return;
|
||||
}
|
||||
$this->startCoverageCollection($e->getTest()->getName());
|
||||
}
|
||||
|
||||
public function afterStep(StepEvent $e)
|
||||
{
|
||||
if (!$this->isEnabled()) {
|
||||
return;
|
||||
}
|
||||
$this->fetchErrors();
|
||||
}
|
||||
|
||||
public function afterSuite(SuiteEvent $e)
|
||||
{
|
||||
if (!$this->isEnabled()) {
|
||||
return;
|
||||
}
|
||||
$coverageFile = Configuration::outputDir() . 'c3tmp/codecoverage.serialized';
|
||||
|
||||
$retries = 5;
|
||||
while (!file_exists($coverageFile) && --$retries >= 0) {
|
||||
usleep(0.5 * 1000000); // 0.5 sec
|
||||
}
|
||||
|
||||
if (!file_exists($coverageFile)) {
|
||||
if (file_exists(Configuration::outputDir() . 'c3tmp/error.txt')) {
|
||||
throw new \RuntimeException(file_get_contents(Configuration::outputDir() . 'c3tmp/error.txt'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$contents = file_get_contents($coverageFile);
|
||||
$coverage = @unserialize($contents);
|
||||
if ($coverage === false) {
|
||||
return;
|
||||
}
|
||||
$this->mergeToPrint($coverage);
|
||||
}
|
||||
|
||||
protected function c3Request($action)
|
||||
{
|
||||
$this->addC3AccessHeader(self::COVERAGE_HEADER, 'remote-access');
|
||||
$context = stream_context_create($this->c3Access);
|
||||
$c3Url = $this->settings['c3_url'] ? $this->settings['c3_url'] : $this->module->_getUrl();
|
||||
$contents = file_get_contents($c3Url . '/c3/report/' . $action, false, $context);
|
||||
|
||||
$okHeaders = array_filter(
|
||||
$http_response_header,
|
||||
function ($h) {
|
||||
return preg_match('~^HTTP(.*?)\s200~', $h);
|
||||
}
|
||||
);
|
||||
if (empty($okHeaders)) {
|
||||
throw new RemoteException("Request was not successful. See response header: " . $http_response_header[0]);
|
||||
}
|
||||
if ($contents === false) {
|
||||
$this->getRemoteError($http_response_header);
|
||||
}
|
||||
return $contents;
|
||||
}
|
||||
|
||||
protected function startCoverageCollection($testName)
|
||||
{
|
||||
$value = [
|
||||
'CodeCoverage' => $testName,
|
||||
'CodeCoverage_Suite' => $this->suiteName,
|
||||
'CodeCoverage_Config' => $this->settings['remote_config']
|
||||
];
|
||||
$value = json_encode($value);
|
||||
|
||||
if ($this->module instanceof \Codeception\Module\WebDriver) {
|
||||
$this->module->amOnPage('/');
|
||||
}
|
||||
|
||||
$c3Url = parse_url($this->settings['c3_url'] ? $this->settings['c3_url'] : $this->module->_getUrl());
|
||||
|
||||
// we need to separate coverage cookies by host; we can't separate cookies by port.
|
||||
$c3Host = isset($c3Url['host']) ? $c3Url['host'] : 'localhost';
|
||||
|
||||
$this->module->setCookie(self::COVERAGE_COOKIE, $value, ['domain' => $c3Host]);
|
||||
|
||||
// putting in configuration ensures the cookie is used for all sessions of a MultiSession test
|
||||
|
||||
$cookies = $this->module->_getConfig('cookies');
|
||||
if (!$cookies || !is_array($cookies)) {
|
||||
$cookies = [];
|
||||
}
|
||||
|
||||
$found = false;
|
||||
foreach ($cookies as &$cookie) {
|
||||
if (!is_array($cookie) || !array_key_exists('Name', $cookie) || !array_key_exists('Value', $cookie)) {
|
||||
// \Codeception\Lib\InnerBrowser will complain about this
|
||||
continue;
|
||||
}
|
||||
if ($cookie['Name'] === self::COVERAGE_COOKIE) {
|
||||
$found = true;
|
||||
$cookie['Value'] = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
$cookies[] = [
|
||||
'Name' => self::COVERAGE_COOKIE,
|
||||
'Value' => $value
|
||||
];
|
||||
}
|
||||
|
||||
$this->module->_setConfig(['cookies' => $cookies]);
|
||||
}
|
||||
|
||||
protected function fetchErrors()
|
||||
{
|
||||
try {
|
||||
$error = $this->module->grabCookie(self::COVERAGE_COOKIE_ERROR);
|
||||
} catch (ModuleException $e) {
|
||||
// when a new session is started we can't get cookies because there is no
|
||||
// current page, but there can be no code coverage error either
|
||||
$error = null;
|
||||
}
|
||||
if (!empty($error)) {
|
||||
$this->module->resetCookie(self::COVERAGE_COOKIE_ERROR);
|
||||
throw new RemoteException($error);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getRemoteError($headers)
|
||||
{
|
||||
foreach ($headers as $header) {
|
||||
if (strpos($header, self::COVERAGE_HEADER_ERROR) === 0) {
|
||||
throw new RemoteException($header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function addC3AccessHeader($header, $value)
|
||||
{
|
||||
$headerString = "$header: $value\r\n";
|
||||
if (strpos($this->c3Access['http']['header'], $headerString) === false) {
|
||||
$this->c3Access['http']['header'] .= $headerString;
|
||||
}
|
||||
}
|
||||
|
||||
protected function applySettings($settings)
|
||||
{
|
||||
parent::applySettings($settings);
|
||||
if (isset($settings['coverage']['remote_context_options'])) {
|
||||
$this->c3Access = array_replace_recursive($this->c3Access, $settings['coverage']['remote_context_options']);
|
||||
}
|
||||
}
|
||||
}
|
||||
151
vendor/codeception/base/src/Codeception/Coverage/Subscriber/Printer.php
vendored
Normal file
151
vendor/codeception/base/src/Codeception/Coverage/Subscriber/Printer.php
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
namespace Codeception\Coverage\Subscriber;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Coverage\Filter;
|
||||
use Codeception\Event\PrintResultEvent;
|
||||
use Codeception\Events;
|
||||
use Codeception\Subscriber\Shared\StaticEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class Printer implements EventSubscriberInterface
|
||||
{
|
||||
use StaticEvents;
|
||||
|
||||
public static $events = [
|
||||
Events::RESULT_PRINT_AFTER => 'printResult'
|
||||
];
|
||||
|
||||
protected $settings = [
|
||||
'enabled' => true,
|
||||
'low_limit' => '35',
|
||||
'high_limit' => '70',
|
||||
'show_uncovered' => false
|
||||
];
|
||||
|
||||
public static $coverage;
|
||||
protected $options;
|
||||
protected $logDir;
|
||||
protected $destination = [];
|
||||
|
||||
public function __construct($options)
|
||||
{
|
||||
$this->options = $options;
|
||||
$this->logDir = Configuration::outputDir();
|
||||
$this->settings = array_merge($this->settings, Configuration::config()['coverage']);
|
||||
self::$coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage();
|
||||
|
||||
// Apply filter
|
||||
$filter = new Filter(self::$coverage);
|
||||
$filter
|
||||
->whiteList(Configuration::config())
|
||||
->blackList(Configuration::config());
|
||||
}
|
||||
|
||||
protected function absolutePath($path)
|
||||
{
|
||||
if ((strpos($path, '/') === 0) || (strpos($path, ':') === 1)) { // absolute path
|
||||
return $path;
|
||||
}
|
||||
return $this->logDir . $path;
|
||||
}
|
||||
|
||||
public function printResult(PrintResultEvent $e)
|
||||
{
|
||||
$printer = $e->getPrinter();
|
||||
if (!$this->settings['enabled']) {
|
||||
$printer->write("\nCodeCoverage is disabled in `codeception.yml` config\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->options['quiet']) {
|
||||
$this->printConsole($printer);
|
||||
}
|
||||
$printer->write("Remote CodeCoverage reports are not printed to console\n");
|
||||
$this->printPHP();
|
||||
$printer->write("\n");
|
||||
if ($this->options['coverage-html']) {
|
||||
$this->printHtml();
|
||||
$printer->write("HTML report generated in {$this->options['coverage-html']}\n");
|
||||
}
|
||||
if ($this->options['coverage-xml']) {
|
||||
$this->printXml();
|
||||
$printer->write("XML report generated in {$this->options['coverage-xml']}\n");
|
||||
}
|
||||
if ($this->options['coverage-text']) {
|
||||
$this->printText();
|
||||
$printer->write("Text report generated in {$this->options['coverage-text']}\n");
|
||||
}
|
||||
if ($this->options['coverage-crap4j']) {
|
||||
$this->printCrap4j();
|
||||
$printer->write("Crap4j report generated in {$this->options['coverage-crap4j']}\n");
|
||||
}
|
||||
if ($this->options['coverage-phpunit']) {
|
||||
$this->printPHPUnit();
|
||||
$printer->write("PHPUnit report generated in {$this->options['coverage-phpunit']}\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected function printConsole(\PHPUnit\Util\Printer $printer)
|
||||
{
|
||||
$writer = new \SebastianBergmann\CodeCoverage\Report\Text(
|
||||
$this->settings['low_limit'],
|
||||
$this->settings['high_limit'],
|
||||
$this->settings['show_uncovered'],
|
||||
false
|
||||
);
|
||||
$printer->write($writer->process(self::$coverage, $this->options['colors']));
|
||||
}
|
||||
|
||||
protected function printHtml()
|
||||
{
|
||||
$writer = new \SebastianBergmann\CodeCoverage\Report\Html\Facade(
|
||||
$this->settings['low_limit'],
|
||||
$this->settings['high_limit'],
|
||||
sprintf(
|
||||
', <a href="http://codeception.com">Codeception</a> and <a href="http://phpunit.de/">PHPUnit %s</a>',
|
||||
\PHPUnit\Runner\Version::id()
|
||||
)
|
||||
);
|
||||
|
||||
$writer->process(self::$coverage, $this->absolutePath($this->options['coverage-html']));
|
||||
}
|
||||
|
||||
protected function printXml()
|
||||
{
|
||||
$writer = new \SebastianBergmann\CodeCoverage\Report\Clover();
|
||||
$writer->process(self::$coverage, $this->absolutePath($this->options['coverage-xml']));
|
||||
}
|
||||
|
||||
protected function printPHP()
|
||||
{
|
||||
$writer = new \SebastianBergmann\CodeCoverage\Report\PHP;
|
||||
$writer->process(self::$coverage, $this->absolutePath($this->options['coverage']));
|
||||
}
|
||||
|
||||
protected function printText()
|
||||
{
|
||||
$writer = new \SebastianBergmann\CodeCoverage\Report\Text(
|
||||
$this->settings['low_limit'],
|
||||
$this->settings['high_limit'],
|
||||
$this->settings['show_uncovered'],
|
||||
false
|
||||
);
|
||||
file_put_contents(
|
||||
$this->absolutePath($this->options['coverage-text']),
|
||||
$writer->process(self::$coverage, false)
|
||||
);
|
||||
}
|
||||
|
||||
protected function printCrap4j()
|
||||
{
|
||||
$writer = new \SebastianBergmann\CodeCoverage\Report\Crap4j;
|
||||
$writer->process(self::$coverage, $this->absolutePath($this->options['coverage-crap4j']));
|
||||
}
|
||||
|
||||
protected function printPHPUnit()
|
||||
{
|
||||
$writer = new \SebastianBergmann\CodeCoverage\Report\Xml\Facade(\PHPUnit\Runner\Version::id());
|
||||
$writer->process(self::$coverage, $this->absolutePath($this->options['coverage-phpunit']));
|
||||
}
|
||||
}
|
||||
90
vendor/codeception/base/src/Codeception/Coverage/Subscriber/RemoteServer.php
vendored
Normal file
90
vendor/codeception/base/src/Codeception/Coverage/Subscriber/RemoteServer.php
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
namespace Codeception\Coverage\Subscriber;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Event\SuiteEvent;
|
||||
use Codeception\Util\FileSystem;
|
||||
|
||||
/**
|
||||
* When collecting code coverage on remote server
|
||||
* data is retrieved over HTTP and not merged with the local code coverage results.
|
||||
*
|
||||
* Class RemoteServer
|
||||
* @package Codeception\Coverage\Subscriber
|
||||
*/
|
||||
class RemoteServer extends LocalServer
|
||||
{
|
||||
public function isEnabled()
|
||||
{
|
||||
return $this->module and $this->settings['remote'] and $this->settings['enabled'];
|
||||
}
|
||||
|
||||
public function afterSuite(SuiteEvent $e)
|
||||
{
|
||||
if (!$this->isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$suite = strtr($e->getSuite()->getName(), ['\\' => '.']);
|
||||
if ($this->options['coverage-xml']) {
|
||||
$this->retrieveAndPrintXml($suite);
|
||||
}
|
||||
if ($this->options['coverage-html']) {
|
||||
$this->retrieveAndPrintHtml($suite);
|
||||
}
|
||||
if ($this->options['coverage-crap4j']) {
|
||||
$this->retrieveAndPrintCrap4j($suite);
|
||||
}
|
||||
if ($this->options['coverage-phpunit']) {
|
||||
$this->retrieveAndPrintPHPUnit($suite);
|
||||
}
|
||||
}
|
||||
|
||||
protected function retrieveAndPrintHtml($suite)
|
||||
{
|
||||
$tempFile = tempnam(sys_get_temp_dir(), 'C3') . '.tar';
|
||||
file_put_contents($tempFile, $this->c3Request('html'));
|
||||
|
||||
$destDir = Configuration::outputDir() . $suite . '.remote.coverage';
|
||||
if (is_dir($destDir)) {
|
||||
FileSystem::doEmptyDir($destDir);
|
||||
} else {
|
||||
mkdir($destDir, 0777, true);
|
||||
}
|
||||
|
||||
$phar = new \PharData($tempFile);
|
||||
$phar->extractTo($destDir);
|
||||
|
||||
unlink($tempFile);
|
||||
}
|
||||
|
||||
protected function retrieveAndPrintXml($suite)
|
||||
{
|
||||
$destFile = Configuration::outputDir() . $suite . '.remote.coverage.xml';
|
||||
file_put_contents($destFile, $this->c3Request('clover'));
|
||||
}
|
||||
|
||||
protected function retrieveAndPrintCrap4j($suite)
|
||||
{
|
||||
$destFile = Configuration::outputDir() . $suite . '.remote.crap4j.xml';
|
||||
file_put_contents($destFile, $this->c3Request('crap4j'));
|
||||
}
|
||||
|
||||
protected function retrieveAndPrintPHPUnit($suite)
|
||||
{
|
||||
$tempFile = tempnam(sys_get_temp_dir(), 'C3') . '.tar';
|
||||
file_put_contents($tempFile, $this->c3Request('phpunit'));
|
||||
|
||||
$destDir = Configuration::outputDir() . $suite . '.remote.coverage-phpunit';
|
||||
if (is_dir($destDir)) {
|
||||
FileSystem::doEmptyDir($destDir);
|
||||
} else {
|
||||
mkdir($destDir, 0777, true);
|
||||
}
|
||||
|
||||
$phar = new \PharData($tempFile);
|
||||
$phar->extractTo($destDir);
|
||||
|
||||
unlink($tempFile);
|
||||
}
|
||||
}
|
||||
95
vendor/codeception/base/src/Codeception/Coverage/SuiteSubscriber.php
vendored
Normal file
95
vendor/codeception/base/src/Codeception/Coverage/SuiteSubscriber.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
namespace Codeception\Coverage;
|
||||
|
||||
use Codeception\Configuration;
|
||||
use Codeception\Coverage\Subscriber\Printer;
|
||||
use Codeception\Lib\Interfaces\Remote;
|
||||
use Codeception\Stub;
|
||||
use Codeception\Subscriber\Shared\StaticEvents;
|
||||
use PHPUnit\Framework\CodeCoverageException;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
abstract class SuiteSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
use StaticEvents;
|
||||
|
||||
protected $defaultSettings = [
|
||||
'enabled' => false,
|
||||
'remote' => false,
|
||||
'local' => false,
|
||||
'xdebug_session' => 'codeception',
|
||||
'remote_config' => null,
|
||||
'show_uncovered' => false,
|
||||
'c3_url' => null
|
||||
];
|
||||
|
||||
protected $settings = [];
|
||||
protected $filters = [];
|
||||
protected $modules = [];
|
||||
|
||||
protected $coverage;
|
||||
protected $logDir;
|
||||
protected $options;
|
||||
public static $events = [];
|
||||
|
||||
abstract protected function isEnabled();
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
$this->logDir = Configuration::outputDir();
|
||||
}
|
||||
|
||||
protected function applySettings($settings)
|
||||
{
|
||||
try {
|
||||
$this->coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage();
|
||||
} catch (CodeCoverageException $e) {
|
||||
throw new \Exception(
|
||||
'XDebug is required to collect CodeCoverage. Please install xdebug extension and enable it in php.ini'
|
||||
);
|
||||
}
|
||||
|
||||
$this->filters = $settings;
|
||||
$this->settings = $this->defaultSettings;
|
||||
$keys = array_keys($this->defaultSettings);
|
||||
foreach ($keys as $key) {
|
||||
if (isset($settings['coverage'][$key])) {
|
||||
$this->settings[$key] = $settings['coverage'][$key];
|
||||
}
|
||||
}
|
||||
$this->coverage->setProcessUncoveredFilesFromWhitelist($this->settings['show_uncovered']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $modules
|
||||
* @return \Codeception\Lib\Interfaces\Remote|null
|
||||
*/
|
||||
protected function getServerConnectionModule(array $modules)
|
||||
{
|
||||
foreach ($modules as $module) {
|
||||
if ($module instanceof Remote) {
|
||||
return $module;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function applyFilter(\PHPUnit\Framework\TestResult $result)
|
||||
{
|
||||
$driver = Stub::makeEmpty('SebastianBergmann\CodeCoverage\Driver\Driver');
|
||||
$result->setCodeCoverage(new CodeCoverage($driver));
|
||||
|
||||
Filter::setup($this->coverage)
|
||||
->whiteList($this->filters)
|
||||
->blackList($this->filters);
|
||||
|
||||
$result->setCodeCoverage($this->coverage);
|
||||
}
|
||||
|
||||
protected function mergeToPrint($coverage)
|
||||
{
|
||||
Printer::$coverage->merge($coverage);
|
||||
}
|
||||
}
|
||||
14
vendor/codeception/base/src/Codeception/CustomCommandInterface.php
vendored
Normal file
14
vendor/codeception/base/src/Codeception/CustomCommandInterface.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Codeception;
|
||||
|
||||
interface CustomCommandInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* returns the name of the command
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCommandName();
|
||||
}
|
||||
32
vendor/codeception/base/src/Codeception/Event/FailEvent.php
vendored
Normal file
32
vendor/codeception/base/src/Codeception/Event/FailEvent.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Codeception\Event;
|
||||
|
||||
class FailEvent extends TestEvent
|
||||
{
|
||||
/**
|
||||
* @var \Exception
|
||||
*/
|
||||
protected $fail;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $count;
|
||||
|
||||
public function __construct(\PHPUnit\Framework\Test $test, $time, $e, $count = 0)
|
||||
{
|
||||
parent::__construct($test, $time);
|
||||
$this->fail = $e;
|
||||
$this->count = $count;
|
||||
}
|
||||
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
public function getFail()
|
||||
{
|
||||
return $this->fail;
|
||||
}
|
||||
}
|
||||
39
vendor/codeception/base/src/Codeception/Event/PrintResultEvent.php
vendored
Normal file
39
vendor/codeception/base/src/Codeception/Event/PrintResultEvent.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace Codeception\Event;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class PrintResultEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @var \PHPUnit\Framework\TestResult
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit\Util\Printer
|
||||
*/
|
||||
protected $printer;
|
||||
|
||||
public function __construct(\PHPUnit\Framework\TestResult $result, \PHPUnit\Util\Printer $printer)
|
||||
{
|
||||
$this->result = $result;
|
||||
$this->printer = $printer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit\Util\Printer
|
||||
*/
|
||||
public function getPrinter()
|
||||
{
|
||||
return $this->printer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit\Framework\TestResult
|
||||
*/
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
38
vendor/codeception/base/src/Codeception/Event/StepEvent.php
vendored
Normal file
38
vendor/codeception/base/src/Codeception/Event/StepEvent.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
namespace Codeception\Event;
|
||||
|
||||
use Codeception\Step;
|
||||
use Codeception\TestInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class StepEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @var Step
|
||||
*/
|
||||
protected $step;
|
||||
|
||||
/**
|
||||
* @var TestInterface
|
||||
*/
|
||||
protected $test;
|
||||
|
||||
public function __construct(TestInterface $test, Step $step)
|
||||
{
|
||||
$this->test = $test;
|
||||
$this->step = $step;
|
||||
}
|
||||
|
||||
public function getStep()
|
||||
{
|
||||
return $this->step;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TestInterface
|
||||
*/
|
||||
public function getTest()
|
||||
{
|
||||
return $this->test;
|
||||
}
|
||||
}
|
||||
54
vendor/codeception/base/src/Codeception/Event/SuiteEvent.php
vendored
Normal file
54
vendor/codeception/base/src/Codeception/Event/SuiteEvent.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace Codeception\Event;
|
||||
|
||||
use Codeception\Suite;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class SuiteEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @var \PHPUnit\Framework\TestSuite
|
||||
*/
|
||||
protected $suite;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit\Framework\TestResult
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
public function __construct(
|
||||
\PHPUnit\Framework\TestSuite $suite,
|
||||
\PHPUnit\Framework\TestResult $result = null,
|
||||
$settings = []
|
||||
) {
|
||||
$this->suite = $suite;
|
||||
$this->result = $result;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Suite
|
||||
*/
|
||||
public function getSuite()
|
||||
{
|
||||
return $this->suite;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit\Framework\TestResult
|
||||
*/
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function getSettings()
|
||||
{
|
||||
return $this->settings;
|
||||
}
|
||||
}
|
||||
39
vendor/codeception/base/src/Codeception/Event/TestEvent.php
vendored
Normal file
39
vendor/codeception/base/src/Codeception/Event/TestEvent.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace Codeception\Event;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class TestEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @var \PHPUnit\Framework\Test
|
||||
*/
|
||||
protected $test;
|
||||
|
||||
/**
|
||||
* @var float Time taken
|
||||
*/
|
||||
protected $time;
|
||||
|
||||
public function __construct(\PHPUnit\Framework\Test $test, $time = 0)
|
||||
{
|
||||
$this->test = $test;
|
||||
$this->time = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getTime()
|
||||
{
|
||||
return $this->time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Codeception\TestInterface
|
||||
*/
|
||||
public function getTest()
|
||||
{
|
||||
return $this->test;
|
||||
}
|
||||
}
|
||||
127
vendor/codeception/base/src/Codeception/Events.php
vendored
Normal file
127
vendor/codeception/base/src/Codeception/Events.php
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
namespace Codeception;
|
||||
|
||||
/**
|
||||
* Contains all events dispatched by Codeception.
|
||||
*
|
||||
* @author tiger-seo <tiger.seo@gmail.com>
|
||||
*/
|
||||
final class Events
|
||||
{
|
||||
/**
|
||||
* Private constructor. This class cannot be instantiated.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* The <b>MODULE_INIT</b> event occurs before modules are initialized.
|
||||
*
|
||||
* The event listener method receives a {@link Codeception\Event\SuiteEvent} instance.
|
||||
*/
|
||||
const MODULE_INIT = 'module.init';
|
||||
|
||||
/**
|
||||
* The <b>SUITE_INIT</b> event occurs when suite is initialized.
|
||||
* Modules are created and initialized, but Actor class is not loaded.
|
||||
*
|
||||
* The event listener method receives a {@link Codeception\Event\SuiteEvent} instance.
|
||||
*/
|
||||
const SUITE_INIT = 'suite.init';
|
||||
|
||||
/**
|
||||
* The <b>SUITE_BEFORE</b> event occurs before suite is executed.
|
||||
*
|
||||
* The event listener method receives a {@link Codeception\Event\SuiteEvent} instance.
|
||||
*/
|
||||
const SUITE_BEFORE = 'suite.before';
|
||||
|
||||
/**
|
||||
* The <b>SUITE_AFTER</b> event occurs after suite has been executed.
|
||||
*
|
||||
* The event listener method receives a {@link Codeception\Event\SuiteEvent} instance.
|
||||
*/
|
||||
const SUITE_AFTER = 'suite.after';
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\TestEvent} instance.
|
||||
*/
|
||||
const TEST_START = 'test.start';
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\TestEvent} instance.
|
||||
*/
|
||||
const TEST_BEFORE = 'test.before';
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\StepEvent} instance.
|
||||
*/
|
||||
const STEP_BEFORE = 'step.before';
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\StepEvent} instance.
|
||||
*/
|
||||
const STEP_AFTER = 'step.after';
|
||||
|
||||
/**
|
||||
* The <b>TEST_FAIL</b> event occurs whenever test has failed.
|
||||
*
|
||||
* The event listener method receives a {@link Codeception\Event\FailEvent} instance.
|
||||
*/
|
||||
const TEST_FAIL = 'test.fail';
|
||||
|
||||
/**
|
||||
* The <b>TEST_ERROR</b> event occurs whenever test got an error while being executed.
|
||||
*
|
||||
* The event listener method receives a {@link Codeception\Event\FailEvent} instance.
|
||||
*/
|
||||
const TEST_ERROR = 'test.error';
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\TestEvent} instance.
|
||||
*/
|
||||
const TEST_PARSED = 'test.parsed';
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\FailEvent} instance.
|
||||
*/
|
||||
const TEST_INCOMPLETE = 'test.incomplete';
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\FailEvent} instance.
|
||||
*/
|
||||
const TEST_SKIPPED = 'test.skipped';
|
||||
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\FailEvent} instance.
|
||||
*/
|
||||
const TEST_WARNING = 'test.warning';
|
||||
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\TestEvent} instance.
|
||||
*/
|
||||
const TEST_SUCCESS = 'test.success';
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\TestEvent} instance.
|
||||
*/
|
||||
const TEST_AFTER = 'test.after';
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\TestEvent} instance.
|
||||
*/
|
||||
const TEST_END = 'test.end';
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\FailEvent} instance.
|
||||
*/
|
||||
const TEST_FAIL_PRINT = 'test.fail.print';
|
||||
|
||||
/**
|
||||
* The event listener method receives a {@link Codeception\Event\PrintResultEvent} instance.
|
||||
*/
|
||||
const RESULT_PRINT_AFTER = 'result.print.after';
|
||||
}
|
||||
105
vendor/codeception/base/src/Codeception/Example.php
vendored
Normal file
105
vendor/codeception/base/src/Codeception/Example.php
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
namespace Codeception;
|
||||
|
||||
use Traversable;
|
||||
|
||||
class Example implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
{
|
||||
protected $data;
|
||||
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a offset exists
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
|
||||
* @param mixed $offset <p>
|
||||
* An offset to check for.
|
||||
* </p>
|
||||
* @return boolean true on success or false on failure.
|
||||
* </p>
|
||||
* <p>
|
||||
* The return value will be casted to boolean if non-boolean was returned.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists($offset, $this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to retrieve
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetget.php
|
||||
* @param mixed $offset <p>
|
||||
* The offset to retrieve.
|
||||
* </p>
|
||||
* @return mixed Can return all value types.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if (!$this->offsetExists($offset)) {
|
||||
throw new \PHPUnit\Framework\AssertionFailedError("Example $offset doesn't exist");
|
||||
};
|
||||
return $this->data[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to set
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetset.php
|
||||
* @param mixed $offset <p>
|
||||
* The offset to assign the value to.
|
||||
* </p>
|
||||
* @param mixed $value <p>
|
||||
* The value to set.
|
||||
* </p>
|
||||
* @return void
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->data[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to unset
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
|
||||
* @param mixed $offset <p>
|
||||
* The offset to unset.
|
||||
* </p>
|
||||
* @return void
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->data[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count elements of an object
|
||||
* @link http://php.net/manual/en/countable.count.php
|
||||
* @return int The custom count as an integer.
|
||||
* </p>
|
||||
* <p>
|
||||
* The return value is cast to an integer.
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
|
||||
* @return Traversable An instance of an object implementing <b>Iterator</b> or
|
||||
* <b>Traversable</b>
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->data);
|
||||
}
|
||||
}
|
||||
6
vendor/codeception/base/src/Codeception/Exception/ConditionalAssertionFailed.php
vendored
Normal file
6
vendor/codeception/base/src/Codeception/Exception/ConditionalAssertionFailed.php
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class ConditionalAssertionFailed extends \PHPUnit\Framework\AssertionFailedError
|
||||
{
|
||||
}
|
||||
6
vendor/codeception/base/src/Codeception/Exception/ConfigurationException.php
vendored
Normal file
6
vendor/codeception/base/src/Codeception/Exception/ConfigurationException.php
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class ConfigurationException extends \Exception
|
||||
{
|
||||
}
|
||||
6
vendor/codeception/base/src/Codeception/Exception/ConnectionException.php
vendored
Normal file
6
vendor/codeception/base/src/Codeception/Exception/ConnectionException.php
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class ConnectionException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
6
vendor/codeception/base/src/Codeception/Exception/ContentNotFound.php
vendored
Normal file
6
vendor/codeception/base/src/Codeception/Exception/ContentNotFound.php
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class ContentNotFound extends \PHPUnit\Framework\AssertionFailedError
|
||||
{
|
||||
}
|
||||
13
vendor/codeception/base/src/Codeception/Exception/ElementNotFound.php
vendored
Normal file
13
vendor/codeception/base/src/Codeception/Exception/ElementNotFound.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
use Codeception\Util\Locator;
|
||||
|
||||
class ElementNotFound extends \PHPUnit\Framework\AssertionFailedError
|
||||
{
|
||||
public function __construct($selector, $message = null)
|
||||
{
|
||||
$selector = Locator::humanReadableString($selector);
|
||||
parent::__construct($message . " element with $selector was not found.");
|
||||
}
|
||||
}
|
||||
14
vendor/codeception/base/src/Codeception/Exception/ExtensionException.php
vendored
Normal file
14
vendor/codeception/base/src/Codeception/Exception/ExtensionException.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class ExtensionException extends \Exception
|
||||
{
|
||||
public function __construct($extension, $message, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $previous);
|
||||
if (is_object($extension)) {
|
||||
$extension = get_class($extension);
|
||||
}
|
||||
$this->message = $extension . "\n\n" . $this->message;
|
||||
}
|
||||
}
|
||||
7
vendor/codeception/base/src/Codeception/Exception/ExternalUrlException.php
vendored
Normal file
7
vendor/codeception/base/src/Codeception/Exception/ExternalUrlException.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class ExternalUrlException extends \Exception
|
||||
{
|
||||
}
|
||||
7
vendor/codeception/base/src/Codeception/Exception/Fail.php
vendored
Normal file
7
vendor/codeception/base/src/Codeception/Exception/Fail.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class Fail extends \PHPUnit\Framework\AssertionFailedError
|
||||
{
|
||||
|
||||
}
|
||||
7
vendor/codeception/base/src/Codeception/Exception/Incomplete.php
vendored
Normal file
7
vendor/codeception/base/src/Codeception/Exception/Incomplete.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class Incomplete extends \PHPUnit\Framework\IncompleteTestError
|
||||
{
|
||||
|
||||
}
|
||||
6
vendor/codeception/base/src/Codeception/Exception/InjectionException.php
vendored
Normal file
6
vendor/codeception/base/src/Codeception/Exception/InjectionException.php
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class InjectionException extends \Exception
|
||||
{
|
||||
}
|
||||
10
vendor/codeception/base/src/Codeception/Exception/MalformedLocatorException.php
vendored
Normal file
10
vendor/codeception/base/src/Codeception/Exception/MalformedLocatorException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class MalformedLocatorException extends TestRuntimeException
|
||||
{
|
||||
public function __construct($locator, $type = "CSS or XPath")
|
||||
{
|
||||
parent::__construct(ucfirst($type) . " locator is malformed: $locator");
|
||||
}
|
||||
}
|
||||
15
vendor/codeception/base/src/Codeception/Exception/ModuleConfigException.php
vendored
Normal file
15
vendor/codeception/base/src/Codeception/Exception/ModuleConfigException.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class ModuleConfigException extends \Exception
|
||||
{
|
||||
public function __construct($module, $message, \Exception $previous = null)
|
||||
{
|
||||
if (is_object($module)) {
|
||||
$module = get_class($module);
|
||||
}
|
||||
$module = str_replace('Codeception\Module\\', '', ltrim($module, '\\'));
|
||||
parent::__construct($message, 0, $previous);
|
||||
$this->message = $module . " module is not configured!\n \n" . $this->message;
|
||||
}
|
||||
}
|
||||
23
vendor/codeception/base/src/Codeception/Exception/ModuleConflictException.php
vendored
Normal file
23
vendor/codeception/base/src/Codeception/Exception/ModuleConflictException.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class ModuleConflictException extends \Exception
|
||||
{
|
||||
public function __construct($module, $conflicted, $additional = '')
|
||||
{
|
||||
if (is_object($module)) {
|
||||
$module = get_class($module);
|
||||
}
|
||||
if (is_object($conflicted)) {
|
||||
$conflicted = get_class($conflicted);
|
||||
}
|
||||
$module = ltrim(str_replace('Codeception\Module\\', '', $module), '\\');
|
||||
$conflicted = ltrim(str_replace('Codeception\Module\\', '', $conflicted), '\\');
|
||||
$this->message = "$module module conflicts with $conflicted\n\n--\n"
|
||||
. "This usually happens when you enable two modules with the same actions but with different backends.\n"
|
||||
. "For instance, you can't run PhpBrowser, WebDriver, Laravel5 modules in one suite,\n"
|
||||
. "as they implement similar methods but use different drivers to execute them.\n"
|
||||
. "You can load a part of module (like: ORM) to avoid conflict.\n"
|
||||
. $additional;
|
||||
}
|
||||
}
|
||||
18
vendor/codeception/base/src/Codeception/Exception/ModuleException.php
vendored
Normal file
18
vendor/codeception/base/src/Codeception/Exception/ModuleException.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class ModuleException extends \Exception
|
||||
{
|
||||
protected $module;
|
||||
|
||||
public function __construct($module, $message)
|
||||
{
|
||||
if (is_object($module)) {
|
||||
$module = get_class($module);
|
||||
}
|
||||
$module = ltrim(str_replace('Codeception\Module\\', '', $module), '\\');
|
||||
$this->module = $module;
|
||||
parent::__construct($message);
|
||||
$this->message = "$module: {$this->message}";
|
||||
}
|
||||
}
|
||||
15
vendor/codeception/base/src/Codeception/Exception/ModuleRequireException.php
vendored
Normal file
15
vendor/codeception/base/src/Codeception/Exception/ModuleRequireException.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class ModuleRequireException extends \Exception
|
||||
{
|
||||
public function __construct($module, $message)
|
||||
{
|
||||
if (is_object($module)) {
|
||||
$module = get_class($module);
|
||||
}
|
||||
$module = str_replace('Codeception\\Module\\', '', ltrim($module, '\\'));
|
||||
parent::__construct($message);
|
||||
$this->message = "[$module] module requirements not met --\n \n" . $this->message;
|
||||
}
|
||||
}
|
||||
6
vendor/codeception/base/src/Codeception/Exception/ParseException.php
vendored
Normal file
6
vendor/codeception/base/src/Codeception/Exception/ParseException.php
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class ParseException extends \Exception
|
||||
{
|
||||
}
|
||||
11
vendor/codeception/base/src/Codeception/Exception/RemoteException.php
vendored
Normal file
11
vendor/codeception/base/src/Codeception/Exception/RemoteException.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class RemoteException extends \Exception
|
||||
{
|
||||
public function __construct($message)
|
||||
{
|
||||
parent::__construct($message);
|
||||
$this->message = "Remote Application Error:\n" . $this->message;
|
||||
}
|
||||
}
|
||||
7
vendor/codeception/base/src/Codeception/Exception/Skip.php
vendored
Normal file
7
vendor/codeception/base/src/Codeception/Exception/Skip.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class Skip extends \PHPUnit\Framework\SkippedTestError
|
||||
{
|
||||
|
||||
}
|
||||
17
vendor/codeception/base/src/Codeception/Exception/TestParseException.php
vendored
Normal file
17
vendor/codeception/base/src/Codeception/Exception/TestParseException.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class TestParseException extends \Exception
|
||||
{
|
||||
public function __construct($fileName, $errors = null, $line = null)
|
||||
{
|
||||
if ($line) {
|
||||
$this->message = "Couldn't parse test '$fileName' on line $line";
|
||||
} else {
|
||||
$this->message = "Couldn't parse test '$fileName'";
|
||||
}
|
||||
if ($errors) {
|
||||
$this->message .= "\n$errors";
|
||||
}
|
||||
}
|
||||
}
|
||||
6
vendor/codeception/base/src/Codeception/Exception/TestRuntimeException.php
vendored
Normal file
6
vendor/codeception/base/src/Codeception/Exception/TestRuntimeException.php
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Codeception\Exception;
|
||||
|
||||
class TestRuntimeException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
134
vendor/codeception/base/src/Codeception/Extension.php
vendored
Normal file
134
vendor/codeception/base/src/Codeception/Extension.php
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
namespace Codeception;
|
||||
|
||||
use Codeception\Configuration as Config;
|
||||
use Codeception\Event\SuiteEvent;
|
||||
use Codeception\Exception\ModuleRequireException;
|
||||
use Codeception\Lib\Console\Output;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* A base class for all Codeception Extensions and GroupObjects
|
||||
*
|
||||
* Available Properties:
|
||||
*
|
||||
* * config: current extension configuration
|
||||
* * options: passed running options
|
||||
*
|
||||
*/
|
||||
abstract class Extension implements EventSubscriberInterface
|
||||
{
|
||||
protected $config = [];
|
||||
protected $options;
|
||||
protected $output;
|
||||
protected $globalConfig;
|
||||
private $modules = [];
|
||||
|
||||
public function __construct($config, $options)
|
||||
{
|
||||
$this->config = array_merge($this->config, $config);
|
||||
$this->options = $options;
|
||||
$this->output = new Output($options);
|
||||
$this->_initialize();
|
||||
}
|
||||
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
if (!isset(static::$events)) {
|
||||
return [Events::SUITE_INIT => 'receiveModuleContainer'];
|
||||
}
|
||||
if (isset(static::$events[Events::SUITE_INIT])) {
|
||||
if (!is_array(static::$events[Events::SUITE_INIT])) {
|
||||
static::$events[Events::SUITE_INIT] = [[static::$events[Events::SUITE_INIT]]];
|
||||
}
|
||||
static::$events[Events::SUITE_INIT][] = ['receiveModuleContainer'];
|
||||
} else {
|
||||
static::$events[Events::SUITE_INIT] = 'receiveModuleContainer';
|
||||
}
|
||||
return static::$events;
|
||||
}
|
||||
|
||||
public function receiveModuleContainer(SuiteEvent $e)
|
||||
{
|
||||
$this->modules = $e->getSuite()->getModules();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass config variables that should be injected into global config.
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function _reconfigure($config = [])
|
||||
{
|
||||
if (is_array($config)) {
|
||||
Config::append($config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* You can do all preparations here. No need to override constructor.
|
||||
* Also you can skip calling `_reconfigure` if you don't need to.
|
||||
*/
|
||||
public function _initialize()
|
||||
{
|
||||
$this->_reconfigure(); // hook for BC only.
|
||||
}
|
||||
|
||||
protected function write($message)
|
||||
{
|
||||
if (!$this->options['silent']) {
|
||||
$this->output->write($message);
|
||||
}
|
||||
}
|
||||
|
||||
protected function writeln($message)
|
||||
{
|
||||
if (!$this->options['silent']) {
|
||||
$this->output->writeln($message);
|
||||
}
|
||||
}
|
||||
|
||||
public function hasModule($name)
|
||||
{
|
||||
return isset($this->modules[$name]);
|
||||
}
|
||||
|
||||
public function getCurrentModuleNames()
|
||||
{
|
||||
return array_keys($this->modules);
|
||||
}
|
||||
|
||||
public function getModule($name)
|
||||
{
|
||||
if (!$this->hasModule($name)) {
|
||||
throw new ModuleRequireException($name, "module is not enabled");
|
||||
}
|
||||
return $this->modules[$name];
|
||||
}
|
||||
|
||||
public function getTestsDir()
|
||||
{
|
||||
return Config::testsDir();
|
||||
}
|
||||
|
||||
public function getLogDir()
|
||||
{
|
||||
return Config::outputDir();
|
||||
}
|
||||
|
||||
public function getDataDir()
|
||||
{
|
||||
return Config::dataDir();
|
||||
}
|
||||
|
||||
public function getRootDir()
|
||||
{
|
||||
return Config::projectDir();
|
||||
}
|
||||
|
||||
public function getGlobalConfig()
|
||||
{
|
||||
return Config::config();
|
||||
}
|
||||
}
|
||||
30
vendor/codeception/base/src/Codeception/GroupObject.php
vendored
Normal file
30
vendor/codeception/base/src/Codeception/GroupObject.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace Codeception;
|
||||
|
||||
use Codeception\Event\TestEvent;
|
||||
|
||||
abstract class GroupObject extends Extension
|
||||
{
|
||||
public static $group;
|
||||
|
||||
public function _before(TestEvent $e)
|
||||
{
|
||||
}
|
||||
|
||||
public function _after(TestEvent $e)
|
||||
{
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
$inheritedEvents = parent::getSubscribedEvents();
|
||||
$events = [];
|
||||
if (static::$group) {
|
||||
$events = [
|
||||
Events::TEST_BEFORE . '.' . static::$group => '_before',
|
||||
Events::TEST_AFTER . '.' . static::$group => '_after',
|
||||
];
|
||||
}
|
||||
return array_merge($events, $inheritedEvents);
|
||||
}
|
||||
}
|
||||
249
vendor/codeception/base/src/Codeception/InitTemplate.php
vendored
Normal file
249
vendor/codeception/base/src/Codeception/InitTemplate.php
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace Codeception;
|
||||
|
||||
use Codeception\Command\Shared\FileSystem;
|
||||
use Codeception\Command\Shared\Style;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
/**
|
||||
* Codeception templates allow creating a customized setup and configuration for your project.
|
||||
* An abstract class for installation template. Each init template should extend it and implement a `setup` method.
|
||||
* Use it to build a custom setup class which can be started with `codecept init` command.
|
||||
*
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* namespace Codeception\Template; // it is important to use this namespace so codecept init could locate this template
|
||||
* class CustomInstall extends \Codeception\InitTemplate
|
||||
* {
|
||||
* public function setup()
|
||||
* {
|
||||
* // implement this
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* This class provides various helper methods for building customized setup
|
||||
*/
|
||||
abstract class InitTemplate
|
||||
{
|
||||
use FileSystem;
|
||||
use Style;
|
||||
|
||||
const GIT_IGNORE = '.gitignore';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $actorSuffix = 'Tester';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $workDir = '.';
|
||||
|
||||
/**
|
||||
* @var InputInterface
|
||||
*/
|
||||
protected $input;
|
||||
|
||||
/**
|
||||
* @var OutputInterface
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
public function __construct(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->addStyles($output);
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the directory where Codeception should be installed.
|
||||
*/
|
||||
public function initDir($workDir)
|
||||
{
|
||||
$this->checkInstalled($workDir);
|
||||
$this->sayInfo("Initializing Codeception in $workDir");
|
||||
$this->createDirectoryFor($workDir);
|
||||
chdir($workDir);
|
||||
$this->workDir = $workDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this class to create customized setup.
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function setup();
|
||||
|
||||
/**
|
||||
* ```php
|
||||
* <?php
|
||||
* // propose firefox as default browser
|
||||
* $this->ask('select the browser of your choice', 'firefox');
|
||||
*
|
||||
* // propose firefox or chrome possible options
|
||||
* $this->ask('select the browser of your choice', ['firefox', 'chrome']);
|
||||
*
|
||||
* // ask true/false question
|
||||
* $this->ask('do you want to proceed (y/n)', true);
|
||||
* ```
|
||||
*
|
||||
* @param $question
|
||||
* @param null $answer
|
||||
* @return mixed|string
|
||||
*/
|
||||
protected function ask($question, $answer = null)
|
||||
{
|
||||
$question = "? $question";
|
||||
$dialog = new QuestionHelper();
|
||||
if (is_array($answer)) {
|
||||
$question .= " <info>(" . $answer[0] . ")</info> ";
|
||||
return $dialog->ask($this->input, $this->output, new ChoiceQuestion($question, $answer, 0));
|
||||
}
|
||||
if (is_bool($answer)) {
|
||||
$question .= " (y/n) ";
|
||||
return $dialog->ask($this->input, $this->output, new ConfirmationQuestion($question, $answer));
|
||||
}
|
||||
if ($answer) {
|
||||
$question .= " <info>($answer)</info>";
|
||||
}
|
||||
return $dialog->ask($this->input, $this->output, new Question("$question ", $answer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a message to console.
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* $this->say('Welcome to Setup');
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
protected function say($message = '')
|
||||
{
|
||||
$this->output->writeln($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a successful message
|
||||
* @param $message
|
||||
*/
|
||||
protected function saySuccess($message)
|
||||
{
|
||||
$this->say("<notice> $message </notice>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print warning message
|
||||
* @param $message
|
||||
*/
|
||||
protected function sayWarning($message)
|
||||
{
|
||||
$this->say("<warning> $message </warning>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print info message
|
||||
* @param $message
|
||||
*/
|
||||
protected function sayInfo($message)
|
||||
{
|
||||
$this->say("<debug>> $message</debug>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a helper class inside a directory
|
||||
*
|
||||
* @param $name
|
||||
* @param $directory
|
||||
*/
|
||||
protected function createHelper($name, $directory)
|
||||
{
|
||||
$file = $this->createDirectoryFor(
|
||||
$dir = $directory . DIRECTORY_SEPARATOR . "Helper",
|
||||
"$name.php"
|
||||
) . "$name.php";
|
||||
|
||||
$gen = new Lib\Generator\Helper($name, $this->namespace);
|
||||
// generate helper
|
||||
$this->createFile(
|
||||
$file,
|
||||
$gen->produce()
|
||||
);
|
||||
require_once $file;
|
||||
$this->sayInfo("$name helper has been created in $dir");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty directory and add a placeholder file into it
|
||||
* @param $dir
|
||||
*/
|
||||
protected function createEmptyDirectory($dir)
|
||||
{
|
||||
$this->createDirectoryFor($dir);
|
||||
$this->createFile($dir . DIRECTORY_SEPARATOR . '.gitkeep', '');
|
||||
}
|
||||
|
||||
protected function gitIgnore($path)
|
||||
{
|
||||
if (file_exists(self::GIT_IGNORE)) {
|
||||
file_put_contents($path . DIRECTORY_SEPARATOR . self::GIT_IGNORE, "*\n!" . self::GIT_IGNORE);
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkInstalled($dir = '.')
|
||||
{
|
||||
if (file_exists($dir . DIRECTORY_SEPARATOR . 'codeception.yml') || file_exists($dir . DIRECTORY_SEPARATOR . 'codeception.dist.yml')) {
|
||||
throw new \Exception("Codeception is already installed in this directory");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Actor class and generate actions for it.
|
||||
* Requires a suite config as array in 3rd parameter.
|
||||
*
|
||||
* @param $name
|
||||
* @param $directory
|
||||
* @param $suiteConfig
|
||||
*/
|
||||
protected function createActor($name, $directory, $suiteConfig)
|
||||
{
|
||||
$file = $this->createDirectoryFor(
|
||||
$directory,
|
||||
$name
|
||||
) . $this->getShortClassName($name);
|
||||
$file .= '.php';
|
||||
|
||||
$suiteConfig['namespace'] = $this->namespace;
|
||||
$config = Configuration::mergeConfigs(Configuration::$defaultSuiteSettings, $suiteConfig);
|
||||
|
||||
$actorGenerator = new Lib\Generator\Actor($config);
|
||||
|
||||
$content = $actorGenerator->produce();
|
||||
|
||||
$this->createFile($file, $content);
|
||||
$this->sayInfo("$name actor has been created in $directory");
|
||||
|
||||
$actionsGenerator = new Lib\Generator\Actions($config);
|
||||
$content = $actionsGenerator->produce();
|
||||
|
||||
$generatedDir = $directory . DIRECTORY_SEPARATOR . '_generated';
|
||||
$this->createDirectoryFor($generatedDir, 'Actions.php');
|
||||
$this->createFile($generatedDir . DIRECTORY_SEPARATOR . $actorGenerator->getActorName() . 'Actions.php', $content);
|
||||
$this->sayInfo("Actions have been loaded");
|
||||
}
|
||||
}
|
||||
47
vendor/codeception/base/src/Codeception/Lib/Actor/Shared/Comment.php
vendored
Normal file
47
vendor/codeception/base/src/Codeception/Lib/Actor/Shared/Comment.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Actor\Shared;
|
||||
|
||||
trait Comment
|
||||
{
|
||||
/**
|
||||
* @return \Codeception\Scenario
|
||||
*/
|
||||
abstract protected function getScenario();
|
||||
|
||||
public function expectTo($prediction)
|
||||
{
|
||||
return $this->comment('I expect to ' . $prediction);
|
||||
}
|
||||
|
||||
public function expect($prediction)
|
||||
{
|
||||
return $this->comment('I expect ' . $prediction);
|
||||
}
|
||||
|
||||
public function amGoingTo($argumentation)
|
||||
{
|
||||
return $this->comment('I am going to ' . $argumentation);
|
||||
}
|
||||
|
||||
public function am($role)
|
||||
{
|
||||
$role = trim($role);
|
||||
|
||||
if (stripos('aeiou', $role[0]) !== false) {
|
||||
return $this->comment('As an ' . $role);
|
||||
}
|
||||
|
||||
return $this->comment('As a ' . $role);
|
||||
}
|
||||
|
||||
public function lookForwardTo($achieveValue)
|
||||
{
|
||||
return $this->comment('So that I ' . $achieveValue);
|
||||
}
|
||||
|
||||
public function comment($description)
|
||||
{
|
||||
$this->getScenario()->comment($description);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
29
vendor/codeception/base/src/Codeception/Lib/Actor/Shared/Friend.php
vendored
Normal file
29
vendor/codeception/base/src/Codeception/Lib/Actor/Shared/Friend.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Actor\Shared;
|
||||
|
||||
use Codeception\Lib\Friend as LibFriend;
|
||||
use Codeception\Scenario;
|
||||
|
||||
trait Friend
|
||||
{
|
||||
protected $friends = [];
|
||||
|
||||
/**
|
||||
* @return Scenario
|
||||
*/
|
||||
abstract protected function getScenario();
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $actorClass
|
||||
* @return \Codeception\Lib\Friend
|
||||
*/
|
||||
public function haveFriend($name, $actorClass = null)
|
||||
{
|
||||
if (!isset($this->friends[$name])) {
|
||||
$actor = $actorClass === null ? $this : new $actorClass($this->getScenario());
|
||||
$this->friends[$name] = new LibFriend($name, $actor, $this->getScenario()->current('modules'));
|
||||
}
|
||||
return $this->friends[$name];
|
||||
}
|
||||
}
|
||||
295
vendor/codeception/base/src/Codeception/Lib/Connector/Guzzle.php
vendored
Normal file
295
vendor/codeception/base/src/Codeception/Lib/Connector/Guzzle.php
vendored
Normal file
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
use Aws\Credentials\Credentials;
|
||||
use Aws\Signature\SignatureV4;
|
||||
use Codeception\Util\Uri;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Message\Response;
|
||||
use GuzzleHttp\Post\PostFile;
|
||||
use Symfony\Component\BrowserKit\Client;
|
||||
use Symfony\Component\BrowserKit\Response as BrowserKitResponse;
|
||||
use GuzzleHttp\Url;
|
||||
use Symfony\Component\BrowserKit\Request as BrowserKitRequest;
|
||||
|
||||
class Guzzle extends Client
|
||||
{
|
||||
protected $baseUri;
|
||||
protected $requestOptions = [
|
||||
'allow_redirects' => false,
|
||||
'headers' => [],
|
||||
];
|
||||
protected $refreshMaxInterval = 0;
|
||||
|
||||
protected $awsCredentials = null;
|
||||
protected $awsSignature = null;
|
||||
|
||||
/** @var \GuzzleHttp\Client */
|
||||
protected $client;
|
||||
|
||||
public function setBaseUri($uri)
|
||||
{
|
||||
$this->baseUri = $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum allowable timeout interval for a meta tag refresh to
|
||||
* automatically redirect a request.
|
||||
*
|
||||
* A meta tag detected with an interval equal to or greater than $seconds
|
||||
* would not result in a redirect. A meta tag without a specified interval
|
||||
* or one with a value less than $seconds would result in the client
|
||||
* automatically redirecting to the specified URL
|
||||
*
|
||||
* @param int $seconds Number of seconds
|
||||
*/
|
||||
public function setRefreshMaxInterval($seconds)
|
||||
{
|
||||
$this->refreshMaxInterval = $seconds;
|
||||
}
|
||||
|
||||
public function setClient(\GuzzleHttp\Client $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the request header to the passed value. The header will be
|
||||
* sent along with the next request.
|
||||
*
|
||||
* Passing an empty value clears the header, which is the equivalent
|
||||
* of calling deleteHeader.
|
||||
*
|
||||
* @param string $name the name of the header
|
||||
* @param string $value the value of the header
|
||||
*/
|
||||
public function setHeader($name, $value)
|
||||
{
|
||||
if (strval($value) === '') {
|
||||
$this->deleteHeader($name);
|
||||
} else {
|
||||
$this->requestOptions['headers'][$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the header with the passed name from the list of headers
|
||||
* that will be sent with the request.
|
||||
*
|
||||
* @param string $name the name of the header to delete.
|
||||
*/
|
||||
public function deleteHeader($name)
|
||||
{
|
||||
unset($this->requestOptions['headers'][$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param string $type Default: 'basic'
|
||||
*/
|
||||
public function setAuth($username, $password, $type = 'basic')
|
||||
{
|
||||
if (!$username) {
|
||||
unset($this->requestOptions['auth']);
|
||||
return;
|
||||
}
|
||||
$this->requestOptions['auth'] = [$username, $password, $type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Taken from Mink\BrowserKitDriver
|
||||
*
|
||||
* @param Response $response
|
||||
*
|
||||
* @return \Symfony\Component\BrowserKit\Response
|
||||
*/
|
||||
protected function createResponse(Response $response)
|
||||
{
|
||||
$contentType = $response->getHeader('Content-Type');
|
||||
|
||||
if (!$contentType) {
|
||||
$contentType = 'text/html';
|
||||
}
|
||||
|
||||
if (strpos($contentType, 'charset=') === false) {
|
||||
$body = $response->getBody(true);
|
||||
if (preg_match('/\<meta[^\>]+charset *= *["\']?([a-zA-Z\-0-9]+)/i', $body, $matches)) {
|
||||
$contentType .= ';charset=' . $matches[1];
|
||||
}
|
||||
$response->setHeader('Content-Type', $contentType);
|
||||
}
|
||||
|
||||
$headers = $response->getHeaders();
|
||||
$status = $response->getStatusCode();
|
||||
if ($status < 300 || $status >= 400) {
|
||||
$matches = [];
|
||||
|
||||
$matchesMeta = preg_match(
|
||||
'/\<meta[^\>]+http-equiv="refresh" content="\s*(\d*)\s*;\s*url=(.*?)"/i',
|
||||
$response->getBody(true),
|
||||
$matches
|
||||
);
|
||||
|
||||
if (!$matchesMeta) {
|
||||
// match by header
|
||||
preg_match(
|
||||
'/^\s*(\d*)\s*;\s*url=(.*)/i',
|
||||
(string)$response->getHeader('Refresh'),
|
||||
$matches
|
||||
);
|
||||
}
|
||||
|
||||
if ((!empty($matches)) && (empty($matches[1]) || $matches[1] < $this->refreshMaxInterval)) {
|
||||
$uri = $this->getAbsoluteUri($matches[2]);
|
||||
$partsUri = parse_url($uri);
|
||||
$partsCur = parse_url($this->getHistory()->current()->getUri());
|
||||
foreach ($partsCur as $key => $part) {
|
||||
if ($key === 'fragment') {
|
||||
continue;
|
||||
}
|
||||
if (!isset($partsUri[$key]) || $partsUri[$key] !== $part) {
|
||||
$status = 302;
|
||||
$headers['Location'] = $matchesMeta ? htmlspecialchars_decode($uri) : $uri;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new BrowserKitResponse($response->getBody(), $status, $headers);
|
||||
}
|
||||
|
||||
public function getAbsoluteUri($uri)
|
||||
{
|
||||
$baseUri = $this->baseUri;
|
||||
if (strpos($uri, '://') === false && strpos($uri, '//') !== 0) {
|
||||
if (strpos($uri, '/') === 0) {
|
||||
$baseUriPath = parse_url($baseUri, PHP_URL_PATH);
|
||||
if (!empty($baseUriPath) && strpos($uri, $baseUriPath) === 0) {
|
||||
$uri = substr($uri, strlen($baseUriPath));
|
||||
}
|
||||
|
||||
return Uri::appendPath((string)$baseUri, $uri);
|
||||
}
|
||||
// relative url
|
||||
if (!$this->getHistory()->isEmpty()) {
|
||||
return Uri::mergeUrls((string)$this->getHistory()->current()->getUri(), $uri);
|
||||
}
|
||||
}
|
||||
return Uri::mergeUrls($baseUri, $uri);
|
||||
}
|
||||
|
||||
protected function doRequest($request)
|
||||
{
|
||||
/** @var $request BrowserKitRequest **/
|
||||
$requestOptions = [
|
||||
'body' => $this->extractBody($request),
|
||||
'cookies' => $this->extractCookies($request),
|
||||
'headers' => $this->extractHeaders($request)
|
||||
];
|
||||
|
||||
$requestOptions = array_replace_recursive($requestOptions, $this->requestOptions);
|
||||
|
||||
$guzzleRequest = $this->client->createRequest(
|
||||
$request->getMethod(),
|
||||
$request->getUri(),
|
||||
$requestOptions
|
||||
);
|
||||
foreach ($this->extractFiles($request) as $postFile) {
|
||||
$guzzleRequest->getBody()->addFile($postFile);
|
||||
}
|
||||
|
||||
// Let BrowserKit handle redirects
|
||||
try {
|
||||
if (null !== $this->awsCredentials) {
|
||||
$response = $this->client->send($this->awsSignature->signRequest($guzzleRequest, $this->awsCredentials));
|
||||
} else {
|
||||
$response = $this->client->send($guzzleRequest);
|
||||
}
|
||||
} catch (RequestException $e) {
|
||||
if ($e->hasResponse()) {
|
||||
$response = $e->getResponse();
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
return $this->createResponse($response);
|
||||
}
|
||||
|
||||
protected function extractHeaders(BrowserKitRequest $request)
|
||||
{
|
||||
$headers = [];
|
||||
$server = $request->getServer();
|
||||
|
||||
$contentHeaders = ['Content-Length' => true, 'Content-Md5' => true, 'Content-Type' => true];
|
||||
foreach ($server as $header => $val) {
|
||||
$header = html_entity_decode(implode('-', array_map('ucfirst', explode('-', strtolower(str_replace('_', '-', $header))))), ENT_NOQUOTES);
|
||||
if (strpos($header, 'Http-') === 0) {
|
||||
$headers[substr($header, 5)] = $val;
|
||||
} elseif (isset($contentHeaders[$header])) {
|
||||
$headers[$header] = $val;
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
protected function extractBody(BrowserKitRequest $request)
|
||||
{
|
||||
if (in_array(strtoupper($request->getMethod()), ['GET', 'HEAD'])) {
|
||||
return null;
|
||||
}
|
||||
if ($request->getContent() !== null) {
|
||||
return $request->getContent();
|
||||
}
|
||||
|
||||
return $request->getParameters();
|
||||
}
|
||||
|
||||
protected function extractFiles(BrowserKitRequest $request)
|
||||
{
|
||||
if (!in_array(strtoupper($request->getMethod()), ['POST', 'PUT'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->mapFiles($request->getFiles());
|
||||
}
|
||||
|
||||
protected function mapFiles($requestFiles, $arrayName = '')
|
||||
{
|
||||
$files = [];
|
||||
foreach ($requestFiles as $name => $info) {
|
||||
if (!empty($arrayName)) {
|
||||
$name = $arrayName.'['.$name.']';
|
||||
}
|
||||
|
||||
if (is_array($info)) {
|
||||
if (isset($info['tmp_name'])) {
|
||||
if ($info['tmp_name']) {
|
||||
$handle = fopen($info['tmp_name'], 'r');
|
||||
$filename = isset($info['name']) ? $info['name'] : null;
|
||||
|
||||
$files[] = new PostFile($name, $handle, $filename);
|
||||
}
|
||||
} else {
|
||||
$files = array_merge($files, $this->mapFiles($info, $name));
|
||||
}
|
||||
} else {
|
||||
$files[] = new PostFile($name, fopen($info, 'r'));
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
protected function extractCookies(BrowserKitRequest $request)
|
||||
{
|
||||
return $this->getCookieJar()->allRawValues($request->getUri());
|
||||
}
|
||||
|
||||
public function setAwsAuth($config)
|
||||
{
|
||||
$this->awsCredentials = new Credentials($config['key'], $config['secret']);
|
||||
$this->awsSignature = new SignatureV4($config['service'], $config['region']);
|
||||
}
|
||||
}
|
||||
356
vendor/codeception/base/src/Codeception/Lib/Connector/Guzzle6.php
vendored
Normal file
356
vendor/codeception/base/src/Codeception/Lib/Connector/Guzzle6.php
vendored
Normal file
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
use Aws\Credentials\Credentials;
|
||||
use Aws\Signature\SignatureV4;
|
||||
use Codeception\Util\Uri;
|
||||
use GuzzleHttp\Client as GuzzleClient;
|
||||
use GuzzleHttp\Cookie\CookieJar;
|
||||
use GuzzleHttp\Cookie\SetCookie;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Handler\CurlHandler;
|
||||
use GuzzleHttp\Handler\StreamHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Request as Psr7Request;
|
||||
use GuzzleHttp\Psr7\Response as Psr7Response;
|
||||
use GuzzleHttp\Psr7\Uri as Psr7Uri;
|
||||
use Symfony\Component\BrowserKit\Client;
|
||||
use Symfony\Component\BrowserKit\Cookie;
|
||||
use Symfony\Component\BrowserKit\Request as BrowserKitRequest;
|
||||
use Symfony\Component\BrowserKit\Request;
|
||||
use Symfony\Component\BrowserKit\Response as BrowserKitResponse;
|
||||
|
||||
class Guzzle6 extends Client
|
||||
{
|
||||
protected $requestOptions = [
|
||||
'allow_redirects' => false,
|
||||
'headers' => [],
|
||||
];
|
||||
protected $refreshMaxInterval = 0;
|
||||
|
||||
protected $awsCredentials = null;
|
||||
protected $awsSignature = null;
|
||||
|
||||
/** @var \GuzzleHttp\Client */
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Sets the maximum allowable timeout interval for a meta tag refresh to
|
||||
* automatically redirect a request.
|
||||
*
|
||||
* A meta tag detected with an interval equal to or greater than $seconds
|
||||
* would not result in a redirect. A meta tag without a specified interval
|
||||
* or one with a value less than $seconds would result in the client
|
||||
* automatically redirecting to the specified URL
|
||||
*
|
||||
* @param int $seconds Number of seconds
|
||||
*/
|
||||
public function setRefreshMaxInterval($seconds)
|
||||
{
|
||||
$this->refreshMaxInterval = $seconds;
|
||||
}
|
||||
|
||||
public function setClient(GuzzleClient &$client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the request header to the passed value. The header will be
|
||||
* sent along with the next request.
|
||||
*
|
||||
* Passing an empty value clears the header, which is the equivalent
|
||||
* of calling deleteHeader.
|
||||
*
|
||||
* @param string $name the name of the header
|
||||
* @param string $value the value of the header
|
||||
*/
|
||||
public function setHeader($name, $value)
|
||||
{
|
||||
if (strval($value) === '') {
|
||||
$this->deleteHeader($name);
|
||||
} else {
|
||||
$this->requestOptions['headers'][$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the header with the passed name from the list of headers
|
||||
* that will be sent with the request.
|
||||
*
|
||||
* @param string $name the name of the header to delete.
|
||||
*/
|
||||
public function deleteHeader($name)
|
||||
{
|
||||
unset($this->requestOptions['headers'][$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param string $type Default: 'basic'
|
||||
*/
|
||||
public function setAuth($username, $password, $type = 'basic')
|
||||
{
|
||||
if (!$username) {
|
||||
unset($this->requestOptions['auth']);
|
||||
return;
|
||||
}
|
||||
$this->requestOptions['auth'] = [$username, $password, $type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Taken from Mink\BrowserKitDriver
|
||||
*
|
||||
* @param Response $response
|
||||
*
|
||||
* @return \Symfony\Component\BrowserKit\Response
|
||||
*/
|
||||
protected function createResponse(Psr7Response $response)
|
||||
{
|
||||
$body = (string) $response->getBody();
|
||||
$headers = $response->getHeaders();
|
||||
|
||||
$contentType = null;
|
||||
|
||||
if (isset($headers['Content-Type'])) {
|
||||
$contentType = reset($headers['Content-Type']);
|
||||
}
|
||||
if (!$contentType) {
|
||||
$contentType = 'text/html';
|
||||
}
|
||||
|
||||
if (strpos($contentType, 'charset=') === false) {
|
||||
if (preg_match('/\<meta[^\>]+charset *= *["\']?([a-zA-Z\-0-9]+)/i', $body, $matches)) {
|
||||
$contentType .= ';charset=' . $matches[1];
|
||||
}
|
||||
$headers['Content-Type'] = [$contentType];
|
||||
}
|
||||
|
||||
$status = $response->getStatusCode();
|
||||
if ($status < 300 || $status >= 400) {
|
||||
$matches = [];
|
||||
|
||||
$matchesMeta = preg_match(
|
||||
'/\<meta[^\>]+http-equiv="refresh" content="\s*(\d*)\s*;\s*url=(.*?)"/i',
|
||||
$body,
|
||||
$matches
|
||||
);
|
||||
|
||||
if (!$matchesMeta && isset($headers['Refresh'])) {
|
||||
// match by header
|
||||
preg_match(
|
||||
'/^\s*(\d*)\s*;\s*url=(.*)/i',
|
||||
(string) reset($headers['Refresh']),
|
||||
$matches
|
||||
);
|
||||
}
|
||||
|
||||
if ((!empty($matches)) && (empty($matches[1]) || $matches[1] < $this->refreshMaxInterval)) {
|
||||
$uri = new Psr7Uri($this->getAbsoluteUri($matches[2]));
|
||||
$currentUri = new Psr7Uri($this->getHistory()->current()->getUri());
|
||||
|
||||
if ($uri->withFragment('') != $currentUri->withFragment('')) {
|
||||
$status = 302;
|
||||
$headers['Location'] = $matchesMeta ? htmlspecialchars_decode($uri) : (string)$uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new BrowserKitResponse($body, $status, $headers);
|
||||
}
|
||||
|
||||
public function getAbsoluteUri($uri)
|
||||
{
|
||||
$baseUri = $this->client->getConfig('base_uri');
|
||||
if (strpos($uri, '://') === false && strpos($uri, '//') !== 0) {
|
||||
if (strpos($uri, '/') === 0) {
|
||||
$baseUriPath = $baseUri->getPath();
|
||||
if (!empty($baseUriPath) && strpos($uri, $baseUriPath) === 0) {
|
||||
$uri = substr($uri, strlen($baseUriPath));
|
||||
}
|
||||
|
||||
return Uri::appendPath((string)$baseUri, $uri);
|
||||
}
|
||||
// relative url
|
||||
if (!$this->getHistory()->isEmpty()) {
|
||||
return Uri::mergeUrls((string)$this->getHistory()->current()->getUri(), $uri);
|
||||
}
|
||||
}
|
||||
return Uri::mergeUrls($baseUri, $uri);
|
||||
}
|
||||
|
||||
protected function doRequest($request)
|
||||
{
|
||||
/** @var $request BrowserKitRequest **/
|
||||
$guzzleRequest = new Psr7Request(
|
||||
$request->getMethod(),
|
||||
$request->getUri(),
|
||||
$this->extractHeaders($request),
|
||||
$request->getContent()
|
||||
);
|
||||
$options = $this->requestOptions;
|
||||
$options['cookies'] = $this->extractCookies($guzzleRequest->getUri()->getHost());
|
||||
$multipartData = $this->extractMultipartFormData($request);
|
||||
if (!empty($multipartData)) {
|
||||
$options['multipart'] = $multipartData;
|
||||
}
|
||||
|
||||
$formData = $this->extractFormData($request);
|
||||
if (empty($multipartData) and $formData) {
|
||||
$options['form_params'] = $formData;
|
||||
}
|
||||
|
||||
try {
|
||||
if (null !== $this->awsCredentials) {
|
||||
$response = $this->client->send($this->awsSignature->signRequest($guzzleRequest, $this->awsCredentials), $options);
|
||||
} else {
|
||||
$response = $this->client->send($guzzleRequest, $options);
|
||||
}
|
||||
} catch (RequestException $e) {
|
||||
if (!$e->hasResponse()) {
|
||||
throw $e;
|
||||
}
|
||||
$response = $e->getResponse();
|
||||
}
|
||||
return $this->createResponse($response);
|
||||
}
|
||||
|
||||
protected function extractHeaders(BrowserKitRequest $request)
|
||||
{
|
||||
$headers = [];
|
||||
$server = $request->getServer();
|
||||
|
||||
$contentHeaders = ['Content-Length' => true, 'Content-Md5' => true, 'Content-Type' => true];
|
||||
foreach ($server as $header => $val) {
|
||||
$header = html_entity_decode(implode('-', array_map('ucfirst', explode('-', strtolower(str_replace('_', '-', $header))))), ENT_NOQUOTES);
|
||||
if (strpos($header, 'Http-') === 0) {
|
||||
$headers[substr($header, 5)] = $val;
|
||||
} elseif (isset($contentHeaders[$header])) {
|
||||
$headers[$header] = $val;
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
protected function extractFormData(BrowserKitRequest $request)
|
||||
{
|
||||
if (!in_array(strtoupper($request->getMethod()), ['POST', 'PUT', 'PATCH', 'DELETE'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// guessing if it is a form data
|
||||
$headers = $request->getServer();
|
||||
if (isset($headers['HTTP_CONTENT_TYPE'])) {
|
||||
// not a form
|
||||
if ($headers['HTTP_CONTENT_TYPE'] !== 'application/x-www-form-urlencoded') {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if ($request->getContent() !== null) {
|
||||
return null;
|
||||
}
|
||||
return $request->getParameters();
|
||||
}
|
||||
|
||||
protected function extractMultipartFormData(Request $request)
|
||||
{
|
||||
if (!in_array(strtoupper($request->getMethod()), ['POST', 'PUT', 'PATCH'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$parts = $this->mapFiles($request->getFiles());
|
||||
if (empty($parts)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($request->getParameters() as $k => $v) {
|
||||
$parts = $this->formatMultipart($parts, $k, $v);
|
||||
}
|
||||
return $parts;
|
||||
}
|
||||
|
||||
protected function formatMultipart($parts, $key, $value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $subKey => $subValue) {
|
||||
$parts = array_merge($this->formatMultipart([], $key."[$subKey]", $subValue), $parts);
|
||||
}
|
||||
return $parts;
|
||||
}
|
||||
$parts[] = ['name' => $key, 'contents' => (string) $value];
|
||||
return $parts;
|
||||
}
|
||||
|
||||
protected function mapFiles($requestFiles, $arrayName = '')
|
||||
{
|
||||
$files = [];
|
||||
foreach ($requestFiles as $name => $info) {
|
||||
if (!empty($arrayName)) {
|
||||
$name = $arrayName . '[' . $name . ']';
|
||||
}
|
||||
|
||||
if (is_array($info)) {
|
||||
if (isset($info['tmp_name'])) {
|
||||
if ($info['tmp_name']) {
|
||||
$handle = fopen($info['tmp_name'], 'r');
|
||||
$filename = isset($info['name']) ? $info['name'] : null;
|
||||
|
||||
$files[] = [
|
||||
'name' => $name,
|
||||
'contents' => $handle,
|
||||
'filename' => $filename
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$files = array_merge($files, $this->mapFiles($info, $name));
|
||||
}
|
||||
} else {
|
||||
$files[] = [
|
||||
'name' => $name,
|
||||
'contents' => fopen($info, 'r')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
protected function extractCookies($host)
|
||||
{
|
||||
$jar = [];
|
||||
$cookies = $this->getCookieJar()->all();
|
||||
foreach ($cookies as $cookie) {
|
||||
/** @var $cookie Cookie **/
|
||||
$setCookie = SetCookie::fromString((string)$cookie);
|
||||
if (!$setCookie->getDomain()) {
|
||||
$setCookie->setDomain($host);
|
||||
}
|
||||
$jar[] = $setCookie;
|
||||
}
|
||||
return new CookieJar(false, $jar);
|
||||
}
|
||||
|
||||
public static function createHandler($handler)
|
||||
{
|
||||
if ($handler === 'curl') {
|
||||
return HandlerStack::create(new CurlHandler());
|
||||
}
|
||||
if ($handler === 'stream') {
|
||||
return HandlerStack::create(new StreamHandler());
|
||||
}
|
||||
if (class_exists($handler)) {
|
||||
return HandlerStack::create(new $handler);
|
||||
}
|
||||
if (is_callable($handler)) {
|
||||
return HandlerStack::create($handler);
|
||||
}
|
||||
return HandlerStack::create();
|
||||
}
|
||||
|
||||
public function setAwsAuth($config)
|
||||
{
|
||||
$this->awsCredentials = new Credentials($config['key'], $config['secret']);
|
||||
$this->awsSignature = new SignatureV4($config['service'], $config['region']);
|
||||
}
|
||||
}
|
||||
354
vendor/codeception/base/src/Codeception/Lib/Connector/Laravel5.php
vendored
Normal file
354
vendor/codeception/base/src/Codeception/Lib/Connector/Laravel5.php
vendored
Normal file
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
use Codeception\Lib\Connector\Laravel5\ExceptionHandlerDecorator;
|
||||
use Codeception\Lib\Connector\Shared\LaravelCommon;
|
||||
use Codeception\Stub;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Client;
|
||||
|
||||
class Laravel5 extends Client
|
||||
{
|
||||
use LaravelCommon;
|
||||
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
private $app;
|
||||
|
||||
/**
|
||||
* @var \Codeception\Module\Laravel5
|
||||
*/
|
||||
private $module;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $firstRequest = true;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $triggeredEvents = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $exceptionHandlingDisabled;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $middlewareDisabled;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $eventsDisabled;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $modelEventsDisabled;
|
||||
|
||||
/**
|
||||
* @var object
|
||||
*/
|
||||
private $oldDb;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Codeception\Module\Laravel5 $module
|
||||
*/
|
||||
public function __construct($module)
|
||||
{
|
||||
$this->module = $module;
|
||||
|
||||
$this->exceptionHandlingDisabled = $this->module->config['disable_exception_handling'];
|
||||
$this->middlewareDisabled = $this->module->config['disable_middleware'];
|
||||
$this->eventsDisabled = $this->module->config['disable_events'];
|
||||
$this->modelEventsDisabled = $this->module->config['disable_model_events'];
|
||||
|
||||
$this->initialize();
|
||||
|
||||
$components = parse_url($this->app['config']->get('app.url', 'http://localhost'));
|
||||
if (array_key_exists('url', $this->module->config)) {
|
||||
$components = parse_url($this->module->config['url']);
|
||||
}
|
||||
$host = isset($components['host']) ? $components['host'] : 'localhost';
|
||||
|
||||
parent::__construct($this->app, ['HTTP_HOST' => $host]);
|
||||
|
||||
// Parent constructor defaults to not following redirects
|
||||
$this->followRedirects(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a request.
|
||||
*
|
||||
* @param SymfonyRequest $request
|
||||
* @return Response
|
||||
*/
|
||||
protected function doRequest($request)
|
||||
{
|
||||
if (!$this->firstRequest) {
|
||||
$this->initialize($request);
|
||||
}
|
||||
$this->firstRequest = false;
|
||||
|
||||
$this->applyBindings();
|
||||
$this->applyContextualBindings();
|
||||
$this->applyInstances();
|
||||
$this->applyApplicationHandlers();
|
||||
|
||||
$request = Request::createFromBase($request);
|
||||
$response = $this->kernel->handle($request);
|
||||
$this->app->make('Illuminate\Contracts\Http\Kernel')->terminate($request, $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure files are \Illuminate\Http\UploadedFile instances with the private $test property set to true.
|
||||
* Fixes issue https://github.com/Codeception/Codeception/pull/3417.
|
||||
*
|
||||
* @param array $files
|
||||
* @return array
|
||||
*/
|
||||
protected function filterFiles(array $files)
|
||||
{
|
||||
$files = parent::filterFiles($files);
|
||||
|
||||
if (! class_exists('Illuminate\Http\UploadedFile')) {
|
||||
// The \Illuminate\Http\UploadedFile class was introduced in Laravel 5.2.15,
|
||||
// so don't change the $files array if it does not exist.
|
||||
return $files;
|
||||
}
|
||||
|
||||
return $this->convertToTestFiles($files);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $files
|
||||
* @return array
|
||||
*/
|
||||
private function convertToTestFiles(array $files)
|
||||
{
|
||||
$filtered = [];
|
||||
|
||||
foreach ($files as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$filtered[$key] = $this->convertToTestFiles($value);
|
||||
} else {
|
||||
$filtered[$key] = UploadedFile::createFromBase($value, true);
|
||||
}
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Laravel framework.
|
||||
*
|
||||
* @param SymfonyRequest $request
|
||||
*/
|
||||
private function initialize($request = null)
|
||||
{
|
||||
// Store a reference to the database object
|
||||
// so the database connection can be reused during tests
|
||||
$this->oldDb = null;
|
||||
if (isset($this->app['db']) && $this->app['db']->connection()) {
|
||||
$this->oldDb = $this->app['db'];
|
||||
}
|
||||
|
||||
$this->app = $this->kernel = $this->loadApplication();
|
||||
|
||||
// Set the request instance for the application,
|
||||
if (is_null($request)) {
|
||||
$appConfig = require $this->module->config['project_dir'] . 'config/app.php';
|
||||
$request = SymfonyRequest::create($appConfig['url']);
|
||||
}
|
||||
$this->app->instance('request', Request::createFromBase($request));
|
||||
|
||||
// Reset the old database after all the service providers are registered.
|
||||
if ($this->oldDb) {
|
||||
$this->app['events']->listen('bootstrapped: Illuminate\Foundation\Bootstrap\RegisterProviders', function () {
|
||||
$this->app->singleton('db', function () {
|
||||
return $this->oldDb;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$this->app->make('Illuminate\Contracts\Http\Kernel')->bootstrap();
|
||||
|
||||
// Record all triggered events by adding a wildcard event listener
|
||||
// Since Laravel 5.4 wildcard event handlers receive the event name as the first argument,
|
||||
// but for earlier Laravel versions the firing() method of the event dispatcher should be used
|
||||
// to determine the event name.
|
||||
if (method_exists($this->app['events'], 'firing')) {
|
||||
$listener = function () {
|
||||
$this->triggeredEvents[] = $this->normalizeEvent($this->app['events']->firing());
|
||||
};
|
||||
} else {
|
||||
$listener = function ($event) {
|
||||
$this->triggeredEvents[] = $this->normalizeEvent($event);
|
||||
};
|
||||
}
|
||||
$this->app['events']->listen('*', $listener);
|
||||
|
||||
// Replace the Laravel exception handler with our decorated exception handler,
|
||||
// so exceptions can be intercepted for the disable_exception_handling functionality.
|
||||
$decorator = new ExceptionHandlerDecorator($this->app['Illuminate\Contracts\Debug\ExceptionHandler']);
|
||||
$decorator->exceptionHandlingDisabled($this->exceptionHandlingDisabled);
|
||||
$this->app->instance('Illuminate\Contracts\Debug\ExceptionHandler', $decorator);
|
||||
|
||||
if ($this->module->config['disable_middleware'] || $this->middlewareDisabled) {
|
||||
$this->app->instance('middleware.disable', true);
|
||||
}
|
||||
|
||||
if ($this->module->config['disable_events'] || $this->eventsDisabled) {
|
||||
$this->mockEventDispatcher();
|
||||
}
|
||||
|
||||
if ($this->module->config['disable_model_events'] || $this->modelEventsDisabled) {
|
||||
Model::unsetEventDispatcher();
|
||||
}
|
||||
|
||||
$this->module->setApplication($this->app);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot the Laravel application object.
|
||||
* @return Application
|
||||
* @throws ModuleConfig
|
||||
*/
|
||||
private function loadApplication()
|
||||
{
|
||||
$app = require $this->module->config['bootstrap_file'];
|
||||
$app->loadEnvironmentFrom($this->module->config['environment_file']);
|
||||
$app->instance('request', new Request());
|
||||
|
||||
return $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the Laravel event dispatcher with a mock.
|
||||
*/
|
||||
private function mockEventDispatcher()
|
||||
{
|
||||
// Even if events are disabled we still want to record the triggered events.
|
||||
// But by mocking the event dispatcher the wildcard listener registered in the initialize method is removed.
|
||||
// So to record the triggered events we have to catch the calls to the fire method of the event dispatcher mock.
|
||||
$callback = function ($event) {
|
||||
$this->triggeredEvents[] = $this->normalizeEvent($event);
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
// In Laravel 5.4 the Illuminate\Contracts\Events\Dispatcher interface was changed,
|
||||
// the 'fire' method was renamed to 'dispatch'. This code determines the correct method to mock.
|
||||
$method = method_exists($this->app['events'], 'dispatch') ? 'dispatch' : 'fire';
|
||||
|
||||
$mock = Stub::makeEmpty('Illuminate\Contracts\Events\Dispatcher', [
|
||||
$method => $callback
|
||||
]);
|
||||
|
||||
$this->app->instance('events', $mock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize events to class names.
|
||||
*
|
||||
* @param $event
|
||||
* @return string
|
||||
*/
|
||||
private function normalizeEvent($event)
|
||||
{
|
||||
if (is_object($event)) {
|
||||
$event = get_class($event);
|
||||
}
|
||||
|
||||
if (preg_match('/^bootstrapp(ing|ed): /', $event)) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
// Events can be formatted as 'event.name: parameters'
|
||||
$segments = explode(':', $event);
|
||||
|
||||
return $segments[0];
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Public methods called by module
|
||||
//======================================================================
|
||||
|
||||
/**
|
||||
* Did an event trigger?
|
||||
*
|
||||
* @param $event
|
||||
* @return bool
|
||||
*/
|
||||
public function eventTriggered($event)
|
||||
{
|
||||
$event = $this->normalizeEvent($event);
|
||||
|
||||
foreach ($this->triggeredEvents as $triggeredEvent) {
|
||||
if ($event == $triggeredEvent || is_subclass_of($event, $triggeredEvent)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable Laravel exception handling.
|
||||
*/
|
||||
public function disableExceptionHandling()
|
||||
{
|
||||
$this->exceptionHandlingDisabled = true;
|
||||
$this->app['Illuminate\Contracts\Debug\ExceptionHandler']->exceptionHandlingDisabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable Laravel exception handling.
|
||||
*/
|
||||
public function enableExceptionHandling()
|
||||
{
|
||||
$this->exceptionHandlingDisabled = false;
|
||||
$this->app['Illuminate\Contracts\Debug\ExceptionHandler']->exceptionHandlingDisabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable events.
|
||||
*/
|
||||
public function disableEvents()
|
||||
{
|
||||
$this->eventsDisabled = true;
|
||||
$this->mockEventDispatcher();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable model events.
|
||||
*/
|
||||
public function disableModelEvents()
|
||||
{
|
||||
$this->modelEventsDisabled = true;
|
||||
Model::unsetEventDispatcher();
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable middleware.
|
||||
*/
|
||||
public function disableMiddleware()
|
||||
{
|
||||
$this->middlewareDisabled = true;
|
||||
$this->app->instance('middleware.disable', true);
|
||||
}
|
||||
}
|
||||
106
vendor/codeception/base/src/Codeception/Lib/Connector/Laravel5/ExceptionHandlerDecorator.php
vendored
Normal file
106
vendor/codeception/base/src/Codeception/Lib/Connector/Laravel5/ExceptionHandlerDecorator.php
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector\Laravel5;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract;
|
||||
|
||||
/**
|
||||
* Class ExceptionHandlerDecorator
|
||||
*
|
||||
* @package Codeception\Lib\Connector\Laravel5
|
||||
*/
|
||||
class ExceptionHandlerDecorator implements ExceptionHandlerContract
|
||||
{
|
||||
/**
|
||||
* @var ExceptionHandlerContract
|
||||
*/
|
||||
private $laravelExceptionHandler;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
private $exceptionHandlingDisabled = true;
|
||||
|
||||
/**
|
||||
* ExceptionHandlerDecorator constructor.
|
||||
*
|
||||
* @param object $laravelExceptionHandler
|
||||
*/
|
||||
public function __construct($laravelExceptionHandler)
|
||||
{
|
||||
$this->laravelExceptionHandler = $laravelExceptionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $exceptionHandlingDisabled
|
||||
*/
|
||||
public function exceptionHandlingDisabled($exceptionHandlingDisabled)
|
||||
{
|
||||
$this->exceptionHandlingDisabled = $exceptionHandlingDisabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @return void
|
||||
*/
|
||||
public function report(Exception $e)
|
||||
{
|
||||
$this->laravelExceptionHandler->report($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $request
|
||||
* @param Exception $e
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function render($request, Exception $e)
|
||||
{
|
||||
$response = $this->laravelExceptionHandler->render($request, $e);
|
||||
|
||||
if ($this->exceptionHandlingDisabled && $this->isSymfonyExceptionHandlerOutput($response->getContent())) {
|
||||
// If content was generated by the \Symfony\Component\Debug\ExceptionHandler class
|
||||
// the Laravel application could not handle the exception,
|
||||
// so re-throw this exception if the Codeception user disabled Laravel's exception handling.
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the response content is HTML output of the Symfony exception handler class.
|
||||
*
|
||||
* @param string $content
|
||||
* @return bool
|
||||
*/
|
||||
private function isSymfonyExceptionHandlerOutput($content)
|
||||
{
|
||||
return strpos($content, '<div id="sf-resetcontent" class="sf-reset">') !== false ||
|
||||
strpos($content, '<div class="exception-summary">') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception to the console.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @param \Exception $e
|
||||
* @return void
|
||||
*/
|
||||
public function renderForConsole($output, Exception $e)
|
||||
{
|
||||
$this->laravelExceptionHandler->renderForConsole($output, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return call_user_func_array([$this->laravelExceptionHandler, $method], $args);
|
||||
}
|
||||
}
|
||||
167
vendor/codeception/base/src/Codeception/Lib/Connector/Lumen.php
vendored
Normal file
167
vendor/codeception/base/src/Codeception/Lib/Connector/Lumen.php
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
use Codeception\Lib\Connector\Lumen\DummyKernel;
|
||||
use Codeception\Lib\Connector\Shared\LaravelCommon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Client;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
|
||||
class Lumen extends Client
|
||||
{
|
||||
use LaravelCommon;
|
||||
|
||||
/**
|
||||
* @var \Laravel\Lumen\Application
|
||||
*/
|
||||
private $app;
|
||||
|
||||
/**
|
||||
* @var \Codeception\Module\Lumen
|
||||
*/
|
||||
private $module;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $firstRequest = true;
|
||||
|
||||
/**
|
||||
* @var object
|
||||
*/
|
||||
private $oldDb;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Codeception\Module\Lumen $module
|
||||
*/
|
||||
public function __construct($module)
|
||||
{
|
||||
$this->module = $module;
|
||||
|
||||
$components = parse_url($this->module->config['url']);
|
||||
$server = ['HTTP_HOST' => $components['host']];
|
||||
|
||||
// Pass a DummyKernel to satisfy the arguments of the parent constructor.
|
||||
// The actual kernel object is set in the initialize() method.
|
||||
parent::__construct(new DummyKernel(), $server);
|
||||
|
||||
// Parent constructor defaults to not following redirects
|
||||
$this->followRedirects(true);
|
||||
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a request.
|
||||
*
|
||||
* @param SymfonyRequest $request
|
||||
* @return Response
|
||||
*/
|
||||
protected function doRequest($request)
|
||||
{
|
||||
if (!$this->firstRequest) {
|
||||
$this->initialize($request);
|
||||
}
|
||||
$this->firstRequest = false;
|
||||
|
||||
$this->applyBindings();
|
||||
$this->applyContextualBindings();
|
||||
$this->applyInstances();
|
||||
$this->applyApplicationHandlers();
|
||||
|
||||
$request = Request::createFromBase($request);
|
||||
$response = $this->kernel->handle($request);
|
||||
|
||||
$method = new \ReflectionMethod(get_class($this->app), 'callTerminableMiddleware');
|
||||
$method->setAccessible(true);
|
||||
$method->invoke($this->app, $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Lumen framework.
|
||||
*
|
||||
* @param SymfonyRequest|null $request
|
||||
*/
|
||||
private function initialize($request = null)
|
||||
{
|
||||
// Store a reference to the database object
|
||||
// so the database connection can be reused during tests
|
||||
$this->oldDb = null;
|
||||
if (isset($this->app['db']) && $this->app['db']->connection()) {
|
||||
$this->oldDb = $this->app['db'];
|
||||
}
|
||||
|
||||
if (class_exists(Facade::class)) {
|
||||
// If the container has been instantiated ever,
|
||||
// we need to clear its static fields before create new container.
|
||||
Facade::clearResolvedInstances();
|
||||
}
|
||||
|
||||
$this->app = $this->kernel = require $this->module->config['bootstrap_file'];
|
||||
|
||||
// Lumen registers necessary bindings on demand when calling $app->make(),
|
||||
// so here we force the request binding before registering our own request object,
|
||||
// otherwise Lumen will overwrite our request object.
|
||||
$this->app->make('request');
|
||||
|
||||
$request = $request ?: SymfonyRequest::create($this->module->config['url']);
|
||||
$this->app->instance('Illuminate\Http\Request', Request::createFromBase($request));
|
||||
|
||||
// Reset the old database if there is one
|
||||
if ($this->oldDb) {
|
||||
$this->app->singleton('db', function () {
|
||||
return $this->oldDb;
|
||||
});
|
||||
Model::setConnectionResolver($this->oldDb);
|
||||
}
|
||||
|
||||
$this->module->setApplication($this->app);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure files are \Illuminate\Http\UploadedFile instances with the private $test property set to true.
|
||||
* Fixes issue https://github.com/Codeception/Codeception/pull/3417.
|
||||
*
|
||||
* @param array $files
|
||||
* @return array
|
||||
*/
|
||||
protected function filterFiles(array $files)
|
||||
{
|
||||
$files = parent::filterFiles($files);
|
||||
|
||||
if (! class_exists('Illuminate\Http\UploadedFile')) {
|
||||
// The \Illuminate\Http\UploadedFile class was introduced in Laravel 5.2.15,
|
||||
// so don't change the $files array if it does not exist.
|
||||
return $files;
|
||||
}
|
||||
|
||||
return $this->convertToTestFiles($files);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $files
|
||||
* @return array
|
||||
*/
|
||||
private function convertToTestFiles(array $files)
|
||||
{
|
||||
$filtered = [];
|
||||
|
||||
foreach ($files as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$filtered[$key] = $this->convertToTestFiles($value);
|
||||
} else {
|
||||
$filtered[$key] = UploadedFile::createFromBase($value, true);
|
||||
}
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
}
|
||||
16
vendor/codeception/base/src/Codeception/Lib/Connector/Lumen/DummyKernel.php
vendored
Normal file
16
vendor/codeception/base/src/Codeception/Lib/Connector/Lumen/DummyKernel.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector\Lumen;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
/**
|
||||
* Dummy kernel to satisfy the parent constructor of the LumenConnector class.
|
||||
*/
|
||||
class DummyKernel implements HttpKernelInterface
|
||||
{
|
||||
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
159
vendor/codeception/base/src/Codeception/Lib/Connector/Phalcon.php
vendored
Normal file
159
vendor/codeception/base/src/Codeception/Lib/Connector/Phalcon.php
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
use Closure;
|
||||
use Phalcon\Di;
|
||||
use Phalcon\Http;
|
||||
use RuntimeException;
|
||||
use ReflectionProperty;
|
||||
use Codeception\Util\Stub;
|
||||
use Phalcon\Mvc\Application;
|
||||
use Symfony\Component\BrowserKit\Cookie;
|
||||
use Symfony\Component\BrowserKit\Client;
|
||||
use Phalcon\Mvc\Micro as MicroApplication;
|
||||
use Symfony\Component\BrowserKit\Response;
|
||||
use Codeception\Lib\Connector\Shared\PhpSuperGlobalsConverter;
|
||||
|
||||
class Phalcon extends Client
|
||||
{
|
||||
use PhpSuperGlobalsConverter;
|
||||
|
||||
/**
|
||||
* Phalcon Application
|
||||
* @var mixed
|
||||
*/
|
||||
private $application;
|
||||
|
||||
/**
|
||||
* Set Phalcon Application by \Phalcon\DI\Injectable, Closure or bootstrap file path
|
||||
*
|
||||
* @param mixed $application
|
||||
*/
|
||||
public function setApplication($application)
|
||||
{
|
||||
$this->application = $application;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Phalcon Application
|
||||
*
|
||||
* @return Application|MicroApplication
|
||||
*/
|
||||
public function getApplication()
|
||||
{
|
||||
$application = $this->application;
|
||||
|
||||
if ($application instanceof Closure) {
|
||||
return $application();
|
||||
} elseif (is_string($application)) {
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
return require $application;
|
||||
}
|
||||
|
||||
return $application;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a request.
|
||||
*
|
||||
* @param \Symfony\Component\BrowserKit\Request $request
|
||||
*
|
||||
* @return \Symfony\Component\BrowserKit\Response
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function doRequest($request)
|
||||
{
|
||||
$application = $this->getApplication();
|
||||
if (!$application instanceof Application && !$application instanceof MicroApplication) {
|
||||
throw new RuntimeException('Unsupported application class.');
|
||||
}
|
||||
|
||||
$di = $application->getDI();
|
||||
/** @var Http\Request $phRequest */
|
||||
if ($di->has('request')) {
|
||||
$phRequest = $di->get('request');
|
||||
}
|
||||
|
||||
if (!$phRequest instanceof Http\RequestInterface) {
|
||||
$phRequest = new Http\Request();
|
||||
}
|
||||
|
||||
$uri = $request->getUri() ?: $phRequest->getURI();
|
||||
$pathString = parse_url($uri, PHP_URL_PATH);
|
||||
$queryString = parse_url($uri, PHP_URL_QUERY);
|
||||
|
||||
$_SERVER = $request->getServer();
|
||||
$_SERVER['REQUEST_METHOD'] = strtoupper($request->getMethod());
|
||||
$_SERVER['REQUEST_URI'] = null === $queryString ? $pathString : $pathString . '?' . $queryString;
|
||||
|
||||
$_COOKIE = $request->getCookies();
|
||||
$_FILES = $this->remapFiles($request->getFiles());
|
||||
$_REQUEST = $this->remapRequestParameters($request->getParameters());
|
||||
$_POST = [];
|
||||
$_GET = [];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
|
||||
$_GET = $_REQUEST;
|
||||
} else {
|
||||
$_POST = $_REQUEST;
|
||||
}
|
||||
|
||||
parse_str($queryString, $output);
|
||||
foreach ($output as $k => $v) {
|
||||
$_GET[$k] = $v;
|
||||
}
|
||||
|
||||
$_GET['_url'] = $pathString;
|
||||
$_SERVER['QUERY_STRING'] = http_build_query($_GET);
|
||||
|
||||
Di::reset();
|
||||
Di::setDefault($di);
|
||||
|
||||
$di['request'] = Stub::construct($phRequest, [], ['getRawBody' => $request->getContent()]);
|
||||
|
||||
$response = $application->handle();
|
||||
if (!$response instanceof Http\ResponseInterface) {
|
||||
$response = $application->response;
|
||||
}
|
||||
|
||||
$headers = $response->getHeaders();
|
||||
$status = (int) $headers->get('Status');
|
||||
|
||||
$headersProperty = new ReflectionProperty($headers, '_headers');
|
||||
$headersProperty->setAccessible(true);
|
||||
$headers = $headersProperty->getValue($headers);
|
||||
if (!is_array($headers)) {
|
||||
$headers = [];
|
||||
}
|
||||
|
||||
$cookiesProperty = new ReflectionProperty($di['cookies'], '_cookies');
|
||||
$cookiesProperty->setAccessible(true);
|
||||
$cookies = $cookiesProperty->getValue($di['cookies']);
|
||||
if (is_array($cookies)) {
|
||||
$restoredProperty = new ReflectionProperty('\Phalcon\Http\Cookie', '_restored');
|
||||
$restoredProperty->setAccessible(true);
|
||||
$valueProperty = new ReflectionProperty('\Phalcon\Http\Cookie', '_value');
|
||||
$valueProperty->setAccessible(true);
|
||||
foreach ($cookies as $name => $cookie) {
|
||||
if (!$restoredProperty->getValue($cookie)) {
|
||||
$clientCookie = new Cookie(
|
||||
$name,
|
||||
$valueProperty->getValue($cookie),
|
||||
$cookie->getExpiration(),
|
||||
$cookie->getPath(),
|
||||
$cookie->getDomain(),
|
||||
$cookie->getSecure(),
|
||||
$cookie->getHttpOnly()
|
||||
);
|
||||
$headers['Set-Cookie'][] = (string)$clientCookie;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(
|
||||
$response->getContent(),
|
||||
$status ? $status : 200,
|
||||
$headers
|
||||
);
|
||||
}
|
||||
}
|
||||
309
vendor/codeception/base/src/Codeception/Lib/Connector/Phalcon/MemorySession.php
vendored
Normal file
309
vendor/codeception/base/src/Codeception/Lib/Connector/Phalcon/MemorySession.php
vendored
Normal file
@@ -0,0 +1,309 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector\Phalcon;
|
||||
|
||||
use Phalcon\Session\AdapterInterface;
|
||||
|
||||
class MemorySession implements AdapterInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sessionId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $started = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $memory = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
public function __construct(array $options = null)
|
||||
{
|
||||
$this->sessionId = $this->generateId();
|
||||
|
||||
if (is_array($options)) {
|
||||
$this->setOptions($options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
if ($this->status() !== PHP_SESSION_ACTIVE) {
|
||||
$this->memory = [];
|
||||
$this->started = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
{
|
||||
if (isset($options['uniqueId'])) {
|
||||
$this->sessionId = $options['uniqueId'];
|
||||
}
|
||||
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param string $index
|
||||
* @param mixed $defaultValue
|
||||
* @param bool $remove
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($index, $defaultValue = null, $remove = false)
|
||||
{
|
||||
$key = $this->prepareIndex($index);
|
||||
|
||||
if (!isset($this->memory[$key])) {
|
||||
return $defaultValue;
|
||||
}
|
||||
|
||||
$return = $this->memory[$key];
|
||||
|
||||
if ($remove) {
|
||||
unset($this->memory[$key]);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param string $index
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function set($index, $value)
|
||||
{
|
||||
$this->memory[$this->prepareIndex($index)] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param string $index
|
||||
* @return bool
|
||||
*/
|
||||
public function has($index)
|
||||
{
|
||||
return isset($this->memory[$this->prepareIndex($index)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param string $index
|
||||
*/
|
||||
public function remove($index)
|
||||
{
|
||||
unset($this->memory[$this->prepareIndex($index)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isStarted()
|
||||
{
|
||||
return $this->started;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the current session
|
||||
*
|
||||
* ``` php
|
||||
* <?php
|
||||
* if ($session->status() !== PHP_SESSION_ACTIVE) {
|
||||
* $session->start();
|
||||
* }
|
||||
* ?>
|
||||
* ```
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function status()
|
||||
{
|
||||
if ($this->isStarted()) {
|
||||
return PHP_SESSION_ACTIVE;
|
||||
}
|
||||
|
||||
return PHP_SESSION_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param bool $removeData
|
||||
* @return bool
|
||||
*/
|
||||
public function destroy($removeData = false)
|
||||
{
|
||||
if ($removeData) {
|
||||
if (!empty($this->sessionId)) {
|
||||
foreach ($this->memory as $key => $value) {
|
||||
if (0 === strpos($key, $this->sessionId . '#')) {
|
||||
unset($this->memory[$key]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->memory = [];
|
||||
}
|
||||
}
|
||||
|
||||
$this->started = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param bool $deleteOldSession
|
||||
* @return \Phalcon\Session\AdapterInterface
|
||||
*/
|
||||
public function regenerateId($deleteOldSession = true)
|
||||
{
|
||||
$this->sessionId = $this->generateId();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump all session
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return (array) $this->memory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias: Gets a session variable from an application context
|
||||
*
|
||||
* @param string $index
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($index)
|
||||
{
|
||||
return $this->get($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias: Sets a session variable in an application context
|
||||
*
|
||||
* @param string $index
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set($index, $value)
|
||||
{
|
||||
$this->set($index, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias: Check whether a session variable is set in an application context
|
||||
*
|
||||
* @param string $index
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($index)
|
||||
{
|
||||
return $this->has($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias: Removes a session variable from an application context
|
||||
*
|
||||
* @param string $index
|
||||
*/
|
||||
public function __unset($index)
|
||||
{
|
||||
$this->remove($index);
|
||||
}
|
||||
|
||||
private function prepareIndex($index)
|
||||
{
|
||||
if ($this->sessionId) {
|
||||
$key = $this->sessionId . '#' . $index;
|
||||
} else {
|
||||
$key = $index;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function generateId()
|
||||
{
|
||||
return md5(time());
|
||||
}
|
||||
}
|
||||
139
vendor/codeception/base/src/Codeception/Lib/Connector/Shared/LaravelCommon.php
vendored
Normal file
139
vendor/codeception/base/src/Codeception/Lib/Connector/Shared/LaravelCommon.php
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector\Shared;
|
||||
|
||||
/**
|
||||
* Common functions for Laravel family
|
||||
*
|
||||
* @package Codeception\Lib\Connector\Shared
|
||||
*/
|
||||
trait LaravelCommon
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $bindings = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $contextualBindings = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $instances = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $applicationHandlers = [];
|
||||
|
||||
/**
|
||||
* Apply the registered application handlers.
|
||||
*/
|
||||
private function applyApplicationHandlers()
|
||||
{
|
||||
foreach ($this->applicationHandlers as $handler) {
|
||||
call_user_func($handler, $this->app);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the registered Laravel service container bindings.
|
||||
*/
|
||||
private function applyBindings()
|
||||
{
|
||||
foreach ($this->bindings as $abstract => $binding) {
|
||||
list($concrete, $shared) = $binding;
|
||||
|
||||
$this->app->bind($abstract, $concrete, $shared);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the registered Laravel service container contextual bindings.
|
||||
*/
|
||||
private function applyContextualBindings()
|
||||
{
|
||||
foreach ($this->contextualBindings as $concrete => $bindings) {
|
||||
foreach ($bindings as $abstract => $implementation) {
|
||||
$this->app->addContextualBinding($concrete, $abstract, $implementation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the registered Laravel service container instance bindings.
|
||||
*/
|
||||
private function applyInstances()
|
||||
{
|
||||
foreach ($this->instances as $abstract => $instance) {
|
||||
$this->app->instance($abstract, $instance);
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Public methods called by module
|
||||
//======================================================================
|
||||
|
||||
/**
|
||||
* Register a Laravel service container binding that should be applied
|
||||
* after initializing the Laravel Application object.
|
||||
*
|
||||
* @param $abstract
|
||||
* @param $concrete
|
||||
* @param bool $shared
|
||||
*/
|
||||
public function haveBinding($abstract, $concrete, $shared = false)
|
||||
{
|
||||
$this->bindings[$abstract] = [$concrete, $shared];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a Laravel service container contextual binding that should be applied
|
||||
* after initializing the Laravel Application object.
|
||||
*
|
||||
* @param $concrete
|
||||
* @param $abstract
|
||||
* @param $implementation
|
||||
*/
|
||||
public function haveContextualBinding($concrete, $abstract, $implementation)
|
||||
{
|
||||
if (! isset($this->contextualBindings[$concrete])) {
|
||||
$this->contextualBindings[$concrete] = [];
|
||||
}
|
||||
|
||||
$this->contextualBindings[$concrete][$abstract] = $implementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a Laravel service container instance binding that should be applied
|
||||
* after initializing the Laravel Application object.
|
||||
*
|
||||
* @param $abstract
|
||||
* @param $instance
|
||||
*/
|
||||
public function haveInstance($abstract, $instance)
|
||||
{
|
||||
$this->instances[$abstract] = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler than can be used to modify the Laravel application object after it is initialized.
|
||||
* The Laravel application object will be passed as an argument to the handler.
|
||||
*
|
||||
* @param $handler
|
||||
*/
|
||||
public function haveApplicationHandler($handler)
|
||||
{
|
||||
$this->applicationHandlers[] = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the registered application handlers.
|
||||
*/
|
||||
public function clearApplicationHandlers()
|
||||
{
|
||||
$this->applicationHandlers = [];
|
||||
}
|
||||
}
|
||||
117
vendor/codeception/base/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php
vendored
Normal file
117
vendor/codeception/base/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector\Shared;
|
||||
|
||||
/**
|
||||
* Converts BrowserKit\Request's request parameters and files into PHP-compatible structure
|
||||
*
|
||||
* @see https://bugs.php.net/bug.php?id=25589
|
||||
* @see https://bugs.php.net/bug.php?id=25589
|
||||
*
|
||||
* @package Codeception\Lib\Connector
|
||||
*/
|
||||
trait PhpSuperGlobalsConverter
|
||||
{
|
||||
/**
|
||||
* Rearrange files array to be compatible with PHP $_FILES superglobal structure
|
||||
* @see https://bugs.php.net/bug.php?id=25589
|
||||
*
|
||||
* @param array $requestFiles
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function remapFiles(array $requestFiles)
|
||||
{
|
||||
$files = $this->rearrangeFiles($requestFiles);
|
||||
|
||||
return $this->replaceSpaces($files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape high-level variable name with dots, underscores and other "special" chars
|
||||
* to be compatible with PHP "bug"
|
||||
* @see https://bugs.php.net/bug.php?id=40000
|
||||
*
|
||||
* @param array $parameters
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function remapRequestParameters(array $parameters)
|
||||
{
|
||||
return $this->replaceSpaces($parameters);
|
||||
}
|
||||
|
||||
private function rearrangeFiles($requestFiles)
|
||||
{
|
||||
$files = [];
|
||||
foreach ($requestFiles as $name => $info) {
|
||||
if (!is_array($info)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have a form with fields like
|
||||
* ```
|
||||
* <input type="file" name="foo" />
|
||||
* <input type="file" name="foo[bar]" />
|
||||
* ```
|
||||
* then only array variable will be used while simple variable will be ignored in php $_FILES
|
||||
* (eg $_FILES = [
|
||||
* foo => [
|
||||
* tmp_name => [
|
||||
* 'bar' => 'asdf'
|
||||
* ],
|
||||
* //...
|
||||
* ]
|
||||
* ]
|
||||
* )
|
||||
* (notice there is no entry for file "foo", only for file "foo[bar]"
|
||||
* this will check if current element contains inner arrays within it's keys
|
||||
* so we can ignore element itself and only process inner files
|
||||
*/
|
||||
$hasInnerArrays = count(array_filter($info, 'is_array'));
|
||||
|
||||
if ($hasInnerArrays || !isset($info['tmp_name'])) {
|
||||
$inner = $this->remapFiles($info);
|
||||
foreach ($inner as $innerName => $innerInfo) {
|
||||
/**
|
||||
* Convert from ['a' => ['tmp_name' => '/tmp/test.txt'] ]
|
||||
* to ['tmp_name' => ['a' => '/tmp/test.txt'] ]
|
||||
*/
|
||||
$innerInfo = array_map(
|
||||
function ($v) use ($innerName) {
|
||||
return [$innerName => $v];
|
||||
},
|
||||
$innerInfo
|
||||
);
|
||||
|
||||
if (empty($files[$name])) {
|
||||
$files[$name] = [];
|
||||
}
|
||||
|
||||
$files[$name] = array_replace_recursive($files[$name], $innerInfo);
|
||||
}
|
||||
} else {
|
||||
$files[$name] = $info;
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace spaces and dots and other chars in high-level query parameters for
|
||||
* compatibility with PHP bug (or not a bug)
|
||||
* @see https://bugs.php.net/bug.php?id=40000
|
||||
*
|
||||
* @param array $parameters Array of request parameters to be converted
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function replaceSpaces($parameters)
|
||||
{
|
||||
$qs = http_build_query($parameters, '', '&');
|
||||
parse_str($qs, $output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
86
vendor/codeception/base/src/Codeception/Lib/Connector/Symfony.php
vendored
Normal file
86
vendor/codeception/base/src/Codeception/Lib/Connector/Symfony.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
class Symfony extends \Symfony\Component\HttpKernel\Client
|
||||
{
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
private $rebootable = true;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
private $hasPerformedRequest = false;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
private $container = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $persistentServices = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Kernel $kernel A booted HttpKernel instance
|
||||
* @param array $services An injected services
|
||||
* @param boolean $rebootable
|
||||
*/
|
||||
public function __construct(\Symfony\Component\HttpKernel\Kernel $kernel, array $services = [], $rebootable = true)
|
||||
{
|
||||
parent::__construct($kernel);
|
||||
$this->followRedirects(true);
|
||||
$this->rebootable = (boolean)$rebootable;
|
||||
$this->persistentServices = $services;
|
||||
$this->rebootKernel();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
*/
|
||||
protected function doRequest($request)
|
||||
{
|
||||
if ($this->rebootable) {
|
||||
if ($this->hasPerformedRequest) {
|
||||
$this->rebootKernel();
|
||||
} else {
|
||||
$this->hasPerformedRequest = true;
|
||||
}
|
||||
}
|
||||
return parent::doRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reboot kernel
|
||||
*
|
||||
* Services from the list of persistent services
|
||||
* are updated from service container before kernel shutdown
|
||||
* and injected into newly initialized container after kernel boot.
|
||||
*/
|
||||
public function rebootKernel()
|
||||
{
|
||||
if ($this->container) {
|
||||
foreach ($this->persistentServices as $serviceName => $service) {
|
||||
if ($this->container->has($serviceName)) {
|
||||
$this->persistentServices[$serviceName] = $this->container->get($serviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->kernel->shutdown();
|
||||
$this->kernel->boot();
|
||||
$this->container = $this->kernel->getContainer();
|
||||
|
||||
foreach ($this->persistentServices as $serviceName => $service) {
|
||||
$this->container->set($serviceName, $service);
|
||||
}
|
||||
|
||||
if ($this->container->has('profiler')) {
|
||||
$this->container->get('profiler')->enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
72
vendor/codeception/base/src/Codeception/Lib/Connector/Universal.php
vendored
Normal file
72
vendor/codeception/base/src/Codeception/Lib/Connector/Universal.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
use Symfony\Component\BrowserKit\Client;
|
||||
use Symfony\Component\BrowserKit\Response;
|
||||
|
||||
class Universal extends Client
|
||||
{
|
||||
use Shared\PhpSuperGlobalsConverter;
|
||||
|
||||
protected $mockedResponse;
|
||||
protected $index;
|
||||
|
||||
public function setIndex($index)
|
||||
{
|
||||
$this->index = $index;
|
||||
}
|
||||
|
||||
public function mockResponse($response)
|
||||
{
|
||||
$this->mockedResponse = $response;
|
||||
}
|
||||
|
||||
public function doRequest($request)
|
||||
{
|
||||
if ($this->mockedResponse) {
|
||||
$response = $this->mockedResponse;
|
||||
$this->mockedResponse = null;
|
||||
return $response;
|
||||
}
|
||||
|
||||
$_COOKIE = $request->getCookies();
|
||||
$_SERVER = $request->getServer();
|
||||
$_FILES = $this->remapFiles($request->getFiles());
|
||||
|
||||
$uri = str_replace('http://localhost', '', $request->getUri());
|
||||
|
||||
$_REQUEST = $this->remapRequestParameters($request->getParameters());
|
||||
if (strtoupper($request->getMethod()) == 'GET') {
|
||||
$_GET = $_REQUEST;
|
||||
} else {
|
||||
$_POST = $_REQUEST;
|
||||
}
|
||||
|
||||
$_SERVER['REQUEST_METHOD'] = strtoupper($request->getMethod());
|
||||
$_SERVER['REQUEST_URI'] = $uri;
|
||||
|
||||
ob_start();
|
||||
include $this->index;
|
||||
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$headers = [];
|
||||
$php_headers = headers_list();
|
||||
foreach ($php_headers as $value) {
|
||||
// Get the header name
|
||||
$parts = explode(':', $value);
|
||||
if (count($parts) > 1) {
|
||||
$name = trim(array_shift($parts));
|
||||
// Build the header hash map
|
||||
$headers[$name] = trim(implode(':', $parts));
|
||||
}
|
||||
}
|
||||
$headers['Content-type'] = isset($headers['Content-type'])
|
||||
? $headers['Content-type']
|
||||
: "text/html; charset=UTF-8";
|
||||
|
||||
$response = new Response($content, 200, $headers);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
147
vendor/codeception/base/src/Codeception/Lib/Connector/Yii1.php
vendored
Normal file
147
vendor/codeception/base/src/Codeception/Lib/Connector/Yii1.php
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
use Codeception\Util\Stub;
|
||||
use Symfony\Component\BrowserKit\Client;
|
||||
use Symfony\Component\BrowserKit\Response;
|
||||
|
||||
class Yii1 extends Client
|
||||
{
|
||||
use Shared\PhpSuperGlobalsConverter;
|
||||
/**
|
||||
* http://localhost/path/to/your/app/index.php
|
||||
* @var string url of the entry Yii script
|
||||
*/
|
||||
public $url;
|
||||
|
||||
/**
|
||||
* Current application settings {@see Codeception\Module\Yii1::$appSettings}
|
||||
* @var array
|
||||
*/
|
||||
public $appSettings;
|
||||
|
||||
/**
|
||||
* Full path to your application
|
||||
* @var string
|
||||
*/
|
||||
public $appPath;
|
||||
|
||||
/**
|
||||
* Current request headers
|
||||
* @var array
|
||||
*/
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param \Symfony\Component\BrowserKit\Request $request
|
||||
*
|
||||
* @return \Symfony\Component\BrowserKit\Response
|
||||
*/
|
||||
public function doRequest($request)
|
||||
{
|
||||
$this->headers = [];
|
||||
$_COOKIE = array_merge($_COOKIE, $request->getCookies());
|
||||
$_SERVER = array_merge($_SERVER, $request->getServer());
|
||||
$_FILES = $this->remapFiles($request->getFiles());
|
||||
$_REQUEST = $this->remapRequestParameters($request->getParameters());
|
||||
$_POST = $_GET = [];
|
||||
|
||||
if (strtoupper($request->getMethod()) == 'GET') {
|
||||
$_GET = $_REQUEST;
|
||||
} else {
|
||||
$_POST = $_REQUEST;
|
||||
}
|
||||
|
||||
// Parse url parts
|
||||
$uriPath = ltrim(parse_url($request->getUri(), PHP_URL_PATH), '/');
|
||||
$uriQuery = ltrim(parse_url($request->getUri(), PHP_URL_QUERY), '?');
|
||||
$scriptName = trim(parse_url($this->url, PHP_URL_PATH), '/');
|
||||
if (!empty($uriQuery)) {
|
||||
$uriPath .= "?{$uriQuery}";
|
||||
|
||||
parse_str($uriQuery, $params);
|
||||
foreach ($params as $k => $v) {
|
||||
$_GET[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
// Add script name to request if none
|
||||
if ($scriptName and strpos($uriPath, $scriptName) === false) {
|
||||
$uriPath = "/{$scriptName}/{$uriPath}";
|
||||
}
|
||||
|
||||
// Add forward slash if not exists
|
||||
if (strpos($uriPath, '/') !== 0) {
|
||||
$uriPath = "/{$uriPath}";
|
||||
}
|
||||
|
||||
$_SERVER['REQUEST_METHOD'] = strtoupper($request->getMethod());
|
||||
$_SERVER['REQUEST_URI'] = $uriPath;
|
||||
|
||||
/**
|
||||
* Hack to be sure that CHttpRequest will resolve route correctly
|
||||
*/
|
||||
$_SERVER['SCRIPT_NAME'] = "/{$scriptName}";
|
||||
$_SERVER['SCRIPT_FILENAME'] = $this->appPath;
|
||||
|
||||
ob_start();
|
||||
\Yii::setApplication(null);
|
||||
\Yii::createApplication($this->appSettings['class'], $this->appSettings['config']);
|
||||
|
||||
$app = \Yii::app();
|
||||
// disabling logging. Logs slow down test execution
|
||||
if ($app->hasComponent('log')) {
|
||||
foreach ($app->getComponent('log')->routes as $route) {
|
||||
$route->enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($app->hasComponent('session')) { // disable regenerate id in session
|
||||
$app->setComponent('session', Stub::make('CHttpSession', ['regenerateID' => false]));
|
||||
}
|
||||
|
||||
$app->onEndRequest->add([$this, 'setHeaders']);
|
||||
$app->run();
|
||||
|
||||
if ($app->hasComponent('db')) {
|
||||
// close connection
|
||||
$app->getDb()->setActive(false);
|
||||
// cleanup metadata cache
|
||||
$property = new \ReflectionProperty('CActiveRecord', '_md');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue([]);
|
||||
}
|
||||
|
||||
$content = ob_get_clean();
|
||||
|
||||
$headers = $this->getHeaders();
|
||||
$statusCode = 200;
|
||||
foreach ($headers as $header => $val) {
|
||||
if ($header == 'Location') {
|
||||
$statusCode = 302;
|
||||
}
|
||||
}
|
||||
|
||||
$response = new Response($content, $statusCode, $this->getHeaders());
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current client headers when terminating yii application (onEndRequest)
|
||||
*/
|
||||
public function setHeaders()
|
||||
{
|
||||
$this->headers = \Yii::app()->request->getAllHeaders();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current client headers
|
||||
* @return array headers
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
}
|
||||
396
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2.php
vendored
Normal file
396
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2.php
vendored
Normal file
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
use Codeception\Lib\Connector\Yii2\Logger;
|
||||
use Codeception\Lib\InnerBrowser;
|
||||
use Codeception\Util\Debug;
|
||||
use Symfony\Component\BrowserKit\Client;
|
||||
use Symfony\Component\BrowserKit\Cookie;
|
||||
use Symfony\Component\BrowserKit\Response;
|
||||
use Yii;
|
||||
use yii\base\ExitException;
|
||||
use yii\base\Security;
|
||||
use yii\web\Application;
|
||||
use yii\web\ErrorHandler;
|
||||
use yii\web\HttpException;
|
||||
use yii\web\Request;
|
||||
use yii\web\Response as YiiResponse;
|
||||
|
||||
class Yii2 extends Client
|
||||
{
|
||||
use Shared\PhpSuperGlobalsConverter;
|
||||
|
||||
const CLEAN_METHODS = [
|
||||
self::CLEAN_RECREATE,
|
||||
self::CLEAN_CLEAR,
|
||||
self::CLEAN_FORCE_RECREATE,
|
||||
self::CLEAN_MANUAL
|
||||
];
|
||||
/**
|
||||
* Clean the response object by recreating it.
|
||||
* This might lose behaviors / event handlers / other changes that are done in the application bootstrap phase.
|
||||
*/
|
||||
const CLEAN_RECREATE = 'recreate';
|
||||
/**
|
||||
* Same as recreate but will not warn when behaviors / event handlers are lost.
|
||||
*/
|
||||
const CLEAN_FORCE_RECREATE = 'force_recreate';
|
||||
/**
|
||||
* Clean the response object by resetting specific properties via its' `clear()` method.
|
||||
* This will keep behaviors / event handlers, but could inadvertently leave some changes intact.
|
||||
* @see \Yii\web\Response::clear()
|
||||
*/
|
||||
const CLEAN_CLEAR = 'clear';
|
||||
|
||||
/**
|
||||
* Do not clean the response, instead the test writer will be responsible for manually resetting the response in
|
||||
* between requests during one test
|
||||
*/
|
||||
const CLEAN_MANUAL = 'manual';
|
||||
|
||||
|
||||
/**
|
||||
* @var string application config file
|
||||
*/
|
||||
public $configFile;
|
||||
|
||||
/**
|
||||
* @var string method for cleaning the response object before each request
|
||||
*/
|
||||
public $responseCleanMethod;
|
||||
|
||||
/**
|
||||
* @var string method for cleaning the request object before each request
|
||||
*/
|
||||
public $requestCleanMethod;
|
||||
|
||||
/**
|
||||
* @var string[] List of component names that must be recreated before each request
|
||||
*/
|
||||
public $recreateComponents = [];
|
||||
|
||||
/**
|
||||
* This option is there primarily for backwards compatibility.
|
||||
* It means you cannot make any modification to application state inside your app, since they will get discarded.
|
||||
* @var bool whether to recreate the whole application before each request
|
||||
*/
|
||||
public $recreateApplication = false;
|
||||
|
||||
/**
|
||||
* @return \yii\web\Application
|
||||
*/
|
||||
public function getApplication()
|
||||
{
|
||||
if (!isset(Yii::$app)) {
|
||||
$this->startApp();
|
||||
}
|
||||
return Yii::$app;
|
||||
}
|
||||
|
||||
public function resetApplication()
|
||||
{
|
||||
codecept_debug('Destroying application');
|
||||
Yii::$app = null;
|
||||
\yii\web\UploadedFile::reset();
|
||||
if (method_exists(\yii\base\Event::className(), 'offAll')) {
|
||||
\yii\base\Event::offAll();
|
||||
}
|
||||
Yii::setLogger(null);
|
||||
// This resolves an issue with database connections not closing properly.
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
public function startApp()
|
||||
{
|
||||
codecept_debug('Starting application');
|
||||
$config = require($this->configFile);
|
||||
if (!isset($config['class'])) {
|
||||
$config['class'] = 'yii\web\Application';
|
||||
}
|
||||
|
||||
$config = $this->mockMailer($config);
|
||||
/** @var \yii\web\Application $app */
|
||||
Yii::$app = Yii::createObject($config);
|
||||
|
||||
Yii::setLogger(new Logger());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param \Symfony\Component\BrowserKit\Request $request
|
||||
*
|
||||
* @return \Symfony\Component\BrowserKit\Response
|
||||
*/
|
||||
public function doRequest($request)
|
||||
{
|
||||
$_COOKIE = $request->getCookies();
|
||||
$_SERVER = $request->getServer();
|
||||
$_FILES = $this->remapFiles($request->getFiles());
|
||||
$_REQUEST = $this->remapRequestParameters($request->getParameters());
|
||||
$_POST = $_GET = [];
|
||||
|
||||
if (strtoupper($request->getMethod()) === 'GET') {
|
||||
$_GET = $_REQUEST;
|
||||
} else {
|
||||
$_POST = $_REQUEST;
|
||||
}
|
||||
|
||||
$uri = $request->getUri();
|
||||
|
||||
$pathString = parse_url($uri, PHP_URL_PATH);
|
||||
$queryString = parse_url($uri, PHP_URL_QUERY);
|
||||
$_SERVER['REQUEST_URI'] = $queryString === null ? $pathString : $pathString . '?' . $queryString;
|
||||
$_SERVER['REQUEST_METHOD'] = strtoupper($request->getMethod());
|
||||
|
||||
parse_str($queryString, $params);
|
||||
foreach ($params as $k => $v) {
|
||||
$_GET[$k] = $v;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
$this->beforeRequest();
|
||||
|
||||
$app = $this->getApplication();
|
||||
|
||||
// disabling logging. Logs are slowing test execution down
|
||||
foreach ($app->log->targets as $target) {
|
||||
$target->enabled = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$yiiRequest = $app->getRequest();
|
||||
if ($request->getContent() !== null) {
|
||||
$yiiRequest->setRawBody($request->getContent());
|
||||
$yiiRequest->setBodyParams(null);
|
||||
} else {
|
||||
$yiiRequest->setRawBody(null);
|
||||
$yiiRequest->setBodyParams($_POST);
|
||||
}
|
||||
$yiiRequest->setQueryParams($_GET);
|
||||
|
||||
try {
|
||||
/*
|
||||
* This is basically equivalent to $app->run() without sending the response.
|
||||
* Sending the response is problematic because it tries to send headers.
|
||||
*/
|
||||
$app->trigger($app::EVENT_BEFORE_REQUEST);
|
||||
$response = $app->handleRequest($yiiRequest);
|
||||
$app->trigger($app::EVENT_AFTER_REQUEST);
|
||||
$response->send();
|
||||
} catch (\Exception $e) {
|
||||
if ($e instanceof HttpException) {
|
||||
// Don't discard output and pass exception handling to Yii to be able
|
||||
// to expect error response codes in tests.
|
||||
$app->errorHandler->discardExistingOutput = false;
|
||||
$app->errorHandler->handleException($e);
|
||||
} elseif (!$e instanceof ExitException) {
|
||||
// for exceptions not related to Http, we pass them to Codeception
|
||||
throw $e;
|
||||
}
|
||||
$response = $app->response;
|
||||
}
|
||||
|
||||
$this->encodeCookies($response, $yiiRequest, $app->security);
|
||||
|
||||
if ($response->isRedirection) {
|
||||
Debug::debug("[Redirect with headers]" . print_r($response->getHeaders()->toArray(), true));
|
||||
}
|
||||
|
||||
$content = ob_get_clean();
|
||||
if (empty($content) && !empty($response->content)) {
|
||||
throw new \Exception('No content was sent from Yii application');
|
||||
}
|
||||
|
||||
return new Response($content, $response->statusCode, $response->getHeaders()->toArray());
|
||||
}
|
||||
|
||||
protected function revertErrorHandler()
|
||||
{
|
||||
$handler = new ErrorHandler();
|
||||
set_error_handler([$handler, 'errorHandler']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encodes the cookies and adds them to the headers.
|
||||
* @param \yii\web\Response $response
|
||||
* @throws \yii\base\InvalidConfigException
|
||||
*/
|
||||
protected function encodeCookies(
|
||||
YiiResponse $response,
|
||||
Request $request,
|
||||
Security $security
|
||||
) {
|
||||
if ($request->enableCookieValidation) {
|
||||
$validationKey = $request->cookieValidationKey;
|
||||
}
|
||||
|
||||
foreach ($response->getCookies() as $cookie) {
|
||||
/** @var \yii\web\Cookie $cookie */
|
||||
$value = $cookie->value;
|
||||
if ($cookie->expire != 1 && isset($validationKey)) {
|
||||
$data = version_compare(Yii::getVersion(), '2.0.2', '>')
|
||||
? [$cookie->name, $cookie->value]
|
||||
: $cookie->value;
|
||||
$value = $security->hashData(serialize($data), $validationKey);
|
||||
}
|
||||
$c = new Cookie(
|
||||
$cookie->name,
|
||||
$value,
|
||||
$cookie->expire,
|
||||
$cookie->path,
|
||||
$cookie->domain,
|
||||
$cookie->secure,
|
||||
$cookie->httpOnly
|
||||
);
|
||||
$this->getCookieJar()->set($c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace mailer with in memory mailer
|
||||
* @param array $config Original configuration
|
||||
* @return array New configuration
|
||||
*/
|
||||
protected function mockMailer(array $config)
|
||||
{
|
||||
// options that make sense for mailer mock
|
||||
$allowedOptions = [
|
||||
'htmlLayout',
|
||||
'textLayout',
|
||||
'messageConfig',
|
||||
'messageClass',
|
||||
'useFileTransport',
|
||||
'fileTransportPath',
|
||||
'fileTransportCallback',
|
||||
'view',
|
||||
'viewPath',
|
||||
];
|
||||
|
||||
$mailerConfig = [
|
||||
'class' => 'Codeception\Lib\Connector\Yii2\TestMailer',
|
||||
];
|
||||
|
||||
if (isset($config['components']['mailer']) && is_array($config['components']['mailer'])) {
|
||||
foreach ($config['components']['mailer'] as $name => $value) {
|
||||
if (in_array($name, $allowedOptions, true)) {
|
||||
$mailerConfig[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
$config['components']['mailer'] = $mailerConfig;
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
public function restart()
|
||||
{
|
||||
parent::restart();
|
||||
$this->resetApplication();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the applications' response object.
|
||||
* The method used depends on the module configuration.
|
||||
*/
|
||||
protected function resetResponse(Application $app)
|
||||
{
|
||||
$method = $this->responseCleanMethod;
|
||||
// First check the current response object.
|
||||
if (($app->response->hasEventHandlers(\yii\web\Response::EVENT_BEFORE_SEND)
|
||||
|| $app->response->hasEventHandlers(\yii\web\Response::EVENT_AFTER_SEND)
|
||||
|| $app->response->hasEventHandlers(\yii\web\Response::EVENT_AFTER_PREPARE)
|
||||
|| count($app->response->getBehaviors()) > 0
|
||||
) && $method === self::CLEAN_RECREATE
|
||||
) {
|
||||
Debug::debug(<<<TEXT
|
||||
[WARNING] You are attaching event handlers or behaviors to the response object. But the Yii2 module is configured to recreate
|
||||
the response object, this means any behaviors or events that are not attached in the component config will be lost.
|
||||
We will fall back to clearing the response. If you are certain you want to recreate it, please configure
|
||||
responseCleanMethod = 'force_recreate' in the module.
|
||||
TEXT
|
||||
);
|
||||
$method = self::CLEAN_CLEAR;
|
||||
}
|
||||
|
||||
switch ($method) {
|
||||
case self::CLEAN_FORCE_RECREATE:
|
||||
case self::CLEAN_RECREATE:
|
||||
$app->set('response', $app->getComponents()['response']);
|
||||
break;
|
||||
case self::CLEAN_CLEAR:
|
||||
$app->response->clear();
|
||||
break;
|
||||
case self::CLEAN_MANUAL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function resetRequest(Application $app)
|
||||
{
|
||||
$method = $this->requestCleanMethod;
|
||||
$request = $app->request;
|
||||
|
||||
// First check the current request object.
|
||||
if (count($request->getBehaviors()) > 0 && $method === self::CLEAN_RECREATE) {
|
||||
Debug::debug(<<<TEXT
|
||||
[WARNING] You are attaching event handlers or behaviors to the request object. But the Yii2 module is configured to recreate
|
||||
the request object, this means any behaviors or events that are not attached in the component config will be lost.
|
||||
We will fall back to clearing the request. If you are certain you want to recreate it, please configure
|
||||
requestCleanMethod = 'force_recreate' in the module.
|
||||
TEXT
|
||||
);
|
||||
$method = self::CLEAN_CLEAR;
|
||||
}
|
||||
|
||||
switch ($method) {
|
||||
case self::CLEAN_FORCE_RECREATE:
|
||||
case self::CLEAN_RECREATE:
|
||||
$app->set('request', $app->getComponents()['request']);
|
||||
break;
|
||||
case self::CLEAN_CLEAR:
|
||||
$request->getHeaders()->removeAll();
|
||||
$request->setBaseUrl(null);
|
||||
$request->setHostInfo(null);
|
||||
$request->setPathInfo(null);
|
||||
$request->setScriptFile(null);
|
||||
$request->setScriptUrl(null);
|
||||
$request->setUrl(null);
|
||||
$request->setPort(null);
|
||||
$request->setSecurePort(null);
|
||||
$request->setAcceptableContentTypes(null);
|
||||
$request->setAcceptableLanguages(null);
|
||||
|
||||
break;
|
||||
case self::CLEAN_MANUAL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before each request, preparation happens here.
|
||||
*/
|
||||
protected function beforeRequest()
|
||||
{
|
||||
if ($this->recreateApplication) {
|
||||
$this->resetApplication();
|
||||
return;
|
||||
}
|
||||
|
||||
$application = $this->getApplication();
|
||||
|
||||
$this->resetResponse($application);
|
||||
$this->resetRequest($application);
|
||||
|
||||
$definitions = $application->getComponents(true);
|
||||
foreach ($this->recreateComponents as $component) {
|
||||
// Only recreate if it has actually been instantiated.
|
||||
if ($application->has($component, true)) {
|
||||
$application->set($component, $definitions[$component]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
67
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php
vendored
Normal file
67
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Codeception\Lib\Connector\Yii2;
|
||||
|
||||
use yii\base\Event;
|
||||
use yii\db\Connection;
|
||||
|
||||
/**
|
||||
* Class ConnectionWatcher
|
||||
* This class will watch for new database connection and store a reference to the connection object.
|
||||
* @package Codeception\Lib\Connector\Yii2
|
||||
*/
|
||||
class ConnectionWatcher
|
||||
{
|
||||
private $handler;
|
||||
|
||||
/** @var Connection[] */
|
||||
private $connections = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->handler = function (Event $event) {
|
||||
if ($event->sender instanceof Connection) {
|
||||
$this->connectionOpened($event->sender);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected function connectionOpened(Connection $connection)
|
||||
{
|
||||
$this->debug('Connection opened!');
|
||||
if ($connection instanceof Connection) {
|
||||
$this->connections[] = $connection;
|
||||
}
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
Event::on(Connection::class, Connection::EVENT_AFTER_OPEN, $this->handler);
|
||||
$this->debug('watching new connections');
|
||||
}
|
||||
|
||||
public function stop()
|
||||
{
|
||||
Event::off(Connection::class, Connection::EVENT_AFTER_OPEN, $this->handler);
|
||||
$this->debug('no longer watching new connections');
|
||||
}
|
||||
|
||||
public function closeAll()
|
||||
{
|
||||
$count = count($this->connections);
|
||||
$this->debug("closing all ($count) connections");
|
||||
foreach ($this->connections as $connection) {
|
||||
$connection->close();
|
||||
}
|
||||
}
|
||||
|
||||
protected function debug($message)
|
||||
{
|
||||
$title = (new \ReflectionClass($this))->getShortName();
|
||||
if (is_array($message) or is_object($message)) {
|
||||
$message = stripslashes(json_encode($message));
|
||||
}
|
||||
codecept_debug("[$title] $message");
|
||||
}
|
||||
}
|
||||
35
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2/FixturesStore.php
vendored
Normal file
35
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2/FixturesStore.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector\Yii2;
|
||||
|
||||
use yii\test\FixtureTrait;
|
||||
use yii\test\InitDbFixture;
|
||||
|
||||
class FixturesStore
|
||||
{
|
||||
use FixtureTrait;
|
||||
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Expects fixtures config
|
||||
*
|
||||
* FixturesStore constructor.
|
||||
* @param $data
|
||||
*/
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function fixtures()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function globalFixtures()
|
||||
{
|
||||
return [
|
||||
InitDbFixture::className()
|
||||
];
|
||||
}
|
||||
}
|
||||
33
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2/Logger.php
vendored
Normal file
33
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2/Logger.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector\Yii2;
|
||||
|
||||
use Codeception\Util\Debug;
|
||||
|
||||
class Logger extends \yii\log\Logger
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
// overridden to prevent register_shutdown_function
|
||||
}
|
||||
|
||||
public function log($message, $level, $category = 'application')
|
||||
{
|
||||
if (!in_array($level, [
|
||||
\yii\log\Logger::LEVEL_INFO,
|
||||
\yii\log\Logger::LEVEL_WARNING,
|
||||
\yii\log\Logger::LEVEL_ERROR,
|
||||
])) {
|
||||
return;
|
||||
}
|
||||
if (strpos($category, 'yii\db\Command')===0) {
|
||||
return; // don't log queries
|
||||
}
|
||||
|
||||
// https://github.com/Codeception/Codeception/issues/3696
|
||||
if ($message instanceof \yii\base\Exception) {
|
||||
$message = $message->__toString();
|
||||
}
|
||||
|
||||
Debug::debug("[$category] " . \yii\helpers\VarDumper::export($message));
|
||||
}
|
||||
}
|
||||
32
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2/TestMailer.php
vendored
Normal file
32
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2/TestMailer.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector\Yii2;
|
||||
|
||||
use yii\mail\BaseMailer;
|
||||
|
||||
class TestMailer extends BaseMailer
|
||||
{
|
||||
public $messageClass = 'yii\swiftmailer\Message';
|
||||
|
||||
private $sentMessages = [];
|
||||
|
||||
protected function sendMessage($message)
|
||||
{
|
||||
$this->sentMessages[] = $message;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function saveMessage($message)
|
||||
{
|
||||
return $this->sendMessage($message);
|
||||
}
|
||||
|
||||
public function getSentMessages()
|
||||
{
|
||||
return $this->sentMessages;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->sentMessages = [];
|
||||
}
|
||||
}
|
||||
97
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php
vendored
Normal file
97
vendor/codeception/base/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Codeception\Lib\Connector\Yii2;
|
||||
|
||||
use yii\base\Event;
|
||||
use yii\db\Connection;
|
||||
use yii\db\Transaction;
|
||||
|
||||
/**
|
||||
* Class TransactionForcer
|
||||
* This class adds support for forcing transactions as well as reusing PDO objects.
|
||||
* @package Codeception\Lib\Connector\Yii2
|
||||
*/
|
||||
class TransactionForcer extends ConnectionWatcher
|
||||
{
|
||||
private $ignoreCollidingDSN;
|
||||
|
||||
private $pdoCache = [];
|
||||
|
||||
private $dsnCache;
|
||||
|
||||
private $transactions = [];
|
||||
|
||||
public function __construct(
|
||||
$ignoreCollidingDSN
|
||||
) {
|
||||
parent::__construct();
|
||||
$this->ignoreCollidingDSN = $ignoreCollidingDSN;
|
||||
}
|
||||
|
||||
|
||||
protected function connectionOpened(Connection $connection)
|
||||
{
|
||||
parent::connectionOpened($connection);
|
||||
/**
|
||||
* We should check if the known PDO objects are the same, in which case we should reuse the PDO
|
||||
* object so only 1 transaction is started and multiple connections to the same database see the
|
||||
* same data (due to writes inside a transaction not being visible from the outside).
|
||||
*
|
||||
*/
|
||||
$key = md5(json_encode([
|
||||
'dsn' => $connection->dsn,
|
||||
'user' => $connection->username,
|
||||
'pass' => $connection->password,
|
||||
'attributes' => $connection->attributes,
|
||||
'emulatePrepare' => $connection->emulatePrepare,
|
||||
'charset' => $connection->charset
|
||||
]));
|
||||
|
||||
/*
|
||||
* If keys match we assume connections are "similar enough".
|
||||
*/
|
||||
if (isset($this->pdoCache[$key])) {
|
||||
$connection->pdo = $this->pdoCache[$key];
|
||||
} else {
|
||||
$this->pdoCache[$key] = $connection->pdo;
|
||||
}
|
||||
|
||||
if (isset($this->dsnCache[$connection->dsn])
|
||||
&& $this->dsnCache[$connection->dsn] !== $key
|
||||
&& !$this->ignoreCollidingDSN
|
||||
) {
|
||||
$this->debug(<<<TEXT
|
||||
You use multiple connections to the same DSN ({$connection->dsn}) with different configuration.
|
||||
These connections will not see the same database state since we cannot share a transaction between different PDO
|
||||
instances.
|
||||
You can remove this message by adding 'ignoreCollidingDSN = true' in the module configuration.
|
||||
TEXT
|
||||
);
|
||||
Debug::pause();
|
||||
}
|
||||
|
||||
if (isset($this->transactions[$key])) {
|
||||
$this->debug('Reusing PDO, so no need for a new transaction');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->debug('Transaction started for: ' . $connection->dsn);
|
||||
$this->transactions[$key] = $connection->beginTransaction();
|
||||
}
|
||||
|
||||
public function rollbackAll()
|
||||
{
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($this->transactions as $transaction) {
|
||||
if ($transaction->db->isActive) {
|
||||
$transaction->rollBack();
|
||||
$this->debug('Transaction cancelled; all changes reverted.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->transactions = [];
|
||||
$this->pdoCache = [];
|
||||
$this->dsnCache = [];
|
||||
}
|
||||
}
|
||||
145
vendor/codeception/base/src/Codeception/Lib/Connector/ZF1.php
vendored
Normal file
145
vendor/codeception/base/src/Codeception/Lib/Connector/ZF1.php
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
use Symfony\Component\BrowserKit\Client;
|
||||
use Symfony\Component\BrowserKit\Response;
|
||||
use Symfony\Component\BrowserKit\Request as BrowserKitRequest;
|
||||
|
||||
class ZF1 extends Client
|
||||
{
|
||||
use Shared\PhpSuperGlobalsConverter;
|
||||
|
||||
/**
|
||||
* @var \Zend_Controller_Front
|
||||
*/
|
||||
protected $front;
|
||||
|
||||
/**
|
||||
* @var \Zend_Application
|
||||
*/
|
||||
protected $bootstrap;
|
||||
|
||||
/**
|
||||
* @var \Zend_Controller_Request_HttpTestCase
|
||||
*/
|
||||
protected $zendRequest;
|
||||
|
||||
public function setBootstrap($bootstrap)
|
||||
{
|
||||
$this->bootstrap = $bootstrap;
|
||||
|
||||
$this->front = $this->bootstrap
|
||||
->getBootstrap()
|
||||
->getResource('frontcontroller');
|
||||
$this->front
|
||||
->throwExceptions(true)
|
||||
->returnResponse(false);
|
||||
}
|
||||
|
||||
public function doRequest($request)
|
||||
{
|
||||
|
||||
// redirector should not exit
|
||||
$redirector = \Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
|
||||
$redirector->setExit(false);
|
||||
|
||||
// json helper should not exit
|
||||
$json = \Zend_Controller_Action_HelperBroker::getStaticHelper('json');
|
||||
$json->suppressExit = true;
|
||||
|
||||
$zendRequest = new \Zend_Controller_Request_HttpTestCase();
|
||||
$zendRequest->setMethod($request->getMethod());
|
||||
$zendRequest->setCookies($request->getCookies());
|
||||
$zendRequest->setParams($request->getParameters());
|
||||
// Sf2's BrowserKit does not distinguish between GET, POST, PUT etc.,
|
||||
// so we set all parameters in ZF's request here to not break apps
|
||||
// relying on $request->getPost()
|
||||
$zendRequest->setPost($request->getParameters());
|
||||
$zendRequest->setRawBody($request->getContent());
|
||||
|
||||
$uri = $request->getUri();
|
||||
$queryString = parse_url($uri, PHP_URL_QUERY);
|
||||
$requestUri = parse_url($uri, PHP_URL_PATH);
|
||||
if (!empty($queryString)) {
|
||||
$requestUri .= '?' . $queryString;
|
||||
}
|
||||
$zendRequest->setRequestUri($requestUri);
|
||||
|
||||
$zendRequest->setHeaders($this->extractHeaders($request));
|
||||
$_FILES = $this->remapFiles($request->getFiles());
|
||||
$_SERVER = array_merge($_SERVER, $request->getServer());
|
||||
|
||||
$zendResponse = new \Zend_Controller_Response_HttpTestCase;
|
||||
$this->front->setRequest($zendRequest)->setResponse($zendResponse);
|
||||
|
||||
ob_start();
|
||||
try {
|
||||
$this->bootstrap->run();
|
||||
$_GET = $_POST = [];
|
||||
} catch (\Exception $e) {
|
||||
ob_end_clean();
|
||||
$_GET = $_POST = [];
|
||||
throw $e;
|
||||
}
|
||||
ob_end_clean();
|
||||
|
||||
$this->zendRequest = $zendRequest;
|
||||
|
||||
$response = new Response(
|
||||
$zendResponse->getBody(),
|
||||
$zendResponse->getHttpResponseCode(),
|
||||
$this->formatResponseHeaders($zendResponse)
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format up the ZF1 response headers into Symfony\Component\BrowserKit\Response headers format.
|
||||
*
|
||||
* @param \Zend_Controller_Response_Abstract $response The ZF1 Response Object.
|
||||
* @return array the clean key/value headers
|
||||
*/
|
||||
private function formatResponseHeaders(\Zend_Controller_Response_Abstract $response)
|
||||
{
|
||||
$headers = [];
|
||||
foreach ($response->getHeaders() as $header) {
|
||||
$name = $header['name'];
|
||||
if (array_key_exists($name, $headers)) {
|
||||
if ($header['replace']) {
|
||||
$headers[$name] = $header['value'];
|
||||
}
|
||||
} else {
|
||||
$headers[$name] = $header['value'];
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return \Zend_Controller_Request_HttpTestCase
|
||||
*/
|
||||
public function getZendRequest()
|
||||
{
|
||||
return $this->zendRequest;
|
||||
}
|
||||
|
||||
private function extractHeaders(BrowserKitRequest $request)
|
||||
{
|
||||
$headers = [];
|
||||
$server = $request->getServer();
|
||||
|
||||
$contentHeaders = ['Content-Length' => true, 'Content-Md5' => true, 'Content-Type' => true];
|
||||
foreach ($server as $header => $val) {
|
||||
$header = html_entity_decode(implode('-', array_map('ucfirst', explode('-', strtolower(str_replace('_', '-', $header))))), ENT_NOQUOTES);
|
||||
|
||||
if (strpos($header, 'Http-') === 0) {
|
||||
$headers[substr($header, 5)] = $val;
|
||||
} elseif (isset($contentHeaders[$header])) {
|
||||
$headers[$header] = $val;
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
192
vendor/codeception/base/src/Codeception/Lib/Connector/ZF2.php
vendored
Normal file
192
vendor/codeception/base/src/Codeception/Lib/Connector/ZF2.php
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
use Codeception\Exception\ModuleException;
|
||||
use Codeception\Lib\Connector\ZF2\PersistentServiceManager;
|
||||
use Symfony\Component\BrowserKit\Client;
|
||||
use Symfony\Component\BrowserKit\Request;
|
||||
use Symfony\Component\BrowserKit\Response;
|
||||
use Zend\Http\Request as HttpRequest;
|
||||
use Zend\Http\Headers as HttpHeaders;
|
||||
use Zend\Mvc\Application;
|
||||
use Zend\Stdlib\Parameters;
|
||||
use Zend\Uri\Http as HttpUri;
|
||||
use Symfony\Component\BrowserKit\Request as BrowserKitRequest;
|
||||
|
||||
class ZF2 extends Client
|
||||
{
|
||||
/**
|
||||
* @var \Zend\Mvc\ApplicationInterface
|
||||
*/
|
||||
protected $application;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $applicationConfig;
|
||||
|
||||
/**
|
||||
* @var \Zend\Http\PhpEnvironment\Request
|
||||
*/
|
||||
protected $zendRequest;
|
||||
|
||||
/**
|
||||
* @var PersistentServiceManager
|
||||
*/
|
||||
private $persistentServiceManager;
|
||||
|
||||
/**
|
||||
* @param array $applicationConfig
|
||||
*/
|
||||
public function setApplicationConfig($applicationConfig)
|
||||
{
|
||||
$this->applicationConfig = $applicationConfig;
|
||||
$this->createApplication();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function doRequest($request)
|
||||
{
|
||||
$this->createApplication();
|
||||
$zendRequest = $this->application->getRequest();
|
||||
|
||||
$uri = new HttpUri($request->getUri());
|
||||
$queryString = $uri->getQuery();
|
||||
$method = strtoupper($request->getMethod());
|
||||
|
||||
$zendRequest->setCookies(new Parameters($request->getCookies()));
|
||||
|
||||
$query = [];
|
||||
$post = [];
|
||||
$content = $request->getContent();
|
||||
if ($queryString) {
|
||||
parse_str($queryString, $query);
|
||||
}
|
||||
|
||||
if ($method !== HttpRequest::METHOD_GET) {
|
||||
$post = $request->getParameters();
|
||||
}
|
||||
|
||||
$zendRequest->setQuery(new Parameters($query));
|
||||
$zendRequest->setPost(new Parameters($post));
|
||||
$zendRequest->setFiles(new Parameters($request->getFiles()));
|
||||
$zendRequest->setContent($content);
|
||||
$zendRequest->setMethod($method);
|
||||
$zendRequest->setUri($uri);
|
||||
$requestUri = $uri->getPath();
|
||||
if (!empty($queryString)) {
|
||||
$requestUri .= '?' . $queryString;
|
||||
}
|
||||
|
||||
$zendRequest->setRequestUri($requestUri);
|
||||
|
||||
$zendRequest->setHeaders($this->extractHeaders($request));
|
||||
$this->application->run();
|
||||
|
||||
// get the response *after* the application has run, because other ZF
|
||||
// libraries like API Agility may *replace* the application's response
|
||||
//
|
||||
$zendResponse = $this->application->getResponse();
|
||||
|
||||
$this->zendRequest = $zendRequest;
|
||||
|
||||
$exception = $this->application->getMvcEvent()->getParam('exception');
|
||||
if ($exception instanceof \Exception) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$response = new Response(
|
||||
$zendResponse->getBody(),
|
||||
$zendResponse->getStatusCode(),
|
||||
$zendResponse->getHeaders()->toArray()
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Zend\Http\PhpEnvironment\Request
|
||||
*/
|
||||
public function getZendRequest()
|
||||
{
|
||||
return $this->zendRequest;
|
||||
}
|
||||
|
||||
private function extractHeaders(BrowserKitRequest $request)
|
||||
{
|
||||
$headers = [];
|
||||
$server = $request->getServer();
|
||||
|
||||
$contentHeaders = ['Content-Length' => true, 'Content-Md5' => true, 'Content-Type' => true];
|
||||
foreach ($server as $header => $val) {
|
||||
$header = html_entity_decode(implode('-', array_map('ucfirst', explode('-', strtolower(str_replace('_', '-', $header))))), ENT_NOQUOTES);
|
||||
|
||||
if (strpos($header, 'Http-') === 0) {
|
||||
$headers[substr($header, 5)] = $val;
|
||||
} elseif (isset($contentHeaders[$header])) {
|
||||
$headers[$header] = $val;
|
||||
}
|
||||
}
|
||||
$zendHeaders = new HttpHeaders();
|
||||
$zendHeaders->addHeaders($headers);
|
||||
return $zendHeaders;
|
||||
}
|
||||
|
||||
public function grabServiceFromContainer($service)
|
||||
{
|
||||
$serviceManager = $this->application->getServiceManager();
|
||||
|
||||
if (!$serviceManager->has($service)) {
|
||||
throw new \PHPUnit\Framework\AssertionFailedError("Service $service is not available in container");
|
||||
}
|
||||
|
||||
if ($service === 'Doctrine\ORM\EntityManager' && !isset($this->persistentServiceManager)) {
|
||||
if (!method_exists($serviceManager, 'addPeeringServiceManager')) {
|
||||
throw new ModuleException('Codeception\Module\ZF2', 'integration with Doctrine2 module is not compatible with ZF3');
|
||||
}
|
||||
$this->persistentServiceManager = new PersistentServiceManager($serviceManager);
|
||||
}
|
||||
|
||||
return $serviceManager->get($service);
|
||||
}
|
||||
|
||||
public function addServiceToContainer($name, $service)
|
||||
{
|
||||
if (!isset($this->persistentServiceManager)) {
|
||||
$serviceManager = $this->application->getServiceManager();
|
||||
if (!method_exists($serviceManager, 'addPeeringServiceManager')) {
|
||||
throw new ModuleException('Codeception\Module\ZF2', 'addServiceToContainer method is not compatible with ZF3');
|
||||
}
|
||||
$this->persistentServiceManager = new PersistentServiceManager($serviceManager);
|
||||
$serviceManager->addPeeringServiceManager($this->persistentServiceManager);
|
||||
$serviceManager->setRetrieveFromPeeringManagerFirst(true);
|
||||
}
|
||||
$this->persistentServiceManager->setAllowOverride(true);
|
||||
$this->persistentServiceManager->setService($name, $service);
|
||||
$this->persistentServiceManager->setAllowOverride(false);
|
||||
}
|
||||
|
||||
private function createApplication()
|
||||
{
|
||||
$this->application = Application::init($this->applicationConfig);
|
||||
$serviceManager = $this->application->getServiceManager();
|
||||
|
||||
if (isset($this->persistentServiceManager)) {
|
||||
$serviceManager->addPeeringServiceManager($this->persistentServiceManager);
|
||||
$serviceManager->setRetrieveFromPeeringManagerFirst(true);
|
||||
}
|
||||
|
||||
$sendResponseListener = $serviceManager->get('SendResponseListener');
|
||||
$events = $this->application->getEventManager();
|
||||
if (class_exists('Zend\EventManager\StaticEventManager')) {
|
||||
$events->detach($sendResponseListener); //ZF2
|
||||
} else {
|
||||
$events->detach([$sendResponseListener, 'sendResponse']); //ZF3
|
||||
}
|
||||
}
|
||||
}
|
||||
43
vendor/codeception/base/src/Codeception/Lib/Connector/ZF2/PersistentServiceManager.php
vendored
Normal file
43
vendor/codeception/base/src/Codeception/Lib/Connector/ZF2/PersistentServiceManager.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Codeception\Lib\Connector\ZF2;
|
||||
|
||||
use \Zend\ServiceManager\ServiceLocatorInterface;
|
||||
use \Zend\ServiceManager\ServiceManager;
|
||||
|
||||
class PersistentServiceManager extends ServiceManager implements ServiceLocatorInterface
|
||||
{
|
||||
/**
|
||||
* @var ServiceLocatorInterface Used to retrieve Doctrine services
|
||||
*/
|
||||
private $serviceManager;
|
||||
|
||||
public function __construct(ServiceLocatorInterface $serviceManager)
|
||||
{
|
||||
$this->serviceManager = $serviceManager;
|
||||
}
|
||||
|
||||
public function get($name, $usePeeringServiceManagers = true)
|
||||
{
|
||||
if (parent::has($name)) {
|
||||
return parent::get($name, $usePeeringServiceManagers);
|
||||
}
|
||||
return $this->serviceManager->get($name);
|
||||
}
|
||||
|
||||
public function has($name, $checkAbstractFactories = true, $usePeeringServiceManagers = true)
|
||||
{
|
||||
if (parent::has($name, $checkAbstractFactories, $usePeeringServiceManagers)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/doctrine/i', $name)) {
|
||||
return $this->serviceManager->has($name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setService($name, $service)
|
||||
{
|
||||
parent::setService($name, $service);
|
||||
}
|
||||
}
|
||||
142
vendor/codeception/base/src/Codeception/Lib/Connector/ZendExpressive.php
vendored
Normal file
142
vendor/codeception/base/src/Codeception/Lib/Connector/ZendExpressive.php
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector;
|
||||
|
||||
use Codeception\Lib\Connector\ZendExpressive\ResponseCollector;
|
||||
use Symfony\Component\BrowserKit\Client;
|
||||
use Symfony\Component\BrowserKit\Request;
|
||||
use Symfony\Component\BrowserKit\Response;
|
||||
use Symfony\Component\BrowserKit\Request as BrowserKitRequest;
|
||||
use Zend\Diactoros\ServerRequest;
|
||||
use Zend\Expressive\Application;
|
||||
use Zend\Diactoros\UploadedFile;
|
||||
|
||||
class ZendExpressive extends Client
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $application;
|
||||
|
||||
/**
|
||||
* @var ResponseCollector
|
||||
*/
|
||||
protected $responseCollector;
|
||||
|
||||
/**
|
||||
* @param Application
|
||||
*/
|
||||
public function setApplication(Application $application)
|
||||
{
|
||||
$this->application = $application;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ResponseCollector $responseCollector
|
||||
*/
|
||||
public function setResponseCollector(ResponseCollector $responseCollector)
|
||||
{
|
||||
$this->responseCollector = $responseCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function doRequest($request)
|
||||
{
|
||||
$inputStream = fopen('php://memory', 'r+');
|
||||
$content = $request->getContent();
|
||||
if ($content !== null) {
|
||||
fwrite($inputStream, $content);
|
||||
rewind($inputStream);
|
||||
}
|
||||
|
||||
$queryParams = [];
|
||||
$postParams = [];
|
||||
$queryString = parse_url($request->getUri(), PHP_URL_QUERY);
|
||||
if ($queryString != '') {
|
||||
parse_str($queryString, $queryParams);
|
||||
}
|
||||
if ($request->getMethod() !== 'GET') {
|
||||
$postParams = $request->getParameters();
|
||||
}
|
||||
|
||||
$serverParams = $request->getServer();
|
||||
if (!isset($serverParams['SCRIPT_NAME'])) {
|
||||
//required by WhoopsErrorHandler
|
||||
$serverParams['SCRIPT_NAME'] = 'Codeception';
|
||||
}
|
||||
|
||||
$zendRequest = new ServerRequest(
|
||||
$serverParams,
|
||||
$this->convertFiles($request->getFiles()),
|
||||
$request->getUri(),
|
||||
$request->getMethod(),
|
||||
$inputStream,
|
||||
$this->extractHeaders($request)
|
||||
);
|
||||
|
||||
$zendRequest = $zendRequest->withCookieParams($request->getCookies())
|
||||
->withQueryParams($queryParams)
|
||||
->withParsedBody($postParams);
|
||||
|
||||
$cwd = getcwd();
|
||||
chdir(codecept_root_dir());
|
||||
$this->application->run($zendRequest);
|
||||
chdir($cwd);
|
||||
|
||||
$this->request = $zendRequest;
|
||||
|
||||
$response = $this->responseCollector->getResponse();
|
||||
$this->responseCollector->clearResponse();
|
||||
|
||||
return new Response(
|
||||
$response->getBody(),
|
||||
$response->getStatusCode(),
|
||||
$response->getHeaders()
|
||||
);
|
||||
}
|
||||
|
||||
private function convertFiles(array $files)
|
||||
{
|
||||
$fileObjects = [];
|
||||
foreach ($files as $fieldName => $file) {
|
||||
if ($file instanceof UploadedFile) {
|
||||
$fileObjects[$fieldName] = $file;
|
||||
} elseif (!isset($file['tmp_name']) && !isset($file['name'])) {
|
||||
$fileObjects[$fieldName] = $this->convertFiles($file);
|
||||
} else {
|
||||
$fileObjects[$fieldName] = new UploadedFile(
|
||||
$file['tmp_name'],
|
||||
$file['size'],
|
||||
$file['error'],
|
||||
$file['name'],
|
||||
$file['type']
|
||||
);
|
||||
}
|
||||
}
|
||||
return $fileObjects;
|
||||
}
|
||||
|
||||
private function extractHeaders(BrowserKitRequest $request)
|
||||
{
|
||||
$headers = [];
|
||||
$server = $request->getServer();
|
||||
|
||||
$contentHeaders = ['Content-Length' => true, 'Content-Md5' => true, 'Content-Type' => true];
|
||||
foreach ($server as $header => $val) {
|
||||
$header = html_entity_decode(implode('-', array_map('ucfirst', explode('-', strtolower(str_replace('_', '-', $header))))), ENT_NOQUOTES);
|
||||
|
||||
if (strpos($header, 'Http-') === 0) {
|
||||
$headers[substr($header, 5)] = $val;
|
||||
} elseif (isset($contentHeaders[$header])) {
|
||||
$headers[$header] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
31
vendor/codeception/base/src/Codeception/Lib/Connector/ZendExpressive/ResponseCollector.php
vendored
Normal file
31
vendor/codeception/base/src/Codeception/Lib/Connector/ZendExpressive/ResponseCollector.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Connector\ZendExpressive;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Zend\Diactoros\Response\EmitterInterface;
|
||||
|
||||
class ResponseCollector implements EmitterInterface
|
||||
{
|
||||
/**
|
||||
* @var ResponseInterface
|
||||
*/
|
||||
private $response;
|
||||
|
||||
public function emit(ResponseInterface $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
public function getResponse()
|
||||
{
|
||||
if ($this->response === null) {
|
||||
throw new \LogicException('Response wasn\'t emitted yet');
|
||||
}
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
public function clearResponse()
|
||||
{
|
||||
$this->response = null;
|
||||
}
|
||||
}
|
||||
37
vendor/codeception/base/src/Codeception/Lib/Console/Colorizer.php
vendored
Normal file
37
vendor/codeception/base/src/Codeception/Lib/Console/Colorizer.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Console;
|
||||
|
||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||
|
||||
class Colorizer
|
||||
{
|
||||
/**
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public function colorize($string = '')
|
||||
{
|
||||
$fp = fopen('php://memory', 'r+');
|
||||
fwrite($fp, $string);
|
||||
rewind($fp);
|
||||
|
||||
$colorizedMessage = '';
|
||||
while ($line = fgets($fp)) {
|
||||
$char = $line[0];
|
||||
$line = OutputFormatter::escape(trim($line));
|
||||
|
||||
switch ($char) {
|
||||
case '+':
|
||||
$line = "<info>$line</info>";
|
||||
break;
|
||||
case '-':
|
||||
$line = "<comment>$line</comment>";
|
||||
break;
|
||||
}
|
||||
|
||||
$colorizedMessage .= $line . "\n";
|
||||
}
|
||||
|
||||
return trim($colorizedMessage);
|
||||
}
|
||||
}
|
||||
41
vendor/codeception/base/src/Codeception/Lib/Console/DiffFactory.php
vendored
Normal file
41
vendor/codeception/base/src/Codeception/Lib/Console/DiffFactory.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Console;
|
||||
|
||||
use SebastianBergmann\Comparator\ComparisonFailure;
|
||||
use SebastianBergmann\Diff\Differ;
|
||||
|
||||
/**
|
||||
* DiffFactory
|
||||
**/
|
||||
class DiffFactory
|
||||
{
|
||||
/**
|
||||
* @param ComparisonFailure $failure
|
||||
* @return string|null
|
||||
*/
|
||||
public function createDiff(ComparisonFailure $failure)
|
||||
{
|
||||
$diff = $this->getDiff($failure->getExpectedAsString(), $failure->getActualAsString());
|
||||
if (!$diff) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $expected
|
||||
* @param string $actual
|
||||
* @return string
|
||||
*/
|
||||
private function getDiff($expected = '', $actual = '')
|
||||
{
|
||||
if (!$actual && !$expected) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$differ = new Differ('');
|
||||
|
||||
return $differ->diff($expected, $actual);
|
||||
}
|
||||
}
|
||||
122
vendor/codeception/base/src/Codeception/Lib/Console/Message.php
vendored
Normal file
122
vendor/codeception/base/src/Codeception/Lib/Console/Message.php
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Console;
|
||||
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class Message
|
||||
{
|
||||
protected $output;
|
||||
protected $message;
|
||||
|
||||
public function __construct($message, Output $output = null)
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
public function with($param)
|
||||
{
|
||||
$args = array_merge([$this->message], func_get_args());
|
||||
$this->message = call_user_func_array('sprintf', $args);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function style($name)
|
||||
{
|
||||
$this->message = sprintf('<%s>%s</%s>', $name, $this->message, $name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function width($length, $char = ' ')
|
||||
{
|
||||
$message_length = $this->getLength();
|
||||
|
||||
if ($message_length < $length) {
|
||||
$this->message .= str_repeat($char, $length - $message_length);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function cut($length)
|
||||
{
|
||||
$this->message = mb_substr($this->message, 0, $length, 'utf-8');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function write($verbose = OutputInterface::VERBOSITY_NORMAL)
|
||||
{
|
||||
if ($verbose > $this->output->getVerbosity()) {
|
||||
return;
|
||||
}
|
||||
$this->output->write($this->message);
|
||||
}
|
||||
|
||||
public function writeln($verbose = OutputInterface::VERBOSITY_NORMAL)
|
||||
{
|
||||
if ($verbose > $this->output->getVerbosity()) {
|
||||
return;
|
||||
}
|
||||
$this->output->writeln($this->message);
|
||||
}
|
||||
|
||||
public function prepend($string)
|
||||
{
|
||||
if ($string instanceof Message) {
|
||||
$string = $string->getMessage();
|
||||
}
|
||||
$this->message = $string . $this->message;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function append($string)
|
||||
{
|
||||
if ($string instanceof Message) {
|
||||
$string = $string->getMessage();
|
||||
}
|
||||
$this->message .= $string;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function apply($func)
|
||||
{
|
||||
$this->message = call_user_func($func, $this->message);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function center($char)
|
||||
{
|
||||
$this->message = $char . $this->message . $char;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMessage()
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function block($style)
|
||||
{
|
||||
$this->message = $this->output->formatHelper->formatBlock($this->message, $style, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLength($includeTags = false)
|
||||
{
|
||||
return mb_strwidth($includeTags ? $this->message : strip_tags($this->message), 'utf-8');
|
||||
}
|
||||
|
||||
public static function ucfirst($text)
|
||||
{
|
||||
return mb_strtoupper(mb_substr($text, 0, 1, 'utf-8'), 'utf-8') . mb_substr($text, 1, null, 'utf-8');
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
}
|
||||
59
vendor/codeception/base/src/Codeception/Lib/Console/MessageFactory.php
vendored
Normal file
59
vendor/codeception/base/src/Codeception/Lib/Console/MessageFactory.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Console;
|
||||
|
||||
use SebastianBergmann\Comparator\ComparisonFailure;
|
||||
|
||||
/**
|
||||
* MessageFactory
|
||||
**/
|
||||
class MessageFactory
|
||||
{
|
||||
/**
|
||||
* @var DiffFactory
|
||||
*/
|
||||
protected $diffFactory;
|
||||
/**
|
||||
* @var Output
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* @var Colorizer
|
||||
*/
|
||||
protected $colorizer;
|
||||
|
||||
/**
|
||||
* MessageFactory constructor.
|
||||
* @param Output $output
|
||||
*/
|
||||
public function __construct(Output $output)
|
||||
{
|
||||
$this->output = $output;
|
||||
$this->diffFactory = new DiffFactory();
|
||||
$this->colorizer = new Colorizer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $text
|
||||
* @return Message
|
||||
*/
|
||||
public function message($text = '')
|
||||
{
|
||||
return new Message($text, $this->output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ComparisonFailure $failure
|
||||
* @return string
|
||||
*/
|
||||
public function prepareComparisonFailureMessage(ComparisonFailure $failure)
|
||||
{
|
||||
$diff = $this->diffFactory->createDiff($failure);
|
||||
if (!$diff) {
|
||||
return '';
|
||||
}
|
||||
$diff = $this->colorizer->colorize($diff);
|
||||
|
||||
return "\n<comment>- Expected</comment> | <info>+ Actual</info>\n$diff";
|
||||
}
|
||||
}
|
||||
100
vendor/codeception/base/src/Codeception/Lib/Console/Output.php
vendored
Normal file
100
vendor/codeception/base/src/Codeception/Lib/Console/Output.php
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
namespace Codeception\Lib\Console;
|
||||
|
||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Helper\FormatterHelper;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
|
||||
class Output extends ConsoleOutput
|
||||
{
|
||||
protected $config = [
|
||||
'colors' => true,
|
||||
'verbosity' => self::VERBOSITY_NORMAL,
|
||||
'interactive' => true
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Console\Helper\FormatterHelper
|
||||
*/
|
||||
public $formatHelper;
|
||||
|
||||
public $waitForDebugOutput = true;
|
||||
|
||||
protected $isInteractive = false;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = array_merge($this->config, $config);
|
||||
|
||||
// enable interactive output mode for CLI
|
||||
$this->isInteractive = $this->config['interactive']
|
||||
&& isset($_SERVER['TERM'])
|
||||
&& php_sapi_name() == 'cli'
|
||||
&& $_SERVER['TERM'] != 'linux';
|
||||
|
||||
$formatter = new OutputFormatter($this->config['colors']);
|
||||
$formatter->setStyle('default', new OutputFormatterStyle());
|
||||
$formatter->setStyle('bold', new OutputFormatterStyle(null, null, ['bold']));
|
||||
$formatter->setStyle('focus', new OutputFormatterStyle('magenta', null, ['bold']));
|
||||
$formatter->setStyle('ok', new OutputFormatterStyle('green', null, ['bold']));
|
||||
$formatter->setStyle('error', new OutputFormatterStyle('white', 'red', ['bold']));
|
||||
$formatter->setStyle('fail', new OutputFormatterStyle('red', null, ['bold']));
|
||||
$formatter->setStyle('pending', new OutputFormatterStyle('yellow', null, ['bold']));
|
||||
$formatter->setStyle('debug', new OutputFormatterStyle('cyan'));
|
||||
$formatter->setStyle('comment', new OutputFormatterStyle('yellow'));
|
||||
$formatter->setStyle('info', new OutputFormatterStyle('green'));
|
||||
|
||||
$this->formatHelper = new FormatterHelper();
|
||||
|
||||
|
||||
parent::__construct($this->config['verbosity'], $this->config['colors'], $formatter);
|
||||
}
|
||||
|
||||
public function isInteractive()
|
||||
{
|
||||
return $this->isInteractive;
|
||||
}
|
||||
|
||||
protected function clean($message)
|
||||
{
|
||||
// clear json serialization
|
||||
$message = str_replace('\/', '/', $message);
|
||||
return $message;
|
||||
}
|
||||
|
||||
public function debug($message)
|
||||
{
|
||||
$message = print_r($message, true);
|
||||
$message = str_replace("\n", "\n ", $message);
|
||||
$message = $this->clean($message);
|
||||
$message = OutputFormatter::escape($message);
|
||||
|
||||
if ($this->waitForDebugOutput) {
|
||||
$this->writeln('');
|
||||
$this->waitForDebugOutput = false;
|
||||
}
|
||||
$this->writeln("<debug> $message</debug>");
|
||||
}
|
||||
|
||||
public function message($message)
|
||||
{
|
||||
$message = call_user_func_array('sprintf', func_get_args());
|
||||
return new Message($message, $this);
|
||||
}
|
||||
|
||||
public function exception(\Exception $e)
|
||||
{
|
||||
$class = get_class($e);
|
||||
|
||||
$this->writeln("");
|
||||
$this->writeln("(![ $class ]!)");
|
||||
$this->writeln($e->getMessage());
|
||||
$this->writeln("");
|
||||
}
|
||||
|
||||
public function notification($message)
|
||||
{
|
||||
$this->writeln("<comment>$message</comment>");
|
||||
}
|
||||
}
|
||||
105
vendor/codeception/base/src/Codeception/Lib/DbPopulator.php
vendored
Normal file
105
vendor/codeception/base/src/Codeception/Lib/DbPopulator.php
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Codeception\Lib;
|
||||
|
||||
/**
|
||||
* Populates a db using a parameterized command built from the Db module configuration.
|
||||
*/
|
||||
class DbPopulator
|
||||
{
|
||||
/**
|
||||
* The command to be executed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $builtCommand;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Constructs a DbPopulator object for the given command and Db module.
|
||||
*
|
||||
* @param $config
|
||||
* @internal param string $command The parameterized command to evaluate and execute later.
|
||||
* @internal param Codeception\Module\Db|null $dbModule The Db module used to build the populator command or null.
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$command = $this->config['populator'];
|
||||
$this->builtCommand = $this->buildCommand((string) $command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds out a command replacing any found `$key` with its value if found in the given configuration.
|
||||
*
|
||||
* Process any $key found in the configuration array as a key of the array and replaces it with
|
||||
* the found value for the key. Example:
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
*
|
||||
* $command = 'Hello $name';
|
||||
* $config = ['name' => 'Mauro'];
|
||||
*
|
||||
* // With the above parameters it will return `'Hello Mauro'`.
|
||||
* ```
|
||||
*
|
||||
* @param string $command The command to be evaluated using the given config
|
||||
* @param array $config The configuration values used to replace any found $keys with values from this array.
|
||||
* @return string The resulting command string after evaluating any configuration's key
|
||||
*/
|
||||
protected function buildCommand($command)
|
||||
{
|
||||
$dsn = isset($this->config['dsn']) ? $this->config['dsn'] : '';
|
||||
$dsnVars = [];
|
||||
$dsnWithoutDriver = preg_replace('/^[a-z]+:/i', '', $dsn);
|
||||
foreach (explode(';', $dsnWithoutDriver) as $item) {
|
||||
$keyValueTuple = explode('=', $item);
|
||||
if (count($keyValueTuple) > 1) {
|
||||
list($k, $v) = array_values($keyValueTuple);
|
||||
$dsnVars[$k] = $v;
|
||||
}
|
||||
}
|
||||
$vars = array_merge($dsnVars, $this->config);
|
||||
foreach ($vars as $key => $value) {
|
||||
$vars['$'.$key] = $value;
|
||||
unset($vars[$key]);
|
||||
}
|
||||
return str_replace(array_keys($vars), array_values($vars), $command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the command built using the Db module configuration.
|
||||
*
|
||||
* Uses the PHP `exec` to spin off a child process for the built command.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$command = $this->getBuiltCommand();
|
||||
codecept_debug("[Db] Executing Populator: `$command`");
|
||||
|
||||
exec($command, $output, $exitCode);
|
||||
|
||||
if (0 !== $exitCode) {
|
||||
throw new \RuntimeException(
|
||||
"The populator command did not end successfully: \n" .
|
||||
" Exit code: $exitCode \n" .
|
||||
" Output:" . implode("\n", $output)
|
||||
);
|
||||
}
|
||||
|
||||
codecept_debug("[Db] Populator Finished.");
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getBuiltCommand()
|
||||
{
|
||||
return $this->builtCommand;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user