Skip to content

Commit 625a784

Browse files
committed
minor #1617 [Icons] Add Icon + Iconify tests (smnandre)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [Icons] Add Icon + Iconify tests Add missing tests on Icon attributes (html + with/out) (before making changes to the Renderer) Commits ------- ad25b69 [Icons] Add Icon + Iconify tests
2 parents 9d1fbf5 + ad25b69 commit 625a784

File tree

8 files changed

+175
-36
lines changed

8 files changed

+175
-36
lines changed

src/Icons/src/DependencyInjection/UXIconsExtension.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
2020
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
2121
use Symfony\Contracts\HttpClient\HttpClientInterface;
22+
use Symfony\UX\Icons\Iconify;
2223

2324
/**
2425
* @author Kevin Bond <[email protected]>
@@ -52,7 +53,7 @@ public function getConfigTreeBuilder(): TreeBuilder
5253
->end()
5354
->scalarNode('endpoint')
5455
->info('The endpoint for the Iconify API.')
55-
->defaultValue('https://api.iconify.design')
56+
->defaultValue(Iconify::API_ENDPOINT)
5657
->cannotBeEmpty()
5758
->end()
5859
->end()

src/Icons/src/Iconify.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626
*/
2727
final class Iconify
2828
{
29+
public const API_ENDPOINT = 'https://api.iconify.design';
30+
2931
private HttpClientInterface $http;
3032
private \ArrayObject $sets;
3133

3234
public function __construct(
3335
private CacheInterface $cache,
34-
string $endpoint = 'https://api.iconify.design',
36+
string $endpoint = self::API_ENDPOINT,
3537
?HttpClientInterface $http = null,
3638
) {
3739
if (!class_exists(HttpClient::class)) {

src/Icons/src/Svg/Icon.php

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
* @internal
1818
*/
19-
final class Icon implements \Stringable, \Serializable, \ArrayAccess
19+
final class Icon implements \Stringable, \Serializable
2020
{
2121
/**
2222
* Transforms a valid icon ID into an icon name.
@@ -188,11 +188,6 @@ public function withAttributes(array $attributes): self
188188
return new self($this->innerSvg, [...$this->attributes, ...$attributes]);
189189
}
190190

191-
public function withInnerSvg(string $innerSvg): self
192-
{
193-
return new self($innerSvg, $this->attributes);
194-
}
195-
196191
public function __toString(): string
197192
{
198193
return $this->toHtml();
@@ -217,24 +212,4 @@ public function __unserialize(array $data): void
217212
{
218213
[$this->innerSvg, $this->attributes] = $data;
219214
}
220-
221-
public function offsetExists(mixed $offset): bool
222-
{
223-
return isset($this->attributes[$offset]);
224-
}
225-
226-
public function offsetGet(mixed $offset): mixed
227-
{
228-
return $this->attributes[$offset];
229-
}
230-
231-
public function offsetSet(mixed $offset, mixed $value): void
232-
{
233-
throw new \LogicException('The Icon object is immutable.');
234-
}
235-
236-
public function offsetUnset(mixed $offset): void
237-
{
238-
throw new \LogicException('The Icon object is immutable.');
239-
}
240215
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"fa6-solid": {
3+
"name": "Font Awesome Solid",
4+
"total": 1388,
5+
"version": "6.2.0",
6+
"author": {
7+
"name": "Dave Gandy",
8+
"url": "https://github.com/FortAwesome/Font-Awesome"
9+
},
10+
"license": {
11+
"title": "CC BY 4.0",
12+
"spdx": "CC-BY-4.0",
13+
"url": "https://creativecommons.org/licenses/by/4.0/"
14+
},
15+
"samples": [
16+
"location-pin",
17+
"gem",
18+
"folder"
19+
],
20+
"height": 32,
21+
"displayHeight": 16,
22+
"category": "General",
23+
"palette": false
24+
},
25+
"fa6-regular": {
26+
"name": "Font Awesome Regular",
27+
"total": 163,
28+
"version": "6.2.0",
29+
"author": {
30+
"name": "Dave Gandy",
31+
"url": "https://github.com/FortAwesome/Font-Awesome"
32+
},
33+
"license": {
34+
"title": "CC BY 4.0",
35+
"spdx": "CC-BY-4.0",
36+
"url": "https://creativecommons.org/licenses/by/4.0/"
37+
},
38+
"samples": [
39+
"message",
40+
"clock",
41+
"folder"
42+
],
43+
"height": 32,
44+
"displayHeight": 16,
45+
"category": "General",
46+
"palette": false
47+
}
48+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"prefix": "bi",
3+
"lastModified": 1704439122,
4+
"aliases": {},
5+
"width": 16,
6+
"height": 16,
7+
"icons": {
8+
"heart": {
9+
"body": "<path fill=\"currentColor\" d=\"m8 2.748l-.717-.737C5.6.281 2.514.878 1.4 3.053c-.523 1.023-.641 2.5.314 4.385c.92 1.815 2.834 3.989 6.286 6.357c3.452-2.368 5.365-4.542 6.286-6.357c.955-1.886.838-3.362.314-4.385C13.486.878 10.4.28 8.717 2.01zM8 15C-7.333 4.868 3.279-3.04 7.824 1.143q.09.083.176.171a3 3 0 0 1 .176-.17C12.72-3.042 23.333 4.867 8 15\"/>"
10+
}
11+
}
12+
}
Lines changed: 1 addition & 0 deletions
Loading

src/Icons/tests/Unit/IconifyTest.php

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Cache\Adapter\NullAdapter;
1616
use Symfony\Component\HttpClient\MockHttpClient;
1717
use Symfony\Component\HttpClient\Response\JsonMockResponse;
18+
use Symfony\Component\HttpClient\Response\MockResponse;
1819
use Symfony\UX\Icons\Exception\IconNotFoundException;
1920
use Symfony\UX\Icons\Iconify;
2021

@@ -51,13 +52,7 @@ public function testFetchIcon(): void
5152

5253
public function testFetchIconThrowsWhenIconSetDoesNotExists(): void
5354
{
54-
$iconify = new Iconify(
55-
cache: new NullAdapter(),
56-
endpoint: 'https://example.com',
57-
http: new MockHttpClient([
58-
new JsonMockResponse([]),
59-
]),
60-
);
55+
$iconify = new Iconify(new NullAdapter(), 'https://example.com', new MockHttpClient(new JsonMockResponse([])));
6156

6257
$this->expectException(IconNotFoundException::class);
6358
$this->expectExceptionMessage('The icon "bi:heart" does not exist on iconify.design.');
@@ -117,4 +112,37 @@ public function testFetchIconThrowsWhenViewBoxCannotBeComputed(): void
117112

118113
$iconify->fetchIcon('bi', 'heart');
119114
}
115+
116+
public function testGetMetadata(): void
117+
{
118+
$responseFile = __DIR__.'/../Fixtures/Iconify/collections.json';
119+
$client = $this->createHttpClient(json_decode(file_get_contents($responseFile)));
120+
$iconify = new Iconify(new NullAdapter(), 'https://localhost', $client);
121+
122+
$metadata = $iconify->metadataFor('fa6-solid');
123+
$this->assertSame('Font Awesome Solid', $metadata['name']);
124+
}
125+
126+
public function testFetchSvg(): void
127+
{
128+
$client = new MockHttpClient([
129+
new MockResponse(file_get_contents(__DIR__.'/../Fixtures/Iconify/collections.json'), [
130+
'response_headers' => ['content-type' => 'application/json'],
131+
]),
132+
new MockResponse(file_get_contents(__DIR__.'/../Fixtures/Iconify/icon.svg')),
133+
]);
134+
$iconify = new Iconify(new NullAdapter(), 'https://localhost', $client);
135+
136+
$svg = $iconify->fetchSvg('fa6-regular', 'bar');
137+
138+
$this->assertIsString($svg);
139+
$this->stringContains('-.224l.235-.468ZM6.013 2.06c-.649-.1', $svg);
140+
}
141+
142+
private function createHttpClient(mixed $data, int $code = 200): MockHttpClient
143+
{
144+
$mockResponse = new JsonMockResponse($data, ['http_code' => $code]);
145+
146+
return new MockHttpClient($mockResponse);
147+
}
120148
}

src/Icons/tests/Unit/Svg/IconTest.php

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public function testConstructor()
2020
{
2121
$icon = new Icon('foo', ['foo' => 'bar']);
2222
$this->assertSame('foo', $icon->getInnerSvg());
23-
$this->assertSame('bar', $icon['foo']);
23+
$this->assertSame('bar', $icon->getAttributes()['foo']);
2424
}
2525

2626
/**
@@ -105,6 +105,25 @@ public function testInvalidIdToName(string $id)
105105
Icon::idToName($id);
106106
}
107107

108+
/**
109+
* @dataProvider provideRenderAttributesTestCases
110+
*/
111+
public function testRenderAttributes(array $attributes, string $expected): void
112+
{
113+
$icon = new Icon('', $attributes);
114+
$this->assertStringStartsWith($expected, $icon->toHtml());
115+
}
116+
117+
/**
118+
* @dataProvider provideWithAttributesTestCases
119+
*/
120+
public function testWithAttributes(array $attributes, array $withAttributes, array $expected): void
121+
{
122+
$icon = new Icon('foo', $attributes);
123+
$icon = $icon->withAttributes($withAttributes);
124+
$this->assertSame($expected, $icon->getAttributes());
125+
}
126+
108127
public static function provideIdToName(): iterable
109128
{
110129
yield from [
@@ -203,4 +222,57 @@ private static function provideInvalidIdentifiers(): iterable
203222
['🙂'],
204223
];
205224
}
225+
226+
public static function provideRenderAttributesTestCases(): iterable
227+
{
228+
yield 'it_renders_empty_attributes' => [
229+
[],
230+
'<svg>',
231+
];
232+
yield 'it_renders_one_attribute' => [
233+
['foo' => 'bar'],
234+
'<svg foo="bar">',
235+
];
236+
yield 'it_renders_multiple_attributes' => [
237+
['foo' => 'bar', 'baz' => 'qux'],
238+
'<svg foo="bar" baz="qux">',
239+
];
240+
yield 'it_renders_true_attribute_with_no_value' => [
241+
['foo' => true],
242+
'<svg foo>',
243+
];
244+
yield 'it_does_not_render_attribute_with_false_value' => [
245+
['foo' => false],
246+
'<svg>',
247+
];
248+
}
249+
250+
public static function provideWithAttributesTestCases(): iterable
251+
{
252+
yield 'it_does_nothing_with_empty_attributes' => [
253+
['foo' => 'bar'],
254+
[],
255+
['foo' => 'bar'],
256+
];
257+
yield 'it_does_nothing_with_same_attributes' => [
258+
['foo' => 'bar'],
259+
['foo' => 'bar'],
260+
['foo' => 'bar'],
261+
];
262+
yield 'it_does_nothing_with_same_attributes_incomplete' => [
263+
['foo' => 'bar', 'baz' => 'qux'],
264+
['foo' => 'bar'],
265+
['foo' => 'bar', 'baz' => 'qux'],
266+
];
267+
yield 'it_replaces_value_with_same_key' => [
268+
['foo' => 'bar'],
269+
['foo' => 'foobar'],
270+
['foo' => 'foobar'],
271+
];
272+
yield 'it_replaces_value_with_same_key_and_keep_others' => [
273+
['foo' => 'bar', 'baz' => 'qux'],
274+
['foo' => 'foobar'],
275+
['foo' => 'foobar', 'baz' => 'qux'],
276+
];
277+
}
206278
}

0 commit comments

Comments
 (0)