<?php
/**
 * core.framework
 *
 * @category   Core
 * @package    Core_Form
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 */

/**
 * @category   Core
 * @package    Core_Form
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 * @group      Core_Form
 */
class Core_Form_Element_AbstractTest extends CoreTest_Container_TestCase
{
    /**
     * @var Core_Form_Element_Abstract
     */
    protected $_object;

    protected $_error;

    protected function setUp()
    {
        $this->_object = $this->getMockForAbstractClass('Core_Form_Element_Abstract');
    }
    
    /**
     * @covers Core_Form_Element_Abstract::setTranslator
     * @covers Core_Form_Element_Abstract::getTranslator
     */
    public function testSetAndGetTranslator()
    {
        $translator = $this->getMock('Zend_Translate');
        $this->_object->setTranslator($translator);

        $this->assertEquals($translator, $this->_object->getTranslator());
    }
    
    /**
     * @covers Core_Form_Element_Abstract::getTranslator
     */
    public function testIfTranslatorNotSetWillTryToFetchItFromParent()
    {
        $translator = $this->getMock('Zend_Translate');
        $parent     = $this->getMock('Core_Form', array('getTranslator'));
        $parent
            ->expects($this->once())
            ->method('getTranslator')
            ->will($this->returnValue($translator));
        $this->_object->setOwner($parent);

        $this->assertEquals($translator, $this->_object->getTranslator());
    }

    /**
     * @covers Core_Form_Element_Abstract::setPluginManager
     * @covers Core_Form_Element_Abstract::getPluginManager
     */
    public function testSetAndGetPluginManager()
    {
        $pluginManager = $this->getMock('Core_Plugin_Manager', array(), array($this->_object));
        $this->_object->setPluginManager($pluginManager);

        $this->assertEquals($pluginManager, $this->_object->getPluginManager());
    }

    /**
     * @covers Core_Form_Element_Abstract::init
     */
    public function testInitWillDoNothingByDefault()
    {
        $this->assertSame($this->_object, $this->_object->init());
    }

    /**
     * @covers Core_Form_Element_Abstract::setOwner
     * @covers Core_Form_Element_Abstract::getParent
     */
    public function testWillStoreParentPassedViaSetOwner()
    {
        $parent = $this->getMock('Core_Form', array(), array(), '', false);
        $this->_object->setOwner($parent);

        $this->assertEquals($parent, $this->_object->getParent());
    }

    /**
     * @covers Core_Form_Element_Abstract::getRoot
     */
    public function testRootIsSelfIfNoParent()
    {
        $this->assertSame($this->_object, $this->_object->getRoot());
    }

    /**
     * @covers Core_Form_Element_Abstract::getRoot
     */
    public function testRootIsParentRootIfParentSet()
    {
        $parent = $this->getMock('Core_Form', array('getRoot'));
        $parent
            ->expects($this->once())
            ->method('getRoot')
            ->will($this->returnValue($this));
        $this->_object->setOwner($parent);

        $this->assertSame($this, $this->_object->getRoot());
    }

    /**
     * @covers Core_Form_Element_Abstract::isInCollection
     */
    public function testNotInCollectionIfItHasNoParent()
    {
        $this->assertFalse($this->_object->isInCollection());
    }

    /**
     * @covers Core_Form_Element_Abstract::isInCollection
     */
    public function testNotInCollectionIfParentHasNoParent()
    {
        $parent = $this->getMockForAbstractClass('Core_Form_Group_Abstract', array(), '', true, true, true, array('getParent'));

        $object = $this->getMockForAbstractClass('Core_Form_Element_Abstract', array(), '', true, true, true, array('getParent'));
        $object
            ->expects($this->once())
            ->method('getParent')
            ->will($this->returnValue($parent));

        $this->assertFalse($object->isInCollection());
    }

    /**
     * @covers Core_Form_Element_Abstract::isInCollection
     */
    public function testIsInCollectionIfParentHasIsInstanceOfCollection()
    {
        $grandparent = $this->getMockForAbstractClass('Core_Form_Group_Collection');

        $parent = $this->getMockForAbstractClass('Core_Form_Group_Abstract', array(), '', true, true, true, array('getParent'));
        $parent
            ->expects($this->once())
            ->method('getParent')
            ->will($this->returnValue($grandparent));

        $object = $this->getMockForAbstractClass('Core_Form_Element_Abstract', array(), '', true, true, true, array('getParent'));
        $object
            ->expects($this->once())
            ->method('getParent')
            ->will($this->returnValue($parent));

        $this->assertTrue($object->isInCollection());
    }

    /**
     * @covers Core_Form_Element_Abstract::__construct
     * @covers Core_Form_Element_Abstract::__call
     */
    public function testCanManageFilters()
    {
        $filter = $this->getMockForAbstractClass('Core_Filter_Abstract', array(), '', true, true, true, array('getName'));
        $filter
            ->expects($this->once())
            ->method('getName')
            ->will($this->returnValue('myFilter'));
        $this->_object->addFilter($filter);

        $this->assertTrue($this->_object->hasFilter('MyFilter'));
    }

