Skip to content

Commit

Permalink
Use Laravel's prepareForValidation instead of hooking into $this->all()
Browse files Browse the repository at this point in the history
This commit changes the behavior to be more compliant with the way
Laravel's internal validation works.

After a FormRequest instance has resolved in the application container,
the validator will be automatically called. Before the validation runs,
"prepareForValidation()" is triggered which has the purpose of alterting
request data before it enters validation (which, is exactly what this
package does!)

This commit also borrows a lot of test scaffolding from Laravel's
internal testing, because it is not trivial to create a FormRequest
object without initializing the entire application.
  • Loading branch information
arondeparon committed Jul 21, 2020
1 parent 673ce54 commit 3e399c0
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 15 deletions.
8 changes: 3 additions & 5 deletions src/Traits/SanitizesInputs.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@
trait SanitizesInputs
{
/**
* Get data to be validated from the request.
* Prepare the data for validation.
*
* @return array
* @return void
*/
public function validationData()
protected function prepareForValidation()
{
$this->sanitize();

return $this->all();
}

/**
Expand Down
10 changes: 10 additions & 0 deletions tests/Objects/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,14 @@
class Request extends FormRequest
{
use SanitizesInputs;

public function authorize(): bool
{
return true;
}

public function rules(): array
{
return [];
}
}
53 changes: 43 additions & 10 deletions tests/SanitizesInputsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,36 @@
use ArondeParon\RequestSanitizer\Contracts\Sanitizer;
use ArondeParon\RequestSanitizer\Sanitizers\TrimDuplicateSpaces;
use ArondeParon\RequestSanitizer\Tests\Objects\Request;
use Illuminate\Validation\ValidationException;
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;

class SanitizesInputsTest extends TestCase
{
use MockeryPHPUnitIntegration;

public function test_it_can_add_a_sanitizer()
{
$request = new Request();
$request = $this->createRequest();
$request->addSanitizer('foo', new TrimDuplicateSpaces());
$this->assertEquals(1, count($request->getSanitizers()));

self::assertCount(1, $request->getSanitizers());
}

public function test_it_can_retrieve_sanitizers_for_a_given_input()
{
$request = new Request();
$request = $this->createRequest();
$request->addSanitizer('foo', new TrimDuplicateSpaces());
$sanitizers = $request->getSanitizers('foo');

$this->assertInstanceOf(TrimDuplicateSpaces::class, $sanitizers[0]);
self::assertInstanceOf(TrimDuplicateSpaces::class, $sanitizers[0]);
}

public function test_it_will_return_an_empty_array_if_no_sanitizer_exists()
{
$request = new Request();
$request = $this->createRequest();
$sanitizers = $request->getSanitizers('foo');

$this->assertEmpty($sanitizers);
self::assertEmpty($sanitizers);
}

public function test_it_will_call_each_sanitizer_if_the_key_exists()
Expand All @@ -40,20 +45,20 @@ public function test_it_will_call_each_sanitizer_if_the_key_exists()
\Mockery::mock(Sanitizer::class),
];

$request = new Request(['foo' => 'This is a regular string']);
$request = $this->createRequest(['foo' => 'This is a regular string']);
$request->addSanitizers('foo', $sanitizers);

/** @var \Mockery\MockInterface $sanitizer */
foreach ($sanitizers as $sanitizer) {
$sanitizer->shouldReceive('sanitize')->once();
}

$request->sanitize();
$request->validateResolved();
}

public function test_it_will_handle_dot_notation()
{
$request = new Request([
$request = $this->createRequest([
'foo' => [
'bar' => 'This is a regular string',
],
Expand All @@ -66,6 +71,34 @@ public function test_it_will_handle_dot_notation()
->with('This is a regular string')
->once();

$request->sanitize();
$request->validateResolved();
}

public function test_it_should_sanitize_even_if_the_request_is_invalid()
{
$request = $this->createRequest([
'bar' => 'This is a regular string',
], RequiredFieldsRequest::class);

$request->addSanitizers('bar', [$sanitizer = \Mockery::mock(Sanitizer::class)]);

/** @var \Mockery\MockInterface $sanitizer */
$sanitizer->shouldReceive('sanitize')
->with('This is a regular string')
->once();

$this->expectException(ValidationException::class);

$request->validateResolved();
}
}

class RequiredFieldsRequest extends Request
{
public function rules(): array
{
return [
'name' => 'required'
];
}
}
97 changes: 97 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,105 @@

namespace ArondeParon\RequestSanitizer\Tests;

use ArondeParon\RequestSanitizer\Tests\Objects\Request;
use Illuminate\Container\Container;
use Illuminate\Contracts\Translation\Translator;
use Illuminate\Contracts\Validation\Factory as ValidationFactoryContract;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Redirector;
use Illuminate\Routing\UrlGenerator;
use Illuminate\Validation\Factory as ValidationFactory;
use Orchestra\Testbench\TestCase as OrchestraTestCase;
use Mockery as m;

abstract class TestCase extends OrchestraTestCase
{
protected $mocks = [];

protected function tearDown(): void
{
m::close();

$this->mocks = [];

parent::tearDown();
}

/**
* Create a new request of the given type.
*
* @param array $payload
* @param string $class
* @return Request
*/
protected function createRequest($payload = [], $class = Request::class)
{
$container = tap(new Container, function ($container) {
$container->instance(
ValidationFactoryContract::class,
$this->createValidationFactory($container)
);
});

$request = $class::create('/', 'GET', $payload);

return $request->setRedirector($this->createMockRedirector($request))
->setContainer($container);
}

/**
* Create a new validation factory.
*
* @param \Illuminate\Container\Container $container
* @return \Illuminate\Validation\Factory
*/
protected function createValidationFactory($container)
{
$translator = m::mock(Translator::class)->shouldReceive('get')
->zeroOrMoreTimes()->andReturn('error')->getMock();

return new ValidationFactory($translator, $container);
}

/**
* Create a mock redirector.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\Redirector
*/
protected function createMockRedirector($request)
{
$redirector = $this->mocks['redirector'] = m::mock(Redirector::class);

$redirector->shouldReceive('getUrlGenerator')->zeroOrMoreTimes()
->andReturn($generator = $this->createMockUrlGenerator());

$redirector->shouldReceive('to')->zeroOrMoreTimes()
->andReturn($this->createMockRedirectResponse());

$generator->shouldReceive('previous')->zeroOrMoreTimes()
->andReturn('previous');

return $redirector;
}

/**
* Create a mock URL generator.
*
* @return \Illuminate\Routing\UrlGenerator
*/
protected function createMockUrlGenerator()
{
return $this->mocks['generator'] = m::mock(UrlGenerator::class);
}

/**
* Create a mock redirect response.
*
* @return \Illuminate\Http\RedirectResponse
*/
protected function createMockRedirectResponse()
{
return $this->mocks['redirect'] = m::mock(RedirectResponse::class);
}
}

0 comments on commit 3e399c0

Please sign in to comment.