Skip to content

Commit

Permalink
CloneTest: Add testFormELementsCloning with helper functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Timm Ortloff committed Dec 15, 2022
1 parent eb24387 commit 68c821b
Showing 1 changed file with 186 additions and 15 deletions.
201 changes: 186 additions & 15 deletions tests/CloneTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,154 @@

namespace ipl\Tests\Html;

use Closure;
use InvalidArgumentException;
use ipl\Html\Attribute;
use ipl\Html\Attributes;
use ipl\Html\BaseHtmlElement;
use ipl\Html\Form;
use ipl\Html\FormElement\InputElement;
use ipl\Html\FormElement\SelectElement;
use ipl\Html\FormElement\SubmitElement;
use ReflectionException;
use ReflectionFunction;
use ReflectionProperty;

class CloneTest extends TestCase
{
public const IGNORED_FILES = [
"BaseFormElement.php",
"FormElements.php",
"RadioOption.php"
];

public const ELEMENT_PARAMS = [
"_defaultName" => 'default-name',
"ButtonElement" => [],
"SelectElement" => [
'label' => 'test-label',
'options' => [
1 => 'option 1',
'option 2' => 2
]
],
"SelectOption" => 'test-value'
];

public static function noopCallback()
{
}

/**
* @throws ReflectionException
*/
public function testFormElementsCloning(): void
{
$elementsDir = realpath(__DIR__ . '/../src/FormElement');

if ($handle = opendir($elementsDir)) {
while (false !== ($file = readdir($handle))) {
if ($file === '.' || $file === '..' || in_array($file, self::IGNORED_FILES)) {
continue;
}

include_once $elementsDir . '/' . $file;

$className = explode(".", $file)[0];
$prefix = uniqid();
$classPath = "ipl\\Html\\FormElement\\" . $className;

/** @var BaseHtmlElement $element */
$element = new $classPath(
self::ELEMENT_PARAMS['_defaultName'],
self::ELEMENT_PARAMS[$className] ?? []
);

$this->registerCallbackFor($element, 'test-global-scope', 'strtolower');
$this->registerCallbackFor($element, 'test-instance-scope-noop-inline', function () {
});
$this->registerCallbackFor($element, 'test-instance-noop-attribute', [$this, 'noopCallback']);

$this->registerCallbackFor(
$element,
'test-closure-static-scope-noop',
Closure::fromCallable('self::noopCallback')
);

$this->registerCallbackFor(
$element,
'test-closure-instance-scope-noop',
Closure::fromCallable([$this, 'noopCallback'])
);

$this->registerCallbackFor(
$element,
'test-closure-instance-element-get-label',
Closure::fromCallable([$element, 'getLabel'])
);

$clone = clone $element;

$this->assertCallbacksFor($element, $this);
$this->assertCallbacksFor($clone, $this);
}
}
}

/**
* @param BaseHtmlElement $element
* @param string $name
* @param callable|null $callback
* @param callable|null $setterCallback
*
* @return void
*/
public function registerCallbackFor(
BaseHtmlElement $element,
string $name,
?callable $callback,
?callable $setterCallback = null
): void
{
$element->getAttributes()->registerAttributeCallback($name, $callback, $setterCallback);
}

public function assertCallbacksFor(object $element, object $thisRef)
{
$this->assertGlobalOrStaticCallback($element->getAttributes(), 'test-global-scope');
$this->assertCallbackBelongsTo($element->getAttributes(), 'test-instance-scope-noop-inline', $thisRef);
$this->assertCallbackBelongsTo(
$element->getAttributes(),
'test-instance-noop-attribute',
$thisRef
);
$this->assertGlobalOrStaticCallback(
$element->getAttributes(),
'test-closure-static-scope-noop'
);
$this->assertGlobalOrStaticCallback(
$element->getAttributes(),
'test-closure-instance-scope-noop'
);
$this->assertCallbackBelongsTo(
$element->getAttributes(),
'test-closure-instance-element-get-label',
$element
);
}

public function assertGlobalOrStaticCallback(Attributes $attributes, string $callbackName)
{
$callback = $this->getAttributeCallback($attributes, $callbackName);
$this->assertTrue($this->isCallbackGlobalOrStatic($callback));
}

public function assertCallbackBelongsTo(Attributes $attributes, string $callbackName, object $owner)
{
$callback = $this->getAttributeCallback($attributes, $callbackName);
$callbackThis = $this->getCallbackThis($callback);
$this->assertTrue(spl_object_id($callbackThis) === spl_object_id($owner));
}

public function testCloningSubmitElement(): void
{
$originalButton = new SubmitElement('exampleButton', ['class' => 'class01']);
Expand Down Expand Up @@ -69,33 +206,67 @@ public function testCloningAttributes(): void
$this->assertNotSame($original, $clone);
}

public function testCallbacks()
/**
* @param callable $callback
*
* @return bool
* @throws ReflectionException
*/
protected function isCallbackGlobalOrStatic(callable $callback): bool
{
$input = (new InputElement('text', 'text'))
->setValue('original');
$clone = (clone $input)
->setValue('clone');
if (! $callback instanceof Closure) {
if (is_array($callback) && ! is_string($callback[0])) {
return false;
}
} else {
$closureThis = (new ReflectionFunction($callback))
->getClosureThis();

if ($closureThis) {
return false;
}
}

$this->assertEquals('original', $this->getAttributeCallbackValue($input->getAttributes(), 'value'));
$this->assertEquals('clone', $this->getAttributeCallbackValue($clone->getAttributes(), 'value'));
return true;
}

protected function getAttributeCallbackValue(Attributes $attributes, string $name)
/**
* @param Attributes $attributes
* @param string $name
*
* @return mixed
* @throws ReflectionException
*/
protected function getAttributeCallback(Attributes $attributes, string $name)
{
$callbacksProperty = new ReflectionProperty(get_class($attributes), 'callbacks');
$callbacksProperty->setAccessible(true);
$callbacks = $callbacksProperty->getValue($attributes);

if (isset($callbacks[$name])) {
$attribute = $callbacks[$name]();
return $callbacks[$name];
}

if ($attribute instanceof Attribute) {
return $attribute->getValue();
}
throw new InvalidArgumentException();
}

return $attribute;
/**
* @param callable $callback
*
* @return mixed|object|null
* @throws ReflectionException
*/
protected function getCallbackThis(callable $callback)
{
if (! $callback instanceof Closure) {
if (is_array($callback) && ! is_string($callback[0])) {
return $callback[0];
} else {
return null;
}
}

throw new InvalidArgumentException();
return (new ReflectionFunction($callback))
->getClosureThis();
}
}

0 comments on commit 68c821b

Please sign in to comment.