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