class RecursiveContextualValidatorTest

Same name in this branch
  1. 11.x core/tests/Drupal/KernelTests/Core/TypedData/RecursiveContextualValidatorTest.php \Drupal\KernelTests\Core\TypedData\RecursiveContextualValidatorTest
Same name and namespace in other branches
  1. 9 core/tests/Drupal/KernelTests/Core/TypedData/RecursiveContextualValidatorTest.php \Drupal\KernelTests\Core\TypedData\RecursiveContextualValidatorTest
  2. 9 core/tests/Drupal/Tests/Core/TypedData/RecursiveContextualValidatorTest.php \Drupal\Tests\Core\TypedData\RecursiveContextualValidatorTest
  3. 8.9.x core/tests/Drupal/KernelTests/Core/TypedData/RecursiveContextualValidatorTest.php \Drupal\KernelTests\Core\TypedData\RecursiveContextualValidatorTest
  4. 8.9.x core/tests/Drupal/Tests/Core/TypedData/RecursiveContextualValidatorTest.php \Drupal\Tests\Core\TypedData\RecursiveContextualValidatorTest
  5. 10 core/tests/Drupal/KernelTests/Core/TypedData/RecursiveContextualValidatorTest.php \Drupal\KernelTests\Core\TypedData\RecursiveContextualValidatorTest
  6. 10 core/tests/Drupal/Tests/Core/TypedData/RecursiveContextualValidatorTest.php \Drupal\Tests\Core\TypedData\RecursiveContextualValidatorTest

@coversDefaultClass \Drupal\Core\TypedData\Validation\RecursiveContextualValidator
@group TypedData

Hierarchy

Expanded class hierarchy of RecursiveContextualValidatorTest

File

core/tests/Drupal/Tests/Core/TypedData/RecursiveContextualValidatorTest.php, line 24

Namespace

Drupal\Tests\Core\TypedData
View source
class RecursiveContextualValidatorTest extends UnitTestCase {
  
  /**
   * The type data manager.
   *
   * @var \Drupal\Core\TypedData\TypedDataManager
   */
  protected $typedDataManager;
  
  /**
   * The recursive validator.
   *
   * @var \Drupal\Core\TypedData\Validation\RecursiveValidator
   */
  protected $recursiveValidator;
  
  /**
   * The validator factory.
   *
   * @var \Symfony\Component\Validator\ConstraintValidatorFactoryInterface
   */
  protected $validatorFactory;
  
  /**
   * The execution context factory.
   *
   * @var \Drupal\Core\Validation\ExecutionContextFactory
   */
  protected $contextFactory;
  
  /**
   * {@inheritdoc}
   */
  protected function setUp() : void {
    parent::setUp();
    $cache_backend = new NullBackend('cache');
    $namespaces = new \ArrayObject([
      'Drupal\\Core\\TypedData' => $this->root . '/core/lib/Drupal/Core/TypedData',
      'Drupal\\Core\\Validation' => $this->root . '/core/lib/Drupal/Core/Validation',
    ]);
    $module_handler = $this->getMockBuilder('Drupal\\Core\\Extension\\ModuleHandlerInterface')
      ->disableOriginalConstructor()
      ->getMock();
    $class_resolver = $this->getMockBuilder('Drupal\\Core\\DependencyInjection\\ClassResolverInterface')
      ->disableOriginalConstructor()
      ->getMock();
    $this->typedDataManager = new TypedDataManager($namespaces, $cache_backend, $module_handler, $class_resolver);
    $this->typedDataManager
      ->setValidationConstraintManager(new ConstraintManager($namespaces, $cache_backend, $module_handler));
    // Typed data definitions access the manager in the container.
    $container = new ContainerBuilder();
    $container->set('typed_data_manager', $this->typedDataManager);
    \Drupal::setContainer($container);
    $translator = $this->createMock('Drupal\\Core\\Validation\\TranslatorInterface');
    $translator->expects($this->any())
      ->method('trans')
      ->willReturnCallback(function ($id) {
      return $id;
    });
    $this->contextFactory = new ExecutionContextFactory($translator);
    $this->validatorFactory = new ConstraintValidatorFactory();
    $this->recursiveValidator = new RecursiveValidator($this->contextFactory, $this->validatorFactory, $this->typedDataManager);
  }
  
