Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/Illuminate/Testing/TestResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use ArrayAccess;
use Closure;
use Illuminate\Contracts\Validation\Rule as RuleContract;
use Illuminate\Contracts\View\View;
use Illuminate\Cookie\CookieValuePrefix;
use Illuminate\Database\Eloquent\Model;
Expand All @@ -18,6 +19,7 @@
use Illuminate\Testing\Assert as PHPUnit;
use Illuminate\Testing\Constraints\SeeInOrder;
use Illuminate\Testing\Fluent\AssertableJson;
use Illuminate\Validation\Validator;
use LogicException;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\StreamedResponse;
Expand Down Expand Up @@ -914,6 +916,35 @@ public function assertJsonValidationErrorFor($key, $responseKey = 'errors')
return $this;
}

/**
* Assert that the response has the given JSON validation errors.
*
* @param string|array $attribute
* @param string|\Illuminate\Contracts\Validation\Rule|null $rule
* @param string $responseKey
* @return $this
*/
public function assertJsonValidationErrorRule(string|array $attribute, string|RuleContract|null $rule = null, $responseKey = 'errors')
{
$validationRules = $attribute;

if (is_string($attribute)) {
PHPUnit::assertNotNull($rule, 'No validation rule was provided.');

$validationRules = [$attribute => $rule];
}

$validator = new Validator(app('translator'), [], []);

foreach ($validationRules as $attribute => $rule) {
$this->assertJsonValidationErrors([
$attribute => $validator->getErrorMessage($attribute, $rule),
], $responseKey);
}

return $this;
}

/**
* Assert that the response has no JSON validation errors for the given keys.
*
Expand Down
54 changes: 54 additions & 0 deletions src/Illuminate/Validation/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,60 @@ public function setFallbackMessages(array $messages)
$this->fallbackMessages = $messages;
}

/**
* Get the error messages for an attribute and a validation rule.
*
* @param string $attribute
* @param string|\Illuminate\Contracts\Validation\Rule $rule
* @return array
*/
public function getErrorMessage(string $attribute, string|RuleContract $rule): array
{
[$rule, $parameters] = ValidationRuleParser::parse($rule);
$result = [];

if ($rule === '') {
return $result;
}

// First we will get the correct keys for the given attribute in case the field is nested in
// an array. Then we determine if the given rule accepts other field names as parameters.
// If so, we will replace any asterisks found in the parameters with the correct keys.
if ($this->dependsOnOtherFields($rule)) {
$parameters = $this->replaceDotInParameters($parameters);

if ($keys = $this->getExplicitKeys($attribute)) {
$parameters = $this->replaceAsterisksInParameters($parameters, $keys);
}
}

if ($rule instanceof RuleContract) {
$messages = $rule->message();

$messages = $messages ? (array) $messages : [get_class($rule)];

foreach ($messages as $message) {
$result[] = $this->makeReplacements(
$message, $attribute, get_class($rule), []
);
}

return $result;
}

$attribute = str_replace(
[$this->dotPlaceholder, '__asterisk__'],
['.', '*'],
$attribute
);

return [
$this->makeReplacements(
$this->getMessage($attribute, $rule), $attribute, $rule, $parameters
),
];
}

/**
* Get the Presence Verifier implementation.
*
Expand Down
76 changes: 76 additions & 0 deletions tests/Integration/Testing/TestResponseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace Illuminate\Tests\Integration\Testing;

use Illuminate\Contracts\Validation\Rule as RuleContract;
use Illuminate\Http\Response;
use Illuminate\Testing\TestResponse;
use Orchestra\Testbench\TestCase;
use PHPUnit\Framework\ExpectationFailedException;

class TestResponseTest extends TestCase
{
public function testassertJsonValidationErrorRuleWithString()
{
$data = [
'status' => 'ok',
'errors' => ['key' => 'The key field is required.'],
];

$testResponse = TestResponse::fromBaseResponse(
(new Response)->setContent(json_encode($data))
);

$testResponse->assertJsonValidationErrorRule('key', 'required');
}

public function testassertJsonValidationErrorRuleWithArray()
{
$data = [
'status' => 'ok',
'errors' => ['key' => 'The key field is required.'],
];

$testResponse = TestResponse::fromBaseResponse(
(new Response)->setContent(json_encode($data))
);

$testResponse->assertJsonValidationErrorRule(['key' => 'required']);
}

public function testassertJsonValidationErrorRuleWithCustomRule()
{
$rule = new class implements RuleContract
{
public function passes($attribute, $value)
{
return true;
}

public function message()
{
return ':attribute must be baz';
}
};

$data = [
'status' => 'ok',
'errors' => ['key' => 'key must be baz'],
];

$testResponse = TestResponse::fromBaseResponse(
(new Response)->setContent(json_encode($data))
);

$testResponse->assertJsonValidationErrorRule('key', $rule);
}

public function testassertJsonValidationErrorRuleWithNoRule()
{
$this->expectException(ExpectationFailedException::class);
$this->expectExceptionMessage('No validation rule was provided.');

$response = TestResponse::fromBaseResponse(new Response());
$response->assertJsonValidationErrorRule('foo');
}
}
48 changes: 48 additions & 0 deletions tests/Validation/ValidationValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7096,6 +7096,54 @@ public function testArrayKeysValidationFailsWithNotAnArray()
);
}

public function testGetErrorMessageWithBuiltinRule()
{
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines([
'validation.required_array_keys' => 'The :attribute field must contain entries for :values',
'validation.required' => 'The :attribute field is required.',
], 'en');

$validator = new Validator($trans, [], []);

$this->assertSame(
['The foo field is required.'],
$validator->getErrorMessage('foo', 'required')
);

$this->assertSame(
['The foo field must contain entries for bar, baz'],
$validator->getErrorMessage('foo', 'required_array_keys:bar,baz')
);
}

public function testGetErrorMessageWithCustomRule()
{
$rule = new class implements Rule
{
public function passes($attribute, $value)
{
return true;
}

public function message()
{
return ':attribute must be baz';
}
};

$validator = new Validator(
$this->getIlluminateArrayTranslator(),
[],
[]
);

$this->assertSame(
['foo must be baz'],
$validator->getErrorMessage('foo', $rule)
);
}

protected function getTranslator()
{
return m::mock(TranslatorContract::class);
Expand Down