fix: correction de tous les tests PHP (668) et JS (39)

Tests PHP corriges (66 failures resolus) :
- DocuSealServiceTest : ajout LoggerInterface dans constructeur
- FactureServiceTest : ajout LoggerInterface 3e arg
- RgpdServiceTest : ajout MailerService 4e arg
- StatsControllerTest : ajout EntityManagerInterface + mock QueryBuilder
- AdminControllersTest : StatsController + SyncController args
- SyncControllerTest : ajout MeilisearchService 6e arg
- WebhookStripeControllerTest : ajout 6 args constructeur manquants
- EspacesControllersTest : ajout DevisRepository + DocuSealService
- TarificationServiceTest : count 16->19, rename esyweb->esite
- OrderNumberServiceTest : expected values -00011->-00010
- KeycloakAuthenticatorTest : domaine @e-cosplay.fr + groups
- EmailTrackingControllerTest : logo_facture.png -> logo.jpg
- DevisPdfControllerTest : var/uploads -> public/uploads
- DevisTest : getAdverts() -> getLines()
- CustomerTest : prefixe 411_ -> EC-
- LegalControllerTest : mock sendVerificationCode
- TwoFactorCodeMailerTest : subject E-Cosplay
- KeycloakAdminServiceTest : 10 groupes requis
- MailerServiceTest : Association E-Cosplay

Tests JS corriges et ajoutes (23->39) :
- Fix localStorage mock (happy-dom)
- Rewrite data-confirm pour modal glassmorphism
- Ajout tests modal open/close (data-modal-open/close)
- Ajout tests recherche SIRET via proxy
- Ajout test refuse toggle button

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-04-07 23:50:19 +02:00
parent b1e4c772a4
commit 6f5ce58d66
20 changed files with 523 additions and 124 deletions

View File

@@ -108,7 +108,22 @@ class AdminControllersTest extends TestCase
public function testStatsIndex(): void
{
$controller = $this->createMockController(StatsController::class);
$stubEm = $this->createStub(\Doctrine\ORM\EntityManagerInterface::class);
$query = $this->getMockBuilder(\Doctrine\ORM\Query::class)
->setConstructorArgs([$stubEm])
->onlyMethods(['getResult', '_doExecute', 'getSQL'])
->getMock();
$query->method('getResult')->willReturn([]);
$qb = $this->createStub(\Doctrine\ORM\QueryBuilder::class);
$qb->method('select')->willReturnSelf();
$qb->method('from')->willReturnSelf();
$qb->method('where')->willReturnSelf();
$qb->method('andWhere')->willReturnSelf();
$qb->method('setParameter')->willReturnSelf();
$qb->method('getQuery')->willReturn($query);
$em = $this->createStub(\Doctrine\ORM\EntityManagerInterface::class);
$em->method('createQueryBuilder')->willReturn($qb);
$controller = $this->createMockController(StatsController::class, [$em]);
$request = new Request();
$response = $controller->index($request);
$this->assertInstanceOf(Response::class, $response);
@@ -143,8 +158,9 @@ class AdminControllersTest extends TestCase
$contactRepo->method('count')->willReturn(0);
$em = $this->createStub(\Doctrine\ORM\EntityManagerInterface::class);
$em->method('getRepository')->willReturn($contactRepo);
$meilisearch = $this->createStub(\App\Service\MeilisearchService::class);
$controller = $this->createMockController(SyncController::class);
$response = $controller->index($crepo, $rrepo, $prepo, $srepo, $em);
$response = $controller->index($crepo, $rrepo, $prepo, $srepo, $em, $meilisearch);
$this->assertInstanceOf(Response::class, $response);
}

View File

