diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index e36135e..e50f4ec 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,51 +1,52 @@
-name: Tests
-
-env:
- php_version: '7.3'
+name: "PHPUnit"
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
- workflow_dispatch:
- inputs:
- name:
- description: 'PHP version'
- required: true
- default: '7.3'
jobs:
- build:
-
- runs-on: ubuntu-latest
+ phpunit:
+ name: "PHPUnit"
+ env:
+ LC_ALL: en_US.UTF-8
+ CODE_COVERAGE: n
+ continue-on-error: ${{ matrix.php-version == '8.1' }}
+
+ runs-on: ${{ matrix.operating-system }}
+ strategy:
+ matrix:
+ php-version:
+ - "7.2"
+ - "7.3"
+ - "7.4"
+ - "8.0"
+ - "8.1"
+ dependencies:
+ - "locked"
+ operating-system:
+ - "ubuntu-latest"
+ composer:
+ - "composer:v2"
steps:
- - name: Checkout code
- uses: actions/checkout@v2
+ - name: "Checkout"
+ uses: "actions/checkout@v2"
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: '7.3'
- extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, gd, redis, memcached
- tools: composer:v2
- coverage: none
-
- - name: Cache Composer packages
- id: composer-cache
- uses: actions/cache@v2
+ - name: "Install PHP"
+ uses: shivammathur/setup-php@2.11.0
with:
- path: vendor
- key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
- restore-keys: |
- ${{ runner.os }}-php-
+ php-version: "${{ matrix.php-version }}"
+ extensions: mbstring, xml, ctype, iconv
+ tools: ${{matrix.composer}}
- - name: Install dependencies
- run: composer install --prefer-dist --no-progress
+ - name: "Install dependencies"
+ run: |
+ composer update --no-interaction --no-progress
- # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
- # Docs: https://getcomposer.org/doc/articles/scripts.md
+ - name: "Unit Tests"
+ run: composer test-unit
- # - name: Run test suite
- # run: composer test
+ - name: "Integration Tests"
+ run: composer test-int
diff --git a/.gitignore b/.gitignore
index 1e7fe90..26723eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,5 @@
/composer.lock
/php_errors.log
/test*.php
+/coverage.clover
+/.php-cs-fixer.cache
diff --git a/composer.json b/composer.json
index e558a22..63c85aa 100644
--- a/composer.json
+++ b/composer.json
@@ -11,6 +11,11 @@
"role": "Developer"
}
],
+ "archive": {
+ "exclude": [
+ "/tests", "/.github", "/phpunit.xml"
+ ]
+ },
"autoload": {
"psr-4": {
"Ht7\\Html\\": "src/"
@@ -34,19 +39,16 @@
}
],
"require": {
- "php": "^7.0",
- "ht7/ht7-base": "master"
+ "php": "^7.0|^8.0",
+ "ht7/ht7-base": "dev-develop"
},
"require-dev": {
- "phpunit/phpunit": "^7.0"
+ "phpunit/phpunit": "^10.5"
},
"minimum-stability": "dev",
"prefer-stable": true,
"scripts": {
- "test": ".\\vendor\\bin\\phpunit --configuration .\\tests\\configuration.xml --colors --testdox",
- "test-unit": "php .\\vendor\\phpunit\\phpunit\\phpunit --colors --bootstrap .\\tests\\bootstrap.php --configuration .\\tests\\configuration.xml --testsuite \"ht7 html - unit\"",
- "test-func": "php .\\vendor\\phpunit\\phpunit\\phpunit --colors --bootstrap .\\tests\\bootstrap.php --configuration .\\tests\\configuration.xml --testsuite \"ht7 html - functional\"",
- "test-unit1": "C:\\dev\\PHP\\current\\php.exe \"C:\\data\\edv\\php\\misc\\ht7-html\\vendor\\phpunit\\phpunit\\phpunit\" \"--colors\" \"--log-junit\" \"C:\\Users\\zoom4u\\AppData\\Local\\Temp\\nb-phpunit-log.xml\" \"--bootstrap\" \"C:\\data\\edv\\php\\misc\\ht7-html\\tests\\bootstrap.php\" \"--configuration\" \"C:\\data\\edv\\php\\misc\\ht7-html\\tests\\configuration.xml\" \"--coverage-clover\" \"C:\\Users\\zoom4u\\AppData\\Local\\Temp\\nb-phpunit-coverage.xml\" \"C:\\Program Files\\NetBeans-11.2\\netbeans\\php\\phpunit\\NetBeansSuite.php",
- "phpv": "php -v"
+ "test-unit": "php vendor/phpunit/phpunit/phpunit --colors --testdox --configuration tests/configuration.xml --testsuite unit",
+ "test-int": "php vendor/phpunit/phpunit/phpunit --colors --testdox --configuration tests/configuration.xml --testsuite integration"
}
}
diff --git a/src/Attribute.php b/src/Attribute.php
index af2050b..c54faff 100644
--- a/src/Attribute.php
+++ b/src/Attribute.php
@@ -1,47 +1,18 @@
setName($name);
- $this->setValue($value);
}
-
/**
* Get a string representation of the current class.
*
@@ -54,7 +25,6 @@ public function __toString()
return $this->getName() . ($value === '' ? '' : '="' . $value . '"');
}
-
/**
* @Overridden
*/
@@ -62,76 +32,51 @@ public function getHash()
{
return $this->getName();
}
-
/**
* Get the name of the present attribute.
- *
- * @return string The attribute name.
*/
- public function getName()
+ public function getName(): string
{
return $this->name;
}
-
/**
* Get the value of the present attribute.
- *
- * @return mixed The attribute value.
*/
- public function getValue()
+ public function getValue(): string|float|int|bool
{
return $this->value;
}
-
/**
* {@inheritdoc}
*/
- public function jsonSerialize()
+ public function jsonSerialize(): mixed
{
return $this->getValue();
}
-
/**
* Set the name of the current attribute instance.
- * The name must be a string and must not be empty.
- *
- * @param string $name The attribute name.
- * @throws InvalidArgumentException
+ * The name must not be empty.
*/
- public function setName($name)
+ public function setName(string $name): static
{
if (empty($name)) {
- $msg = 'The attribute name must not be empty.';
- $e = sprintf($msg, gettype($name));
+ $e = 'The attribute name must not be empty.';
- throw new InvalidArgumentException($e);
- } elseif (is_string($name)) {
- $this->name = $name;
- } else {
- $msg = 'The attribute name must be a string, found %s.';
- $e = sprintf($msg, gettype($name));
-
- throw new InvalidArgumentException($e);
+ throw new \InvalidArgumentException($e);
}
- }
+
+ $this->name = $name;
+ return $this;
+ }
/**
* Set the value of the current attribute instance.
- * The value must be either string, int or float.
- *
- * @param mixed $value The attribute value.
- * @throws InvalidArgumentException
+ * The value must be either string, float, int or bool.
*/
- public function setValue($value)
+ public function setValue(string|float|int|bool $value): static
{
- if (is_string($value) || is_int($value) || is_float($value)) {
- $this->value = $value;
- } else {
- $msg = 'The attribute value must be a string, int or float, found %s.';
- $e = sprintf($msg, gettype($value));
+ $this->value = $value;
- throw new InvalidArgumentException($e);
- }
+ return $this;
}
-
}
diff --git a/src/Lists/AbstractRenderableList.php b/src/Lists/AbstractRenderableList.php
index 1f4fcdc..01edabe 100644
--- a/src/Lists/AbstractRenderableList.php
+++ b/src/Lists/AbstractRenderableList.php
@@ -2,9 +2,9 @@
namespace Ht7\Html\Lists;
-use \Ht7\Base\Lists\ItemList;
-use \Ht7\Html\Utilities\CanRenderList;
-use \Ht7\Html\Renderable;
+use Ht7\Base\Lists\ItemList;
+use Ht7\Html\Utilities\CanRenderList;
+use Ht7\Html\Renderable;
/**
* Description of AbstractItemList
diff --git a/src/Lists/AttributeList.php b/src/Lists/AttributeList.php
index ba67720..14e8f0c 100644
--- a/src/Lists/AttributeList.php
+++ b/src/Lists/AttributeList.php
@@ -17,14 +17,12 @@ class AttributeList extends HashList implements \JsonSerializable, Renderable
{
use CanRenderList;
-
public function __construct(array $data = [])
{
$this->divider = ' ';
parent::__construct($data);
}
-
/**
* {@inheritdoc}
*/
@@ -35,7 +33,6 @@ public function __toString()
return implode($this->getDivider(), $all);
}
-
/**
* Add an attribute to the present AttributeList.
*
@@ -56,7 +53,6 @@ public function add($item)
throw new InvalidArgumentException($e);
}
}
-
/**
* Add an attribute name and its value to the present AttributeList.
*
@@ -68,7 +64,6 @@ public function addPlain($name, $value)
{
return $this->add((new Attribute($name, $value)));
}
-
/**
* Check wheter a value can be found or not in the present AttributeList instance.
*
@@ -91,15 +86,13 @@ public function hasByValue($compare)
return false;
}
-
/**
* {@inheritdoc}
*/
- public function jsonSerialize()
+ public function jsonSerialize(): mixed
{
return $this->getAll();
}
-
/**
* Load the present AttributeList instance with the submitted data.
*
@@ -120,5 +113,4 @@ public function load(array $data)
}
}
}
-
}
diff --git a/src/Lists/NodeList.php b/src/Lists/NodeList.php
index 541f8c6..90d6a88 100644
--- a/src/Lists/NodeList.php
+++ b/src/Lists/NodeList.php
@@ -11,7 +11,6 @@
*/
class NodeList extends AbstractRenderableList implements \JsonSerializable
{
-
/**
* {@inheritdoc}
*/
@@ -21,7 +20,6 @@ public function add($item)
return $this;
}
-
/**
* Serialize the object to a value that can beserialized natively by json_encode.
*
@@ -32,7 +30,7 @@ public function add($item)
*
* @return array An array representation of this instance.
*/
- public function jsonSerialize()
+ public function jsonSerialize(): mixed
{
// $items = [];
// $all = $this->getAll();
@@ -44,5 +42,4 @@ public function jsonSerialize()
// return $items;
return $this->getAll();
}
-
}
diff --git a/src/Node.php b/src/Node.php
index 67b4e48..941e9c7 100644
--- a/src/Node.php
+++ b/src/Node.php
@@ -2,7 +2,8 @@
namespace Ht7\Html;
-use \Ht7\Html\Renderable;
+use Ht7\Html\Renderable;
+use Ht7\Html\Lists\NodeList;
/**
* Base class.
@@ -12,33 +13,25 @@
abstract class Node implements \JsonSerializable, Renderable
{
- /**
- * The content of the current Node.
- *
- * @var mixed The content of the current Node, which can be a
- * string, Text- or a Tag-instance.
- */
- protected $content;
+ protected NodeList|string $content;
/**
- * Get the content of the current HTML element.
- *
- * @return NodeList The content of the current HTML element.
+ * Get the content of the present HTML element.
*/
- public function getContent()
+ public function getContent(): NodeList|string
{
return $this->content;
}
/**
- * Set the inner content of the current tag.
+ * Set the inner content of the present HTML element.
*
- * This method will throw an exception if the current tag is self closing.
+ * This method will throw an exception if the present tag is self closing.
*
- * @param mixed $content The content of the current Node
+ * @param NodeList|array|string|float|int|bool $content The content of the current Node
* instance.
* @throws BadMethodCallException
* @throws InvalidArgumentException
*/
- abstract public function setContent($content);
+ abstract public function setContent(NodeList|array|string|float|int|bool $content): static;
}
diff --git a/src/Tag.php b/src/Tag.php
index c37770e..ac51637 100644
--- a/src/Tag.php
+++ b/src/Tag.php
@@ -1,21 +1,11 @@
setTagName($tagName);
- $this->setContent($content);
- $this->setAttributes($attributes);
+ $this->setContent($content)
+ ->setAttributes($attributes);
}
-
/**
* Get a string representation of the current tag instance.
*
@@ -77,37 +47,27 @@ public function __toString()
$attrStr = (string) $this->getAttributes();
$attrStrSanitized = empty($attrStr) ? '' : ' ' . $attrStr;
- if ($this->isSelfClosing()) {
- $html = '<' . $tagName . $attrStrSanitized . ' />';
- } else {
- $html = '<' . $tagName . $attrStrSanitized . '>';
- $html .= $this->getContent();
- $html .= '' . $tagName . '>';
- }
-
- return $html;
+ return "<{$tagName}{$attrStrSanitized}"
+ . ($this->isSelfClosing() ? ' />' : ">{$this->getContent()}{$tagName}>");
}
-
/**
* Get the defined attributes of the current tag instance.
*
* @return AttributeList The attributes of the present tag.
*/
- public function getAttributes()
+ public function getAttributes(): AttributeList
{
return $this->attributes;
}
-
/**
* Get the content of the current HTML element.
*
* @return NodeList The content of the current HTML element.
*/
- public function getContent()
+ public function getContent(): NodeList
{
return parent::getContent();
}
-
/**
* Get an iterator instance to iterate the current Tag.
*
@@ -115,21 +75,19 @@ public function getContent()
*
* @return Iterator
*/
- public function getIterator()
+ public function getIterator(): \Traversable
{
return $this->getIteratorPreOrder();
}
-
/**
* Get the tag name of the current element.
*
* @return string The tag name.
*/
- public function getTagName()
+ public function getTagName(): string
{
return $this->tagName;
}
-
/**
* Get a tree iterator which goes first every tree up before searching the
* next.
@@ -138,32 +96,25 @@ public function getTagName()
// {
//
// }
-
/**
* Get a tree iterator which searches first every sibling before going up to
* the next level.
- *
- * @return PreOrderIterator
*/
- public function getIteratorPreOrder()
+ public function getIteratorPreOrder(): PreOrderIterator
{
return new PreOrderIterator($this);
}
-
/**
* Whetever the current tag is self closing or not.
- *
- * @return boolean True if the current element is self closing.
*/
- public function isSelfClosing()
+ public function isSelfClosing(): bool
{
return SelfClosing::is($this->getTagName());
}
-
/**
* {@inheritdoc}
*/
- public function jsonSerialize()
+ public function jsonSerialize(): mixed
{
return [
'attributes' => $this->getAttributes(),
@@ -171,31 +122,20 @@ public function jsonSerialize()
'tag' => $this->getTagName()
];
}
-
/**
* Set the attributes of the current HTML element.
*
- * @param mixed $attributes Indexed array of
+ * @param AttributeList|array $attributes Indexed array of
* \Ht7\Html\Attribute
* instances or an instance of
* AttributeList.
*/
- public function setAttributes($attributes)
+ public function setAttributes(AttributeList|array $attributes): static
{
- if ($attributes instanceof AttributeList) {
- $this->attributes = $attributes;
- } elseif (is_array($attributes)) {
- $this->attributes = new AttributeList($attributes);
- } else {
- throw new InvalidDatatypeException(
- 'attributes',
- $attributes,
- ['array'],
- [NodeList::class]
- );
- }
- }
+ $this->attributes = $attributes instanceof AttributeList ? $attributes : new AttributeList($attributes);
+ return $this;
+ }
/**
* Set the inner content of the current tag.
*
@@ -205,20 +145,20 @@ public function setAttributes($attributes)
* will be created. In this case the input validation will be delegated to
* the NodeList.
*
- * @param mixed $content The content of the current Tag
+ * @param NodeList|array|string|float|int|bool $content The content of the current Tag
* instance. This must be a NodeList
* instance or an array.
- * @throws BadMethodCallException
- * @throws InvalidArgumentException
+ * @throws \BadMethodCallException
*/
- public function setContent($content)
+ // public function setContent(mixed $content): static
+ public function setContent(NodeList|array|string|float|int|bool $content): static
{
- if (!empty($content) && $this->isSelfClosing()) {
+ if ($this->isSelfClosing() && !empty($content)) {
$msg = 'This tag (%s) can not have content, because it is self'
- . ' closing.';
+ . ' closing.';
$e = sprintf($msg, gettype($this->getTagName()));
- throw new BadMethodCallException($e);
+ throw new \BadMethodCallException($e);
}
if (is_scalar($content) || $content instanceof Node) {
@@ -226,21 +166,18 @@ public function setContent($content)
}
$this->content = $content instanceof NodeList ? $content : new NodeList($content);
- }
+ return $this;
+ }
/**
* Set the name of the current tag.
*
* @param string $name The tag name of the current HTML element.
- * @throws InvalidArgumentException
*/
- public function setTagName($name)
+ public function setTagName(string $name): static
{
- if (is_string($name)) {
- $this->tagName = $name;
- } else {
- throw new InvalidDatatypeException('tag name', $name, ['string']);
- }
- }
+ $this->tagName = $name;
+ return $this;
+ }
}
diff --git a/src/Text.php b/src/Text.php
index 77eff95..2cdba37 100644
--- a/src/Text.php
+++ b/src/Text.php
@@ -2,22 +2,22 @@
namespace Ht7\Html;
+use Ht7\Html\Lists\NodeList;
+
/**
* This is a simple text node.
*/
class Text extends Node
{
-
/**
* Create an instance of the text element.
*
* @param string $text The content.
*/
- public function __construct($text)
+ public function __construct(string $text)
{
$this->setContent($text);
}
-
/**
* Get a string representation of the current class.
*
@@ -27,34 +27,31 @@ public function __toString()
{
return $this->getContent();
}
-
/**
* Get the content.
*
* @return string The content of the current text element.
*/
- public function getContent()
+ public function getContent(): NodeList|string
{
return parent::getContent();
}
-
/**
* {@inheritdoc}
*/
- public function jsonSerialize()
+ public function jsonSerialize(): mixed
{
return $this->getContent();
}
-
/**
* Set the content.
*
* Only scalar types will be accepted.
*
- * @param mixed $text The content as string, integer or float.
+ * @param NodeList|array|string|float|int|bool $text The content as string, integer, float or bool.
* @throws \InvalidArgumentException
*/
- public function setContent($text)
+ public function setContent(NodeList|array|string|float|int|bool $text): static
{
if (is_scalar($text)) {
$this->content = (string) $text;
@@ -63,6 +60,7 @@ public function setContent($text)
throw new \InvalidArgumentException($e);
}
- }
+ return $this;
+ }
}
diff --git a/src/Utilities/CanRenderList.php b/src/Utilities/CanRenderList.php
index bc8a89f..26ff130 100644
--- a/src/Utilities/CanRenderList.php
+++ b/src/Utilities/CanRenderList.php
@@ -10,14 +10,12 @@
*/
trait CanRenderList
{
-
- protected $divider;
+ protected $divider = '';
public function getDivider()
{
return $this->divider;
}
-
public function setDivider($divider)
{
if (is_string($divider)) {
@@ -26,10 +24,8 @@ public function setDivider($divider)
throw new InvalidDatatypeException('divider', $divider, ['string']);
}
}
-
public function __toString()
{
return implode($this->getDivider(), $this->getAll());
}
-
}
diff --git a/tests/Integration/CallbackTest.php b/tests/Integration/CallbackTest.php
index 704c0f1..98cdd1c 100644
--- a/tests/Integration/CallbackTest.php
+++ b/tests/Integration/CallbackTest.php
@@ -55,6 +55,8 @@ public function testWithMethod()
{
if (file_exists('./assets/functions/callbacks.php')) {
include_once './assets/functions/callbacks.php';
+ } elseif (file_exists('./tests/assets/functions/callbacks.php')) {
+ include_once './tests/assets/functions/callbacks.php';
} else {
throw new \BadMethodCallException('Missing callback functions file.');
}
diff --git a/tests/Unit/AttributeTest.php b/tests/Unit/AttributeTest.php
index d815897..a4bfff2 100644
--- a/tests/Unit/AttributeTest.php
+++ b/tests/Unit/AttributeTest.php
@@ -4,168 +4,118 @@
use \InvalidArgumentException;
use \stdClass;
-use \PHPUnit\Framework\TestCase;
-use \Ht7\Html\Attribute;
+use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\Attributes\TestDox;
+use PHPUnit\Framework\MockObject\MockObject;
+use Ht7\Html\Attribute;
class AttributeTest extends TestCase
{
+ private string $className = Attribute::class;
- public function testConstructor()
+ #[Test]
+ #[TestDox('Get name.')]
+ public function getName(): void
{
- // see: http://miljar.github.io/blog/2013/12/20/phpunit-testing-the-constructor/
- $className = Attribute::class;
- $name = 'class';
- $value = 'btn btn-primary';
-
- $mock = $this->getMockBuilder($className)
- ->setMethods(['setName', 'setValue'])
- ->disableOriginalConstructor()
- ->getMock();
-
- $mock->expects($this->once())
- ->method('setName')
- ->with($this->equalTo($name));
- $mock->expects($this->once())
- ->method('setValue')
- ->with($this->equalTo($value));
-
- $reflectedClass = new \ReflectionClass($className);
- $constructor = $reflectedClass->getConstructor();
- $constructor->invoke($mock, $name, $value);
- }
-
- public function testGetName()
- {
- $className = Attribute::class;
-
- $mock = $this->getMockBuilder($className)
- ->setMethods(['setName'])
- ->disableOriginalConstructor()
- ->getMock();
+ $expected = 'class';
+ /** @var Attribute $sut */
+ $sut = $this->getSut(['setName']);
- $reflectedClass = new \ReflectionClass($className);
+ $reflectedClass = new \ReflectionClass($this->className);
$property = $reflectedClass->getProperty('name');
$property->setAccessible(true);
+ $property->setValue($sut, $expected);
- $expected = 'class';
-
- $property->setValue($mock, $expected);
-
- $this->assertEquals($expected, $mock->getName());
+ $this->assertEquals($expected, $sut->getName());
}
- public function testGetValue()
+ #[Test]
+ #[TestDox('Get value.')]
+ public function getValue(): void
{
- $className = Attribute::class;
-
- $mock = $this->getMockBuilder($className)
- ->setMethods(['setValue'])
- ->disableOriginalConstructor()
- ->getMock();
+ $expected = 'btn btn-primary';
+ /** @var Attribute $sut */
+ $sut = $this->getSut(['setName']);
- $reflectedClass = new \ReflectionClass($className);
+ $reflectedClass = new \ReflectionClass($this->className);
$property = $reflectedClass->getProperty('value');
$property->setAccessible(true);
+ $property->setValue($sut, $expected);
- $expected = 'btn btn-primary';
-
- $property->setValue($mock, $expected);
-
- $this->assertEquals($expected, $mock->getValue());
+ $this->assertEquals($expected, $sut->getValue());
}
- public function testJsonEncode()
+ #[Test]
+ #[TestDox('Json encode.')]
+ public function jsonEncode(): void
{
- $mock = $this->getMockBuilder(Attribute::class)
- ->setMethods(['getValue'])
- ->disableOriginalConstructor()
- ->getMock();
+ $expected = '"btn btn-primary"';
+ $sut = $this->getSut(['getValue']);
- $mock->expects($this->once())
+ $sut->expects($this->once())
->method('getValue')
->willReturn('btn btn-primary');
- $expected = '"btn btn-primary"';
-
- $this->assertEquals($expected, json_encode($mock));
- }
-
- public function testSetNameWithException()
- {
- $mock = $this->getMockBuilder(Attribute::class)
- ->setMethods(['setValue']) // Without this, an exception would not been thrown.
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->expectException(InvalidArgumentException::class);
-
- $mock->setName((new stdClass()));
+ $this->assertEquals($expected, json_encode($sut));
}
- public function testSetNameEmptyWithException()
+ #[Test]
+ #[TestDox('Set name with exception.')]
+ public function setNameWithException(): void
{
- $mock = $this->getMockBuilder(Attribute::class)
- ->setMethods(['setValue']) // Without this, an exception would not been thrown.
- ->disableOriginalConstructor()
- ->getMock();
+ /** @var Attribute $sut */
+ $sut = $this->getSut(['setValue']);
- $this->expectException(InvalidArgumentException::class);
+ $this->expectException(\InvalidArgumentException::class);
- $mock->setName('');
+ $sut->setName('');
}
- public function testSetValueWithException()
+ #[Test]
+ #[TestDox('Render the attribute.')]
+ public function render(): void
{
- $mock = $this->getMockBuilder(Attribute::class)
- ->setMethods(['setName']) // Without this, an exception would not been thrown.
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->expectException(InvalidArgumentException::class);
-
- $mock->setValue((new stdClass()));
- }
-
- public function testToString()
- {
- $mock = $this->getMockBuilder(Attribute::class)
- ->setMethods(['getName', 'getValue'])
- ->disableOriginalConstructor()
- ->getMock();
+ $expected = 'class="btn btn-primary"';
+ $sut = $this->getSut(['getName', 'getValue']);
- $mock->expects($this->once())
+ $sut->expects($this->once())
->method('getName')
->willReturn('class');
- $mock->expects($this->once())
+ $sut->expects($this->once())
->method('getValue')
->willReturn('btn btn-primary');
-
- $actual = (string) $mock;
- $expected = 'class="btn btn-primary"';
+ $actual = (string) $sut;
$this->assertEquals($expected, $actual);
}
- public function testToStringNoValue()
+ #[Test]
+ #[TestDox('Render the attribute with no value.')]
+ public function renderNoValue(): void
{
- $mock = $this->getMockBuilder(Attribute::class)
- ->setMethods(['getName', 'getValue'])
- ->disableOriginalConstructor()
- ->getMock();
+ $expected = 'required';
+ $sut = $this->getSut(['getName', 'getValue']);
- $mock->expects($this->once())
+ $sut->expects($this->once())
->method('getName')
->willReturn('required');
- $mock->expects($this->once())
+ $sut->expects($this->once())
->method('getValue')
->willReturn('');
-
- $actual = (string) $mock;
- $expected = 'required';
+ $actual = (string) $sut;
$this->assertEquals($expected, $actual);
}
+ private function getSut(array $methods): MockObject
+ {
+ return $this->getMockBuilder($this->className)
+ ->onlyMethods($methods)
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
}
diff --git a/tests/Unit/CallbackTest.php b/tests/Unit/CallbackTest.php
index d9d4694..f920e78 100644
--- a/tests/Unit/CallbackTest.php
+++ b/tests/Unit/CallbackTest.php
@@ -284,8 +284,12 @@ public function testProcessWithMethod()
{
if (file_exists('./assets/functions/callbacks.php')) {
include_once './assets/functions/callbacks.php';
+ } elseif (file_exists('./../assets/functions/callbacks.php')) {
+ include_once './../assets/functions/callbacks.php';
+ } elseif (file_exists('./tests/assets/functions/callbacks.php')) {
+ include_once './tests/assets/functions/callbacks.php';
} else {
- throw new \BadMethodCallException('Missing callback functions file.');
+ throw new \BadMethodCallException('Missing callback functions file. - ' . getcwd());
}
$className = Callback::class;
diff --git a/tests/Unit/TagTest.php b/tests/Unit/TagTest.php
index d1c281a..4b5273a 100644
--- a/tests/Unit/TagTest.php
+++ b/tests/Unit/TagTest.php
@@ -2,48 +2,49 @@
namespace Ht7\Html\Tests\Unit;
-use \BadMethodCallException;
-use \InvalidArgumentException;
-use \stdClass;
-use \PHPUnit\Framework\TestCase;
-use \Ht7\Html\Node;
-use \Ht7\Html\Tag;
-use \Ht7\Html\Iterators\PreOrderIterator;
-use \Ht7\Html\Lists\AttributeList;
-use \Ht7\Html\Lists\NodeList;
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\Attributes\TestDox;
+use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\MockObject\MockObject;
+use Ht7\Html\Tag;
+use Ht7\Html\Text;
+use Ht7\Html\Iterators\PreOrderIterator;
+use Ht7\Html\Lists\AttributeList;
+use Ht7\Html\Lists\NodeList;
class TagTest extends TestCase
{
+ private string $className = Tag::class;
- public function testConstructor()
+ #[Test]
+ #[TestDox('Tag initialisation.')]
+ public function tagConstructor(): void
{
// see: http://miljar.github.io/blog/2013/12/20/phpunit-testing-the-constructor/
- $className = Tag::class;
$tagName = 'span';
$content = ['test text'];
$attributes = ['class' => 'btn btn-primary'];
- $mock = $this->getMockBuilder($className)
- ->setMethods(['setTagName', 'setContent', 'setAttributes'])
- ->disableOriginalConstructor()
- ->getMock();
-
- $mock->expects($this->once())
- ->method('setTagName')
- ->with($this->equalTo($tagName));
- $mock->expects($this->once())
+ $sut = $this->getMockTag(['setTagName', 'setContent', 'setAttributes']);
+ $sut->expects($this->never())
+ ->method('setTagName');
+ $sut->expects($this->once())
->method('setContent')
- ->with($this->equalTo($content));
- $mock->expects($this->once())
+ ->with($this->equalTo($content))
+ ->willReturnSelf();
+ $sut->expects($this->once())
->method('setAttributes')
- ->with($this->equalTo($attributes));
+ ->with($this->equalTo($attributes))
+ ->willReturnSelf();
- $reflectedClass = new \ReflectionClass($className);
+ $reflectedClass = new \ReflectionClass($this->className);
$constructor = $reflectedClass->getConstructor();
- $constructor->invoke($mock, $tagName, $content, $attributes);
+ $constructor->invoke($sut, $tagName, $content, $attributes);
}
- public function testGetAttributes()
+ #[Test]
+ #[TestDox('Get attributes.')]
+ public function getAttributes(): void
{
$tag1 = new Tag('div', ['bla']);
@@ -54,7 +55,9 @@ public function testGetAttributes()
$this->assertInstanceOf(AttributeList::class, $tag2->getAttributes());
}
- public function testGetContent()
+ #[Test]
+ #[TestDox('Get content.')]
+ public function getContent(): void
{
$tag1 = new Tag('div');
@@ -65,7 +68,9 @@ public function testGetContent()
$this->assertInstanceOf(NodeList::class, $tag2->getContent());
}
- public function testGetIterator()
+ #[Test]
+ #[TestDox('Get the default iterator.')]
+ public function getIterator(): void
{
$tag1 = new Tag('div');
@@ -76,7 +81,9 @@ public function testGetIterator()
$this->assertInstanceOf(PreOrderIterator::class, $tag2->getIterator());
}
- public function testGetIteratorPreOrder()
+ #[Test]
+ #[TestDox('Get the perorder iterator.')]
+ public function getIteratorPreOrder(): void
{
$tag1 = new Tag('div');
@@ -87,7 +94,9 @@ public function testGetIteratorPreOrder()
$this->assertInstanceOf(PreOrderIterator::class, $tag2->getIteratorPreOrder());
}
- public function testJsonSerialize()
+ #[Test]
+ #[TestDox('Json serialize.')]
+ public function jsonSerialize(): void
{
$nlMock = $this->createMock(NodeList::class);
@@ -101,17 +110,14 @@ public function testJsonSerialize()
->method('jsonSerialize')
->willReturn(['class' => 'btn btn-primary']);
- $mock = $this->getMockBuilder(Tag::class)
- ->setMethods(['getAttributes', 'getContent', 'getTagName'])
- ->getMock();
-
- $mock->expects($this->once())
+ $sut = $this->getMockTag(['getAttributes', 'getContent', 'getTagName']);
+ $sut->expects($this->once())
->method('getAttributes')
->willReturn($alMock);
- $mock->expects($this->once())
+ $sut->expects($this->once())
->method('getContent')
->willReturn($nlMock);
- $mock->expects($this->once())
+ $sut->expects($this->once())
->method('getTagName')
->willReturn('span');
@@ -121,164 +127,219 @@ public function testJsonSerialize()
'tag' => 'span',
];
- $this->assertEquals($expected, json_decode(json_encode($mock), JSON_OBJECT_AS_ARRAY));
- }
-
- public function testSetAttributes()
- {
- $mock = $this->getMockBuilder(Tag::class)
- ->setMethods(['setTagName'])
- ->disableOriginalConstructor()
- ->getMock();
-
- $mock->setAttributes(['class' => 'test']);
-
- $this->assertInstanceOf(AttributeList::class, $mock->getAttributes());
+ $this->assertEquals($expected, json_decode(json_encode($sut), JSON_OBJECT_AS_ARRAY));
}
- public function testSetAttributesAttributeList()
+ #[Test]
+ #[TestDox('Set attributes.')]
+ public function setAttributes(): void
{
- $mock = $this->getMockBuilder(Tag::class)
- ->setMethods(['setTagName'])
- ->disableOriginalConstructor()
- ->getMock();
-
- $mock->setAttributes((new AttributeList()));
-
- $this->assertInstanceOf(AttributeList::class, $mock->getAttributes());
+ $attributes = ['class' => 'btn btn-primary'];
+ $sut = $this->getMockTag([]);
+
+ /** @var Tag $sut */
+ $sut->setAttributes($attributes);
+
+ $return = $sut->getAttributes();
+ $this->assertInstanceOf(AttributeList::class, $return);
+ $reflectedClassAttrList = new \ReflectionClass(AttributeList::class);
+ $itemsProperty = $reflectedClassAttrList->getProperty('items');
+ $itemsProperty->setAccessible(true);
+ $items = $itemsProperty->getValue($return);
+ $this->assertCount(1, $items);
}
- public function testSetAttributesEmpty()
+ #[Test]
+ #[TestDox('Set attributes with an attribute list.')]
+ public function setAttributesAttributeList(): void
{
- $mock = $this->getMockBuilder(Tag::class)
- ->setMethods(['setTagName'])
- ->disableOriginalConstructor()
- ->getMock();
+ $attrList = new AttributeList();
+ $sut = $this->getMockTag([]);
- $mock->setAttributes([]);
+ /** @var Tag $sut */
+ $sut->setAttributes($attrList);
- $this->assertInstanceOf(AttributeList::class, $mock->getAttributes());
+ $this->assertSame($attrList, $sut->getAttributes());
}
- public function testSetAttributesWithException()
+ #[Test]
+ #[TestDox('Set attributes with an empty array.')]
+ public function setAttributesEmpty(): void
{
- $mock = $this->getMockBuilder(Tag::class)
- ->setMethods(['setTagName'])
- ->disableOriginalConstructor()
- ->getMock();
+ $sut = $this->getMockTag([]);
- $this->expectException(\InvalidArgumentException::class);
+ /** @var Tag $sut */
+ $sut->setAttributes([]);
- $mock->setAttributes((new NodeList()));
+ $attrList = $sut->getAttributes();
+ $this->assertInstanceOf(AttributeList::class, $attrList);
+ $this->assertEmpty($attrList);
}
- public function testSetContent()
+ #[Test]
+ #[TestDox('Set content.')]
+ public function setContent(): void
{
- $mock = $this->getMockBuilder(Tag::class)
- ->setMethods(['setTagName'])
- ->disableOriginalConstructor()
- ->getMock();
-
- $mock->setContent(['test text']);
-
- $this->assertInstanceOf(NodeList::class, $mock->getContent());
- }
-
- public function testSetContentEmpty()
- {
- $mock = $this->getMockBuilder(Tag::class)
- ->setMethods(['setTagName'])
- ->disableOriginalConstructor()
- ->getMock();
-
- $mock->setContent([]);
-
- $this->assertInstanceOf(NodeList::class, $mock->getContent());
+ $content = ['test text'];
+ $sut = $this->getMockTag([]);
+
+ $reflectedClass = new \ReflectionClass($this->className);
+ $tagName = $reflectedClass->getProperty('tagName');
+ $tagName->setAccessible(true);
+ $tagName->setValue($sut, 'div');
+
+ /** @var Tag $sut */
+ $sut->setContent($content);
+
+ $return = $sut->getContent();
+ $this->assertInstanceOf(NodeList::class, $return);
+ $reflectedClassNodeList = new \ReflectionClass(NodeList::class);
+ $itemsProperty = $reflectedClassNodeList->getProperty('items');
+ $itemsProperty->setAccessible(true);
+ $items = $itemsProperty->getValue($return);
+ $this->assertCount(1, $items);
+ $this->assertInstanceOf(Text::class, $items[0]);
+ $reflectedClassText = new \ReflectionClass(Text::class);
+ $contentProperty = $reflectedClassText->getProperty('content');
+ $contentProperty->setAccessible(true);
+ $contentFromProperty = $contentProperty->getValue($items[0]);
+ $this->assertSame($content[0], $contentFromProperty);
}
- public function testSetTagName()
+ #[Test]
+ #[TestDox('Set content with an empty array.')]
+ public function setContentEmpty(): void
{
- $mock = $this->getMockBuilder(Tag::class)
- ->setMethods(['isSelfClosing'])
- ->disableOriginalConstructor()
- ->getMock();
-
- $mock->setTagName('test');
-
- $this->assertEquals('test', $mock->getTagName());
+ $sut = $this->getMockTag([]);
+
+ $reflectedClass = new \ReflectionClass($this->className);
+ $tagName = $reflectedClass->getProperty('tagName');
+ $tagName->setAccessible(true);
+ $tagName->setValue($sut, 'div');
+
+ /** @var Tag $sut */
+ $sut->setContent([]);
+
+ $content = $sut->getContent();
+ $this->assertInstanceOf(NodeList::class, $content);
+ $reflectedClass = new \ReflectionClass(NodeList::class);
+ $items = $reflectedClass->getProperty('items');
+ $items->setAccessible(true);
+ $this->assertEmpty($items->getValue($content));
}
- public function testSetTagNameWithException()
+ #[Test]
+ #[TestDox('Set tag name.')]
+ public function setTagName(): void
{
- $mock = $this->getMockBuilder(Tag::class)
- ->setMethods(['isSelfClosing'])
- ->disableOriginalConstructor()
- ->getMock();
+ $tagName = 'test';
+ $sut = $this->getMockTag([]);
- $this->expectException(\InvalidArgumentException::class);
+ /** @var Tag $sut */
+ $return = $sut->setTagName($tagName);
- $mock->setTagName(123);
+ $this->assertEquals($tagName, $sut->getTagName());
+ $this->assertSame($sut, $return);
}
- public function testSetContentSelfClosing()
+ #[Test]
+ #[TestDox('Set content self closing with an exception.')]
+ public function setContentSelfClosing(): void
{
- $mock = $this->getMockBuilder(Tag::class)
- ->setMethods(['isSelfClosing'])
- ->disableOriginalConstructor()
- ->getMock();
-
- $mock->expects($this->once())
+ $sut = $this->getMockTag(['isSelfClosing']);
+ $sut->expects($this->once())
->method('isSelfClosing')
->willReturn(true);
+ $reflectedClass = new \ReflectionClass($this->className);
+ $tagName = $reflectedClass->getProperty('tagName');
+ $tagName->setAccessible(true);
+ $tagName->setValue($sut, 'br');
+
$this->expectException(\BadMethodCallException::class);
- $mock->setContent(['test text']);
+ /** @var Tag $sut */
+ $sut->setContent(['test text']);
}
- public function testToString()
+ #[Test]
+ #[TestDox('Type conversion to string.')]
+ public function render(): void
{
- $mock = $this->getMockBuilder(Tag::class)
- ->setMethods(['getAttributes', 'getContent', 'getTagName', 'isSelfClosing'])
+ $tagName = 'div';
+ $attr = 'class="btn btn-primary"';
+ $content = 'test text.';
+ $attrList = $this->getMockAttributeList($attr);
+ $nodeList = $this->getMockBuilder(NodeList::class)
+ ->onlyMethods(['__toString'])
->getMock();
+ $nodeList->expects($this->once())
+ ->method('__toString')
+ ->willReturn($content);
- $mock->expects($this->once())
+ $sut = $this->getMockTag(['getAttributes', 'getContent', 'getTagName', 'isSelfClosing']);
+ $sut->expects($this->once())
->method('getTagName')
- ->willReturn('div');
- $mock->expects($this->once())
+ ->willReturn($tagName);
+ $sut->expects($this->once())
->method('getAttributes')
- ->willReturn('class="btn btn-primary"');
- $mock->expects($this->once())
+ ->willReturn($attrList);
+ $sut->expects($this->once())
->method('isSelfClosing')
->willReturn(false);
- $mock->expects($this->once())
+ $sut->expects($this->once())
->method('getContent')
- ->willReturn('test text.');
+ ->willReturn($nodeList);
- $expected = '