diff --git a/src/Aggregate/ArrayElement.php b/src/Aggregate/ArrayElement.php index ac20899..951188a 100644 --- a/src/Aggregate/ArrayElement.php +++ b/src/Aggregate/ArrayElement.php @@ -294,6 +294,14 @@ public function valid(): bool return $this->valid; } + /** + * {@inheritdoc} + */ + public function failed(): bool + { + return !$this->valid; + } + /** * {@inheritdoc} */ diff --git a/src/Aggregate/Form.php b/src/Aggregate/Form.php index 4a11627..2d0ac3a 100644 --- a/src/Aggregate/Form.php +++ b/src/Aggregate/Form.php @@ -184,6 +184,14 @@ public function valid(): bool return $this->valid; } + /** + * {@inheritdoc} + */ + public function failed(): bool + { + return !$this->valid; + } + /** * {@inheritdoc} */ diff --git a/src/Aggregate/FormBuilder.php b/src/Aggregate/FormBuilder.php index c05ad4f..cf7688f 100644 --- a/src/Aggregate/FormBuilder.php +++ b/src/Aggregate/FormBuilder.php @@ -14,6 +14,8 @@ use Bdf\Form\Custom\CustomForm; use Bdf\Form\ElementBuilderInterface; use Bdf\Form\ElementInterface; +use Bdf\Form\Leaf\AnyElement; +use Bdf\Form\Leaf\AnyElementBuilder; use Bdf\Form\Leaf\BooleanElement; use Bdf\Form\Leaf\BooleanElementBuilder; use Bdf\Form\Leaf\Date\DateTimeChildBuilder; @@ -128,6 +130,20 @@ public function add(string $name, string $element): ChildBuilderInterface return $this->children[$name] = $this->registry()->childBuilder($element, $name); } + /** + * {@inheritdoc} + * + * @psalm-param non-empty-string $name + * @psalm-return ChildBuilder + * + * @psalm-suppress MoreSpecificReturnType + * @psalm-suppress LessSpecificReturnStatement + */ + public function any(string $name): ChildBuilderInterface + { + return $this->add($name, AnyElement::class); + } + /** * {@inheritdoc} * diff --git a/src/Aggregate/FormBuilderInterface.php b/src/Aggregate/FormBuilderInterface.php index ac2ed8a..fda02db 100644 --- a/src/Aggregate/FormBuilderInterface.php +++ b/src/Aggregate/FormBuilderInterface.php @@ -9,6 +9,7 @@ use Bdf\Form\Csrf\CsrfElementBuilder; use Bdf\Form\ElementBuilderInterface; use Bdf\Form\ElementInterface; +use Bdf\Form\Leaf\AnyElementBuilder; use Bdf\Form\Leaf\BooleanElementBuilder; use Bdf\Form\Leaf\Date\DateTimeChildBuilder; use Bdf\Form\Leaf\Date\DateTimeElementBuilder; @@ -36,6 +37,8 @@ * * * @extends ElementBuilderInterface + * + * @method ChildBuilderInterface any(string $name) */ interface FormBuilderInterface extends ElementBuilderInterface { @@ -56,6 +59,25 @@ interface FormBuilderInterface extends ElementBuilderInterface */ public function add(string $name, string $element): ChildBuilderInterface; + /** + * Add a new child element which can be of any type + * + * Note: the any element remove all type safety, and should be used with caution + * + * + * $builder->any('value', IntegerElement::class)->required(); + * + * + * @param non-empty-string $name The child name + * + * @return ChildBuilder|AnyElementBuilder + * @return ChildBuilderInterface The child builder + * + * @since 1.5 + * @todo uncomment in 2.0 + */ + //public function any(string $name): ChildBuilderInterface; + /** * Add a new string element on the form * diff --git a/src/Aggregate/RootForm.php b/src/Aggregate/RootForm.php index f43a11e..97e392b 100644 --- a/src/Aggregate/RootForm.php +++ b/src/Aggregate/RootForm.php @@ -171,6 +171,15 @@ public function valid(): bool return $this->form->get()->valid(); } + /** + * {@inheritdoc} + */ + public function failed(): bool + { + // Do not use $this->form->get()->failed() because it may be not implemented + return !$this->valid(); + } + /** * {@inheritdoc} */ diff --git a/src/Child/ChildBuilder.php b/src/Child/ChildBuilder.php index 19d5291..381f232 100644 --- a/src/Child/ChildBuilder.php +++ b/src/Child/ChildBuilder.php @@ -333,6 +333,34 @@ final public function setter($propertyName = null, ?callable $transformer = null return $this->hydrator(new Setter($propertyName, $transformer, $customAccessor)); } + /** + * Helper method for define both simple getter and setter + * This method is a shortcut for `$builder->getter($name)->setter($name)` + * + * This method does not supports transformer and custom accessor. + * To define one of them, use the getter() and setter() methods. + * + * + * $builder->string('foo')->getset(); // fill() and import() the "foo" property + * $builder->string('foo')->getset('bar'); // fill() and import() the "bar" property + * + * + * @param string|null $propertyName The property name. If null use the child name. + * + * @return $this + * + * @see Setter + * @see Getter + * @see ChildBuilder::getter() + * @see ChildBuilder::setter() + * + * @since 1.5 + */ + final public function getset(?string $propertyName = null): self + { + return $this->getter($propertyName)->setter($propertyName); + } + /** * Define the child class name * diff --git a/src/Csrf/CsrfElement.php b/src/Csrf/CsrfElement.php index acd6d4d..11e11d0 100644 --- a/src/Csrf/CsrfElement.php +++ b/src/Csrf/CsrfElement.php @@ -130,6 +130,14 @@ public function valid(): bool return $this->value && $this->error->empty(); } + /** + * {@inheritdoc} + */ + public function failed(): bool + { + return !$this->valid(); + } + /** * {@inheritdoc} */ diff --git a/src/Custom/CustomForm.php b/src/Custom/CustomForm.php index 39169d7..af7916d 100644 --- a/src/Custom/CustomForm.php +++ b/src/Custom/CustomForm.php @@ -183,6 +183,15 @@ public function valid(): bool return $this->form()->valid(); } + /** + * {@inheritdoc} + */ + public function failed(): bool + { + // Do not use $this->form()->failed() because it may be not implemented + return !$this->valid(); + } + /** * {@inheritdoc} */ diff --git a/src/ElementInterface.php b/src/ElementInterface.php index 0747caa..4194dd9 100644 --- a/src/ElementInterface.php +++ b/src/ElementInterface.php @@ -20,6 +20,8 @@ * * * @template T + * + * @method bool failed() */ interface ElementInterface { @@ -124,6 +126,22 @@ public function httpValue(); */ public function valid(): bool; + /** + * Check if the element value validation has failed + * On aggregate element, if at least on child is invalid, this method will return true + * + * This method is a shortcut for `!$element->valid()` + * + * Note: A non-submit()'ed element will return true + * + * @return bool + * + * @see ElementInterface::error() To get error + * @since 1.5 + * @todo uncomment in 2.0 + */ + //public function failed(): bool; + /** * Get the errors related to the element * diff --git a/src/Error/FormError.php b/src/Error/FormError.php index b3fe4f3..6ea6909 100644 --- a/src/Error/FormError.php +++ b/src/Error/FormError.php @@ -114,6 +114,37 @@ public function empty(): bool return empty($this->global) && empty($this->code) && empty($this->children); } + /** + * Get a single error message + * + * Note: if the error is an aggregate error, the method will return null, + * so it must not be used to check if a child has an error + * + * @param string $child The child name + * + * @return string|null The child error message, or null if the child has no global error, or is not found + * @since 1.5 + */ + public function get(string $child): ?string + { + $child = $this->children[$child] ?? null; + + return $child ? $child->global : null; + } + + /** + * Get the error of a child + * An object is always returned, even if the child has no error, so check {@see FormError::empty()} to know if the child has an error + * @param string $child The child name + * + * @return FormError The child error + * @since 1.5 + */ + public function child(string $child): FormError + { + return $this->children[$child] ?? self::null(); + } + /** * Export the errors into an array * diff --git a/src/Leaf/LeafElement.php b/src/Leaf/LeafElement.php index 176773e..8ea0f80 100644 --- a/src/Leaf/LeafElement.php +++ b/src/Leaf/LeafElement.php @@ -130,6 +130,14 @@ final public function valid(): bool return $this->submitted && $this->error->empty(); } + /** + * {@inheritdoc} + */ + final public function failed(): bool + { + return !$this->valid(); + } + /** * {@inheritdoc} */ @@ -188,14 +196,14 @@ final public function root(): RootElementInterface */ public function view(?HttpFieldPath $field = null): ElementViewInterface { - $normalizedConstraints = ConstraintsNormalizer::normalize($this->validator); + [$required, $normalizedConstraints] = $this->parseConstraints($this->validator); return new SimpleElementView( static::class, (string) $field, $this->httpValue(), $this->error->global(), - isset($normalizedConstraints[NotBlank::class]), + $required, $normalizedConstraints, $this->choiceView() ); @@ -278,4 +286,22 @@ protected function choiceView(): ?array $view->setValue($this->transformer->transformToHttp($this->toHttp($view->value()), $this)); }); } + + /** + * Parse constraints and required value + * + * By default, will use {@see ConstraintsNormalizer} to extract constraints, + * and check the presence of {@see NotBlank} constraint to determine if the field is required + * + * @return list{bool, array} + */ + protected function parseConstraints(ValueValidatorInterface $validator): array + { + $normalizedConstraints = ConstraintsNormalizer::normalize($validator); + + return [ + isset($normalizedConstraints[NotBlank::class]), + $normalizedConstraints + ]; + } } diff --git a/src/Leaf/LeafRootElement.php b/src/Leaf/LeafRootElement.php index 5b60f41..f9faf9d 100644 --- a/src/Leaf/LeafRootElement.php +++ b/src/Leaf/LeafRootElement.php @@ -96,6 +96,15 @@ public function valid(): bool return $this->element->valid(); } + /** + * {@inheritdoc} + */ + public function failed(): bool + { + // Do not use $this->element->failed() because it may be not implemented + return !$this->valid(); + } + /** * {@inheritdoc} */ diff --git a/src/Leaf/View/SimpleFieldHtmlRenderer.php b/src/Leaf/View/SimpleFieldHtmlRenderer.php index 52457a8..d630fa5 100644 --- a/src/Leaf/View/SimpleFieldHtmlRenderer.php +++ b/src/Leaf/View/SimpleFieldHtmlRenderer.php @@ -62,7 +62,11 @@ public function render(FieldViewInterface $view, array $attributes): string $attributes['name'] = $view->name(); $attributes['value'] = $view->value(); - $attributes['required'] = $view->required(); + + if (!isset($attributes['required'])) { + $attributes['required'] = $view->required(); + } + $attributes += $this->constraintsToAttributes($view->constraints()); return HtmlRenderer::element('input', $attributes); diff --git a/src/Phone/PhoneElement.php b/src/Phone/PhoneElement.php index e9fe59f..db85784 100644 --- a/src/Phone/PhoneElement.php +++ b/src/Phone/PhoneElement.php @@ -129,4 +129,25 @@ public function parseValue(string $rawPhoneNumber): PhoneNumber return (new PhoneNumber())->setRawInput($rawPhoneNumber); } } + + /** + * {@inheritdoc} + */ + protected function parseConstraints(ValueValidatorInterface $validator): array + { + $result = parent::parseConstraints($validator); + + if (!$result[0]) { + // PhoneElement use NotEmptyPhoneNumber instead of NotBlank + // So we need to check if this constraint is present + foreach ($validator->constraints() as $constraint) { + if ($constraint instanceof NotEmptyPhoneNumber) { + $result[0] = true; + break; + } + } + } + + return $result; + } } diff --git a/src/View/ElementViewInterface.php b/src/View/ElementViewInterface.php index 953ade2..d4bded2 100644 --- a/src/View/ElementViewInterface.php +++ b/src/View/ElementViewInterface.php @@ -4,6 +4,8 @@ /** * Base form element view type + * + * @method self setError(?string $error) */ interface ElementViewInterface { @@ -22,6 +24,21 @@ public function type(): string; */ public function error(): ?string; + /** + * Overwrite the current element error + * In case of aggregate element, this method should set the global error and not the children errors + * + * This method can be used to display error from an external source, for example from an API error. + * Set to null to remove the error + * + * @param string|null $error The error message, or null to mark the element as valid + * + * @return $this + * @since 1.5 + * @todo uncomment in 2.0 + */ + //public function setError(?string $error): self; + /** * Check if the current element is on error * In case of aggregate element, this method will return false if there is at least one child on error or a global error diff --git a/src/View/ElementViewTrait.php b/src/View/ElementViewTrait.php index 44b997d..4f29b15 100644 --- a/src/View/ElementViewTrait.php +++ b/src/View/ElementViewTrait.php @@ -2,6 +2,8 @@ namespace Bdf\Form\View; +use function is_string; + /** * Implements @see ElementViewInterface * @@ -35,6 +37,16 @@ public function error(): ?string return $this->error; } + /** + * {@inheritdoc} + */ + public function setError(?string $error): ElementViewInterface + { + $this->error = $error; + + return $this; + } + /** * {@inheritdoc} */ diff --git a/src/View/FieldSetViewInterface.php b/src/View/FieldSetViewInterface.php index 1583fdd..ba688e8 100644 --- a/src/View/FieldSetViewInterface.php +++ b/src/View/FieldSetViewInterface.php @@ -28,8 +28,21 @@ * * @extends ArrayAccess * @extends Traversable + * + * @method array errors() */ interface FieldSetViewInterface extends ElementViewInterface, ArrayAccess, Traversable { - + /** + * Get errors of all children + * If there are no errors, this method should return an empty array + * + * The returned array is indexed by the child name, and the value is the error message as string when child is a leaf element, + * or an array when child is an aggregate element + * + * @return array + * @since 1.5 + * @todo uncomment in 2.0 + */ + //public function errors(): array; } diff --git a/src/View/FieldSetViewTrait.php b/src/View/FieldSetViewTrait.php index 93be0c9..e3af4c8 100644 --- a/src/View/FieldSetViewTrait.php +++ b/src/View/FieldSetViewTrait.php @@ -6,6 +6,8 @@ use BadMethodCallException; use Iterator; +use function method_exists; + /** * Implements @see FieldSetViewInterface * @@ -68,6 +70,28 @@ public function hasError(): bool return false; } + /** + * {@inheritdoc} + */ + public function errors(): array + { + $errors = []; + + foreach ($this->elements as $name => $element) { + if (!$element->hasError()) { + continue; + } + + if ($element instanceof FieldSetViewInterface && method_exists($element, 'errors')) { + $errors[$name] = $element->errors(); + } elseif ($error = $element->error()) { + $errors[$name] = $error; + } + } + + return $errors; + } + /** * {@inheritdoc} * diff --git a/src/View/FieldViewInterface.php b/src/View/FieldViewInterface.php index fd0dde1..e8f5383 100644 --- a/src/View/FieldViewInterface.php +++ b/src/View/FieldViewInterface.php @@ -9,6 +9,8 @@ /** * Base type for HTTP input / field * The implementations must be renderable + * + * @method self setValue($value) Override the value */ interface FieldViewInterface extends ElementViewInterface, Renderable { @@ -26,6 +28,18 @@ public function name(): string; */ public function value(); + /** + * Override the value + * The given value MUST be an HTTP value + * + * @param mixed $value The new value + * + * @return $this Return the current instance + * @since 1.5 + * @todo uncomment in 2.0 + */ + //public function setValue($value): self; + /** * Does the current field is required (i.e. the value must not be empty) * diff --git a/src/View/FieldViewTrait.php b/src/View/FieldViewTrait.php index 6acff0a..33696e6 100644 --- a/src/View/FieldViewTrait.php +++ b/src/View/FieldViewTrait.php @@ -54,6 +54,16 @@ public function value() return $this->value; } + /** + * {@inheritdoc} + */ + public function setValue($value): FieldViewInterface + { + $this->value = $value; + + return $this; + } + /** * {@inheritdoc} */ diff --git a/src/View/Renderable.php b/src/View/Renderable.php index 15cb7fd..071d9cf 100644 --- a/src/View/Renderable.php +++ b/src/View/Renderable.php @@ -2,10 +2,13 @@ namespace Bdf\Form\View; +use phpDocumentor\Reflection\Types\Scalar; use Stringable; /** * Base type for a renderable element of the form view tree + * + * @method self with(array $attributes) */ interface Renderable extends Stringable { @@ -29,6 +32,22 @@ public function __call(string $name, array $arguments); */ public function set(string $name, $value); + /** + * Define multiple attributes + * Use key-value for simple attributes, and only value for flag attributes + * + * Example: + * + * $element->with(['id' => 'my-id', 'required']); + * + * + * @param array $attributes + * @return $this + * @since 1.5 + * @todo uncomment in 2.0 + */ + //public function with(array $attributes); + /** * Remove an attribute * diff --git a/src/View/RenderableTrait.php b/src/View/RenderableTrait.php index 3f9b28f..1ca124a 100644 --- a/src/View/RenderableTrait.php +++ b/src/View/RenderableTrait.php @@ -5,6 +5,9 @@ use ArgumentCountError; use TypeError; +use function is_int; +use function is_scalar; + /** * Implements @see Renderable * @@ -45,6 +48,26 @@ public function set(string $name, $value) return $this; } + /** + * {@inheritdoc} + */ + public function with(array $attributes) + { + foreach ($attributes as $name => $value) { + if (!is_scalar($value)) { + throw new TypeError('The attribute value must be a scalar value.'); + } + + if (is_int($name)) { + $this->attributes[(string) $value] = true; + } else { + $this->attributes[$name] = $value; + } + } + + return $this; + } + /** * {@inheritdoc} */ diff --git a/tests/Aggregate/ArrayElementTest.php b/tests/Aggregate/ArrayElementTest.php index d4e4256..725339c 100644 --- a/tests/Aggregate/ArrayElementTest.php +++ b/tests/Aggregate/ArrayElementTest.php @@ -34,6 +34,7 @@ public function test_defaults() $element = new ArrayElement(new StringElement()); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertTrue($element->error()->empty()); $this->assertArrayNotHasKey(0, $element); $this->assertSame([], $element->value()); @@ -62,6 +63,7 @@ public function test_submit_empty() $element = new ArrayElement(new StringElement()); $this->assertTrue($element->submit(null)->valid()); + $this->assertFalse($element->failed()); $this->assertSame([], $element->value()); } @@ -90,6 +92,7 @@ public function test_submit_should_filter_empty_elements() $element = new ArrayElement(new StringElement()); $this->assertTrue($element->submit(['foo', null, [], 'bar'])->valid()); + $this->assertFalse($element->failed()); $this->assertSame([0 => 'foo', 3 => 'bar'], $element->value()); } @@ -101,6 +104,7 @@ public function test_submit_should_ignore_null_element_errors() $element = (new ArrayElementBuilder())->satisfy(function () { return 'error'; })->buildElement(); $this->assertFalse($element->submit([null, 'bar'])->valid()); + $this->assertTrue($element->failed()); $this->assertEquals([1 => 'error'], $element->error()->toArray()); } @@ -112,6 +116,7 @@ public function test_submit_scalar() $element = new ArrayElement(new StringElement()); $this->assertTrue($element->submit('foo')->valid()); + $this->assertFalse($element->failed()); $this->assertSame(['foo'], $element->value()); } @@ -123,6 +128,7 @@ public function test_submit_with_element_error() $element = new ArrayElement(new StringElement(new ConstraintValueValidator([new NotEqualTo('foo')]))); $this->assertFalse($element->submit(['foo', 'bar'])->valid()); + $this->assertTrue($element->failed()); $this->assertEquals(['foo', 'bar'], $element->value()); $this->assertEquals([0 => 'This value should not be equal to "foo".'], $element->error()->toArray()); } @@ -135,6 +141,7 @@ public function test_submit_with_transformer_error() $element = new ArrayElement(new StringElement(), new ClosureTransformer(function () { throw new Exception('My error'); })); $this->assertFalse($element->submit(['foo', 'bar'])->valid()); + $this->assertTrue($element->failed()); $this->assertSame([], $element->value()); $this->assertEquals('My error', $element->error()->global()); } diff --git a/tests/Aggregate/FormBuilderTest.php b/tests/Aggregate/FormBuilderTest.php index a163715..fdd9599 100644 --- a/tests/Aggregate/FormBuilderTest.php +++ b/tests/Aggregate/FormBuilderTest.php @@ -5,6 +5,7 @@ use Bdf\Form\Button\SubmitButtonBuilder; use Bdf\Form\Child\ChildBuilder; use Bdf\Form\Csrf\CsrfElement; +use Bdf\Form\Leaf\AnyElement; use Bdf\Form\Leaf\BooleanElement; use Bdf\Form\Leaf\Date\DateTimeChildBuilder; use Bdf\Form\Leaf\Date\DateTimeElement; @@ -37,6 +38,21 @@ protected function setUp(): void $this->builder = new FormBuilder(); } + /** + * + */ + public function test_any() + { + $this->assertInstanceOf(ChildBuilder::class, $this->builder->any('firstName')); + $this->assertInstanceOf(ChildBuilder::class, $this->builder->any('lastName')); + + $form = $this->builder->buildElement(); + + $this->assertInstanceOf(Form::class, $form); + $this->assertInstanceOf(AnyElement::class, $form['firstName']->element()); + $this->assertInstanceOf(AnyElement::class, $form['lastName']->element()); + } + /** * */ diff --git a/tests/Aggregate/FormTest.php b/tests/Aggregate/FormTest.php index 05fc498..fb22492 100644 --- a/tests/Aggregate/FormTest.php +++ b/tests/Aggregate/FormTest.php @@ -60,6 +60,7 @@ public function test_submit_success() ]); $this->assertTrue($this->form->valid()); + $this->assertFalse($this->form->failed()); $this->assertTrue($this->form->error()->empty()); $this->assertSame('John', $this->form['firstName']->element()->value()); @@ -86,6 +87,7 @@ public function test_submit_without_constraints_should_not_generate_the_value_on ]); $this->assertTrue($this->form->valid()); + $this->assertFalse($this->form->failed()); $this->assertTrue($this->form->error()->empty()); $this->assertFalse($called); @@ -105,6 +107,7 @@ public function test_submit_error_on_child() ]); $this->assertFalse($this->form->valid()); + $this->assertTrue($this->form->failed()); $this->assertFalse($this->form->error()->empty()); $this->assertEquals([ 'firstName' => 'This value is too short. It should have 2 characters or more.', @@ -121,6 +124,7 @@ public function test_submit_error_on_child() $this->assertSame('J', $this->form['firstName']->element()->value()); $this->assertFalse($this->form['firstName']->element()->valid()); + $this->assertTrue($this->form['firstName']->element()->failed()); $this->assertSame('S', $this->form['lastName']->element()->value()); $this->assertFalse($this->form['lastName']->element()->valid()); $this->assertSame(4, $this->form['id']->element()->value()); diff --git a/tests/Aggregate/RootFormTest.php b/tests/Aggregate/RootFormTest.php index 9117585..b75d6c0 100644 --- a/tests/Aggregate/RootFormTest.php +++ b/tests/Aggregate/RootFormTest.php @@ -210,6 +210,7 @@ public function test_delegation() $this->assertSame([], $form->value()); $this->assertSame(['value' => '42'], $form->httpValue()); $this->assertTrue($form->valid()); + $this->assertFalse($form->failed()); $this->assertTrue($form->error()->empty()); $this->assertNull($form->container()); $this->assertSame($form, $form->root()); diff --git a/tests/Aggregate/View/ArrayElementViewTest.php b/tests/Aggregate/View/ArrayElementViewTest.php index fa86a36..343ec50 100644 --- a/tests/Aggregate/View/ArrayElementViewTest.php +++ b/tests/Aggregate/View/ArrayElementViewTest.php @@ -43,6 +43,45 @@ public function test_getters() $this->assertEquals($elements, iterator_to_array($view)); } + /** + * + */ + public function test_setError() + { + $view = new ArrayElementView(ArrayElement::class, 'foo', ['aaa', 'bbb'], null, $elements = [ + $this->createMock(ElementViewInterface::class), + $this->createMock(ElementViewInterface::class), + ], true, [Count::class => ['max' => 5]]); + + $this->assertSame(ArrayElement::class, $view->type()); + $this->assertFalse($view->hasError()); + $this->assertNull($view->error()); + $this->assertTrue($view->required()); + $this->assertEquals([Count::class => ['max' => 5]], $view->constraints()); + $this->assertFalse($view->isCsv()); + $this->assertEquals(['aaa', 'bbb'], $view->value()); + + $view->setError('my error'); + $this->assertTrue($view->hasError()); + $this->assertSame('my error', $view->error()); + } + + /** + * + */ + public function test_setValue() + { + $view = new ArrayElementView(ArrayElement::class, 'foo', ['aaa', 'bbb'], null, $elements = [ + $this->createMock(ElementViewInterface::class), + $this->createMock(ElementViewInterface::class), + ], true, [Count::class => ['max' => 5]]); + + $this->assertEquals(['aaa', 'bbb'], $view->value()); + + $view->setValue(['ccc', 'ddd']); + $this->assertEquals(['ccc', 'ddd'], $view->value()); + } + /** * */ @@ -217,6 +256,7 @@ public function test_onError_without_error() $this->assertNull($out); } + /** * */ @@ -232,6 +272,20 @@ public function test_build_attributes() $this->assertSame([], $view->unset('foo')->attributes()); $this->assertSame(['foo' => 'bar', 'rab' => 'oof'], $view->foo('bar')->rab('oof')->attributes()); } + + /** + * + */ + public function test_with() + { + $view = new ArrayElementView(ArrayElement::class, 'foo', ['aaa', 'bbb'], null, $elements = [ + $this->createMock(ElementViewInterface::class), + $this->createMock(ElementViewInterface::class), + ], true, [Count::class => ['max' => 5]]); + + $this->assertSame(['foo' => 'bar', 'baz' => true], $view->with(['foo' => 'bar', 'baz'])->attributes()); + } + /** * */ diff --git a/tests/Aggregate/View/FormViewTest.php b/tests/Aggregate/View/FormViewTest.php index 345e9b2..df470e2 100644 --- a/tests/Aggregate/View/FormViewTest.php +++ b/tests/Aggregate/View/FormViewTest.php @@ -37,6 +37,25 @@ public function test_getters() $this->assertEquals($elements, iterator_to_array($view)); } + /** + * + */ + public function test_setError() + { + $view = new FormView(Form::class, null, $elements = [ + 'foo' => $this->createMock(ElementViewInterface::class), + 'bar' => $this->createMock(ElementViewInterface::class), + ]); + + $this->assertSame(Form::class, $view->type()); + $this->assertFalse($view->hasError()); + $this->assertNull($view->error()); + + $view->setError('my error'); + $this->assertTrue($view->hasError()); + $this->assertSame('my error', $view->error()); + } + /** * */ @@ -120,6 +139,54 @@ public function test_hasError_with_error_on_child_should_return_true() $this->assertTrue($view->hasError()); } + /** + * + */ + public function test_errors_with_error_on_child_should() + { + $view = new FormView(Form::class, null, [ + 'foo' => $child = $this->createMock(ElementViewInterface::class), + 'bar' => $this->createMock(ElementViewInterface::class), + ]); + + $child->expects($this->once())->method('hasError')->willReturn(true); + $child->expects($this->once())->method('error')->willReturn('my error'); + $this->assertEquals(['foo' => 'my error'], $view->errors()); + } + + /** + * + */ + public function test_errors_without_error() + { + $view = new FormView(Form::class, null, [ + 'foo' => $this->createMock(ElementViewInterface::class), + 'bar' => $this->createMock(ElementViewInterface::class), + ]); + + $this->assertEquals([], $view->errors()); + } + + /** + * + */ + public function test_errors_with_fieldset_errors() + { + $inner = new FormView(Form::class, null, [ + 'foo' => $foo = $this->createMock(ElementViewInterface::class), + 'bar' => $this->createMock(ElementViewInterface::class), + ]); + + $view = new FormView(Form::class, null, [ + 'foo' => $inner, + 'bar' => $this->createMock(ElementViewInterface::class), + ]); + + $foo->expects($this->exactly(2))->method('hasError')->willReturn(true); + $foo->expects($this->once())->method('error')->willReturn('my error'); + $this->assertEquals(['foo' => ['foo' => 'my error']], $view->errors()); + } + /** * */ diff --git a/tests/Child/ChildBuilderTest.php b/tests/Child/ChildBuilderTest.php index c4843f2..cb8357e 100644 --- a/tests/Child/ChildBuilderTest.php +++ b/tests/Child/ChildBuilderTest.php @@ -247,6 +247,24 @@ public function test_getter() $this->assertEquals('value', $child->element()->value()); } + /** + * + */ + public function test_getset() + { + $child = $this->builder->getset('prop')->buildChild(); + $child->setParent($form = new Form(new ChildrenCollection())); + + $target = ['prop' => 'value']; + $child->import($target); + $this->assertEquals('value', $child->element()->value()); + + $target = []; + $child->fill($target); + + $this->assertEquals(['prop' => 'value'], $target); + } + /** * */ diff --git a/tests/Csrf/CsrfElementBuilderTest.php b/tests/Csrf/CsrfElementBuilderTest.php index 04d838d..e121b37 100644 --- a/tests/Csrf/CsrfElementBuilderTest.php +++ b/tests/Csrf/CsrfElementBuilderTest.php @@ -82,7 +82,9 @@ public function test_invalidate() $value = $element->value()->getValue(); $this->assertTrue($element->submit($value)->valid()); + $this->assertFalse($element->failed()); $this->assertFalse($element->submit($value)->valid()); + $this->assertTrue($element->failed()); } /** diff --git a/tests/Csrf/CsrfElementTest.php b/tests/Csrf/CsrfElementTest.php index d05adb0..2371ac3 100644 --- a/tests/Csrf/CsrfElementTest.php +++ b/tests/Csrf/CsrfElementTest.php @@ -25,6 +25,7 @@ public function test_default() $element = new CsrfElement(); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertTrue($element->error()->empty()); $this->assertInstanceOf(CsrfToken::class, $element->value()); $this->assertEquals(CsrfElement::class, $element->value()->getId()); diff --git a/tests/Custom/CustomFormTest.php b/tests/Custom/CustomFormTest.php index 15d58f4..6fe5bb4 100644 --- a/tests/Custom/CustomFormTest.php +++ b/tests/Custom/CustomFormTest.php @@ -49,6 +49,7 @@ public function test_submit_success() { $this->assertSame($this->form, $this->form->submit(['firstName' => 'John', 'lastName' => 'Doe', 'birthDate' => (new \DateTime('1992-05-22'))->getTimestamp()])); $this->assertTrue($this->form->valid()); + $this->assertFalse($this->form->failed()); $person = $this->form->value(); @@ -108,6 +109,7 @@ public function test_submit_with_button() 'btn' => 'ok', ])); $this->assertTrue($this->form->valid()); + $this->assertFalse($this->form->failed()); $person = $this->form->value(); @@ -125,6 +127,7 @@ public function test_submit_error() { $this->assertSame($this->form, $this->form->submit(['firstName' => 'John'])); $this->assertFalse($this->form->valid()); + $this->assertTrue($this->form->failed()); $this->assertEquals(['lastName' => 'This value should not be blank.'], $this->form->error()->toArray()); } @@ -142,6 +145,7 @@ public function test_patch_success() $this->assertSame($this->form, $this->form->patch(['firstName' => 'Paul'])); $this->assertTrue($this->form->valid()); + $this->assertFalse($this->form->failed()); $person = $this->form->value(); @@ -234,6 +238,7 @@ public function test_functional_array_of_custom_form() ]); $this->assertTrue($array->valid()); + $this->assertFalse($array->failed()); $this->assertCount(2, $array); $this->assertContainsOnly(Person::class, $array->value()); @@ -548,12 +553,14 @@ public function configure(FormBuilderInterface $builder): void $form->submit(['foo' => 'bar']); $this->assertFalse($form->valid()); + $this->assertTrue($form->failed()); $this->assertEquals(['_token' => 'The CSRF token is invalid.'], $form->error()->toArray()); $form->disableCsrfValidation(); $form->submit(['foo' => 'bar']); $this->assertTrue($form->valid()); + $this->assertFalse($form->failed()); } } @@ -569,8 +576,8 @@ protected function configure(FormBuilderInterface $builder): void { $builder->generates(Person::class); - $builder->string('firstName')->required()->getter()->setter(); - $builder->string('lastName')->required()->getter()->setter(); + $builder->string('firstName')->required()->getset(); + $builder->string('lastName')->required()->getset(); $builder->integer('birthDate') ->raw() ->satisfy(new LessThan(time())) diff --git a/tests/Error/FormErrorTest.php b/tests/Error/FormErrorTest.php index a666f96..8813a3e 100644 --- a/tests/Error/FormErrorTest.php +++ b/tests/Error/FormErrorTest.php @@ -21,6 +21,8 @@ public function test_null() $this->assertInstanceOf(FormError::class, FormError::null()); $this->assertTrue(FormError::null()->empty()); $this->assertSame(FormError::null(), FormError::null()); + $this->assertNull(FormError::null()->get('foo')); + $this->assertSame(FormError::null(), FormError::null()->child('foo')); } /** @@ -35,6 +37,8 @@ public function test_message() $this->assertEquals('my error', $error->global()); $this->assertNull($error->code()); $this->assertEmpty($error->children()); + $this->assertNull($error->get('foo')); + $this->assertSame(FormError::null(), $error->child('foo')); } /** @@ -99,6 +103,10 @@ public function test_aggregate() $this->assertNull($error->global()); $this->assertNull($error->code()); $this->assertSame($errors, $error->children()); + $this->assertSame('child error', $error->get('child')); + $this->assertSame($errors['child'], $error->child('child')); + $this->assertNull($error->get('not_found')); + $this->assertSame(FormError::null(), $error->child('not_found')); } /** diff --git a/tests/Leaf/AnyElementBuilderTest.php b/tests/Leaf/AnyElementBuilderTest.php index 7e811da..666a4ce 100644 --- a/tests/Leaf/AnyElementBuilderTest.php +++ b/tests/Leaf/AnyElementBuilderTest.php @@ -155,6 +155,7 @@ public function test_choices() $element->submit('aaa'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('The value you selected is not a valid choice.', $element->error()->global()); } @@ -167,6 +168,7 @@ public function test_choices_custom_message() $element->submit('aaa'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('my error', $element->error()->global()); } } diff --git a/tests/Leaf/AnyElementTest.php b/tests/Leaf/AnyElementTest.php index 5e95d5e..515c9f9 100644 --- a/tests/Leaf/AnyElementTest.php +++ b/tests/Leaf/AnyElementTest.php @@ -31,6 +31,7 @@ public function test_default() $element = new AnyElement(); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertNull($element->value()); $this->assertNull($element->httpValue()); $this->assertTrue($element->error()->empty()); @@ -57,6 +58,7 @@ public function test_submit_null() $element = new AnyElement(); $this->assertTrue($element->submit(null)->valid()); + $this->assertFalse($element->failed()); $this->assertNull($element->value()); $this->assertTrue($element->error()->empty()); } @@ -104,6 +106,7 @@ public function test_submit_with_transformer_exception_ignored() ); $this->assertTrue($element->submit('aa')->valid()); + $this->assertFalse($element->failed()); $this->assertSame('aa', $element->value()); } @@ -152,6 +155,7 @@ public function test_patch_null_with_constraints_should_be_validated() $this->assertSame($element, $element->patch(null)); $this->assertSame('foo', $element->value()); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('This value is too short. It should have 5 characters or more.', $element->error()->global()); } diff --git a/tests/Leaf/BooleanElementTest.php b/tests/Leaf/BooleanElementTest.php index ef454ad..b7c45c5 100644 --- a/tests/Leaf/BooleanElementTest.php +++ b/tests/Leaf/BooleanElementTest.php @@ -31,6 +31,7 @@ public function test_default() $element = new BooleanElement(); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertNull($element->value()); $this->assertTrue($element->error()->empty()); } @@ -43,6 +44,7 @@ public function test_submit_success() $element = new BooleanElement(); $this->assertTrue($element->submit('1')->valid()); + $this->assertFalse($element->failed()); $this->assertTrue($element->value()); $this->assertTrue($element->error()->empty()); } diff --git a/tests/Leaf/BooleanStringElementTest.php b/tests/Leaf/BooleanStringElementTest.php index ffcdfda..8617663 100644 --- a/tests/Leaf/BooleanStringElementTest.php +++ b/tests/Leaf/BooleanStringElementTest.php @@ -28,6 +28,7 @@ public function test_default() $element = new BooleanStringElement(); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertNull($element->value()); $this->assertTrue($element->error()->empty()); } @@ -113,6 +114,7 @@ public function test_submit_with_constraint() $element = new BooleanStringElement(new ConstraintValueValidator([new NotBlank()])); $this->assertFalse($element->submit('invalid')->valid()); + $this->assertTrue($element->failed()); $this->assertNull($element->value()); $this->assertEquals('This value should not be blank.', $element->error()->global()); diff --git a/tests/Leaf/Date/DateTimeElementBuilderTest.php b/tests/Leaf/Date/DateTimeElementBuilderTest.php index 8753ace..42c853b 100644 --- a/tests/Leaf/Date/DateTimeElementBuilderTest.php +++ b/tests/Leaf/Date/DateTimeElementBuilderTest.php @@ -69,6 +69,7 @@ public function test_default_transformer_error() $element->submit('invalid'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('This value is not a valid datetime.', $element->error()->global()); $this->assertEquals('INVALID_DATETIME_ERROR', $element->error()->code()); } diff --git a/tests/Leaf/Date/DateTimeElementTest.php b/tests/Leaf/Date/DateTimeElementTest.php index 807d3f6..aa5920e 100644 --- a/tests/Leaf/Date/DateTimeElementTest.php +++ b/tests/Leaf/Date/DateTimeElementTest.php @@ -35,6 +35,7 @@ public function test_default() $element = new DateTimeElement(); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertNull($element->value()); $this->assertNull($element->httpValue()); $this->assertTrue($element->error()->empty()); @@ -48,6 +49,7 @@ public function test_submit_success() $element = new DateTimeElement(); $this->assertTrue($element->submit('2020-12-17T10:40:00+1000')->valid()); + $this->assertFalse($element->failed()); $this->assertEquals(new DateTime('2020-12-17 10:40:00', new DateTimeZone('+1000')), $element->value()); $this->assertTrue($element->error()->empty()); } @@ -264,6 +266,7 @@ public function test_patch_null_with_constraints_should_be_validated() $this->assertSame($element, $element->patch(null)); $this->assertEquals(new DateTime('2020-10-14 15:00:00'), $element->value()); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('This value should be greater than Oct 14, 2030, 3:00 PM.', str_replace("\u{202F}", ' ', $element->error()->global())); } diff --git a/tests/Leaf/FloatElementBuilderTest.php b/tests/Leaf/FloatElementBuilderTest.php index 9add9c4..5a9cc89 100644 --- a/tests/Leaf/FloatElementBuilderTest.php +++ b/tests/Leaf/FloatElementBuilderTest.php @@ -138,6 +138,7 @@ public function test_default_transformer_error() $element->submit('invalid'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('The value is not a valid number.', $element->error()->global()); $this->assertEquals('INVALID_NUMBER_ERROR', $element->error()->code()); } @@ -320,6 +321,7 @@ public function test_choices() $element->submit('14.7'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('The value you selected is not a valid choice.', $element->error()->global()); $element->submit('45.6'); @@ -335,6 +337,7 @@ public function test_choices_custom_message() $element->submit('14.7'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('my error', $element->error()->global()); } } diff --git a/tests/Leaf/FloatElementTest.php b/tests/Leaf/FloatElementTest.php index 029b0ec..7a0e3f9 100644 --- a/tests/Leaf/FloatElementTest.php +++ b/tests/Leaf/FloatElementTest.php @@ -33,6 +33,7 @@ public function test_default() $element = new FloatElement(); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertNull($element->value()); $this->assertNull($element->httpValue()); $this->assertTrue($element->error()->empty()); @@ -70,6 +71,7 @@ public function test_submit_with_constraint() $element = new FloatElement(new ConstraintValueValidator([new LessThan(2)])); $this->assertFalse($element->submit('5.1')->valid()); + $this->assertTrue($element->failed()); $this->assertSame(5.1, $element->value()); $this->assertEquals('This value should be less than 2.', $element->error()->global()); @@ -153,6 +155,7 @@ public function test_patch_null_with_constraints_should_be_validated() $this->assertSame($element, $element->patch(null)); $this->assertSame(1.23, $element->value()); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('This value should be greater than or equal to 3.', $element->error()->global()); } diff --git a/tests/Leaf/IntegerElementBuilderTest.php b/tests/Leaf/IntegerElementBuilderTest.php index 149b843..036d913 100644 --- a/tests/Leaf/IntegerElementBuilderTest.php +++ b/tests/Leaf/IntegerElementBuilderTest.php @@ -139,6 +139,7 @@ public function test_default_transformer_error() $element->submit('invalid'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('The value is not a valid number.', $element->error()->global()); $this->assertEquals('INVALID_NUMBER_ERROR', $element->error()->code()); } @@ -287,6 +288,7 @@ public function test_choices() $element->submit(22); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('The value you selected is not a valid choice.', $element->error()->global()); } @@ -299,6 +301,7 @@ public function test_choices_custom_message() $element->submit('14'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('my error', $element->error()->global()); } } diff --git a/tests/Leaf/IntegerElementTest.php b/tests/Leaf/IntegerElementTest.php index e3b8d59..e59773b 100644 --- a/tests/Leaf/IntegerElementTest.php +++ b/tests/Leaf/IntegerElementTest.php @@ -33,6 +33,7 @@ public function test_default() $element = new IntegerElement(); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertNull($element->value()); $this->assertNull($element->httpValue()); $this->assertTrue($element->error()->empty()); @@ -70,6 +71,7 @@ public function test_submit_with_constraint() $element = new IntegerElement(new ConstraintValueValidator([new LessThan(2)])); $this->assertFalse($element->submit('5')->valid()); + $this->assertTrue($element->failed()); $this->assertSame(5, $element->value()); $this->assertEquals('This value should be less than 2.', $element->error()->global()); @@ -153,6 +155,7 @@ public function test_patch_null_with_constraints_should_be_validated() $this->assertSame($element, $element->patch(null)); $this->assertSame(1, $element->value()); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('This value should be greater than or equal to 3.', $element->error()->global()); } diff --git a/tests/Leaf/LeafRootElementTest.php b/tests/Leaf/LeafRootElementTest.php index 69b2d6d..f976217 100644 --- a/tests/Leaf/LeafRootElementTest.php +++ b/tests/Leaf/LeafRootElementTest.php @@ -84,8 +84,9 @@ public function test_valid() $element = $this->createMock(ElementInterface::class); $root = new LeafRootElement($element); - $element->expects($this->once())->method('valid')->willReturn(true); + $element->expects($this->exactly(2))->method('valid')->willReturn(true); $this->assertTrue($root->valid()); + $this->assertFalse($root->failed()); } /** diff --git a/tests/Leaf/StringElementBuilderTest.php b/tests/Leaf/StringElementBuilderTest.php index 47d2bf7..257065f 100644 --- a/tests/Leaf/StringElementBuilderTest.php +++ b/tests/Leaf/StringElementBuilderTest.php @@ -177,6 +177,7 @@ public function test_choices() $element->submit('aaa'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('The value you selected is not a valid choice.', $element->error()->global()); } @@ -189,6 +190,7 @@ public function test_choices_custom_message() $element->submit('aaa'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('my error', $element->error()->global()); } } diff --git a/tests/Leaf/StringElementTest.php b/tests/Leaf/StringElementTest.php index ecc3a24..7c910ee 100644 --- a/tests/Leaf/StringElementTest.php +++ b/tests/Leaf/StringElementTest.php @@ -31,6 +31,7 @@ public function test_default() $element = new StringElement(); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertNull($element->value()); $this->assertNull($element->httpValue()); $this->assertTrue($element->error()->empty()); @@ -68,6 +69,7 @@ public function test_submit_with_constraint() $element = new StringElement(new ConstraintValueValidator([new Length(['max' => 2])])); $this->assertFalse($element->submit('hello')->valid()); + $this->assertTrue($element->failed()); $this->assertSame('hello', $element->value()); $this->assertEquals('This value is too long. It should have 2 characters or less.', $element->error()->global()); @@ -151,6 +153,7 @@ public function test_patch_null_with_constraints_should_be_validated() $this->assertSame($element, $element->patch(null)); $this->assertSame('foo', $element->value()); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('This value is too short. It should have 5 characters or more.', $element->error()->global()); } diff --git a/tests/Leaf/View/BooleanElementViewTest.php b/tests/Leaf/View/BooleanElementViewTest.php index fbbc5b3..74ec767 100644 --- a/tests/Leaf/View/BooleanElementViewTest.php +++ b/tests/Leaf/View/BooleanElementViewTest.php @@ -32,6 +32,43 @@ public function test_getters() $this->assertSame('true', $view->httpValue()); } + /** + * + */ + public function test_setValue() + { + $view = new BooleanElementView(BooleanElement::class, 'foo', 'true', 'true', true, 'my error'); + + $this->assertSame('foo', $view->name()); + $this->assertSame('true', $view->value()); + + $view->setValue('false'); + $this->assertSame('false', $view->value()); + } + + /** + * + */ + public function test_setError() + { + $view = new BooleanElementView(BooleanElement::class, 'foo', 'true', 'true', true, null); + + $this->assertSame(BooleanElement::class, $view->type()); + $this->assertSame('foo', $view->name()); + $this->assertSame('true', $view->value()); + $this->assertNull($view->error()); + $this->assertFalse($view->hasError()); + $this->assertFalse($view->required()); + $this->assertEquals([], $view->constraints()); + $this->assertSame([], $view->attributes()); + $this->assertTrue($view->checked()); + $this->assertSame('true', $view->httpValue()); + + $view->setError('my error'); + $this->assertEquals('my error', $view->error()); + $this->assertTrue($view->hasError()); + } + /** * */ @@ -103,6 +140,16 @@ public function test_build_attributes() $this->assertSame(['foo' => 'bar', 'rab' => 'oof'], $view->foo('bar')->rab('oof')->attributes()); } + /** + * + */ + public function test_with() + { + $view = new BooleanElementView(BooleanElement::class, 'foo', 'true', 'true', true, null); + + $this->assertSame(['foo' => 'bar', 'baz' => true], $view->with(['foo' => 'bar', 'baz'])->attributes()); + } + /** * */ diff --git a/tests/Leaf/View/SimpleElementViewTest.php b/tests/Leaf/View/SimpleElementViewTest.php index 31c73f4..a63867f 100644 --- a/tests/Leaf/View/SimpleElementViewTest.php +++ b/tests/Leaf/View/SimpleElementViewTest.php @@ -30,6 +30,40 @@ public function test_getters() $this->assertSame([], $view->attributes()); } + /** + * + */ + public function test_setError() + { + $view = new SimpleElementView(StringElement::class, 'foo', 'bar', null, true, [Length::class => ['min' => 5]]); + + $this->assertSame(StringElement::class, $view->type()); + $this->assertSame('foo', $view->name()); + $this->assertSame('bar', $view->value()); + $this->assertNull($view->error()); + $this->assertFalse($view->hasError()); + $this->assertTrue($view->required()); + $this->assertEquals([Length::class => ['min' => 5]], $view->constraints()); + $this->assertSame([], $view->attributes()); + + $view->setError('my error'); + $this->assertSame('my error', $view->error()); + $this->assertTrue($view->hasError()); + } + + /** + * + */ + public function test_setValue() + { + $view = new SimpleElementView(StringElement::class, 'foo', 'bar', null, true, [Length::class => ['min' => 5]]); + + $this->assertSame('bar', $view->value()); + + $view->setValue('baz'); + $this->assertSame('baz', $view->value()); + } + /** * */ @@ -99,6 +133,28 @@ public function test_build_attributes() $this->assertSame(['foo' => 'bar', 'rab' => 'oof'], $view->foo('bar')->rab('oof')->attributes()); } + /** + * + */ + public function test_with() + { + $view = new SimpleElementView(StringElement::class, 'foo', 'bar', null, true, [Length::class => ['min' => 5]]); + + $this->assertSame(['foo' => 'bar', 'baz' => true], $view->with(['foo' => 'bar', 'baz'])->attributes()); + } + + /** + * + */ + public function test_with_invalid_type() + { + $this->expectException(\TypeError::class); + + $view = new SimpleElementView(StringElement::class, 'foo', 'bar', null, true, [Length::class => ['min' => 5]]); + + $view->with(['foo' => 'bar', new \stdClass()]); + } + /** * */ diff --git a/tests/Leaf/View/SimpleFieldHtmlRendererTest.php b/tests/Leaf/View/SimpleFieldHtmlRendererTest.php index 9fd6714..9fc8cdf 100644 --- a/tests/Leaf/View/SimpleFieldHtmlRendererTest.php +++ b/tests/Leaf/View/SimpleFieldHtmlRendererTest.php @@ -31,6 +31,17 @@ public function test_render_simple() $this->assertEquals('', $renderer->render($view, ['type' => 'email'])); } + /** + * + */ + public function test_render_allow_override_required() + { + $view = new SimpleElementView(StringElement::class, 'foo', 'bar', null, true, [Length::class => ['min' => 5]]); + $renderer = new SimpleFieldHtmlRenderer(); + + $this->assertEquals('', $renderer->render($view, ['type' => 'email', 'required' => false])); + } + /** * @dataProvider provideType */ diff --git a/tests/Phone/PhoneElementBuilderTest.php b/tests/Phone/PhoneElementBuilderTest.php index 017f848..309aade 100644 --- a/tests/Phone/PhoneElementBuilderTest.php +++ b/tests/Phone/PhoneElementBuilderTest.php @@ -43,6 +43,7 @@ public function test_must_validate_number_by_default() $element->submit('1'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('The phone number is not valid.', $element->error()->global()); $this->assertEquals('INVALID_PHONE_NUMBER_ERROR', $element->error()->code()); } @@ -69,6 +70,7 @@ public function test_validateNumber() $element->submit('1'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('my error', $element->error()->global()); $this->assertEquals('INVALID_PHONE_NUMBER_ERROR', $element->error()->code()); } @@ -82,6 +84,7 @@ public function test_errorMessage() $element->submit('1'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('my error', $element->error()->global()); $this->assertEquals('INVALID_PHONE_NUMBER_ERROR', $element->error()->code()); } diff --git a/tests/Phone/PhoneElementTest.php b/tests/Phone/PhoneElementTest.php index 77c38b9..7117ffd 100644 --- a/tests/Phone/PhoneElementTest.php +++ b/tests/Phone/PhoneElementTest.php @@ -29,6 +29,7 @@ public function test_default() $element = new PhoneElement(); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertNull($element->value()); $this->assertTrue($element->error()->empty()); } @@ -41,6 +42,7 @@ public function test_submit_success() $element = new PhoneElement(); $this->assertTrue($element->submit('+330142563698')->valid()); + $this->assertFalse($element->failed()); $this->assertInstanceOf(PhoneNumber::class, $element->value()); $this->assertEquals(33, $element->value()->getCountryCode()); $this->assertEquals('142563698', $element->value()->getNationalNumber()); @@ -290,5 +292,22 @@ public function test_view() $this->assertEquals('', (string) $view); $this->assertEquals('tel', $view->name()); $this->assertFalse($view->hasError()); + $this->assertFalse($view->required()); + } + + /** + * + */ + public function test_view_required() + { + $element = (new PhoneElementBuilder())->required()->buildElement(); + + $view = $element->view(HttpFieldPath::named('tel')); + + $this->assertEquals(PhoneElement::class, $view->type()); + $this->assertEquals('', (string) $view); + $this->assertEquals('tel', $view->name()); + $this->assertTrue($view->required()); + $this->assertFalse($view->hasError()); } } diff --git a/tests/Util/ValidatorBuilderTraitTest.php b/tests/Util/ValidatorBuilderTraitTest.php index ff5b696..7b6394c 100644 --- a/tests/Util/ValidatorBuilderTraitTest.php +++ b/tests/Util/ValidatorBuilderTraitTest.php @@ -153,6 +153,7 @@ public function test_ignoreTransformerException() $element->submit('foo'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('my error', $element->error()->global()); $this->assertEquals('TRANSFORM_ERROR', $element->error()->code()); } @@ -168,6 +169,7 @@ public function test_transformerErrorMessage() $element->submit('foo'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('custom message', $element->error()->global()); $this->assertEquals('TRANSFORM_ERROR', $element->error()->code()); } @@ -183,6 +185,7 @@ public function test_transformerErrorCode() $element->submit('foo'); $this->assertFalse($element->valid()); + $this->assertTrue($element->failed()); $this->assertEquals('my error', $element->error()->global()); $this->assertEquals('CUSTOM_ERROR', $element->error()->code()); }