    /**
     * @covers Core_Form_Element_Abstract::__construct
     * @covers Core_Form_Element_Abstract::__call
     */
    public function testCanManageValidators()
    {
        $validator = $this->getMockForAbstractClass('Core_Validate_Abstract', array(), '', true, true, true, array('getName'));
        $validator
            ->expects($this->once())
            ->method('getName')
            ->will($this->returnValue('myValidator'));
        $this->_object->addValidator($validator);

        $this->assertTrue($this->_object->hasValidator('MyValidator'));
    }

    /**
     * @covers Core_Form_Element_Abstract::__call
     */
    public function testCallsToNonexistantMethodsNotPrefixedWithGetWillThrowABadMethodCallException()
    {
        $this->setExpectedException('BadMethodCallException');
        $this->_object->fooSomething();
    }


    /**
     * @covers Core_Form_Element_Abstract::setPrototype
     * @covers Core_Form_Element_Abstract::isPrototype
     */
    public function testCanSetAndGetPrototypeFlag()
    {
        $this->_object->setPrototype(true);
        $this->assertTrue($this->_object->isPrototype());
    }

    /**
     * @covers Core_Form_Element_Abstract::isPrototype
     */
    public function testIsNotPrototypeByDefault()
    {
        $this->assertFalse($this->_object->isPrototype());
    }

    /**
     * @covers Core_Form_Element_Abstract::setName
     * @covers Core_Form_Element_Abstract::getName
     */
    public function testCanSetAndGetName()
    {
        $this->_object->setName('fooName');

        $this->assertEquals('fooName', $this->_object->getName());
    }

    /**
     * @covers Core_Form_Element_Abstract::getFullyQualifiedName
     */
    public function testFullyQualifiedNameIsEqualToName()
    {
        $this->_object->setName('fooName');

        $this->assertEquals('fooName', $this->_object->getFullyQualifiedName());
    }

    /**
     * @covers Core_Form_Element_Abstract::setLabel
     * @covers Core_Form_Element_Abstract::getLabel
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testCanSetAndGetLabel()
    {
        $this->_object->setLabel('Some label');

        $this->assertEquals('Some label', $this->_object->getLabel());
    }
    
    /**
     * @covers Core_Form_Element_Abstract::getLabel
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillTranslateLabelIfTranslatorAvailable()
    {
        $translator = $this->getMock('Zend_Translate', array('translate'), array(), '', false);
        $translator
            ->expects($this->once())
            ->method('translate')
            ->with($this->equalTo('Some label'))
            ->will($this->returnValue('Neka labela'));
        
        $this->_object
            ->setTranslator($translator)
            ->setLabel('Some label');        

        $this->assertEquals('Neka labela', $this->_object->getLabel());
    }

    /**
     * @covers Core_Form_Element_Abstract::setDescription
     * @covers Core_Form_Element_Abstract::getDescription
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testCanSetAndGetDescription()
    {
        $this->_object->setDescription('Some additional description');

        $this->assertEquals('Some additional description', $this->_object->getDescription());
    }
    
    /**
     * @covers Core_Form_Element_Abstract::getDescription
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillTranslateDescriptionlIfTranslatorAvailable()
    {
        $translator = $this->getMock('Zend_Translate', array('translate'), array(), '', false);
        $translator
            ->expects($this->once())
            ->method('translate')
            ->with($this->equalTo('Some description'))
            ->will($this->returnValue('Neki opis'));
        
        $this->_object
            ->setTranslator($translator)
            ->setDescription('Some description');        

        $this->assertEquals('Neki opis', $this->_object->getDescription());
    }

    /**
     * @covers Core_Form_Element_Abstract::setPlaceholder
     * @covers Core_Form_Element_Abstract::getPlaceholder
     */
    public function testCanSetAndGetPlaceholder()
    {
        $this->_object->setPlaceholder('Some placeholder');

        $this->assertEquals('Some placeholder', $this->_object->getPlaceholder());
    }
    
    /**
     * @covers Core_Form_Element_Abstract::getDescription
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillTranslatePlaceholderlIfTranslatorAvailable()
    {
        $translator = $this->getMock('Zend_Translate', array('translate'), array(), '', false);
        $translator
            ->expects($this->once())
            ->method('translate')
            ->with($this->equalTo('Some placeholder'))
            ->will($this->returnValue('Neki mjestodrzac'));
        
        $this->_object
            ->setTranslator($translator)
            ->setPlaceholder('Some placeholder');        

        $this->assertEquals('Neki mjestodrzac', $this->_object->getPlaceholder());
    }

    /**
     * @covers Core_Form_Element_Abstract::getLabel
     */
    public function testNameIsTheDefaultLabel()
    {
        $this->_object->setName('Some label');

        $this->assertEquals('Some label', $this->_object->getLabel());
    }

    /**
     * @covers Core_Form_Element_Abstract::setId
     * @covers Core_Form_Element_Abstract::getId
     */
    public function testCanSetAndGetId()
    {
        $this->_object->setId('fooId');

        $this->assertEquals('fooId', $this->_object->getId());
    }

    /**
     * @covers Core_Form_Element_Abstract::getId
     */
    public function testDefaultIdIsName()
    {
        $this->_object->setName('fooBla');

        $this->assertEquals('fooBla', $this->_object->getId());
    }


    /**
     * @covers Core_Form_Element_Abstract::setId
     * @covers Core_Form_Element_Abstract::getAttrib
     */
    public function testSettingIdWillUpdateAttribId()
    {
        $this->_object->setId('fooId');

        $this->assertEquals('fooId', $this->_object->getAttrib('id'));
    }