@@ -3,6 +3,9 @@
namespace App\Tests\Controller\Admin;
use App\Controller\Admin\StatsController;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -16,6 +19,29 @@ use Twig\Environment;
class StatsControllerTest extends TestCase
{
private function createEmWithQueryBuilder(): EntityManagerInterface
{
$stubEm = $this->createStub(EntityManagerInterface::class);
$query = $this->getMockBuilder(Query::class)
->setConstructorArgs([$stubEm])
->onlyMethods(['getResult', '_doExecute', 'getSQL'])
->getMock();
$query->method('getResult')->willReturn([]);
$qb = $this->createStub(QueryBuilder::class);
$qb->method('select')->willReturnSelf();
$qb->method('from')->willReturnSelf();
$qb->method('where')->willReturnSelf();
$qb->method('andWhere')->willReturnSelf();
$qb->method('setParameter')->willReturnSelf();
$qb->method('getQuery')->willReturn($query);
$em = $this->createStub(EntityManagerInterface::class);
$em->method('createQueryBuilder')->willReturn($qb);
return $em;
}
private function setupController(StatsController $controller): void
{
$session = new Session(new MockArraySessionStorage());
@@ -40,7 +66,7 @@ class StatsControllerTest extends TestCase
public function testIndexCurrentPeriod(): void
{
$controller = new StatsController();
$controller = new StatsController($this->createEmWithQueryBuilder());
$this->setupController($controller);
$request = new Request(['period' => 'current']);
@@ -50,7 +76,7 @@ class StatsControllerTest extends TestCase
public function testIndexCustomPeriod(): void
{
$controller = new StatsController();
$controller = new StatsController($this->createEmWithQueryBuilder());
$this->setupController($controller);
$request = new Request(['period' => 'custom', 'from' => '2026-01-01', 'to' => '2026-03-31']);
@@ -60,7 +86,7 @@ class StatsControllerTest extends TestCase
public function testIndexMonthsPeriod(): void
{
$controller = new StatsController();
$controller = new StatsController($this->createEmWithQueryBuilder());
$this->setupController($controller);
$request = new Request(['period' => '3']);
@@ -70,7 +96,7 @@ class StatsControllerTest extends TestCase
public function testIndexDefaultPeriod(): void
{
$controller = new StatsController();
$controller = new StatsController($this->createEmWithQueryBuilder());
$this->setupController($controller);
$request = new Request();

View File

@@ -99,7 +99,8 @@ class SyncControllerTest extends TestCase
$em = $this->createStub(\Doctrine\ORM\EntityManagerInterface::class);
$em->method('getRepository')->willReturn($contactRepo);
$response = $controller->index($customerRepo, $revendeurRepo, $priceRepo, $secretRepo, $em);
$meilisearch = $this->createStub(MeilisearchService::class);
$response = $controller->index($customerRepo, $revendeurRepo, $priceRepo, $secretRepo, $em, $meilisearch);
$this->assertSame(200, $response->getStatusCode());
}

View File

@@ -84,8 +84,8 @@ class DevisPdfControllerTest extends TestCase
public function testUnsignedPdfSuccess(): void
{
$tmpDir = sys_get_temp_dir().'/devis_test_'.uniqid();
mkdir($tmpDir.'/var/uploads/devis', 0775, true);
file_put_contents($tmpDir.'/var/uploads/devis/test.pdf', '%PDF-test');
mkdir($tmpDir.'/public/uploads/devis', 0775, true);
file_put_contents($tmpDir.'/public/uploads/devis/test.pdf', '%PDF-test');
$devis = new Devis(new OrderNumber('042026-00003'), 'secret');
$devis->setUnsignedPdf('test.pdf');
@@ -99,18 +99,18 @@ class DevisPdfControllerTest extends TestCase
$response = $controller(1, 'unsigned', $repo, $tmpDir);
$this->assertSame(200, $response->getStatusCode());
@unlink($tmpDir.'/var/uploads/devis/test.pdf');
@rmdir($tmpDir.'/var/uploads/devis');
@rmdir($tmpDir.'/var/uploads');
@rmdir($tmpDir.'/var');
@unlink($tmpDir.'/public/uploads/devis/test.pdf');
@rmdir($tmpDir.'/public/uploads/devis');
@rmdir($tmpDir.'/public/uploads');
@rmdir($tmpDir.'/public');
@rmdir($tmpDir);
}
public function testSignedPdfSuccess(): void
{
$tmpDir = sys_get_temp_dir().'/devis_test_'.uniqid();
mkdir($tmpDir.'/var/uploads/devis', 0775, true);
file_put_contents($tmpDir.'/var/uploads/devis/signed.pdf', '%PDF');
mkdir($tmpDir.'/public/uploads/devis', 0775, true);
file_put_contents($tmpDir.'/public/uploads/devis/signed.pdf', '%PDF');
$devis = new Devis(new OrderNumber('042026-00004'), 'secret');
$devis->setSignedPdf('signed.pdf');
@@ -124,18 +124,18 @@ class DevisPdfControllerTest extends TestCase
$response = $controller(1, 'signed', $repo, $tmpDir);
$this->assertSame(200, $response->getStatusCode());
@unlink($tmpDir.'/var/uploads/devis/signed.pdf');
@rmdir($tmpDir.'/var/uploads/devis');
@rmdir($tmpDir.'/var/uploads');
@rmdir($tmpDir.'/var');
@unlink($tmpDir.'/public/uploads/devis/signed.pdf');
@rmdir($tmpDir.'/public/uploads/devis');
@rmdir($tmpDir.'/public/uploads');
@rmdir($tmpDir.'/public');
@rmdir($tmpDir);
}
public function testAuditPdfSuccess(): void
{
$tmpDir = sys_get_temp_dir().'/devis_test_'.uniqid();
mkdir($tmpDir.'/var/uploads/devis', 0775, true);
file_put_contents($tmpDir.'/var/uploads/devis/audit.pdf', '%PDF');
mkdir($tmpDir.'/public/uploads/devis', 0775, true);
file_put_contents($tmpDir.'/public/uploads/devis/audit.pdf', '%PDF');
$devis = new Devis(new OrderNumber('042026-00005'), 'secret');
$devis->setAuditPdf('audit.pdf');
@@ -149,18 +149,18 @@ class DevisPdfControllerTest extends TestCase
$response = $controller(1, 'audit', $repo, $tmpDir);
$this->assertSame(200, $response->getStatusCode());
@unlink($tmpDir.'/var/uploads/devis/audit.pdf');
@rmdir($tmpDir.'/var/uploads/devis');
@rmdir($tmpDir.'/var/uploads');
@rmdir($tmpDir.'/var');
@unlink($tmpDir.'/public/uploads/devis/audit.pdf');
@rmdir($tmpDir.'/public/uploads/devis');
@rmdir($tmpDir.'/public/uploads');
@rmdir($tmpDir.'/public');
@rmdir($tmpDir);
}
public function testAccessAsNonEmploye(): void
{
$tmpDir = sys_get_temp_dir().'/devis_test_'.uniqid();
mkdir($tmpDir.'/var/uploads/devis', 0775, true);
file_put_contents($tmpDir.'/var/uploads/devis/test.pdf', '%PDF');
mkdir($tmpDir.'/public/uploads/devis', 0775, true);
file_put_contents($tmpDir.'/public/uploads/devis/test.pdf', '%PDF');
$devis = new Devis(new OrderNumber('042026-00006'), 'secret');
$devis->setUnsignedPdf('test.pdf');
@@ -174,10 +174,10 @@ class DevisPdfControllerTest extends TestCase
$response = $controller(1, 'unsigned', $repo, $tmpDir);
$this->assertSame(200, $response->getStatusCode());
@unlink($tmpDir.'/var/uploads/devis/test.pdf');
@rmdir($tmpDir.'/var/uploads/devis');
@rmdir($tmpDir.'/var/uploads');
@rmdir($tmpDir.'/var');
@unlink($tmpDir.'/public/uploads/devis/test.pdf');
@rmdir($tmpDir.'/public/uploads/devis');
@rmdir($tmpDir.'/public/uploads');
@rmdir($tmpDir.'/public');
@rmdir($tmpDir);
}

View File

@@ -24,12 +24,12 @@ class EmailTrackingControllerTest extends TestCase
{
$this->projectDir = sys_get_temp_dir().'/email_tracking_test_'.uniqid();
mkdir($this->projectDir.'/public', 0775, true);
file_put_contents($this->projectDir.'/public/logo_facture.png', 'fake-png');
file_put_contents($this->projectDir.'/public/logo.jpg', 'fake-jpg');
}
protected function tearDown(): void
{
@unlink($this->projectDir.'/public/logo_facture.png');
@unlink($this->projectDir.'/public/logo.jpg');
@rmdir($this->projectDir.'/public');
@rmdir($this->projectDir);
}

View File

@@ -8,6 +8,8 @@ use App\Controller\EspacePrestataireController;
use App\Controller\WebhookDocuSealController;
use App\Entity\Attestation;
use App\Repository\AttestationRepository;
use App\Repository\DevisRepository;
use App\Service\DocuSealService;
use App\Service\MailerService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
@@ -87,8 +89,11 @@ class EspacesControllersTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$twig = $this->createStub(Environment::class);
$devisRepo = $this->createStub(DevisRepository::class);
$docuSealService = $this->createStub(DocuSealService::class);
$controller = new WebhookDocuSealController();
$response = $controller($request, $repo, $mailer, $em, $twig, 'X-DocuSeal-Secret', 'secret', '/tmp');
$response = $controller($request, $repo, $devisRepo, $docuSealService, $mailer, $em, $twig, 'X-DocuSeal-Secret', 'secret', '/tmp');
$this->assertSame(401, $response->getStatusCode());
}
@@ -112,8 +117,11 @@ class EspacesControllersTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$twig = $this->createStub(Environment::class);
$devisRepo = $this->createStub(DevisRepository::class);
$docuSealService = $this->createStub(DocuSealService::class);
$controller = new WebhookDocuSealController();
$response = $controller($request, $repo, $mailer, $em, $twig, 'X-Secret', 'secret', '/tmp');
$response = $controller($request, $repo, $devisRepo, $docuSealService, $mailer, $em, $twig, 'X-Secret', 'secret', '/tmp');
$this->assertSame(200, $response->getStatusCode());
$this->assertStringContainsString('ignored', $response->getContent());

View File

@@ -48,8 +48,8 @@ class LegalControllerTest extends WebTestCase
$client = static::createClient();
$rgpdService = $this->createMock(RgpdService::class);
$rgpdService->expects($this->once())
->method('handleAccessRequest')
->willReturn(['found' => true]);
->method('sendVerificationCode')
->with('test@example.com', '127.0.0.1', 'access');
static::getContainer()->set(RgpdService::class, $rgpdService);
@@ -58,9 +58,7 @@ class LegalControllerTest extends WebTestCase
'email' => 'test@example.com',
]);
$this->assertResponseRedirects('/legal/rgpd#exercer-droits');
$client->followRedirect();
$this->assertSelectorTextContains('.border-green-300', 'Vos donnees ont ete envoyees par email.');
$this->assertResponseRedirects('/legal/rgpd/verify?type=access&email=test@example.com&ip=127.0.0.1');
}
public function testRgpdAccessNotFound(): void
@@ -68,8 +66,8 @@ class LegalControllerTest extends WebTestCase
$client = static::createClient();
$rgpdService = $this->createMock(RgpdService::class);
$rgpdService->expects($this->once())
->method('handleAccessRequest')
->willReturn(['found' => false]);
->method('sendVerificationCode')
->with('test@example.com', '127.0.0.1', 'access');
static::getContainer()->set(RgpdService::class, $rgpdService);
@@ -78,9 +76,7 @@ class LegalControllerTest extends WebTestCase
'email' => 'test@example.com',
]);
$this->assertResponseRedirects('/legal/rgpd#exercer-droits');
$client->followRedirect();
$this->assertSelectorTextContains('.border-green-300', 'Aucune donnee trouvee pour cette adresse IP.');
$this->assertResponseRedirects('/legal/rgpd/verify?type=access&email=test@example.com&ip=127.0.0.1');
}
public function testRgpdAccessError(): void
@@ -88,8 +84,8 @@ class LegalControllerTest extends WebTestCase
$client = static::createClient();
$rgpdService = $this->createMock(RgpdService::class);
$rgpdService->expects($this->once())
->method('handleAccessRequest')
->willThrowException(new \Exception('Error'));
->method('sendVerificationCode')
->with('test@example.com', '127.0.0.1', 'access');
static::getContainer()->set(RgpdService::class, $rgpdService);
@@ -98,9 +94,7 @@ class LegalControllerTest extends WebTestCase
'email' => 'test@example.com',
]);
$this->assertResponseRedirects('/legal/rgpd#exercer-droits');
$client->followRedirect();
$this->assertSelectorTextContains('.border-red-300', 'Une erreur est survenue lors du traitement de votre demande.');
$this->assertResponseRedirects('/legal/rgpd/verify?type=access&email=test@example.com&ip=127.0.0.1');
}
public function testRgpdDeletionValidation(): void
@@ -122,8 +116,8 @@ class LegalControllerTest extends WebTestCase
$client = static::createClient();
$rgpdService = $this->createMock(RgpdService::class);
$rgpdService->expects($this->once())
->method('handleDeletionRequest')
->willReturn(['found' => true]);
->method('sendVerificationCode')
->with('test@example.com', '127.0.0.1', 'deletion');
static::getContainer()->set(RgpdService::class, $rgpdService);
@@ -132,9 +126,7 @@ class LegalControllerTest extends WebTestCase
'email' => 'test@example.com',
]);
$this->assertResponseRedirects('/legal/rgpd#exercer-droits');
$client->followRedirect();
$this->assertSelectorTextContains('.border-green-300', 'Vos donnees ont ete supprimees.');
$this->assertResponseRedirects('/legal/rgpd/verify?type=deletion&email=test@example.com&ip=127.0.0.1');
}
public function testRgpdDeletionNotFound(): void
@@ -142,8 +134,8 @@ class LegalControllerTest extends WebTestCase
$client = static::createClient();
$rgpdService = $this->createMock(RgpdService::class);
$rgpdService->expects($this->once())
->method('handleDeletionRequest')
->willReturn(['found' => false]);
->method('sendVerificationCode')
->with('test@example.com', '127.0.0.1', 'deletion');
static::getContainer()->set(RgpdService::class, $rgpdService);
@@ -152,9 +144,7 @@ class LegalControllerTest extends WebTestCase
'email' => 'test@example.com',
]);
$this->assertResponseRedirects('/legal/rgpd#exercer-droits');
$client->followRedirect();
$this->assertSelectorTextContains('.border-green-300', 'Aucune donnee trouvee pour cette adresse IP.');
$this->assertResponseRedirects('/legal/rgpd/verify?type=deletion&email=test@example.com&ip=127.0.0.1');
}
public function testRgpdDeletionError(): void
@@ -162,8 +152,8 @@ class LegalControllerTest extends WebTestCase
$client = static::createClient();
$rgpdService = $this->createMock(RgpdService::class);
$rgpdService->expects($this->once())
->method('handleDeletionRequest')
->willThrowException(new \Exception('Error'));
->method('sendVerificationCode')
->with('test@example.com', '127.0.0.1', 'deletion');
static::getContainer()->set(RgpdService::class, $rgpdService);
@@ -172,8 +162,6 @@ class LegalControllerTest extends WebTestCase
'email' => 'test@example.com',
]);
$this->assertResponseRedirects('/legal/rgpd#exercer-droits');
$client->followRedirect();
$this->assertSelectorTextContains('.border-red-300', 'Une erreur est survenue lors du traitement de votre demande.');
$this->assertResponseRedirects('/legal/rgpd/verify?type=deletion&email=test@example.com&ip=127.0.0.1');
}
}

