Skip to content

Commit 2de9b4f

Browse files
committed
Added tests for tag component
1 parent 37e4e70 commit 2de9b4f

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ibexa\Tests\Integration\DesignSystemTwig\Twig\Components;
6+
7+
use Generator;
8+
use Ibexa\DesignSystemTwig\Twig\Components\Tag;
9+
use PHPUnit\Framework\Attributes\DataProvider;
10+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
11+
use Symfony\Component\DomCrawler\Crawler;
12+
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
13+
use Symfony\UX\TwigComponent\Test\InteractsWithTwigComponents;
14+
15+
final class TagTest extends KernelTestCase
16+
{
17+
use InteractsWithTwigComponents;
18+
19+
public function testMount(): void
20+
{
21+
$component = $this->mountTwigComponent(Tag::class, [
22+
'type' => 'primary',
23+
'size' => 'small',
24+
'isDark' => true,
25+
'is_ghost_type' => true,
26+
'icon' => 'check',
27+
]);
28+
29+
self::assertInstanceOf(Tag::class, $component, 'Component should mount as Tag.');
30+
}
31+
32+
public function testDefaultRenderProducesWrapperAndRendersSlot(): void
33+
{
34+
$crawler = $this->renderTwigComponent(
35+
Tag::class,
36+
[],
37+
'Hello tag'
38+
)->crawler();
39+
40+
$wrapper = $this->getWrapper($crawler);
41+
42+
self::assertStringContainsString('ids-tag', $this->getClassAttr($wrapper), 'Wrapper should include base class "ids-tag".');
43+
self::assertStringContainsString('Hello tag', trim($wrapper->text('')), 'Slot content should be rendered inside the tag.');
44+
}
45+
46+
/**
47+
* @param array<string, mixed> $props
48+
* @param list<string> $expectedClasses
49+
*/
50+
#[DataProvider('variantProvider')]
51+
public function testVariantsProduceExpectedClasses(array $props, array $expectedClasses): void
52+
{
53+
$crawler = $this->renderTwigComponent(
54+
Tag::class,
55+
$props,
56+
'X'
57+
)->crawler();
58+
59+
$wrapper = $this->getWrapper($crawler);
60+
$class = $this->getClassAttr($wrapper);
61+
62+
foreach ($expectedClasses as $cls) {
63+
self::assertStringContainsString($cls, $class, sprintf('Expected class "%s" should be present.', $cls));
64+
}
65+
}
66+
67+
public static function variantProvider(): Generator
68+
{
69+
yield 'size: small' => [['size' => 'small'], ['ids-tag--small']];
70+
71+
yield 'size: medium' => [['size' => 'medium'], ['ids-tag--medium']];
72+
73+
yield 'isDark: true' => [['isDark' => true], ['ids-tag--dark']];
74+
75+
$types = [
76+
'primary',
77+
'success',
78+
'info',
79+
'warning',
80+
'error',
81+
'neutral',
82+
'icon-tag',
83+
'primary-alt',
84+
'success-ghost',
85+
'error-ghost',
86+
'neutral-ghost',
87+
];
88+
89+
foreach ($types as $type) {
90+
yield "type: {$type}" => [['type' => $type], ["ids-tag--{$type}"]];
91+
}
92+
}
93+
94+
public function testGhostTypeRendersDot(): void
95+
{
96+
$crawler = $this->renderTwigComponent(
97+
Tag::class,
98+
['type' => 'neutral-ghost'],
99+
'Ghost'
100+
)->crawler();
101+
102+
$dot = $crawler->filter('.ids-tag__ghost-dot, .ids-tag__dot')->first();
103+
104+
self::assertGreaterThan(0, $dot->count(), 'Ghost dot should be rendered for ghost tag types.');
105+
}
106+
107+
public function testIconRendersIconContainer(): void
108+
{
109+
$crawler = $this->renderTwigComponent(
110+
Tag::class,
111+
['icon' => 'check'],
112+
'With icon'
113+
)->crawler();
114+
115+
$icon = $crawler->filter('.ids-tag__icon')->first();
116+
117+
self::assertGreaterThan(0, $icon->count(), 'Icon container should be rendered when "icon" is provided.');
118+
}
119+
120+
public function testAttributesMergeClass(): void
121+
{
122+
$crawler = $this->renderTwigComponent(
123+
Tag::class,
124+
['attributes' => ['class' => 'extra-class']],
125+
'Merge'
126+
)->crawler();
127+
128+
$wrapper = $this->getWrapper($crawler);
129+
130+
self::assertStringContainsString('extra-class', $this->getClassAttr($wrapper), 'Custom class should be merged into wrapper class attribute.');
131+
}
132+
133+
public function testInvalidSizeValueCausesResolverErrorOnMount(): void
134+
{
135+
$this->expectException(InvalidOptionsException::class);
136+
$this->mountTwigComponent(Tag::class, ['size' => 'giant']);
137+
}
138+
139+
public function testInvalidTypeValueCausesResolverErrorOnMount(): void
140+
{
141+
$this->expectException(InvalidOptionsException::class);
142+
$this->mountTwigComponent(Tag::class, ['type' => 'unknown']);
143+
}
144+
145+
public function testInvalidIsDarkTypeCausesResolverErrorOnMount(): void
146+
{
147+
$this->expectException(InvalidOptionsException::class);
148+
$this->mountTwigComponent(Tag::class, ['isDark' => 'yes']);
149+
}
150+
151+
private function getWrapper(Crawler $crawler): Crawler
152+
{
153+
$node = $crawler->filter('div.ids-tag')->first();
154+
self::assertGreaterThan(0, $node->count(), 'Tag wrapper ".ids-tag" should be present.');
155+
156+
return $node;
157+
}
158+
159+
private function getClassAttr(Crawler $node): string
160+
{
161+
return (string) $node->attr('class');
162+
}
163+
}

0 commit comments

Comments
 (0)