mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-12 23:14:18 +01:00
N°8772 - Form dependencies manager implementation
- Form SDK implementation - Basic Forms - Dynamics Forms - Basic Blocks + Data Model Block - Form Compilation - Turbo integration
This commit is contained in:
@@ -69,6 +69,7 @@
|
||||
<file>../../core/apc-emulation.php</file>
|
||||
<file>../../core/ormlinkset.class.inc.php</file>
|
||||
<file>../../datamodels/2.x/itop-tickets/main.itop-tickets.php</file>
|
||||
<directory>../../sources/Forms/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
|
||||
@@ -10,9 +10,11 @@ namespace Combodo\iTop\Test\UnitTest;
|
||||
use CMDBSource;
|
||||
use DeprecatedCallsLog;
|
||||
use MySQLTransactionNotClosedException;
|
||||
use ParseError;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use ReflectionMethod;
|
||||
use SetupUtils;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\ErrorHandler\Error\FatalError;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
use const DEBUG_BACKTRACE_IGNORE_ARGS;
|
||||
@@ -583,6 +585,23 @@ abstract class ItopTestCase extends KernelTestCase
|
||||
self::assertLessThan(2, $iTimeInterval, $sMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.3.0
|
||||
*/
|
||||
protected static function AssertPHPCodeIsValid(string $sPHPCode, $sMessage = ''): void
|
||||
{
|
||||
try {
|
||||
eval($sPHPCode);
|
||||
} catch (ParseError $e) {
|
||||
$aLines = explode("\n", $sPHPCode);
|
||||
foreach ($aLines as $iLine => $sLine) {
|
||||
echo sprintf("%02d: %s\n", $iLine + 1, $sLine);
|
||||
}
|
||||
echo 'Parse Error: '.$e->getMessage().' at line: '.$e->getLine()."\n";
|
||||
self::fail($sMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Control which Kernel will be loaded when invoking the bootKernel method
|
||||
*
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\sources\Forms;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\FormBlock;
|
||||
use Combodo\iTop\Forms\IO\Format\StringIOFormat;
|
||||
use Combodo\iTop\Forms\IO\FormBlockIOException;
|
||||
use Combodo\iTop\Forms\IO\FormInput;
|
||||
use Combodo\iTop\Forms\IO\FormOutput;
|
||||
use Combodo\iTop\Forms\Register\IORegister;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
abstract class AbstractFormsTest extends ItopDataTestCase
|
||||
{
|
||||
/**
|
||||
* @throws FormBlockIOException
|
||||
*/
|
||||
public function GivenInput(string $sName, string $sType = StringIOFormat::class): FormInput
|
||||
{
|
||||
$oBlock = $this->GivenFormBlock($sName);
|
||||
|
||||
$oInput = new FormInput($sName, $sType);
|
||||
$oInput->SetOwnerBlock($oBlock);
|
||||
|
||||
return $oInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FormBlockIOException
|
||||
*/
|
||||
public function GivenOutput(string $sName, string $sType = StringIOFormat::class): FormOutput
|
||||
{
|
||||
$oBlock = $this->GivenFormBlock($sName);
|
||||
|
||||
$oOutput = new FormOutput($sName, $sType);
|
||||
$oOutput->SetOwnerBlock($oBlock);
|
||||
|
||||
return $oOutput;
|
||||
}
|
||||
|
||||
public function GivenFormBlock(string $sName): FormBlock
|
||||
{
|
||||
return new FormBlock($sName, []);
|
||||
}
|
||||
|
||||
public function GivenSubFormBlock(FormBlock $oParent, string $sName, string $ssBlockClass = FormBlock::class): AbstractFormBlock
|
||||
{
|
||||
$oParent->Add($sName, $ssBlockClass, []);
|
||||
|
||||
return $oParent->Get($sName);
|
||||
}
|
||||
|
||||
public function GivenIORegister(AbstractFormBlock $oFormBlock): IORegister
|
||||
{
|
||||
$reflection = new ReflectionClass(AbstractFormBlock::class);
|
||||
$reflection_property = $reflection->getProperty('oIORegister');
|
||||
$reflection_property->setAccessible(true);
|
||||
return $reflection_property->getValue($oFormBlock);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Sources\Forms\Block;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractTypeFormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\CheckboxFormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\FormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\TextFormBlock;
|
||||
use Combodo\iTop\Forms\Block\FormBlockException;
|
||||
use Combodo\iTop\Forms\Block\IFormBlock;
|
||||
use Combodo\iTop\Forms\Forms;
|
||||
use Combodo\iTop\ItopSdkFormDemonstrator\Form\Block\Dashboard\GenericDashlet;
|
||||
use Combodo\iTop\Service\InterfaceDiscovery\InterfaceDiscovery;
|
||||
use Combodo\iTop\Test\UnitTest\sources\Forms\AbstractFormsTest;
|
||||
use OutOfBoundsException;
|
||||
use ReflectionException;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
|
||||
/**
|
||||
* Test forms block.
|
||||
*
|
||||
*/
|
||||
class BlockTest extends AbstractFormsTest
|
||||
{
|
||||
/**
|
||||
* Block get form type must return a class derived from Symfony form AbstractType.
|
||||
*
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function testGetFormTypeReturnSymfonyType(): void
|
||||
{
|
||||
$aFormBlocks = InterfaceDiscovery::GetInstance()->FindItopClasses(iFormBlock::class);
|
||||
foreach ($aFormBlocks as $sFormBlock) {
|
||||
$oChoiceBlock = new ($sFormBlock)($sFormBlock);
|
||||
if ($oChoiceBlock instanceof AbstractTypeFormBlock) {
|
||||
if (!$oChoiceBlock instanceof GenericDashlet) {
|
||||
$oClass = new \ReflectionClass($oChoiceBlock->GetFormType());
|
||||
$this->assertTrue($oClass->isSubclassOf(AbstractType::class));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass a Symfony type instead of a FormBlock type will raise an exception
|
||||
*
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function testAddChildBlockExpectFormBlockClass(): void
|
||||
{
|
||||
$oFormBlock = new FormBlock('formBlock');
|
||||
$this->expectException(FormBlockException::class);
|
||||
$oFormBlock->Add('wrong', TextType::class, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* All block must contain a reference to themselves in their options
|
||||
*/
|
||||
public function testBlockOptionsContainsBlockReference(): void
|
||||
{
|
||||
$aFormBlocks = InterfaceDiscovery::GetInstance()->FindItopClasses(iFormBlock::class);
|
||||
foreach ($aFormBlocks as $sFormBlock) {
|
||||
$oChoiceBlock = new ($sFormBlock)($sFormBlock);
|
||||
$this->assertTrue($oChoiceBlock->GetOption('form_block') === $oChoiceBlock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a block with dependencies return true for HasDependenciesBlocks.
|
||||
*
|
||||
* @return void
|
||||
* @throws FormBlockException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function testCheckDependencyState(): void
|
||||
{
|
||||
$oFormBlock = new FormBlock('formBlock');
|
||||
$oFormBlock->Add('allow_age', CheckboxFormBlock::class, []);
|
||||
$oBirthdateBlock = $oFormBlock->Add('birthdate', TextFormBlock::class, [])
|
||||
->InputDependsOn(AbstractTypeFormBlock::INPUT_VISIBLE, 'allow_age', CheckboxFormBlock::OUTPUT_CHECKED);
|
||||
|
||||
$this->assertTrue($oBirthdateBlock->HasDependenciesBlocks());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependent fields are not added to the form directly.
|
||||
*
|
||||
* @return void
|
||||
* @throws FormBlockException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function testFormBlockNotContainsDependentFields(): void
|
||||
{
|
||||
// form with a dependent field
|
||||
$oFormBlock = new FormBlock('formBlock');
|
||||
$oFormBlock->Add('firstname', TextFormBlock::class, []);
|
||||
$oFormBlock->Add('lastname', TextFormBlock::class, []);
|
||||
$oFormBlock->Add('allow_age', CheckboxFormBlock::class, []);
|
||||
$oFormBlock->Add('birthdate', TextFormBlock::class, [])
|
||||
->InputDependsOn(AbstractTypeFormBlock::INPUT_VISIBLE, 'allow_age', CheckboxFormBlock::OUTPUT_CHECKED);
|
||||
|
||||
// form builder
|
||||
$oFormFactoryBuilder = Forms::createFormFactoryBuilder();
|
||||
$oForm = $oFormFactoryBuilder->getFormFactory()->createNamedBuilder($oFormBlock->GetName(), $oFormBlock->GetFormType(), [], $oFormBlock->GetOptions())->getForm();
|
||||
|
||||
// try to get the dependent field
|
||||
$this->expectException(OutOfBoundsException::class);
|
||||
$oForm->get('birthdate');
|
||||
}
|
||||
|
||||
public function testIsRootBlock(): void
|
||||
{
|
||||
/** @var FormBlock $oFormBlock */
|
||||
$oFormBlock = $this->GivenFormBlock('OneBlock');
|
||||
|
||||
$oFormBlock->Add('subform', FormBlock::class);
|
||||
|
||||
$this->assertTrue($oFormBlock->IsRootBlock());
|
||||
$this->assertFalse($oFormBlock->Get('subform')->IsRootBlock());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,751 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Forms\Compiler\FormsCompiler;
|
||||
use Combodo\iTop\Service\DependencyInjection\DIService;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
class FormsCompilerTest extends ItopDataTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider CompileFormFromXMLProvider
|
||||
*
|
||||
* @param string $sXMLContent
|
||||
* @param string $sExpectedPHP
|
||||
*
|
||||
* @return void
|
||||
* @throws \Combodo\iTop\Forms\Compiler\FormsCompilerException
|
||||
* @throws \Combodo\iTop\PropertyTree\PropertyTreeException
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
public function testCompileFormFromXML(string $sXMLContent, string $sExpectedPHP)
|
||||
{
|
||||
DIService::GetInstance()->RegisterService('ModelReflection', new ModelReflectionRuntime());
|
||||
|
||||
$sProducedPHP = FormsCompiler::GetInstance()->CompileFormFromXML($sXMLContent);
|
||||
|
||||
$this->AssertPHPCodeIsValid($sProducedPHP);
|
||||
$sMessage = $this->dataName();
|
||||
$this->assertEquals($sExpectedPHP, $sProducedPHP, $sMessage);
|
||||
}
|
||||
|
||||
public function CompileFormFromXMLProvider()
|
||||
{
|
||||
return [
|
||||
'Basic scalar properties should generate PHP' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<property_tree id="basic_test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="title_property">
|
||||
<label>UI:BasicTest:Prop-Title</label>
|
||||
<value-type xsi:type="Combodo-ValueType-Label">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="class_property">
|
||||
<label>UI:BasicTest:Prop-Class</label>
|
||||
<value-type xsi:type="Combodo-ValueType-Class">
|
||||
<categories-csv>test</categories-csv>
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</property_tree>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__basic_test extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
\$this->Add('title_property', 'Combodo\iTop\Forms\Block\DataModel\LabelFormBlock', [
|
||||
'label' => 'UI:BasicTest:Prop-Title',
|
||||
]);
|
||||
|
||||
\$this->Add('class_property', 'Combodo\iTop\Forms\Block\Base\ChoiceFormBlock', [
|
||||
'label' => 'UI:BasicTest:Prop-Class',
|
||||
'choices' => [
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
|
||||
'Empty property tree should generate minimal PHP' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<property_tree id="EmptyTest" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
</nodes>
|
||||
</property_tree>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__EmptyTest extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{ }
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
|
||||
'Empty property tree lower case should generate lower case class name' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<property_tree id="empty_test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
</nodes>
|
||||
</property_tree>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__empty_test extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{ }
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
|
||||
'Properties with all value-types' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<property_tree id="AllValueTypesTest" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="aggregate_function_property">
|
||||
<label>UI:AggregateFunction</label>
|
||||
<value-type xsi:type="Combodo-ValueType-AggregateFunction">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="choice_property">
|
||||
<label>UI:Choice</label>
|
||||
<value-type xsi:type="Combodo-ValueType-Choice">
|
||||
<values>
|
||||
<value id="value_a">
|
||||
<label>Label A</label>
|
||||
</value>
|
||||
<value id="value_b">
|
||||
<label>Label B</label>
|
||||
</value>
|
||||
</values>
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="class_property">
|
||||
<label>UI:Class</label>
|
||||
<value-type xsi:type="Combodo-ValueType-Class">
|
||||
<categories-csv>test</categories-csv>
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="class_attribute_property">
|
||||
<label>UI:ClassAttribute</label>
|
||||
<value-type xsi:type="Combodo-ValueType-ClassAttribute">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="class_attribute_group_by_property">
|
||||
<label>UI:ClassAttributeGroupBy</label>
|
||||
<value-type xsi:type="Combodo-ValueType-ClassAttributeGroupBy">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="class_attribute_value_property">
|
||||
<label>UI:ClassAttributeValue</label>
|
||||
<value-type xsi:type="Combodo-ValueType-ClassAttributeValue">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="integer_property">
|
||||
<label>UI:Integer</label>
|
||||
<value-type xsi:type="Combodo-ValueType-Integer">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="label_property">
|
||||
<label>UI:Label</label>
|
||||
<value-type xsi:type="Combodo-ValueType-Label">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="oql_property">
|
||||
<label>UI:OQL</label>
|
||||
<value-type xsi:type="Combodo-ValueType-OQL">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="string_property">
|
||||
<label>UI:String</label>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="choice_from_input">
|
||||
<label>UI:ChoiceFromInput</label>
|
||||
<value-type xsi:type="Combodo-ValueType-ChoiceFromInput">
|
||||
<values>
|
||||
<value id="value_a">
|
||||
<label>{{class_attribute_property.label}}</label>
|
||||
</value>
|
||||
<value id="value_b">
|
||||
<label>{{class_attribute_group_by_property.label}}</label>
|
||||
</value>
|
||||
</values>
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</property_tree>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__AllValueTypesTest extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
\$this->Add('aggregate_function_property', 'Combodo\iTop\Forms\Block\DataModel\Dashlet\AggregateFunctionFormBlock', [
|
||||
'label' => 'UI:AggregateFunction',
|
||||
]);
|
||||
|
||||
\$this->Add('choice_property', 'Combodo\iTop\Forms\Block\Base\ChoiceFormBlock', [
|
||||
'label' => 'UI:Choice',
|
||||
'choices' => [
|
||||
\Dict::S('Label A') => 'value_a',
|
||||
\Dict::S('Label B') => 'value_b',
|
||||
],
|
||||
]);
|
||||
|
||||
\$this->Add('class_property', 'Combodo\iTop\Forms\Block\Base\ChoiceFormBlock', [
|
||||
'label' => 'UI:Class',
|
||||
'choices' => [
|
||||
],
|
||||
]);
|
||||
|
||||
\$this->Add('class_attribute_property', 'Combodo\iTop\Forms\Block\DataModel\AttributeChoiceFormBlock', [
|
||||
'label' => 'UI:ClassAttribute',
|
||||
]);
|
||||
|
||||
\$this->Add('class_attribute_group_by_property', 'Combodo\iTop\Forms\Block\DataModel\Dashlet\ClassAttributeGroupByFormBlock', [
|
||||
'label' => 'UI:ClassAttributeGroupBy',
|
||||
]);
|
||||
|
||||
\$this->Add('class_attribute_value_property', 'Combodo\iTop\Forms\Block\DataModel\AttributeValueChoiceFormBlock', [
|
||||
'label' => 'UI:ClassAttributeValue',
|
||||
]);
|
||||
|
||||
\$this->Add('integer_property', 'Combodo\iTop\Forms\Block\Base\IntegerFormBlock', [
|
||||
'label' => 'UI:Integer',
|
||||
]);
|
||||
|
||||
\$this->Add('label_property', 'Combodo\iTop\Forms\Block\DataModel\LabelFormBlock', [
|
||||
'label' => 'UI:Label',
|
||||
]);
|
||||
|
||||
\$this->Add('oql_property', 'Combodo\iTop\Forms\Block\DataModel\OqlFormBlock', [
|
||||
'label' => 'UI:OQL',
|
||||
]);
|
||||
|
||||
\$this->Add('string_property', 'Combodo\iTop\Forms\Block\Base\TextFormBlock', [
|
||||
'label' => 'UI:String',
|
||||
]);
|
||||
|
||||
\$this->Add('choice_from_input', 'Combodo\iTop\Forms\Block\Base\ChoiceFromInputsBlock', [
|
||||
'label' => 'UI:ChoiceFromInput',
|
||||
])
|
||||
->AddInputDependsOn('value_a', 'class_attribute_property', 'label')
|
||||
->AddInputDependsOn('value_b', 'class_attribute_group_by_property', 'label');
|
||||
}
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
|
||||
'Collection of trees' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<property_tree id="collection_of_trees_test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="sub_tree_collection">
|
||||
<label>UI:SubTree</label>
|
||||
<value-type xsi:type="Combodo-ValueType-Collection">
|
||||
<prototype>
|
||||
<node id="string_property">
|
||||
<label>UI:String</label>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="integer_property">
|
||||
<label>UI:Integer</label>
|
||||
<relevance-condition>{{string_property.text != 'no-display'}}</relevance-condition>
|
||||
<value-type xsi:type="Combodo-ValueType-Integer">
|
||||
</value-type>
|
||||
</node>
|
||||
</prototype>
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</property_tree>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class SubFormFor__collection_of_trees_test__sub_tree_collection extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
\$this->Add('string_property', 'Combodo\iTop\Forms\Block\Base\TextFormBlock', [
|
||||
'label' => 'UI:String',
|
||||
]);
|
||||
|
||||
\$this->Add('integer_property_visible_expression', 'Combodo\iTop\Forms\Block\Expression\BooleanExpressionFormBlock', [
|
||||
'expression' => 'string_property.text != \'no-display\'',
|
||||
])
|
||||
->AddInputDependsOn('string_property.text', 'string_property', 'text');
|
||||
|
||||
\$this->Add('integer_property', 'Combodo\iTop\Forms\Block\Base\IntegerFormBlock', [
|
||||
'label' => 'UI:Integer',
|
||||
])
|
||||
->InputDependsOn('visible', 'integer_property_visible_expression', 'result');
|
||||
}
|
||||
}
|
||||
|
||||
class FormFor__collection_of_trees_test extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
\$this->Add('sub_tree_collection', 'Combodo\iTop\Forms\Block\Base\CollectionBlock', [
|
||||
'label' => 'UI:SubTree',
|
||||
'button_label' => 'UI:AddSubTree',
|
||||
'block_entry_type' => 'SubFormFor__collection_of_trees_test__sub_tree_collection',
|
||||
]);
|
||||
}
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
|
||||
'Static inputs should be bound and invalid input should be ignored' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="input_static_test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="class_attribute_property">
|
||||
<label>UI:ClassAttribute</label>
|
||||
<value-type xsi:type="Combodo-ValueType-ClassAttribute">
|
||||
<class>Contact</class>
|
||||
<invalid-input>Test</invalid-input>
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__input_static_test extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
\$this->Add('class_attribute_property', 'Combodo\iTop\Forms\Block\DataModel\AttributeChoiceFormBlock', [
|
||||
'label' => 'UI:ClassAttribute',
|
||||
])
|
||||
->SetInputValue('class', 'Contact');
|
||||
}
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
|
||||
'Quotes should be handled gracefully' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="input_quotes_test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="class_attribute_property">
|
||||
<label>'Class' and "Attribute"</label>
|
||||
<value-type xsi:type="Combodo-ValueType-ClassAttribute">
|
||||
<class>{{CONCAT("'", '"')}}</class>
|
||||
<category>'Class' and "Attribute"</category>
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__input_quotes_test extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
\$this->Add('class_attribute_property_class_expression', 'Combodo\iTop\Forms\Block\Expression\StringExpressionFormBlock', [
|
||||
'expression' => 'CONCAT("\'", \'"\')',
|
||||
]);
|
||||
|
||||
\$this->Add('class_attribute_property', 'Combodo\iTop\Forms\Block\DataModel\AttributeChoiceFormBlock', [
|
||||
'label' => '\'Class\' and "Attribute"',
|
||||
])
|
||||
->InputDependsOn('class', 'class_attribute_property_class_expression', 'result')
|
||||
->SetInputValue('category', '\'Class\' and "Attribute"');
|
||||
}
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
|
||||
'Dynamic input should be bound' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="input_binding_test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="class_property">
|
||||
<label>UI:Class</label>
|
||||
<value-type xsi:type="Combodo-ValueType-Class">
|
||||
<categories-csv>test</categories-csv>
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="class_attribute_property">
|
||||
<label>UI:ClassAttribute</label>
|
||||
<value-type xsi:type="Combodo-ValueType-ClassAttribute">
|
||||
<class>{{class_property.text}}</class>
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__input_binding_test extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
\$this->Add('class_property', 'Combodo\iTop\Forms\Block\Base\ChoiceFormBlock', [
|
||||
'label' => 'UI:Class',
|
||||
'choices' => [
|
||||
],
|
||||
]);
|
||||
|
||||
\$this->Add('class_attribute_property', 'Combodo\iTop\Forms\Block\DataModel\AttributeChoiceFormBlock', [
|
||||
'label' => 'UI:ClassAttribute',
|
||||
])
|
||||
->InputDependsOn('class', 'class_property', 'text');
|
||||
}
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
|
||||
'Dynamic input can be an expression' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="input_binding_expression" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="class_property">
|
||||
<label>UI:Class</label>
|
||||
<value-type xsi:type="Combodo-ValueType-Class">
|
||||
<categories-csv>test</categories-csv>
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="class_attribute_property">
|
||||
<label>UI:ClassAttribute</label>
|
||||
<value-type xsi:type="Combodo-ValueType-ClassAttribute">
|
||||
<class>{{IF(class_property.value = '', 'Person', class_property.value)}}</class>
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__input_binding_expression extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
\$this->Add('class_property', 'Combodo\iTop\Forms\Block\Base\ChoiceFormBlock', [
|
||||
'label' => 'UI:Class',
|
||||
'choices' => [
|
||||
],
|
||||
]);
|
||||
|
||||
\$this->Add('class_attribute_property_class_expression', 'Combodo\iTop\Forms\Block\Expression\StringExpressionFormBlock', [
|
||||
'expression' => 'IF(class_property.value = \'\', \'Person\', class_property.value)',
|
||||
])
|
||||
->AddInputDependsOn('class_property.value', 'class_property', 'value');
|
||||
|
||||
\$this->Add('class_attribute_property', 'Combodo\iTop\Forms\Block\DataModel\AttributeChoiceFormBlock', [
|
||||
'label' => 'UI:ClassAttribute',
|
||||
])
|
||||
->InputDependsOn('class', 'class_attribute_property_class_expression', 'result');
|
||||
}
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
|
||||
'Relevance condition should generate a boolean block expression' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="RelevanceCondition" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="source_property">
|
||||
<label>UI:Source</label>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="dependant_property">
|
||||
<label>UI:Dependant</label>
|
||||
<relevance-condition>{{source_property.text != 'count'}}</relevance-condition>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__RelevanceCondition extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
\$this->Add('source_property', 'Combodo\iTop\Forms\Block\Base\TextFormBlock', [
|
||||
'label' => 'UI:Source',
|
||||
]);
|
||||
|
||||
\$this->Add('dependant_property_visible_expression', 'Combodo\iTop\Forms\Block\Expression\BooleanExpressionFormBlock', [
|
||||
'expression' => 'source_property.text != \'count\'',
|
||||
])
|
||||
->AddInputDependsOn('source_property.text', 'source_property', 'text');
|
||||
|
||||
\$this->Add('dependant_property', 'Combodo\iTop\Forms\Block\Base\TextFormBlock', [
|
||||
'label' => 'UI:Dependant',
|
||||
])
|
||||
->InputDependsOn('visible', 'dependant_property_visible_expression', 'result');
|
||||
}
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
|
||||
'Complex Relevance condition should generate a boolean block expression' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="ComplexRelevanceCondition" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="source_a_property">
|
||||
<label>UI:Source</label>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="source_b_property">
|
||||
<label>UI:Source</label>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="dependant_property">
|
||||
<label>UI:Dependant</label>
|
||||
<relevance-condition>{{IF(source_a_property.text != '', source_a_property.text, source_b_property.text)}}</relevance-condition>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__ComplexRelevanceCondition extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
\$this->Add('source_a_property', 'Combodo\iTop\Forms\Block\Base\TextFormBlock', [
|
||||
'label' => 'UI:Source',
|
||||
]);
|
||||
|
||||
\$this->Add('source_b_property', 'Combodo\iTop\Forms\Block\Base\TextFormBlock', [
|
||||
'label' => 'UI:Source',
|
||||
]);
|
||||
|
||||
\$this->Add('dependant_property_visible_expression', 'Combodo\iTop\Forms\Block\Expression\BooleanExpressionFormBlock', [
|
||||
'expression' => 'IF(source_a_property.text != \'\', source_a_property.text, source_b_property.text)',
|
||||
])
|
||||
->AddInputDependsOn('source_a_property.text', 'source_a_property', 'text')
|
||||
->AddInputDependsOn('source_b_property.text', 'source_b_property', 'text');
|
||||
|
||||
\$this->Add('dependant_property', 'Combodo\iTop\Forms\Block\Base\TextFormBlock', [
|
||||
'label' => 'UI:Dependant',
|
||||
])
|
||||
->InputDependsOn('visible', 'dependant_property_visible_expression', 'result');
|
||||
}
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
'Class category for value type class' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="ClassCategory" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="class_property">
|
||||
<label>UI:Class</label>
|
||||
<value-type xsi:type="Combodo-ValueType-Class">
|
||||
<categories-csv>addon/authentication,grant_by_profile,silo</categories-csv>
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__ClassCategory extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
\$this->Add('class_property', 'Combodo\iTop\Forms\Block\Base\ChoiceFormBlock', [
|
||||
'label' => 'UI:Class',
|
||||
'choices' => [
|
||||
\Dict::S('Class:ActionEmail') => 'ActionEmail',
|
||||
\Dict::S('Class:ActionNewsroom') => 'ActionNewsroom',
|
||||
\Dict::S('Class:AuditCategory') => 'AuditCategory',
|
||||
\Dict::S('Class:AuditDomain') => 'AuditDomain',
|
||||
\Dict::S('Class:AuditRule') => 'AuditRule',
|
||||
\Dict::S('Class:OAuthClientAzure') => 'OAuthClientAzure',
|
||||
\Dict::S('Class:OAuthClientGoogle') => 'OAuthClientGoogle',
|
||||
\Dict::S('Class:QueryOQL') => 'QueryOQL',
|
||||
\Dict::S('Class:SynchroAttExtKey') => 'SynchroAttExtKey',
|
||||
\Dict::S('Class:SynchroAttLinkSet') => 'SynchroAttLinkSet',
|
||||
\Dict::S('Class:SynchroAttribute') => 'SynchroAttribute',
|
||||
\Dict::S('Class:SynchroDataSource') => 'SynchroDataSource',
|
||||
\Dict::S('Class:SynchroLog') => 'SynchroLog',
|
||||
\Dict::S('Class:SynchroReplica') => 'SynchroReplica',
|
||||
\Dict::S('Class:TriggerOnAttachmentCreate') => 'TriggerOnAttachmentCreate',
|
||||
\Dict::S('Class:TriggerOnAttachmentDelete') => 'TriggerOnAttachmentDelete',
|
||||
\Dict::S('Class:TriggerOnAttachmentDownload') => 'TriggerOnAttachmentDownload',
|
||||
\Dict::S('Class:TriggerOnAttributeBlobDownload') => 'TriggerOnAttributeBlobDownload',
|
||||
\Dict::S('Class:TriggerOnObjectCreate') => 'TriggerOnObjectCreate',
|
||||
\Dict::S('Class:TriggerOnObjectDelete') => 'TriggerOnObjectDelete',
|
||||
\Dict::S('Class:TriggerOnObjectMention') => 'TriggerOnObjectMention',
|
||||
\Dict::S('Class:TriggerOnObjectUpdate') => 'TriggerOnObjectUpdate',
|
||||
\Dict::S('Class:TriggerOnPortalUpdate') => 'TriggerOnPortalUpdate',
|
||||
\Dict::S('Class:TriggerOnStateEnter') => 'TriggerOnStateEnter',
|
||||
\Dict::S('Class:TriggerOnStateLeave') => 'TriggerOnStateLeave',
|
||||
\Dict::S('Class:TriggerOnThresholdReached') => 'TriggerOnThresholdReached',
|
||||
\Dict::S('Class:URP_Profiles') => 'URP_Profiles',
|
||||
\Dict::S('Class:URP_UserOrg') => 'URP_UserOrg',
|
||||
\Dict::S('Class:UserExternal') => 'UserExternal',
|
||||
\Dict::S('Class:UserLDAP') => 'UserLDAP',
|
||||
\Dict::S('Class:UserLocal') => 'UserLocal',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
'test' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="Test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedPHP' => <<<PHP
|
||||
class FormFor__Test extends Combodo\iTop\Forms\Block\Base\FormBlock
|
||||
{
|
||||
protected function BuildForm(): void
|
||||
{ }
|
||||
}
|
||||
PHP,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider CompileFormFromInvalidXMLProvider
|
||||
* @param string $sXMLContent
|
||||
* @param string $sExpectedClass
|
||||
* @param string $sExpectedMessage
|
||||
*
|
||||
* @return void
|
||||
* @throws \Combodo\iTop\Forms\Compiler\FormsCompilerException
|
||||
* @throws \Combodo\iTop\PropertyTree\PropertyTreeException
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
public function testCompileFormFromInvalidXML(string $sXMLContent, string $sExpectedClass, string $sExpectedMessage)
|
||||
{
|
||||
$this->expectException($sExpectedClass);
|
||||
$this->expectExceptionMessage($sExpectedMessage);
|
||||
FormsCompiler::GetInstance()->CompileFormFromXML($sXMLContent);
|
||||
}
|
||||
|
||||
public function CompileFormFromInvalidXMLProvider()
|
||||
{
|
||||
return [
|
||||
'Invalid OQL expression in condition' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="RelevanceCondition" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="dependant_property">
|
||||
<label>UI:Dependant</label>
|
||||
<relevance-condition>{{source_property.text == 'count'}}</relevance-condition>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedClass' => 'Combodo\iTop\PropertyTree\PropertyTreeException',
|
||||
'sExpectedMessage' => 'Node: dependant_property, invalid syntax in condition: Unexpected token EQ - found \'=\' at 22 in \'source_property.text == \'count\'\'',
|
||||
],
|
||||
|
||||
'Unknown source in relevance condition' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="RelevanceCondition" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="dependant_property">
|
||||
<label>UI:Dependant</label>
|
||||
<relevance-condition>{{source_property.text = 'count'}}</relevance-condition>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedClass' => 'Combodo\iTop\PropertyTree\PropertyTreeException',
|
||||
'sExpectedMessage' => 'Node: dependant_property, invalid source in condition: source_property',
|
||||
],
|
||||
|
||||
'Unknown output in relevance condition' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="RelevanceCondition" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="source_property">
|
||||
<label>UI:Source</label>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="dependant_property">
|
||||
<label>UI:Dependant</label>
|
||||
<relevance-condition>{{source_property.text_output != 'count'}}</relevance-condition>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedClass' => 'Combodo\iTop\PropertyTree\PropertyTreeException',
|
||||
'sExpectedMessage' => 'Node: dependant_property, invalid output in condition: source_property.text_output',
|
||||
],
|
||||
|
||||
'Missing output or source in relevance condition' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="RelevanceCondition" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="source_property">
|
||||
<label>UI:Source</label>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
<node id="dependant_property">
|
||||
<label>UI:Dependant</label>
|
||||
<relevance-condition>{{source_property != 'count'}}</relevance-condition>
|
||||
<value-type xsi:type="Combodo-ValueType-String">
|
||||
</value-type>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedClass' => 'Combodo\iTop\PropertyTree\PropertyTreeException',
|
||||
'sExpectedMessage' => 'Node: dependant_property, missing output or source in condition: source_property',
|
||||
],
|
||||
|
||||
'Missing value-type in node specification' => [
|
||||
'sXMLContent' => <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node id="RelevanceCondition" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
|
||||
<nodes>
|
||||
<node id="source_property">
|
||||
<label>UI:Source</label>
|
||||
</node>
|
||||
</nodes>
|
||||
</node>
|
||||
XML,
|
||||
'sExpectedClass' => 'Combodo\iTop\PropertyTree\PropertyTreeException',
|
||||
'sExpectedMessage' => 'Node: source_property, missing value-type in node specification',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\sources\Forms\IO;
|
||||
|
||||
use Combodo\iTop\Forms\IO\FormBlockIOException;
|
||||
use Combodo\iTop\Test\UnitTest\sources\Forms\AbstractFormsTest;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
class AbstractFormIOTest extends AbstractFormsTest
|
||||
{
|
||||
public function testFormIoHasNoDataAtCreation()
|
||||
{
|
||||
|
||||
$oInput = $this->GivenInput('test');
|
||||
|
||||
$this->assertFalse($oInput->IsDataReady(), 'Created Input must no have data ready at creation');
|
||||
$this->assertFalse($oInput->HasValue(), 'Created Input must no have value at creation');
|
||||
$this->assertFalse($oInput->HasBindingOut());
|
||||
|
||||
$oOutput = $this->GivenOutput('test');
|
||||
|
||||
$this->assertFalse($oOutput->IsDataReady(), 'Created output must no have data ready at creation');
|
||||
$this->assertFalse($oOutput->HasValue(), 'Created output must no have value at creation');
|
||||
$this->assertFalse($oOutput->HasBindingOut());
|
||||
}
|
||||
|
||||
public function testFormIoHasDataAfterSetValue()
|
||||
{
|
||||
|
||||
$oInput = $this->GivenInput('test');
|
||||
$oInput->SetValue(FormEvents::POST_SET_DATA, 'test');
|
||||
|
||||
$this->assertTrue($oInput->IsDataReady(), 'Input must have data ready when set');
|
||||
$this->assertTrue($oInput->HasValue(), 'Input must have value when set');
|
||||
|
||||
$oOutput = $this->GivenOutput('test');
|
||||
$oOutput->SetValue(FormEvents::POST_SET_DATA, 'test');
|
||||
|
||||
$this->assertTrue($oOutput->IsDataReady(), 'Output must have data ready when set');
|
||||
$this->assertTrue($oOutput->HasValue(), 'Output must have value when set');
|
||||
}
|
||||
|
||||
public function testIOValueReflectsTheValuePostedOrTheValueSet()
|
||||
{
|
||||
$oInput = $this->GivenInput('test');
|
||||
|
||||
// When
|
||||
$oInput->SetValue(FormEvents::POST_SET_DATA, 'The value set');
|
||||
|
||||
// Then
|
||||
$this->assertEquals('The value set', $oInput->GetValue());
|
||||
|
||||
// When
|
||||
$oInput->SetValue(FormEvents::POST_SUBMIT, 'The value posted');
|
||||
|
||||
// Then
|
||||
$this->assertEquals('The value posted', $oInput->GetValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider NameFormatSupportsOnlyLettersUnderscoreAndNumbersProvider
|
||||
* @return void
|
||||
* @throws \Combodo\iTop\Forms\IO\FormBlockIOException
|
||||
*/
|
||||
public function testNameFormatSupportsOnlyLettersUnderscoreAndNumbersAndDot(string $sName, bool $bGenerateException = true)
|
||||
{
|
||||
|
||||
if ($bGenerateException) {
|
||||
$this->expectException(FormBlockIOException::class);
|
||||
}
|
||||
$oInput = $this->GivenInput($sName);
|
||||
if (!$bGenerateException) {
|
||||
$this->assertEquals($sName, $oInput->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
public function NameFormatSupportsOnlyLettersUnderscoreAndNumbersProvider()
|
||||
{
|
||||
return [
|
||||
// Incorrects
|
||||
'Spaces not supported' => ['The test name'],
|
||||
'Minus not supported' => ['The-test-name'],
|
||||
'Percent not supported' => ['name%'],
|
||||
'Accent not supported' => ['namé'],
|
||||
'emoji not supported' => ['🎄🎄🎄🎄🎄'],
|
||||
'.name not supported' => ['.name'],
|
||||
'name. not supported' => ['name.'],
|
||||
|
||||
// Corrects
|
||||
'Numbers OK' => ['name123', false],
|
||||
'Starting with number OK' => ['123name123', false],
|
||||
'Underscore OK' => ['The_test_name', false],
|
||||
'Camel OK' => ['TheTestName', false],
|
||||
'name.subname OK' => ['name.subname', false],
|
||||
];
|
||||
}
|
||||
|
||||
public function testCreatingIOWithUnknownFormatThrowsException()
|
||||
{
|
||||
$this->expectException(FormBlockIOException::class);
|
||||
$oInput = $this->GivenInput('test', 'test_toto');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Sources\Forms\IO;
|
||||
|
||||
use Combodo\iTop\Forms\IO\Format\AttributeIOFormat;
|
||||
use Combodo\iTop\Forms\IO\Format\BooleanIOFormat;
|
||||
use Combodo\iTop\Forms\IO\Format\ClassIOFormat;
|
||||
use Combodo\iTop\Forms\IO\Format\NumberIOFormat;
|
||||
use Combodo\iTop\Forms\IO\Format\StringIOFormat;
|
||||
use Combodo\iTop\Forms\IO\FormBinding;
|
||||
use Combodo\iTop\Forms\IO\FormBlockIOException;
|
||||
use Combodo\iTop\Test\UnitTest\sources\Forms\AbstractFormsTest;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class FormBindingTest extends AbstractFormsTest
|
||||
{
|
||||
public function testCreatingABinding()
|
||||
{
|
||||
$oInputIO = $this->GivenInput('test');
|
||||
$oOutputIO = $this->GivenOutput('test');
|
||||
|
||||
// When Linking output to input
|
||||
new FormBinding($oOutputIO, $oInputIO);
|
||||
|
||||
// Then
|
||||
$this->assertTrue($oInputIO->IsBound(), 'DestinationIO must be Bound when creating a new binding');
|
||||
}
|
||||
|
||||
public function testBindingTwiceToTheSameInputIsNotPossible()
|
||||
{
|
||||
$oInputIO = $this->GivenInput('test');
|
||||
$oOutputIO1 = $this->GivenOutput('test1');
|
||||
$oOutputIO2 = $this->GivenOutput('test2');
|
||||
|
||||
// When
|
||||
new FormBinding($oOutputIO1, $oInputIO);
|
||||
|
||||
// Then
|
||||
$this->expectException(FormBlockIOException::class);
|
||||
new FormBinding($oOutputIO2, $oInputIO);
|
||||
}
|
||||
|
||||
public function testBindingTwiceToTheSameOutputIsNotPossible()
|
||||
{
|
||||
$oOutputIO1 = $this->GivenOutput('test1');
|
||||
$oOutputIO2 = $this->GivenOutput('test2');
|
||||
$oOutputIO3 = $this->GivenOutput('test3');
|
||||
|
||||
// When
|
||||
new FormBinding($oOutputIO1, $oOutputIO3);
|
||||
|
||||
// Then
|
||||
$this->expectException(FormBlockIOException::class);
|
||||
new FormBinding($oOutputIO2, $oOutputIO3);
|
||||
|
||||
}
|
||||
|
||||
public function testOutputCanBeBoundToInputAndInputIsBoundAfterThat()
|
||||
{
|
||||
$oInputIO = $this->GivenInput('test');
|
||||
$oOutputIO = $this->GivenOutput('test1');
|
||||
|
||||
$this->assertFalse($oInputIO->IsBound(), 'Input must not be Bound by default');
|
||||
|
||||
// When
|
||||
$oOutputIO->BindToInput($oInputIO);
|
||||
|
||||
// Then
|
||||
$this->assertTrue($oInputIO->IsBound(), 'Input must be Bound when binding from an output');
|
||||
}
|
||||
|
||||
public function testInputCanBeBoundToAnotherInputAndItIsBoundAfterThat()
|
||||
{
|
||||
$oInputIO1 = $this->GivenInput('test1');
|
||||
$oInputIO2 = $this->GivenInput('test2');
|
||||
|
||||
// When
|
||||
$oInputIO1->BindToInput($oInputIO2);
|
||||
|
||||
// Then
|
||||
$this->assertTrue($oInputIO2->IsBound(), 'Input must be Bound when binding from an input');
|
||||
}
|
||||
|
||||
public function testOutputCanBeBoundToAnotherOutputAndItIsBoundAfterThat()
|
||||
{
|
||||
$oOutputIO1 = $this->GivenOutput('test1');
|
||||
$oOutputIO2 = $this->GivenOutput('test2');
|
||||
|
||||
$this->assertFalse($oOutputIO2->IsBound(), 'Output must not be bound by default');
|
||||
|
||||
// When
|
||||
$oOutputIO1->BindToOutput($oOutputIO2);
|
||||
|
||||
// Then
|
||||
$this->assertTrue($oOutputIO2->IsBound(), 'Output must be Bound when binding from an output');
|
||||
}
|
||||
|
||||
public function testOutBindingsAreStoredWhenBindToInput()
|
||||
{
|
||||
$oInputIO1 = $this->GivenInput('test1');
|
||||
$oInputIO2 = $this->GivenInput('test2');
|
||||
$oOutputIO1 = $this->GivenOutput('test1');
|
||||
|
||||
// When
|
||||
$oBindingO2ToI1 = $oOutputIO1->BindToInput($oInputIO1);
|
||||
|
||||
// Then
|
||||
$this->assertTrue($oOutputIO1->HasBindingOut(), 'Must have bindings after BindToInput');
|
||||
$this->assertEquals([$oBindingO2ToI1], $oOutputIO1->GetBindingsToInputs(), 'Must have bindings after BindToInput');
|
||||
|
||||
// When
|
||||
$oBindingO1ToI2 = $oOutputIO1->BindToInput($oInputIO2);
|
||||
|
||||
// Then
|
||||
$this->assertEquals([$oBindingO2ToI1, $oBindingO1ToI2], $oOutputIO1->GetBindingsToInputs(), 'Must have bindings after BindToInput');
|
||||
}
|
||||
|
||||
public function testOutBindingsAreStoredWhenBindToOutput()
|
||||
{
|
||||
$oOutputIO1 = $this->GivenOutput('test1');
|
||||
$oOutputIO2 = $this->GivenOutput('test2');
|
||||
$oOutputIO3 = $this->GivenOutput('test3');
|
||||
|
||||
// When
|
||||
$oBindingO1ToO2 = $oOutputIO1->BindToOutput($oOutputIO2);
|
||||
|
||||
// Then
|
||||
$this->assertTrue($oOutputIO1->HasBindingOut(), 'Must have bindings after BindToInput');
|
||||
$this->assertEquals([$oBindingO1ToO2], $oOutputIO1->GetBindingsToOutputs(), 'Must have bindings after BindToOutput');
|
||||
|
||||
// When
|
||||
$oBindingO1ToO3 = $oOutputIO1->BindToOutput($oOutputIO3);
|
||||
|
||||
// Then
|
||||
$this->assertEquals([$oBindingO1ToO2, $oBindingO1ToO3], $oOutputIO1->GetBindingsToOutputs(), 'Must have bindings after BindToOutput');
|
||||
}
|
||||
|
||||
public function testSourceValueIsPropagatedToDestIO()
|
||||
{
|
||||
$oOutputIO1 = $this->GivenOutput('test1');
|
||||
$oInputIO1 = $this->GivenInput('test1');
|
||||
$oBinding = $oOutputIO1->BindToInput($oInputIO1);
|
||||
$oOutputIO1->SetValue(FormEvents::PRE_SET_DATA, 'The Value');
|
||||
|
||||
// When
|
||||
$oBinding->PropagateValues();
|
||||
|
||||
// Then
|
||||
$this->assertEquals('The Value', $oOutputIO1->GetValue(FormEvents::PRE_SET_DATA));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider BindingIncompatibleFormatsProvider
|
||||
*
|
||||
* @param string $sSourceFormat
|
||||
* @param string $sDestinationFormat
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBindingIncompatibleFormatsThrowsException(string $sSourceFormat, string $sDestinationFormat)
|
||||
{
|
||||
$oOutputIO = $this->GivenOutput('test', $sSourceFormat);
|
||||
$oInputIO = $this->GivenInput('test', $sDestinationFormat);
|
||||
|
||||
$this->expectException(FormBlockIOException::class);
|
||||
$oOutputIO->BindToInput($oInputIO);
|
||||
}
|
||||
|
||||
public function BindingIncompatibleFormatsProvider(): array
|
||||
{
|
||||
return [
|
||||
'Attribute -> Boolean' => [AttributeIOFormat::class, BooleanIOFormat::class],
|
||||
'Attribute -> Class' => [AttributeIOFormat::class, ClassIOFormat::class],
|
||||
'Attribute -> Number' => [AttributeIOFormat::class, NumberIOFormat::class],
|
||||
'Attribute -> String' => [AttributeIOFormat::class, StringIOFormat::class],
|
||||
|
||||
'Boolean => Attribute' => [BooleanIOFormat::class, AttributeIOFormat::class],
|
||||
'Boolean => Class' => [BooleanIOFormat::class, ClassIOFormat::class],
|
||||
'Boolean => Number' => [BooleanIOFormat::class, NumberIOFormat::class],
|
||||
'Boolean -> String' => [BooleanIOFormat::class, StringIOFormat::class],
|
||||
|
||||
'Class => Attribute' => [ClassIOFormat::class, AttributeIOFormat::class],
|
||||
'Class => Boolean' => [ClassIOFormat::class, BooleanIOFormat::class],
|
||||
'Class => Number' => [ClassIOFormat::class, NumberIOFormat::class],
|
||||
'Class -> String' => [ClassIOFormat::class, StringIOFormat::class],
|
||||
|
||||
'Number => Attribute' => [NumberIOFormat::class, AttributeIOFormat::class],
|
||||
'Number => Class' => [NumberIOFormat::class, ClassIOFormat::class],
|
||||
'Number => Boolean' => [NumberIOFormat::class, BooleanIOFormat::class],
|
||||
'Number -> String' => [NumberIOFormat::class, StringIOFormat::class],
|
||||
|
||||
'String => Attribute' => [StringIOFormat::class, AttributeIOFormat::class],
|
||||
'String => Class' => [StringIOFormat::class, ClassIOFormat::class],
|
||||
'String => Boolean' => [StringIOFormat::class, BooleanIOFormat::class],
|
||||
'String -> Number' => [StringIOFormat::class, NumberIOFormat::class],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider BindingCompatibleFormatsProvider
|
||||
*
|
||||
* @param string $sSourceFormat
|
||||
* @param string $sDestinationFormat
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBindingCompatibleFormatsWorks(string $sSourceFormat, string $sDestinationFormat)
|
||||
{
|
||||
$oOutputIO = $this->GivenOutput('test', $sSourceFormat);
|
||||
$oInputIO = $this->GivenInput('test', $sDestinationFormat);
|
||||
|
||||
$oBinding = $oOutputIO->BindToInput($oInputIO);
|
||||
$this->assertTrue(is_a($oBinding, FormBinding::class));
|
||||
}
|
||||
|
||||
public function BindingCompatibleFormatsProvider(): array
|
||||
{
|
||||
return [
|
||||
'Attribute -> Attribute' => [AttributeIOFormat::class, AttributeIOFormat::class],
|
||||
'Boolean => Boolean' => [BooleanIOFormat::class, BooleanIOFormat::class],
|
||||
'Class => Class' => [ClassIOFormat::class, ClassIOFormat::class],
|
||||
'Number => Number' => [NumberIOFormat::class, NumberIOFormat::class],
|
||||
'String => String' => [StringIOFormat::class, StringIOFormat::class],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\sources\Forms\IO\Format;
|
||||
|
||||
use Combodo\iTop\Forms\IO\Format\AttributeIOFormat;
|
||||
use Combodo\iTop\Test\UnitTest\sources\Forms\AbstractFormsTest;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
class TestAttributeIOFormat extends AbstractFormsTest
|
||||
{
|
||||
public function testAttributeIOIsAString()
|
||||
{
|
||||
$oInputIO = $this->GivenInput('test', AttributeIOFormat::class);
|
||||
$oInputIO->SetValue(FormEvents::POST_SUBMIT, 'name');
|
||||
|
||||
$this->assertEquals('name', $oInputIO->GetValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\sources\Forms\IO\Format;
|
||||
|
||||
use Combodo\iTop\Forms\IO\Format\BooleanIOFormat;
|
||||
use Combodo\iTop\Test\UnitTest\sources\Forms\AbstractFormsTest;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
class TestBooleanIOFormat extends AbstractFormsTest
|
||||
{
|
||||
public function testBooleanIOFormatIsABoolean()
|
||||
{
|
||||
$oInputIO = $this->GivenInput('test', BooleanIOFormat::class);
|
||||
|
||||
$oInputIO->SetValue(FormEvents::POST_SUBMIT, 'true');
|
||||
|
||||
$this->assertEquals(true, $oInputIO->GetValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\sources\Forms\IO\Format;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\sources\Forms\AbstractFormsTest;
|
||||
|
||||
class TestClassIOFormat extends AbstractFormsTest
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\sources\Forms\IO\Format;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\sources\Forms\AbstractFormsTest;
|
||||
|
||||
class TestNumberIOFormat extends AbstractFormsTest
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Sources\Forms\Register;
|
||||
|
||||
use Combodo\iTop\Forms\Block\Base\CheckboxFormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\FormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\TextFormBlock;
|
||||
use Combodo\iTop\Forms\IO\Format\BooleanIOFormat;
|
||||
use Combodo\iTop\Forms\IO\Format\StringIOFormat;
|
||||
use Combodo\iTop\Forms\Register\IORegister;
|
||||
use Combodo\iTop\Forms\Register\RegisterException;
|
||||
use Combodo\iTop\Test\UnitTest\sources\Forms\AbstractFormsTest;
|
||||
|
||||
class IORegisterTest extends AbstractFormsTest
|
||||
{
|
||||
private FormBlock $oFormBlock;
|
||||
private IORegister $oIORegister;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->oFormBlock = $this->GivenFormBlock('OneBlock');
|
||||
$this->oIORegister = $this->GivenIORegister($this->oFormBlock);
|
||||
}
|
||||
|
||||
public function testAddInput(): void
|
||||
{
|
||||
$this->oIORegister->AddInput('input', StringIOFormat::class);
|
||||
|
||||
$this->assertTrue($this->oIORegister->HasInput('input'));
|
||||
$this->assertNotNull($this->oIORegister->GetInput('input'));
|
||||
}
|
||||
|
||||
public function testGetInputs(): void
|
||||
{
|
||||
$iOriginInputCount = count($this->oIORegister->GetInputs());
|
||||
$this->oIORegister->AddInput('input_1', StringIOFormat::class);
|
||||
$this->oIORegister->AddInput('input_2', BooleanIOFormat::class);
|
||||
$this->oIORegister->AddInput('input_3', StringIOFormat::class);
|
||||
|
||||
$this->assertCount(3 + $iOriginInputCount, $this->oIORegister->GetInputs());
|
||||
$this->assertArrayHasKey('input_1', $this->oIORegister->GetInputs());
|
||||
}
|
||||
|
||||
public function testGetOutputs(): void
|
||||
{
|
||||
$this->oIORegister->AddOutput('output_1', StringIOFormat::class);
|
||||
$this->oIORegister->AddOutput('output_2', BooleanIOFormat::class);
|
||||
|
||||
$this->assertCount(2, $this->oIORegister->GetOutputs());
|
||||
$this->assertArrayHasKey('output_1', $this->oIORegister->GetOutputs());
|
||||
}
|
||||
|
||||
public function testMissingInput(): void
|
||||
{
|
||||
$this->expectException(RegisterException::class);
|
||||
$this->oIORegister->GetInput('missing_input');
|
||||
}
|
||||
|
||||
public function testMissingOutput(): void
|
||||
{
|
||||
$this->expectException(RegisterException::class);
|
||||
$this->oIORegister->GetOutput('missing_output');
|
||||
}
|
||||
|
||||
public function testGetBoundInputs(): void
|
||||
{
|
||||
$this->GivenSubFormBlock($this->oFormBlock, 'SubFormA', TextFormBlock::class);
|
||||
$this->GivenSubFormBlock($this->oFormBlock, 'SubFormB', CheckboxFormBlock::class);
|
||||
$this->GivenSubFormBlock($this->oFormBlock, 'SubFormC', TextFormBlock::class);
|
||||
|
||||
$oSubForm = $this->GivenSubFormBlock($this->oFormBlock, 'SubForm', TextFormBlock::class);
|
||||
$oSubForm->AddInput('input_from_A', StringIOFormat::class);
|
||||
$oSubForm->AddInput('input_from_B', BooleanIOFormat::class);
|
||||
$oSubForm->AddInput('input_from_C', StringIOFormat::class);
|
||||
$oSubForm->AddInput('unbound_input', StringIOFormat::class);
|
||||
|
||||
$this->GivenIORegister($oSubForm)->InputDependsOn('input_from_A', 'SubFormA', TextFormBlock::OUTPUT_TEXT);
|
||||
$this->GivenIORegister($oSubForm)->InputDependsOn('input_from_B', 'SubFormB', CheckboxFormBlock::OUTPUT_CHECKED);
|
||||
$this->GivenIORegister($oSubForm)->InputDependsOn('input_from_C', 'SubFormC', TextFormBlock::OUTPUT_TEXT);
|
||||
|
||||
$aBoundInputs = $this->GivenIORegister($oSubForm)->GetBoundInputs();
|
||||
$this->assertCount(3, $aBoundInputs);
|
||||
}
|
||||
|
||||
public function testGetBoundOutputs(): void
|
||||
{
|
||||
$this->oFormBlock->AddOutput('output', StringIOFormat::class);
|
||||
|
||||
$oSubFormA = $this->GivenSubFormBlock($this->oFormBlock, 'SubFormA', TextFormBlock::class);
|
||||
$oIORegisterA = $this->GivenIORegister($oSubFormA);
|
||||
|
||||
$oIORegisterA->OutputImpactParent(TextFormBlock::OUTPUT_TEXT, 'output');
|
||||
|
||||
$this->assertCount(1, $this->oIORegister->GetBoundOutputs());
|
||||
}
|
||||
|
||||
public function testAddInputDependsOn(): void
|
||||
{
|
||||
$this->GivenSubFormBlock($this->oFormBlock, 'SubFormA', TextFormBlock::class);
|
||||
$oSubFormB = $this->GivenSubFormBlock($this->oFormBlock, 'SubFormB', TextFormBlock::class);
|
||||
$oIORegisterB = $this->GivenIORegister($oSubFormB);
|
||||
|
||||
$oIORegisterB->AddInputDependsOn('input', 'SubFormA', TextFormBlock::OUTPUT_TEXT);
|
||||
|
||||
$this->assertNotNull($oIORegisterB->GetInput('input'));
|
||||
}
|
||||
|
||||
public function testImpactParent(): void
|
||||
{
|
||||
$this->oFormBlock->AddOutput('output', StringIOFormat::class);
|
||||
|
||||
$oSubFormA = $this->GivenSubFormBlock($this->oFormBlock, 'SubFormA', TextFormBlock::class);
|
||||
$oIORegisterA = $this->GivenIORegister($oSubFormA);
|
||||
|
||||
$oIORegisterA->OutputImpactParent(TextFormBlock::OUTPUT_TEXT, 'output');
|
||||
|
||||
$this->assertTrue($this->oFormBlock->GetOutput('output')->IsBound());
|
||||
}
|
||||
|
||||
public function testAddingTwiceTheSameInputThrowsException(): void
|
||||
{
|
||||
$this->oIORegister->AddInput('test_input', StringIOFormat::class);
|
||||
$this->expectException(RegisterException::class);
|
||||
$this->oIORegister->AddInput('test_input', StringIOFormat::class);
|
||||
}
|
||||
|
||||
public function testAddingTwiceTheSameOutputThrowsException(): void
|
||||
{
|
||||
$this->oIORegister->AddOutput('test_output', StringIOFormat::class);
|
||||
$this->expectException(RegisterException::class);
|
||||
$this->oIORegister->AddOutput('test_output', StringIOFormat::class);
|
||||
}
|
||||
|
||||
public function testDependingOnNonExistingInputThrowsException(): void
|
||||
{
|
||||
$this->oIORegister->AddInput('test_input', StringIOFormat::class);
|
||||
$this->oIORegister->AddOutput('test_output', StringIOFormat::class);
|
||||
|
||||
$this->expectException(RegisterException::class);
|
||||
|
||||
$this->oIORegister->InputDependsOn('non_existing_input', 'OtherBlock', 'test_output');
|
||||
}
|
||||
|
||||
public function testDependingOnNonExistingOutputThrowsException(): void
|
||||
{
|
||||
$this->oIORegister->AddInput('test_input', StringIOFormat::class);
|
||||
|
||||
$this->expectException(RegisterException::class);
|
||||
$this->oIORegister->InputDependsOn('test_input', 'OtherBlock', 'non_existing_output');
|
||||
}
|
||||
|
||||
public function testDependingOnNonExistingBlockThrowsException(): void
|
||||
{
|
||||
$this->oIORegister->AddInput('test_input', StringIOFormat::class);
|
||||
$this->oIORegister->AddOutput('test_output', StringIOFormat::class);
|
||||
|
||||
$this->expectException(RegisterException::class);
|
||||
$this->oIORegister->InputDependsOn('test_input', 'UnknownBlock', 'test');
|
||||
}
|
||||
|
||||
public function testHasDependenciesBlocks(): void
|
||||
{
|
||||
$this->GivenSubFormBlock($this->oFormBlock, 'SubFormA', TextFormBlock::class);
|
||||
|
||||
$oSubForm = $this->GivenSubFormBlock($this->oFormBlock, 'SubForm', TextFormBlock::class);
|
||||
$oSubForm->AddInput('input_from_A', StringIOFormat::class);
|
||||
|
||||
$this->GivenIORegister($oSubForm)->InputDependsOn('input_from_A', 'SubFormA', TextFormBlock::OUTPUT_TEXT);
|
||||
$this->assertTrue($this->GivenIORegister($oSubForm)->HasDependenciesBlocks());
|
||||
|
||||
$this->assertFalse($this->oIORegister->HasDependenciesBlocks());
|
||||
}
|
||||
|
||||
public function testImpactBlocks(): void
|
||||
{
|
||||
$oSubFormA = $this->GivenSubFormBlock($this->oFormBlock, 'SubFormA', TextFormBlock::class);
|
||||
|
||||
$oSubForm = $this->GivenSubFormBlock($this->oFormBlock, 'SubForm', TextFormBlock::class);
|
||||
$oSubForm->AddInput('input_from_A', StringIOFormat::class);
|
||||
|
||||
$this->GivenIORegister($oSubForm)->InputDependsOn('input_from_A', 'SubFormA', TextFormBlock::OUTPUT_TEXT);
|
||||
$this->assertFalse($this->GivenIORegister($oSubForm)->IsImpactingBlocks());
|
||||
|
||||
$this->assertTrue($this->GivenIORegister($oSubFormA)->IsImpactingBlocks());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Sources\Forms\Register;
|
||||
|
||||
use Combodo\iTop\Forms\Register\OptionsRegister;
|
||||
use Combodo\iTop\Forms\Register\RegisterException;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
class OptionsRegisterTest extends ItopDataTestCase
|
||||
{
|
||||
private OptionsRegister $oOptionsRegister;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->oOptionsRegister = new OptionsRegister();
|
||||
}
|
||||
|
||||
public function testSetOptionWithInvalidName(): void
|
||||
{
|
||||
$this->oOptionsRegister->SetOption('valid_option_name', 'value');
|
||||
|
||||
$this->expectException(RegisterException::class);
|
||||
$this->oOptionsRegister->SetOption('not valid option name', 'value');
|
||||
}
|
||||
|
||||
public function testSetOptionTwice(): void
|
||||
{
|
||||
$this->oOptionsRegister->SetOption('valid_option_name', 'value');
|
||||
$this->oOptionsRegister->SetOption('valid_option_name', 'value2');
|
||||
|
||||
$this->assertEquals('value2', $this->oOptionsRegister->GetOption('valid_option_name'));
|
||||
}
|
||||
|
||||
public function testSetNonTypeOption(): void
|
||||
{
|
||||
$this->oOptionsRegister->SetOption('not_a_type_option', 'value', false);
|
||||
|
||||
$this->assertArrayNotHasKey('not_a_type_option', $this->oOptionsRegister->GetOptions());
|
||||
}
|
||||
|
||||
public function testSetOptionArrayValue(): void
|
||||
{
|
||||
$this->oOptionsRegister->SetOptionArrayValue('att', 'class', 'ibo-class');
|
||||
|
||||
$this->assertEquals('ibo-class', $this->oOptionsRegister->GetOption('att')['class']);
|
||||
}
|
||||
|
||||
public function testHasOption(): void
|
||||
{
|
||||
$this->oOptionsRegister->SetOption('option', true);
|
||||
|
||||
$this->assertTrue($this->oOptionsRegister->HasOption('option'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -156,10 +156,26 @@ class DataModelDependantCacheTest extends ItopTestCase
|
||||
$this->assertEquals($iRefTime, $this->oCacheService->GetEntryModificationTime('pool-A', 'key'), 'GetEntryModificationTime should return the modification time of the cache file');
|
||||
$this->assertEquals(null, $this->oCacheService->GetEntryModificationTime('pool-A', 'non-existing-key'), 'GetEntryModificationTime should return null for an invalid key');
|
||||
}
|
||||
|
||||
public function testKeyUndesiredCharactersShouldBeTransformedToUnderscore()
|
||||
{
|
||||
$sUglyKey = 'key with ugly characters:\{&"#@ç^²/,;[(|🤔';
|
||||
$sUglyKey = 'key with ugly characters:\{&"#@ç^²,;[(|🤔';
|
||||
$sFilePath = $this->InvokeNonPublicMethod(DataModelDependantCache::class, 'MakeCacheFileName', $this->oCacheService, ['pool-A', $sUglyKey]);
|
||||
$this->assertEquals('key_with_ugly_characters______________________.php', basename($sFilePath));
|
||||
$this->assertEquals('key_with_ugly_characters_____________________.php', basename($sFilePath));
|
||||
}
|
||||
|
||||
public function testKeyCanBeADirectoryTree()
|
||||
{
|
||||
$sBaseKey = 'test';
|
||||
$sBaseFilePath = $this->InvokeNonPublicMethod(DataModelDependantCache::class, 'MakeCacheFileName', $this->oCacheService, ['pool-A', $sBaseKey]);
|
||||
|
||||
$sKey = 'Path/To/KeyCanBePath';
|
||||
$sFilePath = $this->InvokeNonPublicMethod(DataModelDependantCache::class, 'MakeCacheFileName', $this->oCacheService, ['pool-A', $sKey]);
|
||||
|
||||
$this->assertEquals(dirname($sBaseFilePath).'/Path/To', dirname($sFilePath));
|
||||
|
||||
$this->oCacheService->Store('pool-A', $sKey, 'some data...');
|
||||
$this->assertTrue($this->oCacheService->HasEntry('pool-A', $sKey), 'The data should have been stored');
|
||||
$this->assertFileExists($sFilePath, 'A file should have been created in the corresponding directory');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user