init
This commit is contained in:
110
vendor/codeception/base/ext/DotReporter.php
vendored
Normal file
110
vendor/codeception/base/ext/DotReporter.php
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
namespace Codeception\Extension;
|
||||
|
||||
use Codeception\Event\FailEvent;
|
||||
use Codeception\Events;
|
||||
use Codeception\Extension;
|
||||
use Codeception\Subscriber\Console;
|
||||
|
||||
/**
|
||||
* DotReporter provides less verbose output for test execution.
|
||||
* Like PHPUnit printer it prints dots "." for successful testes and "F" for failures.
|
||||
*
|
||||
* 
|
||||
*
|
||||
* ```bash
|
||||
* ..........
|
||||
* ..........
|
||||
* ..........
|
||||
* ..........
|
||||
* ..........
|
||||
* ..........
|
||||
* ..........
|
||||
* ..........
|
||||
*
|
||||
* Time: 2.07 seconds, Memory: 20.00MB
|
||||
*
|
||||
* OK (80 tests, 124 assertions)
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* Enable this reporter with `--ext option`
|
||||
*
|
||||
* ```
|
||||
* codecept run --ext DotReporter
|
||||
* ```
|
||||
*
|
||||
* Failures and Errors are printed by a standard Codeception reporter.
|
||||
* Use this extension as an example for building custom reporters.
|
||||
*/
|
||||
class DotReporter extends Extension
|
||||
{
|
||||
/**
|
||||
* @var Console
|
||||
*/
|
||||
protected $standardReporter;
|
||||
|
||||
protected $errors = [];
|
||||
protected $failures = [];
|
||||
|
||||
protected $width = 10;
|
||||
protected $currentPos = 0;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
$this->options['silent'] = false; // turn on printing for this extension
|
||||
$this->_reconfigure(['settings' => ['silent' => true]]); // turn off printing for everything else
|
||||
$this->standardReporter = new Console($this->options);
|
||||
$this->width = $this->standardReporter->detectWidth();
|
||||
}
|
||||
|
||||
// we are listening for events
|
||||
public static $events = [
|
||||
Events::SUITE_BEFORE => 'beforeSuite',
|
||||
Events::TEST_SUCCESS => 'success',
|
||||
Events::TEST_FAIL => 'fail',
|
||||
Events::TEST_ERROR => 'error',
|
||||
Events::TEST_SKIPPED => 'skipped',
|
||||
Events::TEST_FAIL_PRINT => 'printFailed'
|
||||
];
|
||||
|
||||
public function beforeSuite()
|
||||
{
|
||||
$this->writeln("");
|
||||
}
|
||||
|
||||
public function success()
|
||||
{
|
||||
$this->printChar('.');
|
||||
}
|
||||
|
||||
public function fail(FailEvent $e)
|
||||
{
|
||||
$this->printChar("<error>F</error>");
|
||||
}
|
||||
|
||||
public function error(FailEvent $e)
|
||||
{
|
||||
$this->printChar('<error>E</error>');
|
||||
}
|
||||
|
||||
public function skipped()
|
||||
{
|
||||
$this->printChar('S');
|
||||
}
|
||||
|
||||
protected function printChar($char)
|
||||
{
|
||||
if ($this->currentPos >= $this->width) {
|
||||
$this->writeln('');
|
||||
$this->currentPos = 0;
|
||||
}
|
||||
$this->write($char);
|
||||
$this->currentPos++;
|
||||
}
|
||||
|
||||
public function printFailed(FailEvent $event)
|
||||
{
|
||||
$this->standardReporter->printFail($event);
|
||||
}
|
||||
}
|
||||
123
vendor/codeception/base/ext/Logger.php
vendored
Normal file
123
vendor/codeception/base/ext/Logger.php
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
namespace Codeception\Extension;
|
||||
|
||||
use Codeception\Event\FailEvent;
|
||||
use Codeception\Event\StepEvent;
|
||||
use Codeception\Event\SuiteEvent;
|
||||
use Codeception\Event\TestEvent;
|
||||
use Codeception\Events;
|
||||
use Codeception\Exception\ConfigurationException;
|
||||
use Codeception\Extension;
|
||||
use Codeception\Test\Descriptor;
|
||||
use Monolog\Handler\RotatingFileHandler;
|
||||
|
||||
/**
|
||||
* Log suites/tests/steps using Monolog library.
|
||||
* Monolog should be installed additionally by Composer.
|
||||
*
|
||||
* ```
|
||||
* composer require monolog/monolog
|
||||
* ```
|
||||
*
|
||||
* Steps are logged into `tests/_output/codeception.log`
|
||||
*
|
||||
* To enable this module add to your `codeception.yml`:
|
||||
*
|
||||
* ``` yaml
|
||||
* extensions:
|
||||
* enabled: [Codeception\Extension\Logger]
|
||||
* ```
|
||||
*
|
||||
* #### Config
|
||||
*
|
||||
* * `max_files` (default: 3) - how many log files to keep
|
||||
*
|
||||
*/
|
||||
class Logger extends Extension
|
||||
{
|
||||
public static $events = [
|
||||
Events::SUITE_BEFORE => 'beforeSuite',
|
||||
Events::TEST_BEFORE => 'beforeTest',
|
||||
Events::TEST_AFTER => 'afterTest',
|
||||
Events::TEST_END => 'endTest',
|
||||
Events::STEP_BEFORE => 'beforeStep',
|
||||
Events::TEST_FAIL => 'testFail',
|
||||
Events::TEST_ERROR => 'testError',
|
||||
Events::TEST_INCOMPLETE => 'testIncomplete',
|
||||
Events::TEST_SKIPPED => 'testSkipped',
|
||||
];
|
||||
|
||||
protected $logHandler;
|
||||
|
||||
/**
|
||||
* @var \Monolog\Logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
protected $path;
|
||||
|
||||
protected $config = ['max_files' => 3];
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
if (!class_exists('\Monolog\Logger')) {
|
||||
throw new ConfigurationException("Logger extension requires Monolog library to be installed");
|
||||
}
|
||||
$this->path = $this->getLogDir();
|
||||
|
||||
// internal log
|
||||
$logHandler = new RotatingFileHandler($this->path . 'codeception.log', $this->config['max_files']);
|
||||
$this->logger = new \Monolog\Logger('Codeception');
|
||||
$this->logger->pushHandler($logHandler);
|
||||
}
|
||||
|
||||
public function beforeSuite(SuiteEvent $e)
|
||||
{
|
||||
$suite = str_replace('\\', '_', $e->getSuite()->getName());
|
||||
$this->logHandler = new RotatingFileHandler($this->path . $suite, $this->config['max_files']);
|
||||
}
|
||||
|
||||
public function beforeTest(TestEvent $e)
|
||||
{
|
||||
$this->logger = new \Monolog\Logger(Descriptor::getTestFileName($e->getTest()));
|
||||
$this->logger->pushHandler($this->logHandler);
|
||||
$this->logger->info('------------------------------------');
|
||||
$this->logger->info("STARTED: " . ucfirst(Descriptor::getTestAsString($e->getTest())));
|
||||
}
|
||||
|
||||
public function afterTest(TestEvent $e)
|
||||
{
|
||||
}
|
||||
|
||||
public function endTest(TestEvent $e)
|
||||
{
|
||||
$this->logger->info("PASSED");
|
||||
}
|
||||
|
||||
public function testFail(FailEvent $e)
|
||||
{
|
||||
$this->logger->alert($e->getFail()->getMessage());
|
||||
$this->logger->info("# FAILED #");
|
||||
}
|
||||
|
||||
public function testError(FailEvent $e)
|
||||
{
|
||||
$this->logger->alert($e->getFail()->getMessage());
|
||||
$this->logger->info("# ERROR #");
|
||||
}
|
||||
|
||||
public function testSkipped(FailEvent $e)
|
||||
{
|
||||
$this->logger->info("# Skipped #");
|
||||
}
|
||||
|
||||
public function testIncomplete(FailEvent $e)
|
||||
{
|
||||
$this->logger->info("# Incomplete #");
|
||||
}
|
||||
|
||||
public function beforeStep(StepEvent $e)
|
||||
{
|
||||
$this->logger->info((string) $e->getStep());
|
||||
}
|
||||
}
|
||||
221
vendor/codeception/base/ext/README.md
vendored
Normal file
221
vendor/codeception/base/ext/README.md
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
# Official Extensions
|
||||
|
||||
## DotReporter
|
||||
|
||||
[See Source](https://github.com/Codeception/Codeception/blob/2.4/ext/DotReporter.php)
|
||||
|
||||
DotReporter provides less verbose output for test execution.
|
||||
Like PHPUnit printer it prints dots "." for successful testes and "F" for failures.
|
||||
|
||||

|
||||
|
||||
```bash
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
|
||||
Time: 2.07 seconds, Memory: 20.00MB
|
||||
|
||||
OK (80 tests, 124 assertions)
|
||||
```
|
||||
|
||||
|
||||
Enable this reporter with `--ext option`
|
||||
|
||||
```
|
||||
codecept run --ext DotReporter
|
||||
```
|
||||
|
||||
Failures and Errors are printed by a standard Codeception reporter.
|
||||
Use this extension as an example for building custom reporters.
|
||||
|
||||
|
||||
|
||||
## Logger
|
||||
|
||||
[See Source](https://github.com/Codeception/Codeception/blob/2.4/ext/Logger.php)
|
||||
|
||||
Log suites/tests/steps using Monolog library.
|
||||
Monolog should be installed additionally by Composer.
|
||||
|
||||
```
|
||||
composer require monolog/monolog
|
||||
```
|
||||
|
||||
Steps are logged into `tests/_output/codeception.log`
|
||||
|
||||
To enable this module add to your `codeception.yml`:
|
||||
|
||||
``` yaml
|
||||
extensions:
|
||||
enabled: [Codeception\Extension\Logger]
|
||||
```
|
||||
|
||||
#### Config
|
||||
|
||||
* `max_files` (default: 3) - how many log files to keep
|
||||
|
||||
|
||||
|
||||
|
||||
## Recorder
|
||||
|
||||
[See Source](https://github.com/Codeception/Codeception/blob/2.4/ext/Recorder.php)
|
||||
|
||||
Saves a screenshot of each step in acceptance tests and shows them as a slideshow on one HTML page (here's an [example](http://codeception.com/images/recorder.gif))
|
||||
Activated only for suites with WebDriver module enabled.
|
||||
|
||||
The screenshots are saved to `tests/_output/record_*` directories, open `index.html` to see them as a slideshow.
|
||||
|
||||
#### Installation
|
||||
|
||||
Add this to the list of enabled extensions in `codeception.yml` or `acceptance.suite.yml`:
|
||||
|
||||
``` yaml
|
||||
extensions:
|
||||
enabled:
|
||||
- Codeception\Extension\Recorder
|
||||
```
|
||||
|
||||
#### Configuration
|
||||
|
||||
* `delete_successful` (default: true) - delete screenshots for successfully passed tests (i.e. log only failed and errored tests).
|
||||
* `module` (default: WebDriver) - which module for screenshots to use. Set `AngularJS` if you want to use it with AngularJS module. Generally, the module should implement `Codeception\Lib\Interfaces\ScreenshotSaver` interface.
|
||||
* `ignore_steps` (default: []) - array of step names that should not be recorded, * wildcards supported
|
||||
|
||||
|
||||
#### Examples:
|
||||
|
||||
``` yaml
|
||||
extensions:
|
||||
enabled:
|
||||
- Codeception\Extension\Recorder:
|
||||
module: AngularJS # enable for Angular
|
||||
delete_successful: false # keep screenshots of successful tests
|
||||
ignore_steps: [have, grab*]
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## RunBefore
|
||||
|
||||
[See Source](https://github.com/Codeception/Codeception/blob/2.4/ext/RunBefore.php)
|
||||
|
||||
Extension for execution of some processes before running tests.
|
||||
|
||||
Processes can be independent and dependent.
|
||||
Independent processes run independently of each other.
|
||||
Dependent processes run sequentially one by one.
|
||||
|
||||
Can be configured in suite config:
|
||||
|
||||
```yaml
|
||||
# acceptance.suite.yml
|
||||
extensions:
|
||||
enabled:
|
||||
- Codeception\Extension\RunBefore:
|
||||
- independent_process_1
|
||||
-
|
||||
- dependent_process_1_1
|
||||
- dependent_process_1_2
|
||||
- independent_process_2
|
||||
-
|
||||
- dependent_process_2_1
|
||||
- dependent_process_2_2
|
||||
```
|
||||
|
||||
HINT: you can use different configurations per environment.
|
||||
|
||||
|
||||
|
||||
## RunFailed
|
||||
|
||||
[See Source](https://github.com/Codeception/Codeception/blob/2.4/ext/RunFailed.php)
|
||||
|
||||
Saves failed tests into tests/log/failed in order to rerun failed tests.
|
||||
|
||||
To rerun failed tests just run the `failed` group:
|
||||
|
||||
```
|
||||
php codecept run -g failed
|
||||
```
|
||||
|
||||
To change failed group name add:
|
||||
```
|
||||
--override "extensions: config: Codeception\Extension\RunFailed: fail-group: another_group1"
|
||||
```
|
||||
Remember: if you run tests and they generated custom-named fail group, to run this group, you should add override too
|
||||
|
||||
Starting from Codeception 2.1 **this extension is enabled by default**.
|
||||
|
||||
``` yaml
|
||||
extensions:
|
||||
enabled: [Codeception\Extension\RunFailed]
|
||||
```
|
||||
|
||||
On each execution failed tests are logged and saved into `tests/_output/failed` file.
|
||||
|
||||
|
||||
|
||||
## RunProcess
|
||||
|
||||
[See Source](https://github.com/Codeception/Codeception/blob/2.4/ext/RunProcess.php)
|
||||
|
||||
Extension to start and stop processes per suite.
|
||||
Can be used to start/stop selenium server, chromedriver, phantomjs, mailcatcher, etc.
|
||||
|
||||
Can be configured in suite config:
|
||||
|
||||
```yaml
|
||||
# acceptance.suite.yml
|
||||
extensions:
|
||||
enabled:
|
||||
- Codeception\Extension\RunProcess:
|
||||
- chromedriver
|
||||
```
|
||||
|
||||
Multiple parameters can be passed as array:
|
||||
|
||||
```yaml
|
||||
# acceptance.suite.yml
|
||||
|
||||
extensions:
|
||||
enabled:
|
||||
- Codeception\Extension\RunProcess:
|
||||
- php -S 127.0.0.1:8000 -t tests/data/app
|
||||
- java -jar ~/selenium-server.jar
|
||||
```
|
||||
|
||||
In the end of a suite all launched processes will be stopped.
|
||||
|
||||
To wait for the process to be launched use `sleep` option.
|
||||
In this case you need configuration to be specified as object:
|
||||
|
||||
```yaml
|
||||
extensions:
|
||||
enabled:
|
||||
- Codeception\Extension\RunProcess:
|
||||
0: java -jar ~/selenium-server.jar
|
||||
1: mailcatcher
|
||||
sleep: 5 # wait 5 seconds for processes to boot
|
||||
```
|
||||
|
||||
HINT: you can use different configurations per environment.
|
||||
|
||||
|
||||
|
||||
## SimpleReporter
|
||||
|
||||
[See Source](https://github.com/Codeception/Codeception/blob/2.4/ext/SimpleReporter.php)
|
||||
|
||||
This extension demonstrates how you can implement console output of your own.
|
||||
Recommended to be used for development purposes only.
|
||||
|
||||
|
||||
|
||||
438
vendor/codeception/base/ext/Recorder.php
vendored
Normal file
438
vendor/codeception/base/ext/Recorder.php
vendored
Normal file
@@ -0,0 +1,438 @@
|
||||
<?php
|
||||
|
||||
namespace Codeception\Extension;
|
||||
|
||||
use Codeception\Event\StepEvent;
|
||||
use Codeception\Event\TestEvent;
|
||||
use Codeception\Events;
|
||||
use Codeception\Exception\ExtensionException;
|
||||
use Codeception\Lib\Interfaces\ScreenshotSaver;
|
||||
use Codeception\Module\WebDriver;
|
||||
use Codeception\Step;
|
||||
use Codeception\Step\Comment as CommentStep;
|
||||
use Codeception\Test\Descriptor;
|
||||
use Codeception\Util\FileSystem;
|
||||
use Codeception\Util\Template;
|
||||
|
||||
/**
|
||||
* Saves a screenshot of each step in acceptance tests and shows them as a slideshow on one HTML page (here's an [example](http://codeception.com/images/recorder.gif))
|
||||
* Activated only for suites with WebDriver module enabled.
|
||||
*
|
||||
* The screenshots are saved to `tests/_output/record_*` directories, open `index.html` to see them as a slideshow.
|
||||
*
|
||||
* #### Installation
|
||||
*
|
||||
* Add this to the list of enabled extensions in `codeception.yml` or `acceptance.suite.yml`:
|
||||
*
|
||||
* ``` yaml
|
||||
* extensions:
|
||||
* enabled:
|
||||
* - Codeception\Extension\Recorder
|
||||
* ```
|
||||
*
|
||||
* #### Configuration
|
||||
*
|
||||
* * `delete_successful` (default: true) - delete screenshots for successfully passed tests (i.e. log only failed and errored tests).
|
||||
* * `module` (default: WebDriver) - which module for screenshots to use. Set `AngularJS` if you want to use it with AngularJS module. Generally, the module should implement `Codeception\Lib\Interfaces\ScreenshotSaver` interface.
|
||||
* * `ignore_steps` (default: []) - array of step names that should not be recorded, * wildcards supported
|
||||
*
|
||||
*
|
||||
* #### Examples:
|
||||
*
|
||||
* ``` yaml
|
||||
* extensions:
|
||||
* enabled:
|
||||
* - Codeception\Extension\Recorder:
|
||||
* module: AngularJS # enable for Angular
|
||||
* delete_successful: false # keep screenshots of successful tests
|
||||
* ignore_steps: [have, grab*]
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
class Recorder extends \Codeception\Extension
|
||||
{
|
||||
protected $config = [
|
||||
'delete_successful' => true,
|
||||
'module' => 'WebDriver',
|
||||
'template' => null,
|
||||
'animate_slides' => true,
|
||||
'ignore_steps' => [],
|
||||
];
|
||||
|
||||
protected $template = <<<EOF
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Recorder Result</title>
|
||||
|
||||
<!-- Bootstrap Core CSS -->
|
||||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
.carousel,
|
||||
.item,
|
||||
.active {
|
||||
height: 100%;
|
||||
}
|
||||
.navbar {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
.carousel-caption {
|
||||
background: rgba(0,0,0,0.8);
|
||||
padding-bottom: 50px !important;
|
||||
}
|
||||
.carousel-caption.error {
|
||||
background: #c0392b !important;
|
||||
}
|
||||
|
||||
.carousel-inner {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.fill {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
overflow-y: scroll;
|
||||
background-position: top;
|
||||
-webkit-background-size: cover;
|
||||
-moz-background-size: cover;
|
||||
background-size: cover;
|
||||
-o-background-size: cover;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="#">{{feature}}
|
||||
<small>{{test}}</small>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<header id="steps" class="carousel{{carousel_class}}">
|
||||
<!-- Indicators -->
|
||||
<ol class="carousel-indicators">
|
||||
{{indicators}}
|
||||
</ol>
|
||||
|
||||
<!-- Wrapper for Slides -->
|
||||
<div class="carousel-inner">
|
||||
{{slides}}
|
||||
</div>
|
||||
|
||||
<!-- Controls -->
|
||||
<a class="left carousel-control" href="#steps" data-slide="prev">
|
||||
<span class="icon-prev"></span>
|
||||
</a>
|
||||
<a class="right carousel-control" href="#steps" data-slide="next">
|
||||
<span class="icon-next"></span>
|
||||
</a>
|
||||
|
||||
</header>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
|
||||
|
||||
<!-- Script to Activate the Carousel -->
|
||||
<script>
|
||||
$('.carousel').carousel({
|
||||
wrap: true,
|
||||
interval: false
|
||||
})
|
||||
|
||||
$(document).bind('keyup', function(e) {
|
||||
if(e.keyCode==39){
|
||||
jQuery('a.carousel-control.right').trigger('click');
|
||||
}
|
||||
|
||||
else if(e.keyCode==37){
|
||||
jQuery('a.carousel-control.left').trigger('click');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
EOF;
|
||||
|
||||
protected $indicatorTemplate = <<<EOF
|
||||
<li data-target="#steps" data-slide-to="{{step}}" {{isActive}}></li>
|
||||
EOF;
|
||||
|
||||
protected $indexTemplate = <<<EOF
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Recorder Results Index</title>
|
||||
|
||||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="#">Recorded Tests
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container">
|
||||
<h1>Record #{{seed}}</h1>
|
||||
<ul>
|
||||
{{records}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
EOF;
|
||||
|
||||
protected $slidesTemplate = <<<EOF
|
||||
<div class="item {{isActive}}">
|
||||
<div class="fill">
|
||||
<img src="{{image}}">
|
||||
</div>
|
||||
<div class="carousel-caption {{isError}}">
|
||||
<h2>{{caption}}</h2>
|
||||
<small>scroll up and down to see the full page</small>
|
||||
</div>
|
||||
</div>
|
||||
EOF;
|
||||
|
||||
public static $events = [
|
||||
Events::SUITE_BEFORE => 'beforeSuite',
|
||||
Events::SUITE_AFTER => 'afterSuite',
|
||||
Events::TEST_BEFORE => 'before',
|
||||
Events::TEST_ERROR => 'persist',
|
||||
Events::TEST_FAIL => 'persist',
|
||||
Events::TEST_SUCCESS => 'cleanup',
|
||||
Events::STEP_AFTER => 'afterStep',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var WebDriver
|
||||
*/
|
||||
protected $webDriverModule;
|
||||
protected $dir;
|
||||
protected $slides = [];
|
||||
protected $stepNum = 0;
|
||||
protected $seed;
|
||||
protected $recordedTests = [];
|
||||
protected $errors = [];
|
||||
protected $errorMessages = [];
|
||||
|
||||
public function beforeSuite()
|
||||
{
|
||||
$this->webDriverModule = null;
|
||||
if (!$this->hasModule($this->config['module'])) {
|
||||
$this->writeln("Recorder is disabled, no available modules");
|
||||
return;
|
||||
}
|
||||
$this->seed = uniqid();
|
||||
$this->webDriverModule = $this->getModule($this->config['module']);
|
||||
if (!$this->webDriverModule instanceof ScreenshotSaver) {
|
||||
throw new ExtensionException(
|
||||
$this,
|
||||
'You should pass module which implements Codeception\Lib\Interfaces\ScreenshotSaver interface'
|
||||
);
|
||||
}
|
||||
$this->writeln(sprintf(
|
||||
"⏺ <bold>Recording</bold> ⏺ step-by-step screenshots will be saved to <info>%s</info>",
|
||||
codecept_output_dir()
|
||||
));
|
||||
$this->writeln("Directory Format: <debug>record_{$this->seed}_{testname}</debug> ----");
|
||||
}
|
||||
|
||||
public function afterSuite()
|
||||
{
|
||||
if (!$this->webDriverModule or !$this->dir) {
|
||||
return;
|
||||
}
|
||||
$links = '';
|
||||
|
||||
if (count($this->slides)) {
|
||||
foreach ($this->recordedTests as $link => $url) {
|
||||
$links .= "<li><a href='$url'>$link</a></li>\n";
|
||||
}
|
||||
$indexHTML = (new Template($this->indexTemplate))
|
||||
->place('seed', $this->seed)
|
||||
->place('records', $links)
|
||||
->produce();
|
||||
|
||||
file_put_contents(codecept_output_dir() . 'records.html', $indexHTML);
|
||||
$this->writeln("⏺ Records saved into: <info>file://" . codecept_output_dir() . 'records.html</info>');
|
||||
}
|
||||
|
||||
foreach ($this->errors as $testPath => $screenshotPath) {
|
||||
while (count($this->errorMessages[$testPath])) {
|
||||
$this->writeln(array_pop($this->errorMessages[$testPath]));
|
||||
}
|
||||
|
||||
if ($screenshotPath !== null) {
|
||||
$this->writeln("⏺ Screenshot saved into: <info>file://{$screenshotPath}</info>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function before(TestEvent $e)
|
||||
{
|
||||
if (!$this->webDriverModule) {
|
||||
return;
|
||||
}
|
||||
$this->dir = null;
|
||||
$this->stepNum = 0;
|
||||
$this->slides = [];
|
||||
$this->errors = [];
|
||||
$this->errorMessages = [];
|
||||
|
||||
$testName = preg_replace('~\W~', '_', Descriptor::getTestAsString($e->getTest()));
|
||||
$this->dir = codecept_output_dir() . "record_{$this->seed}_$testName";
|
||||
@mkdir($this->dir);
|
||||
}
|
||||
|
||||
public function cleanup(TestEvent $e)
|
||||
{
|
||||
if (!$this->webDriverModule or !$this->dir) {
|
||||
return;
|
||||
}
|
||||
if (!$this->config['delete_successful']) {
|
||||
$this->persist($e);
|
||||
return;
|
||||
}
|
||||
|
||||
// deleting successfully executed tests
|
||||
FileSystem::deleteDir($this->dir);
|
||||
}
|
||||
|
||||
public function persist(TestEvent $e)
|
||||
{
|
||||
if (!$this->webDriverModule) {
|
||||
return;
|
||||
}
|
||||
$indicatorHtml = '';
|
||||
$slideHtml = '';
|
||||
|
||||
$testName = preg_replace('~\W~', '_', Descriptor::getTestAsString($e->getTest()));
|
||||
$testPath = codecept_relative_path(Descriptor::getTestFullName($e->getTest()));
|
||||
$dir = codecept_output_dir() . "record_{$this->seed}_$testName";
|
||||
|
||||
if ($this->dir !== $dir) {
|
||||
$screenshotPath = "{$dir}/error.png";
|
||||
@mkdir($dir);
|
||||
$this->errors = [];
|
||||
$this->recordedTests = [];
|
||||
$this->slides = [];
|
||||
$this->errorMessages[$testPath] = [
|
||||
"⏺ An error has occurred in <info>{$testName}</info> before any steps could've executed",
|
||||
];
|
||||
|
||||
try {
|
||||
$this->webDriverModule->webDriver->takeScreenshot($screenshotPath);
|
||||
$this->errors[$testPath] = $screenshotPath;
|
||||
} catch (\Exception $exception) {
|
||||
$this->errors[$testPath] = null;
|
||||
FileSystem::deleteDir($dir);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!array_key_exists($testPath, $this->errors)) {
|
||||
foreach ($this->slides as $i => $step) {
|
||||
$indicatorHtml .= (new Template($this->indicatorTemplate))
|
||||
->place('step', (int)$i)
|
||||
->place('isActive', (int)$i ? '' : 'class="active"')
|
||||
->produce();
|
||||
|
||||
$slideHtml .= (new Template($this->slidesTemplate))
|
||||
->place('image', $i)
|
||||
->place('caption', $step->getHtml('#3498db'))
|
||||
->place('isActive', (int)$i ? '' : 'active')
|
||||
->place('isError', $step->hasFailed() ? 'error' : '')
|
||||
->produce();
|
||||
}
|
||||
|
||||
$html = (new Template($this->template))
|
||||
->place('indicators', $indicatorHtml)
|
||||
->place('slides', $slideHtml)
|
||||
->place('feature', ucfirst($e->getTest()->getFeature()))
|
||||
->place('test', Descriptor::getTestSignature($e->getTest()))
|
||||
->place('carousel_class', $this->config['animate_slides'] ? ' slide' : '')
|
||||
->produce();
|
||||
|
||||
$indexFile = $this->dir . DIRECTORY_SEPARATOR . 'index.html';
|
||||
file_put_contents($indexFile, $html);
|
||||
$testName = Descriptor::getTestSignature($e->getTest()). ' - '.ucfirst($e->getTest()->getFeature());
|
||||
$this->recordedTests[$testName] = substr($indexFile, strlen(codecept_output_dir()));
|
||||
}
|
||||
}
|
||||
|
||||
public function afterStep(StepEvent $e)
|
||||
{
|
||||
if (!$this->webDriverModule or !$this->dir) {
|
||||
return;
|
||||
}
|
||||
if ($e->getStep() instanceof CommentStep) {
|
||||
return;
|
||||
}
|
||||
if ($this->isStepIgnored($e->getStep())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$filename = str_pad($this->stepNum, 3, "0", STR_PAD_LEFT) . '.png';
|
||||
|
||||
try {
|
||||
$this->webDriverModule->webDriver->takeScreenshot($this->dir . DIRECTORY_SEPARATOR . $filename);
|
||||
} catch (\Exception $exception) {
|
||||
$testPath = codecept_relative_path(Descriptor::getTestFullName($e->getTest()));
|
||||
$this->errors[$testPath] = null;
|
||||
|
||||
if (array_key_exists($testPath, $this->errorMessages)) {
|
||||
$this->errorMessages[$testPath] = array_merge(
|
||||
$this->errorMessages[$testPath],
|
||||
["⏺ Unable to capture a screenshot for <info>{$testPath}/{$e->getStep()->getAction()}</info>"]
|
||||
);
|
||||
} else {
|
||||
$this->errorMessages[$testPath] = [
|
||||
"⏺ Unable to capture a screenshot for <info>{$testPath}/{$e->getStep()->getAction()}</info>",
|
||||
];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->stepNum++;
|
||||
$this->slides[$filename] = $e->getStep();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Step $step
|
||||
* @return bool
|
||||
*/
|
||||
protected function isStepIgnored($step)
|
||||
{
|
||||
foreach ($this->config['ignore_steps'] as $stepPattern) {
|
||||
$stepRegexp = '/^' . str_replace('*', '.*?', $stepPattern) . '$/i';
|
||||
if (preg_match($stepRegexp, $step->getAction())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
145
vendor/codeception/base/ext/RunBefore.php
vendored
Normal file
145
vendor/codeception/base/ext/RunBefore.php
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace Codeception\Extension;
|
||||
|
||||
use Codeception\Events;
|
||||
use Codeception\Exception\ExtensionException;
|
||||
use Codeception\Extension;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* Extension for execution of some processes before running tests.
|
||||
*
|
||||
* Processes can be independent and dependent.
|
||||
* Independent processes run independently of each other.
|
||||
* Dependent processes run sequentially one by one.
|
||||
*
|
||||
* Can be configured in suite config:
|
||||
*
|
||||
* ```yaml
|
||||
* # acceptance.suite.yml
|
||||
* extensions:
|
||||
* enabled:
|
||||
* - Codeception\Extension\RunBefore:
|
||||
* - independent_process_1
|
||||
* -
|
||||
* - dependent_process_1_1
|
||||
* - dependent_process_1_2
|
||||
* - independent_process_2
|
||||
* -
|
||||
* - dependent_process_2_1
|
||||
* - dependent_process_2_2
|
||||
* ```
|
||||
*
|
||||
* HINT: you can use different configurations per environment.
|
||||
*/
|
||||
class RunBefore extends Extension
|
||||
{
|
||||
protected $config = [];
|
||||
|
||||
protected static $events = [
|
||||
Events::SUITE_BEFORE => 'runBefore'
|
||||
];
|
||||
|
||||
/** @var array[] */
|
||||
private $processes = [];
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
if (!class_exists('Symfony\Component\Process\Process')) {
|
||||
throw new ExtensionException($this, 'symfony/process package is required');
|
||||
}
|
||||
}
|
||||
|
||||
public function runBefore()
|
||||
{
|
||||
$this->runProcesses();
|
||||
$this->processMonitoring();
|
||||
}
|
||||
|
||||
private function runProcesses()
|
||||
{
|
||||
foreach ($this->config as $item) {
|
||||
if (is_array($item)) {
|
||||
$currentCommand = array_shift($item);
|
||||
$followingCommands = $item;
|
||||
} else {
|
||||
$currentCommand = $item;
|
||||
$followingCommands = [];
|
||||
}
|
||||
|
||||
$process = $this->runProcess($currentCommand);
|
||||
$this->addProcessToMonitoring($process, $followingCommands);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $command
|
||||
* @return Process
|
||||
*/
|
||||
private function runProcess($command)
|
||||
{
|
||||
$this->output->debug('[RunBefore] Starting ' . $command);
|
||||
$process = new Process($command, $this->getRootDir());
|
||||
$process->start();
|
||||
|
||||
return $process;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $followingCommands
|
||||
*/
|
||||
private function addProcessToMonitoring(Process $process, array $followingCommands)
|
||||
{
|
||||
$this->processes[] = [
|
||||
'instance' => $process,
|
||||
'following' => $followingCommands
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
*/
|
||||
private function removeProcessFromMonitoring($index)
|
||||
{
|
||||
unset($this->processes[$index]);
|
||||
}
|
||||
|
||||
private function processMonitoring()
|
||||
{
|
||||
while (count($this->processes) !== 0) {
|
||||
$this->checkProcesses();
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
private function checkProcesses()
|
||||
{
|
||||
foreach ($this->processes as $index => $process) {
|
||||
if (!$this->isRunning($process['instance'])) {
|
||||
$this->output->debug('[RunBefore] Completing ' . $process['instance']->getCommandLine());
|
||||
$this->runFollowingCommand($process['following']);
|
||||
$this->removeProcessFromMonitoring($index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $followingCommands
|
||||
*/
|
||||
private function runFollowingCommand(array $followingCommands)
|
||||
{
|
||||
if (count($followingCommands) > 0) {
|
||||
$process = $this->runProcess(array_shift($followingCommands));
|
||||
$this->addProcessToMonitoring($process, $followingCommands);
|
||||
}
|
||||
}
|
||||
|
||||
private function isRunning(Process $process)
|
||||
{
|
||||
if ($process->isRunning()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
80
vendor/codeception/base/ext/RunFailed.php
vendored
Normal file
80
vendor/codeception/base/ext/RunFailed.php
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
namespace Codeception\Extension;
|
||||
|
||||
use Codeception\Event\PrintResultEvent;
|
||||
use Codeception\Events;
|
||||
use Codeception\Extension;
|
||||
use Codeception\Test\Descriptor;
|
||||
|
||||
/**
|
||||
* Saves failed tests into tests/log/failed in order to rerun failed tests.
|
||||
*
|
||||
* To rerun failed tests just run the `failed` group:
|
||||
*
|
||||
* ```
|
||||
* php codecept run -g failed
|
||||
* ```
|
||||
*
|
||||
* To change failed group name add:
|
||||
* ```
|
||||
* --override "extensions: config: Codeception\Extension\RunFailed: fail-group: another_group1"
|
||||
* ```
|
||||
* Remember: if you run tests and they generated custom-named fail group, to run this group, you should add override too
|
||||
*
|
||||
* Starting from Codeception 2.1 **this extension is enabled by default**.
|
||||
*
|
||||
* ``` yaml
|
||||
* extensions:
|
||||
* enabled: [Codeception\Extension\RunFailed]
|
||||
* ```
|
||||
*
|
||||
* On each execution failed tests are logged and saved into `tests/_output/failed` file.
|
||||
*/
|
||||
class RunFailed extends Extension
|
||||
{
|
||||
public static $events = [
|
||||
Events::RESULT_PRINT_AFTER => 'saveFailed'
|
||||
];
|
||||
|
||||
/** @var string filename/groupname for failed tests */
|
||||
protected $group = 'failed';
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
if (array_key_exists('fail-group', $this->config) && $this->config['fail-group']) {
|
||||
$this->group = $this->config['fail-group'];
|
||||
}
|
||||
$logPath = str_replace($this->getRootDir(), '', $this->getLogDir()); // get local path to logs
|
||||
$this->_reconfigure(['groups' => [$this->group => $logPath . $this->group]]);
|
||||
}
|
||||
|
||||
public function saveFailed(PrintResultEvent $e)
|
||||
{
|
||||
$file = $this->getLogDir() . $this->group;
|
||||
$result = $e->getResult();
|
||||
if ($result->wasSuccessful()) {
|
||||
if (is_file($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
return;
|
||||
}
|
||||
$output = [];
|
||||
foreach ($result->failures() as $fail) {
|
||||
$output[] = $this->localizePath(Descriptor::getTestFullName($fail->failedTest()));
|
||||
}
|
||||
foreach ($result->errors() as $fail) {
|
||||
$output[] = $this->localizePath(Descriptor::getTestFullName($fail->failedTest()));
|
||||
}
|
||||
|
||||
file_put_contents($file, implode("\n", $output));
|
||||
}
|
||||
|
||||
protected function localizePath($path)
|
||||
{
|
||||
$root = realpath($this->getRootDir()) . DIRECTORY_SEPARATOR;
|
||||
if (substr($path, 0, strlen($root)) == $root) {
|
||||
return substr($path, strlen($root));
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
105
vendor/codeception/base/ext/RunProcess.php
vendored
Normal file
105
vendor/codeception/base/ext/RunProcess.php
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Codeception\Extension;
|
||||
|
||||
use Codeception\Events;
|
||||
use Codeception\Exception\ExtensionException;
|
||||
use Codeception\Extension;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* Extension to start and stop processes per suite.
|
||||
* Can be used to start/stop selenium server, chromedriver, phantomjs, mailcatcher, etc.
|
||||
*
|
||||
* Can be configured in suite config:
|
||||
*
|
||||
* ```yaml
|
||||
* # acceptance.suite.yml
|
||||
* extensions:
|
||||
* enabled:
|
||||
* - Codeception\Extension\RunProcess:
|
||||
* - chromedriver
|
||||
* ```
|
||||
*
|
||||
* Multiple parameters can be passed as array:
|
||||
*
|
||||
* ```yaml
|
||||
* # acceptance.suite.yml
|
||||
*
|
||||
* extensions:
|
||||
* enabled:
|
||||
* - Codeception\Extension\RunProcess:
|
||||
* - php -S 127.0.0.1:8000 -t tests/data/app
|
||||
* - java -jar ~/selenium-server.jar
|
||||
* ```
|
||||
*
|
||||
* In the end of a suite all launched processes will be stopped.
|
||||
*
|
||||
* To wait for the process to be launched use `sleep` option.
|
||||
* In this case you need configuration to be specified as object:
|
||||
*
|
||||
* ```yaml
|
||||
* extensions:
|
||||
* enabled:
|
||||
* - Codeception\Extension\RunProcess:
|
||||
* 0: java -jar ~/selenium-server.jar
|
||||
* 1: mailcatcher
|
||||
* sleep: 5 # wait 5 seconds for processes to boot
|
||||
* ```
|
||||
*
|
||||
* HINT: you can use different configurations per environment.
|
||||
*/
|
||||
class RunProcess extends Extension
|
||||
{
|
||||
public $config = ['sleep' => 0];
|
||||
|
||||
static $events = [
|
||||
Events::SUITE_BEFORE => 'runProcess',
|
||||
Events::SUITE_AFTER => 'stopProcess'
|
||||
];
|
||||
|
||||
protected $processes = [];
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
if (!class_exists('Symfony\Component\Process\Process')) {
|
||||
throw new ExtensionException($this, 'symfony/process package is required');
|
||||
}
|
||||
}
|
||||
|
||||
public function runProcess()
|
||||
{
|
||||
$this->processes = [];
|
||||
foreach ($this->config as $key => $command) {
|
||||
if (!$command) {
|
||||
continue;
|
||||
}
|
||||
if (!is_int($key)) {
|
||||
continue; // configuration options
|
||||
}
|
||||
$process = new Process($command, $this->getRootDir(), null, null, null);
|
||||
$process->start();
|
||||
$this->processes[] = $process;
|
||||
$this->output->debug('[RunProcess] Starting '.$command);
|
||||
}
|
||||
sleep($this->config['sleep']);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->stopProcess();
|
||||
}
|
||||
|
||||
public function stopProcess()
|
||||
{
|
||||
foreach (array_reverse($this->processes) as $process) {
|
||||
/** @var $process Process **/
|
||||
if (!$process->isRunning()) {
|
||||
continue;
|
||||
}
|
||||
$this->output->debug('[RunProcess] Stopping ' . $process->getCommandLine());
|
||||
$process->stop();
|
||||
}
|
||||
$this->processes = [];
|
||||
}
|
||||
}
|
||||
61
vendor/codeception/base/ext/SimpleReporter.php
vendored
Normal file
61
vendor/codeception/base/ext/SimpleReporter.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace Codeception\Extension;
|
||||
|
||||
use Codeception\Event\TestEvent;
|
||||
use Codeception\Events;
|
||||
use Codeception\Extension;
|
||||
use Codeception\Test\Descriptor;
|
||||
|
||||
/**
|
||||
* This extension demonstrates how you can implement console output of your own.
|
||||
* Recommended to be used for development purposes only.
|
||||
*/
|
||||
class SimpleReporter extends Extension
|
||||
{
|
||||
public function _initialize()
|
||||
{
|
||||
$this->options['silent'] = false; // turn on printing for this extension
|
||||
$this->_reconfigure(['settings' => ['silent' => true]]); // turn off printing for everything else
|
||||
}
|
||||
|
||||
// we are listening for events
|
||||
public static $events = [
|
||||
Events::SUITE_BEFORE => 'beforeSuite',
|
||||
Events::TEST_END => 'after',
|
||||
Events::TEST_SUCCESS => 'success',
|
||||
Events::TEST_FAIL => 'fail',
|
||||
Events::TEST_ERROR => 'error',
|
||||
];
|
||||
|
||||
public function beforeSuite()
|
||||
{
|
||||
$this->writeln("");
|
||||
}
|
||||
|
||||
public function success()
|
||||
{
|
||||
$this->write('[+] ');
|
||||
}
|
||||
|
||||
public function fail()
|
||||
{
|
||||
$this->write('[-] ');
|
||||
}
|
||||
|
||||
public function error()
|
||||
{
|
||||
$this->write('[E] ');
|
||||
}
|
||||
|
||||
// we are printing test status and time taken
|
||||
public function after(TestEvent $e)
|
||||
{
|
||||
$seconds_input = $e->getTime();
|
||||
// stack overflow: http://stackoverflow.com/questions/16825240/how-to-convert-microtime-to-hhmmssuu
|
||||
$seconds = (int)($milliseconds = (int)($seconds_input * 1000)) / 1000;
|
||||
$time = ($seconds % 60) . (($milliseconds === 0) ? '' : '.' . $milliseconds);
|
||||
|
||||
$this->write(Descriptor::getTestSignature($e->getTest()));
|
||||
$this->writeln(' (' . $time . 's)');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user