diff --git a/library/Message/Placeholder/Quoted.php b/library/Message/Placeholder/Quoted.php index 31781b3c9..381d922f6 100644 --- a/library/Message/Placeholder/Quoted.php +++ b/library/Message/Placeholder/Quoted.php @@ -16,6 +16,11 @@ public function __construct( ) { } + public static function fromPath(int|string $path): self + { + return new self('.' . $path); + } + public function getValue(): string { return $this->value; diff --git a/library/Message/StandardFormatter.php b/library/Message/StandardFormatter.php index d3d9a6b7d..d12b4c9c0 100644 --- a/library/Message/StandardFormatter.php +++ b/library/Message/StandardFormatter.php @@ -9,8 +9,8 @@ namespace Respect\Validation\Message; -use Respect\Validation\Exceptions\ComponentException; use Respect\Validation\Result; +use Respect\Validation\ResultSet; use function array_filter; use function array_key_exists; @@ -18,9 +18,6 @@ use function array_values; use function count; use function current; -use function is_array; -use function is_string; -use function Respect\Stringifier\stringify; use function rtrim; use function sprintf; use function str_repeat; @@ -35,22 +32,22 @@ public function __construct( } /** - * @param array $templates + * @param array $templates */ public function main(Result $result, array $templates, Translator $translator): string { - $selectedTemplates = $this->selectTemplates($result, $templates); - if (!$this->isFinalTemplate($result, $selectedTemplates)) { - foreach ($this->extractDeduplicatedChildren($result) as $child) { - return $this->main($child, $selectedTemplates, $translator); + $selector = new TemplateSelector($result, $templates); + if (!$selector->hasOnlyItsOwnTemplate()) { + foreach (new ResultSet($result) as $child) { + return $this->main($child, $selector->templates, $translator); } } - return $this->renderer->render($this->getTemplated($result, $selectedTemplates), $translator); + return $this->renderer->render($selector->getResult(), $translator); } /** - * @param array $templates + * @param array $templates */ public function full( Result $result, @@ -59,29 +56,30 @@ public function full( int $depth = 0, Result ...$siblings ): string { - $selectedTemplates = $this->selectTemplates($result, $templates); - $isFinalTemplate = $this->isFinalTemplate($result, $selectedTemplates); - + $selector = new TemplateSelector($result, $templates); $rendered = ''; - if ($this->isAlwaysVisible($result, ...$siblings) || $isFinalTemplate) { + if ($this->isAlwaysVisible($result, ...$siblings) || $selector->hasOnlyItsOwnTemplate()) { $indentation = str_repeat(' ', $depth * 2); $rendered .= sprintf( '%s- %s' . PHP_EOL, $indentation, - $this->renderer->render($this->getTemplated($result, $selectedTemplates), $translator), + $this->renderer->render( + $depth > 0 ? $selector->getResult()->withDeepestPath() : $selector->getResult(), + $translator + ), ); $depth++; } - if (!$isFinalTemplate) { - $results = $this->extractDeduplicatedChildren($result); + if (!$selector->hasOnlyItsOwnTemplate()) { + $results = new ResultSet($result); foreach ($results as $child) { $rendered .= $this->full( $child, - $selectedTemplates, + $selector->templates, $translator, $depth, - ...array_filter($results, static fn (Result $sibling) => $sibling !== $child) + ...array_filter($results->getArrayCopy(), static fn (Result $sibling) => $sibling !== $child) ); $rendered .= PHP_EOL; } @@ -91,37 +89,41 @@ public function full( } /** - * @param array $templates + * @param array $templates * - * @return array + * @return array */ public function array(Result $result, array $templates, Translator $translator): array { - $selectedTemplates = $this->selectTemplates($result, $templates); + $selector = new TemplateSelector($result, $templates); $deduplicatedChildren = $this->extractDeduplicatedChildren($result); - if (count($deduplicatedChildren) === 0 || $this->isFinalTemplate($result, $selectedTemplates)) { + if (count($deduplicatedChildren) === 0 || $selector->hasOnlyItsOwnTemplate()) { return [ - $result->id => $this->renderer->render($this->getTemplated($result, $selectedTemplates), $translator), + $result->path ?? $result->id => $this->renderer->render( + $selector->getResult()->withDeepestPath(), + $translator + ), ]; } $messages = []; foreach ($deduplicatedChildren as $child) { - $messages[$child->id] = $this->array( - $child, - $this->selectTemplates($child, $selectedTemplates), + $key = $child->path ?? $child->id; + $messages[$key] = $this->array( + $this->resultWithPath($result, $child), + $selector->templates, $translator ); - if (count($messages[$child->id]) !== 1) { + if (count($messages[$key]) !== 1) { continue; } - $messages[$child->id] = current($messages[$child->id]); + $messages[$key] = current($messages[$key]); } if (count($messages) > 1) { $self = [ - '__root__' => $this->renderer->render($this->getTemplated($result, $selectedTemplates), $translator), + '__root__' => $this->renderer->render($selector->getResult()->withDeepestPath(), $translator), ]; return $self + $messages; @@ -130,6 +132,19 @@ public function array(Result $result, array $templates, Translator $translator): return $messages; } + public function resultWithPath(Result $parent, Result $child): Result + { + if ($parent->path !== null && $child->path !== null && $child->path !== $parent->path) { + return $child->withPath($parent->path); + } + + if ($parent->path !== null && $child->path === null) { + return $child->withPath($parent->path); + } + + return $child; + } + private function isAlwaysVisible(Result $result, Result ...$siblings): bool { if ($result->isValid) { @@ -165,61 +180,6 @@ private function isAlwaysVisible(Result $result, Result ...$siblings): bool ); } - /** @param array $templates */ - private function getTemplated(Result $result, array $templates): Result - { - if ($result->hasCustomTemplate()) { - return $result; - } - - if (!isset($templates[$result->id]) && isset($templates['__root__'])) { - return $result->withTemplate($templates['__root__']); - } - - if (!isset($templates[$result->id])) { - return $result; - } - - $template = $templates[$result->id]; - if (is_string($template)) { - return $result->withTemplate($template); - } - - throw new ComponentException( - sprintf('Template for "%s" must be a string, %s given', $result->id, stringify($template)) - ); - } - - /** - * @param array $templates - */ - private function isFinalTemplate(Result $result, array $templates): bool - { - if (isset($templates[$result->id]) && is_string($templates[$result->id])) { - return true; - } - - if (count($templates) !== 1) { - return false; - } - - return isset($templates['__root__']) || isset($templates[$result->id]); - } - - /** - * @param array $templates - * - * @return array - */ - private function selectTemplates(Result $message, array $templates): array - { - if (isset($templates[$message->id]) && is_array($templates[$message->id])) { - return $templates[$message->id]; - } - - return $templates; - } - /** @return array */ private function extractDeduplicatedChildren(Result $result): array { @@ -227,6 +187,11 @@ private function extractDeduplicatedChildren(Result $result): array $deduplicatedResults = []; $duplicateCounters = []; foreach ($result->children as $child) { + if ($child->path !== null) { + $deduplicatedResults[$child->path] = $child->isValid ? null : $child; + continue; + } + $id = $child->id; if (isset($duplicateCounters[$id])) { $id .= '.' . ++$duplicateCounters[$id]; @@ -236,7 +201,7 @@ private function extractDeduplicatedChildren(Result $result): array $duplicateCounters[$id] = 2; $id .= '.2'; } - $deduplicatedResults[$id] = $child->isValid ? null : $child->withId($id); + $deduplicatedResults[$id] = $child->isValid ? null : $child->withId((string) $id); } return array_values(array_filter($deduplicatedResults)); diff --git a/library/Message/StandardRenderer.php b/library/Message/StandardRenderer.php index 6a2069972..0dc8e7775 100644 --- a/library/Message/StandardRenderer.php +++ b/library/Message/StandardRenderer.php @@ -37,9 +37,12 @@ public function __construct( public function render(Result $result, Translator $translator, ?string $template = null): string { $parameters = $result->parameters; - $parameters['name'] ??= $result->name ?? $this->placeholder('input', $result->input, $translator); + $parameters['path'] = $result->path !== null ? Quoted::fromPath($result->path) : null; $parameters['input'] = $result->input; + $builtName = $result->name ?? $parameters['path'] ?? $this->placeholder('input', $result->input, $translator); + $parameters['name'] ??= $builtName; + $rendered = (string) preg_replace_callback( '/{{(\w+)(\|([^}]+))?}}/', function (array $matches) use ($parameters, $translator) { diff --git a/library/Message/TemplateSelector.php b/library/Message/TemplateSelector.php new file mode 100644 index 000000000..cf874abd0 --- /dev/null +++ b/library/Message/TemplateSelector.php @@ -0,0 +1,93 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Message; + +use Respect\Validation\Exceptions\ComponentException; +use Respect\Validation\Result; + +use function count; +use function is_array; +use function is_string; +use function Respect\Stringifier\stringify; +use function sprintf; + +final class TemplateSelector +{ + /** @var array */ + public readonly array $templates; + + /** @param array $templates */ + public function __construct( + private readonly Result $result, + array $templates, + ) { + $this->templates = $this->selectedTemplates($templates); + } + + public function getResult(): Result + { + if ($this->result->hasCustomTemplate()) { + return $this->result; + } + + foreach ([$this->result->getDeepestPath(), $this->result->name, $this->result->id, '__root__'] as $key) { + if (!isset($this->templates[$key])) { + continue; + } + + if (is_string($this->templates[$key])) { + return $this->result->withTemplate($this->templates[$key]); + } + + throw new ComponentException( + sprintf('Template for "%s" must be a string, %s given', $key, stringify($this->templates[$key])) + ); + } + + return $this->result; + } + + public function hasOnlyItsOwnTemplate(): bool + { + $keys = [$this->result->getDeepestPath(), $this->result->name, $this->result->id]; + foreach ($keys as $key) { + if (isset($this->templates[$key]) && is_string($this->templates[$key])) { + return true; + } + } + + if (count($this->templates) !== 1) { + return false; + } + + foreach ($keys as $key) { + if (isset($this->templates[$key])) { + return true; + } + } + + return isset($this->templates['__root__']); + } + + /** + * @param array $templates + * @return array + */ + private function selectedTemplates(array $templates): array + { + foreach ([$this->result->getDeepestPath(), $this->result->name, $this->result->id] as $key) { + if (isset($templates[$key]) && is_array($templates[$key])) { + return $templates[$key]; + } + } + + return $templates; + } +} diff --git a/library/Result.php b/library/Result.php index 4a78edba7..1a97219d3 100644 --- a/library/Result.php +++ b/library/Result.php @@ -10,11 +10,12 @@ namespace Respect\Validation; use Respect\Validation\Rules\Core\Nameable; -use Respect\Validation\Rules\Core\Renameable; use function array_filter; use function array_map; use function count; +use function end; +use function explode; use function lcfirst; use function preg_match; use function strrchr; @@ -39,7 +40,7 @@ public function __construct( public readonly ?string $name = null, ?string $id = null, public readonly ?Result $adjacent = null, - public readonly bool $unchangeableId = false, + public readonly string|int|null $path = null, Result ...$children, ) { $this->id = $id ?? lcfirst(substr((string) strrchr($rule::class, '\\'), 1)); @@ -102,10 +103,6 @@ public function withExtraParameters(array $parameters): self public function withId(string $id): self { - if ($this->unchangeableId) { - return $this; - } - return $this->clone(id: $id); } @@ -114,14 +111,44 @@ public function withIdFrom(Rule $rule): self return $this->clone(id: lcfirst(substr((string) strrchr($rule::class, '\\'), 1))); } - public function withUnchangeableId(string $id): self + public function withPath(string|int $path): self + { + return $this->clone( + adjacent: $this->adjacent?->withPath($path), + path: $this->path === null ? $path : $path . '.' . $this->path, + ); + } + + public function withDeepestPath(): self + { + $path = $this->getDeepestPath(); + if ($path === null || $path === (string) $this->path) { + return $this; + } + + return $this->clone( + adjacent: $this->adjacent?->withPath($path), + path: $path, + ); + } + + public function getDeepestPath(): ?string { - return $this->clone(id: $id, unchangeableId: true); + if ($this->path === null) { + return null; + } + + $paths = explode('.', (string) $this->path); + if (count($paths) === 1) { + return (string) $this->path; + } + + return end($paths); } public function withPrefix(string $prefix): self { - if ($this->id === $this->name || $this->unchangeableId) { + if ($this->id === $this->name || $this->path !== null) { return $this; } @@ -136,10 +163,10 @@ public function withChildren(Result ...$children): self public function withName(string $name): self { return $this->clone( - name: $this->rule instanceof Renameable ? $name : ($this->name ?? $name), + name: $this->name ?? $name, adjacent: $this->adjacent?->withName($name), children: array_map( - static fn (Result $child) => $child->withName($child->name ?? $name), + static fn (Result $child) => $child->path === null ? $child->withName($child->name ?? $name) : $child, $this->children ), ); @@ -223,7 +250,7 @@ private function clone( ?string $name = null, ?string $id = null, ?Result $adjacent = null, - ?bool $unchangeableId = null, + string|int|null $path = null, ?array $children = null ): self { return new self( @@ -236,7 +263,7 @@ private function clone( $name ?? $this->name, $id ?? $this->id, $adjacent ?? $this->adjacent, - $unchangeableId ?? $this->unchangeableId, + $path ?? $this->path, ...($children ?? $this->children) ); } diff --git a/library/ResultSet.php b/library/ResultSet.php new file mode 100644 index 000000000..43c757c2f --- /dev/null +++ b/library/ResultSet.php @@ -0,0 +1,115 @@ + + * SPDX-License-Identifier: MIT + */ + +namespace Respect\Validation; + +use Countable; +use Iterator; + +use function array_filter; +use function array_key_exists; +use function array_map; +use function array_values; +use function count; +use function current; +use function key; +use function next; +use function reset; + +/** + * @implements Iterator + */ +final class ResultSet implements Iterator, Countable +{ + /** @var array */ + private array $children; + + public function __construct( + private readonly Result $result, + ) { + $this->children = $this->extractDeduplicatedChildren(); + } + + /** @return array */ + public function extractDeduplicatedChildren(): array + { + /** @var array $deduplicatedResults */ + $deduplicatedResults = []; + $duplicateCounters = []; + foreach ($this->result->children as $child) { + if ($child->path !== null) { + $deduplicatedResults[$child->path] = $child->isValid ? null : $child; + continue; + } + + $id = $child->id; + if (isset($duplicateCounters[$id])) { + $id .= '.' . ++$duplicateCounters[$id]; + } elseif (array_key_exists($id, $deduplicatedResults)) { + $deduplicatedResults[$id . '.1'] = $deduplicatedResults[$id]?->withId($id . '.1'); + unset($deduplicatedResults[$id]); + $duplicateCounters[$id] = 2; + $id .= '.2'; + } + + $deduplicatedResults[$id] = $child->isValid ? null : $child->withId($id); + } + + return array_map( + function (Result $child): Result { + if ($this->result->path !== null && $child->path !== null && $child->path !== $this->result->path) { + return $child->withPath($this->result->path); + } + + if ($this->result->path !== null && $child->path === null) { + return $child->withPath($this->result->path); + } + + return $child; + }, + array_values(array_filter($deduplicatedResults)) + ); + } + + /** @return array */ + public function getArrayCopy(): array + { + return $this->children; + } + + public function current(): Result|false + { + return current($this->children); + } + + public function next(): void + { + next($this->children); + } + + public function key(): ?int + { + return key($this->children); + } + + public function valid(): bool + { + return key($this->children) !== null; + } + + public function rewind(): void + { + reset($this->children); + } + + public function count(): int + { + return count($this->children); + } +} diff --git a/library/Rules/Core/Renameable.php b/library/Rules/Core/Renameable.php deleted file mode 100644 index a8c60ac2e..000000000 --- a/library/Rules/Core/Renameable.php +++ /dev/null @@ -1,14 +0,0 @@ - - * SPDX-License-Identifier: MIT - */ - -declare(strict_types=1); - -namespace Respect\Validation\Rules\Core; - -interface Renameable -{ -} diff --git a/library/Rules/Each.php b/library/Rules/Each.php index ee1dad562..6ccb2f234 100644 --- a/library/Rules/Each.php +++ b/library/Rules/Each.php @@ -28,7 +28,7 @@ protected function evaluateNonEmptyArray(array $input): Result { $children = []; foreach ($input as $key => $value) { - $children[] = $this->rule->evaluate($value)->withUnchangeableId((string) $key); + $children[] = $this->rule->evaluate($value)->withPath($key); } $isValid = array_reduce($children, static fn ($carry, $childResult) => $carry && $childResult->isValid, true); diff --git a/library/Rules/Key.php b/library/Rules/Key.php index bebe073c4..2fe90fe76 100644 --- a/library/Rules/Key.php +++ b/library/Rules/Key.php @@ -13,7 +13,6 @@ use Respect\Validation\Result; use Respect\Validation\Rule; use Respect\Validation\Rules\Core\KeyRelated; -use Respect\Validation\Rules\Core\Nameable; use Respect\Validation\Rules\Core\Wrapper; #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] @@ -35,21 +34,9 @@ public function evaluate(mixed $input): Result { $keyExistsResult = (new KeyExists($this->key))->evaluate($input); if (!$keyExistsResult->isValid) { - return $keyExistsResult->withName($this->getName()); + return $keyExistsResult->withNameFrom($this->rule); } - return $this->rule - ->evaluate($input[$this->key]) - ->withName($this->getName()) - ->withUnchangeableId((string) $this->key); - } - - private function getName(): string - { - if ($this->rule instanceof Nameable) { - return $this->rule->getName() ?? ((string) $this->key); - } - - return (string) $this->key; + return $this->rule->evaluate($input[$this->key])->withPath($this->key); } } diff --git a/library/Rules/KeyExists.php b/library/Rules/KeyExists.php index 3d79e3876..46d5e1164 100644 --- a/library/Rules/KeyExists.php +++ b/library/Rules/KeyExists.php @@ -14,7 +14,6 @@ use Respect\Validation\Message\Template; use Respect\Validation\Result; use Respect\Validation\Rules\Core\KeyRelated; -use Respect\Validation\Rules\Core\Renameable; use Respect\Validation\Rules\Core\Standard; use function array_key_exists; @@ -25,7 +24,7 @@ '{{name}} must be present', '{{name}} must not be present', )] -final class KeyExists extends Standard implements KeyRelated, Renameable +final class KeyExists extends Standard implements KeyRelated { public function __construct( private readonly int|string $key @@ -39,7 +38,7 @@ public function getKey(): int|string public function evaluate(mixed $input): Result { - return new Result($this->hasKey($input), $input, $this, name: (string) $this->key, id: (string) $this->key); + return new Result($this->hasKey($input), $input, $this, path: $this->key); } private function hasKey(mixed $input): bool diff --git a/library/Rules/Property.php b/library/Rules/Property.php index 0eaac24b4..365413b61 100644 --- a/library/Rules/Property.php +++ b/library/Rules/Property.php @@ -13,7 +13,6 @@ use ReflectionObject; use Respect\Validation\Result; use Respect\Validation\Rule; -use Respect\Validation\Rules\Core\Nameable; use Respect\Validation\Rules\Core\Wrapper; #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] @@ -30,22 +29,12 @@ public function evaluate(mixed $input): Result { $propertyExistsResult = (new PropertyExists($this->propertyName))->evaluate($input); if (!$propertyExistsResult->isValid) { - return $propertyExistsResult->withName($this->getName()); + return $propertyExistsResult->withNameFrom($this->rule); } return $this->rule ->evaluate($this->extractPropertyValue($input, $this->propertyName)) - ->withName($this->getName()) - ->withUnchangeableId($this->propertyName); - } - - private function getName(): string - { - if ($this->rule instanceof Nameable) { - return $this->rule->getName() ?? $this->propertyName; - } - - return $this->propertyName; + ->withPath($this->propertyName); } private function extractPropertyValue(object $input, string $property): mixed diff --git a/library/Rules/PropertyExists.php b/library/Rules/PropertyExists.php index 3a8a66c61..a3f00cdd6 100644 --- a/library/Rules/PropertyExists.php +++ b/library/Rules/PropertyExists.php @@ -13,7 +13,6 @@ use ReflectionObject; use Respect\Validation\Message\Template; use Respect\Validation\Result; -use Respect\Validation\Rules\Core\Renameable; use Respect\Validation\Rules\Core\Standard; use function is_object; @@ -23,7 +22,7 @@ '{{name}} must be present', '{{name}} must not be present', )] -final class PropertyExists extends Standard implements Renameable +final class PropertyExists extends Standard { public function __construct( private readonly string $propertyName @@ -36,8 +35,7 @@ public function evaluate(mixed $input): Result $this->hasProperty($input), $input, $this, - name: $this->propertyName, - id: $this->propertyName + path: $this->propertyName, ); } diff --git a/library/Transformers/DeprecatedKeyValue.php b/library/Transformers/DeprecatedKeyValue.php index acca88599..61d8ef1ff 100644 --- a/library/Transformers/DeprecatedKeyValue.php +++ b/library/Transformers/DeprecatedKeyValue.php @@ -9,6 +9,7 @@ namespace Respect\Validation\Transformers; +use Respect\Validation\Message\Placeholder\Quoted; use Respect\Validation\Rules\AlwaysInvalid; use Respect\Validation\Rules\Key; use Respect\Validation\Rules\KeyExists; @@ -52,8 +53,8 @@ static function ($input) use ($comparedKey, $ruleName, $baseKey) { } catch (Throwable) { return new Templated( new AlwaysInvalid(), - '{{baseKey|raw}} must be valid to validate {{comparedKey|raw}}', - ['comparedKey' => $comparedKey, 'baseKey' => $baseKey] + '{{baseKey}} must be valid to validate {{comparedKey}}', + ['comparedKey' => Quoted::fromPath($comparedKey), 'baseKey' => Quoted::fromPath($baseKey)] ); } } diff --git a/tests/feature/AssertWithKeysTest.php b/tests/feature/AssertWithKeysTest.php index 521cc0d5c..8d755969d 100644 --- a/tests/feature/AssertWithKeysTest.php +++ b/tests/feature/AssertWithKeysTest.php @@ -38,15 +38,15 @@ ]), <<<'FULL_MESSAGE' - the given data must pass all the rules - - mysql must pass all the rules - - host must be a string - - user must be present - - password must be present - - schema must be a string - - postgresql must pass all the rules - - host must be present - - user must be a string - - password must be a string - - schema must be present + - `.mysql` must pass all the rules + - `.host` must be a string + - `.user` must be present + - `.password` must be present + - `.schema` must be a string + - `.postgresql` must pass all the rules + - `.host` must be present + - `.user` must be a string + - `.password` must be a string + - `.schema` must be present FULL_MESSAGE, )); diff --git a/tests/feature/AssertWithPropertiesTest.php b/tests/feature/AssertWithPropertiesTest.php index a57bbd4f2..b039a83fb 100644 --- a/tests/feature/AssertWithPropertiesTest.php +++ b/tests/feature/AssertWithPropertiesTest.php @@ -46,9 +46,9 @@ function (): void { }, <<<'FULL_MESSAGE' - the given data must pass all the rules - - mysql must pass the rules - - host must be a string - - postgresql must pass the rules - - user must be a string + - `.mysql` must pass the rules + - `.host` must be a string + - `.postgresql` must pass the rules + - `.user` must be a string FULL_MESSAGE, )); diff --git a/tests/feature/GetMessagesShouldIncludeAllValidationMessagesInAChainTest.php b/tests/feature/GetMessagesShouldIncludeAllValidationMessagesInAChainTest.php index e329867c3..290f1b882 100644 --- a/tests/feature/GetMessagesShouldIncludeAllValidationMessagesInAChainTest.php +++ b/tests/feature/GetMessagesShouldIncludeAllValidationMessagesInAChainTest.php @@ -21,9 +21,9 @@ function (): void { }, [ '__root__' => '`["username": "u", "birthdate": "Not a date", "password": ""]` must pass all the rules', - 'username' => 'The length of username must be between 2 and 32', - 'birthdate' => 'birthdate must be a valid date/time', - 'password' => 'password must not be empty', - 'email' => 'email must be present', + 'username' => 'The length of `.username` must be between 2 and 32', + 'birthdate' => '`.birthdate` must be a valid date/time', + 'password' => '`.password` must not be empty', + 'email' => '`.email` must be present', ], )); diff --git a/tests/feature/GetMessagesTest.php b/tests/feature/GetMessagesTest.php index b12b234c1..b15ecb70d 100644 --- a/tests/feature/GetMessagesTest.php +++ b/tests/feature/GetMessagesTest.php @@ -22,18 +22,18 @@ [ '__root__' => '`["mysql": ["host": 42, "schema": 42], "postgresql": ["user": 42, "password": 42]]` must pass all the rules', 'mysql' => [ - '__root__' => 'mysql must pass all the rules', - 'host' => 'host must be a string', - 'user' => 'user must be present', - 'password' => 'password must be present', - 'schema' => 'schema must be a string', + '__root__' => '`.mysql` must pass all the rules', + 'host' => '`.host` must be a string', + 'user' => '`.user` must be present', + 'password' => '`.password` must be present', + 'schema' => '`.schema` must be a string', ], 'postgresql' => [ - '__root__' => 'postgresql must pass all the rules', - 'host' => 'host must be present', - 'user' => 'user must be a string', - 'password' => 'password must be a string', - 'schema' => 'schema must be present', + '__root__' => '`.postgresql` must pass all the rules', + 'host' => '`.host` must be present', + 'user' => '`.user` must be a string', + 'password' => '`.password` must be a string', + 'schema' => '`.schema` must be present', ], ], )); diff --git a/tests/feature/GetMessagesWithReplacementsTest.php b/tests/feature/GetMessagesWithReplacementsTest.php index c57cf13ee..da6d709ac 100644 --- a/tests/feature/GetMessagesWithReplacementsTest.php +++ b/tests/feature/GetMessagesWithReplacementsTest.php @@ -51,17 +51,17 @@ function (): void { [ '__root__' => '`["mysql": ["host": 42, "schema": 42], "postgresql": ["user": 42, "password": 42]]` must pass all the rules', 'mysql' => [ - '__root__' => 'mysql must pass all the rules', - 'host' => '`host` should be a MySQL host', + '__root__' => '`.mysql` must pass all the rules', + 'host' => '``.host`` should be a MySQL host', 'user' => 'Value should be a MySQL username', - 'password' => 'password must be present', - 'schema' => 'schema must be a string', + 'password' => '`.password` must be present', + 'schema' => '`.schema` must be a string', ], 'postgresql' => [ - '__root__' => 'postgresql must pass all the rules', - 'host' => 'host must be present', - 'user' => 'user must be a string', - 'password' => 'password must be a string', + '__root__' => '`.postgresql` must pass all the rules', + 'host' => '`.host` must be present', + 'user' => '`.user` must be a string', + 'password' => '`.password` must be a string', 'schema' => 'You must provide a valid PostgreSQL schema', ], ], diff --git a/tests/feature/Issues/Issue1033Test.php b/tests/feature/Issues/Issue1033Test.php index 3eecc2732..ac9982d54 100644 --- a/tests/feature/Issues/Issue1033Test.php +++ b/tests/feature/Issues/Issue1033Test.php @@ -9,17 +9,17 @@ test('https://github.com/Respect/Validation/issues/1033', expectAll( fn() => v::each(v::equals(1))->assert(['A', 'B', 'B']), - '"A" must be equal to 1', + '`.0` must be equal to 1', <<<'FULL_MESSAGE' - Each item in `["A", "B", "B"]` must be valid - - "A" must be equal to 1 - - "B" must be equal to 1 - - "B" must be equal to 1 + - `.0` must be equal to 1 + - `.1` must be equal to 1 + - `.2` must be equal to 1 FULL_MESSAGE, [ '__root__' => 'Each item in `["A", "B", "B"]` must be valid', - 0 => '"A" must be equal to 1', - 1 => '"B" must be equal to 1', - 2 => '"B" must be equal to 1', + 0 => '`.0` must be equal to 1', + 1 => '`.1` must be equal to 1', + 2 => '`.2` must be equal to 1', ], )); diff --git a/tests/feature/Issues/Issue1289Test.php b/tests/feature/Issues/Issue1289Test.php index 9bb2767b0..240493d37 100644 --- a/tests/feature/Issues/Issue1289Test.php +++ b/tests/feature/Issues/Issue1289Test.php @@ -45,23 +45,23 @@ 'children' => ['nope'], ], ]), - 'default must be a string', + '`.0.default` must be a string', <<<'FULL_MESSAGE' - - `["default": 2, "description": [], "children": ["nope"]]` must pass the rules - - default must pass one of the rules - - default must be a string - - default must be a boolean - - description must be a string value + - `.0` must pass the rules + - `.default` must pass one of the rules + - `.default` must be a string + - `.default` must be a boolean + - `.description` must be a string value FULL_MESSAGE, [ 0 => [ - '__root__' => '`["default": 2, "description": [], "children": ["nope"]]` must pass the rules', + '__root__' => '`.0` must pass the rules', 'default' => [ - '__root__' => 'default must pass one of the rules', - 'stringType' => 'default must be a string', - 'boolType' => 'default must be a boolean', + '__root__' => '`.default` must pass one of the rules', + 'stringType' => '`.default` must be a string', + 'boolType' => '`.default` must be a boolean', ], - 'description' => 'description must be a string value', + 'description' => '`.description` must be a string value', ], ], )); diff --git a/tests/feature/Issues/Issue1334Test.php b/tests/feature/Issues/Issue1334Test.php index 8a7b676fc..c209effa1 100644 --- a/tests/feature/Issues/Issue1334Test.php +++ b/tests/feature/Issues/Issue1334Test.php @@ -22,28 +22,28 @@ function (): void { ], ); }, - 'street must be present', + '`.0.street` must be present', <<<'FULL_MESSAGE' - Each item in `[["region": "Oregon", "country": "USA", "other": 123], ["street": "", "region": "Oregon", "country": "USA"], ["s ... ]` must be valid - - `["region": "Oregon", "country": "USA", "other": 123]` must pass the rules - - street must be present - - other must pass the rules - - other must be a string or must be null - - `["street": "", "region": "Oregon", "country": "USA"]` must pass the rules - - street must not be empty - - `["street": 123, "region": "Oregon", "country": "USA"]` must pass the rules - - street must be a string + - `.0` must pass the rules + - `.street` must be present + - `.other` must pass the rules + - `.other` must be a string or must be null + - `.1` must pass the rules + - `.street` must not be empty + - `.2` must pass the rules + - `.street` must be a string FULL_MESSAGE, [ 'each' => [ '__root__' => 'Each item in `[["region": "Oregon", "country": "USA", "other": 123], ["street": "", "region": "Oregon", "country": "USA"], ["s ... ]` must be valid', 0 => [ - '__root__' => '`["region": "Oregon", "country": "USA", "other": 123]` must pass the rules', - 'street' => 'street must be present', - 'other' => 'other must be a string or must be null', + '__root__' => '`.0` must pass the rules', + 'street' => '`.street` must be present', + 'other' => '`.other` must be a string or must be null', ], - 1 => 'street must not be empty', - 2 => 'street must be a string', + 1 => '`.street` must not be empty', + 2 => '`.street` must be a string', ], ], )); diff --git a/tests/feature/Issues/Issue1376Test.php b/tests/feature/Issues/Issue1376Test.php index 3671c121c..639089c52 100644 --- a/tests/feature/Issues/Issue1376Test.php +++ b/tests/feature/Issues/Issue1376Test.php @@ -14,25 +14,25 @@ ->property('author', v::intType()->lengthBetween(1, 2)) ->property('user', v::intVal()->lengthBetween(1, 2)) ->assert((object) ['author' => 'foo']), - 'title must be present', + '`.title` must be present', <<<'FULL_MESSAGE' - `stdClass { +$author="foo" }` must pass all the rules - - title must be present - - description must be present - - author must pass all the rules - - author must be an integer - - The length of author must be between 1 and 2 - - user must be present + - `.title` must be present + - `.description` must be present + - `.author` must pass all the rules + - `.author` must be an integer + - The length of `.author` must be between 1 and 2 + - `.user` must be present FULL_MESSAGE, [ '__root__' => '`stdClass { +$author="foo" }` must pass all the rules', - 'title' => 'title must be present', - 'description' => 'description must be present', + 'title' => '`.title` must be present', + 'description' => '`.description` must be present', 'author' => [ - '__root__' => 'author must pass all the rules', - 'intType' => 'author must be an integer', - 'lengthBetween' => 'The length of author must be between 1 and 2', + '__root__' => '`.author` must pass all the rules', + 'intType' => '`.author` must be an integer', + 'lengthBetween' => 'The length of `.author` must be between 1 and 2', ], - 'user' => 'user must be present', + 'user' => '`.user` must be present', ], )); diff --git a/tests/feature/Issues/Issue1469Test.php b/tests/feature/Issues/Issue1469Test.php index 6a07b2abd..ab0f3d86f 100644 --- a/tests/feature/Issues/Issue1469Test.php +++ b/tests/feature/Issues/Issue1469Test.php @@ -9,45 +9,64 @@ test('https://github.com/Respect/Validation/issues/1469', expectAll( function (): void { - $data = [ - 'order_items' => [ - [ - 'product_title' => 'test', - 'quantity' => 'test', + v::create() + ->arrayVal() + ->keySet( + v::key( + 'order_items', + v::create() + ->arrayVal() + ->each( + v::keySet( + v::key('product_title', v::stringVal()->notEmpty()), + v::key('quantity', v::intVal()->notEmpty()), + ) + ) + ->notEmpty() + ), + ) + ->assert([ + 'order_items' => [ + [ + 'product_title' => 'test', + 'quantity' => 'test', + ], + [ + 'product_title2' => 'test', + ], ], - [ - 'product_title2' => 'test', - ], - ], - ]; - - v::arrayVal()->keySet( - v::key('order_items', v::arrayVal()->each(v::keySet( - v::key('product_title', v::stringVal()->notEmpty()), - v::key('quantity', v::intVal()->notEmpty()), - ))->notEmpty()), - )->assert($data); + ]); }, - 'quantity must be an integer value', + '`.order_items.0.quantity` must be an integer value', <<<'FULL_MESSAGE' - - Each item in order_items must be valid - - order_items validation failed - - quantity must be an integer value - - order_items contains both missing and extra keys - - product_title must be present - - quantity must be present - - product_title2 must not be present + - Each item in `.order_items` must be valid + - `.0` validation failed + - `.quantity` must be an integer value + - `.1` contains both missing and extra keys + - `.product_title` must be present + - `.quantity` must be present + - `.product_title2` must not be present FULL_MESSAGE, [ 'keySet' => [ - '__root__' => 'Each item in order_items must be valid', + '__root__' => 'Each item in `.order_items` must be valid', 0 => 'quantity must be an integer value', 1 => [ - '__root__' => 'order_items contains both missing and extra keys', - 'product_title' => 'product_title must be present', - 'quantity' => 'quantity must be present', - 'product_title2' => 'product_title2 must not be present', + '__root__' => '`.order_items` contains both missing and extra keys', + 'product_title' => '`.product_title` must be present', + 'quantity' => '`.quantity` must be present', + 'product_title2' => '`.product_title2` must not be present', ], ], ], -)); +))->skip(<<assert(['Address' => 'cvejvn']); }, - 'Address is not good!', - '- Address is not good!', - ['Address' => 'Address is not good!'], + '`.Address` is not good!', + '- `.Address` is not good!', + ['Address' => '`.Address` is not good!'], )); diff --git a/tests/feature/Issues/Issue179Test.php b/tests/feature/Issues/Issue179Test.php index f94f18258..1111c28d5 100644 --- a/tests/feature/Issues/Issue179Test.php +++ b/tests/feature/Issues/Issue179Test.php @@ -23,15 +23,15 @@ function (): void { $validator->key('schema', v::stringType()); $validator->assert($config); }, - 'host must be a string', + '`.host` must be a string', <<<'FULL_MESSAGE' - Settings must pass the rules - - host must be a string - - user must be present + - `.host` must be a string + - `.user` must be present FULL_MESSAGE, [ '__root__' => 'Settings must pass the rules', - 'host' => 'host must be a string', - 'user' => 'user must be present', + 'host' => '`.host` must be a string', + 'user' => '`.user` must be present', ], )); diff --git a/tests/feature/Issues/Issue425Test.php b/tests/feature/Issues/Issue425Test.php index 75813b832..9f2f8d4c0 100644 --- a/tests/feature/Issues/Issue425Test.php +++ b/tests/feature/Issues/Issue425Test.php @@ -14,7 +14,7 @@ function (): void { ->key('reference', v::stringType()->notEmpty()->lengthBetween(1, 50)); $validator->assert(['age' => 1]); }, - 'reference must be present', - '- reference must be present', - ['reference' => 'reference must be present'], + '`.reference` must be present', + '- `.reference` must be present', + ['reference' => '`.reference` must be present'], )); diff --git a/tests/feature/Issues/Issue446Test.php b/tests/feature/Issues/Issue446Test.php index 151a54a68..8947da8fc 100644 --- a/tests/feature/Issues/Issue446Test.php +++ b/tests/feature/Issues/Issue446Test.php @@ -17,7 +17,7 @@ ->key('name', v::lengthBetween(2, 32)) ->key('email', v::email()) ->assert($arr), - 'The length of name must be between 2 and 32', - '- The length of name must be between 2 and 32', - ['name' => 'The length of name must be between 2 and 32'], + 'The length of `.name` must be between 2 and 32', + '- The length of `.name` must be between 2 and 32', + ['name' => 'The length of `.name` must be between 2 and 32'], )); diff --git a/tests/feature/Issues/Issue796Test.php b/tests/feature/Issues/Issue796Test.php index 8fe9842ca..975705ff6 100644 --- a/tests/feature/Issues/Issue796Test.php +++ b/tests/feature/Issues/Issue796Test.php @@ -40,17 +40,17 @@ 'schema' => 'schema', ], ]), - 'host must be a string', + '`.mysql.host` must be a string', <<<'FULL_MESSAGE' - the given data must pass all the rules - - mysql must pass the rules - - host must be a string - - postgresql must pass the rules - - user must be a string + - `.mysql` must pass the rules + - `.host` must be a string + - `.postgresql` must pass the rules + - `.user` must be a string FULL_MESSAGE, [ '__root__' => 'the given data must pass all the rules', - 'mysql' => 'host must be a string', - 'postgresql' => 'user must be a string', + 'mysql' => '`.host` must be a string', + 'postgresql' => '`.user` must be a string', ], )); diff --git a/tests/feature/Issues/Issue799Test.php b/tests/feature/Issues/Issue799Test.php index e8075a7af..34060c5a7 100644 --- a/tests/feature/Issues/Issue799Test.php +++ b/tests/feature/Issues/Issue799Test.php @@ -22,12 +22,12 @@ <<<'FULL_MESSAGE' - 1 must pass all the rules - 1 must be an array value - - scheme must be present + - `.scheme` must be present FULL_MESSAGE, [ '__root__' => '1 must pass all the rules', 'arrayVal' => '1 must be an array value', - 'scheme' => 'scheme must be present', + 'scheme' => '`.scheme` must be present', ], )); @@ -38,7 +38,7 @@ v::arrayVal()->key('scheme', v::startsWith('https')), ) ->assert($input), - 'scheme must start with "https"', - '- scheme must start with "https"', - ['scheme' => 'scheme must start with "https"'], + '`.scheme` must start with "https"', + '- `.scheme` must start with "https"', + ['scheme' => '`.scheme` must start with "https"'], )); diff --git a/tests/feature/KeysAsValidatorNamesTest.php b/tests/feature/KeysAsValidatorNamesTest.php index 37a74aa92..82d801ba8 100644 --- a/tests/feature/KeysAsValidatorNamesTest.php +++ b/tests/feature/KeysAsValidatorNamesTest.php @@ -21,7 +21,7 @@ function (): void { }, <<<'FULL_MESSAGE' - User Subscription Form must pass all the rules - - The length of username must be between 2 and 32 - - birthdate must be a valid date/time + - The length of `.username` must be between 2 and 32 + - `.birthdate` must be a valid date/time FULL_MESSAGE, )); diff --git a/tests/feature/Rules/AttributesTest.php b/tests/feature/Rules/AttributesTest.php index 902be09fa..497f25234 100644 --- a/tests/feature/Rules/AttributesTest.php +++ b/tests/feature/Rules/AttributesTest.php @@ -11,16 +11,16 @@ test('Default', expectAll( fn() => v::attributes()->assert(new WithAttributes('', 'john.doe@gmail.com', '2024-06-23')), - 'name must not be empty', - '- name must not be empty', - ['name' => 'name must not be empty'], + '`.name` must not be empty', + '- `.name` must not be empty', + ['name' => '`.name` must not be empty'], )); test('Inverted', expectAll( fn() => v::attributes()->assert(new WithAttributes('John Doe', 'john.doe@gmail.com', '2024-06-23', '+1234567890')), - 'phone must be a valid telephone number or must be null', - '- phone must be a valid telephone number or must be null', - ['phone' => 'phone must be a valid telephone number or must be null'], + '`.phone` must be a valid telephone number or must be null', + '- `.phone` must be a valid telephone number or must be null', + ['phone' => '`.phone` must be a valid telephone number or must be null'], )); test('Not an object', expectAll( @@ -32,39 +32,39 @@ test('Nullable', expectAll( fn() => v::attributes()->assert(new WithAttributes('John Doe', 'john.doe@gmail.com', '2024-06-23', 'not a phone number')), - 'phone must be a valid telephone number or must be null', - '- phone must be a valid telephone number or must be null', - ['phone' => 'phone must be a valid telephone number or must be null'], + '`.phone` must be a valid telephone number or must be null', + '- `.phone` must be a valid telephone number or must be null', + ['phone' => '`.phone` must be a valid telephone number or must be null'], )); test('Multiple attributes, all failed', expectAll( fn() => v::attributes()->assert(new WithAttributes('', 'not an email', 'not a date', 'not a phone number')), - 'name must not be empty', + '`.name` must not be empty', <<<'FULL_MESSAGE' - `Respect\Validation\Test\Stubs\WithAttributes { +$name="" +$email="not an email" +$birthdate="not a date" +$phone ... }` must pass all the rules - - name must not be empty - - email must be a valid email address - - birthdate must pass all the rules - - birthdate must be a valid date in the format "2005-12-30" - - For comparison with now, birthdate must be a valid datetime - - phone must be a valid telephone number or must be null + - `.name` must not be empty + - `.email` must be a valid email address + - `.birthdate` must pass all the rules + - `.birthdate` must be a valid date in the format "2005-12-30" + - For comparison with now, `.birthdate` must be a valid datetime + - `.phone` must be a valid telephone number or must be null FULL_MESSAGE, [ - '__root__' => '`Respect\\Validation\\Test\\Stubs\\WithAttributes { +$name="" +$email="not an email" +$birthdate="not a date" +$phone ... }` must pass all the rules', - 'name' => 'name must not be empty', - 'email' => 'email must be a valid email address', + '__root__' => '`Respect\Validation\Test\Stubs\WithAttributes { +$name="" +$email="not an email" +$birthdate="not a date" +$phone ... }` must pass all the rules', + 'name' => '`.name` must not be empty', + 'email' => '`.email` must be a valid email address', 'birthdate' => [ - '__root__' => 'birthdate must pass all the rules', - 'date' => 'birthdate must be a valid date in the format "2005-12-30"', - 'dateTimeDiffLessThanOrEqual' => 'For comparison with now, birthdate must be a valid datetime', + '__root__' => '`.birthdate` must pass all the rules', + 'date' => '`.birthdate` must be a valid date in the format "2005-12-30"', + 'dateTimeDiffLessThanOrEqual' => 'For comparison with now, `.birthdate` must be a valid datetime', ], - 'phone' => 'phone must be a valid telephone number or must be null', + 'phone' => '`.phone` must be a valid telephone number or must be null', ], )); test('Multiple attributes, one failed', expectAll( fn() => v::attributes()->assert(new WithAttributes('John Doe', 'john.doe@gmail.com', '22 years ago')), - 'birthdate must be a valid date in the format "2005-12-30"', - '- birthdate must be a valid date in the format "2005-12-30"', - ['birthdate' => 'birthdate must be a valid date in the format "2005-12-30"'], + '`.birthdate` must be a valid date in the format "2005-12-30"', + '- `.birthdate` must be a valid date in the format "2005-12-30"', + ['birthdate' => '`.birthdate` must be a valid date in the format "2005-12-30"'], )); diff --git a/tests/feature/Rules/CircuitTest.php b/tests/feature/Rules/CircuitTest.php index 31a0779e4..f9ba6a7dc 100644 --- a/tests/feature/Rules/CircuitTest.php +++ b/tests/feature/Rules/CircuitTest.php @@ -86,7 +86,7 @@ v::key('countyCode', v::countryCode()), v::lazy(fn ($input) => v::key('subdivisionCode', v::subdivisionCode($input['countyCode']))), )->assert(['countyCode' => 'BR', 'subdivisionCode' => 'CA']), - 'subdivisionCode must be a subdivision code of Brazil', - '- subdivisionCode must be a subdivision code of Brazil', - ['subdivisionCode' => 'subdivisionCode must be a subdivision code of Brazil'], + '`.subdivisionCode` must be a subdivision code of Brazil', + '- `.subdivisionCode` must be a subdivision code of Brazil', + ['subdivisionCode' => '`.subdivisionCode` must be a subdivision code of Brazil'], )); diff --git a/tests/feature/Rules/EachTest.php b/tests/feature/Rules/EachTest.php index 5110ff574..7476d19e6 100644 --- a/tests/feature/Rules/EachTest.php +++ b/tests/feature/Rules/EachTest.php @@ -23,35 +23,35 @@ test('Default', expectAll( fn() => v::each(v::intType())->assert(['a', 'b', 'c']), - '"a" must be an integer', + '`.0` must be an integer', <<<'FULL_MESSAGE' - Each item in `["a", "b", "c"]` must be valid - - "a" must be an integer - - "b" must be an integer - - "c" must be an integer + - `.0` must be an integer + - `.1` must be an integer + - `.2` must be an integer FULL_MESSAGE, [ '__root__' => 'Each item in `["a", "b", "c"]` must be valid', - 0 => '"a" must be an integer', - 1 => '"b" must be an integer', - 2 => '"c" must be an integer', + 0 => '`.0` must be an integer', + 1 => '`.1` must be an integer', + 2 => '`.2` must be an integer', ], )); test('Inverted', expectAll( fn() => v::not(v::each(v::intType()))->assert([1, 2, 3]), - '1 must not be an integer', + '`.0` must not be an integer', <<<'FULL_MESSAGE' - Each item in `[1, 2, 3]` must be invalid - - 1 must not be an integer - - 2 must not be an integer - - 3 must not be an integer + - `.0` must not be an integer + - `.1` must not be an integer + - `.2` must not be an integer FULL_MESSAGE, [ '__root__' => 'Each item in `[1, 2, 3]` must be invalid', - 0 => '1 must not be an integer', - 1 => '2 must not be an integer', - 2 => '3 must not be an integer', + 0 => '`.0` must not be an integer', + 1 => '`.1` must not be an integer', + 2 => '`.2` must not be an integer', ], )); @@ -105,52 +105,52 @@ test('With wrapper name, default', expectAll( fn() => v::each(v::intType())->setName('Wrapper')->assert(['a', 'b', 'c']), - 'Wrapper must be an integer', + '`.0` must be an integer', <<<'FULL_MESSAGE' - Each item in Wrapper must be valid - - Wrapper must be an integer - - Wrapper must be an integer - - Wrapper must be an integer + - `.0` must be an integer + - `.1` must be an integer + - `.2` must be an integer FULL_MESSAGE, [ '__root__' => 'Each item in Wrapper must be valid', - 0 => 'Wrapper must be an integer', - 1 => 'Wrapper must be an integer', - 2 => 'Wrapper must be an integer', + 0 => '`.0` must be an integer', + 1 => '`.1` must be an integer', + 2 => '`.2` must be an integer', ], )); test('With wrapper name, inverted', expectAll( fn() => v::not(v::each(v::intType())->setName('Wrapper'))->setName('Not')->assert([1, 2, 3]), - 'Wrapper must not be an integer', + '`.0` must not be an integer', <<<'FULL_MESSAGE' - Each item in Wrapper must be invalid - - Wrapper must not be an integer - - Wrapper must not be an integer - - Wrapper must not be an integer + - `.0` must not be an integer + - `.1` must not be an integer + - `.2` must not be an integer FULL_MESSAGE, [ '__root__' => 'Each item in Wrapper must be invalid', - 0 => 'Wrapper must not be an integer', - 1 => 'Wrapper must not be an integer', - 2 => 'Wrapper must not be an integer', + 0 => '`.0` must not be an integer', + 1 => '`.1` must not be an integer', + 2 => '`.2` must not be an integer', ], )); test('With Not name, inverted', expectAll( fn() => v::not(v::each(v::intType()))->setName('Not')->assert([1, 2, 3]), - 'Not must not be an integer', + '`.0` must not be an integer', <<<'FULL_MESSAGE' - Each item in Not must be invalid - - Not must not be an integer - - Not must not be an integer - - Not must not be an integer + - `.0` must not be an integer + - `.1` must not be an integer + - `.2` must not be an integer FULL_MESSAGE, [ '__root__' => 'Each item in Not must be invalid', - 0 => 'Not must not be an integer', - 1 => 'Not must not be an integer', - 2 => 'Not must not be an integer', + 0 => '`.0` must not be an integer', + 1 => '`.1` must not be an integer', + 2 => '`.2` must not be an integer', ], )); @@ -225,69 +225,69 @@ ], ]) ->assert(['a', 'b', 'c']), - 'Wrapped must be an integer', + 'First item should have been an integer', <<<'FULL_MESSAGE' - - Each item in Wrapped must be valid - - Wrapped must be an integer - - Wrapped must be an integer - - Wrapped must be an integer + - Here a sequence of items that did not pass the validation + - First item should have been an integer + - Second item should have been an integer + - Third item should have been an integer FULL_MESSAGE, [ - '__root__' => 'Each item in Wrapped must be valid', - 0 => 'Wrapped must be an integer', - 1 => 'Wrapped must be an integer', - 2 => 'Wrapped must be an integer', + '__root__' => 'Here a sequence of items that did not pass the validation', + 0 => 'First item should have been an integer', + 1 => 'Second item should have been an integer', + 2 => 'Third item should have been an integer', ], )); test('Chained wrapped rule', expectAll( fn() => v::each(v::between(5, 7)->odd())->assert([2, 4]), - '2 must be between 5 and 7', + '`.0` must be between 5 and 7', <<<'FULL_MESSAGE' - Each item in `[2, 4]` must be valid - - 2 must pass all the rules - - 2 must be between 5 and 7 - - 2 must be an odd number - - 4 must pass all the rules - - 4 must be between 5 and 7 - - 4 must be an odd number + - `.0` must pass all the rules + - `.0` must be between 5 and 7 + - `.0` must be an odd number + - `.1` must pass all the rules + - `.1` must be between 5 and 7 + - `.1` must be an odd number FULL_MESSAGE, [ '__root__' => 'Each item in `[2, 4]` must be valid', 0 => [ - '__root__' => '2 must pass all the rules', - 'between' => '2 must be between 5 and 7', - 'odd' => '2 must be an odd number', + '__root__' => '`.0` must pass all the rules', + 'between' => '`.0` must be between 5 and 7', + 'odd' => '`.0` must be an odd number', ], 1 => [ - '__root__' => '4 must pass all the rules', - 'between' => '4 must be between 5 and 7', - 'odd' => '4 must be an odd number', + '__root__' => '`.1` must pass all the rules', + 'between' => '`.1` must be between 5 and 7', + 'odd' => '`.1` must be an odd number', ], ], )); test('Multiple nested rules', expectAll( fn() => v::each(v::arrayType()->key('my_int', v::intType()->odd()))->assert([['not_int' => 'wrong'], ['my_int' => 2], 'not an array']), - 'my_int must be present', + '`.0.my_int` must be present', <<<'FULL_MESSAGE' - Each item in `[["not_int": "wrong"], ["my_int": 2], "not an array"]` must be valid - - `["not_int": "wrong"]` must pass the rules - - my_int must be present - - `["my_int": 2]` must pass the rules - - my_int must be an odd number - - "not an array" must pass all the rules - - "not an array" must be an array - - my_int must be present + - `.0` must pass the rules + - `.my_int` must be present + - `.1` must pass the rules + - `.my_int` must be an odd number + - `.2` must pass all the rules + - `.2` must be an array + - `.my_int` must be present FULL_MESSAGE, [ '__root__' => 'Each item in `[["not_int": "wrong"], ["my_int": 2], "not an array"]` must be valid', - 0 => 'my_int must be present', - 1 => 'my_int must be an odd number', + 0 => '`.my_int` must be present', + 1 => '`.my_int` must be an odd number', 2 => [ - '__root__' => '"not an array" must pass all the rules', - 'arrayType' => '"not an array" must be an array', - 'my_int' => 'my_int must be present', + '__root__' => '`.2` must pass all the rules', + 'arrayType' => '`.2` must be an array', + 'my_int' => '`.my_int` must be present', ], ], )); diff --git a/tests/feature/Rules/KeyExistsTest.php b/tests/feature/Rules/KeyExistsTest.php index 54d4ecf5e..9f4fe3260 100644 --- a/tests/feature/Rules/KeyExistsTest.php +++ b/tests/feature/Rules/KeyExistsTest.php @@ -9,16 +9,16 @@ test('Default mode', expectAll( fn() => v::keyExists('foo')->assert(['bar' => 'baz']), - 'foo must be present', - '- foo must be present', - ['foo' => 'foo must be present'], + '`.foo` must be present', + '- `.foo` must be present', + ['foo' => '`.foo` must be present'], )); test('Inverted mode', expectAll( fn() => v::not(v::keyExists('foo'))->assert(['foo' => 'baz']), - 'foo must not be present', - '- foo must not be present', - ['foo' => 'foo must not be present'], + '`.foo` must not be present', + '- `.foo` must not be present', + ['foo' => '`.foo` must not be present'], )); test('Custom name', expectAll( @@ -29,8 +29,8 @@ )); test('Custom template', expectAll( - fn() => v::keyExists('foo')->assert(['bar' => 'baz'], 'Custom template for `{{name}}`'), - 'Custom template for `foo`', - '- Custom template for `foo`', - ['foo' => 'Custom template for `foo`'], + fn() => v::keyExists('foo')->assert(['bar' => 'baz'], 'Custom template for {{name}}'), + 'Custom template for `.foo`', + '- Custom template for `.foo`', + ['foo' => 'Custom template for `.foo`'], )); diff --git a/tests/feature/Rules/KeyOptionalTest.php b/tests/feature/Rules/KeyOptionalTest.php index 543d3cf16..1f38131c2 100644 --- a/tests/feature/Rules/KeyOptionalTest.php +++ b/tests/feature/Rules/KeyOptionalTest.php @@ -9,23 +9,23 @@ test('Default', expectAll( fn() => v::keyOptional('foo', v::intType())->assert(['foo' => 'string']), - 'foo must be an integer', - '- foo must be an integer', - ['foo' => 'foo must be an integer'], + '`.foo` must be an integer', + '- `.foo` must be an integer', + ['foo' => '`.foo` must be an integer'], )); test('Inverted', expectAll( fn() => v::not(v::keyOptional('foo', v::intType()))->assert(['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + '`.foo` must not be an integer', + '- `.foo` must not be an integer', + ['foo' => '`.foo` must not be an integer'], )); test('Inverted with missing key', expectAll( fn() => v::not(v::keyOptional('foo', v::intType()))->assert([]), - 'foo must be present', - '- foo must be present', - ['foo' => 'foo must be present'], + '`.foo` must be present', + '- `.foo` must be present', + ['foo' => '`.foo` must be present'], )); test('With wrapped name, default', expectAll( @@ -44,23 +44,23 @@ test('With wrapper name, default', expectAll( fn() => v::keyOptional('foo', v::intType())->setName('Wrapper')->assert(['foo' => 'string']), - 'foo must be an integer', - '- foo must be an integer', - ['foo' => 'foo must be an integer'], + 'Wrapper must be an integer', + '- Wrapper must be an integer', + ['foo' => 'Wrapper must be an integer'], )); test('With wrapper name, inverted', expectAll( fn() => v::not(v::keyOptional('foo', v::intType())->setName('Wrapper'))->setName('Not')->assert(['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + 'Wrapper must not be an integer', + '- Wrapper must not be an integer', + ['foo' => 'Wrapper must not be an integer'], )); test('With "Not" name, inverted', expectAll( fn() => v::not(v::keyOptional('foo', v::intType()))->setName('Not')->assert(['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + 'Not must not be an integer', + '- Not must not be an integer', + ['foo' => 'Not must not be an integer'], )); test('With template, default', expectAll( diff --git a/tests/feature/Rules/KeySetTest.php b/tests/feature/Rules/KeySetTest.php index c5155b28a..c5d7d43f1 100644 --- a/tests/feature/Rules/KeySetTest.php +++ b/tests/feature/Rules/KeySetTest.php @@ -9,52 +9,52 @@ test('one rule / one failed', expectAll( fn() => v::keySet(v::key('foo', v::intType()))->assert(['foo' => 'string']), - 'foo must be an integer', - '- foo must be an integer', - ['foo' => 'foo must be an integer'], + '`.foo` must be an integer', + '- `.foo` must be an integer', + ['foo' => '`.foo` must be an integer'], )); test('one rule / one missing key', expectAll( fn() => v::keySet(v::keyExists('foo'))->assert([]), - 'foo must be present', - '- foo must be present', - ['foo' => 'foo must be present'], + '`.foo` must be present', + '- `.foo` must be present', + ['foo' => '`.foo` must be present'], )); test('one rule / one extra key', expectAll( fn() => v::keySet(v::keyExists('foo'))->assert(['foo' => 42, 'bar' => 'string']), - 'bar must not be present', - '- bar must not be present', - ['bar' => 'bar must not be present'], + '`.bar` must not be present', + '- `.bar` must not be present', + ['bar' => '`.bar` must not be present'], )); test('one rule / one extra key / one missing key', expectAll( fn() => v::keySet(v::keyExists('foo'))->assert(['bar' => true]), - 'foo must be present', + '`.foo` must be present', <<<'FULL_MESSAGE' - `["bar": true]` contains both missing and extra keys - - foo must be present - - bar must not be present + - `.foo` must be present + - `.bar` must not be present FULL_MESSAGE, [ '__root__' => '`["bar": true]` contains both missing and extra keys', - 'foo' => 'foo must be present', - 'bar' => 'bar must not be present', + 'foo' => '`.foo` must be present', + 'bar' => '`.bar` must not be present', ], )); test('one rule / two extra keys', expectAll( fn() => v::keySet(v::keyExists('foo'))->assert(['foo' => 42, 'bar' => 'string', 'baz' => true]), - 'bar must not be present', + '`.bar` must not be present', <<<'FULL_MESSAGE' - `["foo": 42, "bar": "string", "baz": true]` contains extra keys - - bar must not be present - - baz must not be present + - `.bar` must not be present + - `.baz` must not be present FULL_MESSAGE, [ '__root__' => '`["foo": 42, "bar": "string", "baz": true]` contains extra keys', - 'bar' => 'bar must not be present', - 'baz' => 'baz must not be present', + 'bar' => '`.bar` must not be present', + 'baz' => '`.baz` must not be present', ], )); @@ -75,54 +75,54 @@ 'xyzzy' => false, 'thud' => 42, ]), - 'bar must not be present', + '`.bar` must not be present', <<<'FULL_MESSAGE' - `["foo": 42, "bar": "string", "baz": true, "qux": false, "quux": 42, ...]` contains extra keys - - bar must not be present - - baz must not be present - - qux must not be present - - quux must not be present - - corge must not be present - - grault must not be present - - garply must not be present - - waldo must not be present - - fred must not be present - - plugh must not be present + - `.bar` must not be present + - `.baz` must not be present + - `.qux` must not be present + - `.quux` must not be present + - `.corge` must not be present + - `.grault` must not be present + - `.garply` must not be present + - `.waldo` must not be present + - `.fred` must not be present + - `.plugh` must not be present FULL_MESSAGE, [ '__root__' => '`["foo": 42, "bar": "string", "baz": true, "qux": false, "quux": 42, ...]` contains extra keys', - 'bar' => 'bar must not be present', - 'baz' => 'baz must not be present', - 'qux' => 'qux must not be present', - 'quux' => 'quux must not be present', - 'corge' => 'corge must not be present', - 'grault' => 'grault must not be present', - 'garply' => 'garply must not be present', - 'waldo' => 'waldo must not be present', - 'fred' => 'fred must not be present', - 'plugh' => 'plugh must not be present', + 'bar' => '`.bar` must not be present', + 'baz' => '`.baz` must not be present', + 'qux' => '`.qux` must not be present', + 'quux' => '`.quux` must not be present', + 'corge' => '`.corge` must not be present', + 'grault' => '`.grault` must not be present', + 'garply' => '`.garply` must not be present', + 'waldo' => '`.waldo` must not be present', + 'fred' => '`.fred` must not be present', + 'plugh' => '`.plugh` must not be present', ], )); test('multiple rules / one failed', expectAll( fn() => v::keySet(v::keyExists('foo'), v::keyExists('bar'))->assert(['foo' => 42]), - 'bar must be present', - '- bar must be present', - ['bar' => 'bar must be present'], + '`.bar` must be present', + '- `.bar` must be present', + ['bar' => '`.bar` must be present'], )); test('multiple rules / all failed', expectAll( fn() => v::keySet(v::keyExists('foo'), v::keyExists('bar'))->assert([]), - 'foo must be present', + '`.foo` must be present', <<<'FULL_MESSAGE' - `[]` contains missing keys - - foo must be present - - bar must be present + - `.foo` must be present + - `.bar` must be present FULL_MESSAGE, [ '__root__' => '`[]` contains missing keys', - 'foo' => 'foo must be present', - 'bar' => 'bar must be present', + 'foo' => '`.foo` must be present', + 'bar' => '`.bar` must be present', ], )); @@ -131,9 +131,9 @@ v::keyExists('foo'), v::keyExists('bar'), )->assert(['foo' => 42, 'bar' => 'string', 'baz' => true]), - 'baz must not be present', - '- baz must not be present', - ['baz' => 'baz must not be present'], + '`.baz` must not be present', + '- `.baz` must not be present', + ['baz' => '`.baz` must not be present'], )); test('multiple rules / one extra key / one missing', expectAll( @@ -141,16 +141,16 @@ v::keyExists('foo'), v::keyExists('bar'), )->assert(['bar' => 'string', 'baz' => true]), - 'foo must be present', + '`.foo` must be present', <<<'FULL_MESSAGE' - `["bar": "string", "baz": true]` contains both missing and extra keys - - foo must be present - - baz must not be present + - `.foo` must be present + - `.baz` must not be present FULL_MESSAGE, [ '__root__' => '`["bar": "string", "baz": true]` contains both missing and extra keys', - 'foo' => 'foo must be present', - 'baz' => 'baz must not be present', + 'foo' => '`.foo` must be present', + 'baz' => '`.baz` must not be present', ], )); @@ -160,16 +160,16 @@ v::keyExists('bar'), v::keyOptional('qux', v::intType()), )->assert(['foo' => 42, 'bar' => 'string', 'baz' => true, 'qux' => false]), - 'qux must be an integer', + '`.qux` must be an integer', <<<'FULL_MESSAGE' - `["foo": 42, "bar": "string", "baz": true, "qux": false]` contains extra keys - - qux must be an integer - - baz must not be present + - `.qux` must be an integer + - `.baz` must not be present FULL_MESSAGE, [ '__root__' => '`["foo": 42, "bar": "string", "baz": true, "qux": false]` contains extra keys', - 'qux' => 'qux must be an integer', - 'baz' => 'baz must not be present', + 'qux' => '`.qux` must be an integer', + 'baz' => '`.baz` must not be present', ], )); @@ -180,16 +180,16 @@ v::key('baz', v::intType()), ) ->assert(['foo' => 42, 'bar' => 'string', 'baz' => true]), - 'bar must be an integer', + '`.bar` must be an integer', <<<'FULL_MESSAGE' - `["foo": 42, "bar": "string", "baz": true]` validation failed - - bar must be an integer - - baz must be an integer + - `.bar` must be an integer + - `.baz` must be an integer FULL_MESSAGE, [ '__root__' => '`["foo": 42, "bar": "string", "baz": true]` validation failed', - 'bar' => 'bar must be an integer', - 'baz' => 'baz must be an integer', + 'bar' => '`.bar` must be an integer', + 'baz' => '`.baz` must be an integer', ], )); @@ -201,15 +201,15 @@ ->key('baz', v::intType()), ) ->assert(['foo' => 42, 'bar' => 'string']), - 'bar must be an integer', + '`.bar` must be an integer', <<<'FULL_MESSAGE' - `["foo": 42, "bar": "string"]` contains missing keys - - bar must be an integer - - baz must be present + - `.bar` must be an integer + - `.baz` must be present FULL_MESSAGE, [ '__root__' => '`["foo": 42, "bar": "string"]` contains missing keys', - 'bar' => 'bar must be an integer', - 'baz' => 'baz must be present', + 'bar' => '`.bar` must be an integer', + 'baz' => '`.baz` must be present', ], )); diff --git a/tests/feature/Rules/KeyTest.php b/tests/feature/Rules/KeyTest.php index 9d3205063..ca964ebd5 100644 --- a/tests/feature/Rules/KeyTest.php +++ b/tests/feature/Rules/KeyTest.php @@ -9,30 +9,30 @@ test('Missing key', expectAll( fn() => v::key('foo', v::intType())->assert([]), - 'foo must be present', - '- foo must be present', - ['foo' => 'foo must be present'], + '`.foo` must be present', + '- `.foo` must be present', + ['foo' => '`.foo` must be present'], )); test('Default', expectAll( fn() => v::key('foo', v::intType())->assert(['foo' => 'string']), - 'foo must be an integer', - '- foo must be an integer', - ['foo' => 'foo must be an integer'], + '`.foo` must be an integer', + '- `.foo` must be an integer', + ['foo' => '`.foo` must be an integer'], )); test('Inverted', expectAll( fn() => v::not(v::key('foo', v::intType()))->assert(['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + '`.foo` must not be an integer', + '- `.foo` must not be an integer', + ['foo' => '`.foo` must not be an integer'], )); test('Double-inverted with missing key', expectAll( fn() => v::not(v::not(v::key('foo', v::intType())))->assert([]), - 'foo must be present', - '- foo must be present', - ['foo' => 'foo must be present'], + '`.foo` must be present', + '- `.foo` must be present', + ['foo' => '`.foo` must be present'], )); test('With wrapped name, missing key', expectAll( @@ -58,9 +58,9 @@ test('With wrapper name, default', expectAll( fn() => v::key('foo', v::intType())->setName('Wrapper')->assert(['foo' => 'string']), - 'foo must be an integer', - '- foo must be an integer', - ['foo' => 'foo must be an integer'], + 'Wrapper must be an integer', + '- Wrapper must be an integer', + ['foo' => 'Wrapper must be an integer'], )); test('With wrapper name, missing key', expectAll( @@ -72,16 +72,16 @@ test('With wrapper name, inverted', expectAll( fn() => v::not(v::key('foo', v::intType())->setName('Wrapper'))->setName('Not')->assert(['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + 'Wrapper must not be an integer', + '- Wrapper must not be an integer', + ['foo' => 'Wrapper must not be an integer'], )); test('With "Not" name, inverted', expectAll( fn() => v::not(v::key('foo', v::intType()))->setName('Not')->assert(['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + 'Not must not be an integer', + '- Not must not be an integer', + ['foo' => 'Not must not be an integer'], )); test('With template, default', expectAll( diff --git a/tests/feature/Rules/NamedTest.php b/tests/feature/Rules/NamedTest.php index aa23b9d1b..ef2138c62 100644 --- a/tests/feature/Rules/NamedTest.php +++ b/tests/feature/Rules/NamedTest.php @@ -69,17 +69,17 @@ 'Vegetables' ), )->assert(['vegetables' => ['root' => 12, 'stems' => 12]]), - 'root must be a string', + '`.vegetables.root` must be a string', <<<'FULL_MESSAGE' - Vegetables must pass all the rules - - root must be a string - - stems must be a string - - fruits must be present + - `.root` must be a string + - `.stems` must be a string + - `.fruits` must be present FULL_MESSAGE, [ '__root__' => 'Vegetables must pass all the rules', - 'root' => 'root must be a string', - 'stems' => 'stems must be a string', - 'fruits' => 'fruits must be present', + 'root' => '`.root` must be a string', + 'stems' => '`.stems` must be a string', + 'fruits' => '`.fruits` must be present', ], )); diff --git a/tests/feature/Rules/PropertyExistsTest.php b/tests/feature/Rules/PropertyExistsTest.php index 6327a8286..f92bf4d2b 100644 --- a/tests/feature/Rules/PropertyExistsTest.php +++ b/tests/feature/Rules/PropertyExistsTest.php @@ -9,16 +9,16 @@ test('Default mode', expectAll( fn() => v::propertyExists('foo')->assert((object) ['bar' => 'baz']), - 'foo must be present', - '- foo must be present', - ['foo' => 'foo must be present'], + '`.foo` must be present', + '- `.foo` must be present', + ['foo' => '`.foo` must be present'], )); test('Inverted mode', expectAll( fn() => v::not(v::propertyExists('foo'))->assert((object) ['foo' => 'baz']), - 'foo must not be present', - '- foo must not be present', - ['foo' => 'foo must not be present'], + '`.foo` must not be present', + '- `.foo` must not be present', + ['foo' => '`.foo` must not be present'], )); test('Custom name', expectAll( @@ -29,8 +29,8 @@ )); test('Custom template', expectAll( - fn() => v::propertyExists('foo')->assert((object) ['bar' => 'baz'], 'Custom template for `{{name}}`'), - 'Custom template for `foo`', - '- Custom template for `foo`', - ['foo' => 'Custom template for `foo`'], + fn() => v::propertyExists('foo')->assert((object) ['bar' => 'baz'], 'Custom template for {{name}}'), + 'Custom template for `.foo`', + '- Custom template for `.foo`', + ['foo' => 'Custom template for `.foo`'], )); diff --git a/tests/feature/Rules/PropertyOptionalTest.php b/tests/feature/Rules/PropertyOptionalTest.php index 777e176b3..29582c0e8 100644 --- a/tests/feature/Rules/PropertyOptionalTest.php +++ b/tests/feature/Rules/PropertyOptionalTest.php @@ -9,23 +9,23 @@ test('Default', expectAll( fn() => v::propertyOptional('foo', v::intType())->assert((object) ['foo' => 'string']), - 'foo must be an integer', - '- foo must be an integer', - ['foo' => 'foo must be an integer'], + '`.foo` must be an integer', + '- `.foo` must be an integer', + ['foo' => '`.foo` must be an integer'], )); test('Inverted', expectAll( fn() => v::not(v::propertyOptional('foo', v::intType()))->assert((object) ['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + '`.foo` must not be an integer', + '- `.foo` must not be an integer', + ['foo' => '`.foo` must not be an integer'], )); test('Inverted with missing property', expectAll( fn() => v::not(v::propertyOptional('foo', v::intType()))->assert(new stdClass()), - 'foo must be present', - '- foo must be present', - ['foo' => 'foo must be present'], + '`.foo` must be present', + '- `.foo` must be present', + ['foo' => '`.foo` must be present'], )); test('With wrapped name, default', expectAll( @@ -43,27 +43,24 @@ )); test('With wrapper name, default', expectAll( - fn() => v::propertyOptional('foo', v::intType()) - ->setName('Wrapper') - ->assert((object) ['foo' => 'string']), - 'foo must be an integer', - '- foo must be an integer', - ['foo' => 'foo must be an integer'], + fn() => v::propertyOptional('foo', v::intType())->setName('Wrapper')->assert((object) ['foo' => 'string']), + 'Wrapper must be an integer', + '- Wrapper must be an integer', + ['foo' => 'Wrapper must be an integer'], )); test('With wrapper name, inverted', expectAll( - fn() => v::not(v::propertyOptional('foo', v::intType())->setName('Wrapper')) - ->setName('Not')->assert((object) ['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + fn() => v::not(v::propertyOptional('foo', v::intType())->setName('Wrapper'))->setName('Not')->assert((object) ['foo' => 12]), + 'Wrapper must not be an integer', + '- Wrapper must not be an integer', + ['foo' => 'Wrapper must not be an integer'], )); test('With "Not" name, inverted', expectAll( fn() => v::not(v::propertyOptional('foo', v::intType()))->setName('Not')->assert((object) ['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + 'Not must not be an integer', + '- Not must not be an integer', + ['foo' => 'Not must not be an integer'], )); test('With template, default', expectAll( diff --git a/tests/feature/Rules/PropertyTest.php b/tests/feature/Rules/PropertyTest.php index 52ecb7634..0438ce049 100644 --- a/tests/feature/Rules/PropertyTest.php +++ b/tests/feature/Rules/PropertyTest.php @@ -9,30 +9,30 @@ test('Missing property', expectAll( fn() => v::property('foo', v::intType())->assert(new stdClass()), - 'foo must be present', - '- foo must be present', - ['foo' => 'foo must be present'], + '`.foo` must be present', + '- `.foo` must be present', + ['foo' => '`.foo` must be present'], )); test('Default', expectAll( fn() => v::property('foo', v::intType())->assert((object) ['foo' => 'string']), - 'foo must be an integer', - '- foo must be an integer', - ['foo' => 'foo must be an integer'], + '`.foo` must be an integer', + '- `.foo` must be an integer', + ['foo' => '`.foo` must be an integer'], )); test('Inverted', expectAll( fn() => v::not(v::property('foo', v::intType()))->assert((object) ['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + '`.foo` must not be an integer', + '- `.foo` must not be an integer', + ['foo' => '`.foo` must not be an integer'], )); test('Double-inverted with missing property', expectAll( fn() => v::not(v::not(v::property('foo', v::intType())))->assert(new stdClass()), - 'foo must be present', - '- foo must be present', - ['foo' => 'foo must be present'], + '`.foo` must be present', + '- `.foo` must be present', + ['foo' => '`.foo` must be present'], )); test('With wrapped name, missing property', expectAll( @@ -62,9 +62,9 @@ test('With wrapper name, default', expectAll( fn() => v::property('foo', v::intType())->setName('Wrapper')->assert((object) ['foo' => 'string']), - 'foo must be an integer', - '- foo must be an integer', - ['foo' => 'foo must be an integer'], + 'Wrapper must be an integer', + '- Wrapper must be an integer', + ['foo' => 'Wrapper must be an integer'], )); test('With wrapper name, missing property', expectAll( @@ -77,16 +77,16 @@ test('With wrapper name, inverted', expectAll( fn() => v::not(v::property('foo', v::intType())->setName('Wrapper'))->setName('Not') ->assert((object) ['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + 'Wrapper must not be an integer', + '- Wrapper must not be an integer', + ['foo' => 'Wrapper must not be an integer'], )); test('With "Not" name, inverted', expectAll( fn() => v::not(v::property('foo', v::intType()))->setName('Not')->assert((object) ['foo' => 12]), - 'foo must not be an integer', - '- foo must not be an integer', - ['foo' => 'foo must not be an integer'], + 'Not must not be an integer', + '- Not must not be an integer', + ['foo' => 'Not must not be an integer'], )); test('With template, default', expectAll( diff --git a/tests/feature/ShouldNotOverwriteDefinedNamesTest.php b/tests/feature/ShouldNotOverwriteDefinedNamesTest.php index fa34d358f..183300a54 100644 --- a/tests/feature/ShouldNotOverwriteDefinedNamesTest.php +++ b/tests/feature/ShouldNotOverwriteDefinedNamesTest.php @@ -16,10 +16,10 @@ test('Scenario #2', expectMessage( fn() => v::key('email', v::email())->setName('Email')->assert($input), - 'email must be a valid email address', + 'Email must be a valid email address', )); test('Scenario #3', expectMessage( fn() => v::key('email', v::email())->assert($input), - 'email must be a valid email address', + '`.email` must be a valid email address', )); diff --git a/tests/feature/Transformers/DeprecatedAttributeTest.php b/tests/feature/Transformers/DeprecatedAttributeTest.php index f8c43a2be..30dfc09fe 100644 --- a/tests/feature/Transformers/DeprecatedAttributeTest.php +++ b/tests/feature/Transformers/DeprecatedAttributeTest.php @@ -13,48 +13,48 @@ test('Scenario #1', expectMessageAndError( fn() => v::attribute('baz')->assert($object), - 'baz must be present', + '`.baz` must be present', 'The attribute() rule has been deprecated and will be removed in the next major version. Use propertyExists() instead.', )); test('Scenario #2', expectMessageAndError( fn() => v::not(v::attribute('foo'))->assert($object), - 'foo must not be present', + '`.foo` must not be present', 'The attribute() rule has been deprecated and will be removed in the next major version. Use propertyExists() instead.', )); test('Scenario #3', expectMessageAndError( fn() => v::attribute('foo', v::falseVal())->assert($object), - 'foo must evaluate to `false`', + '`.foo` must evaluate to `false`', 'The attribute() rule has been deprecated and will be removed in the next major version. Use property() instead.', )); test('Scenario #4', expectMessageAndError( fn() => v::not(v::attribute('foo', v::trueVal()))->assert($object), - 'foo must not evaluate to `true`', + '`.foo` must not evaluate to `true`', 'The attribute() rule has been deprecated and will be removed in the next major version. Use property() instead.', )); test('Scenario #5', expectMessageAndError( fn() => v::attribute('foo', v::falseVal(), true)->assert($object), - 'foo must evaluate to `false`', + '`.foo` must evaluate to `false`', 'The attribute() rule has been deprecated and will be removed in the next major version. Use property() instead.', )); test('Scenario #6', expectMessageAndError( fn() => v::not(v::attribute('foo', v::trueVal(), true))->assert($object), - 'foo must not evaluate to `true`', + '`.foo` must not evaluate to `true`', 'The attribute() rule has been deprecated and will be removed in the next major version. Use property() instead.', )); test('Scenario #7', expectMessageAndError( fn() => v::attribute('foo', v::falseVal(), false)->assert($object), - 'foo must evaluate to `false`', + '`.foo` must evaluate to `false`', 'The attribute() rule has been deprecated and will be removed in the next major version. Use propertyOptional() instead.', )); test('Scenario #8', expectMessageAndError( fn() => v::not(v::attribute('foo', v::trueVal(), false))->assert($object), - 'foo must not evaluate to `true`', + '`.foo` must not evaluate to `true`', 'The attribute() rule has been deprecated and will be removed in the next major version. Use propertyOptional() instead.', )); diff --git a/tests/feature/Transformers/DeprecatedKeyNestedTest.php b/tests/feature/Transformers/DeprecatedKeyNestedTest.php index 33264833d..e585b0e1a 100644 --- a/tests/feature/Transformers/DeprecatedKeyNestedTest.php +++ b/tests/feature/Transformers/DeprecatedKeyNestedTest.php @@ -15,24 +15,42 @@ test('Scenario #1', expectMessageAndError( fn() => v::keyNested('foo.bar.baz')->assert(['foo.bar.baz' => false]), - 'foo must be present', + '`.foo` must be present', + 'The keyNested() rule is deprecated and will be removed in the next major version. Use nested key() or property() instead.', +)); + +test('Scenario #A', expectMessageAndError( + fn() => v::keyNested('foo.bar.baz')->assert(['foo' => []]), + '`.foo.bar` must be present', + 'The keyNested() rule is deprecated and will be removed in the next major version. Use nested key() or property() instead.', +)); + +test('Scenario #B', expectMessageAndError( + fn() => v::keyNested('foo.bar.baz')->assert(['foo' => []]), + '`.foo.bar` must be present', + 'The keyNested() rule is deprecated and will be removed in the next major version. Use nested key() or property() instead.', +)); + +test('Scenario #C', expectMessageAndError( + fn() => v::keyNested('foo.bar.baz')->assert(['foo' => ['bar' => []]]), + '`.foo.bar.baz` must be present', 'The keyNested() rule is deprecated and will be removed in the next major version. Use nested key() or property() instead.', )); test('Scenario #2', expectMessageAndError( fn() => v::keyNested('foo.bar', v::negative())->assert($input), - 'bar must be a negative number', + '`.foo.bar` must be a negative number', 'The keyNested() rule is deprecated and will be removed in the next major version. Use nested key() or property() instead.', )); test('Scenario #3', expectMessageAndError( fn() => v::keyNested('foo.bar', v::stringType())->assert(new ArrayObject($input)), - 'bar must be a string', + '`.foo.bar` must be a string', 'The keyNested() rule is deprecated and will be removed in the next major version. Use nested key() or property() instead.', )); test('Scenario #4', expectMessageAndError( fn() => v::keyNested('foo.bar', v::floatType(), false)->assert($input), - 'bar must be float', + '`.foo.bar` must be float', 'The keyNested() rule is deprecated and will be removed in the next major version. Use nested key() or property() instead.', )); diff --git a/tests/feature/Transformers/DeprecatedKeyTest.php b/tests/feature/Transformers/DeprecatedKeyTest.php index c9ae85154..dc6665c3d 100644 --- a/tests/feature/Transformers/DeprecatedKeyTest.php +++ b/tests/feature/Transformers/DeprecatedKeyTest.php @@ -11,36 +11,36 @@ test('Scenario #1', expectMessageAndError( fn() => v::key('baz')->assert($array), - 'baz must be present', + '`.baz` must be present', 'Calling key() without a second parameter has been deprecated, and will be not be allowed in the next major version. Use keyExists() instead.', )); test('Scenario #2', expectMessageAndError( fn() => v::not(v::key('foo'))->assert($array), - 'foo must not be present', + '`.foo` must not be present', 'Calling key() without a second parameter has been deprecated, and will be not be allowed in the next major version. Use keyExists() instead.', )); test('Scenario #3', expectMessageAndError( fn() => v::key('foo', v::falseVal(), true)->assert($array), - 'foo must evaluate to `false`', + '`.foo` must evaluate to `false`', 'Calling key() with a third parameter has been deprecated, and will be not be allowed in the next major version. Use key() without the third parameter.', )); test('Scenario #4', expectMessageAndError( fn() => v::not(v::key('foo', v::trueVal(), true))->assert($array), - 'foo must not evaluate to `true`', + '`.foo` must not evaluate to `true`', 'Calling key() with a third parameter has been deprecated, and will be not be allowed in the next major version. Use key() without the third parameter.', )); test('Scenario #5', expectMessageAndError( fn() => v::key('foo', v::falseVal(), false)->assert($array), - 'foo must evaluate to `false`', + '`.foo` must evaluate to `false`', 'Calling key() with a third parameter has been deprecated, and will be not be allowed in the next major version. Use keyOptional() instead.', )); test('Scenario #6', expectMessageAndError( fn() => v::not(v::key('foo', v::trueVal(), false))->assert($array), - 'foo must not evaluate to `true`', + '`.foo` must not evaluate to `true`', 'Calling key() with a third parameter has been deprecated, and will be not be allowed in the next major version. Use keyOptional() instead.', )); diff --git a/tests/feature/Transformers/DeprecatedKeyValueTest.php b/tests/feature/Transformers/DeprecatedKeyValueTest.php index b0465f64b..5eba7307a 100644 --- a/tests/feature/Transformers/DeprecatedKeyValueTest.php +++ b/tests/feature/Transformers/DeprecatedKeyValueTest.php @@ -9,60 +9,60 @@ test('Scenario #1', expectMessageAndError( fn() => v::keyValue('foo', 'equals', 'bar')->assert(['bar' => 42]), - 'foo must be present', + '`.foo` must be present', 'The keyValue() rule has been deprecated and will be removed in the next major version. Use nested lazy() instead.', )); test('Scenario #2', expectMessageAndError( fn() => v::keyValue('foo', 'equals', 'bar')->assert(['foo' => 42]), - 'bar must be present', + '`.bar` must be present', 'The keyValue() rule has been deprecated and will be removed in the next major version. Use nested lazy() instead.', )); test('Scenario #3', expectMessageAndError( fn() => v::keyValue('foo', 'json', 'bar')->assert(['foo' => 42, 'bar' => 43]), - 'bar must be valid to validate foo', + '`.bar` must be valid to validate `.foo`', 'The keyValue() rule has been deprecated and will be removed in the next major version. Use nested lazy() instead.', )); test('Scenario #4', expectMessageAndError( fn() => v::keyValue('foo', 'equals', 'bar')->assert(['foo' => 1, 'bar' => 2]), - 'foo must be equal to 2', + '`.foo` must be equal to 2', 'The keyValue() rule has been deprecated and will be removed in the next major version. Use nested lazy() instead.', )); test('Scenario #5', expectMessageAndError( fn() => v::not(v::keyValue('foo', 'equals', 'bar'))->assert(['foo' => 1, 'bar' => 1]), - 'foo must not be equal to 1', + '`.foo` must not be equal to 1', 'The keyValue() rule has been deprecated and will be removed in the next major version. Use nested lazy() instead.', )); test('Scenario #6', expectMessageAndError( fn() => v::keyValue('foo', 'equals', 'bar')->assert(['bar' => 42]), - 'foo must be present', + '`.foo` must be present', 'The keyValue() rule has been deprecated and will be removed in the next major version. Use nested lazy() instead.', )); test('Scenario #7', expectMessageAndError( fn() => v::keyValue('foo', 'equals', 'bar')->assert(['foo' => 42]), - 'bar must be present', + '`.bar` must be present', 'The keyValue() rule has been deprecated and will be removed in the next major version. Use nested lazy() instead.', )); test('Scenario #8', expectMessageAndError( fn() => v::keyValue('foo', 'json', 'bar')->assert(['foo' => 42, 'bar' => 43]), - 'bar must be valid to validate foo', + '`.bar` must be valid to validate `.foo`', 'The keyValue() rule has been deprecated and will be removed in the next major version. Use nested lazy() instead.', )); test('Scenario #9', expectMessageAndError( fn() => v::keyValue('foo', 'equals', 'bar')->assert(['foo' => 1, 'bar' => 2]), - 'foo must be equal to 2', + '`.foo` must be equal to 2', 'The keyValue() rule has been deprecated and will be removed in the next major version. Use nested lazy() instead.', )); test('Scenario #10', expectMessageAndError( fn() => v::not(v::keyValue('foo', 'equals', 'bar'))->assert(['foo' => 1, 'bar' => 1]), - 'foo must not be equal to 1', + '`.foo` must not be equal to 1', 'The keyValue() rule has been deprecated and will be removed in the next major version. Use nested lazy() instead.', )); diff --git a/tests/feature/Transformers/PrefixTest.php b/tests/feature/Transformers/PrefixTest.php index 83689aaa8..9d2bded2a 100644 --- a/tests/feature/Transformers/PrefixTest.php +++ b/tests/feature/Transformers/PrefixTest.php @@ -11,9 +11,9 @@ test('Key', expectAll( fn() => v::keyEquals('foo', 12)->assert(['foo' => 10]), - 'foo must be equal to 12', - '- foo must be equal to 12', - ['foo' => 'foo must be equal to 12'], + '`.foo` must be equal to 12', + '- `.foo` must be equal to 12', + ['foo' => '`.foo` must be equal to 12'], )); test('Length', expectAll( @@ -53,9 +53,9 @@ test('Property', expectAll( fn() => v::propertyBetween('foo', 1, 3)->assert((object) ['foo' => 5]), - 'foo must be between 1 and 3', - '- foo must be between 1 and 3', - ['foo' => 'foo must be between 1 and 3'], + '`.foo` must be between 1 and 3', + '- `.foo` must be between 1 and 3', + ['foo' => '`.foo` must be between 1 and 3'], )); test('UndefOr', expectAll( diff --git a/tests/library/Builders/ResultBuilder.php b/tests/library/Builders/ResultBuilder.php index d37a9f0b3..21ab87075 100644 --- a/tests/library/Builders/ResultBuilder.php +++ b/tests/library/Builders/ResultBuilder.php @@ -35,8 +35,6 @@ final class ResultBuilder private ?Result $adjacent = null; - private bool $unchangeableId = false; - /** @var array */ private array $children = []; @@ -57,7 +55,7 @@ public function build(): Result $this->name, $this->id, $this->adjacent, - $this->unchangeableId, + null, ...$this->children ); }