    /**
     * @covers Core_Form_Element_Abstract::getFullyQualifiedId
     */
    public function testFullyQualifiedIdIsEqualToId()
    {
        $this->_object->setId('fooId');

        $this->assertEquals('fooId', $this->_object->getFullyQualifiedId());
    }

    /**
     * @covers Core_Form_Element_Abstract::getFullyQualifiedAngularModel
     */
    public function testCanGetFullyQualifiedAngularModel()
    {
        $this->_object->setName('myLocalForm');

        $this->assertEquals('data.myLocalForm', $this->_object->getFullyQualifiedAngularModel());
    }

    /**
     * @covers Core_Form_Element_Abstract::setAttribs
     * @covers Core_Form_Element_Abstract::getAttribs
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testCanSetAndGetAttribs()
    {
        $this->_object->setAttribs(array('id' => 'form', 'class' => 'g_h_123'));
        $this->assertEquals(array('id' => 'form', 'class' => 'g_h_123'), $this->_object->getAttribs());
    }

    /**
     * @covers Core_Form_Element_Abstract::getAttribs
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testGetAttribsWillMergeMandatoryAttribsForRequestedContext()
    {
        $object = new Invalid_Form_Element_Invalid;
        $object->setName('abla');

        $this->assertEquals(array(
            'name'     => 'abla',
            'id'       => 'abla',
            'mojMakro' => 'nope',
        ), $object->getAttribs('randomContext'));
    }

    /**
     * @covers Core_Form_Element_Abstract::getAttribs
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testGetAttribsInAngularContextWillAutoMergeAngularRepresentation()
    {
        $object = new Invalid_Form_Element_Angular;
        $object->setName('abla');

        $this->assertEquals(array(
            'name'            => 'abla',
            'required'        => 'required',
            'min'             => 3
        ), $object->getAttribs(Core_Form::CONTEXT_ANGULAR));
    }

    /**
     * @covers Core_Form_Element_Abstract::setAttrib
     * @covers Core_Form_Element_Abstract::getAttrib
     */
    public function testCanSetAndGetAttrib()
    {
        $this->_object->setAttrib('aBC', 'err');
        $this->assertEquals('err', $this->_object->getAttrib('Abc'));
    }

    /**
     * @covers Core_Form_Element_Abstract::setAttrib
     */
    public function testCanInvokeAttribFilterOnSetAttrib()
    {
        $object = new Invalid_Form_Element_Invalid;
        $object->setAttrib('foo', 44);

        $this->assertEquals(45, $object->getAttrib('foo'));
    }

    /**
     * @covers Core_Form_Element_Abstract::getAttrib
     */
    public function testCanInvokeAttribFilterOnGetAttrib()
    {
        $object = new Invalid_Form_Element_Invalid;
        $object->setAttrib('bar', 23);

        $this->assertEquals('ne', $object->getAttrib('bar'));
    }
    
    /**
     * @covers Core_Form_Element_Abstract::setAttrib
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillAutoAddStringLengthValidatorWhenSettingPatternNgMixlength()
    {
        $minLength       = 2;
        $object          = new Invalid_Form_Element_Invalid;
        $object->test    = $this;
        $object->options = array('min' => $minLength);

        $object->setAttrib('ng-minlength', $minLength);
    }
    
    /**
     * @covers Core_Form_Element_Abstract::setAttrib
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillAutoAddStringLengthValidatorWhenSettingPatternNgMaxlength()
    {
        $maxLength       = 3;
        $object          = new Invalid_Form_Element_Invalid;
        $object->test    = $this;
        $object->options = array('max' => $maxLength);

        $object->setAttrib('ng-maxlength', $maxLength);
    }
    
    /**
     * @covers Core_Form_Element_Abstract::setAttrib
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillAutoAddStringLengthValidatorWhenSettingPatternMaxlength()
    {
        $maxLength       = 3;
        $object          = new Invalid_Form_Element_Invalid;
        $object->test    = $this;
        $object->options = array('max' => $maxLength);

        $object->setAttrib('maxlength', $maxLength);
    }

    /**
     * @covers Core_Form_Element_Abstract::setAttrib
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillAutoAddRegexValidatorWhenSettingPatternAttrib()
    {
        $pattern         = '/[a-zA-Z0-9]{3}/';
        $object          = new Invalid_Form_Element_Invalid;
        $object->test    = $this;
        $object->options = array('regex' => $pattern);

        $object->setAttrib('pattern', $pattern);
    }

    /**
     * @covers Core_Form_Element_Abstract::setAttrib
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillAutoAddMinValidatorWhenSettingMinAttrib()
    {
        $object          = new Invalid_Form_Element_Invalid;
        $object->test    = $this;
        $object->options = array('min' => 0.3);

        $object->setAttrib('min', '0.3');
    }

    /**
     * @covers Core_Form_Element_Abstract::setAttrib
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillAutoAddMaxValidatorWhenSettingMaxAttrib()
    {
        $object          = new Invalid_Form_Element_Invalid;
        $object->test    = $this;
        $object->options = array('max' => 100.3);

        $object->setAttrib('max', '100.3');
    }

    /**
     * @covers Core_Form_Element_Abstract::setAttrib
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillAutoAddDivisableValidatorWhenSettingStepAttrib()
    {
        $object          = new Invalid_Form_Element_Invalid;
        $object->test    = $this;
        $object->options = array('step' => 2);

        $object->setAttrib('step', 2);
    }
    
    /**
     * @covers Core_Form_Element_Abstract::getAttrib
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillTranslatePlaceholderAttribIfTranslatorAvailable()
    {
        $translator = $this->getMock('Zend_Translate', array('translate'), array(), '', false);
        $translator
            ->expects($this->once())
            ->method('translate')
            ->with($this->equalTo('Some placeholder'))
            ->will($this->returnValue('Neki drzac mjesta'));
        
        $this->_object
            ->setTranslator($translator)
            ->setAttrib('placeholder', 'Some placeholder');

        $this->assertEquals('Neki drzac mjesta', $this->_object->getAttrib('placeholder'));
    }

    /**
     * @covers Core_Form_Element_Abstract::getAttrib
     */
    public function testFetchingAnUnknownAttribWillReturnDefault()
    {
        $this->assertEquals('default value', $this->_object->getAttrib('no such attrib', 'default value'));
    }

