feat: make email optional for under-16 participants except applicant

This commit is contained in:
fromme committed 2026-10-16 17:49:04 +02:00
1 parent 55a9189284
commit 1ec9c972cd
18 files changed
+435 -336

No files matched your search

@@ -74,14 +74,69 @@ class CreateFieldStateProviderTest extends TestCase
);
}
public function testEmailIsRequiredForEveryParticipantIncludingChildren(): void
public function testEmailIsNotRequiredForNonApplicantChild(): void
{
$bookingDto = $this->createBookingDtoWithApplicantAndDependent(new \DateTimeImmutable('2015-01-01'));
$this->assertArrayNotHasKey(
'required',
$this->provider->getFieldState('email', $bookingDto, 1),
'Requiredness is the form type base option now that it is unconditional'
$this->assertFalse(
$this->provider->getFieldState('email', $bookingDto, 1)['required'],
'A child travelling with an applicant does not need an email address of their own'
);
}
public function testEmailIsRequiredForNonApplicantAdult(): void
{
$bookingDto = $this->createBookingDtoWithApplicantAndDependent(new \DateTimeImmutable('1990-01-01'));
$this->assertTrue($this->provider->getFieldState('email', $bookingDto, 1)['required']);
}
public function testEmailIsRequiredForTheApplicantEvenAsAChild(): void
{
$bookingDto = $this->createBookingDtoWithApplicantAndDependent(new \DateTimeImmutable('2015-01-01'));
$bookingDto->participants[0]->dateOfBirth = new \DateTimeImmutable('2015-01-01');
$this->assertTrue(
$this->provider->getFieldState('email', $bookingDto, 0)['required'],
'The applicant is the booking contact and always has to be reachable'
);
}
public function testEmailIsNotRequiredForAChildAtIndexZeroWhenTheApplicantDoesNotTravel(): void
{
$bookingDto = $this->createBookingDtoWithApplicantAndDependent(new \DateTimeImmutable('2015-01-01'));
$bookingDto->participants[0]->dateOfBirth = new \DateTimeImmutable('2015-01-01');
$bookingDto->applicantTravels = false;
$this->assertFalse(
$this->provider->getFieldState('email', $bookingDto, 0)['required'],
'On a split booking participant[0] is an ordinary traveller'
);
}
public function testEmailIsRequiredWhenDateOfBirthIsUnknown(): void
{
$bookingDto = $this->createBookingDtoWithApplicantAndDependent(new \DateTimeImmutable('2015-01-01'));
$bookingDto->participants[1]->dateOfBirth = null;
$this->assertTrue(
$this->provider->getFieldState('email', $bookingDto, 1)['required'],
'An unknown age counts as an adult'
);
}
public function testEmailRequirementFlipsWhenDateOfBirthChanges(): void
{
$bookingDto = $this->createBookingDtoWithApplicantAndDependent(new \DateTimeImmutable('2015-01-01'));
$dependent = $bookingDto->participants[1];
$this->assertFalse($this->provider->getFieldState('email', $bookingDto, 1)['required']);
$dependent->dateOfBirth = new \DateTimeImmutable('1990-01-01');
$this->assertTrue(
$this->provider->getFieldState('email', $bookingDto, 1)['required'],
'Correcting a child date of birth to an adult one must reinstate the requirement'
);
}
@@ -31,14 +31,11 @@ class EditFieldStateProviderTest extends TestCase
$this->assertTrue($this->provider->getFieldState('email', $bookingDto, 1)['required']);
}
public function testEmailIsRequiredForNonApplicantChild(): void
public function testEmailIsNotRequiredForNonApplicantChild(): void
{
$bookingDto = $this->createBookingDto(new \DateTimeImmutable('2015-01-01'), false);
$this->assertTrue(
$this->provider->getFieldState('email', $bookingDto, 1)['required'],
'BusPro requires a Mail-Adresse for every participant, children included'
);
$this->assertFalse($this->provider->getFieldState('email', $bookingDto, 1)['required']);
}
public function testEmailIsNotRequiredForInternalAgencyBookingsEvenForAdults(): void
@@ -1,162 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Tests\Form\Service;
use App\BusProNet\Model\Booking;
use App\BusProNet\Model\Travel;
use App\BusProNet\XmlLoader\AgencyLoader;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use App\Form\Service\ParticipantDateOfBirthFieldHandler;
use App\Form\Service\ParticipantEmailFieldHandler;
use App\Form\Service\ParticipantFieldHandlerRegistry;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
class ParticipantEmailFieldHandlerTest extends TestCase
{
private ParticipantEmailFieldHandler $handler;
protected function setUp(): void
{
$this->handler = new ParticipantEmailFieldHandler();
}
public function testChildWithoutEmailInheritsTheApplicants(): void
{
$bookingDto = $this->createBookingDto();
$this->handler->processField(['email' => ''], $bookingDto, 1);
$this->assertSame('[email protected]', $bookingDto->participants[1]->email);
}
public function testChildKeepsAnEmailThatWasTypedIn(): void
{
$bookingDto = $this->createBookingDto();
$this->handler->processField(['email' => '[email protected]'], $bookingDto, 1);
$this->assertSame(
'[email protected]',
$bookingDto->participants[1]->email,
'The pre-filled address stays overridable'
);
}
public function testAdultWithoutEmailDoesNotInherit(): void
{
$bookingDto = $this->createBookingDto();
$bookingDto->participants[1]->dateOfBirth = new \DateTimeImmutable('1990-01-01');
$this->handler->processField(['email' => ''], $bookingDto, 1);
$this->assertNull(
$bookingDto->participants[1]->email,
'An adult supplies their own address; NotBlank reports the gap'
);
}
public function testApplicantDoesNotInheritFromThemselves(): void
{
$bookingDto = $this->createBookingDto();
$bookingDto->participants[0]->dateOfBirth = new \DateTimeImmutable('2015-01-01');
$this->handler->processField(['email' => ''], $bookingDto, 0);
$this->assertNull($bookingDto->participants[0]->email);
}
public function testChildAtIndexZeroInheritsWhenTheApplicantDoesNotTravel(): void
{
$bookingDto = $this->createBookingDto();
$bookingDto->applicantTravels = false;
$bookingDto->applicant = $this->createParticipant('[email protected]', '1981-03-16');
$bookingDto->participants[0]->dateOfBirth = new \DateTimeImmutable('2015-01-01');
$this->handler->processField(['email' => ''], $bookingDto, 0);
$this->assertSame('[email protected]', $bookingDto->participants[0]->email);
}
public function testEditModeNeverPreFills(): void
{
$bookingDto = $this->createBookingDto();
$bookingDto->booking = new Booking();
$this->handler->processField(['email' => ''], $bookingDto, 1);
$this->assertNull(
$bookingDto->participants[1]->email,
'participant[0] is not reliably the applicant in edit mode, and a blank may be intended'
);
}
public function testEditModeLeavesAnInternalAgencyChildWithoutEmail(): void
{
$bookingDto = $this->createBookingDto();
$bookingDto->booking = new Booking();
$bookingDto->agencyCode = AgencyLoader::INTERNAL_AGENCY_CODE;
$this->handler->processField(['email' => ''], $bookingDto, 1);
$this->assertNull($bookingDto->participants[1]->email);
}
public function testHandlerRunsAfterDateOfBirth(): void
{
$this->assertSame(
['dateOfBirth'],
$this->handler->getDependencies(),
'Whether someone counts as a child is only known once their DOB is processed'
);
}
/**
* The handler runs in PRE_SUBMIT and writes to the DTO, but the form then binds the submitted
* data over it. Only fields the registry syncs back survive, so the pre-filled address has to
* reappear in the returned data - otherwise the blank wins and the field renders empty.
*/
public function testPreFilledEmailIsSyncedBackIntoTheSubmittedData(): void
{
$bookingDto = $this->createBookingDto();
$registry = new ParticipantFieldHandlerRegistry([
new ParticipantDateOfBirthFieldHandler(new NullLogger()),
new ParticipantEmailFieldHandler(),
]);
$synced = $registry->processFieldsForParticipantAndSync(['email' => ''], $bookingDto, 1);
$this->assertSame('[email protected]', $bookingDto->participants[1]->email);
$this->assertSame(
'[email protected]',
$synced['email'],
'Without the sync the form binds the blank back over the pre-filled address'
);
}
private function createBookingDto(): BookingDto
{
$bookingDto = new BookingDto(new Travel(), 1);
$bookingDto->participants = [
$this->createParticipant('[email protected]', '1980-01-01'),
$this->createParticipant('', '2015-01-01'),
];
$bookingDto->participants[0]->index = 0;
$bookingDto->participants[1]->index = 1;
$bookingDto->participants[1]->email = null;
return $bookingDto;
}
private function createParticipant(string $email, string $dateOfBirth): ParticipantDto
{
$participant = new ParticipantDto();
$participant->email = '' !== $email ? $email : null;
$participant->dateOfBirth = new \DateTimeImmutable($dateOfBirth);
return $participant;
}
}