Repository: bmitch/churn-php (php)

Issue:
Support markdown format
It would be great to support the markdown format, especially now the GH actions can display it as a job summary:
https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/


Discussion:

Patch:
diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -330,4 +330,4 @@ jobs:
         run: chmod +x churn.phar
 
       - name: "Run Phar"
-        run: ./churn.phar
+        run: ./churn.phar --format=markdown >> $GITHUB_STEP_SUMMARY
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -147,6 +147,7 @@ You can configure `churn` to output the result in different formats. The availab
 
 * `csv`
 * `json`
+* `markdown`
 * `text` (default)
 
 To use a different format use `--format` option. Example command for `json`: 
diff --git a/src/Result/Render/MarkdownResultsRenderer.php b/src/Result/Render/MarkdownResultsRenderer.php
new file mode 100644
--- /dev/null
+++ b/src/Result/Render/MarkdownResultsRenderer.php
@@ -0,0 +1,43 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Churn\Result\Render;
+
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * @internal
+ */
+final class MarkdownResultsRenderer implements ResultsRendererInterface
+{
+    /**
+     * Renders the results.
+     *
+     * @param OutputInterface $output Output Interface.
+     * @param array<array<float|integer|string>> $results The results.
+     */
+    public function render(OutputInterface $output, array $results): void
+    {
+        $output->writeln('| File | Times Changed | Complexity | Score |');
+        $output->writeln('|------|---------------|------------|-------|');
+
+        foreach ($results as $result) {
+            $output->writeln($this->inline($result));
+        }
+    }
+
+    /**
+     * @param array<float|integer|string> $data The data to inline.
+     */
+    private function inline(array $data): string
+    {
+        $escapedData = \array_map(static function ($item) {
+            return \is_string($item)
+                ? \str_replace('|', '\\|', $item)
+                : $item;
+        }, $data);
+
+        return '| ' . \implode(' | ', $escapedData) . ' |';
+    }
+}
diff --git a/src/Result/ResultsRendererFactory.php b/src/Result/ResultsRendererFactory.php
--- a/src/Result/ResultsRendererFactory.php
+++ b/src/Result/ResultsRendererFactory.php
@@ -7,6 +7,7 @@
 use Churn\Result\Render\ConsoleResultsRenderer;
 use Churn\Result\Render\CsvResultsRenderer;
 use Churn\Result\Render\JsonResultsRenderer;
+use Churn\Result\Render\MarkdownResultsRenderer;
 use Churn\Result\Render\ResultsRendererInterface;
 use InvalidArgumentException;
 
@@ -15,8 +16,9 @@
  */
 final class ResultsRendererFactory
 {
-    private const FORMAT_JSON = 'json';
     private const FORMAT_CSV = 'csv';
+    private const FORMAT_JSON = 'json';
+    private const FORMAT_MD = 'markdown';
     private const FORMAT_TEXT = 'text';
 
     /**
@@ -35,6 +37,10 @@ public function getRenderer(string $format): ResultsRendererInterface
             return new JsonResultsRenderer();
         }
 
+        if (self::FORMAT_MD === $format) {
+            return new MarkdownResultsRenderer();
+        }
+
         if (self::FORMAT_TEXT === $format) {
             return new ConsoleResultsRenderer();
         }


Test patch:
diff --git a/tests/Unit/Result/Render/CsvResultsRendererTest.php b/tests/Unit/Result/Render/CsvResultsRendererTest.php
--- a/tests/Unit/Result/Render/CsvResultsRendererTest.php
+++ b/tests/Unit/Result/Render/CsvResultsRendererTest.php
@@ -18,7 +18,7 @@ public function it_can_be_instantiated()
     }
 
     /** @test */
-    public function it_can_render_the_results_as_json()
+    public function it_can_render_the_results_as_csv()
     {
         $results = [
             ['filename1.php', 5, 7, 0.625],
diff --git a/tests/Unit/Result/Render/MarkdownResultsRendererTest.php b/tests/Unit/Result/Render/MarkdownResultsRendererTest.php
new file mode 100644
--- /dev/null
+++ b/tests/Unit/Result/Render/MarkdownResultsRendererTest.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Churn\Tests\Unit\Result\Render;
+
+use Churn\Result\Render\MarkdownResultsRenderer;
+use Churn\Tests\BaseTestCase;
+use Mockery as m;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class MarkdownResultsRendererTest extends BaseTestCase
+{
+    /** @test */
+    public function it_can_be_instantiated()
+    {
+        $this->assertInstanceOf(MarkdownResultsRenderer::class, new MarkdownResultsRenderer());
+    }
+
+    /** @test */
+    public function it_can_render_the_results_as_markdown()
+    {
+        $results = [
+            ['filename1.php', 5, 7, 0.625],
+            ['path/filename2.php', 3, 4, 0.242],
+            ['pa|th/filename3.php', 1, 5, 0.08],
+        ];
+
+        $output = m::mock(OutputInterface::class);
+        $output->shouldReceive('writeln')->once()->with('| File | Times Changed | Complexity | Score |');
+        $output->shouldReceive('writeln')->once()->with('|------|---------------|------------|-------|');
+        $output->shouldReceive('writeln')->once()->with('| filename1.php | 5 | 7 | 0.625 |');
+        $output->shouldReceive('writeln')->once()->with('| path/filename2.php | 3 | 4 | 0.242 |');
+        $output->shouldReceive('writeln')->once()->with('| pa\\|th/filename3.php | 1 | 5 | 0.08 |');
+
+        (new MarkdownResultsRenderer())->render($output, $results);
+    }
+}
diff --git a/tests/Unit/Result/ResultsRendererFactoryTest.php b/tests/Unit/Result/ResultsRendererFactoryTest.php
--- a/tests/Unit/Result/ResultsRendererFactoryTest.php
+++ b/tests/Unit/Result/ResultsRendererFactoryTest.php
@@ -7,6 +7,7 @@
 use Churn\Result\Render\ConsoleResultsRenderer;
 use Churn\Result\Render\CsvResultsRenderer;
 use Churn\Result\Render\JsonResultsRenderer;
+use Churn\Result\Render\MarkdownResultsRenderer;
 use Churn\Result\ResultsRendererFactory;
 use Churn\Tests\BaseTestCase;
 use InvalidArgumentException;
@@ -36,6 +37,12 @@ public function it_returns_the_csv_renderer_when_provided_csv_format()
         $this->assertInstanceOf(CsvResultsRenderer::class, $this->factory->getRenderer('csv'));
     }
 
+    /** @test */
+    public function it_returns_the_markdown_renderer_when_provided_markdown_format()
+    {
+        $this->assertInstanceOf(MarkdownResultsRenderer::class, $this->factory->getRenderer('markdown'));
+    }
+
     /** @test */
     public function it_returns_the_console_renderer_when_provided_text_format()
     {