    /**
     * @covers Core_Form_Element_Abstract::getAttrib
     */
    public function testFetchingAnUnknownAttribWillReturnNull()
    {
        $this->assertNull($this->_object->getAttrib('no such attrib'));
    }

    /**
     * @covers Core_Form_Element_Abstract::setOptions
     */
    public function testCanSetOptions()
    {
        $options = array('attribs' => array('class' => 'g_h_23'));
        $this->_object->setOptions($options);

        $this->assertEquals('g_h_23', $this->_object->getAttrib('class'));
    }

    /**
     * @covers Core_Form_Element_Abstract::setConfig
     */
    public function testCanSetConfig()
    {
        $config = $this->getMock('Zend_Config', null, array(array('attribs' => array('class' => 'g_h_23'))));
        $this->_object->setConfig($config);

        $this->assertEquals('g_h_23', $this->_object->getAttrib('class'));
    }

    /**
     * @covers Core_Form_Element_Abstract::setOptions
     * @covers Core_Form_Element_Abstract::set
     */
    public function testPassingAnUnknownOptionsThrowsAnInvalidArgumentException()
    {
        $this->setExpectedException('InvalidArgumentException');
        $this->_object->setOptions(array('no-such' => 'option'));
    }

    /**
     * @covers Core_Form_Element_Abstract::__construct
     */
    public function testCanSetOptionsViaConstructor()
    {
        $options = array('attribs' => array('class' => 'g_h_23'));
        $object  = $this->getMockForAbstractClass('Core_Form_Element_Abstract', array($options), '', true, true, true, array('getName'));

        $this->assertEquals('g_h_23', $object->getAttrib('class'));
    }

    /**
     * @covers Core_Form_Element_Abstract::__construct
     */
    public function testCanSetConfigViaConstructor()
    {
        $config = $this->getMock('Zend_Config', null, array(array('attribs' => array('class' => 'g_h_23'))));
        $object  = $this->getMockForAbstractClass('Core_Form_Element_Abstract', array($config), '', true, true, true, array('getName'));

        $this->assertEquals('g_h_23', $object->getAttrib('class'));
    }

    /**
     * @covers Core_Form_Element_Abstract::setOptions
     */
    public function testCanAddFiltersViaOptions()
    {
        $filter = $this->getMockForAbstractClass('Core_Filter_Abstract', array(), '', true, true, true, array('getName'));
        $filter
            ->expects($this->once())
            ->method('getName')
            ->will($this->returnValue('myFilter'));
        $this->_object->setOptions(array('filters' => array($filter)));

        $this->assertTrue($this->_object->hasFilter('MyFilter'));
    }

    /**
     * @covers Core_Form_Element_Abstract::setOptions
     */
    public function testCanAddValidatorsViaOptions()
    {
        $validator = $this->getMockForAbstractClass('Core_Validate_Abstract', array(), '', true, true, true, array('getName'));
        $validator
            ->expects($this->once())
            ->method('getName')
            ->will($this->returnValue('myValidator'));
        $this->_object->setOptions(array('validators' => array($validator)));

        $this->assertTrue($this->_object->hasValidator('MyValidator'));
    }

    /**
     * @covers Core_Form_Element_Abstract::setOwner
     * @covers Core_Form_Element_Abstract::getParent
     */
    public function testParentMustBeAnInstanceOfGroup()
    {
        $this->setExpectedException('InvalidArgumentException');
        $parent = $this->getMock('Core_Plugin_Interface');

        $this->_object->setOwner($parent);
    }

    /**
     * @covers Core_Form_Element_Abstract::getId
     */
    public function testDefaultIdIsEqualToName()
    {
        $this->_object->setName('fooBla');

        $this->assertEquals('fooBla', $this->_object->getId());
    }

    /**
     * @covers Core_Form_Element_Abstract::getFullyQualifiedName
     */
    public function testCanGetFullyQualifiedName()
    {
        $parent = $this->getMockForAbstractClass('Core_Form_Group_Abstract', array(), '', true, true, true, array('getFullyQualifiedName'));
        $parent
            ->expects($this->once())
            ->method('getFullyQualifiedName')
            ->will($this->returnValue('myFormGroup'));

        $this->_object
            ->setName('myLocalForm')
            ->setOwner($parent)
            ->init();

        $this->assertEquals('myFormGroup[myLocalForm]', $this->_object->getFullyQualifiedName());
    }