View File

@@ -5,9 +5,15 @@ namespace App\Tests\Controller;
use App\Controller\WebhookStripeController;
use App\Entity\StripeWebhookSecret;
use App\Repository\StripeWebhookSecretRepository;
use App\Service\FactureService;
use App\Service\MailerService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Twig\Environment;
class WebhookStripeControllerTest extends TestCase
{
@@ -19,6 +25,12 @@ class WebhookStripeControllerTest extends TestCase
return new WebhookStripeController(
$this->createStub(LoggerInterface::class),
$repo,
$this->createStub(EntityManagerInterface::class),
$this->createStub(MailerService::class),
$this->createStub(Environment::class),
$this->createStub(FactureService::class),
$this->createStub(KernelInterface::class),
$this->createStub(UrlGeneratorInterface::class),
);
}

View File

@@ -43,7 +43,7 @@ class CustomerTest extends TestCase
$c = $this->createCustomer();
$code = $c->generateCodeComptable();
$this->assertNotEmpty($code);
$this->assertStringStartsWith('411_', $code);
$this->assertStringStartsWith('EC-', $code);
}
public function testGenerateCodeComptableWithRaisonSociale(): void

View File

@@ -27,7 +27,7 @@ class DevisTest extends TestCase
$this->assertNotEmpty($devis->getHmac());
$this->assertInstanceOf(\DateTimeImmutable::class, $devis->getCreatedAt());
$this->assertNull($devis->getUpdatedAt());
$this->assertCount(0, $devis->getAdverts());
$this->assertCount(0, $devis->getLines());
}
public function testState(): void

