diff --git a/src/Codeception/Reporter/HtmlReporter.php b/src/Codeception/Reporter/HtmlReporter.php
index 131a369eba..3b7da22a29 100644
--- a/src/Codeception/Reporter/HtmlReporter.php
+++ b/src/Codeception/Reporter/HtmlReporter.php
@@ -19,6 +19,7 @@
use SebastianBergmann\Template\Template;
use SebastianBergmann\Timer\Timer;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use function trigger_error;
class HtmlReporter implements EventSubscriberInterface
{
@@ -45,8 +46,6 @@ class HtmlReporter implements EventSubscriberInterface
protected string $templatePath;
- protected array $failures = [];
-
private string $reportFile;
private Timer $timer;
@@ -88,41 +87,52 @@ public function beforeSuite(SuiteEvent $event): void
public function testSuccess(TestEvent $event): void
{
- $this->printTestResult($event->getTest(), $event->getTime(), 'scenarioSuccess');
+ $this->printTestEvent($event, 'scenarioSuccess');
}
public function testError(FailEvent $event): void
{
- $this->printTestResult($event->getTest(), $event->getTime(), 'scenarioFailed');
+ $this->printTestEvent($event, 'scenarioFailed');
}
public function testFailure(FailEvent $event): void
{
- $this->printTestResult($event->getTest(), $event->getTime(), 'scenarioFailed');
+ $this->printTestEvent($event, 'scenarioFailed');
}
public function testWarning(FailEvent $event): void
{
- $this->printTestResult($event->getTest(), $event->getTime(), 'scenarioSuccess');
+ $this->printTestEvent($event, 'scenarioSuccess');
}
public function testSkipped(FailEvent $event): void
{
- $this->printTestResult($event->getTest(), $event->getTime(), 'scenarioSkipped');
+ $this->printTestEvent($event, 'scenarioSkipped');
}
public function testIncomplete(FailEvent $event): void
{
- $this->printTestResult($event->getTest(), $event->getTime(), 'scenarioIncomplete');
+ $this->printTestEvent($event, 'scenarioIncomplete');
}
public function testUseless(FailEvent $event): void
{
- $this->printTestResult($event->getTest(), $event->getTime(), 'scenarioUseless');
+ $this->printTestEvent($event, 'scenarioUseless');
}
- public function printTestResult(Test $test, float $time, string $scenarioStatus): void
+ 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) {
@@ -154,23 +164,88 @@ public function printTestResult(Test $test, float $time, string $scenarioStatus)
}
}
+ $png = '';
+ $html = '';
+ if ($test instanceof TestInterface) {
+ $reports = $test->getMetadata()->getReports();
+ if (isset($reports['png'])) {
+ $localPath = PathResolver::getRelativeDir($reports['png'], codecept_output_dir());
+ $png = "
|
";
+ }
+ if (isset($reports['html'])) {
+ $localPath = PathResolver::getRelativeDir($reports['html'], codecept_output_dir());
+ $html = "See HTML snapshot of a failed page |
";
+ }
+ }
+
+ $toggle = $stepsBuffer ? '+' : '';
+
+ $testString = htmlspecialchars(ucfirst(Descriptor::getTestAsString($test)), ENT_QUOTES | ENT_SUBSTITUTE);
+ $testString = preg_replace('~^([\s\w\\\]+):\s~', '$1 » ', $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),
+ ]
+ );
- $failures = '';
- $name = Descriptor::getTestSignatureUnique($test);
- if (isset($this->failures[$name])) {
- $failTemplate = new Template(
- $this->templatePath . 'fail.html'
- );
- foreach ($this->failures[$name] as $failure) {
- $failTemplate->setVar(['fail' => nl2br($failure)]);
- $failures .= $failTemplate->render() . PHP_EOL;
+ $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);
}
- $this->failures[$name] = [];
}
+ $scenarioTemplate = new Template(
+ $this->templatePath . 'scenario.html'
+ );
+
$png = '';
$html = '';
if ($test instanceof TestInterface) {
@@ -197,7 +272,7 @@ public function printTestResult(Test $test, float $time, string $scenarioStatus)
'scenarioStatus' => $scenarioStatus,
'steps' => $stepsBuffer,
'toggle' => $toggle,
- 'failure' => $failures,
+ 'failure' => '',
'png' => $png,
'html' => $html,
'time' => round($time, 2)
diff --git a/tests/cli/RunCest.php b/tests/cli/RunCest.php
index 31e40248b1..0466efc4bd 100755
--- a/tests/cli/RunCest.php
+++ b/tests/cli/RunCest.php
@@ -779,6 +779,7 @@ public function runHtmlWithPhpBrowserCheckReport(CliGuy $I)
$I->seeInShellOutput('Response: ' . $expectedReportAbsFilename);
$I->seeFileFound('report.html', $expectedRelReportPath);
$I->seeInThisFile("See HTML snapshot of a failed page");
+ $I->seeInThisFile('Failed asserting that on page /');
}
private function htmlReportRegexCheckProvider(): array