Skip to content

Commit ee07bde

Browse files
authored
[TASK] Add trait providing standard implementation of Commentable (#1206)
Part of #813.
1 parent ece5633 commit ee07bde

File tree

5 files changed

+293
-0
lines changed

5 files changed

+293
-0
lines changed

Diff for: 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
},

Diff for: src/Comment/CommentContainer.php

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
* @phpstan-require-implements Commentable
13+
*/
14+
trait CommentContainer
15+
{
16+
/**
17+
* @var list<Comment>
18+
*/
19+
protected $comments = [];
20+
21+
/**
22+
* @param list<Comment> $comments
23+
*/
24+
public function addComments(array $comments): void
25+
{
26+
$this->comments = \array_merge($this->comments, $comments);
27+
}
28+
29+
/**
30+
* @return list<Comment>
31+
*/
32+
public function getComments(): array
33+
{
34+
return $this->comments;
35+
}
36+
37+
/**
38+
* @param list<Comment> $comments
39+
*/
40+
public function setComments(array $comments): void
41+
{
42+
$this->comments = $comments;
43+
}
44+
}

Diff for: 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
/**

Diff for: tests/Unit/Comment/CommentContainerTest.php

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