From 554ec2d089933b2a6a308ffda23b10c02d9d8f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Andr=C3=A9=20Fiskvik?= Date: Fri, 19 Jun 2020 00:19:13 +0200 Subject: [PATCH] feat: add configurable support of outputing a reportfile per suite (#7) contributors: @grEvenX --- .travis.yml | 4 +- README.md | 8 +- features/bootstrap/FeatureContext.php | 142 ++++++++++++++++++----- features/enable-output-per-suite.feature | 49 ++++++++ src/Extension.php | 4 +- src/Formatter/Formatter.php | 26 +++-- src/Node/Feature.php | 8 -- src/Node/Suite.php | 8 ++ src/Renderer/JsonRenderer.php | 21 +++- src/Renderer/RendererInterface.php | 9 ++ 10 files changed, 230 insertions(+), 49 deletions(-) create mode 100644 features/enable-output-per-suite.feature diff --git a/.travis.yml b/.travis.yml index d28f8ab..2cc94b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,6 @@ php: - 7.0 - 7.1 -before_script: composer install --prefer-source --no-interaction +before_script: COMPOSER_MEMORY_LIMIT=-1 composer install --prefer-source --no-interaction -script: composer run-script test \ No newline at end of file +script: composer run-script test diff --git a/README.md b/README.md index cb128a4..3c776f8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Behat Cucumber Json Formatter +# Behat Cucumber Json Formatter *Note*: this is a fork of [Vanare/behat-cucumber-formatter](https://github.com/Vanare/behat-cucumber-formatter). As the original project seems unmaintained and there is no possibility to contact the owner, I publish the library under my handle. Many thanks to the original team of Vanare for starting this great library! @@ -27,6 +27,7 @@ default: extensions: Vanare\BehatCucumberJsonFormatter\Extension: fileNamePrefix: report + resultFilePerSuite: true outputDir: %paths.base%/build/tests ``` @@ -41,7 +42,10 @@ bin/behat -f cucumber_json - `fileNamePrefix`: Filename prefix of generated report - `outputDir`: Generated report will be placed in this directory - `fileName` _(optional)_: Filename of generated report - current feature name will be used by default. +Only applicable when `resultFilePerSuite` is not enabled. +- `resultFilePerSuite` _(optional)_: The default behaviour is to generate a single report named `all.json`. +If this option is set to `true`, a report will be created per behat suite. ## Licence -MIT Licence \ No newline at end of file +MIT Licence diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index 64248bd..59bfe6b 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -17,6 +17,12 @@ class FeatureContext implements Context /** @var string */ private $workingDir; + /** @var string */ + private $reportsDir; + + /** @var bool */ + protected $resultFilePerSuiteEnabled; + /** * Cleans test folders in the temporary directory. * @@ -60,7 +66,8 @@ public function prepareTestFolders() { $dir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . md5(microtime() . rand(0, 10000)); - $reportsDir = $dir . DIRECTORY_SEPARATOR . 'reports'; + $this->reportsDir = $dir . DIRECTORY_SEPARATOR . 'reports'; + // create directories mkdir( sprintf( @@ -72,23 +79,9 @@ public function prepareTestFolders() true ); mkdir($dir . DIRECTORY_SEPARATOR . 'junit'); - mkdir($reportsDir); + mkdir($this->reportsDir); - // create configuration - file_put_contents($dir . DIRECTORY_SEPARATOR . 'behat.yml', <<writeBehatConfigForTests($dir); // copy context copy( @@ -120,21 +113,59 @@ public function prepareTestFolders() */ public function iHaveTheFollowingFeature(PyStringNode $string) { - file_put_contents($this->workingDir . '/features/feature.feature', $string->getRaw()); + $this->iHaveTheFollowingFeatureFileStoredIn('feature.feature', 'default', $string); + } + + /** + * @Given I have the following feature file :fileName stored in :subDirectory: + */ + public function iHaveTheFollowingFeatureFileStoredIn($fileName, $subDirectory = '', PyStringNode $string) + { + $filePath = $this->workingDir . '/features' . (!empty($subDirectory) ? '/' . $subDirectory : '') . '/' . $fileName; + if (!empty($subDirectory) && !file_exists($subDirectory)) { + mkdir(dirname($filePath), 0777, true); + } + file_put_contents($filePath, $string->getRaw()); + } + + /** + * @Given I have the enabled the "resultFilePerSuite" option + */ + public function iHaveTheEnabledTheResultFilePerSuiteOption() + { + // manipulate the behat config + $this->resultFilePerSuiteEnabled = true; + $this->writeBehatConfigForTests($this->workingDir, [ + 'resultFilePerSuite' => 'true' + ]); + } + + /** + * @When I run behat with the converter and no specific suite is specified + */ + public function iRunBehatWithTheConverterAndNoSpecificSuiteIsSpecified() + { + $this->runBehatWithConverter(); } /** * @When I run behat with the converter */ public function iRunBehatWithTheConverter() + { + $this->runBehatWithConverter('-s default'); + } + + protected function runBehatWithConverter($extraParameters = '') { $this->process->setWorkingDirectory($this->workingDir); $this->process->setCommandLine( sprintf( - '%s %s -c %s -s default --no-interaction -f cucumber_json', + '%s %s -c %s %s --no-interaction -f cucumber_json', $this->phpBin, escapeshellarg(BEHAT_BIN_PATH), - $this->workingDir . DIRECTORY_SEPARATOR . 'behat.yml' + $this->workingDir . DIRECTORY_SEPARATOR . 'behat.yml', + !empty($extraParameters) ? $extraParameters : '' ) ); // Don't reset the LANG variable on HHVM, because it breaks HHVM itself @@ -152,13 +183,7 @@ public function iRunBehatWithTheConverter() */ public function theResultFileWillBe(PyStringNode $string) { - $reportFiles = glob( - sprintf( - '%1$s%2$sreports%2$sreport*.json', - $this->workingDir, - DIRECTORY_SEPARATOR - ) - ); + $reportFiles = $this->generatedReportFiles(); $expected = json_decode($string->getRaw(), true); $actual = json_decode(file_get_contents(sprintf($reportFiles[0])), true); @@ -169,6 +194,28 @@ public function theResultFileWillBe(PyStringNode $string) ); } + /** + * @Then there should be :featureCount features in the report :reportName + * @Then there should be :featureCount feature in the report :reportName + */ + public function thereShouldBeFeaturesInTheReport(int $featureCount, string $reportName) + { + $reportFiles = $this->generatedReportFiles($reportName); + + $reportData = json_decode(file_get_contents(sprintf($reportFiles[0])), true); + PHPUnit_Framework_Assert::assertCount($featureCount, $reportData); + } + + /** + * @Then :count result file should be generated + * @Then :count result files should be generated + */ + public function resultFileShouldBeGenerated(int $count) + { + $reportFiles = $this->generatedReportFiles(); + PHPUnit_Framework_Assert::assertCount($count, $reportFiles); + } + /** * Removes the dynamic parts of a result, like the feature path and durations. * @@ -187,4 +234,45 @@ private static function removeDynamics(array $array) } return $array; } + + private function generatedReportFiles($reportName = 'report*.json'): array + { + return glob( + sprintf( + '%1$s%2$sreports%2$s%3$s', + $this->workingDir, + DIRECTORY_SEPARATOR, + $reportName + ) + ); + } + + private function writeBehatConfigForTests(string $dir, array $extraOptions = []) + { + // create configuration + $reportsDir = $this->reportsDir; + $content = <<children()->scalarNode('fileNamePrefix')->defaultValue('report'); + $builder->children()->scalarNode('fileNamePrefix')->defaultValue(''); $builder->children()->scalarNode('outputDir')->defaultValue('build/tests'); $builder->children()->scalarNode('fileName'); + $builder->children()->booleanNode('resultFilePerSuite')->defaultFalse(); } /** @@ -56,6 +57,7 @@ public function load(ContainerBuilder $container, array $config) if (!empty($config['fileName'])) { $definition->addMethodCall('setFileName', [$config['fileName']]); } + $definition->addMethodCall('setResultFilePerSuite', [$config['resultFilePerSuite']]); $container ->setDefinition('json.formatter', $definition) diff --git a/src/Formatter/Formatter.php b/src/Formatter/Formatter.php index 01765f1..84d5003 100644 --- a/src/Formatter/Formatter.php +++ b/src/Formatter/Formatter.php @@ -43,6 +43,9 @@ class Formatter implements FormatterInterface /** @var Node\Scenario */ private $currentScenario; + /** @var bool */ + private $resultFilePerSuite = false; + /** * @param string $fileNamePrefix * @param string $outputDir @@ -78,6 +81,11 @@ public function setFileName($fileName) { $this->printer->setResultFileName($fileName); } + /** @inheritdoc */ + public function setResultFilePerSuite(bool $enabled) { + $this->resultFilePerSuite = $enabled; + } + /** @inheritdoc */ public function getDescription() { @@ -128,14 +136,18 @@ public function onAfterExercise(TestworkEvent\ExerciseCompleted $event) $this->timer->stop(); $this->renderer->render(); + + if ($this->resultFilePerSuite) { + foreach ($this->suites as $suite) { + $this->printer->setResultFileName($suite->getFilenameForReport()); + $suiteResult = $this->renderer->getResultForSuite($suite->getName()); + $this->printer->write($suiteResult); + } + return; + } + if (!$this->printer->getResultFileName()) { - $this->printer->setResultFileName( - str_replace( - DIRECTORY_SEPARATOR, - FileOutputPrinter::FILE_SEPARATOR, - $this->currentFeature->getFilenameForReport() - ) - ); + $this->printer->setResultFileName('all'); } $this->printer->write($this->renderer->getResult()); diff --git a/src/Node/Feature.php b/src/Node/Feature.php index f62954e..ccd0a38 100644 --- a/src/Node/Feature.php +++ b/src/Node/Feature.php @@ -194,14 +194,6 @@ public function setFile($file) $this->file = $file; } - /** - * @return string - */ - public function getFilenameForReport() - { - return dirname($this->file) . FileOutputPrinter::FILE_SEPARATOR . basename($this->file, '.feature'); - } - /** * @return Scenario[] */ diff --git a/src/Node/Suite.php b/src/Node/Suite.php index a30bbb3..26d4862 100644 --- a/src/Node/Suite.php +++ b/src/Node/Suite.php @@ -53,4 +53,12 @@ public function addFeature($feature) { $this->features[] = $feature; } + + /** + * @return string + */ + public function getFilenameForReport() + { + return $this->getName(); + } } diff --git a/src/Renderer/JsonRenderer.php b/src/Renderer/JsonRenderer.php index 2d045c5..aab8820 100644 --- a/src/Renderer/JsonRenderer.php +++ b/src/Renderer/JsonRenderer.php @@ -32,7 +32,7 @@ public function render() if (is_array($suites)) { foreach ($suites as $suite) { - array_push($this->result, $this->processSuite($suite)); + $this->result[$suite->getName()] = $this->processSuite($suite); } } } @@ -41,12 +41,29 @@ public function render() public function getResult($asString = true) { if ($asString) { - return json_encode(array_pop($this->result)); + $mergedResults = []; + foreach ($this->result as $result) { + $mergedResults = array_merge($mergedResults, $result); + } + return json_encode($mergedResults); } return $this->result; } + public function getResultForSuite($suiteName, $asString = true) + { + $result = null; + if (isset($this->result[$suiteName])) { + $result = $this->result[$suiteName]; + } + + if ($asString) { + return json_encode($result); + } + return $result; + } + /** * @param Node\Suite $suite * diff --git a/src/Renderer/RendererInterface.php b/src/Renderer/RendererInterface.php index fbf71ac..ea535b5 100644 --- a/src/Renderer/RendererInterface.php +++ b/src/Renderer/RendererInterface.php @@ -16,4 +16,13 @@ public function render(); * @return array|string */ public function getResult($asString); + + /** + * Returns the JSON result as an array or string representation for a suite with a given name. + * + * @param string $suiteName + * @param bool $asString + * @return array|string + */ + public function getResultForSuite($suiteName, $asString); }