Skip to content

Commit fa0e11c

Browse files
committed
[TASK] Add trait providing standard implementation of Commentable
Part of #813.
1 parent 6393660 commit fa0e11c

File tree

5 files changed

+298
-0
lines changed

5 files changed

+298
-0
lines changed

composer.json

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"phpstan/phpstan-phpunit": "1.4.2 || 2.0.4",
3434
"phpstan/phpstan-strict-rules": "1.6.2 || 2.0.3",
3535
"phpunit/phpunit": "8.5.41",
36+
"rawr/cross-data-providers": "2.4.0",
3637
"rector/rector": "1.2.10 || 2.0.7",
3738
"rector/type-perfect": "1.0.0 || 2.0.2"
3839
},

src/Comment/CommentContainer.php

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sabberworm\CSS\Comment;
6+
7+
/**
8+
* Provides a standard reusable implementation of `Commentable`.
9+
*
10+
* @internal
11+
*/
12+
trait CommentContainer
13+
{
14+
/**
15+
* @var list<Comment>
16+
*/
17+
protected $comments = [];
18+
19+
/**
20+
* @param list<Comment> $comments
21+
*/
22+
public function addComments(array $comments): void
23+
{
24+
$this->comments = \array_merge($this->comments, $comments);
25+
}
26+
27+
/**
28+
* @return list<Comment>
29+
*/
30+
public function getComments(): array
31+
{
32+
return $this->comments;
33+
}
34+
35+
/**
36+
* @param list<Comment> $comments
37+
*/
38+
public function setComments(array $comments): void
39+
{
40+
$this->comments = $comments;
41+
}
42+
}

src/Comment/Commentable.php

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
namespace Sabberworm\CSS\Comment;
66