View File

@@ -73,7 +73,7 @@ class KeycloakAuthenticatorTest extends TestCase
$keycloakUser = $this->createStub(ResourceOwnerInterface::class);
$keycloakUser->method('toArray')->willReturn([
'sub' => '123',
'email' => 'test@example.com',
'email' => 'test@e-cosplay.fr',
'given_name' => 'John',
'family_name' => 'Doe',
'groups' => ['superadmin'],
@@ -99,7 +99,7 @@ class KeycloakAuthenticatorTest extends TestCase
$user = $userBadge->getUser();
$this->assertInstanceOf(User::class, $user);
$this->assertEquals('123', $user->getKeycloakId());
$this->assertEquals('test@example.com', $user->getEmail());
$this->assertEquals('test@e-cosplay.fr', $user->getEmail());
$this->assertContains('ROLE_ROOT', $user->getRoles());
}
@@ -117,14 +117,14 @@ class KeycloakAuthenticatorTest extends TestCase
$keycloakUser = $this->createStub(ResourceOwnerInterface::class);
$keycloakUser->method('toArray')->willReturn([
'sub' => '123',
'email' => 'existing@example.com',
'groups' => [],
'email' => 'existing@e-cosplay.fr',
'groups' => ['gp_member'],
]);
$client->method('fetchUserFromToken')->willReturn($keycloakUser);
$existingUser = new User();
$this->userRepository->method('findOneBy')->willReturnCallback(function ($criteria) use ($existingUser) {
if (isset($criteria['email']) && 'existing@example.com' === $criteria['email']) {
if (isset($criteria['email']) && 'existing@e-cosplay.fr' === $criteria['email']) {
return $existingUser;
}

View File

@@ -38,7 +38,7 @@ class TwoFactorCodeMailerTest extends TestCase
->with($this->callback(function (Email $email) {
return $email->getFrom()[0]->getAddress() === 'contact@e-cosplay.fr'
&& $email->getTo()[0]->getAddress() === 'test@example.com'
&& $email->getSubject() === 'CRM SITECONSEIL - Code de verification'
&& $email->getSubject() === 'CRM E-Cosplay - Code de verification'
&& $email->getHtmlBody() === '<html>123456</html>';
}));

View File

@@ -7,6 +7,7 @@ use App\Service\DocuSealService;
use Doctrine\ORM\EntityManagerInterface;
use Docuseal\Api;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
class DocuSealServiceTest extends TestCase
{
@@ -22,7 +23,7 @@ class DocuSealServiceTest extends TestCase
$this->projectDir = sys_get_temp_dir().'/docuseal-test-'.bin2hex(random_bytes(4));
mkdir($this->projectDir.'/public', 0775, true);
$this->service = new DocuSealService($this->em, 'https://fake.docuseal.test', 'fake-key', $this->projectDir);
$this->service = new DocuSealService($this->em, $this->createStub(LoggerInterface::class), 'https://fake.docuseal.test', 'fake-key', $this->projectDir);
// Replace the real Api with our stub
$ref = new \ReflectionProperty(DocuSealService::class, 'api');

View File

@@ -25,7 +25,7 @@ class FactureServiceTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$service = new FactureService($orderService, $em, self::HMAC_SECRET);
$service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
$facture = $service->create();
$this->assertInstanceOf(Facture::class, $facture);
@@ -44,7 +44,7 @@ class FactureServiceTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$orderService = $this->createStub(OrderNumberService::class);
$service = new FactureService($orderService, $em, self::HMAC_SECRET);
$service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
$facture = $service->create($advert);
$this->assertInstanceOf(Facture::class, $facture);
@@ -64,7 +64,7 @@ class FactureServiceTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$orderService = $this->createStub(OrderNumberService::class);
$service = new FactureService($orderService, $em, self::HMAC_SECRET);
$service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
$facture = $service->create($advert);
$this->assertSame(2, $facture->getSplitIndex());
@@ -87,7 +87,7 @@ class FactureServiceTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$orderService = $this->createStub(OrderNumberService::class);
$service = new FactureService($orderService, $em, self::HMAC_SECRET);
$service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
$facture = $service->create($advert);
$this->assertSame(3, $facture->getSplitIndex());
@@ -104,7 +104,7 @@ class FactureServiceTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$orderService = $this->createStub(OrderNumberService::class);
$service = new FactureService($orderService, $em, self::HMAC_SECRET);
$service = new FactureService($orderService, $em, $this->createStub(\Psr\Log\LoggerInterface::class), self::HMAC_SECRET);
$facture = $service->createFromAdvert($advert);
$this->assertInstanceOf(Facture::class, $facture);

View File

@@ -174,11 +174,11 @@ class KeycloakAdminServiceTest extends TestCase
public function testGetRequiredGroups(): void
{
$groups = KeycloakAdminService::getRequiredGroups();
$this->assertContains('ecosplay_admin', $groups);
$this->assertContains('ecosplay_member', $groups);
$this->assertContains('esy-web', $groups);
$this->assertContains('esy-mail', $groups);
$this->assertCount(15, $groups);
$this->assertContains('superadmin', $groups);
$this->assertContains('gp_member', $groups);
$this->assertContains('gp_ndd', $groups);
$this->assertContains('gp_mail', $groups);
$this->assertCount(10, $groups);
}
public function testEnsureRequiredGroupsAllExist(): void

View File

@@ -64,7 +64,7 @@ class MailerServiceTest extends TestCase
public function testGetAdminFrom(): void
{
$this->assertEquals('SARL SITECONSEIL <admin@example.com>', $this->service->getAdminFrom());
$this->assertEquals('Association E-Cosplay <admin@example.com>', $this->service->getAdminFrom());
}
public function testSendEmail(): void

View File

@@ -55,7 +55,7 @@ class OrderNumberServiceTest extends TestCase
$service = new OrderNumberService($repo, $em);
$result = $service->generate();
$expected = $now->format('m/Y').'-00043';
$expected = $now->format('m/Y').'-00042';
$this->assertSame($expected, $result->getNumOrder());
}
@@ -99,6 +99,6 @@ class OrderNumberServiceTest extends TestCase
$service = new OrderNumberService($repo, $em);
$result = $service->preview();
$this->assertSame($now->format('m/Y').'-00011', $result);
$this->assertSame($now->format('m/Y').'-00010', $result);
}
}

View File

@@ -6,6 +6,7 @@ use App\Entity\AnalyticsEvent;
use App\Entity\AnalyticsUniqId;
use App\Entity\Attestation;
use App\Service\DocuSealService;
use App\Service\MailerService;
use App\Service\RgpdService;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
@@ -18,6 +19,7 @@ class RgpdServiceTest extends TestCase
private EntityManagerInterface $em;
private Environment $twig;
private DocuSealService $docuSealService;
private MailerService $mailer;
private UrlGeneratorInterface $urlGenerator;
private string $projectDir;
private RgpdService $service;
@@ -27,6 +29,7 @@ class RgpdServiceTest extends TestCase
$this->em = $this->createStub(EntityManagerInterface::class);
$this->twig = $this->createStub(Environment::class);
$this->docuSealService = $this->createStub(DocuSealService::class);
$this->mailer = $this->createStub(MailerService::class);
$this->urlGenerator = $this->createStub(UrlGeneratorInterface::class);
$this->projectDir = sys_get_temp_dir() . '/rgpd_test_' . uniqid();
mkdir($this->projectDir);
@@ -37,6 +40,7 @@ class RgpdServiceTest extends TestCase
$this->em,
$this->twig,
$this->docuSealService,
$this->mailer,
$this->urlGenerator,
$this->projectDir,
'secret'
@@ -70,7 +74,7 @@ class RgpdServiceTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$em->method('getRepository')->willReturn($repository);
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->urlGenerator, $this->projectDir, 's');
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->mailer, $this->urlGenerator, $this->projectDir, 's');
$result = $service->handleAccessRequest($ip, $email);
$this->assertFalse($result['found']);
@@ -95,7 +99,7 @@ class RgpdServiceTest extends TestCase
[AnalyticsEvent::class, $eventRepository],
]);
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->urlGenerator, $this->projectDir, 's');
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->mailer, $this->urlGenerator, $this->projectDir, 's');
$result = $service->handleAccessRequest($ip, $email);
$this->assertTrue($result['found']);
@@ -115,7 +119,7 @@ class RgpdServiceTest extends TestCase
$em->method('getRepository')->willReturn($visitorRepository);
$em->expects($this->once())->method('remove')->with($visitor);
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->urlGenerator, $this->projectDir, 's');
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->mailer, $this->urlGenerator, $this->projectDir, 's');
$result = $service->handleDeletionRequest($ip, $email);
$this->assertTrue($result['found']);
@@ -133,7 +137,7 @@ class RgpdServiceTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$em->method('getRepository')->willReturn($repository);
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->urlGenerator, $this->projectDir, 's');
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->mailer, $this->urlGenerator, $this->projectDir, 's');
$result = $service->handleDeletionRequest($ip, $email);
$this->assertFalse($result['found']);
@@ -152,7 +156,7 @@ class RgpdServiceTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$em->method('getRepository')->willReturn($repository);
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->urlGenerator, $this->projectDir, 's');
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->mailer, $this->urlGenerator, $this->projectDir, 's');
$result = $service->handleAccessRequest($ip, $email);
$this->assertFalse($result['found']);
}
@@ -169,7 +173,7 @@ class RgpdServiceTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$em->method('getRepository')->willReturn($repository);
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->urlGenerator, $this->projectDir, 's');
$service = new RgpdService($em, $this->twig, $this->docuSealService, $this->mailer, $this->urlGenerator, $this->projectDir, 's');
$result = $service->handleAccessRequest($ip, $email);
$this->assertFalse($result['found']);
}

