Add ShirtSize validation

This commit is contained in:
Michael Weimann 2023-05-03 17:51:43 +02:00 committed by Igor Scheller
parent 8bb8837e38
commit c5317e2536
9 changed files with 104 additions and 13 deletions

View File

@ -20,6 +20,9 @@
<div class="col-md-6"> <div class="col-md-6">
{{ f.select('shirt_size', __('user.shirt_size'), config('tshirt_sizes'), { {{ f.select('shirt_size', __('user.shirt_size'), config('tshirt_sizes'), {
'selected': userdata.personalData.shirt_size, 'selected': userdata.personalData.shirt_size,
'required': true,
'required_icon': true,
'default_option': __('Please select...'),
}) }} }) }}
</div> </div>
{% endif %} {% endif %}

View File

@ -143,6 +143,7 @@
'selected': user.personalData.shirt_size, 'selected': user.personalData.shirt_size,
'required': true, 'required': true,
'required_icon': true, 'required_icon': true,
'default_option': __('Please select...'),
}) }} }) }}
</div> </div>
{% endif %} {% endif %}

View File

@ -58,13 +58,13 @@ class UserShirtController extends BaseController
$user = $this->user->findOrFail($userId); $user = $this->user->findOrFail($userId);
$data = $this->validate($request, [ $data = $this->validate($request, [
'shirt_size' => $shirtEnabled ? 'required' : 'optional', 'shirt_size' => ($shirtEnabled ? 'required' : 'optional') . '|shirt_size',
'arrived' => 'optional|checked', 'arrived' => 'optional|checked',
'active' => 'optional|checked', 'active' => 'optional|checked',
'got_shirt' => 'optional|checked', 'got_shirt' => 'optional|checked',
]); ]);
if ($shirtEnabled && isset($this->config->get('tshirt_sizes')[$data['shirt_size']])) { if ($shirtEnabled) {
$user->personalData->shirt_size = $data['shirt_size']; $user->personalData->shirt_size = $data['shirt_size'];
$user->personalData->save(); $user->personalData->save();
} }

View File

@ -296,7 +296,7 @@ class SettingsController extends BaseController
$rules['planned_departure_date'] = 'optional|date:Y-m-d'; $rules['planned_departure_date'] = 'optional|date:Y-m-d';
} }
if ($goodie_tshirt) { if ($goodie_tshirt) {
$rules['shirt_size'] = 'required'; $rules['shirt_size'] = 'required|shirt_size';
} }
return $rules; return $rules;
} }

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Http\Validation\Rules;
use Respect\Validation\Rules\In;
/**
* Validates whether the value is contained in the keys of the config item "tshirt_sizes".
*/
class ShirtSize extends In
{
public function __construct()
{
parent::__construct(array_keys(config('tshirt_sizes')));
}
}

View File

