![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/vendor/codeception/codeception/src/Codeception/Reporter/ |
<?php namespace Codeception\Reporter; use Codeception\Event\FailEvent; use Codeception\Event\PrintResultEvent; use Codeception\Event\SuiteEvent; use Codeception\Event\TestEvent; use Codeception\Events; use Codeception\Lib\Console\Output; use Codeception\Step; use Codeception\Step\Meta; use Codeception\Subscriber\Shared\StaticEventsTrait; use Codeception\Test\Descriptor; use Codeception\Test\Interfaces\ScenarioDriven; use Codeception\Test\Test; use Codeception\TestInterface; use Codeception\Util\PathResolver; use SebastianBergmann\Template\Template; use SebastianBergmann\Timer\Timer; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function trigger_error; class HtmlReporter implements EventSubscriberInterface { use StaticEventsTrait; /** * @var array<string, string> */ protected static array $events = [ Events::SUITE_BEFORE => 'beforeSuite', Events::RESULT_PRINT_AFTER => 'afterResult', Events::TEST_SUCCESS => 'testSuccess', Events::TEST_FAIL => 'testFailure', Events::TEST_ERROR => 'testError', Events::TEST_INCOMPLETE => 'testIncomplete', Events::TEST_SKIPPED => 'testSkipped', Events::TEST_USELESS => 'testUseless', Events::TEST_WARNING => 'testWarning', ]; protected int $id = 0; protected string $scenarios = ''; protected string $templatePath; private string $reportFile; private Timer $timer; public function __construct(array $options, private Output $output) { $this->reportFile = $options['html']; if (!codecept_is_path_absolute($this->reportFile)) { $this->reportFile = codecept_output_dir($this->reportFile); } codecept_debug(sprintf("Printing HTML report to %s", $this->reportFile)); $this->templatePath = sprintf( '%s%stemplate%s', __DIR__, DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR ); $this->timer = new Timer(); $this->timer->start(); } public function beforeSuite(SuiteEvent $event): void { $suite = $event->getSuite(); if (!$suite->getName()) { return; } $suiteTemplate = new Template( $this->templatePath . 'suite.html' ); $suiteTemplate->setVar(['suite' => ucfirst($suite->getName())]); $this->scenarios .= $suiteTemplate->render(); } public function testSuccess(TestEvent $event): void { $this->printTestEvent($event, 'scenarioSuccess'); } public function testError(FailEvent $event): void { $this->printTestEvent($event, 'scenarioFailed'); } public function testFailure(FailEvent $event): void { $this->printTestEvent($event, 'scenarioFailed'); } public function testWarning(FailEvent $event): void { $this->printTestEvent($event, 'scenarioSuccess'); } public function testSkipped(FailEvent $event): void { $this->printTestEvent($event, 'scenarioSkipped'); } public function testIncomplete(FailEvent $event): void { $this->printTestEvent($event, 'scenarioIncomplete'); } public function testUseless(FailEvent $event): void { $this->printTestEvent($event, 'scenarioUseless'); } public function printTestEvent(TestEvent $event, string $scenarioStatus): void { $failure = ''; $test = $event->getTest(); if ($event instanceof FailEvent) { $failTemplate = new Template( $this->templatePath . 'fail.html' ); $failTemplate->setVar(['fail' => nl2br($event->getFail()->getMessage())]); $failure = $failTemplate->render() . PHP_EOL; } $steps = []; if ($test instanceof ScenarioDriven) { $steps = $test->getScenario()->getSteps(); } $stepsBuffer = ''; $subStepsRendered = []; foreach ($steps as $step) { $metaStep = $step->getMetaStep(); if ($metaStep) { $key = $this->getMetaStepKey($metaStep); $subStepsRendered[$key][] = $this->renderStep($step); } } foreach ($steps as $step) { $metaStep = $step->getMetaStep(); if ($metaStep) { $key = $this->getMetaStepKey($metaStep); if (! empty($subStepsRendered[$key])) { $subStepsBuffer = implode('', $subStepsRendered[$key]); unset($subStepsRendered[$key]); $stepsBuffer .= $this->renderSubsteps($step->getMetaStep(), $subStepsBuffer); } } else { $stepsBuffer .= $this->renderStep($step); } } $png = ''; $html = ''; if ($test instanceof TestInterface) { $reports = $test->getMetadata()->getReports(); if (isset($reports['png'])) { $localPath = PathResolver::getRelativeDir($reports['png'], codecept_output_dir()); $png = "<tr><td class='error'><div class='screenshot'><img src='$localPath' alt='failure screenshot'></div></td></tr>"; } if (isset($reports['html'])) { $localPath = PathResolver::getRelativeDir($reports['html'], codecept_output_dir()); $html = "<tr><td class='error'>See <a href='$localPath' target='_blank'>HTML snapshot</a> of a failed page</td></tr>"; } } $toggle = $stepsBuffer ? '<span class="toggle">+</span>' : ''; $testString = htmlspecialchars(ucfirst(Descriptor::getTestAsString($test)), ENT_QUOTES | ENT_SUBSTITUTE); $testString = preg_replace('~^([\s\w\\\]+):\s~', '<span class="quiet">$1 »</span> ', $testString); $scenarioTemplate = new Template( $this->templatePath . 'scenario.html' ); $scenarioTemplate->setVar( [ 'id' => ++$this->id, 'name' => $testString, 'scenarioStatus' => $scenarioStatus, 'steps' => $stepsBuffer, 'toggle' => $toggle, 'failure' => $failure, 'png' => $png, 'html' => $html, 'time' => round($event->getTime(), 2), ] ); $this->scenarios .= $scenarioTemplate->render(); } public function printTestResult(Test $test, float $time, string $scenarioStatus): void { //keep this method for backwards compatibility, remove in Codeception 6.0 trigger_error( __METHOD__ . ' is deprecated, please use printTestEvent instead', E_USER_DEPRECATED, ); $steps = []; if ($test instanceof ScenarioDriven) { $steps = $test->getScenario()->getSteps(); } $stepsBuffer = ''; $subStepsRendered = []; foreach ($steps as $step) { $metaStep = $step->getMetaStep(); if ($metaStep) { $key = $this->getMetaStepKey($metaStep); $subStepsRendered[$key][] = $this->renderStep($step); } } foreach ($steps as $step) { $metaStep = $step->getMetaStep(); if ($metaStep) { $key = $this->getMetaStepKey($metaStep); if (! empty($subStepsRendered[$key])) { $subStepsBuffer = implode('', $subStepsRendered[$key]); unset($subStepsRendered[$key]); $stepsBuffer .= $this->renderSubsteps($step->getMetaStep(), $subStepsBuffer); } } else { $stepsBuffer .= $this->renderStep($step); } } $scenarioTemplate = new Template( $this->templatePath . 'scenario.html' ); $png = ''; $html = ''; if ($test instanceof TestInterface) { $reports = $test->getMetadata()->getReports(); if (isset($reports['png'])) { $localPath = PathResolver::getRelativeDir($reports['png'], codecept_output_dir()); $png = "<tr><td class='error'><div class='screenshot'><img src='$localPath' alt='failure screenshot'></div></td></tr>"; } if (isset($reports['html'])) { $localPath = PathResolver::getRelativeDir($reports['html'], codecept_output_dir()); $html = "<tr><td class='error'>See <a href='$localPath' target='_blank'>HTML snapshot</a> of a failed page</td></tr>"; } } $toggle = $stepsBuffer ? '<span class="toggle">+</span>' : ''; $testString = htmlspecialchars(ucfirst(Descriptor::getTestAsString($test)), ENT_QUOTES | ENT_SUBSTITUTE); $testString = preg_replace('~^([\s\w\\\]+):\s~', '<span class="quiet">$1 »</span> ', $testString); $scenarioTemplate->setVar( [ 'id' => ++$this->id, 'name' => $testString, 'scenarioStatus' => $scenarioStatus, 'steps' => $stepsBuffer, 'toggle' => $toggle, 'failure' => '', 'png' => $png, 'html' => $html, 'time' => round($time, 2) ] ); $this->scenarios .= $scenarioTemplate->render(); } private function getMetaStepKey(Meta $metaStep): string { $key = ''; $filePath = $metaStep->getFilePath(); if ($filePath !== null) { $key = $filePath; $lineNumber = $metaStep->getLineNumber(); if ($lineNumber !== null) { $key .= ':' . $lineNumber; } } return $key . $metaStep->getAction(); } protected function renderStep(Step $step): string { $stepTemplate = new Template($this->templatePath . 'step.html'); $stepTemplate->setVar(['action' => $step->getHtml(), 'error' => $step->hasFailed() ? 'failedStep' : '']); return $stepTemplate->render(); } protected function renderSubsteps(Meta $metaStep, string $substepsBuffer): string { $metaTemplate = new Template($this->templatePath . 'substeps.html'); $metaTemplate->setVar(['metaStep' => $metaStep->getHtml(), 'error' => $metaStep->hasFailed() ? 'failedStep' : '', 'steps' => $substepsBuffer, 'id' => uniqid()]); return $metaTemplate->render(); } public function afterResult(PrintResultEvent $event): void { $timeTaken = $this->timer->stop()->asString(); $result = $event->getResult(); $scenarioHeaderTemplate = new Template( $this->templatePath . 'scenario_header.html' ); $status = $result->wasSuccessfulIgnoringWarnings() ? '<span style="color: green">OK</span>' : '<span style="color: #e74c3c">FAILED</span>'; $scenarioHeaderTemplate->setVar( [ 'name' => 'Codeception Results', 'status' => $status, 'time' => $timeTaken ] ); $header = $scenarioHeaderTemplate->render(); $scenariosTemplate = new Template( $this->templatePath . 'scenarios.html' ); $scenariosTemplate->setVar( [ 'header' => $header, 'scenarios' => $this->scenarios, 'successfulScenarios' => $result->successfulCount(), 'failedScenarios' => $result->failureCount(), 'skippedScenarios' => $result->skippedCount(), 'incompleteScenarios' => $result->incompleteCount(), 'uselessScenarios' => $result->uselessCount(), ] ); file_put_contents($this->reportFile, $scenariosTemplate->render()); $this->output->message( "- <bold>HTML</bold> report generated in <comment>file://%s</comment>", $this->reportFile )->writeln(); } }