Skip to content

Commit 20ffbba

Browse files
committed
[Icons] Test IconRenderer
Add internal comment to clarify the attributes precedence
1 parent dd85ab2 commit 20ffbba

File tree

2 files changed

+180
-2
lines changed

2 files changed

+180
-2
lines changed

src/Icons/src/IconRenderer.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,20 @@
1919
final class IconRenderer
2020
{
2121
public function __construct(
22-
private IconRegistryInterface $registry,
23-
private array $defaultIconAttributes = [],
22+
private readonly IconRegistryInterface $registry,
23+
private readonly array $defaultIconAttributes = [],
2424
) {
2525
}
2626

2727
/**
28+
* Renders an icon.
29+
*
30+
* Provided attributes are merged with the default attributes.
31+
* Existing icon attributes are then merged with those new attributes.
32+
*
33+
* Precedence order:
34+
* Icon file < Renderer configuration < Renderer invocation
35+
*
2836
* @param array<string,string|bool> $attributes
2937
*/
3038
public function renderIcon(string $name, array $attributes = []): string
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
<?php
2+
3+
namespace Symfony\UX\Icons\Tests\Unit;
4+
5+
use InvalidArgumentException;
6+
use PHPUnit\Framework\TestCase;
7+
use Symfony\UX\Icons\Exception\IconNotFoundException;
8+
use Symfony\UX\Icons\IconRegistryInterface;
9+
use Symfony\UX\Icons\IconRenderer;
10+
use Symfony\UX\Icons\Registry\InMemoryIconRegistry;
11+
use Symfony\UX\Icons\Svg\Icon;
12+
13+
/**
14+
* @author Simon André <[email protected]>
15+
*/
16+
class IconRendererTest extends TestCase
17+
{
18+
public function testRenderIcon(): void
19+
{
20+
$registry = $this->createRegistry([
21+
'user' => '<circle ',
22+
]);
23+
$iconRenderer = new IconRenderer($registry);
24+
25+
$icon = $iconRenderer->renderIcon('user');
26+
27+
$this->assertStringContainsString('<svg', $icon);
28+
$this->assertStringContainsString('<circle', $icon);
29+
}
30+
31+
public function testRenderIconThrowsExceptionWhenIconNotFound(): void
32+
{
33+
$registry = $this->createRegistry([]);
34+
$iconRenderer = new IconRenderer($registry);
35+
36+
$this->expectException(IconNotFoundException::class);
37+
38+
$iconRenderer->renderIcon('foo');
39+
}
40+
41+
public function testRenderIconThrowsExceptionWhenAttributesAreInvalid(): void
42+
{
43+
$registry = $this->createRegistry(['foo' => '<path d="M0 0L12 12"/>']);
44+
$iconRenderer = new IconRenderer($registry);
45+
46+
$this->expectException(InvalidArgumentException::class);
47+
48+
$iconRenderer->renderIcon('foo', [1, 2, null]);
49+
}
50+
51+
public function testRenderIconWithAttributes(): void
52+
{
53+
$registry = $this->createRegistry([
54+
'foo' => '<path d="M0 0L12 12"/>',
55+
]);
56+
$iconRenderer = new IconRenderer($registry);
57+
$attributes = ['viewBox' => '0 0 24 24', 'class' => 'icon', 'id' => 'FooBar'];
58+
59+
$svg = $iconRenderer->renderIcon('foo', $attributes);
60+
61+
$this->assertSame('<svg viewBox="0 0 24 24" class="icon" id="FooBar"><path d="M0 0L12 12"/></svg>', $svg);
62+
}
63+
64+
public function testRenderIconWithDefaultAttributes(): void
65+
{
66+
$registry = $this->createRegistry([
67+
'foo' => '<path d="M0 0L12 12"/>',
68+
]);
69+
$iconRenderer = new IconRenderer($registry, ['viewBox' => '0 0 24 24', 'class' => 'icon']);
70+
71+
$svg = $iconRenderer->renderIcon('foo');
72+
73+
$this->assertSame('<svg viewBox="0 0 24 24" class="icon"><path d="M0 0L12 12"/></svg>', $svg);
74+
}
75+
76+
/**
77+
* @dataProvider provideAttributesWithDefaultAttributesCases
78+
*/
79+
public function testRenderIconWithAttributesAndDefaultAttributes($iconAttrs, $defaultAttrs, $renderAttr, $expectedTag): void
80+
{
81+
$registry = $this->createRegistry([
82+
'foo' => ['', $iconAttrs],
83+
]);
84+
$iconRenderer = new IconRenderer($registry, $defaultAttrs);
85+
86+
$svg = $iconRenderer->renderIcon('foo', $renderAttr);
87+
$this->assertStringStartsWith($expectedTag, $svg);
88+
}
89+
90+
/**
91+
* @return array [iconAttributes, defaultAttributes, renderAttributes, expectedTag]
92+
*/
93+
public static function provideAttributesWithDefaultAttributesCases(): iterable
94+
{
95+
yield 'no_attributes' => [
96+
[],
97+
[],
98+
[],
99+
'<svg>',
100+
];
101+
yield 'icon_attributes_are_used' => [
102+
['id' => 'icon'],
103+
[],
104+
[],
105+
'<svg id="icon">',
106+
];
107+
yield 'default_attributes_are_used' => [
108+
[],
109+
['id' => 'default'],
110+
[],
111+
'<svg id="default">',
112+
];
113+
yield 'render_attributes_are_used' => [
114+
[],
115+
[],
116+
['id' => 'render'],
117+
'<svg id="render">',
118+
];
119+
yield 'default_attributes_take_precedence_on_icon' => [
120+
['id' => 'icon'],
121+
['id' => 'default'],
122+
[],
123+
'<svg id="default">',
124+
];
125+
yield 'default_attributes_are_merged_with_icon_attributes' => [
126+
['id' => 'icon', 'foo' => 'bar'],
127+
['id' => 'default', 'baz' => 'qux'],
128+
[],
129+
'<svg id="default" foo="bar" baz="qux">',
130+
];
131+
yield 'render_attributes_take_precedence_on_default' => [
132+
[],
133+
['id' => 'default'],
134+
['id' => 'render'],
135+
'<svg id="render">',
136+
];
137+
yield 'render_attributes_are_merged_with_default_attributes' => [
138+
[],
139+
['id' => 'default', 'foo' => 'bar'],
140+
['id' => 'render', 'baz' => 'qux'],
141+
'<svg id="render" foo="bar" baz="qux">',
142+
];
143+
yield 'render_attributes_take_precedence_on_icon' => [
144+
['id' => 'icon'],
145+
[],
146+
['id' => 'render'],
147+
'<svg id="render">',
148+
];
149+
yield 'render_attributes_are_merged_with_icon_attributes' => [
150+
['id' => 'icon', 'foo' => 'bar'],
151+
[],
152+
['id' => 'render', 'baz' => 'qux'],
153+
'<svg id="render" foo="bar" baz="qux">',
154+
];
155+
}
156+
157+
private function createRegistry(array $icons): IconRegistryInterface
158+
{
159+
$registryIcons = [];
160+
foreach ($icons as $name => $data) {
161+
$data = (array) $data;
162+
if (array_is_list($data)) {
163+
$data = ['innerSvg' => $data[0], 'attributes' => $data[1] ?? []];
164+
}
165+
$registryIcons[$name] = new Icon(...$data);
166+
}
167+
168+
return new InMemoryIconRegistry($registryIcons);
169+
}
170+
}

0 commit comments

Comments
 (0)