Skip to content

Commit 9d216b5

Browse files
committed
fix: handle dynamic rules
1 parent adeabea commit 9d216b5

File tree

5 files changed

+406
-7
lines changed

5 files changed

+406
-7
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
All notable changes to `laravel-json-validation-testing` will be documented in this file.
44

5+
## 1.0.2 - 2022-05-04
6+
7+
**Fixed**
8+
- Handle dynamic rules such as `size.array`, `max.string` etc.
9+
510
## 1.0.1 - 2022-03-03
611

712
**Fixed**

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ it('throws validation error', function () {
4747
});
4848
```
4949

50+
It supports as well dynamic rules, such as `between`, `size`, `max` etc. You just need to specify the type of rule you want to apply:
51+
52+
```php
53+
it('throws validation error', function () {
54+
$this->postJson('/')
55+
->assertJsonValidationErrorRule('foo', 'between.string:1,5') // The foo must be between 1 and 5 characters.
56+
->assertJsonValidationErrorRule('foo', 'size.array:3'); // The foo must contain 3 items.
57+
});
58+
```
59+
5060
You can even test multiple validation errors at once by providing an array of `field => rule` as argument:
5161

5262
```php

src/Validator.php

+23
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,23 @@
33
namespace DaniloPolani\JsonValidation;
44

55
use Illuminate\Contracts\Validation\Rule as RuleContract;
6+
use Illuminate\Support\Str;
67
use Illuminate\Validation\ValidationRuleParser;
78

