packages/requirements/tests/Unit/Config/Markdown/Render/FieldWriterTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Config\Markdown\Render;
6
7use JsonException;
8use PHPUnit\Framework\Attributes\CoversClass;
9use PHPUnit\Framework\Attributes\DataProvider;
10use PHPUnit\Framework\Attributes\Small;
11use PHPUnit\Framework\Attributes\UsesClass;
12use PHPUnit\Framework\TestCase;
13use Requirements\Config\Markdown\Nodes;
14use Requirements\Config\Markdown\Render\FieldWriter;
15use Requirements\Config\Markdown\Render\MetadataWriter;
16use Requirements\Config\Markdown\Render\Record;
17use Requirements\Input\Fields;
18use Requirements\Input\InvalidInputException;
19
20#[CoversClass(FieldWriter::class)]
21#[UsesClass(Nodes::class)]
22#[UsesClass(Fields::class)]
23#[UsesClass(MetadataWriter::class)]
24#[UsesClass(Record::class)]
25#[Small]
26final class FieldWriterTest extends TestCase
27{
28 /**
29 * @param array<string, string> $links
30 * @throws JsonException
31 */
32 #[DataProvider('providerFields')]
33 public function testWriteWritesTheSectionBody(string $name, mixed $value, array $links, string $expected): void
34 {
35 self::assertSame($expected, (new FieldWriter())->write($name, $value, $links));
36 }
37
38 /**
39 * @return array<string, array{string, mixed, array<string, string>, string}>
40 */
41 public static function providerFields(): array
42 {
43 return [
44 'prose' => ['reason', 'Keep *keywords*.', [], 'Keep \\*keywords\\*.'],
45 'metadata' => ['metadata', (object) ['owner' => 'Parser team', 'values' => [1]], [], "- **owner:** Parser team\n- **values:**\n - 1"],
46 'empty metadata' => ['metadata', (object) [], [], '{}'],
47 'read requirement link' => ['requirements', ['REQ-001'], ['REQ-001' => 'reference.yaml#req-001'], '- [REQ-001](reference.yaml#req-001)'],
48 'new requirement link' => ['requirements', ['REQ.A-1', 'REQ-002'], [], "- [REQ.A-1](#reqa-1)\n- [REQ-002](#req-002)"],
49 'related link with a space' => ['related', ['SPEC-001'], ['SPEC-001' => 'other file.md#spec-001'], '- [SPEC-001](<other%20file.md#spec-001>)'],
50 'manual tests' => ['tests', [(object) ['runner' => 'fuzz', 'target' => 'slow', 'run' => 'manual']], [], "- **fuzz:** slow\n - **run:** manual"],
51 'automatic tests' => ['tests', [(object) ['runner' => 'unit', 'target' => 'fast', 'run' => 'auto']], [], "- **unit:** fast\n - **run:** auto"],
52 'tests' => ['tests', [(object) ['runner' => 'unit', 'target' => 'Sample\\PassingTest::test_pass']], [], '- **unit:** Sample\\\\PassingTest::test\\_pass'],
53 'design link with text' => ['design', [(object) ['url' => 'https://example.org/design', 'text' => 'Parser design']], [], '- [Parser design](https://example.org/design)'],
54 'design link without text' => ['design', [(object) ['url' => 'https://example.org/design']], [], '- [https://example.org/design](https://example.org/design)'],
55 'design text' => ['design', [(object) ['text' => 'Preserve the original source spelling.']], [], '- Preserve the original source spelling.'],
56 'empty list' => ['labels', [], [], 'None.'],
57 'empty evidence' => ['evidence', [], [], 'None.'],
58 ];
59 }
60
61 /**
62 * @throws JsonException
63 */
64 public function testWriteRejectsAValueThatIsNotAList(): void
65 {
66 $this->expectException(InvalidInputException::class);
67 $this->expectExceptionMessage('tests must be a list.');
68 (new FieldWriter())->write('tests', 5, []);
69 }
70
71 /**
72 * @throws JsonException
73 */
74 public function testWriteRejectsARecordOfAnotherField(): void
75 {
76 $this->expectException(InvalidInputException::class);
77 $this->expectExceptionMessage("Cannot render Markdown field 'evidence'.");
78 (new FieldWriter())->write('evidence', [(object) ['selector' => '#a', 'quote' => 'Names shall start with a letter.']], []);
79 }
80
81 /**
82 * @throws JsonException
83 */
84 public function testWriteRejectsAStringEntryOutsideTheReferenceFields(): void
85 {
86 $this->expectException(InvalidInputException::class);
87 $this->expectExceptionMessage('Expected a Markdown record.');
88 (new FieldWriter())->write('labels', ['grammar'], []);
89 }
90
91 /**
92 * @throws JsonException
93 */
94 public function testWriteRejectsATestWithoutATarget(): void
95 {
96 $this->expectException(InvalidInputException::class);
97 $this->expectExceptionMessage('target must be a nonempty string.');
98 (new FieldWriter())->write('tests', [(object) ['runner' => 'unit']], []);
99 }
100}
101