  /**
   * Ensures that passing an explicit group is not supported.
   *
   * @covers ::validate
   */
  public function testValidateWithGroups() : void {
    $this->expectException(\LogicException::class);
    $this->recursiveValidator
      ->validate('test', NULL, 'test group');
  }
  
  /**
   * Ensures that passing a non typed data value is not supported.
   *
   * @covers ::validate
   */
  public function testValidateWithoutTypedData() : void {
    $this->expectException(\InvalidArgumentException::class);
    $this->recursiveValidator
      ->validate('test');
  }
  
  /**
   * @covers ::validate
   */
  public function testBasicValidateWithoutConstraints() : void {
    $typed_data = $this->typedDataManager
      ->create(DataDefinition::create('string'));
    $violations = $this->recursiveValidator
      ->validate($typed_data);
    $this->assertCount(0, $violations);
  }
  
  /**
   * @covers ::validate
   */
  public function testBasicValidateWithConstraint() : void {
    $typed_data = $this->typedDataManager
      ->create(DataDefinition::create('string')->addConstraint('Callback', [
      'callback' => function ($value, ExecutionContextInterface $context) {
        $context->addViolation('test violation: ' . $value);
      },
    ]));
    $typed_data->setValue('foo');
    $violations = $this->recursiveValidator
      ->validate($typed_data);
    $this->assertCount(1, $violations);
    // Ensure that the right value is passed into the validator.
    $this->assertEquals('test violation: foo', $violations->get(0)
      ->getMessage());
  }
  
  /**
   * @covers ::validate
   */
  public function testBasicValidateWithMultipleConstraints() : void {
    $options = [
      'callback' => function ($value, ExecutionContextInterface $context) {
        $context->addViolation('test violation');
      },
    ];
    $typed_data = $this->typedDataManager
      ->create(DataDefinition::create('string')->addConstraint('Callback', $options)
      ->addConstraint('NotNull'));
    $violations = $this->recursiveValidator
      ->validate($typed_data);
    $this->assertCount(2, $violations);
  }
  
  /**
   * @covers ::validate
   */
  public function testPropertiesValidateWithMultipleLevels() : void {
    $typed_data = $this->buildExampleTypedDataWithProperties();
    $violations = $this->recursiveValidator
      ->validate($typed_data);
    $this->assertCount(6, $violations);
    $this->assertEquals('violation: 3', $violations->get(0)
      ->getMessage());
    $this->assertEquals('violation: value1', $violations->get(1)
      ->getMessage());
    $this->assertEquals('violation: value2', $violations->get(2)
      ->getMessage());
    $this->assertEquals('violation: 2', $violations->get(3)
      ->getMessage());
    $this->assertEquals('violation: subvalue1', $violations->get(4)
      ->getMessage());
    $this->assertEquals('violation: subvalue2', $violations->get(5)
      ->getMessage());
    $this->assertEquals('', $violations->get(0)
      ->getPropertyPath());
    $this->assertEquals('key1', $violations->get(1)
      ->getPropertyPath());
    $this->assertEquals('key2', $violations->get(2)
      ->getPropertyPath());
    $this->assertEquals('key_with_properties', $violations->get(3)
      ->getPropertyPath());
    $this->assertEquals('key_with_properties.subkey1', $violations->get(4)
      ->getPropertyPath());
    $this->assertEquals('key_with_properties.subkey2', $violations->get(5)
      ->getPropertyPath());
  }
  
  /**
   * Setups a typed data object used for test purposes.
   *
   * @param array $tree
   *   An array of value, constraints and properties.
   * @param string $name
   *   The name to use for the object.
   *
   * @return \Drupal\Core\TypedData\TypedDataInterface|\PHPUnit\Framework\MockObject\MockObject
   *   The typed data object.
   */
  protected function setupTypedData(array $tree, $name = '') {
    $callback = function ($value, ExecutionContextInterface $context) {
      $context->addViolation('violation: ' . (is_array($value) ? count($value) : $value));
    };
    $tree += [
      'constraints' => [],
    ];
    if (isset($tree['properties'])) {
      $map_data_definition = MapDataDefinition::create();
      $map_data_definition->addConstraint('Callback', [
        'callback' => $callback,
      ]);
      foreach ($tree['properties'] as $property_name => $property) {
        $sub_typed_data = $this->setupTypedData($property, $property_name);
        $map_data_definition->setPropertyDefinition($property_name, $sub_typed_data->getDataDefinition());
      }
      $typed_data = $this->typedDataManager
        ->create($map_data_definition, $tree['value'], $name);
    }
    else {
      /** @var \Drupal\Core\TypedData\TypedDataInterface $typed_data */
      $typed_data = $this->typedDataManager
        ->create(DataDefinition::create('string')->addConstraint('Callback', [
        'callback' => $callback,
      ]), $tree['value'], $name);
    }
    return $typed_data;
  }
  
