Skip to content

Commit 5765598

Browse files
committed
Added tests for DropdownSingle twig component
1 parent 7350356 commit 5765598

File tree

1 file changed

+176
-0
lines changed

1 file changed

+176
-0
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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(Input::class, $component, 'Component should mount as DropdownSingle\\Input.');
27+
}
28+
29+
public function testEmptyStateShowsPlaceholderAndHidesSelectedItems(): void
30+
{
31+
$crawler = $this->renderTwigComponent(
32+
Input::class,
33+
$this->baseProps()
34+
)->crawler();
35+
36+
$select = $this->getSelect($crawler);
37+
$selectedOption = $select->filter('option[selected]')->first();
38+
self::assertSame('a', $selectedOption->attr('value'), 'Without a value, the first option should be selected by default.');
39+
self::assertSame('Alpha', trim($selectedOption->text('')), 'The first option label should be shown as selected by default.');
40+
41+
$placeholder = $crawler->filter('.ids-dropdown__placeholder')->first();
42+
$selectedBox = $crawler->filter('.ids-dropdown__selection-info-items')->first();
43+
44+
self::assertNotNull($placeholder->attr('hidden'), 'Placeholder should be hidden when no explicit value is selected.');
45+
self::assertNull($selectedBox->attr('hidden'), 'Selection box should be visible when no explicit value is selected.');
46+
self::assertStringContainsString('Alpha', trim($selectedBox->text('')), 'Selection box should show the first item label by default.');
47+
}
48+
49+
public function testSelectedValueHidesPlaceholderAndShowsSelectedLabel(): void
50+
{
51+
$crawler = $this->renderTwigComponent(Input::class, $this->baseProps(['value' => 'b']))->crawler();
52+
53+
$select = $this->getSelect($crawler);
54+
$selectedOption = $select->filter('option[selected]')->first();
55+
56+
self::assertSame('b', $selectedOption->attr('value'), 'Selected <option> should match provided value.');
57+
self::assertSame('Beta', trim($selectedOption->text('')), 'Selected <option> text should be the item label.');
58+
59+
$placeholder = $crawler->filter('.ids-dropdown__placeholder')->first();
60+
$selectedBox = $crawler->filter('.ids-dropdown__selection-info-items')->first();
61+
62+
self::assertNotNull($placeholder->attr('hidden'), 'Placeholder should be hidden when a value is selected.');
63+
self::assertNull($selectedBox->attr('hidden'), 'Selection box should be visible when a value is selected.');
64+
self::assertStringContainsString('Beta', trim($selectedBox->text('')), 'Selection box should render selected label.');
65+
}
66+
67+
public function testOptionsAreRenderedAndOneIsMarkedSelected(): void
68+
{
69+
$crawler = $this->renderTwigComponent(Input::class, $this->baseProps(['value' => 'a']))->crawler();
70+
71+
$select = $this->getSelect($crawler);
72+
$options = $select->filter('option');
73+
74+
self::assertSame(3, $options->count(), 'Should render three <option> elements.');
75+
self::assertSame('a', $select->filter('option[selected]')->attr('value'), 'The <option> with selected attribute should match provided value.');
76+
77+
$labels = $options->each(static fn (Crawler $o) => trim($o->text('')));
78+
79+
self::assertSame(['Alpha', 'Beta', 'Gamma'], $labels, 'Options should render provided labels in order.');
80+
}
81+
82+
public function testDisabledAndErrorAddClassesAndSelectDisabled(): void
83+
{
84+
$crawler = $this->renderTwigComponent(
85+
Input::class,
86+
$this->baseProps(['disabled' => true, 'error' => true])
87+
)->crawler();
88+
89+
$wrapper = $this->getWrapper($crawler);
90+
$widget = $this->getWidget($crawler);
91+
$select = $this->getSelect($crawler);
92+
93+
self::assertStringContainsString('ids-dropdown--disabled', $this->getClassAttr($wrapper), 'Wrapper should include disabled modifier.');
94+
self::assertStringContainsString('ids-dropdown--error', $this->getClassAttr($wrapper), 'Wrapper should include error modifier.');
95+
self::assertStringContainsString('ids-input--disabled', $this->getClassAttr($widget), 'Widget should include disabled modifier.');
96+
self::assertStringContainsString('ids-input--error', $this->getClassAttr($widget), 'Widget should include error modifier.');
97+
self::assertNotNull($select->attr('disabled'), 'Native "disabled" attribute should be present on <select> when disabled=true.');
98+
}
99+
100+
public function testWrapperClassMergesFromAttributes(): void
101+
{
102+
$crawler = $this->renderTwigComponent(
103+
Input::class,
104+
$this->baseProps([
105+
'attributes' => ['class' => 'extra-class'],
106+
])
107+
)->crawler();
108+
109+
$wrapper = $this->getWrapper($crawler);
110+
111+
self::assertStringContainsString('extra-class', $this->getClassAttr($wrapper), 'Custom class should be merged into wrapper classes.');
112+
}
113+
114+
public function testInvalidItemsTypeCausesResolverErrorOnMount(): void
115+
{
116+
$this->expectException(InvalidOptionsException::class);
117+
118+
$this->mountTwigComponent(Input::class, [
119+
'name' => 'group',
120+
'items' => 'not-an-array',
121+
]);
122+
}
123+
124+
public function testMissingRequiredOptionsCauseResolverErrorOnMount(): void
125+
{
126+
$this->expectException(MissingOptionsException::class);
127+
128+
$this->mountTwigComponent(Input::class);
129+
}
130+
131+
/**
132+
* @param array<string, mixed> $overrides
133+
*
134+
* @return array<string, mixed>
135+
*/
136+
private function baseProps(array $overrides = []): array
137+
{
138+
return array_replace([
139+
'name' => 'group',
140+
'items' => [
141+
['id' => 'a', 'label' => 'Alpha'],
142+
['id' => 'b', 'label' => 'Beta'],
143+
['id' => 'c', 'label' => 'Gamma'],
144+
],
145+
], $overrides);
146+
}
147+
148+
private function getWrapper(Crawler $crawler): Crawler
149+
{
150+
$node = $crawler->filter('.ids-dropdown')->first();
151+
self::assertGreaterThan(0, $node->count(), 'Wrapper ".ids-dropdown" should be present.');
152+
153+
return $node;
154+
}
155+
156+
private function getWidget(Crawler $crawler): Crawler
157+
{
158+
$node = $crawler->filter('.ids-dropdown__widget')->first();
159+
self::assertGreaterThan(0, $node->count(), 'Widget ".ids-dropdown__widget" should be present.');
160+
161+
return $node;
162+
}
163+
164+
private function getSelect(Crawler $crawler): Crawler
165+
{
166+
$node = $crawler->filter('.ids-dropdown__source > select')->first();
167+
self::assertGreaterThan(0, $node->count(), 'Source <select> should be present.');
168+
169+
return $node;
170+
}
171+
172+
private function getClassAttr(Crawler $node): string
173+
{
174+
return (string) $node->attr('class');
175+
}
176+
}

0 commit comments

Comments
 (0)