Recorder
extends Extension
in package
Saves a screenshot of each step in acceptance tests and shows them as a slideshow on one HTML page (here's an [example](https://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
:
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. SetAngularJS
if you want to use it with AngularJS module. Generally, the module should implementCodeception\Lib\Interfaces\ScreenshotSaver
interface. -
ignore_steps
(default: []) - array of step names that should not be recorded (given the step passed), * wildcards supported. Meta steps can also be ignored. -
success_color
(default: success) - bootstrap values to be used for color representation for passed tests -
failure_color
(default: danger) - bootstrap values to be used for color representation for failed tests -
error_color
(default: dark) - bootstrap values to be used for color representation for scenarios where there's an issue occurred while generating a recording -
delete_orphaned
(default: false) - delete recording folders created via previous runs -
include_microseconds
(default: false) - enable microsecond precision for recorded step time details
Examples:
extensions:
enabled:
- Codeception\Extension\Recorder:
module: AngularJS # enable for Angular
delete_successful: false # keep screenshots of successful tests
ignore_steps: [have, grab*]
Skipping recording of steps with annotations
It is also possible to skip recording of steps for specified tests by using the @skipRecording annotation.
/**
* @skipRecording login
* @skipRecording amOnUrl
*\/
public function testLogin(AcceptanceTester $I)
{
$I->login();
$I->amOnUrl('https://codeception.com');
}
Table of Contents
Properties
- $events : array<string|int, mixed>
- $ansi : bool
- $colors : bool
- $config : array<string|int, mixed>
- $dir : string
- $errorMessages : array<string|int, mixed>
- $globalConfig : mixed
- $indexTemplate : string
- $indicatorTemplate : string
- $options : mixed
- $output : mixed
- $recordedTests : array<string|int, mixed>
- $seed : string
- $seeds : array<string|int, mixed>
- $skipRecording : array<string|int, mixed>
- $slides : array<string|int, mixed>
- $slidesTemplate : string
- $stepNum : int
- $template : string
- $timeStamps : array<string|int, mixed>
- $webDriverModule : WebDriver
- $dateFormat : string
- $modules : mixed
Methods
- __construct() : mixed
- _initialize() : mixed
- You can do all preparations here. No need to override constructor.
- _reconfigure() : mixed
- Pass config variables that should be injected into global config.
- afterStep() : mixed
- afterSuite() : mixed
- before() : mixed
- beforeSuite() : mixed
- cleanup() : mixed
- getCurrentModuleNames() : mixed
- getDataDir() : mixed
- getGlobalConfig() : mixed
- getLogDir() : mixed
- getModule() : mixed
- getRootDir() : mixed
- getSubscribedEvents() : array<string, string|array{0: string, 1: int}|array<int, array{0: string, 1?: int}>>
- Returns an array of event names this subscriber wants to listen to.
- getTestsDir() : mixed
- hasModule() : mixed
- persist() : mixed
- receiveModuleContainer() : mixed
- isStepIgnored() : bool
- write() : mixed
- writeln() : mixed
- appendErrorMessage() : mixed
- getTestName() : string
Properties
$events
public
static array<string|int, mixed>
$events
= [\Codeception\Events::SUITE_BEFORE => 'beforeSuite', \Codeception\Events::SUITE_AFTER => 'afterSuite', \Codeception\Events::TEST_BEFORE => 'before', \Codeception\Events::TEST_ERROR => 'persist', \Codeception\Events::TEST_FAIL => 'persist', \Codeception\Events::TEST_SUCCESS => 'cleanup', \Codeception\Events::STEP_AFTER => 'afterStep']
$ansi
protected
bool
$ansi
$colors
protected
bool
$colors
$config
protected
array<string|int, mixed>
$config
= ['delete_successful' => true, 'module' => 'WebDriver', 'template' => null, 'animate_slides' => true, 'ignore_steps' => [], 'success_color' => 'success', 'failure_color' => 'danger', 'error_color' => 'dark', 'delete_orphaned' => false, 'include_microseconds' => false]
$dir
protected
string
$dir
$errorMessages
protected
array<string|int, mixed>
$errorMessages
= []
$globalConfig
protected
mixed
$globalConfig
$indexTemplate
protected
string
$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/4.1.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="navbar-header">
<a class="navbar-brand" href="#">Recorded Tests
</a>
</div>
</nav>
<div class="container py-4">
<h1>Record #{{seed}}</h1>
<ul>
{{records}}
</ul>
</div>
</body>
</html>
EOF
$indicatorTemplate
protected
string
$indicatorTemplate
= <<<EOF
<li data-target="#steps" data-slide-to="{{step}}" class="{{isActive}}"></li>
EOF
$options
protected
mixed
$options
$output
protected
mixed
$output
$recordedTests
protected
array<string|int, mixed>
$recordedTests
= []
$seed
protected
string
$seed
$seeds
protected
array<string|int, mixed>
$seeds
$skipRecording
protected
array<string|int, mixed>
$skipRecording
= []
$slides
protected
array<string|int, mixed>
$slides
= []
$slidesTemplate
protected
string
$slidesTemplate
= <<<EOF
<div class="carousel-item {{isActive}}">
<img class="mx-auto d-block mh-100" src="{{image}}">
<div class="carousel-caption {{isError}}">
<h5>{{caption}}</h5>
<p>Step finished at <span style="color: #3498db">"{{timeStamp}}"</span></p>
</div>
</div>
EOF
$stepNum
protected
int
$stepNum
= 0
$template
protected
string
$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/4.1.3/css/bootstrap.min.css" rel="stylesheet">
<style>
html,
body {
height: 100%;
}
.active {
height: 100%;
}
.carousel-caption {
background: rgba(0,0,0,0.8);
}
.carousel-caption.error {
background: #c0392b !important;
}
.carousel-item {
min-height: 100vh;
}
.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;
}
.gradient-right {
background:
linear-gradient(to left, rgba(0,0,0,.4), rgba(0,0,0,.0))
}
.gradient-left {
background:
linear-gradient(to right, rgba(0,0,0,.4), rgba(0,0,0,.0))
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="navbar-header">
<a class="navbar-brand" href="../records.html"></span>Recorded Tests</a>
</div>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav mr-auto">
<span class="navbar-text">{{feature}}</span>
</ul>
<span class="navbar-text">{{test}}</span>
</div>
</nav>
<header id="steps" class="carousel slide" data-ride="carousel">
<!-- Indicators -->
<ol class="carousel-indicators">
{{indicators}}
</ol>
<!-- Wrapper for Slides -->
<div class="carousel-inner">
{{slides}}
</div>
<!-- Controls -->
<a class="carousel-control-prev gradient-left" href="#steps" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="false"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next gradient-right" href="#steps" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="false"></span>
<span class="sr-only">Next</span>
</a>
</header>
<!-- jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/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
$timeStamps
protected
array<string|int, mixed>
$timeStamps
= []
$webDriverModule
protected
WebDriver
$webDriverModule
$dateFormat
private
string
$dateFormat
$modules
private
mixed
$modules
= []
Methods
__construct()
public
__construct(mixed $config, mixed $options) : mixed
Parameters
- $config : mixed
- $options : mixed
_initialize()
You can do all preparations here. No need to override constructor.
public
_initialize() : mixed
Also you can skip calling _reconfigure
if you don't need to.
_reconfigure()
Pass config variables that should be injected into global config.
public
_reconfigure([array<string|int, mixed> $config = [] ]) : mixed
Parameters
- $config : array<string|int, mixed> = []
afterStep()
public
afterStep(StepEvent $e) : mixed
Parameters
- $e : StepEvent
afterSuite()
public
afterSuite() : mixed
before()
public
before(TestEvent $e) : mixed
Parameters
- $e : TestEvent
beforeSuite()
public
beforeSuite() : mixed
cleanup()
public
cleanup(TestEvent $e) : mixed
Parameters
- $e : TestEvent
getCurrentModuleNames()
public
getCurrentModuleNames() : mixed
getDataDir()
public
getDataDir() : mixed
getGlobalConfig()
public
getGlobalConfig() : mixed
getLogDir()
public
getLogDir() : mixed
getModule()
public
getModule(mixed $name) : mixed
Parameters
- $name : mixed
getRootDir()
public
getRootDir() : mixed
getSubscribedEvents()
Returns an array of event names this subscriber wants to listen to.
public
static getSubscribedEvents() : array<string, string|array{0: string, 1: int}|array<int, array{0: string, 1?: int}>>
The array keys are event names and the value can be:
- The method name to call (priority defaults to 0)
- An array composed of the method name to call and the priority
- An array of arrays composed of the method names to call and respective priorities, or 0 if unset
For instance:
- ['eventName' => 'methodName']
- ['eventName' => ['methodName', $priority]]
- ['eventName' => [['methodName1', $priority], ['methodName2']]]
The code must not depend on runtime state as it will only be called at compile time. All logic depending on runtime state must be put into the individual methods handling the events.
Return values
array<string, string|array{0: string, 1: int}|array<int, array{0: string, 1?: int}>>getTestsDir()
public
getTestsDir() : mixed
hasModule()
public
hasModule(mixed $name) : mixed
Parameters
- $name : mixed
persist()
public
persist(TestEvent $e) : mixed
Parameters
- $e : TestEvent
receiveModuleContainer()
public
receiveModuleContainer(SuiteEvent $e) : mixed
Parameters
- $e : SuiteEvent
isStepIgnored()
protected
isStepIgnored(StepEvent $e) : bool
Parameters
- $e : StepEvent
Return values
boolwrite()
protected
write(mixed $message) : mixed
Parameters
- $message : mixed
writeln()
protected
writeln(string $message) : mixed
Parameters
- $message : string
appendErrorMessage()
private
appendErrorMessage(string $testPath, string $message) : mixed
Parameters
- $testPath : string
- $message : string
getTestName()
private
getTestName(StepEvent|TestEvent $e) : string