Skip to content

Commit 622069c

Browse files
authored
Added tests for DropdownSingle twig component (#52)
* Added tests for DropdownSingle twig component * Refactor assertions in InputTest for improved readability
1 parent edf58a7 commit 622069c

File tree

1 file changed

+251
-0
lines changed

1 file changed

+251
-0
lines changed
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (C) Ibexa AS. All rights reserved.
5+
* @license For full copyright and license information view LICENSE file distributed with this source code.
6+
*/
7+
declare(strict_types=1);
8+
9+
namespace Ibexa\Tests\Integration\DesignSystemTwig\Twig\Components\DropdownSingle;
10+
11+
use Ibexa\DesignSystemTwig\Twig\Components\DropdownSingle\Input;
12+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
13+
use Symfony\Component\DomCrawler\Crawler;
14+
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
15+
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
16+
use Symfony\UX\TwigComponent\Test\InteractsWithTwigComponents;
17+
18+
final class InputTest extends KernelTestCase
19+
{
20+
use InteractsWithTwigComponents;
21+
22+
public function testMount(): void
23+
{
24+
$component = $this->mountTwigComponent(Input::class, $this->baseProps(['value' => 'foo']));
25+
26+
self::assertInstanceOf(
27+
Input::class,
28+
$component,
29+
'Component should mount as DropdownSingle\\Input.'
30+
);
31+
}
32+
33+
public function testEmptyStateShowsPlaceholderAndHidesSelectedItems(): void
34+
{
35+
$crawler = $this->renderTwigComponent(
36+
Input::class,
37+
$this->baseProps()
38+
)->crawler();
39+
40+
$select = $this->getSelect($crawler);
41+
$selectedOption = $select->filter('option[selected]')->first();
42+
self::assertSame(
43+
'a',
44+
$selectedOption->attr('value'),
45+
'Without a value, the first option should be selected by default.'
46+
);
47+
self::assertSame(
48+
'Alpha',
49+
trim($selectedOption->text('')),
50+
'The first option label should be shown as selected by default.'
51+
);
52+
53+
$placeholder = $crawler->filter('.ids-dropdown__placeholder')->first();
54+
$selectedBox = $crawler->filter('.ids-dropdown__selection-info-items')->first();
55+
56+
self::assertNotNull(
57+
$placeholder->attr('hidden'),
58+
'Placeholder should be hidden when no explicit value is selected.'
59+
);
60+
self::assertNull(
61+
$selectedBox->attr('hidden'),
62+
'Selection box should be visible when no explicit value is selected.'
63+
);
64+
self::assertStringContainsString(
65+
'Alpha',
66+
trim($selectedBox->text('')),
67+
'Selection box should show the first item label by default.'
68+
);
69+
}
70+
71+
public function testSelectedValueHidesPlaceholderAndShowsSelectedLabel(): void
72+
{
73+
$crawler = $this->renderTwigComponent(Input::class, $this->baseProps(['value' => 'b']))->crawler();
74+
75+
$select = $this->getSelect($crawler);
76+
$selectedOption = $select->filter('option[selected]')->first();
77+
78+
self::assertSame(
79+
'b',
80+
$selectedOption->attr('value'),
81+
'Selected <option> should match provided value.'
82+
);
83+
self::assertSame(
84+
'Beta',
85+
trim($selectedOption->text('')),
86+
'Selected <option> text should be the item label.'
87+
);
88+
89+
$placeholder = $crawler->filter('.ids-dropdown__placeholder')->first();
90+
$selectedBox = $crawler->filter('.ids-dropdown__selection-info-items')->first();
91+
92+
self::assertNotNull(
93+
$placeholder->attr('hidden'),
94+
'Placeholder should be hidden when a value is selected.'
95+
);
96+
self::assertNull(
97+
$selectedBox->attr('hidden'),
98+
'Selection box should be visible when a value is selected.'
99+
);
100+
self::assertStringContainsString(
101+
'Beta',
102+
trim($selectedBox->text('')),
103+
'Selection box should render selected label.'
104+
);
105+
}
106+
107+
public function testOptionsAreRenderedAndOneIsMarkedSelected(): void
108+
{
109+
$crawler = $this->renderTwigComponent(Input::class, $this->baseProps(['value' => 'a']))->crawler();
110+
111+
$select = $this->getSelect($crawler);
112+
$options = $select->filter('option');
113+
114+
self::assertSame(
115+
3,
116+
$options->count(),
117+
'Should render three <option> elements.'
118+
);
119+
self::assertSame(
120+
'a',
121+
$select->filter('option[selected]')->attr('value'),
122+
'The <option> with selected attribute should match provided value.'
123+
);
124+
125+
$labels = $options->each(static fn (Crawler $o): string => trim($o->text('')));
126+
127+
self::assertSame(
128+
['Alpha', 'Beta', 'Gamma'],
129+
$labels,
130+
'Options should render provided labels in order.'
131+
);
132+
}
133+
134+
public function testDisabledAndErrorAddClassesAndSelectDisabled(): void
135+
{
136+
$crawler = $this->renderTwigComponent(
137+
Input::class,
138+
$this->baseProps(['disabled' => true, 'error' => true])
139+
)->crawler();
140+
141+
$wrapper = $this->getWrapper($crawler);
142+
$widget = $this->getWidget($crawler);
143+
$select = $this->getSelect($crawler);
144+
145+
self::assertStringContainsString(
146+
'ids-dropdown--disabled',
147+
$this->getClassAttr($wrapper),
148+
'Wrapper should include disabled modifier.'
149+
);
150+
self::assertStringContainsString(
151+
'ids-dropdown--error',
152+
$this->getClassAttr($wrapper),
153+
'Wrapper should include error modifier.'
154+
);
155+
self::assertStringContainsString(
156+
'ids-input--disabled',
157+
$this->getClassAttr($widget),
158+
'Widget should include disabled modifier.'
159+
);
160+
self::assertStringContainsString(
161+
'ids-input--error',
162+
$this->getClassAttr($widget),
163+
'Widget should include error modifier.'
164+
);
165+
self::assertNotNull(
166+
$select->attr('disabled'),
167+
'Native "disabled" attribute should be present on <select> when disabled=true.'
168+
);
169+
}
170+
171+
public function testWrapperClassMergesFromAttributes(): void
172+
{
173+
$crawler = $this->renderTwigComponent(
174+
Input::class,
175+
$this->baseProps([
176+
'attributes' => ['class' => 'extra-class'],
177+
])
178+
)->crawler();
179+
180+
$wrapper = $this->getWrapper($crawler);
181+
182+
self::assertStringContainsString(
183+
'extra-class',
184+
$this->getClassAttr($wrapper),
185+
'Custom class should be merged into wrapper classes.'
186+
);
187+
}
188+
189+
public function testInvalidItemsTypeCausesResolverErrorOnMount(): void
190+
{
191+
$this->expectException(InvalidOptionsException::class);
192+
193+
$this->mountTwigComponent(Input::class, [
194+
'name' => 'group',
195+
'items' => 'not-an-array',
196+
]);
197+
}
198+
199+
public function testMissingRequiredOptionsCauseResolverErrorOnMount(): void
200+
{
201+
$this->expectException(MissingOptionsException::class);
202+
203+
$this->mountTwigComponent(Input::class);
204+
}
205+
206+
/**
207+
* @param array<string, mixed> $overrides
208+
*
209+
* @return array<string, mixed>
210+
*/
211+
private function baseProps(array $overrides = []): array
212+
{
213+
return array_replace([
214+
'name' => 'group',
215+
'items' => [
216+
['id' => 'a', 'label' => 'Alpha'],
217+
['id' => 'b', 'label' => 'Beta'],
218+
['id' => 'c', 'label' => 'Gamma'],
219+
],
220+
], $overrides);
221+
}
222+
223+
private function getWrapper(Crawler $crawler): Crawler
224+
{
225+
$node = $crawler->filter('.ids-dropdown')->first();
226+
self::assertGreaterThan(0, $node->count(), 'Wrapper ".ids-dropdown" should be present.');
227+
228+
return $node;
229+
}
230+
231+
private function getWidget(Crawler $crawler): Crawler
232+
{
233+
$node = $crawler->filter('.ids-dropdown__widget')->first();
234+
self::assertGreaterThan(0, $node->count(), 'Widget ".ids-dropdown__widget" should be present.');
235+
236+
return $node;
237+
}
238+
239+
private function getSelect(Crawler $crawler): Crawler
240+
{
241+
$node = $crawler->filter('.ids-dropdown__source > select')->first();
242+
self::assertGreaterThan(0, $node->count(), 'Source <select> should be present.');
243+
244+
return $node;
245+
}
246+
247+
private function getClassAttr(Crawler $node): string
248+
{
249+
return (string) $node->attr('class');
250+
}
251+
}

0 commit comments

Comments
 (0)