mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-10-31 10:39:59 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			331 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * @link https://www.yiiframework.com/
 | |
|  * @copyright Copyright (c) 2008 Yii Software LLC
 | |
|  * @license https://www.yiiframework.com/license/
 | |
|  */
 | |
| 
 | |
| namespace yiiunit\framework\validators;
 | |
| 
 | |
| use yii\base\DynamicModel;
 | |
| use yii\validators\BooleanValidator;
 | |
| use yii\validators\InlineValidator;
 | |
| use yii\validators\NumberValidator;
 | |
| use yii\validators\RequiredValidator;
 | |
| use yii\validators\Validator;
 | |
| use yiiunit\data\validators\models\FakedValidationModel;
 | |
| use yiiunit\data\validators\models\ValidatorTestFunctionModel;
 | |
| use yiiunit\data\validators\TestValidator;
 | |
| use yiiunit\TestCase;
 | |
| use yii\validators\SafeValidator;
 | |
| 
 | |
| /**
 | |
|  * @group validators
 | |
|  */
 | |
| class ValidatorTest extends TestCase
 | |
| {
 | |
|     protected function setUp(): void
 | |
|     {
 | |
|         parent::setUp();
 | |
| 
 | |
|         // destroy application, Validator must work without Yii::$app
 | |
|         $this->destroyApplication();
 | |
|     }
 | |
| 
 | |
|     protected function getTestModel($additionalAttributes = [])
 | |
|     {
 | |
|         $attributes = array_merge(
 | |
|             ['attr_runMe1' => true, 'attr_runMe2' => true, 'attr_skip' => true],
 | |
|             $additionalAttributes
 | |
|         );
 | |
| 
 | |
|         return FakedValidationModel::createWithAttributes($attributes);
 | |
|     }
 | |
| 
 | |
|     public function testCreateValidator()
 | |
|     {
 | |
|         $model = FakedValidationModel::createWithAttributes(['attr_test1' => 'abc', 'attr_test2' => '2013']);
 | |
|         /** @var NumberValidator $numberVal */
 | |
|         $numberVal = TestValidator::createValidator('number', $model, ['attr_test1']);
 | |
|         $this->assertInstanceOf(NumberValidator::className(), $numberVal);
 | |
|         $numberVal = TestValidator::createValidator('integer', $model, ['attr_test2']);
 | |
|         $this->assertInstanceOf(NumberValidator::className(), $numberVal);
 | |
|         $this->assertTrue($numberVal->integerOnly);
 | |
|         $val = TestValidator::createValidator(
 | |
|             'boolean',
 | |
|             $model,
 | |
|             ['attr_test1', 'attr_test2'],
 | |
|             ['on' => ['a', 'b']]
 | |
|         );
 | |
|         $this->assertInstanceOf(BooleanValidator::className(), $val);
 | |
|         $this->assertSame(['a', 'b'], $val->on);
 | |
|         $this->assertSame(['attr_test1', 'attr_test2'], $val->attributes);
 | |
|         $val = TestValidator::createValidator(
 | |
|             'boolean',
 | |
|             $model,
 | |
|             ['attr_test1', 'attr_test2'],
 | |
|             ['on' => ['a', 'b'], 'except' => ['c', 'd', 'e']]
 | |
|         );
 | |
|         $this->assertInstanceOf(BooleanValidator::className(), $val);
 | |
|         $this->assertSame(['a', 'b'], $val->on);
 | |
|         $this->assertSame(['c', 'd', 'e'], $val->except);
 | |
|         $val = TestValidator::createValidator('inlineVal', $model, ['val_attr_a'], ['params' => ['foo' => 'bar']]);
 | |
|         $this->assertInstanceOf(InlineValidator::className(), $val);
 | |
|         $this->assertSame('inlineVal', $val->method[1]);
 | |
|         $this->assertSame(['foo' => 'bar'], $val->params);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @see https://github.com/yiisoft/yii2/issues/14370
 | |
|      */
 | |
|     public function testCreateBuiltInValidatorWithSameNameFunction()
 | |
|     {
 | |
|         $model = new ValidatorTestFunctionModel();
 | |
| 
 | |
|         $validator = TestValidator::createValidator('required', $model, ['firstAttribute']);
 | |
| 
 | |
|         $this->assertInstanceOf(RequiredValidator::className(), $validator);
 | |
|     }
 | |
| 
 | |
|     public function testValidateAttributes()
 | |
|     {
 | |
|         $val = new TestValidator(['attributes' => ['attr_runMe1', 'attr_runMe2']]);
 | |
|         $model = $this->getTestModel();
 | |
|         $val->validateAttributes($model);
 | |
|         $this->assertTrue($val->isAttributeValidated('attr_runMe1'));
 | |
|         $this->assertTrue($val->isAttributeValidated('attr_runMe2'));
 | |
|         $this->assertFalse($val->isAttributeValidated('attr_skip'));
 | |
|     }
 | |
| 
 | |
|     public function testValidateWithAttributeIntersect()
 | |
|     {
 | |
|         $val = new TestValidator(['attributes' => ['attr_runMe1', 'attr_runMe2']]);
 | |
|         $model = $this->getTestModel();
 | |
|         $val->validateAttributes($model, ['attr_runMe1']);
 | |
|         $this->assertTrue($val->isAttributeValidated('attr_runMe1'));
 | |
|         $this->assertFalse($val->isAttributeValidated('attr_runMe2'));
 | |
|         $this->assertFalse($val->isAttributeValidated('attr_skip'));
 | |
|     }
 | |
| 
 | |
|     public function testValidateWithEmptyAttributes()
 | |
|     {
 | |
|         $val = new TestValidator();
 | |
|         $model = $this->getTestModel();
 | |
|         $val->validateAttributes($model, ['attr_runMe1']);
 | |
|         $this->assertFalse($val->isAttributeValidated('attr_runMe1'));
 | |
|         $this->assertFalse($val->isAttributeValidated('attr_runMe2'));
 | |
|         $this->assertFalse($val->isAttributeValidated('attr_skip'));
 | |
|         $val->validateAttributes($model);
 | |
|         $this->assertFalse($val->isAttributeValidated('attr_runMe1'));
 | |
|         $this->assertFalse($val->isAttributeValidated('attr_runMe2'));
 | |
|         $this->assertFalse($val->isAttributeValidated('attr_skip'));
 | |
|     }
 | |
| 
 | |
|     public function testValidateWithError()
 | |
|     {
 | |
|         $val = new TestValidator(['attributes' => ['attr_runMe1', 'attr_runMe2'], 'skipOnError' => false]);
 | |
|         $model = $this->getTestModel();
 | |
|         $val->validateAttributes($model);
 | |
|         $this->assertTrue($val->isAttributeValidated('attr_runMe1'));
 | |
|         $this->assertTrue($val->isAttributeValidated('attr_runMe2'));
 | |
|         $this->assertFalse($val->isAttributeValidated('attr_skip'));
 | |
|         $this->assertEquals(1, $val->countAttributeValidations('attr_runMe2'));
 | |
|         $this->assertEquals(1, $val->countAttributeValidations('attr_runMe1'));
 | |
|         $val->validateAttributes($model, ['attr_runMe2']);
 | |
|         $this->assertEquals(2, $val->countAttributeValidations('attr_runMe2'));
 | |
|         $this->assertEquals(1, $val->countAttributeValidations('attr_runMe1'));
 | |
|         $this->assertEquals(0, $val->countAttributeValidations('attr_skip'));
 | |
|         $val = new TestValidator(['attributes' => ['attr_runMe1', 'attr_runMe2'], 'skipOnError' => true]);
 | |
|         $model = $this->getTestModel();
 | |
|         $val->enableErrorOnValidateAttribute();
 | |
|         $val->validateAttributes($model);
 | |
|         $this->assertTrue($val->isAttributeValidated('attr_runMe1'));
 | |
|         $this->assertTrue($val->isAttributeValidated('attr_runMe2'));
 | |
|         $this->assertFalse($val->isAttributeValidated('attr_skip'));
 | |
|         $this->assertEquals(1, $val->countAttributeValidations('attr_runMe1'));
 | |
|         $this->assertEquals(1, $val->countAttributeValidations('attr_runMe1'));
 | |
|         $this->assertEquals(0, $val->countAttributeValidations('attr_skip'));
 | |
|         $val->validateAttributes($model, ['attr_runMe2']);
 | |
|         $this->assertEquals(1, $val->countAttributeValidations('attr_runMe2'));
 | |
|         $this->assertEquals(1, $val->countAttributeValidations('attr_runMe1'));
 | |
|         $this->assertEquals(0, $val->countAttributeValidations('attr_skip'));
 | |
|     }
 | |
| 
 | |
|     public function testValidateWithEmpty()
 | |
|     {
 | |
|         $model = $this->getTestModel(['attr_empty1' => '', 'attr_empty2' => ' ']);
 | |
|         $attributes = ['attr_runMe1', 'attr_runMe2', 'attr_empty1', 'attr_empty2'];
 | |
| 
 | |
|         $validator = new TestValidator(['attributes' => $attributes, 'skipOnEmpty' => false]);
 | |
|         $validator->validateAttributes($model);
 | |
| 
 | |
|         $this->assertTrue($validator->isAttributeValidated('attr_runMe1'));
 | |
|         $this->assertTrue($validator->isAttributeValidated('attr_runMe2'));
 | |
|         $this->assertTrue($validator->isAttributeValidated('attr_empty1'));
 | |
|         $this->assertTrue($validator->isAttributeValidated('attr_empty2'));
 | |
| 
 | |
| 
 | |
|         $validator = new TestValidator(['attributes' => $attributes, 'skipOnEmpty' => true]);
 | |
|         $validator->validateAttributes($model);
 | |
| 
 | |
|         $this->assertTrue($validator->isAttributeValidated('attr_runMe1'));
 | |
|         $this->assertTrue($validator->isAttributeValidated('attr_runMe2'));
 | |
|         $this->assertFalse($validator->isAttributeValidated('attr_empty1'));
 | |
|         $this->assertTrue($validator->isAttributeValidated('attr_empty2'));
 | |
| 
 | |
|         $model->attr_empty1 = 'not empty anymore';
 | |
|         $validator->validateAttributes($model);
 | |
|         $this->assertTrue($validator->isAttributeValidated('attr_empty1'));
 | |
|     }
 | |
| 
 | |
|     public function testIsEmpty()
 | |
|     {
 | |
|         $val = new TestValidator();
 | |
|         $this->assertTrue($val->isEmpty(null));
 | |
|         $this->assertTrue($val->isEmpty([]));
 | |
|         $this->assertTrue($val->isEmpty(''));
 | |
|         $this->assertFalse($val->isEmpty(5));
 | |
|         $this->assertFalse($val->isEmpty(0));
 | |
|         $this->assertFalse($val->isEmpty(new \stdClass()));
 | |
|         $this->assertFalse($val->isEmpty('  '));
 | |
|     }
 | |
| 
 | |
|     public function testValidateValue()
 | |
|     {
 | |
|         $this->expectException('yii\base\NotSupportedException');
 | |
|         $this->expectExceptionMessage(TestValidator::className() . ' does not support validateValue().');
 | |
|         $val = new TestValidator();
 | |
|         $val->validate('abc');
 | |
|     }
 | |
| 
 | |
|     public function testValidateAttribute()
 | |
|     {
 | |
|         // Access to validator in inline validation (https://github.com/yiisoft/yii2/issues/6242)
 | |
| 
 | |
|         $model = new FakedValidationModel();
 | |
|         $model->val_attr_a = 'a';
 | |
|         $val = Validator::createValidator('inlineVal', $model, ['val_attr_a'], ['params' => ['foo' => 'bar']]);
 | |
|         $val->validateAttribute($model, 'val_attr_a');
 | |
|         $args = $model->getInlineValArgs();
 | |
| 
 | |
|         $this->assertCount(4, $args);
 | |
|         $this->assertEquals('val_attr_a', $args[0]);
 | |
|         $this->assertEquals('a', $args[3]);
 | |
|         $this->assertEquals(['foo' => 'bar'], $args[1]);
 | |
|         $this->assertInstanceOf(InlineValidator::className(), $args[2]);
 | |
|     }
 | |
| 
 | |
|     public function testClientValidateAttribute()
 | |
|     {
 | |
|         $view = new \yii\base\View();
 | |
|         $val = new TestValidator();
 | |
|         $this->assertNull(
 | |
|             $val->clientValidateAttribute($this->getTestModel(), 'attr_runMe1', $view)
 | |
|         );
 | |
| 
 | |
|         // Access to validator in inline validation (https://github.com/yiisoft/yii2/issues/6242)
 | |
| 
 | |
|         $model = new FakedValidationModel();
 | |
|         $val = Validator::createValidator('inlineVal', $model, ['val_attr_a'], ['params' => ['foo' => 'bar']]);
 | |
|         $val->clientValidate = 'clientInlineVal';
 | |
|         $args = $val->clientValidateAttribute($model, 'val_attr_a', $view);
 | |
| 
 | |
|         $this->assertCount(5, $args);
 | |
|         $this->assertEquals('val_attr_a', $args[0]);
 | |
|         $this->assertEquals(['foo' => 'bar'], $args[1]);
 | |
|         $this->assertInstanceOf(InlineValidator::className(), $args[2]);
 | |
|     }
 | |
| 
 | |
|     public function testIsActive()
 | |
|     {
 | |
|         $val = new TestValidator();
 | |
|         $this->assertTrue($val->isActive('scenA'));
 | |
|         $this->assertTrue($val->isActive('scenB'));
 | |
|         $val->except = ['scenB'];
 | |
|         $this->assertTrue($val->isActive('scenA'));
 | |
|         $this->assertFalse($val->isActive('scenB'));
 | |
|         $val->on = ['scenC'];
 | |
|         $this->assertFalse($val->isActive('scenA'));
 | |
|         $this->assertFalse($val->isActive('scenB'));
 | |
|         $this->assertTrue($val->isActive('scenC'));
 | |
|     }
 | |
| 
 | |
|     public function testAddError()
 | |
|     {
 | |
|         $val = new TestValidator();
 | |
|         $m = $this->getTestModel(['attr_msg_val' => 'abc']);
 | |
|         $val->addError($m, 'attr_msg_val', '{attribute}::{value}');
 | |
|         $errors = $m->getErrors('attr_msg_val');
 | |
|         $this->assertEquals('attr_msg_val::abc', $errors[0]);
 | |
|         $m = $this->getTestModel(['attr_msg_val' => ['bcc']]);
 | |
|         $val->addError($m, 'attr_msg_val', '{attribute}::{value}');
 | |
|         $errors = $m->getErrors('attr_msg_val');
 | |
|         $this->assertEquals('attr_msg_val::array()', $errors[0]);
 | |
|         $m = $this->getTestModel(['attr_msg_val' => 'abc']);
 | |
|         $val->addError($m, 'attr_msg_val', '{attribute}::{value}::{param}', ['param' => 'param_value']);
 | |
|         $errors = $m->getErrors('attr_msg_val');
 | |
|         $this->assertEquals('attr_msg_val::abc::param_value', $errors[0]);
 | |
|     }
 | |
| 
 | |
|     public function testGetAttributeNames()
 | |
|     {
 | |
|         $validator = new TestValidator();
 | |
|         $validator->attributes = ['id', 'name', '!email'];
 | |
|         $this->assertEquals(['id', 'name', 'email'], $validator->getAttributeNames());
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @depends  testGetAttributeNames
 | |
|      */
 | |
|     public function testGetActiveValidatorsForSafeAttributes()
 | |
|     {
 | |
|         $model = $this->getTestModel();
 | |
|         $validators = $model->getActiveValidators('safe_attr');
 | |
|         $isFound = false;
 | |
|         foreach ($validators as $v) {
 | |
|             if ($v instanceof NumberValidator) {
 | |
|                 $isFound = true;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         $this->assertTrue($isFound);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Make sure attribute names are calculated dynamically.
 | |
|      * @see https://github.com/yiisoft/yii2/issues/13979
 | |
|      * @see https://github.com/yiisoft/yii2/pull/14413
 | |
|      */
 | |
|     public function testAttributeNamesDynamic()
 | |
|     {
 | |
|         $model = new DynamicModel(['email1' => 'invalid', 'email2' => 'invalid']);
 | |
|         $validator = new TestValidator();
 | |
|         $validator->enableErrorOnValidateAttribute();
 | |
| 
 | |
|         $validator->attributes = ['email1'];
 | |
|         $model->getValidators()->append($validator);
 | |
|         $this->assertFalse($model->validate());
 | |
| 
 | |
|         $validator->attributes = ['email2'];
 | |
|         $model->getValidators()->append($validator);
 | |
|         $this->assertFalse($model->validate());
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @see https://github.com/yiisoft/yii2/issues/17233
 | |
|      * @see https://github.com/yiisoft/yii2/pull/17234
 | |
|      */
 | |
|     public function testScalarAttributeNames()
 | |
|     {
 | |
|         $model = new DynamicModel();
 | |
|         $model->defineAttribute(1);
 | |
|         $model->addRule([1], SafeValidator::className());
 | |
| 
 | |
|         $this->assertNull($model->{1});
 | |
|         $this->assertTrue($model->validate([1]));
 | |
| 
 | |
|         $validator = SafeValidator::createValidator('safe', $model, [1]);
 | |
|         $this->assertSame([1], $validator->getValidationAttributes(1));
 | |
|     }
 | |
| }
 | 
