Skip to content

[Icons] Add Icon + Iconify tests #1617

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Icons/src/DependencyInjection/UXIconsExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\UX\Icons\Iconify;

/**
* @author Kevin Bond <[email protected]>
Expand Down Expand Up @@ -52,7 +53,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->end()
->scalarNode('endpoint')
->info('The endpoint for the Iconify API.')
->defaultValue('https://api.iconify.design')
->defaultValue(Iconify::API_ENDPOINT)
->cannotBeEmpty()
->end()
->end()
Expand Down
4 changes: 3 additions & 1 deletion src/Icons/src/Iconify.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@
*/
final class Iconify
{
public const API_ENDPOINT = 'https://api.iconify.design';

private HttpClientInterface $http;
private \ArrayObject $sets;

public function __construct(
private CacheInterface $cache,
string $endpoint = 'https://api.iconify.design',
string $endpoint = self::API_ENDPOINT,
?HttpClientInterface $http = null,
) {
if (!class_exists(HttpClient::class)) {
Expand Down
27 changes: 1 addition & 26 deletions src/Icons/src/Svg/Icon.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*
* @internal
*/
final class Icon implements \Stringable, \Serializable, \ArrayAccess
final class Icon implements \Stringable, \Serializable
{
/**
* Transforms a valid icon ID into an icon name.
Expand Down Expand Up @@ -188,11 +188,6 @@ public function withAttributes(array $attributes): self
return new self($this->innerSvg, [...$this->attributes, ...$attributes]);
}

public function withInnerSvg(string $innerSvg): self
{
return new self($innerSvg, $this->attributes);
}

public function __toString(): string
{
return $this->toHtml();
Expand All @@ -217,24 +212,4 @@ public function __unserialize(array $data): void
{
[$this->innerSvg, $this->attributes] = $data;
}

public function offsetExists(mixed $offset): bool
{
return isset($this->attributes[$offset]);
}

public function offsetGet(mixed $offset): mixed
{
return $this->attributes[$offset];
}

public function offsetSet(mixed $offset, mixed $value): void
{
throw new \LogicException('The Icon object is immutable.');
}

public function offsetUnset(mixed $offset): void
{
throw new \LogicException('The Icon object is immutable.');
}
}
48 changes: 48 additions & 0 deletions src/Icons/tests/Fixtures/Iconify/collections.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"fa6-solid": {
"name": "Font Awesome Solid",
"total": 1388,
"version": "6.2.0",
"author": {
"name": "Dave Gandy",
"url": "https://github.com/FortAwesome/Font-Awesome"
},
"license": {
"title": "CC BY 4.0",
"spdx": "CC-BY-4.0",
"url": "https://creativecommons.org/licenses/by/4.0/"
},
"samples": [
"location-pin",
"gem",
"folder"
],
"height": 32,
"displayHeight": 16,
"category": "General",
"palette": false
},
"fa6-regular": {
"name": "Font Awesome Regular",
"total": 163,
"version": "6.2.0",
"author": {
"name": "Dave Gandy",
"url": "https://github.com/FortAwesome/Font-Awesome"
},
"license": {
"title": "CC BY 4.0",
"spdx": "CC-BY-4.0",
"url": "https://creativecommons.org/licenses/by/4.0/"
},
"samples": [
"message",
"clock",
"folder"
],
"height": 32,
"displayHeight": 16,
"category": "General",
"palette": false
}
}
12 changes: 12 additions & 0 deletions src/Icons/tests/Fixtures/Iconify/icon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"prefix": "bi",
"lastModified": 1704439122,
"aliases": {},
"width": 16,
"height": 16,
"icons": {
"heart": {
"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\"/>"
}
}
}
1 change: 1 addition & 0 deletions src/Icons/tests/Fixtures/Iconify/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 35 additions & 7 deletions src/Icons/tests/Unit/IconifyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\Cache\Adapter\NullAdapter;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\JsonMockResponse;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\UX\Icons\Exception\IconNotFoundException;
use Symfony\UX\Icons\Iconify;

Expand Down Expand Up @@ -51,13 +52,7 @@ public function testFetchIcon(): void

public function testFetchIconThrowsWhenIconSetDoesNotExists(): void
{
$iconify = new Iconify(
cache: new NullAdapter(),
endpoint: 'https://example.com',
http: new MockHttpClient([
new JsonMockResponse([]),
]),
);
$iconify = new Iconify(new NullAdapter(), 'https://example.com', new MockHttpClient(new JsonMockResponse([])));

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

$iconify->fetchIcon('bi', 'heart');
}

public function testGetMetadata(): void
{
$responseFile = __DIR__.'/../Fixtures/Iconify/collections.json';
$client = $this->createHttpClient(json_decode(file_get_contents($responseFile)));
$iconify = new Iconify(new NullAdapter(), 'https://localhost', $client);

$metadata = $iconify->metadataFor('fa6-solid');
$this->assertSame('Font Awesome Solid', $metadata['name']);
}

public function testFetchSvg(): void
{
$client = new MockHttpClient([
new MockResponse(file_get_contents(__DIR__.'/../Fixtures/Iconify/collections.json'), [
'response_headers' => ['content-type' => 'application/json'],
]),
new MockResponse(file_get_contents(__DIR__.'/../Fixtures/Iconify/icon.svg')),
]);
$iconify = new Iconify(new NullAdapter(), 'https://localhost', $client);

$svg = $iconify->fetchSvg('fa6-regular', 'bar');

$this->assertIsString($svg);
$this->stringContains('-.224l.235-.468ZM6.013 2.06c-.649-.1', $svg);
}

private function createHttpClient(mixed $data, int $code = 200): MockHttpClient
{
$mockResponse = new JsonMockResponse($data, ['http_code' => $code]);

return new MockHttpClient($mockResponse);
}
}
74 changes: 73 additions & 1 deletion src/Icons/tests/Unit/Svg/IconTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function testConstructor()
{
$icon = new Icon('foo', ['foo' => 'bar']);
$this->assertSame('foo', $icon->getInnerSvg());
$this->assertSame('bar', $icon['foo']);
$this->assertSame('bar', $icon->getAttributes()['foo']);
}

/**
Expand Down Expand Up @@ -105,6 +105,25 @@ public function testInvalidIdToName(string $id)
Icon::idToName($id);
}

/**
* @dataProvider provideRenderAttributesTestCases
*/
public function testRenderAttributes(array $attributes, string $expected): void
{
$icon = new Icon('', $attributes);
$this->assertStringStartsWith($expected, $icon->toHtml());
}

/**
* @dataProvider provideWithAttributesTestCases
*/
public function testWithAttributes(array $attributes, array $withAttributes, array $expected): void
{
$icon = new Icon('foo', $attributes);
$icon = $icon->withAttributes($withAttributes);
$this->assertSame($expected, $icon->getAttributes());
}

public static function provideIdToName(): iterable
{
yield from [
Expand Down Expand Up @@ -203,4 +222,57 @@ private static function provideInvalidIdentifiers(): iterable
['🙂'],
];
}

public static function provideRenderAttributesTestCases(): iterable
{
yield 'it_renders_empty_attributes' => [
[],
'<svg>',
];
yield 'it_renders_one_attribute' => [
['foo' => 'bar'],
'<svg foo="bar">',
];
yield 'it_renders_multiple_attributes' => [
['foo' => 'bar', 'baz' => 'qux'],
'<svg foo="bar" baz="qux">',
];
yield 'it_renders_true_attribute_with_no_value' => [
['foo' => true],
'<svg foo>',
];
yield 'it_does_not_render_attribute_with_false_value' => [
['foo' => false],
'<svg>',
];
}

public static function provideWithAttributesTestCases(): iterable
{
yield 'it_does_nothing_with_empty_attributes' => [
['foo' => 'bar'],
[],
['foo' => 'bar'],
];
yield 'it_does_nothing_with_same_attributes' => [
['foo' => 'bar'],
['foo' => 'bar'],
['foo' => 'bar'],
];
yield 'it_does_nothing_with_same_attributes_incomplete' => [
['foo' => 'bar', 'baz' => 'qux'],
['foo' => 'bar'],
['foo' => 'bar', 'baz' => 'qux'],
];
yield 'it_replaces_value_with_same_key' => [
['foo' => 'bar'],
['foo' => 'foobar'],
['foo' => 'foobar'],
];
yield 'it_replaces_value_with_same_key_and_keep_others' => [
['foo' => 'bar', 'baz' => 'qux'],
['foo' => 'foobar'],
['foo' => 'foobar', 'baz' => 'qux'],
];
}
}