diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 14395d8..e5f78e0 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,13 +1,22 @@ - - - - ./src - - + + tests + + + + src + + diff --git a/src/Generator/Formatter/MarkdownFormatter.php b/src/Generator/Formatter/MarkdownFormatter.php new file mode 100644 index 0000000..7d0aafa --- /dev/null +++ b/src/Generator/Formatter/MarkdownFormatter.php @@ -0,0 +1,183 @@ +getDescription(); + if ($description === '') { + return ''; + } + + return <<getDocblock(); + if ($docblock === '') { + return ''; + } + + return <<getDiffBlocks($diffs)); + + return <<prependLinesWith('-', $diff->getBefore())} + {$this->prependLinesWith('+', $diff->getAfter())} + ``` + MD; + }, $diffs); + } + + private function prependLinesWith(string $prefix, string $lines): string + { + $prependedLines = array_map(function (Stringy $line) use ($prefix) { + return (string)$line->prepend($prefix); + }, s($lines)->lines()); + + return implode("\n", $prependedLines); + } + + /** + * @param Property[] $properties + */ + public function formatPublicProperties(array $properties): string + { + if ($properties === []) { + return ''; + } + + $propertyLines = implode("\n", $this->getPublicPropertyLines($properties)); + + return <<getName()}` : {$property->getType()} {$property->getDescription()}"; + }, $properties); + } + + /** + * @return string + */ + public function formatSeeAlso(UrlList $urls): string + { + if ($urls->toArray() === []) { + return ''; + } + + $linkLines = implode("\n", $this->getLinkLines($urls)); + + return <<toArray()); + } + + /** + * @param Violation[] $violations + * @return string + */ + public function formatViolations(array $violations): string + { + if ($violations === []) { + return ''; + } + + $violations = implode("\n", $this->getViolationBlocks($violations)); + + return << + {$violation->getCode()} + {$violation->getDescription()} + + {$this->formatComparisons($violation->getDiffs())} + + {$this->formatSeeAlso($violation->getUrls())} + + ``` + MD; + }, $violations); + } +} diff --git a/src/Generator/Generator.php b/src/Generator/Generator.php index 55438aa..c0aed54 100644 --- a/src/Generator/Generator.php +++ b/src/Generator/Generator.php @@ -8,7 +8,7 @@ interface Generator { - public function createViolationDoc(Violation $doc): string; + public function createViolationDoc(Violation $violation): string; public function createSniffDoc(Sniff $sniff): string; } diff --git a/src/Generator/JekyllPageGenerator.php b/src/Generator/JekyllPageGenerator.php index 08c7aed..a7bdbd0 100644 --- a/src/Generator/JekyllPageGenerator.php +++ b/src/Generator/JekyllPageGenerator.php @@ -3,28 +3,52 @@ namespace App\Generator; +use App\Generator\Formatter\MarkdownFormatter; use App\Value\Sniff; +use App\Value\Violation; -class JekyllPageGenerator extends MarkdownGenerator implements Generator +final class JekyllPageGenerator implements Generator { + private MarkdownFormatter $formatter; + private const LINE_ENDING_REGEX = '\r\n|\n'; + + public function __construct(MarkdownFormatter $formatter) + { + $this->formatter = $formatter; + } + public function createSniffDoc(Sniff $sniff): string { - $sniffDoc = $this->getFrontMatter($sniff) . "\n"; - $sniffDoc .= parent::createSniffDoc($sniff); + $sniffDoc = <<getFrontMatter($sniff)} + # {$sniff->getCode()} + + {$this->formatter->formatDescription($sniff)} + {$this->formatter->formatDocblock($sniff)} + {$this->formatter->formatComparisons($sniff->getDiffs())} + {$this->formatter->formatPublicProperties($sniff->getProperties())} + {$this->formatter->formatSeeAlso($sniff->getUrls())} + {$this->formatter->formatViolations($sniff->getViolations())} + MD; - return $sniffDoc; + $sniffDoc = preg_replace('`'.self::LINE_ENDING_REGEX.'{3,}`', "\n\n", $sniffDoc); + return preg_replace('`'.self::LINE_ENDING_REGEX.'{2,}$`', "\n", $sniffDoc); + } + + public function createViolationDoc(Violation $violation): string + { + return <<getDescription()} + + {$this->formatter->formatComparisons($violation->getDiffs())} + + {$this->formatter->formatSeeAlso($violation->getUrls())} + MD; } private function getFrontMatter(Sniff $sniff): string { $sniffName = $sniff->getSniffName(); - if ($sniffName === '') { - return <<<'MD' - --- - --- - - MD; - } return <<getCode()} - - {$this->getDescription($sniff)} - {$this->getDocblock($sniff)} - {$this->getComparisons($sniff->getDiffs())} - {$this->getPublicProperties($sniff->getProperties())} - {$this->getSeeAlso($sniff->getUrls())} - {$this->getViolations($sniff->getViolations())} - MD; - - $sniffDoc = preg_replace('/\n{3,}/', "\n\n",$sniffDoc); - return preg_replace('/\n{2,}$/', "\n",$sniffDoc); - } - - private function getDescription(Sniff $sniff): string - { - $description = $sniff->getDescription(); - if ($description === '') { - return ''; - } - - return <<getDocblock(); - if ($docblock === '') { - return ''; - } - - return <<getDiffBlocks($diffs)); - - return <<prependLinesWith('-', $diff->getBefore())} - {$this->prependLinesWith('+', $diff->getAfter())} - ``` - MD; - }, $diffs); - } + private MarkdownFormatter $formatter; + private const LINE_ENDING_REGEX = '\r\n|\n'; - private function prependLinesWith(string $prefix, string $lines): string + public function __construct(MarkdownFormatter $formatter) { - $prependedLines = array_map(function (Stringy $line) use ($prefix) { - return (string)$line->prepend($prefix); - }, s($lines)->lines()); - - return implode("\n", $prependedLines); + $this->formatter = $formatter; } - /** - * @param Property[] $properties - */ - private function getPublicProperties(array $properties): string + public function createSniffDoc(Sniff $sniff): string { - if ($properties === []) { - return ''; - } - - $propertyLines = implode("\n", $this->getPublicPropertyLines($properties)); - - return <<getCode()} - {$propertyLines} + {$this->formatter->formatDescription($sniff)} - MD; - } - - /** - * @param Property[] $properties - * @return string[] - */ - private function getPublicPropertyLines(array $properties): array - { - return array_map(function (Property $property) { - return "- `\${$property->getName()}` : {$property->getType()} {$property->getDescription()}"; - }, $properties); - } - - /** - * @return string - */ - private function getSeeAlso(UrlList $urls): string - { - if ($urls->toArray() === []) { - return ''; - } - - $linkLines = implode("\n", $this->getLinkLines($urls)); - - return <<formatter->formatDocblock($sniff)} - {$linkLines} + {$this->formatter->formatComparisons($sniff->getDiffs())} - MD; - } - - /** - * @return string[] - */ - private function getLinkLines(UrlList $urls): array - { - return array_map(function (Url $url) { - return "- [$url]($url)"; - }, $urls->toArray()); - } - - /** - * @param Violation[] $violations - * @return string - */ - private function getViolations(array $violations): string - { - if ($violations === []) { - return ''; - } - - $violations = implode("\n", $this->getViolationBlocks($violations)); - - return <<formatter->formatPublicProperties($sniff->getProperties())} - {$violations} + {$this->formatter->formatSeeAlso($sniff->getUrls())} + {$this->formatter->formatViolations($sniff->getViolations())} MD; - } - /** - * @param Violation[] $violations - * @return string[] - */ - private function getViolationBlocks(array $violations): array - { - return array_map(function (Violation $violation): string { - return << - {$violation->getCode()} - {$this->createViolationDoc($violation)} - - ``` - MD; - }, $violations); + $sniffDoc = preg_replace('`'.self::LINE_ENDING_REGEX.'{3,}`', "\n\n", $sniffDoc); + return preg_replace('`'.self::LINE_ENDING_REGEX.'{2,}$`', "\n", $sniffDoc); } - public function createViolationDoc(Violation $doc): string + public function createViolationDoc(Violation $violation): string { return <<getDescription()} + {$violation->getDescription()} - {$this->getComparisons($doc->getDiffs())} + {$this->formatter->formatComparisons($violation->getDiffs())} - {$this->getSeeAlso($doc->getUrls())} + {$this->formatter->formatSeeAlso($violation->getUrls())} MD; } } diff --git a/src/Value/Sniff.php b/src/Value/Sniff.php index 46d89cc..ebc8947 100644 --- a/src/Value/Sniff.php +++ b/src/Value/Sniff.php @@ -71,21 +71,25 @@ public function __construct( $this->violations = $violations; } + /** @return non-empty-string */ public function getCode(): string { return $this->code; } + /** @return non-empty-string */ public function getStandardName(): string { return $this->standardName; } + /** @return non-empty-string */ public function getCategoryName(): string { return $this->categoryName; } + /** @return non-empty-string */ public function getSniffName(): string { return $this->sniffName; @@ -129,4 +133,104 @@ public function getViolations(): array { return $this->violations; } + + public function withCode(string $newValue): self + { + return new self( + $newValue, + $this->docblock, + $this->properties, + $this->urls, + $this->description, + $this->diffs, + $this->violations + ); + } + + public function withDocblock(string $newValue): self + { + return new self( + $this->code, + $newValue, + $this->properties, + $this->urls, + $this->description, + $this->diffs, + $this->violations + ); + } + + /** + * @param Property[] $newValue + */ + public function withProperties(array $newValue): self + { + return new self( + $this->code, + $this->docblock, + $newValue, + $this->urls, + $this->description, + $this->diffs, + $this->violations + ); + } + + public function withUrls(UrlList $newValue): self + { + return new self( + $this->code, + $this->docblock, + $this->properties, + $newValue, + $this->description, + $this->diffs, + $this->violations + ); + } + + public function withDescription(string $newValue): self + { + return new self( + $this->code, + $this->docblock, + $this->properties, + $this->urls, + $newValue, + $this->diffs, + $this->violations + ); + } + + /** + * @param Diff[] $newValue + */ + public function withDiffs(array $newValue): self + { + return new self( + $this->code, + $this->docblock, + $this->properties, + $this->urls, + $this->description, + $newValue, + $this->violations + ); + } + + /** + * @param Violation[] $newValue + */ + public function withViolations(array $newValue): self + { + return new self( + $this->code, + $this->docblock, + $this->properties, + $this->urls, + $this->description, + $this->diffs, + $newValue + ); + } } diff --git a/tests/App/Generator/Formatter/MarkdownFormatterTest.php b/tests/App/Generator/Formatter/MarkdownFormatterTest.php new file mode 100644 index 0000000..0a22b32 --- /dev/null +++ b/tests/App/Generator/Formatter/MarkdownFormatterTest.php @@ -0,0 +1,237 @@ +formatter->formatDescription( + $this->createSniff()->withDescription('Description') + ) + ); + } + + /** @test */ + public function formatDescription_WithBlankString_ReturnBlankString(): void + { + self::assertEquals( + '', + $this->formatter->formatDescription( + $this->createSniff()->withDescription('') + ) + ); + } + + /** @test */ + public function formatDocblock(): void + { + self::assertEquals( + <<formatter->formatDocblock( + $this->createSniff()->withDocblock('Docblock') + ) + ); + } + + /** @test */ + public function formatDocblock_WithBlankString_ReturnBlankString(): void + { + self::assertEquals( + '', + $this->formatter->formatDocblock( + $this->createSniff()->withDocblock('') + ) + ); + } + + /** @test */ + public function formatComparisons(): void + { + self::assertEquals( + <<formatter->formatComparisons( + $this->createSniff()->withDiffs([ + new Diff('a();', 'b();'), + new Diff('a();', 'b();') + ])->getDiffs() + ) + ); + } + + /** @test */ + public function formatComparisons_WithEmptyList_ReturnBlankString(): void + { + self::assertEquals( + '', + $this->formatter->formatComparisons( + $this->createSniff()->withDiffs([])->getDiffs() + ) + ); + } + + /** @test */ + public function formatPublicProperties(): void + { + self::assertEquals( + <<formatter->formatPublicProperties( + $this->createSniff()->withProperties([ + new Property('a', 'string', 'DescriptionA'), + new Property('b', 'int', 'DescriptionB') + ])->getProperties() + ) + ); + } + + /** @test */ + public function formatPublicProperties_WithEmptyList_ReturnBlankString(): void + { + self::assertEquals( + '', + $this->formatter->formatPublicProperties( + $this->createSniff()->withProperties([])->getProperties() + ) + ); + } + + /** @test */ + public function formatSeeAlso(): void + { + self::assertEquals( + <<formatter->formatSeeAlso( + $this->createSniff()->withUrls(new UrlList([ + new Url('http://link1.com'), + new Url('http://link2.com') + ]))->getUrls() + ) + ); + } + + /** @test */ + public function formatSeeAlso_WithEmptyList_ReturnBlankString(): void + { + self::assertEquals( + '', + $this->formatter->formatSeeAlso( + $this->createSniff()->withUrls(new UrlList([]))->getUrls() + ) + ); + } + + /** @test */ + public function formatViolations(): void + { + self::assertEquals( + << + Standard.Category.My.ErrorCode + Description + + ## Comparisons + + ```diff + -a(); + +b(); + ``` + + ## See Also + + - [http://link1.com](http://link1.com) + + ``` + MD, + $this->formatter->formatViolations( + $this->createSniff()->withViolations([ + new Violation( + 'Standard.Category.My.ErrorCode', + 'Description', + [ + new Diff('a();', 'b();') + ], + new UrlList([ + new Url('http://link1.com') + ]) + ) + ])->getViolations() + ) + ); + } + + /** @test */ + public function formatViolations_WithEmptyList_ReturnBlankString(): void + { + self::assertEquals( + '', + $this->formatter->formatViolations( + $this->createSniff()->withViolations([])->getViolations() + ) + ); + } + + protected function setUp(): void + { + $this->formatter = new MarkdownFormatter(); + } +} diff --git a/tests/Generator/JekyllPageGeneratorTest.php b/tests/Generator/JekyllPageGeneratorTest.php index eae2d38..70d2750 100644 --- a/tests/Generator/JekyllPageGeneratorTest.php +++ b/tests/Generator/JekyllPageGeneratorTest.php @@ -3,18 +3,22 @@ namespace App\Tests\Generator; +use App\Generator\Formatter\MarkdownFormatter; use App\Generator\JekyllPageGenerator; +use App\Value\Diff; use App\Value\Sniff; +use App\Value\Url; use App\Value\UrlList; +use App\Value\Violation; use PHPUnit\Framework\TestCase; -/** @covers \App\Generator\JekyllPage */ -class JekyllPageTest extends TestCase +/** @covers \App\Generator\JekyllPageGenerator */ +class JekyllPageGeneratorTest extends TestCase { private JekyllPageGenerator $generator; /** @test */ - public function fromSniff_WithMinimalData_WriteMinimalDetails() + public function createSniffDoc_WithMinimalData_WriteMinimalDetails() { $doc = new Sniff( 'Standard.Category.My', @@ -39,8 +43,41 @@ public function fromSniff_WithMinimalData_WriteMinimalDetails() ); } + /** @test */ + public function createViolationDoc_WriteMinimalDetails() + { + self::assertEquals( + <<<'MD' + Description + + ## Comparisons + + ```diff + -a(); + +b(); + ``` + + ## See Also + + - [http://link1.com](http://link1.com) + MD, + $this->generator->createViolationDoc( + new Violation( + 'Standard.Category.My.ErrorCode', + 'Description', + [ + new Diff('a();', 'b();'), + ], + new UrlList([ + new Url('http://link1.com') + ]) + ) + ) + ); + } + protected function setUp(): void { - $this->generator = new JekyllPageGenerator(); + $this->generator = new JekyllPageGenerator(new MarkdownFormatter()); } } diff --git a/tests/Generator/MarkdownGeneratorTest.php b/tests/Generator/MarkdownGeneratorTest.php index b79647c..cd6d85b 100644 --- a/tests/Generator/MarkdownGeneratorTest.php +++ b/tests/Generator/MarkdownGeneratorTest.php @@ -3,6 +3,7 @@ namespace App\Tests\Generator; +use App\Generator\Formatter\MarkdownFormatter; use App\Generator\MarkdownGenerator; use App\Value\Diff; use App\Value\Property; @@ -18,7 +19,7 @@ class MarkdownGeneratorTest extends TestCase private MarkdownGenerator $generator; /** @test */ - public function fromSniff_WithMinimalData_WriteMinimalDetails() + public function createSniffDoc_WithMinimalData_WriteMinimalDetails() { $doc = new Sniff( 'Standard.Category.My', @@ -40,18 +41,16 @@ public function fromSniff_WithMinimalData_WriteMinimalDetails() } /** @test */ - public function fromSniff_WithCompleteData_WriteAllDetails() + public function createSniffDoc_WithCompleteData_WriteAllDetails() { $doc = new Sniff( 'Standard.Category.My', 'DocBlock', [ new Property('a', 'string', 'DescriptionA'), - new Property('b', 'int', 'DescriptionB') ], new UrlList([ - new Url('http://link1.com'), - new Url('http://link2.com') + new Url('http://link1.com') ]), 'Description', [], @@ -61,11 +60,9 @@ public function fromSniff_WithCompleteData_WriteAllDetails() 'Description', [ new Diff('a();', 'b();'), - new Diff('a();', 'b();') ], new UrlList([ - new Url('http://link1.com'), - new Url('http://link2.com') + new Url('http://link1.com') ]) ) ] @@ -84,12 +81,10 @@ public function fromSniff_WithCompleteData_WriteAllDetails() ## Public Properties - `$a` : string DescriptionA - - `$b` : int DescriptionB ## See Also - [http://link1.com](http://link1.com) - - [http://link2.com](http://link2.com) ## Troubleshooting @@ -105,6 +100,25 @@ public function fromSniff_WithCompleteData_WriteAllDetails() +b(); ``` + ## See Also + + - [http://link1.com](http://link1.com) + + ``` + MD, + $this->generator->createSniffDoc($doc) + ); + } + + /** @test */ + public function createViolationDoc_WriteMinimalDetails() + { + self::assertEquals( + <<<'MD' + Description + + ## Comparisons + ```diff -a(); +b(); @@ -113,18 +127,24 @@ public function fromSniff_WithCompleteData_WriteAllDetails() ## See Also - [http://link1.com](http://link1.com) - - [http://link2.com](http://link2.com) - - - ``` - MD, - $this->generator->createSniffDoc($doc) + $this->generator->createViolationDoc( + new Violation( + 'Standard.Category.My.ErrorCode', + 'Description', + [ + new Diff('a();', 'b();'), + ], + new UrlList([ + new Url('http://link1.com') + ]) + ) + ) ); } protected function setUp(): void { - $this->generator = new MarkdownGenerator(); + $this->generator = new MarkdownGenerator(new MarkdownFormatter()); } } diff --git a/tests/Value/SniffTest.php b/tests/Value/SniffTest.php index f1f3e1a..b1284d5 100644 --- a/tests/Value/SniffTest.php +++ b/tests/Value/SniffTest.php @@ -1,176 +1 @@ -expectException(InvalidArgumentException::class); - $params = $this->getValidParams(); - $params[0] = ''; - new Sniff(...$params); - } - - private function getValidParams(): array - { - return [ - self::CODE, - self::DOCBLOCK, - $this->PROPERTIES, - new UrlList($this->URLS), - self::DESCRIPTION, - $this->DIFFS, - $this->VIOLATIONS - ]; - } - - /** @test */ - public function getProperties() - { - $properties = [ - new Property('name', 'int', 'description') - ]; - self::assertEquals( - $properties, - $this->createSniff()->getProperties() - ); - } - - private function createSniff(): Sniff - { - return new Sniff(...$this->getValidParams()); - } - - /** @test */ - public function getDocblock() - { - self::assertEquals( - self::DOCBLOCK, - $this->createSniff()->getDocblock() - ); - } - - /** @test */ - public function getDescription() - { - self::assertEquals( - self::DESCRIPTION, - $this->createSniff()->getDescription() - ); - } - - /** @test */ - public function getViolations() - { - self::assertEquals( - $this->VIOLATIONS, - $this->createSniff()->getViolations() - ); - } - - /** @test */ - public function getDiffs() - { - self::assertEquals( - $this->DIFFS, - $this->createSniff()->getDiffs() - ); - } - - /** @test */ - public function getUrls() - { - self::assertEquals( - $this->URLS, - $this->createSniff()->getUrls()->toArray() - ); - } - - /** @test */ - public function getCode() - { - self::assertEquals( - self::CODE, - $this->createSniff()->getCode() - ); - } - - /** @test */ - public function getStandardName() - { - self::assertSame( - self::STANDARD, - $this->createSniff()->getStandardName() - ); - } - - /** @test */ - public function getCategoryName() - { - self::assertSame( - self::CATEGORY, - $this->createSniff()->getCategoryName() - ); - } - - /** @test */ - public function getSniffName() - { - self::assertSame( - self::SNIFFNAME, - $this->createSniff()->getSniffName() - ); - } - - protected function setUp(): void - { - $this->PROPERTIES = [ - new Property('name', 'int', 'description') - ]; - $this->URLS = [ - new Url('https://link.com') - ]; - $this->DIFFS = [ - new Diff('a();', 'b();') - ]; - $this->VIOLATIONS = [ - new Violation('code', '', [], new UrlList([])) - ]; - } -} +expectException(InvalidArgumentException::class); $this->createSniff()->withCode(''); } /** @test */ public function getProperties() { $properties = [ new Property('name', 'int', 'description') ]; self::assertEquals( $properties, $this->createSniff()->withProperties($properties)->getProperties() ); } /** @test */ public function getDocblock() { self::assertEquals( 'Docblock', $this->createSniff()->withDocblock('Docblock')->getDocblock() ); } /** @test */ public function getDescription() { self::assertEquals( 'Description', $this->createSniff()->withDescription('Description')->getDescription() ); } /** @test */ public function getViolations() { $violations = [ new Violation('code', '', [], new UrlList([])) ]; self::assertEquals( $violations, $this->createSniff()->withViolations($violations)->getViolations() ); } /** @test */ public function getDiffs() { $diffs = [ new Diff('a();', 'b();') ]; self::assertEquals( $diffs, $this->createSniff()->withDiffs($diffs)->getDiffs() ); } /** @test */ public function getUrls() { $urls = new UrlList([ new Url('https://link.com') ]); self::assertEquals( $urls, $this->createSniff()->withUrls($urls)->getUrls() ); } /** @test */ public function getCode() { self::assertEquals( 'Standard.Category.SniffName', $this->createSniff()->withCode('Standard.Category.SniffName')->getCode() ); } /** @test */ public function getStandardName() { self::assertSame( 'Standard', $this->createSniff()->withCode('Standard.Category.SniffName')->getStandardName() ); } /** @test */ public function getCategoryName() { self::assertSame( 'Category', $this->createSniff()->withCode('Standard.Category.SniffName')->getCategoryName() ); } /** @test */ public function getSniffName() { self::assertSame( 'SniffName', $this->createSniff()->withCode('Standard.Category.SniffName')->getSniffName() ); } private function createSniff(): Sniff { return new Sniff( 'Standard.Category.SniffName', '', [], new UrlList([]), '', [], [] ); } } \ No newline at end of file