true, 'module' => 'WebDriver', 'template' => null, 'animate_slides' => true ]; protected $template = << Recorder Result
EOF; protected $indicatorTemplate = << EOF; protected $indexTemplate = << Recorder Results Index

Record #{{seed}}

    {{records}}
EOF; protected $slidesTemplate = <<
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 = []; 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( "⏺ Recording ⏺ step-by-step screenshots will be saved to %s", codecept_output_dir() )); $this->writeln("Directory Format: record_{$this->seed}_{testname} ----"); } public function afterSuite() { if (!$this->webDriverModule or !$this->dir) { return; } $links = ''; foreach ($this->recordedTests as $link => $url) { $links .= "
  • $link
  • \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: file://" . codecept_output_dir().'records.html'); } public function before(TestEvent $e) { if (!$this->webDriverModule) { return; } $this->dir = null; $this->stepNum = 0; $this->slides = []; $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 or !$this->dir) { return; } $indicatorHtml = ''; $slideHtml = ''; 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; } $filename = str_pad($this->stepNum, 3, "0", STR_PAD_LEFT) . '.png'; $this->webDriverModule->_saveScreenshot($this->dir . DIRECTORY_SEPARATOR . $filename); $this->stepNum++; $this->slides[$filename] = $e->getStep(); } }