  /**
   * @covers ::validateProperty
   */
  public function testValidatePropertyWithCustomGroup() : void {
    $tree = [
      'value' => [],
      'properties' => [
        'key1' => [
          'value' => 'value1',
        ],
      ],
    ];
    $typed_data = $this->setupTypedData($tree, 'test_name');
    $this->expectException(\LogicException::class);
    $this->recursiveValidator
      ->validateProperty($typed_data, 'key1', 'test group');
  }
  
  /**
   * @covers ::validateProperty
   *
   * @dataProvider providerTestValidatePropertyWithInvalidObjects
   */
  public function testValidatePropertyWithInvalidObjects($object) : void {
    $this->expectException(\InvalidArgumentException::class);
    $this->recursiveValidator
      ->validateProperty($object, 'key1', NULL);
  }
  
  /**
   * Provides data for testValidatePropertyWithInvalidObjects.
   */
  public static function providerTestValidatePropertyWithInvalidObjects() : \Generator {
    $dataDefinition = new DataDefinition();
    (yield [
      new \stdClass(),
    ]);
    (yield [
      new class  {

},
    ]);
    (yield [
      new class ($dataDefinition) extends TypedDataBase {

},
    ]);
  }
  
  /**
   * @covers ::validateProperty
   */
  public function testValidateProperty() : void {
    $typed_data = $this->buildExampleTypedDataWithProperties();
    $violations = $this->recursiveValidator
      ->validateProperty($typed_data, 'key_with_properties');
    $this->assertCount(3, $violations);
    $this->assertEquals('violation: 2', $violations->get(0)
      ->getMessage());
    $this->assertEquals('violation: subvalue1', $violations->get(1)
      ->getMessage());
    $this->assertEquals('violation: subvalue2', $violations->get(2)
      ->getMessage());
    $this->assertEquals('', $violations->get(0)
      ->getPropertyPath());
    $this->assertEquals('subkey1', $violations->get(1)
      ->getPropertyPath());
    $this->assertEquals('subkey2', $violations->get(2)
      ->getPropertyPath());
  }
  
  /**
   * @covers ::validatePropertyValue
   *
   * @dataProvider providerTestValidatePropertyWithInvalidObjects
   */
  public function testValidatePropertyValueWithInvalidObjects($object) : void {
    $this->expectException(\InvalidArgumentException::class);
    $this->recursiveValidator
      ->validatePropertyValue($object, 'key1', [], NULL);
  }
  
  /**
   * @covers ::validatePropertyValue
   */
  public function testValidatePropertyValue() : void {
    $typed_data = $this->buildExampleTypedDataWithProperties([
      'subkey1' => 'subvalue11',
      'subkey2' => 'subvalue22',
    ]);
    $violations = $this->recursiveValidator
      ->validatePropertyValue($typed_data, 'key_with_properties', $typed_data->get('key_with_properties'));
    $this->assertCount(3, $violations);
    $this->assertEquals('violation: 2', $violations->get(0)
      ->getMessage());
    $this->assertEquals('violation: subvalue11', $violations->get(1)
      ->getMessage());
    $this->assertEquals('violation: subvalue22', $violations->get(2)
      ->getMessage());
    $this->assertEquals('', $violations->get(0)
      ->getPropertyPath());
    $this->assertEquals('subkey1', $violations->get(1)
      ->getPropertyPath());
    $this->assertEquals('subkey2', $violations->get(2)
      ->getPropertyPath());
  }
  
