Skip to content

Commit

Permalink
Select element implementation (#10)
Browse files Browse the repository at this point in the history
* Implement required select field

* Implement <select> option matching, closes #3
  • Loading branch information
g105b authored Jan 1, 2020
1 parent ef1d4b1 commit a771f4f
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/DefaultValidationRules.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use Gt\DomValidation\Rule\Pattern;
use Gt\DomValidation\Rule\Required;
use Gt\DomValidation\Rule\SelectElement;
use Gt\DomValidation\Rule\TypeDate;
use Gt\DomValidation\Rule\TypeEmail;
use Gt\DomValidation\Rule\TypeNumber;
Expand All @@ -17,6 +18,7 @@ protected function setRuleList() {
new TypeEmail(),
new TypeUrl(),
new TypeDate(),
new SelectElement(),
];
}
}
4 changes: 3 additions & 1 deletion src/Rule/Rule.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ abstract class Rule {
* equals sign (e.g. "type=email"). For attributes without a value, pass the
* attribute name on its own (e.g. "required").
*/
protected $attributes;
protected $attributes = [
"name"
];

public function getAttributes():array {
return $this->attributes;
Expand Down
44 changes: 44 additions & 0 deletions src/Rule/SelectElement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php
namespace Gt\DomValidation\Rule;

use DOMElement;

class SelectElement extends Rule {
public function isValid(DOMElement $element, string $value):bool {
$availableValues = [];

if($element->tagName !== "select") {
return true;
}

if($value === "") {
return true;
}

$optionElementList = $element->getElementsByTagName("option");
for($i = 0, $len = $optionElementList->length; $i < $len; $i++) {
$option = $optionElementList->item($i);

if($option->hasAttribute("value")) {
$optionValue = $option->getAttribute("value");
}
else {
$optionValue = $option->nodeValue;
}

if($optionValue !== "") {
$availableValues []= $optionValue;
}
}

if(!in_array($value, $availableValues)) {
return false;
}

return true;
}

public function getHint(DOMElement $element, string $value):string {
return "This field's value must match one of the available options";
}
}
5 changes: 3 additions & 2 deletions src/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ public function validate(DOMElement $form, array $input):void {
foreach($this->rules->getAttributeRuleList() as $attrString => $ruleArray) {
/** @var Rule[] $ruleArray */

$xpath = new DOMXPath($form->ownerDocument);
$cssSelector = "[$attrString]";

$xpath = new DOMXPath($form->ownerDocument);
$inputElementList = $xpath->query(
new Translator("[$attrString]")
new Translator($cssSelector)
);

for($i = 0, $len = $inputElementList->length; $i < $len; $i++) {
Expand Down
31 changes: 31 additions & 0 deletions test/phpunit/Helper/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,37 @@ class Helper {
</form>
HTML;

const HTML_SELECT = <<<HTML
<!doctype html>
<form method="post">
<label>
<span>Currency</span>
<select name="currency" required>
<option></option>
<option value="GBP">£ Pound (GBP)</option>
<option value="USD">$ Dollar (USD)</option>
<option value="EUR">€ Euro (EUR)</option>
</select>
</label>
<label>
<span>Sort order</span>
<select name="sort">
<option>Ascending</option>
<option>Descending</option>
</select>
</label>
<label>
<span>Show connections</span>
<select name="connections">
<option value="">All</option>
<option value="native">Native only</option>
<option value="complex">Complex only</option>
</select>
</label>
</form>
HTML;


}
136 changes: 136 additions & 0 deletions test/phpunit/Rule/SelectElementTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php
namespace Gt\DomValidation\Test\Rule;

use Gt\DomValidation\Test\DomValidationTestCase;
use Gt\DomValidation\Test\Helper\Helper;
use Gt\DomValidation\ValidationException;
use Gt\DomValidation\Validator;

class SelectElementTest extends DomValidationTestCase {
public function testSelect() {
$form = self::getFormFromHtml(Helper::HTML_SELECT);
$validator = new Validator();

$exception = null;

try {
$validator->validate($form, [
"currency" => "GBP",
]);
}
catch(ValidationException $exception) {}

self::assertNull($exception);
}

public function testSelectMissingRequired() {
$form = self::getFormFromHtml(Helper::HTML_SELECT);
$validator = new Validator();

try {
$validator->validate($form, [
"currency" => "",
]);
}
catch(ValidationException $exception) {
$errorArray = iterator_to_array($validator->getLastErrorList());
self::assertCount(1, $errorArray);
$currencyErrorArray = $errorArray["currency"];
self::assertContains(
"This field is required",
$currencyErrorArray
);
}
}

public function testSelectTextContent() {
$form = self::getFormFromHtml(Helper::HTML_SELECT);
$validator = new Validator();

$exception = null;

try {
$validator->validate($form, [
"currency" => "USD",
"sort" => "Descending",
]);
}
catch(ValidationException $exception) {}

self::assertNull($exception);
}

public function testSelectTextContentInvalid() {
$form = self::getFormFromHtml(Helper::HTML_SELECT);
$validator = new Validator();

try {
$validator->validate($form, [
"currency" => "USD",
"sort" => "Random", // This <option> does not exist
]);
}
catch(ValidationException $exception) {
$errorArray = iterator_to_array($validator->getLastErrorList());
self::assertCount(1, $errorArray);
$currencyErrorArray = $errorArray["sort"];
self::assertContains(
"This field's value must match one of the available options",
$currencyErrorArray
);
}
}

public function testSelectValueInvalid() {
$form = self::getFormFromHtml(Helper::HTML_SELECT);
$validator = new Validator();

try {
$validator->validate($form, [
"currency" => "USD",
"connections" => "All", // This is invalid
// There is an <option> with "All" as its text content, but not with this value.
]);
}
catch(ValidationException $exception) {
$errorArray = iterator_to_array($validator->getLastErrorList());
self::assertCount(1, $errorArray);
$currencyErrorArray = $errorArray["connections"];
self::assertContains(
"This field's value must match one of the available options",
$currencyErrorArray
);
}
}

public function testSelectTwoInvalidOptionsAndOneMissing() {
$form = self::getFormFromHtml(Helper::HTML_SELECT);
$validator = new Validator();

try {
$validator->validate($form, [
"connections" => "none",
"sort" => "random",
]);
}
catch(ValidationException $exception) {
$errorArray = iterator_to_array($validator->getLastErrorList());
self::assertCount(3, $errorArray);
$currencyErrorArray = $errorArray["currency"];
$sortErrorArray = $errorArray["sort"];
$connectionsErrorArray = $errorArray["connections"];
self::assertContains(
"This field is required",
$currencyErrorArray
);
self::assertContains(
"This field's value must match one of the available options",
$sortErrorArray
);
self::assertContains(
"This field's value must match one of the available options",
$connectionsErrorArray
);
}
}
}

0 comments on commit a771f4f

Please sign in to comment.