    /**
     * @covers Core_Form_Element_Abstract::getFullyQualifiedName
     */
    public function testDefaultFullyQualifiedNameIsEqualToName()
    {
        $this->_object->setName('fooBla');

        $this->assertEquals('fooBla', $this->_object->getFullyQualifiedName());
    }

    /**
     * @covers Core_Form_Element_Abstract::getFullyQualifiedId
     */
    public function testCanGetFullyQualifiedId()
    {
        $parent = $this->getMockForAbstractClass('Core_Form_Group_Abstract', array(), '', true, true, true, array('getFullyQualifiedId'));
        $parent
            ->expects($this->once())
            ->method('getFullyQualifiedId')
            ->will($this->returnValue('myFormGroup'));

        $this->_object
            ->setId('myLocalForm')
            ->setOwner($parent)
            ->init();

        $this->assertEquals('myFormGroup_myLocalForm', $this->_object->getFullyQualifiedId());
    }

    /**
     * @covers Core_Form_Element_Abstract::getFullyQualifiedId
     */
    public function testDefaultFullyQualifiedIdIsEqualToId()
    {
        $this->_object->setId('fooBla');

        $this->assertEquals('fooBla', $this->_object->getFullyQualifiedId());
    }

    /**
     * @covers Core_Form_Element_Abstract::getFullyQualifiedAngularId
     */
    public function testFullyQualifiedAngularIdIsEqualToFullyQualifiedIdByDefault()
    {
        $this->_object
            ->setId('my_id')
            ->init();

        $this->assertEquals('my_id', $this->_object->getFullyQualifiedAngularId());
    }

    /**
     * @covers Core_Form_Element_Abstract::getFullyQualifiedAngularId
     */
    public function testIfParentIsPrototypeWillUseParentFullyQualifiedAngularIdForFullyQualifiedAngularId()
    {
        $parent = $this->getMock('Core_Form', array('getFullyQualifiedAngularId', 'isPrototype'));
        $parent
            ->expects($this->once())
            ->method('getFullyQualifiedAngularId')
            ->will($this->returnValue('myFormGroup_mySubGroup'));
        $parent
            ->expects($this->once())
            ->method('isPrototype')
            ->will($this->returnValue(true));

        $this->_object
            ->setId('ignored')
            ->setOwner($parent)
            ->init();

        $this->assertEquals('myFormGroup_mySubGroup', $this->_object->getFullyQualifiedAngularId());
    }

    /**
     * @covers Core_Form_Element_Abstract::getFullyQualifiedAngularId
     */
    public function testIfParentIsNotPrototypeFullyQualifiedAngularIdWillEqualToFullyQualifiedId()
    {
        $parent = $this->getMock('Core_Form', array('getFullyQualifiedId', 'isPrototype'));
        $parent
            ->expects($this->once())
            ->method('getFullyQualifiedId')
            ->will($this->returnValue('myFormGroup_mySubGroup'));
        $parent
            ->expects($this->once())
            ->method('isPrototype')
            ->will($this->returnValue(false));

        $this->_object
            ->setId('ignored')
            ->setOwner($parent)
            ->init();

        $this->assertEquals('myFormGroup_mySubGroup_ignored', $this->_object->getFullyQualifiedAngularId());
    }

    /**
     * @covers Core_Form_Element_Abstract::setRawValue
     * @covers Core_Form_Element_Abstract::getRawValue
     */
    public function testCanSetAndGetRawValue()
    {
        $this->_object->setRawValue('value1');
        $this->assertEquals('value1', $this->_object->getRawValue());
    }

    /**
     * @covers Core_Form_Element_Abstract::setValue
     */
    public function testSettingValueWillSetRawValue()
    {
        $this->_object->setValue('value1');
        $this->assertEquals('value1', $this->_object->getRawValue());
    }

    /**
     * @covers Core_Form_Element_Abstract::getValue
     */
    public function testGetValueWillFilterRawValueThroughAttachedFilters()
    {
        $filter1 = $this->getMockForAbstractClass('Core_Filter_Abstract', array(), '', true, true, true, array('getName', 'filter'));
        $filter1
            ->expects($this->once())
            ->method('getName')
            ->will($this->returnValue('myFilter1'));
        $filter1
            ->expects($this->once())
            ->method('filter')
            ->with($this->equalTo('value0'))
            ->will($this->returnValue('value1'));
        $filter2 = $this->getMockForAbstractClass('Core_Filter_Abstract', array(), '', true, true, true, array('getName', 'filter'));
        $filter2
            ->expects($this->once())
            ->method('getName')
            ->will($this->returnValue('myFilter2'));
        $filter2
            ->expects($this->once())
            ->method('filter')
            ->with($this->equalTo('value1'))
            ->will($this->returnValue('value2'));
        $this->_object->setOptions(array('filters' => array($filter1, $filter2)));
        $this->_object->setRawValue('value0');

        $this->assertEquals('value2', $this->_object->getValue());
    }

    /**
     * @covers Core_Form_Element_Abstract::setRequired
     * @covers Core_Form_Element_Abstract::isRequired
     */
    public function testCanSetAndGetRequired()
    {
        $object          = new Invalid_Form_Element_Invalid;
        $object->test    = $this;
        $object->options = array('a' => 'b');

        $object->setRequired(true);

        $this->assertTrue($object->isRequired());
    }

