diff --git a/composer.json b/composer.json index 336dbded8..2c726da6f 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "php": ">=8.5", "php-di/php-di": "^7.1", "psr/container": "^2.0", - "respect/stringifier": "^2.0.0", + "respect/string-formatter": "^1.0", + "respect/stringifier": "^3.0", "symfony/polyfill-mbstring": "^1.33" }, "require-dev": { @@ -46,6 +47,7 @@ "sokil/php-isocodes-db-only": "^4.0", "squizlabs/php_codesniffer": "^4.0", "symfony/console": "^7.4", + "symfony/translation": "^8.0", "symfony/var-exporter": "^6.4 || ^7.0" }, "suggest": { diff --git a/docs/messages/placeholder-pipes.md b/docs/messages/placeholder-pipes.md index aabbe62b5..960f19b90 100644 --- a/docs/messages/placeholder-pipes.md +++ b/docs/messages/placeholder-pipes.md @@ -62,25 +62,25 @@ v::templated( // → Le champ "adresse e-mail" est invalide ``` -### listOr +### list:or -The `listOr` modifier formats arrays as readable lists using "or": +The `list:or` modifier formats arrays as readable lists using "or": ```php v::templated( - 'Status must be {{haystack|listOr}}', + 'Status must be {{haystack|list:or}}', v::in(['active', 'pending', 'archived']), )->assert('deleted'); // → Status must be "active", "pending", or "archived" ``` -### listAnd +### list:and -The `listAnd` modifier formats arrays as readable lists using "and": +The `list:and` modifier formats arrays as readable lists using "and": ```php v::templated( - 'User must have {{roles|listAnd}} roles to perform this action', + 'User must have {{roles|list:and}} roles to perform this action', v::callback(fn(User $user) => $user->hasRoles(['admin', 'editor'])), ['roles' => ['admin', 'editor']], )->assert($user); diff --git a/library/ContainerRegistry.php b/library/ContainerRegistry.php index 6aa4704dd..40a57406b 100644 --- a/library/ContainerRegistry.php +++ b/library/ContainerRegistry.php @@ -12,27 +12,33 @@ use DI\Container; use Psr\Container\ContainerInterface; +use Respect\StringFormatter\BypassTranslator; +use Respect\StringFormatter\Modifier; +use Respect\StringFormatter\Modifiers\ListModifier; +use Respect\StringFormatter\Modifiers\QuoteModifier; +use Respect\StringFormatter\Modifiers\RawModifier; +use Respect\StringFormatter\Modifiers\StringifyModifier; +use Respect\StringFormatter\Modifiers\TransModifier; +use Respect\StringFormatter\PlaceholderFormatter; +use Respect\Stringifier\DumpStringifier; +use Respect\Stringifier\Handler; +use Respect\Stringifier\Handlers\CompositeHandler; +use Respect\Stringifier\HandlerStringifier; use Respect\Stringifier\Quoter; -use Respect\Stringifier\Quoters\StandardQuoter; +use Respect\Stringifier\Quoters\CodeQuoter; use Respect\Stringifier\Stringifier; use Respect\Validation\Message\Formatter\FirstResultStringFormatter; use Respect\Validation\Message\Formatter\NestedArrayFormatter; use Respect\Validation\Message\Formatter\NestedListStringFormatter; use Respect\Validation\Message\Formatter\TemplateResolver; use Respect\Validation\Message\InterpolationRenderer; -use Respect\Validation\Message\Modifier; -use Respect\Validation\Message\Modifier\ListAndModifier; -use Respect\Validation\Message\Modifier\ListOrModifier; -use Respect\Validation\Message\Modifier\QuoteModifier; -use Respect\Validation\Message\Modifier\RawModifier; -use Respect\Validation\Message\Modifier\StringifyModifier; -use Respect\Validation\Message\Modifier\TransModifier; +use Respect\Validation\Message\Parameters\NameHandler; +use Respect\Validation\Message\Parameters\PathHandler; +use Respect\Validation\Message\Parameters\ResultHandler; use Respect\Validation\Message\Renderer; -use Respect\Validation\Message\Translator; -use Respect\Validation\Message\Translator\DummyTranslator; -use Respect\Validation\Message\ValidationStringifier; use Respect\Validation\Transformers\Prefix; use Respect\Validation\Transformers\Transformer; +use Symfony\Contracts\Translation\TranslatorInterface; use function DI\autowire; use function DI\create; @@ -47,9 +53,7 @@ public static function createContainer(): Container return new Container([ Transformer::class => create(Prefix::class), TemplateResolver::class => create(TemplateResolver::class), - Quoter::class => create(StandardQuoter::class)->constructor(ValidationStringifier::MAXIMUM_LENGTH), - Stringifier::class => autowire(ValidationStringifier::class), - Translator::class => autowire(DummyTranslator::class), + TranslatorInterface::class => autowire(BypassTranslator::class), Renderer::class => autowire(InterpolationRenderer::class), ResultFilter::class => create(OnlyFailedChildrenResultFilter::class), 'respect.validation.formatter.message' => autowire(FirstResultStringFormatter::class), @@ -61,19 +65,33 @@ public static function createContainer(): Container $container->get(Transformer::class), $container->get('respect.validation.rule_factory.namespaces'), )), + Quoter::class => create(CodeQuoter::class)->constructor(120), + Handler::class => factory(static function (Container $container) { + $handler = CompositeHandler::create(); + $handler->prependHandler(new PathHandler($container->get(Quoter::class))); + $handler->prependHandler(new NameHandler()); + $handler->prependHandler(new ResultHandler($handler)); + + return $handler; + }), + PlaceholderFormatter::class => factory(static fn(Container $container) => new PlaceholderFormatter( + [], + $container->get(Modifier::class), + )), + Stringifier::class => factory(static fn(Container $container) => new HandlerStringifier( + $container->get(Handler::class), + new DumpStringifier(), + )), Modifier::class => factory(static fn(Container $container) => new TransModifier( - $container->get(Translator::class), - new ListOrModifier( - $container->get(Translator::class), - new ListAndModifier( - $container->get(Translator::class), - new QuoteModifier( - new RawModifier( - new StringifyModifier($container->get(Stringifier::class)), - ), + new ListModifier( + new QuoteModifier( + new RawModifier( + new StringifyModifier($container->get(Stringifier::class)), ), ), + $container->get(TranslatorInterface::class), ), + $container->get(TranslatorInterface::class), )), ValidatorBuilder::class => factory(static fn(Container $container) => new ValidatorBuilder( $container->get(ValidatorFactory::class), diff --git a/library/Exceptions/ValidationException.php b/library/Exceptions/ValidationException.php index fc6e8d1a8..ae9991254 100644 --- a/library/Exceptions/ValidationException.php +++ b/library/Exceptions/ValidationException.php @@ -18,6 +18,7 @@ namespace Respect\Validation\Exceptions; use InvalidArgumentException; +use Respect\Validation\ResultQuery; use function array_shift; use function in_array; @@ -25,15 +26,10 @@ final class ValidationException extends InvalidArgumentException implements Exception { - /** - * @param array $messages - * @param array $ignoredBacktracePaths - */ public function __construct( string $message, - private readonly string $fullMessage, - private readonly array $messages, - array $ignoredBacktracePaths = [], + private readonly ResultQuery $resultQuery, + string ...$ignoredBacktracePaths, ) { $this->overwriteFileAndLine($ignoredBacktracePaths); @@ -42,13 +38,13 @@ public function __construct( public function getFullMessage(): string { - return $this->fullMessage; + return $this->resultQuery->getFullMessage(); } /** @return array */ public function getMessages(): array { - return $this->messages; + return $this->resultQuery->getMessages(); } /** @param array $ignoredBacktracePaths */ diff --git a/library/Message/InterpolationRenderer.php b/library/Message/InterpolationRenderer.php index befd46d52..ef359716e 100644 --- a/library/Message/InterpolationRenderer.php +++ b/library/Message/InterpolationRenderer.php @@ -10,21 +10,16 @@ namespace Respect\Validation\Message; +use Respect\StringFormatter\PlaceholderFormatter; use Respect\Validation\Message\Formatter\TemplateResolver; -use Respect\Validation\Message\Placeholder\Subject; use Respect\Validation\Result; - -use function array_key_exists; -use function array_pad; -use function assert; -use function is_string; -use function preg_replace_callback; +use Symfony\Contracts\Translation\TranslatorInterface; final readonly class InterpolationRenderer implements Renderer { public function __construct( - private Translator $translator, - private Modifier $modifier, + private TranslatorInterface $translator, + private PlaceholderFormatter $formatter, private TemplateResolver $templateResolver, ) { } @@ -32,16 +27,15 @@ public function __construct( /** @param array $templates */ public function render(Result $result, array $templates): string { - $parameters = ['path' => $result->path, 'input' => $result->input, 'subject' => Subject::fromResult($result)]; + $parameters = ['path' => $result->path, 'input' => $result->input, 'subject' => $result]; $parameters += $result->parameters; $givenTemplate = $this->templateResolver->getGivenTemplate($result, $templates); $ruleTemplate = $this->templateResolver->getValidatorTemplate($result); - $rendered = (string) preg_replace_callback( - '/{{(\w+)(\|([^}]+))?}}/', - fn(array $matches) => $this->processPlaceholder($parameters, $matches), - $this->translator->translate($givenTemplate ?? $ruleTemplate), + $rendered = $this->formatter->formatUsing( + $this->translator->trans($givenTemplate ?? $ruleTemplate), + $parameters, ); if (!$result->hasCustomTemplate() && $givenTemplate === null && $result->adjacent !== null) { @@ -50,20 +44,4 @@ public function render(Result $result, array $templates): string return $rendered; } - - /** - * @param array $parameters - * @param array $matches - */ - private function processPlaceholder(array $parameters, array $matches): string - { - [$placeholder, $name, , $pipe] = array_pad($matches, 4, null); - assert(is_string($placeholder)); - assert(is_string($name)); - if (!array_key_exists($name, $parameters)) { - return $placeholder; - } - - return $this->modifier->modify($parameters[$name], $pipe); - } } diff --git a/library/Message/Modifier.php b/library/Message/Modifier.php deleted file mode 100644 index 4aa15ec2b..000000000 --- a/library/Message/Modifier.php +++ /dev/null @@ -1,16 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message; - -interface Modifier -{ - public function modify(mixed $value, string|null $pipe): string; -} diff --git a/library/Message/Modifier/ListAndModifier.php b/library/Message/Modifier/ListAndModifier.php deleted file mode 100644 index fb20cf1aa..000000000 --- a/library/Message/Modifier/ListAndModifier.php +++ /dev/null @@ -1,38 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use Respect\Validation\Message\Modifier; -use Respect\Validation\Message\Placeholder\Listed; -use Respect\Validation\Message\Translator; - -use function is_array; - -final readonly class ListAndModifier implements Modifier -{ - public function __construct( - private Translator $translator, - private Modifier $nextModifier, - ) { - } - - public function modify(mixed $value, string|null $pipe): string - { - if ($pipe !== 'listAnd' || !is_array($value)) { - return $this->nextModifier->modify($value, $pipe); - } - - return $this->nextModifier->modify( - new Listed($value, $this->translator->translate('and')), - null, - ); - } -} diff --git a/library/Message/Modifier/ListOrModifier.php b/library/Message/Modifier/ListOrModifier.php deleted file mode 100644 index 4d330d0c2..000000000 --- a/library/Message/Modifier/ListOrModifier.php +++ /dev/null @@ -1,38 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use Respect\Validation\Message\Modifier; -use Respect\Validation\Message\Placeholder\Listed; -use Respect\Validation\Message\Translator; - -use function is_array; - -final readonly class ListOrModifier implements Modifier -{ - public function __construct( - private Translator $translator, - private Modifier $nextModifier, - ) { - } - - public function modify(mixed $value, string|null $pipe): string - { - if ($pipe !== 'listOr' || !is_array($value)) { - return $this->nextModifier->modify($value, $pipe); - } - - return $this->nextModifier->modify( - new Listed($value, $this->translator->translate('or')), - null, - ); - } -} diff --git a/library/Message/Modifier/QuoteModifier.php b/library/Message/Modifier/QuoteModifier.php deleted file mode 100644 index 10bd29af0..000000000 --- a/library/Message/Modifier/QuoteModifier.php +++ /dev/null @@ -1,33 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use Respect\Validation\Message\Modifier; -use Respect\Validation\Message\Placeholder\Quoted; - -use function is_string; - -final readonly class QuoteModifier implements Modifier -{ - public function __construct( - private Modifier $nextModifier, - ) { - } - - public function modify(mixed $value, string|null $pipe): string - { - if ($pipe !== 'quote' || !is_string($value)) { - return $this->nextModifier->modify($value, $pipe); - } - - return $this->nextModifier->modify(new Quoted($value), null); - } -} diff --git a/library/Message/Modifier/RawModifier.php b/library/Message/Modifier/RawModifier.php deleted file mode 100644 index e5a1f2f76..000000000 --- a/library/Message/Modifier/RawModifier.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use Respect\Validation\Message\Modifier; - -use function is_bool; -use function is_scalar; - -final readonly class RawModifier implements Modifier -{ - public function __construct( - private Modifier $nextModifier, - ) { - } - - public function modify(mixed $value, string|null $pipe): string - { - if ($pipe !== 'raw') { - return $this->nextModifier->modify($value, $pipe); - } - - if (!is_scalar($value)) { - return $this->nextModifier->modify($value, null); - } - - return is_bool($value) ? (string) (int) $value : (string) $value; - } -} diff --git a/library/Message/Modifier/StringifyModifier.php b/library/Message/Modifier/StringifyModifier.php deleted file mode 100644 index ed813a76b..000000000 --- a/library/Message/Modifier/StringifyModifier.php +++ /dev/null @@ -1,38 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use Respect\Stringifier\Stringifier; -use Respect\Validation\Exceptions\ComponentException; -use Respect\Validation\Message\Modifier; - -use function print_r; -use function sprintf; - -final readonly class StringifyModifier implements Modifier -{ - public function __construct( - private Stringifier $stringifier, - ) { - } - - public function modify(mixed $value, string|null $pipe): string - { - if ($pipe !== null) { - throw new ComponentException(sprintf( - 'StringifyModifier only accepts null as pipe but "%s" was given.', - $pipe, - )); - } - - return $this->stringifier->stringify($value, 0) ?? print_r($value, true); - } -} diff --git a/library/Message/Modifier/TransModifier.php b/library/Message/Modifier/TransModifier.php deleted file mode 100644 index 488d61223..000000000 --- a/library/Message/Modifier/TransModifier.php +++ /dev/null @@ -1,34 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use Respect\Validation\Message\Modifier; -use Respect\Validation\Message\Translator; - -use function is_string; - -final readonly class TransModifier implements Modifier -{ - public function __construct( - private Translator $translator, - private Modifier $nextModifier, - ) { - } - - public function modify(mixed $value, string|null $pipe): string - { - if ($pipe !== 'trans' || !is_string($value)) { - return $this->nextModifier->modify($value, $pipe); - } - - return $this->translator->translate($value); - } -} diff --git a/library/Message/Stringifier/NameStringifier.php b/library/Message/Parameters/NameHandler.php similarity index 61% rename from library/Message/Stringifier/NameStringifier.php rename to library/Message/Parameters/NameHandler.php index 0fb4e670f..d6cc019a4 100644 --- a/library/Message/Stringifier/NameStringifier.php +++ b/library/Message/Parameters/NameHandler.php @@ -1,21 +1,21 @@ */ -namespace Respect\Validation\Message\Stringifier; +declare(strict_types=1); + +namespace Respect\Validation\Message\Parameters; -use Respect\Stringifier\Stringifier; +use Respect\Stringifier\Handler; use Respect\Validation\Name; -final readonly class NameStringifier implements Stringifier +final readonly class NameHandler implements Handler { - public function stringify(mixed $raw, int $depth): string|null + public function handle(mixed $raw, int $depth): string|null { if (!$raw instanceof Name) { return null; diff --git a/library/Message/Stringifier/PathStringifier.php b/library/Message/Parameters/PathHandler.php similarity index 81% rename from library/Message/Stringifier/PathStringifier.php rename to library/Message/Parameters/PathHandler.php index 36b6c7a0b..9affb1ee7 100644 --- a/library/Message/Stringifier/PathStringifier.php +++ b/library/Message/Parameters/PathHandler.php @@ -1,30 +1,30 @@ */ -namespace Respect\Validation\Message\Stringifier; +declare(strict_types=1); + +namespace Respect\Validation\Message\Parameters; +use Respect\Stringifier\Handler; use Respect\Stringifier\Quoter; -use Respect\Stringifier\Stringifier; use Respect\Validation\Path; use function array_reverse; use function implode; -final readonly class PathStringifier implements Stringifier +final readonly class PathHandler implements Handler { public function __construct( private Quoter $quoter, ) { } - public function stringify(mixed $raw, int $depth): string|null + public function handle(mixed $raw, int $depth): string|null { if (!$raw instanceof Path) { return null; diff --git a/library/Message/Parameters/ResultHandler.php b/library/Message/Parameters/ResultHandler.php new file mode 100644 index 000000000..43f075934 --- /dev/null +++ b/library/Message/Parameters/ResultHandler.php @@ -0,0 +1,49 @@ + + */ + +declare(strict_types=1); + +namespace Respect\Validation\Message\Parameters; + +use Respect\Stringifier\Handler; +use Respect\Validation\Result; + +use function sprintf; + +final readonly class ResultHandler implements Handler +{ + public function __construct( + private Handler $handler, + ) { + } + + public function handle(mixed $raw, int $depth): string|null + { + if (!$raw instanceof Result) { + return null; + } + + if ($raw->path === null && $raw->name === null) { + return $this->handler->handle($raw->input, $depth); + } + + if ($raw->name === null) { + return $this->handler->handle($raw->path, $depth); + } + + if ($raw->path === null || $raw->hasPrecedentName) { + return $this->handler->handle($raw->name, $depth); + } + + return sprintf( + '%s (<- %s)', + $this->handler->handle($raw->path, $depth), + $this->handler->handle($raw->name, $depth), + ); + } +} diff --git a/library/Message/Placeholder/Listed.php b/library/Message/Placeholder/Listed.php deleted file mode 100644 index 3ec8b746f..000000000 --- a/library/Message/Placeholder/Listed.php +++ /dev/null @@ -1,23 +0,0 @@ - - * SPDX-FileContributor: Danilo Correa - * SPDX-FileContributor: Henrique Moody - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Placeholder; - -final readonly class Listed -{ - /** @param array $values */ - public function __construct( - public array $values, - public string $lastGlue, - ) { - } -} diff --git a/library/Message/Placeholder/Quoted.php b/library/Message/Placeholder/Quoted.php deleted file mode 100644 index 6e5e35fae..000000000 --- a/library/Message/Placeholder/Quoted.php +++ /dev/null @@ -1,21 +0,0 @@ - - * SPDX-FileContributor: Danilo Correa - * SPDX-FileContributor: Henrique Moody - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Placeholder; - -final readonly class Quoted -{ - public function __construct( - public string $value, - ) { - } -} diff --git a/library/Message/Placeholder/Subject.php b/library/Message/Placeholder/Subject.php deleted file mode 100644 index 69aa089b3..000000000 --- a/library/Message/Placeholder/Subject.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Placeholder; - -use Respect\Validation\Name; -use Respect\Validation\Path; -use Respect\Validation\Result; - -final readonly class Subject -{ - public function __construct( - public mixed $input, - public Path|null $path = null, - public Name|null $name = null, - public bool $hasPrecedentName = true, - ) { - } - - public static function fromResult(Result $result): self - { - return new self($result->input, $result->path, $result->name, $result->hasPrecedentName); - } -} diff --git a/library/Message/Stringifier/ListedStringifier.php b/library/Message/Stringifier/ListedStringifier.php deleted file mode 100644 index 010e2aacb..000000000 --- a/library/Message/Stringifier/ListedStringifier.php +++ /dev/null @@ -1,48 +0,0 @@ - - */ - -namespace Respect\Validation\Message\Stringifier; - -use Respect\Stringifier\Stringifier; -use Respect\Validation\Message\Placeholder\Listed; - -use function array_map; -use function array_pop; -use function count; -use function implode; -use function sprintf; - -final readonly class ListedStringifier implements Stringifier -{ - public function __construct( - private Stringifier $stringifier, - ) { - } - - public function stringify(mixed $raw, int $depth): string|null - { - if (!$raw instanceof Listed) { - return null; - } - - if (count($raw->values) === 0) { - return null; - } - - $strings = array_map(fn($value) => $this->stringifier->stringify($value, $depth + 1), $raw->values); - if (count($strings) < 3) { - return implode(sprintf(' %s ', $raw->lastGlue), $strings); - } - - $lastString = array_pop($strings); - - return sprintf('%s, %s %s', implode(', ', $strings), $raw->lastGlue, $lastString); - } -} diff --git a/library/Message/Stringifier/QuotedStringifier.php b/library/Message/Stringifier/QuotedStringifier.php deleted file mode 100644 index 87aacbaed..000000000 --- a/library/Message/Stringifier/QuotedStringifier.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ - -namespace Respect\Validation\Message\Stringifier; - -use Respect\Stringifier\Quoter; -use Respect\Stringifier\Stringifier; -use Respect\Validation\Message\Placeholder\Quoted; - -final readonly class QuotedStringifier implements Stringifier -{ - public function __construct( - private Quoter $quoter, - ) { - } - - public function stringify(mixed $raw, int $depth): string|null - { - if (!$raw instanceof Quoted) { - return null; - } - - return $this->quoter->quote($raw->value, $depth); - } -} diff --git a/library/Message/Stringifier/SubjectStringifier.php b/library/Message/Stringifier/SubjectStringifier.php deleted file mode 100644 index 8938a9a6c..000000000 --- a/library/Message/Stringifier/SubjectStringifier.php +++ /dev/null @@ -1,49 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Stringifier; - -use Respect\Stringifier\Stringifier; -use Respect\Validation\Message\Placeholder\Subject; - -use function sprintf; - -final readonly class SubjectStringifier implements Stringifier -{ - public function __construct( - private Stringifier $stringifier, - ) { - } - - public function stringify(mixed $raw, int $depth): string|null - { - if (!$raw instanceof Subject) { - return null; - } - - if ($raw->path === null && $raw->name === null) { - return $this->stringifier->stringify($raw->input, $depth); - } - - if ($raw->name === null) { - return $this->stringifier->stringify($raw->path, $depth); - } - - if ($raw->path === null || $raw->hasPrecedentName) { - return $this->stringifier->stringify($raw->name, $depth); - } - - return sprintf( - '%s (<- %s)', - $this->stringifier->stringify($raw->path, $depth), - $this->stringifier->stringify($raw->name, $depth), - ); - } -} diff --git a/library/Message/Translator.php b/library/Message/Translator.php deleted file mode 100644 index 8dfe4be19..000000000 --- a/library/Message/Translator.php +++ /dev/null @@ -1,16 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message; - -interface Translator -{ - public function translate(string $message): string; -} diff --git a/library/Message/Translator/ArrayTranslator.php b/library/Message/Translator/ArrayTranslator.php deleted file mode 100644 index 9a0cecbb1..000000000 --- a/library/Message/Translator/ArrayTranslator.php +++ /dev/null @@ -1,33 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Translator; - -use Respect\Validation\Message\Translator; - -use function is_string; - -final readonly class ArrayTranslator implements Translator -{ - /** @param array $messages */ - public function __construct( - private array $messages, - ) { - } - - public function translate(string $message): string - { - if (isset($this->messages[$message]) && is_string($this->messages[$message])) { - return $this->messages[$message]; - } - - return $message; - } -} diff --git a/library/Message/Translator/DummyTranslator.php b/library/Message/Translator/DummyTranslator.php deleted file mode 100644 index 7f7a26438..000000000 --- a/library/Message/Translator/DummyTranslator.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ - -namespace Respect\Validation\Message\Translator; - -use Respect\Validation\Message\Translator; - -final class DummyTranslator implements Translator -{ - public function translate(string $message): string - { - return $message; - } -} diff --git a/library/Message/Translator/GettextTranslator.php b/library/Message/Translator/GettextTranslator.php deleted file mode 100644 index 619a124c6..000000000 --- a/library/Message/Translator/GettextTranslator.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Translator; - -use Respect\Validation\Exceptions\ComponentException; -use Respect\Validation\Message\Translator; - -use function function_exists; -use function gettext; - -final class GettextTranslator implements Translator -{ - public function __construct() - { - if (!function_exists('gettext')) { - throw new ComponentException('This translator requires the gettext extension'); - } - } - - public function translate(string $message): string - { - return gettext($message); - } -} diff --git a/library/Message/ValidationStringifier.php b/library/Message/ValidationStringifier.php deleted file mode 100644 index 5cdbbf589..000000000 --- a/library/Message/ValidationStringifier.php +++ /dev/null @@ -1,104 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message; - -use DateTimeInterface; -use Respect\Stringifier\Quoter; -use Respect\Stringifier\Stringifier; -use Respect\Stringifier\Stringifiers\ArrayObjectStringifier; -use Respect\Stringifier\Stringifiers\ArrayStringifier; -use Respect\Stringifier\Stringifiers\BoolStringifier; -use Respect\Stringifier\Stringifiers\CompositeStringifier; -use Respect\Stringifier\Stringifiers\DateTimeStringifier; -use Respect\Stringifier\Stringifiers\DeclaredStringifier; -use Respect\Stringifier\Stringifiers\EnumerationStringifier; -use Respect\Stringifier\Stringifiers\InfiniteNumberStringifier; -use Respect\Stringifier\Stringifiers\IteratorObjectStringifier; -use Respect\Stringifier\Stringifiers\JsonEncodableStringifier; -use Respect\Stringifier\Stringifiers\JsonSerializableObjectStringifier; -use Respect\Stringifier\Stringifiers\NotANumberStringifier; -use Respect\Stringifier\Stringifiers\NullStringifier; -use Respect\Stringifier\Stringifiers\ObjectStringifier; -use Respect\Stringifier\Stringifiers\ObjectWithDebugInfoStringifier; -use Respect\Stringifier\Stringifiers\ResourceStringifier; -use Respect\Stringifier\Stringifiers\StringableObjectStringifier; -use Respect\Stringifier\Stringifiers\ThrowableObjectStringifier; -use Respect\Validation\Message\Stringifier\ListedStringifier; -use Respect\Validation\Message\Stringifier\NameStringifier; -use Respect\Validation\Message\Stringifier\PathStringifier; -use Respect\Validation\Message\Stringifier\QuotedStringifier; -use Respect\Validation\Message\Stringifier\SubjectStringifier; - -final readonly class ValidationStringifier implements Stringifier -{ - public const int MAXIMUM_LENGTH = 120; - private const int MAXIMUM_DEPTH = 3; - private const int MAXIMUM_NUMBER_OF_ITEMS = 5; - private const int MAXIMUM_NUMBER_OF_PROPERTIES = self::MAXIMUM_NUMBER_OF_ITEMS; - - private Stringifier $stringifier; - - public function __construct( - private Quoter $quoter, - ) { - $this->stringifier = $this->createStringifier($quoter); - } - - public function stringify(mixed $raw, int $depth): string - { - return $this->stringifier->stringify($raw, $depth) ?? $this->quoter->quote('unknown', $depth); - } - - private function createStringifier(Quoter $quoter): Stringifier - { - $jsonEncodableStringifier = new JsonEncodableStringifier(); - - $stringifier = new CompositeStringifier( - new InfiniteNumberStringifier($quoter), - new NotANumberStringifier($quoter), - new ResourceStringifier($quoter), - new BoolStringifier($quoter), - new NullStringifier($quoter), - new DeclaredStringifier($quoter), - $jsonEncodableStringifier, - ); - $arrayStringifier = new ArrayStringifier( - $stringifier, - $quoter, - self::MAXIMUM_DEPTH, - self::MAXIMUM_NUMBER_OF_ITEMS, - ); - $stringifier->prependStringifier($arrayStringifier); - $stringifier->prependStringifier(new ObjectStringifier( - $stringifier, - $quoter, - self::MAXIMUM_DEPTH, - self::MAXIMUM_NUMBER_OF_PROPERTIES, - )); - $stringifier->prependStringifier(new EnumerationStringifier($quoter)); - $stringifier->prependStringifier(new ObjectWithDebugInfoStringifier($arrayStringifier, $quoter)); - $stringifier->prependStringifier(new ArrayObjectStringifier($arrayStringifier, $quoter)); - $stringifier->prependStringifier(new JsonSerializableObjectStringifier($jsonEncodableStringifier, $quoter)); - $stringifier->prependStringifier(new StringableObjectStringifier($jsonEncodableStringifier, $quoter)); - $stringifier->prependStringifier(new ThrowableObjectStringifier($jsonEncodableStringifier, $quoter)); - $stringifier->prependStringifier(new DateTimeStringifier($quoter, DateTimeInterface::ATOM)); - $stringifier->prependStringifier(new IteratorObjectStringifier($stringifier, $quoter)); - - // Stringifiers from this project only - $stringifier->prependStringifier(new PathStringifier($quoter)); - $stringifier->prependStringifier(new QuotedStringifier($quoter)); - $stringifier->prependStringifier(new ListedStringifier($stringifier)); - $stringifier->prependStringifier(new NameStringifier()); - $stringifier->prependStringifier(new SubjectStringifier($stringifier)); - - return $stringifier; - } -} diff --git a/library/ResultQuery.php b/library/ResultQuery.php index 0143eeb78..0fb013f56 100644 --- a/library/ResultQuery.php +++ b/library/ResultQuery.php @@ -99,7 +99,7 @@ public function isValid(): bool return $this->result->hasPassed; } - public function toMessage(): string + public function getMessage(): string { if ($this->result->hasPassed) { return ''; @@ -108,7 +108,7 @@ public function toMessage(): string return $this->messageFormatter->format($this->result, $this->renderer, $this->templates); } - public function toFullMessage(): string + public function getFullMessage(): string { if ($this->result->hasPassed) { return ''; @@ -118,7 +118,7 @@ public function toFullMessage(): string } /** @return array */ - public function toArrayMessages(): array + public function getMessages(): array { if ($this->result->hasPassed) { return []; @@ -129,6 +129,6 @@ public function toArrayMessages(): array public function __toString(): string { - return $this->toMessage(); + return $this->getMessage(); } } diff --git a/library/ValidatorBuilder.php b/library/ValidatorBuilder.php index 16bbc0e27..03eca0e5b 100644 --- a/library/ValidatorBuilder.php +++ b/library/ValidatorBuilder.php @@ -90,13 +90,7 @@ public function assert(mixed $input, array|string|Throwable|callable|null $templ $resultQuery = $this->toResultQuery($result, is_callable($template) ? null : $template); - $exception = new ValidationException( - $resultQuery->toMessage(), - $resultQuery->toFullMessage(), - $resultQuery->toArrayMessages(), - $this->ignoredBacktracePaths, - ); - + $exception = new ValidationException($resultQuery->getMessage(), $resultQuery, ...$this->ignoredBacktracePaths); if (is_callable($template)) { throw $template($exception); } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index ecf879cdc..08ea7ff65 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -13,10 +13,8 @@ parameters: path: tests/feature/Validators/SizeTest.php - message: '/Undefined variable: \$this/' path: tests/Pest.php - - message: '/Method .+\\TestingStringifier::stringify\(\) never returns null so it can be removed from the return type./' - path: tests/library/Message/TestingStringifier.php - - message: '/Parameter #1 \$messages of class .+\\ArrayTranslator constructor expects array, array given./' - path: tests/unit/Message/Translator/ArrayTranslatorTest.php + - message: '/Method .+\\TestingHandler::handle\(\) never returns null so it can be removed from the return type./' + path: tests/library/Message/TestingHandler.php - message: '/Access to an undefined property PHPUnit\\Framework\\TestCase/' path: tests/feature/Validators/SizeTest.php - message: '/Property Respect\\Validation\\Test\\Stubs\\.+::\$[a-zA-Z]+ is never read, only written./' diff --git a/tests/feature/Message/ValidationStringifierTest.php b/tests/feature/Message/ValidationStringifierTest.php deleted file mode 100644 index 276db9a85..000000000 --- a/tests/feature/Message/ValidationStringifierTest.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ - -declare(strict_types=1); - -use Respect\Stringifier\Quoters\StandardQuoter; -use Respect\Validation\Message\ValidationStringifier; - -test('Should return `unknown` when cannot stringify value', function (): void { - $resource = tmpfile(); - fclose($resource); - - $stringifier = new ValidationStringifier(new StandardQuoter(ValidationStringifier::MAXIMUM_LENGTH)); - - expect($stringifier->stringify($resource, 0))->toBe('`unknown`'); -}); diff --git a/tests/feature/TranslatorTest.php b/tests/feature/TranslatorTest.php index 2011b1f43..e46395749 100644 --- a/tests/feature/TranslatorTest.php +++ b/tests/feature/TranslatorTest.php @@ -9,25 +9,33 @@ declare(strict_types=1); use Respect\Validation\ContainerRegistry; -use Respect\Validation\Message\Translator; -use Respect\Validation\Message\Translator\ArrayTranslator; +use Symfony\Component\Translation\Loader\ArrayLoader; +use Symfony\Component\Translation\Translator; +use Symfony\Contracts\Translation\TranslatorInterface; +$translator = new Translator('en'); +$translator->addLoader('array', new ArrayLoader()); +$translator->addResource( + 'array', + [ + '{{subject}} must pass all the rules' => 'Todas as regras requeridas devem passar para {{subject}}', + 'The length of' => 'O comprimento de', + '{{subject}} must be a string' => '{{subject}} deve ser uma string', + '{{subject}} must be between {{minValue}} and {{maxValue}}' => '{{subject}} deve possuir de {{minValue}} a {{maxValue}} caracteres', + '{{subject}} must be a valid telephone number for country {{countryName|trans}}' => '{{subject}} deve ser um número de telefone válido para o país {{countryName|trans}}', + 'United States' => 'Estados Unidos', + 'years' => 'anos', + 'The number of {{type|trans}} between now and' => 'O número de {{type|trans}} entre agora e', + '{{subject}} must be equal to {{compareTo}}' => '{{subject}} deve ser igual a {{compareTo}}', + 'Your name must be {{haystack|list:or}}' => 'Seu nome deve ser {{haystack|list:or}}', + 'or' => 'ou', + '{{haystack|list:and}} are the only possible names' => '{{haystack|list:and}} são os únicos nomes possíveis', + 'and' => 'e', + ], + 'en', +); $container = ContainerRegistry::createContainer(); -$container->set(Translator::class, new ArrayTranslator([ - '{{subject}} must pass all the rules' => 'Todas as regras requeridas devem passar para {{subject}}', - 'The length of' => 'O comprimento de', - '{{subject}} must be a string' => '{{subject}} deve ser uma string', - '{{subject}} must be between {{minValue}} and {{maxValue}}' => '{{subject}} deve possuir de {{minValue}} a {{maxValue}} caracteres', - '{{subject}} must be a valid telephone number for country {{countryName|trans}}' => '{{subject}} deve ser um número de telefone válido para o país {{countryName|trans}}', - 'United States' => 'Estados Unidos', - 'years' => 'anos', - 'The number of {{type|trans}} between now and' => 'O número de {{type|trans}} entre agora e', - '{{subject}} must be equal to {{compareTo}}' => '{{subject}} deve ser igual a {{compareTo}}', - 'Your name must be {{haystack|listOr}}' => 'Seu nome deve ser {{haystack|listOr}}', - 'or' => 'ou', - '{{haystack|listAnd}} are the only possible names' => '{{haystack|listAnd}} são os únicos nomes possíveis', - 'and' => 'e', -])); +$container->set(TranslatorInterface::class, $translator); beforeAll(fn() => ContainerRegistry::setContainer($container)); @@ -48,12 +56,12 @@ fn(string $message) => expect($message)->toBe('O número de anos entre agora e "1972-02-09" deve ser igual a 2'), )); -test('Using "listOr"', catchMessage( - fn() => v::templated('Your name must be {{haystack|listOr}}', v::in(['Respect', 'Validation']))->assert(''), +test('Using "list:or"', catchMessage( + fn() => v::templated('Your name must be {{haystack|list:or}}', v::in(['Respect', 'Validation']))->assert(''), fn(string $message) => expect($message)->toBe('Seu nome deve ser "Respect" ou "Validation"'), )); -test('Using "listAnd"', catchMessage( - fn() => v::templated('{{haystack|listAnd}} are the only possible names', v::in(['Respect', 'Validation']))->assert(''), +test('Using "list:and"', catchMessage( + fn() => v::templated('{{haystack|list:and}} are the only possible names', v::in(['Respect', 'Validation']))->assert(''), fn(string $message) => expect($message)->toBe('"Respect" e "Validation" são os únicos nomes possíveis'), )); diff --git a/tests/feature/ValidationExceptionStackTraceTest.php b/tests/feature/ValidationExceptionStackTraceTest.php index 3a8b10eee..5408ef6e2 100644 --- a/tests/feature/ValidationExceptionStackTraceTest.php +++ b/tests/feature/ValidationExceptionStackTraceTest.php @@ -9,8 +9,22 @@ declare(strict_types=1); use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\ResultQuery; +use Respect\Validation\Test\Builders\ResultBuilder; +use Respect\Validation\Test\Message\TestingArrayFormatter; +use Respect\Validation\Test\Message\TestingMessageRenderer; +use Respect\Validation\Test\Message\TestingStringFormatter; use Respect\Validation\Test\Stubs\MyValidator; +$resultQuery = new ResultQuery( + (new ResultBuilder())->build(), + new TestingMessageRenderer(), + new TestingStringFormatter(), + new TestingStringFormatter(), + new TestingArrayFormatter(), + [], +); + test('Should overwrite file and line in the Validator class', function (): void { try { v::intType()->assert('string'); @@ -29,9 +43,9 @@ } }); -test('Should not overwrite file and line when created manually', function (): void { +test('Should not overwrite file and line when created manually', function () use ($resultQuery): void { try { - throw new ValidationException('message', 'fullMessage', ['id' => 'message']); + throw new ValidationException('message', $resultQuery); } catch (ValidationException $e) { expect($e->getFile())->toBe(__FILE__); expect($e->getLine())->toBe(__LINE__ - 3); @@ -39,9 +53,9 @@ }); -test('Should not overwrite file and line when file cannot be ever traced', function (): void { +test('Should not overwrite file and line when file cannot be ever traced', function () use ($resultQuery): void { try { - throw new ValidationException('message', 'fullMessage', ['id' => 'message'], ['/tmp/unknown']); + throw new ValidationException('message', $resultQuery, '/tmp/unknown'); } catch (ValidationException $e) { expect($e->getFile())->toBe(__FILE__); expect($e->getLine())->toBe(__LINE__ - 3); @@ -49,7 +63,7 @@ }); -test('Should go not overwrite file and line when it runs out of choices', function (): void { +test('Should go not overwrite file and line when it runs out of choices', function () use ($resultQuery): void { try { $trace = array_unique( array_filter( @@ -61,7 +75,7 @@ ); $trace[] = __FILE__; - throw new ValidationException('message', 'fullMessage', ['id' => 'message'], $trace); + throw new ValidationException('message', $resultQuery, ...$trace); } catch (ValidationException $e) { expect($e->getFile())->toBe(__FILE__); expect($e->getLine())->toBe(__LINE__ - 3); diff --git a/tests/feature/Validators/CallableTypeTest.php b/tests/feature/Validators/CallableTypeTest.php index cbdf0740e..a0873d964 100644 --- a/tests/feature/Validators/CallableTypeTest.php +++ b/tests/feature/Validators/CallableTypeTest.php @@ -27,5 +27,5 @@ fn() => v::not(v::callableType())->assert(function (): void { // Do nothing }), - fn(string $fullMessage) => expect($fullMessage)->toBe('- `Closure {}` must not be a callable'), + fn(string $fullMessage) => expect($fullMessage)->toBe('- `Closure { fn(): void }` must not be a callable'), )); diff --git a/tests/library/Builders/ResultBuilder.php b/tests/library/Builders/ResultBuilder.php index 350c745be..adc1ec581 100644 --- a/tests/library/Builders/ResultBuilder.php +++ b/tests/library/Builders/ResultBuilder.php @@ -70,7 +70,7 @@ public function build(): Result ); } - public function withPath(Path $path): self + public function path(Path $path): self { $this->path = $path; @@ -84,6 +84,13 @@ public function hasPassed(bool $hasPassed): self return $this; } + public function hasPrecedentName(bool $hasPrecedentName): self + { + $this->hasPrecedentName = $hasPrecedentName; + + return $this; + } + public function template(string $template): self { $this->template = $template; @@ -106,9 +113,9 @@ public function parameters(array $parameters): self return $this; } - public function name(string $name): self + public function name(string|Name $name): self { - $this->name = new Name($name); + $this->name = $name instanceof Name ? $name : new Name($name); return $this; } diff --git a/tests/library/Message/NullStringifier.php b/tests/library/Message/NullStringifier.php deleted file mode 100644 index e68db985f..000000000 --- a/tests/library/Message/NullStringifier.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Test\Message; - -use Respect\Stringifier\Stringifier; - -final class NullStringifier implements Stringifier -{ - public function stringify(mixed $raw, int $depth): string|null - { - return null; - } -} diff --git a/tests/library/Message/TestingStringifier.php b/tests/library/Message/TestingHandler.php similarity index 70% rename from tests/library/Message/TestingStringifier.php rename to tests/library/Message/TestingHandler.php index 3ea5b9ea6..0188c83d7 100644 --- a/tests/library/Message/TestingStringifier.php +++ b/tests/library/Message/TestingHandler.php @@ -10,14 +10,14 @@ namespace Respect\Validation\Test\Message; -use Respect\Stringifier\Stringifier; +use Respect\Stringifier\Handler; use function print_r; use function sprintf; -final class TestingStringifier implements Stringifier +final class TestingHandler implements Handler { - public function stringify(mixed $raw, int $depth): string|null + public function handle(mixed $raw, int $depth): string|null { return sprintf('<%s:%d>', print_r($raw, true), $depth); } diff --git a/tests/library/Message/TestingModifier.php b/tests/library/Message/TestingModifier.php index 6c357cc74..0e783eee7 100644 --- a/tests/library/Message/TestingModifier.php +++ b/tests/library/Message/TestingModifier.php @@ -10,7 +10,7 @@ namespace Respect\Validation\Test\Message; -use Respect\Validation\Message\Modifier; +use Respect\StringFormatter\Modifier; use function print_r; use function sprintf; diff --git a/tests/library/Message/TestingTranslator.php b/tests/library/Message/TestingTranslator.php new file mode 100644 index 000000000..8fd781772 --- /dev/null +++ b/tests/library/Message/TestingTranslator.php @@ -0,0 +1,37 @@ + + */ + +declare(strict_types=1); + +namespace Respect\Validation\Test\Message; + +use Symfony\Contracts\Translation\TranslatorInterface; + +final readonly class TestingTranslator implements TranslatorInterface +{ + /** @param array $translations */ + public function __construct( + private array $translations = [], + ) { + } + + /** @param array $parameters */ + public function trans( + string $id, + array $parameters = [], + string|null $domain = null, + string|null $locale = null, + ): string { + return $this->translations[$id] ?? $id; + } + + public function getLocale(): string + { + return 'en'; + } +} diff --git a/tests/unit/Message/Formatter/NestedArrayFormatterTest.php b/tests/unit/Message/Formatter/NestedArrayFormatterTest.php index 9a1ed911e..a96393cbb 100644 --- a/tests/unit/Message/Formatter/NestedArrayFormatterTest.php +++ b/tests/unit/Message/Formatter/NestedArrayFormatterTest.php @@ -82,8 +82,8 @@ public static function provideForArray(): array 'with string key collision' => [ (new ResultBuilder())->id('root')->template('root_msg') ->children( - (new ResultBuilder())->id('c1')->template('msg1')->withPath(new Path('foo'))->build(), - (new ResultBuilder())->id('c2')->template('msg2')->withPath(new Path('foo'))->build(), + (new ResultBuilder())->id('c1')->template('msg1')->path(new Path('foo'))->build(), + (new ResultBuilder())->id('c2')->template('msg2')->path(new Path('foo'))->build(), )->build(), [ 'foo' => ['msg1', 'msg2'], @@ -92,8 +92,8 @@ public static function provideForArray(): array 'with numeric key collision (list)' => [ (new ResultBuilder())->id('root')->template('root_msg') ->children( - (new ResultBuilder())->id('c1')->template('msg1')->withPath(new Path(0))->build(), - (new ResultBuilder())->id('c2')->template('msg2')->withPath(new Path(0))->build(), + (new ResultBuilder())->id('c1')->template('msg1')->path(new Path(0))->build(), + (new ResultBuilder())->id('c2')->template('msg2')->path(new Path(0))->build(), )->build(), [ '__root__' => 'root_msg', @@ -104,8 +104,8 @@ public static function provideForArray(): array 'with mixed keys replacement' => [ (new ResultBuilder())->id('root')->template('root_msg') ->children( - (new ResultBuilder())->id('c1')->template('msg1')->withPath(new Path('foo'))->build(), - (new ResultBuilder())->id('c2')->template('msg2')->withPath(new Path(0))->build(), + (new ResultBuilder())->id('c1')->template('msg1')->path(new Path('foo'))->build(), + (new ResultBuilder())->id('c2')->template('msg2')->path(new Path(0))->build(), )->build(), [ '__root__' => 'root_msg', @@ -116,9 +116,9 @@ public static function provideForArray(): array 'with mixed keys and ID collision' => [ (new ResultBuilder())->id('root')->template('root_msg') ->children( - (new ResultBuilder())->id('c1')->template('msg1')->withPath(new Path('foo'))->build(), - (new ResultBuilder())->id('sameId')->template('msg2')->withPath(new Path(0))->build(), - (new ResultBuilder())->id('sameId')->template('msg3')->withPath(new Path(1))->build(), + (new ResultBuilder())->id('c1')->template('msg1')->path(new Path('foo'))->build(), + (new ResultBuilder())->id('sameId')->template('msg2')->path(new Path(0))->build(), + (new ResultBuilder())->id('sameId')->template('msg3')->path(new Path(1))->build(), )->build(), [ '__root__' => 'root_msg', @@ -129,8 +129,8 @@ public static function provideForArray(): array 'with pure numeric keys' => [ (new ResultBuilder())->id('root')->template('root_msg') ->children( - (new ResultBuilder())->id('c1')->template('msg1')->withPath(new Path(10))->build(), - (new ResultBuilder())->id('c2')->template('msg2')->withPath(new Path(20))->build(), + (new ResultBuilder())->id('c1')->template('msg1')->path(new Path(10))->build(), + (new ResultBuilder())->id('c2')->template('msg2')->path(new Path(20))->build(), )->build(), [ '__root__' => 'root_msg', diff --git a/tests/unit/Message/Formatter/OnlyFailedChildrenResultFilterTest.php b/tests/unit/Message/Formatter/OnlyFailedChildrenResultFilterTest.php index b26d2b1b7..7cba752b5 100644 --- a/tests/unit/Message/Formatter/OnlyFailedChildrenResultFilterTest.php +++ b/tests/unit/Message/Formatter/OnlyFailedChildrenResultFilterTest.php @@ -55,7 +55,7 @@ public function itReturnsSameWhenAllChildrenPassed(): void public function itKeepsChildrenWithNonNullPathUnchanged(): void { $child1 = (new ResultBuilder())->id('a')->hasPassed(false)->build(); // path null - $child2 = (new ResultBuilder())->id('b')->hasPassed(false)->withPath(new Path(0))->build(); // non‑null path + $child2 = (new ResultBuilder())->id('b')->hasPassed(false)->path(new Path(0))->build(); // non‑null path $parent = (new ResultBuilder()) ->hasPassed(false) diff --git a/tests/unit/Message/Formatter/TemplateResolverTest.php b/tests/unit/Message/Formatter/TemplateResolverTest.php index a1bd558db..45ccba5f2 100644 --- a/tests/unit/Message/Formatter/TemplateResolverTest.php +++ b/tests/unit/Message/Formatter/TemplateResolverTest.php @@ -22,7 +22,7 @@ final class TemplateResolverTest extends TestCase #[Test] public function itShouldReturnResultWithTemplateWhenKeyExists(): void { - $result = (new ResultBuilder())->withPath(new Path('foo-path'))->build(); + $result = (new ResultBuilder())->path(new Path('foo-path'))->build(); $templates = ['foo-path' => 'My custom template']; $sut = new TemplateResolver(); $template = $sut->getGivenTemplate($result, $templates); diff --git a/tests/unit/Message/InterpolationRendererTest.php b/tests/unit/Message/InterpolationRendererTest.php index 3bf58c68c..d5f6444e0 100644 --- a/tests/unit/Message/InterpolationRendererTest.php +++ b/tests/unit/Message/InterpolationRendererTest.php @@ -12,23 +12,23 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; +use Respect\StringFormatter\PlaceholderFormatter; use Respect\Validation\Message\Formatter\TemplateResolver; -use Respect\Validation\Message\Placeholder\Subject; -use Respect\Validation\Message\Translator\ArrayTranslator; -use Respect\Validation\Message\Translator\DummyTranslator; use Respect\Validation\Test\Builders\ResultBuilder; -use Respect\Validation\Test\Message\TestingModifier; +use Respect\Validation\Test\Message\TestingTranslator; use Respect\Validation\Test\TestCase; -use function sprintf; - #[CoversClass(InterpolationRenderer::class)] final class InterpolationRendererTest extends TestCase { #[Test] public function itShouldRenderResultWithCustomTemplate(): void { - $renderer = new InterpolationRenderer(new DummyTranslator(), new TestingModifier(), new TemplateResolver()); + $renderer = new InterpolationRenderer( + new TestingTranslator(), + new PlaceholderFormatter([]), + new TemplateResolver(), + ); $result = (new ResultBuilder())->template('This is my template')->build(); @@ -36,242 +36,153 @@ public function itShouldRenderResultWithCustomTemplate(): void } #[Test] - public function itShouldRenderResultProcessingParametersInTheTemplate(): void - { - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer(new DummyTranslator(), $modifier, new TemplateResolver()); - - $key = 'foo'; - $value = true; - - $result = (new ResultBuilder()) - ->template(sprintf('Will replace {{%s}}', $key)) - ->parameters([$key => $value]) - ->build(); - - self::assertSame( - 'Will replace ' . $modifier->modify($value, null), - $renderer->render($result, []), - ); - } - - #[Test] - public function itShouldRenderResultProcessingModifierParametersInTheTemplate(): void - { - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer(new DummyTranslator(), $modifier, new TemplateResolver()); - - $key = 'foo'; - $value = 0.1; - - $result = (new ResultBuilder()) - ->template(sprintf('Will replace {{%1$s}} and {{%1$s|modifier}}', $key)) - ->parameters([$key => $value]) - ->build(); - - self::assertSame( - sprintf('Will replace %s and %s', $modifier->modify($value, null), $modifier->modify($value, 'modifier')), - $renderer->render($result, []), - ); - } - - #[Test] - public function itShouldRenderResultProcessingNameAsSomeParameterInTheTemplate(): void + public function itShouldRenderResultProcessingParametersFromTheResult(): void { - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer(new DummyTranslator(), $modifier, new TemplateResolver()); - - $name = 'my name'; - + $parameters = ['foo' => 42]; + $template = 'Value: {{foo}}'; + $formatter = new PlaceholderFormatter([]); + $expected = $formatter->formatUsing($template, $parameters); $result = (new ResultBuilder()) - ->template('Will replace {{subject}}') - ->name($name) + ->template($template) + ->parameters($parameters) ->build(); + $renderer = new InterpolationRenderer(new TestingTranslator(), $formatter, new TemplateResolver()); - $subject = Subject::fromResult($result); + $actual = $renderer->render($result, []); - self::assertSame( - 'Will replace ' . $modifier->modify($subject, null), - $renderer->render($result, []), - ); + self::assertSame($expected, $actual); } #[Test] - public function itShouldRenderResultProcessingInputAsNameWhenResultHasNoName(): void + public function itShouldRenderResultWithInputParameter(): void { - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer(new DummyTranslator(), $modifier, new TemplateResolver()); - - $input = 42; - + $template = 'Input: {{input}}'; + $input = 'test input'; + $formatter = new PlaceholderFormatter([]); + $expected = $formatter->formatUsing($template, ['input' => $input]); $result = (new ResultBuilder()) - ->template('Will replace {{subject}}') + ->template($template) ->input($input) ->build(); + $renderer = new InterpolationRenderer(new TestingTranslator(), $formatter, new TemplateResolver()); - $subject = Subject::fromResult($result); + $actual = $renderer->render($result, []); - self::assertSame( - sprintf( - 'Will replace %s', - $modifier->modify($subject, null), - ), - $renderer->render($result, []), - ); + self::assertSame($expected, $actual); } #[Test] - public function itShouldRenderResultProcessingInputAsSomeParameterInTheTemplate(): void + public function itShouldRenderResultWithSubjectParameter(): void { - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer(new DummyTranslator(), $modifier, new TemplateResolver()); - - $input = 42; - + $template = 'Subject: {{subject}}'; + $subject = 'test'; + $formatter = new PlaceholderFormatter([]); $result = (new ResultBuilder()) - ->template('Will replace {{input}}') - ->input($input) + ->template($template) + ->input($subject) ->build(); + $expected = $formatter->formatUsing($template, ['subject' => $result]); + $renderer = new InterpolationRenderer(new TestingTranslator(), $formatter, new TemplateResolver()); - self::assertSame( - sprintf('Will replace %s', $modifier->modify($input, null)), - $renderer->render($result, []), - ); + $actual = $renderer->render($result, []); + + self::assertSame($expected, $actual); } #[Test] - public function itShouldRenderResultNotOverwritingNameParameterWithRealName(): void + public function itShouldRenderResultWithPathParameter(): void { - $parameterNameValue = 'fake name'; - - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer(new DummyTranslator(), $modifier, new TemplateResolver()); - + $template = 'Path: {{path}}'; + $formatter = new PlaceholderFormatter([]); $result = (new ResultBuilder()) - ->template('Will replace {{subject}}') - ->name('real name') - ->parameters(['name' => $parameterNameValue]) + ->template($template) ->build(); + $expected = $formatter->formatUsing($template, ['path' => $result->path]); + $renderer = new InterpolationRenderer(new TestingTranslator(), $formatter, new TemplateResolver()); - $subject = Subject::fromResult($result); + $actual = $renderer->render($result, []); - self::assertSame( - sprintf('Will replace %s', $modifier->modify($subject, null)), - $renderer->render($result, []), - ); + self::assertSame($expected, $actual); } #[Test] - public function itShouldRenderResultNotOverwritingInputParameterWithRealInput(): void + public function itShouldNotAllowParametersToOverrideDefaultOnes(): void { - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer(new DummyTranslator(), $modifier, new TemplateResolver()); - + $template = 'Input: {{input}}'; $input = 'real input'; - + $parameters = ['input' => 'custom input']; + $formatter = new PlaceholderFormatter([]); + $expected = $formatter->formatUsing($template, ['input' => $input]); $result = (new ResultBuilder()) - ->template('Will replace {{input}}') + ->template($template) ->input($input) - ->parameters(['input' => 'fake input']) + ->parameters($parameters) ->build(); + $renderer = new InterpolationRenderer(new TestingTranslator(), $formatter, new TemplateResolver()); - self::assertSame( - sprintf('Will replace %s', $modifier->modify($input, null)), - $renderer->render($result, []), - ); - } - - #[Test] - public function itShouldRenderResultProcessingNonExistingParameters(): void - { - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer(new DummyTranslator(), $modifier, new TemplateResolver()); - - $result = (new ResultBuilder()) - ->template('Will not replace {{unknown}}') - ->build(); + $actual = $renderer->render($result, []); - self::assertSame('Will not replace {{unknown}}', $renderer->render($result, [])); + self::assertSame($expected, $actual); } #[Test] public function itShouldRenderResultTranslatingTemplate(): void { - $template = 'This is my template with {{foo}}'; - $translations = [$template => 'This is my translated template with {{foo}}']; - - $translator = new ArrayTranslator($translations); - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer($translator, $modifier, new TemplateResolver()); - + $template = 'Original message {{name}}'; + $translations = [$template => 'Translated message {{name}}']; + $translator = new TestingTranslator($translations); + $formatter = new PlaceholderFormatter([]); + $expected = $formatter->formatUsing($translations[$template], []); $result = (new ResultBuilder()) ->template($template) ->build(); + $renderer = new InterpolationRenderer($translator, $formatter, new TemplateResolver()); - self::assertSame($translations[$template], $renderer->render($result, [])); + $actual = $renderer->render($result, []); + + self::assertSame($expected, $actual); } #[Test] public function itShouldRenderResultWithNonCustomTemplate(): void { - $translator = new DummyTranslator(); - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer($translator, $modifier, new TemplateResolver()); + $renderer = new InterpolationRenderer( + new TestingTranslator(), + new PlaceholderFormatter([]), + new TemplateResolver(), + ); $result = (new ResultBuilder())->build(); - $subject = Subject::fromResult($result); + $output = $renderer->render($result, []); - self::assertSame( - sprintf( - '%s must be a valid stub', - $modifier->modify($subject, null), - ), - $renderer->render($result, []), - ); + self::assertStringContainsString('must be a valid stub', $output); } #[Test] - public function itShouldRenderResultWithNonCustomTemplateAndInvertedMode(): void + public function itShouldRenderResultWithInvertedMode(): void { - $translator = new DummyTranslator(); - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer($translator, $modifier, new TemplateResolver()); + $renderer = new InterpolationRenderer( + new TestingTranslator(), + new PlaceholderFormatter([]), + new TemplateResolver(), + ); $result = (new ResultBuilder())->hasInvertedMode()->build(); - $subject = Subject::fromResult($result); + $output = $renderer->render($result, []); - self::assertSame( - sprintf( - '%s must not be a valid stub', - $modifier->modify($subject, null), - ), - $renderer->render($result, []), - ); + self::assertStringContainsString('must not be a valid stub', $output); } #[Test] - public function itShouldRenderResultWithNonCustomTemplateWhenCannotFindAttachedTemplate(): void + public function itShouldRenderResultWithItsAdjacentWhenItHasNoCustomTemplate(): void { - $translator = new DummyTranslator(); - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer($translator, $modifier, new TemplateResolver()); - - $result = (new ResultBuilder())->template('__not_standard__')->hasInvertedMode()->build(); - - self::assertSame( - $result->template, - $renderer->render($result, []), + $renderer = new InterpolationRenderer( + new TestingTranslator(), + new PlaceholderFormatter([]), + new TemplateResolver(), ); - } - - #[Test] - public function itShouldRenderResultWithItsAdjacentsWhenItHasNoCustomTemplate(): void - { - $translator = new DummyTranslator(); - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer($translator, $modifier, new TemplateResolver()); $result = (new ResultBuilder())->template('__1st__') ->adjacent( @@ -289,18 +200,20 @@ public function itShouldRenderResultWithItsAdjacentsWhenItHasNoCustomTemplate(): } #[Test] - public function itShouldRenderResultWithoutItsAdjacentsWhenItHasCustomTemplate(): void + public function itShouldNotRenderAdjacentsWhenItHasCustomTemplate(): void { $template = 'Custom template'; + $renderer = new InterpolationRenderer( + new TestingTranslator(), + new PlaceholderFormatter([]), + new TemplateResolver(), + ); + $result = (new ResultBuilder())->template($template) ->adjacent((new ResultBuilder())->template('and this is a adjacent')->build()) ->build(); - $translator = new DummyTranslator(); - $modifier = new TestingModifier(); - $renderer = new InterpolationRenderer($translator, $modifier, new TemplateResolver()); - self::assertSame($template, $renderer->render($result, [])); } } diff --git a/tests/unit/Message/Modifier/ListAndModifierTest.php b/tests/unit/Message/Modifier/ListAndModifierTest.php deleted file mode 100644 index d84f8b4be..000000000 --- a/tests/unit/Message/Modifier/ListAndModifierTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Test; -use Respect\Validation\Message\Placeholder\Listed; -use Respect\Validation\Message\Translator\ArrayTranslator; -use Respect\Validation\Test\Message\TestingModifier; -use Respect\Validation\Test\TestCase; - -#[CoversClass(ListAndModifier::class)] -final class ListAndModifierTest extends TestCase -{ - #[Test] - public function itShouldNotModifyWhenModifierIsNotListAnd(): void - { - $translator = new ArrayTranslator(['and' => 'and']); - $nextModifier = new TestingModifier(); - $modifier = new ListAndModifier($translator, $nextModifier); - - $value = ['item1', 'item2']; - $pipe = 'listOr'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($nextModifier->modify($value, $pipe), $result); - } - - #[Test] - public function itShouldNotModifyWhenValueIsNotArray(): void - { - $translator = new ArrayTranslator(['and' => 'and']); - $nextModifier = new TestingModifier(); - $modifier = new ListAndModifier($translator, $nextModifier); - - $value = 'not an array'; - $pipe = 'listAnd'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($nextModifier->modify($value, $pipe), $result); - } - - #[Test] - public function itShouldModifyWhenModifierIsListAndAndValueIsArray(): void - { - $translator = new ArrayTranslator(['and' => 'and']); - $nextModifier = new TestingModifier(); - $modifier = new ListAndModifier($translator, $nextModifier); - - $value = ['item1', 'item2', 'item3']; - $pipe = 'listAnd'; - - $result = $modifier->modify($value, $pipe); - - $expectedValue = new Listed($value, $translator->translate('and')); - $expected = $nextModifier->modify($expectedValue, null); - - self::assertSame($expected, $result); - } -} diff --git a/tests/unit/Message/Modifier/ListOrModifierTest.php b/tests/unit/Message/Modifier/ListOrModifierTest.php deleted file mode 100644 index 02c67f48c..000000000 --- a/tests/unit/Message/Modifier/ListOrModifierTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Test; -use Respect\Validation\Message\Placeholder\Listed; -use Respect\Validation\Message\Translator\ArrayTranslator; -use Respect\Validation\Test\Message\TestingModifier; -use Respect\Validation\Test\TestCase; - -#[CoversClass(ListOrModifier::class)] -final class ListOrModifierTest extends TestCase -{ - #[Test] - public function itShouldNotModifyWhenModifierIsNotListOr(): void - { - $translator = new ArrayTranslator(['or' => 'or']); - $nextModifier = new TestingModifier(); - $modifier = new ListOrModifier($translator, $nextModifier); - - $value = ['item1', 'item2']; - $pipe = 'listAnd'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($nextModifier->modify($value, $pipe), $result); - } - - #[Test] - public function itShouldNotModifyWhenValueIsNotArray(): void - { - $translator = new ArrayTranslator(['or' => 'or']); - $nextModifier = new TestingModifier(); - $modifier = new ListOrModifier($translator, $nextModifier); - - $value = 'not an array'; - $pipe = 'listOr'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($nextModifier->modify($value, $pipe), $result); - } - - #[Test] - public function itShouldModifyWhenModifierIsListOrAndValueIsArray(): void - { - $translator = new ArrayTranslator(['or' => 'or']); - $nextModifier = new TestingModifier(); - $modifier = new ListOrModifier($translator, $nextModifier); - - $value = ['item1', 'item2', 'item3']; - $pipe = 'listOr'; - - $result = $modifier->modify($value, $pipe); - - $expectedValue = new Listed($value, $translator->translate('or')); - $expected = $nextModifier->modify($expectedValue, null); - - self::assertSame($expected, $result); - } -} diff --git a/tests/unit/Message/Modifier/QuoteModifierTest.php b/tests/unit/Message/Modifier/QuoteModifierTest.php deleted file mode 100644 index 7ad9eb133..000000000 --- a/tests/unit/Message/Modifier/QuoteModifierTest.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Test; -use Respect\Validation\Message\Placeholder\Quoted; -use Respect\Validation\Test\Message\TestingModifier; -use Respect\Validation\Test\TestCase; - -#[CoversClass(QuoteModifier::class)] -final class QuoteModifierTest extends TestCase -{ - #[Test] - public function itShouldNotModifyWhenModifierIsNotQuote(): void - { - $nextModifier = new TestingModifier(); - $modifier = new QuoteModifier($nextModifier); - - $value = 'some string'; - $pipe = 'notQuote'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($nextModifier->modify($value, $pipe), $result); - } - - #[Test] - public function itShouldNotModifyWhenValueIsNotString(): void - { - $nextModifier = new TestingModifier(); - $modifier = new QuoteModifier($nextModifier); - - $value = ['not', 'a', 'string']; - $pipe = 'quote'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($nextModifier->modify($value, $pipe), $result); - } - - #[Test] - public function itShouldModifyWhenModifierIsQuoteAndValueIsString(): void - { - $nextModifier = new TestingModifier(); - $modifier = new QuoteModifier($nextModifier); - - $value = 'some string'; - $pipe = 'quote'; - - $result = $modifier->modify($value, $pipe); - - $expectedValue = new Quoted($value); - $expected = $nextModifier->modify($expectedValue, null); - - self::assertSame($expected, $result); - } -} diff --git a/tests/unit/Message/Modifier/RawModifierTest.php b/tests/unit/Message/Modifier/RawModifierTest.php deleted file mode 100644 index a63792db5..000000000 --- a/tests/unit/Message/Modifier/RawModifierTest.php +++ /dev/null @@ -1,118 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Test; -use Respect\Validation\Test\Message\TestingModifier; -use Respect\Validation\Test\TestCase; - -#[CoversClass(RawModifier::class)] -final class RawModifierTest extends TestCase -{ - #[Test] - public function itShouldNotModifyWhenModifierIsNotRaw(): void - { - $nextModifier = new TestingModifier(); - $modifier = new RawModifier($nextModifier); - - $value = 'some value'; - $pipe = 'notRaw'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($nextModifier->modify($value, $pipe), $result); - } - - #[Test] - public function itShouldNotModifyWhenValueIsNotScalar(): void - { - $nextModifier = new TestingModifier(); - $modifier = new RawModifier($nextModifier); - - $value = ['not', 'scalar']; - $pipe = 'raw'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($nextModifier->modify($value, null), $result); - } - - #[Test] - public function itShouldModifyWhenModifierIsRawAndValueIsScalarString(): void - { - $nextModifier = new TestingModifier(); - $modifier = new RawModifier($nextModifier); - - $value = 'some string'; - $pipe = 'raw'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($value, $result); - } - - #[Test] - public function itShouldModifyWhenModifierIsRawAndValueIsScalarInt(): void - { - $nextModifier = new TestingModifier(); - $modifier = new RawModifier($nextModifier); - - $value = 123; - $pipe = 'raw'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame('123', $result); - } - - #[Test] - public function itShouldModifyWhenModifierIsRawAndValueIsScalarFloat(): void - { - $nextModifier = new TestingModifier(); - $modifier = new RawModifier($nextModifier); - - $value = 123.456; - $pipe = 'raw'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame('123.456', $result); - } - - #[Test] - public function itShouldModifyWhenModifierIsRawAndValueIsScalarBoolTrue(): void - { - $nextModifier = new TestingModifier(); - $modifier = new RawModifier($nextModifier); - - $value = true; - $pipe = 'raw'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame('1', $result); - } - - #[Test] - public function itShouldModifyWhenModifierIsRawAndValueIsScalarBoolFalse(): void - { - $nextModifier = new TestingModifier(); - $modifier = new RawModifier($nextModifier); - - $value = false; - $pipe = 'raw'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame('0', $result); - } -} diff --git a/tests/unit/Message/Modifier/StringifyModifierTest.php b/tests/unit/Message/Modifier/StringifyModifierTest.php deleted file mode 100644 index 19fca148e..000000000 --- a/tests/unit/Message/Modifier/StringifyModifierTest.php +++ /dev/null @@ -1,64 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Test; -use Respect\Validation\Exceptions\ComponentException; -use Respect\Validation\Test\Message\NullStringifier; -use Respect\Validation\Test\Message\TestingStringifier; -use Respect\Validation\Test\TestCase; - -use function print_r; -use function sprintf; - -#[CoversClass(StringifyModifier::class)] -final class StringifyModifierTest extends TestCase -{ - #[Test] - public function itShouldUseStringifierWhenAvailable(): void - { - $value = ['some', 'array']; - - $stringifier = new TestingStringifier(); - $modifier = new StringifyModifier($stringifier); - - $expected = $stringifier->stringify($value, 0); - - self::assertSame($expected, $modifier->modify($value, null)); - } - - #[Test] - public function itShouldUseFallbackWhenStringifierIsNull(): void - { - $value = ['some', 'array']; - $expected = print_r($value, true); - - $modifier = new StringifyModifier(new NullStringifier()); - - self::assertSame($expected, $modifier->modify($value, null)); - } - - #[Test] - public function itShouldFailWhenPipeParameterIsGiven(): void - { - $pipe = 'someModifier'; - - $modifier = new StringifyModifier(new TestingStringifier()); - - $this->expectExceptionObject(new ComponentException(sprintf( - 'StringifyModifier only accepts null as pipe but "%s" was given.', - $pipe, - ))); - - $modifier->modify(['some', 'array'], $pipe); - } -} diff --git a/tests/unit/Message/Modifier/TransModifierTest.php b/tests/unit/Message/Modifier/TransModifierTest.php deleted file mode 100644 index 5aab5dc26..000000000 --- a/tests/unit/Message/Modifier/TransModifierTest.php +++ /dev/null @@ -1,84 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Modifier; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Test; -use Respect\Validation\Message\Translator\ArrayTranslator; -use Respect\Validation\Test\Message\TestingModifier; -use Respect\Validation\Test\TestCase; - -#[CoversClass(TransModifier::class)] -final class TransModifierTest extends TestCase -{ - #[Test] - public function itShouldNotModifyWhenModifierIsNotTrans(): void - { - $translator = new ArrayTranslator(['message' => 'translated message']); - $nextModifier = new TestingModifier(); - $modifier = new TransModifier($translator, $nextModifier); - - $value = 'message'; - $pipe = 'notTrans'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($nextModifier->modify($value, $pipe), $result); - } - - #[Test] - public function itShouldNotModifyWhenValueIsNotString(): void - { - $translator = new ArrayTranslator(['message' => 'translated message']); - $nextModifier = new TestingModifier(); - $modifier = new TransModifier($translator, $nextModifier); - - $value = ['not', 'a', 'string']; - $pipe = 'trans'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($nextModifier->modify($value, $pipe), $result); - } - - #[Test] - public function itShouldModifyWhenModifierIsTransAndValueIsString(): void - { - $key = 'message'; - $translatedMessage = 'translated message'; - $translator = new ArrayTranslator([$key => $translatedMessage]); - $nextModifier = new TestingModifier(); - $modifier = new TransModifier($translator, $nextModifier); - - $value = $key; - $pipe = 'trans'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($translatedMessage, $result); - } - - #[Test] - public function itShouldReturnKeyWhenTranslationNotFound(): void - { - $key = 'nonexistent'; - $translator = new ArrayTranslator(['message' => 'translated message']); - $nextModifier = new TestingModifier(); - $modifier = new TransModifier($translator, $nextModifier); - - $value = $key; - $pipe = 'trans'; - - $result = $modifier->modify($value, $pipe); - - self::assertSame($key, $result); - } -} diff --git a/tests/unit/Message/Stringifier/SubjectStringifierTest.php b/tests/unit/Message/Parameters/ResultHandlerTest.php similarity index 51% rename from tests/unit/Message/Stringifier/SubjectStringifierTest.php rename to tests/unit/Message/Parameters/ResultHandlerTest.php index 7f249d38a..8be029252 100644 --- a/tests/unit/Message/Stringifier/SubjectStringifierTest.php +++ b/tests/unit/Message/Parameters/ResultHandlerTest.php @@ -8,43 +8,43 @@ declare(strict_types=1); -namespace Respect\Validation\Message\Stringifier; +namespace Respect\Validation\Message\Parameters; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; -use Respect\Validation\Message\Placeholder\Subject; use Respect\Validation\Name; use Respect\Validation\Path; -use Respect\Validation\Test\Message\TestingStringifier; +use Respect\Validation\Test\Builders\ResultBuilder; +use Respect\Validation\Test\Message\TestingHandler; use Respect\Validation\Test\TestCase; use stdClass; use function sprintf; -#[CoversClass(SubjectStringifier::class)] -final class SubjectStringifierTest extends TestCase +#[CoversClass(ResultHandler::class)] +final class ResultHandlerTest extends TestCase { #[Test] #[DataProvider('providerForNonSubjectValues')] public function itShouldNotStringifyWhenValueIsNotAnInstanceOfSubject(mixed $value): void { - $stringifier = new SubjectStringifier(new TestingStringifier()); + $handler = new ResultHandler(new TestingHandler()); - self::assertNull($stringifier->stringify($value, 0)); + self::assertNull($handler->handle($value, 0)); } #[Test] public function itShouldStringifyInputWhenPathAndNameAreNull(): void { $input = ['test' => 'value']; - $subject = new Subject($input); + $result = (new ResultBuilder())->input($input)->build(); - $testingStringifier = new TestingStringifier(); - $stringifier = new SubjectStringifier($testingStringifier); + $testingHandler = new TestingHandler(); + $handler = new ResultHandler($testingHandler); - $expected = $testingStringifier->stringify($input, 0); - $actual = $stringifier->stringify($subject, 0); + $expected = $testingHandler->handle($input, 0); + $actual = $handler->handle($result, 0); self::assertSame($expected, $actual); } @@ -53,13 +53,13 @@ public function itShouldStringifyInputWhenPathAndNameAreNull(): void public function itShouldStringifyPathWhenNameIsNull(): void { $path = new Path('field1'); - $subject = new Subject('input', $path); + $result = (new ResultBuilder())->path($path)->build(); - $testingStringifier = new TestingStringifier(); - $stringifier = new SubjectStringifier($testingStringifier); + $testingHandler = new TestingHandler(); + $handler = new ResultHandler($testingHandler); - $expected = $testingStringifier->stringify($path, 0); - $actual = $stringifier->stringify($subject, 0); + $expected = $testingHandler->handle($path, 0); + $actual = $handler->handle($result, 0); self::assertSame($expected, $actual); } @@ -68,13 +68,13 @@ public function itShouldStringifyPathWhenNameIsNull(): void public function itShouldStringifyNameWhenPathIsNull(): void { $name = new Name('field_name'); - $subject = new Subject('input', null, $name); + $result = (new ResultBuilder())->name($name)->build(); - $testingStringifier = new TestingStringifier(); - $stringifier = new SubjectStringifier($testingStringifier); + $testingHandler = new TestingHandler(); + $handler = new ResultHandler($testingHandler); - $expected = $testingStringifier->stringify($name, 0); - $actual = $stringifier->stringify($subject, 0); + $expected = $testingHandler->handle($name, 0); + $actual = $handler->handle($result, 0); self::assertSame($expected, $actual); } @@ -84,13 +84,13 @@ public function itShouldStringifyNameWhenNameHasPrecedence(): void { $path = new Path('field1'); $name = new Name('field_name'); - $subject = new Subject('input', $path, $name, true); + $result = (new ResultBuilder())->name($name)->path($path)->hasPrecedentName(true)->build(); - $testingStringifier = new TestingStringifier(); - $stringifier = new SubjectStringifier($testingStringifier); + $testingHandler = new TestingHandler(); + $handler = new ResultHandler($testingHandler); - $expected = $testingStringifier->stringify($name, 0); - $actual = $stringifier->stringify($subject, 0); + $expected = $testingHandler->handle($name, 0); + $actual = $handler->handle($result, 0); self::assertSame($expected, $actual); } @@ -100,15 +100,15 @@ public function itShouldStringifyWithPathAndNameWhenNameHasNoPrecedence(): void { $path = new Path('field1'); $name = new Name('field_name'); - $subject = new Subject('input', $path, $name, false); + $result = (new ResultBuilder())->name($name)->path($path)->hasPrecedentName(false)->build(); - $testingStringifier = new TestingStringifier(); - $stringifier = new SubjectStringifier($testingStringifier); + $testingHandler = new TestingHandler(); + $handler = new ResultHandler($testingHandler); - $pathString = $testingStringifier->stringify($path, 0); - $nameString = $testingStringifier->stringify($name, 0); + $pathString = $testingHandler->handle($path, 0); + $nameString = $testingHandler->handle($name, 0); $expected = sprintf('%s (<- %s)', $pathString, $nameString); - $actual = $stringifier->stringify($subject, 0); + $actual = $handler->handle($result, 0); self::assertSame($expected, $actual); } @@ -119,15 +119,15 @@ public function itShouldStringifyWithNestedPathWhenNameHasNoPrecedence(): void $path1 = new Path('field1'); $path2 = new Path('field2', $path1); $name = new Name('field_name'); - $subject = new Subject('input', $path2, $name, false); + $result = (new ResultBuilder())->name($name)->path($path2)->hasPrecedentName(false)->build(); - $testingStringifier = new TestingStringifier(); - $stringifier = new SubjectStringifier($testingStringifier); + $testingHandler = new TestingHandler(); + $handler = new ResultHandler($testingHandler); - $pathString = $testingStringifier->stringify($path2, 0); - $nameString = $testingStringifier->stringify($name, 0); + $pathString = $testingHandler->handle($path2, 0); + $nameString = $testingHandler->handle($name, 0); $expected = sprintf('%s (<- %s)', $pathString, $nameString); - $actual = $stringifier->stringify($subject, 0); + $actual = $handler->handle($result, 0); self::assertSame($expected, $actual); } diff --git a/tests/unit/Message/Stringifier/ListedStringifierTest.php b/tests/unit/Message/Stringifier/ListedStringifierTest.php deleted file mode 100644 index 655d95000..000000000 --- a/tests/unit/Message/Stringifier/ListedStringifierTest.php +++ /dev/null @@ -1,61 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Stringifier; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\Attributes\Test; -use Respect\Stringifier\Stringifiers\JsonEncodableStringifier; -use Respect\Validation\Message\Placeholder\Listed; -use Respect\Validation\Test\TestCase; - -#[CoversClass(ListedStringifier::class)] -final class ListedStringifierTest extends TestCase -{ - #[Test] - #[DataProvider('providerForAnyValues')] - public function itShouldNotStringifyWhenValueIsNotAnInstanceOfListed(mixed $value): void - { - $quoter = new JsonEncodableStringifier(); - $stringifier = new ListedStringifier($quoter); - - self::assertNull($stringifier->stringify($value, 0)); - } - - #[Test] - public function itShouldNotStringifyEmptyListed(): void - { - $stringifier = new ListedStringifier(new JsonEncodableStringifier()); - - self::assertNull($stringifier->stringify(new Listed([], '-'), 0)); - } - - #[Test] - #[DataProvider('providerForListed')] - public function itShouldStringifyWhenValueIsAnInstanceOfListed(Listed $listed, string $expected): void - { - $stringifier = new ListedStringifier(new JsonEncodableStringifier()); - - $actual = $stringifier->stringify($listed, 0); - - self::assertSame($expected, $actual); - } - - /** @return array */ - public static function providerForListed(): array - { - return [ - '1 item' => [new Listed([1], 'and'), '1'], - '2 items' => [new Listed([1, 2], 'and'), '1 and 2'], - '3 items' => [new Listed([1, 2, 3], 'or'), '1, 2, or 3'], - ]; - } -} diff --git a/tests/unit/Message/Stringifier/QuotedStringifierTest.php b/tests/unit/Message/Stringifier/QuotedStringifierTest.php deleted file mode 100644 index 5bda4c3a3..000000000 --- a/tests/unit/Message/Stringifier/QuotedStringifierTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Stringifier; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\Attributes\Test; -use Respect\Stringifier\Quoters\StandardQuoter; -use Respect\Validation\Message\Placeholder\Quoted; -use Respect\Validation\Test\TestCase; - -#[CoversClass(QuotedStringifier::class)] -final class QuotedStringifierTest extends TestCase -{ - #[Test] - #[DataProvider('providerForAnyValues')] - public function itShouldNotStringifyWhenValueIsNotAnInstanceOfQuoted(mixed $value): void - { - $quoter = new StandardQuoter(1); - $stringifier = new QuotedStringifier($quoter); - - self::assertNull($stringifier->stringify($value, 0)); - } - - #[Test] - #[DataProvider('providerForStringTypes')] - public function itShouldStringifyWhenValueIsAnInstanceOfQuoted(string $value): void - { - $quoted = new Quoted($value); - $quoter = new StandardQuoter(1); - $stringifier = new QuotedStringifier($quoter); - - $expected = $quoter->quote($quoted->value, 0); - $actual = $stringifier->stringify($quoted, 0); - - self::assertSame($expected, $actual); - } -} diff --git a/tests/unit/Message/Translator/ArrayTranslatorTest.php b/tests/unit/Message/Translator/ArrayTranslatorTest.php deleted file mode 100644 index bcc5f55db..000000000 --- a/tests/unit/Message/Translator/ArrayTranslatorTest.php +++ /dev/null @@ -1,48 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Respect\Validation\Message\Translator; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Test; -use Respect\Validation\Test\TestCase; - -#[CoversClass(ArrayTranslator::class)] -final class ArrayTranslatorTest extends TestCase -{ - #[Test] - public function shouldReturnOriginalMessageWhenCannotFindTranslation(): void - { - $translator = new ArrayTranslator([]); - $message = 'This is a test message'; - - self::assertSame($message, $translator->translate($message)); - } - - #[Test] - public function shouldReturnTranslatedMessage(): void - { - $messages = ['foo' => 'bar']; - - $translator = new ArrayTranslator($messages); - - self::assertSame($messages['foo'], $translator->translate('foo')); - } - - #[Test] - public function shouldReturnOriginalMessageWhenTranslationIsNotString(): void - { - $messages = ['foo' => 123]; - - $translator = new ArrayTranslator($messages); - - self::assertSame('foo', $translator->translate('foo')); - } -} diff --git a/tests/unit/ResultQueryTest.php b/tests/unit/ResultQueryTest.php index d30c06aca..06673034f 100644 --- a/tests/unit/ResultQueryTest.php +++ b/tests/unit/ResultQueryTest.php @@ -52,7 +52,7 @@ public function itShouldReturnEmptyMessageWhenResultHasPassed(): void $resultQuery = $this->createResultQuery($result); - self::assertSame('', $resultQuery->toMessage()); + self::assertSame('', $resultQuery->getMessage()); } #[Test] @@ -65,7 +65,7 @@ public function itShouldReturnFormattedMessageWhenResultHasNotPassed(): void $resultQuery = $this->createResultQuery($result, renderer: $renderer, messageFormatter: $formatter); - self::assertSame($formatter->format($result, $renderer, []), $resultQuery->toMessage()); + self::assertSame($formatter->format($result, $renderer, []), $resultQuery->getMessage()); } #[Test] @@ -75,7 +75,7 @@ public function itShouldReturnEmptyFullMessageWhenResultHasPassed(): void $resultQuery = $this->createResultQuery($result); - self::assertSame('', $resultQuery->toFullMessage()); + self::assertSame('', $resultQuery->getFullMessage()); } #[Test] @@ -88,7 +88,7 @@ public function itShouldReturnFormattedFullMessageWhenResultHasNotPassed(): void $resultQuery = $this->createResultQuery($result, renderer: $renderer, fullMessageFormatter: $formatter); - self::assertSame($formatter->format($result, $renderer, []), $resultQuery->toFullMessage()); + self::assertSame($formatter->format($result, $renderer, []), $resultQuery->getFullMessage()); } #[Test] @@ -98,7 +98,7 @@ public function itShouldReturnEmptyArrayWhenResultHasPassed(): void $resultQuery = $this->createResultQuery($result); - self::assertSame([], $resultQuery->toArrayMessages()); + self::assertSame([], $resultQuery->getMessages()); } #[Test] @@ -111,7 +111,7 @@ public function itShouldReturnFormattedArrayWhenResultHasNotPassed(): void $resultQuery = $this->createResultQuery($result, renderer: $renderer, messagesFormatter: $formatter); - self::assertSame($formatter->format($result, $renderer, []), $resultQuery->toArrayMessages()); + self::assertSame($formatter->format($result, $renderer, []), $resultQuery->getMessages()); } #[Test] @@ -154,7 +154,7 @@ public function itShouldFindByIdWhenIdMatchesCurrentResult(): void $found = $resultQuery->findById($id); self::assertNotNull($found); - self::assertSame($formatter->format($result, $renderer, []), $found->toMessage()); + self::assertSame($formatter->format($result, $renderer, []), $found->getMessage()); } #[Test] @@ -180,7 +180,7 @@ public function itShouldFindByIdInDirectChildren(): void $found = $resultQuery->findById($childId); self::assertNotNull($found); - self::assertSame($formatter->format($child, $renderer, []), $found->toMessage()); + self::assertSame($formatter->format($child, $renderer, []), $found->getMessage()); } #[Test] @@ -212,7 +212,7 @@ public function itShouldFindByIdInNestedChildren(): void $found = $resultQuery->findById($grandchildId); self::assertNotNull($found); - self::assertSame($formatter->format($grandchild, $renderer, []), $found->toMessage()); + self::assertSame($formatter->format($grandchild, $renderer, []), $found->getMessage()); } #[Test] @@ -256,7 +256,7 @@ public function itShouldFindByNameWhenNameMatchesCurrentResult(): void $found = $resultQuery->findByName($name); self::assertNotNull($found); - self::assertSame($formatter->format($result, $renderer, []), $found->toMessage()); + self::assertSame($formatter->format($result, $renderer, []), $found->getMessage()); } #[Test] @@ -282,7 +282,7 @@ public function itShouldFindByNameInDirectChildren(): void $found = $resultQuery->findByName($childName); self::assertNotNull($found); - self::assertSame($formatter->format($child, $renderer, []), $found->toMessage()); + self::assertSame($formatter->format($child, $renderer, []), $found->getMessage()); } #[Test] @@ -314,7 +314,7 @@ public function itShouldFindByNameInNestedChildren(): void $found = $resultQuery->findByName($grandchildName); self::assertNotNull($found); - self::assertSame($formatter->format($grandchild, $renderer, []), $found->toMessage()); + self::assertSame($formatter->format($grandchild, $renderer, []), $found->getMessage()); } #[Test] @@ -347,7 +347,7 @@ public function itShouldFindByPathWhenPathMatchesCurrentResult(): void $path = uniqid(); $result = (new ResultBuilder()) - ->withPath(new Path($path)) + ->path(new Path($path)) ->hasPassed(false) ->build(); @@ -356,7 +356,7 @@ public function itShouldFindByPathWhenPathMatchesCurrentResult(): void $found = $resultQuery->findByPath($path); self::assertNotNull($found); - self::assertSame($formatter->format($result, $renderer, []), $found->toMessage()); + self::assertSame($formatter->format($result, $renderer, []), $found->getMessage()); } #[Test] @@ -367,12 +367,12 @@ public function itShouldFindByPathInDirectChildren(): void $childPath = uniqid(); $child = (new ResultBuilder()) - ->withPath(new Path($childPath)) + ->path(new Path($childPath)) ->hasPassed(false) ->build(); $parent = (new ResultBuilder()) - ->withPath(new Path(uniqid())) + ->path(new Path(uniqid())) ->hasPassed(false) ->children($child) ->build(); @@ -382,7 +382,7 @@ public function itShouldFindByPathInDirectChildren(): void $found = $resultQuery->findByPath($childPath); self::assertNotNull($found); - self::assertSame($formatter->format($child, $renderer, []), $found->toMessage()); + self::assertSame($formatter->format($child, $renderer, []), $found->getMessage()); } #[Test] @@ -395,18 +395,18 @@ public function itShouldFindByDottedPathInNestedChildren(): void $grandchildPath = uniqid(); $grandchild = (new ResultBuilder()) - ->withPath(new Path($grandchildPath)) + ->path(new Path($grandchildPath)) ->hasPassed(false) ->build(); $child = (new ResultBuilder()) - ->withPath(new Path($childPath)) + ->path(new Path($childPath)) ->hasPassed(false) ->children($grandchild) ->build(); $parent = (new ResultBuilder()) - ->withPath(new Path(uniqid())) + ->path(new Path(uniqid())) ->hasPassed(false) ->children($child) ->build(); @@ -416,7 +416,7 @@ public function itShouldFindByDottedPathInNestedChildren(): void $found = $resultQuery->findByPath($childPath . '.' . $grandchildPath); self::assertNotNull($found); - self::assertSame($formatter->format($grandchild, $renderer, []), $found->toMessage()); + self::assertSame($formatter->format($grandchild, $renderer, []), $found->getMessage()); } #[Test] @@ -427,7 +427,7 @@ public function itShouldFindByIntegerPath(): void $integerPath = 0; $child = (new ResultBuilder()) - ->withPath(new Path($integerPath)) + ->path(new Path($integerPath)) ->hasPassed(false) ->build(); @@ -441,14 +441,14 @@ public function itShouldFindByIntegerPath(): void $found = $resultQuery->findByPath($integerPath); self::assertNotNull($found); - self::assertSame($formatter->format($child, $renderer, []), $found->toMessage()); + self::assertSame($formatter->format($child, $renderer, []), $found->getMessage()); } #[Test] public function itShouldReturnNullWhenPathNotFound(): void { $result = (new ResultBuilder()) - ->withPath(new Path(uniqid())) + ->path(new Path(uniqid())) ->build(); $resultQuery = $this->createResultQuery($result); @@ -471,7 +471,7 @@ public function itShouldReturnNullWhenDottedPathPartiallyMatches(): void { $childPath = uniqid(); $child = (new ResultBuilder()) - ->withPath(new Path($childPath)) + ->path(new Path($childPath)) ->hasPassed(false) ->build(); @@ -489,7 +489,7 @@ public function itShouldReturnNullWhenDottedPathPartiallyMatches(): void public function itShouldReturnNullWhenChildPathDoesNotMatch(): void { $child = (new ResultBuilder()) - ->withPath(new Path(uniqid())) + ->path(new Path(uniqid())) ->hasPassed(false) ->build(); @@ -532,8 +532,8 @@ public function itShouldPreserveFormattingCapabilitiesAfterFindById(): void $found = $resultQuery->findById($childId); self::assertNotNull($found); - self::assertSame($messageFormatter->format($child, $renderer, []), $found->toMessage()); - self::assertSame($fullMessageFormatter->format($child, $renderer, []), $found->toFullMessage()); + self::assertSame($messageFormatter->format($child, $renderer, []), $found->getMessage()); + self::assertSame($fullMessageFormatter->format($child, $renderer, []), $found->getFullMessage()); } #[Test] @@ -563,7 +563,7 @@ public function itShouldPreserveFormattingCapabilitiesAfterFindByName(): void $found = $resultQuery->findByName($childName); self::assertNotNull($found); - self::assertSame($messagesFormatter->format($child, $renderer, []), $found->toArrayMessages()); + self::assertSame($messagesFormatter->format($child, $renderer, []), $found->getMessages()); } #[Test] @@ -574,7 +574,7 @@ public function itShouldPreserveFormattingCapabilitiesAfterFindByPath(): void $childPath = uniqid(); $child = (new ResultBuilder()) - ->withPath(new Path($childPath)) + ->path(new Path($childPath)) ->hasPassed(false) ->build(); diff --git a/tests/unit/ValidatorTest.php b/tests/unit/ValidatorTest.php index cfa8f0bba..5d1d4c802 100644 --- a/tests/unit/ValidatorTest.php +++ b/tests/unit/ValidatorTest.php @@ -111,7 +111,7 @@ public function itShouldValidateUsingStringTemplateWhenProvided(): void $resultQuery = $validator->validate('whatever', $template); - self::assertSame($template, $resultQuery->toMessage()); + self::assertSame($template, $resultQuery->getMessage()); } #[Test] @@ -123,7 +123,7 @@ public function itShouldValidateUsingArrayTemplatesWhenProvided(): void $resultQuery = $validator->validate('whatever', ['stub' => $template]); - self::assertSame($template, $resultQuery->toMessage()); + self::assertSame($template, $resultQuery->getMessage()); } #[Test]