From 3fd24a9d1550fcedaa111d530c7f35412324d317 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Fri, 10 Nov 2023 11:28:18 +0000 Subject: [PATCH] Component binder (#472) * build: upgrade dom requirement and loosen version range * docs: update examples * feature: trim whitespace when there are only template children closes #363 * maintenance: phpstorm analysis improvements * tweak: remove data-element attribute * feature: implement ComponentBinder and abstract Binder class for #470 * fix: bindable cache allows nested nullable objects closes #474 * refactor: all domtemplate classes set their dependencies outside of the constructor for #470 * tweak: stricter reflection type checking * wip: better exception on missing list element * tweak: handle nullable iterables closes #473 * tweak: handle binding of outer list item * tidy: improve types and coverage * ci: php 8.2 * ci: phpstan level 6 --- .github/workflows/ci.yml | 6 +- src/BindableCache.php | 46 ++-- src/Binder.php | 53 +++++ src/CommentIni.php | 4 +- src/ComponentBinder.php | 56 +++++ ...omponentDoesNotContainContextException.php | 4 + src/DocumentBinder.php | 62 +++--- src/ElementBinder.php | 16 +- src/HTMLAttributeBinder.php | 10 +- src/ListBinder.php | 54 +++-- src/ListElementCollection.php | 13 +- src/TableBinder.php | 48 +++-- test/phpunit/BindableCacheTest.php | 24 +++ test/phpunit/ComponentBinderTest.php | 74 +++++++ test/phpunit/DocumentBinderTest.php | 163 +++++++++++++- test/phpunit/ListBinderTest.php | 204 ++++++++++++------ test/phpunit/ListElementCollectionTest.php | 29 +++ test/phpunit/TableBinderTest.php | 73 +++++-- test/phpunit/TestHelper/HTMLPageContent.php | 49 +++++ test/phpunit/TestHelper/Model/Customer.php | 6 +- test/phpunit/phpunit.xml | 36 ++-- 21 files changed, 823 insertions(+), 207 deletions(-) create mode 100644 src/Binder.php create mode 100644 src/ComponentBinder.php create mode 100644 src/ComponentDoesNotContainContextException.php create mode 100644 test/phpunit/ComponentBinderTest.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 47c3096..98e8699 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - name: Composer install uses: php-actions/composer@v6 with: - php_version: 8.1 + php_version: 8.2 - name: Archive build run: mkdir /tmp/github-actions/ && tar -cvf /tmp/github-actions/build.tar ./ @@ -50,7 +50,7 @@ jobs: env: XDEBUG_MODE: cover with: - php_version: 8.1 + php_version: 8.2 php_extensions: xdebug configuration: test/phpunit/phpunit.xml bootstrap: vendor/autoload.php @@ -97,7 +97,9 @@ jobs: - name: PHP Static Analysis uses: php-actions/phpstan@v3 with: + php_version: 8.2 path: src/ + level: 6 remove_old_artifacts: runs-on: ubuntu-latest diff --git a/src/BindableCache.php b/src/BindableCache.php index 750d867..17f45b3 100644 --- a/src/BindableCache.php +++ b/src/BindableCache.php @@ -8,6 +8,7 @@ use ReflectionNamedType; use ReflectionObject; use ReflectionProperty; +use ReflectionType; use stdClass; use Stringable; @@ -69,8 +70,7 @@ public function isBindable(object $object):bool { foreach($refAttributes as $refAttr) { $bindKey = $this->getBindKey($refAttr, $refMethod); - $attributeCache[$bindKey] = fn(object $object):null|iterable|string - => $this->nullableStringOrIterable($object->$methodName()); + $attributeCache[$bindKey] = fn(object $object):null|iterable|string => $this->nullableStringOrIterable($object->$methodName()); if(class_exists($refReturnName)) { $cacheObjectKeys[$bindKey] = $refReturnName; } @@ -91,11 +91,17 @@ public function isBindable(object $object):bool { elseif($refProp->isPublic()) { $bindKey = $propName; - /** @var ?ReflectionNamedType $refType */ + $refTypeName = null; $refType = $refProp->getType(); - $refTypeName = $refType?->getName(); - $attributeCache[$bindKey] - = fn(object $object, $key):null|iterable|string => isset($object->$key) ? $this->nullableStringOrIterable($object->$key) : null; + if($refType instanceof ReflectionNamedType) { + $refTypeName = $refType->getName(); + } + $attributeCache[$bindKey] = + fn(object $object, $key):null|iterable|string => + isset($object->$key) + ? $this->nullableStringOrIterable($object->$key) + : null; + if(class_exists($refTypeName)) { $cacheObjectKeys[$bindKey] = $refTypeName; } @@ -143,9 +149,15 @@ private function expandObjects(array $cache, array $objectKeys):array { return $cache; } - /** @return array */ - public function convertToKvp(object $object):array { + /** + * @param object|array $object + * @return array + */ + public function convertToKvp(object|array $object):array { $kvp = []; + if(is_array($object)) { + return $object; + } if($object instanceof stdClass) { foreach(get_object_vars($object) as $key => $value) { @@ -178,10 +190,21 @@ public function convertToKvp(object $object):array { // TODO: This "get*()" function should not be hard coded here - it should load the appropriate // Bind/BindGetter by matching the correct Attribute. $bindFunc = "get" . ucfirst($propName); - $objectToExtract = $objectToExtract->$propName ?? $objectToExtract->$bindFunc(); + if($objectToExtract) { + if(property_exists($objectToExtract, $propName)) { + $objectToExtract = $objectToExtract->$propName; + } + elseif(method_exists($objectToExtract, $bindFunc)) { + $objectToExtract = $objectToExtract->$bindFunc(); + } + } + } + + $value = null; + if($objectToExtract) { + $value = $closure($objectToExtract, $deepestKey); } - $value = $closure($objectToExtract, $deepestKey); if(is_null($value)) { $kvp[$key] = null; } @@ -200,8 +223,7 @@ public function convertToKvp(object $object):array { private function getBindAttributes(ReflectionMethod|ReflectionProperty $ref):array { return array_filter( $ref->getAttributes(), - fn(ReflectionAttribute $refAttr) => - $refAttr->getName() === Bind::class + fn(ReflectionAttribute $refAttr) => $refAttr->getName() === Bind::class || $refAttr->getName() === BindGetter::class ); } diff --git a/src/Binder.php b/src/Binder.php new file mode 100644 index 0000000..8cffba7 --- /dev/null +++ b/src/Binder.php @@ -0,0 +1,53 @@ + $listData + */ + abstract public function bindList( + iterable $listData, + ?Element $context = null, + ?string $templateName = null + ):int; + + /** @param iterable $listData */ + abstract public function bindListCallback( + iterable $listData, + callable $callback, + ?Element $context = null, + ?string $templateName = null + ):int; +} diff --git a/src/CommentIni.php b/src/CommentIni.php index 7861920..b4cbc91 100644 --- a/src/CommentIni.php +++ b/src/CommentIni.php @@ -36,7 +36,9 @@ public function __construct( $data = trim($commentNode->data); try { - $ini = parse_ini_string($data, true); +// We know that sometimes this data will not be correct ini format, and it might actually be a textual comment. +// Therefore, we must suppress the warning that is emitted by parse_ini_string: + $ini = @parse_ini_string($data, true); $commentNodeToRemove = $commentNode; } catch(Throwable) { diff --git a/src/ComponentBinder.php b/src/ComponentBinder.php new file mode 100644 index 0000000..cce543e --- /dev/null +++ b/src/ComponentBinder.php @@ -0,0 +1,56 @@ +componentElement = $componentElement; + } + + public function bindList( + iterable $listData, + ?Element $context = null, + ?string $templateName = null + ):int { + if($context) { + $this->checkElementContainedWithinComponent($context); + } + else { + $context = $this->componentElement; + } + + return parent::bindList($listData, $context, $templateName); + } + + protected function bind( + ?string $key, + mixed $value, + ?Element $context = null + ):void { + if($context) { + $this->checkElementContainedWithinComponent($context); + } + else { + $context = $this->componentElement; + } + + if(is_callable($value) && !is_string($value)) { + $value = call_user_func($value); + } + + $this->elementBinder->bind($key, $value, $context); + $this->placeholderBinder->bind($key, $value, $context); + } + + private function checkElementContainedWithinComponent(Element $context):void { + if($this->componentElement !== $context && !$this->componentElement->contains($context)) { + throw new ComponentDoesNotContainContextException( + "<{$this->componentElement->tagName}> does not contain requested <$context->tagName>." + ); + } + } +} diff --git a/src/ComponentDoesNotContainContextException.php b/src/ComponentDoesNotContainContextException.php new file mode 100644 index 0000000..0b8772c --- /dev/null +++ b/src/ComponentDoesNotContainContextException.php @@ -0,0 +1,4 @@ + $config - */ public function __construct( - private readonly Document $document, - private array $config = [], - ?ElementBinder $elementBinder = null, - ?PlaceholderBinder $placeholderBinder = null, - ?TableBinder $tableBinder = null, - ?ListBinder $listBinder = null, - ?ListElementCollection $templateCollection = null, - ?BindableCache $bindableCache = null - ) { - $this->templateCollection = $templateCollection ?? new ListElementCollection($document); - $this->elementBinder = $elementBinder ?? new ElementBinder(); - $this->placeholderBinder = $placeholderBinder ?? new PlaceholderBinder(); - $this->tableBinder = $tableBinder ?? new TableBinder($this->templateCollection); - $this->listBinder = $listBinder ?? new ListBinder($this->templateCollection); - $this->bindableCache = $bindableCache ?? new BindableCache(); - -// This is temporary, to suppress PHPStan's complaints for declaring a variable -// without using it. There are plans to use the config variable, but it is -// currently not yet used, and this technique prevents the constructor -// parameters from changing over time. - if(!$this->config) { - $this->config = []; - } + protected readonly Document $document, + ) {} + + public function setDependencies( + ElementBinder $elementBinder, + PlaceholderBinder $placeholderBinder, + TableBinder $tableBinder, + ListBinder $listBinder, + ListElementCollection $listElementCollection, + BindableCache $bindableCache, + ):void { + $this->elementBinder = $elementBinder; + $this->placeholderBinder = $placeholderBinder; + $this->tableBinder = $tableBinder; + $this->listBinder = $listBinder; + $this->templateCollection = $listElementCollection; + $this->bindableCache = $bindableCache; } /** @@ -87,7 +78,7 @@ public function bindData( } } - foreach($kvp as $key => $value) { + foreach($kvp ?? [] as $key => $value) { $this->bindKeyValue($key, $value, $context); } } @@ -163,7 +154,7 @@ public function cleanupDocument():void { } } - private function bind( + protected function bind( ?string $key, mixed $value, ?Element $context = null @@ -177,7 +168,6 @@ private function bind( } $this->elementBinder->bind($key, $value, $context); - $this->placeholderBinder->bind($key, $value, $context); } private function isIndexedArray(mixed $data):bool { diff --git a/src/ElementBinder.php b/src/ElementBinder.php index 592bb7b..5dc644e 100644 --- a/src/ElementBinder.php +++ b/src/ElementBinder.php @@ -8,14 +8,14 @@ class ElementBinder { private HTMLAttributeCollection $htmlAttributeCollection; private PlaceholderBinder $placeholderBinder; - public function __construct( - ?HTMLAttributeBinder $htmlAttributeBinder = null, - ?HTMLAttributeCollection $htmlAttributeCollection = null, - ?PlaceholderBinder $placeholderBinder = null, - ) { - $this->htmlAttributeBinder = $htmlAttributeBinder ?? new HTMLAttributeBinder(); - $this->htmlAttributeCollection = $htmlAttributeCollection ?? new HTMLAttributeCollection(); - $this->placeholderBinder = $placeholderBinder ?? new PlaceholderBinder(); + public function setDependencies( + HTMLAttributeBinder $htmlAttributeBinder, + HTMLAttributeCollection $htmlAttributeCollection, + PlaceholderBinder $placeholderBinder, + ):void { + $this->htmlAttributeBinder = $htmlAttributeBinder; + $this->htmlAttributeCollection = $htmlAttributeCollection; + $this->placeholderBinder = $placeholderBinder; } /** diff --git a/src/HTMLAttributeBinder.php b/src/HTMLAttributeBinder.php index 39a9fa6..7c98025 100644 --- a/src/HTMLAttributeBinder.php +++ b/src/HTMLAttributeBinder.php @@ -8,8 +8,14 @@ use Gt\Dom\Element; class HTMLAttributeBinder { + private ListBinder $listBinder; private TableBinder $tableBinder; + public function setDependencies(ListBinder $listBinder, TableBinder $tableBinder):void { + $this->listBinder = $listBinder; + $this->tableBinder = $tableBinder; + } + public function bind( ?string $key, mixed $value, @@ -202,9 +208,6 @@ private function setBindProperty( break; case "table": - if(!isset($this->tableBinder)) { - $this->tableBinder = new TableBinder(); - } $this->tableBinder->bindTableData( $bindValue, $element, @@ -217,6 +220,7 @@ private function setBindProperty( break; case "list"; + $this->listBinder->bindListData($bindValue, $element); break; default: diff --git a/src/ListBinder.php b/src/ListBinder.php index 742fd93..8d0933c 100644 --- a/src/ListBinder.php +++ b/src/ListBinder.php @@ -5,17 +5,29 @@ use Gt\Dom\Document; use Gt\Dom\Element; use Iterator; +use IteratorAggregate; use Stringable; class ListBinder { + private ElementBinder $elementBinder; + private ListElementCollection $listElementCollection; private BindableCache $bindableCache; - - /** @noinspection PhpPropertyCanBeReadonlyInspection */ - public function __construct( - private ListElementCollection $listElementCollection, - ?BindableCache $bindableCache = null - ) { - $this->bindableCache = $bindableCache ?? new BindableCache(); + /** + * @noinspection PhpPropertyOnlyWrittenInspection + * @phpstan-ignore-next-line + */ + private TableBinder $tableBinder; + + public function setDependencies( + ElementBinder $elementBinder, + ListElementCollection $listElementCollection, + BindableCache $bindableCache, + TableBinder $tableBinder, + ):void { + $this->elementBinder = $elementBinder; + $this->listElementCollection = $listElementCollection; + $this->bindableCache = $bindableCache; + $this->tableBinder = $tableBinder; } /** @param iterable $listData */ @@ -50,9 +62,10 @@ public function bindListData( } } - $elementBinder = new ElementBinder(); + $elementBinder = $this->elementBinder; $nestedCount = 0; $i = -1; + foreach($listData as $listKey => $listValue) { $i++; $t = $listItem->insertListItem(); @@ -60,12 +73,9 @@ public function bindListData( // If the $listValue's first value is iterable, then treat this as a nested list. if($this->isNested($listValue)) { $elementBinder->bind(null, $listKey, $t); - $nestedCount += $this->bindListData( - $listValue, - $t, - $listItemName, - recursiveCall: true - ); + foreach($this->bindableCache->convertToKvp($listValue) as $key => $value) { + $elementBinder->bind($key, $value, $t); + } continue; } @@ -122,9 +132,15 @@ private function isEmpty(iterable $listData):bool { return is_null(array_key_first($listData)); } else { - /** @var Iterator $listData */ - $listData->rewind(); - return !$listData->valid(); + /** @var Iterator|IteratorAggregate $iterator */ + $iterator = $listData; + + if($iterator instanceof IteratorAggregate) { + $iterator = $iterator->getIterator(); + /** @var Iterator $iterator */ + } + $iterator->rewind(); + return !$iterator->valid(); } } @@ -155,9 +171,9 @@ private function isKVP(mixed $item):bool { private function isNested(mixed $item):bool { if(is_array($item)) { $key = array_key_first($item); - return is_int($key) || is_iterable($item[$key]); + return is_int($key) || (isset($item[$key]) && is_iterable($item[$key])); } - elseif($item instanceof Iterator) { + elseif(is_iterable($item)) { return true; } diff --git a/src/ListElementCollection.php b/src/ListElementCollection.php index a7c99ee..f88e057 100644 --- a/src/ListElementCollection.php +++ b/src/ListElementCollection.php @@ -85,8 +85,19 @@ private function findMatch(Element $context):ListElement { } } + $elementDescription = $context->tagName; + foreach($context->classList as $className) { + $elementDescription .= ".$className"; + } + + if($context->id) { + $elementDescription .= "#$context->id"; + } + + $elementNodePath = $context->getNodePath(); + throw new ListElementNotFoundInContextException( - "There is no unnamed template element in the context element ($context->tagName)." + "There is no unnamed list element in the context element $elementDescription ($elementNodePath)." ); } } diff --git a/src/TableBinder.php b/src/TableBinder.php index 06ff6bf..adfa56f 100644 --- a/src/TableBinder.php +++ b/src/TableBinder.php @@ -7,14 +7,32 @@ use Traversable; class TableBinder { - /** @noinspection PhpPropertyCanBeReadonlyInspection */ - public function __construct( - private ?ListElementCollection $templateCollection = null, - private ?ElementBinder $elementBinder = null, - private ?HTMLAttributeBinder $htmlAttributeBinder = null, - private ?HTMLAttributeCollection $htmlAttributeCollection = null, - private ?PlaceholderBinder $placeholderBinder = null - ) {} + /** + * @noinspection PhpPropertyOnlyWrittenInspection + * @phpstan-ignore-next-line + */ + private ListBinder $listBinder; + private ListElementCollection $templateCollection; + private ElementBinder $elementBinder; + private HTMLAttributeBinder $htmlAttributeBinder; + private HTMLAttributeCollection $htmlAttributeCollection; + private PlaceholderBinder $placeholderBinder; + + public function setDependencies( + ListBinder $listBinder, + ListElementCollection $listElementCollection, + ElementBinder $elementBinder, + HTMLAttributeBinder $htmlAttributeBinder, + HTMLAttributeCollection $htmlAttributeCollection, + PlaceholderBinder $placeholderBinder, + ):void { + $this->listBinder = $listBinder; + $this->templateCollection = $listElementCollection; + $this->elementBinder = $elementBinder; + $this->htmlAttributeBinder = $htmlAttributeBinder; + $this->htmlAttributeCollection = $htmlAttributeCollection; + $this->placeholderBinder = $placeholderBinder; + } /** * @param array>|array>> $tableData @@ -329,21 +347,17 @@ private function normaliseTableData(iterable $bindValue):array { } private function initBinders():void { - if(!$this->htmlAttributeBinder) { + if(!isset($this->htmlAttributeBinder)) { $this->htmlAttributeBinder = new HTMLAttributeBinder(); } - if(!$this->htmlAttributeCollection) { + if(!isset($this->htmlAttributeCollection)) { $this->htmlAttributeCollection = new HTMLAttributeCollection(); } - if(!$this->placeholderBinder) { + if(!isset($this->placeholderBinder)) { $this->placeholderBinder = new PlaceholderBinder(); } - if(!$this->elementBinder) { - $this->elementBinder = new ElementBinder( - $this->htmlAttributeBinder, - $this->htmlAttributeCollection, - $this->placeholderBinder - ); + if(!isset($this->elementBinder)) { + $this->elementBinder = new ElementBinder(); } } } diff --git a/test/phpunit/BindableCacheTest.php b/test/phpunit/BindableCacheTest.php index c557df7..e3b0529 100644 --- a/test/phpunit/BindableCacheTest.php +++ b/test/phpunit/BindableCacheTest.php @@ -177,4 +177,28 @@ public function testConvertToKvp_nestedObject():void { self::assertSame($customer->address->country->getName(), $kvpList[$i]["address.country.name"]); } } + + public function testConvertToKvp_nestedNullProperty():void { + $sut = new BindableCache(); + + $customerList = TestData::getCustomerOrderOverview1(); + $kvpList = []; + foreach($customerList as $customer) { +// force the address to be null, so address.street can't resolve. + $customer->address = null; + array_push($kvpList, $sut->convertToKvp($customer)); + } + + foreach($customerList as $i => $customer) { + self::assertSame((string)$customer->id, $kvpList[$i]["id"]); + self::assertSame($customer->name, $kvpList[$i]["name"]); + self::assertNull($customer->address); + self::assertNull($kvpList[$i]["address.street"]); + self::assertNull($kvpList[$i]["address.line2"]); + self::assertNull($kvpList[$i]["address.cityState"]); + self::assertNull($kvpList[$i]["address.postcodeZip"]); + self::assertNull($kvpList[$i]["address.country.code"]); + self::assertNull($kvpList[$i]["address.country.name"]); + } + } } diff --git a/test/phpunit/ComponentBinderTest.php b/test/phpunit/ComponentBinderTest.php new file mode 100644 index 0000000..8e37e2d --- /dev/null +++ b/test/phpunit/ComponentBinderTest.php @@ -0,0 +1,74 @@ +querySelector("todo-list"); + $sut = new ComponentBinder($document); + $sut->setComponentBinderDependencies($componentElement); + self::expectException(ComponentDoesNotContainContextException::class); + self::expectExceptionMessage(" does not contain requested "); + $sut->bindKeyValue("example-key", "example-value", $document->querySelector("body")); + } + + public function testBindList_invalidContext():void { + $document = new HTMLDocument(HTMLPageContent::HTML_TODO_CUSTOM_ELEMENT_ALREADY_EXPANDED); + $componentElement = $document->querySelector("todo-list"); + $sut = new ComponentBinder($document); + $sut->setComponentBinderDependencies($componentElement); + self::expectException(ComponentDoesNotContainContextException::class); + self::expectExceptionMessage(" does not contain requested "); + $sut->bindList([], $document->querySelector("body")); + } + + public function testBindKeyValue_notInContext():void { + $document = new HTMLDocument(HTMLPageContent::HTML_TODO_CUSTOM_ELEMENT_ALREADY_EXPANDED); + $componentElement = $document->querySelector("todo-list"); + $sut = new ComponentBinder($document); + $sut->setDependencies( + self::createMock(ElementBinder::class), + self::createMock(PlaceholderBinder::class), + self::createMock(TableBinder::class), + self::createMock(ListBinder::class), + self::createMock(ListElementCollection::class), + self::createMock(BindableCache::class), + ); + $sut->setComponentBinderDependencies($componentElement); + $sut->bindKeyValue("subtitle", "This should not change!"); + self::assertSame("Subtitle here", $document->querySelector("h2")->innerText); + } + + public function testBindKeyValue():void { + $document = new HTMLDocument(HTMLPageContent::HTML_TODO_CUSTOM_ELEMENT_ALREADY_EXPANDED); + $componentElement = $document->querySelector("todo-list"); + $elementBinder = self::createMock(ElementBinder::class); + $elementBinder->expects(self::once()) + ->method("bind") + ->with("listTitle", "This should change!", $componentElement); + + $sut = new ComponentBinder($document); + $sut->setDependencies( + $elementBinder, + self::createMock(PlaceholderBinder::class), + self::createMock(TableBinder::class), + self::createMock(ListBinder::class), + self::createMock(ListElementCollection::class), + self::createMock(BindableCache::class), + ); + $sut->setComponentBinderDependencies($componentElement); + $sut->bindKeyValue("listTitle", "This should change!"); + } +} diff --git a/test/phpunit/DocumentBinderTest.php b/test/phpunit/DocumentBinderTest.php index 4ee9212..fa25588 100644 --- a/test/phpunit/DocumentBinderTest.php +++ b/test/phpunit/DocumentBinderTest.php @@ -8,11 +8,19 @@ use Gt\Dom\HTMLCollection; use Gt\Dom\HTMLDocument; use Gt\DomTemplate\Bind; +use Gt\DomTemplate\BindableCache; use Gt\DomTemplate\BindGetter; +use Gt\DomTemplate\BindValue; use Gt\DomTemplate\DocumentBinder; +use Gt\DomTemplate\ElementBinder; +use Gt\DomTemplate\HTMLAttributeBinder; +use Gt\DomTemplate\HTMLAttributeCollection; use Gt\DomTemplate\IncompatibleBindDataException; use Gt\DomTemplate\InvalidBindPropertyException; +use Gt\DomTemplate\ListBinder; +use Gt\DomTemplate\ListElementCollection; use Gt\DomTemplate\PlaceholderBinder; +use Gt\DomTemplate\TableBinder; use Gt\DomTemplate\TableElementNotFoundInContextException; use Gt\DomTemplate\Test\TestHelper\HTMLPageContent; use Gt\DomTemplate\Test\TestHelper\ExampleClass; @@ -31,6 +39,7 @@ class DocumentBinderTest extends TestCase { public function testBindValue_missingBindProperty():void { $document = new HTMLDocument(HTMLPageContent::HTML_NO_BIND_PROPERTY); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); self::expectException(InvalidBindPropertyException::class); self::expectExceptionMessage(" Element has a data-bind attribute with missing bind property - did you mean `data-bind:text`?"); $sut->bindValue("Test!"); @@ -39,6 +48,7 @@ public function testBindValue_missingBindProperty():void { public function testBindValue_singleElement():void { $document = new HTMLDocument(HTMLPageContent::HTML_SINGLE_ELEMENT); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $output = $document->querySelector("output"); self::assertSame("Nothing is bound", $output->textContent); $sut->bindValue("Test!"); @@ -48,6 +58,7 @@ public function testBindValue_singleElement():void { public function testBindValue_multipleElements():void { $document = new HTMLDocument(HTMLPageContent::HTML_MULTIPLE_ELEMENTS); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $output1 = $document->getElementById("o1"); $output2 = $document->getElementById("o2"); $output3 = $document->getElementById("o3"); @@ -60,6 +71,7 @@ public function testBindValue_multipleElements():void { public function testBindValue_multipleNestedElements():void { $document = new HTMLDocument(HTMLPageContent::HTML_MULTIPLE_NESTED_ELEMENTS); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $container1 = $document->getElementById("container1"); $container2 = $document->getElementById("container2"); $sut->bindValue("Test!", $container1); @@ -83,6 +95,7 @@ public function testBindValue_multipleNestedElements():void { public function testBindValue_multipleNestedElements_skipsElementWithBindProperty():void { $document = new HTMLDocument(HTMLPageContent::HTML_MULTIPLE_NESTED_ELEMENTS); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $container3 = $document->getElementById("container3"); $sut->bindValue("Test!", $container3); self::assertSame("Default title", $document->querySelector("#container3 h1")->textContent); @@ -92,6 +105,7 @@ public function testBindValue_multipleNestedElements_skipsElementWithBindPropert public function testBindValue_synonymousProperties():void { $document = new HTMLDocument(HTMLPageContent::HTML_SYNONYMOUS_BIND_PROPERTIES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindValue("updated bold"); self::assertSame("updated <b>bold</b>", $document->getElementById("o1")->innerHTML); @@ -108,6 +122,7 @@ public function testBindValue_synonymousProperties():void { public function testBindValue_null():void { $document = new HTMLDocument(HTMLPageContent::HTML_SINGLE_ELEMENT); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $exception = null; try { @@ -121,6 +136,7 @@ public function testBindValue_null():void { public function testBindKeyValue_noMatches():void { $document = new HTMLDocument(HTMLPageContent::HTML_SINGLE_ELEMENT); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindKeyValue("missing", "example"); self::assertSame("Nothing is bound", $document->querySelector("output")->innerHTML); } @@ -128,6 +144,7 @@ public function testBindKeyValue_noMatches():void { public function testBindKeyValue_noMatchesInDifferentHierarchy():void { $document = new HTMLDocument(HTMLPageContent::HTML_MULTIPLE_NESTED_ELEMENTS); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); // The "title" bind element is actually within the #c3 hierarchy so should not be bound. $sut->bindKeyValue("title", "This should not bind", $document->getElementById("container1")); self::assertSame("Default title", $document->querySelector("#container3 h1")->textContent); @@ -136,6 +153,7 @@ public function testBindKeyValue_noMatchesInDifferentHierarchy():void { public function testBindKeyValue():void { $document = new HTMLDocument(HTMLPageContent::HTML_MULTIPLE_NESTED_ELEMENTS); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindKeyValue("title", "This should bind"); self::assertSame("This should bind", $document->querySelector("#container3 h1")->textContent); self::assertSame("This should bind", $document->querySelector("#container3 p span")->textContent); @@ -144,6 +162,7 @@ public function testBindKeyValue():void { public function testBindKeyValue_null():void { $document = new HTMLDocument(HTMLPageContent::HTML_MULTIPLE_NESTED_ELEMENTS); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $exception = null; try { @@ -163,6 +182,7 @@ public function testBindData_assocArray():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindData([ "username" => $username, "email" => $email, @@ -181,6 +201,7 @@ public function testBindData_assocArray_withNull():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindData([ "username" => $username, "email" => $email, @@ -195,6 +216,7 @@ public function testBindData_assocArray_withNull():void { public function testBindData_indexedArray():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); self::expectException(IncompatibleBindDataException::class); self::expectExceptionMessage("bindData is only compatible with key-value-pair data, but it was passed an indexed array."); $sut->bindData(["one", "two", "three"]); @@ -208,6 +230,7 @@ public function testBindData_object():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindData($userObject); self::assertSame($userObject->username, $document->getElementById("dd1")->textContent); @@ -233,6 +256,7 @@ public function __construct( $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindData($userObject); self::assertSame($userObject->username, $document->getElementById("dd1")->textContent); @@ -266,6 +290,7 @@ public function __construct( $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindData($userObject); self::assertSame($userObject->username, $document->getElementById("dd1")->textContent); @@ -285,6 +310,7 @@ public function __construct( $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindData($userObject); self::assertSame($userObject->username, $document->getElementById("dd1")->textContent); @@ -300,6 +326,7 @@ public function testBindData_object_withNull():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindData($userObject); self::assertSame($userObject->username, $document->getElementById("dd1")->textContent); @@ -318,6 +345,7 @@ public function testBindData_indexArray_shouldThrowException():void { public function testBindData_outOfContext():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindData([ "username" => "will-not-bind", "email" => "will-not-bind", @@ -332,6 +360,7 @@ public function testBindData_outOfContext():void { public function testBindKeyValue_arbitraryAttributes():void { $document = new HTMLDocument(HTMLPageContent::HTML_DIFFERENT_BIND_PROPERTIES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $img = $document->getElementById("img1"); $sut->bindKeyValue("photoURL", "/cat.jpg"); @@ -344,6 +373,7 @@ public function testBindKeyValue_arbitraryAttributes():void { public function testBindKeyValue_classAttribute():void { $document = new HTMLDocument(HTMLPageContent::HTML_DIFFERENT_BIND_PROPERTIES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $img = $document->getElementById("img1"); @@ -357,6 +387,7 @@ public function testBindKeyValue_classAttribute():void { public function testBindKeyValue_classToggle():void { $document = new HTMLDocument(HTMLPageContent::HTML_DIFFERENT_BIND_PROPERTIES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $img = $document->getElementById("img2"); @@ -370,6 +401,7 @@ public function testBindKeyValue_classToggle():void { public function testBindKeyValue_classToggle_differentClassNameToBindKey():void { $document = new HTMLDocument(HTMLPageContent::HTML_DIFFERENT_BIND_PROPERTIES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $img = $document->getElementById("img3"); @@ -383,6 +415,7 @@ public function testBindKeyValue_classToggle_differentClassNameToBindKey():void public function testBindKeyValue_toggleArbitraryAttribute():void { $document = new HTMLDocument(HTMLPageContent::HTML_DIFFERENT_BIND_PROPERTIES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $paragraph = $document->getElementById("p1"); @@ -403,6 +436,7 @@ public function testBindKeyValue_toggleArbitraryAttribute():void { public function testBindKeyValue_toggleDisabled():void { $document = new HTMLDocument(HTMLPageContent::HTML_DIFFERENT_BIND_PROPERTIES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $button = $document->getElementById("btn1"); @@ -424,6 +458,7 @@ public function testBindKeyValue_toggleDisabled():void { public function testBindKeyValue_toggleDisabled_inverseLogic():void { $document = new HTMLDocument(HTMLPageContent::HTML_DIFFERENT_BIND_PROPERTIES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $button = $document->getElementById("btn2"); @@ -437,6 +472,7 @@ public function testBindKeyValue_toggleDisabled_inverseLogic():void { public function testBindKeyValue_tableData_noTable():void { $document = new HTMLDocument(HTMLPageContent::HTML_NO_TABLE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); self::expectException(TableElementNotFoundInContextException::class); $sut->bindKeyValue("tableData", []); } @@ -444,6 +480,7 @@ public function testBindKeyValue_tableData_noTable():void { public function testBindTable():void { $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $tableData = [ ["Name", "Position"], @@ -472,6 +509,7 @@ public function testBindTable():void { public function testBindTable_withNullData():void { $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $tableData = [ ["Name", "Position"], @@ -514,6 +552,7 @@ public function testBindTable_withNullData():void { public function testBindKeyValue_tableData():void { $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $tableData = [ ["Name", "Position"], @@ -542,6 +581,7 @@ public function testBindKeyValue_tableData():void { public function testBindList():void { $document = new HTMLDocument(HTMLPageContent::HTML_LIST); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $listData = ["One", "Two", "Three"]; $sut->bindList($listData); @@ -556,6 +596,7 @@ public function testBindList():void { public function testBindList_nullData():void { $document = new HTMLDocument(HTMLPageContent::HTML_LIST); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $listData = ["One", null, "Three"]; $sut->bindList($listData); @@ -575,6 +616,7 @@ public function testBindList_nullData():void { public function testBindList_emptyLeavesNoWhiteSpace():void { $document = new HTMLDocument(HTMLPageContent::HTML_LIST); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $listData = []; $sut->bindList($listData); self::assertEquals("", $document->querySelector("ul")->innerHTML); @@ -583,6 +625,7 @@ public function testBindList_emptyLeavesNoWhiteSpace():void { public function testBindData_objectWithAttribute():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $userObject = new class { #[Bind("username")] @@ -604,6 +647,7 @@ public function getEmailAddress():string { public function testBindList_objectListWithAttributes():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_ORDER_LIST); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $userObjectList = [ new class { @@ -685,6 +729,7 @@ public function ordersCompleted():int { public function testBindData_castToArray():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $row = new class { private string $username = "g105b"; @@ -706,6 +751,7 @@ public function asArray():array { public function testBindList_castToArray():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_ORDER_LIST); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $row1 = new class { private int $userId = 123; @@ -736,6 +782,7 @@ public function asArray():array { public function testBindValue_callable():void { $document = new HTMLDocument(HTMLPageContent::HTML_SINGLE_ELEMENT); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindValue(fn() => "test"); self::assertSame("test", $document->querySelector("output")->textContent); } @@ -821,6 +868,7 @@ public function testBindList_complexHTML():void { $document = new HTMLDocument(HTMLPageContent::HTML_TRANSPORT_ROUTES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindKeyValue("from", $from); $sut->bindKeyValue("to", $to); @@ -891,6 +939,7 @@ public function testBindListData_callback():void { $document = new HTMLDocument(HTMLPageContent::HTML_SALES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindListCallback( $salesData, $salesCallback @@ -912,6 +961,7 @@ public function testBindListData_callback():void { public function testCleanDatasets_dataBind():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_PROFILE); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindData([ "username" => "codyboy123", "email" => "codyboy@g105b.com", @@ -932,6 +982,7 @@ public function testCleanDatasets_dataBind():void { public function testCleanDatasets_dataTemplate():void { $document = new HTMLDocument(HTMLPageContent::HTML_LIST); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindList(["One", "Two", "Three", "Four"]); $sut->cleanupDocument(); @@ -952,6 +1003,7 @@ public function testCleanDatasets_dataTemplate():void { public function testBindListData_twoListsDifferentContexts():void { $document = new HTMLDocument(HTMLPageContent::HTML_TWO_LISTS_WITH_UNNAMED_TEMPLATES); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $progLangData = ["PHP", "HTML", "bash"]; $sut->bindList($progLangData, $document->getElementById("prog-lang-list")); @@ -970,6 +1022,7 @@ public function testBindListData_twoListsDifferentContexts():void { public function testBindListData_twoListsDifferentContexts_withHtmlParents():void { $document = new HTMLDocument(HTMLPageContent::HTML_TWO_LISTS_WITH_UNNAMED_TEMPLATES_CLASS_PARENTS); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $progLangData = ["PHP", "HTML", "bash"]; $sut->bindList($progLangData, $document->querySelector(".favourite-list.prog-lang")); @@ -988,6 +1041,7 @@ public function testBindListData_twoListsDifferentContexts_withHtmlParents():voi public function testBindValue_callableString():void { $document = new HTMLDocument(HTMLPageContent::HTML_SINGLE_ELEMENT); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $value = "explode"; $sut->bindValue($value); self::assertSame($value, $document->querySelector("output")->textContent); @@ -996,6 +1050,7 @@ public function testBindValue_callableString():void { public function testBindList_twoListsWithSamePath():void { $document = new HTMLDocument(HTMLPageContent::HTML_TEMPLATES_WITH_SAME_XPATH); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $list1 = [ ["uuid" => "AAAAAAAA", "fullName" => "Test 1"], ["uuid" => "BBBBBBBB", "fullName" => "Test 2"], @@ -1051,6 +1106,7 @@ public function __construct( $document = new HTMLDocument(HTMLPageContent::HTML_USER_ORDER_LIST); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindList([$userObject1, $userObject2]); $li1 = $document->getElementById("user-1"); @@ -1068,6 +1124,7 @@ public function testBindList_readOnlyProperties_fullClass():void { $document = new HTMLDocument(HTMLPageContent::HTML_USER_ORDER_LIST); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindList([$userObject1, $userObject2]); $li1 = $document->getElementById("user-1"); @@ -1080,6 +1137,7 @@ public function testBindList_readOnlyProperties_fullClass():void { public function test_onlyBindOnce():void { $document = new HTMLDocument(HTMLPageContent::HTML_BIND_KEY_REUSED); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $shopList = [ ["id" => "123", "name" => "Alice's Animals"], @@ -1112,13 +1170,15 @@ public function testBindKeyValue_onlyBindPlaceholderOnce():void { ->with("name", "Cody"); $document = new HTMLDocument(HTMLPageContent::HTML_PLACEHOLDER); - $sut = new DocumentBinder($document, placeholderBinder: $placeholderBinder); + $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document, $placeholderBinder)); $sut->bindKeyValue("name", "Cody", $document->getElementById("test1")); } public function testBindKeyValue_nestedObject():void { $document = new HTMLDocument(HTMLPageContent::HTML_ADDRESS_NESTED_OBJECT); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $address = new Address( "2184 Jasper Avenue", @@ -1158,8 +1218,109 @@ public function test_removesUnboundDataElement():void { public function test_keepsElementWhenBound():void { $document = new HTMLDocument(HTMLPageContent::HTML_REMOVE_UNBOUND); $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); $sut->bindKeyValue("error", "Example error!"); $sut->cleanupDocument(); self::assertStringContainsStringIgnoringCase("error", (string)$document); } + + public function test_bindData_withList_dataBindList():void { + $document = new HTMLDocument(HTMLPageContent::HTML_DATA_BIND_LIST); + $sut = new DocumentBinder($document); + $sut->setDependencies(...$this->documentBinderDependencies($document)); + + $obj = new class { + public string $name = "This name was bound"; + public array $exampleList = ["one", "two", "three"]; + }; + + $sut->bindData($obj); + + self::assertSame("This name was bound", $document->querySelector("h1")->innerText); + $liElementList = $document->querySelectorAll("ul.second-list>li"); + self::assertCount(3, $liElementList); + foreach($liElementList as $i => $liElement) { + if($i === 0) { + $expectedNumber = "one"; + } + if($i === 1) { + $expectedNumber = "two"; + } + if($i === 2) { + $expectedNumber = "three"; + } + self::assertSame($expectedNumber, $liElement->textContent); + } + } + + private function documentBinderDependencies(HTMLDocument $document, mixed...$otherObjectList):array { + $htmlAttributeBinder = new HTMLAttributeBinder(); + $htmlAttributeCollection = new HTMLAttributeCollection(); + $elementBinder = new ElementBinder(); + $placeholderBinder = new PlaceholderBinder(); + $tableBinder = new TableBinder(); + $listBinder = new ListBinder(); + $listElementCollection = new ListElementCollection($document); + $bindableCache = new BindableCache(); + + foreach($otherObjectList as $object) { + if($object instanceof HTMLAttributeBinder) { + $htmlAttributeBinder = $object; + } + elseif($object instanceof HTMLAttributeCollection) { + $htmlAttributeCollection = $object; + } + elseif($object instanceof ElementBinder) { + $elementBinder = $object; + } + elseif($object instanceof PlaceholderBinder) { + $placeholderBinder = $object; + } + elseif($object instanceof TableBinder) { + $tableBinder = $object; + } + elseif($object instanceof ListBinder) { + $listBinder = $object; + } + elseif($object instanceof ListElementCollection) { + $listElementCollection = $object; + } + elseif($object instanceof BindableCache) { + $bindableCache = $object; + } + } + + $htmlAttributeBinder->setDependencies( + $listBinder, + $tableBinder, + ); + $elementBinder->setDependencies( + $htmlAttributeBinder, + $htmlAttributeCollection, + $placeholderBinder, + ); + $tableBinder->setDependencies( + $listBinder, + $listElementCollection, + $elementBinder, + $htmlAttributeBinder, + $htmlAttributeCollection, + $placeholderBinder, + ); + $listBinder->setDependencies( + $elementBinder, + $listElementCollection, + $bindableCache, + $tableBinder, + ); + + return [ + $elementBinder, + $placeholderBinder, + $tableBinder, + $listBinder, + $listElementCollection, + $bindableCache, + ]; + } } diff --git a/test/phpunit/ListBinderTest.php b/test/phpunit/ListBinderTest.php index bc01319..eb668b6 100644 --- a/test/phpunit/ListBinderTest.php +++ b/test/phpunit/ListBinderTest.php @@ -8,8 +8,14 @@ use Gt\Dom\Element; use Gt\Dom\HTMLDocument; use Gt\DomTemplate\Bind; +use Gt\DomTemplate\BindableCache; +use Gt\DomTemplate\ElementBinder; +use Gt\DomTemplate\HTMLAttributeBinder; +use Gt\DomTemplate\HTMLAttributeCollection; use Gt\DomTemplate\ListBinder; use Gt\DomTemplate\ListElementNotFoundInContextException; +use Gt\DomTemplate\PlaceholderBinder; +use Gt\DomTemplate\TableBinder; use Gt\DomTemplate\TableElementNotFoundInContextException; use Gt\DomTemplate\ListElementCollection; use Gt\DomTemplate\ListElement; @@ -23,8 +29,8 @@ class ListBinderTest extends TestCase { public function testBindList_emptyList():void { $document = new HTMLDocument(HTMLPageContent::HTML_LIST); - $templateCollection = new ListElementCollection($document); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $boundCount = $sut->bindListData( [], $document @@ -34,8 +40,8 @@ public function testBindList_emptyList():void { public function testBindList_empty_shouldHaveNoWhitespace():void { $document = new HTMLDocument(HTMLPageContent::HTML_LIST); - $templateCollection = new ListElementCollection($document); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $sut->bindListData([], $document); self::assertSame("", $document->querySelector("ul")->innerHTML); } @@ -47,11 +53,12 @@ public function testBindList_emptyList_iterator():void { $templateElement = self::createMock(ListElement::class); $templateElement->method("getListItemParent") ->willReturn($templateParent); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->method("get") + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->method("get") ->willReturn($templateElement); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); $boundCount = $sut->bindListData( new ArrayIterator([]), $document @@ -61,15 +68,16 @@ public function testBindList_emptyList_iterator():void { public function testBindList_noMatchingTemplate():void { $document = new HTMLDocument(HTMLPageContent::HTML_LIST); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->expects(self::once()) + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->expects(self::once()) ->method("get") ->with($document->documentElement, "missing") ->willReturnCallback(function() { throw new TableElementNotFoundInContextException(); }); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); self::expectException(TableElementNotFoundInContextException::class); $sut->bindListData( ["one", "two", "three"], @@ -82,8 +90,8 @@ public function testBindList_simpleList():void { $document = new HTMLDocument(HTMLPageContent::HTML_LIST); $templateElement = new ListElement($document->querySelector("li[data-list]")); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->expects(self::once()) + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->expects(self::once()) ->method("get") ->with($document->documentElement, null) ->willReturn($templateElement); @@ -98,7 +106,8 @@ public function testBindList_simpleList():void { ); $testData = ["one", "two", "three"]; - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); $boundCount = $sut->bindListData( $testData, $document @@ -120,8 +129,8 @@ public function testBindListData_existingChildren():void { $document = new HTMLDocument(HTMLPageContent::HTML_SELECT_OPTIONS_TEMPLATE_WITH_EXISTING_CHILDREN); $templateElement = new ListElement($document->querySelector("[data-list]")); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->expects(self::once()) + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->expects(self::once()) ->method("get") ->with($document->documentElement, null) ->willReturn($templateElement); @@ -135,7 +144,8 @@ public function testBindListData_existingChildren():void { ["id" => "bovril", "name" => "Bovril"], ["id" => "almond-milk", "name" => "Almond Milk"], ]; - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); $bindCount = $sut->bindListData($testData, $document); $newOptionList = $document->querySelectorAll("select[name='drink'] option"); @@ -172,13 +182,14 @@ public function testBindListData_twoLists():void { $document->querySelector("#favourites li[data-list='game']") ); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->method("get") + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->method("get") ->willReturnCallback(function(Element $documentElement, string $name)use($templateElementProgLang, $templateElementGame):ListElement { return $name === "game" ? $templateElementGame : $templateElementProgLang; }); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); $progLangData = ["PHP", "HTML", "bash"]; $sut->bindListData($progLangData, $document, "prog-lang"); $gameData = ["Pac Man", "Mega Man", "Tetris"]; @@ -209,15 +220,16 @@ public function testBindListData_twoListsDifferentContexts():void { $document->querySelector("#game-list li[data-list]") ); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->method("get") + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->method("get") ->willReturnCallback(function(Element $element)use($templateElementProgLang, $templateElementGame):ListElement { return ($element->id === "prog-lang-list") ? $templateElementProgLang : $templateElementGame; }); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); $progLangData = ["PHP", "HTML", "bash"]; $sut->bindListData($progLangData, $document->getElementById("prog-lang-list")); $gameData = ["Pac Man", "Mega Man", "Tetris"]; @@ -236,13 +248,14 @@ public function testBindListData_twoListsDifferentContexts():void { public function testBindListData_empty_parentShouldBeEmpty():void { $document = new HTMLDocument(HTMLPageContent::HTML_LIST); - $templateElement = new ListElement($document->querySelector("li[data-list]")); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->method("get") - ->willReturn($templateElement); - $templateElement->removeOriginalElement(); - - $sut = new ListBinder($templateCollection); + $listElement = new ListElement($document->querySelector("li[data-list]")); + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->method("get") + ->willReturn($listElement); + $listElement->removeOriginalElement(); + + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); $sut->bindListData([], $document); self::assertSame("", $document->querySelector("ul")->innerHTML); @@ -258,12 +271,13 @@ public function testBindListData_kvpList_array():void { $orderList = $document->querySelector("ul"); $templateElement = new ListElement($document->querySelector("ul li[data-list]")); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->method("get") + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->method("get") ->willReturn($templateElement); $templateElement->removeOriginalElement(); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); $sut->bindListData($kvpList, $orderList); foreach($orderList->children as $i => $li) { @@ -283,12 +297,13 @@ public function testBindListData_kvpList_object():void { $orderList = $document->querySelector("ul"); $templateElement = new ListElement($document->querySelector("ul li[data-list]")); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->method("get") + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->method("get") ->willReturn($templateElement); $templateElement->removeOriginalElement(); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); $sut->bindListData($kvpList, $orderList); foreach($orderList->children as $i => $li) { @@ -309,12 +324,13 @@ public function testBindListData_kvpList_instanceObject():void { $orderList = $document->querySelector("ul"); $templateElement = new ListElement($document->querySelector("ul li[data-list]")); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->method("get") + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->method("get") ->willReturn($templateElement); $templateElement->removeOriginalElement(); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); $sut->bindListData($kvpList, $orderList); foreach($orderList->children as $i => $li) { @@ -387,12 +403,13 @@ public function getTotalOrders():int { $orderList = $document->querySelector("ul"); $templateElement = new ListElement($document->querySelector("ul li[data-list]")); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->method("get") + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->method("get") ->willReturn($templateElement); $templateElement->removeOriginalElement(); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); $sut->bindListData($kvpList, $orderList); foreach($orderList->children as $i => $li) { @@ -443,12 +460,13 @@ public function testBindListData_kvpList_instanceObjectWithBindAttributeProperti $orderList = $document->querySelector("ul"); $templateElement = new ListElement($document->querySelector("ul li[data-list]")); - $templateCollection = self::createMock(ListElementCollection::class); - $templateCollection->method("get") + $listElementCollection = self::createMock(ListElementCollection::class); + $listElementCollection->method("get") ->willReturn($templateElement); $templateElement->removeOriginalElement(); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document, $listElementCollection)); $sut->bindListData($kvpList, $orderList); foreach($orderList->children as $i => $li) { @@ -460,8 +478,8 @@ public function testBindListData_kvpList_instanceObjectWithBindAttributeProperti public function testBindListData_nestedList():void { $document = new HTMLDocument(HTMLPageContent::HTML_MUSIC_NO_TEMPLATE_NAMES); - $templateCollection = new ListElementCollection($document); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $sut->bindListData(TestData::MUSIC, $document); $artistNameArray = array_keys(TestData::MUSIC); @@ -494,8 +512,8 @@ public function testBindListData_nestedList():void { public function testBindListData_nestedList_withKvps():void { $document = new HTMLDocument(HTMLPageContent::HTML_STUDENT_LIST); - $templateCollection = new ListElementCollection($document); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $sut->bindListData(TestData::STUDENTS, $document); foreach($document->querySelectorAll("body>ul>li") as $i => $studentLiElement) { @@ -515,12 +533,12 @@ public function testBindListData_nestedList_withKvps():void { public function testBindListData_iterativeSomething():void { $document = new HTMLDocument(HTMLPageContent::HTML_SEQUENCES); - $templateCollection = new ListElementCollection($document); $listData = [ "Primes" => new ArrayIterator([2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71]), "Fibonacci" => new ArrayIterator([0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765]), ]; - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $sut->bindListData($listData, $document); $listDataKeys = array_keys($listData); @@ -535,7 +553,6 @@ public function testBindListData_iterativeSomething():void { public function testBindListData_dateTime():void { $document = new HTMLDocument(HTMLPageContent::HTML_DATES); - $templateCollection = new ListElementCollection($document); $listData = []; $dateTime = new DateTime(); @@ -552,7 +569,8 @@ public function __toString():string { $dateTime->add(new DateInterval("P1M")); } - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $sut->bindListData($listData, $document); foreach($document->querySelectorAll("li") as $i => $li) { @@ -562,7 +580,6 @@ public function __toString():string { public function testBindListData_dateTimeAutomatic():void { $document = new HTMLDocument(HTMLPageContent::HTML_DATES); - $templateCollection = new ListElementCollection($document); /** @var array $listData */ $listData = []; @@ -575,7 +592,8 @@ public function testBindListData_dateTimeAutomatic():void { $dateTime->add(new DateInterval("P1M")); } - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $sut->bindListData($listData, $document); foreach($document->querySelectorAll("li") as $i => $li) { @@ -585,9 +603,9 @@ public function testBindListData_dateTimeAutomatic():void { public function testBindListData_todoList():void { $document = new HTMLDocument(HTMLPageContent::HTML_TODO); - $templateCollection = new ListElementCollection($document); $data = TestData::TODO_DATA; - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $sut->bindListData($data, $document); $todoLiElements = $document->querySelectorAll("ul>li"); @@ -603,8 +621,8 @@ public function testBindListData_todoList():void { public function testBindListData_multipleTemplateSiblings():void { $document = new HTMLDocument(HTMLPageContent::HTML_GOOD_BAD); - $listElementCollection = new ListElementCollection($document); - $sut = new ListBinder($listElementCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $sut->bindListData(["Good news 1", "Good news 2"], $document, "good"); $sut->bindListData(["Bad news 1", "Bad news 2"], $document, "bad"); @@ -651,8 +669,8 @@ public function testBindListData_callback():void { }; $document = new HTMLDocument(HTMLPageContent::HTML_SALES); - $templateCollection = new ListElementCollection($document); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $sut->bindListData( $salesData, $document, @@ -675,8 +693,8 @@ public function testBindListData_callback():void { public function testBindListData_complexStructure():void { $customerOrderData = TestData::getCustomerOrderOverview1(); $document = new HTMLDocument(HTMLPageContent::HTML_MAP_SHOP_CUSTOMER_OVERVIEW); - $templateCollection = new ListElementCollection($document); - $sut = new ListBinder($templateCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $sut->bindListData($customerOrderData, $document); foreach($customerOrderData as $customerIndex => $customer) { @@ -713,8 +731,8 @@ public function testBindListData_objectWithArrayProperties():void { new Student("Charlie", "Chudder", ["seven", "eight", "nine"]), ]; $document = new HTMLDocument(HTMLPageContent::HTML_STUDENT_LIST); - $listElementCollection = new ListElementCollection($document); - $sut = new ListBinder($listElementCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $sut->bindListData($list, $document); self::assertCount(count($list), $document->querySelectorAll("body>ul>li")); @@ -738,8 +756,8 @@ public function testBindListData_objectWithArrayProperties_noNestedList():void { new Student("Charlie", "Chudder", ["seven", "eight", "nine"]), ]; $document = new HTMLDocument(HTMLPageContent::HTML_STUDENT_LIST_NO_MODULE_LIST); - $listElementCollection = new ListElementCollection($document); - $sut = new ListBinder($listElementCollection); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); $numBound = $sut->bindListData($list, $document); self::assertCount(count($list), $document->querySelectorAll("body>ul>li")); self::assertSame(count($list), $numBound); @@ -747,8 +765,60 @@ public function testBindListData_objectWithArrayProperties_noNestedList():void { public function testBindListData_noListInDocument():void { $document = new HTMLDocument(HTMLPageContent::HTML_SINGLE_ELEMENT); - $sut = new ListBinder(new ListElementCollection($document)); + $sut = new ListBinder(); + $sut->setDependencies(...$this->listBinderDependencies($document)); self::expectException(ListElementNotFoundInContextException::class); $sut->bindListData(["one", "two", "three"], $document); } + + private function listBinderDependencies(HTMLDocument $document, mixed...$otherObjectList):array { + $htmlAttributeBinder = new HTMLAttributeBinder(); + $htmlAttributeCollection = new HTMLAttributeCollection(); + $placeholderBinder = new PlaceholderBinder(); + $elementBinder = new ElementBinder(); + $listElementCollection = new ListElementCollection($document); + $bindableCache = new BindableCache(); + $listBinder = new ListBinder(); + $tableBinder = new TableBinder(); + + foreach($otherObjectList as $object) { + if($object instanceof HTMLAttributeBinder) { + $htmlAttributeBinder = $object; + } + elseif($object instanceof HTMLAttributeCollection) { + $htmlAttributeCollection = $object; + } + elseif($object instanceof ElementBinder) { + $elementBinder = $object; + } + elseif($object instanceof PlaceholderBinder) { + $placeholderBinder = $object; + } + elseif($object instanceof TableBinder) { + $tableBinder = $object; + } + elseif($object instanceof ListBinder) { + $listBinder = $object; + } + elseif($object instanceof ListElementCollection) { + $listElementCollection = $object; + } + elseif($object instanceof BindableCache) { + $bindableCache = $object; + } + } + + $htmlAttributeBinder->setDependencies($listBinder, $tableBinder); + $elementBinder->setDependencies($htmlAttributeBinder, $htmlAttributeCollection, $placeholderBinder); + $listBinder->setDependencies($elementBinder, $listElementCollection, $bindableCache, $tableBinder); + $tableBinder->setDependencies($listBinder, $listElementCollection, $elementBinder, $htmlAttributeBinder, $htmlAttributeCollection, $placeholderBinder); + + return [ + $elementBinder, + $listElementCollection, + $bindableCache, + $tableBinder, + ]; + } + } diff --git a/test/phpunit/ListElementCollectionTest.php b/test/phpunit/ListElementCollectionTest.php index 9802055..bdf65e4 100644 --- a/test/phpunit/ListElementCollectionTest.php +++ b/test/phpunit/ListElementCollectionTest.php @@ -2,9 +2,15 @@ namespace Gt\DomTemplate\Test; use Gt\Dom\HTMLDocument; +use Gt\DomTemplate\BindableCache; use Gt\DomTemplate\ElementBinder; +use Gt\DomTemplate\HTMLAttributeBinder; +use Gt\DomTemplate\HTMLAttributeCollection; +use Gt\DomTemplate\ListBinder; use Gt\DomTemplate\ListElementCollection; use Gt\DomTemplate\ListElementNotFoundInContextException; +use Gt\DomTemplate\PlaceholderBinder; +use Gt\DomTemplate\TableBinder; use Gt\DomTemplate\Test\TestHelper\HTMLPageContent; use Gt\DomTemplate\Test\TestHelper\TestData; use PHPUnit\Framework\TestCase; @@ -67,6 +73,7 @@ public function testBindListData_nestedList_manual():void { $document = new HTMLDocument(HTMLPageContent::HTML_MUSIC_EXPLICIT_LIST_NAMES); $listElementCollection = new ListElementCollection($document); $elementBinder = new ElementBinder(); + $elementBinder->setDependencies(...$this->elementBinderDependencies($document)); foreach(TestData::MUSIC as $artistName => $albumList) { $artistListItem = $listElementCollection->get($document, "artist"); @@ -143,4 +150,26 @@ public function testConstructor_nonListChildrenArePreservedInOrder():void { self::assertCount(4, $ulChildren); self::assertSame("This list item will always show at the end", $ulChildren[3]->textContent); } + + private function elementBinderDependencies(HTMLDocument $document, mixed...$otherObjectList):array { + $htmlAttributeBinder = new HTMLAttributeBinder(); + $htmlAttributeCollection = new HTMLAttributeCollection(); + $placeholderBinder = new PlaceholderBinder(); + $elementBinder = new ElementBinder(); + $listElementCollection = new ListElementCollection($document); + $bindableCache = new BindableCache(); + $listBinder = new ListBinder(); + $tableBinder = new TableBinder(); + + $htmlAttributeBinder->setDependencies($listBinder, $tableBinder); + $elementBinder->setDependencies($htmlAttributeBinder, $htmlAttributeCollection, $placeholderBinder); + $listBinder->setDependencies($elementBinder, $listElementCollection, $bindableCache, $tableBinder); + $tableBinder->setDependencies($listBinder, $listElementCollection, $elementBinder, $htmlAttributeBinder, $htmlAttributeCollection, $placeholderBinder); + + return [ + $htmlAttributeBinder, + $htmlAttributeCollection, + $placeholderBinder, + ]; + } } diff --git a/test/phpunit/TableBinderTest.php b/test/phpunit/TableBinderTest.php index ca6d80d..f70f424 100644 --- a/test/phpunit/TableBinderTest.php +++ b/test/phpunit/TableBinderTest.php @@ -2,7 +2,14 @@ namespace Gt\DomTemplate\Test; use Gt\Dom\HTMLDocument; +use Gt\DomTemplate\BindableCache; +use Gt\DomTemplate\ElementBinder; +use Gt\DomTemplate\HTMLAttributeBinder; +use Gt\DomTemplate\HTMLAttributeCollection; use Gt\DomTemplate\IncorrectTableDataFormat; +use Gt\DomTemplate\ListBinder; +use Gt\DomTemplate\ListElementCollection; +use Gt\DomTemplate\PlaceholderBinder; use Gt\DomTemplate\TableBinder; use Gt\DomTemplate\TableDataStructureType; use Gt\DomTemplate\Test\TestHelper\HTMLPageContent; @@ -14,8 +21,9 @@ class TableBinderTest extends TestCase { * appropriate , , , , and elements. */ public function testBindTable_emptyTable():void { - $sut = new TableBinder(); $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); + $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $table = $document->getElementById("tbl1"); @@ -44,8 +52,9 @@ public function testBindTable_emptyTable():void { * use of the bind key, to identify which table to bind data to. */ public function testBindTable_emptyTableSpecifiedByName():void { - $sut = new TableBinder(); $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); + $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $table0 = $document->getElementById("tbl0"); $table1 = $document->getElementById("tbl1"); @@ -79,8 +88,9 @@ public function testBindTable_emptyTableSpecifiedByName():void { * will use the existing values to limit which columns are output. */ public function testBindTable_existingTHead():void { - $sut = new TableBinder(); $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); + $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $table = $document->getElementById("tbl2"); @@ -124,8 +134,9 @@ public function testBindTable_existingTHead():void { } public function testBindTable_dataNormalised():void { - $sut = new TableBinder(); $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); + $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $table = $document->getElementById("tbl2"); @@ -169,8 +180,9 @@ public function testBindTable_dataNormalised():void { * @link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody */ public function testBindTable_doubleHeader_shouldEmitTHElementsInRows():void { - $sut = new TableBinder(); $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); + $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $table = $document->getElementById("tbl1"); @@ -285,7 +297,9 @@ public function testBindTable_assocArrayWithoutIterableColumns():void { } public function testBindTable_multipleTables():void { + $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $tableData = [ "id" => [34, 35, 25], "firstName" => ["Derek", "Christoph", "Sara"], @@ -294,7 +308,6 @@ public function testBindTable_multipleTables():void { "email" => ["derek@php.net", "cmbecker69@php.net", "pollita@php.net"], ]; - $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); $sut->bindTableData( $tableData, $document->getElementById("multi-table-container"), @@ -324,7 +337,9 @@ public function testBindTable_multipleTables():void { } public function testBindTable_keyNamesInTHead():void { + $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $tableData = [ "id" => [34, 35, 25], "firstName" => ["Derek", "Christoph", "Sara"], @@ -333,7 +348,6 @@ public function testBindTable_keyNamesInTHead():void { "email" => ["derek@php.net", "cmbecker69@php.net", "pollita@php.net"], ]; - $document = new HTMLDocument(HTMLPageContent::HTML_TABLES); $table = $document->getElementById("tbl3"); $sut->bindTableData( $tableData, @@ -364,7 +378,9 @@ public function testBindTable_keyNamesInTHead():void { } public function testBindTableData_documentContext():void { + $document = new HTMLDocument(HTMLPageContent::HTML_TABLE_NO_BIND_KEY); $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $tableData = [ "id" => [34, 35, 25], "firstName" => ["Derek", "Christoph", "Sara"], @@ -373,14 +389,15 @@ public function testBindTableData_documentContext():void { "email" => ["derek@php.net", "cmbecker69@php.net", "pollita@php.net"], ]; - $document = new HTMLDocument(HTMLPageContent::HTML_TABLE_NO_BIND_KEY); $sut->bindTableData($tableData, $document); self::assertCount(4, $document->querySelectorAll("table tr")); } public function testBindTableData_emptyHeader():void { + $document = new HTMLDocument(HTMLPageContent::HTML_TABLE_ID_NAME_CODE); $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $tableData = [ ["ID", "Name", "Code"], ]; @@ -389,7 +406,6 @@ public function testBindTableData_emptyHeader():void { array_push($tableData, [$i, $name, md5($name)]); } - $document = new HTMLDocument(HTMLPageContent::HTML_TABLE_ID_NAME_CODE); $sut->bindTableData($tableData, $document); $table = $document->querySelector("table"); @@ -407,6 +423,7 @@ public function testBindTableData_emptyHeader():void { } public function testBindTableData_existingBodyRow():void { + $document = new HTMLDocument(HTMLPageContent::HTML_TABLE_EXISTING_CELLS); $tableData = [ ["id", "code", "name", "deleted"], ]; @@ -416,8 +433,8 @@ public function testBindTableData_existingBodyRow():void { array_push($tableData, [$i, md5($name), $name, $i % 3 === 0]); } - $document = new HTMLDocument(HTMLPageContent::HTML_TABLE_EXISTING_CELLS); $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $sut->bindTableData($tableData, $document); @@ -468,6 +485,7 @@ public function testBindTableData_existingBodyRow_differentDataShape():void { $document = new HTMLDocument(HTMLPageContent::HTML_TABLE_EXISTING_CELLS); $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $sut->bindTableData($tableData, $document); @@ -516,16 +534,9 @@ public function testBindTableData_datumPerRow():void { "Country" => "Germany", ], ]; -// $tableData = [ -// ["Item", "Price", "Stock Level"], -// [ -// "Washing machine" => [698_00, 24], -// "Television" => [998_00, 7], -// "Laptop" => [799_99, 60], -// ] -// ]; $document = new HTMLDocument(HTMLPageContent::HTML_TABLE_CRUD); $sut = new TableBinder(); + $sut->setDependencies(...$this->tablebinderDependencies($document)); $sut->bindTableData($tableData, $document); $tBody = $document->querySelector("table tbody"); self::assertCount(count($tableData), $tBody->rows); @@ -625,4 +636,30 @@ public function testDetectTableStructureType_emptyIsNormalised():void { $sut->detectTableDataStructureType([]), ); } + + private function tablebinderDependencies(HTMLDocument $document):array { + $htmlAttributeBinder = new HTMLAttributeBinder(); + $htmlAttributeCollection = new HTMLAttributeCollection(); + $placeholderBinder = new PlaceholderBinder(); + $elementBinder = new ElementBinder(); + $listElementCollection = new ListElementCollection($document); + $bindableCache = new BindableCache(); + $listBinder = new ListBinder(); + $tableBinder = new TableBinder(); + + $htmlAttributeBinder->setDependencies($listBinder, $tableBinder); + $elementBinder->setDependencies($htmlAttributeBinder, $htmlAttributeCollection, $placeholderBinder); + $listBinder->setDependencies($elementBinder, $listElementCollection, $bindableCache, $tableBinder); + $tableBinder->setDependencies($listBinder, $listElementCollection, $elementBinder, $htmlAttributeBinder, $htmlAttributeCollection, $placeholderBinder); + + return [ + $listBinder, + $listElementCollection, + $elementBinder, + $htmlAttributeBinder, + $htmlAttributeCollection, + $placeholderBinder, + ]; + } + } diff --git a/test/phpunit/TestHelper/HTMLPageContent.php b/test/phpunit/TestHelper/HTMLPageContent.php index 7b8018d..c7ccb55 100644 --- a/test/phpunit/TestHelper/HTMLPageContent.php +++ b/test/phpunit/TestHelper/HTMLPageContent.php @@ -530,12 +530,15 @@ class HTMLPageContent {

TODO LIST!

+

Subtitle here

+ HTML; const HTML_TODO_COMPONENT_TODO_LIST = <<List title here
@@ -552,6 +555,38 @@ class HTMLPageContent { HTML; + const HTML_TODO_CUSTOM_ELEMENT_ALREADY_EXPANDED = << + + + + TODO LIST! + + +

TODO LIST!

+

Subtitle here

+ + +

List title here

+ +
    + +
  • +
    + + + + +
    +
  • +
    +
+
+ + +HTML; + + const HTML_COMPONENT_NESTED_OUTER = << @@ -1017,6 +1052,20 @@ class HTMLPageContent { HTML; + const HTML_DATA_BIND_LIST = << +

Data-bind:list example

+ +

This shouldn't be bound:

+
    +
  • +
+ +

This should!

+
    +
  • +
+HTML; public static function createHTML(string $html = ""):HTMLDocument { diff --git a/test/phpunit/TestHelper/Model/Customer.php b/test/phpunit/TestHelper/Model/Customer.php index be89035..78af391 100644 --- a/test/phpunit/TestHelper/Model/Customer.php +++ b/test/phpunit/TestHelper/Model/Customer.php @@ -4,9 +4,9 @@ class Customer { /** @param array $orderList */ public function __construct( - public readonly int $id, - public readonly string $name, - public readonly Address $address, + public int $id, + public string $name, + public ?Address $address = null, public array $orderList = [], ) {} diff --git a/test/phpunit/phpunit.xml b/test/phpunit/phpunit.xml index b26aff2..953a463 100644 --- a/test/phpunit/phpunit.xml +++ b/test/phpunit/phpunit.xml @@ -1,23 +1,21 @@ - - - - ../../src - - - - - - - ./ - - - - + + + + ./ + + + + + + ../../src + +