    /**
     * @covers Core_Form_Element_Abstract::isRequired
     */
    public function testIsNotRequiredByDefault()
    {
        $this->assertFalse($this->_object->isRequired());
    }

    /**
     * @covers Core_Form_Element_Abstract::setIgnored
     * @covers Core_Form_Element_Abstract::isIgnored
     */
    public function testCanSetAndGetIgnored()
    {
        $this->_object->setIgnored(true);

        $this->assertTrue($this->_object->isIgnored());
    }

    /**
     * @covers Core_Form_Element_Abstract::isIgnored
     */
    public function testIsNotIgnoredByDefault()
    {
        $this->assertFalse($this->_object->isIgnored());
    }

    /**
     * @covers Core_Form_Element_Abstract::setAllowEmpty
     * @covers Core_Form_Element_Abstract::isAllowEmpty
     */
    public function testCanSetAndGetAllowEmpty()
    {
        $this->_object->setAllowEmpty(true);

        $this->assertTrue($this->_object->isAllowEmpty());
    }

    /**
     * @covers Core_Form_Element_Abstract::isAllowEmpty
     */
    public function testAllowEmptyWillEnableRuntimeInspection()
    {
        $object = $this->getMockForAbstractClass('Core_Form_Element_Abstract', array(), '', true, true, true, array('get'));
        $object
            ->expects($this->once())
            ->method('get')
            ->with($this->equalTo('/foo/bar/value'))
            ->will($this->returnValue(23));
        $object->setAllowEmpty('/foo/bar/value == 24');
        
        $this->assertFalse($object->isAllowEmpty());
    }

    /**
     * @covers Core_Form_Element_Abstract::isAllowEmpty
     */
    public function testIfNotSetIsNotAllowedEmptyIfRequired()
    {
        $object          = new Invalid_Form_Element_Invalid;
        $object->test    = $this;
        $object->options = array('a' => 'b');

        $object->setRequired(true);

        $this->assertFalse($object->isAllowEmpty());
    }

    /**
     * @covers Core_Form_Element_Abstract::isValid
     */
    public function testElementWithNoValidatorsIsConsideredValid()
    {
        $this->assertTrue($this->_object->isValid());
    }

    /**
     * @covers Core_Form_Element_Abstract::isValid
     */
    public function testEmptyElementWhichIsAllowedEmptyIsConsideredValid()
    {
        $validator = $this->getMockForAbstractClass('Core_Validate_Abstract');
        $this->_object
            ->setAllowEmpty(true)
            ->addValidator($validator);

        $this->assertTrue($this->_object->isValid());
    }

    /**
     * @covers Core_Form_Element_Abstract::isValid
     */
    public function testIfAnAttachedValidatorIsInvalidTheElementIsInvalid()
    {
        // TODO:
        $this->markTestIncomplete('Marin dodavao neki kod, ne znam');
        
        $validator = $this->getMockForAbstractClass('Core_Validate_Abstract', array(), '', true, true, true, array('getName', 'isValid', 'getErrorMessage'));
        $validator
            ->expects($this->any())
            ->method('getName')
            ->will($this->returnValue('myValidator'));
        $validator
            ->expects($this->once())
            ->method('isValid')
            ->with($this->equalTo('someValue'))
            ->will($this->returnValue(false));
        $validator
            ->expects($this->once())
            ->method('getErrorMessage')
            ->with($this->equalTo('Element-level error message'))
            ->will($this->returnValue('Oh noez!'));

        $this->_object
            ->addValidator($validator)
            ->setErrorMessage('Element-level error message')
            ->setValue('someValue');

        $this->assertFalse($this->_object->isValid());
    }

    /**
     * @covers Core_Form_Element_Abstract::setErrorMessage
     * @covers Core_Form_Element_Abstract::getErrorMessage
     */
    public function testCanSetAndGetErrorMessage()
    {
        $this->_object->setErrorMessage('This is an error message');

        $this->assertEquals('This is an error message', $this->_object->getErrorMessage());
    }
    
    
    /**
     * @covers Core_Form_Element_Abstract::getAttrib
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testWillTranslateErrorMessageAttribIfTranslatorAvailable()
    {
        $translator = $this->getMock('Zend_Translate', array('translate'), array(), '', false);
        $translator
            ->expects($this->once())
            ->method('translate')
            ->with($this->equalTo('Some error message'))
            ->will($this->returnValue('Neka greska poruka'));
        
        $this->_object
            ->setTranslator($translator)
            ->setErrorMessage('Some error message');

        $this->assertEquals('Neka greska poruka', $this->_object->getErrorMessage());
    }

    /**
     * @covers Core_Form_Element_Abstract::setError
     * @covers Core_Form_Element_Abstract::getError
     */
    public function testCanSetAndGetError()
    {
        $this->_object->setError('This is an actual error');

        $this->assertEquals('This is an actual error', $this->_object->getError());
    }

    /**
     * @covers Core_Form_Element_Abstract::hasError
     */
    public function testCanTestIfHasError()
    {
        $this->_object->setError('This is an actual error');

        $this->assertTrue($this->_object->hasError());
    }