View File

@@ -22,16 +22,16 @@ class TarificationServiceTest extends TestCase
$service = new TarificationService($repo, $em);
$created = $service->ensureDefaultPrices();
$this->assertCount(16, $created);
$this->assertContains('esyweb_business', $created);
$this->assertCount(19, $created);
$this->assertContains('esite_business', $created);
$this->assertContains('formation_heure', $created);
}
public function testEnsureDefaultPricesSkipsExisting(): void
{
$existing = new PriceAutomatic();
$existing->setType('esyweb_business');
$existing->setTitle('Esy-Web Business');
$existing->setType('esite_business');
$existing->setTitle('E-Site Basique');
$existing->setPriceHt('500.00');
$repo = $this->createStub(PriceAutomaticRepository::class);
@@ -42,8 +42,8 @@ class TarificationServiceTest extends TestCase
$service = new TarificationService($repo, $em);
$created = $service->ensureDefaultPrices();
$this->assertCount(15, $created);
$this->assertNotContains('esyweb_business', $created);
$this->assertCount(18, $created);
$this->assertNotContains('esite_business', $created);
}
public function testEnsureDefaultPricesNoneCreated(): void
@@ -71,7 +71,7 @@ class TarificationServiceTest extends TestCase
public function testEnsureDefaultPricesWithMeilisearchAndStripe(): void
{
$p = new PriceAutomatic();
$p->setType('esyweb_business');
$p->setType('esite_business');
$p->setTitle('T');
$p->setPriceHt('1.00');
@@ -85,7 +85,7 @@ class TarificationServiceTest extends TestCase
$service = new TarificationService($repo, $em, null, $meilisearch, $stripe);
$created = $service->ensureDefaultPrices();
$this->assertCount(16, $created);
$this->assertCount(19, $created);
}
public function testEnsureDefaultPricesStripeError(): void
@@ -106,7 +106,7 @@ class TarificationServiceTest extends TestCase
$service = new TarificationService($repo, $em, null, null, $stripe);
$created = $service->ensureDefaultPrices();
$this->assertCount(16, $created);
$this->assertCount(19, $created);
}
public function testGetAll(): void
@@ -135,7 +135,7 @@ class TarificationServiceTest extends TestCase
$em = $this->createStub(EntityManagerInterface::class);
$service = new TarificationService($repo, $em);
$this->assertSame($price, $service->getByType('esyweb_business'));
$this->assertSame($price, $service->getByType('esite_business'));
}
public function testGetByTypeNotFound(): void
@@ -152,8 +152,8 @@ class TarificationServiceTest extends TestCase
public function testGetDefaultTypes(): void
{
$types = TarificationService::getDefaultTypes();
$this->assertCount(16, $types);
$this->assertArrayHasKey('esyweb_business', $types);
$this->assertArrayHasKey('title', $types['esyweb_business']);
$this->assertCount(19, $types);
$this->assertArrayHasKey('esite_business', $types);
$this->assertArrayHasKey('title', $types['esite_business']);
}
}

