From c7b70dbd5641dd46230ee74c1cbabe460d382bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Sat, 14 Feb 2026 15:56:56 +0100 Subject: [PATCH] feat: dummy data fill closes #869c3khk0 --- .../Booking/Create/Step2Controller.php | 47 +++-- src/Form/Service/DummyDataFillService.php | 71 ++++++++ .../Form/Service/DummyDataFillServiceTest.php | 160 ++++++++++++++++++ 3 files changed, 266 insertions(+), 12 deletions(-) create mode 100644 src/Form/Service/DummyDataFillService.php create mode 100644 tests/Form/Service/DummyDataFillServiceTest.php diff --git a/src/Controller/Booking/Create/Step2Controller.php b/src/Controller/Booking/Create/Step2Controller.php index 133a278..57719ac 100644 --- a/src/Controller/Booking/Create/Step2Controller.php +++ b/src/Controller/Booking/Create/Step2Controller.php @@ -4,11 +4,13 @@ declare(strict_types=1); namespace App\Controller\Booking\Create; +use App\BusProNet\Constants; use App\Controller\Booking\Traits\BookingCreateTrait; use App\Controller\Booking\Traits\BookingExceptionHandlerTrait; use App\Controller\Booking\Traits\ParticipantCardFlowTrait; use App\Form\BookingCreateStep2Type; use App\Form\Model\BookingDto; +use App\Form\Service\DummyDataFillService; use App\Form\Service\ParticipantFieldOptionsProvider; use App\Htmx\HxTrait; use App\Service\BookingService; @@ -18,6 +20,7 @@ use App\Service\ParticipantPrepopulationService; use App\Service\RoomAssignmentService; use App\Service\TravelDataService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; @@ -43,6 +46,7 @@ class Step2Controller extends AbstractController private readonly ParticipantCardDataService $participantCardService, private readonly ParticipantFieldOptionsProvider $fieldOptionsProvider, private readonly ParticipantPrepopulationService $prepopulationService, + private readonly DummyDataFillService $dummyDataFillService, ) { } @@ -146,6 +150,22 @@ class Step2Controller extends AbstractController $form->handleRequest($request); + // Detect dummy data fill token — render pre-filled form immediately, skipping validation + $isDummyDataFill = $this + ->dummyDataFillService + ->isTokenMatch($bookingDto->participants[$index], $bookingDto->getMode()) + ; + if (true === $form->isSubmitted() && true === $isDummyDataFill) { + $this->dummyDataFillService->fill($bookingDto->participants[$index], $index); + $bookingDto->bookingStatus = Constants::BOOKING_STATUS_OPEN; + $this->bookingService->saveBookingDto($request, $bookingDto, BookingDto::MODE_CREATE); + + // Recreate form with filled DTO so the view shows the dummy data + $form = $this->createParticipantForm($bookingDto, $index); + + return $this->renderParticipantForm($form, $index, $bookingDto); + } + // Collect notifications from field handlers (run during PRE_SUBMIT) $notifications = $this->collectAndClearNotifications($bookingDto); @@ -160,18 +180,7 @@ class Step2Controller extends AbstractController return $this->redirectToRoute('app_booking_create_step_2'); } - // Get complete summary data (pricing, rooms, CMS data) - $summaryData = $this->summaryDataService->getSummaryData($bookingDto); - - $templateData = [ - 'form' => $form->createView(), - 'participantIndex' => $index, - 'bookingDto' => $bookingDto, - 'summaryData' => $summaryData, - 'refreshRouteName' => 'app_booking_create_step_2_participant_refresh', - ]; - - return $this->render('booking/create/step_2_participant.html.twig', $templateData); + return $this->renderParticipantForm($form, $index, $bookingDto); } /** @@ -207,4 +216,18 @@ class Step2Controller extends AbstractController 'app_booking_create_step_2_participant_refresh' ); } + + private function renderParticipantForm( + FormInterface $form, + int $index, + BookingDto $bookingDto, + ): Response { + return $this->render('booking/create/step_2_participant.html.twig', [ + 'form' => $form->createView(), + 'participantIndex' => $index, + 'bookingDto' => $bookingDto, + 'summaryData' => $this->summaryDataService->getSummaryData($bookingDto), + 'refreshRouteName' => 'app_booking_create_step_2_participant_refresh', + ]); + } } diff --git a/src/Form/Service/DummyDataFillService.php b/src/Form/Service/DummyDataFillService.php new file mode 100644 index 0000000..7d84534 --- /dev/null +++ b/src/Form/Service/DummyDataFillService.php @@ -0,0 +1,71 @@ +lastName; + } + + /** + * Fills the participant DTO with generated dummy personal data. + * + * Generates firstName, lastName, email, mobile, date of birth and address + * values based on the participant number (1-based). The lastName includes + * the current time for easy identification of test bookings. + * + * @param ParticipantDto $participant The participant DTO to fill + * @param int $participantIndex The zero-based participant index + */ + public function fill(ParticipantDto $participant, int $participantIndex): void + { + $participantNumber = $participantIndex + 1; + $now = CarbonImmutable::now(); + + $participant->firstName = sprintf('Vorname %d', $participantNumber); + $participant->lastName = sprintf('Muster %d %s', $participantNumber, $now->format('H:i')); + $participant->mobile = '0171/111111'; + $participant->email = sprintf('teilnehmer.in%d@example.com', $participantNumber); + $participant->dateOfBirth = $now->subYears(20)->toImmutable(); + + $address = new Address(); + $address->street = 'Musterstr. 123'; + $address->postCode = '99999'; + $address->city = 'MusterOrt'; + $address->country = 'Deutschland'; + $participant->address = $address; + } +} diff --git a/tests/Form/Service/DummyDataFillServiceTest.php b/tests/Form/Service/DummyDataFillServiceTest.php new file mode 100644 index 0000000..b78796c --- /dev/null +++ b/tests/Form/Service/DummyDataFillServiceTest.php @@ -0,0 +1,160 @@ +service = new DummyDataFillService(); + } + + public function testTokenMatchInCreateMode(): void + { + $participant = new ParticipantDto(); + $participant->lastName = DummyDataFillService::TOKEN; + + $this->assertTrue($this->service->isTokenMatch($participant, BookingDto::MODE_CREATE)); + } + + public function testTokenMatchReturnsFalseInEditMode(): void + { + $participant = new ParticipantDto(); + $participant->lastName = DummyDataFillService::TOKEN; + + $this->assertFalse($this->service->isTokenMatch($participant, BookingDto::MODE_EDIT)); + } + + public function testNonMatchingLastNameReturnsFalse(): void + { + $participant = new ParticipantDto(); + $participant->lastName = 'Schmidt'; + + $this->assertFalse($this->service->isTokenMatch($participant, BookingDto::MODE_CREATE)); + } + + public function testNullLastNameReturnsFalse(): void + { + $participant = new ParticipantDto(); + $participant->lastName = null; + + $this->assertFalse($this->service->isTokenMatch($participant, BookingDto::MODE_CREATE)); + } + + public function testFillSetsFirstNameWithParticipantNumber(): void + { + $participant = new ParticipantDto(); + + $this->service->fill($participant, 0); + + $this->assertSame('Vorname 1', $participant->firstName); + } + + public function testFillSetsLastNameWithParticipantNumberAndTime(): void + { + CarbonImmutable::setTestNow(CarbonImmutable::create(2026, 1, 15, 14, 30)); + + $participant = new ParticipantDto(); + + $this->service->fill($participant, 0); + + $this->assertSame('Muster 1 14:30', $participant->lastName); + + CarbonImmutable::setTestNow(); + } + + public function testFillSetsMobilePhone(): void + { + $participant = new ParticipantDto(); + + $this->service->fill($participant, 0); + + $this->assertSame('0171/111111', $participant->mobile); + } + + public function testFillSetsEmailWithParticipantNumber(): void + { + $participant = new ParticipantDto(); + + $this->service->fill($participant, 0); + + $this->assertSame('teilnehmer.in1@example.com', $participant->email); + } + + public function testFillUsesOneBasedParticipantNumber(): void + { + CarbonImmutable::setTestNow(CarbonImmutable::create(2026, 1, 15, 9, 5)); + + $participant0 = new ParticipantDto(); + $participant1 = new ParticipantDto(); + $participant4 = new ParticipantDto(); + + $this->service->fill($participant0, 0); + $this->service->fill($participant1, 1); + $this->service->fill($participant4, 4); + + $this->assertSame('Vorname 1', $participant0->firstName); + $this->assertSame('Muster 1 09:05', $participant0->lastName); + $this->assertSame('teilnehmer.in1@example.com', $participant0->email); + + $this->assertSame('Vorname 2', $participant1->firstName); + $this->assertSame('Muster 2 09:05', $participant1->lastName); + $this->assertSame('teilnehmer.in2@example.com', $participant1->email); + + $this->assertSame('Vorname 5', $participant4->firstName); + $this->assertSame('Muster 5 09:05', $participant4->lastName); + $this->assertSame('teilnehmer.in5@example.com', $participant4->email); + + CarbonImmutable::setTestNow(); + } + + public function testFillSetsDateOfBirthToTwentyYearsAgo(): void + { + CarbonImmutable::setTestNow(CarbonImmutable::create(2026, 3, 10, 12, 0)); + + $participant = new ParticipantDto(); + + $this->service->fill($participant, 0); + + $this->assertInstanceOf(\DateTimeImmutable::class, $participant->dateOfBirth); + $this->assertSame('2006-03-10', $participant->dateOfBirth->format('Y-m-d')); + + CarbonImmutable::setTestNow(); + } + + public function testFillSetsAddress(): void + { + $participant = new ParticipantDto(); + + $this->service->fill($participant, 0); + + $this->assertNotNull($participant->address); + $this->assertSame('Musterstr. 123', $participant->address->street); + $this->assertSame('99999', $participant->address->postCode); + $this->assertSame('MusterOrt', $participant->address->city); + $this->assertSame('Deutschland', $participant->address->country); + } + + public function testFillSetsAddressForAllParticipants(): void + { + $participant0 = new ParticipantDto(); + $participant3 = new ParticipantDto(); + + $this->service->fill($participant0, 0); + $this->service->fill($participant3, 3); + + $this->assertNotNull($participant0->address); + $this->assertNotNull($participant3->address); + $this->assertSame('Musterstr. 123', $participant3->address->street); + } +}