    /**
     * @covers Core_Form_Element_Abstract::resetError
     */
    public function testCanResetError()
    {
        $this->_object->setError('This is an actual error');
        $this->_object->resetError();

        $this->assertNull($this->_object->getError());
    }

    /**
     * @covers Core_Form_Element_Abstract::__clone
     */
    public function testWillClonePluginManagerWhileCloning()
    {
        $object = clone $this->_object;

        $this->assertNotSame($object->getPluginManager(), $this->_object->getPluginManager());
    }

    /**
     * @covers Core_Form_Element_Abstract::getValidators
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testSettingRequiredWillAutoPrependANotEmptyValidatorWhenFetchingValidators()
    {
        $validator = $this->getMockForAbstractClass('Core_Validate_Abstract', array(), '', true, true, true, array('isValid', 'getErrorMessage'));

        $object = $this->getMock('Core_Form_Element_Abstract', array('hasValidator', 'createValidator'));
        $object
            ->expects($this->once())
            ->method('hasValidator')
            ->with($this->equalTo('Notempty'))
            ->will($this->returnValue(false));
        $object
            ->expects($this->once())
            ->method('createValidator')
            ->with($this->equalTo('Notempty'), $this->equalTo('Notempty'), $this->equalTo(null));
        $object->setRequired(true);

        $object->getValidators();
    }

    /**
     * @covers Core_Form_Element_Abstract::isValid
     * @covers Core_Form_Element_Abstract::<protected>
     */
    public function testSettingRequiredWillReconfigureExistingNotEmptyValidatorWhenDoingAIsValid()
    {
        $object          = new Invalid_Form_Element_Invalid;
        $object->test    = $this;
        $object->options = array('a' => 'b');
        $object
            ->setValue(123)
            ->setRequired(true);

        $object->isValid();
    }

    /**
     * @covers Core_Form_Element_Abstract::setView
     * @covers Core_Form_Element_Abstract::getView
     */
    public function testCanSetAndGetView()
    {
        $view = $this->getMock('Twig_Environment');
        $this->_object->setView($view);

        $this->assertEquals($view, $this->_object->getView());
    }

    /**
     * @covers Core_Form_Element_Abstract::getView
     */
    public function testIfViewNotSetItWillFetchItFromApplicationAsView()
    {
        $view       = $this->getMock('Twig_Environment');
        $container  = $this->_mockContainer('View', $view);
        Core_Application::getInstance()->setContainer($container);

        $this->assertEquals($view, $this->_object->getView());
    }

    /**
     * @covers Core_Form_Element_Abstract::setTemplate
     * @covers Core_Form_Element_Abstract::getTemplate
     */
    public function testCanSetAndGetTemplate()
    {
        $template = $this->getMock('Twig_TemplateInterface');
        $view     = $this->getMock('Twig_Environment', array('loadTemplate'));
        $view
            ->expects($this->once())
            ->method('loadTemplate')
            ->with($this->equalTo('my.tpl'))
            ->will($this->returnValue($template));
        $this->_object
            ->setView($view)
            ->setTemplate('my.tpl');

        $this->assertEquals($template, $this->_object->getTemplate());
    }

    /**
     * @covers Core_Form_Element_Abstract::setMacro
     * @covers Core_Form_Element_Abstract::getMacro
     */
    public function testCanSetAndGetMacro()
    {
        $this->_object->setMacro('macro_custom');

        $this->assertEquals('macro_custom', $this->_object->getMacro());
    }

    /**
     * @covers Core_Form_Element_Abstract::set
     * @covers Core_Form_Element_Abstract::get
     */
    public function testCanGetAndSetProperty()
    {
        $this->_object->set('name', 'foo');

        $this->assertEquals('foo', $this->_object->get('name'));
    }

    /**
     * @covers Core_Form_Element_Abstract::get
     */
    public function testGettingAnUnknownPropertyWillThrowAnInvalidArgumentException()
    {
        $this->setExpectedException('InvalidArgumentException');

        $this->_object->get('no-such');
    }
    
    /**
     * @covers Core_Form_Element_Abstract::get
     */
    public function testCanGetRootViaGet()
    {
        $parent = $this->getMock('Core_Form', array('getRoot'));
        $parent
            ->expects($this->once())
            ->method('getRoot')
            ->will($this->returnValue($this));
        $this->_object->setOwner($parent);

        $this->assertEquals($this, $this->_object->get('/'));
    }

    /**
     * @covers Core_Form_Element_Abstract::get
     */
    public function testCanMakeAbsoluteGetRequest()
    {
        $root   = $this->getMock('Core_Form', array(), array(), '', false);
        $root
            ->expects($this->once())
            ->method('get')
            ->with($this->equalTo('foo/bar'))
            ->will($this->returnValue('barValue'));

        $parent = $this->getMock('Core_Form', array('getRoot'));
        $parent
            ->expects($this->once())
            ->method('getRoot')
            ->will($this->returnValue($root));
        $this->_object->setOwner($parent);

        $this->assertEquals('barValue', $this->_object->get('/foo/bar'));
    }

    /**
     * @covers Core_Form_Element_Abstract::__set
     * @covers Core_Form_Element_Abstract::__get
     */
    public function testCanGetAndSetPropertyViaMagicMethods()
    {
        $this->_object->name = 'foo';

        $this->assertEquals('foo', $this->_object->name);
    }