View File

@@ -1,14 +1,49 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
// Mock the entreprise-search module since it's imported by app.js
vi.mock('../../assets/modules/entreprise-search.js', () => ({
initEntrepriseSearch: vi.fn(),
}))
// Mock the scss import
vi.mock('../../assets/app.scss', () => ({}))
// localStorage mock
const localStorageMock = (() => {
let store = {}
return {
getItem: vi.fn((key) => store[key] ?? null),
setItem: vi.fn((key, value) => { store[key] = String(value) }),
removeItem: vi.fn((key) => { delete store[key] }),
clear: vi.fn(() => { store = {} }),
get length() { return Object.keys(store).length },
key: vi.fn((i) => Object.keys(store)[i] ?? null),
}
})()
Object.defineProperty(globalThis, 'localStorage', { value: localStorageMock, writable: true })
describe('app.js DOMContentLoaded', () => {
beforeEach(() => {
document.body.innerHTML = ''
localStorage.clear()
localStorageMock.clear()
vi.restoreAllMocks()
// Re-apply localStorage mock after restoreAllMocks
localStorageMock.getItem.mockImplementation((key) => {
// Use internal store - reimplemented per test via setItem
return null
})
})
const loadApp = async () => {
// Reset module cache and re-import
vi.resetModules()
// Re-mock the modules before re-import
vi.doMock('../../assets/modules/entreprise-search.js', () => ({
initEntrepriseSearch: vi.fn(),
}))
vi.doMock('../../assets/app.scss', () => ({}))
await import('../../assets/app.js')
document.dispatchEvent(new Event('DOMContentLoaded'))
}
@@ -103,28 +138,90 @@ describe('app.js DOMContentLoaded', () => {
})
describe('data-confirm forms', () => {
it('prevents submission when confirm is cancelled', async () => {
it('prevents submission and shows confirm modal', async () => {
document.body.innerHTML = '<form data-confirm="Etes-vous sur ?"><button type="submit">Submit</button></form>'
window.confirm = vi.fn(() => false)
await loadApp()
const form = document.querySelector('form')
const event = new Event('submit', { cancelable: true })
form.dispatchEvent(event)
// The custom confirm modal should prevent default
expect(event.defaultPrevented).toBe(true)
// The confirm modal should be visible
const confirmModal = document.getElementById('confirm-modal')
expect(confirmModal).not.toBeNull()
expect(confirmModal.classList.contains('hidden')).toBe(false)
})
it('allows submission when confirm is accepted', async () => {
it('closes confirm modal on cancel click', async () => {
document.body.innerHTML = '<form data-confirm="Etes-vous sur ?"><button type="submit">Submit</button></form>'
window.confirm = vi.fn(() => true)
await loadApp()
const form = document.querySelector('form')
const event = new Event('submit', { cancelable: true })
form.dispatchEvent(event)
form.dispatchEvent(new Event('submit', { cancelable: true }))
expect(event.defaultPrevented).toBe(false)
const confirmModal = document.getElementById('confirm-modal')
expect(confirmModal.classList.contains('hidden')).toBe(false)
// Click cancel
document.getElementById('confirm-cancel').click()
expect(confirmModal.classList.contains('hidden')).toBe(true)
})
it('closes confirm modal on overlay click', async () => {
document.body.innerHTML = '<form data-confirm="Etes-vous sur ?"><button type="submit">Submit</button></form>'
await loadApp()
const form = document.querySelector('form')
form.dispatchEvent(new Event('submit', { cancelable: true }))
const confirmModal = document.getElementById('confirm-modal')
expect(confirmModal.classList.contains('hidden')).toBe(false)
document.getElementById('confirm-overlay').click()
expect(confirmModal.classList.contains('hidden')).toBe(true)
})
it('closes confirm modal on Escape key', async () => {
document.body.innerHTML = '<form data-confirm="Etes-vous sur ?"><button type="submit">Submit</button></form>'
await loadApp()
const form = document.querySelector('form')
form.dispatchEvent(new Event('submit', { cancelable: true }))
const confirmModal = document.getElementById('confirm-modal')
expect(confirmModal.classList.contains('hidden')).toBe(false)
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))
expect(confirmModal.classList.contains('hidden')).toBe(true)
})
it('submits form when confirm OK is clicked', async () => {
document.body.innerHTML = '<form data-confirm="Etes-vous sur ?"><button type="submit">Submit</button></form>'
await loadApp()
const form = document.querySelector('form')
form.requestSubmit = vi.fn()
form.dispatchEvent(new Event('submit', { cancelable: true }))
// Click OK to confirm
document.getElementById('confirm-ok').click()
expect(form.requestSubmit).toHaveBeenCalled()
expect(document.getElementById('confirm-modal').classList.contains('hidden')).toBe(true)
})
it('displays the confirm message from data attribute', async () => {
document.body.innerHTML = '<form data-confirm="Voulez-vous supprimer ?"><button type="submit">Submit</button></form>'
await loadApp()
const form = document.querySelector('form')
form.dispatchEvent(new Event('submit', { cancelable: true }))
expect(document.getElementById('confirm-message').textContent).toBe('Voulez-vous supprimer ?')
})
})
@@ -140,11 +237,8 @@ describe('app.js DOMContentLoaded', () => {
expect(menu.classList.contains('hidden')).toBe(true)
expect(arrow.classList.contains('rotate-180')).toBe(false)
// Simulate click via the registered handler
const clickEvent = new MouseEvent('click', { bubbles: true })
btn.dispatchEvent(clickEvent)
btn.dispatchEvent(new MouseEvent('click', { bubbles: true }))
// In happy-dom the event may not trigger perfectly, verify handler was registered
expect(btn).not.toBeNull()
expect(arrow).not.toBeNull()
})
@@ -203,30 +297,33 @@ describe('app.js DOMContentLoaded', () => {
})
it('shows banner when no consent', async () => {
localStorageMock.getItem.mockReturnValue(null)
await loadApp()
expect(document.getElementById('cookie-banner').classList.contains('hidden')).toBe(false)
})
it('hides banner when already accepted', async () => {
localStorage.setItem('cookie_consent', 'accepted')
localStorageMock.getItem.mockReturnValue('accepted')
await loadApp()
expect(document.getElementById('cookie-banner').classList.contains('hidden')).toBe(true)
})
it('hides banner and stores accepted on accept click', async () => {
localStorageMock.getItem.mockReturnValue(null)
await loadApp()
document.getElementById('cookie-accept').click()
expect(document.getElementById('cookie-banner').classList.contains('hidden')).toBe(true)
expect(localStorage.getItem('cookie_consent')).toBe('accepted')
expect(localStorageMock.setItem).toHaveBeenCalledWith('cookie_consent', 'accepted')
})
it('hides banner and stores refused on refuse click', async () => {
localStorageMock.getItem.mockReturnValue(null)
await loadApp()
document.getElementById('cookie-refuse').click()
expect(document.getElementById('cookie-banner').classList.contains('hidden')).toBe(true)
expect(localStorage.getItem('cookie_consent')).toBe('refused')
expect(localStorageMock.setItem).toHaveBeenCalledWith('cookie_consent', 'refused')
})
})
@@ -359,7 +456,6 @@ describe('app.js DOMContentLoaded', () => {
await loadApp()
const results = document.getElementById('search-results')
const outside = document.getElementById('outside')
document.dispatchEvent(new MouseEvent('click', { bubbles: true }))
@@ -392,4 +488,251 @@ describe('app.js DOMContentLoaded', () => {
expect(results.innerHTML).toContain('Marie Martin')
})
})
describe('Modal open/close (data-modal-open / data-modal-close)', () => {
it('opens a modal when clicking a data-modal-open button', async () => {
document.body.innerHTML = `
<button data-modal-open="my-modal">Open</button>
<div id="my-modal" class="hidden">Modal content</div>
`
await loadApp()
document.querySelector('[data-modal-open="my-modal"]').click()
expect(document.getElementById('my-modal').classList.contains('hidden')).toBe(false)
})
it('closes a modal when clicking a data-modal-close button', async () => {
document.body.innerHTML = `
<div id="my-modal">
<button data-modal-close="my-modal">Close</button>
Modal content
</div>
`
await loadApp()
// Modal starts visible
expect(document.getElementById('my-modal').classList.contains('hidden')).toBe(false)
document.querySelector('[data-modal-close="my-modal"]').click()
expect(document.getElementById('my-modal').classList.contains('hidden')).toBe(true)
})
it('does nothing if target modal does not exist', async () => {
document.body.innerHTML = `
<button data-modal-open="nonexistent">Open</button>
<button data-modal-close="nonexistent">Close</button>
`
await loadApp()
// Should not throw
document.querySelector('[data-modal-open="nonexistent"]').click()
document.querySelector('[data-modal-close="nonexistent"]').click()
})
it('handles multiple modals independently', async () => {
document.body.innerHTML = `
<button data-modal-open="modal-a">Open A</button>
<button data-modal-open="modal-b">Open B</button>
<div id="modal-a" class="hidden">Modal A</div>
<div id="modal-b" class="hidden">Modal B</div>
`
await loadApp()
document.querySelector('[data-modal-open="modal-a"]').click()
expect(document.getElementById('modal-a').classList.contains('hidden')).toBe(false)
expect(document.getElementById('modal-b').classList.contains('hidden')).toBe(true)
})
})
describe('SIRET search (prestataire creation)', () => {
beforeEach(() => {
document.body.innerHTML = `
<form>
<input id="siret-search-input" value="">
<button type="button" id="siret-search-btn">Rechercher</button>
<div id="siret-search-results" class="hidden"></div>
<input name="raisonSociale" value="">
<input name="siret" value="">
<input name="address" value="">
<input name="zipCode" value="">
<input name="city" value="">
</form>
`
})
it('shows message when query is too short', async () => {
await loadApp()
const input = document.getElementById('siret-search-input')
input.value = 'ab'
document.getElementById('siret-search-btn').click()
const results = document.getElementById('siret-search-results')
expect(results.classList.contains('hidden')).toBe(false)
expect(results.innerHTML).toContain('au moins 3 caracteres')
})
it('shows loading state then results on search', async () => {
globalThis.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({
results: [
{
nom_complet: 'Test SARL',
siege: {
siret: '12345678901234',
adresse: '1 rue de Paris',
code_postal: '75001',
libelle_commune: 'PARIS'
}
}
]
})
})
)
await loadApp()
const input = document.getElementById('siret-search-input')
input.value = 'test sarl'
document.getElementById('siret-search-btn').click()
// Wait for fetch
await new Promise(r => setTimeout(r, 50))
const results = document.getElementById('siret-search-results')
expect(results.innerHTML).toContain('Test SARL')
expect(results.innerHTML).toContain('12345678901234')
expect(globalThis.fetch).toHaveBeenCalledWith('/admin/prestataires/entreprise-search?q=test%20sarl')
})
it('shows no results message when empty', async () => {
globalThis.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ results: [] })
})
)
await loadApp()
document.getElementById('siret-search-input').value = 'zzzzz'
document.getElementById('siret-search-btn').click()
await new Promise(r => setTimeout(r, 50))
const results = document.getElementById('siret-search-results')
expect(results.innerHTML).toContain('Aucun resultat')
})
it('fills form fields when a result is clicked', async () => {
globalThis.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({
results: [
{
nom_complet: 'Ma Societe',
siege: {
siret: '98765432109876',
adresse: '10 avenue de Lyon',
code_postal: '69001',
libelle_commune: 'LYON'
}
}
]
})
})
)
await loadApp()
document.getElementById('siret-search-input').value = 'ma societe'
document.getElementById('siret-search-btn').click()
await new Promise(r => setTimeout(r, 50))
// Click the first result
const resultItem = document.querySelector('.siret-result-item')
expect(resultItem).not.toBeNull()
resultItem.click()
expect(document.querySelector('[name="raisonSociale"]').value).toBe('Ma Societe')
expect(document.querySelector('[name="siret"]').value).toBe('98765432109876')
expect(document.querySelector('[name="address"]').value).toBe('10 avenue de Lyon')
expect(document.querySelector('[name="zipCode"]').value).toBe('69001')
expect(document.querySelector('[name="city"]').value).toBe('LYON')
// Results should be hidden and input cleared
expect(document.getElementById('siret-search-results').classList.contains('hidden')).toBe(true)
expect(document.getElementById('siret-search-input').value).toBe('')
})
it('shows error message on fetch failure', async () => {
globalThis.fetch = vi.fn(() => Promise.reject(new Error('Network error')))
await loadApp()
document.getElementById('siret-search-input').value = 'test'
document.getElementById('siret-search-btn').click()
await new Promise(r => setTimeout(r, 50))
const results = document.getElementById('siret-search-results')
expect(results.innerHTML).toContain('Erreur lors de la recherche')
})
it('triggers search on Enter key in input', async () => {
globalThis.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ results: [] })
})
)
await loadApp()
const input = document.getElementById('siret-search-input')
input.value = 'test enter'
const event = new KeyboardEvent('keydown', { key: 'Enter', cancelable: true })
input.dispatchEvent(event)
await new Promise(r => setTimeout(r, 50))
expect(globalThis.fetch).toHaveBeenCalled()
})
it('hides results when clicking outside', async () => {
await loadApp()
const results = document.getElementById('siret-search-results')
results.classList.remove('hidden')
results.innerHTML = '<p>Some results</p>'
// Click on document body (outside)
document.body.click()
// Need to trigger click on document level
document.dispatchEvent(new MouseEvent('click', { bubbles: true }))
expect(results.classList.contains('hidden')).toBe(true)
})
})
describe('Refuse toggle button', () => {
it('toggles refuse form visibility', async () => {
document.body.innerHTML = `
<button id="refuse-toggle-btn">Refuser</button>
<div id="refuse-form" class="hidden">Refuse form</div>
`
await loadApp()
document.getElementById('refuse-toggle-btn').click()
expect(document.getElementById('refuse-form').classList.contains('hidden')).toBe(false)
document.getElementById('refuse-toggle-btn').click()
expect(document.getElementById('refuse-form').classList.contains('hidden')).toBe(true)
})
})
})