  /**
   * Builds some example type data object.
   *
   * @return \Drupal\Core\TypedData\TypedDataInterface|\PHPUnit\Framework\MockObject\MockObject
   *   A typed data object with nested properties for testing purposes.
   */
  protected function buildExampleTypedDataWithProperties($subkey_value = NULL) {
    $subkey_value = $subkey_value ?: [
      'subkey1' => 'subvalue1',
      'subkey2' => 'subvalue2',
    ];
    $tree = [
      'value' => [
        'key1' => 'value1',
        'key2' => 'value2',
        'key_with_properties' => $subkey_value,
      ],
    ];
    $tree['properties'] = [
      'key1' => [
        'value' => 'value1',
      ],
      'key2' => [
        'value' => 'value2',
      ],
      'key_with_properties' => [
        'value' => $subkey_value ?: [
          'subkey1' => 'subvalue1',
          'subkey2' => 'subvalue2',
        ],
      ],
    ];
    $tree['properties']['key_with_properties']['properties']['subkey1'] = [
      'value' => $tree['properties']['key_with_properties']['value']['subkey1'],
    ];
    $tree['properties']['key_with_properties']['properties']['subkey2'] = [
      'value' => $tree['properties']['key_with_properties']['value']['subkey2'],
    ];
    return $this->setupTypedData($tree, 'test_name');
  }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
ExpectDeprecationTrait::expectDeprecation public function Adds an expected deprecation.
ExpectDeprecationTrait::setUpErrorHandler public function Sets up the test error handler.
ExpectDeprecationTrait::tearDownErrorHandler public function Tears down the test error handler.
RandomGeneratorTrait::getRandomGenerator protected function Gets the random generator for the utility methods.
RandomGeneratorTrait::randomMachineName protected function Generates a unique random string containing letters and numbers.
RandomGeneratorTrait::randomObject public function Generates a random PHP object.
RandomGeneratorTrait::randomString public function Generates a pseudo-random string of ASCII characters of codes 32 to 126.
RecursiveContextualValidatorTest::$contextFactory protected property The execution context factory.
RecursiveContextualValidatorTest::$recursiveValidator protected property The recursive validator.
RecursiveContextualValidatorTest::$typedDataManager protected property The type data manager.
RecursiveContextualValidatorTest::$validatorFactory protected property The validator factory.
RecursiveContextualValidatorTest::buildExampleTypedDataWithProperties protected function Builds some example type data object.
RecursiveContextualValidatorTest::providerTestValidatePropertyWithInvalidObjects public static function Provides data for testValidatePropertyWithInvalidObjects.
RecursiveContextualValidatorTest::setUp protected function Overrides UnitTestCase::setUp
RecursiveContextualValidatorTest::setupTypedData protected function Setups a typed data object used for test purposes.
RecursiveContextualValidatorTest::testBasicValidateWithConstraint public function @covers ::validate[[api-linebreak]]
RecursiveContextualValidatorTest::testBasicValidateWithMultipleConstraints public function @covers ::validate[[api-linebreak]]
RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints public function @covers ::validate[[api-linebreak]]
RecursiveContextualValidatorTest::testPropertiesValidateWithMultipleLevels public function @covers ::validate[[api-linebreak]]
RecursiveContextualValidatorTest::testValidateProperty public function @covers ::validateProperty[[api-linebreak]]
RecursiveContextualValidatorTest::testValidatePropertyValue public function @covers ::validatePropertyValue[[api-linebreak]]
RecursiveContextualValidatorTest::testValidatePropertyValueWithInvalidObjects public function @covers ::validatePropertyValue[[api-linebreak]]
RecursiveContextualValidatorTest::testValidatePropertyWithCustomGroup public function @covers ::validateProperty[[api-linebreak]]
RecursiveContextualValidatorTest::testValidatePropertyWithInvalidObjects public function @covers ::validateProperty[[api-linebreak]]
RecursiveContextualValidatorTest::testValidateWithGroups public function Ensures that passing an explicit group is not supported.
RecursiveContextualValidatorTest::testValidateWithoutTypedData public function Ensures that passing a non typed data value is not supported.
UnitTestCase::$root protected property The app root.
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::setDebugDumpHandler public static function Registers the dumper CLI handler when the DebugDump extension is enabled.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.