    /**
     * @covers Core_Form_Element_Abstract::getAngularRepresentation
     */
    public function testCanGetAngularRepresentation()
    {
        $validator1 = $this->getMockForAbstractClass('Core_Validate_Abstract', array(), '', true, true, true, array('getName', 'getAngularRepresentation'));
        $validator1
            ->expects($this->once())
            ->method('getName')
            ->will($this->returnValue('myValidator1'));
        $validator1
            ->expects($this->exactly(2))
            ->method('getAngularRepresentation')
            ->with($this->equalTo('Element error message'))
            ->will($this->returnValue(array('required' => array('errorMessage' => 'Element error message', 'config' => 'required'))));
        $this->_object->addValidator($validator1);

        $validator2 = $this->getMockForAbstractClass('Core_Validate_Abstract', array(), '', true, true, true, array('getName', 'getAngularRepresentation'));
        $validator2
            ->expects($this->once())
            ->method('getName')
            ->will($this->returnValue('myValidator2'));
        $validator2
            ->expects($this->exactly(2))
            ->method('getAngularRepresentation')
            ->with($this->equalTo('Element error message'))
            ->will($this->returnValue(array('min' => array('errorMessage' => 'Element error message', 'config' => 3))));
        $this->_object->addValidator($validator2);

        $this->_object->setErrorMessage('Element error message');

        $this->assertEquals(array(
            'required' => 'Element error message',
            'min'      => 'Element error message',
        ), $this->_object->getAngularRepresentation());

        $this->assertEquals(array(
            'required' => 'required',
            'min'      => 3,
        ), $this->_object->getAngularRepresentation(true));
    }

    /**
     * @covers Core_Form_Element_Abstract::render
     */
    public function testCanRender()
    {
        $template = $this->getMockForAbstractClass('Twig_TemplateInterface', array(), '', true, true, true, array('render'));
        $template
            ->expects($this->once())
            ->method('render')
            ->with($this->equalTo(array('this' => $this->_object, 'macro' => 'foo', 'context' => Core_Form::CONTEXT_DEFAULT)))
            ->will($this->returnValue('<rendered/>'));
        $this->_object
            ->setMacro('foo')
            ->setTemplate($template);

        $this->assertEquals('<rendered/>', $this->_object->render(Core_Form::CONTEXT_DEFAULT));
    }

    /**
     * @covers Core_Form_Element_Abstract::__toString
     */
    public function testCastingAnObjectToStringWillRender()
    {
        $template = $this->getMockForAbstractClass('Twig_TemplateInterface', array(), '', true, true, true, array('render'));
        $template
            ->expects($this->once())
            ->method('render')
            ->with($this->equalTo(array('this' => $this->_object, 'macro' => 'foo', 'context' => Core_Form::CONTEXT_DEFAULT)))
            ->will($this->returnValue('<rendered/>'));
        $this->_object
            ->setMacro('foo')
            ->setTemplate($template);

        $this->assertEquals('<rendered/>', (string) $this->_object);
    }

    /**
     * @covers Core_Form_Element_Abstract::__toString
     */
    public function testAnyExceptionRaisedDuringRenderingWillBeThrownAsAnUserError()
    {
        $template = $this->getMockForAbstractClass('Twig_TemplateInterface', array(), '', true, true, true, array('render'));
        $template
            ->expects($this->once())
            ->method('render')
            ->will($this->throwException(new Exception));
        $this->_object->setTemplate($template);

        set_error_handler(array($this, 'handleErrors'));
        $rendered = (string) $this->_object;
        restore_error_handler();

        $this->assertEmpty($rendered);
        $this->assertEquals(0, strpos($this->_error, 'Exception "Exception" while rendering form group'));
    }

    public function handleErrors($errno, $errstr)
    {
        if ($errno !== E_USER_ERROR) {
            throw new Exception($errstr);
        } else {
            $this->_error = $errstr;
        }
    }
}

class Invalid_Form_Element_Angular extends Core_Form_Element_Abstract
{
    protected $_mandatoryAttribs = array(
        Core_Form::CONTEXT_ANGULAR => array('name'),
    );

    public function getAngularRepresentation($errors = null)
    {
        if (null !== $errors) {
            return array('required' => 'required', 'min' => 3);
        }
        return array();
    }
}

class Invalid_Form_Element_Invalid extends Core_Form_Element_Abstract
{
    public $test;
    public $options = array();

    protected $_macro = 'nope';

    protected $_defaultAttribs = array(
        'randomContext' => array(),
    );

    protected $_mandatoryAttribs = array(
        'randomContext' => array('name', 'id' => 'fullyQualifiedId', 'mojMakro' => 'macro'),
    );

    public function hasValidator($name)
    {
        return true;
    }

    public function getValidator($name)
    {
        $validator = $this->test->getMockForAbstractClass('Core_Validate_Abstract', array(), '', true, true, true, array('setOptions'));
        $validator
            ->expects($this->test->once())
            ->method('setOptions')
            ->with($this->test->equalTo($this->options));
        return $validator;
    }

    protected function _prependRequiredValidator()
    {
        $this->_configureValidator('Notempty', $this->options);
    }

    protected function _setAttribFoo($value)
    {
        return $value + 1;
    }

    protected function _getAttribBar($value)
    {
        if ($value == 23) {
            return 'ne';
        }
        return $value;
    }
}