7+
/**
8+
* A standard implementation of this interface is available in the `CommentContainer` trait.
9+
*/
710
interface Commentable
811
{
912
/**
+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sabberworm\CSS\Tests\Unit\Comment;
6+
7+
use PHPUnit\Framework\Constraint\LogicalAnd;
8+
use PHPUnit\Framework\Constraint\TraversableContains;
9+
use PHPUnit\Framework\TestCase;
10+
use Sabberworm\CSS\Comment\Comment;
11+
use Sabberworm\CSS\Comment\Commentable;
12+
use Sabberworm\CSS\Tests\Unit\Comment\Fixtures\ConcreteCommentContainer;
13+
use TRegx\DataProvider\DataProviders;
14+
15+
/**
16+
* @covers \Sabberworm\CSS\Comment\CommentContainer
17+
*/
18+
final class CommentContainerTest extends TestCase
19+
{
20+
/**
21+
* @var Commentable
22+
*/
23+
private $subject;
24+
25+
protected function setUp(): void
26+
{
27+
$this->subject = new ConcreteCommentContainer();
28+
}
29+
30+
/**
31+
* @test
32+
*/
33+
public function getCommentsInitiallyReturnsEmptyArray(): void
34+
{
35+
self::assertSame([], $this->subject->getComments());
36+
}
37+
38+
/**
39+
* @return array<non-empty-string, array{0: list<Comment>}>
40+
*/
41+
public function provideCommentArray(): array
42+
{
43+
return [
44+
'no comment' => [[]],
45+
'one comment' => [[new Comment('Is this really a spoon?')]],
46+
'two comments' => [[
47+
new Comment('I’m a teapot.'),
48+
new Comment('I’m a cafetière.'),
49+
]],
50+
];
51+
}
52+
53+
/**
54+
* @test
55+
*
56+
* @param list<Comment> $comments
57+
*
58+
* @dataProvider provideCommentArray
59+
*/
60+
public function getCommentsAfterCommentsAddedToVirginContainerReturnsThoseComments(array $comments): void
61+
{
62+
$this->subject->addComments($comments);
63+
64+
self::assertSame($comments, $this->subject->getComments());
65+
}
66+
67+
/**
68+
* @test
69+
*
70+
* @param list<Comment> $comments
71+
*
72+
* @dataProvider provideCommentArray
73+
*/
74+
public function getCommentsAfterEmptyArrayOfCommentsAddedReturnsOriginalComments(array $comments): void
75+
{
76+
$this->subject->setComments($comments);
77+
78+
$this->subject->addComments([]);
79+
80+
self::assertSame($comments, $this->subject->getComments());
81+
}
82+
83+
/**
84+
* @return array<non-empty-string, array{0: list<Comment>}>
85+
*/
86+
public function provideAlternativeCommentArray(): array
87+
{
88+
return [
89+
'no comment' => [[]],
90+
'one comment' => [[new Comment('Can I eat it with my hands?')]],
91+
'two comments' => [[
92+
new Comment('I’m a beer barrel.'),
93+
new Comment('I’m a vineyard.'),
94+
]],
95+
];
96+
}
97+
98+
/**
99+
* @return array<non-empty-string, array{0: non-empty-list<Comment>}>
100+
*/
101+
public function provideAlternativeNonemptyCommentArray(): array
102+
{
103+
$data = $this->provideAlternativeCommentArray();
104+
105+
unset($data['no comment']);
106+
107+
return $data;
108+
}
109+
110+
/**
111+
* This provider crosses two comment arrays (0, 1 or 2 comments) with different comments,
112+
* so that all combinations can be tested.
113+
*
114+
* @return array<non-empty-string, array{0: list<Comment>, 1: list<Comment>}>
115+
*/
116+
public function provideTwoDistinctCommentArrays(): array
117+
{
118+
return DataProviders::cross($this->provideCommentArray(), $this->provideAlternativeCommentArray());
119+
}
120+
121+
/**
122+
* @return array<non-empty-string, array{0: list<Comment>, 1: non-empty-list<Comment>}>
123+
*/
124+
public function provideTwoDistinctCommentArraysWithSecondNonempty(): array
125+
{
126+
return DataProviders::cross($this->provideCommentArray(), $this->provideAlternativeNonemptyCommentArray());
127+
}
128+
129+
private static function createContainsContstraint(Comment $comment): TraversableContains
130+
{
131+
return new TraversableContains($comment);
132+
}
133+
134+
/**
135+
* @param non-empty-list<Comment> $comments
136+
*
137+
* @return non-empty-list<TraversableContains>
138+
*/
139+
private static function createContainsContstraints(array $comments): array
140+
{
141+
return \array_map([self::class, 'createContainsContstraint'], $comments);
142+
}
143+
144+
/**
145+
* @test
146+
*
147+
* @param list<Comment> $commentsToAdd
148+
* @param non-empty-list<Comment> $originalComments
149+
*
150+
* @dataProvider provideTwoDistinctCommentArraysWithSecondNonempty
151+
*/
152+
public function getCommentsAfterCommentsAddedIncludesOriginalComments(
153+
array $commentsToAdd,
154+
array $originalComments
155+
): void {
156+
$this->subject->setComments($originalComments);
157+
158+
$this->subject->addComments($commentsToAdd);
159+
160+
self::assertThat(
161+
$this->subject->getComments(),
162+
LogicalAnd::fromConstraints(...self::createContainsContstraints($originalComments))
163+
);
164+
}
165+
166+
/**
167+
* @test
168+
*
169+
* @param list<Comment> $originalComments
170+
* @param non-empty-list<Comment> $commentsToAdd
171+
*
172+
* @dataProvider provideTwoDistinctCommentArraysWithSecondNonempty
173+
*/
174+
public function getCommentsAfterCommentsAddedIncludesCommentsAdded(
175+
array $originalComments,
176+
array $commentsToAdd
177+
): void {
178+
$this->subject->setComments($originalComments);
179+
180+
$this->subject->addComments($commentsToAdd);
181+
182+
self::assertThat(
183+
$this->subject->getComments(),
184+
LogicalAnd::fromConstraints(...self::createContainsContstraints($commentsToAdd))
185+
);
186+
}
187+
188+
/**
189+
* @test
190+
*
191+
* @param non-empty-list<Comment> $comments
192+
*
193+
* @dataProvider provideAlternativeNonemptyCommentArray
194+
*/
195+
public function addCommentsAppends(array $comments): void
196+
{
197+
$firstComment = new Comment('I must be first!');
198+
$this->subject->setComments([$firstComment]);
199+
200+
$this->subject->addComments($comments);
201+
202+
$result = $this->subject->getComments();
203+
self::assertNotEmpty($result);
204+
self::assertSame($firstComment, $result[0]);
205+
}
206+
207+
/**
208+
* @test
209+
*
210+
* @param list<Comment> $comments
211+
*
212+
* @dataProvider provideCommentArray
213+
*/
214+
public function getCommentsAfterCommentsSetOnVirginContainerReturnsThoseComments(array $comments): void
215+
{
216+
$this->subject->setComments($comments);
217+
218+
self::assertSame($comments, $this->subject->getComments());
219+
}
220+
221+
/**
222+
* @test
223+
*
224+
* @param list<Comment> $originalComments
225+
* @param list<Comment> $commentsToSet
226+
*
227+
* @dataProvider provideTwoDistinctCommentArrays
228+
*/
229+
public function getCommentsAfterCommentsSetOnContainerWithCommentsReturnsOnlyCommentsSet(
230+
array $originalComments,
231+
array $commentsToSet
232+
): void {
233+
$this->subject->setComments($originalComments);
234+
235+
$this->subject->setComments($commentsToSet);
236+
237+
self::assertSame($commentsToSet, $this->subject->getComments());
238+
}
239+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sabberworm\CSS\Tests\Unit\Comment\Fixtures;
6+
7+
use Sabberworm\CSS\Comment\Commentable;
8+
use Sabberworm\CSS\Comment\CommentContainer;
9+
10+
final class ConcreteCommentContainer implements Commentable
11+
{
12+
use CommentContainer;
13+
}

0 commit comments

Comments
 (0)