89
class Validator extends \Illuminate\Validation\Validator
910
{
11+
protected array $dynamicRules = [
12+
'between',
13+
'gt',
14+
'gte',
15+
'lt',
16+
'lte',
17+
'max',
18+
'min',
19+
'password',
20+
'size',
21+
];
22+
1023
/**
1124
* Get the error messages for an attribute and a validation rule.
1225
*
@@ -68,6 +81,16 @@ public function getErrorMessage(string $attribute, string|RuleContract $rule): a
6881
*/
6982
protected function buildMessage(string $message, string $attribute, string $rule, array $parameters): string
7083
{
84+
// Convert dynamic rules such as "size.array" into their original shape ("size")
85+
if (str_contains($rule, '.')) {
86+
$originalRule = Str::of($rule)->before('.')->snake()->toString();
87+
88+
// $originalRule !== $rule is needed because when no "." is present on the rule, it returns the whole string
89+
if ($originalRule !== $rule && in_array($originalRule, $this->dynamicRules)) {
90+
$rule = $originalRule;
91+
}
92+
}
93+
7194
$result = $this->makeReplacements(...func_get_args());
7295

7396
// Preserve original attribute name if nested (e.g. array.1.field)

tests/ValidationRuleMessageTest.php

+199-7
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22

33
use DaniloPolani\JsonValidation\JsonValidation;
44
use Illuminate\Contracts\Validation\Rule as RuleContract;
5+
use Illuminate\Support\Collection;
56
use Illuminate\Support\Facades\App;
67
use Illuminate\Translation\ArrayLoader;
78
use Illuminate\Translation\Translator;
89

9-
it('returns the error message for built-in rules', function () {
10+
$rules = require './tests/validation.php';
11+
$rulesToLoad = Collection::make($rules)->mapWithKeys(fn (mixed $value, string $key) => [
12+
'validation.' . $key => $value,
13+
])->toArray();
14+
15+
it('returns the error message for built-in rules', function () use ($rulesToLoad) {
1016
$trans = new Translator(new ArrayLoader(), 'en');
11-
$trans->addLines([
12-
'validation.required_array_keys' => 'The :attribute field must contain entries for :values',
13-
'validation.required' => 'The :attribute field is required.',
14-
'validation.required_without' => 'The :attribute field is required when :values is not present.',
15-
], 'en');
17+
$trans->addLines($rulesToLoad, 'en');
1618

1719
/** @var \DaniloPolani\JsonValidation\Tests\TestCase $this */
1820
App::getFacadeApplication()->instance('translator', $trans);
@@ -21,7 +23,7 @@
2123
'The foo field is required.',
2224
]);
2325
expect(JsonValidation::getRuleErrorMessage('foo', 'required_array_keys:bar,baz'))->toBe([
24-
'The foo field must contain entries for bar, baz',
26+
'The foo field must contain entries for: bar, baz.',
2527
]);
2628
expect(JsonValidation::getRuleErrorMessage('foo', 'required_without:bar'))->toBe([
2729
'The foo field is required when bar is not present.',
@@ -51,3 +53,193 @@ public function message(): string
5153

5254
expect(JsonValidation::getRuleErrorMessage('foo', $rule))->toBe(['foo must be baz']);
5355
});
56+
57+
it('handles dynamic "between" rule', function (string $shape) use ($rules, $rulesToLoad) {
58+
$trans = new Translator(new ArrayLoader(), 'en');
59+
$trans->addLines($rulesToLoad, 'en');
60+
61+
/** @var \DaniloPolani\JsonValidation\Tests\TestCase $this */
62+
App::getFacadeApplication()->instance('translator', $trans);
63+
64+
$expected = str_replace(
65+
[':min', ':max', ':attribute'],
66+
[3, 5, 'foo'],
67+
$rules['between'][$shape],
68+
);
69+
70+
expect(JsonValidation::getRuleErrorMessage('foo', sprintf('between.%s:3,5', $shape)))->toBe([$expected]);
71+
})->with([
72+
'array',
73+
'file',
74+
'numeric',
75+
'string',
76+
]);
77+
78+
it('handles dynamic "gt" rule', function (string $shape) use ($rules, $rulesToLoad) {
79+
$trans = new Translator(new ArrayLoader(), 'en');
80+
$trans->addLines($rulesToLoad, 'en');
81+
82+
/** @var \DaniloPolani\JsonValidation\Tests\TestCase $this */
83+
App::getFacadeApplication()->instance('translator', $trans);
84+
85+
$expected = str_replace(
86+
[':value', ':attribute'],
87+
[3, 'foo'],
88+
$rules['gt'][$shape],
89+
);
90+
91+
expect(JsonValidation::getRuleErrorMessage('foo', sprintf('gt.%s:3', $shape)))->toBe([$expected]);
92+
})->with([
93+
'array',
94+
'file',
95+
'numeric',
96+
'string',
97+
]);
98+
99+
it('handles dynamic "gte" rule', function (string $shape) use ($rules, $rulesToLoad) {
100+
$trans = new Translator(new ArrayLoader(), 'en');
101+
$trans->addLines($rulesToLoad, 'en');
102+
103+
/** @var \DaniloPolani\JsonValidation\Tests\TestCase $this */
104+
App::getFacadeApplication()->instance('translator', $trans);
105+
106+
$expected = str_replace(
107+
[':value', ':attribute'],
108+
[3, 'foo'],
109+
$rules['gte'][$shape],
110+
);
111+
112+
expect(JsonValidation::getRuleErrorMessage('foo', sprintf('gte.%s:3', $shape)))->toBe([$expected]);
113+
})->with([
114+
'array',
115+
'file',
116+
'numeric',
117+
'string',
118+
]);
119+
120+
it('handles dynamic "lt" rule', function (string $shape) use ($rules, $rulesToLoad) {
121+
$trans = new Translator(new ArrayLoader(), 'en');
122+
$trans->addLines($rulesToLoad, 'en');
123+
124+
/** @var \DaniloPolani\JsonValidation\Tests\TestCase $this */
125+
App::getFacadeApplication()->instance('translator', $trans);
126+
127+
$expected = str_replace(
128+
[':value', ':attribute'],
129+
[3, 'foo'],
130+
$rules['lt'][$shape],
131+
);
132+
133+
expect(JsonValidation::getRuleErrorMessage('foo', sprintf('lt.%s:3', $shape)))->toBe([$expected]);
134+
})->with([
135+
'array',
136+
'file',
137+
'numeric',
138+
'string',
139+
]);
140+
141+
it('handles dynamic "lte" rule', function (string $shape) use ($rules, $rulesToLoad) {
142+
$trans = new Translator(new ArrayLoader(), 'en');
143+
$trans->addLines($rulesToLoad, 'en');
144+
145+
/** @var \DaniloPolani\JsonValidation\Tests\TestCase $this */
146+
App::getFacadeApplication()->instance('translator', $trans);
147+
148+
$expected = str_replace(
149+
[':value', ':attribute'],
150+
[3, 'foo'],
151+
$rules['lte'][$shape],
152+
);
153+
154+
expect(JsonValidation::getRuleErrorMessage('foo', sprintf('lte.%s:3', $shape)))->toBe([$expected]);
155+
})->with([
156+
'array',
157+
'file',
158+
'numeric',
159+
'string',
160+
]);
161+
162+
it('handles dynamic "max" rule', function (string $shape) use ($rules, $rulesToLoad) {
163+
$trans = new Translator(new ArrayLoader(), 'en');
164+
$trans->addLines($rulesToLoad, 'en');
165+
166+
/** @var \DaniloPolani\JsonValidation\Tests\TestCase $this */
167+
App::getFacadeApplication()->instance('translator', $trans);
168+
169+
$expected = str_replace(
170+
[':max', ':attribute'],
171+
[3, 'foo'],
172+
$rules['max'][$shape],
173+
);
174+
175+
expect(JsonValidation::getRuleErrorMessage('foo', sprintf('max.%s:3', $shape)))->toBe([$expected]);
176+
})->with([
177+
'array',
178+
'file',
179+
'numeric',
180+
'string',
181+
]);
182+
183+
it('handles dynamic "min" rule', function (string $shape) use ($rules, $rulesToLoad) {
184+
$trans = new Translator(new ArrayLoader(), 'en');
185+
$trans->addLines($rulesToLoad, 'en');
186+
187+
/** @var \DaniloPolani\JsonValidation\Tests\TestCase $this */
188+
App::getFacadeApplication()->instance('translator', $trans);
189+
190+
$expected = str_replace(
191+
[':min', ':attribute'],
192+
[3, 'foo'],
193+
$rules['min'][$shape],
194+
);
195+
196+
expect(JsonValidation::getRuleErrorMessage('foo', sprintf('min.%s:3', $shape)))->toBe([$expected]);
197+
})->with([
198+
'array',
199+
'file',
200+
'numeric',
201+
'string',
202+
]);
203+
204+
it('handles dynamic "password" rule', function (string $shape) use ($rules, $rulesToLoad) {
205+
$trans = new Translator(new ArrayLoader(), 'en');
206+
$trans->addLines($rulesToLoad, 'en');
207+
208+
/** @var \DaniloPolani\JsonValidation\Tests\TestCase $this */
209+
App::getFacadeApplication()->instance('translator', $trans);
210+
211+
$expected = str_replace(
212+
':attribute',
213+
'foo',
214+
$rules['password'][$shape],
215+
);
216+
217+
expect(JsonValidation::getRuleErrorMessage('foo', sprintf('password.%s', $shape)))->toBe([$expected]);
218+
})->with([
219+
'letters',
220+
'mixed',
221+
'numbers',
222+
'symbols',
223+
'uncompromised',
224+
]);
225+
226+
it('handles dynamic "size" rule', function (string $shape) use ($rules, $rulesToLoad) {
227+
$trans = new Translator(new ArrayLoader(), 'en');
228+
$trans->addLines($rulesToLoad, 'en');
229+
230+
/** @var \DaniloPolani\JsonValidation\Tests\TestCase $this */
231+
App::getFacadeApplication()->instance('translator', $trans);
232+
233+
$expected = str_replace(
234+
[':size', ':attribute'],
235+
[3, 'foo'],
236+
$rules['size'][$shape],
237+
);
238+
239+
expect(JsonValidation::getRuleErrorMessage('foo', sprintf('size.%s:3', $shape)))->toBe([$expected]);
240+
})->with([
241+
'array',
242+
'file',
243+
'numeric',
244+
'string',
245+
]);

0 commit comments

Comments
 (0)