Add organizer logo upload, Meilisearch organizer search, and webp URL rewriting

VichUploader organizer logo:
- Add organizer_logo mapping with local Flysystem storage
- Add logoFile, logoName, updatedAt fields to User entity
- Use Vich Attribute (not deprecated Annotation)
- Add migration for logo_name and updated_at columns

Meilisearch organizer search:
- Add search bar on /admin/organisateurs page (hides tabs during search)
- Index organizers in Meilisearch on approval
- Sync button on dashboard now syncs both buyers and organizers
- Add tests: search query, search error

Liip Imagine webp:
- Add format filter to all filter_sets for explicit webp conversion
- Add organizer_logo filter_set (400x400, webp)
- Create WebpExtensionSubscriber to rewrite image URLs to .webp extension
- 8 tests for subscriber (png, jpg, jpeg, gif, webp passthrough, case insensitive, null)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-03-19 18:46:34 +01:00
parent a047cfa787
commit 82829f6240
11 changed files with 350 additions and 11 deletions

View File

@@ -107,8 +107,7 @@ class AdminControllerTest extends WebTestCase
$em->flush();
$meilisearch = $this->createMock(MeilisearchService::class);
$meilisearch->expects(self::once())->method('createIndexIfNotExists');
$meilisearch->expects(self::once())->method('addDocuments');
$meilisearch->expects(self::exactly(2))->method('createIndexIfNotExists');
static::getContainer()->set(MeilisearchService::class, $meilisearch);
$client->loginUser($admin);
@@ -329,6 +328,39 @@ class AdminControllerTest extends WebTestCase
self::assertNull($buyer->getEmailVerificationToken());
}
public function testOrganizersSearchWithQuery(): void
{
$client = static::createClient();
$admin = $this->createUser(['ROLE_ROOT']);
$meilisearch = $this->createMock(MeilisearchService::class);
$meilisearch->expects(self::once())->method('search')->with('organizers')->willReturn([
'hits' => [],
'estimatedTotalHits' => 0,
]);
static::getContainer()->set(MeilisearchService::class, $meilisearch);
$client->loginUser($admin);
$client->request('GET', '/admin/organisateurs?q=test');
self::assertResponseIsSuccessful();
}
public function testOrganizersSearchWithError(): void
{
$client = static::createClient();
$admin = $this->createUser(['ROLE_ROOT']);
$meilisearch = $this->createMock(MeilisearchService::class);
$meilisearch->method('search')->willThrowException(new \RuntimeException('Meilisearch down'));
static::getContainer()->set(MeilisearchService::class, $meilisearch);
$client->loginUser($admin);
$client->request('GET', '/admin/organisateurs?q=test');
self::assertResponseIsSuccessful();
}
public function testOrganizersPagePendingTab(): void
{
$client = static::createClient();
@@ -363,6 +395,11 @@ class AdminControllerTest extends WebTestCase
$mailer->expects(self::once())->method('sendEmail');
static::getContainer()->set(MailerService::class, $mailer);
$meilisearch = $this->createMock(MeilisearchService::class);
$meilisearch->expects(self::once())->method('createIndexIfNotExists')->with('organizers');
$meilisearch->expects(self::once())->method('addDocuments');
static::getContainer()->set(MeilisearchService::class, $meilisearch);
$client->loginUser($admin);
$client->request('POST', '/admin/organisateur/'.$orga->getId().'/approuver');

View File

@@ -88,6 +88,33 @@ class UserTest extends TestCase
self::assertNull($user->getPhone());
}
public function testLogoFields(): void
{
$user = new User();
self::assertNull($user->getLogoFile());
self::assertNull($user->getLogoName());
self::assertNull($user->getUpdatedAt());
$result = $user->setLogoName('logo.png');
self::assertSame($user, $result);
self::assertSame('logo.png', $user->getLogoName());
$file = new \Symfony\Component\HttpFoundation\File\File(__FILE__);
$result = $user->setLogoFile($file);
self::assertSame($user, $result);
self::assertSame($file, $user->getLogoFile());
self::assertNotNull($user->getUpdatedAt());
}
public function testSetLogoFileNullDoesNotUpdateTimestamp(): void
{
$user = new User();
$user->setLogoFile(null);
self::assertNull($user->getUpdatedAt());
}
public function testResetCodeFields(): void
{
$user = new User();

View File

@@ -0,0 +1,81 @@
<?php
namespace App\Tests\EventSubscriber;
use App\EventSubscriber\WebpExtensionSubscriber;
use Liip\ImagineBundle\Events\CacheResolveEvent;
use PHPUnit\Framework\TestCase;
class WebpExtensionSubscriberTest extends TestCase
{
private WebpExtensionSubscriber $subscriber;
protected function setUp(): void
{
$this->subscriber = new WebpExtensionSubscriber();
}
public function testGetSubscribedEvents(): void
{
$events = WebpExtensionSubscriber::getSubscribedEvents();
self::assertArrayHasKey('liip_imagine.post_resolve', $events);
self::assertSame('onPostResolve', $events['liip_imagine.post_resolve']);
}
public function testRewritesPngToWebp(): void
{
$event = new CacheResolveEvent('logo.png', 'thumbnail', '/media/cache/thumbnail/logo.png');
$this->subscriber->onPostResolve($event);
self::assertSame('/media/cache/thumbnail/logo.webp', $event->getUrl());
}
public function testRewritesJpgToWebp(): void
{
$event = new CacheResolveEvent('photo.jpg', 'medium', '/media/cache/medium/photo.jpg');
$this->subscriber->onPostResolve($event);
self::assertSame('/media/cache/medium/photo.webp', $event->getUrl());
}
public function testRewritesJpegToWebp(): void
{
$event = new CacheResolveEvent('image.jpeg', 'large', '/media/cache/large/image.jpeg');
$this->subscriber->onPostResolve($event);
self::assertSame('/media/cache/large/image.webp', $event->getUrl());
}
public function testRewritesGifToWebp(): void
{
$event = new CacheResolveEvent('anim.gif', 'thumbnail', '/media/cache/thumbnail/anim.gif');
$this->subscriber->onPostResolve($event);
self::assertSame('/media/cache/thumbnail/anim.webp', $event->getUrl());
}
public function testDoesNotRewriteWebp(): void
{
$event = new CacheResolveEvent('already.webp', 'thumbnail', '/media/cache/thumbnail/already.webp');
$this->subscriber->onPostResolve($event);
self::assertSame('/media/cache/thumbnail/already.webp', $event->getUrl());
}
public function testCaseInsensitive(): void
{
$event = new CacheResolveEvent('logo.PNG', 'thumbnail', '/media/cache/thumbnail/logo.PNG');
$this->subscriber->onPostResolve($event);
self::assertSame('/media/cache/thumbnail/logo.webp', $event->getUrl());
}
public function testNullUrlIsIgnored(): void
{
$event = new CacheResolveEvent('logo.png', 'thumbnail');
$this->subscriber->onPostResolve($event);
self::assertNull($event->getUrl());
}
}