@ -7,6 +7,7 @@ namespace Engelsystem\Test\Unit\Controllers\Admin;
use Engelsystem\Config\GoodieType; use Engelsystem\Config\GoodieType;
use Engelsystem\Controllers\Admin\UserShirtController; use Engelsystem\Controllers\Admin\UserShirtController;
use Engelsystem\Helpers\Authenticator; use Engelsystem\Helpers\Authenticator;
use Engelsystem\Http\Exceptions\ValidationException;
use Engelsystem\Http\Redirector; use Engelsystem\Http\Redirector;
use Engelsystem\Http\Validation\Validator; use Engelsystem\Http\Validation\Validator;
use Engelsystem\Models\User\PersonalData; use Engelsystem\Models\User\PersonalData;
@ -60,6 +61,7 @@ class UserShirtControllerTest extends ControllerTest
} }
/** /**
* @todo Factor out separate tests. Isolated User, Config and permissions per test.
* @covers \Engelsystem\Controllers\Admin\UserShirtController::saveShirt * @covers \Engelsystem\Controllers\Admin\UserShirtController::saveShirt
*/ */
public function testSaveShirt(): void public function testSaveShirt(): void
@ -81,11 +83,11 @@ class UserShirtControllerTest extends ControllerTest
->create(); ->create();
$auth $auth
->expects($this->exactly(6)) ->expects($this->exactly(5))
->method('can') ->method('can')
->with('admin_arrive') ->with('admin_arrive')
->willReturnOnConsecutiveCalls(true, true, true, false, false, true); ->willReturnOnConsecutiveCalls(true, true, false, false, true);
$this->setExpects($redirector, 'back', null, $this->response, $this->exactly(6)); $this->setExpects($redirector, 'back', null, $this->response, $this->exactly(5));
$controller = new UserShirtController( $controller = new UserShirtController(
$auth, $auth,
@ -131,7 +133,12 @@ class UserShirtControllerTest extends ControllerTest
'shirt_size' => 'L', 'shirt_size' => 'L',
]); ]);
try {
$controller->saveShirt($request); $controller->saveShirt($request);
self::fail('Expected exception was not raised');
} catch (ValidationException $e) {
// ignore
}
$user = User::find(1); $user = User::find(1);
$this->assertEquals('S', $user->personalData->shirt_size); $this->assertEquals('S', $user->personalData->shirt_size);
@ -142,6 +149,8 @@ class UserShirtControllerTest extends ControllerTest
'arrived' => '1', 'arrived' => '1',
]); ]);
$user->state->arrived = false;
$user->state->save();
$this->assertFalse($user->state->arrived); $this->assertFalse($user->state->arrived);
$controller->saveShirt($request); $controller->saveShirt($request);
$user = User::find(1); $user = User::find(1);

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Test\Unit\Http\Validation\Rules;
use Engelsystem\Http\Validation\Rules\ShirtSize;
use Engelsystem\Test\Unit\ServiceProviderTest;
class ShirtSizeTest extends ServiceProviderTest
{
private ShirtSize $subject;
public function setUp(): void
{
$app = $this->createAndSetUpAppWithConfig([]);
$app->get('config')->set('tshirt_sizes', [
'S' => 'Small Straight-Cut',
'M' => 'Medium Straight-Cut',
]);
$this->subject = new ShirtSize();
}
/**
* @return array<string, array{string, bool}>
*/
public function provideTestValidateData(): array
{
$data = [
'empty string' => ['', false],
'null' => [null, false],
'0' => [0, false],
'"S" (known value)' => ['S', true],
'"M" (known value)' => ['M', true],
'"L" (unknown value)' => ['L', false],
];
return $data;
}
/**
* @covers \Engelsystem\Http\Validation\Rules\ShirtSize::__construct
* @dataProvider provideTestValidateData
*/
public function testValidate(mixed $value, bool $expectedValid): void
{
self::assertSame($expectedValid, $this->subject->validate($value));
}
}

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Engelsystem\Test\Unit\Http\Validation\Rules; namespace Engelsystem\Test\Unit\Http\Validation\Rules;
use Engelsystem\Application;
use Engelsystem\Config\Config; use Engelsystem\Config\Config;
use Engelsystem\Http\Validation\Rules\Username; use Engelsystem\Http\Validation\Rules\Username;
use Engelsystem\Test\Unit\ServiceProviderTest; use Engelsystem\Test\Unit\ServiceProviderTest;
@ -20,11 +19,8 @@ class UsernameTest extends ServiceProviderTest
{ {
$this->subject = new Username(); $this->subject = new Username();
$app = $this->getApp([]); $app = $this->createAndSetUpAppWithConfig([]);
$this->config = new Config([]); $this->config = $app->get('config');
$app->instance('config', $this->config);
$app->get('config');
Application::setInstance($app);
// load "username_regex" from the default config // load "username_regex" from the default config
$defaultConfig = include __DIR__ . '/../../../../../config/config.default.php'; $defaultConfig = include __DIR__ . '/../../../../../config/config.default.php';

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Engelsystem\Test\Unit; namespace Engelsystem\Test\Unit;
use Engelsystem\Application; use Engelsystem\Application;
use Engelsystem\Config\Config;
use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\MockObject;
abstract class ServiceProviderTest extends TestCase abstract class ServiceProviderTest extends TestCase
@ -15,4 +16,18 @@ abstract class ServiceProviderTest extends TestCase
->onlyMethods($methods) ->onlyMethods($methods)
->getMock(); ->getMock();
} }
/**
* Creates an Application instance with a Config set as 'config'.
* Also sets up the instance as global Application instance.
*
* @param string[] $methods Names of the methods to mock
*/
protected function createAndSetUpAppWithConfig(array $methods = ['make', 'instance']): Application|MockObject
{
$app = $this->getApp($methods);
$app->instance('config', new Config([]));
Application::setInstance($app);
return $app;
}
} }